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

Generated by: LCOV version 1.13