1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * The XUL Popup Manager keeps track of all open popups.
11 #ifndef nsXULPopupManager_h__
12 #define nsXULPopupManager_h__
14 #include "mozilla/Logging.h"
15 #include "nsHashtablesFwd.h"
16 #include "nsIContent.h"
17 #include "nsIRollupListener.h"
18 #include "nsIDOMEventListener.h"
23 #include "nsIObserver.h"
24 #include "nsThreadUtils.h"
25 #include "mozilla/Attributes.h"
26 #include "mozilla/widget/InitData.h"
27 #include "mozilla/widget/NativeMenu.h"
29 // XXX Avoid including this here by moving function bodies to the cpp file.
30 #include "mozilla/dom/Element.h"
32 // X.h defines KeyPress
38 * There are two types that are used:
39 * - dismissable popups such as menus, which should close up when there is a
40 * click outside the popup. In this situation, the entire chain of menus
41 * above should also be closed.
42 * - panels, which stay open until a request is made to close them. This
43 * type is used by tooltips.
45 * When a new popup is opened, it is appended to the popup chain, stored in a
46 * linked list in mPopups.
47 * Popups are stored in this list linked from newest to oldest. When a click
48 * occurs outside one of the open dismissable popups, the chain is closed by
52 class nsContainerFrame
;
54 class nsIDocShellTreeItem
;
55 class nsMenuPopupFrame
;
56 class nsPIDOMWindowOuter
;
57 class nsRefreshDriver
;
65 class XULButtonElement
;
66 class XULMenuBarElement
;
67 class XULPopupElement
;
69 } // namespace mozilla
71 // XUL popups can be in several different states. When opening a popup, the
72 // state changes as follows:
73 // ePopupClosed - initial state
74 // ePopupShowing - during the period when the popupshowing event fires
75 // ePopupOpening - between the popupshowing event and being visible. Creation
76 // of the child frames, layout and reflow occurs in this
77 // state. The popup is stored in the popup manager's list of
78 // open popups during this state.
79 // ePopupVisible - layout is done and the popup's view and widget are made
80 // visible. The popup is visible on screen but may be
81 // transitioning. The popupshown event has not yet fired.
82 // ePopupShown - the popup has been shown and is fully ready. This state is
83 // assigned just before the popupshown event fires.
84 // When closing a popup:
85 // ePopupHidden - during the period when the popuphiding event fires and
86 // the popup is removed.
87 // ePopupClosed - the popup's widget is made invisible.
89 // state when a popup is not open
91 // state from when a popup is requested to be shown to after the
92 // popupshowing event has been fired.
94 // state while a popup is waiting to be laid out and positioned
96 // state while a popup is open but the widget is not yet visible
98 // state while a popup is visible and waiting for the popupshown event
100 // state while a popup is open and visible on screen
102 // state from when a popup is requested to be hidden to when it is closed.
104 // state which indicates that the popup was hidden without firing the
105 // popuphiding or popuphidden events. It is used when executing a menu
106 // command because the menu needs to be hidden before the command event
107 // fires, yet the popuphiding and popuphidden events are fired after. This
108 // state can also occur when the popup is removed because the document is
113 // when a menu command is executed, the closemenu attribute may be used
114 // to define how the menu should be closed up
116 CloseMenuMode_Auto
, // close up the chain of menus, default value
117 CloseMenuMode_None
, // don't close up any menus
118 CloseMenuMode_Single
// close up only the menu the command is inside
122 * nsNavigationDirection: an enum expressing navigation through the menus in
123 * terms which are independent of the directionality of the chrome. The
124 * terminology, derived from XSL-FO and CSS3 (e.g.
125 * http://www.w3.org/TR/css3-text/#TextLayout), is BASE (Before, After, Start,
126 * End), with the addition of First and Last (mapped to Home and End
129 * In languages such as English where the inline progression is left-to-right
130 * and the block progression is top-to-bottom (lr-tb), these terms will map out
131 * as in the following diagram
133 * --- inline progression --->
139 * Start | | End progression
147 enum nsNavigationDirection
{
148 eNavigationDirection_Last
,
149 eNavigationDirection_First
,
150 eNavigationDirection_Start
,
151 eNavigationDirection_Before
,
152 eNavigationDirection_End
,
153 eNavigationDirection_After
159 eIgnoreKeys_Shortcuts
,
162 enum class HidePopupOption
: uint8_t {
163 // If the entire chain of menus should be closed.
165 // If the parent <menu> of the popup should not be deselected. This will not
166 // be set when the menu is closed by pressing the Escape key.
168 // If the first popuphiding event should be sent asynchrously. This should
169 // be set if HidePopup is called from a frame.
171 // If this popup is hiding due to being cancelled.
173 // Whether animations should be disabled for rolled-up popups.
177 using HidePopupOptions
= mozilla::EnumSet
<HidePopupOption
>;
180 * DirectionFromKeyCodeTable: two arrays, the first for left-to-right and the
181 * other for right-to-left, that map keycodes to values of
182 * nsNavigationDirection.
184 extern const nsNavigationDirection DirectionFromKeyCodeTable
[2][6];
186 #define NS_DIRECTION_FROM_KEY_CODE(frame, keycode) \
187 (DirectionFromKeyCodeTable[static_cast<uint8_t>( \
188 (frame)->StyleVisibility()->mDirection)][( \
189 keycode)-mozilla::dom::KeyboardEvent_Binding::DOM_VK_END])
191 // Used to hold information about a popup that is about to be opened.
192 struct PendingPopup
{
193 using Element
= mozilla::dom::Element
;
194 using Event
= mozilla::dom::Event
;
196 PendingPopup(Element
* aPopup
, Event
* aEvent
);
198 const RefPtr
<Element
> mPopup
;
199 const RefPtr
<Event
> mEvent
;
201 // Device pixels relative to the showing popup's presshell's
202 // root prescontext's root frame.
203 mozilla::LayoutDeviceIntPoint mMousePoint
;
205 // Cached modifiers used to trigger the popup.
206 mozilla::Modifiers mModifiers
;
208 already_AddRefed
<nsIContent
> GetTriggerContent() const;
210 void InitMousePoint();
212 void SetMousePoint(mozilla::LayoutDeviceIntPoint aMousePoint
) {
213 mMousePoint
= aMousePoint
;
216 uint16_t MouseInputSource() const;
219 // nsMenuChainItem holds info about an open popup. Items are stored in a
220 // doubly linked list. Note that the linked list is stored beginning from
221 // the lowest child in a chain of menus, as this is the active submenu.
222 class nsMenuChainItem
{
223 using PopupType
= mozilla::widget::PopupType
;
225 nsMenuPopupFrame
* mFrame
; // the popup frame
226 PopupType mPopupType
; // the popup type of the frame
227 bool mNoAutoHide
; // true for noautohide panels
228 bool mIsContext
; // true for context menus
229 bool mOnMenuBar
; // true if the menu is on a menu bar
230 nsIgnoreKeys mIgnoreKeys
; // indicates how keyboard listeners should be used
232 // True if the popup should maintain its position relative to the anchor when
236 // The last seen position of the anchor, relative to the screen.
239 mozilla::UniquePtr
<nsMenuChainItem
> mParent
;
240 // Back pointer, safe because mChild keeps us alive.
241 nsMenuChainItem
* mChild
= nullptr;
244 nsMenuChainItem(nsMenuPopupFrame
* aFrame
, bool aNoAutoHide
, bool aIsContext
,
245 PopupType aPopupType
)
247 mPopupType(aPopupType
),
248 mNoAutoHide(aNoAutoHide
),
249 mIsContext(aIsContext
),
251 mIgnoreKeys(eIgnoreKeys_False
),
252 mFollowAnchor(false) {
253 NS_ASSERTION(aFrame
, "null frame passed to nsMenuChainItem constructor");
254 MOZ_COUNT_CTOR(nsMenuChainItem
);
257 MOZ_COUNTED_DTOR(nsMenuChainItem
)
259 mozilla::dom::XULPopupElement
* Element();
260 nsMenuPopupFrame
* Frame() { return mFrame
; }
261 PopupType
GetPopupType() { return mPopupType
; }
262 bool IsNoAutoHide() { return mNoAutoHide
; }
263 void SetNoAutoHide(bool aNoAutoHide
) { mNoAutoHide
= aNoAutoHide
; }
264 bool IsMenu() { return mPopupType
== PopupType::Menu
; }
265 bool IsContextMenu() { return mIsContext
; }
266 nsIgnoreKeys
IgnoreKeys() { return mIgnoreKeys
; }
267 void SetIgnoreKeys(nsIgnoreKeys aIgnoreKeys
) { mIgnoreKeys
= aIgnoreKeys
; }
268 bool IsOnMenuBar() { return mOnMenuBar
; }
269 void SetOnMenuBar(bool aOnMenuBar
) { mOnMenuBar
= aOnMenuBar
; }
270 nsMenuChainItem
* GetParent() { return mParent
.get(); }
271 nsMenuChainItem
* GetChild() { return mChild
; }
272 bool FollowsAnchor() { return mFollowAnchor
; }
273 void UpdateFollowAnchor();
274 void CheckForAnchorChange();
276 // set the parent of this item to aParent, also changing the parent
277 // to have this as a child.
278 void SetParent(mozilla::UniquePtr
<nsMenuChainItem
> aParent
);
279 // Removes the parent pointer and returns it.
280 mozilla::UniquePtr
<nsMenuChainItem
> Detach();
283 // this class is used for dispatching popuphiding events asynchronously.
284 class nsXULPopupHidingEvent
: public mozilla::Runnable
{
285 using PopupType
= mozilla::widget::PopupType
;
286 using Element
= mozilla::dom::Element
;
289 nsXULPopupHidingEvent(Element
* aPopup
, Element
* aNextPopup
,
290 Element
* aLastPopup
, PopupType aPopupType
,
291 HidePopupOptions aOptions
)
292 : mozilla::Runnable("nsXULPopupHidingEvent"),
294 mNextPopup(aNextPopup
),
295 mLastPopup(aLastPopup
),
296 mPopupType(aPopupType
),
299 "null popup supplied to nsXULPopupHidingEvent constructor");
300 // aNextPopup and aLastPopup may be null
303 NS_IMETHOD
Run() override
;
306 nsCOMPtr
<Element
> mPopup
;
307 nsCOMPtr
<Element
> mNextPopup
;
308 nsCOMPtr
<Element
> mLastPopup
;
309 PopupType mPopupType
;
310 HidePopupOptions mOptions
;
313 // this class is used for dispatching popuppositioned events asynchronously.
314 class nsXULPopupPositionedEvent
: public mozilla::Runnable
{
315 using Element
= mozilla::dom::Element
;
318 explicit nsXULPopupPositionedEvent(Element
* aPopup
)
319 : mozilla::Runnable("nsXULPopupPositionedEvent"), mPopup(aPopup
) {
323 NS_IMETHOD
Run() override
;
325 // Asynchronously dispatch a popuppositioned event at aPopup if this is a
326 // panel that should receieve such events. Return true if the event was sent.
327 static bool DispatchIfNeeded(Element
* aPopup
);
330 const nsCOMPtr
<Element
> mPopup
;
333 // this class is used for dispatching menu command events asynchronously.
334 class nsXULMenuCommandEvent
: public mozilla::Runnable
{
335 using Element
= mozilla::dom::Element
;
338 nsXULMenuCommandEvent(Element
* aMenu
, bool aIsTrusted
,
339 mozilla::Modifiers aModifiers
, bool aUserInput
,
340 bool aFlipChecked
, int16_t aButton
)
341 : mozilla::Runnable("nsXULMenuCommandEvent"),
343 mModifiers(aModifiers
),
345 mIsTrusted(aIsTrusted
),
346 mUserInput(aUserInput
),
347 mFlipChecked(aFlipChecked
),
348 mCloseMenuMode(CloseMenuMode_Auto
) {
350 "null menu supplied to nsXULMenuCommandEvent constructor");
353 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
;
355 void SetCloseMenuMode(CloseMenuMode aCloseMenuMode
) {
356 mCloseMenuMode
= aCloseMenuMode
;
360 RefPtr
<Element
> mMenu
;
362 mozilla::Modifiers mModifiers
;
367 CloseMenuMode mCloseMenuMode
;
370 class nsXULPopupManager final
: public nsIDOMEventListener
,
371 public nsIRollupListener
,
373 public mozilla::widget::NativeMenu::Observer
{
375 friend class nsXULPopupHidingEvent
;
376 friend class nsXULPopupPositionedEvent
;
377 friend class nsXULMenuCommandEvent
;
378 friend class TransitionEnder
;
380 using PopupType
= mozilla::widget::PopupType
;
381 using Element
= mozilla::dom::Element
;
385 NS_DECL_NSIDOMEVENTLISTENER
388 MOZ_CAN_RUN_SCRIPT_BOUNDARY
389 bool Rollup(const RollupOptions
&,
390 nsIContent
** aLastRolledUp
= nullptr) override
;
391 bool ShouldRollupOnMouseWheelEvent() override
;
392 bool ShouldConsumeOnMouseWheelEvent() override
;
393 bool ShouldRollupOnMouseActivate() override
;
394 uint32_t GetSubmenuWidgetChain(nsTArray
<nsIWidget
*>* aWidgetChain
) override
;
395 nsIWidget
* GetRollupWidget() override
;
396 bool RollupNativeMenu() override
;
398 MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool RollupTooltips();
400 enum class RollupKind
{ Tooltip
, Menu
};
402 bool RollupInternal(RollupKind
, const RollupOptions
&,
403 nsIContent
** aLastRolledUp
);
405 // NativeMenu::Observer
406 void OnNativeMenuOpened() override
;
407 void OnNativeMenuClosed() override
;
408 void OnNativeSubMenuWillOpen(mozilla::dom::Element
* aPopupElement
) override
;
409 void OnNativeSubMenuDidOpen(mozilla::dom::Element
* aPopupElement
) override
;
410 void OnNativeSubMenuClosed(mozilla::dom::Element
* aPopupElement
) override
;
411 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void OnNativeMenuWillActivateItem(
412 mozilla::dom::Element
* aMenuItemElement
) override
;
414 static nsXULPopupManager
* sInstance
;
416 // initialize and shutdown methods called by nsLayoutStatics
417 static nsresult
Init();
418 static void Shutdown();
420 // returns a weak reference to the popup manager instance, could return null
421 // if a popup manager could not be allocated
422 static nsXULPopupManager
* GetInstance();
424 // This should be called when a window is moved or resized to adjust the
425 // popups accordingly.
426 void AdjustPopupsOnWindowChange(nsPIDOMWindowOuter
* aWindow
);
427 void AdjustPopupsOnWindowChange(mozilla::PresShell
* aPresShell
);
429 // inform the popup manager that a menu bar has been activated or deactivated,
430 // either because one of its menus has opened or closed, or that the menubar
431 // has been focused such that its menus may be navigated with the keyboard.
432 // aActivate should be true when the menubar should be focused, and false
433 // when the active menu bar should be defocused. In the latter case, if
434 // aMenuBar isn't currently active, yet another menu bar is, that menu bar
435 // will remain active.
436 void SetActiveMenuBar(mozilla::dom::XULMenuBarElement
* aMenuBar
,
439 struct MayShowMenuResult
{
440 const bool mIsNative
= false;
441 mozilla::dom::XULButtonElement
* const mMenuButton
= nullptr;
442 nsMenuPopupFrame
* const mMenuPopupFrame
= nullptr;
444 explicit operator bool() const {
445 MOZ_ASSERT(!!mMenuButton
== !!mMenuPopupFrame
);
446 return mIsNative
|| mMenuButton
;
450 MayShowMenuResult
MayShowMenu(nsIContent
* aMenu
);
453 * Open a <menu> given its content node. If aSelectFirstItem is
454 * set to true, the first item on the menu will automatically be
457 void ShowMenu(nsIContent
* aMenu
, bool aSelectFirstItem
);
460 * Open a popup, either anchored or unanchored. If aSelectFirstItem is
461 * true, then the first item in the menu is selected. The arguments are
462 * similar to those for XULPopupElement::OpenPopup.
464 * aTriggerEvent should be the event that triggered the event. This is used
465 * to determine the coordinates and trigger node for the popup. This may be
466 * null if the popup was not triggered by an event.
468 * This fires the popupshowing event synchronously.
470 void ShowPopup(Element
* aPopup
, nsIContent
* aAnchorContent
,
471 const nsAString
& aPosition
, int32_t aXPos
, int32_t aYPos
,
472 bool aIsContextMenu
, bool aAttributesOverride
,
473 bool aSelectFirstItem
, mozilla::dom::Event
* aTriggerEvent
);
476 * Open a popup at a specific screen position specified by aXPos and aYPos,
477 * measured in CSS pixels.
479 * This fires the popupshowing event synchronously.
481 * If aIsContextMenu is true, the popup is positioned at a slight
482 * offset from aXPos/aYPos to ensure that it is not under the mouse
485 void ShowPopupAtScreen(Element
* aPopup
, int32_t aXPos
, int32_t aYPos
,
487 mozilla::dom::Event
* aTriggerEvent
);
489 /* Open a popup anchored at a screen rectangle specified by aRect.
490 * The remaining arguments are similar to ShowPopup.
492 void ShowPopupAtScreenRect(Element
* aPopup
, const nsAString
& aPosition
,
493 const nsIntRect
& aRect
, bool aIsContextMenu
,
494 bool aAttributesOverride
,
495 mozilla::dom::Event
* aTriggerEvent
);
498 * Open a popup as a native menu, at a specific screen position specified by
499 * aXPos and aYPos, measured in CSS pixels.
501 * This fires the popupshowing event synchronously.
503 * Returns whether native menus are supported for aPopup on this platform.
504 * TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
506 MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool ShowPopupAsNativeMenu(
507 Element
* aPopup
, int32_t aXPos
, int32_t aYPos
, bool aIsContextMenu
,
508 mozilla::dom::Event
* aTriggerEvent
);
511 * Open a tooltip at a specific screen position specified by aXPos and aYPos,
512 * measured in device pixels. This fires the popupshowing event synchronously.
514 void ShowTooltipAtScreen(Element
* aPopup
, nsIContent
* aTriggerContent
,
515 const mozilla::LayoutDeviceIntPoint
&);
518 * Hide a popup aPopup. If the popup is in a <menu>, then also inform the
519 * menu that the popup is being hidden.
520 * aLastPopup - optional popup to close last when hiding a chain of menus.
521 * If null, then all popups will be closed.
523 void HidePopup(Element
* aPopup
, HidePopupOptions
,
524 Element
* aLastPopup
= nullptr);
527 * Hide the popup of a <menu>.
529 void HideMenu(nsIContent
* aMenu
);
532 * Hide a popup after a short delay. This is used when rolling over menu
533 * items. This timer is stored in mCloseTimer. The timer may be cancelled and
534 * the popup closed by calling KillMenuTimer.
536 void HidePopupAfterDelay(nsMenuPopupFrame
* aPopup
, int32_t aDelay
);
539 * Hide all of the popups from a given docshell. This should be called when
540 * the document is hidden.
542 MOZ_CAN_RUN_SCRIPT_BOUNDARY
543 void HidePopupsInDocShell(nsIDocShellTreeItem
* aDocShellToHide
);
546 * Check if any popups need to be repositioned or hidden after a style or
547 * layout change. This will update, for example, any arrow type panels when
548 * the anchor that is is pointing to has moved, resized or gone away.
549 * Only those popups that pertain to the supplied aRefreshDriver are updated.
551 void UpdatePopupPositions(nsRefreshDriver
* aRefreshDriver
);
554 * Enable or disable anchor following on the popup if needed.
556 void UpdateFollowAnchor(nsMenuPopupFrame
* aPopup
);
559 * Execute a menu command from the triggering event aEvent.
561 * aMenu - a menuitem to execute
562 * aEvent - an nsXULMenuCommandEvent that contains all the info from the mouse
563 * event which triggered the menu to be executed, may not be null
565 MOZ_CAN_RUN_SCRIPT
void ExecuteMenu(nsIContent
* aMenu
,
566 nsXULMenuCommandEvent
* aEvent
);
569 * If a native menu is open, and aItem is an item in the menu's subtree,
570 * execute the item with the help of the native menu and close the menu.
571 * Returns true if a native menu was open.
573 bool ActivateNativeMenuItem(nsIContent
* aItem
, mozilla::Modifiers aModifiers
,
574 int16_t aButton
, mozilla::ErrorResult
& aRv
);
577 * Return true if the popup for the supplied content node is open.
579 bool IsPopupOpen(Element
* aPopup
);
582 * Return the frame for the topmost open popup of a given type, or null if
583 * no popup of that type is open. If aType is PopupType::Any, a menu of any
586 nsIFrame
* GetTopPopup(PopupType aType
);
589 * Returns the topmost active menuitem that's currently visible, if any.
591 nsIContent
* GetTopActiveMenuItemContent();
594 * Return an array of all the open and visible popup frames for
595 * menus, in order from top to bottom.
597 void GetVisiblePopups(nsTArray
<nsIFrame
*>& aPopups
);
600 * Get the node that last triggered a popup or tooltip in the document
601 * aDocument. aDocument must be non-null and be a document contained within
602 * the same window hierarchy as the popup to retrieve.
604 already_AddRefed
<nsINode
> GetLastTriggerPopupNode(
605 mozilla::dom::Document
* aDocument
) {
606 return GetLastTriggerNode(aDocument
, false);
609 already_AddRefed
<nsINode
> GetLastTriggerTooltipNode(
610 mozilla::dom::Document
* aDocument
) {
611 return GetLastTriggerNode(aDocument
, true);
615 * Return false if a popup may not be opened. This will return false if the
616 * popup is already open, if the popup is in a content shell that is not
617 * focused, or if it is a submenu of another menu that isn't open.
619 bool MayShowPopup(nsMenuPopupFrame
* aFrame
);
622 * Indicate that the popup associated with aView has been moved to the
623 * specified device pixel coordinates.
625 void PopupMoved(nsIFrame
* aFrame
, const mozilla::LayoutDeviceIntPoint
& aPoint
,
629 * Indicate that the popup associated with aView has been resized to the
630 * given device pixel size aSize.
632 void PopupResized(nsIFrame
* aFrame
,
633 const mozilla::LayoutDeviceIntSize
& aSize
);
636 * Called when a popup frame is destroyed. In this case, just remove the
637 * item and later popups from the list. No point going through HidePopup as
638 * the frames have gone away.
640 MOZ_CAN_RUN_SCRIPT
void PopupDestroyed(nsMenuPopupFrame
* aFrame
);
643 * Returns true if there is a context menu open. If aPopup is specified,
644 * then the context menu must be later in the chain than aPopup. If aPopup
645 * is null, returns true if any context menu at all is open.
647 bool HasContextMenu(nsMenuPopupFrame
* aPopup
);
650 * Update the commands for the menus within the menu popup for a given
651 * content node. aPopup should be a XUL menupopup element. This method
652 * changes attributes on the children of aPopup, and deals only with the
653 * content of the popup, not the frames.
655 void UpdateMenuItems(Element
* aPopup
);
658 * Stop the timer which hides a popup after a delay, started by a previous
659 * call to HidePopupAfterDelay. In addition, the popup awaiting to be hidden
660 * is closed asynchronously.
662 void KillMenuTimer();
665 * Cancel the timer which closes menus after delay, but only if the menu to
666 * close is aMenuParent. When a submenu is opened, the user might move the
667 * mouse over a sibling menuitem which would normally close the menu. This
668 * menu is closed via a timer. However, if the user moves the mouse over the
669 * submenu before the timer fires, we should instead cancel the timer. This
670 * ensures that the user can move the mouse diagonally over a menu.
672 void CancelMenuTimer(nsMenuPopupFrame
*);
675 * Handles navigation for menu accelkeys. If aFrame is specified, then the
676 * key is handled by that popup, otherwise if aFrame is null, the key is
677 * handled by the active popup or menubar.
679 MOZ_CAN_RUN_SCRIPT
bool HandleShortcutNavigation(
680 mozilla::dom::KeyboardEvent
& aKeyEvent
, nsMenuPopupFrame
* aFrame
);
683 * Handles cursor navigation within a menu. Returns true if the key has
686 MOZ_CAN_RUN_SCRIPT
bool HandleKeyboardNavigation(uint32_t aKeyCode
);
689 * Handle keyboard navigation within a menu popup specified by aFrame.
690 * Returns true if the key was handled and other default handling
693 MOZ_CAN_RUN_SCRIPT
bool HandleKeyboardNavigationInPopup(
694 nsMenuPopupFrame
* aFrame
, nsNavigationDirection aDir
) {
695 return HandleKeyboardNavigationInPopup(nullptr, aFrame
, aDir
);
699 * Handles the keyboard event with keyCode value. Returns true if the event
702 MOZ_CAN_RUN_SCRIPT
bool HandleKeyboardEventWithKeyCode(
703 mozilla::dom::KeyboardEvent
* aKeyEvent
,
704 nsMenuChainItem
* aTopVisibleMenuItem
);
706 // Sets mIgnoreKeys of the Top Visible Menu Item
707 nsresult
UpdateIgnoreKeys(bool aIgnoreKeys
);
709 nsPopupState
GetPopupState(mozilla::dom::Element
* aPopupElement
);
711 mozilla::dom::Event
* GetOpeningPopupEvent() const {
712 return mPendingPopup
->mEvent
.get();
715 MOZ_CAN_RUN_SCRIPT nsresult
KeyUp(mozilla::dom::KeyboardEvent
* aKeyEvent
);
716 MOZ_CAN_RUN_SCRIPT nsresult
KeyDown(mozilla::dom::KeyboardEvent
* aKeyEvent
);
717 MOZ_CAN_RUN_SCRIPT nsresult
KeyPress(mozilla::dom::KeyboardEvent
* aKeyEvent
);
721 ~nsXULPopupManager();
723 // get the nsMenuPopupFrame, if any, for the given content node
724 MOZ_CAN_RUN_SCRIPT_BOUNDARY
725 nsMenuPopupFrame
* GetPopupFrameForContent(nsIContent
* aContent
,
728 // Get the menu to start rolling up.
729 nsMenuChainItem
* GetRollupItem(RollupKind
);
731 // Return the topmost menu, skipping over invisible popups
732 nsMenuChainItem
* GetTopVisibleMenu() {
733 return GetRollupItem(RollupKind::Menu
);
736 // Removes the chain item from the chain and deletes it.
737 void RemoveMenuChainItem(nsMenuChainItem
*);
739 // Hide all of the visible popups from the given list. This function can
740 // cause style changes and frame destruction.
741 MOZ_CAN_RUN_SCRIPT
void HidePopupsInList(
742 const nsTArray
<nsMenuPopupFrame
*>& aFrames
);
744 // Hide, but don't close, visible menus. Called before executing a menu item.
745 // The caller promises to close the menus properly (with a call to HidePopup)
746 // once the item has been executed.
747 MOZ_CAN_RUN_SCRIPT
void HideOpenMenusBeforeExecutingMenu(CloseMenuMode aMode
);
749 // callbacks for ShowPopup and HidePopup as events may be done asynchronously
750 MOZ_CAN_RUN_SCRIPT
void ShowPopupCallback(Element
* aPopup
,
751 nsMenuPopupFrame
* aPopupFrame
,
753 bool aSelectFirstItem
);
754 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void HidePopupCallback(
755 Element
* aPopup
, nsMenuPopupFrame
* aPopupFrame
, Element
* aNextPopup
,
756 Element
* aLastPopup
, PopupType aPopupType
, HidePopupOptions
);
759 * Trigger frame construction and reflow in the popup, fire a popupshowing
760 * event on the popup and then open the popup.
762 * aPendingPopup - information about the popup to open
763 * aIsContextMenu - true for context menus
764 * aSelectFirstItem - true to select the first item in the menu
765 * TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
767 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void BeginShowingPopup(
768 const PendingPopup
& aPendingPopup
, bool aIsContextMenu
,
769 bool aSelectFirstItem
);
772 * Fire a popuphiding event and then hide the popup. This will be called
773 * recursively if aNextPopup and aLastPopup are set in order to hide a chain
774 * of open menus. If these are not set, only one popup is closed. However,
775 * if the popup type indicates a menu, yet the next popup is not a menu,
776 * then this ends the closing of popups. This allows a menulist inside a
777 * non-menu to close up the menu but not close up the panel it is contained
780 * The caller must keep a strong reference to aPopup, aNextPopup and
783 * aPopup - the popup to hide
784 * aNextPopup - the next popup to hide
785 * aLastPopup - the last popup in the chain to hide
786 * aPresContext - nsPresContext for the popup's frame
787 * aPopupType - the PopupType of the frame.
788 * aOptions - the relevant options to hide the popup. Only a subset is looked
791 MOZ_CAN_RUN_SCRIPT_BOUNDARY
792 void FirePopupHidingEvent(Element
* aPopup
, Element
* aNextPopup
,
793 Element
* aLastPopup
, nsPresContext
* aPresContext
,
794 PopupType aPopupType
, HidePopupOptions aOptions
);
797 * Handle keyboard navigation within a menu popup specified by aItem.
800 bool HandleKeyboardNavigationInPopup(nsMenuChainItem
* aItem
,
801 nsNavigationDirection aDir
) {
802 return HandleKeyboardNavigationInPopup(aItem
, aItem
->Frame(), aDir
);
807 * Handle keyboard navigation within a menu popup aFrame. If aItem is
808 * supplied, then it is expected to have a frame equal to aFrame.
809 * If aItem is non-null, then the navigation may be redirected to
810 * an open submenu if one exists. Returns true if the key was
811 * handled and other default handling should not occur.
813 MOZ_CAN_RUN_SCRIPT
bool HandleKeyboardNavigationInPopup(
814 nsMenuChainItem
* aItem
, nsMenuPopupFrame
* aFrame
,
815 nsNavigationDirection aDir
);
818 already_AddRefed
<nsINode
> GetLastTriggerNode(
819 mozilla::dom::Document
* aDocument
, bool aIsTooltip
);
822 * Fire a popupshowing event for aPopup.
824 MOZ_CAN_RUN_SCRIPT nsEventStatus
FirePopupShowingEvent(
825 const PendingPopup
& aPendingPopup
, nsPresContext
* aPresContext
);
828 * Set mouse capturing for the current popup. This traps mouse clicks that
829 * occur outside the popup so that it can be closed up. aOldPopup should be
830 * set to the popup that was previously the current popup.
832 void SetCaptureState(nsIContent
* aOldPopup
);
835 * Key event listeners are attached to the document containing the current
836 * menu for menu and shortcut navigation. Only one listener is needed at a
837 * time, stored in mKeyListener, so switch it only if the document changes.
838 * Having menus in different documents is very rare, so the listeners will
839 * usually only be attached when the first menu opens and removed when all
842 * This is also used when only a menubar is active without any open menus,
843 * so that keyboard navigation between menus on the menubar may be done.
845 // TODO: Convert UpdateKeyboardListeners() to MOZ_CAN_RUN_SCRIPT and get rid
846 // of the kungFuDeathGrip in it.
847 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void UpdateKeyboardListeners();
850 * Returns true if the docshell for aDoc is aExpected or a child of aExpected.
852 bool IsChildOfDocShell(mozilla::dom::Document
* aDoc
,
853 nsIDocShellTreeItem
* aExpected
);
855 // Finds a chain item in mPopups.
856 nsMenuChainItem
* FindPopup(Element
* aPopup
) const;
858 // the document the key event listener is attached to
859 nsCOMPtr
<mozilla::dom::EventTarget
> mKeyListener
;
861 // widget that is currently listening to rollup events
862 nsCOMPtr
<nsIWidget
> mWidget
;
864 // set to the currently active menu bar, if any
865 mozilla::dom::XULMenuBarElement
* mActiveMenuBar
;
867 // linked list of normal menus and panels. mPopups points to the innermost
868 // popup, which keeps alive all their parents.
869 mozilla::UniquePtr
<nsMenuChainItem
> mPopups
;
871 // timer used for HidePopupAfterDelay
872 nsCOMPtr
<nsITimer
> mCloseTimer
;
873 nsMenuPopupFrame
* mTimerMenu
= nullptr;
875 // Information about the popup that is currently firing a popupshowing event.
876 const PendingPopup
* mPendingPopup
;
878 // If a popup is displayed as a native menu, this is non-null while the
879 // native menu is open.
880 // mNativeMenu has a strong reference to the menupopup nsIContent.
881 RefPtr
<mozilla::widget::NativeMenu
> mNativeMenu
;
883 // If the currently open native menu activated an item, this is the item's
884 // close menu mode. Nothing() if mNativeMenu is null or if no item was
886 mozilla::Maybe
<CloseMenuMode
> mNativeMenuActivatedItemCloseMenuMode
;
888 // If a popup is displayed as a native menu, this map contains the popup state
889 // for any of its non-closed submenus. This state cannot be stored on the
890 // submenus' nsMenuPopupFrames, because we usually don't generate frames for
891 // the contents of native menus.
892 // If a submenu is not present in this map, it means it's closed.
893 // This map is empty if mNativeMenu is null.
894 nsTHashMap
<RefPtr
<mozilla::dom::Element
>, nsPopupState
>
895 mNativeMenuSubmenuStates
;