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