nsXULPopupManager.h 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /**
  6. * The XUL Popup Manager keeps track of all open popups.
  7. */
  8. #ifndef nsXULPopupManager_h__
  9. #define nsXULPopupManager_h__
  10. #include "mozilla/Logging.h"
  11. #include "nsIContent.h"
  12. #include "nsIRollupListener.h"
  13. #include "nsIDOMEventListener.h"
  14. #include "nsPoint.h"
  15. #include "nsCOMPtr.h"
  16. #include "nsTArray.h"
  17. #include "nsIObserver.h"
  18. #include "nsITimer.h"
  19. #include "nsIReflowCallback.h"
  20. #include "nsThreadUtils.h"
  21. #include "nsStyleConsts.h"
  22. #include "nsWidgetInitData.h"
  23. #include "mozilla/Attributes.h"
  24. #include "Units.h"
  25. // X.h defines KeyPress
  26. #ifdef KeyPress
  27. #undef KeyPress
  28. #endif
  29. /**
  30. * There are two types that are used:
  31. * - dismissable popups such as menus, which should close up when there is a
  32. * click outside the popup. In this situation, the entire chain of menus
  33. * above should also be closed.
  34. * - panels, which stay open until a request is made to close them. This
  35. * type is used by tooltips.
  36. *
  37. * When a new popup is opened, it is appended to the popup chain, stored in a
  38. * linked list in mPopups for dismissable menus and panels or mNoHidePanels
  39. * for tooltips and panels with noautohide="true".
  40. * Popups are stored in this list linked from newest to oldest. When a click
  41. * occurs outside one of the open dismissable popups, the chain is closed by
  42. * calling Rollup.
  43. */
  44. class nsContainerFrame;
  45. class nsMenuFrame;
  46. class nsMenuPopupFrame;
  47. class nsMenuBarFrame;
  48. class nsMenuParent;
  49. class nsIDOMKeyEvent;
  50. class nsIDocShellTreeItem;
  51. class nsPIDOMWindowOuter;
  52. // when a menu command is executed, the closemenu attribute may be used
  53. // to define how the menu should be closed up
  54. enum CloseMenuMode {
  55. CloseMenuMode_Auto, // close up the chain of menus, default value
  56. CloseMenuMode_None, // don't close up any menus
  57. CloseMenuMode_Single // close up only the menu the command is inside
  58. };
  59. /**
  60. * nsNavigationDirection: an enum expressing navigation through the menus in
  61. * terms which are independent of the directionality of the chrome. The
  62. * terminology, derived from XSL-FO and CSS3 (e.g.
  63. * http://www.w3.org/TR/css3-text/#TextLayout), is BASE (Before, After, Start,
  64. * End), with the addition of First and Last (mapped to Home and End
  65. * respectively).
  66. *
  67. * In languages such as English where the inline progression is left-to-right
  68. * and the block progression is top-to-bottom (lr-tb), these terms will map out
  69. * as in the following diagram
  70. *
  71. * --- inline progression --->
  72. *
  73. * First |
  74. * ... |
  75. * Before |
  76. * +--------+ block
  77. * Start | | End progression
  78. * +--------+ |
  79. * After |
  80. * ... |
  81. * Last V
  82. *
  83. */
  84. enum nsNavigationDirection {
  85. eNavigationDirection_Last,
  86. eNavigationDirection_First,
  87. eNavigationDirection_Start,
  88. eNavigationDirection_Before,
  89. eNavigationDirection_End,
  90. eNavigationDirection_After
  91. };
  92. enum nsIgnoreKeys {
  93. eIgnoreKeys_False,
  94. eIgnoreKeys_True,
  95. eIgnoreKeys_Shortcuts,
  96. };
  97. #define NS_DIRECTION_IS_INLINE(dir) (dir == eNavigationDirection_Start || \
  98. dir == eNavigationDirection_End)
  99. #define NS_DIRECTION_IS_BLOCK(dir) (dir == eNavigationDirection_Before || \
  100. dir == eNavigationDirection_After)
  101. #define NS_DIRECTION_IS_BLOCK_TO_EDGE(dir) (dir == eNavigationDirection_First || \
  102. dir == eNavigationDirection_Last)
  103. static_assert(NS_STYLE_DIRECTION_LTR == 0 && NS_STYLE_DIRECTION_RTL == 1,
  104. "Left to Right should be 0 and Right to Left should be 1");
  105. /**
  106. * DirectionFromKeyCodeTable: two arrays, the first for left-to-right and the
  107. * other for right-to-left, that map keycodes to values of
  108. * nsNavigationDirection.
  109. */
  110. extern const nsNavigationDirection DirectionFromKeyCodeTable[2][6];
  111. #define NS_DIRECTION_FROM_KEY_CODE(frame, keycode) \
  112. (DirectionFromKeyCodeTable[frame->StyleVisibility()->mDirection] \
  113. [keycode - nsIDOMKeyEvent::DOM_VK_END])
  114. // nsMenuChainItem holds info about an open popup. Items are stored in a
  115. // doubly linked list. Note that the linked list is stored beginning from
  116. // the lowest child in a chain of menus, as this is the active submenu.
  117. class nsMenuChainItem
  118. {
  119. private:
  120. nsMenuPopupFrame* mFrame; // the popup frame
  121. nsPopupType mPopupType; // the popup type of the frame
  122. bool mIsContext; // true for context menus
  123. bool mOnMenuBar; // true if the menu is on a menu bar
  124. nsIgnoreKeys mIgnoreKeys; // indicates how keyboard listeners should be used
  125. nsMenuChainItem* mParent;
  126. nsMenuChainItem* mChild;
  127. public:
  128. nsMenuChainItem(nsMenuPopupFrame* aFrame, bool aIsContext, nsPopupType aPopupType)
  129. : mFrame(aFrame),
  130. mPopupType(aPopupType),
  131. mIsContext(aIsContext),
  132. mOnMenuBar(false),
  133. mIgnoreKeys(eIgnoreKeys_False),
  134. mParent(nullptr),
  135. mChild(nullptr)
  136. {
  137. NS_ASSERTION(aFrame, "null frame passed to nsMenuChainItem constructor");
  138. MOZ_COUNT_CTOR(nsMenuChainItem);
  139. }
  140. ~nsMenuChainItem()
  141. {
  142. MOZ_COUNT_DTOR(nsMenuChainItem);
  143. }
  144. nsIContent* Content();
  145. nsMenuPopupFrame* Frame() { return mFrame; }
  146. nsPopupType PopupType() { return mPopupType; }
  147. bool IsMenu() { return mPopupType == ePopupTypeMenu; }
  148. bool IsContextMenu() { return mIsContext; }
  149. nsIgnoreKeys IgnoreKeys() { return mIgnoreKeys; }
  150. void SetIgnoreKeys(nsIgnoreKeys aIgnoreKeys) { mIgnoreKeys = aIgnoreKeys; }
  151. bool IsOnMenuBar() { return mOnMenuBar; }
  152. void SetOnMenuBar(bool aOnMenuBar) { mOnMenuBar = aOnMenuBar; }
  153. nsMenuChainItem* GetParent() { return mParent; }
  154. nsMenuChainItem* GetChild() { return mChild; }
  155. // set the parent of this item to aParent, also changing the parent
  156. // to have this as a child.
  157. void SetParent(nsMenuChainItem* aParent);
  158. // removes an item from the chain. The root pointer must be supplied in case
  159. // the item is the first item in the chain in which case the pointer will be
  160. // set to the next item, or null if there isn't another item. After detaching,
  161. // this item will not have a parent or a child.
  162. void Detach(nsMenuChainItem** aRoot);
  163. };
  164. // this class is used for dispatching popupshowing events asynchronously.
  165. class nsXULPopupShowingEvent : public mozilla::Runnable
  166. {
  167. public:
  168. nsXULPopupShowingEvent(nsIContent *aPopup,
  169. bool aIsContextMenu,
  170. bool aSelectFirstItem)
  171. : mPopup(aPopup),
  172. mIsContextMenu(aIsContextMenu),
  173. mSelectFirstItem(aSelectFirstItem)
  174. {
  175. NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupShowingEvent constructor");
  176. }
  177. NS_IMETHOD Run() override;
  178. private:
  179. nsCOMPtr<nsIContent> mPopup;
  180. bool mIsContextMenu;
  181. bool mSelectFirstItem;
  182. };
  183. // this class is used for dispatching popuphiding events asynchronously.
  184. class nsXULPopupHidingEvent : public mozilla::Runnable
  185. {
  186. public:
  187. nsXULPopupHidingEvent(nsIContent *aPopup,
  188. nsIContent* aNextPopup,
  189. nsIContent* aLastPopup,
  190. nsPopupType aPopupType,
  191. bool aDeselectMenu,
  192. bool aIsCancel)
  193. : mPopup(aPopup),
  194. mNextPopup(aNextPopup),
  195. mLastPopup(aLastPopup),
  196. mPopupType(aPopupType),
  197. mDeselectMenu(aDeselectMenu),
  198. mIsRollup(aIsCancel)
  199. {
  200. NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupHidingEvent constructor");
  201. // aNextPopup and aLastPopup may be null
  202. }
  203. NS_IMETHOD Run() override;
  204. private:
  205. nsCOMPtr<nsIContent> mPopup;
  206. nsCOMPtr<nsIContent> mNextPopup;
  207. nsCOMPtr<nsIContent> mLastPopup;
  208. nsPopupType mPopupType;
  209. bool mDeselectMenu;
  210. bool mIsRollup;
  211. };
  212. // this class is used for dispatching popuppositioned events asynchronously.
  213. class nsXULPopupPositionedEvent : public mozilla::Runnable
  214. {
  215. public:
  216. explicit nsXULPopupPositionedEvent(nsIContent *aPopup,
  217. bool aIsContextMenu,
  218. bool aSelectFirstItem)
  219. : mPopup(aPopup)
  220. , mIsContextMenu(aIsContextMenu)
  221. , mSelectFirstItem(aSelectFirstItem)
  222. {
  223. NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupShowingEvent constructor");
  224. }
  225. NS_IMETHOD Run() override;
  226. // Asynchronously dispatch a popuppositioned event at aPopup if this is a
  227. // panel that should receieve such events. Return true if the event was sent.
  228. static bool DispatchIfNeeded(nsIContent *aPopup,
  229. bool aIsContextMenu,
  230. bool aSelectFirstItem);
  231. private:
  232. nsCOMPtr<nsIContent> mPopup;
  233. bool mIsContextMenu;
  234. bool mSelectFirstItem;
  235. };
  236. // this class is used for dispatching menu command events asynchronously.
  237. class nsXULMenuCommandEvent : public mozilla::Runnable
  238. {
  239. public:
  240. nsXULMenuCommandEvent(nsIContent *aMenu,
  241. bool aIsTrusted,
  242. bool aShift,
  243. bool aControl,
  244. bool aAlt,
  245. bool aMeta,
  246. bool aUserInput,
  247. bool aFlipChecked)
  248. : mMenu(aMenu),
  249. mIsTrusted(aIsTrusted),
  250. mShift(aShift),
  251. mControl(aControl),
  252. mAlt(aAlt),
  253. mMeta(aMeta),
  254. mUserInput(aUserInput),
  255. mFlipChecked(aFlipChecked),
  256. mCloseMenuMode(CloseMenuMode_Auto)
  257. {
  258. NS_ASSERTION(aMenu, "null menu supplied to nsXULMenuCommandEvent constructor");
  259. }
  260. NS_IMETHOD Run() override;
  261. void SetCloseMenuMode(CloseMenuMode aCloseMenuMode) { mCloseMenuMode = aCloseMenuMode; }
  262. private:
  263. nsCOMPtr<nsIContent> mMenu;
  264. bool mIsTrusted;
  265. bool mShift;
  266. bool mControl;
  267. bool mAlt;
  268. bool mMeta;
  269. bool mUserInput;
  270. bool mFlipChecked;
  271. CloseMenuMode mCloseMenuMode;
  272. };
  273. class nsXULPopupManager final : public nsIDOMEventListener,
  274. public nsIRollupListener,
  275. public nsITimerCallback,
  276. public nsIObserver
  277. {
  278. public:
  279. friend class nsXULPopupShowingEvent;
  280. friend class nsXULPopupHidingEvent;
  281. friend class nsXULPopupPositionedEvent;
  282. friend class nsXULMenuCommandEvent;
  283. friend class TransitionEnder;
  284. NS_DECL_ISUPPORTS
  285. NS_DECL_NSIOBSERVER
  286. NS_DECL_NSITIMERCALLBACK
  287. NS_DECL_NSIDOMEVENTLISTENER
  288. // nsIRollupListener
  289. virtual bool Rollup(uint32_t aCount, bool aFlush,
  290. const nsIntPoint* pos, nsIContent** aLastRolledUp) override;
  291. virtual bool ShouldRollupOnMouseWheelEvent() override;
  292. virtual bool ShouldConsumeOnMouseWheelEvent() override;
  293. virtual bool ShouldRollupOnMouseActivate() override;
  294. virtual uint32_t GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain) override;
  295. virtual void NotifyGeometryChange() override {}
  296. virtual nsIWidget* GetRollupWidget() override;
  297. static nsXULPopupManager* sInstance;
  298. // initialize and shutdown methods called by nsLayoutStatics
  299. static nsresult Init();
  300. static void Shutdown();
  301. // returns a weak reference to the popup manager instance, could return null
  302. // if a popup manager could not be allocated
  303. static nsXULPopupManager* GetInstance();
  304. // This should be called when a window is moved or resized to adjust the
  305. // popups accordingly.
  306. void AdjustPopupsOnWindowChange(nsPIDOMWindowOuter* aWindow);
  307. void AdjustPopupsOnWindowChange(nsIPresShell* aPresShell);
  308. // given a menu frame, find the prevous or next menu frame. If aPopup is
  309. // true then navigate a menupopup, from one item on the menu to the previous
  310. // or next one. This is used for cursor navigation between items in a popup
  311. // menu. If aIsPopup is false, the navigation is on a menubar, so navigate
  312. // between menus on the menubar. This is used for left/right cursor navigation.
  313. //
  314. // Items that are not valid, such as non-menu or non-menuitem elements are
  315. // skipped, and the next or previous item after that is checked.
  316. //
  317. // If aStart is null, the first valid item is retrieved by GetNextMenuItem
  318. // and the last valid item is retrieved by GetPreviousMenuItem.
  319. //
  320. // Both methods will loop around the beginning or end if needed.
  321. //
  322. // aParent - the parent menubar or menupopup
  323. // aStart - the menu/menuitem to start navigation from. GetPreviousMenuItem
  324. // returns the item before it, while GetNextMenuItem returns the
  325. // item after it.
  326. // aIsPopup - true for menupopups, false for menubars
  327. static nsMenuFrame* GetPreviousMenuItem(nsContainerFrame* aParent,
  328. nsMenuFrame* aStart,
  329. bool aIsPopup);
  330. static nsMenuFrame* GetNextMenuItem(nsContainerFrame* aParent,
  331. nsMenuFrame* aStart,
  332. bool aIsPopup);
  333. // returns true if the menu item aContent is a valid menuitem which may
  334. // be navigated to. aIsPopup should be true for items on a popup, or false
  335. // for items on a menubar.
  336. static bool IsValidMenuItem(nsIContent* aContent, bool aOnPopup);
  337. // inform the popup manager that a menu bar has been activated or deactivated,
  338. // either because one of its menus has opened or closed, or that the menubar
  339. // has been focused such that its menus may be navigated with the keyboard.
  340. // aActivate should be true when the menubar should be focused, and false
  341. // when the active menu bar should be defocused. In the latter case, if
  342. // aMenuBar isn't currently active, yet another menu bar is, that menu bar
  343. // will remain active.
  344. void SetActiveMenuBar(nsMenuBarFrame* aMenuBar, bool aActivate);
  345. // retrieve the node and offset of the last mouse event used to open a
  346. // context menu. This information is determined from the rangeParent and
  347. // the rangeOffset of the event supplied to ShowPopup or ShowPopupAtScreen.
  348. // This is used by the implementation of nsIDOMXULDocument::GetPopupRangeParent
  349. // and nsIDOMXULDocument::GetPopupRangeOffset.
  350. void GetMouseLocation(nsIDOMNode** aNode, int32_t* aOffset);
  351. /**
  352. * Open a <menu> given its content node. If aSelectFirstItem is
  353. * set to true, the first item on the menu will automatically be
  354. * selected. If aAsynchronous is true, the event will be dispatched
  355. * asynchronously. This should be true when called from frame code.
  356. */
  357. void ShowMenu(nsIContent *aMenu, bool aSelectFirstItem, bool aAsynchronous);
  358. /**
  359. * Open a popup, either anchored or unanchored. If aSelectFirstItem is
  360. * true, then the first item in the menu is selected. The arguments are
  361. * similar to those for nsIPopupBoxObject::OpenPopup.
  362. *
  363. * aTriggerEvent should be the event that triggered the event. This is used
  364. * to determine the coordinates and trigger node for the popup. This may be
  365. * null if the popup was not triggered by an event.
  366. *
  367. * This fires the popupshowing event synchronously.
  368. */
  369. void ShowPopup(nsIContent* aPopup,
  370. nsIContent* aAnchorContent,
  371. const nsAString& aPosition,
  372. int32_t aXPos, int32_t aYPos,
  373. bool aIsContextMenu,
  374. bool aAttributesOverride,
  375. bool aSelectFirstItem,
  376. nsIDOMEvent* aTriggerEvent);
  377. /**
  378. * Open a popup at a specific screen position specified by aXPos and aYPos,
  379. * measured in CSS pixels.
  380. *
  381. * This fires the popupshowing event synchronously.
  382. *
  383. * If aIsContextMenu is true, the popup is positioned at a slight
  384. * offset from aXPos/aYPos to ensure that it is not under the mouse
  385. * cursor.
  386. */
  387. void ShowPopupAtScreen(nsIContent* aPopup,
  388. int32_t aXPos, int32_t aYPos,
  389. bool aIsContextMenu,
  390. nsIDOMEvent* aTriggerEvent);
  391. /* Open a popup anchored at a screen rectangle specified by aRect.
  392. * The remaining arguments are similar to ShowPopup.
  393. */
  394. void ShowPopupAtScreenRect(nsIContent* aPopup,
  395. const nsAString& aPosition,
  396. const nsIntRect& aRect,
  397. bool aIsContextMenu,
  398. bool aAttributesOverride,
  399. nsIDOMEvent* aTriggerEvent);
  400. /**
  401. * Open a tooltip at a specific screen position specified by aXPos and aYPos,
  402. * measured in CSS pixels.
  403. *
  404. * This fires the popupshowing event synchronously.
  405. */
  406. void ShowTooltipAtScreen(nsIContent* aPopup,
  407. nsIContent* aTriggerContent,
  408. int32_t aXPos, int32_t aYPos);
  409. /**
  410. * This method is provided only for compatibility with an older popup API.
  411. * New code should not call this function and should call ShowPopup instead.
  412. *
  413. * This fires the popupshowing event synchronously.
  414. */
  415. void ShowPopupWithAnchorAlign(nsIContent* aPopup,
  416. nsIContent* aAnchorContent,
  417. nsAString& aAnchor,
  418. nsAString& aAlign,
  419. int32_t aXPos, int32_t aYPos,
  420. bool aIsContextMenu);
  421. /*
  422. * Hide a popup aPopup. If the popup is in a <menu>, then also inform the
  423. * menu that the popup is being hidden.
  424. *
  425. * aHideChain - true if the entire chain of menus should be closed. If false,
  426. * only this popup is closed.
  427. * aDeselectMenu - true if the parent <menu> of the popup should be deselected.
  428. * This will be false when the menu is closed by pressing the
  429. * Escape key.
  430. * aAsynchronous - true if the first popuphiding event should be sent
  431. * asynchrously. This should be true if HidePopup is called
  432. * from a frame.
  433. * aIsCancel - true if this popup is hiding due to being cancelled.
  434. * aLastPopup - optional popup to close last when hiding a chain of menus.
  435. * If null, then all popups will be closed.
  436. */
  437. void HidePopup(nsIContent* aPopup,
  438. bool aHideChain,
  439. bool aDeselectMenu,
  440. bool aAsynchronous,
  441. bool aIsCancel,
  442. nsIContent* aLastPopup = nullptr);
  443. /**
  444. * Hide the popup aFrame. This method is called by the view manager when the
  445. * close button is pressed.
  446. */
  447. void HidePopup(nsIFrame* aFrame);
  448. /**
  449. * Hide a popup after a short delay. This is used when rolling over menu items.
  450. * This timer is stored in mCloseTimer. The timer may be cancelled and the popup
  451. * closed by calling KillMenuTimer.
  452. */
  453. void HidePopupAfterDelay(nsMenuPopupFrame* aPopup);
  454. /**
  455. * Hide all of the popups from a given docshell. This should be called when the
  456. * document is hidden.
  457. */
  458. void HidePopupsInDocShell(nsIDocShellTreeItem* aDocShellToHide);
  459. /**
  460. * Enable or disable the dynamic noautohide state of a panel.
  461. *
  462. * aPanel - the panel whose state is to change
  463. * aShouldRollup - whether the panel is no longer noautohide
  464. */
  465. void EnableRollup(nsIContent* aPopup, bool aShouldRollup);
  466. /**
  467. * Execute a menu command from the triggering event aEvent.
  468. *
  469. * aMenu - a menuitem to execute
  470. * aEvent - an nsXULMenuCommandEvent that contains all the info from the mouse
  471. * event which triggered the menu to be executed, may not be null
  472. */
  473. void ExecuteMenu(nsIContent* aMenu, nsXULMenuCommandEvent* aEvent);
  474. /**
  475. * Return true if the popup for the supplied content node is open.
  476. */
  477. bool IsPopupOpen(nsIContent* aPopup);
  478. /**
  479. * Return true if the popup for the supplied menu parent is open.
  480. */
  481. bool IsPopupOpenForMenuParent(nsMenuParent* aMenuParent);
  482. /**
  483. * Return the frame for the topmost open popup of a given type, or null if
  484. * no popup of that type is open. If aType is ePopupTypeAny, a menu of any
  485. * type is returned, except for popups in the mNoHidePanels list.
  486. */
  487. nsIFrame* GetTopPopup(nsPopupType aType);
  488. /**
  489. * Return an array of all the open and visible popup frames for
  490. * menus, in order from top to bottom.
  491. */
  492. void GetVisiblePopups(nsTArray<nsIFrame *>& aPopups);
  493. /**
  494. * Get the node that last triggered a popup or tooltip in the document
  495. * aDocument. aDocument must be non-null and be a document contained within
  496. * the same window hierarchy as the popup to retrieve.
  497. */
  498. already_AddRefed<nsIDOMNode> GetLastTriggerPopupNode(nsIDocument* aDocument)
  499. {
  500. return GetLastTriggerNode(aDocument, false);
  501. }
  502. already_AddRefed<nsIDOMNode> GetLastTriggerTooltipNode(nsIDocument* aDocument)
  503. {
  504. return GetLastTriggerNode(aDocument, true);
  505. }
  506. /**
  507. * Return false if a popup may not be opened. This will return false if the
  508. * popup is already open, if the popup is in a content shell that is not
  509. * focused, or if it is a submenu of another menu that isn't open.
  510. */
  511. bool MayShowPopup(nsMenuPopupFrame* aFrame);
  512. /**
  513. * Indicate that the popup associated with aView has been moved to the
  514. * specified screen coordiates.
  515. */
  516. void PopupMoved(nsIFrame* aFrame, nsIntPoint aPoint);
  517. /**
  518. * Indicate that the popup associated with aView has been resized to the
  519. * given device pixel size aSize.
  520. */
  521. void PopupResized(nsIFrame* aFrame, mozilla::LayoutDeviceIntSize aSize);
  522. /**
  523. * Called when a popup frame is destroyed. In this case, just remove the
  524. * item and later popups from the list. No point going through HidePopup as
  525. * the frames have gone away.
  526. */
  527. void PopupDestroyed(nsMenuPopupFrame* aFrame);
  528. /**
  529. * Returns true if there is a context menu open. If aPopup is specified,
  530. * then the context menu must be later in the chain than aPopup. If aPopup
  531. * is null, returns true if any context menu at all is open.
  532. */
  533. bool HasContextMenu(nsMenuPopupFrame* aPopup);
  534. /**
  535. * Update the commands for the menus within the menu popup for a given
  536. * content node. aPopup should be a XUL menupopup element. This method
  537. * changes attributes on the children of aPopup, and deals only with the
  538. * content of the popup, not the frames.
  539. */
  540. void UpdateMenuItems(nsIContent* aPopup);
  541. /**
  542. * Stop the timer which hides a popup after a delay, started by a previous
  543. * call to HidePopupAfterDelay. In addition, the popup awaiting to be hidden
  544. * is closed asynchronously.
  545. */
  546. void KillMenuTimer();
  547. /**
  548. * Cancel the timer which closes menus after delay, but only if the menu to
  549. * close is aMenuParent. When a submenu is opened, the user might move the
  550. * mouse over a sibling menuitem which would normally close the menu. This
  551. * menu is closed via a timer. However, if the user moves the mouse over the
  552. * submenu before the timer fires, we should instead cancel the timer. This
  553. * ensures that the user can move the mouse diagonally over a menu.
  554. */
  555. void CancelMenuTimer(nsMenuParent* aMenuParent);
  556. /**
  557. * Handles navigation for menu accelkeys. If aFrame is specified, then the
  558. * key is handled by that popup, otherwise if aFrame is null, the key is
  559. * handled by the active popup or menubar.
  560. */
  561. bool HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent,
  562. nsMenuPopupFrame* aFrame);
  563. /**
  564. * Handles cursor navigation within a menu. Returns true if the key has
  565. * been handled.
  566. */
  567. bool HandleKeyboardNavigation(uint32_t aKeyCode);
  568. /**
  569. * Handle keyboard navigation within a menu popup specified by aFrame.
  570. * Returns true if the key was handled and other default handling
  571. * should not occur.
  572. */
  573. bool HandleKeyboardNavigationInPopup(nsMenuPopupFrame* aFrame,
  574. nsNavigationDirection aDir)
  575. {
  576. return HandleKeyboardNavigationInPopup(nullptr, aFrame, aDir);
  577. }
  578. /**
  579. * Handles the keyboard event with keyCode value. Returns true if the event
  580. * has been handled.
  581. */
  582. bool HandleKeyboardEventWithKeyCode(nsIDOMKeyEvent* aKeyEvent,
  583. nsMenuChainItem* aTopVisibleMenuItem);
  584. nsresult KeyUp(nsIDOMKeyEvent* aKeyEvent);
  585. nsresult KeyDown(nsIDOMKeyEvent* aKeyEvent);
  586. nsresult KeyPress(nsIDOMKeyEvent* aKeyEvent);
  587. protected:
  588. nsXULPopupManager();
  589. ~nsXULPopupManager();
  590. // get the nsMenuPopupFrame, if any, for the given content node
  591. nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent, bool aShouldFlush);
  592. // return the topmost menu, skipping over invisible popups
  593. nsMenuChainItem* GetTopVisibleMenu();
  594. // Hide all of the visible popups from the given list. This function can
  595. // cause style changes and frame destruction.
  596. void HidePopupsInList(const nsTArray<nsMenuPopupFrame *> &aFrames);
  597. // set the event that was used to trigger the popup, or null to clear the
  598. // event details. aTriggerContent will be set to the target of the event.
  599. void InitTriggerEvent(nsIDOMEvent* aEvent, nsIContent* aPopup, nsIContent** aTriggerContent);
  600. // callbacks for ShowPopup and HidePopup as events may be done asynchronously
  601. void ShowPopupCallback(nsIContent* aPopup,
  602. nsMenuPopupFrame* aPopupFrame,
  603. bool aIsContextMenu,
  604. bool aSelectFirstItem);
  605. void HidePopupCallback(nsIContent* aPopup,
  606. nsMenuPopupFrame* aPopupFrame,
  607. nsIContent* aNextPopup,
  608. nsIContent* aLastPopup,
  609. nsPopupType aPopupType,
  610. bool aDeselectMenu);
  611. /**
  612. * Fire a popupshowing event on the popup and then open the popup.
  613. *
  614. * aPopup - the popup to open
  615. * aIsContextMenu - true for context menus
  616. * aSelectFirstItem - true to select the first item in the menu
  617. */
  618. void FirePopupShowingEvent(nsIContent* aPopup,
  619. bool aIsContextMenu,
  620. bool aSelectFirstItem);
  621. /**
  622. * Fire a popuphiding event and then hide the popup. This will be called
  623. * recursively if aNextPopup and aLastPopup are set in order to hide a chain
  624. * of open menus. If these are not set, only one popup is closed. However,
  625. * if the popup type indicates a menu, yet the next popup is not a menu,
  626. * then this ends the closing of popups. This allows a menulist inside a
  627. * non-menu to close up the menu but not close up the panel it is contained
  628. * within.
  629. *
  630. * The caller must keep a strong reference to aPopup, aNextPopup and aLastPopup.
  631. *
  632. * aPopup - the popup to hide
  633. * aNextPopup - the next popup to hide
  634. * aLastPopup - the last popup in the chain to hide
  635. * aPresContext - nsPresContext for the popup's frame
  636. * aPopupType - the PopupType of the frame.
  637. * aDeselectMenu - true to unhighlight the menu when hiding it
  638. * aIsCancel - true if this popup is hiding due to being cancelled.
  639. */
  640. void FirePopupHidingEvent(nsIContent* aPopup,
  641. nsIContent* aNextPopup,
  642. nsIContent* aLastPopup,
  643. nsPresContext *aPresContext,
  644. nsPopupType aPopupType,
  645. bool aDeselectMenu,
  646. bool aIsCancel);
  647. /**
  648. * Handle keyboard navigation within a menu popup specified by aItem.
  649. */
  650. bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem,
  651. nsNavigationDirection aDir)
  652. {
  653. return HandleKeyboardNavigationInPopup(aItem, aItem->Frame(), aDir);
  654. }
  655. private:
  656. /**
  657. * Handle keyboard navigation within a menu popup aFrame. If aItem is
  658. * supplied, then it is expected to have a frame equal to aFrame.
  659. * If aItem is non-null, then the navigation may be redirected to
  660. * an open submenu if one exists. Returns true if the key was
  661. * handled and other default handling should not occur.
  662. */
  663. bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem,
  664. nsMenuPopupFrame* aFrame,
  665. nsNavigationDirection aDir);
  666. protected:
  667. already_AddRefed<nsIDOMNode> GetLastTriggerNode(nsIDocument* aDocument, bool aIsTooltip);
  668. /**
  669. * Set mouse capturing for the current popup. This traps mouse clicks that
  670. * occur outside the popup so that it can be closed up. aOldPopup should be
  671. * set to the popup that was previously the current popup.
  672. */
  673. void SetCaptureState(nsIContent *aOldPopup);
  674. /**
  675. * Key event listeners are attached to the document containing the current
  676. * menu for menu and shortcut navigation. Only one listener is needed at a
  677. * time, stored in mKeyListener, so switch it only if the document changes.
  678. * Having menus in different documents is very rare, so the listeners will
  679. * usually only be attached when the first menu opens and removed when all
  680. * menus have closed.
  681. *
  682. * This is also used when only a menubar is active without any open menus,
  683. * so that keyboard navigation between menus on the menubar may be done.
  684. */
  685. void UpdateKeyboardListeners();
  686. /*
  687. * Returns true if the docshell for aDoc is aExpected or a child of aExpected.
  688. */
  689. bool IsChildOfDocShell(nsIDocument* aDoc, nsIDocShellTreeItem* aExpected);
  690. // the document the key event listener is attached to
  691. nsCOMPtr<mozilla::dom::EventTarget> mKeyListener;
  692. // widget that is currently listening to rollup events
  693. nsCOMPtr<nsIWidget> mWidget;
  694. // range parent and offset set in SetTriggerEvent
  695. nsCOMPtr<nsIDOMNode> mRangeParent;
  696. int32_t mRangeOffset;
  697. // Device pixels relative to the showing popup's presshell's
  698. // root prescontext's root frame.
  699. mozilla::LayoutDeviceIntPoint mCachedMousePoint;
  700. // cached modifiers
  701. mozilla::Modifiers mCachedModifiers;
  702. // set to the currently active menu bar, if any
  703. nsMenuBarFrame* mActiveMenuBar;
  704. // linked list of normal menus and panels.
  705. nsMenuChainItem* mPopups;
  706. // linked list of noautohide panels and tooltips.
  707. nsMenuChainItem* mNoHidePanels;
  708. // timer used for HidePopupAfterDelay
  709. nsCOMPtr<nsITimer> mCloseTimer;
  710. // a popup that is waiting on the timer
  711. nsMenuPopupFrame* mTimerMenu;
  712. // the popup that is currently being opened, stored only during the
  713. // popupshowing event
  714. nsCOMPtr<nsIContent> mOpeningPopup;
  715. // If true, all popups won't hide automatically on blur
  716. static bool sDevtoolsDisableAutoHide;
  717. };
  718. #endif