nsFocusManager.h (44952B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef nsFocusManager_h___ 8 #define nsFocusManager_h___ 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/RefPtr.h" 12 #include "mozilla/StaticPtr.h" 13 #include "mozilla/dom/Document.h" 14 #include "nsCycleCollectionParticipant.h" 15 #include "nsIContent.h" 16 #include "nsIFocusManager.h" 17 #include "nsIObserver.h" 18 #include "nsWeakReference.h" 19 20 #define FOCUSMANAGER_CONTRACTID "@mozilla.org/focus-manager;1" 21 22 class nsIContent; 23 class nsPIDOMWindowOuter; 24 25 namespace mozilla { 26 class PresShell; 27 namespace dom { 28 class Element; 29 class HTMLAreaElement; 30 struct FocusOptions; 31 class BrowserParent; 32 class ContentChild; 33 class ContentParent; 34 } // namespace dom 35 } // namespace mozilla 36 37 struct nsDelayedBlurOrFocusEvent; 38 39 /** 40 * The focus manager keeps track of where the focus is, that is, the node 41 * which receives key events. 42 */ 43 44 class nsFocusManager final : public nsIFocusManager, 45 public nsIObserver, 46 public nsSupportsWeakReference { 47 using InputContextAction = mozilla::widget::InputContextAction; 48 using Document = mozilla::dom::Document; 49 friend class mozilla::dom::ContentChild; 50 friend class mozilla::dom::ContentParent; 51 52 public: 53 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFocusManager, nsIFocusManager) 54 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 55 NS_DECL_NSIOBSERVER 56 NS_DECL_NSIFOCUSMANAGER 57 58 // called to initialize and stop the focus manager at startup and shutdown 59 static nsresult Init(); 60 static void Shutdown(); 61 62 // Simple helper to call SetFocusedWindow on the instance. 63 // 64 // This raises the window and switches to the tab as needed. 65 MOZ_CAN_RUN_SCRIPT static void FocusWindow( 66 nsPIDOMWindowOuter* aWindow, mozilla::dom::CallerType aCallerType); 67 68 MOZ_CAN_RUN_SCRIPT_BOUNDARY static void PrefChanged(const char* aPref, 69 void* aSelf); 70 MOZ_CAN_RUN_SCRIPT void PrefChanged(const char* aPref); 71 72 /** 73 * Retrieve the single focus manager. 74 */ 75 static nsFocusManager* GetFocusManager() { return sInstance; } 76 77 /** 78 * A faster version of nsIFocusManager::GetFocusedElement, returning a 79 * raw Element pointer (instead of having AddRef-ed Element 80 * pointer filled in to an out-parameter). 81 */ 82 mozilla::dom::Element* GetFocusedElement() { return mFocusedElement; } 83 static mozilla::dom::Element* GetFocusedElementStatic() { 84 return sInstance ? sInstance->GetFocusedElement() : nullptr; 85 } 86 87 /** 88 * Returns true if aContent currently has focus. 89 */ 90 bool IsFocused(nsIContent* aContent); 91 92 /** 93 * Returns true if test mode is enabled. 94 */ 95 bool IsTestMode(); 96 97 /** 98 * Return a focused window. Version of nsIFocusManager::GetFocusedWindow. 99 */ 100 nsPIDOMWindowOuter* GetFocusedWindow() const { return mFocusedWindow; } 101 static nsPIDOMWindowOuter* GetFocusedWindowStatic() { 102 return sInstance ? sInstance->GetFocusedWindow() : nullptr; 103 } 104 105 /** 106 * In the chrome process, retrieves the BrowsingContext corresponding 107 * to GetFocusedWindow(). In a content process, retrieves the 108 * focused BrowsingContext, which may not belong to this process. 109 */ 110 mozilla::dom::BrowsingContext* GetFocusedBrowsingContext() const { 111 if (XRE_IsParentProcess()) { 112 if (mFocusedWindow) { 113 return mFocusedWindow->GetBrowsingContext(); 114 } 115 return nullptr; 116 } 117 return mFocusedBrowsingContextInContent; 118 } 119 120 /** 121 * Returns whether the given browsing context is in the active window. 122 */ 123 bool IsInActiveWindow(mozilla::dom::BrowsingContext*) const; 124 125 /** 126 * Return an active window. Version of nsIFocusManager::GetActiveWindow. 127 */ 128 nsPIDOMWindowOuter* GetActiveWindow() const { return mActiveWindow; } 129 130 /** 131 * In the chrome process, retrieves the BrowsingContext corresponding 132 * to GetActiveWindow(). In a content process, retrieves the 133 * BrowsingContext of the top-level Web content in the active tab if 134 * in the same process as the caller or nullptr otherwise. 135 */ 136 mozilla::dom::BrowsingContext* GetActiveBrowsingContext() const { 137 if (XRE_IsParentProcess()) { 138 if (mActiveWindow) { 139 return mActiveWindow->GetBrowsingContext(); 140 } 141 return nullptr; 142 } 143 return mActiveBrowsingContextInContent; 144 } 145 146 void ContentInserted(nsIContent* aChild, const ContentInsertInfo& aInfo); 147 148 void ContentAppended(nsIContent* aFirstNewContent, 149 const ContentAppendInfo& aInfo); 150 151 /** 152 * Called when content has been removed. 153 */ 154 MOZ_CAN_RUN_SCRIPT nsresult ContentRemoved(Document* aDocument, 155 nsIContent* aContent, 156 const ContentRemoveInfo& aInfo); 157 158 void NeedsFlushBeforeEventHandling(mozilla::dom::Element* aElement) { 159 if (mFocusedElement == aElement) { 160 mEventHandlingNeedsFlush = true; 161 } 162 } 163 164 bool CanSkipFocus(nsIContent* aContent); 165 166 MOZ_CAN_RUN_SCRIPT void FlushBeforeEventHandlingIfNeeded( 167 nsIContent* aContent) { 168 if (mEventHandlingNeedsFlush) { 169 nsCOMPtr<Document> doc = aContent->GetComposedDoc(); 170 if (doc) { 171 mEventHandlingNeedsFlush = false; 172 doc->FlushPendingNotifications(mozilla::FlushType::Layout); 173 } 174 } 175 } 176 177 /** 178 * Update the caret with current mode (whether in caret browsing mode or not). 179 */ 180 MOZ_CAN_RUN_SCRIPT void UpdateCaretForCaretBrowsingMode(); 181 182 /** @see nsIFocusManager.getLastFocusMethod() */ 183 uint32_t GetLastFocusMethod(nsPIDOMWindowOuter*) const; 184 185 /** 186 * Returns the content node that would be focused if aWindow was in an 187 * active window. This will traverse down the frame hierarchy, starting at 188 * the given window aWindow. Sets aFocusedWindow to the window with the 189 * document containing aFocusedContent. If no element is focused, 190 * aFocusedWindow may be still be set -- this means that the document is 191 * focused but no element within it is focused. 192 * 193 * aWindow, aFocusIsOutOfProcess, aFocusedWindow must all be non-null. 194 */ 195 enum SearchRange { 196 // Return focused content in aWindow. So, aFocusedWindow is always aWindow. 197 eOnlyCurrentWindow, 198 // Return focused content in aWindow or one of all sub windows. 199 eIncludeAllDescendants, 200 // Return focused content in aWindow or one of visible sub windows. 201 eIncludeVisibleDescendants, 202 }; 203 static mozilla::dom::Element* GetFocusedDescendant( 204 nsPIDOMWindowOuter* aWindow, SearchRange aSearchRange, 205 nsPIDOMWindowOuter** aFocusedWindow); 206 207 /** 208 * Helper function for MoveFocus which determines the next element 209 * to move the focus to and returns it in aNextContent. 210 * 211 * aWindow is the window to adjust the focus within, and aStart is 212 * the element to start navigation from. For tab key navigation, 213 * this should be the currently focused element. 214 * 215 * aType is the type passed to MoveFocus. If aNoParentTraversal is set, 216 * navigation is not done to parent documents and iteration returns to the 217 * beginning (or end) of the starting document. 218 * 219 * aNavigateByKey to move focus by keyboard as a side effect of computing the 220 * next target. 221 */ 222 MOZ_CAN_RUN_SCRIPT nsresult DetermineElementToMoveFocus( 223 nsPIDOMWindowOuter* aWindow, nsIContent* aStart, int32_t aType, 224 bool aNoParentTraversal, bool aNavigateByKey, nsIContent** aNextContent); 225 226 /** 227 * Setter for focusedWindow with CallerType 228 */ 229 MOZ_CAN_RUN_SCRIPT nsresult SetFocusedWindowWithCallerType( 230 mozIDOMWindowProxy* aWindowToFocus, mozilla::dom::CallerType aCallerType); 231 232 /** Given a focused frame loader owner, fix up the focus to be consistent */ 233 MOZ_CAN_RUN_SCRIPT void FixUpFocusAfterFrameLoaderChange( 234 mozilla::dom::Element&); 235 /** 236 * Keep track of whether the focused window is about to go away, and if so fix 237 * up the focus state so that we can know if we're still focused by the time 238 * the frame loader swap ends. 239 */ 240 void FixUpFocusBeforeFrameLoaderChange(mozilla::dom::Element&, 241 mozilla::dom::BrowsingContext* aBc); 242 243 /** 244 * Raises the top-level window aWindow at the widget level. 245 */ 246 MOZ_CAN_RUN_SCRIPT void RaiseWindow(nsPIDOMWindowOuter* aWindow, 247 mozilla::dom::CallerType aCallerType, 248 uint64_t aActionId); 249 250 /** 251 * Called when a window has been raised. 252 */ 253 MOZ_CAN_RUN_SCRIPT void WindowRaised(mozIDOMWindowProxy* aWindow, 254 uint64_t aActionId); 255 256 /** 257 * Called when a window has been lowered. 258 */ 259 MOZ_CAN_RUN_SCRIPT void WindowLowered(mozIDOMWindowProxy* aWindow, 260 uint64_t aActionId); 261 262 /** 263 * Called when a new document in a window is shown. 264 * 265 * If aNeedsFocus is true, then focus events are expected to be fired on the 266 * window if this window is in the focused window chain. 267 */ 268 MOZ_CAN_RUN_SCRIPT void WindowShown(mozIDOMWindowProxy* aWindow, 269 bool aNeedsFocus); 270 271 /** 272 * Called when a document in a window has been hidden or otherwise can no 273 * longer accept focus. 274 */ 275 MOZ_CAN_RUN_SCRIPT void WindowHidden(mozIDOMWindowProxy* aWindow, 276 uint64_t aActionId, 277 bool aIsEnteringBFCache); 278 279 /** 280 * Fire any events that have been delayed due to synchronized actions. 281 */ 282 MOZ_CAN_RUN_SCRIPT void FireDelayedEvents(Document* aDocument); 283 284 void WasNuked(nsPIDOMWindowOuter* aWindow); 285 286 static uint32_t ProgrammaticFocusFlags( 287 const mozilla::dom::FocusOptions& aOptions); 288 289 /** 290 * Returns an InputContextAction cause for aFlags. 291 */ 292 static InputContextAction::Cause GetFocusMoveActionCause(uint32_t aFlags); 293 294 /** 295 * Notify of re-focus to same element. 296 * 297 * aElement is focused element. 298 */ 299 MOZ_CAN_RUN_SCRIPT void NotifyOfReFocus(mozilla::dom::Element& aElement); 300 301 static void MarkUncollectableForCCGeneration(uint32_t aGeneration); 302 303 struct BlurredElementInfo { 304 const mozilla::OwningNonNull<mozilla::dom::Element> mElement; 305 306 explicit BlurredElementInfo(mozilla::dom::Element&); 307 ~BlurredElementInfo(); 308 }; 309 310 protected: 311 nsFocusManager(); 312 ~nsFocusManager(); 313 314 /** 315 * Ensure that the widget associated with the currently focused window is 316 * focused at the widget level. 317 */ 318 void EnsureCurrentWidgetFocused(mozilla::dom::CallerType aCallerType); 319 320 /** 321 * Focus the last focused element in aWindow, after aWindow was raised (or if 322 * aWindow was already raised). 323 */ 324 MOZ_CAN_RUN_SCRIPT void MoveFocusToWindowAfterRaise(nsPIDOMWindowOuter*, 325 uint64_t aActionId); 326 327 /** 328 * Activate or deactivate the window and send the activate/deactivate events. 329 */ 330 void ActivateOrDeactivate(nsPIDOMWindowOuter* aWindow, bool aActive); 331 332 /** 333 * Blur whatever is currently focused and focus aNewContent. aFlags is a 334 * bitmask of the flags defined in nsIFocusManager. If aFocusChanged is 335 * true, then the focus has actually shifted and the caret position will be 336 * updated to the new focus, aNewContent will be scrolled into view (unless 337 * a flag disables this) and the focus method for the window will be updated. 338 * If aAdjustWidget is false, don't change the widget focus state. 339 * 340 * All actual focus changes must use this method to do so. (as opposed 341 * to those that update the focus in an inactive window for instance). 342 * 343 * Returns Nothing() if we end up not trying to focus the element, 344 * otherwise returns the generated action id. 345 */ 346 MOZ_CAN_RUN_SCRIPT mozilla::Maybe<uint64_t> SetFocusInner( 347 mozilla::dom::Element* aNewContent, int32_t aFlags, bool aFocusChanged, 348 bool aAdjustWidget); 349 350 /** 351 * Returns true if aPossibleAncestor is the same as aWindow or an 352 * ancestor of aWindow. 353 */ 354 bool IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor, 355 nsPIDOMWindowOuter* aWindow) const; 356 bool IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor, 357 mozilla::dom::BrowsingContext* aContext) const; 358 bool IsSameOrAncestor(mozilla::dom::BrowsingContext* aPossibleAncestor, 359 nsPIDOMWindowOuter* aWindow) const; 360 361 public: 362 bool IsSameOrAncestor(mozilla::dom::BrowsingContext* aPossibleAncestor, 363 mozilla::dom::BrowsingContext* aContext) const; 364 365 protected: 366 /** 367 * Returns the window that is the lowest common ancestor of both aWindow 368 * and aContext, or null if they share no common ancestor. 369 */ 370 mozilla::dom::BrowsingContext* GetCommonAncestor( 371 nsPIDOMWindowOuter* aWindow, mozilla::dom::BrowsingContext* aContext); 372 373 /** 374 * When aBrowsingContext is focused or blurred, adjust the ancestors of 375 * aBrowsingContext so that they also have their corresponding frames focused 376 * or blurred. Thus, one can start at the active top-level window and navigate 377 * down the currently focused elements for each frame in the tree to get to 378 * aBrowsingContext. 379 */ 380 MOZ_CAN_RUN_SCRIPT bool AdjustInProcessWindowFocus( 381 mozilla::dom::BrowsingContext* aBrowsingContext, bool aCheckPermission, 382 bool aIsVisible, uint64_t aActionId, bool aShouldClearAncestorFocus, 383 mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus); 384 385 MOZ_CAN_RUN_SCRIPT void AdjustWindowFocus( 386 mozilla::dom::BrowsingContext* aBrowsingContext, bool aCheckPermission, 387 bool aIsVisible, uint64_t aActionId, bool aShouldClearAncestorFocus, 388 mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus); 389 390 /** 391 * Returns true if aWindow is visible. 392 */ 393 bool IsWindowVisible(nsPIDOMWindowOuter* aWindow); 394 395 /** 396 * Returns true if aContent is a root element and not focusable. 397 * I.e., even if aContent is editable root element, this returns true when 398 * the document is in designMode. 399 * 400 * @param aContent must not be null and must be in a document. 401 */ 402 bool IsNonFocusableRoot(nsIContent* aContent); 403 404 /** 405 * First flushes the pending notifications to ensure the PresShell and frames 406 * are updated. 407 * Checks and returns aElement if it may be focused, another element node if 408 * the focus should be retargeted at another node, or null if the node 409 * cannot be focused. aFlags are the flags passed to SetFocus and similar 410 * methods. 411 * 412 * An element is focusable if it is in a document, the document isn't in 413 * print preview mode and the element has an nsIFrame where the 414 * IsFocusable method returns true. For <area> elements, there is no 415 * frame, so only the IsFocusable method on the content node must be 416 * true. 417 */ 418 MOZ_CAN_RUN_SCRIPT mozilla::dom::Element* FlushAndCheckIfFocusable( 419 mozilla::dom::Element* aElement, uint32_t aFlags); 420 421 /** 422 * Blurs the currently focused element. Returns false if another element was 423 * focused as a result. This would mean that the caller should not proceed 424 * with a pending call to Focus. Normally, true would be returned. 425 * 426 * The currently focused element within aBrowsingContextToClear will be 427 * cleared. aBrowsingContextToClear may be null, which means that no window is 428 * cleared. This will be the case, for example, when lowering a window, as we 429 * want to fire a blur, but not actually change what element would be focused, 430 * so that the same element will be focused again when the window is raised. 431 * 432 * aAncestorBrowsingContextToFocus should be set to the common ancestor of the 433 * window that is being blurred and the window that is going to focused, when 434 * switching focus to a sibling window. 435 * 436 * aIsLeavingDocument should be set to true if the document/window is being 437 * blurred as well. Document/window blur events will be fired. It should be 438 * false if an element is the same document is about to be focused. 439 * 440 * If aAdjustWidget is false, don't change the widget focus state. 441 */ 442 MOZ_CAN_RUN_SCRIPT bool Blur( 443 mozilla::dom::BrowsingContext* aBrowsingContextToClear, 444 mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus, 445 bool aIsLeavingDocument, bool aAdjustWidget, bool aRemainActive, 446 uint64_t aActionId, mozilla::dom::Element* aElementToFocus = nullptr); 447 MOZ_CAN_RUN_SCRIPT void BlurFromOtherProcess( 448 mozilla::dom::BrowsingContext* aFocusedBrowsingContext, 449 mozilla::dom::BrowsingContext* aBrowsingContextToClear, 450 mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus, 451 bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId); 452 MOZ_CAN_RUN_SCRIPT bool BlurImpl( 453 mozilla::dom::BrowsingContext* aBrowsingContextToClear, 454 mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus, 455 bool aIsLeavingDocument, bool aAdjustWidget, bool aRemainActive, 456 mozilla::dom::Element* aElementToFocus, uint64_t aActionId); 457 458 /** 459 * Focus an element in the active window and child frame. 460 * 461 * aWindow is the window containing the element aContent to focus. 462 * 463 * aFlags is the flags passed to the various focus methods in 464 * nsIFocusManager. 465 * 466 * aIsNewDocument should be true if a new document is being focused. 467 * Document/window focus events will be fired. 468 * 469 * aFocusChanged should be true if a new content node is being focused, so 470 * the focused content will be scrolled into view and the caret position 471 * will be updated. If false is passed, then a window is simply being 472 * refocused, for instance, due to a window being raised, or a tab is being 473 * switched to. 474 * 475 * If aFocusChanged is true, then the focus has moved to a new location. 476 * Otherwise, the focus is just being updated because the window was 477 * raised. 478 * 479 * aWindowRaised should be true if the window is being raised. In this case, 480 * command updaters will not be called. 481 * 482 * If aAdjustWidget is false, don't change the widget focus state. 483 */ 484 MOZ_CAN_RUN_SCRIPT void Focus( 485 nsPIDOMWindowOuter* aWindow, mozilla::dom::Element* aContent, 486 uint32_t aFlags, bool aIsNewDocument, bool aFocusChanged, 487 bool aWindowRaised, bool aAdjustWidget, uint64_t aActionId, 488 const mozilla::Maybe<BlurredElementInfo>& = mozilla::Nothing()); 489 490 /** 491 * Send a focus or blur event at aTarget. It may be added to the delayed 492 * event queue if the document is suppressing events. 493 * 494 * aEventMessage should be either eFocus or eBlur. 495 * 496 * aWindowRaised should only be true if called from WindowRaised. 497 */ 498 MOZ_CAN_RUN_SCRIPT void SendFocusOrBlurEvent( 499 mozilla::EventMessage aEventMessage, mozilla::PresShell* aPresShell, 500 Document* aDocument, mozilla::dom::EventTarget* aTarget, 501 bool aWindowRaised, bool aIsRefocus = false, 502 mozilla::dom::EventTarget* aRelatedTarget = nullptr); 503 /** 504 * Fire a focus or blur event at aTarget. 505 * 506 * aEventMessage should be either eFocus or eBlur. 507 * For blur events, aFocusMethod should normally be non-zero. 508 * 509 * aWindowRaised should only be true if called from WindowRaised. 510 */ 511 MOZ_CAN_RUN_SCRIPT void FireFocusOrBlurEvent( 512 mozilla::EventMessage aEventMessage, mozilla::PresShell* aPresShell, 513 mozilla::dom::EventTarget* aTarget, bool aWindowRaised, 514 bool aIsRefocus = false, 515 mozilla::dom::EventTarget* aRelatedTarget = nullptr); 516 517 /** 518 * Fire a focusin or focusout event 519 * 520 * aEventMessage should be either eFocusIn or eFocusOut. 521 * 522 * aTarget is the content the event will fire on (the object that gained 523 * focus for focusin, the object blurred for focusout). 524 * 525 * aCurrentFocusedWindow is the window focused before the focus/blur event 526 * was fired. 527 * 528 * aCurrentFocusedContent is the content focused before the focus/blur event 529 * was fired. 530 * 531 * aRelatedTarget is the content related to the event (the object 532 * losing focus for focusin, the object getting focus for focusout). 533 */ 534 MOZ_CAN_RUN_SCRIPT void FireFocusInOrOutEvent( 535 mozilla::EventMessage aEventMessage, mozilla::PresShell* aPresShell, 536 mozilla::dom::EventTarget* aTarget, 537 nsPIDOMWindowOuter* aCurrentFocusedWindow, 538 nsIContent* aCurrentFocusedContent, 539 mozilla::dom::EventTarget* aRelatedTarget = nullptr); 540 541 /** 542 * Scrolls aContent into view unless the FLAG_NOSCROLL flag is set. 543 */ 544 MOZ_CAN_RUN_SCRIPT 545 void ScrollIntoView(mozilla::PresShell* aPresShell, nsIContent* aContent, 546 uint32_t aFlags); 547 548 /** 549 * Updates the caret positon and visibility to match the focus. 550 * 551 * aMoveCaretToFocus should be true to move the caret to aContent. 552 * 553 * aUpdateVisibility should be true to update whether the caret is 554 * visible or not. 555 */ 556 MOZ_CAN_RUN_SCRIPT void UpdateCaret(bool aMoveCaretToFocus, 557 bool aUpdateVisibility, 558 nsIContent* aContent); 559 560 /** 561 * Helper method to move the caret to the focused element aContent. 562 */ 563 MOZ_CAN_RUN_SCRIPT void MoveCaretToFocus(mozilla::PresShell* aPresShell, 564 nsIContent* aContent); 565 566 /** 567 * Makes the caret visible or not, depending on aVisible. 568 */ 569 nsresult SetCaretVisible(mozilla::PresShell* aPresShell, bool aVisible, 570 nsIContent* aContent); 571 572 // the remaining functions are used for tab key and document-navigation 573 574 /** 575 * Retrieves the start and end points of the current selection for 576 * aDocument and stores them in aStartContent and aEndContent. 577 */ 578 void GetSelectionLocation(Document* aDocument, mozilla::PresShell* aPresShell, 579 nsIContent** aStartContent, 580 nsIContent** aEndContent); 581 582 /** 583 * Retrieve the next tabbable element in scope owned by aOwner, using 584 * focusability and tabindex to determine the tab order. 585 * 586 * aOwner is the owner of scope to search in. 587 * 588 * aStartContent is the starting point for this call of this method. 589 * 590 * aOriginalStartContent is the initial starting point for sequential 591 * navigation. 592 * 593 * aForward should be true for forward navigation or false for backward 594 * navigation. 595 * 596 * aCurrentTabIndex is the current tabindex. 597 * 598 * aIgnoreTabIndex to ignore the current tabindex and find the element 599 * irrespective or the tab index. 600 * 601 * aForDocumentNavigation informs whether we're navigating only through 602 * documents. 603 * 604 * aSkipOwner to skip owner while searching. The flag is set when caller is 605 * |GetNextTabbableContent| in order to let caller handle owner. 606 * 607 * aReachedToEndForDocumentNavigation is true when this is a document 608 * navigation and the focus algorithm has reached to the end of the top-level 609 * document. 610 * 611 * NOTE: 612 * Consider the method searches downwards in flattened subtree 613 * rooted at aOwner. 614 */ 615 MOZ_CAN_RUN_SCRIPT nsIContent* GetNextTabbableContentInScope( 616 nsIContent* aOwner, nsIContent* aStartContent, 617 nsIContent* aOriginalStartContent, bool aForward, 618 int32_t aCurrentTabIndex, bool aIgnoreTabIndex, 619 bool aForDocumentNavigation, bool aNavigateByKey, bool aSkipOwner, 620 bool aReachedToEndForDocumentNavigation); 621 622 /** 623 * Retrieve the next tabbable element in scope including aStartContent 624 * and the scope's ancestor scopes, using focusability and tabindex to 625 * determine the tab order. 626 * 627 * aStartOwner is the scope owner of the aStartContent. 628 * 629 * aStartContent an in/out paremeter. It as input is the starting point 630 * for this call of this method; as output it is the shadow host in 631 * light DOM if the next tabbable element is not found in shadow DOM, 632 * in order to continue searching in light DOM. 633 * 634 * aOriginalStartContent is the initial starting point for sequential 635 * navigation. 636 * 637 * aForward should be true for forward navigation or false for backward 638 * navigation. 639 * 640 * aCurrentTabIndex returns tab index of shadow host in light DOM if the 641 * next tabbable element is not found in shadow DOM, in order to continue 642 * searching in light DOM. 643 * 644 * aIgnoreTabIndex to ignore the current tabindex and find the element 645 * irrespective or the tab index. 646 * 647 * aForDocumentNavigation informs whether we're navigating only through 648 * documents. 649 * 650 * aNavigateByKey to move focus by keyboard as a side effect of computing the 651 * next target. 652 * 653 * aReachedToEndForDocumentNavigation is true when this is a document 654 * navigation and the focus algorithm has reached to the end of the top-level 655 * document. 656 * 657 * NOTE: 658 * Consider the method searches upwards in all shadow host- or slot-rooted 659 * flattened subtrees that contains aStartContent as non-root, except 660 * the flattened subtree rooted at shadow host in light DOM. 661 */ 662 MOZ_CAN_RUN_SCRIPT nsIContent* GetNextTabbableContentInAncestorScopes( 663 nsIContent* aStartOwner, nsCOMPtr<nsIContent>& aStartContent /* inout */, 664 nsIContent* aOriginalStartContent, bool aForward, 665 int32_t* aCurrentTabIndex, bool* aIgnoreTabIndex, 666 bool aForDocumentNavigation, bool aNavigateByKey, 667 bool aReachedToEndForDocumentNavigation); 668 669 /** 670 * Retrieve the next tabbable element within a document, using focusability 671 * and tabindex to determine the tab order. The element is returned in 672 * aResultContent. 673 * 674 * aRootContent is the root node -- nodes above this will not be examined. 675 * Typically this will be the root node of a document, but could also be 676 * a popup node. 677 * 678 * aOriginalStartContent is the content which was originally the starting 679 * node, in the case of recursive or looping calls. 680 * 681 * aStartContent is the starting point for this call of this method. 682 * If aStartContent doesn't have visual representation, the next content 683 * object, which does have a primary frame, will be used as a start. 684 * If that content object is focusable, the method may return it. 685 * 686 * aForward should be true for forward navigation or false for backward 687 * navigation. 688 * 689 * aCurrentTabIndex is the current tabindex. 690 * 691 * aIgnoreTabIndex to ignore the current tabindex and find the element 692 * irrespective or the tab index. This will be true when a selection is 693 * active, since we just want to focus the next element in tree order 694 * from where the selection is. Similarly, if the starting element isn't 695 * focusable, since it doesn't really have a defined tab index. 696 * 697 * aSkipPopover should be true to avoid an invoker triggering to step into 698 * the popover that was already been visited again. 699 * 700 * aNavigateByKey to move focus by keyboard as a side effect of computing the 701 * next target. 702 * 703 * aReachedToEndForDocumentNavigation is true when this is a document 704 * navigation and the focus algorithm has reached to the end of the top-level 705 * document. 706 */ 707 MOZ_CAN_RUN_SCRIPT nsresult GetNextTabbableContent( 708 mozilla::PresShell* aPresShell, nsIContent* aRootContent, 709 nsIContent* aOriginalStartContent, nsIContent* aStartContent, 710 bool aForward, int32_t aCurrentTabIndex, bool aIgnoreTabIndex, 711 bool aForDocumentNavigation, bool aNavigateByKey, bool aSkipPopover, 712 bool aReachedToEndForDocumentNavigation, nsIContent** aResultContent); 713 714 /** 715 * Get the next tabbable image map area and returns it. 716 * 717 * aForward should be true for forward navigation or false for backward 718 * navigation. 719 * 720 * aCurrentTabIndex is the current tabindex. 721 * 722 * aImageContent is the image. 723 * 724 * aStartContent is the current image map area. 725 */ 726 nsIContent* GetNextTabbableMapArea(bool aForward, int32_t aCurrentTabIndex, 727 mozilla::dom::Element* aImageContent, 728 nsIContent* aStartContent); 729 730 /** 731 * Return the next valid tabindex value after aCurrentTabIndex, if aForward 732 * is true, or the previous tabindex value if aForward is false. aParent is 733 * the node from which to start looking for tab indicies. 734 */ 735 int32_t GetNextTabIndex(nsIContent* aParent, int32_t aCurrentTabIndex, 736 bool aForward); 737 738 /** 739 * Focus the first focusable content within the document with a root node of 740 * aRootContent. For content documents, this will be aRootContent itself, but 741 * for chrome documents, this will locate the next focusable content. 742 * 743 * aReachedToEndForDocumentNavigation is true when the focus algorithm has 744 * reached to the end of the top-level document. 745 */ 746 MOZ_CAN_RUN_SCRIPT nsresult 747 FocusFirst(mozilla::dom::Element* aRootContent, nsIContent** aNextContent, 748 bool aReachedToEndForDocumentNavigation); 749 750 /** 751 * Retrieves and returns the root node from aDocument to be focused. Will 752 * return null if the root node cannot be focused. There are several reasons 753 * for this: 754 * 755 * - if aForDocumentNavigation is false and aWindow is a chrome shell. 756 * - if aCheckVisibility is true and the aWindow is not visible. 757 * - if aDocument is a frameset document. 758 */ 759 mozilla::dom::Element* GetRootForFocus(nsPIDOMWindowOuter* aWindow, 760 Document* aDocument, 761 bool aForDocumentNavigation, 762 bool aCheckVisibility); 763 764 /** 765 * Retrieves and returns the root node as with GetRootForFocus but only if 766 * aContent is a frame with a valid child document. 767 */ 768 mozilla::dom::Element* GetRootForChildDocument(nsIContent* aContent); 769 770 /** 771 * Retreives a focusable element within the current selection of aWindow. 772 * Currently, this only detects links. 773 * 774 * This is used when MoveFocus is called with a type of MOVEFOCUS_CARET, 775 * which is used, for example, to focus links as the caret is moved over 776 * them. 777 */ 778 void GetFocusInSelection(nsPIDOMWindowOuter* aWindow, 779 nsIContent* aStartSelection, 780 nsIContent* aEndSelection, 781 nsIContent** aFocusedContent); 782 783 private: 784 /** 785 * Given an element, which must be the focused element, activate the remote 786 * frame it embeds, if any. 787 */ 788 void ActivateRemoteFrameIfNeeded(mozilla::dom::Element&, uint64_t aActionId); 789 790 // Notify that the focus state of aElement has changed. Note that we need to 791 // pass in whether the window should show a focus ring before the 792 // SetFocusedNode call on it happened when losing focus and after the 793 // SetFocusedNode call when gaining focus, which is why that information needs 794 // to be an explicit argument instead of just passing in the window and asking 795 // it whether it should show focus rings: in the losing focus case that 796 // information could be wrong. 797 // 798 // aShouldShowFocusRing is only relevant if aGettingFocus is true. 799 static void NotifyFocusStateChange(mozilla::dom::Element* aElement, 800 mozilla::dom::Element* aElementToFocus, 801 int32_t aFlags, bool aGettingFocus, 802 bool aShouldShowFocusRing); 803 804 void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow, uint64_t aActionId, 805 bool aSyncBrowsingContext = true); 806 807 MOZ_CAN_RUN_SCRIPT bool TryDocumentNavigation(nsIContent* aCurrentContent, 808 bool* aCheckSubDocument, 809 nsIContent** aResultContent); 810 811 MOZ_CAN_RUN_SCRIPT bool TryToMoveFocusToSubDocument( 812 nsIContent* aCurrentContent, nsIContent* aOriginalStartContent, 813 bool aForward, bool aForDocumentNavigation, bool aNavigateByKey, 814 bool aReachedToEndForDocumentNavigation, nsIContent** aResultContent); 815 816 // Sets the focused BrowsingContext and, if appropriate, syncs it to 817 // other processes. 818 void SetFocusedBrowsingContext(mozilla::dom::BrowsingContext* aContext, 819 uint64_t aActionId); 820 821 // Content-only 822 // Called when receiving an IPC message about another process setting 823 // the focused BrowsingContext. 824 void SetFocusedBrowsingContextFromOtherProcess( 825 mozilla::dom::BrowsingContext* aContext, uint64_t aActionId); 826 827 // Chrome-only 828 // When returning true, sets the chrome process notion of what 829 // BrowsingContext is focused in content. When returning false, 830 // ignores the attempt to set as out-of-sequence. 831 bool SetFocusedBrowsingContextInChrome( 832 mozilla::dom::BrowsingContext* aContext, uint64_t aActionId); 833 834 void InsertNewFocusActionId(uint64_t aActionId); 835 836 bool ProcessPendingActiveBrowsingContextActionId(uint64_t aActionId, 837 bool aSettingToNonNull); 838 839 bool ProcessPendingFocusedBrowsingContextActionId(uint64_t aActionId); 840 841 public: 842 // Chrome-only 843 // Gets the chrome process notion of what BrowsingContext is focused 844 // in content. 845 mozilla::dom::BrowsingContext* GetFocusedBrowsingContextInChrome(); 846 847 // Chrome-only 848 // Notifies the focus manager that BrowsingContext::Detach was called 849 // on a BrowsingContext so that pointers to it can be forgotten. 850 void BrowsingContextDetached(mozilla::dom::BrowsingContext* aContext); 851 852 private: 853 // Content-only 854 // Sets the BrowsingContext corresponding to top-level Web content 855 // in the frontmost tab if focus is in Web content. 856 void SetActiveBrowsingContextInContent( 857 mozilla::dom::BrowsingContext* aContext, uint64_t aActionId, 858 bool aIsEnteringBFCache); 859 860 // Content-only 861 // Receives notification of another process setting the top-level Web 862 // content as being in the frontmost tab with focus in Web content. 863 void SetActiveBrowsingContextFromOtherProcess( 864 mozilla::dom::BrowsingContext* aContext, uint64_t aActionId); 865 866 // Content-only 867 // Receives notification that another process determined that focus 868 // moved to chrome so a particular BrowsingContext is no longer the 869 // "active" one. 870 void UnsetActiveBrowsingContextFromOtherProcess( 871 mozilla::dom::BrowsingContext* aContext, uint64_t aActionId); 872 873 // Content-only 874 // Receives a notification from parent that this content process's 875 // attempt to set the active browsing context was late and the 876 // prevailing browsing context is instead the second argument of 877 // this method call. This should be ignored if the first argument 878 // doesn't match the latest action id associated with setting the 879 // active browsing context in this process, because in that case, 880 // this revision is late. 881 void ReviseActiveBrowsingContext(uint64_t aOldActionId, 882 mozilla::dom::BrowsingContext* aContext, 883 uint64_t aNewActionId); 884 885 // Receives a notification from parent that this content process's 886 // attempt to set the focused browsing context was late and the 887 // prevailing browsing context is instead the second argument of 888 // this method call. This should be ignored if the first argument 889 // doesn't match the latest action id associated with setting the 890 // active browsing context in this process, because in that case, 891 // this revision is late. 892 void ReviseFocusedBrowsingContext(uint64_t aOldActionId, 893 mozilla::dom::BrowsingContext* aContext, 894 uint64_t aNewActionId); 895 896 // Chrome-only 897 // Sets the chrome process notion of what content believes to be 898 // the top-level BrowsingContext in the frontmost tab when focus 899 // is in Web content. 900 // Returns true if set and false if ignored. 901 bool SetActiveBrowsingContextInChrome(mozilla::dom::BrowsingContext* aContext, 902 uint64_t aActionId); 903 904 void FocusedElementMayHaveMoved(nsIContent* aContent, nsINode* aOldParent); 905 906 public: 907 // Chrome-only 908 // Gets the chrome process notion of what content believes to be 909 // the top-level BrowsingContext in the frontmost tab when focus 910 // is in Web content. 911 mozilla::dom::BrowsingContext* GetActiveBrowsingContextInChrome(); 912 913 uint64_t GetActionIdForActiveBrowsingContextInChrome() const; 914 915 uint64_t GetActionIdForFocusedBrowsingContextInChrome() const; 916 917 static uint64_t GenerateFocusActionId(); 918 919 // This function works very similar to 920 // https://html.spec.whatwg.org/#get-the-focusable-area 921 static mozilla::dom::Element* GetTheFocusableArea( 922 mozilla::dom::Element* aTarget, uint32_t aFlags); 923 924 // Returns true if it's an area element with one or more shapes that are 925 // focusable areas. 926 static bool IsAreaElementFocusable(mozilla::dom::HTMLAreaElement& aArea); 927 928 private: 929 // In the chrome process, the currently active and front-most top-most 930 // window. Not supposed to be used in a meaningful way in content 931 // processes. For legacy reasons, this exists as a separate field 932 // instead of being derived from mFocusedWindow when needed, because 933 // the defined relation that mActiveWindow is supposed to be the same 934 // as or ancestor of mFocusedWindow is temporarily broken when a 935 // window is being raised or lowered. 936 nsCOMPtr<nsPIDOMWindowOuter> mActiveWindow; 937 938 // In a content process, the BrowsingContext corresponding to top-level 939 // Web content in the active tab or nullptr if focus is not in a 940 // BrowsingContextGroup that this process participates in. Synced 941 // across processes in a BrowsingContextGroup. This field exists 942 // separately from mFocusedBrowsingContextInContent instead of being being 943 // derived from it, because for legacy reasons the relation 944 // mFocusedBrowsingContextInContent->Top() == mActiveBrowsingContextInContent 945 // is temporarily broken when a window is being raised or lowered. 946 // Not supposed to be used in a meaningful way in the chrome process. 947 RefPtr<mozilla::dom::BrowsingContext> mActiveBrowsingContextInContent; 948 949 // If this content process set mActiveBrowsingContextInContent, this 950 // field holds the corresponding actionId so that 951 // mActiveBrowsingContextInContent can be revised of the parent rejects 952 // the update. This field is used for accepting revisions only if nothing 953 // else has updated mActiveBrowsingContextInContent before the revision 954 // arrives. 955 uint64_t mActionIdForActiveBrowsingContextInContent; 956 957 uint64_t mActionIdForActiveBrowsingContextInChrome; 958 959 // If this content process set mFocusedBrowsingContextInContent, this 960 // field holds the corresponding actionId so that 961 // mFocusedBrowsingContextInContent can be revised of the parent rejects 962 // the update. This field is used for accepting revisions only if nothing 963 // else has updated mFocusedBrowsingContextInContent before the revision 964 // arrives. 965 uint64_t mActionIdForFocusedBrowsingContextInContent; 966 967 uint64_t mActionIdForFocusedBrowsingContextInChrome; 968 969 // Whether or not mActiveBrowsingContextInContent was set from another process 970 // or from this process. 971 bool mActiveBrowsingContextInContentSetFromOtherProcess; 972 973 // This is the chrome process notion of content's 974 // mActiveBrowsingContextInContent. Avoiding field reuse for different 975 // semantics in different process types to make it easier to catch bugs. 976 RefPtr<mozilla::dom::BrowsingContext> mActiveBrowsingContextInChrome; 977 978 // the child or top-level window that is currently focused. In the chrome 979 // process, when a window isn't being raised or lowered, this window will 980 // either be the same window as mActiveWindow or a descendant of it. 981 // Except during shutdown use SetFocusedWindowInternal to set mFocusedWindow! 982 nsCOMPtr<nsPIDOMWindowOuter> mFocusedWindow; 983 984 // The focused BrowsingContext if this is a chrome process and focus is 985 // in chrome or if this is a content process and focus is in Web content 986 // in this BrowsingContextGroup. nullptr otherwise. 987 // Except during shutdown, must be set via SetFocusedWindowInternal which 988 // calls SetFocusedBrowsingContext or if the value is coming in via IPC 989 // via SetFocusedBrowsingContextFromOtherProcess. 990 RefPtr<mozilla::dom::BrowsingContext> mFocusedBrowsingContextInContent; 991 992 // This is the chrome process notion of content's 993 // mFocusedBrowsingContextInContent. Avoiding field reuse for different 994 // semantics in different process types to make it easier to catch bugs. 995 RefPtr<mozilla::dom::BrowsingContext> mFocusedBrowsingContextInChrome; 996 997 // the currently focused content if in-process or the XUL browser in which 998 // Web content focus resides. Always inside mFocusedWindow. When a window 999 // isn't being raised or lowered, this is a cached copy of the 1000 // mFocusedWindow's current content. This may be null if no content is 1001 // focused. 1002 RefPtr<mozilla::dom::Element> mFocusedElement; 1003 1004 // keep track of a window while it is being lowered 1005 nsCOMPtr<nsPIDOMWindowOuter> mWindowBeingLowered; 1006 1007 // synchronized actions cannot be interrupted with events, so queue these up 1008 // and fire them later. 1009 nsTArray<nsDelayedBlurOrFocusEvent> mDelayedBlurFocusEvents; 1010 1011 // Array of focus action ids for which we haven't seen an active browsing 1012 // context set yet. As set is allowed to overwrite an unset. Therefore, 1013 // an unset removes earlier ids but not the matching id. A set removes 1014 // earlier ids and the matching id. 1015 // 1016 // Conceptually, active browsing context shouldn't have to exist as a 1017 // field, because it should be possible to always derive it from the 1018 // focused browsing context. Unfortunately, for legacy reasons, this 1019 // is not the case while a window is being raised or lowered. 1020 // 1021 // Conceptually, it should be possible for the parent to manage the 1022 // active browsing context. Unfortunately, for legacy reasons, the 1023 // code for setting the active browsing context needs to reside in 1024 // the content process to retain the existing and test-passing code 1025 // flow. 1026 // 1027 // This, obviously, raises the issue of content processes racing to 1028 // set the active browsing context. In particular, there is a pattern 1029 // that the parent initiates actions that cause multiple content 1030 // processes to mutate the active browsing context at almost the 1031 // same time. When two native browser windows change order, the 1032 // lowering isn't distinguished from the case of lowering the 1033 // entire app. For this reason, the owner of the previous active 1034 // browsing context tries to unset it and at almost the same time 1035 // the another content process sets a new active browsing context. 1036 // If the IPC messages for these unset and set actions were to 1037 // arrive in the wrong order, this could get in the wrong state. 1038 // 1039 // To address this issue, the parent manages an authortative order 1040 // of attempts to (un)set the active browsing context using the 1041 // array mPendingActiveBrowsingContextActions. 1042 // 1043 // A process reserves a slot in the order by calling 1044 // GenerateFocusActionId(). Per one call to GenerateFocusActionId(), 1045 // there may be at most one action to set the active browsing context 1046 // to a new value. There may be logically prior attempts to unset it 1047 // (i.e. set it to nullptr). That is, if there are both attempts to 1048 // unset and set the active browsing context with the same action id, 1049 // the attempt to set to a non-null value wins. 1050 // 1051 // The completion of an action from reserting the slot in the order 1052 // and actually performing the setting of the active browsing context 1053 // may span multiple processes and IPC messages. 1054 // 1055 // The at-most-once property is not asserted, because the process 1056 // claiming the position in the order and the process setting the 1057 // active browsing context with that actionId may be different, and 1058 // the act of using an actionId to set the active browsing context 1059 // is used to delete stale items from the array to avoid excessive 1060 // growth of the array. 1061 nsTArray<uint64_t> mPendingActiveBrowsingContextActions; 1062 1063 // Like mPendingActiveBrowsingContextActions but for the focused 1064 // browsing context. 1065 nsTArray<uint64_t> mPendingFocusedBrowsingContextActions; 1066 1067 // If set to true, layout of the document of the event target should be 1068 // flushed before handling focus depending events. 1069 bool mEventHandlingNeedsFlush; 1070 1071 static bool sTestMode; 1072 1073 // Process-specific counter for maintaining the prosess-specific 1074 // uniqueness of actionIds. 1075 static uint64_t sFocusActionCounter; 1076 1077 // the single focus manager 1078 static mozilla::StaticRefPtr<nsFocusManager> sInstance; 1079 }; 1080 1081 nsresult NS_NewFocusManager(nsIFocusManager** aResult); 1082 1083 #endif