带有多个 windows 的空白 Firefox 插件面板页面

Blank Firefox addon panel page with multiple windows

我已经按照 MDN's document 创建了一个切换按钮插件。

除一个问题外一切正常:

  1. 打开第二个浏览器window(cmd+n 或 ctrl+n)并单击那里的切换按钮
  2. 点击原始浏览器上的切换按钮 window 而不关闭第二个 window 上的切换按钮
  3. 切换按钮的面板变为空白并显示以下错误消息:

    JavaScript 错误:资源:///modules/WindowsPreviewPerTab.jsm,第 406 行:NS_ERR OR_FAILURE:组件返回失败代码:0x80004005 (NS_ERROR_FAILURE) [nsIT askbarTabPreview.invalidate]

// ./lib/main.js
var { ToggleButton } = require("sdk/ui/button/toggle");
var panels = require("sdk/panel");
var self = require("sdk/self");

var buttonIndex = 0;
var lastKnownButtonIndex = 0;

var button = ToggleButton({
    id: "button",
    label: "my button",
    icon: {
        "16": "./icon-16.png"
    },
    onClick: handleChange,
});

var panel = panels.Panel({
    contentURL: self.data.url("menu.html"),
    onHide: handleHide
});

function handleChange(state) {
    if (state.checked) {
        panel.show({
            position: button
        });
    }
}

function handleHide() {
  button.state('window', {checked: false});
}

function assignButtonIndex() {
    return (buttonIndex++).toString();
}

完整的插件在这里:https://goo.gl/9N3jle

要重现:提取 zip 文件和 $ cd testButton; cfx run 并按照上述步骤操作。

我真的希望有人能帮我解决这个问题。提前致谢!

这是一个错误;你没有做错任何事。这是 windows' 焦点事件和面板事件之间的竞争条件,以某种方式阻止正确发出面板的隐藏事件。

您可以尝试通过解决方法来缓解问题,直到问题得到妥善解决。我在错误中添加了一些解释:https://bugzilla.mozilla.org/show_bug.cgi?id=1174425#c2 但简而言之,您可以尝试添加一个 setTimeout 以在显示面板时延迟一点,以避免与 window的重点。类似于:

const { setTimeout } = require("sdk/timers");

/* ... your code here ... */    

function handleChange(state) {
  if (state.checked) {
    setTimeout(() => panel.show({ position: button }), 100);
  }
}

我目前正在使用一种变通方法,每次用户按下工具栏按钮时我都会动态创建一个新的 Panel

它比 100 毫秒的解决方法更快,并且还可以处理用户在面板打开时完全关闭其中一个浏览器 windows 的情况。 (在这种情况下,100 毫秒的解决方法失败了,仍然显示空白面板。)

它是这样工作的:

let myPanel = null;

const toolbarButton = ToggleButton({
    ...,
    onChange: function (state) {
        if (state.checked) {
            createPanel();
        }
    }
});

function createPanel(){   
    // Prevent memory leaks
    if(myPanel){
        myPanel.destroy();
    }

    // Create a new instance of the panel
    myPanel = Panel({
        ...,
        onHide: function(){
            // Destroy the panel instead of just hiding it.
            myPanel.destroy();
        }
    });

    // Display the panel immediately
    myPanel.show();
}