|
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 "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" |
|
27 |
|
28 // X.h defines KeyPress |
|
29 #ifdef KeyPress |
|
30 #undef KeyPress |
|
31 #endif |
|
32 |
|
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 */ |
|
48 |
|
49 class nsMenuFrame; |
|
50 class nsMenuPopupFrame; |
|
51 class nsMenuBarFrame; |
|
52 class nsMenuParent; |
|
53 class nsIDOMKeyEvent; |
|
54 class nsIDocShellTreeItem; |
|
55 class nsPIDOMWindow; |
|
56 |
|
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 }; |
|
64 |
|
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 */ |
|
90 |
|
91 enum nsNavigationDirection { |
|
92 eNavigationDirection_Last, |
|
93 eNavigationDirection_First, |
|
94 eNavigationDirection_Start, |
|
95 eNavigationDirection_Before, |
|
96 eNavigationDirection_End, |
|
97 eNavigationDirection_After |
|
98 }; |
|
99 |
|
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) |
|
106 |
|
107 PR_STATIC_ASSERT(NS_STYLE_DIRECTION_LTR == 0 && NS_STYLE_DIRECTION_RTL == 1); |
|
108 |
|
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]; |
|
115 |
|
116 #define NS_DIRECTION_FROM_KEY_CODE(frame, keycode) \ |
|
117 (DirectionFromKeyCodeTable[frame->StyleVisibility()->mDirection] \ |
|
118 [keycode - nsIDOMKeyEvent::DOM_VK_END]) |
|
119 |
|
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 |
|
131 |
|
132 nsMenuChainItem* mParent; |
|
133 nsMenuChainItem* mChild; |
|
134 |
|
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 } |
|
148 |
|
149 ~nsMenuChainItem() |
|
150 { |
|
151 MOZ_COUNT_DTOR(nsMenuChainItem); |
|
152 } |
|
153 |
|
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; } |
|
165 |
|
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); |
|
169 |
|
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 }; |
|
176 |
|
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 } |
|
190 |
|
191 NS_IMETHOD Run() MOZ_OVERRIDE; |
|
192 |
|
193 private: |
|
194 nsCOMPtr<nsIContent> mPopup; |
|
195 bool mIsContextMenu; |
|
196 bool mSelectFirstItem; |
|
197 }; |
|
198 |
|
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 } |
|
219 |
|
220 NS_IMETHOD Run() MOZ_OVERRIDE; |
|
221 |
|
222 private: |
|
223 nsCOMPtr<nsIContent> mPopup; |
|
224 nsCOMPtr<nsIContent> mNextPopup; |
|
225 nsCOMPtr<nsIContent> mLastPopup; |
|
226 nsPopupType mPopupType; |
|
227 bool mDeselectMenu; |
|
228 bool mIsRollup; |
|
229 }; |
|
230 |
|
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 } |
|
255 |
|
256 NS_IMETHOD Run() MOZ_OVERRIDE; |
|
257 |
|
258 void SetCloseMenuMode(CloseMenuMode aCloseMenuMode) { mCloseMenuMode = aCloseMenuMode; } |
|
259 |
|
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 }; |
|
271 |
|
272 class nsXULPopupManager MOZ_FINAL : public nsIDOMEventListener, |
|
273 public nsIRollupListener, |
|
274 public nsITimerCallback, |
|
275 public nsIObserver |
|
276 { |
|
277 |
|
278 public: |
|
279 friend class nsXULPopupShowingEvent; |
|
280 friend class nsXULPopupHidingEvent; |
|
281 friend class nsXULMenuCommandEvent; |
|
282 friend class TransitionEnder; |
|
283 |
|
284 NS_DECL_ISUPPORTS |
|
285 NS_DECL_NSIOBSERVER |
|
286 NS_DECL_NSITIMERCALLBACK |
|
287 NS_DECL_NSIDOMEVENTLISTENER |
|
288 |
|
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; |
|
297 |
|
298 static nsXULPopupManager* sInstance; |
|
299 |
|
300 // initialize and shutdown methods called by nsLayoutStatics |
|
301 static nsresult Init(); |
|
302 static void Shutdown(); |
|
303 |
|
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(); |
|
307 |
|
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); |
|
312 |
|
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); |
|
338 |
|
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); |
|
345 |
|
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); |
|
354 |
|
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); |
|
361 |
|
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); |
|
369 |
|
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); |
|
389 |
|
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); |
|
404 |
|
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); |
|
414 |
|
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); |
|
427 |
|
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); |
|
450 |
|
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); |
|
456 |
|
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); |
|
463 |
|
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); |
|
469 |
|
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); |
|
478 |
|
479 /** |
|
480 * Return true if the popup for the supplied content node is open. |
|
481 */ |
|
482 bool IsPopupOpen(nsIContent* aPopup); |
|
483 |
|
484 /** |
|
485 * Return true if the popup for the supplied menu parent is open. |
|
486 */ |
|
487 bool IsPopupOpenForMenuParent(nsMenuParent* aMenuParent); |
|
488 |
|
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); |
|
495 |
|
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); |
|
501 |
|
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 } |
|
511 |
|
512 already_AddRefed<nsIDOMNode> GetLastTriggerTooltipNode(nsIDocument* aDocument) |
|
513 { |
|
514 return GetLastTriggerNode(aDocument, true); |
|
515 } |
|
516 |
|
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); |
|
523 |
|
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); |
|
529 |
|
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); |
|
535 |
|
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); |
|
542 |
|
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); |
|
549 |
|
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); |
|
557 |
|
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(); |
|
564 |
|
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); |
|
574 |
|
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); |
|
583 |
|
584 /** |
|
585 * Handles cursor navigation within a menu. Returns true if the key has |
|
586 * been handled. |
|
587 */ |
|
588 bool HandleKeyboardNavigation(uint32_t aKeyCode); |
|
589 |
|
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 } |
|
600 |
|
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); |
|
607 |
|
608 nsresult KeyUp(nsIDOMKeyEvent* aKeyEvent); |
|
609 nsresult KeyDown(nsIDOMKeyEvent* aKeyEvent); |
|
610 nsresult KeyPress(nsIDOMKeyEvent* aKeyEvent); |
|
611 |
|
612 protected: |
|
613 nsXULPopupManager(); |
|
614 ~nsXULPopupManager(); |
|
615 |
|
616 // get the nsMenuPopupFrame, if any, for the given content node |
|
617 nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent, bool aShouldFlush); |
|
618 |
|
619 // return the topmost menu, skipping over invisible popups |
|
620 nsMenuChainItem* GetTopVisibleMenu(); |
|
621 |
|
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); |
|
628 |
|
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); |
|
632 |
|
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); |
|
644 |
|
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); |
|
655 |
|
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); |
|
682 |
|
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 } |
|
691 |
|
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); |
|
703 |
|
704 protected: |
|
705 |
|
706 already_AddRefed<nsIDOMNode> GetLastTriggerNode(nsIDocument* aDocument, bool aIsTooltip); |
|
707 |
|
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); |
|
714 |
|
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(); |
|
727 |
|
728 /* |
|
729 * Returns true if the docshell for aDoc is aExpected or a child of aExpected. |
|
730 */ |
|
731 bool IsChildOfDocShell(nsIDocument* aDoc, nsIDocShellTreeItem* aExpected); |
|
732 |
|
733 // the document the key event listener is attached to |
|
734 nsCOMPtr<mozilla::dom::EventTarget> mKeyListener; |
|
735 |
|
736 // widget that is currently listening to rollup events |
|
737 nsCOMPtr<nsIWidget> mWidget; |
|
738 |
|
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; |
|
745 |
|
746 // cached modifiers |
|
747 mozilla::Modifiers mCachedModifiers; |
|
748 |
|
749 // set to the currently active menu bar, if any |
|
750 nsMenuBarFrame* mActiveMenuBar; |
|
751 |
|
752 // linked list of normal menus and panels. |
|
753 nsMenuChainItem* mPopups; |
|
754 |
|
755 // linked list of noautohide panels and tooltips. |
|
756 nsMenuChainItem* mNoHidePanels; |
|
757 |
|
758 // timer used for HidePopupAfterDelay |
|
759 nsCOMPtr<nsITimer> mCloseTimer; |
|
760 |
|
761 // a popup that is waiting on the timer |
|
762 nsMenuPopupFrame* mTimerMenu; |
|
763 |
|
764 // the popup that is currently being opened, stored only during the |
|
765 // popupshowing event |
|
766 nsCOMPtr<nsIContent> mOpeningPopup; |
|
767 }; |
|
768 |
|
769 #endif |