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