LCOV - code coverage report
Current view: top level - dom/base - nsFocusManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 209 1827 11.4 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "mozilla/dom/TabParent.h"
       8             : 
       9             : #include "nsFocusManager.h"
      10             : 
      11             : #include "AccessibleCaretEventHub.h"
      12             : #include "ChildIterator.h"
      13             : #include "nsIInterfaceRequestorUtils.h"
      14             : #include "nsGkAtoms.h"
      15             : #include "nsGlobalWindow.h"
      16             : #include "nsContentUtils.h"
      17             : #include "nsDocument.h"
      18             : #include "nsIContentParent.h"
      19             : #include "nsPIDOMWindow.h"
      20             : #include "nsIDOMChromeWindow.h"
      21             : #include "nsIHTMLDocument.h"
      22             : #include "nsIDocShell.h"
      23             : #include "nsIDocShellTreeOwner.h"
      24             : #include "nsIFormControl.h"
      25             : #include "nsLayoutUtils.h"
      26             : #include "nsIPresShell.h"
      27             : #include "nsFrameTraversal.h"
      28             : #include "nsIWebNavigation.h"
      29             : #include "nsCaret.h"
      30             : #include "nsIBaseWindow.h"
      31             : #include "nsIXULWindow.h"
      32             : #include "nsViewManager.h"
      33             : #include "nsFrameSelection.h"
      34             : #include "mozilla/dom/Selection.h"
      35             : #include "nsXULPopupManager.h"
      36             : #include "nsMenuPopupFrame.h"
      37             : #include "nsIScriptObjectPrincipal.h"
      38             : #include "nsIPrincipal.h"
      39             : #include "nsIObserverService.h"
      40             : #include "nsIObjectFrame.h"
      41             : #include "nsBindingManager.h"
      42             : #include "nsStyleCoord.h"
      43             : #include "TabChild.h"
      44             : #include "nsFrameLoader.h"
      45             : #include "nsNumberControlFrame.h"
      46             : #include "nsNetUtil.h"
      47             : #include "nsRange.h"
      48             : 
      49             : #include "mozilla/ContentEvents.h"
      50             : #include "mozilla/dom/Element.h"
      51             : #include "mozilla/dom/HTMLInputElement.h"
      52             : #include "mozilla/dom/HTMLSlotElement.h"
      53             : #include "mozilla/dom/Text.h"
      54             : #include "mozilla/EventDispatcher.h"
      55             : #include "mozilla/EventStateManager.h"
      56             : #include "mozilla/EventStates.h"
      57             : #include "mozilla/HTMLEditor.h"
      58             : #include "mozilla/IMEStateManager.h"
      59             : #include "mozilla/LookAndFeel.h"
      60             : #include "mozilla/Preferences.h"
      61             : #include "mozilla/Services.h"
      62             : #include "mozilla/Unused.h"
      63             : #include <algorithm>
      64             : 
      65             : #ifdef MOZ_XUL
      66             : #include "nsIDOMXULMenuListElement.h"
      67             : #endif
      68             : 
      69             : #ifdef ACCESSIBILITY
      70             : #include "nsAccessibilityService.h"
      71             : #endif
      72             : 
      73             : #ifndef XP_MACOSX
      74             : #include "nsIScriptError.h"
      75             : #endif
      76             : 
      77             : using namespace mozilla;
      78             : using namespace mozilla::dom;
      79             : using namespace mozilla::widget;
      80             : 
      81             : // Two types of focus pr logging are available:
      82             : //   'Focus' for normal focus manager calls
      83             : //   'FocusNavigation' for tab and document navigation
      84             : LazyLogModule gFocusLog("Focus");
      85             : LazyLogModule gFocusNavigationLog("FocusNavigation");
      86             : 
      87             : #define LOGFOCUS(args) MOZ_LOG(gFocusLog, mozilla::LogLevel::Debug, args)
      88             : #define LOGFOCUSNAVIGATION(args) MOZ_LOG(gFocusNavigationLog, mozilla::LogLevel::Debug, args)
      89             : 
      90             : #define LOGTAG(log, format, content)                            \
      91             :   if (MOZ_LOG_TEST(log, LogLevel::Debug)) {                         \
      92             :     nsAutoCString tag(NS_LITERAL_CSTRING("(none)"));            \
      93             :     if (content) {                                              \
      94             :       content->NodeInfo()->NameAtom()->ToUTF8String(tag);       \
      95             :     }                                                           \
      96             :     MOZ_LOG(log, LogLevel::Debug, (format, tag.get()));             \
      97             :   }
      98             : 
      99             : #define LOGCONTENT(format, content) LOGTAG(gFocusLog, format, content)
     100             : #define LOGCONTENTNAVIGATION(format, content) LOGTAG(gFocusNavigationLog, format, content)
     101             : 
     102           0 : struct nsDelayedBlurOrFocusEvent
     103             : {
     104           0 :   nsDelayedBlurOrFocusEvent(EventMessage aEventMessage,
     105             :                             nsIPresShell* aPresShell,
     106             :                             nsIDocument* aDocument,
     107             :                             EventTarget* aTarget,
     108             :                             EventTarget* aRelatedTarget)
     109           0 :     : mPresShell(aPresShell)
     110             :     , mDocument(aDocument)
     111             :     , mTarget(aTarget)
     112             :     , mEventMessage(aEventMessage)
     113           0 :     , mRelatedTarget(aRelatedTarget)
     114             :  {
     115           0 :  }
     116             : 
     117           0 :   nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent& aOther)
     118           0 :     : mPresShell(aOther.mPresShell)
     119             :     , mDocument(aOther.mDocument)
     120             :     , mTarget(aOther.mTarget)
     121           0 :     , mEventMessage(aOther.mEventMessage)
     122             :   {
     123           0 :   }
     124             : 
     125             :   nsCOMPtr<nsIPresShell> mPresShell;
     126             :   nsCOMPtr<nsIDocument> mDocument;
     127             :   nsCOMPtr<EventTarget> mTarget;
     128             :   EventMessage mEventMessage;
     129             :   nsCOMPtr<EventTarget> mRelatedTarget;
     130             : };
     131             : 
     132             : inline void ImplCycleCollectionUnlink(nsDelayedBlurOrFocusEvent& aField)
     133             : {
     134             :   aField.mPresShell = nullptr;
     135             :   aField.mDocument = nullptr;
     136             :   aField.mTarget = nullptr;
     137             :   aField.mRelatedTarget = nullptr;
     138             : }
     139             : 
     140             : inline void
     141           0 : ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
     142             :                             nsDelayedBlurOrFocusEvent& aField,
     143             :                             const char* aName,
     144             :                             uint32_t aFlags = 0)
     145             : {
     146           0 :   CycleCollectionNoteChild(aCallback, aField.mPresShell.get(), aName, aFlags);
     147           0 :   CycleCollectionNoteChild(aCallback, aField.mDocument.get(), aName, aFlags);
     148           0 :   CycleCollectionNoteChild(aCallback, aField.mTarget.get(), aName, aFlags);
     149           0 :   CycleCollectionNoteChild(aCallback, aField.mRelatedTarget.get(), aName, aFlags);
     150           0 : }
     151             : 
     152           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager)
     153           0 :   NS_INTERFACE_MAP_ENTRY(nsIFocusManager)
     154           0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     155           0 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     156           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager)
     157           0 : NS_INTERFACE_MAP_END
     158             : 
     159           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager)
     160           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager)
     161             : 
     162           0 : NS_IMPL_CYCLE_COLLECTION(nsFocusManager,
     163             :                          mActiveWindow,
     164             :                          mFocusedWindow,
     165             :                          mFocusedElement,
     166             :                          mFirstBlurEvent,
     167             :                          mFirstFocusEvent,
     168             :                          mWindowBeingLowered,
     169             :                          mDelayedBlurFocusEvents,
     170             :                          mMouseButtonEventHandlingDocument)
     171             : 
     172             : nsFocusManager* nsFocusManager::sInstance = nullptr;
     173             : bool nsFocusManager::sMouseFocusesFormControl = false;
     174             : bool nsFocusManager::sTestMode = false;
     175             : 
     176             : static const char* kObservedPrefs[] = {
     177             :   "accessibility.browsewithcaret",
     178             :   "accessibility.tabfocus_applies_to_xul",
     179             :   "accessibility.mouse_focuses_formcontrol",
     180             :   "focusmanager.testmode",
     181             :   nullptr
     182             : };
     183             : 
     184           0 : nsFocusManager::nsFocusManager()
     185           0 :   : mEventHandlingNeedsFlush(false)
     186           0 : { }
     187             : 
     188           0 : nsFocusManager::~nsFocusManager()
     189             : {
     190           0 :   Preferences::RemoveObservers(this, kObservedPrefs);
     191             : 
     192           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     193           0 :   if (obs) {
     194           0 :     obs->RemoveObserver(this, "xpcom-shutdown");
     195             :   }
     196           0 : }
     197             : 
     198             : // static
     199             : nsresult
     200           0 : nsFocusManager::Init()
     201             : {
     202           0 :   nsFocusManager* fm = new nsFocusManager();
     203           0 :   NS_ADDREF(fm);
     204           0 :   sInstance = fm;
     205             : 
     206           0 :   nsIContent::sTabFocusModelAppliesToXUL =
     207           0 :     Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
     208             :                          nsIContent::sTabFocusModelAppliesToXUL);
     209             : 
     210           0 :   sMouseFocusesFormControl =
     211           0 :     Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false);
     212             : 
     213           0 :   sTestMode = Preferences::GetBool("focusmanager.testmode", false);
     214             : 
     215           0 :   Preferences::AddWeakObservers(fm, kObservedPrefs);
     216             : 
     217           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     218           0 :   if (obs) {
     219           0 :     obs->AddObserver(fm, "xpcom-shutdown", true);
     220             :   }
     221             : 
     222           0 :   return NS_OK;
     223             : }
     224             : 
     225             : // static
     226             : void
     227           0 : nsFocusManager::Shutdown()
     228             : {
     229           0 :   NS_IF_RELEASE(sInstance);
     230           0 : }
     231             : 
     232             : NS_IMETHODIMP
     233           0 : nsFocusManager::Observe(nsISupports *aSubject,
     234             :                         const char *aTopic,
     235             :                         const char16_t *aData)
     236             : {
     237           0 :   if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     238           0 :     nsDependentString data(aData);
     239           0 :     if (data.EqualsLiteral("accessibility.browsewithcaret")) {
     240           0 :       UpdateCaretForCaretBrowsingMode();
     241             :     }
     242           0 :     else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
     243           0 :       nsIContent::sTabFocusModelAppliesToXUL =
     244           0 :         Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
     245             :                              nsIContent::sTabFocusModelAppliesToXUL);
     246             :     }
     247           0 :     else if (data.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
     248           0 :       sMouseFocusesFormControl =
     249           0 :         Preferences::GetBool("accessibility.mouse_focuses_formcontrol",
     250             :                              false);
     251             :     }
     252           0 :     else if (data.EqualsLiteral("focusmanager.testmode")) {
     253           0 :       sTestMode = Preferences::GetBool("focusmanager.testmode", false);
     254             :     }
     255           0 :   } else if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
     256           0 :     mActiveWindow = nullptr;
     257           0 :     mFocusedWindow = nullptr;
     258           0 :     mFocusedElement = nullptr;
     259           0 :     mFirstBlurEvent = nullptr;
     260           0 :     mFirstFocusEvent = nullptr;
     261           0 :     mWindowBeingLowered = nullptr;
     262           0 :     mDelayedBlurFocusEvents.Clear();
     263           0 :     mMouseButtonEventHandlingDocument = nullptr;
     264             :   }
     265             : 
     266           0 :   return NS_OK;
     267             : }
     268             : 
     269             : // given a frame content node, retrieve the nsIDOMWindow displayed in it
     270             : static nsPIDOMWindowOuter*
     271           0 : GetContentWindow(nsIContent* aContent)
     272             : {
     273           0 :   nsIDocument* doc = aContent->GetComposedDoc();
     274           0 :   if (doc) {
     275           0 :     nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
     276           0 :     if (subdoc)
     277           0 :       return subdoc->GetWindow();
     278             :   }
     279             : 
     280             :   return nullptr;
     281             : }
     282             : 
     283             : bool
     284           0 : nsFocusManager::IsFocused(nsIContent* aContent)
     285             : {
     286           0 :   if (!aContent || !mFocusedElement) {
     287             :     return false;
     288             :   }
     289           0 :   return aContent == mFocusedElement;
     290             : }
     291             : 
     292             : // get the current window for the given content node
     293             : static nsPIDOMWindowOuter*
     294           0 : GetCurrentWindow(nsIContent* aContent)
     295             : {
     296           0 :   nsIDocument* doc = aContent->GetComposedDoc();
     297           0 :   return doc ? doc->GetWindow() : nullptr;
     298             : }
     299             : 
     300             : // static
     301             : Element*
     302           0 : nsFocusManager::GetFocusedDescendant(nsPIDOMWindowOuter* aWindow,
     303             :                                      SearchRange aSearchRange,
     304             :                                      nsPIDOMWindowOuter** aFocusedWindow)
     305             : {
     306           0 :   NS_ENSURE_TRUE(aWindow, nullptr);
     307             : 
     308             :   *aFocusedWindow = nullptr;
     309             : 
     310             :   Element* currentElement = nullptr;
     311             :   nsPIDOMWindowOuter* window = aWindow;
     312             :   for (;;) {
     313           0 :     *aFocusedWindow = window;
     314           0 :     currentElement = window->GetFocusedElement();
     315           0 :     if (!currentElement || aSearchRange == eOnlyCurrentWindow) {
     316             :       break;
     317             :     }
     318             : 
     319           0 :     window = GetContentWindow(currentElement);
     320           0 :     if (!window) {
     321             :       break;
     322             :     }
     323             : 
     324           0 :     if (aSearchRange == eIncludeAllDescendants) {
     325             :       continue;
     326             :     }
     327             : 
     328           0 :     MOZ_ASSERT(aSearchRange == eIncludeVisibleDescendants);
     329             : 
     330             :     // If the child window doesn't have PresShell, it means the window is
     331             :     // invisible.
     332           0 :     nsIDocShell* docShell = window->GetDocShell();
     333           0 :     if (!docShell) {
     334             :       break;
     335             :     }
     336           0 :     nsIPresShell* presShell = docShell->GetPresShell();
     337           0 :     if (!presShell) {
     338             :       break;
     339             :     }
     340             :   }
     341             : 
     342           0 :   NS_IF_ADDREF(*aFocusedWindow);
     343             : 
     344           0 :   return currentElement;
     345             : }
     346             : 
     347             : // static
     348             : Element*
     349           0 : nsFocusManager::GetRedirectedFocus(nsIContent* aContent)
     350             : {
     351             :   // For input number, redirect focus to our anonymous text control.
     352           0 :   if (aContent->IsHTMLElement(nsGkAtoms::input)) {
     353             :     bool typeIsNumber =
     354           0 :       static_cast<dom::HTMLInputElement*>(aContent)->ControlType() ==
     355           0 :         NS_FORM_INPUT_NUMBER;
     356             : 
     357           0 :     if (typeIsNumber) {
     358             :       nsNumberControlFrame* numberControlFrame =
     359           0 :         do_QueryFrame(aContent->GetPrimaryFrame());
     360             : 
     361           0 :       if (numberControlFrame) {
     362             :         HTMLInputElement* textControl =
     363           0 :           numberControlFrame->GetAnonTextControl();
     364           0 :         return textControl;
     365             :       }
     366             :     }
     367             :   }
     368             : 
     369             : #ifdef MOZ_XUL
     370           0 :   if (aContent->IsXULElement()) {
     371           0 :     if (aContent->IsXULElement(nsGkAtoms::textbox)) {
     372             :       return aContent->OwnerDoc()->
     373           0 :         GetAnonymousElementByAttribute(aContent, nsGkAtoms::anonid, NS_LITERAL_STRING("input"));
     374             :     }
     375             :     else {
     376           0 :       nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aContent);
     377           0 :       if (menulist) {
     378           0 :         RefPtr<Element> inputField;
     379           0 :         menulist->GetInputField(getter_AddRefs(inputField));
     380           0 :         return inputField;
     381             :       }
     382           0 :       else if (aContent->IsXULElement(nsGkAtoms::scale)) {
     383           0 :         nsCOMPtr<nsIDocument> doc = aContent->GetComposedDoc();
     384           0 :         if (!doc)
     385           0 :           return nullptr;
     386             : 
     387           0 :         nsINodeList* children = doc->BindingManager()->GetAnonymousNodesFor(aContent);
     388           0 :         if (children) {
     389           0 :           nsIContent* child = children->Item(0);
     390           0 :           if (child && child->IsXULElement(nsGkAtoms::slider))
     391           0 :             return child->AsElement();
     392             :         }
     393             :       }
     394             :     }
     395             :   }
     396             : #endif
     397             : 
     398             :   return nullptr;
     399             : }
     400             : 
     401             : // static
     402             : InputContextAction::Cause
     403           0 : nsFocusManager::GetFocusMoveActionCause(uint32_t aFlags)
     404             : {
     405           0 :   if (aFlags & nsIFocusManager::FLAG_BYTOUCH) {
     406             :     return InputContextAction::CAUSE_TOUCH;
     407           0 :   } else if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
     408             :     return InputContextAction::CAUSE_MOUSE;
     409           0 :   } else if (aFlags & nsIFocusManager::FLAG_BYKEY) {
     410             :     return InputContextAction::CAUSE_KEY;
     411             :   }
     412           0 :   return InputContextAction::CAUSE_UNKNOWN;
     413             : }
     414             : 
     415             : NS_IMETHODIMP
     416           0 : nsFocusManager::GetActiveWindow(mozIDOMWindowProxy** aWindow)
     417             : {
     418           0 :   NS_IF_ADDREF(*aWindow = mActiveWindow);
     419           0 :   return NS_OK;
     420             : }
     421             : 
     422             : NS_IMETHODIMP
     423           0 : nsFocusManager::SetActiveWindow(mozIDOMWindowProxy* aWindow)
     424             : {
     425           0 :   NS_ENSURE_STATE(aWindow);
     426             : 
     427             :   // only top-level windows can be made active
     428           0 :   nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(aWindow);
     429           0 :   NS_ENSURE_TRUE(piWindow == piWindow->GetPrivateRoot(), NS_ERROR_INVALID_ARG);
     430             : 
     431           0 :   RaiseWindow(piWindow);
     432           0 :   return NS_OK;
     433             : }
     434             : 
     435             : NS_IMETHODIMP
     436           0 : nsFocusManager::GetFocusedWindow(mozIDOMWindowProxy** aFocusedWindow)
     437             : {
     438           0 :   NS_IF_ADDREF(*aFocusedWindow = mFocusedWindow);
     439           0 :   return NS_OK;
     440             : }
     441             : 
     442           0 : NS_IMETHODIMP nsFocusManager::SetFocusedWindow(mozIDOMWindowProxy* aWindowToFocus)
     443             : {
     444           0 :   LOGFOCUS(("<<SetFocusedWindow begin>>"));
     445             : 
     446           0 :   nsCOMPtr<nsPIDOMWindowOuter> windowToFocus = nsPIDOMWindowOuter::From(aWindowToFocus);
     447           0 :   NS_ENSURE_TRUE(windowToFocus, NS_ERROR_FAILURE);
     448             : 
     449           0 :   windowToFocus = windowToFocus->GetOuterWindow();
     450             : 
     451           0 :   nsCOMPtr<Element> frameElement = windowToFocus->GetFrameElementInternal();
     452           0 :   if (frameElement) {
     453             :     // pass false for aFocusChanged so that the caret does not get updated
     454             :     // and scrolling does not occur.
     455           0 :     SetFocusInner(frameElement, 0, false, true);
     456             :   }
     457             :   else {
     458             :     // this is a top-level window. If the window has a child frame focused,
     459             :     // clear the focus. Otherwise, focus should already be in this frame, or
     460             :     // already cleared. This ensures that focus will be in this frame and not
     461             :     // in a child.
     462           0 :     nsIContent* content = windowToFocus->GetFocusedElement();
     463           0 :     if (content) {
     464           0 :       if (nsCOMPtr<nsPIDOMWindowOuter> childWindow = GetContentWindow(content))
     465           0 :         ClearFocus(windowToFocus);
     466             :     }
     467             :   }
     468             : 
     469           0 :   nsCOMPtr<nsPIDOMWindowOuter> rootWindow = windowToFocus->GetPrivateRoot();
     470           0 :   if (rootWindow)
     471           0 :     RaiseWindow(rootWindow);
     472             : 
     473           0 :   LOGFOCUS(("<<SetFocusedWindow end>>"));
     474             : 
     475             :   return NS_OK;
     476             : }
     477             : 
     478             : NS_IMETHODIMP
     479           0 : nsFocusManager::GetFocusedElement(Element** aFocusedElement)
     480             : {
     481           0 :   RefPtr<Element> focusedElement = mFocusedElement;
     482           0 :   focusedElement.forget(aFocusedElement);
     483           0 :   return NS_OK;
     484             : }
     485             : 
     486             : NS_IMETHODIMP
     487           0 : nsFocusManager::GetLastFocusMethod(mozIDOMWindowProxy* aWindow, uint32_t* aLastFocusMethod)
     488             : {
     489             :   // the focus method is stored on the inner window
     490           0 :   nsCOMPtr<nsPIDOMWindowOuter> window;
     491           0 :   if (aWindow) {
     492           0 :     window = nsPIDOMWindowOuter::From(aWindow);
     493             :   }
     494           0 :   if (!window)
     495           0 :     window = mFocusedWindow;
     496             : 
     497           0 :   *aLastFocusMethod = window ? window->GetFocusMethod() : 0;
     498             : 
     499           0 :   NS_ASSERTION((*aLastFocusMethod & FOCUSMETHOD_MASK) == *aLastFocusMethod,
     500             :                "invalid focus method");
     501           0 :   return NS_OK;
     502             : }
     503             : 
     504             : NS_IMETHODIMP
     505           0 : nsFocusManager::SetFocus(Element* aElement, uint32_t aFlags)
     506             : {
     507           0 :   LOGFOCUS(("<<SetFocus begin>>"));
     508             : 
     509           0 :   NS_ENSURE_ARG(aElement);
     510             : 
     511           0 :   SetFocusInner(aElement, aFlags, true, true);
     512             : 
     513           0 :   LOGFOCUS(("<<SetFocus end>>"));
     514             : 
     515             :   return NS_OK;
     516             : }
     517             : 
     518             : NS_IMETHODIMP
     519           0 : nsFocusManager::ElementIsFocusable(Element* aElement, uint32_t aFlags,
     520             :                                    bool* aIsFocusable)
     521             : {
     522           0 :   NS_ENSURE_TRUE(aElement, NS_ERROR_INVALID_ARG);
     523             : 
     524           0 :   *aIsFocusable = CheckIfFocusable(aElement, aFlags) != nullptr;
     525             : 
     526           0 :   return NS_OK;
     527             : }
     528             : 
     529             : NS_IMETHODIMP
     530           0 : nsFocusManager::MoveFocus(mozIDOMWindowProxy* aWindow, Element* aStartElement,
     531             :                           uint32_t aType, uint32_t aFlags, Element** aElement)
     532             : {
     533           0 :   *aElement = nullptr;
     534             : 
     535           0 :   LOGFOCUS(("<<MoveFocus begin Type: %d Flags: %x>>", aType, aFlags));
     536             : 
     537           0 :   if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug) && mFocusedWindow) {
     538           0 :     nsIDocument* doc = mFocusedWindow->GetExtantDoc();
     539           0 :     if (doc && doc->GetDocumentURI()) {
     540           0 :       LOGFOCUS((" Focused Window: %p %s",
     541             :                 mFocusedWindow.get(),
     542             :                 doc->GetDocumentURI()->GetSpecOrDefault().get()));
     543             :     }
     544             :   }
     545             : 
     546           0 :   LOGCONTENT("  Current Focus: %s", mFocusedElement.get());
     547             : 
     548             :   // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
     549             :   // the other focus methods is already set, or we're just moving to the root
     550             :   // or caret position.
     551           0 :   if (aType != MOVEFOCUS_ROOT && aType != MOVEFOCUS_CARET &&
     552           0 :       (aFlags & FOCUSMETHOD_MASK) == 0) {
     553           0 :     aFlags |= FLAG_BYMOVEFOCUS;
     554             :   }
     555             : 
     556           0 :   nsCOMPtr<nsPIDOMWindowOuter> window;
     557           0 :   if (aStartElement) {
     558           0 :     window = GetCurrentWindow(aStartElement);
     559             :   } else {
     560           0 :     window = aWindow ? nsPIDOMWindowOuter::From(aWindow) : mFocusedWindow.get();
     561             :   }
     562             : 
     563           0 :   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
     564             : 
     565           0 :   bool noParentTraversal = aFlags & FLAG_NOPARENTFRAME;
     566           0 :   nsCOMPtr<nsIContent> newFocus;
     567           0 :   nsresult rv = DetermineElementToMoveFocus(window, aStartElement, aType, noParentTraversal,
     568           0 :                                             getter_AddRefs(newFocus));
     569           0 :   if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
     570             :     return NS_OK;
     571             :   }
     572             : 
     573           0 :   NS_ENSURE_SUCCESS(rv, rv);
     574             : 
     575           0 :   LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get());
     576             : 
     577           0 :   if (newFocus && newFocus->IsElement()) {
     578             :     // for caret movement, pass false for the aFocusChanged argument,
     579             :     // otherwise the caret will end up moving to the focus position. This
     580             :     // would be a problem because the caret would move to the beginning of the
     581             :     // focused link making it impossible to navigate the caret over a link.
     582           0 :     SetFocusInner(newFocus->AsElement(), aFlags, aType != MOVEFOCUS_CARET,
     583           0 :                   true);
     584           0 :     *aElement = do_AddRef(newFocus->AsElement()).take();
     585             :   }
     586           0 :   else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) {
     587             :     // no content was found, so clear the focus for these two types.
     588           0 :     ClearFocus(window);
     589             :   }
     590             : 
     591           0 :   LOGFOCUS(("<<MoveFocus end>>"));
     592             : 
     593             :   return NS_OK;
     594             : }
     595             : 
     596             : NS_IMETHODIMP
     597           0 : nsFocusManager::ClearFocus(mozIDOMWindowProxy* aWindow)
     598             : {
     599           0 :   LOGFOCUS(("<<ClearFocus begin>>"));
     600             : 
     601             :   // if the window to clear is the focused window or an ancestor of the
     602             :   // focused window, then blur the existing focused content. Otherwise, the
     603             :   // focus is somewhere else so just update the current node.
     604           0 :   NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG);
     605           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
     606             : 
     607           0 :   if (IsSameOrAncestor(window, mFocusedWindow)) {
     608           0 :     bool isAncestor = (window != mFocusedWindow);
     609           0 :     if (Blur(window, nullptr, isAncestor, true)) {
     610             :       // if we are clearing the focus on an ancestor of the focused window,
     611             :       // the ancestor will become the new focused window, so focus it
     612           0 :       if (isAncestor)
     613           0 :         Focus(window, nullptr, 0, true, false, false, true);
     614             :     }
     615             :   }
     616             :   else {
     617           0 :     window->SetFocusedElement(nullptr);
     618             :   }
     619             : 
     620           0 :   LOGFOCUS(("<<ClearFocus end>>"));
     621             : 
     622             :   return NS_OK;
     623             : }
     624             : 
     625             : NS_IMETHODIMP
     626           0 : nsFocusManager::GetFocusedElementForWindow(mozIDOMWindowProxy* aWindow,
     627             :                                            bool aDeep,
     628             :                                            mozIDOMWindowProxy** aFocusedWindow,
     629             :                                            Element** aElement)
     630             : {
     631           0 :   *aElement = nullptr;
     632           0 :   if (aFocusedWindow)
     633           0 :     *aFocusedWindow = nullptr;
     634             : 
     635           0 :   NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG);
     636           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
     637             : 
     638           0 :   nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
     639             :   RefPtr<Element> focusedElement =
     640             :     GetFocusedDescendant(window,
     641             :                          aDeep ? nsFocusManager::eIncludeAllDescendants :
     642             :                                  nsFocusManager::eOnlyCurrentWindow,
     643           0 :                          getter_AddRefs(focusedWindow));
     644             : 
     645           0 :   focusedElement.forget(aElement);
     646             : 
     647           0 :   if (aFocusedWindow)
     648           0 :     NS_IF_ADDREF(*aFocusedWindow = focusedWindow);
     649             : 
     650             :   return NS_OK;
     651             : }
     652             : 
     653             : NS_IMETHODIMP
     654           0 : nsFocusManager::MoveCaretToFocus(mozIDOMWindowProxy* aWindow)
     655             : {
     656           0 :   nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
     657           0 :   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
     658           0 :   if (dsti) {
     659           0 :     if (dsti->ItemType() != nsIDocShellTreeItem::typeChrome) {
     660           0 :       nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(dsti);
     661           0 :       NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
     662             : 
     663             :       // don't move the caret for editable documents
     664             :       bool isEditable;
     665           0 :       docShell->GetEditable(&isEditable);
     666           0 :       if (isEditable)
     667             :         return NS_OK;
     668             : 
     669           0 :       nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
     670           0 :       NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
     671             : 
     672           0 :       nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
     673           0 :       nsCOMPtr<nsIContent> content = window->GetFocusedElement();
     674           0 :       if (content)
     675           0 :         MoveCaretToFocus(presShell, content);
     676             :     }
     677             :   }
     678             : 
     679             :   return NS_OK;
     680             : }
     681             : 
     682             : NS_IMETHODIMP
     683           0 : nsFocusManager::WindowRaised(mozIDOMWindowProxy* aWindow)
     684             : {
     685           0 :   NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG);
     686           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
     687             : 
     688           0 :   if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
     689           0 :     LOGFOCUS(("Window %p Raised [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
     690           0 :     nsIDocument* doc = window->GetExtantDoc();
     691           0 :     if (doc && doc->GetDocumentURI()) {
     692           0 :       LOGFOCUS(("  Raised Window: %p %s", aWindow,
     693             :                 doc->GetDocumentURI()->GetSpecOrDefault().get()));
     694             :     }
     695           0 :     if (mActiveWindow) {
     696           0 :       doc = mActiveWindow->GetExtantDoc();
     697           0 :       if (doc && doc->GetDocumentURI()) {
     698           0 :         LOGFOCUS(("  Active Window: %p %s", mActiveWindow.get(),
     699             :                   doc->GetDocumentURI()->GetSpecOrDefault().get()));
     700             :       }
     701             :     }
     702             :   }
     703             : 
     704           0 :   if (mActiveWindow == window) {
     705             :     // The window is already active, so there is no need to focus anything,
     706             :     // but make sure that the right widget is focused. This is a special case
     707             :     // for Windows because when restoring a minimized window, a second
     708             :     // activation will occur and the top-level widget could be focused instead
     709             :     // of the child we want. We solve this by calling SetFocus to ensure that
     710             :     // what the focus manager thinks should be the current widget is actually
     711             :     // focused.
     712           0 :     EnsureCurrentWidgetFocused();
     713           0 :     return NS_OK;
     714             :   }
     715             : 
     716             :   // lower the existing window, if any. This shouldn't happen usually.
     717           0 :   if (mActiveWindow)
     718           0 :     WindowLowered(mActiveWindow);
     719             : 
     720           0 :   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = window->GetDocShell();
     721             :   // If there's no docShellAsItem, this window must have been closed,
     722             :   // in that case there is no tree owner.
     723           0 :   NS_ENSURE_TRUE(docShellAsItem, NS_OK);
     724             : 
     725             :   // set this as the active window
     726           0 :   mActiveWindow = window;
     727             : 
     728             :   // ensure that the window is enabled and visible
     729           0 :   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
     730           0 :   docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
     731           0 :   nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
     732           0 :   if (baseWindow) {
     733           0 :     bool isEnabled = true;
     734           0 :     if (NS_SUCCEEDED(baseWindow->GetEnabled(&isEnabled)) && !isEnabled) {
     735           0 :       return NS_ERROR_FAILURE;
     736             :     }
     737             : 
     738           0 :     baseWindow->SetVisibility(true);
     739             :   }
     740             : 
     741             :   // If this is a parent or single process window, send the activate event.
     742             :   // Events for child process windows will be sent when ParentActivated
     743             :   // is called.
     744           1 :   if (XRE_IsParentProcess()) {
     745           1 :     ActivateOrDeactivate(window, true);
     746             :   }
     747             : 
     748             :   // retrieve the last focused element within the window that was raised
     749           2 :   nsCOMPtr<nsPIDOMWindowOuter> currentWindow;
     750             :   RefPtr<Element> currentFocus =
     751             :     GetFocusedDescendant(window, eIncludeAllDescendants,
     752           3 :                          getter_AddRefs(currentWindow));
     753             : 
     754           0 :   NS_ASSERTION(currentWindow, "window raised with no window current");
     755           1 :   if (!currentWindow)
     756             :     return NS_OK;
     757             : 
     758             :   // If there is no nsIXULWindow, then this is an embedded or child process window.
     759             :   // Pass false for aWindowRaised so that commands get updated.
     760           3 :   nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(baseWindow));
     761           4 :   Focus(currentWindow, currentFocus, 0, true, false, xulWin != nullptr, true);
     762             : 
     763             :   return NS_OK;
     764             : }
     765             : 
     766             : NS_IMETHODIMP
     767           0 : nsFocusManager::WindowLowered(mozIDOMWindowProxy* aWindow)
     768             : {
     769           0 :   NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG);
     770           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
     771             : 
     772           0 :   if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
     773           0 :     LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
     774           0 :     nsIDocument* doc = window->GetExtantDoc();
     775           0 :     if (doc && doc->GetDocumentURI()) {
     776           0 :       LOGFOCUS(("  Lowered Window: %s",
     777             :                 doc->GetDocumentURI()->GetSpecOrDefault().get()));
     778             :     }
     779           0 :     if (mActiveWindow) {
     780           0 :       doc = mActiveWindow->GetExtantDoc();
     781           0 :       if (doc && doc->GetDocumentURI()) {
     782           0 :         LOGFOCUS(("  Active Window: %s",
     783             :                   doc->GetDocumentURI()->GetSpecOrDefault().get()));
     784             :       }
     785             :     }
     786             :   }
     787             : 
     788           0 :   if (mActiveWindow != window)
     789             :     return NS_OK;
     790             : 
     791             :   // clear the mouse capture as the active window has changed
     792           0 :   nsIPresShell::SetCapturingContent(nullptr, 0);
     793             : 
     794             :   // In addition, reset the drag state to ensure that we are no longer in
     795             :   // drag-select mode.
     796           0 :   if (mFocusedWindow) {
     797           0 :     nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
     798           0 :     if (docShell) {
     799           0 :       nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
     800           0 :       if (presShell) {
     801           0 :         RefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
     802           0 :         frameSelection->SetDragState(false);
     803             :       }
     804             :     }
     805             :   }
     806             : 
     807             :   // If this is a parent or single process window, send the deactivate event.
     808             :   // Events for child process windows will be sent when ParentActivated
     809             :   // is called.
     810           0 :   if (XRE_IsParentProcess()) {
     811           0 :     ActivateOrDeactivate(window, false);
     812             :   }
     813             : 
     814             :   // keep track of the window being lowered, so that attempts to raise the
     815             :   // window can be prevented until we return. Otherwise, focus can get into
     816             :   // an unusual state.
     817           0 :   mWindowBeingLowered = mActiveWindow;
     818           0 :   mActiveWindow = nullptr;
     819             : 
     820           0 :   if (mFocusedWindow)
     821           0 :     Blur(nullptr, nullptr, true, true);
     822             : 
     823           0 :   mWindowBeingLowered = nullptr;
     824             : 
     825           0 :   return NS_OK;
     826             : }
     827             : 
     828             : nsresult
     829           5 : nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
     830             : {
     831           0 :   NS_ENSURE_ARG(aDocument);
     832           5 :   NS_ENSURE_ARG(aContent);
     833             : 
     834           0 :   nsPIDOMWindowOuter *window = aDocument->GetWindow();
     835           5 :   if (!window)
     836             :     return NS_OK;
     837             : 
     838             :   // if the content is currently focused in the window, or is an
     839             :   // shadow-including inclusive ancestor of the currently focused element,
     840             :   // reset the focus within that window.
     841          10 :   nsIContent* content = window->GetFocusedElement();
     842           5 :   if (content && nsContentUtils::ContentIsHostIncludingDescendantOf(content, aContent)) {
     843           0 :     bool shouldShowFocusRing = window->ShouldShowFocusRing();
     844           0 :     window->SetFocusedElement(nullptr);
     845             : 
     846             :     // if this window is currently focused, clear the global focused
     847             :     // element as well, but don't fire any events.
     848           0 :     if (window == mFocusedWindow) {
     849           0 :       mFocusedElement = nullptr;
     850             :     } else {
     851             :       // Check if the node that was focused is an iframe or similar by looking
     852             :       // if it has a subdocument. This would indicate that this focused iframe
     853             :       // and its descendants will be going away. We will need to move the
     854             :       // focus somewhere else, so just clear the focus in the toplevel window
     855             :       // so that no element is focused.
     856           0 :       nsIDocument* subdoc = aDocument->GetSubDocumentFor(content);
     857           0 :       if (subdoc) {
     858           0 :         nsCOMPtr<nsIDocShell> docShell = subdoc->GetDocShell();
     859           0 :         if (docShell) {
     860           0 :           nsCOMPtr<nsPIDOMWindowOuter> childWindow = docShell->GetWindow();
     861           0 :           if (childWindow && IsSameOrAncestor(childWindow, mFocusedWindow)) {
     862           0 :             ClearFocus(mActiveWindow);
     863             :           }
     864             :         }
     865             :       }
     866             :     }
     867             : 
     868             :     // Notify the editor in case we removed its ancestor limiter.
     869           0 :     if (content->IsEditable()) {
     870           0 :       nsCOMPtr<nsIDocShell> docShell = aDocument->GetDocShell();
     871           0 :       if (docShell) {
     872           0 :         RefPtr<HTMLEditor> htmlEditor = docShell->GetHTMLEditor();
     873           0 :         if (htmlEditor) {
     874           0 :           RefPtr<Selection> selection = htmlEditor->GetSelection();
     875           0 :           if (selection && selection->GetFrameSelection() &&
     876           0 :               content == selection->GetFrameSelection()->GetAncestorLimiter()) {
     877           0 :             htmlEditor->FinalizeSelection();
     878             :           }
     879             :         }
     880             :       }
     881             :     }
     882             : 
     883           0 :     NotifyFocusStateChange(content, nullptr, shouldShowFocusRing, false);
     884             :   }
     885             : 
     886             :   return NS_OK;
     887             : }
     888             : 
     889             : NS_IMETHODIMP
     890           6 : nsFocusManager::WindowShown(mozIDOMWindowProxy* aWindow, bool aNeedsFocus)
     891             : {
     892           0 :   NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG);
     893          12 :   nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
     894             : 
     895           0 :   if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
     896           0 :     LOGFOCUS(("Window %p Shown [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
     897           0 :     nsIDocument* doc = window->GetExtantDoc();
     898           0 :     if (doc && doc->GetDocumentURI()) {
     899           0 :       LOGFOCUS(("Shown Window: %s",
     900             :                 doc->GetDocumentURI()->GetSpecOrDefault().get()));
     901             :     }
     902             : 
     903           0 :     if (mFocusedWindow) {
     904           0 :       doc = mFocusedWindow->GetExtantDoc();
     905           0 :       if (doc && doc->GetDocumentURI()) {
     906           0 :         LOGFOCUS((" Focused Window: %s",
     907             :                   doc->GetDocumentURI()->GetSpecOrDefault().get()));
     908             :       }
     909             :     }
     910             :   }
     911             : 
     912          12 :   if (nsIDocShell* docShell = window->GetDocShell()) {
     913          18 :     if (nsCOMPtr<nsITabChild> child = docShell->GetTabChild()) {
     914           0 :       bool active = static_cast<TabChild*>(child.get())->ParentIsActive();
     915           0 :       ActivateOrDeactivate(window, active);
     916             :     }
     917             :   }
     918             : 
     919          12 :   if (mFocusedWindow != window)
     920             :     return NS_OK;
     921             : 
     922           0 :   if (aNeedsFocus) {
     923           0 :     nsCOMPtr<nsPIDOMWindowOuter> currentWindow;
     924             :     RefPtr<Element> currentFocus =
     925             :       GetFocusedDescendant(window, eIncludeAllDescendants,
     926           0 :                            getter_AddRefs(currentWindow));
     927           0 :     if (currentWindow)
     928           0 :       Focus(currentWindow, currentFocus, 0, true, false, false, true);
     929             :   }
     930             :   else {
     931             :     // Sometimes, an element in a window can be focused before the window is
     932             :     // visible, which would mean that the widget may not be properly focused.
     933             :     // When the window becomes visible, make sure the right widget is focused.
     934           0 :     EnsureCurrentWidgetFocused();
     935             :   }
     936             : 
     937             :   return NS_OK;
     938             : }
     939             : 
     940             : NS_IMETHODIMP
     941           8 : nsFocusManager::WindowHidden(mozIDOMWindowProxy* aWindow)
     942             : {
     943             :   // if there is no window or it is not the same or an ancestor of the
     944             :   // currently focused window, just return, as the current focus will not
     945             :   // be affected.
     946             : 
     947           8 :   NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG);
     948          16 :   nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
     949             : 
     950           0 :   if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
     951           0 :     LOGFOCUS(("Window %p Hidden [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
     952           0 :     nsAutoCString spec;
     953           0 :     nsIDocument* doc = window->GetExtantDoc();
     954           0 :     if (doc && doc->GetDocumentURI()) {
     955           0 :       LOGFOCUS(("  Hide Window: %s",
     956             :                 doc->GetDocumentURI()->GetSpecOrDefault().get()));
     957             :     }
     958             : 
     959           0 :     if (mFocusedWindow) {
     960           0 :       doc = mFocusedWindow->GetExtantDoc();
     961           0 :       if (doc && doc->GetDocumentURI()) {
     962           0 :         LOGFOCUS(("  Focused Window: %s",
     963             :                   doc->GetDocumentURI()->GetSpecOrDefault().get()));
     964             :       }
     965             :     }
     966             : 
     967           0 :     if (mActiveWindow) {
     968           0 :       doc = mActiveWindow->GetExtantDoc();
     969           0 :       if (doc && doc->GetDocumentURI()) {
     970           0 :         LOGFOCUS(("  Active Window: %s",
     971             :                   doc->GetDocumentURI()->GetSpecOrDefault().get()));
     972             :       }
     973             :     }
     974             :   }
     975             : 
     976          24 :   if (!IsSameOrAncestor(window, mFocusedWindow))
     977             :     return NS_OK;
     978             : 
     979             :   // at this point, we know that the window being hidden is either the focused
     980             :   // window, or an ancestor of the focused window. Either way, the focus is no
     981             :   // longer valid, so it needs to be updated.
     982             : 
     983           0 :   RefPtr<Element> oldFocusedElement = mFocusedElement.forget();
     984             : 
     985           0 :   nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
     986           0 :   nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
     987             : 
     988           0 :   if (oldFocusedElement && oldFocusedElement->IsInComposedDoc()) {
     989           0 :     NotifyFocusStateChange(oldFocusedElement,
     990             :                            nullptr,
     991           0 :                            mFocusedWindow->ShouldShowFocusRing(),
     992           0 :                            false);
     993           0 :     window->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
     994             : 
     995           0 :     if (presShell) {
     996           0 :       SendFocusOrBlurEvent(eBlur, presShell,
     997           0 :                            oldFocusedElement->GetComposedDoc(),
     998           0 :                            oldFocusedElement, 1, false);
     999             :     }
    1000             :   }
    1001             : 
    1002             :   nsPresContext* focusedPresContext =
    1003           0 :     presShell ? presShell->GetPresContext() : nullptr;
    1004           0 :   IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
    1005           0 :                                  GetFocusMoveActionCause(0));
    1006           0 :   if (presShell) {
    1007           0 :     SetCaretVisible(presShell, false, nullptr);
    1008             :   }
    1009             : 
    1010             :   // if the docshell being hidden is being destroyed, then we want to move
    1011             :   // focus somewhere else. Call ClearFocus on the toplevel window, which
    1012             :   // will have the effect of clearing the focus and moving the focused window
    1013             :   // to the toplevel window. But if the window isn't being destroyed, we are
    1014             :   // likely just loading a new document in it, so we want to maintain the
    1015             :   // focused window so that the new document gets properly focused.
    1016           0 :   nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell();
    1017           0 :   bool beingDestroyed = !docShellBeingHidden;
    1018           0 :   if (docShellBeingHidden) {
    1019           0 :     docShellBeingHidden->IsBeingDestroyed(&beingDestroyed);
    1020             :   }
    1021           0 :   if (beingDestroyed) {
    1022             :     // There is usually no need to do anything if a toplevel window is going
    1023             :     // away, as we assume that WindowLowered will be called. However, this may
    1024             :     // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
    1025             :     // a leak. So if the active window is being destroyed, call WindowLowered
    1026             :     // directly.
    1027           0 :     if (mActiveWindow == mFocusedWindow || mActiveWindow == window)
    1028           0 :       WindowLowered(mActiveWindow);
    1029             :     else
    1030           0 :       ClearFocus(mActiveWindow);
    1031             :     return NS_OK;
    1032             :   }
    1033             : 
    1034             :   // if the window being hidden is an ancestor of the focused window, adjust
    1035             :   // the focused window so that it points to the one being hidden. This
    1036             :   // ensures that the focused window isn't in a chain of frames that doesn't
    1037             :   // exist any more.
    1038           0 :   if (window != mFocusedWindow) {
    1039             :     nsCOMPtr<nsIDocShellTreeItem> dsti =
    1040           0 :       mFocusedWindow ? mFocusedWindow->GetDocShell() : nullptr;
    1041           0 :     if (dsti) {
    1042           0 :       nsCOMPtr<nsIDocShellTreeItem> parentDsti;
    1043           0 :       dsti->GetParent(getter_AddRefs(parentDsti));
    1044           0 :       if (parentDsti) {
    1045           0 :         if (nsCOMPtr<nsPIDOMWindowOuter> parentWindow = parentDsti->GetWindow())
    1046           0 :           parentWindow->SetFocusedElement(nullptr);
    1047             :       }
    1048             :     }
    1049             : 
    1050           0 :     SetFocusedWindowInternal(window);
    1051             :   }
    1052             : 
    1053             :   return NS_OK;
    1054             : }
    1055             : 
    1056             : NS_IMETHODIMP
    1057           0 : nsFocusManager::FireDelayedEvents(nsIDocument* aDocument)
    1058             : {
    1059           0 :   NS_ENSURE_ARG(aDocument);
    1060             : 
    1061             :   // fire any delayed focus and blur events in the same order that they were added
    1062           0 :   for (uint32_t i = 0; i < mDelayedBlurFocusEvents.Length(); i++) {
    1063           0 :     if (mDelayedBlurFocusEvents[i].mDocument == aDocument) {
    1064           0 :       if (!aDocument->GetInnerWindow() ||
    1065           0 :           !aDocument->GetInnerWindow()->IsCurrentInnerWindow()) {
    1066             :         // If the document was navigated away from or is defunct, don't bother
    1067             :         // firing events on it. Note the symmetry between this condition and
    1068             :         // the similar one in nsDocument.cpp:FireOrClearDelayedEvents.
    1069           0 :         mDelayedBlurFocusEvents.RemoveElementAt(i);
    1070           0 :         --i;
    1071           0 :       } else if (!aDocument->EventHandlingSuppressed()) {
    1072           0 :         EventMessage message = mDelayedBlurFocusEvents[i].mEventMessage;
    1073           0 :         nsCOMPtr<EventTarget> target = mDelayedBlurFocusEvents[i].mTarget;
    1074           0 :         nsCOMPtr<nsIPresShell> presShell = mDelayedBlurFocusEvents[i].mPresShell;
    1075           0 :         nsCOMPtr<EventTarget> relatedTarget = mDelayedBlurFocusEvents[i].mRelatedTarget;
    1076           0 :         mDelayedBlurFocusEvents.RemoveElementAt(i);
    1077             : 
    1078           0 :         FireFocusOrBlurEvent(message, presShell, target, false, false,
    1079           0 :                              relatedTarget);
    1080           0 :         --i;
    1081             :       }
    1082             :     }
    1083             :   }
    1084             : 
    1085             :   return NS_OK;
    1086             : }
    1087             : 
    1088             : NS_IMETHODIMP
    1089           0 : nsFocusManager::FocusPlugin(Element* aPlugin)
    1090             : {
    1091           0 :   NS_ENSURE_ARG(aPlugin);
    1092           0 :   SetFocusInner(aPlugin, 0, true, false);
    1093           0 :   return NS_OK;
    1094             : }
    1095             : 
    1096             : NS_IMETHODIMP
    1097           0 : nsFocusManager::ParentActivated(mozIDOMWindowProxy* aWindow, bool aActive)
    1098             : {
    1099           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
    1100           0 :   NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
    1101             : 
    1102           0 :   ActivateOrDeactivate(window, aActive);
    1103           0 :   return NS_OK;
    1104             : }
    1105             : 
    1106             : /* static */
    1107             : void
    1108           1 : nsFocusManager::NotifyFocusStateChange(nsIContent* aContent,
    1109             :                                        nsIContent* aContentToFocus,
    1110             :                                        bool aWindowShouldShowFocusRing,
    1111             :                                        bool aGettingFocus)
    1112             : {
    1113           1 :   MOZ_ASSERT_IF(aContentToFocus, !aGettingFocus);
    1114           2 :   if (!aContent->IsElement()) {
    1115             :     return;
    1116             :   }
    1117             : 
    1118           1 :   nsIContent* commonAncestor = nullptr;
    1119           1 :   if (aContentToFocus && aContentToFocus->IsElement()) {
    1120             :     commonAncestor =
    1121           0 :       nsContentUtils::GetCommonFlattenedTreeAncestor(aContent, aContentToFocus);
    1122             :   }
    1123             : 
    1124           1 :   EventStates eventState = NS_EVENT_STATE_FOCUS;
    1125           1 :   if (aWindowShouldShowFocusRing) {
    1126           0 :     eventState |= NS_EVENT_STATE_FOCUSRING;
    1127             :   }
    1128             : 
    1129           1 :   if (aGettingFocus) {
    1130           1 :     aContent->AsElement()->AddStates(eventState);
    1131             :   } else {
    1132           0 :     aContent->AsElement()->RemoveStates(eventState);
    1133             :   }
    1134             : 
    1135           9 :   for (nsIContent* content = aContent;
    1136          10 :        content && content != commonAncestor;
    1137             :        content = content->GetFlattenedTreeParent()) {
    1138           0 :     if (!content->IsElement()) {
    1139             :       continue;
    1140             :     }
    1141             : 
    1142           9 :     Element* element = content->AsElement();
    1143           9 :     if (aGettingFocus) {
    1144           0 :       if (element->State().HasState(NS_EVENT_STATE_FOCUS_WITHIN)) {
    1145             :         break;
    1146             :       }
    1147          18 :       element->AddStates(NS_EVENT_STATE_FOCUS_WITHIN);
    1148             :     } else {
    1149           0 :       element->RemoveStates(NS_EVENT_STATE_FOCUS_WITHIN);
    1150             :     }
    1151             :   }
    1152             : }
    1153             : 
    1154             : // static
    1155             : void
    1156           0 : nsFocusManager::EnsureCurrentWidgetFocused()
    1157             : {
    1158           0 :   if (!mFocusedWindow || sTestMode)
    1159           0 :     return;
    1160             : 
    1161             :   // get the main child widget for the focused window and ensure that the
    1162             :   // platform knows that this widget is focused.
    1163           0 :   nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
    1164           0 :   if (docShell) {
    1165           0 :     nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
    1166           0 :     if (presShell) {
    1167           0 :       nsViewManager* vm = presShell->GetViewManager();
    1168           0 :       if (vm) {
    1169           0 :         nsCOMPtr<nsIWidget> widget;
    1170           0 :         vm->GetRootWidget(getter_AddRefs(widget));
    1171           0 :         if (widget)
    1172           0 :           widget->SetFocus(false);
    1173             :       }
    1174             :     }
    1175             :   }
    1176             : }
    1177             : 
    1178             : bool
    1179           1 : ActivateOrDeactivateChild(TabParent* aParent, void* aArg)
    1180             : {
    1181           0 :   bool active = static_cast<bool>(aArg);
    1182           1 :   Unused << aParent->SendParentActivated(active);
    1183           0 :   return false;
    1184             : }
    1185             : 
    1186             : void
    1187           1 : nsFocusManager::ActivateOrDeactivate(nsPIDOMWindowOuter* aWindow, bool aActive)
    1188             : {
    1189           0 :   if (!aWindow) {
    1190             :     return;
    1191             :   }
    1192             : 
    1193             :   // Inform the DOM window that it has activated or deactivated, so that
    1194             :   // the active attribute is updated on the window.
    1195           1 :   aWindow->ActivateOrDeactivate(aActive);
    1196             : 
    1197             :   // Send the activate event.
    1198           1 :   if (aWindow->GetExtantDoc()) {
    1199           2 :     nsContentUtils::DispatchEventOnlyToChrome(aWindow->GetExtantDoc(),
    1200           0 :                                               aWindow->GetCurrentInnerWindow(),
    1201             :                                               aActive ?
    1202           0 :                                                 NS_LITERAL_STRING("activate") :
    1203           0 :                                                 NS_LITERAL_STRING("deactivate"),
    1204           0 :                                               true, true, nullptr);
    1205             :   }
    1206             : 
    1207             :   // Look for any remote child frames, iterate over them and send the activation notification.
    1208           1 :   nsContentUtils::CallOnAllRemoteChildren(aWindow, ActivateOrDeactivateChild,
    1209           1 :                                           (void *)aActive);
    1210             : }
    1211             : 
    1212             : void
    1213           1 : nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
    1214             :                               bool aFocusChanged, bool aAdjustWidget)
    1215             : {
    1216             :   // if the element is not focusable, just return and leave the focus as is
    1217           2 :   RefPtr<Element> elementToFocus = CheckIfFocusable(aNewContent, aFlags);
    1218           1 :   if (!elementToFocus) {
    1219           0 :     return;
    1220             :   }
    1221             : 
    1222             :   // check if the element to focus is a frame (iframe) containing a child
    1223             :   // document. Frames are never directly focused; instead focusing a frame
    1224             :   // means focus what is inside the frame. To do this, the descendant content
    1225             :   // within the frame is retrieved and that will be focused instead.
    1226           2 :   nsCOMPtr<nsPIDOMWindowOuter> newWindow;
    1227           2 :   nsCOMPtr<nsPIDOMWindowOuter> subWindow = GetContentWindow(elementToFocus);
    1228           0 :   if (subWindow) {
    1229             :     elementToFocus = GetFocusedDescendant(subWindow, eIncludeAllDescendants,
    1230           0 :                                           getter_AddRefs(newWindow));
    1231             :     // since a window is being refocused, clear aFocusChanged so that the
    1232             :     // caret position isn't updated.
    1233           0 :     aFocusChanged = false;
    1234             :   }
    1235             : 
    1236             :   // unless it was set above, retrieve the window for the element to focus
    1237           1 :   if (!newWindow) {
    1238           1 :     newWindow = GetCurrentWindow(elementToFocus);
    1239             :   }
    1240             : 
    1241             :   // if the element is already focused, just return. Note that this happens
    1242             :   // after the frame check above so that we compare the element that will be
    1243             :   // focused rather than the frame it is in.
    1244           3 :   if (!newWindow ||
    1245           2 :       (newWindow == mFocusedWindow && elementToFocus == mFocusedElement)) {
    1246           0 :     return;
    1247             :   }
    1248             : 
    1249             :   // don't allow focus to be placed in docshells or descendants of docshells
    1250             :   // that are being destroyed. Also, ensure that the page hasn't been
    1251             :   // unloaded. The prevents content from being refocused during an unload event.
    1252           3 :   nsCOMPtr<nsIDocShell> newDocShell = newWindow->GetDocShell();
    1253           2 :   nsCOMPtr<nsIDocShell> docShell = newDocShell;
    1254           0 :   while (docShell) {
    1255             :     bool inUnload;
    1256           0 :     docShell->GetIsInUnload(&inUnload);
    1257           1 :     if (inUnload)
    1258           0 :       return;
    1259             : 
    1260             :     bool beingDestroyed;
    1261           1 :     docShell->IsBeingDestroyed(&beingDestroyed);
    1262           1 :     if (beingDestroyed)
    1263             :       return;
    1264             : 
    1265           2 :     nsCOMPtr<nsIDocShellTreeItem> parentDsti;
    1266           2 :     docShell->GetParent(getter_AddRefs(parentDsti));
    1267           0 :     docShell = do_QueryInterface(parentDsti);
    1268             :   }
    1269             : 
    1270             :   // if the new element is in the same window as the currently focused element
    1271           2 :   bool isElementInFocusedWindow = (mFocusedWindow == newWindow);
    1272             : 
    1273           0 :   if (!isElementInFocusedWindow && mFocusedWindow && newWindow &&
    1274             :       nsContentUtils::IsHandlingKeyBoardEvent()) {
    1275             :     nsCOMPtr<nsIScriptObjectPrincipal> focused =
    1276           0 :       do_QueryInterface(mFocusedWindow);
    1277             :     nsCOMPtr<nsIScriptObjectPrincipal> newFocus =
    1278           0 :       do_QueryInterface(newWindow);
    1279           0 :     nsIPrincipal* focusedPrincipal = focused->GetPrincipal();
    1280           0 :     nsIPrincipal* newPrincipal = newFocus->GetPrincipal();
    1281           0 :     if (!focusedPrincipal || !newPrincipal) {
    1282           0 :       return;
    1283             :     }
    1284           0 :     bool subsumes = false;
    1285           0 :     focusedPrincipal->Subsumes(newPrincipal, &subsumes);
    1286           0 :     if (!subsumes && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
    1287           0 :       NS_WARNING("Not allowed to focus the new window!");
    1288           0 :       return;
    1289             :     }
    1290             :   }
    1291             : 
    1292             :   // to check if the new element is in the active window, compare the
    1293             :   // new root docshell for the new element with the active window's docshell.
    1294           1 :   bool isElementInActiveWindow = false;
    1295             : 
    1296           0 :   nsCOMPtr<nsIDocShellTreeItem> dsti = newWindow->GetDocShell();
    1297           2 :   nsCOMPtr<nsPIDOMWindowOuter> newRootWindow;
    1298           0 :   if (dsti) {
    1299           0 :     nsCOMPtr<nsIDocShellTreeItem> root;
    1300           0 :     dsti->GetRootTreeItem(getter_AddRefs(root));
    1301           0 :     newRootWindow = root ? root->GetWindow() : nullptr;
    1302             : 
    1303           0 :     isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow);
    1304             :   }
    1305             : 
    1306             :   // Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX
    1307             :   // system. We don't control event dispatch to windowed plugins on non-MacOSX,
    1308             :   // so we can't display the "Press ESC to leave fullscreen mode" warning on
    1309             :   // key input if a windowed plugin is focused, so just exit fullscreen
    1310             :   // to guard against phishing.
    1311             : #ifndef XP_MACOSX
    1312           2 :   if (elementToFocus &&
    1313             :       nsContentUtils::
    1314           0 :         GetRootDocument(elementToFocus->OwnerDoc())->GetFullscreenElement() &&
    1315           0 :       nsContentUtils::HasPluginWithUncontrolledEventDispatch(elementToFocus)) {
    1316           0 :     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
    1317           0 :                                     NS_LITERAL_CSTRING("DOM"),
    1318           0 :                                     elementToFocus->OwnerDoc(),
    1319             :                                     nsContentUtils::eDOM_PROPERTIES,
    1320           0 :                                     "FocusedWindowedPluginWhileFullscreen");
    1321           0 :     nsIDocument::AsyncExitFullscreen(elementToFocus->OwnerDoc());
    1322             :   }
    1323             : #endif
    1324             : 
    1325             :   // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
    1326             :   // shifted away from the current element if the new shell to focus is
    1327             :   // the same or an ancestor shell of the currently focused shell.
    1328           1 :   bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
    1329           1 :                             IsSameOrAncestor(newWindow, mFocusedWindow);
    1330             : 
    1331             :   // if the element is in the active window, frame switching is allowed and
    1332             :   // the content is in a visible window, fire blur and focus events.
    1333             :   bool sendFocusEvent =
    1334           1 :     isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow);
    1335             : 
    1336             :   // When the following conditions are true:
    1337             :   //  * an element has focus
    1338             :   //  * isn't called by trusted event (i.e., called by untrusted event or by js)
    1339             :   //  * the focus is moved to another document's element
    1340             :   // we need to check the permission.
    1341           1 :   if (sendFocusEvent && mFocusedElement && !nsContentUtils::LegacyIsCallerNativeCode() &&
    1342           0 :       mFocusedElement->OwnerDoc() != aNewContent->OwnerDoc()) {
    1343             :     // If the caller cannot access the current focused node, the caller should
    1344             :     // not be able to steal focus from it. E.g., When the current focused node
    1345             :     // is in chrome, any web contents should not be able to steal the focus.
    1346           0 :     sendFocusEvent = nsContentUtils::CanCallerAccess(mFocusedElement);
    1347           0 :     if (!sendFocusEvent && mMouseButtonEventHandlingDocument) {
    1348             :       // However, while mouse button event is handling, the handling document's
    1349             :       // script should be able to steal focus.
    1350             :       sendFocusEvent =
    1351           0 :         nsContentUtils::CanCallerAccess(mMouseButtonEventHandlingDocument);
    1352             :     }
    1353             :   }
    1354             : 
    1355           1 :   LOGCONTENT("Shift Focus: %s", elementToFocus.get());
    1356           1 :   LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p",
    1357             :            aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedElement.get()));
    1358           0 :   LOGFOCUS((" In Active Window: %d In Focused Window: %d SendFocus: %d",
    1359             :            isElementInActiveWindow, isElementInFocusedWindow, sendFocusEvent));
    1360             : 
    1361           1 :   if (sendFocusEvent) {
    1362           0 :     RefPtr<Element> oldFocusedElement = mFocusedElement;
    1363             :     // return if blurring fails or the focus changes during the blur
    1364           0 :     if (mFocusedWindow) {
    1365             :       // if the focus is being moved to another element in the same document,
    1366             :       // or to a descendant, pass the existing window to Blur so that the
    1367             :       // current node in the existing window is cleared. If moving to a
    1368             :       // window elsewhere, we want to maintain the current node in the
    1369             :       // window but still blur it.
    1370           0 :       bool currentIsSameOrAncestor = IsSameOrAncestor(mFocusedWindow, newWindow);
    1371             :       // find the common ancestor of the currently focused window and the new
    1372             :       // window. The ancestor will need to have its currently focused node
    1373             :       // cleared once the document has been blurred. Otherwise, we'll be in a
    1374             :       // state where a document is blurred yet the chain of windows above it
    1375             :       // still points to that document.
    1376             :       // For instance, in the following frame tree:
    1377             :       //   A
    1378             :       //  B C
    1379             :       //  D
    1380             :       // D is focused and we want to focus C. Once D has been blurred, we need
    1381             :       // to clear out the focus in A, otherwise A would still maintain that B
    1382             :       // was focused, and B that D was focused.
    1383           0 :       nsCOMPtr<nsPIDOMWindowOuter> commonAncestor;
    1384           0 :       if (!isElementInFocusedWindow)
    1385           0 :         commonAncestor = GetCommonAncestor(newWindow, mFocusedWindow);
    1386             : 
    1387           0 :       if (!Blur(currentIsSameOrAncestor ? mFocusedWindow.get() : nullptr,
    1388           0 :                 commonAncestor, !isElementInFocusedWindow, aAdjustWidget,
    1389           0 :                 elementToFocus)) {
    1390           0 :         return;
    1391             :       }
    1392             :     }
    1393             : 
    1394           0 :     Focus(newWindow, elementToFocus, aFlags, !isElementInFocusedWindow,
    1395           0 :           aFocusChanged, false, aAdjustWidget, oldFocusedElement);
    1396             :   }
    1397             :   else {
    1398             :     // otherwise, for inactive windows and when the caller cannot steal the
    1399             :     // focus, update the node in the window, and  raise the window if desired.
    1400           1 :     if (allowFrameSwitch)
    1401           1 :       AdjustWindowFocus(newWindow, true);
    1402             : 
    1403             :     // set the focus node and method as needed
    1404           1 :     uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
    1405           1 :                            newWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
    1406           0 :     newWindow->SetFocusedElement(elementToFocus, focusMethod);
    1407           0 :     if (aFocusChanged) {
    1408           0 :       nsCOMPtr<nsIDocShell> docShell = newWindow->GetDocShell();
    1409             : 
    1410           0 :       nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
    1411           2 :       if (presShell && presShell->DidInitialize())
    1412           0 :         ScrollIntoView(presShell, elementToFocus, aFlags);
    1413             :     }
    1414             : 
    1415             :     // update the commands even when inactive so that the attributes for that
    1416             :     // window are up to date.
    1417           1 :     if (allowFrameSwitch)
    1418           3 :       newWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
    1419             : 
    1420           0 :     if (aFlags & FLAG_RAISE)
    1421           0 :       RaiseWindow(newRootWindow);
    1422             :   }
    1423             : }
    1424             : 
    1425             : bool
    1426           8 : nsFocusManager::IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor,
    1427             :                                  nsPIDOMWindowOuter* aWindow)
    1428             : {
    1429           8 :   if (!aWindow || !aPossibleAncestor) {
    1430             :     return false;
    1431             :   }
    1432             : 
    1433           6 :   nsCOMPtr<nsIDocShellTreeItem> ancestordsti = aPossibleAncestor->GetDocShell();
    1434           6 :   nsCOMPtr<nsIDocShellTreeItem> dsti = aWindow->GetDocShell();
    1435           0 :   while (dsti) {
    1436           0 :     if (dsti == ancestordsti)
    1437           0 :       return true;
    1438           0 :     nsCOMPtr<nsIDocShellTreeItem> parentDsti;
    1439           0 :     dsti->GetParent(getter_AddRefs(parentDsti));
    1440           0 :     dsti.swap(parentDsti);
    1441             :   }
    1442             : 
    1443             :   return false;
    1444             : }
    1445             : 
    1446             : already_AddRefed<nsPIDOMWindowOuter>
    1447           0 : nsFocusManager::GetCommonAncestor(nsPIDOMWindowOuter* aWindow1,
    1448             :                                   nsPIDOMWindowOuter* aWindow2)
    1449             : {
    1450           0 :   NS_ENSURE_TRUE(aWindow1 && aWindow2, nullptr);
    1451             : 
    1452           0 :   nsCOMPtr<nsIDocShellTreeItem> dsti1 = aWindow1->GetDocShell();
    1453           0 :   NS_ENSURE_TRUE(dsti1, nullptr);
    1454             : 
    1455           0 :   nsCOMPtr<nsIDocShellTreeItem> dsti2 = aWindow2->GetDocShell();
    1456           0 :   NS_ENSURE_TRUE(dsti2, nullptr);
    1457             : 
    1458           0 :   AutoTArray<nsIDocShellTreeItem*, 30> parents1, parents2;
    1459           0 :   do {
    1460           0 :     parents1.AppendElement(dsti1);
    1461           0 :     nsCOMPtr<nsIDocShellTreeItem> parentDsti1;
    1462           0 :     dsti1->GetParent(getter_AddRefs(parentDsti1));
    1463           0 :     dsti1.swap(parentDsti1);
    1464             :   } while (dsti1);
    1465           0 :   do {
    1466           0 :     parents2.AppendElement(dsti2);
    1467           0 :     nsCOMPtr<nsIDocShellTreeItem> parentDsti2;
    1468           0 :     dsti2->GetParent(getter_AddRefs(parentDsti2));
    1469           0 :     dsti2.swap(parentDsti2);
    1470             :   } while (dsti2);
    1471             : 
    1472           0 :   uint32_t pos1 = parents1.Length();
    1473           0 :   uint32_t pos2 = parents2.Length();
    1474           0 :   nsIDocShellTreeItem* parent = nullptr;
    1475             :   uint32_t len;
    1476           0 :   for (len = std::min(pos1, pos2); len > 0; --len) {
    1477           0 :     nsIDocShellTreeItem* child1 = parents1.ElementAt(--pos1);
    1478           0 :     nsIDocShellTreeItem* child2 = parents2.ElementAt(--pos2);
    1479           0 :     if (child1 != child2) {
    1480             :       break;
    1481             :     }
    1482           0 :     parent = child1;
    1483             :   }
    1484             : 
    1485           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = parent ? parent->GetWindow() : nullptr;
    1486           0 :   return window.forget();
    1487             : }
    1488             : 
    1489             : void
    1490           2 : nsFocusManager::AdjustWindowFocus(nsPIDOMWindowOuter* aWindow,
    1491             :                                   bool aCheckPermission)
    1492             : {
    1493           2 :   bool isVisible = IsWindowVisible(aWindow);
    1494             : 
    1495           0 :   nsCOMPtr<nsPIDOMWindowOuter> window(aWindow);
    1496           2 :   while (window) {
    1497             :     // get the containing <iframe> or equivalent element so that it can be
    1498             :     // focused below.
    1499           2 :     nsCOMPtr<Element> frameElement = window->GetFrameElementInternal();
    1500             : 
    1501           0 :     nsCOMPtr<nsIDocShellTreeItem> dsti = window->GetDocShell();
    1502           2 :     if (!dsti)
    1503           0 :       return;
    1504           0 :     nsCOMPtr<nsIDocShellTreeItem> parentDsti;
    1505           0 :     dsti->GetParent(getter_AddRefs(parentDsti));
    1506           0 :     if (!parentDsti) {
    1507           0 :       return;
    1508             :     }
    1509             : 
    1510           0 :     window = parentDsti->GetWindow();
    1511           0 :     if (window) {
    1512             :       // if the parent window is visible but aWindow was not, then we have
    1513             :       // likely moved up and out from a hidden tab to the browser window, or a
    1514             :       // similar such arrangement. Stop adjusting the current nodes.
    1515           0 :       if (IsWindowVisible(window) != isVisible)
    1516             :         break;
    1517             : 
    1518             :       // When aCheckPermission is true, we should check whether the caller can
    1519             :       // access the window or not.  If it cannot access, we should stop the
    1520             :       // adjusting.
    1521           0 :       if (aCheckPermission && !nsContentUtils::LegacyIsCallerNativeCode() &&
    1522           0 :           !nsContentUtils::CanCallerAccess(window->GetCurrentInnerWindow())) {
    1523             :         break;
    1524             :       }
    1525             : 
    1526           0 :       window->SetFocusedElement(frameElement);
    1527             :     }
    1528             :   }
    1529             : }
    1530             : 
    1531             : bool
    1532           3 : nsFocusManager::IsWindowVisible(nsPIDOMWindowOuter* aWindow)
    1533             : {
    1534           0 :   if (!aWindow || aWindow->IsFrozen())
    1535             :     return false;
    1536             : 
    1537             :   // Check if the inner window is frozen as well. This can happen when a focus change
    1538             :   // occurs while restoring a previous page.
    1539           3 :   nsPIDOMWindowInner* innerWindow = aWindow->GetCurrentInnerWindow();
    1540           3 :   if (!innerWindow || innerWindow->IsFrozen())
    1541             :     return false;
    1542             : 
    1543           6 :   nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
    1544           9 :   nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(docShell));
    1545           0 :   if (!baseWin)
    1546             :     return false;
    1547             : 
    1548           3 :   bool visible = false;
    1549           3 :   baseWin->GetVisibility(&visible);
    1550           0 :   return visible;
    1551             : }
    1552             : 
    1553             : bool
    1554           1 : nsFocusManager::IsNonFocusableRoot(nsIContent* aContent)
    1555             : {
    1556           0 :   MOZ_ASSERT(aContent, "aContent must not be NULL");
    1557           1 :   MOZ_ASSERT(aContent->IsInComposedDoc(), "aContent must be in a document");
    1558             : 
    1559             :   // If aContent is in designMode, the root element is not focusable.
    1560             :   // NOTE: in designMode, most elements are not focusable, just the document is
    1561             :   //       focusable.
    1562             :   // Also, if aContent is not editable but it isn't in designMode, it's not
    1563             :   // focusable.
    1564             :   // And in userfocusignored context nothing is focusable.
    1565           1 :   nsIDocument* doc = aContent->GetComposedDoc();
    1566           1 :   NS_ASSERTION(doc, "aContent must have current document");
    1567           0 :   return aContent == doc->GetRootElement() &&
    1568           0 :            (doc->HasFlag(NODE_IS_EDITABLE) || !aContent->IsEditable() ||
    1569           0 :             nsContentUtils::IsUserFocusIgnored(aContent));
    1570             : }
    1571             : 
    1572             : Element*
    1573           2 : nsFocusManager::CheckIfFocusable(Element* aElement, uint32_t aFlags)
    1574             : {
    1575           0 :   if (!aElement)
    1576             :     return nullptr;
    1577             : 
    1578             :   // this is a special case for some XUL elements or input number, where an
    1579             :   // anonymous child is actually focusable and not the element itself.
    1580           4 :   RefPtr<Element> redirectedFocus = GetRedirectedFocus(aElement);
    1581           2 :   if (redirectedFocus) {
    1582           0 :     return CheckIfFocusable(redirectedFocus, aFlags);
    1583             :   }
    1584             : 
    1585           4 :   nsCOMPtr<nsIDocument> doc = aElement->GetComposedDoc();
    1586             :   // can't focus elements that are not in documents
    1587           0 :   if (!doc) {
    1588           0 :     LOGCONTENT("Cannot focus %s because content not in document", aElement)
    1589             :     return nullptr;
    1590             :   }
    1591             : 
    1592             :   // Make sure that our frames are up to date while ensuring the presshell is
    1593             :   // also initialized in case we come from a script calling focus() early.
    1594           2 :   mEventHandlingNeedsFlush = false;
    1595           2 :   doc->FlushPendingNotifications(FlushType::EnsurePresShellInitAndFrames);
    1596             : 
    1597           0 :   nsIPresShell *shell = doc->GetShell();
    1598           2 :   if (!shell)
    1599             :     return nullptr;
    1600             : 
    1601             :   // the root content can always be focused,
    1602             :   // except in userfocusignored context.
    1603           2 :   if (aElement == doc->GetRootElement()) {
    1604           0 :     return nsContentUtils::IsUserFocusIgnored(aElement) ? nullptr : aElement;
    1605             :   }
    1606             : 
    1607             :   // cannot focus content in print preview mode. Only the root can be focused.
    1608           2 :   nsPresContext* presContext = shell->GetPresContext();
    1609           2 :   if (presContext && presContext->Type() == nsPresContext::eContext_PrintPreview) {
    1610           0 :     LOGCONTENT("Cannot focus %s while in print preview", aElement)
    1611             :     return nullptr;
    1612             :   }
    1613             : 
    1614           2 :   nsIFrame* frame = aElement->GetPrimaryFrame();
    1615           2 :   if (!frame) {
    1616           0 :     LOGCONTENT("Cannot focus %s as it has no frame", aElement)
    1617             :     return nullptr;
    1618             :   }
    1619             : 
    1620           2 :   if (aElement->IsHTMLElement(nsGkAtoms::area)) {
    1621             :     // HTML areas do not have their own frame, and the img frame we get from
    1622             :     // GetPrimaryFrame() is not relevant as to whether it is focusable or
    1623             :     // not, so we have to do all the relevant checks manually for them.
    1624           0 :     return frame->IsVisibleConsideringAncestors() &&
    1625           0 :            aElement->IsFocusable() ? aElement : nullptr;
    1626             :   }
    1627             : 
    1628             :   // if this is a child frame content node, check if it is visible and
    1629             :   // call the content node's IsFocusable method instead of the frame's
    1630             :   // IsFocusable method. This skips checking the style system and ensures that
    1631             :   // offscreen browsers can still be focused.
    1632           2 :   nsIDocument* subdoc = doc->GetSubDocumentFor(aElement);
    1633           2 :   if (subdoc && IsWindowVisible(subdoc->GetWindow())) {
    1634           0 :     const nsStyleUserInterface* ui = frame->StyleUserInterface();
    1635           0 :     int32_t tabIndex = (ui->mUserFocus == StyleUserFocus::Ignore ||
    1636           0 :                         ui->mUserFocus == StyleUserFocus::None) ? -1 : 0;
    1637           0 :     return aElement->IsFocusable(&tabIndex, aFlags & FLAG_BYMOUSE) ? aElement : nullptr;
    1638             :   }
    1639             : 
    1640           2 :   return frame->IsFocusable(nullptr, aFlags & FLAG_BYMOUSE) ? aElement : nullptr;
    1641             : }
    1642             : 
    1643             : bool
    1644           0 : nsFocusManager::Blur(nsPIDOMWindowOuter* aWindowToClear,
    1645             :                      nsPIDOMWindowOuter* aAncestorWindowToFocus,
    1646             :                      bool aIsLeavingDocument,
    1647             :                      bool aAdjustWidgets,
    1648             :                      nsIContent* aContentToFocus)
    1649             : {
    1650           0 :   LOGFOCUS(("<<Blur begin>>"));
    1651             : 
    1652             :   // hold a reference to the focused content, which may be null
    1653           0 :   RefPtr<Element> element = mFocusedElement;
    1654           0 :   if (element) {
    1655           0 :     if (!element->IsInComposedDoc()) {
    1656           0 :       mFocusedElement = nullptr;
    1657           0 :       return true;
    1658             :     }
    1659           0 :     if (element == mFirstBlurEvent)
    1660             :       return true;
    1661             :   }
    1662             : 
    1663             :   // hold a reference to the focused window
    1664           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = mFocusedWindow;
    1665           0 :   if (!window) {
    1666           0 :     mFocusedElement = nullptr;
    1667           0 :     return true;
    1668             :   }
    1669             : 
    1670           0 :   nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
    1671           0 :   if (!docShell) {
    1672           0 :     mFocusedElement = nullptr;
    1673           0 :     return true;
    1674             :   }
    1675             : 
    1676             :   // Keep a ref to presShell since dispatching the DOM event may cause
    1677             :   // the document to be destroyed.
    1678           0 :   nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
    1679           0 :   if (!presShell) {
    1680           0 :     mFocusedElement = nullptr;
    1681           0 :     return true;
    1682             :   }
    1683             : 
    1684           0 :   bool clearFirstBlurEvent = false;
    1685           0 :   if (!mFirstBlurEvent) {
    1686           0 :     mFirstBlurEvent = element;
    1687           0 :     clearFirstBlurEvent = true;
    1688             :   }
    1689             : 
    1690             :   nsPresContext* focusedPresContext =
    1691           0 :     mActiveWindow ? presShell->GetPresContext() : nullptr;
    1692           0 :   IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
    1693           0 :                                  GetFocusMoveActionCause(0));
    1694             : 
    1695             :   // now adjust the actual focus, by clearing the fields in the focus manager
    1696             :   // and in the window.
    1697           0 :   mFocusedElement = nullptr;
    1698           0 :   bool shouldShowFocusRing = window->ShouldShowFocusRing();
    1699           0 :   if (aWindowToClear)
    1700           0 :     aWindowToClear->SetFocusedElement(nullptr);
    1701             : 
    1702           0 :   LOGCONTENT("Element %s has been blurred", element.get());
    1703             : 
    1704             :   // Don't fire blur event on the root content which isn't editable.
    1705             :   bool sendBlurEvent =
    1706           0 :     element && element->IsInComposedDoc() && !IsNonFocusableRoot(element);
    1707           0 :   if (element) {
    1708           0 :     if (sendBlurEvent) {
    1709           0 :       NotifyFocusStateChange(element,
    1710             :                              aContentToFocus,
    1711             :                              shouldShowFocusRing,
    1712           0 :                              false);
    1713             :     }
    1714             : 
    1715             :     // if an object/plug-in/remote browser is being blurred, move the system focus
    1716             :     // to the parent window, otherwise events will still get fired at the plugin.
    1717             :     // But don't do this if we are blurring due to the window being lowered,
    1718             :     // otherwise, the parent window can get raised again.
    1719           0 :     if (mActiveWindow) {
    1720           0 :       nsIFrame* contentFrame = element->GetPrimaryFrame();
    1721           0 :       nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
    1722           0 :       if (aAdjustWidgets && objectFrame && !sTestMode) {
    1723           0 :         if (XRE_IsContentProcess()) {
    1724             :           // set focus to the top level window via the chrome process.
    1725           0 :           nsCOMPtr<nsITabChild> tabChild = docShell->GetTabChild();
    1726           0 :           if (tabChild) {
    1727           0 :             static_cast<TabChild*>(tabChild.get())->SendDispatchFocusToTopLevelWindow();
    1728             :           }
    1729             :         } else {
    1730             :           // note that the presshell's widget is being retrieved here, not the one
    1731             :           // for the object frame.
    1732           0 :           nsViewManager* vm = presShell->GetViewManager();
    1733           0 :           if (vm) {
    1734           0 :             nsCOMPtr<nsIWidget> widget;
    1735           0 :             vm->GetRootWidget(getter_AddRefs(widget));
    1736           0 :             if (widget) {
    1737             :               // set focus to the top level window but don't raise it.
    1738           0 :               widget->SetFocus(false);
    1739             :             }
    1740             :           }
    1741             :         }
    1742             :       }
    1743             :     }
    1744             : 
    1745             :       // if the object being blurred is a remote browser, deactivate remote content
    1746           0 :     if (TabParent* remote = TabParent::GetFrom(element)) {
    1747           0 :       remote->Deactivate();
    1748           0 :       LOGFOCUS(("Remote browser deactivated"));
    1749             :     }
    1750             :   }
    1751             : 
    1752           0 :   bool result = true;
    1753           0 :   if (sendBlurEvent) {
    1754             :     // if there is an active window, update commands. If there isn't an active
    1755             :     // window, then this was a blur caused by the active window being lowered,
    1756             :     // so there is no need to update the commands
    1757           0 :     if (mActiveWindow)
    1758           0 :       window->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
    1759             : 
    1760           0 :     SendFocusOrBlurEvent(eBlur, presShell,
    1761           0 :                          element->GetComposedDoc(), element, 1,
    1762           0 :                          false, false, aContentToFocus);
    1763             :   }
    1764             : 
    1765             :   // if we are leaving the document or the window was lowered, make the caret
    1766             :   // invisible.
    1767           0 :   if (aIsLeavingDocument || !mActiveWindow) {
    1768           0 :     SetCaretVisible(presShell, false, nullptr);
    1769             :   }
    1770             : 
    1771           0 :   RefPtr<AccessibleCaretEventHub> eventHub = presShell->GetAccessibleCaretEventHub();
    1772           0 :   if (eventHub) {
    1773           0 :     eventHub->NotifyBlur(aIsLeavingDocument || !mActiveWindow);
    1774             :   }
    1775             : 
    1776             :   // at this point, it is expected that this window will be still be
    1777             :   // focused, but the focused element will be null, as it was cleared before
    1778             :   // the event. If this isn't the case, then something else was focused during
    1779             :   // the blur event above and we should just return. However, if
    1780             :   // aIsLeavingDocument is set, a new document is desired, so make sure to
    1781             :   // blur the document and window.
    1782           0 :   if (mFocusedWindow != window ||
    1783           0 :       (mFocusedElement != nullptr && !aIsLeavingDocument)) {
    1784             :     result = false;
    1785             :   }
    1786           0 :   else if (aIsLeavingDocument) {
    1787           0 :     window->TakeFocus(false, 0);
    1788             : 
    1789             :     // clear the focus so that the ancestor frame hierarchy is in the correct
    1790             :     // state. Pass true because aAncestorWindowToFocus is thought to be
    1791             :     // focused at this point.
    1792           0 :     if (aAncestorWindowToFocus)
    1793           0 :       aAncestorWindowToFocus->SetFocusedElement(nullptr, 0, true);
    1794             : 
    1795           0 :     SetFocusedWindowInternal(nullptr);
    1796           0 :     mFocusedElement = nullptr;
    1797             : 
    1798             :     // pass 1 for the focus method when calling SendFocusOrBlurEvent just so
    1799             :     // that the check is made for suppressed documents. Check to ensure that
    1800             :     // the document isn't null in case someone closed it during the blur above
    1801           0 :     nsIDocument* doc = window->GetExtantDoc();
    1802           0 :     if (doc)
    1803           0 :       SendFocusOrBlurEvent(eBlur, presShell, doc, doc, 1, false);
    1804           0 :     if (mFocusedWindow == nullptr)
    1805           0 :       SendFocusOrBlurEvent(eBlur, presShell, doc,
    1806           0 :                            window->GetCurrentInnerWindow(), 1, false);
    1807             : 
    1808             :     // check if a different window was focused
    1809           0 :     result = (mFocusedWindow == nullptr && mActiveWindow);
    1810             :   }
    1811           0 :   else if (mActiveWindow) {
    1812             :     // Otherwise, the blur of the element without blurring the document
    1813             :     // occurred normally. Call UpdateCaret to redisplay the caret at the right
    1814             :     // location within the document. This is needed to ensure that the caret
    1815             :     // used for caret browsing is made visible again when an input field is
    1816             :     // blurred.
    1817           0 :     UpdateCaret(false, true, nullptr);
    1818             :   }
    1819             : 
    1820           0 :   if (clearFirstBlurEvent)
    1821           0 :     mFirstBlurEvent = nullptr;
    1822             : 
    1823             :   return result;
    1824             : }
    1825             : 
    1826             : void
    1827           1 : nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow,
    1828             :                       Element* aElement,
    1829             :                       uint32_t aFlags,
    1830             :                       bool aIsNewDocument,
    1831             :                       bool aFocusChanged,
    1832             :                       bool aWindowRaised,
    1833             :                       bool aAdjustWidgets,
    1834             :                       nsIContent* aContentLostFocus)
    1835             : {
    1836           1 :   LOGFOCUS(("<<Focus begin>>"));
    1837             : 
    1838           0 :   if (!aWindow)
    1839           0 :     return;
    1840             : 
    1841           0 :   if (aElement && (aElement == mFirstFocusEvent || aElement == mFirstBlurEvent))
    1842             :     return;
    1843             : 
    1844             :   // Keep a reference to the presShell since dispatching the DOM event may
    1845             :   // cause the document to be destroyed.
    1846           2 :   nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
    1847           1 :   if (!docShell)
    1848           0 :     return;
    1849             : 
    1850           0 :   nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
    1851           1 :   if (!presShell)
    1852           0 :     return;
    1853             : 
    1854             :   // If the focus actually changed, set the focus method (mouse, keyboard, etc).
    1855             :   // Otherwise, just get the current focus method and use that. This ensures
    1856             :   // that the method is set during the document and window focus events.
    1857           2 :   uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
    1858           2 :                          aWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
    1859             : 
    1860           0 :   if (!IsWindowVisible(aWindow)) {
    1861             :     // if the window isn't visible, for instance because it is a hidden tab,
    1862             :     // update the current focus and scroll it into view but don't do anything else
    1863           0 :     if (CheckIfFocusable(aElement, aFlags)) {
    1864           0 :       aWindow->SetFocusedElement(aElement, focusMethod);
    1865           0 :       if (aFocusChanged)
    1866           0 :         ScrollIntoView(presShell, aElement, aFlags);
    1867             :     }
    1868             :     return;
    1869             :   }
    1870             : 
    1871           1 :   bool clearFirstFocusEvent = false;
    1872           2 :   if (!mFirstFocusEvent) {
    1873           0 :     mFirstFocusEvent = aElement;
    1874           0 :     clearFirstFocusEvent = true;
    1875             :   }
    1876             : 
    1877           1 :   LOGCONTENT("Element %s has been focused", aElement);
    1878             : 
    1879           0 :   if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
    1880           0 :     nsIDocument* docm = aWindow->GetExtantDoc();
    1881           0 :     if (docm) {
    1882           0 :       LOGCONTENT(" from %s", docm->GetRootElement());
    1883             :     }
    1884           0 :     LOGFOCUS((" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x]",
    1885             :              aIsNewDocument, aFocusChanged, aWindowRaised, aFlags));
    1886             :   }
    1887             : 
    1888           1 :   if (aIsNewDocument) {
    1889             :     // if this is a new document, update the parent chain of frames so that
    1890             :     // focus can be traversed from the top level down to the newly focused
    1891             :     // window.
    1892           1 :     AdjustWindowFocus(aWindow, false);
    1893             :   }
    1894             : 
    1895             :   // indicate that the window has taken focus.
    1896           1 :   if (aWindow->TakeFocus(true, focusMethod))
    1897           0 :     aIsNewDocument = true;
    1898             : 
    1899           0 :   SetFocusedWindowInternal(aWindow);
    1900             : 
    1901             :   // Update the system focus by focusing the root widget.  But avoid this
    1902             :   // if 1) aAdjustWidgets is false or 2) aElement is a plugin that has its
    1903             :   // own widget and is either already focused or is about to be focused.
    1904           2 :   nsCOMPtr<nsIWidget> objectFrameWidget;
    1905           1 :   if (aElement) {
    1906           0 :     nsIFrame* contentFrame = aElement->GetPrimaryFrame();
    1907           0 :     nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
    1908           0 :     if (objectFrame)
    1909           0 :       objectFrameWidget = objectFrame->GetWidget();
    1910             :   }
    1911           0 :   if (aAdjustWidgets && !objectFrameWidget && !sTestMode) {
    1912           0 :     nsViewManager* vm = presShell->GetViewManager();
    1913           0 :     if (vm) {
    1914           0 :       nsCOMPtr<nsIWidget> widget;
    1915           0 :       vm->GetRootWidget(getter_AddRefs(widget));
    1916           0 :       if (widget)
    1917           0 :         widget->SetFocus(false);
    1918             :     }
    1919             :   }
    1920             : 
    1921             :   // if switching to a new document, first fire the focus event on the
    1922             :   // document and then the window.
    1923           1 :   if (aIsNewDocument) {
    1924           1 :     nsIDocument* doc = aWindow->GetExtantDoc();
    1925             :     // The focus change should be notified to IMEStateManager from here if
    1926             :     // the focused element is a designMode editor since any content won't
    1927             :     // receive focus event.
    1928           1 :     if (doc && doc->HasFlag(NODE_IS_EDITABLE)) {
    1929           0 :       IMEStateManager::OnChangeFocus(presShell->GetPresContext(), nullptr,
    1930           0 :                                      GetFocusMoveActionCause(aFlags));
    1931             :     }
    1932           0 :     if (doc) {
    1933           2 :       SendFocusOrBlurEvent(eFocus, presShell, doc,
    1934           0 :                            doc, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
    1935             :     }
    1936           0 :     if (mFocusedWindow == aWindow && mFocusedElement == nullptr) {
    1937           2 :       SendFocusOrBlurEvent(eFocus, presShell, doc,
    1938           0 :                            aWindow->GetCurrentInnerWindow(),
    1939           0 :                            aFlags & FOCUSMETHOD_MASK, aWindowRaised);
    1940             :     }
    1941             :   }
    1942             : 
    1943             :   // check to ensure that the element is still focusable, and that nothing
    1944             :   // else was focused during the events above.
    1945           3 :   if (CheckIfFocusable(aElement, aFlags) &&
    1946           4 :       mFocusedWindow == aWindow && mFocusedElement == nullptr) {
    1947           0 :     mFocusedElement = aElement;
    1948             : 
    1949           0 :     nsIContent* focusedNode = aWindow->GetFocusedElement();
    1950           1 :     bool isRefocus = focusedNode && focusedNode->IsEqualNode(aElement);
    1951             : 
    1952           0 :     aWindow->SetFocusedElement(aElement, focusMethod);
    1953             : 
    1954             :     // if the focused element changed, scroll it into view
    1955           1 :     if (aElement && aFocusChanged) {
    1956           0 :       ScrollIntoView(presShell, aElement, aFlags);
    1957             :     }
    1958             : 
    1959             :     bool sendFocusEvent =
    1960           1 :       aElement && aElement->IsInComposedDoc() && !IsNonFocusableRoot(aElement);
    1961           2 :     nsPresContext* presContext = presShell->GetPresContext();
    1962           0 :     if (sendFocusEvent) {
    1963           0 :       NotifyFocusStateChange(aElement,
    1964             :                              nullptr,
    1965           0 :                              aWindow->ShouldShowFocusRing(),
    1966           1 :                              true);
    1967             : 
    1968             :       // if this is an object/plug-in/remote browser, focus its widget.  Note that we might
    1969             :       // no longer be in the same document, due to the events we fired above when
    1970             :       // aIsNewDocument.
    1971           2 :       if (presShell->GetDocument() == aElement->GetComposedDoc()) {
    1972           2 :         if (aAdjustWidgets && objectFrameWidget && !sTestMode)
    1973           0 :           objectFrameWidget->SetFocus(false);
    1974             : 
    1975             :         // if the object being focused is a remote browser, activate remote content
    1976           1 :         if (TabParent* remote = TabParent::GetFrom(aElement)) {
    1977           0 :           remote->Activate();
    1978           0 :           LOGFOCUS(("Remote browser activated"));
    1979             :         }
    1980             :       }
    1981             : 
    1982           1 :       IMEStateManager::OnChangeFocus(presContext, aElement,
    1983           1 :                                      GetFocusMoveActionCause(aFlags));
    1984             : 
    1985             :       // as long as this focus wasn't because a window was raised, update the
    1986             :       // commands
    1987             :       // XXXndeakin P2 someone could adjust the focus during the update
    1988           1 :       if (!aWindowRaised)
    1989           0 :         aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
    1990             : 
    1991           0 :       SendFocusOrBlurEvent(eFocus, presShell,
    1992             :                            aElement->GetComposedDoc(),
    1993             :                            aElement, aFlags & FOCUSMETHOD_MASK,
    1994           1 :                            aWindowRaised, isRefocus, aContentLostFocus);
    1995             :     } else {
    1996           0 :       IMEStateManager::OnChangeFocus(presContext, nullptr,
    1997           0 :                                      GetFocusMoveActionCause(aFlags));
    1998           0 :       if (!aWindowRaised) {
    1999           0 :         aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
    2000             :       }
    2001             :     }
    2002             :   }
    2003             :   else {
    2004             :     // If the window focus event (fired above when aIsNewDocument) caused
    2005             :     // the plugin not to be focusable, update the system focus by focusing
    2006             :     // the root widget.
    2007           0 :     if (aAdjustWidgets && objectFrameWidget &&
    2008           0 :         mFocusedWindow == aWindow && mFocusedElement == nullptr &&
    2009           0 :         !sTestMode) {
    2010           0 :       nsViewManager* vm = presShell->GetViewManager();
    2011           0 :       if (vm) {
    2012           0 :         nsCOMPtr<nsIWidget> widget;
    2013           0 :         vm->GetRootWidget(getter_AddRefs(widget));
    2014           0 :         if (widget)
    2015           0 :           widget->SetFocus(false);
    2016             :       }
    2017             :     }
    2018             : 
    2019           0 :     if (!mFocusedElement) {
    2020             :       // When there is no focused element, IMEStateManager needs to adjust IME
    2021             :       // enabled state with the document.
    2022           0 :       nsPresContext* presContext = presShell->GetPresContext();
    2023           0 :       IMEStateManager::OnChangeFocus(presContext, nullptr,
    2024           0 :                                      GetFocusMoveActionCause(aFlags));
    2025             :     }
    2026             : 
    2027           0 :     if (!aWindowRaised)
    2028           0 :       aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
    2029             :   }
    2030             : 
    2031             :   // update the caret visibility and position to match the newly focused
    2032             :   // element. However, don't update the position if this was a focus due to a
    2033             :   // mouse click as the selection code would already have moved the caret as
    2034             :   // needed. If this is a different document than was focused before, also
    2035             :   // update the caret's visibility. If this is the same document, the caret
    2036             :   // visibility should be the same as before so there is no need to update it.
    2037           2 :   if (mFocusedElement == aElement)
    2038           1 :     UpdateCaret(aFocusChanged && !(aFlags & FLAG_BYMOUSE), aIsNewDocument,
    2039           0 :                 mFocusedElement);
    2040             : 
    2041           0 :   if (clearFirstFocusEvent)
    2042           1 :     mFirstFocusEvent = nullptr;
    2043             : }
    2044             : 
    2045          12 : class FocusBlurEvent : public Runnable
    2046             : {
    2047             : public:
    2048           3 :   FocusBlurEvent(nsISupports* aTarget,
    2049             :                  EventMessage aEventMessage,
    2050             :                  nsPresContext* aContext,
    2051             :                  bool aWindowRaised,
    2052             :                  bool aIsRefocus,
    2053             :                  EventTarget* aRelatedTarget)
    2054           3 :     : mozilla::Runnable("FocusBlurEvent")
    2055             :     , mTarget(aTarget)
    2056             :     , mContext(aContext)
    2057             :     , mEventMessage(aEventMessage)
    2058             :     , mWindowRaised(aWindowRaised)
    2059             :     , mIsRefocus(aIsRefocus)
    2060           3 :     , mRelatedTarget(aRelatedTarget)
    2061             :   {
    2062           0 :   }
    2063             : 
    2064           0 :   NS_IMETHOD Run() override
    2065             :   {
    2066           0 :     InternalFocusEvent event(true, mEventMessage);
    2067           3 :     event.mFlags.mBubbles = false;
    2068           0 :     event.mFlags.mCancelable = false;
    2069           0 :     event.mFromRaise = mWindowRaised;
    2070           0 :     event.mIsRefocus = mIsRefocus;
    2071           0 :     event.mRelatedTarget = mRelatedTarget;
    2072           0 :     return EventDispatcher::Dispatch(mTarget, mContext, &event);
    2073             :   }
    2074             : 
    2075             :   nsCOMPtr<nsISupports>   mTarget;
    2076             :   RefPtr<nsPresContext> mContext;
    2077             :   EventMessage            mEventMessage;
    2078             :   bool                    mWindowRaised;
    2079             :   bool                    mIsRefocus;
    2080             :   nsCOMPtr<EventTarget>   mRelatedTarget;
    2081             : };
    2082             : 
    2083           4 : class FocusInOutEvent : public Runnable
    2084             : {
    2085             : public:
    2086           1 :   FocusInOutEvent(nsISupports* aTarget,
    2087             :                   EventMessage aEventMessage,
    2088             :                   nsPresContext* aContext,
    2089             :                   nsPIDOMWindowOuter* aOriginalFocusedWindow,
    2090             :                   nsIContent* aOriginalFocusedContent,
    2091             :                   EventTarget* aRelatedTarget)
    2092           1 :     : mozilla::Runnable("FocusInOutEvent")
    2093             :     , mTarget(aTarget)
    2094             :     , mContext(aContext)
    2095             :     , mEventMessage(aEventMessage)
    2096             :     , mOriginalFocusedWindow(aOriginalFocusedWindow)
    2097             :     , mOriginalFocusedContent(aOriginalFocusedContent)
    2098           1 :     , mRelatedTarget(aRelatedTarget)
    2099             :   {
    2100           0 :   }
    2101             : 
    2102           0 :   NS_IMETHOD Run() override
    2103             :   {
    2104             :     nsCOMPtr<nsIContent> originalWindowFocus = mOriginalFocusedWindow ?
    2105           1 :         mOriginalFocusedWindow->GetFocusedElement() :
    2106           5 :         nullptr;
    2107             :     // Blink does not check that focus is the same after blur, but WebKit does.
    2108             :     // Opt to follow Blink's behavior (see bug 687787).
    2109           2 :     if (mEventMessage == eFocusOut ||
    2110           2 :         originalWindowFocus == mOriginalFocusedContent) {
    2111           0 :       InternalFocusEvent event(true, mEventMessage);
    2112           0 :       event.mFlags.mBubbles = true;
    2113           0 :       event.mFlags.mCancelable = false;
    2114           0 :       event.mRelatedTarget = mRelatedTarget;
    2115           0 :       return EventDispatcher::Dispatch(mTarget, mContext, &event);
    2116             :     }
    2117             :     return NS_OK;
    2118             :   }
    2119             : 
    2120             :   nsCOMPtr<nsISupports>        mTarget;
    2121             :   RefPtr<nsPresContext>        mContext;
    2122             :   EventMessage                 mEventMessage;
    2123             :   nsCOMPtr<nsPIDOMWindowOuter> mOriginalFocusedWindow;
    2124             :   nsCOMPtr<nsIContent>         mOriginalFocusedContent;
    2125             :   nsCOMPtr<EventTarget>        mRelatedTarget;
    2126             : };
    2127             : 
    2128             : static nsIDocument*
    2129           9 : GetDocumentHelper(EventTarget* aTarget)
    2130             : {
    2131           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
    2132           9 :   if (!node) {
    2133           0 :     nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aTarget);
    2134           0 :     return win ? win->GetExtantDoc() : nullptr;
    2135             :   }
    2136             : 
    2137           4 :   return node->OwnerDoc();
    2138             : }
    2139             : 
    2140           1 : void nsFocusManager::FireFocusInOrOutEvent(EventMessage aEventMessage,
    2141             :                                      nsIPresShell* aPresShell,
    2142             :                                      nsISupports* aTarget,
    2143             :                                      nsPIDOMWindowOuter* aCurrentFocusedWindow,
    2144             :                                      nsIContent* aCurrentFocusedContent,
    2145             :                                      EventTarget* aRelatedTarget)
    2146             : {
    2147           1 :   NS_ASSERTION(aEventMessage == eFocusIn || aEventMessage == eFocusOut,
    2148             :       "Wrong event type for FireFocusInOrOutEvent");
    2149             : 
    2150             :   nsContentUtils::AddScriptRunner(
    2151             :       new FocusInOutEvent(
    2152             :         aTarget,
    2153             :         aEventMessage,
    2154           1 :         aPresShell->GetPresContext(),
    2155             :         aCurrentFocusedWindow,
    2156             :         aCurrentFocusedContent,
    2157           1 :         aRelatedTarget));
    2158           1 : }
    2159             : 
    2160             : void
    2161           3 : nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage,
    2162             :                                      nsIPresShell* aPresShell,
    2163             :                                      nsIDocument* aDocument,
    2164             :                                      nsISupports* aTarget,
    2165             :                                      uint32_t aFocusMethod,
    2166             :                                      bool aWindowRaised,
    2167             :                                      bool aIsRefocus,
    2168             :                                      EventTarget* aRelatedTarget)
    2169             : {
    2170           3 :   NS_ASSERTION(aEventMessage == eFocus || aEventMessage == eBlur,
    2171             :                "Wrong event type for SendFocusOrBlurEvent");
    2172             : 
    2173           6 :   nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(aTarget);
    2174           6 :   nsCOMPtr<nsIDocument> eventTargetDoc = GetDocumentHelper(eventTarget);
    2175           0 :   nsCOMPtr<nsIDocument> relatedTargetDoc = GetDocumentHelper(aRelatedTarget);
    2176             : 
    2177             :   // set aRelatedTarget to null if it's not in the same document as eventTarget
    2178           3 :   if (eventTargetDoc != relatedTargetDoc) {
    2179           3 :     aRelatedTarget = nullptr;
    2180             :   }
    2181             : 
    2182             :   bool dontDispatchEvent =
    2183           6 :     eventTargetDoc && nsContentUtils::IsUserFocusIgnored(eventTargetDoc);
    2184             : 
    2185           0 :   if (!dontDispatchEvent && aDocument && aDocument->EventHandlingSuppressed()) {
    2186           0 :     for (uint32_t i = mDelayedBlurFocusEvents.Length(); i > 0; --i) {
    2187             :       // if this event was already queued, remove it and append it to the end
    2188           0 :       if (mDelayedBlurFocusEvents[i - 1].mEventMessage == aEventMessage &&
    2189           0 :           mDelayedBlurFocusEvents[i - 1].mPresShell == aPresShell &&
    2190           0 :           mDelayedBlurFocusEvents[i - 1].mDocument == aDocument &&
    2191           0 :           mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget &&
    2192           0 :           mDelayedBlurFocusEvents[i - 1].mRelatedTarget == aRelatedTarget) {
    2193           0 :         mDelayedBlurFocusEvents.RemoveElementAt(i - 1);
    2194             :       }
    2195             :     }
    2196             : 
    2197           0 :     mDelayedBlurFocusEvents.AppendElement(
    2198           0 :       nsDelayedBlurOrFocusEvent(aEventMessage, aPresShell,
    2199           0 :                                 aDocument, eventTarget, aRelatedTarget));
    2200           0 :     return;
    2201             :   }
    2202             : 
    2203             :   // If mDelayedBlurFocusEvents queue is not empty, check if there are events
    2204             :   // that belongs to this doc, if yes, fire them first.
    2205           6 :   if (aDocument && !aDocument->EventHandlingSuppressed() &&
    2206           6 :       mDelayedBlurFocusEvents.Length()) {
    2207           0 :     FireDelayedEvents(aDocument);
    2208             :   }
    2209             : 
    2210           3 :   FireFocusOrBlurEvent(aEventMessage, aPresShell, aTarget, aWindowRaised,
    2211           3 :                        aIsRefocus, aRelatedTarget);
    2212             : }
    2213             : 
    2214             : void
    2215           3 : nsFocusManager::FireFocusOrBlurEvent(EventMessage aEventMessage,
    2216             :                                      nsIPresShell* aPresShell,
    2217             :                                      nsISupports* aTarget,
    2218             :                                      bool aWindowRaised,
    2219             :                                      bool aIsRefocus,
    2220             :                                      EventTarget* aRelatedTarget)
    2221             : {
    2222           6 :   nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(aTarget);
    2223           6 :   nsCOMPtr<nsIDocument> eventTargetDoc = GetDocumentHelper(eventTarget);
    2224           0 :   nsCOMPtr<nsPIDOMWindowOuter> currentWindow = mFocusedWindow;
    2225           0 :   nsCOMPtr<nsPIDOMWindowInner> targetWindow = do_QueryInterface(aTarget);
    2226           0 :   nsCOMPtr<nsIDocument> targetDocument = do_QueryInterface(aTarget);
    2227             :   nsCOMPtr<nsIContent> currentFocusedContent = currentWindow ?
    2228           0 :       currentWindow->GetFocusedElement() : nullptr;
    2229             : 
    2230             :   bool dontDispatchEvent =
    2231           6 :     eventTargetDoc && nsContentUtils::IsUserFocusIgnored(eventTargetDoc);
    2232             : 
    2233             : #ifdef ACCESSIBILITY
    2234           3 :   nsAccessibilityService* accService = GetAccService();
    2235           3 :   if (accService) {
    2236           0 :     if (aEventMessage == eFocus) {
    2237           0 :       accService->NotifyOfDOMFocus(aTarget);
    2238             :     } else {
    2239           0 :       accService->NotifyOfDOMBlur(aTarget);
    2240             :     }
    2241             :   }
    2242             : #endif
    2243             : 
    2244           3 :   if (!dontDispatchEvent) {
    2245             :     nsContentUtils::AddScriptRunner(
    2246           0 :       new FocusBlurEvent(aTarget, aEventMessage, aPresShell->GetPresContext(),
    2247           3 :                          aWindowRaised, aIsRefocus, aRelatedTarget));
    2248             : 
    2249             :     // Check that the target is not a window or document before firing
    2250             :     // focusin/focusout. Other browsers do not fire focusin/focusout on window,
    2251             :     // despite being required in the spec, so follow their behavior.
    2252             :     //
    2253             :     // As for document, we should not even fire focus/blur, but until then, we
    2254             :     // need this check. targetDocument should be removed once bug 1228802 is
    2255             :     // resolved.
    2256           5 :     if (!targetWindow && !targetDocument) {
    2257           1 :       EventMessage focusInOrOutMessage = aEventMessage == eFocus ? eFocusIn : eFocusOut;
    2258           0 :       FireFocusInOrOutEvent(focusInOrOutMessage, aPresShell, aTarget,
    2259           0 :           currentWindow, currentFocusedContent, aRelatedTarget);
    2260             :     }
    2261             :   }
    2262           3 : }
    2263             : 
    2264             : void
    2265           1 : nsFocusManager::ScrollIntoView(nsIPresShell* aPresShell,
    2266             :                                nsIContent* aContent,
    2267             :                                uint32_t aFlags)
    2268             : {
    2269             :   // if the noscroll flag isn't set, scroll the newly focused element into view
    2270           1 :   if (!(aFlags & FLAG_NOSCROLL))
    2271           0 :     aPresShell->ScrollContentIntoView(aContent,
    2272             :                                       nsIPresShell::ScrollAxis(
    2273             :                                         nsIPresShell::SCROLL_MINIMUM,
    2274             :                                         nsIPresShell::SCROLL_IF_NOT_VISIBLE),
    2275             :                                       nsIPresShell::ScrollAxis(
    2276             :                                         nsIPresShell::SCROLL_MINIMUM,
    2277             :                                         nsIPresShell::SCROLL_IF_NOT_VISIBLE),
    2278           0 :                                       nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
    2279           1 : }
    2280             : 
    2281             : 
    2282             : void
    2283           0 : nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow)
    2284             : {
    2285             :   // don't raise windows that are already raised or are in the process of
    2286             :   // being lowered
    2287           0 :   if (!aWindow || aWindow == mActiveWindow || aWindow == mWindowBeingLowered)
    2288           0 :     return;
    2289             : 
    2290           0 :   if (sTestMode) {
    2291             :     // In test mode, emulate the existing window being lowered and the new
    2292             :     // window being raised. This happens in a separate runnable to avoid
    2293             :     // touching multiple windows in the current runnable.
    2294           0 :     nsCOMPtr<nsPIDOMWindowOuter> active(mActiveWindow);
    2295           0 :     nsCOMPtr<nsPIDOMWindowOuter> window(aWindow);
    2296           0 :     RefPtr<nsFocusManager> self(this);
    2297           0 :     NS_DispatchToCurrentThread(NS_NewRunnableFunction(
    2298           0 :       "nsFocusManager::RaiseWindow", [self, active, window]() -> void {
    2299           0 :         if (active) {
    2300           0 :           self->WindowLowered(active);
    2301             :         }
    2302           0 :         self->WindowRaised(window);
    2303           0 :       }));
    2304             :     return;
    2305             :   }
    2306             : 
    2307             : #if defined(XP_WIN)
    2308             :   // Windows would rather we focus the child widget, otherwise, the toplevel
    2309             :   // widget will always end up being focused. Fortunately, focusing the child
    2310             :   // widget will also have the effect of raising the window this widget is in.
    2311             :   // But on other platforms, we can just focus the toplevel widget to raise
    2312             :   // the window.
    2313             :   nsCOMPtr<nsPIDOMWindowOuter> childWindow;
    2314             :   GetFocusedDescendant(aWindow, eIncludeAllDescendants,
    2315             :                        getter_AddRefs(childWindow));
    2316             :   if (!childWindow)
    2317             :     childWindow = aWindow;
    2318             : 
    2319             :   nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
    2320             :   if (!docShell)
    2321             :     return;
    2322             : 
    2323             :   nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
    2324             :   if (!presShell)
    2325             :     return;
    2326             : 
    2327             :   nsViewManager* vm = presShell->GetViewManager();
    2328             :   if (vm) {
    2329             :     nsCOMPtr<nsIWidget> widget;
    2330             :     vm->GetRootWidget(getter_AddRefs(widget));
    2331             :     if (widget)
    2332             :       widget->SetFocus(true);
    2333             :   }
    2334             : #else
    2335             :   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin =
    2336           0 :     do_QueryInterface(aWindow->GetDocShell());
    2337           0 :   if (treeOwnerAsWin) {
    2338           0 :     nsCOMPtr<nsIWidget> widget;
    2339           0 :     treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
    2340           0 :     if (widget)
    2341           0 :       widget->SetFocus(true);
    2342             :   }
    2343             : #endif
    2344             : }
    2345             : 
    2346             : void
    2347           0 : nsFocusManager::UpdateCaretForCaretBrowsingMode()
    2348             : {
    2349           0 :   UpdateCaret(false, true, mFocusedElement);
    2350           0 : }
    2351             : 
    2352             : void
    2353           1 : nsFocusManager::UpdateCaret(bool aMoveCaretToFocus,
    2354             :                             bool aUpdateVisibility,
    2355             :                             nsIContent* aContent)
    2356             : {
    2357           1 :   LOGFOCUS(("Update Caret: %d %d", aMoveCaretToFocus, aUpdateVisibility));
    2358             : 
    2359           0 :   if (!mFocusedWindow)
    2360           1 :     return;
    2361             : 
    2362             :   // this is called when a document is focused or when the caretbrowsing
    2363             :   // preference is changed
    2364           2 :   nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
    2365           2 :   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(focusedDocShell);
    2366           0 :   if (!dsti)
    2367           0 :     return;
    2368             : 
    2369           0 :   if (dsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
    2370             :     return;  // Never browse with caret in chrome
    2371             :   }
    2372             : 
    2373             :   bool browseWithCaret =
    2374           0 :     Preferences::GetBool("accessibility.browsewithcaret");
    2375             : 
    2376           0 :   nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
    2377           0 :   if (!presShell)
    2378           0 :     return;
    2379             : 
    2380             :   // If this is an editable document which isn't contentEditable, or a
    2381             :   // contentEditable document and the node to focus is contentEditable,
    2382             :   // return, so that we don't mess with caret visibility.
    2383           0 :   bool isEditable = false;
    2384           0 :   focusedDocShell->GetEditable(&isEditable);
    2385             : 
    2386           0 :   if (isEditable) {
    2387             :     nsCOMPtr<nsIHTMLDocument> doc =
    2388           0 :       do_QueryInterface(presShell->GetDocument());
    2389             : 
    2390             :     bool isContentEditableDoc =
    2391           0 :       doc && doc->GetEditingState() == nsIHTMLDocument::eContentEditable;
    2392             : 
    2393             :     bool isFocusEditable =
    2394           0 :       aContent && aContent->HasFlag(NODE_IS_EDITABLE);
    2395           0 :     if (!isContentEditableDoc || isFocusEditable)
    2396           0 :       return;
    2397             :   }
    2398             : 
    2399           0 :   if (!isEditable && aMoveCaretToFocus)
    2400           0 :     MoveCaretToFocus(presShell, aContent);
    2401             : 
    2402           0 :   if (!aUpdateVisibility)
    2403             :     return;
    2404             : 
    2405             :   // XXXndeakin this doesn't seem right. It should be checking for this only
    2406             :   // on the nearest ancestor frame which is a chrome frame. But this is
    2407             :   // what the existing code does, so just leave it for now.
    2408           0 :   if (!browseWithCaret) {
    2409             :     nsCOMPtr<Element> docElement =
    2410           0 :       mFocusedWindow->GetFrameElementInternal();
    2411           0 :     if (docElement)
    2412           0 :       browseWithCaret = docElement->AttrValueIs(kNameSpaceID_None,
    2413             :                                                 nsGkAtoms::showcaret,
    2414           0 :                                                 NS_LITERAL_STRING("true"),
    2415           0 :                                                 eCaseMatters);
    2416             :   }
    2417             : 
    2418           0 :   SetCaretVisible(presShell, browseWithCaret, aContent);
    2419             : }
    2420             : 
    2421             : void
    2422           0 : nsFocusManager::MoveCaretToFocus(nsIPresShell* aPresShell, nsIContent* aContent)
    2423             : {
    2424           0 :   nsCOMPtr<nsIDocument> doc = aPresShell->GetDocument();
    2425           0 :   if (doc) {
    2426           0 :     RefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
    2427             :     RefPtr<Selection> domSelection =
    2428           0 :       frameSelection->GetSelection(SelectionType::eNormal);
    2429           0 :     if (domSelection) {
    2430             :       // First clear the selection. This way, if there is no currently focused
    2431             :       // content, the selection will just be cleared.
    2432           0 :       domSelection->RemoveAllRanges(IgnoreErrors());
    2433           0 :       if (aContent) {
    2434           0 :         ErrorResult rv;
    2435           0 :         RefPtr<nsRange> newRange = doc->CreateRange(rv);
    2436           0 :         if (NS_WARN_IF(rv.Failed())) {
    2437           0 :           rv.SuppressException();
    2438           0 :           return;
    2439             :         }
    2440             : 
    2441             :         // Set the range to the start of the currently focused node
    2442             :         // Make sure it's collapsed
    2443           0 :         newRange->SelectNodeContents(*aContent, IgnoreErrors());
    2444             : 
    2445           0 :         if (!aContent->GetFirstChild() ||
    2446           0 :             aContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) {
    2447             :           // If current focus node is a leaf, set range to before the
    2448             :           // node by using the parent as a container.
    2449             :           // This prevents it from appearing as selected.
    2450           0 :           newRange->SetStartBefore(*aContent, IgnoreErrors());
    2451           0 :           newRange->SetEndBefore(*aContent, IgnoreErrors());
    2452             :         }
    2453           0 :         domSelection->AddRange(*newRange, IgnoreErrors());
    2454           0 :         domSelection->CollapseToStart(IgnoreErrors());
    2455             :       }
    2456             :     }
    2457             :   }
    2458             : }
    2459             : 
    2460             : nsresult
    2461           0 : nsFocusManager::SetCaretVisible(nsIPresShell* aPresShell,
    2462             :                                 bool aVisible,
    2463             :                                 nsIContent* aContent)
    2464             : {
    2465             :   // When browsing with caret, make sure caret is visible after new focus
    2466             :   // Return early if there is no caret. This can happen for the testcase
    2467             :   // for bug 308025 where a window is closed in a blur handler.
    2468           0 :   RefPtr<nsCaret> caret = aPresShell->GetCaret();
    2469           0 :   if (!caret)
    2470             :     return NS_OK;
    2471             : 
    2472           0 :   bool caretVisible = caret->IsVisible();
    2473           0 :   if (!aVisible && !caretVisible)
    2474             :     return NS_OK;
    2475             : 
    2476           0 :   RefPtr<nsFrameSelection> frameSelection;
    2477           0 :   if (aContent) {
    2478           0 :     NS_ASSERTION(aContent->GetComposedDoc() == aPresShell->GetDocument(),
    2479             :                  "Wrong document?");
    2480           0 :     nsIFrame *focusFrame = aContent->GetPrimaryFrame();
    2481           0 :     if (focusFrame)
    2482           0 :       frameSelection = focusFrame->GetFrameSelection();
    2483             :   }
    2484             : 
    2485           0 :   RefPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
    2486             : 
    2487           0 :   if (docFrameSelection && caret &&
    2488           0 :      (frameSelection == docFrameSelection || !aContent)) {
    2489             :     Selection* domSelection =
    2490           0 :       docFrameSelection->GetSelection(SelectionType::eNormal);
    2491           0 :     if (domSelection) {
    2492           0 :       nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
    2493           0 :       if (!selCon) {
    2494           0 :         return NS_ERROR_FAILURE;
    2495             :       }
    2496             :       // First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
    2497           0 :       selCon->SetCaretEnabled(false);
    2498             : 
    2499             :       // Caret must blink on non-editable elements
    2500           0 :       caret->SetIgnoreUserModify(true);
    2501             :       // Tell the caret which selection to use
    2502           0 :       caret->SetSelection(domSelection);
    2503             : 
    2504             :       // In content, we need to set the caret. The only special case is edit
    2505             :       // fields, which have a different frame selection from the document.
    2506             :       // They will take care of making the caret visible themselves.
    2507             : 
    2508           0 :       selCon->SetCaretReadOnly(false);
    2509           0 :       selCon->SetCaretEnabled(aVisible);
    2510             :     }
    2511             :   }
    2512             : 
    2513             :   return NS_OK;
    2514             : }
    2515             : 
    2516             : nsresult
    2517           0 : nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
    2518             :                                      nsIPresShell* aPresShell,
    2519             :                                      nsIContent **aStartContent,
    2520             :                                      nsIContent **aEndContent)
    2521             : {
    2522           0 :   *aStartContent = *aEndContent = nullptr;
    2523           0 :   nsPresContext* presContext = aPresShell->GetPresContext();
    2524           0 :   NS_ASSERTION(presContext, "mPresContent is null!!");
    2525             : 
    2526           0 :   RefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
    2527             : 
    2528           0 :   RefPtr<Selection> domSelection;
    2529           0 :   if (frameSelection) {
    2530           0 :     domSelection = frameSelection->GetSelection(SelectionType::eNormal);
    2531             :   }
    2532             : 
    2533           0 :   bool isCollapsed = false;
    2534           0 :   nsCOMPtr<nsIContent> startContent, endContent;
    2535           0 :   uint32_t startOffset = 0;
    2536           0 :   if (domSelection) {
    2537           0 :     isCollapsed = domSelection->IsCollapsed();
    2538           0 :     RefPtr<nsRange> domRange = domSelection->GetRangeAt(0);
    2539           0 :     if (domRange) {
    2540           0 :       nsCOMPtr<nsINode> startNode = domRange->GetStartContainer();
    2541           0 :       nsCOMPtr<nsINode> endNode = domRange->GetEndContainer();
    2542           0 :       startOffset = domRange->StartOffset();
    2543             : 
    2544           0 :       nsIContent *childContent = nullptr;
    2545             : 
    2546           0 :       startContent = do_QueryInterface(startNode);
    2547           0 :       if (startContent && startContent->IsElement()) {
    2548           0 :         childContent = startContent->GetChildAt_Deprecated(startOffset);
    2549           0 :         if (childContent) {
    2550           0 :           startContent = childContent;
    2551             :         }
    2552             :       }
    2553             : 
    2554           0 :       endContent = do_QueryInterface(endNode);
    2555           0 :       if (endContent && endContent->IsElement()) {
    2556           0 :         uint32_t endOffset = domRange->EndOffset();
    2557           0 :         childContent = endContent->GetChildAt_Deprecated(endOffset);
    2558           0 :         if (childContent) {
    2559           0 :           endContent = childContent;
    2560             :         }
    2561             :       }
    2562             :     }
    2563             :   }
    2564             :   else {
    2565             :     return NS_ERROR_INVALID_ARG;
    2566             :   }
    2567             : 
    2568           0 :   nsIFrame *startFrame = nullptr;
    2569           0 :   if (startContent) {
    2570           0 :     startFrame = startContent->GetPrimaryFrame();
    2571           0 :     if (isCollapsed) {
    2572             :       // Next check to see if our caret is at the very end of a node
    2573             :       // If so, the caret is actually sitting in front of the next
    2574             :       // logical frame's primary node - so for this case we need to
    2575             :       // change caretContent to that node.
    2576             : 
    2577           0 :       if (startContent->NodeType() == nsINode::TEXT_NODE) {
    2578           0 :         nsAutoString nodeValue;
    2579           0 :         startContent->GetAsText()->AppendTextTo(nodeValue);
    2580             : 
    2581             :         bool isFormControl =
    2582           0 :           startContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL);
    2583             : 
    2584           0 :         if (nodeValue.Length() == startOffset && !isFormControl &&
    2585           0 :             startContent != aDocument->GetRootElement()) {
    2586             :           // Yes, indeed we were at the end of the last node
    2587           0 :           nsCOMPtr<nsIFrameEnumerator> frameTraversal;
    2588           0 :           nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
    2589             :                                              presContext, startFrame,
    2590             :                                              eLeaf,
    2591             :                                              false, // aVisual
    2592             :                                              false, // aLockInScrollView
    2593             :                                              true,  // aFollowOOFs
    2594             :                                              false, // aSkipPopupChecks
    2595             :                                              false  // aSkipShadow
    2596           0 :                                              );
    2597           0 :           NS_ENSURE_SUCCESS(rv, rv);
    2598             : 
    2599           0 :           nsIFrame *newCaretFrame = nullptr;
    2600           0 :           nsCOMPtr<nsIContent> newCaretContent = startContent;
    2601           0 :           bool endOfSelectionInStartNode(startContent == endContent);
    2602           0 :           do {
    2603             :             // Continue getting the next frame until the primary content for the frame
    2604             :             // we are on changes - we don't want to be stuck in the same place
    2605           0 :             frameTraversal->Next();
    2606           0 :             newCaretFrame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
    2607           0 :             if (nullptr == newCaretFrame)
    2608             :               break;
    2609           0 :             newCaretContent = newCaretFrame->GetContent();
    2610           0 :           } while (!newCaretContent || newCaretContent == startContent);
    2611             : 
    2612           0 :           if (newCaretFrame && newCaretContent) {
    2613             :             // If the caret is exactly at the same position of the new frame,
    2614             :             // then we can use the newCaretFrame and newCaretContent for our position
    2615           0 :             nsRect caretRect;
    2616           0 :             nsIFrame *frame = nsCaret::GetGeometry(domSelection, &caretRect);
    2617           0 :             if (frame) {
    2618           0 :               nsPoint caretWidgetOffset;
    2619           0 :               nsIWidget *widget = frame->GetNearestWidget(caretWidgetOffset);
    2620           0 :               caretRect.MoveBy(caretWidgetOffset);
    2621           0 :               nsPoint newCaretOffset;
    2622           0 :               nsIWidget *newCaretWidget = newCaretFrame->GetNearestWidget(newCaretOffset);
    2623           0 :               if (widget == newCaretWidget && caretRect.y == newCaretOffset.y &&
    2624           0 :                   caretRect.x == newCaretOffset.x) {
    2625             :                 // The caret is at the start of the new element.
    2626           0 :                 startFrame = newCaretFrame;
    2627           0 :                 startContent = newCaretContent;
    2628           0 :                 if (endOfSelectionInStartNode) {
    2629           0 :                   endContent = newCaretContent; // Ensure end of selection is not before start
    2630             :                 }
    2631             :               }
    2632             :             }
    2633             :           }
    2634             :         }
    2635             :       }
    2636             :     }
    2637             :   }
    2638             : 
    2639           0 :   *aStartContent = startContent;
    2640           0 :   *aEndContent = endContent;
    2641           0 :   NS_IF_ADDREF(*aStartContent);
    2642           0 :   NS_IF_ADDREF(*aEndContent);
    2643             : 
    2644           0 :   return NS_OK;
    2645             : }
    2646             : 
    2647             : nsresult
    2648           0 : nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindowOuter* aWindow,
    2649             :                                             nsIContent* aStartContent,
    2650             :                                             int32_t aType, bool aNoParentTraversal,
    2651             :                                             nsIContent** aNextContent)
    2652             : {
    2653           0 :   *aNextContent = nullptr;
    2654             : 
    2655             :   // True if we are navigating by document (F6/Shift+F6) or false if we are
    2656             :   // navigating by element (Tab/Shift+Tab).
    2657           0 :   bool forDocumentNavigation = false;
    2658             : 
    2659             :   // This is used for document navigation only. It will be set to true if we
    2660             :   // start navigating from a starting point. If this starting point is near the
    2661             :   // end of the document (for example, an element on a statusbar), and there
    2662             :   // are no child documents or panels before the end of the document, then we
    2663             :   // will need to ensure that we don't consider the root chrome window when we
    2664             :   // loop around and instead find the next child document/panel, as focus is
    2665             :   // already in that window. This flag will be cleared once we navigate into
    2666             :   // another document.
    2667           0 :   bool mayFocusRoot = (aStartContent != nullptr);
    2668             : 
    2669           0 :   nsCOMPtr<nsIContent> startContent = aStartContent;
    2670           0 :   if (!startContent && aType != MOVEFOCUS_CARET) {
    2671           0 :     if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC) {
    2672             :       // When moving between documents, make sure to get the right
    2673             :       // starting content in a descendant.
    2674           0 :       nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
    2675           0 :       startContent = GetFocusedDescendant(aWindow, eIncludeAllDescendants,
    2676           0 :                                           getter_AddRefs(focusedWindow));
    2677             :     }
    2678           0 :     else if (aType != MOVEFOCUS_LASTDOC) {
    2679             :       // Otherwise, start at the focused node. If MOVEFOCUS_LASTDOC is used,
    2680             :       // then we are document-navigating backwards from chrome to the content
    2681             :       // process, and we don't want to use this so that we start from the end
    2682             :       // of the document.
    2683           0 :       startContent = aWindow->GetFocusedElement();
    2684             :     }
    2685             :   }
    2686             : 
    2687           0 :   nsCOMPtr<nsIDocument> doc;
    2688           0 :   if (startContent)
    2689           0 :     doc = startContent->GetComposedDoc();
    2690             :   else
    2691           0 :     doc = aWindow->GetExtantDoc();
    2692           0 :   if (!doc)
    2693             :     return NS_OK;
    2694             : 
    2695             :   LookAndFeel::GetInt(LookAndFeel::eIntID_TabFocusModel,
    2696           0 :                       &nsIContent::sTabFocusModel);
    2697             : 
    2698             :   // These types are for document navigation using F6.
    2699           0 :   if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC ||
    2700           0 :       aType == MOVEFOCUS_FIRSTDOC || aType == MOVEFOCUS_LASTDOC) {
    2701           0 :     forDocumentNavigation = true;
    2702             :   }
    2703             : 
    2704             :   // If moving to the root or first document, find the root element and return.
    2705           0 :   if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_FIRSTDOC) {
    2706           0 :     NS_IF_ADDREF(*aNextContent = GetRootForFocus(aWindow, doc, false, false));
    2707           0 :     if (!*aNextContent && aType == MOVEFOCUS_FIRSTDOC) {
    2708             :       // When looking for the first document, if the root wasn't focusable,
    2709             :       // find the next focusable document.
    2710             :       aType = MOVEFOCUS_FORWARDDOC;
    2711             :     } else {
    2712             :       return NS_OK;
    2713             :     }
    2714             :   }
    2715             : 
    2716           0 :   Element* rootContent = doc->GetRootElement();
    2717           0 :   NS_ENSURE_TRUE(rootContent, NS_OK);
    2718             : 
    2719           0 :   nsIPresShell* presShell = doc->GetShell();
    2720           0 :   NS_ENSURE_TRUE(presShell, NS_OK);
    2721             : 
    2722           0 :   if (aType == MOVEFOCUS_FIRST) {
    2723           0 :     if (!aStartContent)
    2724           0 :       startContent = rootContent;
    2725           0 :     return GetNextTabbableContent(presShell, startContent,
    2726             :                                   nullptr, startContent,
    2727           0 :                                   true, 1, false, false, aNextContent);
    2728             :   }
    2729           0 :   if (aType == MOVEFOCUS_LAST) {
    2730           0 :     if (!aStartContent)
    2731           0 :       startContent = rootContent;
    2732           0 :     return GetNextTabbableContent(presShell, startContent,
    2733             :                                   nullptr, startContent,
    2734           0 :                                   false, 0, false, false, aNextContent);
    2735             :   }
    2736             : 
    2737           0 :   bool forward = (aType == MOVEFOCUS_FORWARD ||
    2738           0 :                   aType == MOVEFOCUS_FORWARDDOC ||
    2739           0 :                   aType == MOVEFOCUS_CARET);
    2740           0 :   bool doNavigation = true;
    2741           0 :   bool ignoreTabIndex = false;
    2742             :   // when a popup is open, we want to ensure that tab navigation occurs only
    2743             :   // within the most recently opened panel. If a popup is open, its frame will
    2744             :   // be stored in popupFrame.
    2745           0 :   nsIFrame* popupFrame = nullptr;
    2746             : 
    2747           0 :   int32_t tabIndex = forward ? 1 : 0;
    2748           0 :   if (startContent) {
    2749           0 :     nsIFrame* frame = startContent->GetPrimaryFrame();
    2750           0 :     if (startContent->IsHTMLElement(nsGkAtoms::area))
    2751           0 :       startContent->IsFocusable(&tabIndex);
    2752           0 :     else if (frame)
    2753           0 :       frame->IsFocusable(&tabIndex, 0);
    2754             :     else
    2755           0 :       startContent->IsFocusable(&tabIndex);
    2756             : 
    2757             :     // if the current element isn't tabbable, ignore the tabindex and just
    2758             :     // look for the next element. The root content won't have a tabindex
    2759             :     // so just treat this as the beginning of the tab order.
    2760           0 :     if (tabIndex < 0) {
    2761           0 :       tabIndex = 1;
    2762           0 :       if (startContent != rootContent)
    2763           0 :         ignoreTabIndex = true;
    2764             :     }
    2765             : 
    2766             :     // check if the focus is currently inside a popup. Elements such as the
    2767             :     // autocomplete widget use the noautofocus attribute to allow the focus to
    2768             :     // remain outside the popup when it is opened.
    2769           0 :     if (frame) {
    2770             :       popupFrame =
    2771           0 :         nsLayoutUtils::GetClosestFrameOfType(frame, LayoutFrameType::MenuPopup);
    2772             :     }
    2773             : 
    2774           0 :     if (popupFrame && !forDocumentNavigation) {
    2775             :       // Don't navigate outside of a popup, so pretend that the
    2776             :       // root content is the popup itself
    2777           0 :       rootContent = popupFrame->GetContent()->AsElement();
    2778           0 :       NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
    2779             :     }
    2780           0 :     else if (!forward) {
    2781             :       // If focus moves backward and when current focused node is root
    2782             :       // content or <body> element which is editable by contenteditable
    2783             :       // attribute, focus should move to its parent document.
    2784           0 :       if (startContent == rootContent) {
    2785             :         doNavigation = false;
    2786             :       } else {
    2787           0 :         nsIDocument* doc = startContent->GetComposedDoc();
    2788           0 :         if (startContent ==
    2789             :               nsLayoutUtils::GetEditableRootContentByContentEditable(doc)) {
    2790           0 :           doNavigation = false;
    2791             :         }
    2792             :       }
    2793             :     }
    2794             :   }
    2795             :   else {
    2796             : #ifdef MOZ_XUL
    2797           0 :     if (aType != MOVEFOCUS_CARET) {
    2798             :       // if there is no focus, yet a panel is open, focus the first item in
    2799             :       // the panel
    2800           0 :       nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2801           0 :       if (pm)
    2802           0 :         popupFrame = pm->GetTopPopup(ePopupTypePanel);
    2803             :     }
    2804             : #endif
    2805           0 :     if (popupFrame) {
    2806             :       // When there is a popup open, and no starting content, start the search
    2807             :       // at the topmost popup.
    2808           0 :       startContent = popupFrame->GetContent();
    2809           0 :       NS_ASSERTION(startContent, "Popup frame doesn't have a content node");
    2810             :       // Unless we are searching for documents, set the root content to the
    2811             :       // popup as well, so that we don't tab-navigate outside the popup.
    2812             :       // When navigating by documents, we start at the popup but can navigate
    2813             :       // outside of it to look for other panels and documents.
    2814           0 :       if (!forDocumentNavigation) {
    2815           0 :         rootContent = startContent->AsElement();
    2816             :       }
    2817             : 
    2818           0 :       doc = startContent ? startContent->GetComposedDoc() : nullptr;
    2819             :     }
    2820             :     else {
    2821             :       // Otherwise, for content shells, start from the location of the caret.
    2822           0 :       nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
    2823           0 :       if (docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
    2824           0 :         nsCOMPtr<nsIContent> endSelectionContent;
    2825           0 :         GetSelectionLocation(doc, presShell,
    2826           0 :                              getter_AddRefs(startContent),
    2827           0 :                              getter_AddRefs(endSelectionContent));
    2828             :         // If the selection is on the rootContent, then there is no selection
    2829           0 :         if (startContent == rootContent) {
    2830           0 :           startContent = nullptr;
    2831             :         }
    2832             : 
    2833           0 :         if (aType == MOVEFOCUS_CARET) {
    2834             :           // GetFocusInSelection finds a focusable link near the caret.
    2835             :           // If there is no start content though, don't do this to avoid
    2836             :           // focusing something unexpected.
    2837           0 :           if (startContent) {
    2838           0 :             GetFocusInSelection(aWindow, startContent,
    2839           0 :                                 endSelectionContent, aNextContent);
    2840             :           }
    2841           0 :           return NS_OK;
    2842             :         }
    2843             : 
    2844           0 :         if (startContent) {
    2845             :           // when starting from a selection, we always want to find the next or
    2846             :           // previous element in the document. So the tabindex on elements
    2847             :           // should be ignored.
    2848           0 :           ignoreTabIndex = true;
    2849             :         }
    2850             :       }
    2851             : 
    2852           0 :       if (!startContent) {
    2853             :         // otherwise, just use the root content as the starting point
    2854           0 :         startContent = rootContent;
    2855           0 :         NS_ENSURE_TRUE(startContent, NS_OK);
    2856             :       }
    2857             :     }
    2858             :   }
    2859             : 
    2860             :   // Check if the starting content is the same as the content assigned to the
    2861             :   // retargetdocumentfocus attribute. Is so, we don't want to start searching
    2862             :   // from there but instead from the beginning of the document. Otherwise, the
    2863             :   // content that appears before the retargetdocumentfocus element will never
    2864             :   // get checked as it will be skipped when the focus is retargetted to it.
    2865           0 :   if (forDocumentNavigation && doc->IsXULDocument()) {
    2866           0 :     nsAutoString retarget;
    2867             : 
    2868           0 :     if (rootContent->GetAttr(kNameSpaceID_None,
    2869             :                              nsGkAtoms::retargetdocumentfocus, retarget)) {
    2870           0 :       nsIContent* retargetElement = doc->GetElementById(retarget);
    2871             :       // The common case here is the urlbar where focus is on the anonymous
    2872             :       // input inside the textbox, but the retargetdocumentfocus attribute
    2873             :       // refers to the textbox. The Contains check will return false and the
    2874             :       // ContentIsDescendantOf check will return true in this case.
    2875           0 :       if (retargetElement && (retargetElement == startContent ||
    2876           0 :                               (!retargetElement->Contains(startContent) &&
    2877           0 :                               nsContentUtils::ContentIsDescendantOf(startContent, retargetElement)))) {
    2878           0 :         startContent = rootContent;
    2879             :       }
    2880             :     }
    2881             :   }
    2882             : 
    2883           0 :   NS_ASSERTION(startContent, "starting content not set");
    2884             : 
    2885             :   // keep a reference to the starting content. If we find that again, it means
    2886             :   // we've iterated around completely and we don't want to adjust the focus.
    2887             :   // The skipOriginalContentCheck will be set to true only for the first time
    2888             :   // GetNextTabbableContent is called. This ensures that we don't break out
    2889             :   // when nothing is focused to start with. Specifically,
    2890             :   // GetNextTabbableContent first checks the root content -- which happens to
    2891             :   // be the same as the start content -- when nothing is focused and tabbing
    2892             :   // forward. Without skipOriginalContentCheck set to true, we'd end up
    2893             :   // returning right away and focusing nothing. Luckily, GetNextTabbableContent
    2894             :   // will never wrap around on its own, and can only return the original
    2895             :   // content when it is called a second time or later.
    2896           0 :   bool skipOriginalContentCheck = true;
    2897           0 :   nsIContent* originalStartContent = startContent;
    2898             : 
    2899           0 :   LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent.get());
    2900           0 :   LOGFOCUSNAVIGATION(("  Forward: %d Tabindex: %d Ignore: %d DocNav: %d",
    2901             :                       forward, tabIndex, ignoreTabIndex, forDocumentNavigation));
    2902             : 
    2903           0 :   while (doc) {
    2904           0 :     if (doNavigation) {
    2905           0 :       nsCOMPtr<nsIContent> nextFocus;
    2906           0 :       nsresult rv = GetNextTabbableContent(presShell, rootContent,
    2907             :                                            skipOriginalContentCheck ? nullptr : originalStartContent,
    2908             :                                            startContent, forward,
    2909             :                                            tabIndex, ignoreTabIndex,
    2910             :                                            forDocumentNavigation,
    2911           0 :                                            getter_AddRefs(nextFocus));
    2912           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2913           0 :       if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
    2914             :         // Navigation was redirected to a child process, so just return.
    2915             :         return NS_OK;
    2916             :       }
    2917             : 
    2918             :       // found a content node to focus.
    2919           0 :       if (nextFocus) {
    2920           0 :         LOGCONTENTNAVIGATION("Next Content: %s", nextFocus.get());
    2921             : 
    2922             :         // as long as the found node was not the same as the starting node,
    2923             :         // set it as the return value. For document navigation, we can return
    2924             :         // the same element in case there is only one content node that could
    2925             :         // be returned, for example, in a child process document.
    2926           0 :         if (nextFocus != originalStartContent || forDocumentNavigation) {
    2927           0 :           nextFocus.forget(aNextContent);
    2928             :         }
    2929             :         return NS_OK;
    2930             :       }
    2931             : 
    2932           0 :       if (popupFrame && !forDocumentNavigation) {
    2933             :         // in a popup, so start again from the beginning of the popup. However,
    2934             :         // if we already started at the beginning, then there isn't anything to
    2935             :         // focus, so just return
    2936           0 :         if (startContent != rootContent) {
    2937           0 :           startContent = rootContent;
    2938           0 :           tabIndex = forward ? 1 : 0;
    2939           0 :           continue;
    2940             :         }
    2941             :         return NS_OK;
    2942             :       }
    2943             :     }
    2944             : 
    2945           0 :     doNavigation = true;
    2946           0 :     skipOriginalContentCheck = forDocumentNavigation;
    2947           0 :     ignoreTabIndex = false;
    2948             : 
    2949           0 :     if (aNoParentTraversal) {
    2950           0 :       if (startContent == rootContent)
    2951             :         return NS_OK;
    2952             : 
    2953           0 :       startContent = rootContent;
    2954           0 :       tabIndex = forward ? 1 : 0;
    2955           0 :       continue;
    2956             :     }
    2957             : 
    2958             :     // Reached the beginning or end of the document. Next, navigate up to the
    2959             :     // parent document and try again.
    2960           0 :     nsCOMPtr<nsPIDOMWindowOuter> piWindow = doc->GetWindow();
    2961           0 :     NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE);
    2962             : 
    2963           0 :     nsCOMPtr<nsIDocShell> docShell = piWindow->GetDocShell();
    2964           0 :     NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
    2965             : 
    2966             :     // Get the frame element this window is inside and, from that, get the
    2967             :     // parent document and presshell. If there is no enclosing frame element,
    2968             :     // then this is a top-level, embedded or remote window.
    2969           0 :     startContent = piWindow->GetFrameElementInternal();
    2970           0 :     if (startContent) {
    2971           0 :       doc = startContent->GetComposedDoc();
    2972           0 :       NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
    2973             : 
    2974           0 :       rootContent = doc->GetRootElement();
    2975           0 :       presShell = doc->GetShell();
    2976             : 
    2977             :       // We can focus the root element now that we have moved to another document.
    2978           0 :       mayFocusRoot = true;
    2979             : 
    2980           0 :       nsIFrame* frame = startContent->GetPrimaryFrame();
    2981           0 :       if (!frame) {
    2982             :         return NS_OK;
    2983             :       }
    2984             : 
    2985           0 :       frame->IsFocusable(&tabIndex, 0);
    2986           0 :       if (tabIndex < 0) {
    2987           0 :         tabIndex = 1;
    2988           0 :         ignoreTabIndex = true;
    2989             :       }
    2990             : 
    2991             :       // if the frame is inside a popup, make sure to scan only within the
    2992             :       // popup. This handles the situation of tabbing amongst elements
    2993             :       // inside an iframe which is itself inside a popup. Otherwise,
    2994             :       // navigation would move outside the popup when tabbing outside the
    2995             :       // iframe.
    2996           0 :       if (!forDocumentNavigation) {
    2997             :         popupFrame = nsLayoutUtils::GetClosestFrameOfType(
    2998           0 :           frame, LayoutFrameType::MenuPopup);
    2999           0 :         if (popupFrame) {
    3000           0 :           rootContent = popupFrame->GetContent()->AsElement();
    3001           0 :           NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
    3002             :         }
    3003             :       }
    3004             :     }
    3005             :     else {
    3006             :       // There is no parent, so call the tree owner. This will tell the
    3007             :       // embedder or parent process that it should take the focus.
    3008             :       bool tookFocus;
    3009           0 :       docShell->TabToTreeOwner(forward, forDocumentNavigation, &tookFocus);
    3010             :       // If the tree owner took the focus, blur the current element.
    3011           0 :       if (tookFocus) {
    3012           0 :         nsCOMPtr<nsPIDOMWindowOuter> window = docShell->GetWindow();
    3013           0 :         if (window->GetFocusedElement() == mFocusedElement)
    3014           0 :           Blur(mFocusedWindow, nullptr, true, true);
    3015             :         else
    3016           0 :           window->SetFocusedElement(nullptr);
    3017             :         return NS_OK;
    3018             :       }
    3019             : 
    3020             :       // If we have reached the end of the top-level document, focus the
    3021             :       // first element in the top-level document. This should always happen
    3022             :       // when navigating by document forwards but when navigating backwards,
    3023             :       // only do this if we started in another document or within a popup frame.
    3024             :       // If the focus started in this window outside a popup however, we should
    3025             :       // continue by looping around to the end again.
    3026           0 :       if (forDocumentNavigation && (forward || mayFocusRoot || popupFrame)) {
    3027             :         // HTML content documents can have their root element focused (a focus
    3028             :         // ring appears around the entire content area frame). This root
    3029             :         // appears in the tab order before all of the elements in the document.
    3030             :         // Chrome documents however cannot be focused directly, so instead we
    3031             :         // focus the first focusable element within the window.
    3032             :         // For example, the urlbar.
    3033           0 :         Element* root = GetRootForFocus(piWindow, doc, true, true);
    3034           0 :         return FocusFirst(root, aNextContent);
    3035             :       }
    3036             : 
    3037             :       // Once we have hit the top-level and have iterated to the end again, we
    3038             :       // just want to break out next time we hit this spot to prevent infinite
    3039             :       // iteration.
    3040           0 :       mayFocusRoot = true;
    3041             : 
    3042             :       // reset the tab index and start again from the beginning or end
    3043           0 :       startContent = rootContent;
    3044           0 :       tabIndex = forward ? 1 : 0;
    3045             :     }
    3046             : 
    3047             :     // wrapped all the way around and didn't find anything to move the focus
    3048             :     // to, so just break out
    3049           0 :     if (startContent == originalStartContent)
    3050             :       break;
    3051             :   }
    3052             : 
    3053             :   return NS_OK;
    3054             : }
    3055             : 
    3056             : // Helper class to iterate contents in scope by traversing flattened tree
    3057             : // in tree order
    3058             : class MOZ_STACK_CLASS ScopedContentTraversal
    3059             : {
    3060             : public:
    3061           0 :   ScopedContentTraversal(nsIContent* aStartContent, nsIContent* aOwner)
    3062           0 :     : mCurrent(aStartContent)
    3063           0 :     , mOwner(aOwner)
    3064             :   {
    3065           0 :     MOZ_ASSERT(aStartContent);
    3066           0 :   }
    3067             : 
    3068             :   void Next();
    3069             :   void Prev();
    3070             : 
    3071             :   void Reset()
    3072             :   {
    3073           0 :     SetCurrent(mOwner);
    3074             :   }
    3075             : 
    3076             :   nsIContent* GetCurrent()
    3077             :   {
    3078             :     return mCurrent;
    3079             :   }
    3080             : 
    3081             : private:
    3082             :   void SetCurrent(nsIContent* aContent)
    3083             :   {
    3084           0 :     mCurrent = aContent;
    3085             :   }
    3086             : 
    3087             :   nsIContent* mCurrent;
    3088             :   nsIContent* mOwner;
    3089             : };
    3090             : 
    3091             : void
    3092           0 : ScopedContentTraversal::Next()
    3093             : {
    3094           0 :   MOZ_ASSERT(mCurrent);
    3095             : 
    3096             :   // Get mCurrent's first child if it's in the same scope.
    3097           0 :   if (!(mCurrent->GetShadowRoot() || mCurrent->IsHTMLElement(nsGkAtoms::slot)) ||
    3098           0 :       mCurrent == mOwner) {
    3099           0 :     FlattenedChildIterator iter(mCurrent);
    3100           0 :     nsIContent* child = iter.GetNextChild();
    3101           0 :     if (child) {
    3102           0 :       SetCurrent(child);
    3103           0 :       return;
    3104             :     }
    3105             :   }
    3106             : 
    3107             :   // If mOwner has no children, END traversal
    3108           0 :   if (mCurrent == mOwner) {
    3109           0 :     SetCurrent(nullptr);
    3110             :     return;
    3111             :   }
    3112             : 
    3113             :   nsIContent* current = mCurrent;
    3114             :   while (1) {
    3115             :     // Create parent's iterator and move to current
    3116           0 :     nsIContent* parent = current->GetFlattenedTreeParent();
    3117           0 :     FlattenedChildIterator parentIter(parent);
    3118           0 :     parentIter.Seek(current);
    3119             : 
    3120             :     // Get next sibling of current
    3121           0 :     nsIContent* next = parentIter.GetNextChild();
    3122           0 :     if (next) {
    3123           0 :       SetCurrent(next);
    3124           0 :       return;
    3125             :     }
    3126             : 
    3127             :     // If no next sibling and parent is mOwner, END traversal
    3128           0 :     if (parent == mOwner) {
    3129           0 :       SetCurrent(nullptr);
    3130             :       return;
    3131             :     }
    3132             : 
    3133           0 :     current = parent;
    3134             :   }
    3135             : }
    3136             : 
    3137             : void
    3138           0 : ScopedContentTraversal::Prev()
    3139             : {
    3140           0 :   MOZ_ASSERT(mCurrent);
    3141             : 
    3142             :   nsIContent* parent;
    3143             :   nsIContent* last;
    3144           0 :   if (mCurrent == mOwner) {
    3145             :     // Get last child of mOwner
    3146           0 :     FlattenedChildIterator ownerIter(mOwner, false /* aStartAtBeginning */);
    3147           0 :     last = ownerIter.GetPreviousChild();
    3148             : 
    3149           0 :     parent = last;
    3150             :   } else {
    3151             :     // Create parent's iterator and move to mCurrent
    3152           0 :     parent = mCurrent->GetFlattenedTreeParent();
    3153           0 :     FlattenedChildIterator parentIter(parent);
    3154           0 :     parentIter.Seek(mCurrent);
    3155             : 
    3156             :     // Get previous sibling
    3157           0 :     last = parentIter.GetPreviousChild();
    3158             :   }
    3159             : 
    3160           0 :   while (last) {
    3161           0 :     parent = last;
    3162           0 :     if (parent->GetShadowRoot() ||
    3163           0 :         parent->IsHTMLElement(nsGkAtoms::slot)) {
    3164             :       // Skip contents in other scopes
    3165             :       break;
    3166             :     }
    3167             : 
    3168             :     // Find last child
    3169           0 :     FlattenedChildIterator iter(parent, false /* aStartAtBeginning */);
    3170           0 :     last = iter.GetPreviousChild();
    3171             :   }
    3172             : 
    3173             :   // If parent is mOwner and no previous sibling remains, END traversal
    3174           0 :   SetCurrent(parent == mOwner ? nullptr : parent);
    3175           0 : }
    3176             : 
    3177             : nsIContent*
    3178           0 : nsFocusManager::FindOwner(nsIContent* aContent)
    3179             : {
    3180           0 :   nsIContent* currentContent = aContent;
    3181           0 :   while (currentContent) {
    3182           0 :     nsIContent* parent = currentContent->GetFlattenedTreeParent();
    3183           0 :     if (!parent) {
    3184             :       // Document root
    3185           0 :       nsIDocument* doc = currentContent->GetUncomposedDoc();
    3186           0 :       if (doc && doc->GetRootElement() == currentContent) {
    3187             :         return currentContent;
    3188             :       }
    3189             : 
    3190             :       break;
    3191             :     }
    3192             : 
    3193             :     // Shadow host / Slot
    3194           0 :     if (IsHostOrSlot(parent)) {
    3195             :       return parent;
    3196             :     }
    3197             : 
    3198             :     currentContent = parent;
    3199             :   }
    3200             : 
    3201             :   return nullptr;
    3202             : }
    3203             : 
    3204             : bool
    3205           0 : nsFocusManager::IsHostOrSlot(nsIContent* aContent)
    3206             : {
    3207           0 :   return aContent->GetShadowRoot() || // shadow host
    3208           0 :          aContent->IsHTMLElement(nsGkAtoms::slot); // slot
    3209             : }
    3210             : 
    3211             : int32_t
    3212           0 : nsFocusManager::HostOrSlotTabIndexValue(nsIContent* aContent)
    3213             : {
    3214           0 :   MOZ_ASSERT(IsHostOrSlot(aContent));
    3215             : 
    3216             :   const nsAttrValue* attrVal =
    3217           0 :     aContent->AsElement()->GetParsedAttr(nsGkAtoms::tabindex);
    3218           0 :   if (!attrVal) {
    3219             :     return 0;
    3220             :   }
    3221             : 
    3222           0 :   if (attrVal->Type() == nsAttrValue::eInteger) {
    3223           0 :     return attrVal->GetIntegerValue();
    3224             :   }
    3225             : 
    3226             :   return -1;
    3227             : }
    3228             : 
    3229             : nsIContent*
    3230           0 : nsFocusManager::GetNextTabbableContentInScope(nsIContent* aOwner,
    3231             :                                               nsIContent* aStartContent,
    3232             :                                               nsIContent* aOriginalStartContent,
    3233             :                                               bool aForward,
    3234             :                                               int32_t aCurrentTabIndex,
    3235             :                                               bool aIgnoreTabIndex,
    3236             :                                               bool aForDocumentNavigation,
    3237             :                                               bool aSkipOwner)
    3238             : {
    3239             :   // Return shadow host at first for forward navigation if its tabindex
    3240             :   // is non-negative
    3241           0 :   bool skipOwner = aSkipOwner || !aOwner->GetShadowRoot();
    3242           0 :   if (!skipOwner && (aForward && aOwner == aStartContent)) {
    3243           0 :     int32_t tabIndex = 0;
    3244           0 :     aOwner->IsFocusable(&tabIndex);
    3245           0 :     if (tabIndex >= 0) {
    3246           0 :       return aOwner;
    3247             :     }
    3248             :   }
    3249             : 
    3250             :   //
    3251             :   // Iterate contents in scope
    3252             :   //
    3253           0 :   ScopedContentTraversal contentTraversal(aStartContent, aOwner);
    3254           0 :   nsCOMPtr<nsIContent> iterContent;
    3255           0 :   nsIContent* firstNonChromeOnly = aStartContent->IsInNativeAnonymousSubtree() ?
    3256           0 :     aStartContent->FindFirstNonChromeOnlyAccessContent() : nullptr;
    3257             :   while (1) {
    3258             :     // Iterate tab index to find corresponding contents in scope
    3259             : 
    3260             :     while (1) {
    3261             :       // Iterate remaining contents in scope to find next content to focus
    3262             : 
    3263             :       // Get next content
    3264           0 :       aForward ? contentTraversal.Next() : contentTraversal.Prev();
    3265           0 :       iterContent = contentTraversal.GetCurrent();
    3266             : 
    3267           0 :       if (firstNonChromeOnly && firstNonChromeOnly == iterContent) {
    3268             :         // We just broke out from the native anonynous content, so move
    3269             :         // to the previous/next node of the native anonymous owner.
    3270           0 :         if (aForward) {
    3271           0 :           contentTraversal.Next();
    3272             :         } else {
    3273           0 :           contentTraversal.Prev();
    3274             :         }
    3275           0 :         iterContent = contentTraversal.GetCurrent();
    3276             :       }
    3277           0 :       if (!iterContent) {
    3278             :         // Reach the end
    3279             :         break;
    3280             :       }
    3281             : 
    3282             :       // Get the tab index of the next element. For NAC we rely on frames.
    3283             :       //XXXsmaug we should probably use frames also for Shadow DOM and special
    3284             :       //         case only display:contents elements.
    3285           0 :       int32_t tabIndex = 0;
    3286           0 :       if (iterContent->IsInNativeAnonymousSubtree() &&
    3287           0 :           iterContent->GetPrimaryFrame()) {
    3288           0 :         iterContent->GetPrimaryFrame()->IsFocusable(&tabIndex);
    3289             :       } else {
    3290           0 :         iterContent->IsFocusable(&tabIndex);
    3291             :       }
    3292           0 :       if (tabIndex < 0 || !(aIgnoreTabIndex || tabIndex == aCurrentTabIndex)) {
    3293             :         // If the element has native anonymous content, we may need to
    3294             :         // focus some NAC element, even if the element itself isn't focusable.
    3295             :         // This happens for example with <input type="date">.
    3296             :         // So, try to find NAC and then traverse the frame tree to find elements
    3297             :         // to focus.
    3298           0 :         nsIFrame* possibleAnonOwnerFrame = iterContent->GetPrimaryFrame();
    3299             :         nsIAnonymousContentCreator* anonCreator =
    3300           0 :           do_QueryFrame(possibleAnonOwnerFrame);
    3301           0 :         if (anonCreator && !iterContent->IsInNativeAnonymousSubtree()) {
    3302           0 :           nsIFrame* frame = nullptr;
    3303             :           // Find the first or last frame in tree order so that
    3304             :           // we can scope frame traversing to NAC.
    3305           0 :           if (aForward) {
    3306           0 :             frame = possibleAnonOwnerFrame->PrincipalChildList().FirstChild();
    3307             :           } else {
    3308           0 :             frame = possibleAnonOwnerFrame->PrincipalChildList().LastChild();
    3309           0 :             nsIFrame* last = frame;
    3310           0 :             while (last) {
    3311           0 :               frame = last;
    3312           0 :               last = frame->PrincipalChildList().LastChild();
    3313             :             }
    3314             :           };
    3315             : 
    3316           0 :           nsCOMPtr<nsIFrameEnumerator> frameTraversal;
    3317           0 :           nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
    3318           0 :                                              iterContent->OwnerDoc()->
    3319             :                                                GetShell()->GetPresContext(),
    3320             :                                              frame,
    3321             :                                              ePreOrder,
    3322             :                                              false, // aVisual
    3323             :                                              false, // aLockInScrollView
    3324             :                                              true, // aFollowOOFs
    3325             :                                              true,  // aSkipPopupChecks
    3326             :                                              false // aSkipShadow
    3327           0 :                                              );
    3328           0 :           if (NS_SUCCEEDED(rv)) {
    3329             :             nsIFrame* frame =
    3330           0 :               static_cast<nsIFrame*>(frameTraversal->CurrentItem());
    3331           0 :             while (frame) {
    3332             :               int32_t tabIndex;
    3333           0 :               frame->IsFocusable(&tabIndex, 0);
    3334           0 :               if (tabIndex >= 0 &&
    3335           0 :                   (aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
    3336           0 :                 return frame->GetContent();
    3337             :               }
    3338             : 
    3339           0 :               if (aForward) {
    3340           0 :                 frameTraversal->Next();
    3341             :               } else {
    3342           0 :                 frameTraversal->Prev();
    3343             :               }
    3344           0 :               frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
    3345           0 :               if (frame == possibleAnonOwnerFrame) {
    3346             :                 break;
    3347             :               }
    3348             :             }
    3349             :           }
    3350             :         }
    3351             : 
    3352           0 :         continue;
    3353             :       }
    3354             : 
    3355           0 :       if (!IsHostOrSlot(iterContent)) {
    3356           0 :         nsCOMPtr<nsIContent> elementInFrame;
    3357           0 :         bool checkSubDocument = true;
    3358           0 :         if (aForDocumentNavigation &&
    3359           0 :             TryDocumentNavigation(iterContent, &checkSubDocument,
    3360           0 :                                   getter_AddRefs(elementInFrame))) {
    3361           0 :           return elementInFrame;
    3362             :         }
    3363           0 :         if (!checkSubDocument) {
    3364           0 :           continue;
    3365             :         }
    3366             : 
    3367           0 :         if (TryToMoveFocusToSubDocument(iterContent, aOriginalStartContent,
    3368             :                                         aForward, aForDocumentNavigation,
    3369           0 :                                         getter_AddRefs(elementInFrame))) {
    3370           0 :           return elementInFrame;
    3371             :         }
    3372             : 
    3373             :         // Found content to focus
    3374           0 :         return iterContent;
    3375             :       }
    3376             : 
    3377             :       // Search in scope owned by iterContent
    3378             :       nsIContent* contentToFocus =
    3379           0 :         GetNextTabbableContentInScope(iterContent, iterContent,
    3380             :                                       aOriginalStartContent, aForward,
    3381             :                                       aForward ? 1 : 0, aIgnoreTabIndex,
    3382             :                                       aForDocumentNavigation,
    3383           0 :                                       false /* aSkipOwner */);
    3384           0 :       if (contentToFocus) {
    3385             :         return contentToFocus;
    3386             :       }
    3387             :     };
    3388             : 
    3389             :     // If already at lowest priority tab (0), end search completely.
    3390             :     // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
    3391           0 :     if (aCurrentTabIndex == (aForward ? 0 : 1)) {
    3392             :       break;
    3393             :     }
    3394             : 
    3395             :     // Continue looking for next highest priority tabindex
    3396           0 :     aCurrentTabIndex = GetNextTabIndex(aOwner, aCurrentTabIndex, aForward);
    3397             :     contentTraversal.Reset();
    3398             :   }
    3399             : 
    3400             :   // Return shadow host at last for backward navigation if its tabindex
    3401             :   // is non-negative
    3402           0 :   if (!skipOwner && !aForward) {
    3403           0 :     int32_t tabIndex = 0;
    3404           0 :     aOwner->IsFocusable(&tabIndex);
    3405           0 :     if (tabIndex >= 0) {
    3406           0 :       return aOwner;
    3407             :     }
    3408             :   }
    3409             : 
    3410             :   return nullptr;
    3411             : }
    3412             : 
    3413             : nsIContent*
    3414           0 : nsFocusManager::GetNextTabbableContentInAncestorScopes(
    3415             :   nsIContent** aStartContent,
    3416             :   nsIContent* aOriginalStartContent,
    3417             :   bool aForward,
    3418             :   int32_t* aCurrentTabIndex,
    3419             :   bool aIgnoreTabIndex,
    3420             :   bool aForDocumentNavigation)
    3421             : {
    3422           0 :   nsIContent* startContent = *aStartContent;
    3423             :   while (1) {
    3424           0 :     nsIContent* owner = FindOwner(startContent);
    3425           0 :     MOZ_ASSERT(owner, "focus navigation scope owner not in document");
    3426             : 
    3427           0 :     int32_t tabIndex = 0;
    3428           0 :     startContent->IsFocusable(&tabIndex);
    3429             :     nsIContent* contentToFocus =
    3430           0 :       GetNextTabbableContentInScope(owner, startContent, aOriginalStartContent,
    3431             :                                     aForward, tabIndex, aIgnoreTabIndex,
    3432             :                                     aForDocumentNavigation,
    3433           0 :                                     false /* aSkipOwner */);
    3434           0 :     if (contentToFocus) {
    3435           0 :       return contentToFocus;
    3436             :     }
    3437             : 
    3438             :     // If not found in shadow DOM, search from the shadow host in light DOM
    3439           0 :     if (!owner->IsInShadowTree()) {
    3440           0 :       MOZ_ASSERT(owner->GetShadowRoot());
    3441             : 
    3442           0 :       *aStartContent = owner;
    3443           0 :       *aCurrentTabIndex = HostOrSlotTabIndexValue(owner);
    3444           0 :       break;
    3445             :     }
    3446             : 
    3447           0 :     startContent = owner;
    3448           0 :   }
    3449             : 
    3450           0 :   return nullptr;
    3451             : }
    3452             : 
    3453             : nsresult
    3454           0 : nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
    3455             :                                        nsIContent* aRootContent,
    3456             :                                        nsIContent* aOriginalStartContent,
    3457             :                                        nsIContent* aStartContent,
    3458             :                                        bool aForward,
    3459             :                                        int32_t aCurrentTabIndex,
    3460             :                                        bool aIgnoreTabIndex,
    3461             :                                        bool aForDocumentNavigation,
    3462             :                                        nsIContent** aResultContent)
    3463             : {
    3464           0 :   *aResultContent = nullptr;
    3465             : 
    3466           0 :   nsCOMPtr<nsIContent> startContent = aStartContent;
    3467           0 :   if (!startContent)
    3468             :     return NS_OK;
    3469             : 
    3470           0 :   LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
    3471           0 :   LOGFOCUSNAVIGATION(("  tabindex: %d", aCurrentTabIndex));
    3472             : 
    3473           0 :   if (nsDocument::IsShadowDOMEnabled(aRootContent)) {
    3474             :     // If aStartContent is a shadow host or slot in forward navigation,
    3475             :     // search in scope owned by aStartContent
    3476           0 :     if (aForward && IsHostOrSlot(aStartContent)) {
    3477             :       nsIContent* contentToFocus =
    3478           0 :         GetNextTabbableContentInScope(aStartContent, aStartContent,
    3479             :                                       aOriginalStartContent, aForward,
    3480             :                                       aForward ? 1 : 0, aIgnoreTabIndex,
    3481             :                                       aForDocumentNavigation,
    3482           0 :                                       true /* aSkipOwner */);
    3483           0 :       if (contentToFocus) {
    3484           0 :         NS_ADDREF(*aResultContent = contentToFocus);
    3485           0 :         return NS_OK;
    3486             :       }
    3487             :     }
    3488             : 
    3489             :     // If aStartContent is not in scope owned by aRootContent
    3490             :     // (e.g., aStartContent is already in shadow DOM),
    3491             :     // search from scope including aStartContent
    3492           0 :     if (aRootContent != FindOwner(aStartContent)) {
    3493             :       nsIContent* contentToFocus =
    3494           0 :         GetNextTabbableContentInAncestorScopes(&aStartContent,
    3495             :                                                aOriginalStartContent,
    3496             :                                                aForward,
    3497             :                                                &aCurrentTabIndex,
    3498             :                                                aIgnoreTabIndex,
    3499           0 :                                                aForDocumentNavigation);
    3500           0 :       if (contentToFocus) {
    3501           0 :         NS_ADDREF(*aResultContent = contentToFocus);
    3502           0 :         return NS_OK;
    3503             :       }
    3504             :     }
    3505             : 
    3506             :     // If we reach here, it means no next tabbable content in shadow DOM.
    3507             :     // We need to continue searching in light DOM, starting at the shadow host
    3508             :     // in light DOM (updated aStartContent) and its tabindex
    3509             :     // (updated aCurrentTabIndex).
    3510             :   }
    3511             : 
    3512           0 :   nsPresContext* presContext = aPresShell->GetPresContext();
    3513             : 
    3514           0 :   bool getNextFrame = true;
    3515           0 :   nsCOMPtr<nsIContent> iterStartContent = aStartContent;
    3516             :   while (1) {
    3517           0 :     nsIFrame* startFrame = iterStartContent->GetPrimaryFrame();
    3518             :     // if there is no frame, look for another content node that has a frame
    3519           0 :     if (!startFrame) {
    3520             :       // if the root content doesn't have a frame, just return
    3521           0 :       if (iterStartContent == aRootContent)
    3522           0 :         return NS_OK;
    3523             : 
    3524             :       // look for the next or previous content node in tree order
    3525           0 :       iterStartContent = aForward ? iterStartContent->GetNextNode() : iterStartContent->GetPreviousContent();
    3526             :       // we've already skipped over the initial focused content, so we
    3527             :       // don't want to traverse frames.
    3528           0 :       getNextFrame = false;
    3529           0 :       if (iterStartContent)
    3530           0 :         continue;
    3531             : 
    3532             :       // otherwise, as a last attempt, just look at the root content
    3533           0 :       iterStartContent = aRootContent;
    3534           0 :       continue;
    3535             :     }
    3536             : 
    3537             :     // For tab navigation, pass false for aSkipPopupChecks so that we don't
    3538             :     // iterate into or out of a popup. For document naviation pass true to
    3539             :     // ignore these boundaries.
    3540           0 :     nsCOMPtr<nsIFrameEnumerator> frameTraversal;
    3541           0 :     nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
    3542             :                                        presContext, startFrame,
    3543             :                                        ePreOrder,
    3544             :                                        false, // aVisual
    3545             :                                        false, // aLockInScrollView
    3546             :                                        true,  // aFollowOOFs
    3547             :                                        aForDocumentNavigation,  // aSkipPopupChecks
    3548           0 :                                        nsDocument::IsShadowDOMEnabled(aRootContent) // aSkipShadow
    3549           0 :                                        );
    3550           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3551             : 
    3552           0 :     if (iterStartContent == aRootContent) {
    3553           0 :       if (!aForward) {
    3554           0 :         frameTraversal->Last();
    3555           0 :       } else if (aRootContent->IsFocusable()) {
    3556           0 :         frameTraversal->Next();
    3557             :       }
    3558             :     }
    3559           0 :     else if (getNextFrame &&
    3560           0 :              (!iterStartContent ||
    3561           0 :               !iterStartContent->IsHTMLElement(nsGkAtoms::area))) {
    3562             :       // Need to do special check in case we're in an imagemap which has multiple
    3563             :       // content nodes per frame, so don't skip over the starting frame.
    3564           0 :       if (aForward)
    3565           0 :         frameTraversal->Next();
    3566             :       else
    3567           0 :         frameTraversal->Prev();
    3568             :     }
    3569             : 
    3570             :     // Walk frames to find something tabbable matching mCurrentTabIndex
    3571           0 :     nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
    3572           0 :     while (frame) {
    3573           0 :       nsIContent* currentContent = frame->GetContent();
    3574             : 
    3575             :       // For document navigation, check if this element is an open panel. Since
    3576             :       // panels aren't focusable (tabIndex would be -1), we'll just assume that
    3577             :       // for document navigation, the tabIndex is 0.
    3578           0 :       if (aForDocumentNavigation && currentContent && (aCurrentTabIndex == 0) &&
    3579           0 :           currentContent->IsXULElement(nsGkAtoms::panel)) {
    3580           0 :         nsMenuPopupFrame* popupFrame = do_QueryFrame(frame);
    3581             :         // Check if the panel is open. Closed panels are ignored since you can't
    3582             :         // focus anything in them.
    3583           0 :         if (popupFrame && popupFrame->IsOpen()) {
    3584             :           // When moving backward, skip the popup we started in otherwise it
    3585             :           // will be selected again.
    3586           0 :           bool validPopup = true;
    3587           0 :           if (!aForward) {
    3588           0 :             nsIContent* content = aStartContent;
    3589           0 :             while (content) {
    3590           0 :               if (content == currentContent) {
    3591             :                 validPopup = false;
    3592             :                 break;
    3593             :               }
    3594             : 
    3595           0 :               content = content->GetParent();
    3596             :             }
    3597             :           }
    3598             : 
    3599           0 :           if (validPopup) {
    3600             :             // Since a panel isn't focusable itself, find the first focusable
    3601             :             // content within the popup. If there isn't any focusable content
    3602             :             // in the popup, skip this popup and continue iterating through the
    3603             :             // frames. We pass the panel itself (currentContent) as the starting
    3604             :             // and root content, so that we only find content within the panel.
    3605             :             // Note also that we pass false for aForDocumentNavigation since we
    3606             :             // want to locate the first content, not the first document.
    3607             :             rv = GetNextTabbableContent(aPresShell, currentContent,
    3608             :                                         nullptr, currentContent,
    3609             :                                         true, 1, false, false,
    3610           0 :                                         aResultContent);
    3611           0 :             if (NS_SUCCEEDED(rv) && *aResultContent) {
    3612           0 :               return rv;
    3613             :             }
    3614             :           }
    3615             :         }
    3616             :       }
    3617             : 
    3618             :       // As of now, 2018/04/12, sequential focus navigation is still
    3619             :       // in the obsolete Shadow DOM specification.
    3620             :       // http://w3c.github.io/webcomponents/spec/shadow/#sequential-focus-navigation
    3621             :       // "if ELEMENT is focusable, a shadow host, or a slot element,
    3622             :       //  append ELEMENT to NAVIGATION-ORDER."
    3623             :       // and later in "For each element ELEMENT in NAVIGATION-ORDER: "
    3624             :       // hosts and slots are handled before other elements.
    3625           0 :       if (currentContent && nsDocument::IsShadowDOMEnabled(currentContent) &&
    3626           0 :           IsHostOrSlot(currentContent)) {
    3627           0 :         int32_t tabIndex = HostOrSlotTabIndexValue(currentContent);
    3628           0 :         if (tabIndex >= 0 &&
    3629           0 :             (aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
    3630             :           nsIContent* contentToFocus =
    3631           0 :             GetNextTabbableContentInScope(currentContent, currentContent,
    3632             :                                           aOriginalStartContent, aForward,
    3633             :                                           aForward ? 1 : 0, aIgnoreTabIndex,
    3634             :                                           aForDocumentNavigation,
    3635           0 :                                           true /* aSkipOwner */);
    3636           0 :           if (contentToFocus) {
    3637           0 :             NS_ADDREF(*aResultContent = contentToFocus);
    3638           0 :             return NS_OK;
    3639             :           }
    3640             :         }
    3641             :       }
    3642             : 
    3643             :       // TabIndex not set defaults to 0 for form elements, anchors and other
    3644             :       // elements that are normally focusable. Tabindex defaults to -1
    3645             :       // for elements that are not normally focusable.
    3646             :       // The returned computed tabindex from IsFocusable() is as follows:
    3647             :       //          < 0 not tabbable at all
    3648             :       //          == 0 in normal tab order (last after positive tabindexed items)
    3649             :       //          > 0 can be tabbed to in the order specified by this value
    3650             :       int32_t tabIndex;
    3651           0 :       frame->IsFocusable(&tabIndex, 0);
    3652             : 
    3653           0 :       LOGCONTENTNAVIGATION("Next Tabbable %s:", frame->GetContent());
    3654           0 :       LOGFOCUSNAVIGATION(("  with tabindex: %d expected: %d", tabIndex, aCurrentTabIndex));
    3655             : 
    3656           0 :       if (tabIndex >= 0) {
    3657           0 :         NS_ASSERTION(currentContent, "IsFocusable set a tabindex for a frame with no content");
    3658           0 :         if (!aForDocumentNavigation &&
    3659           0 :             currentContent->IsHTMLElement(nsGkAtoms::img) &&
    3660           0 :             currentContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::usemap)) {
    3661             :           // This is an image with a map. Image map areas are not traversed by
    3662             :           // nsIFrameTraversal so look for the next or previous area element.
    3663             :           nsIContent *areaContent =
    3664           0 :             GetNextTabbableMapArea(aForward, aCurrentTabIndex,
    3665           0 :                                    currentContent->AsElement(), iterStartContent);
    3666           0 :           if (areaContent) {
    3667           0 :             NS_ADDREF(*aResultContent = areaContent);
    3668           0 :             return NS_OK;
    3669             :           }
    3670             :         }
    3671           0 :         else if (aIgnoreTabIndex || aCurrentTabIndex == tabIndex) {
    3672             :           // break out if we've wrapped around to the start again.
    3673           0 :           if (aOriginalStartContent && currentContent == aOriginalStartContent) {
    3674           0 :             NS_ADDREF(*aResultContent = currentContent);
    3675           0 :             return NS_OK;
    3676             :           }
    3677             : 
    3678             :           // If this is a remote child browser, call NavigateDocument to have
    3679             :           // the child process continue the navigation. Return a special error
    3680             :           // code to have the caller return early. If the child ends up not
    3681             :           // being focusable in some way, the child process will call back
    3682             :           // into document navigation again by calling MoveFocus.
    3683           0 :           TabParent* remote = TabParent::GetFrom(currentContent);
    3684           0 :           if (remote) {
    3685           0 :             remote->NavigateByKey(aForward, aForDocumentNavigation);
    3686           0 :             return NS_SUCCESS_DOM_NO_OPERATION;
    3687             :           }
    3688             : 
    3689             :           // Next, for document navigation, check if this a non-remote child document.
    3690           0 :           bool checkSubDocument = true;
    3691           0 :           if (aForDocumentNavigation &&
    3692           0 :               TryDocumentNavigation(currentContent, &checkSubDocument,
    3693             :                                     aResultContent)) {
    3694             :             return NS_OK;
    3695             :           }
    3696             : 
    3697           0 :           if (checkSubDocument) {
    3698             :             // found a node with a matching tab index. Check if it is a child
    3699             :             // frame. If so, navigate into the child frame instead.
    3700           0 :             if (TryToMoveFocusToSubDocument(currentContent,
    3701             :                                             aOriginalStartContent,
    3702             :                                             aForward, aForDocumentNavigation,
    3703             :                                             aResultContent)) {
    3704           0 :               MOZ_ASSERT(*aResultContent);
    3705             :               return NS_OK;
    3706             :             }
    3707             :             // otherwise, use this as the next content node to tab to, unless
    3708             :             // this was the element we started on. This would happen for
    3709             :             // instance on an element with child frames, where frame navigation
    3710             :             // could return the original element again. In that case, just skip
    3711             :             // it. Also, if the next content node is the root content, then
    3712             :             // return it. This latter case would happen only if someone made a
    3713             :             // popup focusable.
    3714             :             // Also, when going backwards, check to ensure that the focus
    3715             :             // wouldn't be redirected. Otherwise, for example, when an input in
    3716             :             // a textbox is focused, the enclosing textbox would be found and
    3717             :             // the same inner input would be returned again.
    3718           0 :             else if (currentContent == aRootContent ||
    3719           0 :                      (currentContent != startContent &&
    3720           0 :                       (aForward || !GetRedirectedFocus(currentContent)))) {
    3721             : 
    3722           0 :               if (nsDocument::IsShadowDOMEnabled(aRootContent)) {
    3723             :                 // If currentContent is a shadow host in backward
    3724             :                 // navigation, search in scope owned by currentContent
    3725           0 :                 if (!aForward && currentContent->GetShadowRoot()) {
    3726             :                   nsIContent* contentToFocus =
    3727           0 :                     GetNextTabbableContentInScope(currentContent,
    3728             :                                                   currentContent,
    3729             :                                                   aOriginalStartContent,
    3730             :                                                   aForward, aForward ? 1 : 0,
    3731             :                                                   aIgnoreTabIndex,
    3732             :                                                   aForDocumentNavigation,
    3733           0 :                                                   true /* aSkipOwner */);
    3734           0 :                   if (contentToFocus) {
    3735           0 :                     NS_ADDREF(*aResultContent = contentToFocus);
    3736           0 :                     return NS_OK;
    3737             :                   }
    3738             :                 }
    3739             :               }
    3740             : 
    3741           0 :               NS_ADDREF(*aResultContent = currentContent);
    3742           0 :               return NS_OK;
    3743             :             }
    3744             :           }
    3745             :         }
    3746             :       }
    3747           0 :       else if (aOriginalStartContent && currentContent == aOriginalStartContent) {
    3748             :         // not focusable, so return if we have wrapped around to the original
    3749             :         // content. This is necessary in case the original starting content was
    3750             :         // not focusable.
    3751           0 :         NS_ADDREF(*aResultContent = currentContent);
    3752           0 :         return NS_OK;
    3753             :       }
    3754             : 
    3755             :       // Move to the next or previous frame, but ignore continuation frames
    3756             :       // since only the first frame should be involved in focusability.
    3757             :       // Otherwise, a loop will occur in the following example:
    3758             :       //   <span tabindex="1">...<a/><a/>...</span>
    3759             :       // where the text wraps onto multiple lines. Tabbing from the second
    3760             :       // link can find one of the span's continuation frames between the link
    3761             :       // and the end of the span, and the span would end up getting focused
    3762             :       // again.
    3763           0 :       do {
    3764           0 :         if (aForward)
    3765           0 :           frameTraversal->Next();
    3766             :         else
    3767           0 :           frameTraversal->Prev();
    3768           0 :         frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
    3769           0 :       } while (frame && frame->GetPrevContinuation());
    3770             :     }
    3771             : 
    3772             :     // If already at lowest priority tab (0), end search completely.
    3773             :     // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
    3774           0 :     if (aCurrentTabIndex == (aForward ? 0 : 1)) {
    3775             :       // if going backwards, the canvas should be focused once the beginning
    3776             :       // has been reached, so get the root element.
    3777           0 :       if (!aForward) {
    3778           0 :         nsCOMPtr<nsPIDOMWindowOuter> window = GetCurrentWindow(aRootContent);
    3779           0 :         NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
    3780             : 
    3781             :         RefPtr<Element> docRoot =
    3782           0 :           GetRootForFocus(window, aRootContent->GetComposedDoc(), false, true);
    3783           0 :         FocusFirst(docRoot, aResultContent);
    3784             :       }
    3785           0 :       break;
    3786             :     }
    3787             : 
    3788             :     // continue looking for next highest priority tabindex
    3789           0 :     aCurrentTabIndex = GetNextTabIndex(aRootContent, aCurrentTabIndex, aForward);
    3790           0 :     startContent = iterStartContent = aRootContent;
    3791             :   }
    3792             : 
    3793           0 :   return NS_OK;
    3794             : }
    3795             : 
    3796             : bool
    3797           0 : nsFocusManager::TryDocumentNavigation(nsIContent* aCurrentContent,
    3798             :                                       bool* aCheckSubDocument,
    3799             :                                       nsIContent** aResultContent)
    3800             : {
    3801           0 :   *aCheckSubDocument = true;
    3802           0 :   Element* docRoot = GetRootForChildDocument(aCurrentContent);
    3803           0 :   if (docRoot) {
    3804             :     // If GetRootForChildDocument returned something then call
    3805             :     // FocusFirst to find the root or first element to focus within
    3806             :     // the child document. If this is a frameset though, skip this and
    3807             :     // fall through to normal tab navigation to iterate into
    3808             :     // the frameset's frames and locate the first focusable frame.
    3809           0 :     if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
    3810           0 :       *aCheckSubDocument = false;
    3811           0 :       Unused << FocusFirst(docRoot, aResultContent);
    3812           0 :       return *aResultContent != nullptr;
    3813             :     }
    3814             :   } else {
    3815             :     // Set aCheckSubDocument to false, as this was neither a frame
    3816             :     // type element or a child document that was focusable.
    3817           0 :     *aCheckSubDocument = false;
    3818             :   }
    3819             : 
    3820             :   return false;
    3821             : }
    3822             : 
    3823             : bool
    3824           0 : nsFocusManager::TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
    3825             :                                             nsIContent* aOriginalStartContent,
    3826             :                                             bool aForward,
    3827             :                                             bool aForDocumentNavigation,
    3828             :                                             nsIContent** aResultContent)
    3829             : {
    3830           0 :   nsIDocument* doc = aCurrentContent->GetComposedDoc();
    3831           0 :   NS_ASSERTION(doc, "content not in document");
    3832           0 :   nsIDocument* subdoc = doc->GetSubDocumentFor(aCurrentContent);
    3833           0 :   if (subdoc && !subdoc->EventHandlingSuppressed()) {
    3834           0 :     if (aForward) {
    3835             :       // when tabbing forward into a frame, return the root
    3836             :       // frame so that the canvas becomes focused.
    3837           0 :       nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow();
    3838           0 :       if (subframe) {
    3839           0 :         *aResultContent = GetRootForFocus(subframe, subdoc, false, true);
    3840           0 :         if (*aResultContent) {
    3841           0 :           NS_ADDREF(*aResultContent);
    3842           0 :           return true;
    3843             :         }
    3844             :       }
    3845             :     }
    3846           0 :     Element* rootElement = subdoc->GetRootElement();
    3847           0 :     nsIPresShell* subShell = subdoc->GetShell();
    3848           0 :     if (rootElement && subShell) {
    3849           0 :       nsresult rv = GetNextTabbableContent(subShell, rootElement,
    3850             :                                            aOriginalStartContent, rootElement,
    3851             :                                            aForward, (aForward ? 1 : 0),
    3852             :                                            false, aForDocumentNavigation,
    3853           0 :                                            aResultContent);
    3854           0 :       NS_ENSURE_SUCCESS(rv, false);
    3855           0 :       if (*aResultContent) {
    3856             :         return true;
    3857             :       }
    3858             :     }
    3859             :   }
    3860             :   return false;
    3861             : }
    3862             : 
    3863             : nsIContent*
    3864           0 : nsFocusManager::GetNextTabbableMapArea(bool aForward,
    3865             :                                        int32_t aCurrentTabIndex,
    3866             :                                        Element* aImageContent,
    3867             :                                        nsIContent* aStartContent)
    3868             : {
    3869           0 :   nsAutoString useMap;
    3870           0 :   aImageContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, useMap);
    3871             : 
    3872           0 :   nsCOMPtr<nsIDocument> doc = aImageContent->GetComposedDoc();
    3873           0 :   if (doc) {
    3874           0 :     nsCOMPtr<nsIContent> mapContent = doc->FindImageMap(useMap);
    3875           0 :     if (!mapContent)
    3876           0 :       return nullptr;
    3877           0 :     uint32_t count = mapContent->GetChildCount();
    3878             :     // First see if the the start content is in this map
    3879             : 
    3880           0 :     int32_t index = mapContent->ComputeIndexOf(aStartContent);
    3881             :     int32_t tabIndex;
    3882           0 :     if (index < 0 || (aStartContent->IsFocusable(&tabIndex) &&
    3883           0 :                       tabIndex != aCurrentTabIndex)) {
    3884             :       // If aStartContent is in this map we must start iterating past it.
    3885             :       // We skip the case where aStartContent has tabindex == aStartContent
    3886             :       // since the next tab ordered element might be before it
    3887             :       // (or after for backwards) in the child list.
    3888           0 :       index = aForward ? -1 : (int32_t)count;
    3889             :     }
    3890             : 
    3891             :     // GetChildAt_Deprecated will return nullptr if our index < 0 or index >=
    3892             :     // count
    3893           0 :     nsCOMPtr<nsIContent> areaContent;
    3894           0 :     while ((areaContent = mapContent->GetChildAt_Deprecated(aForward ? ++index : --index)) != nullptr) {
    3895           0 :       if (areaContent->IsFocusable(&tabIndex) && tabIndex == aCurrentTabIndex) {
    3896           0 :         return areaContent;
    3897             :       }
    3898             :     }
    3899             :   }
    3900             : 
    3901             :   return nullptr;
    3902             : }
    3903             : 
    3904             : int32_t
    3905           0 : nsFocusManager::GetNextTabIndex(nsIContent* aParent,
    3906             :                                 int32_t aCurrentTabIndex,
    3907             :                                 bool aForward)
    3908             : {
    3909             :   int32_t tabIndex, childTabIndex;
    3910           0 :   FlattenedChildIterator iter(aParent);
    3911             : 
    3912           0 :   if (aForward) {
    3913           0 :     tabIndex = 0;
    3914           0 :     for (nsIContent* child = iter.GetNextChild();
    3915           0 :          child;
    3916             :          child = iter.GetNextChild()) {
    3917             :       // Skip child's descendants if child is a shadow host or slot, as they are
    3918             :       // in the focus navigation scope owned by child's shadow root
    3919           0 :       if (!(nsDocument::IsShadowDOMEnabled(aParent) && IsHostOrSlot(child))) {
    3920           0 :         childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
    3921           0 :         if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
    3922           0 :           tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
    3923             :         }
    3924             :       }
    3925             : 
    3926           0 :       nsAutoString tabIndexStr;
    3927           0 :       if (child->IsElement()) {
    3928           0 :         child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
    3929             :       }
    3930             :       nsresult ec;
    3931           0 :       int32_t val = tabIndexStr.ToInteger(&ec);
    3932           0 :       if (NS_SUCCEEDED (ec) && val > aCurrentTabIndex && val != tabIndex) {
    3933           0 :         tabIndex = (tabIndex == 0 || val < tabIndex) ? val : tabIndex;
    3934             :       }
    3935             :     }
    3936             :   }
    3937             :   else { /* !aForward */
    3938           0 :     tabIndex = 1;
    3939           0 :     for (nsIContent* child = iter.GetNextChild();
    3940           0 :          child;
    3941             :          child = iter.GetNextChild()) {
    3942             :       // Skip child's descendants if child is a shadow host or slot, as they are
    3943             :       // in the focus navigation scope owned by child's shadow root
    3944           0 :       if (!(nsDocument::IsShadowDOMEnabled(aParent) && IsHostOrSlot(child))) {
    3945           0 :         childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
    3946           0 :         if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
    3947           0 :             (childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
    3948           0 :           tabIndex = childTabIndex;
    3949             :         }
    3950             :       }
    3951             : 
    3952           0 :       nsAutoString tabIndexStr;
    3953           0 :       if (child->IsElement()) {
    3954           0 :         child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
    3955             :       }
    3956             :       nsresult ec;
    3957           0 :       int32_t val = tabIndexStr.ToInteger(&ec);
    3958           0 :       if (NS_SUCCEEDED (ec)) {
    3959           0 :         if ((aCurrentTabIndex == 0 && val > tabIndex) ||
    3960           0 :             (val < aCurrentTabIndex && val > tabIndex) ) {
    3961           0 :           tabIndex = val;
    3962             :         }
    3963             :       }
    3964             :     }
    3965             :   }
    3966             : 
    3967           0 :   return tabIndex;
    3968             : }
    3969             : 
    3970             : nsresult
    3971           0 : nsFocusManager::FocusFirst(Element* aRootElement, nsIContent** aNextContent)
    3972             : {
    3973           0 :   if (!aRootElement) {
    3974             :     return NS_OK;
    3975             :   }
    3976             : 
    3977           0 :   nsIDocument* doc = aRootElement->GetComposedDoc();
    3978           0 :   if (doc) {
    3979           0 :     if (doc->IsXULDocument()) {
    3980             :       // If the redirectdocumentfocus attribute is set, redirect the focus to a
    3981             :       // specific element. This is primarily used to retarget the focus to the
    3982             :       // urlbar during document navigation.
    3983           0 :       nsAutoString retarget;
    3984             : 
    3985           0 :       if (aRootElement->GetAttr(kNameSpaceID_None,
    3986             :                                nsGkAtoms::retargetdocumentfocus, retarget)) {
    3987           0 :         nsCOMPtr<Element> element = doc->GetElementById(retarget);
    3988             :         nsCOMPtr<nsIContent> retargetElement =
    3989           0 :           CheckIfFocusable(element, 0);
    3990           0 :         if (retargetElement) {
    3991           0 :           retargetElement.forget(aNextContent);
    3992           0 :           return NS_OK;
    3993             :         }
    3994             :       }
    3995             :     }
    3996             : 
    3997           0 :     nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
    3998           0 :     if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
    3999             :       // If the found content is in a chrome shell, navigate forward one
    4000             :       // tabbable item so that the first item is focused. Note that we
    4001             :       // always go forward and not back here.
    4002           0 :       nsIPresShell* presShell = doc->GetShell();
    4003           0 :       if (presShell) {
    4004             :         return GetNextTabbableContent(presShell, aRootElement,
    4005             :                                       nullptr, aRootElement,
    4006             :                                       true, 1, false, false,
    4007           0 :                                       aNextContent);
    4008             :       }
    4009             :     }
    4010             :   }
    4011             : 
    4012           0 :   NS_ADDREF(*aNextContent = aRootElement);
    4013           0 :   return NS_OK;
    4014             : }
    4015             : 
    4016             : Element*
    4017           0 : nsFocusManager::GetRootForFocus(nsPIDOMWindowOuter* aWindow,
    4018             :                                 nsIDocument* aDocument,
    4019             :                                 bool aForDocumentNavigation,
    4020             :                                 bool aCheckVisibility)
    4021             : {
    4022           0 :   if (!aForDocumentNavigation) {
    4023           0 :     nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
    4024           0 :     if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
    4025           0 :       return nullptr;
    4026             :     }
    4027             :   }
    4028             : 
    4029           0 :   if (aCheckVisibility && !IsWindowVisible(aWindow))
    4030             :     return nullptr;
    4031             : 
    4032             :   // If the body is contenteditable, use the editor's root element rather than
    4033             :   // the actual root element.
    4034             :   RefPtr<Element> rootElement =
    4035           0 :     nsLayoutUtils::GetEditableRootContentByContentEditable(aDocument);
    4036           0 :   if (!rootElement || !rootElement->GetPrimaryFrame()) {
    4037           0 :     rootElement = aDocument->GetRootElement();
    4038           0 :     if (!rootElement) {
    4039             :       return nullptr;
    4040             :     }
    4041             :   }
    4042             : 
    4043           0 :   if (aCheckVisibility && !rootElement->GetPrimaryFrame()) {
    4044             :     return nullptr;
    4045             :   }
    4046             : 
    4047             :   // Finally, check if this is a frameset
    4048           0 :   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
    4049           0 :   if (htmlDoc) {
    4050           0 :     Element* htmlChild = aDocument->GetHtmlChildElement(nsGkAtoms::frameset);
    4051           0 :     if (htmlChild) {
    4052             :       // In document navigation mode, return the frameset so that navigation
    4053             :       // descends into the child frames.
    4054           0 :       return aForDocumentNavigation ? htmlChild : nullptr;
    4055             :     }
    4056             :   }
    4057             : 
    4058           0 :   return rootElement;
    4059             : }
    4060             : 
    4061             : Element*
    4062           0 : nsFocusManager::GetRootForChildDocument(nsIContent* aContent)
    4063             : {
    4064             :   // Check for elements that represent child documents, that is, browsers,
    4065             :   // editors or frames from a frameset. We don't include iframes since we
    4066             :   // consider them to be an integral part of the same window or page.
    4067           0 :   if (!aContent ||
    4068           0 :       !(aContent->IsXULElement(nsGkAtoms::browser) ||
    4069           0 :         aContent->IsXULElement(nsGkAtoms::editor) ||
    4070           0 :         aContent->IsHTMLElement(nsGkAtoms::frame))) {
    4071             :     return nullptr;
    4072             :   }
    4073             : 
    4074           0 :   nsIDocument* doc = aContent->GetComposedDoc();
    4075           0 :   if (!doc) {
    4076             :     return nullptr;
    4077             :   }
    4078             : 
    4079           0 :   nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
    4080           0 :   if (!subdoc || subdoc->EventHandlingSuppressed()) {
    4081             :     return nullptr;
    4082             :   }
    4083             : 
    4084           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = subdoc->GetWindow();
    4085           0 :   return GetRootForFocus(window, subdoc, true, true);
    4086             : }
    4087             : 
    4088             : void
    4089           0 : nsFocusManager::GetFocusInSelection(nsPIDOMWindowOuter* aWindow,
    4090             :                                     nsIContent* aStartSelection,
    4091             :                                     nsIContent* aEndSelection,
    4092             :                                     nsIContent** aFocusedContent)
    4093             : {
    4094           0 :   *aFocusedContent = nullptr;
    4095             : 
    4096           0 :   nsCOMPtr<nsIContent> testContent = aStartSelection;
    4097           0 :   nsCOMPtr<nsIContent> nextTestContent = aEndSelection;
    4098             : 
    4099           0 :   nsCOMPtr<nsIContent> currentFocus = aWindow->GetFocusedElement();
    4100             : 
    4101             :   // We now have the correct start node in selectionContent!
    4102             :   // Search for focusable elements, starting with selectionContent
    4103             : 
    4104             :   // Method #1: Keep going up while we look - an ancestor might be focusable
    4105             :   // We could end the loop earlier, such as when we're no longer
    4106             :   // in the same frame, by comparing selectionContent->GetPrimaryFrame()
    4107             :   // with a variable holding the starting selectionContent
    4108           0 :   while (testContent) {
    4109             :     // Keep testing while selectionContent is equal to something,
    4110             :     // eventually we'll run out of ancestors
    4111             : 
    4112           0 :     nsCOMPtr<nsIURI> uri;
    4113           0 :     if (testContent == currentFocus ||
    4114           0 :         testContent->IsLink(getter_AddRefs(uri))) {
    4115           0 :       testContent.forget(aFocusedContent);
    4116           0 :       return;
    4117             :     }
    4118             : 
    4119             :     // Get the parent
    4120           0 :     testContent = testContent->GetParent();
    4121             : 
    4122           0 :     if (!testContent) {
    4123             :       // We run this loop again, checking the ancestor chain of the selection's end point
    4124           0 :       testContent = nextTestContent;
    4125           0 :       nextTestContent = nullptr;
    4126             :     }
    4127             :   }
    4128             : 
    4129             :   // We couldn't find an anchor that was an ancestor of the selection start
    4130             :   // Method #2: look for anchor in selection's primary range (depth first search)
    4131             : 
    4132           0 :   nsCOMPtr<nsIContent> selectionNode = aStartSelection;
    4133           0 :   nsCOMPtr<nsIContent> endSelectionNode = aEndSelection;
    4134           0 :   nsCOMPtr<nsIContent> testNode;
    4135             : 
    4136           0 :   do {
    4137           0 :     testContent = selectionNode;
    4138             : 
    4139             :     // We're looking for any focusable link that could be part of the
    4140             :     // main document's selection.
    4141           0 :     nsCOMPtr<nsIURI> uri;
    4142           0 :     if (testContent == currentFocus ||
    4143           0 :         testContent->IsLink(getter_AddRefs(uri))) {
    4144           0 :       testContent.forget(aFocusedContent);
    4145           0 :       return;
    4146             :     }
    4147             : 
    4148           0 :     nsIContent* testNode = selectionNode->GetFirstChild();
    4149           0 :     if (testNode) {
    4150           0 :       selectionNode = testNode;
    4151           0 :       continue;
    4152             :     }
    4153             : 
    4154           0 :     if (selectionNode == endSelectionNode)
    4155             :       break;
    4156           0 :     testNode = selectionNode->GetNextSibling();
    4157           0 :     if (testNode) {
    4158           0 :       selectionNode = testNode;
    4159           0 :       continue;
    4160             :     }
    4161             : 
    4162             :     do {
    4163             :       // GetParent is OK here, instead of GetParentNode, because the only case
    4164             :       // where the latter returns something different from the former is when
    4165             :       // GetParentNode is the document.  But in that case we would simply get
    4166             :       // null for selectionNode when setting it to testNode->GetNextSibling()
    4167             :       // (because a document has no next sibling).  And then the next iteration
    4168             :       // of this loop would get null for GetParentNode anyway, and break out of
    4169             :       // all the loops.
    4170           0 :       testNode = selectionNode->GetParent();
    4171           0 :       if (!testNode || testNode == endSelectionNode) {
    4172           0 :         selectionNode = nullptr;
    4173           0 :         break;
    4174             :       }
    4175           0 :       selectionNode = testNode->GetNextSibling();
    4176           0 :       if (selectionNode)
    4177             :         break;
    4178           0 :       selectionNode = testNode;
    4179             :     } while (true);
    4180             :   }
    4181           0 :   while (selectionNode && selectionNode != endSelectionNode);
    4182             : }
    4183             : 
    4184             : class PointerUnlocker : public Runnable
    4185             : {
    4186             : public:
    4187           0 :   PointerUnlocker()
    4188           0 :     : mozilla::Runnable("PointerUnlocker")
    4189             :   {
    4190           0 :     MOZ_ASSERT(!PointerUnlocker::sActiveUnlocker);
    4191           0 :     PointerUnlocker::sActiveUnlocker = this;
    4192           0 :   }
    4193             : 
    4194           0 :   ~PointerUnlocker()
    4195           0 :   {
    4196           0 :     if (PointerUnlocker::sActiveUnlocker == this) {
    4197           0 :       PointerUnlocker::sActiveUnlocker = nullptr;
    4198             :     }
    4199           0 :   }
    4200             : 
    4201           0 :   NS_IMETHOD Run() override
    4202             :   {
    4203           0 :     if (PointerUnlocker::sActiveUnlocker == this) {
    4204           0 :       PointerUnlocker::sActiveUnlocker = nullptr;
    4205             :     }
    4206           0 :     NS_ENSURE_STATE(nsFocusManager::GetFocusManager());
    4207             :     nsPIDOMWindowOuter* focused =
    4208           0 :       nsFocusManager::GetFocusManager()->GetFocusedWindow();
    4209             :     nsCOMPtr<nsIDocument> pointerLockedDoc =
    4210           0 :       do_QueryReferent(EventStateManager::sPointerLockedDoc);
    4211           0 :     if (pointerLockedDoc &&
    4212           0 :         !nsContentUtils::IsInPointerLockContext(focused)) {
    4213           0 :       nsIDocument::UnlockPointer();
    4214             :     }
    4215             :     return NS_OK;
    4216             :   }
    4217             : 
    4218             :   static PointerUnlocker* sActiveUnlocker;
    4219             : };
    4220             : 
    4221             : PointerUnlocker*
    4222             : PointerUnlocker::sActiveUnlocker = nullptr;
    4223             : 
    4224             : void
    4225           1 : nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow)
    4226             : {
    4227           0 :   if (!PointerUnlocker::sActiveUnlocker &&
    4228           2 :       nsContentUtils::IsInPointerLockContext(mFocusedWindow) &&
    4229           0 :       !nsContentUtils::IsInPointerLockContext(aWindow)) {
    4230           0 :     nsCOMPtr<nsIRunnable> runnable = new PointerUnlocker();
    4231           0 :     NS_DispatchToCurrentThread(runnable);
    4232             :   }
    4233             : 
    4234             :   // Update the last focus time on any affected documents
    4235           2 :   if (aWindow && aWindow != mFocusedWindow) {
    4236           1 :     const TimeStamp now(TimeStamp::Now());
    4237           0 :     for (nsIDocument* doc = aWindow->GetExtantDoc();
    4238           0 :          doc;
    4239             :          doc = doc->GetParentDocument()) {
    4240           0 :       doc->SetLastFocusTime(now);
    4241             :     }
    4242             :   }
    4243             : 
    4244           1 :   mFocusedWindow = aWindow;
    4245           1 : }
    4246             : 
    4247             : void
    4248           0 : nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
    4249             : {
    4250           0 :   if (!sInstance) {
    4251             :     return;
    4252             :   }
    4253             : 
    4254           0 :   if (sInstance->mActiveWindow) {
    4255             :     sInstance->mActiveWindow->
    4256           0 :       MarkUncollectableForCCGeneration(aGeneration);
    4257             :   }
    4258           0 :   if (sInstance->mFocusedWindow) {
    4259             :     sInstance->mFocusedWindow->
    4260           0 :       MarkUncollectableForCCGeneration(aGeneration);
    4261             :   }
    4262           0 :   if (sInstance->mWindowBeingLowered) {
    4263             :     sInstance->mWindowBeingLowered->
    4264           0 :       MarkUncollectableForCCGeneration(aGeneration);
    4265             :   }
    4266           0 :   if (sInstance->mFocusedElement) {
    4267           0 :     sInstance->mFocusedElement->OwnerDoc()->
    4268           0 :       MarkUncollectableForCCGeneration(aGeneration);
    4269             :   }
    4270           0 :   if (sInstance->mFirstBlurEvent) {
    4271           0 :     sInstance->mFirstBlurEvent->OwnerDoc()->
    4272           0 :       MarkUncollectableForCCGeneration(aGeneration);
    4273             :   }
    4274           0 :   if (sInstance->mFirstFocusEvent) {
    4275           0 :     sInstance->mFirstFocusEvent->OwnerDoc()->
    4276           0 :       MarkUncollectableForCCGeneration(aGeneration);
    4277             :   }
    4278           0 :   if (sInstance->mMouseButtonEventHandlingDocument) {
    4279             :     sInstance->mMouseButtonEventHandlingDocument->
    4280           0 :       MarkUncollectableForCCGeneration(aGeneration);
    4281             :   }
    4282             : }
    4283             : 
    4284             : bool
    4285           0 : nsFocusManager::CanSkipFocus(nsIContent* aContent)
    4286             : {
    4287           0 :   if (!aContent ||
    4288           0 :       nsContentUtils::IsChromeDoc(aContent->OwnerDoc())) {
    4289             :     return false;
    4290             :   }
    4291             : 
    4292           0 :   if (mFocusedElement == aContent) {
    4293             :     return true;
    4294             :   }
    4295             : 
    4296           0 :   nsIDocShell* ds = aContent->OwnerDoc()->GetDocShell();
    4297           0 :   if (!ds) {
    4298             :     return true;
    4299             :   }
    4300             : 
    4301           0 :   nsCOMPtr<nsIDocShellTreeItem> root;
    4302           0 :   ds->GetRootTreeItem(getter_AddRefs(root));
    4303             :   nsCOMPtr<nsPIDOMWindowOuter> newRootWindow =
    4304           0 :     root ? root->GetWindow() : nullptr;
    4305           0 :   if (mActiveWindow != newRootWindow) {
    4306           0 :     nsPIDOMWindowOuter* outerWindow = aContent->OwnerDoc()->GetWindow();
    4307           0 :     if (outerWindow && outerWindow->GetFocusedElement() == aContent) {
    4308             :       return true;
    4309             :     }
    4310             :   }
    4311             : 
    4312             :   return false;
    4313             : }
    4314             : 
    4315             : nsresult
    4316           1 : NS_NewFocusManager(nsIFocusManager** aResult)
    4317             : {
    4318           0 :   NS_IF_ADDREF(*aResult = nsFocusManager::GetFocusManager());
    4319           1 :   return NS_OK;
    4320             : }

Generated by: LCOV version 1.13-14-ga5dd952