Search code examples
firefox-addondom-events

Nested popups, trouble with popupshowing event


I'm developing a Firefox extension, and I have a toolbar button that displays an overlay in which I put some XUL code inside ToolbarDisplayManager:

<overlay id="custombutton-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

        <toolbarpalette id="BrowserToolbarPalette">
            <toolbarbutton id="boycottToolbarButton" label="Label" tooltiptext="Label" type="menu" popup="ToolbarPopup" />
        </toolbarpalette>
        <popupset>
            <panel id="ToolbarPopup" position="after_start" onpopupshowing="Refresh();">
                <vbox class="ToolbarBody">
                    <box id="ToolbarDisplayManager" />
                </vbox>
            </panel>
        </popupset>
</overlay>

Inside "ToolbarDisplayManager" I create a menulist with a menupopup with javascript (like a "combobox") with this structure:

<menulist id="combo">
    <menupopup>
        <menuitem>
        <menuitem>
        ...
    </menupopup>
</menulist>

Here's the problem: When I click ToolbarPopup I run a "Refresh" function in the popupshowing event. But when the overlay is displayed, and I click on the "combo" to select an item, the popupshowing event of ToolbarPopup is fired again. In other words: it's like the two "popups" are having troubles with each other.

I need to run the "Refresh" function just before the overlay is shown. Is my structure wrong somewhere? How do I handle two nested popups and their popupshowing events?


Solution

  • The popupshowing event bubbles up meaning that it can be intercepted by event handlers higher in the DOM hierarchy than the element triggering the event. So your event handler first receives the popupshowing event from the <panel> and later another one from the <menulist> element (because it is located inside the <panel>). You can easily distinguish the two cases using the event.eventPhase property:

    <panel ... onpopupshowing="if (event.eventPhase == Event.AT_TARGET) Refresh();">
    

    This will make sure that you ignore events bubbling up and only call Refresh() for events that originated in the <panel> itself. The other option would have been looking at the event.target property.