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