Bug 1919083 - [ci] Enable os-integration variant for more suites, r=jmaher
[gecko.git] / layout / xul / nsXULPopupManager.h
blob883700972b7899b9a2ee5411cf18f8f4e8bba2f0
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/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
34 #ifdef KeyPress
35 # undef KeyPress
36 #endif
38 /**
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
50 * calling Rollup.
53 class nsContainerFrame;
54 class nsITimer;
55 class nsIDocShellTreeItem;
56 class nsMenuPopupFrame;
57 class nsPIDOMWindowOuter;
58 class nsRefreshDriver;
60 namespace mozilla {
61 class PresShell;
62 namespace dom {
63 class Event;
64 class KeyboardEvent;
65 class UIEvent;
66 class XULButtonElement;
67 class XULMenuBarElement;
68 class XULPopupElement;
69 } // namespace dom
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.
89 enum nsPopupState {
90 // state when a popup is not open
91 ePopupClosed,
92 // state from when a popup is requested to be shown to after the
93 // popupshowing event has been fired.
94 ePopupShowing,
95 // state while a popup is waiting to be laid out and positioned
96 ePopupPositioning,
97 // state while a popup is open but the widget is not yet visible
98 ePopupOpening,
99 // state while a popup is visible and waiting for the popupshown event
100 ePopupVisible,
101 // state while a popup is open and visible on screen
102 ePopupShown,
103 // state from when a popup is requested to be hidden to when it is closed.
104 ePopupHiding,
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
110 // unloaded.
111 ePopupInvisible
114 // when a menu command is executed, the closemenu attribute may be used
115 // to define how the menu should be closed up
116 enum CloseMenuMode {
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
128 * respectively).
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 --->
136 * First |
137 * ... |
138 * Before |
139 * +--------+ block
140 * Start | | End progression
141 * +--------+ |
142 * After |
143 * ... |
144 * Last V
148 enum nsNavigationDirection {
149 eNavigationDirection_Last,
150 eNavigationDirection_First,
151 eNavigationDirection_Start,
152 eNavigationDirection_Before,
153 eNavigationDirection_End,
154 eNavigationDirection_After
157 enum nsIgnoreKeys {
158 eIgnoreKeys_False,
159 eIgnoreKeys_True,
160 eIgnoreKeys_Shortcuts,
163 enum class HidePopupOption : uint8_t {
164 // If the entire chain of menus should be closed.
165 HideChain,
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.
168 DeselectMenu,
169 // If the first popuphiding event should be sent asynchrously. This should
170 // be set if HidePopup is called from a frame.
171 Async,
172 // If this popup is hiding due to being cancelled.
173 IsRollup,
174 // Whether animations should be disabled for rolled-up popups.
175 DisableAnimations,
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
234 // the anchor moves.
235 bool mFollowAnchor;
237 // The last seen position of the anchor, relative to the screen.
238 nsRect mCurrentRect;
240 mozilla::UniquePtr<nsMenuChainItem> mParent;
241 // Back pointer, safe because mChild keeps us alive.
242 nsMenuChainItem* mChild = nullptr;
244 public:
245 nsMenuChainItem(nsMenuPopupFrame* aFrame, bool aNoAutoHide, bool aIsContext,
246 PopupType aPopupType)
247 : mFrame(aFrame),
248 mPopupType(aPopupType),
249 mNoAutoHide(aNoAutoHide),
250 mIsContext(aIsContext),
251 mOnMenuBar(false),
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;
289 public:
290 nsXULPopupHidingEvent(Element* aPopup, Element* aNextPopup,
291 Element* aLastPopup, PopupType aPopupType,
292 HidePopupOptions aOptions)
293 : mozilla::Runnable("nsXULPopupHidingEvent"),
294 mPopup(aPopup),
295 mNextPopup(aNextPopup),
296 mLastPopup(aLastPopup),
297 mPopupType(aPopupType),
298 mOptions(aOptions) {
299 NS_ASSERTION(aPopup,
300 "null popup supplied to nsXULPopupHidingEvent constructor");
301 // aNextPopup and aLastPopup may be null
304 NS_IMETHOD Run() override;
306 private:
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;
318 public:
319 explicit nsXULPopupPositionedEvent(Element* aPopup)
320 : mozilla::Runnable("nsXULPopupPositionedEvent"), mPopup(aPopup) {
321 MOZ_ASSERT(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);
330 private:
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;
338 public:
339 nsXULMenuCommandEvent(Element* aMenu, bool aIsTrusted,
340 mozilla::Modifiers aModifiers, bool aUserInput,
341 bool aFlipChecked, int16_t aButton)
342 : mozilla::Runnable("nsXULMenuCommandEvent"),
343 mMenu(aMenu),
344 mModifiers(aModifiers),
345 mButton(aButton),
346 mIsTrusted(aIsTrusted),
347 mUserInput(aUserInput),
348 mFlipChecked(aFlipChecked),
349 mCloseMenuMode(CloseMenuMode_Auto) {
350 NS_ASSERTION(aMenu,
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;
360 private:
361 RefPtr<Element> mMenu;
363 mozilla::Modifiers mModifiers;
364 int16_t mButton;
365 bool mIsTrusted;
366 bool mUserInput;
367 bool mFlipChecked;
368 CloseMenuMode mCloseMenuMode;
371 class nsXULPopupManager final : public nsIDOMEventListener,
372 public nsIRollupListener,
373 public nsIObserver,
374 public mozilla::widget::NativeMenu::Observer {
375 public:
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;
384 NS_DECL_ISUPPORTS
385 NS_DECL_NSIOBSERVER
386 NS_DECL_NSIDOMEVENTLISTENER
388 // nsIRollupListener
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 };
402 MOZ_CAN_RUN_SCRIPT
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,
438 bool aActivate);
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
456 * selected.
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
484 * cursor.
486 void ShowPopupAtScreen(Element* aPopup, int32_t aXPos, int32_t aYPos,
487 bool aIsContextMenu,
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
556 * function provided.
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
592 * type is returned.
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,
636 bool aByMoveToRect);
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
694 * been handled.
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
701 * should not occur.
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
710 * has been handled.
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);
729 protected:
730 nsXULPopupManager();
731 ~nsXULPopupManager();
733 // get the nsMenuPopupFrame, if any, for the given content node
734 MOZ_CAN_RUN_SCRIPT_BOUNDARY
735 nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent,
736 bool aShouldFlush);
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,
765 bool aIsContextMenu,
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
791 * within.
793 * The caller must keep a strong reference to aPopup, aNextPopup and
794 * aLastPopup.
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
802 * at.
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.
812 MOZ_CAN_RUN_SCRIPT
813 bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem,
814 nsNavigationDirection aDir) {
815 return HandleKeyboardNavigationInPopup(aItem, aItem->Frame(), aDir);
818 private:
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);
830 protected:
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
853 * menus have closed.
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
898 // activated.
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;
911 #endif