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