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/FunctionRef.h"
27 #include "mozilla/widget/InitData.h"
28 #include "mozilla/widget/NativeMenu.h"
30 // XXX Avoid including this here by moving function bodies to the cpp file.
31 #include "mozilla/dom/Element.h"
33 // X.h defines KeyPress
39 * There are two types that are used:
40 * - dismissable popups such as menus, which should close up when there is a
41 * click outside the popup. In this situation, the entire chain of menus
42 * above should also be closed.
43 * - panels, which stay open until a request is made to close them. This
44 * type is used by tooltips.
46 * When a new popup is opened, it is appended to the popup chain, stored in a
47 * linked list in mPopups.
48 * Popups are stored in this list linked from newest to oldest. When a click
49 * occurs outside one of the open dismissable popups, the chain is closed by
53 class nsContainerFrame
;
55 class nsIDocShellTreeItem
;
56 class nsMenuPopupFrame
;
57 class nsPIDOMWindowOuter
;
58 class nsRefreshDriver
;
66 class XULButtonElement
;
67 class XULMenuBarElement
;
68 class XULPopupElement
;
70 } // namespace mozilla
72 // XUL popups can be in several different states. When opening a popup, the
73 // state changes as follows:
74 // ePopupClosed - initial state
75 // ePopupShowing - during the period when the popupshowing event fires
76 // ePopupOpening - between the popupshowing event and being visible. Creation
77 // of the child frames, layout and reflow occurs in this
78 // state. The popup is stored in the popup manager's list of
79 // open popups during this state.
80 // ePopupVisible - layout is done and the popup's view and widget are made
81 // visible. The popup is visible on screen but may be
82 // transitioning. The popupshown event has not yet fired.
83 // ePopupShown - the popup has been shown and is fully ready. This state is
84 // assigned just before the popupshown event fires.
85 // When closing a popup:
86 // ePopupHidden - during the period when the popuphiding event fires and
87 // the popup is removed.
88 // ePopupClosed - the popup's widget is made invisible.
90 // state when a popup is not open
92 // state from when a popup is requested to be shown to after the
93 // popupshowing event has been fired.
95 // state while a popup is waiting to be laid out and positioned
97 // state while a popup is open but the widget is not yet visible
99 // state while a popup is visible and waiting for the popupshown event
101 // state while a popup is open and visible on screen
103 // state from when a popup is requested to be hidden to when it is closed.
105 // state which indicates that the popup was hidden without firing the
106 // popuphiding or popuphidden events. It is used when executing a menu
107 // command because the menu needs to be hidden before the command event
108 // fires, yet the popuphiding and popuphidden events are fired after. This
109 // state can also occur when the popup is removed because the document is
114 // when a menu command is executed, the closemenu attribute may be used
115 // to define how the menu should be closed up
117 CloseMenuMode_Auto
, // close up the chain of menus, default value
118 CloseMenuMode_None
, // don't close up any menus
119 CloseMenuMode_Single
// close up only the menu the command is inside
123 * nsNavigationDirection: an enum expressing navigation through the menus in
124 * terms which are independent of the directionality of the chrome. The
125 * terminology, derived from XSL-FO and CSS3 (e.g.
126 * http://www.w3.org/TR/css3-text/#TextLayout), is BASE (Before, After, Start,
127 * End), with the addition of First and Last (mapped to Home and End
130 * In languages such as English where the inline progression is left-to-right
131 * and the block progression is top-to-bottom (lr-tb), these terms will map out
132 * as in the following diagram
134 * --- inline progression --->
140 * Start | | End progression
148 enum nsNavigationDirection
{
149 eNavigationDirection_Last
,
150 eNavigationDirection_First
,
151 eNavigationDirection_Start
,
152 eNavigationDirection_Before
,
153 eNavigationDirection_End
,
154 eNavigationDirection_After
160 eIgnoreKeys_Shortcuts
,
163 enum class HidePopupOption
: uint8_t {
164 // If the entire chain of menus should be closed.
166 // If the parent <menu> of the popup should not be deselected. This will not
167 // be set when the menu is closed by pressing the Escape key.
169 // If the first popuphiding event should be sent asynchrously. This should
170 // be set if HidePopup is called from a frame.
172 // If this popup is hiding due to being cancelled.
174 // Whether animations should be disabled for rolled-up popups.
178 using HidePopupOptions
= mozilla::EnumSet
<HidePopupOption
>;
181 * DirectionFromKeyCodeTable: two arrays, the first for left-to-right and the
182 * other for right-to-left, that map keycodes to values of
183 * nsNavigationDirection.
185 extern const nsNavigationDirection DirectionFromKeyCodeTable
[2][6];
187 #define NS_DIRECTION_FROM_KEY_CODE(frame, keycode) \
188 (DirectionFromKeyCodeTable \
189 [static_cast<uint8_t>((frame)->StyleVisibility()->mDirection)] \
190 [(keycode) - mozilla::dom::KeyboardEvent_Binding::DOM_VK_END])
192 // Used to hold information about a popup that is about to be opened.
193 struct PendingPopup
{
194 using Element
= mozilla::dom::Element
;
195 using Event
= mozilla::dom::Event
;
197 PendingPopup(Element
* aPopup
, Event
* aEvent
);
199 const RefPtr
<Element
> mPopup
;
200 const RefPtr
<Event
> mEvent
;
202 // Device pixels relative to the showing popup's presshell's
203 // root prescontext's root frame.
204 mozilla::LayoutDeviceIntPoint mMousePoint
;
206 // Cached modifiers used to trigger the popup.
207 mozilla::Modifiers mModifiers
;
209 already_AddRefed
<nsIContent
> GetTriggerContent() const;
211 void InitMousePoint();
213 void SetMousePoint(mozilla::LayoutDeviceIntPoint aMousePoint
) {
214 mMousePoint
= aMousePoint
;
217 uint16_t MouseInputSource() const;
220 // nsMenuChainItem holds info about an open popup. Items are stored in a
221 // doubly linked list. Note that the linked list is stored beginning from
222 // the lowest child in a chain of menus, as this is the active submenu.
223 class nsMenuChainItem
{
224 using PopupType
= mozilla::widget::PopupType
;
226 nsMenuPopupFrame
* mFrame
; // the popup frame
227 PopupType mPopupType
; // the popup type of the frame
228 bool mNoAutoHide
; // true for noautohide panels
229 bool mIsContext
; // true for context menus
230 bool mOnMenuBar
; // true if the menu is on a menu bar
231 nsIgnoreKeys mIgnoreKeys
; // indicates how keyboard listeners should be used
233 // True if the popup should maintain its position relative to the anchor when
237 // The last seen position of the anchor, relative to the screen.
240 mozilla::UniquePtr
<nsMenuChainItem
> mParent
;
241 // Back pointer, safe because mChild keeps us alive.
242 nsMenuChainItem
* mChild
= nullptr;
245 nsMenuChainItem(nsMenuPopupFrame
* aFrame
, bool aNoAutoHide
, bool aIsContext
,
246 PopupType aPopupType
)
248 mPopupType(aPopupType
),
249 mNoAutoHide(aNoAutoHide
),
250 mIsContext(aIsContext
),
252 mIgnoreKeys(eIgnoreKeys_False
),
253 mFollowAnchor(false) {
254 NS_ASSERTION(aFrame
, "null frame passed to nsMenuChainItem constructor");
255 MOZ_COUNT_CTOR(nsMenuChainItem
);
258 MOZ_COUNTED_DTOR(nsMenuChainItem
)
260 mozilla::dom::XULPopupElement
* Element();
261 nsMenuPopupFrame
* Frame() { return mFrame
; }
262 PopupType
GetPopupType() { return mPopupType
; }
263 bool IsNoAutoHide() { return mNoAutoHide
; }
264 void SetNoAutoHide(bool aNoAutoHide
) { mNoAutoHide
= aNoAutoHide
; }
265 bool IsMenu() { return mPopupType
== PopupType::Menu
; }
266 bool IsContextMenu() { return mIsContext
; }
267 nsIgnoreKeys
IgnoreKeys() { return mIgnoreKeys
; }
268 void SetIgnoreKeys(nsIgnoreKeys aIgnoreKeys
) { mIgnoreKeys
= aIgnoreKeys
; }
269 bool IsOnMenuBar() { return mOnMenuBar
; }
270 void SetOnMenuBar(bool aOnMenuBar
) { mOnMenuBar
= aOnMenuBar
; }
271 nsMenuChainItem
* GetParent() { return mParent
.get(); }
272 nsMenuChainItem
* GetChild() { return mChild
; }
273 bool FollowsAnchor() { return mFollowAnchor
; }
274 void UpdateFollowAnchor();
275 void CheckForAnchorChange();
277 // set the parent of this item to aParent, also changing the parent
278 // to have this as a child.
279 void SetParent(mozilla::UniquePtr
<nsMenuChainItem
> aParent
);
280 // Removes the parent pointer and returns it.
281 mozilla::UniquePtr
<nsMenuChainItem
> Detach();
284 // this class is used for dispatching popuphiding events asynchronously.
285 class nsXULPopupHidingEvent
: public mozilla::Runnable
{
286 using PopupType
= mozilla::widget::PopupType
;
287 using Element
= mozilla::dom::Element
;
290 nsXULPopupHidingEvent(Element
* aPopup
, Element
* aNextPopup
,
291 Element
* aLastPopup
, PopupType aPopupType
,
292 HidePopupOptions aOptions
)
293 : mozilla::Runnable("nsXULPopupHidingEvent"),
295 mNextPopup(aNextPopup
),
296 mLastPopup(aLastPopup
),
297 mPopupType(aPopupType
),
300 "null popup supplied to nsXULPopupHidingEvent constructor");
301 // aNextPopup and aLastPopup may be null
304 NS_IMETHOD
Run() override
;
307 nsCOMPtr
<Element
> mPopup
;
308 nsCOMPtr
<Element
> mNextPopup
;
309 nsCOMPtr
<Element
> mLastPopup
;
310 PopupType mPopupType
;
311 HidePopupOptions mOptions
;
314 // this class is used for dispatching popuppositioned events asynchronously.
315 class nsXULPopupPositionedEvent
: public mozilla::Runnable
{
316 using Element
= mozilla::dom::Element
;
319 explicit nsXULPopupPositionedEvent(Element
* aPopup
)
320 : mozilla::Runnable("nsXULPopupPositionedEvent"), mPopup(aPopup
) {
324 NS_IMETHOD
Run() override
;
326 // Asynchronously dispatch a popuppositioned event at aPopup if this is a
327 // panel that should receieve such events. Return true if the event was sent.
328 static bool DispatchIfNeeded(Element
* aPopup
);
331 const nsCOMPtr
<Element
> mPopup
;
334 // this class is used for dispatching menu command events asynchronously.
335 class nsXULMenuCommandEvent
: public mozilla::Runnable
{
336 using Element
= mozilla::dom::Element
;
339 nsXULMenuCommandEvent(Element
* aMenu
, bool aIsTrusted
,
340 mozilla::Modifiers aModifiers
, bool aUserInput
,
341 bool aFlipChecked
, int16_t aButton
)
342 : mozilla::Runnable("nsXULMenuCommandEvent"),
344 mModifiers(aModifiers
),
346 mIsTrusted(aIsTrusted
),
347 mUserInput(aUserInput
),
348 mFlipChecked(aFlipChecked
),
349 mCloseMenuMode(CloseMenuMode_Auto
) {
351 "null menu supplied to nsXULMenuCommandEvent constructor");
354 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
;
356 void SetCloseMenuMode(CloseMenuMode aCloseMenuMode
) {
357 mCloseMenuMode
= aCloseMenuMode
;
361 RefPtr
<Element
> mMenu
;
363 mozilla::Modifiers mModifiers
;
368 CloseMenuMode mCloseMenuMode
;
371 class nsXULPopupManager final
: public nsIDOMEventListener
,
372 public nsIRollupListener
,
374 public mozilla::widget::NativeMenu::Observer
{
376 friend class nsXULPopupHidingEvent
;
377 friend class nsXULPopupPositionedEvent
;
378 friend class nsXULMenuCommandEvent
;
379 friend class TransitionEnder
;
381 using PopupType
= mozilla::widget::PopupType
;
382 using Element
= mozilla::dom::Element
;
386 NS_DECL_NSIDOMEVENTLISTENER
389 MOZ_CAN_RUN_SCRIPT_BOUNDARY
390 bool Rollup(const RollupOptions
&,
391 nsIContent
** aLastRolledUp
= nullptr) override
;
392 bool ShouldRollupOnMouseWheelEvent() override
;
393 bool ShouldConsumeOnMouseWheelEvent() override
;
394 bool ShouldRollupOnMouseActivate() override
;
395 uint32_t GetSubmenuWidgetChain(nsTArray
<nsIWidget
*>* aWidgetChain
) override
;
396 nsIWidget
* GetRollupWidget() override
;
397 bool RollupNativeMenu() override
;
399 MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool RollupTooltips();
401 enum class RollupKind
{ Tooltip
, Menu
};
403 bool RollupInternal(RollupKind
, const RollupOptions
&,
404 nsIContent
** aLastRolledUp
);
406 // NativeMenu::Observer
407 void OnNativeMenuOpened() override
;
408 void OnNativeMenuClosed() override
;
409 void OnNativeSubMenuWillOpen(mozilla::dom::Element
* aPopupElement
) override
;
410 void OnNativeSubMenuDidOpen(mozilla::dom::Element
* aPopupElement
) override
;
411 void OnNativeSubMenuClosed(mozilla::dom::Element
* aPopupElement
) override
;
412 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void OnNativeMenuWillActivateItem(
413 mozilla::dom::Element
* aMenuItemElement
) override
;
415 static nsXULPopupManager
* sInstance
;
417 // initialize and shutdown methods called by nsLayoutStatics
418 static nsresult
Init();
419 static void Shutdown();
421 // returns a weak reference to the popup manager instance, could return null
422 // if a popup manager could not be allocated
423 static nsXULPopupManager
* GetInstance();
425 // This should be called when a window is moved or resized to adjust the
426 // popups accordingly.
427 void AdjustPopupsOnWindowChange(nsPIDOMWindowOuter
* aWindow
);
428 void AdjustPopupsOnWindowChange(mozilla::PresShell
* aPresShell
);
430 // inform the popup manager that a menu bar has been activated or deactivated,
431 // either because one of its menus has opened or closed, or that the menubar
432 // has been focused such that its menus may be navigated with the keyboard.
433 // aActivate should be true when the menubar should be focused, and false
434 // when the active menu bar should be defocused. In the latter case, if
435 // aMenuBar isn't currently active, yet another menu bar is, that menu bar
436 // will remain active.
437 void SetActiveMenuBar(mozilla::dom::XULMenuBarElement
* aMenuBar
,
440 struct MayShowMenuResult
{
441 const bool mIsNative
= false;
442 mozilla::dom::XULButtonElement
* const mMenuButton
= nullptr;
443 nsMenuPopupFrame
* const mMenuPopupFrame
= nullptr;
445 explicit operator bool() const {
446 MOZ_ASSERT(!!mMenuButton
== !!mMenuPopupFrame
);
447 return mIsNative
|| mMenuButton
;
451 MayShowMenuResult
MayShowMenu(nsIContent
* aMenu
);
454 * Open a <menu> given its content node. If aSelectFirstItem is
455 * set to true, the first item on the menu will automatically be
458 void ShowMenu(nsIContent
* aMenu
, bool aSelectFirstItem
);
461 * Open a popup, either anchored or unanchored. If aSelectFirstItem is
462 * true, then the first item in the menu is selected. The arguments are
463 * similar to those for XULPopupElement::OpenPopup.
465 * aTriggerEvent should be the event that triggered the event. This is used
466 * to determine the coordinates and trigger node for the popup. This may be
467 * null if the popup was not triggered by an event.
469 * This fires the popupshowing event synchronously.
471 void ShowPopup(Element
* aPopup
, nsIContent
* aAnchorContent
,
472 const nsAString
& aPosition
, int32_t aXPos
, int32_t aYPos
,
473 bool aIsContextMenu
, bool aAttributesOverride
,
474 bool aSelectFirstItem
, mozilla::dom::Event
* aTriggerEvent
);
477 * Open a popup at a specific screen position specified by aXPos and aYPos,
478 * measured in CSS pixels.
480 * This fires the popupshowing event synchronously.
482 * If aIsContextMenu is true, the popup is positioned at a slight
483 * offset from aXPos/aYPos to ensure that it is not under the mouse
486 void ShowPopupAtScreen(Element
* aPopup
, int32_t aXPos
, int32_t aYPos
,
488 mozilla::dom::Event
* aTriggerEvent
);
490 /* Open a popup anchored at a screen rectangle specified by aRect.
491 * The remaining arguments are similar to ShowPopup.
493 void ShowPopupAtScreenRect(Element
* aPopup
, const nsAString
& aPosition
,
494 const nsIntRect
& aRect
, bool aIsContextMenu
,
495 bool aAttributesOverride
,
496 mozilla::dom::Event
* aTriggerEvent
);
499 * Open a popup as a native menu, at a specific screen position specified by
500 * aXPos and aYPos, measured in CSS pixels.
502 * This fires the popupshowing event synchronously.
504 * Returns whether native menus are supported for aPopup on this platform.
505 * TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
507 MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool ShowPopupAsNativeMenu(
508 Element
* aPopup
, int32_t aXPos
, int32_t aYPos
, bool aIsContextMenu
,
509 mozilla::dom::Event
* aTriggerEvent
);
512 * Open a tooltip at a specific screen position specified by aXPos and aYPos,
513 * measured in device pixels. This fires the popupshowing event synchronously.
515 void ShowTooltipAtScreen(Element
* aPopup
, nsIContent
* aTriggerContent
,
516 const mozilla::LayoutDeviceIntPoint
&);
519 * Hide a popup aPopup. If the popup is in a <menu>, then also inform the
520 * menu that the popup is being hidden.
521 * aLastPopup - optional popup to close last when hiding a chain of menus.
522 * If null, then all popups will be closed.
524 void HidePopup(Element
* aPopup
, HidePopupOptions
,
525 Element
* aLastPopup
= nullptr);
528 * Hide the popup of a <menu>.
530 void HideMenu(nsIContent
* aMenu
);
533 * Hide a popup after a short delay. This is used when rolling over menu
534 * items. This timer is stored in mCloseTimer. The timer may be cancelled and
535 * the popup closed by calling KillMenuTimer.
537 void HidePopupAfterDelay(nsMenuPopupFrame
* aPopup
, int32_t aDelay
);
540 * Hide all of the popups from a given docshell. This should be called when
541 * the document is hidden.
543 MOZ_CAN_RUN_SCRIPT_BOUNDARY
544 void HidePopupsInDocShell(nsIDocShellTreeItem
* aDocShellToHide
);
547 * Check if any popups need to be repositioned or hidden after a style or
548 * layout change. This will update, for example, any arrow type panels when
549 * the anchor that is is pointing to has moved, resized or gone away.
550 * Only those popups that pertain to the supplied aRefreshDriver are updated.
552 void UpdatePopupPositions(nsRefreshDriver
* aRefreshDriver
);
555 * Get the first nsMenuChainItem that is matched by the matching callback
558 nsMenuChainItem
* FirstMatchingPopup(
559 mozilla::FunctionRef
<bool(nsMenuChainItem
*)> aMatcher
) const;
562 * Enable or disable anchor following on the popup if needed.
564 void UpdateFollowAnchor(nsMenuPopupFrame
* aPopup
);
567 * Execute a menu command from the triggering event aEvent.
569 * aMenu - a menuitem to execute
570 * aEvent - an nsXULMenuCommandEvent that contains all the info from the mouse
571 * event which triggered the menu to be executed, may not be null
573 MOZ_CAN_RUN_SCRIPT
void ExecuteMenu(nsIContent
* aMenu
,
574 nsXULMenuCommandEvent
* aEvent
);
577 * If a native menu is open, and aItem is an item in the menu's subtree,
578 * execute the item with the help of the native menu and close the menu.
579 * Returns true if a native menu was open.
581 bool ActivateNativeMenuItem(nsIContent
* aItem
, mozilla::Modifiers aModifiers
,
582 int16_t aButton
, mozilla::ErrorResult
& aRv
);
585 * Return true if the popup for the supplied content node is open.
587 bool IsPopupOpen(Element
* aPopup
);
590 * Return the frame for the topmost open popup of a given type, or null if
591 * no popup of that type is open. If aType is PopupType::Any, a menu of any
594 nsIFrame
* GetTopPopup(PopupType aType
);
597 * Returns the topmost active menuitem that's currently visible, if any.
599 nsIContent
* GetTopActiveMenuItemContent();
602 * Return an array of all the open and visible popup frames for
603 * menus, in order from top to bottom.
604 * XXX should we always include native menu?
606 void GetVisiblePopups(nsTArray
<nsMenuPopupFrame
*>& aPopups
,
607 bool aIncludeNativeMenu
= false);
610 * Get the node that last triggered a popup or tooltip in the document
611 * aDocument. aDocument must be non-null and be a document contained within
612 * the same window hierarchy as the popup to retrieve.
614 already_AddRefed
<nsINode
> GetLastTriggerPopupNode(
615 mozilla::dom::Document
* aDocument
) {
616 return GetLastTriggerNode(aDocument
, false);
619 already_AddRefed
<nsINode
> GetLastTriggerTooltipNode(
620 mozilla::dom::Document
* aDocument
) {
621 return GetLastTriggerNode(aDocument
, true);
625 * Return false if a popup may not be opened. This will return false if the
626 * popup is already open, if the popup is in a content shell that is not
627 * focused, or if it is a submenu of another menu that isn't open.
629 bool MayShowPopup(nsMenuPopupFrame
* aFrame
);
632 * Indicate that the popup associated with aView has been moved to the
633 * specified device pixel coordinates.
635 void PopupMoved(nsIFrame
* aFrame
, const mozilla::LayoutDeviceIntPoint
& aPoint
,
639 * Indicate that the popup associated with aView has been resized to the
640 * given device pixel size aSize.
642 void PopupResized(nsIFrame
* aFrame
,
643 const mozilla::LayoutDeviceIntSize
& aSize
);
646 * Called when a popup frame is destroyed. In this case, just remove the
647 * item and later popups from the list. No point going through HidePopup as
648 * the frames have gone away.
650 MOZ_CAN_RUN_SCRIPT
void PopupDestroyed(nsMenuPopupFrame
* aFrame
);
653 * Returns true if there is a context menu open. If aPopup is specified,
654 * then the context menu must be later in the chain than aPopup. If aPopup
655 * is null, returns true if any context menu at all is open.
657 bool HasContextMenu(nsMenuPopupFrame
* aPopup
);
660 * Update the commands for the menus within the menu popup for a given
661 * content node. aPopup should be a XUL menupopup element. This method
662 * changes attributes on the children of aPopup, and deals only with the
663 * content of the popup, not the frames.
665 void UpdateMenuItems(Element
* aPopup
);
668 * Stop the timer which hides a popup after a delay, started by a previous
669 * call to HidePopupAfterDelay. In addition, the popup awaiting to be hidden
670 * is closed asynchronously.
672 void KillMenuTimer();
675 * Cancel the timer which closes menus after delay, but only if the menu to
676 * close is aMenuParent. When a submenu is opened, the user might move the
677 * mouse over a sibling menuitem which would normally close the menu. This
678 * menu is closed via a timer. However, if the user moves the mouse over the
679 * submenu before the timer fires, we should instead cancel the timer. This
680 * ensures that the user can move the mouse diagonally over a menu.
682 void CancelMenuTimer(nsMenuPopupFrame
*);
685 * Handles navigation for menu accelkeys. If aFrame is specified, then the
686 * key is handled by that popup, otherwise if aFrame is null, the key is
687 * handled by the active popup or menubar.
689 MOZ_CAN_RUN_SCRIPT
bool HandleShortcutNavigation(
690 mozilla::dom::KeyboardEvent
& aKeyEvent
, nsMenuPopupFrame
* aFrame
);
693 * Handles cursor navigation within a menu. Returns true if the key has
696 MOZ_CAN_RUN_SCRIPT
bool HandleKeyboardNavigation(uint32_t aKeyCode
);
699 * Handle keyboard navigation within a menu popup specified by aFrame.
700 * Returns true if the key was handled and other default handling
703 MOZ_CAN_RUN_SCRIPT
bool HandleKeyboardNavigationInPopup(
704 nsMenuPopupFrame
* aFrame
, nsNavigationDirection aDir
) {
705 return HandleKeyboardNavigationInPopup(nullptr, aFrame
, aDir
);
709 * Handles the keyboard event with keyCode value. Returns true if the event
712 MOZ_CAN_RUN_SCRIPT
bool HandleKeyboardEventWithKeyCode(
713 mozilla::dom::KeyboardEvent
* aKeyEvent
,
714 nsMenuChainItem
* aTopVisibleMenuItem
);
716 // Sets mIgnoreKeys of the Top Visible Menu Item
717 nsresult
UpdateIgnoreKeys(bool aIgnoreKeys
);
719 nsPopupState
GetPopupState(mozilla::dom::Element
* aPopupElement
);
721 mozilla::dom::Event
* GetOpeningPopupEvent() const {
722 return mPendingPopup
->mEvent
.get();
725 MOZ_CAN_RUN_SCRIPT nsresult
KeyUp(mozilla::dom::KeyboardEvent
* aKeyEvent
);
726 MOZ_CAN_RUN_SCRIPT nsresult
KeyDown(mozilla::dom::KeyboardEvent
* aKeyEvent
);
727 MOZ_CAN_RUN_SCRIPT nsresult
KeyPress(mozilla::dom::KeyboardEvent
* aKeyEvent
);
731 ~nsXULPopupManager();
733 // get the nsMenuPopupFrame, if any, for the given content node
734 MOZ_CAN_RUN_SCRIPT_BOUNDARY
735 nsMenuPopupFrame
* GetPopupFrameForContent(nsIContent
* aContent
,
738 // Get the menu to start rolling up.
739 nsMenuChainItem
* GetRollupItem(RollupKind
);
741 // Return the topmost menu, skipping over invisible popups
742 nsMenuChainItem
* GetTopVisibleMenu() {
743 return GetRollupItem(RollupKind::Menu
);
746 // Add the chain item to the chain and update mPopups to point to it.
747 void AddMenuChainItem(mozilla::UniquePtr
<nsMenuChainItem
>);
749 // Removes the chain item from the chain and deletes it.
750 void RemoveMenuChainItem(nsMenuChainItem
*);
752 // Hide all of the visible popups from the given list. This function can
753 // cause style changes and frame destruction.
754 MOZ_CAN_RUN_SCRIPT
void HidePopupsInList(
755 const nsTArray
<nsMenuPopupFrame
*>& aFrames
);
757 // Hide, but don't close, visible menus. Called before executing a menu item.
758 // The caller promises to close the menus properly (with a call to HidePopup)
759 // once the item has been executed.
760 MOZ_CAN_RUN_SCRIPT
void HideOpenMenusBeforeExecutingMenu(CloseMenuMode aMode
);
762 // callbacks for ShowPopup and HidePopup as events may be done asynchronously
763 MOZ_CAN_RUN_SCRIPT
void ShowPopupCallback(Element
* aPopup
,
764 nsMenuPopupFrame
* aPopupFrame
,
766 bool aSelectFirstItem
);
767 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void HidePopupCallback(
768 Element
* aPopup
, nsMenuPopupFrame
* aPopupFrame
, Element
* aNextPopup
,
769 Element
* aLastPopup
, PopupType aPopupType
, HidePopupOptions
);
772 * Trigger frame construction and reflow in the popup, fire a popupshowing
773 * event on the popup and then open the popup.
775 * aPendingPopup - information about the popup to open
776 * aIsContextMenu - true for context menus
777 * aSelectFirstItem - true to select the first item in the menu
778 * TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
780 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void BeginShowingPopup(
781 const PendingPopup
& aPendingPopup
, bool aIsContextMenu
,
782 bool aSelectFirstItem
);
785 * Fire a popuphiding event and then hide the popup. This will be called
786 * recursively if aNextPopup and aLastPopup are set in order to hide a chain
787 * of open menus. If these are not set, only one popup is closed. However,
788 * if the popup type indicates a menu, yet the next popup is not a menu,
789 * then this ends the closing of popups. This allows a menulist inside a
790 * non-menu to close up the menu but not close up the panel it is contained
793 * The caller must keep a strong reference to aPopup, aNextPopup and
796 * aPopup - the popup to hide
797 * aNextPopup - the next popup to hide
798 * aLastPopup - the last popup in the chain to hide
799 * aPresContext - nsPresContext for the popup's frame
800 * aPopupType - the PopupType of the frame.
801 * aOptions - the relevant options to hide the popup. Only a subset is looked
804 MOZ_CAN_RUN_SCRIPT_BOUNDARY
805 void FirePopupHidingEvent(Element
* aPopup
, Element
* aNextPopup
,
806 Element
* aLastPopup
, nsPresContext
* aPresContext
,
807 PopupType aPopupType
, HidePopupOptions aOptions
);
810 * Handle keyboard navigation within a menu popup specified by aItem.
813 bool HandleKeyboardNavigationInPopup(nsMenuChainItem
* aItem
,
814 nsNavigationDirection aDir
) {
815 return HandleKeyboardNavigationInPopup(aItem
, aItem
->Frame(), aDir
);
820 * Handle keyboard navigation within a menu popup aFrame. If aItem is
821 * supplied, then it is expected to have a frame equal to aFrame.
822 * If aItem is non-null, then the navigation may be redirected to
823 * an open submenu if one exists. Returns true if the key was
824 * handled and other default handling should not occur.
826 MOZ_CAN_RUN_SCRIPT
bool HandleKeyboardNavigationInPopup(
827 nsMenuChainItem
* aItem
, nsMenuPopupFrame
* aFrame
,
828 nsNavigationDirection aDir
);
831 already_AddRefed
<nsINode
> GetLastTriggerNode(
832 mozilla::dom::Document
* aDocument
, bool aIsTooltip
);
835 * Fire a popupshowing event for aPopup.
837 MOZ_CAN_RUN_SCRIPT nsEventStatus
FirePopupShowingEvent(
838 const PendingPopup
& aPendingPopup
, nsPresContext
* aPresContext
);
841 * Set mouse capturing for the current popup. This traps mouse clicks that
842 * occur outside the popup so that it can be closed up. aOldPopup should be
843 * set to the popup that was previously the current popup.
845 void SetCaptureState(nsIContent
* aOldPopup
);
848 * Key event listeners are attached to the document containing the current
849 * menu for menu and shortcut navigation. Only one listener is needed at a
850 * time, stored in mKeyListener, so switch it only if the document changes.
851 * Having menus in different documents is very rare, so the listeners will
852 * usually only be attached when the first menu opens and removed when all
855 * This is also used when only a menubar is active without any open menus,
856 * so that keyboard navigation between menus on the menubar may be done.
858 // TODO: Convert UpdateKeyboardListeners() to MOZ_CAN_RUN_SCRIPT and get rid
859 // of the kungFuDeathGrip in it.
860 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void UpdateKeyboardListeners();
863 * Returns true if the docshell for aDoc is aExpected or a child of aExpected.
865 bool IsChildOfDocShell(mozilla::dom::Document
* aDoc
,
866 nsIDocShellTreeItem
* aExpected
);
868 // Finds a chain item in mPopups.
869 nsMenuChainItem
* FindPopup(Element
* aPopup
) const;
871 // the document the key event listener is attached to
872 nsCOMPtr
<mozilla::dom::EventTarget
> mKeyListener
;
874 // widget that is currently listening to rollup events
875 nsCOMPtr
<nsIWidget
> mWidget
;
877 // set to the currently active menu bar, if any
878 mozilla::dom::XULMenuBarElement
* mActiveMenuBar
;
880 // linked list of normal menus and panels. mPopups points to the innermost
881 // popup, which keeps alive all their parents.
882 mozilla::UniquePtr
<nsMenuChainItem
> mPopups
;
884 // timer used for HidePopupAfterDelay
885 nsCOMPtr
<nsITimer
> mCloseTimer
;
886 nsMenuPopupFrame
* mTimerMenu
= nullptr;
888 // Information about the popup that is currently firing a popupshowing event.
889 const PendingPopup
* mPendingPopup
;
891 // If a popup is displayed as a native menu, this is non-null while the
892 // native menu is open.
893 // mNativeMenu has a strong reference to the menupopup nsIContent.
894 RefPtr
<mozilla::widget::NativeMenu
> mNativeMenu
;
896 // If the currently open native menu activated an item, this is the item's
897 // close menu mode. Nothing() if mNativeMenu is null or if no item was
899 mozilla::Maybe
<CloseMenuMode
> mNativeMenuActivatedItemCloseMenuMode
;
901 // If a popup is displayed as a native menu, this map contains the popup state
902 // for any of its non-closed submenus. This state cannot be stored on the
903 // submenus' nsMenuPopupFrames, because we usually don't generate frames for
904 // the contents of native menus.
905 // If a submenu is not present in this map, it means it's closed.
906 // This map is empty if mNativeMenu is null.
907 nsTHashMap
<RefPtr
<mozilla::dom::Element
>, nsPopupState
>
908 mNativeMenuSubmenuStates
;