Search code examples
firefox-addonfirefox-addon-sdk

Blank Firefox addon panel page with multiple windows


I've followed MDN's document to create a toggle button addon.

Everything works fine except one problem:

  1. Open a second browser window (cmd+n or ctrl+n) and click on the toggle button there
  2. Click on the toggle button on the original browser window without closing the toggle button on the second window
  3. the toggle button's panel becomes blank with the following error message:

    JavaScript error: resource:///modules/WindowsPreviewPerTab.jsm, line 406: NS_ERR OR_FAILURE: Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIT askbarTabPreview.invalidate]

problem screenshot

// ./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();
}

The complete addon is here: https://goo.gl/9N3jle

To reproduce: Extract the zip file and $ cd testButton; cfx run and follow the above steps.

I really hope someone can help me with this. Thank you in advance!


Solution

  • It's a bug; you're not doing anything wrong. It's a racing condition between the windows' focus events, and the panel's event, that prevent somehow the panel's hidden event to be emitted properly.

    You can try to mitigate with a workaround the issue until is properly fixed. I added some explanation in the bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1174425#c2 but in short, you can try to add a setTimeout to delay a bit when the panel is shown, in order to avoid the racing condition with the window's focus. Something like:

    const { setTimeout } = require("sdk/timers");
    
    /* ... your code here ... */    
    
    function handleChange(state) {
      if (state.checked) {
        setTimeout(() => panel.show({ position: button }), 100);
      }
    }