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