Backed out changeset ddccd40117a0 (bug 1853271) for causing bug 1854769. CLOSED TREE
[gecko.git] / layout / xul / nsXULPopupManager.h
blobd056322deb486f765e018227d15c5e713d6d8ca6
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/. */
7 /**
8 * The XUL Popup Manager keeps track of all open popups.
9 */
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"
19 #include "Units.h"
20 #include "nsPoint.h"
21 #include "nsCOMPtr.h"
22 #include "nsTArray.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
33 #ifdef KeyPress
34 # undef KeyPress
35 #endif
37 /**
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
49 * calling Rollup.
52 class nsContainerFrame;
53 class nsITimer;
54 class nsIDocShellTreeItem;
55 class nsMenuPopupFrame;
56 class nsPIDOMWindowOuter;
57 class nsRefreshDriver;
59 namespace mozilla {
60 class PresShell;
61 namespace dom {
62 class Event;
63 class KeyboardEvent;
64 class UIEvent;
65 class XULButtonElement;
66 class XULMenuBarElement;
67 class XULPopupElement;
68 } // namespace dom
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.
88 enum nsPopupState {
89 // state when a popup is not open
90 ePopupClosed,
91 // state from when a popup is requested to be shown to after the
92 // popupshowing event has been fired.
93 ePopupShowing,
94 // state while a popup is waiting to be laid out and positioned
95 ePopupPositioning,
96 // state while a popup is open but the widget is not yet visible
97 ePopupOpening,
98 // state while a popup is visible and waiting for the popupshown event
99 ePopupVisible,
100 // state while a popup is open and visible on screen
101 ePopupShown,
102 // state from when a popup is requested to be hidden to when it is closed.
103 ePopupHiding,
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
109 // unloaded.
110 ePopupInvisible
113 // when a menu command is executed, the closemenu attribute may be used
114 // to define how the menu should be closed up
115 enum CloseMenuMode {
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
127 * respectively).
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 --->
135 * First |
136 * ... |
137 * Before |
138 * +--------+ block
139 * Start | | End progression
140 * +--------+ |
141 * After |
142 * ... |
143 * Last V
147 enum nsNavigationDirection {
148 eNavigationDirection_Last,
149 eNavigationDirection_First,
150 eNavigationDirection_Start,
151 eNavigationDirection_Before,
152 eNavigationDirection_End,
153 eNavigationDirection_After
156 enum nsIgnoreKeys {
157 eIgnoreKeys_False,
158 eIgnoreKeys_True,
159 eIgnoreKeys_Shortcuts,
162 enum class HidePopupOption : uint8_t {
163 // If the entire chain of menus should be closed.
164 HideChain,
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.
167 DeselectMenu,
168 // If the first popuphiding event should be sent asynchrously. This should
169 // be set if HidePopup is called from a frame.
170 Async,
171 // If this popup is hiding due to being cancelled.
172 IsRollup,
173 // Whether animations should be disabled for rolled-up popups.
174 DisableAnimations,
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
233 // the anchor moves.
234 bool mFollowAnchor;
236 // The last seen position of the anchor, relative to the screen.
237 nsRect mCurrentRect;
239 mozilla::UniquePtr<nsMenuChainItem> mParent;
240 // Back pointer, safe because mChild keeps us alive.
241 nsMenuChainItem* mChild = nullptr;
243 public:
244 nsMenuChainItem(nsMenuPopupFrame* aFrame, bool aNoAutoHide, bool aIsContext,
245 PopupType aPopupType)
246 : mFrame(aFrame),
247 mPopupType(aPopupType),
248 mNoAutoHide(aNoAutoHide),
249 mIsContext(aIsContext),
250 mOnMenuBar(false),
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;
288 public:
289 nsXULPopupHidingEvent(Element* aPopup, Element* aNextPopup,
290 Element* aLastPopup, PopupType aPopupType,
291 HidePopupOptions aOptions)
292 : mozilla::Runnable("nsXULPopupHidingEvent"),
293 mPopup(aPopup),
294 mNextPopup(aNextPopup),
295 mLastPopup(aLastPopup),
296 mPopupType(aPopupType),
297 mOptions(aOptions) {
298 NS_ASSERTION(aPopup,
299 "null popup supplied to nsXULPopupHidingEvent constructor");
300 // aNextPopup and aLastPopup may be null
303 NS_IMETHOD Run() override;
305 private:
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;
317 public:
318 explicit nsXULPopupPositionedEvent(Element* aPopup)
319 : mozilla::Runnable("nsXULPopupPositionedEvent"), mPopup(aPopup) {
320 MOZ_ASSERT(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);
329 private:
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;
337 public:
338 nsXULMenuCommandEvent(Element* aMenu, bool aIsTrusted,
339 mozilla::Modifiers aModifiers, bool aUserInput,
340 bool aFlipChecked, int16_t aButton)
341 : mozilla::Runnable("nsXULMenuCommandEvent"),
342 mMenu(aMenu),
343 mModifiers(aModifiers),
344 mButton(aButton),
345 mIsTrusted(aIsTrusted),
346 mUserInput(aUserInput),
347 mFlipChecked(aFlipChecked),
348 mCloseMenuMode(CloseMenuMode_Auto) {
349 NS_ASSERTION(aMenu,
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;
359 private:
360 RefPtr<Element> mMenu;
362 mozilla::Modifiers mModifiers;
363 int16_t mButton;
364 bool mIsTrusted;
365 bool mUserInput;
366 bool mFlipChecked;
367 CloseMenuMode mCloseMenuMode;
370 class nsXULPopupManager final : public nsIDOMEventListener,
371 public nsIRollupListener,
372 public nsIObserver,
373 public mozilla::widget::NativeMenu::Observer {
374 public:
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;
383 NS_DECL_ISUPPORTS
384 NS_DECL_NSIOBSERVER
385 NS_DECL_NSIDOMEVENTLISTENER
387 // nsIRollupListener
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 };
401 MOZ_CAN_RUN_SCRIPT
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,
437 bool aActivate);
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
455 * selected.
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
483 * cursor.
485 void ShowPopupAtScreen(Element* aPopup, int32_t aXPos, int32_t aYPos,
486 bool aIsContextMenu,
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
584 * type is returned.
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,
626 bool aByMoveToRect);
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
684 * been handled.
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
691 * should not occur.
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
700 * has been handled.
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);
719 protected:
720 nsXULPopupManager();
721 ~nsXULPopupManager();
723 // get the nsMenuPopupFrame, if any, for the given content node
724 MOZ_CAN_RUN_SCRIPT_BOUNDARY
725 nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent,
726 bool aShouldFlush);
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,
752 bool aIsContextMenu,
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
778 * within.
780 * The caller must keep a strong reference to aPopup, aNextPopup and
781 * aLastPopup.
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
789 * at.
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.
799 MOZ_CAN_RUN_SCRIPT
800 bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem,
801 nsNavigationDirection aDir) {
802 return HandleKeyboardNavigationInPopup(aItem, aItem->Frame(), aDir);
805 private:
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);
817 protected:
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
840 * menus have closed.
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
885 // activated.
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;
898 #endif