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