michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * The XUL Popup Manager keeps track of all open popups. michael@0: */ michael@0: michael@0: #ifndef nsXULPopupManager_h__ michael@0: #define nsXULPopupManager_h__ michael@0: michael@0: #include "prlog.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIRollupListener.h" michael@0: #include "nsIDOMEventListener.h" michael@0: #include "nsPoint.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsTArray.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsITimer.h" michael@0: #include "nsIReflowCallback.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsWidgetInitData.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: // X.h defines KeyPress michael@0: #ifdef KeyPress michael@0: #undef KeyPress michael@0: #endif michael@0: michael@0: /** michael@0: * There are two types that are used: michael@0: * - dismissable popups such as menus, which should close up when there is a michael@0: * click outside the popup. In this situation, the entire chain of menus michael@0: * above should also be closed. michael@0: * - panels, which stay open until a request is made to close them. This michael@0: * type is used by tooltips. michael@0: * michael@0: * When a new popup is opened, it is appended to the popup chain, stored in a michael@0: * linked list in mPopups for dismissable menus and panels or mNoHidePanels michael@0: * for tooltips and panels with noautohide="true". michael@0: * Popups are stored in this list linked from newest to oldest. When a click michael@0: * occurs outside one of the open dismissable popups, the chain is closed by michael@0: * calling Rollup. michael@0: */ michael@0: michael@0: class nsMenuFrame; michael@0: class nsMenuPopupFrame; michael@0: class nsMenuBarFrame; michael@0: class nsMenuParent; michael@0: class nsIDOMKeyEvent; michael@0: class nsIDocShellTreeItem; michael@0: class nsPIDOMWindow; michael@0: michael@0: // when a menu command is executed, the closemenu attribute may be used michael@0: // to define how the menu should be closed up michael@0: enum CloseMenuMode { michael@0: CloseMenuMode_Auto, // close up the chain of menus, default value michael@0: CloseMenuMode_None, // don't close up any menus michael@0: CloseMenuMode_Single // close up only the menu the command is inside michael@0: }; michael@0: michael@0: /** michael@0: * nsNavigationDirection: an enum expressing navigation through the menus in michael@0: * terms which are independent of the directionality of the chrome. The michael@0: * terminology, derived from XSL-FO and CSS3 (e.g. michael@0: * http://www.w3.org/TR/css3-text/#TextLayout), is BASE (Before, After, Start, michael@0: * End), with the addition of First and Last (mapped to Home and End michael@0: * respectively). michael@0: * michael@0: * In languages such as English where the inline progression is left-to-right michael@0: * and the block progression is top-to-bottom (lr-tb), these terms will map out michael@0: * as in the following diagram michael@0: * michael@0: * --- inline progression ---> michael@0: * michael@0: * First | michael@0: * ... | michael@0: * Before | michael@0: * +--------+ block michael@0: * Start | | End progression michael@0: * +--------+ | michael@0: * After | michael@0: * ... | michael@0: * Last V michael@0: * michael@0: */ michael@0: michael@0: enum nsNavigationDirection { michael@0: eNavigationDirection_Last, michael@0: eNavigationDirection_First, michael@0: eNavigationDirection_Start, michael@0: eNavigationDirection_Before, michael@0: eNavigationDirection_End, michael@0: eNavigationDirection_After michael@0: }; michael@0: michael@0: #define NS_DIRECTION_IS_INLINE(dir) (dir == eNavigationDirection_Start || \ michael@0: dir == eNavigationDirection_End) michael@0: #define NS_DIRECTION_IS_BLOCK(dir) (dir == eNavigationDirection_Before || \ michael@0: dir == eNavigationDirection_After) michael@0: #define NS_DIRECTION_IS_BLOCK_TO_EDGE(dir) (dir == eNavigationDirection_First || \ michael@0: dir == eNavigationDirection_Last) michael@0: michael@0: PR_STATIC_ASSERT(NS_STYLE_DIRECTION_LTR == 0 && NS_STYLE_DIRECTION_RTL == 1); michael@0: michael@0: /** michael@0: * DirectionFromKeyCodeTable: two arrays, the first for left-to-right and the michael@0: * other for right-to-left, that map keycodes to values of michael@0: * nsNavigationDirection. michael@0: */ michael@0: extern const nsNavigationDirection DirectionFromKeyCodeTable[2][6]; michael@0: michael@0: #define NS_DIRECTION_FROM_KEY_CODE(frame, keycode) \ michael@0: (DirectionFromKeyCodeTable[frame->StyleVisibility()->mDirection] \ michael@0: [keycode - nsIDOMKeyEvent::DOM_VK_END]) michael@0: michael@0: // nsMenuChainItem holds info about an open popup. Items are stored in a michael@0: // doubly linked list. Note that the linked list is stored beginning from michael@0: // the lowest child in a chain of menus, as this is the active submenu. michael@0: class nsMenuChainItem michael@0: { michael@0: private: michael@0: nsMenuPopupFrame* mFrame; // the popup frame michael@0: nsPopupType mPopupType; // the popup type of the frame michael@0: bool mIsContext; // true for context menus michael@0: bool mOnMenuBar; // true if the menu is on a menu bar michael@0: bool mIgnoreKeys; // true if keyboard listeners should not be used michael@0: michael@0: nsMenuChainItem* mParent; michael@0: nsMenuChainItem* mChild; michael@0: michael@0: public: michael@0: nsMenuChainItem(nsMenuPopupFrame* aFrame, bool aIsContext, nsPopupType aPopupType) michael@0: : mFrame(aFrame), michael@0: mPopupType(aPopupType), michael@0: mIsContext(aIsContext), michael@0: mOnMenuBar(false), michael@0: mIgnoreKeys(false), michael@0: mParent(nullptr), michael@0: mChild(nullptr) michael@0: { michael@0: NS_ASSERTION(aFrame, "null frame passed to nsMenuChainItem constructor"); michael@0: MOZ_COUNT_CTOR(nsMenuChainItem); michael@0: } michael@0: michael@0: ~nsMenuChainItem() michael@0: { michael@0: MOZ_COUNT_DTOR(nsMenuChainItem); michael@0: } michael@0: michael@0: nsIContent* Content(); michael@0: nsMenuPopupFrame* Frame() { return mFrame; } michael@0: nsPopupType PopupType() { return mPopupType; } michael@0: bool IsMenu() { return mPopupType == ePopupTypeMenu; } michael@0: bool IsContextMenu() { return mIsContext; } michael@0: bool IgnoreKeys() { return mIgnoreKeys; } michael@0: bool IsOnMenuBar() { return mOnMenuBar; } michael@0: void SetIgnoreKeys(bool aIgnoreKeys) { mIgnoreKeys = aIgnoreKeys; } michael@0: void SetOnMenuBar(bool aOnMenuBar) { mOnMenuBar = aOnMenuBar; } michael@0: nsMenuChainItem* GetParent() { return mParent; } michael@0: nsMenuChainItem* GetChild() { return mChild; } michael@0: michael@0: // set the parent of this item to aParent, also changing the parent michael@0: // to have this as a child. michael@0: void SetParent(nsMenuChainItem* aParent); michael@0: michael@0: // removes an item from the chain. The root pointer must be supplied in case michael@0: // the item is the first item in the chain in which case the pointer will be michael@0: // set to the next item, or null if there isn't another item. After detaching, michael@0: // this item will not have a parent or a child. michael@0: void Detach(nsMenuChainItem** aRoot); michael@0: }; michael@0: michael@0: // this class is used for dispatching popupshowing events asynchronously. michael@0: class nsXULPopupShowingEvent : public nsRunnable michael@0: { michael@0: public: michael@0: nsXULPopupShowingEvent(nsIContent *aPopup, michael@0: bool aIsContextMenu, michael@0: bool aSelectFirstItem) michael@0: : mPopup(aPopup), michael@0: mIsContextMenu(aIsContextMenu), michael@0: mSelectFirstItem(aSelectFirstItem) michael@0: { michael@0: NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupShowingEvent constructor"); michael@0: } michael@0: michael@0: NS_IMETHOD Run() MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: nsCOMPtr mPopup; michael@0: bool mIsContextMenu; michael@0: bool mSelectFirstItem; michael@0: }; michael@0: michael@0: // this class is used for dispatching popuphiding events asynchronously. michael@0: class nsXULPopupHidingEvent : public nsRunnable michael@0: { michael@0: public: michael@0: nsXULPopupHidingEvent(nsIContent *aPopup, michael@0: nsIContent* aNextPopup, michael@0: nsIContent* aLastPopup, michael@0: nsPopupType aPopupType, michael@0: bool aDeselectMenu, michael@0: bool aIsRollup) michael@0: : mPopup(aPopup), michael@0: mNextPopup(aNextPopup), michael@0: mLastPopup(aLastPopup), michael@0: mPopupType(aPopupType), michael@0: mDeselectMenu(aDeselectMenu), michael@0: mIsRollup(aIsRollup) michael@0: { michael@0: NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupHidingEvent constructor"); michael@0: // aNextPopup and aLastPopup may be null michael@0: } michael@0: michael@0: NS_IMETHOD Run() MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: nsCOMPtr mPopup; michael@0: nsCOMPtr mNextPopup; michael@0: nsCOMPtr mLastPopup; michael@0: nsPopupType mPopupType; michael@0: bool mDeselectMenu; michael@0: bool mIsRollup; michael@0: }; michael@0: michael@0: // this class is used for dispatching menu command events asynchronously. michael@0: class nsXULMenuCommandEvent : public nsRunnable michael@0: { michael@0: public: michael@0: nsXULMenuCommandEvent(nsIContent *aMenu, michael@0: bool aIsTrusted, michael@0: bool aShift, michael@0: bool aControl, michael@0: bool aAlt, michael@0: bool aMeta, michael@0: bool aUserInput, michael@0: bool aFlipChecked) michael@0: : mMenu(aMenu), michael@0: mIsTrusted(aIsTrusted), michael@0: mShift(aShift), michael@0: mControl(aControl), michael@0: mAlt(aAlt), michael@0: mMeta(aMeta), michael@0: mUserInput(aUserInput), michael@0: mFlipChecked(aFlipChecked), michael@0: mCloseMenuMode(CloseMenuMode_Auto) michael@0: { michael@0: NS_ASSERTION(aMenu, "null menu supplied to nsXULMenuCommandEvent constructor"); michael@0: } michael@0: michael@0: NS_IMETHOD Run() MOZ_OVERRIDE; michael@0: michael@0: void SetCloseMenuMode(CloseMenuMode aCloseMenuMode) { mCloseMenuMode = aCloseMenuMode; } michael@0: michael@0: private: michael@0: nsCOMPtr mMenu; michael@0: bool mIsTrusted; michael@0: bool mShift; michael@0: bool mControl; michael@0: bool mAlt; michael@0: bool mMeta; michael@0: bool mUserInput; michael@0: bool mFlipChecked; michael@0: CloseMenuMode mCloseMenuMode; michael@0: }; michael@0: michael@0: class nsXULPopupManager MOZ_FINAL : public nsIDOMEventListener, michael@0: public nsIRollupListener, michael@0: public nsITimerCallback, michael@0: public nsIObserver michael@0: { michael@0: michael@0: public: michael@0: friend class nsXULPopupShowingEvent; michael@0: friend class nsXULPopupHidingEvent; michael@0: friend class nsXULMenuCommandEvent; michael@0: friend class TransitionEnder; michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: NS_DECL_NSITIMERCALLBACK michael@0: NS_DECL_NSIDOMEVENTLISTENER michael@0: michael@0: // nsIRollupListener michael@0: virtual bool Rollup(uint32_t aCount, const nsIntPoint* pos, nsIContent** aLastRolledUp) MOZ_OVERRIDE; michael@0: virtual bool ShouldRollupOnMouseWheelEvent() MOZ_OVERRIDE; michael@0: virtual bool ShouldConsumeOnMouseWheelEvent() MOZ_OVERRIDE; michael@0: virtual bool ShouldRollupOnMouseActivate() MOZ_OVERRIDE; michael@0: virtual uint32_t GetSubmenuWidgetChain(nsTArray *aWidgetChain) MOZ_OVERRIDE; michael@0: virtual void NotifyGeometryChange() MOZ_OVERRIDE {} michael@0: virtual nsIWidget* GetRollupWidget() MOZ_OVERRIDE; michael@0: michael@0: static nsXULPopupManager* sInstance; michael@0: michael@0: // initialize and shutdown methods called by nsLayoutStatics michael@0: static nsresult Init(); michael@0: static void Shutdown(); michael@0: michael@0: // returns a weak reference to the popup manager instance, could return null michael@0: // if a popup manager could not be allocated michael@0: static nsXULPopupManager* GetInstance(); michael@0: michael@0: // This should be called when a window is moved or resized to adjust the michael@0: // popups accordingly. michael@0: void AdjustPopupsOnWindowChange(nsPIDOMWindow* aWindow); michael@0: void AdjustPopupsOnWindowChange(nsIPresShell* aPresShell); michael@0: michael@0: // given a menu frame, find the prevous or next menu frame. If aPopup is michael@0: // true then navigate a menupopup, from one item on the menu to the previous michael@0: // or next one. This is used for cursor navigation between items in a popup michael@0: // menu. If aIsPopup is false, the navigation is on a menubar, so navigate michael@0: // between menus on the menubar. This is used for left/right cursor navigation. michael@0: // michael@0: // Items that are not valid, such as non-menu or non-menuitem elements are michael@0: // skipped, and the next or previous item after that is checked. michael@0: // michael@0: // If aStart is null, the first valid item is retrieved by GetNextMenuItem michael@0: // and the last valid item is retrieved by GetPreviousMenuItem. michael@0: // michael@0: // Both methods will loop around the beginning or end if needed. michael@0: // michael@0: // aParent - the parent menubar or menupopup michael@0: // aStart - the menu/menuitem to start navigation from. GetPreviousMenuItem michael@0: // returns the item before it, while GetNextMenuItem returns the michael@0: // item after it. michael@0: // aIsPopup - true for menupopups, false for menubars michael@0: static nsMenuFrame* GetPreviousMenuItem(nsIFrame* aParent, michael@0: nsMenuFrame* aStart, michael@0: bool aIsPopup); michael@0: static nsMenuFrame* GetNextMenuItem(nsIFrame* aParent, michael@0: nsMenuFrame* aStart, michael@0: bool aIsPopup); michael@0: michael@0: // returns true if the menu item aContent is a valid menuitem which may michael@0: // be navigated to. aIsPopup should be true for items on a popup, or false michael@0: // for items on a menubar. michael@0: static bool IsValidMenuItem(nsPresContext* aPresContext, michael@0: nsIContent* aContent, michael@0: bool aOnPopup); michael@0: michael@0: // inform the popup manager that a menu bar has been activated or deactivated, michael@0: // either because one of its menus has opened or closed, or that the menubar michael@0: // has been focused such that its menus may be navigated with the keyboard. michael@0: // aActivate should be true when the menubar should be focused, and false michael@0: // when the active menu bar should be defocused. In the latter case, if michael@0: // aMenuBar isn't currently active, yet another menu bar is, that menu bar michael@0: // will remain active. michael@0: void SetActiveMenuBar(nsMenuBarFrame* aMenuBar, bool aActivate); michael@0: michael@0: // retrieve the node and offset of the last mouse event used to open a michael@0: // context menu. This information is determined from the rangeParent and michael@0: // the rangeOffset of the event supplied to ShowPopup or ShowPopupAtScreen. michael@0: // This is used by the implementation of nsIDOMXULDocument::GetPopupRangeParent michael@0: // and nsIDOMXULDocument::GetPopupRangeOffset. michael@0: void GetMouseLocation(nsIDOMNode** aNode, int32_t* aOffset); michael@0: michael@0: /** michael@0: * Open a given its content node. If aSelectFirstItem is michael@0: * set to true, the first item on the menu will automatically be michael@0: * selected. If aAsynchronous is true, the event will be dispatched michael@0: * asynchronously. This should be true when called from frame code. michael@0: */ michael@0: void ShowMenu(nsIContent *aMenu, bool aSelectFirstItem, bool aAsynchronous); michael@0: michael@0: /** michael@0: * Open a popup, either anchored or unanchored. If aSelectFirstItem is michael@0: * true, then the first item in the menu is selected. The arguments are michael@0: * similar to those for nsIPopupBoxObject::OpenPopup. michael@0: * michael@0: * aTriggerEvent should be the event that triggered the event. This is used michael@0: * to determine the coordinates and trigger node for the popup. This may be michael@0: * null if the popup was not triggered by an event. michael@0: * michael@0: * This fires the popupshowing event synchronously. michael@0: */ michael@0: void ShowPopup(nsIContent* aPopup, michael@0: nsIContent* aAnchorContent, michael@0: const nsAString& aPosition, michael@0: int32_t aXPos, int32_t aYPos, michael@0: bool aIsContextMenu, michael@0: bool aAttributesOverride, michael@0: bool aSelectFirstItem, michael@0: nsIDOMEvent* aTriggerEvent); michael@0: michael@0: /** michael@0: * Open a popup at a specific screen position specified by aXPos and aYPos, michael@0: * measured in CSS pixels. michael@0: * michael@0: * This fires the popupshowing event synchronously. michael@0: * michael@0: * If aIsContextMenu is true, the popup is positioned at a slight michael@0: * offset from aXPos/aYPos to ensure that it is not under the mouse michael@0: * cursor. michael@0: */ michael@0: void ShowPopupAtScreen(nsIContent* aPopup, michael@0: int32_t aXPos, int32_t aYPos, michael@0: bool aIsContextMenu, michael@0: nsIDOMEvent* aTriggerEvent); michael@0: michael@0: /** michael@0: * Open a tooltip at a specific screen position specified by aXPos and aYPos, michael@0: * measured in CSS pixels. michael@0: * michael@0: * This fires the popupshowing event synchronously. michael@0: */ michael@0: void ShowTooltipAtScreen(nsIContent* aPopup, michael@0: nsIContent* aTriggerContent, michael@0: int32_t aXPos, int32_t aYPos); michael@0: michael@0: /** michael@0: * This method is provided only for compatibility with an older popup API. michael@0: * New code should not call this function and should call ShowPopup instead. michael@0: * michael@0: * This fires the popupshowing event synchronously. michael@0: */ michael@0: void ShowPopupWithAnchorAlign(nsIContent* aPopup, michael@0: nsIContent* aAnchorContent, michael@0: nsAString& aAnchor, michael@0: nsAString& aAlign, michael@0: int32_t aXPos, int32_t aYPos, michael@0: bool aIsContextMenu); michael@0: michael@0: /* michael@0: * Hide a popup aPopup. If the popup is in a , then also inform the michael@0: * menu that the popup is being hidden. michael@0: * michael@0: * aHideChain - true if the entire chain of menus should be closed. If false, michael@0: * only this popup is closed. michael@0: * aDeselectMenu - true if the parent of the popup should be deselected. michael@0: * This will be false when the menu is closed by pressing the michael@0: * Escape key. michael@0: * aAsynchronous - true if the first popuphiding event should be sent michael@0: * asynchrously. This should be true if HidePopup is called michael@0: * from a frame. michael@0: * aIsRollup - true if this popup is hiding due to a rollup or escape keypress. michael@0: * aLastPopup - optional popup to close last when hiding a chain of menus. michael@0: * If null, then all popups will be closed. michael@0: */ michael@0: void HidePopup(nsIContent* aPopup, michael@0: bool aHideChain, michael@0: bool aDeselectMenu, michael@0: bool aAsynchronous, michael@0: bool aIsRollup, michael@0: nsIContent* aLastPopup = nullptr); michael@0: michael@0: /** michael@0: * Hide the popup aFrame. This method is called by the view manager when the michael@0: * close button is pressed. michael@0: */ michael@0: void HidePopup(nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Hide a popup after a short delay. This is used when rolling over menu items. michael@0: * This timer is stored in mCloseTimer. The timer may be cancelled and the popup michael@0: * closed by calling KillMenuTimer. michael@0: */ michael@0: void HidePopupAfterDelay(nsMenuPopupFrame* aPopup); michael@0: michael@0: /** michael@0: * Hide all of the popups from a given docshell. This should be called when the michael@0: * document is hidden. michael@0: */ michael@0: void HidePopupsInDocShell(nsIDocShellTreeItem* aDocShellToHide); michael@0: michael@0: /** michael@0: * Execute a menu command from the triggering event aEvent. michael@0: * michael@0: * aMenu - a menuitem to execute michael@0: * aEvent - an nsXULMenuCommandEvent that contains all the info from the mouse michael@0: * event which triggered the menu to be executed, may not be null michael@0: */ michael@0: void ExecuteMenu(nsIContent* aMenu, nsXULMenuCommandEvent* aEvent); michael@0: michael@0: /** michael@0: * Return true if the popup for the supplied content node is open. michael@0: */ michael@0: bool IsPopupOpen(nsIContent* aPopup); michael@0: michael@0: /** michael@0: * Return true if the popup for the supplied menu parent is open. michael@0: */ michael@0: bool IsPopupOpenForMenuParent(nsMenuParent* aMenuParent); michael@0: michael@0: /** michael@0: * Return the frame for the topmost open popup of a given type, or null if michael@0: * no popup of that type is open. If aType is ePopupTypeAny, a menu of any michael@0: * type is returned, except for popups in the mNoHidePanels list. michael@0: */ michael@0: nsIFrame* GetTopPopup(nsPopupType aType); michael@0: michael@0: /** michael@0: * Return an array of all the open and visible popup frames for michael@0: * menus, in order from top to bottom. michael@0: */ michael@0: void GetVisiblePopups(nsTArray& aPopups); michael@0: michael@0: /** michael@0: * Get the node that last triggered a popup or tooltip in the document michael@0: * aDocument. aDocument must be non-null and be a document contained within michael@0: * the same window hierarchy as the popup to retrieve. michael@0: */ michael@0: already_AddRefed GetLastTriggerPopupNode(nsIDocument* aDocument) michael@0: { michael@0: return GetLastTriggerNode(aDocument, false); michael@0: } michael@0: michael@0: already_AddRefed GetLastTriggerTooltipNode(nsIDocument* aDocument) michael@0: { michael@0: return GetLastTriggerNode(aDocument, true); michael@0: } michael@0: michael@0: /** michael@0: * Return false if a popup may not be opened. This will return false if the michael@0: * popup is already open, if the popup is in a content shell that is not michael@0: * focused, or if it is a submenu of another menu that isn't open. michael@0: */ michael@0: bool MayShowPopup(nsMenuPopupFrame* aFrame); michael@0: michael@0: /** michael@0: * Indicate that the popup associated with aView has been moved to the michael@0: * specified screen coordiates. michael@0: */ michael@0: void PopupMoved(nsIFrame* aFrame, nsIntPoint aPoint); michael@0: michael@0: /** michael@0: * Indicate that the popup associated with aView has been resized to the michael@0: * specified screen width and height. michael@0: */ michael@0: void PopupResized(nsIFrame* aFrame, nsIntSize ASize); michael@0: michael@0: /** michael@0: * Called when a popup frame is destroyed. In this case, just remove the michael@0: * item and later popups from the list. No point going through HidePopup as michael@0: * the frames have gone away. michael@0: */ michael@0: void PopupDestroyed(nsMenuPopupFrame* aFrame); michael@0: michael@0: /** michael@0: * Returns true if there is a context menu open. If aPopup is specified, michael@0: * then the context menu must be later in the chain than aPopup. If aPopup michael@0: * is null, returns true if any context menu at all is open. michael@0: */ michael@0: bool HasContextMenu(nsMenuPopupFrame* aPopup); michael@0: michael@0: /** michael@0: * Update the commands for the menus within the menu popup for a given michael@0: * content node. aPopup should be a XUL menupopup element. This method michael@0: * changes attributes on the children of aPopup, and deals only with the michael@0: * content of the popup, not the frames. michael@0: */ michael@0: void UpdateMenuItems(nsIContent* aPopup); michael@0: michael@0: /** michael@0: * Stop the timer which hides a popup after a delay, started by a previous michael@0: * call to HidePopupAfterDelay. In addition, the popup awaiting to be hidden michael@0: * is closed asynchronously. michael@0: */ michael@0: void KillMenuTimer(); michael@0: michael@0: /** michael@0: * Cancel the timer which closes menus after delay, but only if the menu to michael@0: * close is aMenuParent. When a submenu is opened, the user might move the michael@0: * mouse over a sibling menuitem which would normally close the menu. This michael@0: * menu is closed via a timer. However, if the user moves the mouse over the michael@0: * submenu before the timer fires, we should instead cancel the timer. This michael@0: * ensures that the user can move the mouse diagonally over a menu. michael@0: */ michael@0: void CancelMenuTimer(nsMenuParent* aMenuParent); michael@0: michael@0: /** michael@0: * Handles navigation for menu accelkeys. Returns true if the key has michael@0: * been handled. If aFrame is specified, then the key is handled by that michael@0: * popup, otherwise if aFrame is null, the key is handled by the active michael@0: * popup or menubar. michael@0: */ michael@0: bool HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, michael@0: nsMenuPopupFrame* aFrame); michael@0: michael@0: /** michael@0: * Handles cursor navigation within a menu. Returns true if the key has michael@0: * been handled. michael@0: */ michael@0: bool HandleKeyboardNavigation(uint32_t aKeyCode); michael@0: michael@0: /** michael@0: * Handle keyboard navigation within a menu popup specified by aFrame. michael@0: * Returns true if the key was handled and other default handling michael@0: * should not occur. michael@0: */ michael@0: bool HandleKeyboardNavigationInPopup(nsMenuPopupFrame* aFrame, michael@0: nsNavigationDirection aDir) michael@0: { michael@0: return HandleKeyboardNavigationInPopup(nullptr, aFrame, aDir); michael@0: } michael@0: michael@0: /** michael@0: * Handles the keyboard event with keyCode value. Returns true if the event michael@0: * has been handled. michael@0: */ michael@0: bool HandleKeyboardEventWithKeyCode(nsIDOMKeyEvent* aKeyEvent, michael@0: nsMenuChainItem* aTopVisibleMenuItem); michael@0: michael@0: nsresult KeyUp(nsIDOMKeyEvent* aKeyEvent); michael@0: nsresult KeyDown(nsIDOMKeyEvent* aKeyEvent); michael@0: nsresult KeyPress(nsIDOMKeyEvent* aKeyEvent); michael@0: michael@0: protected: michael@0: nsXULPopupManager(); michael@0: ~nsXULPopupManager(); michael@0: michael@0: // get the nsMenuPopupFrame, if any, for the given content node michael@0: nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent, bool aShouldFlush); michael@0: michael@0: // return the topmost menu, skipping over invisible popups michael@0: nsMenuChainItem* GetTopVisibleMenu(); michael@0: michael@0: // Hide all of the visible popups from the given list. aDeselectMenu michael@0: // indicates whether to deselect the menu of popups when hiding; this michael@0: // flag is passed as the first argument to HidePopup. This function michael@0: // can cause style changes and frame destruction. michael@0: void HidePopupsInList(const nsTArray &aFrames, michael@0: bool aDeselectMenu); michael@0: michael@0: // set the event that was used to trigger the popup, or null to clear the michael@0: // event details. aTriggerContent will be set to the target of the event. michael@0: void InitTriggerEvent(nsIDOMEvent* aEvent, nsIContent* aPopup, nsIContent** aTriggerContent); michael@0: michael@0: // callbacks for ShowPopup and HidePopup as events may be done asynchronously michael@0: void ShowPopupCallback(nsIContent* aPopup, michael@0: nsMenuPopupFrame* aPopupFrame, michael@0: bool aIsContextMenu, michael@0: bool aSelectFirstItem); michael@0: void HidePopupCallback(nsIContent* aPopup, michael@0: nsMenuPopupFrame* aPopupFrame, michael@0: nsIContent* aNextPopup, michael@0: nsIContent* aLastPopup, michael@0: nsPopupType aPopupType, michael@0: bool aDeselectMenu); michael@0: michael@0: /** michael@0: * Fire a popupshowing event on the popup and then open the popup. michael@0: * michael@0: * aPopup - the popup to open michael@0: * aIsContextMenu - true for context menus michael@0: * aSelectFirstItem - true to select the first item in the menu michael@0: */ michael@0: void FirePopupShowingEvent(nsIContent* aPopup, michael@0: bool aIsContextMenu, michael@0: bool aSelectFirstItem); michael@0: michael@0: /** michael@0: * Fire a popuphiding event and then hide the popup. This will be called michael@0: * recursively if aNextPopup and aLastPopup are set in order to hide a chain michael@0: * of open menus. If these are not set, only one popup is closed. However, michael@0: * if the popup type indicates a menu, yet the next popup is not a menu, michael@0: * then this ends the closing of popups. This allows a menulist inside a michael@0: * non-menu to close up the menu but not close up the panel it is contained michael@0: * within. michael@0: * michael@0: * The caller must keep a strong reference to aPopup, aNextPopup and aLastPopup. michael@0: * michael@0: * aPopup - the popup to hide michael@0: * aNextPopup - the next popup to hide michael@0: * aLastPopup - the last popup in the chain to hide michael@0: * aPresContext - nsPresContext for the popup's frame michael@0: * aPopupType - the PopupType of the frame. michael@0: * aDeselectMenu - true to unhighlight the menu when hiding it michael@0: * aIsRollup - true if this popup is hiding due to a rollup or escape keypress michael@0: */ michael@0: void FirePopupHidingEvent(nsIContent* aPopup, michael@0: nsIContent* aNextPopup, michael@0: nsIContent* aLastPopup, michael@0: nsPresContext *aPresContext, michael@0: nsPopupType aPopupType, michael@0: bool aDeselectMenu, michael@0: bool aIsRollup); michael@0: michael@0: /** michael@0: * Handle keyboard navigation within a menu popup specified by aItem. michael@0: */ michael@0: bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem, michael@0: nsNavigationDirection aDir) michael@0: { michael@0: return HandleKeyboardNavigationInPopup(aItem, aItem->Frame(), aDir); michael@0: } michael@0: michael@0: private: michael@0: /** michael@0: * Handle keyboard navigation within a menu popup aFrame. If aItem is michael@0: * supplied, then it is expected to have a frame equal to aFrame. michael@0: * If aItem is non-null, then the navigation may be redirected to michael@0: * an open submenu if one exists. Returns true if the key was michael@0: * handled and other default handling should not occur. michael@0: */ michael@0: bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem, michael@0: nsMenuPopupFrame* aFrame, michael@0: nsNavigationDirection aDir); michael@0: michael@0: protected: michael@0: michael@0: already_AddRefed GetLastTriggerNode(nsIDocument* aDocument, bool aIsTooltip); michael@0: michael@0: /** michael@0: * Set mouse capturing for the current popup. This traps mouse clicks that michael@0: * occur outside the popup so that it can be closed up. aOldPopup should be michael@0: * set to the popup that was previously the current popup. michael@0: */ michael@0: void SetCaptureState(nsIContent *aOldPopup); michael@0: michael@0: /** michael@0: * Key event listeners are attached to the document containing the current michael@0: * menu for menu and shortcut navigation. Only one listener is needed at a michael@0: * time, stored in mKeyListener, so switch it only if the document changes. michael@0: * Having menus in different documents is very rare, so the listeners will michael@0: * usually only be attached when the first menu opens and removed when all michael@0: * menus have closed. michael@0: * michael@0: * This is also used when only a menubar is active without any open menus, michael@0: * so that keyboard navigation between menus on the menubar may be done. michael@0: */ michael@0: void UpdateKeyboardListeners(); michael@0: michael@0: /* michael@0: * Returns true if the docshell for aDoc is aExpected or a child of aExpected. michael@0: */ michael@0: bool IsChildOfDocShell(nsIDocument* aDoc, nsIDocShellTreeItem* aExpected); michael@0: michael@0: // the document the key event listener is attached to michael@0: nsCOMPtr mKeyListener; michael@0: michael@0: // widget that is currently listening to rollup events michael@0: nsCOMPtr mWidget; michael@0: michael@0: // range parent and offset set in SetTriggerEvent michael@0: nsCOMPtr mRangeParent; michael@0: int32_t mRangeOffset; michael@0: // Device pixels relative to the showing popup's presshell's michael@0: // root prescontext's root frame. michael@0: nsIntPoint mCachedMousePoint; michael@0: michael@0: // cached modifiers michael@0: mozilla::Modifiers mCachedModifiers; michael@0: michael@0: // set to the currently active menu bar, if any michael@0: nsMenuBarFrame* mActiveMenuBar; michael@0: michael@0: // linked list of normal menus and panels. michael@0: nsMenuChainItem* mPopups; michael@0: michael@0: // linked list of noautohide panels and tooltips. michael@0: nsMenuChainItem* mNoHidePanels; michael@0: michael@0: // timer used for HidePopupAfterDelay michael@0: nsCOMPtr mCloseTimer; michael@0: michael@0: // a popup that is waiting on the timer michael@0: nsMenuPopupFrame* mTimerMenu; michael@0: michael@0: // the popup that is currently being opened, stored only during the michael@0: // popupshowing event michael@0: nsCOMPtr mOpeningPopup; michael@0: }; michael@0: michael@0: #endif