dom/base/nsFocusManager.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/dom/TabParent.h"
     8 #include "nsFocusManager.h"
    10 #include "nsIInterfaceRequestorUtils.h"
    11 #include "nsGkAtoms.h"
    12 #include "nsContentUtils.h"
    13 #include "nsIDocument.h"
    14 #include "nsIDOMWindow.h"
    15 #include "nsPIDOMWindow.h"
    16 #include "nsIDOMElement.h"
    17 #include "nsIDOMDocument.h"
    18 #include "nsIDOMRange.h"
    19 #include "nsIHTMLDocument.h"
    20 #include "nsIDocShell.h"
    21 #include "nsIDocShellTreeOwner.h"
    22 #include "nsLayoutUtils.h"
    23 #include "nsIPresShell.h"
    24 #include "nsFrameTraversal.h"
    25 #include "nsIWebNavigation.h"
    26 #include "nsCaret.h"
    27 #include "nsIBaseWindow.h"
    28 #include "nsViewManager.h"
    29 #include "nsFrameSelection.h"
    30 #include "mozilla/dom/Selection.h"
    31 #include "nsXULPopupManager.h"
    32 #include "nsIScriptObjectPrincipal.h"
    33 #include "nsIPrincipal.h"
    34 #include "nsIObserverService.h"
    35 #include "nsIObjectFrame.h"
    36 #include "nsBindingManager.h"
    37 #include "nsStyleCoord.h"
    39 #include "mozilla/ContentEvents.h"
    40 #include "mozilla/dom/Element.h"
    41 #include "mozilla/EventDispatcher.h"
    42 #include "mozilla/EventStateManager.h"
    43 #include "mozilla/EventStates.h"
    44 #include "mozilla/IMEStateManager.h"
    45 #include "mozilla/LookAndFeel.h"
    46 #include "mozilla/Preferences.h"
    47 #include "mozilla/Services.h"
    48 #include <algorithm>
    50 #ifdef MOZ_XUL
    51 #include "nsIDOMXULTextboxElement.h"
    52 #include "nsIDOMXULMenuListElement.h"
    53 #endif
    55 #ifdef ACCESSIBILITY
    56 #include "nsAccessibilityService.h"
    57 #endif
    59 #ifndef XP_MACOSX
    60 #include "nsIScriptError.h"
    61 #endif
    63 using namespace mozilla;
    64 using namespace mozilla::dom;
    65 using namespace mozilla::widget;
    67 #ifdef PR_LOGGING
    69 // Two types of focus pr logging are available:
    70 //   'Focus' for normal focus manager calls
    71 //   'FocusNavigation' for tab and document navigation
    72 PRLogModuleInfo* gFocusLog;
    73 PRLogModuleInfo* gFocusNavigationLog;
    75 #define LOGFOCUS(args) PR_LOG(gFocusLog, 4, args)
    76 #define LOGFOCUSNAVIGATION(args) PR_LOG(gFocusNavigationLog, 4, args)
    78 #define LOGTAG(log, format, content)                            \
    79   {                                                             \
    80     nsAutoCString tag(NS_LITERAL_CSTRING("(none)"));            \
    81     if (content) {                                              \
    82       content->Tag()->ToUTF8String(tag);                        \
    83     }                                                           \
    84     PR_LOG(log, 4, (format, tag.get()));                        \
    85   }
    87 #define LOGCONTENT(format, content) LOGTAG(gFocusLog, format, content)
    88 #define LOGCONTENTNAVIGATION(format, content) LOGTAG(gFocusNavigationLog, format, content)
    90 #else
    92 #define LOGFOCUS(args)
    93 #define LOGFOCUSNAVIGATION(args)
    94 #define LOGCONTENT(format, content)
    95 #define LOGCONTENTNAVIGATION(format, content)
    97 #endif
    99 struct nsDelayedBlurOrFocusEvent
   100 {
   101   nsDelayedBlurOrFocusEvent(uint32_t aType,
   102                             nsIPresShell* aPresShell,
   103                             nsIDocument* aDocument,
   104                             EventTarget* aTarget)
   105    : mType(aType),
   106      mPresShell(aPresShell),
   107      mDocument(aDocument),
   108      mTarget(aTarget) { }
   110   nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent& aOther)
   111    : mType(aOther.mType),
   112      mPresShell(aOther.mPresShell),
   113      mDocument(aOther.mDocument),
   114      mTarget(aOther.mTarget) { }
   116   uint32_t mType;
   117   nsCOMPtr<nsIPresShell> mPresShell;
   118   nsCOMPtr<nsIDocument> mDocument;
   119   nsCOMPtr<EventTarget> mTarget;
   120 };
   122 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager)
   123   NS_INTERFACE_MAP_ENTRY(nsIFocusManager)
   124   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   125   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   126   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager)
   127 NS_INTERFACE_MAP_END
   129 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager)
   130 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager)
   132 NS_IMPL_CYCLE_COLLECTION(nsFocusManager,
   133                          mActiveWindow,
   134                          mFocusedWindow,
   135                          mFocusedContent,
   136                          mFirstBlurEvent,
   137                          mFirstFocusEvent,
   138                          mWindowBeingLowered)
   140 nsFocusManager* nsFocusManager::sInstance = nullptr;
   141 bool nsFocusManager::sMouseFocusesFormControl = false;
   142 bool nsFocusManager::sTestMode = false;
   144 static const char* kObservedPrefs[] = {
   145   "accessibility.browsewithcaret",
   146   "accessibility.tabfocus_applies_to_xul",
   147   "accessibility.mouse_focuses_formcontrol",
   148   "focusmanager.testmode",
   149   nullptr
   150 };
   152 nsFocusManager::nsFocusManager()
   153 { }
   155 nsFocusManager::~nsFocusManager()
   156 {
   157   Preferences::RemoveObservers(this, kObservedPrefs);
   159   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   160   if (obs) {
   161     obs->RemoveObserver(this, "xpcom-shutdown");
   162   }
   163 }
   165 // static
   166 nsresult
   167 nsFocusManager::Init()
   168 {
   169   nsFocusManager* fm = new nsFocusManager();
   170   NS_ENSURE_TRUE(fm, NS_ERROR_OUT_OF_MEMORY);
   171   NS_ADDREF(fm);
   172   sInstance = fm;
   174 #ifdef PR_LOGGING
   175   gFocusLog = PR_NewLogModule("Focus");
   176   gFocusNavigationLog = PR_NewLogModule("FocusNavigation");
   177 #endif
   179   nsIContent::sTabFocusModelAppliesToXUL =
   180     Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
   181                          nsIContent::sTabFocusModelAppliesToXUL);
   183   sMouseFocusesFormControl =
   184     Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false);
   186   sTestMode = Preferences::GetBool("focusmanager.testmode", false);
   188   Preferences::AddWeakObservers(fm, kObservedPrefs);
   190   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   191   if (obs) {
   192     obs->AddObserver(fm, "xpcom-shutdown", true);
   193   }
   195   return NS_OK;
   196 }
   198 // static
   199 void
   200 nsFocusManager::Shutdown()
   201 {
   202   NS_IF_RELEASE(sInstance);
   203 }
   205 NS_IMETHODIMP
   206 nsFocusManager::Observe(nsISupports *aSubject,
   207                         const char *aTopic,
   208                         const char16_t *aData)
   209 {
   210   if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
   211     nsDependentString data(aData);
   212     if (data.EqualsLiteral("accessibility.browsewithcaret")) {
   213       UpdateCaretForCaretBrowsingMode();
   214     }
   215     else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
   216       nsIContent::sTabFocusModelAppliesToXUL =
   217         Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
   218                              nsIContent::sTabFocusModelAppliesToXUL);
   219     }
   220     else if (data.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
   221       sMouseFocusesFormControl =
   222         Preferences::GetBool("accessibility.mouse_focuses_formcontrol",
   223                              false);
   224     }
   225     else if (data.EqualsLiteral("focusmanager.testmode")) {
   226       sTestMode = Preferences::GetBool("focusmanager.testmode", false);
   227     }
   228   } else if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
   229     mActiveWindow = nullptr;
   230     mFocusedWindow = nullptr;
   231     mFocusedContent = nullptr;
   232     mFirstBlurEvent = nullptr;
   233     mFirstFocusEvent = nullptr;
   234     mWindowBeingLowered = nullptr;
   235     mDelayedBlurFocusEvents.Clear();
   236     mMouseDownEventHandlingDocument = nullptr;
   237   }
   239   return NS_OK;
   240 }
   242 // given a frame content node, retrieve the nsIDOMWindow displayed in it 
   243 static nsPIDOMWindow*
   244 GetContentWindow(nsIContent* aContent)
   245 {
   246   nsIDocument* doc = aContent->GetCurrentDoc();
   247   if (doc) {
   248     nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
   249     if (subdoc)
   250       return subdoc->GetWindow();
   251   }
   253   return nullptr;
   254 }
   256 // get the current window for the given content node 
   257 static nsPIDOMWindow*
   258 GetCurrentWindow(nsIContent* aContent)
   259 {
   260   nsIDocument *doc = aContent->GetCurrentDoc();
   261   return doc ? doc->GetWindow() : nullptr;
   262 }
   264 // static
   265 nsIContent*
   266 nsFocusManager::GetFocusedDescendant(nsPIDOMWindow* aWindow, bool aDeep,
   267                                      nsPIDOMWindow** aFocusedWindow)
   268 {
   269   NS_ENSURE_TRUE(aWindow, nullptr);
   271   *aFocusedWindow = nullptr;
   273   nsIContent* currentContent = nullptr;
   274   nsPIDOMWindow* window = aWindow->GetOuterWindow();
   275   while (window) {
   276     *aFocusedWindow = window;
   277     currentContent = window->GetFocusedNode();
   278     if (!currentContent || !aDeep)
   279       break;
   281     window = GetContentWindow(currentContent);
   282   }
   284   NS_IF_ADDREF(*aFocusedWindow);
   286   return currentContent;
   287 }
   289 // static
   290 nsIContent*
   291 nsFocusManager::GetRedirectedFocus(nsIContent* aContent)
   292 {
   293 #ifdef MOZ_XUL
   294   if (aContent->IsXUL()) {
   295     nsCOMPtr<nsIDOMNode> inputField;
   297     nsCOMPtr<nsIDOMXULTextBoxElement> textbox = do_QueryInterface(aContent);
   298     if (textbox) {
   299       textbox->GetInputField(getter_AddRefs(inputField));
   300     }
   301     else {
   302       nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aContent);
   303       if (menulist) {
   304         menulist->GetInputField(getter_AddRefs(inputField));
   305       }
   306       else if (aContent->Tag() == nsGkAtoms::scale) {
   307         nsCOMPtr<nsIDocument> doc = aContent->GetCurrentDoc();
   308         if (!doc)
   309           return nullptr;
   311         nsINodeList* children = doc->BindingManager()->GetAnonymousNodesFor(aContent);
   312         if (children) {
   313           nsIContent* child = children->Item(0);
   314           if (child && child->Tag() == nsGkAtoms::slider)
   315             return child;
   316         }
   317       }
   318     }
   320     if (inputField) {
   321       nsCOMPtr<nsIContent> retval = do_QueryInterface(inputField);
   322       return retval;
   323     }
   324   }
   325 #endif
   327   return nullptr;
   328 }
   330 // static
   331 InputContextAction::Cause
   332 nsFocusManager::GetFocusMoveActionCause(uint32_t aFlags)
   333 {
   334   if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
   335     return InputContextAction::CAUSE_MOUSE;
   336   } else if (aFlags & nsIFocusManager::FLAG_BYKEY) {
   337     return InputContextAction::CAUSE_KEY;
   338   }
   339   return InputContextAction::CAUSE_UNKNOWN;
   340 }
   342 NS_IMETHODIMP
   343 nsFocusManager::GetActiveWindow(nsIDOMWindow** aWindow)
   344 {
   345   NS_IF_ADDREF(*aWindow = mActiveWindow);
   346   return NS_OK;
   347 }
   349 NS_IMETHODIMP
   350 nsFocusManager::SetActiveWindow(nsIDOMWindow* aWindow)
   351 {
   352   // only top-level windows can be made active
   353   nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aWindow);
   354   if (piWindow)
   355       piWindow = piWindow->GetOuterWindow();
   357   NS_ENSURE_TRUE(piWindow && (piWindow == piWindow->GetPrivateRoot()),
   358                  NS_ERROR_INVALID_ARG);
   360   RaiseWindow(piWindow);
   361   return NS_OK;
   362 }
   364 NS_IMETHODIMP
   365 nsFocusManager::GetFocusedWindow(nsIDOMWindow** aFocusedWindow)
   366 {
   367   NS_IF_ADDREF(*aFocusedWindow = mFocusedWindow);
   368   return NS_OK;
   369 }
   371 NS_IMETHODIMP nsFocusManager::SetFocusedWindow(nsIDOMWindow* aWindowToFocus)
   372 {
   373   LOGFOCUS(("<<SetFocusedWindow begin>>"));
   375   nsCOMPtr<nsPIDOMWindow> windowToFocus(do_QueryInterface(aWindowToFocus));
   376   NS_ENSURE_TRUE(windowToFocus, NS_ERROR_FAILURE);
   378   windowToFocus = windowToFocus->GetOuterWindow();
   380   nsCOMPtr<nsIContent> frameContent =
   381     do_QueryInterface(windowToFocus->GetFrameElementInternal());
   382   if (frameContent) {
   383     // pass false for aFocusChanged so that the caret does not get updated
   384     // and scrolling does not occur.
   385     SetFocusInner(frameContent, 0, false, true);
   386   }
   387   else {
   388     // this is a top-level window. If the window has a child frame focused,
   389     // clear the focus. Otherwise, focus should already be in this frame, or
   390     // already cleared. This ensures that focus will be in this frame and not
   391     // in a child.
   392     nsIContent* content = windowToFocus->GetFocusedNode();
   393     if (content) {
   394       nsCOMPtr<nsIDOMWindow> childWindow = GetContentWindow(content);
   395       if (childWindow)
   396         ClearFocus(windowToFocus);
   397     }
   398   }
   400   nsCOMPtr<nsPIDOMWindow> rootWindow = windowToFocus->GetPrivateRoot();
   401   if (rootWindow)
   402     RaiseWindow(rootWindow);
   404   LOGFOCUS(("<<SetFocusedWindow end>>"));
   406   return NS_OK;
   407 }
   409 NS_IMETHODIMP
   410 nsFocusManager::GetFocusedElement(nsIDOMElement** aFocusedElement)
   411 {
   412   if (mFocusedContent)
   413     CallQueryInterface(mFocusedContent, aFocusedElement);
   414   else
   415     *aFocusedElement = nullptr;
   416   return NS_OK;
   417 }
   419 NS_IMETHODIMP
   420 nsFocusManager::GetLastFocusMethod(nsIDOMWindow* aWindow, uint32_t* aLastFocusMethod)
   421 {
   422   // the focus method is stored on the inner window
   423   nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   424   if (window)
   425     window = window->GetCurrentInnerWindow();
   426   if (!window)
   427     window = mFocusedWindow;
   429   *aLastFocusMethod = window ? window->GetFocusMethod() : 0;
   431   NS_ASSERTION((*aLastFocusMethod & FOCUSMETHOD_MASK) == *aLastFocusMethod,
   432                "invalid focus method");
   433   return NS_OK;
   434 }
   436 NS_IMETHODIMP
   437 nsFocusManager::SetFocus(nsIDOMElement* aElement, uint32_t aFlags)
   438 {
   439   LOGFOCUS(("<<SetFocus begin>>"));
   441   nsCOMPtr<nsIContent> newFocus = do_QueryInterface(aElement);
   442   NS_ENSURE_ARG(newFocus);
   444   SetFocusInner(newFocus, aFlags, true, true);
   446   LOGFOCUS(("<<SetFocus end>>"));
   448   return NS_OK;
   449 }
   451 NS_IMETHODIMP
   452 nsFocusManager::ElementIsFocusable(nsIDOMElement* aElement, uint32_t aFlags,
   453                                    bool* aIsFocusable)
   454 {
   455   NS_ENSURE_TRUE(aElement, NS_ERROR_INVALID_ARG);
   457   nsCOMPtr<nsIContent> aContent = do_QueryInterface(aElement);
   459   *aIsFocusable = CheckIfFocusable(aContent, aFlags) != nullptr;
   461   return NS_OK;
   462 }
   464 NS_IMETHODIMP
   465 nsFocusManager::MoveFocus(nsIDOMWindow* aWindow, nsIDOMElement* aStartElement,
   466                           uint32_t aType, uint32_t aFlags, nsIDOMElement** aElement)
   467 {
   468   *aElement = nullptr;
   470 #ifdef PR_LOGGING
   471   LOGFOCUS(("<<MoveFocus begin Type: %d Flags: %x>>", aType, aFlags));
   473   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG) && mFocusedWindow) {
   474     nsIDocument* doc = mFocusedWindow->GetExtantDoc();
   475     if (doc && doc->GetDocumentURI()) {
   476       nsAutoCString spec;
   477       doc->GetDocumentURI()->GetSpec(spec);
   478       LOGFOCUS((" Focused Window: %p %s", mFocusedWindow.get(), spec.get()));
   479     }
   480   }
   482   LOGCONTENT("  Current Focus: %s", mFocusedContent.get());
   483 #endif
   485   // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
   486   // the other focus methods is already set, or we're just moving to the root
   487   // or caret position.
   488   if (aType != MOVEFOCUS_ROOT && aType != MOVEFOCUS_CARET &&
   489       (aFlags & FOCUSMETHOD_MASK) == 0) {
   490     aFlags |= FLAG_BYMOVEFOCUS;
   491   }
   493   nsCOMPtr<nsPIDOMWindow> window;
   494   nsCOMPtr<nsIContent> startContent;
   495   if (aStartElement) {
   496     startContent = do_QueryInterface(aStartElement);
   497     NS_ENSURE_TRUE(startContent, NS_ERROR_INVALID_ARG);
   499     window = GetCurrentWindow(startContent);
   500   }
   501   else {
   502     window = aWindow ? do_QueryInterface(aWindow) : mFocusedWindow;
   503     NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   504     window = window->GetOuterWindow();
   505   }
   507   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   509   bool noParentTraversal = aFlags & FLAG_NOPARENTFRAME;
   510   nsCOMPtr<nsIContent> newFocus;
   511   nsresult rv = DetermineElementToMoveFocus(window, startContent, aType, noParentTraversal,
   512                                             getter_AddRefs(newFocus));
   513   NS_ENSURE_SUCCESS(rv, rv);
   515   LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get());
   517   if (newFocus) {
   518     // for caret movement, pass false for the aFocusChanged argument,
   519     // otherwise the caret will end up moving to the focus position. This
   520     // would be a problem because the caret would move to the beginning of the
   521     // focused link making it impossible to navigate the caret over a link.
   522     SetFocusInner(newFocus, aFlags, aType != MOVEFOCUS_CARET, true);
   523     CallQueryInterface(newFocus, aElement);
   524   }
   525   else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) {
   526     // no content was found, so clear the focus for these two types.
   527     ClearFocus(window);
   528   }
   530   LOGFOCUS(("<<MoveFocus end>>"));
   532   return NS_OK;
   533 }
   535 NS_IMETHODIMP
   536 nsFocusManager::ClearFocus(nsIDOMWindow* aWindow)
   537 {
   538   LOGFOCUS(("<<ClearFocus begin>>"));
   540   // if the window to clear is the focused window or an ancestor of the
   541   // focused window, then blur the existing focused content. Otherwise, the
   542   // focus is somewhere else so just update the current node.
   543   nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   544   NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   546   window = window->GetOuterWindow();
   547   NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   549   if (IsSameOrAncestor(window, mFocusedWindow)) {
   550     bool isAncestor = (window != mFocusedWindow);
   551     if (Blur(window, nullptr, isAncestor, true)) {
   552       // if we are clearing the focus on an ancestor of the focused window,
   553       // the ancestor will become the new focused window, so focus it
   554       if (isAncestor)
   555         Focus(window, nullptr, 0, true, false, false, true);
   556     }
   557   }
   558   else {
   559     window->SetFocusedNode(nullptr);
   560   }
   562   LOGFOCUS(("<<ClearFocus end>>"));
   564   return NS_OK;
   565 }
   567 NS_IMETHODIMP
   568 nsFocusManager::GetFocusedElementForWindow(nsIDOMWindow* aWindow,
   569                                            bool aDeep,
   570                                            nsIDOMWindow** aFocusedWindow,
   571                                            nsIDOMElement** aElement)
   572 {
   573   *aElement = nullptr;
   574   if (aFocusedWindow)
   575     *aFocusedWindow = nullptr;
   577   nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   578   NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   580   window = window->GetOuterWindow();
   581   NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   583   nsCOMPtr<nsPIDOMWindow> focusedWindow;
   584   nsCOMPtr<nsIContent> focusedContent =
   585     GetFocusedDescendant(window, aDeep, getter_AddRefs(focusedWindow));
   586   if (focusedContent)
   587     CallQueryInterface(focusedContent, aElement);
   589   if (aFocusedWindow)
   590     NS_IF_ADDREF(*aFocusedWindow = focusedWindow);
   592   return NS_OK;
   593 }
   595 NS_IMETHODIMP
   596 nsFocusManager::MoveCaretToFocus(nsIDOMWindow* aWindow)
   597 {
   598   nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
   599   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
   600   if (dsti) {
   601     if (dsti->ItemType() != nsIDocShellTreeItem::typeChrome) {
   602       nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(dsti);
   603       NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
   605       // don't move the caret for editable documents
   606       bool isEditable;
   607       docShell->GetEditable(&isEditable);
   608       if (isEditable)
   609         return NS_OK;
   611       nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
   612       NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
   614       nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   615       nsCOMPtr<nsIContent> content = window->GetFocusedNode();
   616       if (content)
   617         MoveCaretToFocus(presShell, content);
   618     }
   619   }
   621   return NS_OK;
   622 }
   624 NS_IMETHODIMP
   625 nsFocusManager::WindowRaised(nsIDOMWindow* aWindow)
   626 {
   627   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   628   NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
   630 #ifdef PR_LOGGING
   631   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
   632     LOGFOCUS(("Window %p Raised [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
   633     nsAutoCString spec;
   634     nsIDocument* doc = window->GetExtantDoc();
   635     if (doc && doc->GetDocumentURI()) {
   636       doc->GetDocumentURI()->GetSpec(spec);
   637       LOGFOCUS(("  Raised Window: %p %s", aWindow, spec.get()));
   638     }
   639     if (mActiveWindow) {
   640       doc = mActiveWindow->GetExtantDoc();
   641       if (doc && doc->GetDocumentURI()) {
   642         doc->GetDocumentURI()->GetSpec(spec);
   643         LOGFOCUS(("  Active Window: %p %s", mActiveWindow.get(), spec.get()));
   644       }
   645     }
   646   }
   647 #endif
   649   if (mActiveWindow == window) {
   650     // The window is already active, so there is no need to focus anything,
   651     // but make sure that the right widget is focused. This is a special case
   652     // for Windows because when restoring a minimized window, a second
   653     // activation will occur and the top-level widget could be focused instead
   654     // of the child we want. We solve this by calling SetFocus to ensure that
   655     // what the focus manager thinks should be the current widget is actually
   656     // focused.
   657     EnsureCurrentWidgetFocused();
   658     return NS_OK;
   659   }
   661   // lower the existing window, if any. This shouldn't happen usually.
   662   if (mActiveWindow)
   663     WindowLowered(mActiveWindow);
   665   nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(aWindow));
   666   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(webnav));
   667   // If there's no docShellAsItem, this window must have been closed,
   668   // in that case there is no tree owner.
   669   NS_ENSURE_TRUE(docShellAsItem, NS_OK);
   671   // set this as the active window
   672   mActiveWindow = window;
   674   // ensure that the window is enabled and visible
   675   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   676   docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
   677   nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
   678   if (baseWindow) {
   679     bool isEnabled = true;
   680     if (NS_SUCCEEDED(baseWindow->GetEnabled(&isEnabled)) && !isEnabled) {
   681       return NS_ERROR_FAILURE;
   682     }
   684     if (!sTestMode) {
   685       baseWindow->SetVisibility(true);
   686     }
   687   }
   689   // inform the DOM window that it has activated, so that the active attribute
   690   // is updated on the window
   691   window->ActivateOrDeactivate(true);
   693   // send activate event
   694   nsContentUtils::DispatchTrustedEvent(window->GetExtantDoc(),
   695                                        window,
   696                                        NS_LITERAL_STRING("activate"),
   697                                        true, true, nullptr);
   699   // retrieve the last focused element within the window that was raised
   700   nsCOMPtr<nsPIDOMWindow> currentWindow;
   701   nsCOMPtr<nsIContent> currentFocus =
   702     GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
   704   NS_ASSERTION(currentWindow, "window raised with no window current");
   705   if (!currentWindow)
   706     return NS_OK;
   708   nsCOMPtr<nsIDocShell> currentDocShell = currentWindow->GetDocShell();
   710   nsCOMPtr<nsIPresShell> presShell = currentDocShell->GetPresShell();
   711   if (presShell) {
   712     // disable selection mousedown state on activation
   713     // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
   714     nsRefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
   715     frameSelection->SetMouseDownState(false);
   716   }
   718   Focus(currentWindow, currentFocus, 0, true, false, true, true);
   720   return NS_OK;
   721 }
   723 NS_IMETHODIMP
   724 nsFocusManager::WindowLowered(nsIDOMWindow* aWindow)
   725 {
   726   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   727   NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
   729 #ifdef PR_LOGGING
   730   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
   731     LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
   732     nsAutoCString spec;
   733     nsIDocument* doc = window->GetExtantDoc();
   734     if (doc && doc->GetDocumentURI()) {
   735       doc->GetDocumentURI()->GetSpec(spec);
   736       LOGFOCUS(("  Lowered Window: %s", spec.get()));
   737     }
   738     if (mActiveWindow) {
   739       doc = mActiveWindow->GetExtantDoc();
   740       if (doc && doc->GetDocumentURI()) {
   741         doc->GetDocumentURI()->GetSpec(spec);
   742         LOGFOCUS(("  Active Window: %s", spec.get()));
   743       }
   744     }
   745   }
   746 #endif
   748   if (mActiveWindow != window)
   749     return NS_OK;
   751   // clear the mouse capture as the active window has changed
   752   nsIPresShell::SetCapturingContent(nullptr, 0);
   754   // inform the DOM window that it has deactivated, so that the active
   755   // attribute is updated on the window
   756   window->ActivateOrDeactivate(false);
   758   // send deactivate event
   759   nsContentUtils::DispatchTrustedEvent(window->GetExtantDoc(),
   760                                        window,
   761                                        NS_LITERAL_STRING("deactivate"),
   762                                        true, true, nullptr);
   764   // keep track of the window being lowered, so that attempts to raise the
   765   // window can be prevented until we return. Otherwise, focus can get into
   766   // an unusual state.
   767   mWindowBeingLowered = mActiveWindow;
   768   mActiveWindow = nullptr;
   770   if (mFocusedWindow)
   771     Blur(nullptr, nullptr, true, true);
   773   mWindowBeingLowered = nullptr;
   775   return NS_OK;
   776 }
   778 nsresult
   779 nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
   780 {
   781   NS_ENSURE_ARG(aDocument);
   782   NS_ENSURE_ARG(aContent);
   784   nsPIDOMWindow *window = aDocument->GetWindow();
   785   if (!window)
   786     return NS_OK;
   788   // if the content is currently focused in the window, or is an ancestor
   789   // of the currently focused element, reset the focus within that window.
   790   nsIContent* content = window->GetFocusedNode();
   791   if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) {
   792     bool shouldShowFocusRing = window->ShouldShowFocusRing();
   793     window->SetFocusedNode(nullptr);
   795     // if this window is currently focused, clear the global focused
   796     // element as well, but don't fire any events.
   797     if (window == mFocusedWindow) {
   798       mFocusedContent = nullptr;
   799     }
   800     else {
   801       // Check if the node that was focused is an iframe or similar by looking
   802       // if it has a subdocument. This would indicate that this focused iframe
   803       // and its descendants will be going away. We will need to move the
   804       // focus somewhere else, so just clear the focus in the toplevel window
   805       // so that no element is focused.
   806       nsIDocument* subdoc = aDocument->GetSubDocumentFor(content);
   807       if (subdoc) {
   808         nsCOMPtr<nsISupports> container = subdoc->GetContainer();
   809         nsCOMPtr<nsPIDOMWindow> childWindow = do_GetInterface(container);
   810         if (childWindow && IsSameOrAncestor(childWindow, mFocusedWindow)) {
   811           ClearFocus(mActiveWindow);
   812         }
   813       }
   814     }
   816     NotifyFocusStateChange(content, shouldShowFocusRing, false);
   817   }
   819   return NS_OK;
   820 }
   822 NS_IMETHODIMP
   823 nsFocusManager::WindowShown(nsIDOMWindow* aWindow, bool aNeedsFocus)
   824 {
   825   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   826   NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   828   window = window->GetOuterWindow();
   830 #ifdef PR_LOGGING
   831   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
   832     LOGFOCUS(("Window %p Shown [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
   833     nsAutoCString spec;
   834     nsIDocument* doc = window->GetExtantDoc();
   835     if (doc && doc->GetDocumentURI()) {
   836       doc->GetDocumentURI()->GetSpec(spec);
   837       LOGFOCUS(("Shown Window: %s", spec.get()));
   838     }
   840     if (mFocusedWindow) {
   841       doc = mFocusedWindow->GetExtantDoc();
   842       if (doc && doc->GetDocumentURI()) {
   843         doc->GetDocumentURI()->GetSpec(spec);
   844         LOGFOCUS((" Focused Window: %s", spec.get()));
   845       }
   846     }
   847   }
   848 #endif
   850   if (mFocusedWindow != window)
   851     return NS_OK;
   853   if (aNeedsFocus) {
   854     nsCOMPtr<nsPIDOMWindow> currentWindow;
   855     nsCOMPtr<nsIContent> currentFocus =
   856       GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
   857     if (currentWindow)
   858       Focus(currentWindow, currentFocus, 0, true, false, false, true);
   859   }
   860   else {
   861     // Sometimes, an element in a window can be focused before the window is
   862     // visible, which would mean that the widget may not be properly focused.
   863     // When the window becomes visible, make sure the right widget is focused.
   864     EnsureCurrentWidgetFocused();
   865   }
   867   return NS_OK;
   868 }
   870 NS_IMETHODIMP
   871 nsFocusManager::WindowHidden(nsIDOMWindow* aWindow)
   872 {
   873   // if there is no window or it is not the same or an ancestor of the
   874   // currently focused window, just return, as the current focus will not
   875   // be affected.
   877   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   878   NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   880   window = window->GetOuterWindow();
   882 #ifdef PR_LOGGING
   883   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
   884     LOGFOCUS(("Window %p Hidden [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
   885     nsAutoCString spec;
   886     nsIDocument* doc = window->GetExtantDoc();
   887     if (doc && doc->GetDocumentURI()) {
   888       doc->GetDocumentURI()->GetSpec(spec);
   889       LOGFOCUS(("  Hide Window: %s", spec.get()));
   890     }
   892     if (mFocusedWindow) {
   893       doc = mFocusedWindow->GetExtantDoc();
   894       if (doc && doc->GetDocumentURI()) {
   895         doc->GetDocumentURI()->GetSpec(spec);
   896         LOGFOCUS(("  Focused Window: %s", spec.get()));
   897       }
   898     }
   900     if (mActiveWindow) {
   901       doc = mActiveWindow->GetExtantDoc();
   902       if (doc && doc->GetDocumentURI()) {
   903         doc->GetDocumentURI()->GetSpec(spec);
   904         LOGFOCUS(("  Active Window: %s", spec.get()));
   905       }
   906     }
   907   }
   908 #endif
   910   if (!IsSameOrAncestor(window, mFocusedWindow))
   911     return NS_OK;
   913   // at this point, we know that the window being hidden is either the focused
   914   // window, or an ancestor of the focused window. Either way, the focus is no
   915   // longer valid, so it needs to be updated.
   917   nsCOMPtr<nsIContent> oldFocusedContent = mFocusedContent.forget();
   919   nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
   920   nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
   922   if (oldFocusedContent && oldFocusedContent->IsInDoc()) {
   923     NotifyFocusStateChange(oldFocusedContent,
   924                            mFocusedWindow->ShouldShowFocusRing(),
   925                            false);
   926     window->UpdateCommands(NS_LITERAL_STRING("focus"));
   928     if (presShell) {
   929       SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell,
   930                            oldFocusedContent->GetCurrentDoc(),
   931                            oldFocusedContent, 1, false);
   932     }
   933   }
   935   nsPresContext* focusedPresContext =
   936     presShell ? presShell->GetPresContext() : nullptr;
   937   IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
   938                                  GetFocusMoveActionCause(0));
   939   if (presShell) {
   940     SetCaretVisible(presShell, false, nullptr);
   941   }
   943   // if the docshell being hidden is being destroyed, then we want to move
   944   // focus somewhere else. Call ClearFocus on the toplevel window, which
   945   // will have the effect of clearing the focus and moving the focused window
   946   // to the toplevel window. But if the window isn't being destroyed, we are
   947   // likely just loading a new document in it, so we want to maintain the
   948   // focused window so that the new document gets properly focused.
   949   bool beingDestroyed;
   950   nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell();
   951   docShellBeingHidden->IsBeingDestroyed(&beingDestroyed);
   952   if (beingDestroyed) {
   953     // There is usually no need to do anything if a toplevel window is going
   954     // away, as we assume that WindowLowered will be called. However, this may
   955     // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
   956     // a leak. So if the active window is being destroyed, call WindowLowered
   957     // directly.
   958     NS_ASSERTION(mFocusedWindow->IsOuterWindow(), "outer window expected");
   959     if (mActiveWindow == mFocusedWindow || mActiveWindow == window)
   960       WindowLowered(mActiveWindow);
   961     else
   962       ClearFocus(mActiveWindow);
   963     return NS_OK;
   964   }
   966   // if the window being hidden is an ancestor of the focused window, adjust
   967   // the focused window so that it points to the one being hidden. This
   968   // ensures that the focused window isn't in a chain of frames that doesn't
   969   // exist any more.
   970   if (window != mFocusedWindow) {
   971     nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(mFocusedWindow));
   972     nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
   973     if (dsti) {
   974       nsCOMPtr<nsIDocShellTreeItem> parentDsti;
   975       dsti->GetParent(getter_AddRefs(parentDsti));
   976       nsCOMPtr<nsPIDOMWindow> parentWindow = do_GetInterface(parentDsti);
   977       if (parentWindow)
   978         parentWindow->SetFocusedNode(nullptr);
   979     }
   981     SetFocusedWindowInternal(window);
   982   }
   984   return NS_OK;
   985 }
   987 NS_IMETHODIMP
   988 nsFocusManager::FireDelayedEvents(nsIDocument* aDocument)
   989 {
   990   NS_ENSURE_ARG(aDocument);
   992   // fire any delayed focus and blur events in the same order that they were added
   993   for (uint32_t i = 0; i < mDelayedBlurFocusEvents.Length(); i++) {
   994     if (mDelayedBlurFocusEvents[i].mDocument == aDocument) {
   995       if (!aDocument->GetInnerWindow() ||
   996           !aDocument->GetInnerWindow()->IsCurrentInnerWindow()) {
   997         // If the document was navigated away from or is defunct, don't bother
   998         // firing events on it. Note the symmetry between this condition and
   999         // the similar one in nsDocument.cpp:FireOrClearDelayedEvents.
  1000         mDelayedBlurFocusEvents.RemoveElementAt(i);
  1001         --i;
  1002       } else if (!aDocument->EventHandlingSuppressed()) {
  1003         uint32_t type = mDelayedBlurFocusEvents[i].mType;
  1004         nsCOMPtr<EventTarget> target = mDelayedBlurFocusEvents[i].mTarget;
  1005         nsCOMPtr<nsIPresShell> presShell = mDelayedBlurFocusEvents[i].mPresShell;
  1006         mDelayedBlurFocusEvents.RemoveElementAt(i);
  1007         SendFocusOrBlurEvent(type, presShell, aDocument, target, 0, false);
  1008         --i;
  1013   return NS_OK;
  1016 NS_IMETHODIMP
  1017 nsFocusManager::FocusPlugin(nsIContent* aContent)
  1019   NS_ENSURE_ARG(aContent);
  1020   SetFocusInner(aContent, 0, true, false);
  1021   return NS_OK;
  1024 /* static */
  1025 void
  1026 nsFocusManager::NotifyFocusStateChange(nsIContent* aContent,
  1027                                        bool aWindowShouldShowFocusRing,
  1028                                        bool aGettingFocus)
  1030   if (!aContent->IsElement()) {
  1031     return;
  1033   EventStates eventState = NS_EVENT_STATE_FOCUS;
  1034   if (aWindowShouldShowFocusRing) {
  1035     eventState |= NS_EVENT_STATE_FOCUSRING;
  1037   if (aGettingFocus) {
  1038     aContent->AsElement()->AddStates(eventState);
  1039   } else {
  1040     aContent->AsElement()->RemoveStates(eventState);
  1044 // static
  1045 void
  1046 nsFocusManager::EnsureCurrentWidgetFocused()
  1048   if (!mFocusedWindow || sTestMode)
  1049     return;
  1051   // get the main child widget for the focused window and ensure that the
  1052   // platform knows that this widget is focused.
  1053   nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
  1054   if (docShell) {
  1055     nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1056     if (presShell) {
  1057       nsViewManager* vm = presShell->GetViewManager();
  1058       if (vm) {
  1059         nsCOMPtr<nsIWidget> widget;
  1060         vm->GetRootWidget(getter_AddRefs(widget));
  1061         if (widget)
  1062           widget->SetFocus(false);
  1068 void
  1069 nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags,
  1070                               bool aFocusChanged, bool aAdjustWidget)
  1072   // if the element is not focusable, just return and leave the focus as is
  1073   nsCOMPtr<nsIContent> contentToFocus = CheckIfFocusable(aNewContent, aFlags);
  1074   if (!contentToFocus)
  1075     return;
  1077   // check if the element to focus is a frame (iframe) containing a child
  1078   // document. Frames are never directly focused; instead focusing a frame
  1079   // means focus what is inside the frame. To do this, the descendant content
  1080   // within the frame is retrieved and that will be focused instead.
  1081   nsCOMPtr<nsPIDOMWindow> newWindow;
  1082   nsCOMPtr<nsPIDOMWindow> subWindow = GetContentWindow(contentToFocus);
  1083   if (subWindow) {
  1084     contentToFocus = GetFocusedDescendant(subWindow, true, getter_AddRefs(newWindow));
  1085     // since a window is being refocused, clear aFocusChanged so that the
  1086     // caret position isn't updated.
  1087     aFocusChanged = false;
  1090   // unless it was set above, retrieve the window for the element to focus
  1091   if (!newWindow)
  1092     newWindow = GetCurrentWindow(contentToFocus);
  1094   // if the element is already focused, just return. Note that this happens
  1095   // after the frame check above so that we compare the element that will be
  1096   // focused rather than the frame it is in.
  1097   if (!newWindow || (newWindow == mFocusedWindow && contentToFocus == mFocusedContent))
  1098     return;
  1100   // don't allow focus to be placed in docshells or descendants of docshells
  1101   // that are being destroyed. Also, ensure that the page hasn't been
  1102   // unloaded. The prevents content from being refocused during an unload event.
  1103   nsCOMPtr<nsIDocShell> newDocShell = newWindow->GetDocShell();
  1104   nsCOMPtr<nsIDocShell> docShell = newDocShell;
  1105   while (docShell) {
  1106     bool inUnload;
  1107     docShell->GetIsInUnload(&inUnload);
  1108     if (inUnload)
  1109       return;
  1111     bool beingDestroyed;
  1112     docShell->IsBeingDestroyed(&beingDestroyed);
  1113     if (beingDestroyed)
  1114       return;
  1116     nsCOMPtr<nsIDocShellTreeItem> parentDsti;
  1117     docShell->GetParent(getter_AddRefs(parentDsti));
  1118     docShell = do_QueryInterface(parentDsti);
  1121   // if the new element is in the same window as the currently focused element 
  1122   bool isElementInFocusedWindow = (mFocusedWindow == newWindow);
  1124   if (!isElementInFocusedWindow && mFocusedWindow && newWindow &&
  1125       nsContentUtils::IsHandlingKeyBoardEvent()) {
  1126     nsCOMPtr<nsIScriptObjectPrincipal> focused =
  1127       do_QueryInterface(mFocusedWindow);
  1128     nsCOMPtr<nsIScriptObjectPrincipal> newFocus =
  1129       do_QueryInterface(newWindow);
  1130     nsIPrincipal* focusedPrincipal = focused->GetPrincipal();
  1131     nsIPrincipal* newPrincipal = newFocus->GetPrincipal();
  1132     if (!focusedPrincipal || !newPrincipal) {
  1133       return;
  1135     bool subsumes = false;
  1136     focusedPrincipal->Subsumes(newPrincipal, &subsumes);
  1137     if (!subsumes && !nsContentUtils::IsCallerChrome()) {
  1138       NS_WARNING("Not allowed to focus the new window!");
  1139       return;
  1143   // to check if the new element is in the active window, compare the
  1144   // new root docshell for the new element with the active window's docshell.
  1145   bool isElementInActiveWindow = false;
  1147   nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(newWindow);
  1148   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
  1149   nsCOMPtr<nsPIDOMWindow> newRootWindow;
  1150   if (dsti) {
  1151     nsCOMPtr<nsIDocShellTreeItem> root;
  1152     dsti->GetRootTreeItem(getter_AddRefs(root));
  1153     newRootWindow = do_GetInterface(root);
  1155     isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow);
  1158   // Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX
  1159   // system. We don't control event dispatch to windowed plugins on non-MacOSX,
  1160   // so we can't display the "Press ESC to leave fullscreen mode" warning on
  1161   // key input if a windowed plugin is focused, so just exit fullscreen
  1162   // to guard against phishing.
  1163 #ifndef XP_MACOSX
  1164   nsIDocument* fullscreenAncestor;
  1165   if (contentToFocus &&
  1166       (fullscreenAncestor = nsContentUtils::GetFullscreenAncestor(contentToFocus->OwnerDoc())) &&
  1167       nsContentUtils::HasPluginWithUncontrolledEventDispatch(contentToFocus)) {
  1168     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  1169                                     NS_LITERAL_CSTRING("DOM"),
  1170                                     contentToFocus->OwnerDoc(),
  1171                                     nsContentUtils::eDOM_PROPERTIES,
  1172                                     "FocusedWindowedPluginWhileFullScreen");
  1173     nsIDocument::ExitFullscreen(fullscreenAncestor, /* async */ true);
  1175 #endif
  1177   // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
  1178   // shifted away from the current element if the new shell to focus is
  1179   // the same or an ancestor shell of the currently focused shell.
  1180   bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
  1181                             IsSameOrAncestor(newWindow, mFocusedWindow);
  1183   // if the element is in the active window, frame switching is allowed and
  1184   // the content is in a visible window, fire blur and focus events.
  1185   bool sendFocusEvent =
  1186     isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow);
  1188   // When the following conditions are true:
  1189   //  * an element has focus
  1190   //  * isn't called by trusted event (i.e., called by untrusted event or by js)
  1191   //  * the focus is moved to another document's element
  1192   // we need to check the permission.
  1193   if (sendFocusEvent && mFocusedContent &&
  1194       mFocusedContent->OwnerDoc() != aNewContent->OwnerDoc()) {
  1195     // If the caller cannot access the current focused node, the caller should
  1196     // not be able to steal focus from it. E.g., When the current focused node
  1197     // is in chrome, any web contents should not be able to steal the focus.
  1198     nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mFocusedContent));
  1199     sendFocusEvent = nsContentUtils::CanCallerAccess(domNode);
  1200     if (!sendFocusEvent && mMouseDownEventHandlingDocument) {
  1201       // However, while mouse down event is handling, the handling document's
  1202       // script should be able to steal focus.
  1203       domNode = do_QueryInterface(mMouseDownEventHandlingDocument);
  1204       sendFocusEvent = nsContentUtils::CanCallerAccess(domNode);
  1208   LOGCONTENT("Shift Focus: %s", contentToFocus.get());
  1209   LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p",
  1210            aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedContent.get()));
  1211   LOGFOCUS((" In Active Window: %d In Focused Window: %d SendFocus: %d",
  1212            isElementInActiveWindow, isElementInFocusedWindow, sendFocusEvent));
  1214   if (sendFocusEvent) {
  1215     // return if blurring fails or the focus changes during the blur
  1216     if (mFocusedWindow) {
  1217       // if the focus is being moved to another element in the same document,
  1218       // or to a descendant, pass the existing window to Blur so that the
  1219       // current node in the existing window is cleared. If moving to a
  1220       // window elsewhere, we want to maintain the current node in the
  1221       // window but still blur it.
  1222       bool currentIsSameOrAncestor = IsSameOrAncestor(mFocusedWindow, newWindow);
  1223       // find the common ancestor of the currently focused window and the new
  1224       // window. The ancestor will need to have its currently focused node
  1225       // cleared once the document has been blurred. Otherwise, we'll be in a
  1226       // state where a document is blurred yet the chain of windows above it
  1227       // still points to that document.
  1228       // For instance, in the following frame tree:
  1229       //   A
  1230       //  B C
  1231       //  D
  1232       // D is focused and we want to focus C. Once D has been blurred, we need
  1233       // to clear out the focus in A, otherwise A would still maintain that B
  1234       // was focused, and B that D was focused.
  1235       nsCOMPtr<nsPIDOMWindow> commonAncestor;
  1236       if (!isElementInFocusedWindow)
  1237         commonAncestor = GetCommonAncestor(newWindow, mFocusedWindow);
  1239       if (!Blur(currentIsSameOrAncestor ? mFocusedWindow.get() : nullptr,
  1240                 commonAncestor, !isElementInFocusedWindow, aAdjustWidget))
  1241         return;
  1244     Focus(newWindow, contentToFocus, aFlags, !isElementInFocusedWindow,
  1245           aFocusChanged, false, aAdjustWidget);
  1247   else {
  1248     // otherwise, for inactive windows and when the caller cannot steal the
  1249     // focus, update the node in the window, and  raise the window if desired.
  1250     if (allowFrameSwitch)
  1251       AdjustWindowFocus(newWindow, true);
  1253     // set the focus node and method as needed
  1254     uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
  1255                            newWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
  1256     newWindow->SetFocusedNode(contentToFocus, focusMethod);
  1257     if (aFocusChanged) {
  1258       nsCOMPtr<nsIDocShell> docShell = newWindow->GetDocShell();
  1260       nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1261       if (presShell)
  1262         ScrollIntoView(presShell, contentToFocus, aFlags);
  1265     // update the commands even when inactive so that the attributes for that
  1266     // window are up to date.
  1267     if (allowFrameSwitch)
  1268       newWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
  1270     if (aFlags & FLAG_RAISE)
  1271       RaiseWindow(newRootWindow);
  1275 bool
  1276 nsFocusManager::IsSameOrAncestor(nsPIDOMWindow* aPossibleAncestor,
  1277                                  nsPIDOMWindow* aWindow)
  1279   nsCOMPtr<nsIWebNavigation> awebnav(do_GetInterface(aPossibleAncestor));
  1280   nsCOMPtr<nsIDocShellTreeItem> ancestordsti = do_QueryInterface(awebnav);
  1282   nsCOMPtr<nsIWebNavigation> fwebnav(do_GetInterface(aWindow));
  1283   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(fwebnav);
  1284   while (dsti) {
  1285     if (dsti == ancestordsti)
  1286       return true;
  1287     nsCOMPtr<nsIDocShellTreeItem> parentDsti;
  1288     dsti->GetParent(getter_AddRefs(parentDsti));
  1289     dsti.swap(parentDsti);
  1292   return false;
  1295 already_AddRefed<nsPIDOMWindow>
  1296 nsFocusManager::GetCommonAncestor(nsPIDOMWindow* aWindow1,
  1297                                   nsPIDOMWindow* aWindow2)
  1299   nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(aWindow1));
  1300   nsCOMPtr<nsIDocShellTreeItem> dsti1 = do_QueryInterface(webnav);
  1301   NS_ENSURE_TRUE(dsti1, nullptr);
  1303   webnav = do_GetInterface(aWindow2);
  1304   nsCOMPtr<nsIDocShellTreeItem> dsti2 = do_QueryInterface(webnav);
  1305   NS_ENSURE_TRUE(dsti2, nullptr);
  1307   nsAutoTArray<nsIDocShellTreeItem*, 30> parents1, parents2;
  1308   do {
  1309     parents1.AppendElement(dsti1);
  1310     nsCOMPtr<nsIDocShellTreeItem> parentDsti1;
  1311     dsti1->GetParent(getter_AddRefs(parentDsti1));
  1312     dsti1.swap(parentDsti1);
  1313   } while (dsti1);
  1314   do {
  1315     parents2.AppendElement(dsti2);
  1316     nsCOMPtr<nsIDocShellTreeItem> parentDsti2;
  1317     dsti2->GetParent(getter_AddRefs(parentDsti2));
  1318     dsti2.swap(parentDsti2);
  1319   } while (dsti2);
  1321   uint32_t pos1 = parents1.Length();
  1322   uint32_t pos2 = parents2.Length();
  1323   nsIDocShellTreeItem* parent = nullptr;
  1324   uint32_t len;
  1325   for (len = std::min(pos1, pos2); len > 0; --len) {
  1326     nsIDocShellTreeItem* child1 = parents1.ElementAt(--pos1);
  1327     nsIDocShellTreeItem* child2 = parents2.ElementAt(--pos2);
  1328     if (child1 != child2) {
  1329       break;
  1331     parent = child1;
  1334   nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(parent);
  1335   return window.forget();
  1338 void
  1339 nsFocusManager::AdjustWindowFocus(nsPIDOMWindow* aWindow,
  1340                                   bool aCheckPermission)
  1342   bool isVisible = IsWindowVisible(aWindow);
  1344   nsCOMPtr<nsPIDOMWindow> window(aWindow);
  1345   while (window) {
  1346     // get the containing <iframe> or equivalent element so that it can be
  1347     // focused below.
  1348     nsCOMPtr<nsIContent> frameContent =
  1349       do_QueryInterface(window->GetFrameElementInternal());
  1351     nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(window));
  1352     nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
  1353     if (!dsti) 
  1354       return;
  1355     nsCOMPtr<nsIDocShellTreeItem> parentDsti;
  1356     dsti->GetParent(getter_AddRefs(parentDsti));
  1358     window = do_GetInterface(parentDsti);
  1359     if (window) {
  1360       // if the parent window is visible but aWindow was not, then we have
  1361       // likely moved up and out from a hidden tab to the browser window, or a
  1362       // similar such arrangement. Stop adjusting the current nodes.
  1363       if (IsWindowVisible(window) != isVisible)
  1364         break;
  1366       // When aCheckPermission is true, we should check whether the caller can
  1367       // access the window or not.  If it cannot access, we should stop the
  1368       // adjusting.
  1369       if (aCheckPermission && !nsContentUtils::CanCallerAccess(window))
  1370         break;
  1372       window->SetFocusedNode(frameContent);
  1377 bool
  1378 nsFocusManager::IsWindowVisible(nsPIDOMWindow* aWindow)
  1380   if (!aWindow || aWindow->IsFrozen())
  1381     return false;
  1383   // Check if the inner window is frozen as well. This can happen when a focus change
  1384   // occurs while restoring a previous page.
  1385   nsPIDOMWindow* innerWindow = aWindow->GetCurrentInnerWindow();
  1386   if (!innerWindow || innerWindow->IsFrozen())
  1387     return false;
  1389   nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  1390   nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(docShell));
  1391   if (!baseWin)
  1392     return false;
  1394   bool visible = false;
  1395   baseWin->GetVisibility(&visible);
  1396   return visible;
  1399 bool
  1400 nsFocusManager::IsNonFocusableRoot(nsIContent* aContent)
  1402   NS_PRECONDITION(aContent, "aContent must not be NULL");
  1403   NS_PRECONDITION(aContent->IsInDoc(), "aContent must be in a document");
  1405   // If aContent is in designMode, the root element is not focusable.
  1406   // NOTE: in designMode, most elements are not focusable, just the document is
  1407   //       focusable.
  1408   // Also, if aContent is not editable but it isn't in designMode, it's not
  1409   // focusable.
  1410   // And in userfocusignored context nothing is focusable.
  1411   nsIDocument* doc = aContent->GetCurrentDoc();
  1412   NS_ASSERTION(doc, "aContent must have current document");
  1413   return aContent == doc->GetRootElement() &&
  1414            (doc->HasFlag(NODE_IS_EDITABLE) || !aContent->IsEditable() ||
  1415             nsContentUtils::IsUserFocusIgnored(aContent));
  1418 nsIContent*
  1419 nsFocusManager::CheckIfFocusable(nsIContent* aContent, uint32_t aFlags)
  1421   if (!aContent)
  1422     return nullptr;
  1424   // this is a special case for some XUL elements where an anonymous child is
  1425   // actually focusable and not the element itself.
  1426   nsIContent* redirectedFocus = GetRedirectedFocus(aContent);
  1427   if (redirectedFocus)
  1428     return CheckIfFocusable(redirectedFocus, aFlags);
  1430   nsCOMPtr<nsIDocument> doc = aContent->GetCurrentDoc();
  1431   // can't focus elements that are not in documents
  1432   if (!doc) {
  1433     LOGCONTENT("Cannot focus %s because content not in document", aContent)
  1434     return nullptr;
  1437   // Make sure that our frames are up to date
  1438   doc->FlushPendingNotifications(Flush_Layout);
  1440   nsIPresShell *shell = doc->GetShell();
  1441   if (!shell)
  1442     return nullptr;
  1444   // the root content can always be focused,
  1445   // except in userfocusignored context.
  1446   if (aContent == doc->GetRootElement())
  1447     return nsContentUtils::IsUserFocusIgnored(aContent) ? nullptr : aContent;
  1449   // cannot focus content in print preview mode. Only the root can be focused.
  1450   nsPresContext* presContext = shell->GetPresContext();
  1451   if (presContext && presContext->Type() == nsPresContext::eContext_PrintPreview) {
  1452     LOGCONTENT("Cannot focus %s while in print preview", aContent)
  1453     return nullptr;
  1456   nsIFrame* frame = aContent->GetPrimaryFrame();
  1457   if (!frame) {
  1458     LOGCONTENT("Cannot focus %s as it has no frame", aContent)
  1459     return nullptr;
  1462   if (aContent->Tag() == nsGkAtoms::area && aContent->IsHTML()) {
  1463     // HTML areas do not have their own frame, and the img frame we get from
  1464     // GetPrimaryFrame() is not relevant as to whether it is focusable or
  1465     // not, so we have to do all the relevant checks manually for them.
  1466     return frame->IsVisibleConsideringAncestors() &&
  1467            aContent->IsFocusable() ? aContent : nullptr;
  1470   // if this is a child frame content node, check if it is visible and
  1471   // call the content node's IsFocusable method instead of the frame's
  1472   // IsFocusable method. This skips checking the style system and ensures that
  1473   // offscreen browsers can still be focused.
  1474   nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
  1475   if (subdoc && IsWindowVisible(subdoc->GetWindow())) {
  1476     const nsStyleUserInterface* ui = frame->StyleUserInterface();
  1477     int32_t tabIndex = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE ||
  1478                         ui->mUserFocus == NS_STYLE_USER_FOCUS_NONE) ? -1 : 0;
  1479     return aContent->IsFocusable(&tabIndex, aFlags & FLAG_BYMOUSE) ? aContent : nullptr;
  1482   return frame->IsFocusable(nullptr, aFlags & FLAG_BYMOUSE) ? aContent : nullptr;
  1485 bool
  1486 nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
  1487                      nsPIDOMWindow* aAncestorWindowToFocus,
  1488                      bool aIsLeavingDocument,
  1489                      bool aAdjustWidgets)
  1491   LOGFOCUS(("<<Blur begin>>"));
  1493   // hold a reference to the focused content, which may be null
  1494   nsCOMPtr<nsIContent> content = mFocusedContent;
  1495   if (content) {
  1496     if (!content->IsInDoc()) {
  1497       mFocusedContent = nullptr;
  1498       return true;
  1500     if (content == mFirstBlurEvent)
  1501       return true;
  1504   // hold a reference to the focused window
  1505   nsCOMPtr<nsPIDOMWindow> window = mFocusedWindow;
  1506   if (!window) {
  1507     mFocusedContent = nullptr;
  1508     return true;
  1511   nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
  1512   if (!docShell) {
  1513     mFocusedContent = nullptr;
  1514     return true;
  1517   // Keep a ref to presShell since dispatching the DOM event may cause
  1518   // the document to be destroyed.
  1519   nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1520   if (!presShell) {
  1521     mFocusedContent = nullptr;
  1522     return true;
  1525   bool clearFirstBlurEvent = false;
  1526   if (!mFirstBlurEvent) {
  1527     mFirstBlurEvent = content;
  1528     clearFirstBlurEvent = true;
  1531   nsPresContext* focusedPresContext =
  1532     mActiveWindow ? presShell->GetPresContext() : nullptr;
  1533   IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
  1534                                  GetFocusMoveActionCause(0));
  1536   // now adjust the actual focus, by clearing the fields in the focus manager
  1537   // and in the window.
  1538   mFocusedContent = nullptr;
  1539   bool shouldShowFocusRing = window->ShouldShowFocusRing();
  1540   if (aWindowToClear)
  1541     aWindowToClear->SetFocusedNode(nullptr);
  1543   LOGCONTENT("Element %s has been blurred", content.get());
  1545   // Don't fire blur event on the root content which isn't editable.
  1546   bool sendBlurEvent =
  1547     content && content->IsInDoc() && !IsNonFocusableRoot(content);
  1548   if (content) {
  1549     if (sendBlurEvent) {
  1550       NotifyFocusStateChange(content, shouldShowFocusRing, false);
  1553     // if an object/plug-in/remote browser is being blurred, move the system focus
  1554     // to the parent window, otherwise events will still get fired at the plugin.
  1555     // But don't do this if we are blurring due to the window being lowered,
  1556     // otherwise, the parent window can get raised again.
  1557     if (mActiveWindow) {
  1558       nsIFrame* contentFrame = content->GetPrimaryFrame();
  1559       nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
  1560       if (aAdjustWidgets && objectFrame && !sTestMode) {
  1561         // note that the presshell's widget is being retrieved here, not the one
  1562         // for the object frame.
  1563         nsViewManager* vm = presShell->GetViewManager();
  1564         if (vm) {
  1565           nsCOMPtr<nsIWidget> widget;
  1566           vm->GetRootWidget(getter_AddRefs(widget));
  1567           if (widget)
  1568             widget->SetFocus(false);
  1572       // if the object being blurred is a remote browser, deactivate remote content
  1573       if (TabParent* remote = TabParent::GetFrom(content)) {
  1574         remote->Deactivate();
  1575         LOGFOCUS(("Remote browser deactivated"));
  1580   bool result = true;
  1581   if (sendBlurEvent) {
  1582     // if there is an active window, update commands. If there isn't an active
  1583     // window, then this was a blur caused by the active window being lowered,
  1584     // so there is no need to update the commands
  1585     if (mActiveWindow)
  1586       window->UpdateCommands(NS_LITERAL_STRING("focus"));
  1588     SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell,
  1589                          content->GetCurrentDoc(), content, 1, false);
  1592   // if we are leaving the document or the window was lowered, make the caret
  1593   // invisible.
  1594   if (aIsLeavingDocument || !mActiveWindow)
  1595     SetCaretVisible(presShell, false, nullptr);
  1597   // at this point, it is expected that this window will be still be
  1598   // focused, but the focused content will be null, as it was cleared before
  1599   // the event. If this isn't the case, then something else was focused during
  1600   // the blur event above and we should just return. However, if
  1601   // aIsLeavingDocument is set, a new document is desired, so make sure to
  1602   // blur the document and window.
  1603   if (mFocusedWindow != window ||
  1604       (mFocusedContent != nullptr && !aIsLeavingDocument)) {
  1605     result = false;
  1607   else if (aIsLeavingDocument) {
  1608     window->TakeFocus(false, 0);
  1610     // clear the focus so that the ancestor frame hierarchy is in the correct
  1611     // state. Pass true because aAncestorWindowToFocus is thought to be
  1612     // focused at this point.
  1613     if (aAncestorWindowToFocus)
  1614       aAncestorWindowToFocus->SetFocusedNode(nullptr, 0, true);
  1616     SetFocusedWindowInternal(nullptr);
  1617     mFocusedContent = nullptr;
  1619     // pass 1 for the focus method when calling SendFocusOrBlurEvent just so
  1620     // that the check is made for suppressed documents. Check to ensure that
  1621     // the document isn't null in case someone closed it during the blur above
  1622     nsIDocument* doc = window->GetExtantDoc();
  1623     if (doc)
  1624       SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell, doc, doc, 1, false);
  1625     if (mFocusedWindow == nullptr)
  1626       SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell, doc, window, 1, false);
  1628     // check if a different window was focused
  1629     result = (mFocusedWindow == nullptr && mActiveWindow);
  1631   else if (mActiveWindow) {
  1632     // Otherwise, the blur of the element without blurring the document
  1633     // occurred normally. Call UpdateCaret to redisplay the caret at the right
  1634     // location within the document. This is needed to ensure that the caret
  1635     // used for caret browsing is made visible again when an input field is
  1636     // blurred.
  1637     UpdateCaret(false, true, nullptr);
  1640   if (clearFirstBlurEvent)
  1641     mFirstBlurEvent = nullptr;
  1643   return result;
  1646 void
  1647 nsFocusManager::Focus(nsPIDOMWindow* aWindow,
  1648                       nsIContent* aContent,
  1649                       uint32_t aFlags,
  1650                       bool aIsNewDocument,
  1651                       bool aFocusChanged,
  1652                       bool aWindowRaised,
  1653                       bool aAdjustWidgets)
  1655   LOGFOCUS(("<<Focus begin>>"));
  1657   if (!aWindow)
  1658     return;
  1660   if (aContent && (aContent == mFirstFocusEvent || aContent == mFirstBlurEvent))
  1661     return;
  1663   // Keep a reference to the presShell since dispatching the DOM event may
  1664   // cause the document to be destroyed.
  1665   nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  1666   if (!docShell)
  1667     return;
  1669   nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1670   if (!presShell)
  1671     return;
  1673   // If the focus actually changed, set the focus method (mouse, keyboard, etc).
  1674   // Otherwise, just get the current focus method and use that. This ensures
  1675   // that the method is set during the document and window focus events.
  1676   uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
  1677                          aWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
  1679   if (!IsWindowVisible(aWindow)) {
  1680     // if the window isn't visible, for instance because it is a hidden tab,
  1681     // update the current focus and scroll it into view but don't do anything else
  1682     if (CheckIfFocusable(aContent, aFlags)) {
  1683       aWindow->SetFocusedNode(aContent, focusMethod);
  1684       if (aFocusChanged)
  1685         ScrollIntoView(presShell, aContent, aFlags);
  1687     return;
  1690   bool clearFirstFocusEvent = false;
  1691   if (!mFirstFocusEvent) {
  1692     mFirstFocusEvent = aContent;
  1693     clearFirstFocusEvent = true;
  1696 #ifdef PR_LOGGING
  1697   LOGCONTENT("Element %s has been focused", aContent);
  1699   if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
  1700     nsIDocument* docm = aWindow->GetExtantDoc();
  1701     if (docm) {
  1702       LOGCONTENT(" from %s", docm->GetRootElement());
  1704     LOGFOCUS((" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x]",
  1705              aIsNewDocument, aFocusChanged, aWindowRaised, aFlags));
  1707 #endif
  1709   if (aIsNewDocument) {
  1710     // if this is a new document, update the parent chain of frames so that
  1711     // focus can be traversed from the top level down to the newly focused
  1712     // window.
  1713     AdjustWindowFocus(aWindow, false);
  1715     // Update the window touch registration to reflect the state of
  1716     // the new document that got focus
  1717     aWindow->UpdateTouchState();
  1720   // indicate that the window has taken focus.
  1721   if (aWindow->TakeFocus(true, focusMethod))
  1722     aIsNewDocument = true;
  1724   SetFocusedWindowInternal(aWindow);
  1726   // Update the system focus by focusing the root widget.  But avoid this
  1727   // if 1) aAdjustWidgets is false or 2) aContent is a plugin that has its
  1728   // own widget and is either already focused or is about to be focused.
  1729   nsCOMPtr<nsIWidget> objectFrameWidget;
  1730   if (aContent) {
  1731     nsIFrame* contentFrame = aContent->GetPrimaryFrame();
  1732     nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
  1733     if (objectFrame)
  1734       objectFrameWidget = objectFrame->GetWidget();
  1736   if (aAdjustWidgets && !objectFrameWidget && !sTestMode) {
  1737     nsViewManager* vm = presShell->GetViewManager();
  1738     if (vm) {
  1739       nsCOMPtr<nsIWidget> widget;
  1740       vm->GetRootWidget(getter_AddRefs(widget));
  1741       if (widget)
  1742         widget->SetFocus(false);
  1746   // if switching to a new document, first fire the focus event on the
  1747   // document and then the window.
  1748   if (aIsNewDocument) {
  1749     nsIDocument* doc = aWindow->GetExtantDoc();
  1750     // The focus change should be notified to IMEStateManager from here if
  1751     // the focused content is a designMode editor since any content won't
  1752     // receive focus event.
  1753     if (doc && doc->HasFlag(NODE_IS_EDITABLE)) {
  1754       IMEStateManager::OnChangeFocus(presShell->GetPresContext(), nullptr,
  1755                                      GetFocusMoveActionCause(aFlags));
  1757     if (doc)
  1758       SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, doc,
  1759                            doc, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
  1760     if (mFocusedWindow == aWindow && mFocusedContent == nullptr)
  1761       SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, doc,
  1762                            aWindow, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
  1765   // check to ensure that the element is still focusable, and that nothing
  1766   // else was focused during the events above.
  1767   if (CheckIfFocusable(aContent, aFlags) &&
  1768       mFocusedWindow == aWindow && mFocusedContent == nullptr) {
  1769     mFocusedContent = aContent;
  1771     nsIContent* focusedNode = aWindow->GetFocusedNode();
  1772     bool isRefocus = focusedNode && focusedNode->IsEqualNode(aContent);
  1774     aWindow->SetFocusedNode(aContent, focusMethod);
  1776     bool sendFocusEvent =
  1777       aContent && aContent->IsInDoc() && !IsNonFocusableRoot(aContent);
  1778     nsPresContext* presContext = presShell->GetPresContext();
  1779     if (sendFocusEvent) {
  1780       // if the focused element changed, scroll it into view
  1781       if (aFocusChanged)
  1782         ScrollIntoView(presShell, aContent, aFlags);
  1784       NotifyFocusStateChange(aContent, aWindow->ShouldShowFocusRing(), true);
  1786       // if this is an object/plug-in/remote browser, focus its widget.  Note that we might
  1787       // no longer be in the same document, due to the events we fired above when
  1788       // aIsNewDocument.
  1789       if (presShell->GetDocument() == aContent->GetDocument()) {
  1790         if (aAdjustWidgets && objectFrameWidget && !sTestMode)
  1791           objectFrameWidget->SetFocus(false);
  1793         // if the object being focused is a remote browser, activate remote content
  1794         if (TabParent* remote = TabParent::GetFrom(aContent)) {
  1795           remote->Activate();
  1796           LOGFOCUS(("Remote browser activated"));
  1800       IMEStateManager::OnChangeFocus(presContext, aContent,
  1801                                      GetFocusMoveActionCause(aFlags));
  1803       // as long as this focus wasn't because a window was raised, update the
  1804       // commands
  1805       // XXXndeakin P2 someone could adjust the focus during the update
  1806       if (!aWindowRaised)
  1807         aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
  1809       SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell,
  1810                            aContent->GetCurrentDoc(),
  1811                            aContent, aFlags & FOCUSMETHOD_MASK,
  1812                            aWindowRaised, isRefocus);
  1813     } else {
  1814       IMEStateManager::OnChangeFocus(presContext, nullptr,
  1815                                      GetFocusMoveActionCause(aFlags));
  1816       if (!aWindowRaised) {
  1817         aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
  1821   else {
  1822     // If the window focus event (fired above when aIsNewDocument) caused
  1823     // the plugin not to be focusable, update the system focus by focusing
  1824     // the root widget.
  1825     if (aAdjustWidgets && objectFrameWidget &&
  1826         mFocusedWindow == aWindow && mFocusedContent == nullptr &&
  1827         !sTestMode) {
  1828       nsViewManager* vm = presShell->GetViewManager();
  1829       if (vm) {
  1830         nsCOMPtr<nsIWidget> widget;
  1831         vm->GetRootWidget(getter_AddRefs(widget));
  1832         if (widget)
  1833           widget->SetFocus(false);
  1837     nsPresContext* presContext = presShell->GetPresContext();
  1838     IMEStateManager::OnChangeFocus(presContext, nullptr,
  1839                                    GetFocusMoveActionCause(aFlags));
  1841     if (!aWindowRaised)
  1842       aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
  1845   // update the caret visibility and position to match the newly focused
  1846   // element. However, don't update the position if this was a focus due to a
  1847   // mouse click as the selection code would already have moved the caret as
  1848   // needed. If this is a different document than was focused before, also
  1849   // update the caret's visibility. If this is the same document, the caret
  1850   // visibility should be the same as before so there is no need to update it.
  1851   if (mFocusedContent == aContent)
  1852     UpdateCaret(aFocusChanged && !(aFlags & FLAG_BYMOUSE), aIsNewDocument,
  1853                 mFocusedContent);
  1855   if (clearFirstFocusEvent)
  1856     mFirstFocusEvent = nullptr;
  1859 class FocusBlurEvent : public nsRunnable
  1861 public:
  1862   FocusBlurEvent(nsISupports* aTarget, uint32_t aType,
  1863                  nsPresContext* aContext, bool aWindowRaised,
  1864                  bool aIsRefocus)
  1865   : mTarget(aTarget), mType(aType), mContext(aContext),
  1866     mWindowRaised(aWindowRaised), mIsRefocus(aIsRefocus) {}
  1868   NS_IMETHOD Run()
  1870     InternalFocusEvent event(true, mType);
  1871     event.mFlags.mBubbles = false;
  1872     event.fromRaise = mWindowRaised;
  1873     event.isRefocus = mIsRefocus;
  1874     return EventDispatcher::Dispatch(mTarget, mContext, &event);
  1877   nsCOMPtr<nsISupports>   mTarget;
  1878   uint32_t                mType;
  1879   nsRefPtr<nsPresContext> mContext;
  1880   bool                    mWindowRaised;
  1881   bool                    mIsRefocus;
  1882 };
  1884 void
  1885 nsFocusManager::SendFocusOrBlurEvent(uint32_t aType,
  1886                                      nsIPresShell* aPresShell,
  1887                                      nsIDocument* aDocument,
  1888                                      nsISupports* aTarget,
  1889                                      uint32_t aFocusMethod,
  1890                                      bool aWindowRaised,
  1891                                      bool aIsRefocus)
  1893   NS_ASSERTION(aType == NS_FOCUS_CONTENT || aType == NS_BLUR_CONTENT,
  1894                "Wrong event type for SendFocusOrBlurEvent");
  1896   nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(aTarget);
  1898   nsCOMPtr<nsINode> n = do_QueryInterface(aTarget);
  1899   if (!n) {
  1900     nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aTarget);
  1901     n = win ? win->GetExtantDoc() : nullptr;
  1903   bool dontDispatchEvent = n && nsContentUtils::IsUserFocusIgnored(n);
  1905   // for focus events, if this event was from a mouse or key and event
  1906   // handling on the document is suppressed, queue the event and fire it
  1907   // later. For blur events, a non-zero value would be set for aFocusMethod.
  1908   if (aFocusMethod && !dontDispatchEvent &&
  1909       aDocument && aDocument->EventHandlingSuppressed()) {
  1910     // aFlags is always 0 when aWindowRaised is true so this won't be called
  1911     // on a window raise.
  1912     NS_ASSERTION(!aWindowRaised, "aWindowRaised should not be set");
  1914     for (uint32_t i = mDelayedBlurFocusEvents.Length(); i > 0; --i) {
  1915       // if this event was already queued, remove it and append it to the end
  1916       if (mDelayedBlurFocusEvents[i - 1].mType == aType &&
  1917           mDelayedBlurFocusEvents[i - 1].mPresShell == aPresShell &&
  1918           mDelayedBlurFocusEvents[i - 1].mDocument == aDocument &&
  1919           mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget) {
  1920         mDelayedBlurFocusEvents.RemoveElementAt(i - 1);
  1924     mDelayedBlurFocusEvents.AppendElement(
  1925       nsDelayedBlurOrFocusEvent(aType, aPresShell, aDocument, eventTarget));
  1926     return;
  1929 #ifdef ACCESSIBILITY
  1930   nsAccessibilityService* accService = GetAccService();
  1931   if (accService) {
  1932     if (aType == NS_FOCUS_CONTENT)
  1933       accService->NotifyOfDOMFocus(aTarget);
  1934     else
  1935       accService->NotifyOfDOMBlur(aTarget);
  1937 #endif
  1939   if (!dontDispatchEvent) {
  1940     nsContentUtils::AddScriptRunner(
  1941       new FocusBlurEvent(aTarget, aType, aPresShell->GetPresContext(),
  1942                          aWindowRaised, aIsRefocus));
  1946 void
  1947 nsFocusManager::ScrollIntoView(nsIPresShell* aPresShell,
  1948                                nsIContent* aContent,
  1949                                uint32_t aFlags)
  1951   // if the noscroll flag isn't set, scroll the newly focused element into view
  1952   if (!(aFlags & FLAG_NOSCROLL))
  1953     aPresShell->ScrollContentIntoView(aContent,
  1954                                       nsIPresShell::ScrollAxis(
  1955                                         nsIPresShell::SCROLL_MINIMUM,
  1956                                         nsIPresShell::SCROLL_IF_NOT_VISIBLE),
  1957                                       nsIPresShell::ScrollAxis(
  1958                                         nsIPresShell::SCROLL_MINIMUM,
  1959                                         nsIPresShell::SCROLL_IF_NOT_VISIBLE),
  1960                                       nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
  1964 void
  1965 nsFocusManager::RaiseWindow(nsPIDOMWindow* aWindow)
  1967   // don't raise windows that are already raised or are in the process of
  1968   // being lowered
  1969   if (!aWindow || aWindow == mActiveWindow || aWindow == mWindowBeingLowered)
  1970     return;
  1972   if (sTestMode) {
  1973     // In test mode, emulate the existing window being lowered and the new
  1974     // window being raised.
  1975     if (mActiveWindow)
  1976       WindowLowered(mActiveWindow);
  1977     WindowRaised(aWindow);
  1978     return;
  1981 #if defined(XP_WIN)
  1982   // Windows would rather we focus the child widget, otherwise, the toplevel
  1983   // widget will always end up being focused. Fortunately, focusing the child
  1984   // widget will also have the effect of raising the window this widget is in.
  1985   // But on other platforms, we can just focus the toplevel widget to raise
  1986   // the window.
  1987   nsCOMPtr<nsPIDOMWindow> childWindow;
  1988   GetFocusedDescendant(aWindow, true, getter_AddRefs(childWindow));
  1989   if (!childWindow)
  1990     childWindow = aWindow;
  1992   nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  1993   if (!docShell)
  1994     return;
  1996   nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1997   if (!presShell)
  1998     return;
  2000   nsViewManager* vm = presShell->GetViewManager();
  2001   if (vm) {
  2002     nsCOMPtr<nsIWidget> widget;
  2003     vm->GetRootWidget(getter_AddRefs(widget));
  2004     if (widget)
  2005       widget->SetFocus(true);
  2007 #else
  2008   nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
  2009   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(webnav);
  2010   if (treeOwnerAsWin) {
  2011     nsCOMPtr<nsIWidget> widget;
  2012     treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
  2013     if (widget)
  2014       widget->SetFocus(true);
  2016 #endif
  2019 void
  2020 nsFocusManager::UpdateCaretForCaretBrowsingMode()
  2022   UpdateCaret(false, true, mFocusedContent);
  2025 void
  2026 nsFocusManager::UpdateCaret(bool aMoveCaretToFocus,
  2027                             bool aUpdateVisibility,
  2028                             nsIContent* aContent)
  2030   LOGFOCUS(("Update Caret: %d %d", aMoveCaretToFocus, aUpdateVisibility));
  2032   if (!mFocusedWindow)
  2033     return;
  2035   // this is called when a document is focused or when the caretbrowsing
  2036   // preference is changed
  2037   nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
  2038   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(focusedDocShell);
  2039   if (!dsti)
  2040     return;
  2042   if (dsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
  2043     return;  // Never browse with caret in chrome
  2046   bool browseWithCaret =
  2047     Preferences::GetBool("accessibility.browsewithcaret");
  2049   nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
  2050   if (!presShell)
  2051     return;
  2053   // If this is an editable document which isn't contentEditable, or a
  2054   // contentEditable document and the node to focus is contentEditable,
  2055   // return, so that we don't mess with caret visibility.
  2056   bool isEditable = false;
  2057   focusedDocShell->GetEditable(&isEditable);
  2059   if (isEditable) {
  2060     nsCOMPtr<nsIHTMLDocument> doc =
  2061       do_QueryInterface(presShell->GetDocument());
  2063     bool isContentEditableDoc =
  2064       doc && doc->GetEditingState() == nsIHTMLDocument::eContentEditable;
  2066     bool isFocusEditable =
  2067       aContent && aContent->HasFlag(NODE_IS_EDITABLE);
  2068     if (!isContentEditableDoc || isFocusEditable)
  2069       return;
  2072   if (!isEditable && aMoveCaretToFocus)
  2073     MoveCaretToFocus(presShell, aContent);
  2075   if (!aUpdateVisibility)
  2076     return;
  2078   // XXXndeakin this doesn't seem right. It should be checking for this only
  2079   // on the nearest ancestor frame which is a chrome frame. But this is
  2080   // what the existing code does, so just leave it for now.
  2081   if (!browseWithCaret) {
  2082     nsCOMPtr<nsIContent> docContent =
  2083       do_QueryInterface(mFocusedWindow->GetFrameElementInternal());
  2084     if (docContent)
  2085       browseWithCaret = docContent->AttrValueIs(kNameSpaceID_None,
  2086                                                 nsGkAtoms::showcaret,
  2087                                                 NS_LITERAL_STRING("true"),
  2088                                                 eCaseMatters);
  2091   SetCaretVisible(presShell, browseWithCaret, aContent);
  2094 void
  2095 nsFocusManager::MoveCaretToFocus(nsIPresShell* aPresShell, nsIContent* aContent)
  2097   // domDoc is a document interface we can create a range with
  2098   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aPresShell->GetDocument());
  2099   if (domDoc) {
  2100     nsRefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
  2101     nsCOMPtr<nsISelection> domSelection = frameSelection->
  2102       GetSelection(nsISelectionController::SELECTION_NORMAL);
  2103     if (domSelection) {
  2104       nsCOMPtr<nsIDOMNode> currentFocusNode(do_QueryInterface(aContent));
  2105       // First clear the selection. This way, if there is no currently focused
  2106       // content, the selection will just be cleared.
  2107       domSelection->RemoveAllRanges();
  2108       if (currentFocusNode) {
  2109         nsCOMPtr<nsIDOMRange> newRange;
  2110         nsresult rv = domDoc->CreateRange(getter_AddRefs(newRange));
  2111         if (NS_SUCCEEDED(rv)) {
  2112           // Set the range to the start of the currently focused node
  2113           // Make sure it's collapsed
  2114           newRange->SelectNodeContents(currentFocusNode);
  2115           nsCOMPtr<nsIDOMNode> firstChild;
  2116           currentFocusNode->GetFirstChild(getter_AddRefs(firstChild));
  2117           if (!firstChild ||
  2118               aContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) {
  2119             // If current focus node is a leaf, set range to before the
  2120             // node by using the parent as a container.
  2121             // This prevents it from appearing as selected.
  2122             newRange->SetStartBefore(currentFocusNode);
  2123             newRange->SetEndBefore(currentFocusNode);
  2125           domSelection->AddRange(newRange);
  2126           domSelection->CollapseToStart();
  2133 nsresult
  2134 nsFocusManager::SetCaretVisible(nsIPresShell* aPresShell,
  2135                                 bool aVisible,
  2136                                 nsIContent* aContent)
  2138   // When browsing with caret, make sure caret is visible after new focus
  2139   // Return early if there is no caret. This can happen for the testcase
  2140   // for bug 308025 where a window is closed in a blur handler.
  2141   nsRefPtr<nsCaret> caret = aPresShell->GetCaret();
  2142   if (!caret)
  2143     return NS_OK;
  2145   bool caretVisible = false;
  2146   caret->GetCaretVisible(&caretVisible);
  2147   if (!aVisible && !caretVisible)
  2148     return NS_OK;
  2150   nsRefPtr<nsFrameSelection> frameSelection;
  2151   if (aContent) {
  2152     NS_ASSERTION(aContent->GetDocument() == aPresShell->GetDocument(),
  2153                  "Wrong document?");
  2154     nsIFrame *focusFrame = aContent->GetPrimaryFrame();
  2155     if (focusFrame)
  2156       frameSelection = focusFrame->GetFrameSelection();
  2159   nsRefPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
  2161   if (docFrameSelection && caret &&
  2162      (frameSelection == docFrameSelection || !aContent)) {
  2163     nsISelection* domSelection = docFrameSelection->
  2164       GetSelection(nsISelectionController::SELECTION_NORMAL);
  2165     if (domSelection) {
  2166       // First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
  2167       caret->SetCaretVisible(false);
  2169       // Caret must blink on non-editable elements
  2170       caret->SetIgnoreUserModify(true);
  2171       // Tell the caret which selection to use
  2172       caret->SetCaretDOMSelection(domSelection);
  2174       // In content, we need to set the caret. The only special case is edit
  2175       // fields, which have a different frame selection from the document.
  2176       // They will take care of making the caret visible themselves.
  2178       nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
  2179       if (!selCon)
  2180         return NS_ERROR_FAILURE;
  2182       selCon->SetCaretReadOnly(false);
  2183       selCon->SetCaretEnabled(aVisible);
  2184       caret->SetCaretVisible(aVisible);
  2188   return NS_OK;
  2191 nsresult
  2192 nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
  2193                                      nsIPresShell* aPresShell,
  2194                                      nsIContent **aStartContent,
  2195                                      nsIContent **aEndContent)
  2197   *aStartContent = *aEndContent = nullptr;
  2198   nsresult rv = NS_ERROR_FAILURE;
  2200   nsPresContext* presContext = aPresShell->GetPresContext();
  2201   NS_ASSERTION(presContext, "mPresContent is null!!");
  2203   nsRefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
  2205   nsCOMPtr<nsISelection> domSelection;
  2206   if (frameSelection) {
  2207     domSelection = frameSelection->
  2208       GetSelection(nsISelectionController::SELECTION_NORMAL);
  2211   nsCOMPtr<nsIDOMNode> startNode, endNode;
  2212   bool isCollapsed = false;
  2213   nsCOMPtr<nsIContent> startContent, endContent;
  2214   int32_t startOffset = 0;
  2215   if (domSelection) {
  2216     domSelection->GetIsCollapsed(&isCollapsed);
  2217     nsCOMPtr<nsIDOMRange> domRange;
  2218     rv = domSelection->GetRangeAt(0, getter_AddRefs(domRange));
  2219     if (domRange) {
  2220       domRange->GetStartContainer(getter_AddRefs(startNode));
  2221       domRange->GetEndContainer(getter_AddRefs(endNode));
  2222       domRange->GetStartOffset(&startOffset);
  2224       nsIContent *childContent = nullptr;
  2226       startContent = do_QueryInterface(startNode);
  2227       if (startContent && startContent->IsElement()) {
  2228         NS_ASSERTION(startOffset >= 0, "Start offset cannot be negative");  
  2229         childContent = startContent->GetChildAt(startOffset);
  2230         if (childContent) {
  2231           startContent = childContent;
  2235       endContent = do_QueryInterface(endNode);
  2236       if (endContent && endContent->IsElement()) {
  2237         int32_t endOffset = 0;
  2238         domRange->GetEndOffset(&endOffset);
  2239         NS_ASSERTION(endOffset >= 0, "End offset cannot be negative");
  2240         childContent = endContent->GetChildAt(endOffset);
  2241         if (childContent) {
  2242           endContent = childContent;
  2247   else {
  2248     rv = NS_ERROR_INVALID_ARG;
  2251   nsIFrame *startFrame = nullptr;
  2252   if (startContent) {
  2253     startFrame = startContent->GetPrimaryFrame();
  2254     if (isCollapsed) {
  2255       // Next check to see if our caret is at the very end of a node
  2256       // If so, the caret is actually sitting in front of the next
  2257       // logical frame's primary node - so for this case we need to
  2258       // change caretContent to that node.
  2260       if (startContent->NodeType() == nsIDOMNode::TEXT_NODE) {
  2261         nsAutoString nodeValue;
  2262         startContent->AppendTextTo(nodeValue);
  2264         bool isFormControl =
  2265           startContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL);
  2267         if (nodeValue.Length() == (uint32_t)startOffset && !isFormControl &&
  2268             startContent != aDocument->GetRootElement()) {
  2269           // Yes, indeed we were at the end of the last node
  2270           nsCOMPtr<nsIFrameEnumerator> frameTraversal;
  2271           nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
  2272                                              presContext, startFrame,
  2273                                              eLeaf,
  2274                                              false, // aVisual
  2275                                              false, // aLockInScrollView
  2276                                              true      // aFollowOOFs
  2277                                              );
  2278           NS_ENSURE_SUCCESS(rv, rv);
  2280           nsIFrame *newCaretFrame = nullptr;
  2281           nsCOMPtr<nsIContent> newCaretContent = startContent;
  2282           bool endOfSelectionInStartNode(startContent == endContent);
  2283           do {
  2284             // Continue getting the next frame until the primary content for the frame
  2285             // we are on changes - we don't want to be stuck in the same place
  2286             frameTraversal->Next();
  2287             newCaretFrame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
  2288             if (nullptr == newCaretFrame)
  2289               break;
  2290             newCaretContent = newCaretFrame->GetContent();
  2291           } while (!newCaretContent || newCaretContent == startContent);
  2293           if (newCaretFrame && newCaretContent) {
  2294             // If the caret is exactly at the same position of the new frame,
  2295             // then we can use the newCaretFrame and newCaretContent for our position
  2296             nsRefPtr<nsCaret> caret = aPresShell->GetCaret();
  2297             nsRect caretRect;
  2298             nsIFrame *frame = caret->GetGeometry(domSelection, &caretRect);
  2299             if (frame) {
  2300               nsPoint caretWidgetOffset;
  2301               nsIWidget *widget = frame->GetNearestWidget(caretWidgetOffset);
  2302               caretRect.MoveBy(caretWidgetOffset);
  2303               nsPoint newCaretOffset;
  2304               nsIWidget *newCaretWidget = newCaretFrame->GetNearestWidget(newCaretOffset);
  2305               if (widget == newCaretWidget && caretRect.y == newCaretOffset.y &&
  2306                   caretRect.x == newCaretOffset.x) {
  2307                 // The caret is at the start of the new element.
  2308                 startFrame = newCaretFrame;
  2309                 startContent = newCaretContent;
  2310                 if (endOfSelectionInStartNode) {
  2311                   endContent = newCaretContent; // Ensure end of selection is not before start
  2321   *aStartContent = startContent;
  2322   *aEndContent = endContent;
  2323   NS_IF_ADDREF(*aStartContent);
  2324   NS_IF_ADDREF(*aEndContent);
  2326   return rv;
  2329 nsresult
  2330 nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
  2331                                             nsIContent* aStartContent,
  2332                                             int32_t aType, bool aNoParentTraversal,
  2333                                             nsIContent** aNextContent)
  2335   *aNextContent = nullptr;
  2337   nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  2338   if (!docShell)
  2339     return NS_OK;
  2341   nsCOMPtr<nsIContent> startContent = aStartContent;
  2342   if (!startContent && aType != MOVEFOCUS_CARET) {
  2343     if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC) {
  2344       // When moving between documents, make sure to get the right
  2345       // starting content in a descendant.
  2346       nsCOMPtr<nsPIDOMWindow> focusedWindow;
  2347       startContent = GetFocusedDescendant(aWindow, true, getter_AddRefs(focusedWindow));
  2349     else {
  2350       startContent = aWindow->GetFocusedNode();
  2354   nsCOMPtr<nsIDocument> doc;
  2355   if (startContent)
  2356     doc = startContent->GetCurrentDoc();
  2357   else
  2358     doc = aWindow->GetExtantDoc();
  2359   if (!doc)
  2360     return NS_OK;
  2362   LookAndFeel::GetInt(LookAndFeel::eIntID_TabFocusModel,
  2363                       &nsIContent::sTabFocusModel);
  2365   if (aType == MOVEFOCUS_ROOT) {
  2366     NS_IF_ADDREF(*aNextContent = GetRootForFocus(aWindow, doc, false, false));
  2367     return NS_OK;
  2369   if (aType == MOVEFOCUS_FORWARDDOC) {
  2370     NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, true));
  2371     return NS_OK;
  2373   if (aType == MOVEFOCUS_BACKWARDDOC) {
  2374     NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, false));
  2375     return NS_OK;
  2378   nsIContent* rootContent = doc->GetRootElement();
  2379   NS_ENSURE_TRUE(rootContent, NS_OK);
  2381   nsIPresShell *presShell = doc->GetShell();
  2382   NS_ENSURE_TRUE(presShell, NS_OK);
  2384   if (aType == MOVEFOCUS_FIRST) {
  2385     if (!aStartContent)
  2386       startContent = rootContent;
  2387     return GetNextTabbableContent(presShell, startContent,
  2388                                   nullptr, startContent,
  2389                                   true, 1, false, aNextContent);
  2391   if (aType == MOVEFOCUS_LAST) {
  2392     if (!aStartContent)
  2393       startContent = rootContent;
  2394     return GetNextTabbableContent(presShell, startContent,
  2395                                   nullptr, startContent,
  2396                                   false, 0, false, aNextContent);
  2399   bool forward = (aType == MOVEFOCUS_FORWARD || aType == MOVEFOCUS_CARET);
  2400   bool doNavigation = true;
  2401   bool ignoreTabIndex = false;
  2402   // when a popup is open, we want to ensure that tab navigation occurs only
  2403   // within the most recently opened panel. If a popup is open, its frame will
  2404   // be stored in popupFrame.
  2405   nsIFrame* popupFrame = nullptr;
  2407   int32_t tabIndex = forward ? 1 : 0;
  2408   if (startContent) {
  2409     nsIFrame* frame = startContent->GetPrimaryFrame();
  2410     if (startContent->Tag() == nsGkAtoms::area &&
  2411         startContent->IsHTML())
  2412       startContent->IsFocusable(&tabIndex);
  2413     else if (frame)
  2414       frame->IsFocusable(&tabIndex, 0);
  2415     else
  2416       startContent->IsFocusable(&tabIndex);
  2418     // if the current element isn't tabbable, ignore the tabindex and just
  2419     // look for the next element. The root content won't have a tabindex
  2420     // so just treat this as the beginning of the tab order.
  2421     if (tabIndex < 0) {
  2422       tabIndex = 1;
  2423       if (startContent != rootContent)
  2424         ignoreTabIndex = true;
  2427     // check if the focus is currently inside a popup. Elements such as the
  2428     // autocomplete widget use the noautofocus attribute to allow the focus to
  2429     // remain outside the popup when it is opened.
  2430     if (frame) {
  2431       popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
  2432                                                         nsGkAtoms::menuPopupFrame);
  2435     if (popupFrame) {
  2436       // Don't navigate outside of a popup, so pretend that the
  2437       // root content is the popup itself
  2438       rootContent = popupFrame->GetContent();
  2439       NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
  2441     else if (!forward) {
  2442       // If focus moves backward and when current focused node is root
  2443       // content or <body> element which is editable by contenteditable
  2444       // attribute, focus should move to its parent document.
  2445       if (startContent == rootContent) {
  2446         doNavigation = false;
  2447       } else {
  2448         nsIDocument* doc = startContent->GetCurrentDoc();
  2449         if (startContent ==
  2450               nsLayoutUtils::GetEditableRootContentByContentEditable(doc)) {
  2451           doNavigation = false;
  2456   else {
  2457 #ifdef MOZ_XUL
  2458     if (aType != MOVEFOCUS_CARET) {
  2459       // if there is no focus, yet a panel is open, focus the first item in
  2460       // the panel
  2461       nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  2462       if (pm)
  2463         popupFrame = pm->GetTopPopup(ePopupTypePanel);
  2465 #endif
  2466     if (popupFrame) {
  2467       rootContent = popupFrame->GetContent();
  2468       NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
  2469       startContent = rootContent;
  2471     else {
  2472       // Otherwise, for content shells, start from the location of the caret.
  2473       if (docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
  2474         nsCOMPtr<nsIContent> endSelectionContent;
  2475         GetSelectionLocation(doc, presShell,
  2476                              getter_AddRefs(startContent),
  2477                              getter_AddRefs(endSelectionContent));
  2478         // If the selection is on the rootContent, then there is no selection
  2479         if (startContent == rootContent) {
  2480           startContent = nullptr;
  2483         if (aType == MOVEFOCUS_CARET) {
  2484           // GetFocusInSelection finds a focusable link near the caret.
  2485           // If there is no start content though, don't do this to avoid
  2486           // focusing something unexpected.
  2487           if (startContent) {
  2488             GetFocusInSelection(aWindow, startContent,
  2489                                 endSelectionContent, aNextContent);
  2491           return NS_OK;
  2494         if (startContent) {
  2495           // when starting from a selection, we always want to find the next or
  2496           // previous element in the document. So the tabindex on elements
  2497           // should be ignored.
  2498           ignoreTabIndex = true;
  2502       if (!startContent) {
  2503         // otherwise, just use the root content as the starting point
  2504         startContent = rootContent;
  2505         NS_ENSURE_TRUE(startContent, NS_OK);
  2510   NS_ASSERTION(startContent, "starting content not set");
  2512   // keep a reference to the starting content. If we find that again, it means
  2513   // we've iterated around completely and we don't want to adjust the focus.
  2514   // The skipOriginalContentCheck will be set to true only for the first time
  2515   // GetNextTabbableContent is called. This ensures that we don't break out
  2516   // when nothing is focused to start with. Specifically,
  2517   // GetNextTabbableContent first checks the root content -- which happens to
  2518   // be the same as the start content -- when nothing is focused and tabbing
  2519   // forward. Without skipOriginalContentCheck set to true, we'd end up
  2520   // returning right away and focusing nothing. Luckily, GetNextTabbableContent
  2521   // will never wrap around on its own, and can only return the original
  2522   // content when it is called a second time or later.
  2523   bool skipOriginalContentCheck = true;
  2524   nsIContent* originalStartContent = startContent;
  2526   LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent.get());
  2527   LOGFOCUSNAVIGATION(("  Tabindex: %d Ignore: %d", tabIndex, ignoreTabIndex));
  2529   while (doc) {
  2530     if (doNavigation) {
  2531       nsCOMPtr<nsIContent> nextFocus;
  2532       nsresult rv = GetNextTabbableContent(presShell, rootContent,
  2533                                            skipOriginalContentCheck ? nullptr : originalStartContent,
  2534                                            startContent, forward,
  2535                                            tabIndex, ignoreTabIndex,
  2536                                            getter_AddRefs(nextFocus));
  2537       NS_ENSURE_SUCCESS(rv, rv);
  2539       // found a content node to focus.
  2540       if (nextFocus) {
  2541         LOGCONTENTNAVIGATION("Next Content: %s", nextFocus.get());
  2543         // as long as the found node was not the same as the starting node,
  2544         // set it as the return value.
  2545         if (nextFocus != originalStartContent)
  2546           NS_ADDREF(*aNextContent = nextFocus);
  2547         return NS_OK;
  2550       if (popupFrame) {
  2551         // in a popup, so start again from the beginning of the popup. However,
  2552         // if we already started at the beginning, then there isn't anything to
  2553         // focus, so just return
  2554         if (startContent != rootContent) {
  2555           startContent = rootContent;
  2556           tabIndex = forward ? 1 : 0;
  2557           continue;
  2559         return NS_OK;
  2563     doNavigation = true;
  2564     skipOriginalContentCheck = false;
  2565     ignoreTabIndex = false;
  2567     if (aNoParentTraversal) {
  2568       if (startContent == rootContent)
  2569         return NS_OK;
  2571       startContent = rootContent;
  2572       tabIndex = forward ? 1 : 0;
  2573       continue;
  2576     // reached the beginning or end of the document. Traverse up to the parent
  2577     // document and try again.
  2578     nsCOMPtr<nsIDocShellTreeItem> docShellParent;
  2579     docShell->GetParent(getter_AddRefs(docShellParent));
  2580     if (docShellParent) {
  2581       // move up to the parent shell and try again from there.
  2583       // first, get the frame element this window is inside.
  2584       nsCOMPtr<nsPIDOMWindow> piWindow = do_GetInterface(docShell);
  2585       NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE);
  2587       // Next, retrieve the parent docshell, document and presshell.
  2588       docShell = do_QueryInterface(docShellParent);
  2589       NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
  2591       nsCOMPtr<nsPIDOMWindow> piParentWindow = do_GetInterface(docShellParent);
  2592       NS_ENSURE_TRUE(piParentWindow, NS_ERROR_FAILURE);
  2593       doc = piParentWindow->GetExtantDoc();
  2594       NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
  2596       presShell = doc->GetShell();
  2598       rootContent = doc->GetRootElement();
  2599       startContent = do_QueryInterface(piWindow->GetFrameElementInternal());
  2600       if (startContent) {
  2601         nsIFrame* frame = startContent->GetPrimaryFrame();
  2602         if (!frame)
  2603           return NS_OK;
  2605         frame->IsFocusable(&tabIndex, 0);
  2606         if (tabIndex < 0) {
  2607           tabIndex = 1;
  2608           ignoreTabIndex = true;
  2611         // if the frame is inside a popup, make sure to scan only within the
  2612         // popup. This handles the situation of tabbing amongst elements
  2613         // inside an iframe which is itself inside a popup. Otherwise,
  2614         // navigation would move outside the popup when tabbing outside the
  2615         // iframe.
  2616         popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
  2617                                                           nsGkAtoms::menuPopupFrame);
  2618         if (popupFrame) {
  2619           rootContent = popupFrame->GetContent();
  2620           NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
  2623       else {
  2624         startContent = rootContent;
  2625         tabIndex = forward ? 1 : 0;
  2628     else {
  2629       // no parent, so call the tree owner. This will tell the embedder that
  2630       // it should take the focus.
  2631       bool tookFocus;
  2632       docShell->TabToTreeOwner(forward, &tookFocus);
  2633       // if the tree owner, took the focus, blur the current content
  2634       if (tookFocus) {
  2635         nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(docShell);
  2636         if (window->GetFocusedNode() == mFocusedContent)
  2637           Blur(mFocusedWindow, nullptr, true, true);
  2638         else
  2639           window->SetFocusedNode(nullptr);
  2640         return NS_OK;
  2643       // reset the tab index and start again from the beginning or end
  2644       startContent = rootContent;
  2645       tabIndex = forward ? 1 : 0;
  2648     // wrapped all the way around and didn't find anything to move the focus
  2649     // to, so just break out
  2650     if (startContent == originalStartContent)
  2651       break;
  2654   return NS_OK;
  2657 nsresult
  2658 nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
  2659                                        nsIContent* aRootContent,
  2660                                        nsIContent* aOriginalStartContent,
  2661                                        nsIContent* aStartContent,
  2662                                        bool aForward,
  2663                                        int32_t aCurrentTabIndex,
  2664                                        bool aIgnoreTabIndex,
  2665                                        nsIContent** aResultContent)
  2667   *aResultContent = nullptr;
  2669   nsCOMPtr<nsIContent> startContent = aStartContent;
  2670   if (!startContent)
  2671     return NS_OK;
  2673   LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
  2674   LOGFOCUSNAVIGATION(("  tabindex: %d", aCurrentTabIndex));
  2676   nsPresContext* presContext = aPresShell->GetPresContext();
  2678   bool getNextFrame = true;
  2679   nsCOMPtr<nsIContent> iterStartContent = aStartContent;
  2680   while (1) {
  2681     nsIFrame* startFrame = iterStartContent->GetPrimaryFrame();
  2682     // if there is no frame, look for another content node that has a frame
  2683     if (!startFrame) {
  2684       // if the root content doesn't have a frame, just return
  2685       if (iterStartContent == aRootContent)
  2686         return NS_OK;
  2688       // look for the next or previous content node in tree order
  2689       iterStartContent = aForward ? iterStartContent->GetNextNode() : iterStartContent->GetPreviousContent();
  2690       // we've already skipped over the initial focused content, so we
  2691       // don't want to traverse frames.
  2692       getNextFrame = false;
  2693       if (iterStartContent)
  2694         continue;
  2696       // otherwise, as a last attempt, just look at the root content
  2697       iterStartContent = aRootContent;
  2698       continue;
  2701     nsCOMPtr<nsIFrameEnumerator> frameTraversal;
  2702     nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
  2703                                        presContext, startFrame,
  2704                                        ePreOrder,
  2705                                        false, // aVisual
  2706                                        false, // aLockInScrollView
  2707                                        true      // aFollowOOFs
  2708                                        );
  2709     NS_ENSURE_SUCCESS(rv, rv);
  2711     if (iterStartContent == aRootContent) {
  2712       if (!aForward) {
  2713         frameTraversal->Last();
  2714       } else if (aRootContent->IsFocusable()) {
  2715         frameTraversal->Next();
  2718     else if (getNextFrame &&
  2719              (!iterStartContent || iterStartContent->Tag() != nsGkAtoms::area ||
  2720               !iterStartContent->IsHTML())) {
  2721       // Need to do special check in case we're in an imagemap which has multiple
  2722       // content nodes per frame, so don't skip over the starting frame.
  2723       if (aForward)
  2724         frameTraversal->Next();
  2725       else
  2726         frameTraversal->Prev();
  2729     // Walk frames to find something tabbable matching mCurrentTabIndex
  2730     nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
  2731     while (frame) {
  2732       // TabIndex not set defaults to 0 for form elements, anchors and other
  2733       // elements that are normally focusable. Tabindex defaults to -1
  2734       // for elements that are not normally focusable.
  2735       // The returned computed tabindex from IsFocusable() is as follows:
  2736       //          < 0 not tabbable at all
  2737       //          == 0 in normal tab order (last after positive tabindexed items)
  2738       //          > 0 can be tabbed to in the order specified by this value
  2740       int32_t tabIndex;
  2741       frame->IsFocusable(&tabIndex, 0);
  2743       LOGCONTENTNAVIGATION("Next Tabbable %s:", frame->GetContent());
  2744       LOGFOCUSNAVIGATION(("  with tabindex: %d expected: %d", tabIndex, aCurrentTabIndex));
  2746       nsIContent* currentContent = frame->GetContent();
  2747       if (tabIndex >= 0) {
  2748         NS_ASSERTION(currentContent, "IsFocusable set a tabindex for a frame with no content");
  2749         if (currentContent->Tag() == nsGkAtoms::img &&
  2750             currentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usemap)) {
  2751           // This is an image with a map. Image map areas are not traversed by
  2752           // nsIFrameTraversal so look for the next or previous area element.
  2753           nsIContent *areaContent =
  2754             GetNextTabbableMapArea(aForward, aCurrentTabIndex,
  2755                                    currentContent, iterStartContent);
  2756           if (areaContent) {
  2757             NS_ADDREF(*aResultContent = areaContent);
  2758             return NS_OK;
  2761         else if (aIgnoreTabIndex || aCurrentTabIndex == tabIndex) {
  2762           // break out if we've wrapped around to the start again.
  2763           if (aOriginalStartContent && currentContent == aOriginalStartContent) {
  2764             NS_ADDREF(*aResultContent = currentContent);
  2765             return NS_OK;
  2768           // found a node with a matching tab index. Check if it is a child
  2769           // frame. If so, navigate into the child frame instead.
  2770           nsIDocument* doc = currentContent->GetCurrentDoc();
  2771           NS_ASSERTION(doc, "content not in document");
  2772           nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
  2773           if (subdoc) {
  2774             if (!subdoc->EventHandlingSuppressed()) {
  2775               if (aForward) {
  2776                 // when tabbing forward into a frame, return the root
  2777                 // frame so that the canvas becomes focused.
  2778                 nsCOMPtr<nsPIDOMWindow> subframe = subdoc->GetWindow();
  2779                 if (subframe) {
  2780                   // If the subframe body is editable by contenteditable,
  2781                   // we should set the editor's root element rather than the
  2782                   // actual root element.  Otherwise, we should set the focus
  2783                   // to the root content.
  2784                   *aResultContent =
  2785                     nsLayoutUtils::GetEditableRootContentByContentEditable(subdoc);
  2786                   if (!*aResultContent ||
  2787                       !((*aResultContent)->GetPrimaryFrame())) {
  2788                     *aResultContent =
  2789                       GetRootForFocus(subframe, subdoc, false, true);
  2791                   if (*aResultContent) {
  2792                     NS_ADDREF(*aResultContent);
  2793                     return NS_OK;
  2797               Element* rootElement = subdoc->GetRootElement();
  2798               nsIPresShell* subShell = subdoc->GetShell();
  2799               if (rootElement && subShell) {
  2800                 rv = GetNextTabbableContent(subShell, rootElement,
  2801                                             aOriginalStartContent, rootElement,
  2802                                             aForward, (aForward ? 1 : 0),
  2803                                             false, aResultContent);
  2804                 NS_ENSURE_SUCCESS(rv, rv);
  2805                 if (*aResultContent)
  2806                   return NS_OK;
  2810           // otherwise, use this as the next content node to tab to, unless
  2811           // this was the element we started on. This would happen for
  2812           // instance on an element with child frames, where frame navigation
  2813           // could return the original element again. In that case, just skip
  2814           // it. Also, if the next content node is the root content, then
  2815           // return it. This latter case would happen only if someone made a
  2816           // popup focusable.
  2817           // Also, when going backwards, check to ensure that the focus
  2818           // wouldn't be redirected. Otherwise, for example, when an input in
  2819           // a textbox is focused, the enclosing textbox would be found and
  2820           // the same inner input would be returned again.
  2821           else if (currentContent == aRootContent ||
  2822                    (currentContent != startContent &&
  2823                     (aForward || !GetRedirectedFocus(currentContent)))) {
  2824             NS_ADDREF(*aResultContent = currentContent);
  2825             return NS_OK;
  2829       else if (aOriginalStartContent && currentContent == aOriginalStartContent) {
  2830         // not focusable, so return if we have wrapped around to the original
  2831         // content. This is necessary in case the original starting content was
  2832         // not focusable.
  2833         NS_ADDREF(*aResultContent = currentContent);
  2834         return NS_OK;
  2837       // Move to the next or previous frame, but ignore continuation frames
  2838       // since only the first frame should be involved in focusability.
  2839       // Otherwise, a loop will occur in the following example:
  2840       //   <span tabindex="1">...<a/><a/>...</span>
  2841       // where the text wraps onto multiple lines. Tabbing from the second
  2842       // link can find one of the span's continuation frames between the link
  2843       // and the end of the span, and the span would end up getting focused
  2844       // again.
  2845       do {
  2846         if (aForward)
  2847           frameTraversal->Next();
  2848         else
  2849           frameTraversal->Prev();
  2850         frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
  2851       } while (frame && frame->GetPrevContinuation());
  2854     // If already at lowest priority tab (0), end search completely.
  2855     // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
  2856     if (aCurrentTabIndex == (aForward ? 0 : 1)) {
  2857       // if going backwards, the canvas should be focused once the beginning
  2858       // has been reached.
  2859       if (!aForward) {
  2860         nsCOMPtr<nsPIDOMWindow> window = GetCurrentWindow(aRootContent);
  2861         NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
  2862         NS_IF_ADDREF(*aResultContent =
  2863                      GetRootForFocus(window, aRootContent->GetCurrentDoc(), false, true));
  2865       break;
  2868     // continue looking for next highest priority tabindex
  2869     aCurrentTabIndex = GetNextTabIndex(aRootContent, aCurrentTabIndex, aForward);
  2870     startContent = iterStartContent = aRootContent;
  2873   return NS_OK;
  2876 nsIContent*
  2877 nsFocusManager::GetNextTabbableMapArea(bool aForward,
  2878                                        int32_t aCurrentTabIndex,
  2879                                        nsIContent* aImageContent,
  2880                                        nsIContent* aStartContent)
  2882   nsAutoString useMap;
  2883   aImageContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, useMap);
  2885   nsCOMPtr<nsIDocument> doc = aImageContent->GetDocument();
  2886   if (doc) {
  2887     nsCOMPtr<nsIContent> mapContent = doc->FindImageMap(useMap);
  2888     if (!mapContent)
  2889       return nullptr;
  2890     uint32_t count = mapContent->GetChildCount();
  2891     // First see if the the start content is in this map
  2893     int32_t index = mapContent->IndexOf(aStartContent);
  2894     int32_t tabIndex;
  2895     if (index < 0 || (aStartContent->IsFocusable(&tabIndex) &&
  2896                       tabIndex != aCurrentTabIndex)) {
  2897       // If aStartContent is in this map we must start iterating past it.
  2898       // We skip the case where aStartContent has tabindex == aStartContent
  2899       // since the next tab ordered element might be before it
  2900       // (or after for backwards) in the child list.
  2901       index = aForward ? -1 : (int32_t)count;
  2904     // GetChildAt will return nullptr if our index < 0 or index >= count
  2905     nsCOMPtr<nsIContent> areaContent;
  2906     while ((areaContent = mapContent->GetChildAt(aForward ? ++index : --index)) != nullptr) {
  2907       if (areaContent->IsFocusable(&tabIndex) && tabIndex == aCurrentTabIndex) {
  2908         return areaContent;
  2913   return nullptr;
  2916 int32_t
  2917 nsFocusManager::GetNextTabIndex(nsIContent* aParent,
  2918                                 int32_t aCurrentTabIndex,
  2919                                 bool aForward)
  2921   int32_t tabIndex, childTabIndex;
  2923   if (aForward) {
  2924     tabIndex = 0;
  2925     for (nsIContent* child = aParent->GetFirstChild();
  2926          child;
  2927          child = child->GetNextSibling()) {
  2928       childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
  2929       if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
  2930         tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
  2933       nsAutoString tabIndexStr;
  2934       child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
  2935       nsresult ec;
  2936       int32_t val = tabIndexStr.ToInteger(&ec);
  2937       if (NS_SUCCEEDED (ec) && val > aCurrentTabIndex && val != tabIndex) {
  2938         tabIndex = (tabIndex == 0 || val < tabIndex) ? val : tabIndex;
  2942   else { /* !aForward */
  2943     tabIndex = 1;
  2944     for (nsIContent* child = aParent->GetFirstChild();
  2945          child;
  2946          child = child->GetNextSibling()) {
  2947       childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
  2948       if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
  2949           (childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
  2950         tabIndex = childTabIndex;
  2953       nsAutoString tabIndexStr;
  2954       child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
  2955       nsresult ec;
  2956       int32_t val = tabIndexStr.ToInteger(&ec);
  2957       if (NS_SUCCEEDED (ec)) {
  2958         if ((aCurrentTabIndex == 0 && val > tabIndex) ||
  2959             (val < aCurrentTabIndex && val > tabIndex) ) {
  2960           tabIndex = val;
  2966   return tabIndex;
  2969 nsIContent*
  2970 nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
  2971                                 nsIDocument* aDocument,
  2972                                 bool aIsForDocNavigation,
  2973                                 bool aCheckVisibility)
  2975   // the root element's canvas may be focused as long as the document is in a
  2976   // a non-chrome shell and does not contain a frameset.
  2977   if (aIsForDocNavigation) {
  2978     nsCOMPtr<nsIContent> docContent =
  2979       do_QueryInterface(aWindow->GetFrameElementInternal());
  2980     // document navigation skips iframes and frames that are specifically non-focusable
  2981     if (docContent) {
  2982       if (docContent->Tag() == nsGkAtoms::iframe)
  2983         return nullptr;
  2985       nsIFrame* frame = docContent->GetPrimaryFrame();
  2986       if (!frame || !frame->IsFocusable(nullptr, 0))
  2987         return nullptr;
  2989   } else {
  2990     nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  2991     if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
  2992       return nullptr;
  2996   if (aCheckVisibility && !IsWindowVisible(aWindow))
  2997     return nullptr;
  2999   Element *rootElement = aDocument->GetRootElement();
  3000   if (!rootElement) {
  3001     return nullptr;
  3004   if (aCheckVisibility && !rootElement->GetPrimaryFrame()) {
  3005     return nullptr;
  3008   // Finally, check if this is a frameset
  3009   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
  3010   if (htmlDoc && aDocument->GetHtmlChildElement(nsGkAtoms::frameset)) {
  3011     return nullptr;
  3014   return rootElement;
  3017 void
  3018 nsFocusManager::GetLastDocShell(nsIDocShellTreeItem* aItem,
  3019                                 nsIDocShellTreeItem** aResult)
  3021   *aResult = nullptr;
  3023   nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
  3024   while (curItem) {
  3025     int32_t childCount = 0;
  3026     curItem->GetChildCount(&childCount);
  3027     if (!childCount) {
  3028       *aResult = curItem;
  3029       NS_ADDREF(*aResult);
  3030       return;
  3034     curItem->GetChildAt(childCount - 1, getter_AddRefs(curItem));
  3038 void
  3039 nsFocusManager::GetNextDocShell(nsIDocShellTreeItem* aItem,
  3040                                 nsIDocShellTreeItem** aResult)
  3042   *aResult = nullptr;
  3044   int32_t childCount = 0;
  3045   aItem->GetChildCount(&childCount);
  3046   if (childCount) {
  3047     aItem->GetChildAt(0, aResult);
  3048     if (*aResult)
  3049       return;
  3052   nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
  3053   while (curItem) {
  3054     nsCOMPtr<nsIDocShellTreeItem> parentItem;
  3055     curItem->GetParent(getter_AddRefs(parentItem));
  3056     if (!parentItem)
  3057       return;
  3059     // Note that we avoid using GetChildOffset() here because docshell
  3060     // child offsets can't be trusted to be correct. bug 162283.
  3061     nsCOMPtr<nsIDocShellTreeItem> iterItem;
  3062     childCount = 0;
  3063     parentItem->GetChildCount(&childCount);
  3064     for (int32_t index = 0; index < childCount; ++index) {
  3065       parentItem->GetChildAt(index, getter_AddRefs(iterItem));
  3066       if (iterItem == curItem) {
  3067         ++index;
  3068         if (index < childCount) {
  3069           parentItem->GetChildAt(index, aResult);
  3070           if (*aResult)
  3071             return;
  3073         break;
  3077     curItem = parentItem;
  3081 void
  3082 nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem* aItem,
  3083                                     nsIDocShellTreeItem** aResult)
  3085   *aResult = nullptr;
  3087   nsCOMPtr<nsIDocShellTreeItem> parentItem;
  3088   aItem->GetParent(getter_AddRefs(parentItem));
  3089   if (!parentItem)
  3090     return;
  3092   // Note that we avoid using GetChildOffset() here because docshell
  3093   // child offsets can't be trusted to be correct. bug 162283.
  3094   int32_t childCount = 0;
  3095   parentItem->GetChildCount(&childCount);
  3096   nsCOMPtr<nsIDocShellTreeItem> prevItem, iterItem;
  3097   for (int32_t index = 0; index < childCount; ++index) {
  3098     parentItem->GetChildAt(index, getter_AddRefs(iterItem));
  3099     if (iterItem == aItem)
  3100       break;
  3101     prevItem = iterItem;
  3104   if (prevItem)
  3105     GetLastDocShell(prevItem, aResult);
  3106   else
  3107     NS_ADDREF(*aResult = parentItem);
  3110 nsIContent*
  3111 nsFocusManager::GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward)
  3113   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  3114   if (!pm)
  3115     return nullptr;
  3117   // Iterate through the array backwards if aForward is false.
  3118   nsTArray<nsIFrame *> popups;
  3119   pm->GetVisiblePopups(popups);
  3120   int32_t i = aForward ? 0 : popups.Length() - 1;
  3121   int32_t end = aForward ? popups.Length() : -1;
  3123   for (; i != end; aForward ? i++ : i--) {
  3124     nsIFrame* popupFrame = popups[i];
  3125     if (aCurrentPopup) {
  3126       // If the current popup is set, then we need to skip over this popup and
  3127       // wait until the currently focused popup is found. Once found, the
  3128       // current popup will be cleared so that the next popup is used.
  3129       if (aCurrentPopup == popupFrame)
  3130         aCurrentPopup = nullptr;
  3131       continue;
  3134     // Skip over non-panels
  3135     if (popupFrame->GetContent()->Tag() != nsGkAtoms::panel ||
  3136         (aDocument && popupFrame->GetContent()->GetCurrentDoc() != aDocument)) {
  3137       continue;
  3140     // Find the first focusable content within the popup. If there isn't any
  3141     // focusable content in the popup, skip to the next popup.
  3142     nsIPresShell* presShell = popupFrame->PresContext()->GetPresShell();
  3143     if (presShell) {
  3144       nsCOMPtr<nsIContent> nextFocus;
  3145       nsIContent* popup = popupFrame->GetContent();
  3146       nsresult rv = GetNextTabbableContent(presShell, popup,
  3147                                            nullptr, popup,
  3148                                            true, 1, false,
  3149                                            getter_AddRefs(nextFocus));
  3150       if (NS_SUCCEEDED(rv) && nextFocus) {
  3151         return nextFocus.get();
  3156   return nullptr;
  3159 nsIContent*
  3160 nsFocusManager::GetNextTabbableDocument(nsIContent* aStartContent, bool aForward)
  3162   // If currentPopup is set, then the starting content is in a panel.
  3163   nsIFrame* currentPopup = nullptr;
  3164   nsCOMPtr<nsIDocument> doc;
  3165   nsCOMPtr<nsIDocShell> startDocShell;
  3167   if (aStartContent) {
  3168     doc = aStartContent->GetCurrentDoc();
  3169     if (doc) {
  3170       startDocShell = doc->GetWindow()->GetDocShell();
  3173     // Check if the starting content is inside a panel. Document navigation
  3174     // must start from this panel instead of the document root.
  3175     nsIContent* content = aStartContent;
  3176     while (content) {
  3177       if (content->NodeInfo()->Equals(nsGkAtoms::panel, kNameSpaceID_XUL)) {
  3178         currentPopup = content->GetPrimaryFrame();
  3179         break;
  3181       content = content->GetParent();
  3184   else if (mFocusedWindow) {
  3185     startDocShell = mFocusedWindow->GetDocShell();
  3186     doc = mFocusedWindow->GetExtantDoc();
  3188   else {
  3189     nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mActiveWindow);
  3190     startDocShell = do_QueryInterface(webnav);
  3192     if (mActiveWindow) {
  3193       doc = mActiveWindow->GetExtantDoc();
  3197   if (!startDocShell)
  3198     return nullptr;
  3200   // perform a depth first search (preorder) of the docshell tree
  3201   // looking for an HTML Frame or a chrome document
  3202   nsIContent* content = aStartContent;
  3203   nsCOMPtr<nsIDocShellTreeItem> curItem = startDocShell.get();
  3204   nsCOMPtr<nsIDocShellTreeItem> nextItem;
  3205   do {
  3206     // If moving forward, check for a panel in the starting document. If one
  3207     // exists with focusable content, return that content instead of the next
  3208     // document. If currentPopup is set, then, another panel may exist. If no
  3209     // such panel exists, then continue on to check the next document.
  3210     // When moving backwards, and the starting content is in a panel, then
  3211     // check for additional panels in the starting document. If the starting
  3212     // content is not in a panel, move back to the previous document and check
  3213     // for panels there.
  3215     bool checkPopups = false;
  3216     nsCOMPtr<nsPIDOMWindow> nextFrame = nullptr;
  3218     if (doc && (aForward || currentPopup)) {
  3219       nsIContent* popupContent = GetNextTabbablePanel(doc, currentPopup, aForward);
  3220       if (popupContent)
  3221         return popupContent;
  3223       if (!aForward && currentPopup) {
  3224         // The starting content was in a popup, yet no other popups were
  3225         // found. Move onto the starting content's document.
  3226         nextFrame = doc->GetWindow();
  3230     // Look for the next or previous document.
  3231     if (!nextFrame) {
  3232       if (aForward) {
  3233         GetNextDocShell(curItem, getter_AddRefs(nextItem));
  3234         if (!nextItem) {
  3235           // wrap around to the beginning, which is the top of the tree
  3236           startDocShell->GetRootTreeItem(getter_AddRefs(nextItem));
  3239       else {
  3240         GetPreviousDocShell(curItem, getter_AddRefs(nextItem));
  3241         if (!nextItem) {
  3242           // wrap around to the end, which is the last item in the tree
  3243           nsCOMPtr<nsIDocShellTreeItem> rootItem;
  3244           startDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
  3245           GetLastDocShell(rootItem, getter_AddRefs(nextItem));
  3248         // When going back to the previous document, check for any focusable
  3249         // popups in that previous document first.
  3250         checkPopups = true;
  3253       curItem = nextItem;
  3254       nextFrame = do_GetInterface(nextItem);
  3257     if (!nextFrame)
  3258       return nullptr;
  3260     // Clear currentPopup for the next iteration
  3261     currentPopup = nullptr;
  3263     // If event handling is suppressed, move on to the next document. Set
  3264     // content to null so that the popup check will be skipped on the next
  3265     // loop iteration.
  3266     doc = nextFrame->GetExtantDoc();
  3267     if (!doc || doc->EventHandlingSuppressed()) {
  3268       content = nullptr;
  3269       continue;
  3272     if (checkPopups) {
  3273       // When iterating backwards, check the panels of the previous document
  3274       // first. If a panel exists that has focusable content, focus that.
  3275       // Otherwise, continue on to focus the document.
  3276       nsIContent* popupContent = GetNextTabbablePanel(doc, nullptr, false);
  3277       if (popupContent)
  3278         return popupContent;
  3281     content = GetRootForFocus(nextFrame, doc, true, true);
  3282     if (content && !GetRootForFocus(nextFrame, doc, false, false)) {
  3283       // if the found content is in a chrome shell or a frameset, navigate
  3284       // forward one tabbable item so that the first item is focused. Note
  3285       // that we always go forward and not back here.
  3286       nsCOMPtr<nsIContent> nextFocus;
  3287       Element* rootElement = doc->GetRootElement();
  3288       nsIPresShell* presShell = doc->GetShell();
  3289       if (presShell) {
  3290         nsresult rv = GetNextTabbableContent(presShell, rootElement,
  3291                                              nullptr, rootElement,
  3292                                              true, 1, false,
  3293                                              getter_AddRefs(nextFocus));
  3294         return NS_SUCCEEDED(rv) ? nextFocus.get() : nullptr;
  3298   } while (!content);
  3300   return content;
  3303 void
  3304 nsFocusManager::GetFocusInSelection(nsPIDOMWindow* aWindow,
  3305                                     nsIContent* aStartSelection,
  3306                                     nsIContent* aEndSelection,
  3307                                     nsIContent** aFocusedContent)
  3309   *aFocusedContent = nullptr;
  3311   nsCOMPtr<nsIContent> testContent = aStartSelection;
  3312   nsCOMPtr<nsIContent> nextTestContent = aEndSelection;
  3314   nsCOMPtr<nsIContent> currentFocus = aWindow->GetFocusedNode();
  3316   // We now have the correct start node in selectionContent!
  3317   // Search for focusable elements, starting with selectionContent
  3319   // Method #1: Keep going up while we look - an ancestor might be focusable
  3320   // We could end the loop earlier, such as when we're no longer
  3321   // in the same frame, by comparing selectionContent->GetPrimaryFrame()
  3322   // with a variable holding the starting selectionContent
  3323   while (testContent) {
  3324     // Keep testing while selectionContent is equal to something,
  3325     // eventually we'll run out of ancestors
  3327     nsCOMPtr<nsIURI> uri;
  3328     if (testContent == currentFocus ||
  3329         testContent->IsLink(getter_AddRefs(uri))) {
  3330       NS_ADDREF(*aFocusedContent = testContent);
  3331       return;
  3334     // Get the parent
  3335     testContent = testContent->GetParent();
  3337     if (!testContent) {
  3338       // We run this loop again, checking the ancestor chain of the selection's end point
  3339       testContent = nextTestContent;
  3340       nextTestContent = nullptr;
  3344   // We couldn't find an anchor that was an ancestor of the selection start
  3345   // Method #2: look for anchor in selection's primary range (depth first search)
  3347   // Turn into nodes so that we can use GetNextSibling() and GetFirstChild()
  3348   nsCOMPtr<nsIDOMNode> selectionNode(do_QueryInterface(aStartSelection));
  3349   nsCOMPtr<nsIDOMNode> endSelectionNode(do_QueryInterface(aEndSelection));
  3350   nsCOMPtr<nsIDOMNode> testNode;
  3352   do {
  3353     testContent = do_QueryInterface(selectionNode);
  3355     // We're looking for any focusable link that could be part of the
  3356     // main document's selection.
  3357     nsCOMPtr<nsIURI> uri;
  3358     if (testContent == currentFocus ||
  3359         testContent->IsLink(getter_AddRefs(uri))) {
  3360       NS_ADDREF(*aFocusedContent = testContent);
  3361       return;
  3364     selectionNode->GetFirstChild(getter_AddRefs(testNode));
  3365     if (testNode) {
  3366       selectionNode = testNode;
  3367       continue;
  3370     if (selectionNode == endSelectionNode)
  3371       break;
  3372     selectionNode->GetNextSibling(getter_AddRefs(testNode));
  3373     if (testNode) {
  3374       selectionNode = testNode;
  3375       continue;
  3378     do {
  3379       selectionNode->GetParentNode(getter_AddRefs(testNode));
  3380       if (!testNode || testNode == endSelectionNode) {
  3381         selectionNode = nullptr;
  3382         break;
  3384       testNode->GetNextSibling(getter_AddRefs(selectionNode));
  3385       if (selectionNode)
  3386         break;
  3387       selectionNode = testNode;
  3388     } while (true);
  3390   while (selectionNode && selectionNode != endSelectionNode);
  3393 class PointerUnlocker : public nsRunnable
  3395 public:
  3396   PointerUnlocker()
  3398     MOZ_ASSERT(!PointerUnlocker::sActiveUnlocker);
  3399     PointerUnlocker::sActiveUnlocker = this;
  3402   ~PointerUnlocker()
  3404     if (PointerUnlocker::sActiveUnlocker == this) {
  3405       PointerUnlocker::sActiveUnlocker = nullptr;
  3409   NS_IMETHOD Run()
  3411     if (PointerUnlocker::sActiveUnlocker == this) {
  3412       PointerUnlocker::sActiveUnlocker = nullptr;
  3414     NS_ENSURE_STATE(nsFocusManager::GetFocusManager());
  3415     nsPIDOMWindow* focused =
  3416       nsFocusManager::GetFocusManager()->GetFocusedWindow();
  3417     nsCOMPtr<nsIDocument> pointerLockedDoc =
  3418       do_QueryReferent(EventStateManager::sPointerLockedDoc);
  3419     if (pointerLockedDoc &&
  3420         !nsContentUtils::IsInPointerLockContext(focused)) {
  3421       nsIDocument::UnlockPointer();
  3423     return NS_OK;
  3426   static PointerUnlocker* sActiveUnlocker;
  3427 };
  3429 PointerUnlocker*
  3430 PointerUnlocker::sActiveUnlocker = nullptr;
  3432 void
  3433 nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindow* aWindow)
  3435   if (!PointerUnlocker::sActiveUnlocker &&
  3436       nsContentUtils::IsInPointerLockContext(mFocusedWindow) &&
  3437       !nsContentUtils::IsInPointerLockContext(aWindow)) {
  3438     nsCOMPtr<nsIRunnable> runnable = new PointerUnlocker();
  3439     NS_DispatchToCurrentThread(runnable);
  3441   mFocusedWindow = aWindow;
  3444 void
  3445 nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
  3447   if (!sInstance) {
  3448     return;
  3451   if (sInstance->mActiveWindow) {
  3452     sInstance->mActiveWindow->
  3453       MarkUncollectableForCCGeneration(aGeneration);
  3455   if (sInstance->mFocusedWindow) {
  3456     sInstance->mFocusedWindow->
  3457       MarkUncollectableForCCGeneration(aGeneration);
  3459   if (sInstance->mWindowBeingLowered) {
  3460     sInstance->mWindowBeingLowered->
  3461       MarkUncollectableForCCGeneration(aGeneration);
  3463   if (sInstance->mFocusedContent) {
  3464     sInstance->mFocusedContent->OwnerDoc()->
  3465       MarkUncollectableForCCGeneration(aGeneration);
  3467   if (sInstance->mFirstBlurEvent) {
  3468     sInstance->mFirstBlurEvent->OwnerDoc()->
  3469       MarkUncollectableForCCGeneration(aGeneration);
  3471   if (sInstance->mFirstFocusEvent) {
  3472     sInstance->mFirstFocusEvent->OwnerDoc()->
  3473       MarkUncollectableForCCGeneration(aGeneration);
  3475   if (sInstance->mMouseDownEventHandlingDocument) {
  3476     sInstance->mMouseDownEventHandlingDocument->
  3477       MarkUncollectableForCCGeneration(aGeneration);
  3481 nsresult
  3482 NS_NewFocusManager(nsIFocusManager** aResult)
  3484   NS_IF_ADDREF(*aResult = nsFocusManager::GetFocusManager());
  3485   return NS_OK;

mercurial