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