Blank Firefox addon panel page with multiple windows

273 views Asked by At

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!

2

There are 2 answers

2
ZER0 On BEST ANSWER

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);
  }
}
0
Bragolgirith On

I am currently using a workaround where I dynamically create a new Panel every time the user presses the toolbar button.

It is faster than the 100ms workaround and also handles a scenario where the user outright closes one of the browser windows while the panel is open. (The 100ms workaround fails in this case and a blank panel is still displayed.)

It works like this:

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