XUL:Popups:Issues
How XUL Popups Work Now
There are several typs of popups in XUL.
- Menus use a frame type of 'menu' (nsMenuFrame.cpp). Elements that use this type are <menu>, <menulist> and <button>, the latter when the type attribute is 'menu' or 'menu-button'. A <menupopup> is expected to be a child of this element.
- Attached popups use a popup or context attribute on an element to attach a <popup> to be opened when the mouse button is pressed. The popup attribute is used for normal clicking with the first button, and the context attribute is used for context clicking. The attribute should be set to the id of a <popup> or <menupopup> attribute within the same document.
- Tooltips are attached popups using the tooltip attribute set to a <tooltip> element. The tooltiptext attribute may also be used. This is similar except a default <tooltip> element is used. This default tooltip container can be specified by setting the default attribute to true on a <tooltip> element. In the absense of such an element, a default tooltip is automatically anonymously inserted into the document by the root XUL frame. Tooltips appear while the mouse is hovered over an element.
When a menu popup (the first type in the list above) needs to be opened, the layout code does a variety of initialization steps, and sends the poppshowing event. It then sets the menugenerated attribute on the popup element. Setting this attribute causes a style change, due to the following rule in xul.css:
menupopup[menugenerated="true"], popup[menugenerated="true"], tooltip[menugenerated="true"] { display: -moz-popup; }
Without the attribute, the popup has a display of 'none'. With the menugenerated attribute set to true, the display of -moz-popup causes a popup frame (nsMenuPopupFrame.cpp) to be created.
One key is that a special case is used when the menugenerated attribute is changed, causing the style change to be done synchronously, rather than waiting for the normal asynchronous behaviour.
However, just setting this attribute doesn't cause the popup to open. In order to do that, the popup has an attached 'view' which is shown and hidden as necessary. For menus, the view is made visible by the menu only once it is clear that the menu should be displayed. This is in contrast to the menugenerated attribute which is set regardless, even if the menu shouldn't be opened, for example, because the popupshowing event was cancelled. Thus, a popup frame is created even when no menu will be displayed.
For non-menu types (attached popups and tooltips), a special element is inserted anonymously into the document by the root box which serves as a container for all of those popups. This is because popups need a parent in order to open. For menus, the menu, menulist or button serves as this parent. For other popups, the special popup container (nsPopupSetFrame.cpp) is used.
Again, when opening the menu, the menugenerated attribute is set, causing a popup frame to be created. Note that is done by different code in nsPopupSetFrame.cpp. In this case, the popup set marks itself as dirty so that a reflow/relayout is needed. During layout, the view is marked as visible, showing the popup.
The menugenerated attribute is not necessarly removed when the popup is closed. It appears that popups and tooltips do clear this attribute, but menus do not. Thus, the popup frame, and its children are still present. The only thing that makes the popup disappear is hiding the view.
Issues with Menus
- code duplication between the two types of menus
- menus tend to be crashy due to the strange behaviour described above.