tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsXULPopupManager.h (34097B)


      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/. */
      6 
      7 /**
      8 * The XUL Popup Manager keeps track of all open popups.
      9 */
     10 
     11 #ifndef nsXULPopupManager_h__
     12 #define nsXULPopupManager_h__
     13 
     14 #include "Units.h"
     15 #include "mozilla/Attributes.h"
     16 #include "mozilla/FunctionRef.h"
     17 #include "mozilla/Logging.h"
     18 #include "mozilla/widget/InitData.h"
     19 #include "mozilla/widget/NativeMenu.h"
     20 #include "nsCOMPtr.h"
     21 #include "nsHashtablesFwd.h"
     22 #include "nsIContent.h"
     23 #include "nsIDOMEventListener.h"
     24 #include "nsIObserver.h"
     25 #include "nsIRollupListener.h"
     26 #include "nsPoint.h"
     27 #include "nsTArray.h"
     28 #include "nsThreadUtils.h"
     29 
     30 // XXX Avoid including this here by moving function bodies to the cpp file.
     31 #include "mozilla/dom/Element.h"
     32 
     33 // X.h defines KeyPress
     34 #ifdef KeyPress
     35 #  undef KeyPress
     36 #endif
     37 
     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.
     45 *
     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.
     51 */
     52 
     53 class nsContainerFrame;
     54 class nsITimer;
     55 class nsIDocShellTreeItem;
     56 class nsMenuPopupFrame;
     57 class nsPIDOMWindowOuter;
     58 class nsRefreshDriver;
     59 class PopupQueue;
     60 
     61 namespace mozilla {
     62 class PresShell;
     63 namespace dom {
     64 class Event;
     65 class KeyboardEvent;
     66 class UIEvent;
     67 class XULButtonElement;
     68 class XULMenuBarElement;
     69 class XULPopupElement;
     70 }  // namespace dom
     71 }  // namespace mozilla
     72 
     73 // XUL popups can be in several different states. When opening a popup, the
     74 // state changes as follows:
     75 //   ePopupClosed - initial state
     76 //   ePopupShowing - during the period when the popupshowing event fires
     77 //   ePopupOpening - between the popupshowing event and being visible. Creation
     78 //                   of the child frames, layout and reflow occurs in this
     79 //                   state. The popup is stored in the popup manager's list of
     80 //                   open popups during this state.
     81 //   ePopupVisible - layout is done and the popup's view and widget are made
     82 //                   visible. The popup is visible on screen but may be
     83 //                   transitioning. The popupshown event has not yet fired.
     84 //   ePopupShown - the popup has been shown and is fully ready. This state is
     85 //                 assigned just before the popupshown event fires.
     86 // When closing a popup:
     87 //   ePopupHidden - during the period when the popuphiding event fires and
     88 //                  the popup is removed.
     89 //   ePopupClosed - the popup's widget is made invisible.
     90 enum nsPopupState {
     91  // state when a popup is not open
     92  ePopupClosed,
     93  // state from when a popup is requested to be shown to after the
     94  // popupshowing event has been fired.
     95  ePopupShowing,
     96  // state while a popup is waiting to be laid out and positioned
     97  ePopupPositioning,
     98  // state while a popup is open but the widget is not yet visible
     99  ePopupOpening,
    100  // state while a popup is visible and waiting for the popupshown event
    101  ePopupVisible,
    102  // state while a popup is open and visible on screen
    103  ePopupShown,
    104  // state from when a popup is requested to be hidden to when it is closed.
    105  ePopupHiding,
    106  // state which indicates that the popup was hidden without firing the
    107  // popuphiding or popuphidden events. It is used when executing a menu
    108  // command because the menu needs to be hidden before the command event
    109  // fires, yet the popuphiding and popuphidden events are fired after. This
    110  // state can also occur when the popup is removed because the document is
    111  // unloaded.
    112  ePopupInvisible
    113 };
    114 
    115 // when a menu command is executed, the closemenu attribute may be used
    116 // to define how the menu should be closed up
    117 enum CloseMenuMode {
    118  CloseMenuMode_Auto,   // close up the chain of menus, default value
    119  CloseMenuMode_None,   // don't close up any menus
    120  CloseMenuMode_Single  // close up only the menu the command is inside
    121 };
    122 
    123 /**
    124 * nsNavigationDirection: an enum expressing navigation through the menus in
    125 * terms which are independent of the directionality of the chrome. The
    126 * terminology, derived from XSL-FO and CSS3 (e.g.
    127 * http://www.w3.org/TR/css3-text/#TextLayout), is BASE (Before, After, Start,
    128 * End), with the addition of First and Last (mapped to Home and End
    129 * respectively).
    130 *
    131 * In languages such as English where the inline progression is left-to-right
    132 * and the block progression is top-to-bottom (lr-tb), these terms will map out
    133 * as in the following diagram
    134 *
    135 *  --- inline progression --->
    136 *
    137 *           First              |
    138 *           ...                |
    139 *           Before             |
    140 *         +--------+         block
    141 *   Start |        | End  progression
    142 *         +--------+           |
    143 *           After              |
    144 *           ...                |
    145 *           Last               V
    146 *
    147 */
    148 
    149 enum nsNavigationDirection {
    150  eNavigationDirection_Last,
    151  eNavigationDirection_First,
    152  eNavigationDirection_Start,
    153  eNavigationDirection_Before,
    154  eNavigationDirection_End,
    155  eNavigationDirection_After
    156 };
    157 
    158 enum nsIgnoreKeys {
    159  eIgnoreKeys_False,
    160  eIgnoreKeys_True,
    161  eIgnoreKeys_Shortcuts,
    162 };
    163 
    164 enum class HidePopupOption : uint8_t {
    165  // If the entire chain of menus should be closed.
    166  HideChain,
    167  // If the parent <menu> of the popup should not be deselected. This will not
    168  // be set when the menu is closed by pressing the Escape key.
    169  DeselectMenu,
    170  // If the first popuphiding event should be sent asynchrously. This should
    171  // be set if HidePopup is called from a frame.
    172  Async,
    173  // If this popup is hiding due to being cancelled.
    174  IsRollup,
    175  // Whether animations should be disabled for rolled-up popups.
    176  DisableAnimations,
    177 };
    178 
    179 using HidePopupOptions = mozilla::EnumSet<HidePopupOption>;
    180 
    181 /**
    182 * DirectionFromKeyCodeTable: two arrays, the first for left-to-right and the
    183 * other for right-to-left, that map keycodes to values of
    184 * nsNavigationDirection.
    185 */
    186 extern const nsNavigationDirection DirectionFromKeyCodeTable[2][6];
    187 
    188 #define NS_DIRECTION_FROM_KEY_CODE(frame, keycode)                    \
    189  (DirectionFromKeyCodeTable                                          \
    190       [static_cast<uint8_t>((frame)->StyleVisibility()->mDirection)] \
    191       [(keycode) - mozilla::dom::KeyboardEvent_Binding::DOM_VK_END])
    192 
    193 // Used to hold information about a popup that is about to be opened.
    194 struct PendingPopup {
    195  using Element = mozilla::dom::Element;
    196  using Event = mozilla::dom::Event;
    197 
    198  PendingPopup(Element* aPopup, Event* aEvent);
    199 
    200  const RefPtr<Element> mPopup;
    201  const RefPtr<Event> mEvent;
    202 
    203  // Device pixels relative to the showing popup's presshell's
    204  // root prescontext's root frame.
    205  mozilla::LayoutDeviceIntPoint mMousePoint;
    206 
    207  // Cached modifiers used to trigger the popup.
    208  mozilla::Modifiers mModifiers;
    209 
    210  already_AddRefed<nsIContent> GetTriggerContent() const;
    211 
    212  void InitMousePoint();
    213 
    214  void SetMousePoint(mozilla::LayoutDeviceIntPoint aMousePoint) {
    215    mMousePoint = aMousePoint;
    216  }
    217 
    218  uint16_t MouseInputSource() const;
    219 };
    220 
    221 // nsMenuChainItem holds info about an open popup. Items are stored in a
    222 // doubly linked list. Note that the linked list is stored beginning from
    223 // the lowest child in a chain of menus, as this is the active submenu.
    224 class nsMenuChainItem {
    225  using PopupType = mozilla::widget::PopupType;
    226 
    227  nsMenuPopupFrame* mFrame;  // the popup frame
    228  PopupType mPopupType;      // the popup type of the frame
    229  bool mNoAutoHide;          // true for noautohide panels
    230  bool mIsContext;           // true for context menus
    231  bool mOnMenuBar;           // true if the menu is on a menu bar
    232  nsIgnoreKeys mIgnoreKeys;  // indicates how keyboard listeners should be used
    233 
    234  // True if the popup should maintain its position relative to the anchor when
    235  // the anchor moves.
    236  bool mFollowAnchor;
    237 
    238  // The last seen position of the anchor, relative to the screen.
    239  nsRect mCurrentRect;
    240 
    241  mozilla::UniquePtr<nsMenuChainItem> mParent;
    242  // Back pointer, safe because mChild keeps us alive.
    243  nsMenuChainItem* mChild = nullptr;
    244 
    245 public:
    246  nsMenuChainItem(nsMenuPopupFrame* aFrame, bool aNoAutoHide, bool aIsContext,
    247                  PopupType aPopupType)
    248      : mFrame(aFrame),
    249        mPopupType(aPopupType),
    250        mNoAutoHide(aNoAutoHide),
    251        mIsContext(aIsContext),
    252        mOnMenuBar(false),
    253        mIgnoreKeys(eIgnoreKeys_False),
    254        mFollowAnchor(false) {
    255    NS_ASSERTION(aFrame, "null frame passed to nsMenuChainItem constructor");
    256    MOZ_COUNT_CTOR(nsMenuChainItem);
    257  }
    258 
    259  MOZ_COUNTED_DTOR(nsMenuChainItem)
    260 
    261  mozilla::dom::XULPopupElement* Element();
    262  nsMenuPopupFrame* Frame() { return mFrame; }
    263  PopupType GetPopupType() { return mPopupType; }
    264  bool IsNoAutoHide() { return mNoAutoHide; }
    265  void SetNoAutoHide(bool aNoAutoHide) { mNoAutoHide = aNoAutoHide; }
    266  bool IsMenu() { return mPopupType == PopupType::Menu; }
    267  bool IsContextMenu() { return mIsContext; }
    268  nsIgnoreKeys IgnoreKeys() { return mIgnoreKeys; }
    269  void SetIgnoreKeys(nsIgnoreKeys aIgnoreKeys) { mIgnoreKeys = aIgnoreKeys; }
    270  bool IsOnMenuBar() { return mOnMenuBar; }
    271  void SetOnMenuBar(bool aOnMenuBar) { mOnMenuBar = aOnMenuBar; }
    272  nsMenuChainItem* GetParent() { return mParent.get(); }
    273  nsMenuChainItem* GetChild() { return mChild; }
    274  bool FollowsAnchor() { return mFollowAnchor; }
    275  void UpdateFollowAnchor();
    276  void CheckForAnchorChange();
    277 
    278  // set the parent of this item to aParent, also changing the parent
    279  // to have this as a child.
    280  void SetParent(mozilla::UniquePtr<nsMenuChainItem> aParent);
    281  // Removes the parent pointer and returns it.
    282  mozilla::UniquePtr<nsMenuChainItem> Detach();
    283 };
    284 
    285 // this class is used for dispatching popuphiding events asynchronously.
    286 class nsXULPopupHidingEvent : public mozilla::Runnable {
    287  using PopupType = mozilla::widget::PopupType;
    288  using Element = mozilla::dom::Element;
    289 
    290 public:
    291  nsXULPopupHidingEvent(Element* aPopup, Element* aNextPopup,
    292                        Element* aLastPopup, PopupType aPopupType,
    293                        HidePopupOptions aOptions)
    294      : mozilla::Runnable("nsXULPopupHidingEvent"),
    295        mPopup(aPopup),
    296        mNextPopup(aNextPopup),
    297        mLastPopup(aLastPopup),
    298        mPopupType(aPopupType),
    299        mOptions(aOptions) {
    300    NS_ASSERTION(aPopup,
    301                 "null popup supplied to nsXULPopupHidingEvent constructor");
    302    // aNextPopup and aLastPopup may be null
    303  }
    304 
    305  NS_IMETHOD Run() override;
    306 
    307 private:
    308  nsCOMPtr<Element> mPopup;
    309  nsCOMPtr<Element> mNextPopup;
    310  nsCOMPtr<Element> mLastPopup;
    311  PopupType mPopupType;
    312  HidePopupOptions mOptions;
    313 };
    314 
    315 // this class is used for dispatching popuppositioned events asynchronously.
    316 class nsXULPopupPositionedEvent : public mozilla::Runnable {
    317  using Element = mozilla::dom::Element;
    318 
    319 public:
    320  explicit nsXULPopupPositionedEvent(Element* aPopup)
    321      : mozilla::Runnable("nsXULPopupPositionedEvent"), mPopup(aPopup) {
    322    MOZ_ASSERT(aPopup);
    323  }
    324 
    325  NS_IMETHOD Run() override;
    326 
    327  // Asynchronously dispatch a popuppositioned event at aPopup if this is a
    328  // panel that should receieve such events. Return true if the event was sent.
    329  static bool DispatchIfNeeded(Element* aPopup);
    330 
    331 private:
    332  const nsCOMPtr<Element> mPopup;
    333 };
    334 
    335 // this class is used for dispatching menu command events asynchronously.
    336 class nsXULMenuCommandEvent : public mozilla::Runnable {
    337  using Element = mozilla::dom::Element;
    338 
    339 public:
    340  nsXULMenuCommandEvent(Element* aMenu, bool aIsTrusted,
    341                        mozilla::Modifiers aModifiers, bool aUserInput,
    342                        bool aFlipChecked, int16_t aButton)
    343      : mozilla::Runnable("nsXULMenuCommandEvent"),
    344        mMenu(aMenu),
    345        mModifiers(aModifiers),
    346        mButton(aButton),
    347        mIsTrusted(aIsTrusted),
    348        mUserInput(aUserInput),
    349        mFlipChecked(aFlipChecked),
    350        mCloseMenuMode(CloseMenuMode_Auto) {
    351    NS_ASSERTION(aMenu,
    352                 "null menu supplied to nsXULMenuCommandEvent constructor");
    353  }
    354 
    355  MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override;
    356 
    357  void SetCloseMenuMode(CloseMenuMode aCloseMenuMode) {
    358    mCloseMenuMode = aCloseMenuMode;
    359  }
    360 
    361 private:
    362  RefPtr<Element> mMenu;
    363 
    364  mozilla::Modifiers mModifiers;
    365  int16_t mButton;
    366  bool mIsTrusted;
    367  bool mUserInput;
    368  bool mFlipChecked;
    369  CloseMenuMode mCloseMenuMode;
    370 };
    371 
    372 class nsXULPopupManager final : public nsIDOMEventListener,
    373                                public nsIRollupListener,
    374                                public nsIObserver,
    375                                public mozilla::widget::NativeMenu::Observer {
    376 public:
    377  friend class nsXULPopupHidingEvent;
    378  friend class nsXULPopupPositionedEvent;
    379  friend class nsXULMenuCommandEvent;
    380  friend class TransitionEnder;
    381 
    382  using PopupType = mozilla::widget::PopupType;
    383  using Element = mozilla::dom::Element;
    384 
    385  NS_DECL_ISUPPORTS
    386  NS_DECL_NSIOBSERVER
    387  NS_DECL_NSIDOMEVENTLISTENER
    388 
    389  // nsIRollupListener
    390  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    391  bool Rollup(const RollupOptions&,
    392              nsIContent** aLastRolledUp = nullptr) override;
    393  bool ShouldRollupOnMouseWheelEvent() override;
    394  bool ShouldConsumeOnMouseWheelEvent() override;
    395  bool ShouldRollupOnMouseActivate() override;
    396  uint32_t GetSubmenuWidgetChain(nsTArray<nsIWidget*>* aWidgetChain) override;
    397  nsIWidget* GetRollupWidget() override;
    398  bool RollupNativeMenu() override;
    399 
    400  MOZ_CAN_RUN_SCRIPT_BOUNDARY bool RollupTooltips();
    401 
    402  enum class RollupKind { Tooltip, Menu };
    403  MOZ_CAN_RUN_SCRIPT
    404  bool RollupInternal(RollupKind, const RollupOptions&,
    405                      nsIContent** aLastRolledUp);
    406 
    407  // NativeMenu::Observer
    408  void OnNativeMenuOpened() override;
    409  void OnNativeMenuClosed() override;
    410  void OnNativeSubMenuWillOpen(mozilla::dom::Element* aPopupElement) override;
    411  void OnNativeSubMenuDidOpen(mozilla::dom::Element* aPopupElement) override;
    412  void OnNativeSubMenuClosed(mozilla::dom::Element* aPopupElement) override;
    413  MOZ_CAN_RUN_SCRIPT_BOUNDARY void OnNativeMenuWillActivateItem(
    414      mozilla::dom::Element* aMenuItemElement) override;
    415 
    416  static nsXULPopupManager* sInstance;
    417 
    418  // initialize and shutdown methods called by nsLayoutStatics
    419  static nsresult Init();
    420  static void Shutdown();
    421 
    422  // returns a weak reference to the popup manager instance, could return null
    423  // if a popup manager could not be allocated
    424  static nsXULPopupManager* GetInstance();
    425 
    426  // This should be called when a window is moved or resized to adjust the
    427  // popups accordingly.
    428  void AdjustPopupsOnWindowChange(nsPIDOMWindowOuter* aWindow);
    429  void AdjustPopupsOnWindowChange(mozilla::PresShell* aPresShell);
    430 
    431  // inform the popup manager that a menu bar has been activated or deactivated,
    432  // either because one of its menus has opened or closed, or that the menubar
    433  // has been focused such that its menus may be navigated with the keyboard.
    434  // aActivate should be true when the menubar should be focused, and false
    435  // when the active menu bar should be defocused. In the latter case, if
    436  // aMenuBar isn't currently active, yet another menu bar is, that menu bar
    437  // will remain active.
    438  void SetActiveMenuBar(mozilla::dom::XULMenuBarElement* aMenuBar,
    439                        bool aActivate);
    440 
    441  struct MayShowMenuResult {
    442    const bool mIsNative = false;
    443    mozilla::dom::XULButtonElement* const mMenuButton = nullptr;
    444    nsMenuPopupFrame* const mMenuPopupFrame = nullptr;
    445 
    446    explicit operator bool() const {
    447      MOZ_ASSERT(!!mMenuButton == !!mMenuPopupFrame);
    448      return mIsNative || mMenuButton;
    449    }
    450  };
    451 
    452  MayShowMenuResult MayShowMenu(nsIContent* aMenu);
    453 
    454  /**
    455   * Open a <menu> given its content node. If aSelectFirstItem is
    456   * set to true, the first item on the menu will automatically be
    457   * selected.
    458   */
    459  void ShowMenu(nsIContent* aMenu, bool aSelectFirstItem);
    460 
    461  /**
    462   * Open a popup, either anchored or unanchored. If aSelectFirstItem is
    463   * true, then the first item in the menu is selected. The arguments are
    464   * similar to those for XULPopupElement::OpenPopup.
    465   *
    466   * aTriggerEvent should be the event that triggered the event. This is used
    467   * to determine the coordinates and trigger node for the popup. This may be
    468   * null if the popup was not triggered by an event.
    469   *
    470   * This fires the popupshowing event synchronously.
    471   */
    472  void ShowPopup(Element* aPopup, nsIContent* aAnchorContent,
    473                 const nsAString& aPosition, int32_t aXPos, int32_t aYPos,
    474                 bool aIsContextMenu, bool aAttributesOverride,
    475                 bool aSelectFirstItem, mozilla::dom::Event* aTriggerEvent);
    476 
    477  /**
    478   * Open a popup at a specific screen position specified by aXPos and aYPos,
    479   * measured in CSS pixels.
    480   *
    481   * This fires the popupshowing event synchronously.
    482   *
    483   * If aIsContextMenu is true, the popup is positioned at a slight
    484   * offset from aXPos/aYPos to ensure that it is not under the mouse
    485   * cursor.
    486   */
    487  void ShowPopupAtScreen(Element* aPopup, int32_t aXPos, int32_t aYPos,
    488                         bool aIsContextMenu,
    489                         mozilla::dom::Event* aTriggerEvent);
    490 
    491  /* Open a popup anchored at a screen rectangle specified by aRect.
    492   * The remaining arguments are similar to ShowPopup.
    493   */
    494  void ShowPopupAtScreenRect(Element* aPopup, const nsAString& aPosition,
    495                             const nsIntRect& aRect, bool aIsContextMenu,
    496                             bool aAttributesOverride,
    497                             mozilla::dom::Event* aTriggerEvent);
    498 
    499  /**
    500   * Open a popup as a native menu, at a specific screen position specified by
    501   * aXPos and aYPos, measured in CSS pixels.
    502   *
    503   * This fires the popupshowing event synchronously.
    504   *
    505   * Returns whether native menus are supported for aPopup on this platform.
    506   * TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
    507   */
    508  MOZ_CAN_RUN_SCRIPT_BOUNDARY bool ShowPopupAsNativeMenu(
    509      Element* aPopup, int32_t aXPos, int32_t aYPos, bool aIsContextMenu,
    510      mozilla::dom::Event* aTriggerEvent);
    511 
    512  /**
    513   * Open a tooltip at a specific screen position specified by aXPos and aYPos,
    514   * measured in device pixels. This fires the popupshowing event synchronously.
    515   */
    516  void ShowTooltipAtScreen(Element* aPopup, nsIContent* aTriggerContent,
    517                           const mozilla::LayoutDeviceIntPoint&);
    518 
    519  /*
    520   * Hide a popup aPopup. If the popup is in a <menu>, then also inform the
    521   * menu that the popup is being hidden.
    522   * aLastPopup - optional popup to close last when hiding a chain of menus.
    523   *              If null, then all popups will be closed.
    524   */
    525  void HidePopup(Element* aPopup, HidePopupOptions,
    526                 Element* aLastPopup = nullptr);
    527 
    528  /*
    529   * Hide the popup of a <menu>.
    530   */
    531  void HideMenu(nsIContent* aMenu);
    532 
    533  /**
    534   * Hide a popup after a short delay. This is used when rolling over menu
    535   * items. This timer is stored in mCloseTimer. The timer may be cancelled and
    536   * the popup closed by calling KillMenuTimer.
    537   */
    538  void HidePopupAfterDelay(nsMenuPopupFrame* aPopup, int32_t aDelay);
    539 
    540  /**
    541   * Hide all of the popups from a given docshell. This should be called when
    542   * the document is hidden.
    543   */
    544  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    545  void HidePopupsInDocShell(nsIDocShellTreeItem* aDocShellToHide);
    546 
    547  /**
    548   * Check if any popups need to be repositioned or hidden after a style or
    549   * layout change. This will update, for example, any arrow type panels when
    550   * the anchor that is is pointing to has moved, resized or gone away.
    551   * Only those popups that pertain to the supplied aRefreshDriver are updated.
    552   */
    553  void UpdatePopupPositions(nsRefreshDriver* aRefreshDriver);
    554  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    555  void PaintPopups(nsRefreshDriver* aRefreshDriver);
    556 
    557  /**
    558   * Get the first nsMenuChainItem that is matched by the matching callback
    559   * function provided.
    560   */
    561  nsMenuChainItem* FirstMatchingPopup(
    562      mozilla::FunctionRef<bool(nsMenuChainItem*)> aMatcher) const;
    563 
    564  /**
    565   * Enable or disable anchor following on the popup if needed.
    566   */
    567  void UpdateFollowAnchor(nsMenuPopupFrame* aPopup);
    568 
    569  /**
    570   * Execute a menu command from the triggering event aEvent.
    571   *
    572   * aMenu - a menuitem to execute
    573   * aEvent - an nsXULMenuCommandEvent that contains all the info from the mouse
    574   *          event which triggered the menu to be executed, may not be null
    575   */
    576  MOZ_CAN_RUN_SCRIPT void ExecuteMenu(nsIContent* aMenu,
    577                                      nsXULMenuCommandEvent* aEvent);
    578 
    579  /**
    580   * If a native menu is open, and aItem is an item in the menu's subtree,
    581   * execute the item with the help of the native menu and close the menu.
    582   * Returns true if a native menu was open.
    583   */
    584  bool ActivateNativeMenuItem(nsIContent* aItem, mozilla::Modifiers aModifiers,
    585                              int16_t aButton, mozilla::ErrorResult& aRv);
    586 
    587  /**
    588   * Return true if the popup for the supplied content node is open.
    589   */
    590  bool IsPopupOpen(Element* aPopup);
    591 
    592  /**
    593   * Return the frame for the topmost open popup of a given type, or null if
    594   * no popup of that type is open. If aType is PopupType::Any, a menu of any
    595   * type is returned.
    596   */
    597  nsIFrame* GetTopPopup(PopupType aType);
    598 
    599  /**
    600   * Returns the topmost active menuitem that's currently visible, if any.
    601   */
    602  nsIContent* GetTopActiveMenuItemContent();
    603 
    604  /**
    605   * Return an array of all the open and visible popup frames for
    606   * menus, in order from top to bottom.
    607   * XXX should we always include native menu?
    608   */
    609  void GetVisiblePopups(nsTArray<nsMenuPopupFrame*>& aPopups,
    610                        bool aIncludeNativeMenu = false);
    611 
    612  /**
    613   * Get the node that last triggered a popup or tooltip in the document
    614   * aDocument. aDocument must be non-null and be a document contained within
    615   * the same window hierarchy as the popup to retrieve.
    616   */
    617  already_AddRefed<nsINode> GetLastTriggerPopupNode(
    618      mozilla::dom::Document* aDocument) {
    619    return GetLastTriggerNode(aDocument, false);
    620  }
    621 
    622  already_AddRefed<nsINode> GetLastTriggerTooltipNode(
    623      mozilla::dom::Document* aDocument) {
    624    return GetLastTriggerNode(aDocument, true);
    625  }
    626 
    627  /**
    628   * Return false if a popup may not be opened. This will return false if the
    629   * popup is already open, if the popup is in a content shell that is not
    630   * focused, or if it is a submenu of another menu that isn't open.
    631   */
    632  bool MayShowPopup(nsMenuPopupFrame* aFrame);
    633 
    634  /**
    635   * Called when a popup frame is destroyed. In this case, just remove the
    636   * item and later popups from the list. No point going through HidePopup as
    637   * the frames have gone away.
    638   */
    639  MOZ_CAN_RUN_SCRIPT void PopupDestroyed(nsMenuPopupFrame* aFrame);
    640 
    641  /**
    642   * Returns true if there is a context menu open. If aPopup is specified,
    643   * then the context menu must be later in the chain than aPopup. If aPopup
    644   * is null, returns true if any context menu at all is open.
    645   */
    646  bool HasContextMenu(nsMenuPopupFrame* aPopup);
    647 
    648  /**
    649   * Update the commands for the menus within the menu popup for a given
    650   * content node. aPopup should be a XUL menupopup element. This method
    651   * changes attributes on the children of aPopup, and deals only with the
    652   * content of the popup, not the frames.
    653   */
    654  void UpdateMenuItems(Element* aPopup);
    655 
    656  /**
    657   * Stop the timer which hides a popup after a delay, started by a previous
    658   * call to HidePopupAfterDelay. In addition, the popup awaiting to be hidden
    659   * is closed asynchronously.
    660   */
    661  void KillMenuTimer();
    662 
    663  /**
    664   * Cancel the timer which closes menus after delay, but only if the menu to
    665   * close is aMenuParent. When a submenu is opened, the user might move the
    666   * mouse over a sibling menuitem which would normally close the menu. This
    667   * menu is closed via a timer. However, if the user moves the mouse over the
    668   * submenu before the timer fires, we should instead cancel the timer. This
    669   * ensures that the user can move the mouse diagonally over a menu.
    670   */
    671  void CancelMenuTimer(nsMenuPopupFrame*);
    672 
    673  /**
    674   * Handles navigation for menu accelkeys. If aFrame is specified, then the
    675   * key is handled by that popup, otherwise if aFrame is null, the key is
    676   * handled by the active popup or menubar.
    677   */
    678  MOZ_CAN_RUN_SCRIPT bool HandleShortcutNavigation(
    679      mozilla::dom::KeyboardEvent& aKeyEvent, nsMenuPopupFrame* aFrame);
    680 
    681  /**
    682   * Handles cursor navigation within a menu. Returns true if the key has
    683   * been handled.
    684   */
    685  MOZ_CAN_RUN_SCRIPT bool HandleKeyboardNavigation(uint32_t aKeyCode);
    686 
    687  /**
    688   * Handle keyboard navigation within a menu popup specified by aFrame.
    689   * Returns true if the key was handled and other default handling
    690   * should not occur.
    691   */
    692  MOZ_CAN_RUN_SCRIPT bool HandleKeyboardNavigationInPopup(
    693      nsMenuPopupFrame* aFrame, nsNavigationDirection aDir) {
    694    return HandleKeyboardNavigationInPopup(nullptr, aFrame, aDir);
    695  }
    696 
    697  /**
    698   * Handles the keyboard event with keyCode value. Returns true if the event
    699   * has been handled.
    700   */
    701  MOZ_CAN_RUN_SCRIPT bool HandleKeyboardEventWithKeyCode(
    702      mozilla::dom::KeyboardEvent* aKeyEvent,
    703      nsMenuChainItem* aTopVisibleMenuItem);
    704 
    705  // Sets mIgnoreKeys of the Top Visible Menu Item
    706  nsresult UpdateIgnoreKeys(bool aIgnoreKeys);
    707 
    708  nsPopupState GetPopupState(mozilla::dom::Element* aPopupElement);
    709 
    710  mozilla::dom::Event* GetOpeningPopupEvent() const {
    711    return mPendingPopup->mEvent.get();
    712  }
    713 
    714  MOZ_CAN_RUN_SCRIPT nsresult KeyUp(mozilla::dom::KeyboardEvent* aKeyEvent);
    715  MOZ_CAN_RUN_SCRIPT nsresult KeyDown(mozilla::dom::KeyboardEvent* aKeyEvent);
    716  MOZ_CAN_RUN_SCRIPT nsresult KeyPress(mozilla::dom::KeyboardEvent* aKeyEvent);
    717 
    718 protected:
    719  nsXULPopupManager();
    720  ~nsXULPopupManager();
    721 
    722  // get the nsMenuPopupFrame, if any, for the given content node
    723  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    724  nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent,
    725                                            bool aShouldFlush);
    726 
    727  // Get the menu to start rolling up.
    728  nsMenuChainItem* GetRollupItem(RollupKind);
    729 
    730  // Return the topmost menu, skipping over invisible popups
    731  nsMenuChainItem* GetTopVisibleMenu() {
    732    return GetRollupItem(RollupKind::Menu);
    733  }
    734 
    735  // Add the chain item to the chain and update mPopups to point to it.
    736  void AddMenuChainItem(mozilla::UniquePtr<nsMenuChainItem>);
    737 
    738  // Removes the chain item from the chain and deletes it.
    739  void RemoveMenuChainItem(nsMenuChainItem*);
    740 
    741  // Hide all of the visible popups from the given list. This function can
    742  // cause style changes and frame destruction.
    743  MOZ_CAN_RUN_SCRIPT void HidePopupsInList(
    744      const nsTArray<nsMenuPopupFrame*>& aFrames);
    745 
    746  // Hide, but don't close, visible menus. Called before executing a menu item.
    747  // The caller promises to close the menus properly (with a call to HidePopup)
    748  // once the item has been executed.
    749  MOZ_CAN_RUN_SCRIPT void HideOpenMenusBeforeExecutingMenu(CloseMenuMode aMode);
    750 
    751  // callbacks for ShowPopup and HidePopup as events may be done asynchronously
    752  MOZ_CAN_RUN_SCRIPT void ShowPopupCallback(Element* aPopup,
    753                                            nsMenuPopupFrame* aPopupFrame,
    754                                            bool aIsContextMenu,
    755                                            bool aSelectFirstItem);
    756  MOZ_CAN_RUN_SCRIPT_BOUNDARY void HidePopupCallback(
    757      Element* aPopup, nsMenuPopupFrame* aPopupFrame, Element* aNextPopup,
    758      Element* aLastPopup, PopupType aPopupType, HidePopupOptions);
    759 
    760  /**
    761   * Trigger frame construction and reflow in the popup, fire a popupshowing
    762   * event on the popup and then open the popup.
    763   *
    764   * aPendingPopup - information about the popup to open
    765   * aIsContextMenu - true for context menus
    766   * aSelectFirstItem - true to select the first item in the menu
    767   * TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
    768   *
    769   * Return false if the popup is not going to be shown. This is mainly used for
    770   * the queue popup logic.
    771   */
    772  MOZ_CAN_RUN_SCRIPT_BOUNDARY bool BeginShowingPopup(
    773      const PendingPopup& aPendingPopup, bool aIsContextMenu,
    774      bool aSelectFirstItem);
    775 
    776  /**
    777   * Fire a popuphiding event and then hide the popup. This will be called
    778   * recursively if aNextPopup and aLastPopup are set in order to hide a chain
    779   * of open menus. If these are not set, only one popup is closed. However,
    780   * if the popup type indicates a menu, yet the next popup is not a menu,
    781   * then this ends the closing of popups. This allows a menulist inside a
    782   * non-menu to close up the menu but not close up the panel it is contained
    783   * within.
    784   *
    785   * The caller must keep a strong reference to aPopup, aNextPopup and
    786   * aLastPopup.
    787   *
    788   * aPopup - the popup to hide
    789   * aNextPopup - the next popup to hide
    790   * aLastPopup - the last popup in the chain to hide
    791   * aPresContext - nsPresContext for the popup's frame
    792   * aPopupType - the PopupType of the frame.
    793   * aOptions - the relevant options to hide the popup. Only a subset is looked
    794   * at.
    795   */
    796  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    797  void FirePopupHidingEvent(Element* aPopup, Element* aNextPopup,
    798                            Element* aLastPopup, nsPresContext* aPresContext,
    799                            PopupType aPopupType, HidePopupOptions aOptions);
    800 
    801  /**
    802   * Handle keyboard navigation within a menu popup specified by aItem.
    803   */
    804  MOZ_CAN_RUN_SCRIPT
    805  bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem,
    806                                       nsNavigationDirection aDir) {
    807    return HandleKeyboardNavigationInPopup(aItem, aItem->Frame(), aDir);
    808  }
    809 
    810 private:
    811  /**
    812   * Handle keyboard navigation within a menu popup aFrame. If aItem is
    813   * supplied, then it is expected to have a frame equal to aFrame.
    814   * If aItem is non-null, then the navigation may be redirected to
    815   * an open submenu if one exists. Returns true if the key was
    816   * handled and other default handling should not occur.
    817   */
    818  MOZ_CAN_RUN_SCRIPT bool HandleKeyboardNavigationInPopup(
    819      nsMenuChainItem* aItem, nsMenuPopupFrame* aFrame,
    820      nsNavigationDirection aDir);
    821 
    822 protected:
    823  already_AddRefed<nsINode> GetLastTriggerNode(
    824      mozilla::dom::Document* aDocument, bool aIsTooltip);
    825 
    826  /**
    827   * Fire a popupshowing event for aPopup.
    828   */
    829  MOZ_CAN_RUN_SCRIPT nsEventStatus FirePopupShowingEvent(
    830      const PendingPopup& aPendingPopup, nsPresContext* aPresContext);
    831 
    832  /**
    833   * Set mouse capturing for the current popup. This traps mouse clicks that
    834   * occur outside the popup so that it can be closed up. aOldPopup should be
    835   * set to the popup that was previously the current popup.
    836   */
    837  void SetCaptureState(nsIContent* aOldPopup);
    838 
    839  /**
    840   * Key event listeners are attached to the document containing the current
    841   * menu for menu and shortcut navigation. Only one listener is needed at a
    842   * time, stored in mKeyListener, so switch it only if the document changes.
    843   * Having menus in different documents is very rare, so the listeners will
    844   * usually only be attached when the first menu opens and removed when all
    845   * menus have closed.
    846   *
    847   * This is also used when only a menubar is active without any open menus,
    848   * so that keyboard navigation between menus on the menubar may be done.
    849   */
    850  // TODO: Convert UpdateKeyboardListeners() to MOZ_CAN_RUN_SCRIPT and get rid
    851  //       of the kungFuDeathGrip in it.
    852  MOZ_CAN_RUN_SCRIPT_BOUNDARY void UpdateKeyboardListeners();
    853 
    854  /*
    855   * Returns true if the docshell for aDoc is aExpected or a child of aExpected.
    856   */
    857  bool IsChildOfDocShell(mozilla::dom::Document* aDoc,
    858                         nsIDocShellTreeItem* aExpected);
    859 
    860  // Finds a chain item in mPopups.
    861  nsMenuChainItem* FindPopup(Element* aPopup) const;
    862 
    863  // Dimiss existing queueable shown popups before showing a non-queueable one.
    864  void DismissQueueableShownPopups();
    865 
    866  // the document the key event listener is attached to
    867  nsCOMPtr<mozilla::dom::EventTarget> mKeyListener;
    868 
    869  // widget that is currently listening to rollup events
    870  nsCOMPtr<nsIWidget> mWidget;
    871 
    872  // set to the currently active menu bar, if any
    873  mozilla::dom::XULMenuBarElement* mActiveMenuBar;
    874 
    875  // linked list of normal menus and panels. mPopups points to the innermost
    876  // popup, which keeps alive all their parents.
    877  mozilla::UniquePtr<nsMenuChainItem> mPopups;
    878 
    879  // timer used for HidePopupAfterDelay
    880  nsCOMPtr<nsITimer> mCloseTimer;
    881  nsMenuPopupFrame* mTimerMenu = nullptr;
    882 
    883  // Information about the popup that is currently firing a popupshowing event.
    884  const PendingPopup* mPendingPopup;
    885 
    886  // If a popup is displayed as a native menu, this is non-null while the
    887  // native menu is open.
    888  // mNativeMenu has a strong reference to the menupopup nsIContent.
    889  RefPtr<mozilla::widget::NativeMenu> mNativeMenu;
    890 
    891  // If the currently open native menu activated an item, this is the item's
    892  // close menu mode. Nothing() if mNativeMenu is null or if no item was
    893  // activated.
    894  mozilla::Maybe<CloseMenuMode> mNativeMenuActivatedItemCloseMenuMode;
    895 
    896  // If a popup is displayed as a native menu, this map contains the popup state
    897  // for any of its non-closed submenus. This state cannot be stored on the
    898  // submenus' nsMenuPopupFrames, because we usually don't generate frames for
    899  // the contents of native menus.
    900  // If a submenu is not present in this map, it means it's closed.
    901  // This map is empty if mNativeMenu is null.
    902  nsTHashMap<RefPtr<mozilla::dom::Element>, nsPopupState>
    903      mNativeMenuSubmenuStates;
    904 
    905  // A queue for "queuable" popups.
    906  RefPtr<PopupQueue> mPopupQueue;
    907 };
    908 
    909 #endif