dom/base/nsFocusManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/base/nsFocusManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3486 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/dom/TabParent.h"
    1.10 +
    1.11 +#include "nsFocusManager.h"
    1.12 +
    1.13 +#include "nsIInterfaceRequestorUtils.h"
    1.14 +#include "nsGkAtoms.h"
    1.15 +#include "nsContentUtils.h"
    1.16 +#include "nsIDocument.h"
    1.17 +#include "nsIDOMWindow.h"
    1.18 +#include "nsPIDOMWindow.h"
    1.19 +#include "nsIDOMElement.h"
    1.20 +#include "nsIDOMDocument.h"
    1.21 +#include "nsIDOMRange.h"
    1.22 +#include "nsIHTMLDocument.h"
    1.23 +#include "nsIDocShell.h"
    1.24 +#include "nsIDocShellTreeOwner.h"
    1.25 +#include "nsLayoutUtils.h"
    1.26 +#include "nsIPresShell.h"
    1.27 +#include "nsFrameTraversal.h"
    1.28 +#include "nsIWebNavigation.h"
    1.29 +#include "nsCaret.h"
    1.30 +#include "nsIBaseWindow.h"
    1.31 +#include "nsViewManager.h"
    1.32 +#include "nsFrameSelection.h"
    1.33 +#include "mozilla/dom/Selection.h"
    1.34 +#include "nsXULPopupManager.h"
    1.35 +#include "nsIScriptObjectPrincipal.h"
    1.36 +#include "nsIPrincipal.h"
    1.37 +#include "nsIObserverService.h"
    1.38 +#include "nsIObjectFrame.h"
    1.39 +#include "nsBindingManager.h"
    1.40 +#include "nsStyleCoord.h"
    1.41 +
    1.42 +#include "mozilla/ContentEvents.h"
    1.43 +#include "mozilla/dom/Element.h"
    1.44 +#include "mozilla/EventDispatcher.h"
    1.45 +#include "mozilla/EventStateManager.h"
    1.46 +#include "mozilla/EventStates.h"
    1.47 +#include "mozilla/IMEStateManager.h"
    1.48 +#include "mozilla/LookAndFeel.h"
    1.49 +#include "mozilla/Preferences.h"
    1.50 +#include "mozilla/Services.h"
    1.51 +#include <algorithm>
    1.52 +
    1.53 +#ifdef MOZ_XUL
    1.54 +#include "nsIDOMXULTextboxElement.h"
    1.55 +#include "nsIDOMXULMenuListElement.h"
    1.56 +#endif
    1.57 +
    1.58 +#ifdef ACCESSIBILITY
    1.59 +#include "nsAccessibilityService.h"
    1.60 +#endif
    1.61 +
    1.62 +#ifndef XP_MACOSX
    1.63 +#include "nsIScriptError.h"
    1.64 +#endif
    1.65 +
    1.66 +using namespace mozilla;
    1.67 +using namespace mozilla::dom;
    1.68 +using namespace mozilla::widget;
    1.69 +
    1.70 +#ifdef PR_LOGGING
    1.71 +
    1.72 +// Two types of focus pr logging are available:
    1.73 +//   'Focus' for normal focus manager calls
    1.74 +//   'FocusNavigation' for tab and document navigation
    1.75 +PRLogModuleInfo* gFocusLog;
    1.76 +PRLogModuleInfo* gFocusNavigationLog;
    1.77 +
    1.78 +#define LOGFOCUS(args) PR_LOG(gFocusLog, 4, args)
    1.79 +#define LOGFOCUSNAVIGATION(args) PR_LOG(gFocusNavigationLog, 4, args)
    1.80 +
    1.81 +#define LOGTAG(log, format, content)                            \
    1.82 +  {                                                             \
    1.83 +    nsAutoCString tag(NS_LITERAL_CSTRING("(none)"));            \
    1.84 +    if (content) {                                              \
    1.85 +      content->Tag()->ToUTF8String(tag);                        \
    1.86 +    }                                                           \
    1.87 +    PR_LOG(log, 4, (format, tag.get()));                        \
    1.88 +  }
    1.89 +
    1.90 +#define LOGCONTENT(format, content) LOGTAG(gFocusLog, format, content)
    1.91 +#define LOGCONTENTNAVIGATION(format, content) LOGTAG(gFocusNavigationLog, format, content)
    1.92 +
    1.93 +#else
    1.94 +
    1.95 +#define LOGFOCUS(args)
    1.96 +#define LOGFOCUSNAVIGATION(args)
    1.97 +#define LOGCONTENT(format, content)
    1.98 +#define LOGCONTENTNAVIGATION(format, content)
    1.99 +
   1.100 +#endif
   1.101 +
   1.102 +struct nsDelayedBlurOrFocusEvent
   1.103 +{
   1.104 +  nsDelayedBlurOrFocusEvent(uint32_t aType,
   1.105 +                            nsIPresShell* aPresShell,
   1.106 +                            nsIDocument* aDocument,
   1.107 +                            EventTarget* aTarget)
   1.108 +   : mType(aType),
   1.109 +     mPresShell(aPresShell),
   1.110 +     mDocument(aDocument),
   1.111 +     mTarget(aTarget) { }
   1.112 +
   1.113 +  nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent& aOther)
   1.114 +   : mType(aOther.mType),
   1.115 +     mPresShell(aOther.mPresShell),
   1.116 +     mDocument(aOther.mDocument),
   1.117 +     mTarget(aOther.mTarget) { }
   1.118 +
   1.119 +  uint32_t mType;
   1.120 +  nsCOMPtr<nsIPresShell> mPresShell;
   1.121 +  nsCOMPtr<nsIDocument> mDocument;
   1.122 +  nsCOMPtr<EventTarget> mTarget;
   1.123 +};
   1.124 +
   1.125 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager)
   1.126 +  NS_INTERFACE_MAP_ENTRY(nsIFocusManager)
   1.127 +  NS_INTERFACE_MAP_ENTRY(nsIObserver)
   1.128 +  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   1.129 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager)
   1.130 +NS_INTERFACE_MAP_END
   1.131 +
   1.132 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager)
   1.133 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager)
   1.134 +
   1.135 +NS_IMPL_CYCLE_COLLECTION(nsFocusManager,
   1.136 +                         mActiveWindow,
   1.137 +                         mFocusedWindow,
   1.138 +                         mFocusedContent,
   1.139 +                         mFirstBlurEvent,
   1.140 +                         mFirstFocusEvent,
   1.141 +                         mWindowBeingLowered)
   1.142 +
   1.143 +nsFocusManager* nsFocusManager::sInstance = nullptr;
   1.144 +bool nsFocusManager::sMouseFocusesFormControl = false;
   1.145 +bool nsFocusManager::sTestMode = false;
   1.146 +
   1.147 +static const char* kObservedPrefs[] = {
   1.148 +  "accessibility.browsewithcaret",
   1.149 +  "accessibility.tabfocus_applies_to_xul",
   1.150 +  "accessibility.mouse_focuses_formcontrol",
   1.151 +  "focusmanager.testmode",
   1.152 +  nullptr
   1.153 +};
   1.154 +
   1.155 +nsFocusManager::nsFocusManager()
   1.156 +{ }
   1.157 +
   1.158 +nsFocusManager::~nsFocusManager()
   1.159 +{
   1.160 +  Preferences::RemoveObservers(this, kObservedPrefs);
   1.161 +
   1.162 +  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1.163 +  if (obs) {
   1.164 +    obs->RemoveObserver(this, "xpcom-shutdown");
   1.165 +  }
   1.166 +}
   1.167 +
   1.168 +// static
   1.169 +nsresult
   1.170 +nsFocusManager::Init()
   1.171 +{
   1.172 +  nsFocusManager* fm = new nsFocusManager();
   1.173 +  NS_ENSURE_TRUE(fm, NS_ERROR_OUT_OF_MEMORY);
   1.174 +  NS_ADDREF(fm);
   1.175 +  sInstance = fm;
   1.176 +
   1.177 +#ifdef PR_LOGGING
   1.178 +  gFocusLog = PR_NewLogModule("Focus");
   1.179 +  gFocusNavigationLog = PR_NewLogModule("FocusNavigation");
   1.180 +#endif
   1.181 +
   1.182 +  nsIContent::sTabFocusModelAppliesToXUL =
   1.183 +    Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
   1.184 +                         nsIContent::sTabFocusModelAppliesToXUL);
   1.185 +
   1.186 +  sMouseFocusesFormControl =
   1.187 +    Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false);
   1.188 +
   1.189 +  sTestMode = Preferences::GetBool("focusmanager.testmode", false);
   1.190 +
   1.191 +  Preferences::AddWeakObservers(fm, kObservedPrefs);
   1.192 +
   1.193 +  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1.194 +  if (obs) {
   1.195 +    obs->AddObserver(fm, "xpcom-shutdown", true);
   1.196 +  }
   1.197 +
   1.198 +  return NS_OK;
   1.199 +}
   1.200 +
   1.201 +// static
   1.202 +void
   1.203 +nsFocusManager::Shutdown()
   1.204 +{
   1.205 +  NS_IF_RELEASE(sInstance);
   1.206 +}
   1.207 +
   1.208 +NS_IMETHODIMP
   1.209 +nsFocusManager::Observe(nsISupports *aSubject,
   1.210 +                        const char *aTopic,
   1.211 +                        const char16_t *aData)
   1.212 +{
   1.213 +  if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
   1.214 +    nsDependentString data(aData);
   1.215 +    if (data.EqualsLiteral("accessibility.browsewithcaret")) {
   1.216 +      UpdateCaretForCaretBrowsingMode();
   1.217 +    }
   1.218 +    else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
   1.219 +      nsIContent::sTabFocusModelAppliesToXUL =
   1.220 +        Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
   1.221 +                             nsIContent::sTabFocusModelAppliesToXUL);
   1.222 +    }
   1.223 +    else if (data.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
   1.224 +      sMouseFocusesFormControl =
   1.225 +        Preferences::GetBool("accessibility.mouse_focuses_formcontrol",
   1.226 +                             false);
   1.227 +    }
   1.228 +    else if (data.EqualsLiteral("focusmanager.testmode")) {
   1.229 +      sTestMode = Preferences::GetBool("focusmanager.testmode", false);
   1.230 +    }
   1.231 +  } else if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
   1.232 +    mActiveWindow = nullptr;
   1.233 +    mFocusedWindow = nullptr;
   1.234 +    mFocusedContent = nullptr;
   1.235 +    mFirstBlurEvent = nullptr;
   1.236 +    mFirstFocusEvent = nullptr;
   1.237 +    mWindowBeingLowered = nullptr;
   1.238 +    mDelayedBlurFocusEvents.Clear();
   1.239 +    mMouseDownEventHandlingDocument = nullptr;
   1.240 +  }
   1.241 +
   1.242 +  return NS_OK;
   1.243 +}
   1.244 +
   1.245 +// given a frame content node, retrieve the nsIDOMWindow displayed in it 
   1.246 +static nsPIDOMWindow*
   1.247 +GetContentWindow(nsIContent* aContent)
   1.248 +{
   1.249 +  nsIDocument* doc = aContent->GetCurrentDoc();
   1.250 +  if (doc) {
   1.251 +    nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
   1.252 +    if (subdoc)
   1.253 +      return subdoc->GetWindow();
   1.254 +  }
   1.255 +
   1.256 +  return nullptr;
   1.257 +}
   1.258 +
   1.259 +// get the current window for the given content node 
   1.260 +static nsPIDOMWindow*
   1.261 +GetCurrentWindow(nsIContent* aContent)
   1.262 +{
   1.263 +  nsIDocument *doc = aContent->GetCurrentDoc();
   1.264 +  return doc ? doc->GetWindow() : nullptr;
   1.265 +}
   1.266 +
   1.267 +// static
   1.268 +nsIContent*
   1.269 +nsFocusManager::GetFocusedDescendant(nsPIDOMWindow* aWindow, bool aDeep,
   1.270 +                                     nsPIDOMWindow** aFocusedWindow)
   1.271 +{
   1.272 +  NS_ENSURE_TRUE(aWindow, nullptr);
   1.273 +
   1.274 +  *aFocusedWindow = nullptr;
   1.275 +
   1.276 +  nsIContent* currentContent = nullptr;
   1.277 +  nsPIDOMWindow* window = aWindow->GetOuterWindow();
   1.278 +  while (window) {
   1.279 +    *aFocusedWindow = window;
   1.280 +    currentContent = window->GetFocusedNode();
   1.281 +    if (!currentContent || !aDeep)
   1.282 +      break;
   1.283 +
   1.284 +    window = GetContentWindow(currentContent);
   1.285 +  }
   1.286 +
   1.287 +  NS_IF_ADDREF(*aFocusedWindow);
   1.288 +
   1.289 +  return currentContent;
   1.290 +}
   1.291 +
   1.292 +// static
   1.293 +nsIContent*
   1.294 +nsFocusManager::GetRedirectedFocus(nsIContent* aContent)
   1.295 +{
   1.296 +#ifdef MOZ_XUL
   1.297 +  if (aContent->IsXUL()) {
   1.298 +    nsCOMPtr<nsIDOMNode> inputField;
   1.299 +
   1.300 +    nsCOMPtr<nsIDOMXULTextBoxElement> textbox = do_QueryInterface(aContent);
   1.301 +    if (textbox) {
   1.302 +      textbox->GetInputField(getter_AddRefs(inputField));
   1.303 +    }
   1.304 +    else {
   1.305 +      nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aContent);
   1.306 +      if (menulist) {
   1.307 +        menulist->GetInputField(getter_AddRefs(inputField));
   1.308 +      }
   1.309 +      else if (aContent->Tag() == nsGkAtoms::scale) {
   1.310 +        nsCOMPtr<nsIDocument> doc = aContent->GetCurrentDoc();
   1.311 +        if (!doc)
   1.312 +          return nullptr;
   1.313 +
   1.314 +        nsINodeList* children = doc->BindingManager()->GetAnonymousNodesFor(aContent);
   1.315 +        if (children) {
   1.316 +          nsIContent* child = children->Item(0);
   1.317 +          if (child && child->Tag() == nsGkAtoms::slider)
   1.318 +            return child;
   1.319 +        }
   1.320 +      }
   1.321 +    }
   1.322 +
   1.323 +    if (inputField) {
   1.324 +      nsCOMPtr<nsIContent> retval = do_QueryInterface(inputField);
   1.325 +      return retval;
   1.326 +    }
   1.327 +  }
   1.328 +#endif
   1.329 +
   1.330 +  return nullptr;
   1.331 +}
   1.332 +
   1.333 +// static
   1.334 +InputContextAction::Cause
   1.335 +nsFocusManager::GetFocusMoveActionCause(uint32_t aFlags)
   1.336 +{
   1.337 +  if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
   1.338 +    return InputContextAction::CAUSE_MOUSE;
   1.339 +  } else if (aFlags & nsIFocusManager::FLAG_BYKEY) {
   1.340 +    return InputContextAction::CAUSE_KEY;
   1.341 +  }
   1.342 +  return InputContextAction::CAUSE_UNKNOWN;
   1.343 +}
   1.344 +
   1.345 +NS_IMETHODIMP
   1.346 +nsFocusManager::GetActiveWindow(nsIDOMWindow** aWindow)
   1.347 +{
   1.348 +  NS_IF_ADDREF(*aWindow = mActiveWindow);
   1.349 +  return NS_OK;
   1.350 +}
   1.351 +
   1.352 +NS_IMETHODIMP
   1.353 +nsFocusManager::SetActiveWindow(nsIDOMWindow* aWindow)
   1.354 +{
   1.355 +  // only top-level windows can be made active
   1.356 +  nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aWindow);
   1.357 +  if (piWindow)
   1.358 +      piWindow = piWindow->GetOuterWindow();
   1.359 +
   1.360 +  NS_ENSURE_TRUE(piWindow && (piWindow == piWindow->GetPrivateRoot()),
   1.361 +                 NS_ERROR_INVALID_ARG);
   1.362 +
   1.363 +  RaiseWindow(piWindow);
   1.364 +  return NS_OK;
   1.365 +}
   1.366 +
   1.367 +NS_IMETHODIMP
   1.368 +nsFocusManager::GetFocusedWindow(nsIDOMWindow** aFocusedWindow)
   1.369 +{
   1.370 +  NS_IF_ADDREF(*aFocusedWindow = mFocusedWindow);
   1.371 +  return NS_OK;
   1.372 +}
   1.373 +
   1.374 +NS_IMETHODIMP nsFocusManager::SetFocusedWindow(nsIDOMWindow* aWindowToFocus)
   1.375 +{
   1.376 +  LOGFOCUS(("<<SetFocusedWindow begin>>"));
   1.377 +
   1.378 +  nsCOMPtr<nsPIDOMWindow> windowToFocus(do_QueryInterface(aWindowToFocus));
   1.379 +  NS_ENSURE_TRUE(windowToFocus, NS_ERROR_FAILURE);
   1.380 +
   1.381 +  windowToFocus = windowToFocus->GetOuterWindow();
   1.382 +
   1.383 +  nsCOMPtr<nsIContent> frameContent =
   1.384 +    do_QueryInterface(windowToFocus->GetFrameElementInternal());
   1.385 +  if (frameContent) {
   1.386 +    // pass false for aFocusChanged so that the caret does not get updated
   1.387 +    // and scrolling does not occur.
   1.388 +    SetFocusInner(frameContent, 0, false, true);
   1.389 +  }
   1.390 +  else {
   1.391 +    // this is a top-level window. If the window has a child frame focused,
   1.392 +    // clear the focus. Otherwise, focus should already be in this frame, or
   1.393 +    // already cleared. This ensures that focus will be in this frame and not
   1.394 +    // in a child.
   1.395 +    nsIContent* content = windowToFocus->GetFocusedNode();
   1.396 +    if (content) {
   1.397 +      nsCOMPtr<nsIDOMWindow> childWindow = GetContentWindow(content);
   1.398 +      if (childWindow)
   1.399 +        ClearFocus(windowToFocus);
   1.400 +    }
   1.401 +  }
   1.402 +
   1.403 +  nsCOMPtr<nsPIDOMWindow> rootWindow = windowToFocus->GetPrivateRoot();
   1.404 +  if (rootWindow)
   1.405 +    RaiseWindow(rootWindow);
   1.406 +
   1.407 +  LOGFOCUS(("<<SetFocusedWindow end>>"));
   1.408 +
   1.409 +  return NS_OK;
   1.410 +}
   1.411 +
   1.412 +NS_IMETHODIMP
   1.413 +nsFocusManager::GetFocusedElement(nsIDOMElement** aFocusedElement)
   1.414 +{
   1.415 +  if (mFocusedContent)
   1.416 +    CallQueryInterface(mFocusedContent, aFocusedElement);
   1.417 +  else
   1.418 +    *aFocusedElement = nullptr;
   1.419 +  return NS_OK;
   1.420 +}
   1.421 +
   1.422 +NS_IMETHODIMP
   1.423 +nsFocusManager::GetLastFocusMethod(nsIDOMWindow* aWindow, uint32_t* aLastFocusMethod)
   1.424 +{
   1.425 +  // the focus method is stored on the inner window
   1.426 +  nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   1.427 +  if (window)
   1.428 +    window = window->GetCurrentInnerWindow();
   1.429 +  if (!window)
   1.430 +    window = mFocusedWindow;
   1.431 +
   1.432 +  *aLastFocusMethod = window ? window->GetFocusMethod() : 0;
   1.433 +
   1.434 +  NS_ASSERTION((*aLastFocusMethod & FOCUSMETHOD_MASK) == *aLastFocusMethod,
   1.435 +               "invalid focus method");
   1.436 +  return NS_OK;
   1.437 +}
   1.438 +
   1.439 +NS_IMETHODIMP
   1.440 +nsFocusManager::SetFocus(nsIDOMElement* aElement, uint32_t aFlags)
   1.441 +{
   1.442 +  LOGFOCUS(("<<SetFocus begin>>"));
   1.443 +
   1.444 +  nsCOMPtr<nsIContent> newFocus = do_QueryInterface(aElement);
   1.445 +  NS_ENSURE_ARG(newFocus);
   1.446 +
   1.447 +  SetFocusInner(newFocus, aFlags, true, true);
   1.448 +
   1.449 +  LOGFOCUS(("<<SetFocus end>>"));
   1.450 +
   1.451 +  return NS_OK;
   1.452 +}
   1.453 +
   1.454 +NS_IMETHODIMP
   1.455 +nsFocusManager::ElementIsFocusable(nsIDOMElement* aElement, uint32_t aFlags,
   1.456 +                                   bool* aIsFocusable)
   1.457 +{
   1.458 +  NS_ENSURE_TRUE(aElement, NS_ERROR_INVALID_ARG);
   1.459 +
   1.460 +  nsCOMPtr<nsIContent> aContent = do_QueryInterface(aElement);
   1.461 +
   1.462 +  *aIsFocusable = CheckIfFocusable(aContent, aFlags) != nullptr;
   1.463 +
   1.464 +  return NS_OK;
   1.465 +}
   1.466 +
   1.467 +NS_IMETHODIMP
   1.468 +nsFocusManager::MoveFocus(nsIDOMWindow* aWindow, nsIDOMElement* aStartElement,
   1.469 +                          uint32_t aType, uint32_t aFlags, nsIDOMElement** aElement)
   1.470 +{
   1.471 +  *aElement = nullptr;
   1.472 +
   1.473 +#ifdef PR_LOGGING
   1.474 +  LOGFOCUS(("<<MoveFocus begin Type: %d Flags: %x>>", aType, aFlags));
   1.475 +
   1.476 +  if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG) && mFocusedWindow) {
   1.477 +    nsIDocument* doc = mFocusedWindow->GetExtantDoc();
   1.478 +    if (doc && doc->GetDocumentURI()) {
   1.479 +      nsAutoCString spec;
   1.480 +      doc->GetDocumentURI()->GetSpec(spec);
   1.481 +      LOGFOCUS((" Focused Window: %p %s", mFocusedWindow.get(), spec.get()));
   1.482 +    }
   1.483 +  }
   1.484 +
   1.485 +  LOGCONTENT("  Current Focus: %s", mFocusedContent.get());
   1.486 +#endif
   1.487 +
   1.488 +  // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
   1.489 +  // the other focus methods is already set, or we're just moving to the root
   1.490 +  // or caret position.
   1.491 +  if (aType != MOVEFOCUS_ROOT && aType != MOVEFOCUS_CARET &&
   1.492 +      (aFlags & FOCUSMETHOD_MASK) == 0) {
   1.493 +    aFlags |= FLAG_BYMOVEFOCUS;
   1.494 +  }
   1.495 +
   1.496 +  nsCOMPtr<nsPIDOMWindow> window;
   1.497 +  nsCOMPtr<nsIContent> startContent;
   1.498 +  if (aStartElement) {
   1.499 +    startContent = do_QueryInterface(aStartElement);
   1.500 +    NS_ENSURE_TRUE(startContent, NS_ERROR_INVALID_ARG);
   1.501 +
   1.502 +    window = GetCurrentWindow(startContent);
   1.503 +  }
   1.504 +  else {
   1.505 +    window = aWindow ? do_QueryInterface(aWindow) : mFocusedWindow;
   1.506 +    NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   1.507 +    window = window->GetOuterWindow();
   1.508 +  }
   1.509 +
   1.510 +  NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   1.511 +
   1.512 +  bool noParentTraversal = aFlags & FLAG_NOPARENTFRAME;
   1.513 +  nsCOMPtr<nsIContent> newFocus;
   1.514 +  nsresult rv = DetermineElementToMoveFocus(window, startContent, aType, noParentTraversal,
   1.515 +                                            getter_AddRefs(newFocus));
   1.516 +  NS_ENSURE_SUCCESS(rv, rv);
   1.517 +
   1.518 +  LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get());
   1.519 +
   1.520 +  if (newFocus) {
   1.521 +    // for caret movement, pass false for the aFocusChanged argument,
   1.522 +    // otherwise the caret will end up moving to the focus position. This
   1.523 +    // would be a problem because the caret would move to the beginning of the
   1.524 +    // focused link making it impossible to navigate the caret over a link.
   1.525 +    SetFocusInner(newFocus, aFlags, aType != MOVEFOCUS_CARET, true);
   1.526 +    CallQueryInterface(newFocus, aElement);
   1.527 +  }
   1.528 +  else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) {
   1.529 +    // no content was found, so clear the focus for these two types.
   1.530 +    ClearFocus(window);
   1.531 +  }
   1.532 +
   1.533 +  LOGFOCUS(("<<MoveFocus end>>"));
   1.534 +
   1.535 +  return NS_OK;
   1.536 +}
   1.537 +
   1.538 +NS_IMETHODIMP
   1.539 +nsFocusManager::ClearFocus(nsIDOMWindow* aWindow)
   1.540 +{
   1.541 +  LOGFOCUS(("<<ClearFocus begin>>"));
   1.542 +
   1.543 +  // if the window to clear is the focused window or an ancestor of the
   1.544 +  // focused window, then blur the existing focused content. Otherwise, the
   1.545 +  // focus is somewhere else so just update the current node.
   1.546 +  nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   1.547 +  NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   1.548 +
   1.549 +  window = window->GetOuterWindow();
   1.550 +  NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   1.551 +
   1.552 +  if (IsSameOrAncestor(window, mFocusedWindow)) {
   1.553 +    bool isAncestor = (window != mFocusedWindow);
   1.554 +    if (Blur(window, nullptr, isAncestor, true)) {
   1.555 +      // if we are clearing the focus on an ancestor of the focused window,
   1.556 +      // the ancestor will become the new focused window, so focus it
   1.557 +      if (isAncestor)
   1.558 +        Focus(window, nullptr, 0, true, false, false, true);
   1.559 +    }
   1.560 +  }
   1.561 +  else {
   1.562 +    window->SetFocusedNode(nullptr);
   1.563 +  }
   1.564 +
   1.565 +  LOGFOCUS(("<<ClearFocus end>>"));
   1.566 +
   1.567 +  return NS_OK;
   1.568 +}
   1.569 +
   1.570 +NS_IMETHODIMP
   1.571 +nsFocusManager::GetFocusedElementForWindow(nsIDOMWindow* aWindow,
   1.572 +                                           bool aDeep,
   1.573 +                                           nsIDOMWindow** aFocusedWindow,
   1.574 +                                           nsIDOMElement** aElement)
   1.575 +{
   1.576 +  *aElement = nullptr;
   1.577 +  if (aFocusedWindow)
   1.578 +    *aFocusedWindow = nullptr;
   1.579 +
   1.580 +  nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   1.581 +  NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   1.582 +
   1.583 +  window = window->GetOuterWindow();
   1.584 +  NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   1.585 +
   1.586 +  nsCOMPtr<nsPIDOMWindow> focusedWindow;
   1.587 +  nsCOMPtr<nsIContent> focusedContent =
   1.588 +    GetFocusedDescendant(window, aDeep, getter_AddRefs(focusedWindow));
   1.589 +  if (focusedContent)
   1.590 +    CallQueryInterface(focusedContent, aElement);
   1.591 +
   1.592 +  if (aFocusedWindow)
   1.593 +    NS_IF_ADDREF(*aFocusedWindow = focusedWindow);
   1.594 +
   1.595 +  return NS_OK;
   1.596 +}
   1.597 +
   1.598 +NS_IMETHODIMP
   1.599 +nsFocusManager::MoveCaretToFocus(nsIDOMWindow* aWindow)
   1.600 +{
   1.601 +  nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
   1.602 +  nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
   1.603 +  if (dsti) {
   1.604 +    if (dsti->ItemType() != nsIDocShellTreeItem::typeChrome) {
   1.605 +      nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(dsti);
   1.606 +      NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
   1.607 +
   1.608 +      // don't move the caret for editable documents
   1.609 +      bool isEditable;
   1.610 +      docShell->GetEditable(&isEditable);
   1.611 +      if (isEditable)
   1.612 +        return NS_OK;
   1.613 +
   1.614 +      nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
   1.615 +      NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
   1.616 +
   1.617 +      nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   1.618 +      nsCOMPtr<nsIContent> content = window->GetFocusedNode();
   1.619 +      if (content)
   1.620 +        MoveCaretToFocus(presShell, content);
   1.621 +    }
   1.622 +  }
   1.623 +
   1.624 +  return NS_OK;
   1.625 +}
   1.626 +
   1.627 +NS_IMETHODIMP
   1.628 +nsFocusManager::WindowRaised(nsIDOMWindow* aWindow)
   1.629 +{
   1.630 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   1.631 +  NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
   1.632 +
   1.633 +#ifdef PR_LOGGING
   1.634 +  if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
   1.635 +    LOGFOCUS(("Window %p Raised [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
   1.636 +    nsAutoCString spec;
   1.637 +    nsIDocument* doc = window->GetExtantDoc();
   1.638 +    if (doc && doc->GetDocumentURI()) {
   1.639 +      doc->GetDocumentURI()->GetSpec(spec);
   1.640 +      LOGFOCUS(("  Raised Window: %p %s", aWindow, spec.get()));
   1.641 +    }
   1.642 +    if (mActiveWindow) {
   1.643 +      doc = mActiveWindow->GetExtantDoc();
   1.644 +      if (doc && doc->GetDocumentURI()) {
   1.645 +        doc->GetDocumentURI()->GetSpec(spec);
   1.646 +        LOGFOCUS(("  Active Window: %p %s", mActiveWindow.get(), spec.get()));
   1.647 +      }
   1.648 +    }
   1.649 +  }
   1.650 +#endif
   1.651 +
   1.652 +  if (mActiveWindow == window) {
   1.653 +    // The window is already active, so there is no need to focus anything,
   1.654 +    // but make sure that the right widget is focused. This is a special case
   1.655 +    // for Windows because when restoring a minimized window, a second
   1.656 +    // activation will occur and the top-level widget could be focused instead
   1.657 +    // of the child we want. We solve this by calling SetFocus to ensure that
   1.658 +    // what the focus manager thinks should be the current widget is actually
   1.659 +    // focused.
   1.660 +    EnsureCurrentWidgetFocused();
   1.661 +    return NS_OK;
   1.662 +  }
   1.663 +
   1.664 +  // lower the existing window, if any. This shouldn't happen usually.
   1.665 +  if (mActiveWindow)
   1.666 +    WindowLowered(mActiveWindow);
   1.667 +
   1.668 +  nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(aWindow));
   1.669 +  nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(webnav));
   1.670 +  // If there's no docShellAsItem, this window must have been closed,
   1.671 +  // in that case there is no tree owner.
   1.672 +  NS_ENSURE_TRUE(docShellAsItem, NS_OK);
   1.673 +
   1.674 +  // set this as the active window
   1.675 +  mActiveWindow = window;
   1.676 +
   1.677 +  // ensure that the window is enabled and visible
   1.678 +  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   1.679 +  docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
   1.680 +  nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
   1.681 +  if (baseWindow) {
   1.682 +    bool isEnabled = true;
   1.683 +    if (NS_SUCCEEDED(baseWindow->GetEnabled(&isEnabled)) && !isEnabled) {
   1.684 +      return NS_ERROR_FAILURE;
   1.685 +    }
   1.686 +
   1.687 +    if (!sTestMode) {
   1.688 +      baseWindow->SetVisibility(true);
   1.689 +    }
   1.690 +  }
   1.691 +
   1.692 +  // inform the DOM window that it has activated, so that the active attribute
   1.693 +  // is updated on the window
   1.694 +  window->ActivateOrDeactivate(true);
   1.695 +
   1.696 +  // send activate event
   1.697 +  nsContentUtils::DispatchTrustedEvent(window->GetExtantDoc(),
   1.698 +                                       window,
   1.699 +                                       NS_LITERAL_STRING("activate"),
   1.700 +                                       true, true, nullptr);
   1.701 +
   1.702 +  // retrieve the last focused element within the window that was raised
   1.703 +  nsCOMPtr<nsPIDOMWindow> currentWindow;
   1.704 +  nsCOMPtr<nsIContent> currentFocus =
   1.705 +    GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
   1.706 +
   1.707 +  NS_ASSERTION(currentWindow, "window raised with no window current");
   1.708 +  if (!currentWindow)
   1.709 +    return NS_OK;
   1.710 +
   1.711 +  nsCOMPtr<nsIDocShell> currentDocShell = currentWindow->GetDocShell();
   1.712 +
   1.713 +  nsCOMPtr<nsIPresShell> presShell = currentDocShell->GetPresShell();
   1.714 +  if (presShell) {
   1.715 +    // disable selection mousedown state on activation
   1.716 +    // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
   1.717 +    nsRefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
   1.718 +    frameSelection->SetMouseDownState(false);
   1.719 +  }
   1.720 +
   1.721 +  Focus(currentWindow, currentFocus, 0, true, false, true, true);
   1.722 +
   1.723 +  return NS_OK;
   1.724 +}
   1.725 +
   1.726 +NS_IMETHODIMP
   1.727 +nsFocusManager::WindowLowered(nsIDOMWindow* aWindow)
   1.728 +{
   1.729 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   1.730 +  NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
   1.731 +
   1.732 +#ifdef PR_LOGGING
   1.733 +  if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
   1.734 +    LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
   1.735 +    nsAutoCString spec;
   1.736 +    nsIDocument* doc = window->GetExtantDoc();
   1.737 +    if (doc && doc->GetDocumentURI()) {
   1.738 +      doc->GetDocumentURI()->GetSpec(spec);
   1.739 +      LOGFOCUS(("  Lowered Window: %s", spec.get()));
   1.740 +    }
   1.741 +    if (mActiveWindow) {
   1.742 +      doc = mActiveWindow->GetExtantDoc();
   1.743 +      if (doc && doc->GetDocumentURI()) {
   1.744 +        doc->GetDocumentURI()->GetSpec(spec);
   1.745 +        LOGFOCUS(("  Active Window: %s", spec.get()));
   1.746 +      }
   1.747 +    }
   1.748 +  }
   1.749 +#endif
   1.750 +
   1.751 +  if (mActiveWindow != window)
   1.752 +    return NS_OK;
   1.753 +
   1.754 +  // clear the mouse capture as the active window has changed
   1.755 +  nsIPresShell::SetCapturingContent(nullptr, 0);
   1.756 +
   1.757 +  // inform the DOM window that it has deactivated, so that the active
   1.758 +  // attribute is updated on the window
   1.759 +  window->ActivateOrDeactivate(false);
   1.760 +
   1.761 +  // send deactivate event
   1.762 +  nsContentUtils::DispatchTrustedEvent(window->GetExtantDoc(),
   1.763 +                                       window,
   1.764 +                                       NS_LITERAL_STRING("deactivate"),
   1.765 +                                       true, true, nullptr);
   1.766 +
   1.767 +  // keep track of the window being lowered, so that attempts to raise the
   1.768 +  // window can be prevented until we return. Otherwise, focus can get into
   1.769 +  // an unusual state.
   1.770 +  mWindowBeingLowered = mActiveWindow;
   1.771 +  mActiveWindow = nullptr;
   1.772 +
   1.773 +  if (mFocusedWindow)
   1.774 +    Blur(nullptr, nullptr, true, true);
   1.775 +
   1.776 +  mWindowBeingLowered = nullptr;
   1.777 +
   1.778 +  return NS_OK;
   1.779 +}
   1.780 +
   1.781 +nsresult
   1.782 +nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
   1.783 +{
   1.784 +  NS_ENSURE_ARG(aDocument);
   1.785 +  NS_ENSURE_ARG(aContent);
   1.786 +
   1.787 +  nsPIDOMWindow *window = aDocument->GetWindow();
   1.788 +  if (!window)
   1.789 +    return NS_OK;
   1.790 +
   1.791 +  // if the content is currently focused in the window, or is an ancestor
   1.792 +  // of the currently focused element, reset the focus within that window.
   1.793 +  nsIContent* content = window->GetFocusedNode();
   1.794 +  if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) {
   1.795 +    bool shouldShowFocusRing = window->ShouldShowFocusRing();
   1.796 +    window->SetFocusedNode(nullptr);
   1.797 +
   1.798 +    // if this window is currently focused, clear the global focused
   1.799 +    // element as well, but don't fire any events.
   1.800 +    if (window == mFocusedWindow) {
   1.801 +      mFocusedContent = nullptr;
   1.802 +    }
   1.803 +    else {
   1.804 +      // Check if the node that was focused is an iframe or similar by looking
   1.805 +      // if it has a subdocument. This would indicate that this focused iframe
   1.806 +      // and its descendants will be going away. We will need to move the
   1.807 +      // focus somewhere else, so just clear the focus in the toplevel window
   1.808 +      // so that no element is focused.
   1.809 +      nsIDocument* subdoc = aDocument->GetSubDocumentFor(content);
   1.810 +      if (subdoc) {
   1.811 +        nsCOMPtr<nsISupports> container = subdoc->GetContainer();
   1.812 +        nsCOMPtr<nsPIDOMWindow> childWindow = do_GetInterface(container);
   1.813 +        if (childWindow && IsSameOrAncestor(childWindow, mFocusedWindow)) {
   1.814 +          ClearFocus(mActiveWindow);
   1.815 +        }
   1.816 +      }
   1.817 +    }
   1.818 +
   1.819 +    NotifyFocusStateChange(content, shouldShowFocusRing, false);
   1.820 +  }
   1.821 +
   1.822 +  return NS_OK;
   1.823 +}
   1.824 +
   1.825 +NS_IMETHODIMP
   1.826 +nsFocusManager::WindowShown(nsIDOMWindow* aWindow, bool aNeedsFocus)
   1.827 +{
   1.828 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   1.829 +  NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   1.830 +
   1.831 +  window = window->GetOuterWindow();
   1.832 +
   1.833 +#ifdef PR_LOGGING
   1.834 +  if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
   1.835 +    LOGFOCUS(("Window %p Shown [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
   1.836 +    nsAutoCString spec;
   1.837 +    nsIDocument* doc = window->GetExtantDoc();
   1.838 +    if (doc && doc->GetDocumentURI()) {
   1.839 +      doc->GetDocumentURI()->GetSpec(spec);
   1.840 +      LOGFOCUS(("Shown Window: %s", spec.get()));
   1.841 +    }
   1.842 +
   1.843 +    if (mFocusedWindow) {
   1.844 +      doc = mFocusedWindow->GetExtantDoc();
   1.845 +      if (doc && doc->GetDocumentURI()) {
   1.846 +        doc->GetDocumentURI()->GetSpec(spec);
   1.847 +        LOGFOCUS((" Focused Window: %s", spec.get()));
   1.848 +      }
   1.849 +    }
   1.850 +  }
   1.851 +#endif
   1.852 +
   1.853 +  if (mFocusedWindow != window)
   1.854 +    return NS_OK;
   1.855 +
   1.856 +  if (aNeedsFocus) {
   1.857 +    nsCOMPtr<nsPIDOMWindow> currentWindow;
   1.858 +    nsCOMPtr<nsIContent> currentFocus =
   1.859 +      GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
   1.860 +    if (currentWindow)
   1.861 +      Focus(currentWindow, currentFocus, 0, true, false, false, true);
   1.862 +  }
   1.863 +  else {
   1.864 +    // Sometimes, an element in a window can be focused before the window is
   1.865 +    // visible, which would mean that the widget may not be properly focused.
   1.866 +    // When the window becomes visible, make sure the right widget is focused.
   1.867 +    EnsureCurrentWidgetFocused();
   1.868 +  }
   1.869 +
   1.870 +  return NS_OK;
   1.871 +}
   1.872 +
   1.873 +NS_IMETHODIMP
   1.874 +nsFocusManager::WindowHidden(nsIDOMWindow* aWindow)
   1.875 +{
   1.876 +  // if there is no window or it is not the same or an ancestor of the
   1.877 +  // currently focused window, just return, as the current focus will not
   1.878 +  // be affected.
   1.879 +
   1.880 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   1.881 +  NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
   1.882 +
   1.883 +  window = window->GetOuterWindow();
   1.884 +
   1.885 +#ifdef PR_LOGGING
   1.886 +  if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
   1.887 +    LOGFOCUS(("Window %p Hidden [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
   1.888 +    nsAutoCString spec;
   1.889 +    nsIDocument* doc = window->GetExtantDoc();
   1.890 +    if (doc && doc->GetDocumentURI()) {
   1.891 +      doc->GetDocumentURI()->GetSpec(spec);
   1.892 +      LOGFOCUS(("  Hide Window: %s", spec.get()));
   1.893 +    }
   1.894 +
   1.895 +    if (mFocusedWindow) {
   1.896 +      doc = mFocusedWindow->GetExtantDoc();
   1.897 +      if (doc && doc->GetDocumentURI()) {
   1.898 +        doc->GetDocumentURI()->GetSpec(spec);
   1.899 +        LOGFOCUS(("  Focused Window: %s", spec.get()));
   1.900 +      }
   1.901 +    }
   1.902 +
   1.903 +    if (mActiveWindow) {
   1.904 +      doc = mActiveWindow->GetExtantDoc();
   1.905 +      if (doc && doc->GetDocumentURI()) {
   1.906 +        doc->GetDocumentURI()->GetSpec(spec);
   1.907 +        LOGFOCUS(("  Active Window: %s", spec.get()));
   1.908 +      }
   1.909 +    }
   1.910 +  }
   1.911 +#endif
   1.912 +
   1.913 +  if (!IsSameOrAncestor(window, mFocusedWindow))
   1.914 +    return NS_OK;
   1.915 +
   1.916 +  // at this point, we know that the window being hidden is either the focused
   1.917 +  // window, or an ancestor of the focused window. Either way, the focus is no
   1.918 +  // longer valid, so it needs to be updated.
   1.919 +
   1.920 +  nsCOMPtr<nsIContent> oldFocusedContent = mFocusedContent.forget();
   1.921 +
   1.922 +  nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
   1.923 +  nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
   1.924 +
   1.925 +  if (oldFocusedContent && oldFocusedContent->IsInDoc()) {
   1.926 +    NotifyFocusStateChange(oldFocusedContent,
   1.927 +                           mFocusedWindow->ShouldShowFocusRing(),
   1.928 +                           false);
   1.929 +    window->UpdateCommands(NS_LITERAL_STRING("focus"));
   1.930 +
   1.931 +    if (presShell) {
   1.932 +      SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell,
   1.933 +                           oldFocusedContent->GetCurrentDoc(),
   1.934 +                           oldFocusedContent, 1, false);
   1.935 +    }
   1.936 +  }
   1.937 +
   1.938 +  nsPresContext* focusedPresContext =
   1.939 +    presShell ? presShell->GetPresContext() : nullptr;
   1.940 +  IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
   1.941 +                                 GetFocusMoveActionCause(0));
   1.942 +  if (presShell) {
   1.943 +    SetCaretVisible(presShell, false, nullptr);
   1.944 +  }
   1.945 +
   1.946 +  // if the docshell being hidden is being destroyed, then we want to move
   1.947 +  // focus somewhere else. Call ClearFocus on the toplevel window, which
   1.948 +  // will have the effect of clearing the focus and moving the focused window
   1.949 +  // to the toplevel window. But if the window isn't being destroyed, we are
   1.950 +  // likely just loading a new document in it, so we want to maintain the
   1.951 +  // focused window so that the new document gets properly focused.
   1.952 +  bool beingDestroyed;
   1.953 +  nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell();
   1.954 +  docShellBeingHidden->IsBeingDestroyed(&beingDestroyed);
   1.955 +  if (beingDestroyed) {
   1.956 +    // There is usually no need to do anything if a toplevel window is going
   1.957 +    // away, as we assume that WindowLowered will be called. However, this may
   1.958 +    // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
   1.959 +    // a leak. So if the active window is being destroyed, call WindowLowered
   1.960 +    // directly.
   1.961 +    NS_ASSERTION(mFocusedWindow->IsOuterWindow(), "outer window expected");
   1.962 +    if (mActiveWindow == mFocusedWindow || mActiveWindow == window)
   1.963 +      WindowLowered(mActiveWindow);
   1.964 +    else
   1.965 +      ClearFocus(mActiveWindow);
   1.966 +    return NS_OK;
   1.967 +  }
   1.968 +
   1.969 +  // if the window being hidden is an ancestor of the focused window, adjust
   1.970 +  // the focused window so that it points to the one being hidden. This
   1.971 +  // ensures that the focused window isn't in a chain of frames that doesn't
   1.972 +  // exist any more.
   1.973 +  if (window != mFocusedWindow) {
   1.974 +    nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(mFocusedWindow));
   1.975 +    nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
   1.976 +    if (dsti) {
   1.977 +      nsCOMPtr<nsIDocShellTreeItem> parentDsti;
   1.978 +      dsti->GetParent(getter_AddRefs(parentDsti));
   1.979 +      nsCOMPtr<nsPIDOMWindow> parentWindow = do_GetInterface(parentDsti);
   1.980 +      if (parentWindow)
   1.981 +        parentWindow->SetFocusedNode(nullptr);
   1.982 +    }
   1.983 +
   1.984 +    SetFocusedWindowInternal(window);
   1.985 +  }
   1.986 +
   1.987 +  return NS_OK;
   1.988 +}
   1.989 +
   1.990 +NS_IMETHODIMP
   1.991 +nsFocusManager::FireDelayedEvents(nsIDocument* aDocument)
   1.992 +{
   1.993 +  NS_ENSURE_ARG(aDocument);
   1.994 +
   1.995 +  // fire any delayed focus and blur events in the same order that they were added
   1.996 +  for (uint32_t i = 0; i < mDelayedBlurFocusEvents.Length(); i++) {
   1.997 +    if (mDelayedBlurFocusEvents[i].mDocument == aDocument) {
   1.998 +      if (!aDocument->GetInnerWindow() ||
   1.999 +          !aDocument->GetInnerWindow()->IsCurrentInnerWindow()) {
  1.1000 +        // If the document was navigated away from or is defunct, don't bother
  1.1001 +        // firing events on it. Note the symmetry between this condition and
  1.1002 +        // the similar one in nsDocument.cpp:FireOrClearDelayedEvents.
  1.1003 +        mDelayedBlurFocusEvents.RemoveElementAt(i);
  1.1004 +        --i;
  1.1005 +      } else if (!aDocument->EventHandlingSuppressed()) {
  1.1006 +        uint32_t type = mDelayedBlurFocusEvents[i].mType;
  1.1007 +        nsCOMPtr<EventTarget> target = mDelayedBlurFocusEvents[i].mTarget;
  1.1008 +        nsCOMPtr<nsIPresShell> presShell = mDelayedBlurFocusEvents[i].mPresShell;
  1.1009 +        mDelayedBlurFocusEvents.RemoveElementAt(i);
  1.1010 +        SendFocusOrBlurEvent(type, presShell, aDocument, target, 0, false);
  1.1011 +        --i;
  1.1012 +      }
  1.1013 +    }
  1.1014 +  }
  1.1015 +
  1.1016 +  return NS_OK;
  1.1017 +}
  1.1018 +
  1.1019 +NS_IMETHODIMP
  1.1020 +nsFocusManager::FocusPlugin(nsIContent* aContent)
  1.1021 +{
  1.1022 +  NS_ENSURE_ARG(aContent);
  1.1023 +  SetFocusInner(aContent, 0, true, false);
  1.1024 +  return NS_OK;
  1.1025 +}
  1.1026 +
  1.1027 +/* static */
  1.1028 +void
  1.1029 +nsFocusManager::NotifyFocusStateChange(nsIContent* aContent,
  1.1030 +                                       bool aWindowShouldShowFocusRing,
  1.1031 +                                       bool aGettingFocus)
  1.1032 +{
  1.1033 +  if (!aContent->IsElement()) {
  1.1034 +    return;
  1.1035 +  }
  1.1036 +  EventStates eventState = NS_EVENT_STATE_FOCUS;
  1.1037 +  if (aWindowShouldShowFocusRing) {
  1.1038 +    eventState |= NS_EVENT_STATE_FOCUSRING;
  1.1039 +  }
  1.1040 +  if (aGettingFocus) {
  1.1041 +    aContent->AsElement()->AddStates(eventState);
  1.1042 +  } else {
  1.1043 +    aContent->AsElement()->RemoveStates(eventState);
  1.1044 +  }
  1.1045 +}
  1.1046 +
  1.1047 +// static
  1.1048 +void
  1.1049 +nsFocusManager::EnsureCurrentWidgetFocused()
  1.1050 +{
  1.1051 +  if (!mFocusedWindow || sTestMode)
  1.1052 +    return;
  1.1053 +
  1.1054 +  // get the main child widget for the focused window and ensure that the
  1.1055 +  // platform knows that this widget is focused.
  1.1056 +  nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
  1.1057 +  if (docShell) {
  1.1058 +    nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1.1059 +    if (presShell) {
  1.1060 +      nsViewManager* vm = presShell->GetViewManager();
  1.1061 +      if (vm) {
  1.1062 +        nsCOMPtr<nsIWidget> widget;
  1.1063 +        vm->GetRootWidget(getter_AddRefs(widget));
  1.1064 +        if (widget)
  1.1065 +          widget->SetFocus(false);
  1.1066 +      }
  1.1067 +    }
  1.1068 +  }
  1.1069 +}
  1.1070 +
  1.1071 +void
  1.1072 +nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags,
  1.1073 +                              bool aFocusChanged, bool aAdjustWidget)
  1.1074 +{
  1.1075 +  // if the element is not focusable, just return and leave the focus as is
  1.1076 +  nsCOMPtr<nsIContent> contentToFocus = CheckIfFocusable(aNewContent, aFlags);
  1.1077 +  if (!contentToFocus)
  1.1078 +    return;
  1.1079 +
  1.1080 +  // check if the element to focus is a frame (iframe) containing a child
  1.1081 +  // document. Frames are never directly focused; instead focusing a frame
  1.1082 +  // means focus what is inside the frame. To do this, the descendant content
  1.1083 +  // within the frame is retrieved and that will be focused instead.
  1.1084 +  nsCOMPtr<nsPIDOMWindow> newWindow;
  1.1085 +  nsCOMPtr<nsPIDOMWindow> subWindow = GetContentWindow(contentToFocus);
  1.1086 +  if (subWindow) {
  1.1087 +    contentToFocus = GetFocusedDescendant(subWindow, true, getter_AddRefs(newWindow));
  1.1088 +    // since a window is being refocused, clear aFocusChanged so that the
  1.1089 +    // caret position isn't updated.
  1.1090 +    aFocusChanged = false;
  1.1091 +  }
  1.1092 +
  1.1093 +  // unless it was set above, retrieve the window for the element to focus
  1.1094 +  if (!newWindow)
  1.1095 +    newWindow = GetCurrentWindow(contentToFocus);
  1.1096 +
  1.1097 +  // if the element is already focused, just return. Note that this happens
  1.1098 +  // after the frame check above so that we compare the element that will be
  1.1099 +  // focused rather than the frame it is in.
  1.1100 +  if (!newWindow || (newWindow == mFocusedWindow && contentToFocus == mFocusedContent))
  1.1101 +    return;
  1.1102 +
  1.1103 +  // don't allow focus to be placed in docshells or descendants of docshells
  1.1104 +  // that are being destroyed. Also, ensure that the page hasn't been
  1.1105 +  // unloaded. The prevents content from being refocused during an unload event.
  1.1106 +  nsCOMPtr<nsIDocShell> newDocShell = newWindow->GetDocShell();
  1.1107 +  nsCOMPtr<nsIDocShell> docShell = newDocShell;
  1.1108 +  while (docShell) {
  1.1109 +    bool inUnload;
  1.1110 +    docShell->GetIsInUnload(&inUnload);
  1.1111 +    if (inUnload)
  1.1112 +      return;
  1.1113 +
  1.1114 +    bool beingDestroyed;
  1.1115 +    docShell->IsBeingDestroyed(&beingDestroyed);
  1.1116 +    if (beingDestroyed)
  1.1117 +      return;
  1.1118 +
  1.1119 +    nsCOMPtr<nsIDocShellTreeItem> parentDsti;
  1.1120 +    docShell->GetParent(getter_AddRefs(parentDsti));
  1.1121 +    docShell = do_QueryInterface(parentDsti);
  1.1122 +  }
  1.1123 +
  1.1124 +  // if the new element is in the same window as the currently focused element 
  1.1125 +  bool isElementInFocusedWindow = (mFocusedWindow == newWindow);
  1.1126 +
  1.1127 +  if (!isElementInFocusedWindow && mFocusedWindow && newWindow &&
  1.1128 +      nsContentUtils::IsHandlingKeyBoardEvent()) {
  1.1129 +    nsCOMPtr<nsIScriptObjectPrincipal> focused =
  1.1130 +      do_QueryInterface(mFocusedWindow);
  1.1131 +    nsCOMPtr<nsIScriptObjectPrincipal> newFocus =
  1.1132 +      do_QueryInterface(newWindow);
  1.1133 +    nsIPrincipal* focusedPrincipal = focused->GetPrincipal();
  1.1134 +    nsIPrincipal* newPrincipal = newFocus->GetPrincipal();
  1.1135 +    if (!focusedPrincipal || !newPrincipal) {
  1.1136 +      return;
  1.1137 +    }
  1.1138 +    bool subsumes = false;
  1.1139 +    focusedPrincipal->Subsumes(newPrincipal, &subsumes);
  1.1140 +    if (!subsumes && !nsContentUtils::IsCallerChrome()) {
  1.1141 +      NS_WARNING("Not allowed to focus the new window!");
  1.1142 +      return;
  1.1143 +    }
  1.1144 +  }
  1.1145 +
  1.1146 +  // to check if the new element is in the active window, compare the
  1.1147 +  // new root docshell for the new element with the active window's docshell.
  1.1148 +  bool isElementInActiveWindow = false;
  1.1149 +
  1.1150 +  nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(newWindow);
  1.1151 +  nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
  1.1152 +  nsCOMPtr<nsPIDOMWindow> newRootWindow;
  1.1153 +  if (dsti) {
  1.1154 +    nsCOMPtr<nsIDocShellTreeItem> root;
  1.1155 +    dsti->GetRootTreeItem(getter_AddRefs(root));
  1.1156 +    newRootWindow = do_GetInterface(root);
  1.1157 +
  1.1158 +    isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow);
  1.1159 +  }
  1.1160 +
  1.1161 +  // Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX
  1.1162 +  // system. We don't control event dispatch to windowed plugins on non-MacOSX,
  1.1163 +  // so we can't display the "Press ESC to leave fullscreen mode" warning on
  1.1164 +  // key input if a windowed plugin is focused, so just exit fullscreen
  1.1165 +  // to guard against phishing.
  1.1166 +#ifndef XP_MACOSX
  1.1167 +  nsIDocument* fullscreenAncestor;
  1.1168 +  if (contentToFocus &&
  1.1169 +      (fullscreenAncestor = nsContentUtils::GetFullscreenAncestor(contentToFocus->OwnerDoc())) &&
  1.1170 +      nsContentUtils::HasPluginWithUncontrolledEventDispatch(contentToFocus)) {
  1.1171 +    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  1.1172 +                                    NS_LITERAL_CSTRING("DOM"),
  1.1173 +                                    contentToFocus->OwnerDoc(),
  1.1174 +                                    nsContentUtils::eDOM_PROPERTIES,
  1.1175 +                                    "FocusedWindowedPluginWhileFullScreen");
  1.1176 +    nsIDocument::ExitFullscreen(fullscreenAncestor, /* async */ true);
  1.1177 +  }
  1.1178 +#endif
  1.1179 +
  1.1180 +  // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
  1.1181 +  // shifted away from the current element if the new shell to focus is
  1.1182 +  // the same or an ancestor shell of the currently focused shell.
  1.1183 +  bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
  1.1184 +                            IsSameOrAncestor(newWindow, mFocusedWindow);
  1.1185 +
  1.1186 +  // if the element is in the active window, frame switching is allowed and
  1.1187 +  // the content is in a visible window, fire blur and focus events.
  1.1188 +  bool sendFocusEvent =
  1.1189 +    isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow);
  1.1190 +
  1.1191 +  // When the following conditions are true:
  1.1192 +  //  * an element has focus
  1.1193 +  //  * isn't called by trusted event (i.e., called by untrusted event or by js)
  1.1194 +  //  * the focus is moved to another document's element
  1.1195 +  // we need to check the permission.
  1.1196 +  if (sendFocusEvent && mFocusedContent &&
  1.1197 +      mFocusedContent->OwnerDoc() != aNewContent->OwnerDoc()) {
  1.1198 +    // If the caller cannot access the current focused node, the caller should
  1.1199 +    // not be able to steal focus from it. E.g., When the current focused node
  1.1200 +    // is in chrome, any web contents should not be able to steal the focus.
  1.1201 +    nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mFocusedContent));
  1.1202 +    sendFocusEvent = nsContentUtils::CanCallerAccess(domNode);
  1.1203 +    if (!sendFocusEvent && mMouseDownEventHandlingDocument) {
  1.1204 +      // However, while mouse down event is handling, the handling document's
  1.1205 +      // script should be able to steal focus.
  1.1206 +      domNode = do_QueryInterface(mMouseDownEventHandlingDocument);
  1.1207 +      sendFocusEvent = nsContentUtils::CanCallerAccess(domNode);
  1.1208 +    }
  1.1209 +  }
  1.1210 +
  1.1211 +  LOGCONTENT("Shift Focus: %s", contentToFocus.get());
  1.1212 +  LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p",
  1.1213 +           aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedContent.get()));
  1.1214 +  LOGFOCUS((" In Active Window: %d In Focused Window: %d SendFocus: %d",
  1.1215 +           isElementInActiveWindow, isElementInFocusedWindow, sendFocusEvent));
  1.1216 +
  1.1217 +  if (sendFocusEvent) {
  1.1218 +    // return if blurring fails or the focus changes during the blur
  1.1219 +    if (mFocusedWindow) {
  1.1220 +      // if the focus is being moved to another element in the same document,
  1.1221 +      // or to a descendant, pass the existing window to Blur so that the
  1.1222 +      // current node in the existing window is cleared. If moving to a
  1.1223 +      // window elsewhere, we want to maintain the current node in the
  1.1224 +      // window but still blur it.
  1.1225 +      bool currentIsSameOrAncestor = IsSameOrAncestor(mFocusedWindow, newWindow);
  1.1226 +      // find the common ancestor of the currently focused window and the new
  1.1227 +      // window. The ancestor will need to have its currently focused node
  1.1228 +      // cleared once the document has been blurred. Otherwise, we'll be in a
  1.1229 +      // state where a document is blurred yet the chain of windows above it
  1.1230 +      // still points to that document.
  1.1231 +      // For instance, in the following frame tree:
  1.1232 +      //   A
  1.1233 +      //  B C
  1.1234 +      //  D
  1.1235 +      // D is focused and we want to focus C. Once D has been blurred, we need
  1.1236 +      // to clear out the focus in A, otherwise A would still maintain that B
  1.1237 +      // was focused, and B that D was focused.
  1.1238 +      nsCOMPtr<nsPIDOMWindow> commonAncestor;
  1.1239 +      if (!isElementInFocusedWindow)
  1.1240 +        commonAncestor = GetCommonAncestor(newWindow, mFocusedWindow);
  1.1241 +
  1.1242 +      if (!Blur(currentIsSameOrAncestor ? mFocusedWindow.get() : nullptr,
  1.1243 +                commonAncestor, !isElementInFocusedWindow, aAdjustWidget))
  1.1244 +        return;
  1.1245 +    }
  1.1246 +
  1.1247 +    Focus(newWindow, contentToFocus, aFlags, !isElementInFocusedWindow,
  1.1248 +          aFocusChanged, false, aAdjustWidget);
  1.1249 +  }
  1.1250 +  else {
  1.1251 +    // otherwise, for inactive windows and when the caller cannot steal the
  1.1252 +    // focus, update the node in the window, and  raise the window if desired.
  1.1253 +    if (allowFrameSwitch)
  1.1254 +      AdjustWindowFocus(newWindow, true);
  1.1255 +
  1.1256 +    // set the focus node and method as needed
  1.1257 +    uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
  1.1258 +                           newWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
  1.1259 +    newWindow->SetFocusedNode(contentToFocus, focusMethod);
  1.1260 +    if (aFocusChanged) {
  1.1261 +      nsCOMPtr<nsIDocShell> docShell = newWindow->GetDocShell();
  1.1262 +
  1.1263 +      nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1.1264 +      if (presShell)
  1.1265 +        ScrollIntoView(presShell, contentToFocus, aFlags);
  1.1266 +    }
  1.1267 +
  1.1268 +    // update the commands even when inactive so that the attributes for that
  1.1269 +    // window are up to date.
  1.1270 +    if (allowFrameSwitch)
  1.1271 +      newWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
  1.1272 +
  1.1273 +    if (aFlags & FLAG_RAISE)
  1.1274 +      RaiseWindow(newRootWindow);
  1.1275 +  }
  1.1276 +}
  1.1277 +
  1.1278 +bool
  1.1279 +nsFocusManager::IsSameOrAncestor(nsPIDOMWindow* aPossibleAncestor,
  1.1280 +                                 nsPIDOMWindow* aWindow)
  1.1281 +{
  1.1282 +  nsCOMPtr<nsIWebNavigation> awebnav(do_GetInterface(aPossibleAncestor));
  1.1283 +  nsCOMPtr<nsIDocShellTreeItem> ancestordsti = do_QueryInterface(awebnav);
  1.1284 +
  1.1285 +  nsCOMPtr<nsIWebNavigation> fwebnav(do_GetInterface(aWindow));
  1.1286 +  nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(fwebnav);
  1.1287 +  while (dsti) {
  1.1288 +    if (dsti == ancestordsti)
  1.1289 +      return true;
  1.1290 +    nsCOMPtr<nsIDocShellTreeItem> parentDsti;
  1.1291 +    dsti->GetParent(getter_AddRefs(parentDsti));
  1.1292 +    dsti.swap(parentDsti);
  1.1293 +  }
  1.1294 +
  1.1295 +  return false;
  1.1296 +}
  1.1297 +
  1.1298 +already_AddRefed<nsPIDOMWindow>
  1.1299 +nsFocusManager::GetCommonAncestor(nsPIDOMWindow* aWindow1,
  1.1300 +                                  nsPIDOMWindow* aWindow2)
  1.1301 +{
  1.1302 +  nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(aWindow1));
  1.1303 +  nsCOMPtr<nsIDocShellTreeItem> dsti1 = do_QueryInterface(webnav);
  1.1304 +  NS_ENSURE_TRUE(dsti1, nullptr);
  1.1305 +
  1.1306 +  webnav = do_GetInterface(aWindow2);
  1.1307 +  nsCOMPtr<nsIDocShellTreeItem> dsti2 = do_QueryInterface(webnav);
  1.1308 +  NS_ENSURE_TRUE(dsti2, nullptr);
  1.1309 +
  1.1310 +  nsAutoTArray<nsIDocShellTreeItem*, 30> parents1, parents2;
  1.1311 +  do {
  1.1312 +    parents1.AppendElement(dsti1);
  1.1313 +    nsCOMPtr<nsIDocShellTreeItem> parentDsti1;
  1.1314 +    dsti1->GetParent(getter_AddRefs(parentDsti1));
  1.1315 +    dsti1.swap(parentDsti1);
  1.1316 +  } while (dsti1);
  1.1317 +  do {
  1.1318 +    parents2.AppendElement(dsti2);
  1.1319 +    nsCOMPtr<nsIDocShellTreeItem> parentDsti2;
  1.1320 +    dsti2->GetParent(getter_AddRefs(parentDsti2));
  1.1321 +    dsti2.swap(parentDsti2);
  1.1322 +  } while (dsti2);
  1.1323 +
  1.1324 +  uint32_t pos1 = parents1.Length();
  1.1325 +  uint32_t pos2 = parents2.Length();
  1.1326 +  nsIDocShellTreeItem* parent = nullptr;
  1.1327 +  uint32_t len;
  1.1328 +  for (len = std::min(pos1, pos2); len > 0; --len) {
  1.1329 +    nsIDocShellTreeItem* child1 = parents1.ElementAt(--pos1);
  1.1330 +    nsIDocShellTreeItem* child2 = parents2.ElementAt(--pos2);
  1.1331 +    if (child1 != child2) {
  1.1332 +      break;
  1.1333 +    }
  1.1334 +    parent = child1;
  1.1335 +  }
  1.1336 +
  1.1337 +  nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(parent);
  1.1338 +  return window.forget();
  1.1339 +}
  1.1340 +
  1.1341 +void
  1.1342 +nsFocusManager::AdjustWindowFocus(nsPIDOMWindow* aWindow,
  1.1343 +                                  bool aCheckPermission)
  1.1344 +{
  1.1345 +  bool isVisible = IsWindowVisible(aWindow);
  1.1346 +
  1.1347 +  nsCOMPtr<nsPIDOMWindow> window(aWindow);
  1.1348 +  while (window) {
  1.1349 +    // get the containing <iframe> or equivalent element so that it can be
  1.1350 +    // focused below.
  1.1351 +    nsCOMPtr<nsIContent> frameContent =
  1.1352 +      do_QueryInterface(window->GetFrameElementInternal());
  1.1353 +
  1.1354 +    nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(window));
  1.1355 +    nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
  1.1356 +    if (!dsti) 
  1.1357 +      return;
  1.1358 +    nsCOMPtr<nsIDocShellTreeItem> parentDsti;
  1.1359 +    dsti->GetParent(getter_AddRefs(parentDsti));
  1.1360 +
  1.1361 +    window = do_GetInterface(parentDsti);
  1.1362 +    if (window) {
  1.1363 +      // if the parent window is visible but aWindow was not, then we have
  1.1364 +      // likely moved up and out from a hidden tab to the browser window, or a
  1.1365 +      // similar such arrangement. Stop adjusting the current nodes.
  1.1366 +      if (IsWindowVisible(window) != isVisible)
  1.1367 +        break;
  1.1368 +
  1.1369 +      // When aCheckPermission is true, we should check whether the caller can
  1.1370 +      // access the window or not.  If it cannot access, we should stop the
  1.1371 +      // adjusting.
  1.1372 +      if (aCheckPermission && !nsContentUtils::CanCallerAccess(window))
  1.1373 +        break;
  1.1374 +
  1.1375 +      window->SetFocusedNode(frameContent);
  1.1376 +    }
  1.1377 +  }
  1.1378 +}
  1.1379 +
  1.1380 +bool
  1.1381 +nsFocusManager::IsWindowVisible(nsPIDOMWindow* aWindow)
  1.1382 +{
  1.1383 +  if (!aWindow || aWindow->IsFrozen())
  1.1384 +    return false;
  1.1385 +
  1.1386 +  // Check if the inner window is frozen as well. This can happen when a focus change
  1.1387 +  // occurs while restoring a previous page.
  1.1388 +  nsPIDOMWindow* innerWindow = aWindow->GetCurrentInnerWindow();
  1.1389 +  if (!innerWindow || innerWindow->IsFrozen())
  1.1390 +    return false;
  1.1391 +
  1.1392 +  nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  1.1393 +  nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(docShell));
  1.1394 +  if (!baseWin)
  1.1395 +    return false;
  1.1396 +
  1.1397 +  bool visible = false;
  1.1398 +  baseWin->GetVisibility(&visible);
  1.1399 +  return visible;
  1.1400 +}
  1.1401 +
  1.1402 +bool
  1.1403 +nsFocusManager::IsNonFocusableRoot(nsIContent* aContent)
  1.1404 +{
  1.1405 +  NS_PRECONDITION(aContent, "aContent must not be NULL");
  1.1406 +  NS_PRECONDITION(aContent->IsInDoc(), "aContent must be in a document");
  1.1407 +
  1.1408 +  // If aContent is in designMode, the root element is not focusable.
  1.1409 +  // NOTE: in designMode, most elements are not focusable, just the document is
  1.1410 +  //       focusable.
  1.1411 +  // Also, if aContent is not editable but it isn't in designMode, it's not
  1.1412 +  // focusable.
  1.1413 +  // And in userfocusignored context nothing is focusable.
  1.1414 +  nsIDocument* doc = aContent->GetCurrentDoc();
  1.1415 +  NS_ASSERTION(doc, "aContent must have current document");
  1.1416 +  return aContent == doc->GetRootElement() &&
  1.1417 +           (doc->HasFlag(NODE_IS_EDITABLE) || !aContent->IsEditable() ||
  1.1418 +            nsContentUtils::IsUserFocusIgnored(aContent));
  1.1419 +}
  1.1420 +
  1.1421 +nsIContent*
  1.1422 +nsFocusManager::CheckIfFocusable(nsIContent* aContent, uint32_t aFlags)
  1.1423 +{
  1.1424 +  if (!aContent)
  1.1425 +    return nullptr;
  1.1426 +
  1.1427 +  // this is a special case for some XUL elements where an anonymous child is
  1.1428 +  // actually focusable and not the element itself.
  1.1429 +  nsIContent* redirectedFocus = GetRedirectedFocus(aContent);
  1.1430 +  if (redirectedFocus)
  1.1431 +    return CheckIfFocusable(redirectedFocus, aFlags);
  1.1432 +
  1.1433 +  nsCOMPtr<nsIDocument> doc = aContent->GetCurrentDoc();
  1.1434 +  // can't focus elements that are not in documents
  1.1435 +  if (!doc) {
  1.1436 +    LOGCONTENT("Cannot focus %s because content not in document", aContent)
  1.1437 +    return nullptr;
  1.1438 +  }
  1.1439 +
  1.1440 +  // Make sure that our frames are up to date
  1.1441 +  doc->FlushPendingNotifications(Flush_Layout);
  1.1442 +
  1.1443 +  nsIPresShell *shell = doc->GetShell();
  1.1444 +  if (!shell)
  1.1445 +    return nullptr;
  1.1446 +
  1.1447 +  // the root content can always be focused,
  1.1448 +  // except in userfocusignored context.
  1.1449 +  if (aContent == doc->GetRootElement())
  1.1450 +    return nsContentUtils::IsUserFocusIgnored(aContent) ? nullptr : aContent;
  1.1451 +
  1.1452 +  // cannot focus content in print preview mode. Only the root can be focused.
  1.1453 +  nsPresContext* presContext = shell->GetPresContext();
  1.1454 +  if (presContext && presContext->Type() == nsPresContext::eContext_PrintPreview) {
  1.1455 +    LOGCONTENT("Cannot focus %s while in print preview", aContent)
  1.1456 +    return nullptr;
  1.1457 +  }
  1.1458 +
  1.1459 +  nsIFrame* frame = aContent->GetPrimaryFrame();
  1.1460 +  if (!frame) {
  1.1461 +    LOGCONTENT("Cannot focus %s as it has no frame", aContent)
  1.1462 +    return nullptr;
  1.1463 +  }
  1.1464 +
  1.1465 +  if (aContent->Tag() == nsGkAtoms::area && aContent->IsHTML()) {
  1.1466 +    // HTML areas do not have their own frame, and the img frame we get from
  1.1467 +    // GetPrimaryFrame() is not relevant as to whether it is focusable or
  1.1468 +    // not, so we have to do all the relevant checks manually for them.
  1.1469 +    return frame->IsVisibleConsideringAncestors() &&
  1.1470 +           aContent->IsFocusable() ? aContent : nullptr;
  1.1471 +  }
  1.1472 +
  1.1473 +  // if this is a child frame content node, check if it is visible and
  1.1474 +  // call the content node's IsFocusable method instead of the frame's
  1.1475 +  // IsFocusable method. This skips checking the style system and ensures that
  1.1476 +  // offscreen browsers can still be focused.
  1.1477 +  nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
  1.1478 +  if (subdoc && IsWindowVisible(subdoc->GetWindow())) {
  1.1479 +    const nsStyleUserInterface* ui = frame->StyleUserInterface();
  1.1480 +    int32_t tabIndex = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE ||
  1.1481 +                        ui->mUserFocus == NS_STYLE_USER_FOCUS_NONE) ? -1 : 0;
  1.1482 +    return aContent->IsFocusable(&tabIndex, aFlags & FLAG_BYMOUSE) ? aContent : nullptr;
  1.1483 +  }
  1.1484 +  
  1.1485 +  return frame->IsFocusable(nullptr, aFlags & FLAG_BYMOUSE) ? aContent : nullptr;
  1.1486 +}
  1.1487 +
  1.1488 +bool
  1.1489 +nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
  1.1490 +                     nsPIDOMWindow* aAncestorWindowToFocus,
  1.1491 +                     bool aIsLeavingDocument,
  1.1492 +                     bool aAdjustWidgets)
  1.1493 +{
  1.1494 +  LOGFOCUS(("<<Blur begin>>"));
  1.1495 +
  1.1496 +  // hold a reference to the focused content, which may be null
  1.1497 +  nsCOMPtr<nsIContent> content = mFocusedContent;
  1.1498 +  if (content) {
  1.1499 +    if (!content->IsInDoc()) {
  1.1500 +      mFocusedContent = nullptr;
  1.1501 +      return true;
  1.1502 +    }
  1.1503 +    if (content == mFirstBlurEvent)
  1.1504 +      return true;
  1.1505 +  }
  1.1506 +
  1.1507 +  // hold a reference to the focused window
  1.1508 +  nsCOMPtr<nsPIDOMWindow> window = mFocusedWindow;
  1.1509 +  if (!window) {
  1.1510 +    mFocusedContent = nullptr;
  1.1511 +    return true;
  1.1512 +  }
  1.1513 +
  1.1514 +  nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
  1.1515 +  if (!docShell) {
  1.1516 +    mFocusedContent = nullptr;
  1.1517 +    return true;
  1.1518 +  }
  1.1519 +
  1.1520 +  // Keep a ref to presShell since dispatching the DOM event may cause
  1.1521 +  // the document to be destroyed.
  1.1522 +  nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1.1523 +  if (!presShell) {
  1.1524 +    mFocusedContent = nullptr;
  1.1525 +    return true;
  1.1526 +  }
  1.1527 +
  1.1528 +  bool clearFirstBlurEvent = false;
  1.1529 +  if (!mFirstBlurEvent) {
  1.1530 +    mFirstBlurEvent = content;
  1.1531 +    clearFirstBlurEvent = true;
  1.1532 +  }
  1.1533 +
  1.1534 +  nsPresContext* focusedPresContext =
  1.1535 +    mActiveWindow ? presShell->GetPresContext() : nullptr;
  1.1536 +  IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
  1.1537 +                                 GetFocusMoveActionCause(0));
  1.1538 +
  1.1539 +  // now adjust the actual focus, by clearing the fields in the focus manager
  1.1540 +  // and in the window.
  1.1541 +  mFocusedContent = nullptr;
  1.1542 +  bool shouldShowFocusRing = window->ShouldShowFocusRing();
  1.1543 +  if (aWindowToClear)
  1.1544 +    aWindowToClear->SetFocusedNode(nullptr);
  1.1545 +
  1.1546 +  LOGCONTENT("Element %s has been blurred", content.get());
  1.1547 +
  1.1548 +  // Don't fire blur event on the root content which isn't editable.
  1.1549 +  bool sendBlurEvent =
  1.1550 +    content && content->IsInDoc() && !IsNonFocusableRoot(content);
  1.1551 +  if (content) {
  1.1552 +    if (sendBlurEvent) {
  1.1553 +      NotifyFocusStateChange(content, shouldShowFocusRing, false);
  1.1554 +    }
  1.1555 +
  1.1556 +    // if an object/plug-in/remote browser is being blurred, move the system focus
  1.1557 +    // to the parent window, otherwise events will still get fired at the plugin.
  1.1558 +    // But don't do this if we are blurring due to the window being lowered,
  1.1559 +    // otherwise, the parent window can get raised again.
  1.1560 +    if (mActiveWindow) {
  1.1561 +      nsIFrame* contentFrame = content->GetPrimaryFrame();
  1.1562 +      nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
  1.1563 +      if (aAdjustWidgets && objectFrame && !sTestMode) {
  1.1564 +        // note that the presshell's widget is being retrieved here, not the one
  1.1565 +        // for the object frame.
  1.1566 +        nsViewManager* vm = presShell->GetViewManager();
  1.1567 +        if (vm) {
  1.1568 +          nsCOMPtr<nsIWidget> widget;
  1.1569 +          vm->GetRootWidget(getter_AddRefs(widget));
  1.1570 +          if (widget)
  1.1571 +            widget->SetFocus(false);
  1.1572 +        }
  1.1573 +      }
  1.1574 +
  1.1575 +      // if the object being blurred is a remote browser, deactivate remote content
  1.1576 +      if (TabParent* remote = TabParent::GetFrom(content)) {
  1.1577 +        remote->Deactivate();
  1.1578 +        LOGFOCUS(("Remote browser deactivated"));
  1.1579 +      }
  1.1580 +    }
  1.1581 +  }
  1.1582 +
  1.1583 +  bool result = true;
  1.1584 +  if (sendBlurEvent) {
  1.1585 +    // if there is an active window, update commands. If there isn't an active
  1.1586 +    // window, then this was a blur caused by the active window being lowered,
  1.1587 +    // so there is no need to update the commands
  1.1588 +    if (mActiveWindow)
  1.1589 +      window->UpdateCommands(NS_LITERAL_STRING("focus"));
  1.1590 +
  1.1591 +    SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell,
  1.1592 +                         content->GetCurrentDoc(), content, 1, false);
  1.1593 +  }
  1.1594 +
  1.1595 +  // if we are leaving the document or the window was lowered, make the caret
  1.1596 +  // invisible.
  1.1597 +  if (aIsLeavingDocument || !mActiveWindow)
  1.1598 +    SetCaretVisible(presShell, false, nullptr);
  1.1599 +
  1.1600 +  // at this point, it is expected that this window will be still be
  1.1601 +  // focused, but the focused content will be null, as it was cleared before
  1.1602 +  // the event. If this isn't the case, then something else was focused during
  1.1603 +  // the blur event above and we should just return. However, if
  1.1604 +  // aIsLeavingDocument is set, a new document is desired, so make sure to
  1.1605 +  // blur the document and window.
  1.1606 +  if (mFocusedWindow != window ||
  1.1607 +      (mFocusedContent != nullptr && !aIsLeavingDocument)) {
  1.1608 +    result = false;
  1.1609 +  }
  1.1610 +  else if (aIsLeavingDocument) {
  1.1611 +    window->TakeFocus(false, 0);
  1.1612 +
  1.1613 +    // clear the focus so that the ancestor frame hierarchy is in the correct
  1.1614 +    // state. Pass true because aAncestorWindowToFocus is thought to be
  1.1615 +    // focused at this point.
  1.1616 +    if (aAncestorWindowToFocus)
  1.1617 +      aAncestorWindowToFocus->SetFocusedNode(nullptr, 0, true);
  1.1618 +
  1.1619 +    SetFocusedWindowInternal(nullptr);
  1.1620 +    mFocusedContent = nullptr;
  1.1621 +
  1.1622 +    // pass 1 for the focus method when calling SendFocusOrBlurEvent just so
  1.1623 +    // that the check is made for suppressed documents. Check to ensure that
  1.1624 +    // the document isn't null in case someone closed it during the blur above
  1.1625 +    nsIDocument* doc = window->GetExtantDoc();
  1.1626 +    if (doc)
  1.1627 +      SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell, doc, doc, 1, false);
  1.1628 +    if (mFocusedWindow == nullptr)
  1.1629 +      SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell, doc, window, 1, false);
  1.1630 +
  1.1631 +    // check if a different window was focused
  1.1632 +    result = (mFocusedWindow == nullptr && mActiveWindow);
  1.1633 +  }
  1.1634 +  else if (mActiveWindow) {
  1.1635 +    // Otherwise, the blur of the element without blurring the document
  1.1636 +    // occurred normally. Call UpdateCaret to redisplay the caret at the right
  1.1637 +    // location within the document. This is needed to ensure that the caret
  1.1638 +    // used for caret browsing is made visible again when an input field is
  1.1639 +    // blurred.
  1.1640 +    UpdateCaret(false, true, nullptr);
  1.1641 +  }
  1.1642 +
  1.1643 +  if (clearFirstBlurEvent)
  1.1644 +    mFirstBlurEvent = nullptr;
  1.1645 +
  1.1646 +  return result;
  1.1647 +}
  1.1648 +
  1.1649 +void
  1.1650 +nsFocusManager::Focus(nsPIDOMWindow* aWindow,
  1.1651 +                      nsIContent* aContent,
  1.1652 +                      uint32_t aFlags,
  1.1653 +                      bool aIsNewDocument,
  1.1654 +                      bool aFocusChanged,
  1.1655 +                      bool aWindowRaised,
  1.1656 +                      bool aAdjustWidgets)
  1.1657 +{
  1.1658 +  LOGFOCUS(("<<Focus begin>>"));
  1.1659 +
  1.1660 +  if (!aWindow)
  1.1661 +    return;
  1.1662 +
  1.1663 +  if (aContent && (aContent == mFirstFocusEvent || aContent == mFirstBlurEvent))
  1.1664 +    return;
  1.1665 +
  1.1666 +  // Keep a reference to the presShell since dispatching the DOM event may
  1.1667 +  // cause the document to be destroyed.
  1.1668 +  nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  1.1669 +  if (!docShell)
  1.1670 +    return;
  1.1671 +
  1.1672 +  nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1.1673 +  if (!presShell)
  1.1674 +    return;
  1.1675 +
  1.1676 +  // If the focus actually changed, set the focus method (mouse, keyboard, etc).
  1.1677 +  // Otherwise, just get the current focus method and use that. This ensures
  1.1678 +  // that the method is set during the document and window focus events.
  1.1679 +  uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
  1.1680 +                         aWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
  1.1681 +
  1.1682 +  if (!IsWindowVisible(aWindow)) {
  1.1683 +    // if the window isn't visible, for instance because it is a hidden tab,
  1.1684 +    // update the current focus and scroll it into view but don't do anything else
  1.1685 +    if (CheckIfFocusable(aContent, aFlags)) {
  1.1686 +      aWindow->SetFocusedNode(aContent, focusMethod);
  1.1687 +      if (aFocusChanged)
  1.1688 +        ScrollIntoView(presShell, aContent, aFlags);
  1.1689 +    }
  1.1690 +    return;
  1.1691 +  }
  1.1692 +
  1.1693 +  bool clearFirstFocusEvent = false;
  1.1694 +  if (!mFirstFocusEvent) {
  1.1695 +    mFirstFocusEvent = aContent;
  1.1696 +    clearFirstFocusEvent = true;
  1.1697 +  }
  1.1698 +
  1.1699 +#ifdef PR_LOGGING
  1.1700 +  LOGCONTENT("Element %s has been focused", aContent);
  1.1701 +
  1.1702 +  if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) {
  1.1703 +    nsIDocument* docm = aWindow->GetExtantDoc();
  1.1704 +    if (docm) {
  1.1705 +      LOGCONTENT(" from %s", docm->GetRootElement());
  1.1706 +    }
  1.1707 +    LOGFOCUS((" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x]",
  1.1708 +             aIsNewDocument, aFocusChanged, aWindowRaised, aFlags));
  1.1709 +  }
  1.1710 +#endif
  1.1711 +
  1.1712 +  if (aIsNewDocument) {
  1.1713 +    // if this is a new document, update the parent chain of frames so that
  1.1714 +    // focus can be traversed from the top level down to the newly focused
  1.1715 +    // window.
  1.1716 +    AdjustWindowFocus(aWindow, false);
  1.1717 +
  1.1718 +    // Update the window touch registration to reflect the state of
  1.1719 +    // the new document that got focus
  1.1720 +    aWindow->UpdateTouchState();
  1.1721 +  }
  1.1722 +
  1.1723 +  // indicate that the window has taken focus.
  1.1724 +  if (aWindow->TakeFocus(true, focusMethod))
  1.1725 +    aIsNewDocument = true;
  1.1726 +
  1.1727 +  SetFocusedWindowInternal(aWindow);
  1.1728 +
  1.1729 +  // Update the system focus by focusing the root widget.  But avoid this
  1.1730 +  // if 1) aAdjustWidgets is false or 2) aContent is a plugin that has its
  1.1731 +  // own widget and is either already focused or is about to be focused.
  1.1732 +  nsCOMPtr<nsIWidget> objectFrameWidget;
  1.1733 +  if (aContent) {
  1.1734 +    nsIFrame* contentFrame = aContent->GetPrimaryFrame();
  1.1735 +    nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
  1.1736 +    if (objectFrame)
  1.1737 +      objectFrameWidget = objectFrame->GetWidget();
  1.1738 +  }
  1.1739 +  if (aAdjustWidgets && !objectFrameWidget && !sTestMode) {
  1.1740 +    nsViewManager* vm = presShell->GetViewManager();
  1.1741 +    if (vm) {
  1.1742 +      nsCOMPtr<nsIWidget> widget;
  1.1743 +      vm->GetRootWidget(getter_AddRefs(widget));
  1.1744 +      if (widget)
  1.1745 +        widget->SetFocus(false);
  1.1746 +    }
  1.1747 +  }
  1.1748 +
  1.1749 +  // if switching to a new document, first fire the focus event on the
  1.1750 +  // document and then the window.
  1.1751 +  if (aIsNewDocument) {
  1.1752 +    nsIDocument* doc = aWindow->GetExtantDoc();
  1.1753 +    // The focus change should be notified to IMEStateManager from here if
  1.1754 +    // the focused content is a designMode editor since any content won't
  1.1755 +    // receive focus event.
  1.1756 +    if (doc && doc->HasFlag(NODE_IS_EDITABLE)) {
  1.1757 +      IMEStateManager::OnChangeFocus(presShell->GetPresContext(), nullptr,
  1.1758 +                                     GetFocusMoveActionCause(aFlags));
  1.1759 +    }
  1.1760 +    if (doc)
  1.1761 +      SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, doc,
  1.1762 +                           doc, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
  1.1763 +    if (mFocusedWindow == aWindow && mFocusedContent == nullptr)
  1.1764 +      SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, doc,
  1.1765 +                           aWindow, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
  1.1766 +  }
  1.1767 +
  1.1768 +  // check to ensure that the element is still focusable, and that nothing
  1.1769 +  // else was focused during the events above.
  1.1770 +  if (CheckIfFocusable(aContent, aFlags) &&
  1.1771 +      mFocusedWindow == aWindow && mFocusedContent == nullptr) {
  1.1772 +    mFocusedContent = aContent;
  1.1773 +
  1.1774 +    nsIContent* focusedNode = aWindow->GetFocusedNode();
  1.1775 +    bool isRefocus = focusedNode && focusedNode->IsEqualNode(aContent);
  1.1776 +
  1.1777 +    aWindow->SetFocusedNode(aContent, focusMethod);
  1.1778 +
  1.1779 +    bool sendFocusEvent =
  1.1780 +      aContent && aContent->IsInDoc() && !IsNonFocusableRoot(aContent);
  1.1781 +    nsPresContext* presContext = presShell->GetPresContext();
  1.1782 +    if (sendFocusEvent) {
  1.1783 +      // if the focused element changed, scroll it into view
  1.1784 +      if (aFocusChanged)
  1.1785 +        ScrollIntoView(presShell, aContent, aFlags);
  1.1786 +
  1.1787 +      NotifyFocusStateChange(aContent, aWindow->ShouldShowFocusRing(), true);
  1.1788 +
  1.1789 +      // if this is an object/plug-in/remote browser, focus its widget.  Note that we might
  1.1790 +      // no longer be in the same document, due to the events we fired above when
  1.1791 +      // aIsNewDocument.
  1.1792 +      if (presShell->GetDocument() == aContent->GetDocument()) {
  1.1793 +        if (aAdjustWidgets && objectFrameWidget && !sTestMode)
  1.1794 +          objectFrameWidget->SetFocus(false);
  1.1795 +
  1.1796 +        // if the object being focused is a remote browser, activate remote content
  1.1797 +        if (TabParent* remote = TabParent::GetFrom(aContent)) {
  1.1798 +          remote->Activate();
  1.1799 +          LOGFOCUS(("Remote browser activated"));
  1.1800 +        }
  1.1801 +      }
  1.1802 +
  1.1803 +      IMEStateManager::OnChangeFocus(presContext, aContent,
  1.1804 +                                     GetFocusMoveActionCause(aFlags));
  1.1805 +
  1.1806 +      // as long as this focus wasn't because a window was raised, update the
  1.1807 +      // commands
  1.1808 +      // XXXndeakin P2 someone could adjust the focus during the update
  1.1809 +      if (!aWindowRaised)
  1.1810 +        aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
  1.1811 +
  1.1812 +      SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell,
  1.1813 +                           aContent->GetCurrentDoc(),
  1.1814 +                           aContent, aFlags & FOCUSMETHOD_MASK,
  1.1815 +                           aWindowRaised, isRefocus);
  1.1816 +    } else {
  1.1817 +      IMEStateManager::OnChangeFocus(presContext, nullptr,
  1.1818 +                                     GetFocusMoveActionCause(aFlags));
  1.1819 +      if (!aWindowRaised) {
  1.1820 +        aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
  1.1821 +      }
  1.1822 +    }
  1.1823 +  }
  1.1824 +  else {
  1.1825 +    // If the window focus event (fired above when aIsNewDocument) caused
  1.1826 +    // the plugin not to be focusable, update the system focus by focusing
  1.1827 +    // the root widget.
  1.1828 +    if (aAdjustWidgets && objectFrameWidget &&
  1.1829 +        mFocusedWindow == aWindow && mFocusedContent == nullptr &&
  1.1830 +        !sTestMode) {
  1.1831 +      nsViewManager* vm = presShell->GetViewManager();
  1.1832 +      if (vm) {
  1.1833 +        nsCOMPtr<nsIWidget> widget;
  1.1834 +        vm->GetRootWidget(getter_AddRefs(widget));
  1.1835 +        if (widget)
  1.1836 +          widget->SetFocus(false);
  1.1837 +      }
  1.1838 +    }
  1.1839 +
  1.1840 +    nsPresContext* presContext = presShell->GetPresContext();
  1.1841 +    IMEStateManager::OnChangeFocus(presContext, nullptr,
  1.1842 +                                   GetFocusMoveActionCause(aFlags));
  1.1843 +
  1.1844 +    if (!aWindowRaised)
  1.1845 +      aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
  1.1846 +  }
  1.1847 +
  1.1848 +  // update the caret visibility and position to match the newly focused
  1.1849 +  // element. However, don't update the position if this was a focus due to a
  1.1850 +  // mouse click as the selection code would already have moved the caret as
  1.1851 +  // needed. If this is a different document than was focused before, also
  1.1852 +  // update the caret's visibility. If this is the same document, the caret
  1.1853 +  // visibility should be the same as before so there is no need to update it.
  1.1854 +  if (mFocusedContent == aContent)
  1.1855 +    UpdateCaret(aFocusChanged && !(aFlags & FLAG_BYMOUSE), aIsNewDocument,
  1.1856 +                mFocusedContent);
  1.1857 +
  1.1858 +  if (clearFirstFocusEvent)
  1.1859 +    mFirstFocusEvent = nullptr;
  1.1860 +}
  1.1861 +
  1.1862 +class FocusBlurEvent : public nsRunnable
  1.1863 +{
  1.1864 +public:
  1.1865 +  FocusBlurEvent(nsISupports* aTarget, uint32_t aType,
  1.1866 +                 nsPresContext* aContext, bool aWindowRaised,
  1.1867 +                 bool aIsRefocus)
  1.1868 +  : mTarget(aTarget), mType(aType), mContext(aContext),
  1.1869 +    mWindowRaised(aWindowRaised), mIsRefocus(aIsRefocus) {}
  1.1870 +
  1.1871 +  NS_IMETHOD Run()
  1.1872 +  {
  1.1873 +    InternalFocusEvent event(true, mType);
  1.1874 +    event.mFlags.mBubbles = false;
  1.1875 +    event.fromRaise = mWindowRaised;
  1.1876 +    event.isRefocus = mIsRefocus;
  1.1877 +    return EventDispatcher::Dispatch(mTarget, mContext, &event);
  1.1878 +  }
  1.1879 +
  1.1880 +  nsCOMPtr<nsISupports>   mTarget;
  1.1881 +  uint32_t                mType;
  1.1882 +  nsRefPtr<nsPresContext> mContext;
  1.1883 +  bool                    mWindowRaised;
  1.1884 +  bool                    mIsRefocus;
  1.1885 +};
  1.1886 +
  1.1887 +void
  1.1888 +nsFocusManager::SendFocusOrBlurEvent(uint32_t aType,
  1.1889 +                                     nsIPresShell* aPresShell,
  1.1890 +                                     nsIDocument* aDocument,
  1.1891 +                                     nsISupports* aTarget,
  1.1892 +                                     uint32_t aFocusMethod,
  1.1893 +                                     bool aWindowRaised,
  1.1894 +                                     bool aIsRefocus)
  1.1895 +{
  1.1896 +  NS_ASSERTION(aType == NS_FOCUS_CONTENT || aType == NS_BLUR_CONTENT,
  1.1897 +               "Wrong event type for SendFocusOrBlurEvent");
  1.1898 +
  1.1899 +  nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(aTarget);
  1.1900 +
  1.1901 +  nsCOMPtr<nsINode> n = do_QueryInterface(aTarget);
  1.1902 +  if (!n) {
  1.1903 +    nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aTarget);
  1.1904 +    n = win ? win->GetExtantDoc() : nullptr;
  1.1905 +  }
  1.1906 +  bool dontDispatchEvent = n && nsContentUtils::IsUserFocusIgnored(n);
  1.1907 +
  1.1908 +  // for focus events, if this event was from a mouse or key and event
  1.1909 +  // handling on the document is suppressed, queue the event and fire it
  1.1910 +  // later. For blur events, a non-zero value would be set for aFocusMethod.
  1.1911 +  if (aFocusMethod && !dontDispatchEvent &&
  1.1912 +      aDocument && aDocument->EventHandlingSuppressed()) {
  1.1913 +    // aFlags is always 0 when aWindowRaised is true so this won't be called
  1.1914 +    // on a window raise.
  1.1915 +    NS_ASSERTION(!aWindowRaised, "aWindowRaised should not be set");
  1.1916 +
  1.1917 +    for (uint32_t i = mDelayedBlurFocusEvents.Length(); i > 0; --i) {
  1.1918 +      // if this event was already queued, remove it and append it to the end
  1.1919 +      if (mDelayedBlurFocusEvents[i - 1].mType == aType &&
  1.1920 +          mDelayedBlurFocusEvents[i - 1].mPresShell == aPresShell &&
  1.1921 +          mDelayedBlurFocusEvents[i - 1].mDocument == aDocument &&
  1.1922 +          mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget) {
  1.1923 +        mDelayedBlurFocusEvents.RemoveElementAt(i - 1);
  1.1924 +      }
  1.1925 +    }
  1.1926 +
  1.1927 +    mDelayedBlurFocusEvents.AppendElement(
  1.1928 +      nsDelayedBlurOrFocusEvent(aType, aPresShell, aDocument, eventTarget));
  1.1929 +    return;
  1.1930 +  }
  1.1931 +
  1.1932 +#ifdef ACCESSIBILITY
  1.1933 +  nsAccessibilityService* accService = GetAccService();
  1.1934 +  if (accService) {
  1.1935 +    if (aType == NS_FOCUS_CONTENT)
  1.1936 +      accService->NotifyOfDOMFocus(aTarget);
  1.1937 +    else
  1.1938 +      accService->NotifyOfDOMBlur(aTarget);
  1.1939 +  }
  1.1940 +#endif
  1.1941 +
  1.1942 +  if (!dontDispatchEvent) {
  1.1943 +    nsContentUtils::AddScriptRunner(
  1.1944 +      new FocusBlurEvent(aTarget, aType, aPresShell->GetPresContext(),
  1.1945 +                         aWindowRaised, aIsRefocus));
  1.1946 +  }
  1.1947 +}
  1.1948 +
  1.1949 +void
  1.1950 +nsFocusManager::ScrollIntoView(nsIPresShell* aPresShell,
  1.1951 +                               nsIContent* aContent,
  1.1952 +                               uint32_t aFlags)
  1.1953 +{
  1.1954 +  // if the noscroll flag isn't set, scroll the newly focused element into view
  1.1955 +  if (!(aFlags & FLAG_NOSCROLL))
  1.1956 +    aPresShell->ScrollContentIntoView(aContent,
  1.1957 +                                      nsIPresShell::ScrollAxis(
  1.1958 +                                        nsIPresShell::SCROLL_MINIMUM,
  1.1959 +                                        nsIPresShell::SCROLL_IF_NOT_VISIBLE),
  1.1960 +                                      nsIPresShell::ScrollAxis(
  1.1961 +                                        nsIPresShell::SCROLL_MINIMUM,
  1.1962 +                                        nsIPresShell::SCROLL_IF_NOT_VISIBLE),
  1.1963 +                                      nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
  1.1964 +}
  1.1965 +
  1.1966 +
  1.1967 +void
  1.1968 +nsFocusManager::RaiseWindow(nsPIDOMWindow* aWindow)
  1.1969 +{
  1.1970 +  // don't raise windows that are already raised or are in the process of
  1.1971 +  // being lowered
  1.1972 +  if (!aWindow || aWindow == mActiveWindow || aWindow == mWindowBeingLowered)
  1.1973 +    return;
  1.1974 +
  1.1975 +  if (sTestMode) {
  1.1976 +    // In test mode, emulate the existing window being lowered and the new
  1.1977 +    // window being raised.
  1.1978 +    if (mActiveWindow)
  1.1979 +      WindowLowered(mActiveWindow);
  1.1980 +    WindowRaised(aWindow);
  1.1981 +    return;
  1.1982 +  }
  1.1983 +
  1.1984 +#if defined(XP_WIN)
  1.1985 +  // Windows would rather we focus the child widget, otherwise, the toplevel
  1.1986 +  // widget will always end up being focused. Fortunately, focusing the child
  1.1987 +  // widget will also have the effect of raising the window this widget is in.
  1.1988 +  // But on other platforms, we can just focus the toplevel widget to raise
  1.1989 +  // the window.
  1.1990 +  nsCOMPtr<nsPIDOMWindow> childWindow;
  1.1991 +  GetFocusedDescendant(aWindow, true, getter_AddRefs(childWindow));
  1.1992 +  if (!childWindow)
  1.1993 +    childWindow = aWindow;
  1.1994 +
  1.1995 +  nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  1.1996 +  if (!docShell)
  1.1997 +    return;
  1.1998 +
  1.1999 +  nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  1.2000 +  if (!presShell)
  1.2001 +    return;
  1.2002 +
  1.2003 +  nsViewManager* vm = presShell->GetViewManager();
  1.2004 +  if (vm) {
  1.2005 +    nsCOMPtr<nsIWidget> widget;
  1.2006 +    vm->GetRootWidget(getter_AddRefs(widget));
  1.2007 +    if (widget)
  1.2008 +      widget->SetFocus(true);
  1.2009 +  }
  1.2010 +#else
  1.2011 +  nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
  1.2012 +  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(webnav);
  1.2013 +  if (treeOwnerAsWin) {
  1.2014 +    nsCOMPtr<nsIWidget> widget;
  1.2015 +    treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
  1.2016 +    if (widget)
  1.2017 +      widget->SetFocus(true);
  1.2018 +  }
  1.2019 +#endif
  1.2020 +}
  1.2021 +
  1.2022 +void
  1.2023 +nsFocusManager::UpdateCaretForCaretBrowsingMode()
  1.2024 +{
  1.2025 +  UpdateCaret(false, true, mFocusedContent);
  1.2026 +}
  1.2027 +
  1.2028 +void
  1.2029 +nsFocusManager::UpdateCaret(bool aMoveCaretToFocus,
  1.2030 +                            bool aUpdateVisibility,
  1.2031 +                            nsIContent* aContent)
  1.2032 +{
  1.2033 +  LOGFOCUS(("Update Caret: %d %d", aMoveCaretToFocus, aUpdateVisibility));
  1.2034 +
  1.2035 +  if (!mFocusedWindow)
  1.2036 +    return;
  1.2037 +
  1.2038 +  // this is called when a document is focused or when the caretbrowsing
  1.2039 +  // preference is changed
  1.2040 +  nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
  1.2041 +  nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(focusedDocShell);
  1.2042 +  if (!dsti)
  1.2043 +    return;
  1.2044 +
  1.2045 +  if (dsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
  1.2046 +    return;  // Never browse with caret in chrome
  1.2047 +  }
  1.2048 +
  1.2049 +  bool browseWithCaret =
  1.2050 +    Preferences::GetBool("accessibility.browsewithcaret");
  1.2051 +
  1.2052 +  nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
  1.2053 +  if (!presShell)
  1.2054 +    return;
  1.2055 +
  1.2056 +  // If this is an editable document which isn't contentEditable, or a
  1.2057 +  // contentEditable document and the node to focus is contentEditable,
  1.2058 +  // return, so that we don't mess with caret visibility.
  1.2059 +  bool isEditable = false;
  1.2060 +  focusedDocShell->GetEditable(&isEditable);
  1.2061 +
  1.2062 +  if (isEditable) {
  1.2063 +    nsCOMPtr<nsIHTMLDocument> doc =
  1.2064 +      do_QueryInterface(presShell->GetDocument());
  1.2065 +
  1.2066 +    bool isContentEditableDoc =
  1.2067 +      doc && doc->GetEditingState() == nsIHTMLDocument::eContentEditable;
  1.2068 +
  1.2069 +    bool isFocusEditable =
  1.2070 +      aContent && aContent->HasFlag(NODE_IS_EDITABLE);
  1.2071 +    if (!isContentEditableDoc || isFocusEditable)
  1.2072 +      return;
  1.2073 +  }
  1.2074 +
  1.2075 +  if (!isEditable && aMoveCaretToFocus)
  1.2076 +    MoveCaretToFocus(presShell, aContent);
  1.2077 +
  1.2078 +  if (!aUpdateVisibility)
  1.2079 +    return;
  1.2080 +
  1.2081 +  // XXXndeakin this doesn't seem right. It should be checking for this only
  1.2082 +  // on the nearest ancestor frame which is a chrome frame. But this is
  1.2083 +  // what the existing code does, so just leave it for now.
  1.2084 +  if (!browseWithCaret) {
  1.2085 +    nsCOMPtr<nsIContent> docContent =
  1.2086 +      do_QueryInterface(mFocusedWindow->GetFrameElementInternal());
  1.2087 +    if (docContent)
  1.2088 +      browseWithCaret = docContent->AttrValueIs(kNameSpaceID_None,
  1.2089 +                                                nsGkAtoms::showcaret,
  1.2090 +                                                NS_LITERAL_STRING("true"),
  1.2091 +                                                eCaseMatters);
  1.2092 +  }
  1.2093 +
  1.2094 +  SetCaretVisible(presShell, browseWithCaret, aContent);
  1.2095 +}
  1.2096 +
  1.2097 +void
  1.2098 +nsFocusManager::MoveCaretToFocus(nsIPresShell* aPresShell, nsIContent* aContent)
  1.2099 +{
  1.2100 +  // domDoc is a document interface we can create a range with
  1.2101 +  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aPresShell->GetDocument());
  1.2102 +  if (domDoc) {
  1.2103 +    nsRefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
  1.2104 +    nsCOMPtr<nsISelection> domSelection = frameSelection->
  1.2105 +      GetSelection(nsISelectionController::SELECTION_NORMAL);
  1.2106 +    if (domSelection) {
  1.2107 +      nsCOMPtr<nsIDOMNode> currentFocusNode(do_QueryInterface(aContent));
  1.2108 +      // First clear the selection. This way, if there is no currently focused
  1.2109 +      // content, the selection will just be cleared.
  1.2110 +      domSelection->RemoveAllRanges();
  1.2111 +      if (currentFocusNode) {
  1.2112 +        nsCOMPtr<nsIDOMRange> newRange;
  1.2113 +        nsresult rv = domDoc->CreateRange(getter_AddRefs(newRange));
  1.2114 +        if (NS_SUCCEEDED(rv)) {
  1.2115 +          // Set the range to the start of the currently focused node
  1.2116 +          // Make sure it's collapsed
  1.2117 +          newRange->SelectNodeContents(currentFocusNode);
  1.2118 +          nsCOMPtr<nsIDOMNode> firstChild;
  1.2119 +          currentFocusNode->GetFirstChild(getter_AddRefs(firstChild));
  1.2120 +          if (!firstChild ||
  1.2121 +              aContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) {
  1.2122 +            // If current focus node is a leaf, set range to before the
  1.2123 +            // node by using the parent as a container.
  1.2124 +            // This prevents it from appearing as selected.
  1.2125 +            newRange->SetStartBefore(currentFocusNode);
  1.2126 +            newRange->SetEndBefore(currentFocusNode);
  1.2127 +          }
  1.2128 +          domSelection->AddRange(newRange);
  1.2129 +          domSelection->CollapseToStart();
  1.2130 +        }
  1.2131 +      }
  1.2132 +    }
  1.2133 +  }
  1.2134 +}
  1.2135 +
  1.2136 +nsresult
  1.2137 +nsFocusManager::SetCaretVisible(nsIPresShell* aPresShell,
  1.2138 +                                bool aVisible,
  1.2139 +                                nsIContent* aContent)
  1.2140 +{
  1.2141 +  // When browsing with caret, make sure caret is visible after new focus
  1.2142 +  // Return early if there is no caret. This can happen for the testcase
  1.2143 +  // for bug 308025 where a window is closed in a blur handler.
  1.2144 +  nsRefPtr<nsCaret> caret = aPresShell->GetCaret();
  1.2145 +  if (!caret)
  1.2146 +    return NS_OK;
  1.2147 +
  1.2148 +  bool caretVisible = false;
  1.2149 +  caret->GetCaretVisible(&caretVisible);
  1.2150 +  if (!aVisible && !caretVisible)
  1.2151 +    return NS_OK;
  1.2152 +
  1.2153 +  nsRefPtr<nsFrameSelection> frameSelection;
  1.2154 +  if (aContent) {
  1.2155 +    NS_ASSERTION(aContent->GetDocument() == aPresShell->GetDocument(),
  1.2156 +                 "Wrong document?");
  1.2157 +    nsIFrame *focusFrame = aContent->GetPrimaryFrame();
  1.2158 +    if (focusFrame)
  1.2159 +      frameSelection = focusFrame->GetFrameSelection();
  1.2160 +  }
  1.2161 +
  1.2162 +  nsRefPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
  1.2163 +
  1.2164 +  if (docFrameSelection && caret &&
  1.2165 +     (frameSelection == docFrameSelection || !aContent)) {
  1.2166 +    nsISelection* domSelection = docFrameSelection->
  1.2167 +      GetSelection(nsISelectionController::SELECTION_NORMAL);
  1.2168 +    if (domSelection) {
  1.2169 +      // First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
  1.2170 +      caret->SetCaretVisible(false);
  1.2171 +
  1.2172 +      // Caret must blink on non-editable elements
  1.2173 +      caret->SetIgnoreUserModify(true);
  1.2174 +      // Tell the caret which selection to use
  1.2175 +      caret->SetCaretDOMSelection(domSelection);
  1.2176 +
  1.2177 +      // In content, we need to set the caret. The only special case is edit
  1.2178 +      // fields, which have a different frame selection from the document.
  1.2179 +      // They will take care of making the caret visible themselves.
  1.2180 +
  1.2181 +      nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
  1.2182 +      if (!selCon)
  1.2183 +        return NS_ERROR_FAILURE;
  1.2184 +
  1.2185 +      selCon->SetCaretReadOnly(false);
  1.2186 +      selCon->SetCaretEnabled(aVisible);
  1.2187 +      caret->SetCaretVisible(aVisible);
  1.2188 +    }
  1.2189 +  }
  1.2190 +
  1.2191 +  return NS_OK;
  1.2192 +}
  1.2193 +
  1.2194 +nsresult
  1.2195 +nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
  1.2196 +                                     nsIPresShell* aPresShell,
  1.2197 +                                     nsIContent **aStartContent,
  1.2198 +                                     nsIContent **aEndContent)
  1.2199 +{
  1.2200 +  *aStartContent = *aEndContent = nullptr;
  1.2201 +  nsresult rv = NS_ERROR_FAILURE;
  1.2202 +
  1.2203 +  nsPresContext* presContext = aPresShell->GetPresContext();
  1.2204 +  NS_ASSERTION(presContext, "mPresContent is null!!");
  1.2205 +
  1.2206 +  nsRefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
  1.2207 +
  1.2208 +  nsCOMPtr<nsISelection> domSelection;
  1.2209 +  if (frameSelection) {
  1.2210 +    domSelection = frameSelection->
  1.2211 +      GetSelection(nsISelectionController::SELECTION_NORMAL);
  1.2212 +  }
  1.2213 +
  1.2214 +  nsCOMPtr<nsIDOMNode> startNode, endNode;
  1.2215 +  bool isCollapsed = false;
  1.2216 +  nsCOMPtr<nsIContent> startContent, endContent;
  1.2217 +  int32_t startOffset = 0;
  1.2218 +  if (domSelection) {
  1.2219 +    domSelection->GetIsCollapsed(&isCollapsed);
  1.2220 +    nsCOMPtr<nsIDOMRange> domRange;
  1.2221 +    rv = domSelection->GetRangeAt(0, getter_AddRefs(domRange));
  1.2222 +    if (domRange) {
  1.2223 +      domRange->GetStartContainer(getter_AddRefs(startNode));
  1.2224 +      domRange->GetEndContainer(getter_AddRefs(endNode));
  1.2225 +      domRange->GetStartOffset(&startOffset);
  1.2226 +
  1.2227 +      nsIContent *childContent = nullptr;
  1.2228 +
  1.2229 +      startContent = do_QueryInterface(startNode);
  1.2230 +      if (startContent && startContent->IsElement()) {
  1.2231 +        NS_ASSERTION(startOffset >= 0, "Start offset cannot be negative");  
  1.2232 +        childContent = startContent->GetChildAt(startOffset);
  1.2233 +        if (childContent) {
  1.2234 +          startContent = childContent;
  1.2235 +        }
  1.2236 +      }
  1.2237 +
  1.2238 +      endContent = do_QueryInterface(endNode);
  1.2239 +      if (endContent && endContent->IsElement()) {
  1.2240 +        int32_t endOffset = 0;
  1.2241 +        domRange->GetEndOffset(&endOffset);
  1.2242 +        NS_ASSERTION(endOffset >= 0, "End offset cannot be negative");
  1.2243 +        childContent = endContent->GetChildAt(endOffset);
  1.2244 +        if (childContent) {
  1.2245 +          endContent = childContent;
  1.2246 +        }
  1.2247 +      }
  1.2248 +    }
  1.2249 +  }
  1.2250 +  else {
  1.2251 +    rv = NS_ERROR_INVALID_ARG;
  1.2252 +  }
  1.2253 +
  1.2254 +  nsIFrame *startFrame = nullptr;
  1.2255 +  if (startContent) {
  1.2256 +    startFrame = startContent->GetPrimaryFrame();
  1.2257 +    if (isCollapsed) {
  1.2258 +      // Next check to see if our caret is at the very end of a node
  1.2259 +      // If so, the caret is actually sitting in front of the next
  1.2260 +      // logical frame's primary node - so for this case we need to
  1.2261 +      // change caretContent to that node.
  1.2262 +
  1.2263 +      if (startContent->NodeType() == nsIDOMNode::TEXT_NODE) {
  1.2264 +        nsAutoString nodeValue;
  1.2265 +        startContent->AppendTextTo(nodeValue);
  1.2266 +
  1.2267 +        bool isFormControl =
  1.2268 +          startContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL);
  1.2269 +
  1.2270 +        if (nodeValue.Length() == (uint32_t)startOffset && !isFormControl &&
  1.2271 +            startContent != aDocument->GetRootElement()) {
  1.2272 +          // Yes, indeed we were at the end of the last node
  1.2273 +          nsCOMPtr<nsIFrameEnumerator> frameTraversal;
  1.2274 +          nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
  1.2275 +                                             presContext, startFrame,
  1.2276 +                                             eLeaf,
  1.2277 +                                             false, // aVisual
  1.2278 +                                             false, // aLockInScrollView
  1.2279 +                                             true      // aFollowOOFs
  1.2280 +                                             );
  1.2281 +          NS_ENSURE_SUCCESS(rv, rv);
  1.2282 +
  1.2283 +          nsIFrame *newCaretFrame = nullptr;
  1.2284 +          nsCOMPtr<nsIContent> newCaretContent = startContent;
  1.2285 +          bool endOfSelectionInStartNode(startContent == endContent);
  1.2286 +          do {
  1.2287 +            // Continue getting the next frame until the primary content for the frame
  1.2288 +            // we are on changes - we don't want to be stuck in the same place
  1.2289 +            frameTraversal->Next();
  1.2290 +            newCaretFrame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
  1.2291 +            if (nullptr == newCaretFrame)
  1.2292 +              break;
  1.2293 +            newCaretContent = newCaretFrame->GetContent();
  1.2294 +          } while (!newCaretContent || newCaretContent == startContent);
  1.2295 +
  1.2296 +          if (newCaretFrame && newCaretContent) {
  1.2297 +            // If the caret is exactly at the same position of the new frame,
  1.2298 +            // then we can use the newCaretFrame and newCaretContent for our position
  1.2299 +            nsRefPtr<nsCaret> caret = aPresShell->GetCaret();
  1.2300 +            nsRect caretRect;
  1.2301 +            nsIFrame *frame = caret->GetGeometry(domSelection, &caretRect);
  1.2302 +            if (frame) {
  1.2303 +              nsPoint caretWidgetOffset;
  1.2304 +              nsIWidget *widget = frame->GetNearestWidget(caretWidgetOffset);
  1.2305 +              caretRect.MoveBy(caretWidgetOffset);
  1.2306 +              nsPoint newCaretOffset;
  1.2307 +              nsIWidget *newCaretWidget = newCaretFrame->GetNearestWidget(newCaretOffset);
  1.2308 +              if (widget == newCaretWidget && caretRect.y == newCaretOffset.y &&
  1.2309 +                  caretRect.x == newCaretOffset.x) {
  1.2310 +                // The caret is at the start of the new element.
  1.2311 +                startFrame = newCaretFrame;
  1.2312 +                startContent = newCaretContent;
  1.2313 +                if (endOfSelectionInStartNode) {
  1.2314 +                  endContent = newCaretContent; // Ensure end of selection is not before start
  1.2315 +                }
  1.2316 +              }
  1.2317 +            }
  1.2318 +          }
  1.2319 +        }
  1.2320 +      }
  1.2321 +    }
  1.2322 +  }
  1.2323 +
  1.2324 +  *aStartContent = startContent;
  1.2325 +  *aEndContent = endContent;
  1.2326 +  NS_IF_ADDREF(*aStartContent);
  1.2327 +  NS_IF_ADDREF(*aEndContent);
  1.2328 +
  1.2329 +  return rv;
  1.2330 +}
  1.2331 +
  1.2332 +nsresult
  1.2333 +nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
  1.2334 +                                            nsIContent* aStartContent,
  1.2335 +                                            int32_t aType, bool aNoParentTraversal,
  1.2336 +                                            nsIContent** aNextContent)
  1.2337 +{
  1.2338 +  *aNextContent = nullptr;
  1.2339 +
  1.2340 +  nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  1.2341 +  if (!docShell)
  1.2342 +    return NS_OK;
  1.2343 +
  1.2344 +  nsCOMPtr<nsIContent> startContent = aStartContent;
  1.2345 +  if (!startContent && aType != MOVEFOCUS_CARET) {
  1.2346 +    if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC) {
  1.2347 +      // When moving between documents, make sure to get the right
  1.2348 +      // starting content in a descendant.
  1.2349 +      nsCOMPtr<nsPIDOMWindow> focusedWindow;
  1.2350 +      startContent = GetFocusedDescendant(aWindow, true, getter_AddRefs(focusedWindow));
  1.2351 +    }
  1.2352 +    else {
  1.2353 +      startContent = aWindow->GetFocusedNode();
  1.2354 +    }
  1.2355 +  }
  1.2356 +
  1.2357 +  nsCOMPtr<nsIDocument> doc;
  1.2358 +  if (startContent)
  1.2359 +    doc = startContent->GetCurrentDoc();
  1.2360 +  else
  1.2361 +    doc = aWindow->GetExtantDoc();
  1.2362 +  if (!doc)
  1.2363 +    return NS_OK;
  1.2364 +
  1.2365 +  LookAndFeel::GetInt(LookAndFeel::eIntID_TabFocusModel,
  1.2366 +                      &nsIContent::sTabFocusModel);
  1.2367 +
  1.2368 +  if (aType == MOVEFOCUS_ROOT) {
  1.2369 +    NS_IF_ADDREF(*aNextContent = GetRootForFocus(aWindow, doc, false, false));
  1.2370 +    return NS_OK;
  1.2371 +  }
  1.2372 +  if (aType == MOVEFOCUS_FORWARDDOC) {
  1.2373 +    NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, true));
  1.2374 +    return NS_OK;
  1.2375 +  }
  1.2376 +  if (aType == MOVEFOCUS_BACKWARDDOC) {
  1.2377 +    NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, false));
  1.2378 +    return NS_OK;
  1.2379 +  }
  1.2380 +  
  1.2381 +  nsIContent* rootContent = doc->GetRootElement();
  1.2382 +  NS_ENSURE_TRUE(rootContent, NS_OK);
  1.2383 +
  1.2384 +  nsIPresShell *presShell = doc->GetShell();
  1.2385 +  NS_ENSURE_TRUE(presShell, NS_OK);
  1.2386 +
  1.2387 +  if (aType == MOVEFOCUS_FIRST) {
  1.2388 +    if (!aStartContent)
  1.2389 +      startContent = rootContent;
  1.2390 +    return GetNextTabbableContent(presShell, startContent,
  1.2391 +                                  nullptr, startContent,
  1.2392 +                                  true, 1, false, aNextContent);
  1.2393 +  }
  1.2394 +  if (aType == MOVEFOCUS_LAST) {
  1.2395 +    if (!aStartContent)
  1.2396 +      startContent = rootContent;
  1.2397 +    return GetNextTabbableContent(presShell, startContent,
  1.2398 +                                  nullptr, startContent,
  1.2399 +                                  false, 0, false, aNextContent);
  1.2400 +  }
  1.2401 +
  1.2402 +  bool forward = (aType == MOVEFOCUS_FORWARD || aType == MOVEFOCUS_CARET);
  1.2403 +  bool doNavigation = true;
  1.2404 +  bool ignoreTabIndex = false;
  1.2405 +  // when a popup is open, we want to ensure that tab navigation occurs only
  1.2406 +  // within the most recently opened panel. If a popup is open, its frame will
  1.2407 +  // be stored in popupFrame.
  1.2408 +  nsIFrame* popupFrame = nullptr;
  1.2409 +
  1.2410 +  int32_t tabIndex = forward ? 1 : 0;
  1.2411 +  if (startContent) {
  1.2412 +    nsIFrame* frame = startContent->GetPrimaryFrame();
  1.2413 +    if (startContent->Tag() == nsGkAtoms::area &&
  1.2414 +        startContent->IsHTML())
  1.2415 +      startContent->IsFocusable(&tabIndex);
  1.2416 +    else if (frame)
  1.2417 +      frame->IsFocusable(&tabIndex, 0);
  1.2418 +    else
  1.2419 +      startContent->IsFocusable(&tabIndex);
  1.2420 +
  1.2421 +    // if the current element isn't tabbable, ignore the tabindex and just
  1.2422 +    // look for the next element. The root content won't have a tabindex
  1.2423 +    // so just treat this as the beginning of the tab order.
  1.2424 +    if (tabIndex < 0) {
  1.2425 +      tabIndex = 1;
  1.2426 +      if (startContent != rootContent)
  1.2427 +        ignoreTabIndex = true;
  1.2428 +    }
  1.2429 +
  1.2430 +    // check if the focus is currently inside a popup. Elements such as the
  1.2431 +    // autocomplete widget use the noautofocus attribute to allow the focus to
  1.2432 +    // remain outside the popup when it is opened.
  1.2433 +    if (frame) {
  1.2434 +      popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
  1.2435 +                                                        nsGkAtoms::menuPopupFrame);
  1.2436 +    }
  1.2437 +
  1.2438 +    if (popupFrame) {
  1.2439 +      // Don't navigate outside of a popup, so pretend that the
  1.2440 +      // root content is the popup itself
  1.2441 +      rootContent = popupFrame->GetContent();
  1.2442 +      NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
  1.2443 +    }
  1.2444 +    else if (!forward) {
  1.2445 +      // If focus moves backward and when current focused node is root
  1.2446 +      // content or <body> element which is editable by contenteditable
  1.2447 +      // attribute, focus should move to its parent document.
  1.2448 +      if (startContent == rootContent) {
  1.2449 +        doNavigation = false;
  1.2450 +      } else {
  1.2451 +        nsIDocument* doc = startContent->GetCurrentDoc();
  1.2452 +        if (startContent ==
  1.2453 +              nsLayoutUtils::GetEditableRootContentByContentEditable(doc)) {
  1.2454 +          doNavigation = false;
  1.2455 +        }
  1.2456 +      }
  1.2457 +    }
  1.2458 +  }
  1.2459 +  else {
  1.2460 +#ifdef MOZ_XUL
  1.2461 +    if (aType != MOVEFOCUS_CARET) {
  1.2462 +      // if there is no focus, yet a panel is open, focus the first item in
  1.2463 +      // the panel
  1.2464 +      nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1.2465 +      if (pm)
  1.2466 +        popupFrame = pm->GetTopPopup(ePopupTypePanel);
  1.2467 +    }
  1.2468 +#endif
  1.2469 +    if (popupFrame) {
  1.2470 +      rootContent = popupFrame->GetContent();
  1.2471 +      NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
  1.2472 +      startContent = rootContent;
  1.2473 +    }
  1.2474 +    else {
  1.2475 +      // Otherwise, for content shells, start from the location of the caret.
  1.2476 +      if (docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
  1.2477 +        nsCOMPtr<nsIContent> endSelectionContent;
  1.2478 +        GetSelectionLocation(doc, presShell,
  1.2479 +                             getter_AddRefs(startContent),
  1.2480 +                             getter_AddRefs(endSelectionContent));
  1.2481 +        // If the selection is on the rootContent, then there is no selection
  1.2482 +        if (startContent == rootContent) {
  1.2483 +          startContent = nullptr;
  1.2484 +        }
  1.2485 +
  1.2486 +        if (aType == MOVEFOCUS_CARET) {
  1.2487 +          // GetFocusInSelection finds a focusable link near the caret.
  1.2488 +          // If there is no start content though, don't do this to avoid
  1.2489 +          // focusing something unexpected.
  1.2490 +          if (startContent) {
  1.2491 +            GetFocusInSelection(aWindow, startContent,
  1.2492 +                                endSelectionContent, aNextContent);
  1.2493 +          }
  1.2494 +          return NS_OK;
  1.2495 +        }
  1.2496 +
  1.2497 +        if (startContent) {
  1.2498 +          // when starting from a selection, we always want to find the next or
  1.2499 +          // previous element in the document. So the tabindex on elements
  1.2500 +          // should be ignored.
  1.2501 +          ignoreTabIndex = true;
  1.2502 +        }
  1.2503 +      }
  1.2504 +
  1.2505 +      if (!startContent) {
  1.2506 +        // otherwise, just use the root content as the starting point
  1.2507 +        startContent = rootContent;
  1.2508 +        NS_ENSURE_TRUE(startContent, NS_OK);
  1.2509 +      }
  1.2510 +    }
  1.2511 +  }
  1.2512 +
  1.2513 +  NS_ASSERTION(startContent, "starting content not set");
  1.2514 +
  1.2515 +  // keep a reference to the starting content. If we find that again, it means
  1.2516 +  // we've iterated around completely and we don't want to adjust the focus.
  1.2517 +  // The skipOriginalContentCheck will be set to true only for the first time
  1.2518 +  // GetNextTabbableContent is called. This ensures that we don't break out
  1.2519 +  // when nothing is focused to start with. Specifically,
  1.2520 +  // GetNextTabbableContent first checks the root content -- which happens to
  1.2521 +  // be the same as the start content -- when nothing is focused and tabbing
  1.2522 +  // forward. Without skipOriginalContentCheck set to true, we'd end up
  1.2523 +  // returning right away and focusing nothing. Luckily, GetNextTabbableContent
  1.2524 +  // will never wrap around on its own, and can only return the original
  1.2525 +  // content when it is called a second time or later.
  1.2526 +  bool skipOriginalContentCheck = true;
  1.2527 +  nsIContent* originalStartContent = startContent;
  1.2528 +
  1.2529 +  LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent.get());
  1.2530 +  LOGFOCUSNAVIGATION(("  Tabindex: %d Ignore: %d", tabIndex, ignoreTabIndex));
  1.2531 +
  1.2532 +  while (doc) {
  1.2533 +    if (doNavigation) {
  1.2534 +      nsCOMPtr<nsIContent> nextFocus;
  1.2535 +      nsresult rv = GetNextTabbableContent(presShell, rootContent,
  1.2536 +                                           skipOriginalContentCheck ? nullptr : originalStartContent,
  1.2537 +                                           startContent, forward,
  1.2538 +                                           tabIndex, ignoreTabIndex,
  1.2539 +                                           getter_AddRefs(nextFocus));
  1.2540 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2541 +
  1.2542 +      // found a content node to focus.
  1.2543 +      if (nextFocus) {
  1.2544 +        LOGCONTENTNAVIGATION("Next Content: %s", nextFocus.get());
  1.2545 +
  1.2546 +        // as long as the found node was not the same as the starting node,
  1.2547 +        // set it as the return value.
  1.2548 +        if (nextFocus != originalStartContent)
  1.2549 +          NS_ADDREF(*aNextContent = nextFocus);
  1.2550 +        return NS_OK;
  1.2551 +      }
  1.2552 +
  1.2553 +      if (popupFrame) {
  1.2554 +        // in a popup, so start again from the beginning of the popup. However,
  1.2555 +        // if we already started at the beginning, then there isn't anything to
  1.2556 +        // focus, so just return
  1.2557 +        if (startContent != rootContent) {
  1.2558 +          startContent = rootContent;
  1.2559 +          tabIndex = forward ? 1 : 0;
  1.2560 +          continue;
  1.2561 +        }
  1.2562 +        return NS_OK;
  1.2563 +      }
  1.2564 +    }
  1.2565 +
  1.2566 +    doNavigation = true;
  1.2567 +    skipOriginalContentCheck = false;
  1.2568 +    ignoreTabIndex = false;
  1.2569 +
  1.2570 +    if (aNoParentTraversal) {
  1.2571 +      if (startContent == rootContent)
  1.2572 +        return NS_OK;
  1.2573 +
  1.2574 +      startContent = rootContent;
  1.2575 +      tabIndex = forward ? 1 : 0;
  1.2576 +      continue;
  1.2577 +    }
  1.2578 +
  1.2579 +    // reached the beginning or end of the document. Traverse up to the parent
  1.2580 +    // document and try again.
  1.2581 +    nsCOMPtr<nsIDocShellTreeItem> docShellParent;
  1.2582 +    docShell->GetParent(getter_AddRefs(docShellParent));
  1.2583 +    if (docShellParent) {
  1.2584 +      // move up to the parent shell and try again from there.
  1.2585 +
  1.2586 +      // first, get the frame element this window is inside.
  1.2587 +      nsCOMPtr<nsPIDOMWindow> piWindow = do_GetInterface(docShell);
  1.2588 +      NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE);
  1.2589 +
  1.2590 +      // Next, retrieve the parent docshell, document and presshell.
  1.2591 +      docShell = do_QueryInterface(docShellParent);
  1.2592 +      NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
  1.2593 +
  1.2594 +      nsCOMPtr<nsPIDOMWindow> piParentWindow = do_GetInterface(docShellParent);
  1.2595 +      NS_ENSURE_TRUE(piParentWindow, NS_ERROR_FAILURE);
  1.2596 +      doc = piParentWindow->GetExtantDoc();
  1.2597 +      NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
  1.2598 +
  1.2599 +      presShell = doc->GetShell();
  1.2600 +
  1.2601 +      rootContent = doc->GetRootElement();
  1.2602 +      startContent = do_QueryInterface(piWindow->GetFrameElementInternal());
  1.2603 +      if (startContent) {
  1.2604 +        nsIFrame* frame = startContent->GetPrimaryFrame();
  1.2605 +        if (!frame)
  1.2606 +          return NS_OK;
  1.2607 +
  1.2608 +        frame->IsFocusable(&tabIndex, 0);
  1.2609 +        if (tabIndex < 0) {
  1.2610 +          tabIndex = 1;
  1.2611 +          ignoreTabIndex = true;
  1.2612 +        }
  1.2613 +
  1.2614 +        // if the frame is inside a popup, make sure to scan only within the
  1.2615 +        // popup. This handles the situation of tabbing amongst elements
  1.2616 +        // inside an iframe which is itself inside a popup. Otherwise,
  1.2617 +        // navigation would move outside the popup when tabbing outside the
  1.2618 +        // iframe.
  1.2619 +        popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
  1.2620 +                                                          nsGkAtoms::menuPopupFrame);
  1.2621 +        if (popupFrame) {
  1.2622 +          rootContent = popupFrame->GetContent();
  1.2623 +          NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
  1.2624 +        }
  1.2625 +      }
  1.2626 +      else {
  1.2627 +        startContent = rootContent;
  1.2628 +        tabIndex = forward ? 1 : 0;
  1.2629 +      }
  1.2630 +    }
  1.2631 +    else {
  1.2632 +      // no parent, so call the tree owner. This will tell the embedder that
  1.2633 +      // it should take the focus.
  1.2634 +      bool tookFocus;
  1.2635 +      docShell->TabToTreeOwner(forward, &tookFocus);
  1.2636 +      // if the tree owner, took the focus, blur the current content
  1.2637 +      if (tookFocus) {
  1.2638 +        nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(docShell);
  1.2639 +        if (window->GetFocusedNode() == mFocusedContent)
  1.2640 +          Blur(mFocusedWindow, nullptr, true, true);
  1.2641 +        else
  1.2642 +          window->SetFocusedNode(nullptr);
  1.2643 +        return NS_OK;
  1.2644 +      }
  1.2645 +
  1.2646 +      // reset the tab index and start again from the beginning or end
  1.2647 +      startContent = rootContent;
  1.2648 +      tabIndex = forward ? 1 : 0;
  1.2649 +    }
  1.2650 +
  1.2651 +    // wrapped all the way around and didn't find anything to move the focus
  1.2652 +    // to, so just break out
  1.2653 +    if (startContent == originalStartContent)
  1.2654 +      break;
  1.2655 +  }
  1.2656 +
  1.2657 +  return NS_OK;
  1.2658 +}
  1.2659 +
  1.2660 +nsresult
  1.2661 +nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
  1.2662 +                                       nsIContent* aRootContent,
  1.2663 +                                       nsIContent* aOriginalStartContent,
  1.2664 +                                       nsIContent* aStartContent,
  1.2665 +                                       bool aForward,
  1.2666 +                                       int32_t aCurrentTabIndex,
  1.2667 +                                       bool aIgnoreTabIndex,
  1.2668 +                                       nsIContent** aResultContent)
  1.2669 +{
  1.2670 +  *aResultContent = nullptr;
  1.2671 +
  1.2672 +  nsCOMPtr<nsIContent> startContent = aStartContent;
  1.2673 +  if (!startContent)
  1.2674 +    return NS_OK;
  1.2675 +
  1.2676 +  LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
  1.2677 +  LOGFOCUSNAVIGATION(("  tabindex: %d", aCurrentTabIndex));
  1.2678 +
  1.2679 +  nsPresContext* presContext = aPresShell->GetPresContext();
  1.2680 +
  1.2681 +  bool getNextFrame = true;
  1.2682 +  nsCOMPtr<nsIContent> iterStartContent = aStartContent;
  1.2683 +  while (1) {
  1.2684 +    nsIFrame* startFrame = iterStartContent->GetPrimaryFrame();
  1.2685 +    // if there is no frame, look for another content node that has a frame
  1.2686 +    if (!startFrame) {
  1.2687 +      // if the root content doesn't have a frame, just return
  1.2688 +      if (iterStartContent == aRootContent)
  1.2689 +        return NS_OK;
  1.2690 +
  1.2691 +      // look for the next or previous content node in tree order
  1.2692 +      iterStartContent = aForward ? iterStartContent->GetNextNode() : iterStartContent->GetPreviousContent();
  1.2693 +      // we've already skipped over the initial focused content, so we
  1.2694 +      // don't want to traverse frames.
  1.2695 +      getNextFrame = false;
  1.2696 +      if (iterStartContent)
  1.2697 +        continue;
  1.2698 +
  1.2699 +      // otherwise, as a last attempt, just look at the root content
  1.2700 +      iterStartContent = aRootContent;
  1.2701 +      continue;
  1.2702 +    }
  1.2703 +
  1.2704 +    nsCOMPtr<nsIFrameEnumerator> frameTraversal;
  1.2705 +    nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
  1.2706 +                                       presContext, startFrame,
  1.2707 +                                       ePreOrder,
  1.2708 +                                       false, // aVisual
  1.2709 +                                       false, // aLockInScrollView
  1.2710 +                                       true      // aFollowOOFs
  1.2711 +                                       );
  1.2712 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2713 +
  1.2714 +    if (iterStartContent == aRootContent) {
  1.2715 +      if (!aForward) {
  1.2716 +        frameTraversal->Last();
  1.2717 +      } else if (aRootContent->IsFocusable()) {
  1.2718 +        frameTraversal->Next();
  1.2719 +      }
  1.2720 +    }
  1.2721 +    else if (getNextFrame &&
  1.2722 +             (!iterStartContent || iterStartContent->Tag() != nsGkAtoms::area ||
  1.2723 +              !iterStartContent->IsHTML())) {
  1.2724 +      // Need to do special check in case we're in an imagemap which has multiple
  1.2725 +      // content nodes per frame, so don't skip over the starting frame.
  1.2726 +      if (aForward)
  1.2727 +        frameTraversal->Next();
  1.2728 +      else
  1.2729 +        frameTraversal->Prev();
  1.2730 +    }
  1.2731 +
  1.2732 +    // Walk frames to find something tabbable matching mCurrentTabIndex
  1.2733 +    nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
  1.2734 +    while (frame) {
  1.2735 +      // TabIndex not set defaults to 0 for form elements, anchors and other
  1.2736 +      // elements that are normally focusable. Tabindex defaults to -1
  1.2737 +      // for elements that are not normally focusable.
  1.2738 +      // The returned computed tabindex from IsFocusable() is as follows:
  1.2739 +      //          < 0 not tabbable at all
  1.2740 +      //          == 0 in normal tab order (last after positive tabindexed items)
  1.2741 +      //          > 0 can be tabbed to in the order specified by this value
  1.2742 +
  1.2743 +      int32_t tabIndex;
  1.2744 +      frame->IsFocusable(&tabIndex, 0);
  1.2745 +
  1.2746 +      LOGCONTENTNAVIGATION("Next Tabbable %s:", frame->GetContent());
  1.2747 +      LOGFOCUSNAVIGATION(("  with tabindex: %d expected: %d", tabIndex, aCurrentTabIndex));
  1.2748 +
  1.2749 +      nsIContent* currentContent = frame->GetContent();
  1.2750 +      if (tabIndex >= 0) {
  1.2751 +        NS_ASSERTION(currentContent, "IsFocusable set a tabindex for a frame with no content");
  1.2752 +        if (currentContent->Tag() == nsGkAtoms::img &&
  1.2753 +            currentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usemap)) {
  1.2754 +          // This is an image with a map. Image map areas are not traversed by
  1.2755 +          // nsIFrameTraversal so look for the next or previous area element.
  1.2756 +          nsIContent *areaContent =
  1.2757 +            GetNextTabbableMapArea(aForward, aCurrentTabIndex,
  1.2758 +                                   currentContent, iterStartContent);
  1.2759 +          if (areaContent) {
  1.2760 +            NS_ADDREF(*aResultContent = areaContent);
  1.2761 +            return NS_OK;
  1.2762 +          }
  1.2763 +        }
  1.2764 +        else if (aIgnoreTabIndex || aCurrentTabIndex == tabIndex) {
  1.2765 +          // break out if we've wrapped around to the start again.
  1.2766 +          if (aOriginalStartContent && currentContent == aOriginalStartContent) {
  1.2767 +            NS_ADDREF(*aResultContent = currentContent);
  1.2768 +            return NS_OK;
  1.2769 +          }
  1.2770 +
  1.2771 +          // found a node with a matching tab index. Check if it is a child
  1.2772 +          // frame. If so, navigate into the child frame instead.
  1.2773 +          nsIDocument* doc = currentContent->GetCurrentDoc();
  1.2774 +          NS_ASSERTION(doc, "content not in document");
  1.2775 +          nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
  1.2776 +          if (subdoc) {
  1.2777 +            if (!subdoc->EventHandlingSuppressed()) {
  1.2778 +              if (aForward) {
  1.2779 +                // when tabbing forward into a frame, return the root
  1.2780 +                // frame so that the canvas becomes focused.
  1.2781 +                nsCOMPtr<nsPIDOMWindow> subframe = subdoc->GetWindow();
  1.2782 +                if (subframe) {
  1.2783 +                  // If the subframe body is editable by contenteditable,
  1.2784 +                  // we should set the editor's root element rather than the
  1.2785 +                  // actual root element.  Otherwise, we should set the focus
  1.2786 +                  // to the root content.
  1.2787 +                  *aResultContent =
  1.2788 +                    nsLayoutUtils::GetEditableRootContentByContentEditable(subdoc);
  1.2789 +                  if (!*aResultContent ||
  1.2790 +                      !((*aResultContent)->GetPrimaryFrame())) {
  1.2791 +                    *aResultContent =
  1.2792 +                      GetRootForFocus(subframe, subdoc, false, true);
  1.2793 +                  }
  1.2794 +                  if (*aResultContent) {
  1.2795 +                    NS_ADDREF(*aResultContent);
  1.2796 +                    return NS_OK;
  1.2797 +                  }
  1.2798 +                }
  1.2799 +              }
  1.2800 +              Element* rootElement = subdoc->GetRootElement();
  1.2801 +              nsIPresShell* subShell = subdoc->GetShell();
  1.2802 +              if (rootElement && subShell) {
  1.2803 +                rv = GetNextTabbableContent(subShell, rootElement,
  1.2804 +                                            aOriginalStartContent, rootElement,
  1.2805 +                                            aForward, (aForward ? 1 : 0),
  1.2806 +                                            false, aResultContent);
  1.2807 +                NS_ENSURE_SUCCESS(rv, rv);
  1.2808 +                if (*aResultContent)
  1.2809 +                  return NS_OK;
  1.2810 +              }
  1.2811 +            }
  1.2812 +          }
  1.2813 +          // otherwise, use this as the next content node to tab to, unless
  1.2814 +          // this was the element we started on. This would happen for
  1.2815 +          // instance on an element with child frames, where frame navigation
  1.2816 +          // could return the original element again. In that case, just skip
  1.2817 +          // it. Also, if the next content node is the root content, then
  1.2818 +          // return it. This latter case would happen only if someone made a
  1.2819 +          // popup focusable.
  1.2820 +          // Also, when going backwards, check to ensure that the focus
  1.2821 +          // wouldn't be redirected. Otherwise, for example, when an input in
  1.2822 +          // a textbox is focused, the enclosing textbox would be found and
  1.2823 +          // the same inner input would be returned again.
  1.2824 +          else if (currentContent == aRootContent ||
  1.2825 +                   (currentContent != startContent &&
  1.2826 +                    (aForward || !GetRedirectedFocus(currentContent)))) {
  1.2827 +            NS_ADDREF(*aResultContent = currentContent);
  1.2828 +            return NS_OK;
  1.2829 +          }
  1.2830 +        }
  1.2831 +      }
  1.2832 +      else if (aOriginalStartContent && currentContent == aOriginalStartContent) {
  1.2833 +        // not focusable, so return if we have wrapped around to the original
  1.2834 +        // content. This is necessary in case the original starting content was
  1.2835 +        // not focusable.
  1.2836 +        NS_ADDREF(*aResultContent = currentContent);
  1.2837 +        return NS_OK;
  1.2838 +      }
  1.2839 +
  1.2840 +      // Move to the next or previous frame, but ignore continuation frames
  1.2841 +      // since only the first frame should be involved in focusability.
  1.2842 +      // Otherwise, a loop will occur in the following example:
  1.2843 +      //   <span tabindex="1">...<a/><a/>...</span>
  1.2844 +      // where the text wraps onto multiple lines. Tabbing from the second
  1.2845 +      // link can find one of the span's continuation frames between the link
  1.2846 +      // and the end of the span, and the span would end up getting focused
  1.2847 +      // again.
  1.2848 +      do {
  1.2849 +        if (aForward)
  1.2850 +          frameTraversal->Next();
  1.2851 +        else
  1.2852 +          frameTraversal->Prev();
  1.2853 +        frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
  1.2854 +      } while (frame && frame->GetPrevContinuation());
  1.2855 +    }
  1.2856 +
  1.2857 +    // If already at lowest priority tab (0), end search completely.
  1.2858 +    // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
  1.2859 +    if (aCurrentTabIndex == (aForward ? 0 : 1)) {
  1.2860 +      // if going backwards, the canvas should be focused once the beginning
  1.2861 +      // has been reached.
  1.2862 +      if (!aForward) {
  1.2863 +        nsCOMPtr<nsPIDOMWindow> window = GetCurrentWindow(aRootContent);
  1.2864 +        NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
  1.2865 +        NS_IF_ADDREF(*aResultContent =
  1.2866 +                     GetRootForFocus(window, aRootContent->GetCurrentDoc(), false, true));
  1.2867 +      }
  1.2868 +      break;
  1.2869 +    }
  1.2870 +
  1.2871 +    // continue looking for next highest priority tabindex
  1.2872 +    aCurrentTabIndex = GetNextTabIndex(aRootContent, aCurrentTabIndex, aForward);
  1.2873 +    startContent = iterStartContent = aRootContent;
  1.2874 +  }
  1.2875 +
  1.2876 +  return NS_OK;
  1.2877 +}
  1.2878 +
  1.2879 +nsIContent*
  1.2880 +nsFocusManager::GetNextTabbableMapArea(bool aForward,
  1.2881 +                                       int32_t aCurrentTabIndex,
  1.2882 +                                       nsIContent* aImageContent,
  1.2883 +                                       nsIContent* aStartContent)
  1.2884 +{
  1.2885 +  nsAutoString useMap;
  1.2886 +  aImageContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, useMap);
  1.2887 +
  1.2888 +  nsCOMPtr<nsIDocument> doc = aImageContent->GetDocument();
  1.2889 +  if (doc) {
  1.2890 +    nsCOMPtr<nsIContent> mapContent = doc->FindImageMap(useMap);
  1.2891 +    if (!mapContent)
  1.2892 +      return nullptr;
  1.2893 +    uint32_t count = mapContent->GetChildCount();
  1.2894 +    // First see if the the start content is in this map
  1.2895 +
  1.2896 +    int32_t index = mapContent->IndexOf(aStartContent);
  1.2897 +    int32_t tabIndex;
  1.2898 +    if (index < 0 || (aStartContent->IsFocusable(&tabIndex) &&
  1.2899 +                      tabIndex != aCurrentTabIndex)) {
  1.2900 +      // If aStartContent is in this map we must start iterating past it.
  1.2901 +      // We skip the case where aStartContent has tabindex == aStartContent
  1.2902 +      // since the next tab ordered element might be before it
  1.2903 +      // (or after for backwards) in the child list.
  1.2904 +      index = aForward ? -1 : (int32_t)count;
  1.2905 +    }
  1.2906 +
  1.2907 +    // GetChildAt will return nullptr if our index < 0 or index >= count
  1.2908 +    nsCOMPtr<nsIContent> areaContent;
  1.2909 +    while ((areaContent = mapContent->GetChildAt(aForward ? ++index : --index)) != nullptr) {
  1.2910 +      if (areaContent->IsFocusable(&tabIndex) && tabIndex == aCurrentTabIndex) {
  1.2911 +        return areaContent;
  1.2912 +      }
  1.2913 +    }
  1.2914 +  }
  1.2915 +
  1.2916 +  return nullptr;
  1.2917 +}
  1.2918 +
  1.2919 +int32_t
  1.2920 +nsFocusManager::GetNextTabIndex(nsIContent* aParent,
  1.2921 +                                int32_t aCurrentTabIndex,
  1.2922 +                                bool aForward)
  1.2923 +{
  1.2924 +  int32_t tabIndex, childTabIndex;
  1.2925 +
  1.2926 +  if (aForward) {
  1.2927 +    tabIndex = 0;
  1.2928 +    for (nsIContent* child = aParent->GetFirstChild();
  1.2929 +         child;
  1.2930 +         child = child->GetNextSibling()) {
  1.2931 +      childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
  1.2932 +      if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
  1.2933 +        tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
  1.2934 +      }
  1.2935 +
  1.2936 +      nsAutoString tabIndexStr;
  1.2937 +      child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
  1.2938 +      nsresult ec;
  1.2939 +      int32_t val = tabIndexStr.ToInteger(&ec);
  1.2940 +      if (NS_SUCCEEDED (ec) && val > aCurrentTabIndex && val != tabIndex) {
  1.2941 +        tabIndex = (tabIndex == 0 || val < tabIndex) ? val : tabIndex;
  1.2942 +      }
  1.2943 +    }
  1.2944 +  }
  1.2945 +  else { /* !aForward */
  1.2946 +    tabIndex = 1;
  1.2947 +    for (nsIContent* child = aParent->GetFirstChild();
  1.2948 +         child;
  1.2949 +         child = child->GetNextSibling()) {
  1.2950 +      childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
  1.2951 +      if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
  1.2952 +          (childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
  1.2953 +        tabIndex = childTabIndex;
  1.2954 +      }
  1.2955 +
  1.2956 +      nsAutoString tabIndexStr;
  1.2957 +      child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
  1.2958 +      nsresult ec;
  1.2959 +      int32_t val = tabIndexStr.ToInteger(&ec);
  1.2960 +      if (NS_SUCCEEDED (ec)) {
  1.2961 +        if ((aCurrentTabIndex == 0 && val > tabIndex) ||
  1.2962 +            (val < aCurrentTabIndex && val > tabIndex) ) {
  1.2963 +          tabIndex = val;
  1.2964 +        }
  1.2965 +      }
  1.2966 +    }
  1.2967 +  }
  1.2968 +
  1.2969 +  return tabIndex;
  1.2970 +}
  1.2971 +
  1.2972 +nsIContent*
  1.2973 +nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
  1.2974 +                                nsIDocument* aDocument,
  1.2975 +                                bool aIsForDocNavigation,
  1.2976 +                                bool aCheckVisibility)
  1.2977 +{
  1.2978 +  // the root element's canvas may be focused as long as the document is in a
  1.2979 +  // a non-chrome shell and does not contain a frameset.
  1.2980 +  if (aIsForDocNavigation) {
  1.2981 +    nsCOMPtr<nsIContent> docContent =
  1.2982 +      do_QueryInterface(aWindow->GetFrameElementInternal());
  1.2983 +    // document navigation skips iframes and frames that are specifically non-focusable
  1.2984 +    if (docContent) {
  1.2985 +      if (docContent->Tag() == nsGkAtoms::iframe)
  1.2986 +        return nullptr;
  1.2987 +
  1.2988 +      nsIFrame* frame = docContent->GetPrimaryFrame();
  1.2989 +      if (!frame || !frame->IsFocusable(nullptr, 0))
  1.2990 +        return nullptr;
  1.2991 +    }
  1.2992 +  } else {
  1.2993 +    nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
  1.2994 +    if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
  1.2995 +      return nullptr;
  1.2996 +    }
  1.2997 +  }
  1.2998 +
  1.2999 +  if (aCheckVisibility && !IsWindowVisible(aWindow))
  1.3000 +    return nullptr;
  1.3001 +
  1.3002 +  Element *rootElement = aDocument->GetRootElement();
  1.3003 +  if (!rootElement) {
  1.3004 +    return nullptr;
  1.3005 +  }
  1.3006 +
  1.3007 +  if (aCheckVisibility && !rootElement->GetPrimaryFrame()) {
  1.3008 +    return nullptr;
  1.3009 +  }
  1.3010 +
  1.3011 +  // Finally, check if this is a frameset
  1.3012 +  nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
  1.3013 +  if (htmlDoc && aDocument->GetHtmlChildElement(nsGkAtoms::frameset)) {
  1.3014 +    return nullptr;
  1.3015 +  }
  1.3016 +
  1.3017 +  return rootElement;
  1.3018 +}
  1.3019 +
  1.3020 +void
  1.3021 +nsFocusManager::GetLastDocShell(nsIDocShellTreeItem* aItem,
  1.3022 +                                nsIDocShellTreeItem** aResult)
  1.3023 +{
  1.3024 +  *aResult = nullptr;
  1.3025 +
  1.3026 +  nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
  1.3027 +  while (curItem) {
  1.3028 +    int32_t childCount = 0;
  1.3029 +    curItem->GetChildCount(&childCount);
  1.3030 +    if (!childCount) {
  1.3031 +      *aResult = curItem;
  1.3032 +      NS_ADDREF(*aResult);
  1.3033 +      return;
  1.3034 +    }
  1.3035 +
  1.3036 +    
  1.3037 +    curItem->GetChildAt(childCount - 1, getter_AddRefs(curItem));
  1.3038 +  }
  1.3039 +}
  1.3040 +
  1.3041 +void
  1.3042 +nsFocusManager::GetNextDocShell(nsIDocShellTreeItem* aItem,
  1.3043 +                                nsIDocShellTreeItem** aResult)
  1.3044 +{
  1.3045 +  *aResult = nullptr;
  1.3046 +
  1.3047 +  int32_t childCount = 0;
  1.3048 +  aItem->GetChildCount(&childCount);
  1.3049 +  if (childCount) {
  1.3050 +    aItem->GetChildAt(0, aResult);
  1.3051 +    if (*aResult)
  1.3052 +      return;
  1.3053 +  }
  1.3054 +
  1.3055 +  nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
  1.3056 +  while (curItem) {
  1.3057 +    nsCOMPtr<nsIDocShellTreeItem> parentItem;
  1.3058 +    curItem->GetParent(getter_AddRefs(parentItem));
  1.3059 +    if (!parentItem)
  1.3060 +      return;
  1.3061 +
  1.3062 +    // Note that we avoid using GetChildOffset() here because docshell
  1.3063 +    // child offsets can't be trusted to be correct. bug 162283.
  1.3064 +    nsCOMPtr<nsIDocShellTreeItem> iterItem;
  1.3065 +    childCount = 0;
  1.3066 +    parentItem->GetChildCount(&childCount);
  1.3067 +    for (int32_t index = 0; index < childCount; ++index) {
  1.3068 +      parentItem->GetChildAt(index, getter_AddRefs(iterItem));
  1.3069 +      if (iterItem == curItem) {
  1.3070 +        ++index;
  1.3071 +        if (index < childCount) {
  1.3072 +          parentItem->GetChildAt(index, aResult);
  1.3073 +          if (*aResult)
  1.3074 +            return;
  1.3075 +        }
  1.3076 +        break;
  1.3077 +      }
  1.3078 +    }
  1.3079 +
  1.3080 +    curItem = parentItem;
  1.3081 +  }
  1.3082 +}
  1.3083 +
  1.3084 +void
  1.3085 +nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem* aItem,
  1.3086 +                                    nsIDocShellTreeItem** aResult)
  1.3087 +{
  1.3088 +  *aResult = nullptr;
  1.3089 +
  1.3090 +  nsCOMPtr<nsIDocShellTreeItem> parentItem;
  1.3091 +  aItem->GetParent(getter_AddRefs(parentItem));
  1.3092 +  if (!parentItem)
  1.3093 +    return;
  1.3094 +
  1.3095 +  // Note that we avoid using GetChildOffset() here because docshell
  1.3096 +  // child offsets can't be trusted to be correct. bug 162283.
  1.3097 +  int32_t childCount = 0;
  1.3098 +  parentItem->GetChildCount(&childCount);
  1.3099 +  nsCOMPtr<nsIDocShellTreeItem> prevItem, iterItem;
  1.3100 +  for (int32_t index = 0; index < childCount; ++index) {
  1.3101 +    parentItem->GetChildAt(index, getter_AddRefs(iterItem));
  1.3102 +    if (iterItem == aItem)
  1.3103 +      break;
  1.3104 +    prevItem = iterItem;
  1.3105 +  }
  1.3106 +
  1.3107 +  if (prevItem)
  1.3108 +    GetLastDocShell(prevItem, aResult);
  1.3109 +  else
  1.3110 +    NS_ADDREF(*aResult = parentItem);
  1.3111 +}
  1.3112 +
  1.3113 +nsIContent*
  1.3114 +nsFocusManager::GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward)
  1.3115 +{
  1.3116 +  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1.3117 +  if (!pm)
  1.3118 +    return nullptr;
  1.3119 +
  1.3120 +  // Iterate through the array backwards if aForward is false.
  1.3121 +  nsTArray<nsIFrame *> popups;
  1.3122 +  pm->GetVisiblePopups(popups);
  1.3123 +  int32_t i = aForward ? 0 : popups.Length() - 1;
  1.3124 +  int32_t end = aForward ? popups.Length() : -1;
  1.3125 +
  1.3126 +  for (; i != end; aForward ? i++ : i--) {
  1.3127 +    nsIFrame* popupFrame = popups[i];
  1.3128 +    if (aCurrentPopup) {
  1.3129 +      // If the current popup is set, then we need to skip over this popup and
  1.3130 +      // wait until the currently focused popup is found. Once found, the
  1.3131 +      // current popup will be cleared so that the next popup is used.
  1.3132 +      if (aCurrentPopup == popupFrame)
  1.3133 +        aCurrentPopup = nullptr;
  1.3134 +      continue;
  1.3135 +    }
  1.3136 +
  1.3137 +    // Skip over non-panels
  1.3138 +    if (popupFrame->GetContent()->Tag() != nsGkAtoms::panel ||
  1.3139 +        (aDocument && popupFrame->GetContent()->GetCurrentDoc() != aDocument)) {
  1.3140 +      continue;
  1.3141 +    }
  1.3142 +
  1.3143 +    // Find the first focusable content within the popup. If there isn't any
  1.3144 +    // focusable content in the popup, skip to the next popup.
  1.3145 +    nsIPresShell* presShell = popupFrame->PresContext()->GetPresShell();
  1.3146 +    if (presShell) {
  1.3147 +      nsCOMPtr<nsIContent> nextFocus;
  1.3148 +      nsIContent* popup = popupFrame->GetContent();
  1.3149 +      nsresult rv = GetNextTabbableContent(presShell, popup,
  1.3150 +                                           nullptr, popup,
  1.3151 +                                           true, 1, false,
  1.3152 +                                           getter_AddRefs(nextFocus));
  1.3153 +      if (NS_SUCCEEDED(rv) && nextFocus) {
  1.3154 +        return nextFocus.get();
  1.3155 +      }
  1.3156 +    }
  1.3157 +  }
  1.3158 +
  1.3159 +  return nullptr;
  1.3160 +}
  1.3161 +
  1.3162 +nsIContent*
  1.3163 +nsFocusManager::GetNextTabbableDocument(nsIContent* aStartContent, bool aForward)
  1.3164 +{
  1.3165 +  // If currentPopup is set, then the starting content is in a panel.
  1.3166 +  nsIFrame* currentPopup = nullptr;
  1.3167 +  nsCOMPtr<nsIDocument> doc;
  1.3168 +  nsCOMPtr<nsIDocShell> startDocShell;
  1.3169 +
  1.3170 +  if (aStartContent) {
  1.3171 +    doc = aStartContent->GetCurrentDoc();
  1.3172 +    if (doc) {
  1.3173 +      startDocShell = doc->GetWindow()->GetDocShell();
  1.3174 +    }
  1.3175 +
  1.3176 +    // Check if the starting content is inside a panel. Document navigation
  1.3177 +    // must start from this panel instead of the document root.
  1.3178 +    nsIContent* content = aStartContent;
  1.3179 +    while (content) {
  1.3180 +      if (content->NodeInfo()->Equals(nsGkAtoms::panel, kNameSpaceID_XUL)) {
  1.3181 +        currentPopup = content->GetPrimaryFrame();
  1.3182 +        break;
  1.3183 +      }
  1.3184 +      content = content->GetParent();
  1.3185 +    }
  1.3186 +  }
  1.3187 +  else if (mFocusedWindow) {
  1.3188 +    startDocShell = mFocusedWindow->GetDocShell();
  1.3189 +    doc = mFocusedWindow->GetExtantDoc();
  1.3190 +  }
  1.3191 +  else {
  1.3192 +    nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mActiveWindow);
  1.3193 +    startDocShell = do_QueryInterface(webnav);
  1.3194 +
  1.3195 +    if (mActiveWindow) {
  1.3196 +      doc = mActiveWindow->GetExtantDoc();
  1.3197 +    }
  1.3198 +  }
  1.3199 +
  1.3200 +  if (!startDocShell)
  1.3201 +    return nullptr;
  1.3202 +
  1.3203 +  // perform a depth first search (preorder) of the docshell tree
  1.3204 +  // looking for an HTML Frame or a chrome document
  1.3205 +  nsIContent* content = aStartContent;
  1.3206 +  nsCOMPtr<nsIDocShellTreeItem> curItem = startDocShell.get();
  1.3207 +  nsCOMPtr<nsIDocShellTreeItem> nextItem;
  1.3208 +  do {
  1.3209 +    // If moving forward, check for a panel in the starting document. If one
  1.3210 +    // exists with focusable content, return that content instead of the next
  1.3211 +    // document. If currentPopup is set, then, another panel may exist. If no
  1.3212 +    // such panel exists, then continue on to check the next document.
  1.3213 +    // When moving backwards, and the starting content is in a panel, then
  1.3214 +    // check for additional panels in the starting document. If the starting
  1.3215 +    // content is not in a panel, move back to the previous document and check
  1.3216 +    // for panels there.
  1.3217 +
  1.3218 +    bool checkPopups = false;
  1.3219 +    nsCOMPtr<nsPIDOMWindow> nextFrame = nullptr;
  1.3220 +
  1.3221 +    if (doc && (aForward || currentPopup)) {
  1.3222 +      nsIContent* popupContent = GetNextTabbablePanel(doc, currentPopup, aForward);
  1.3223 +      if (popupContent)
  1.3224 +        return popupContent;
  1.3225 +
  1.3226 +      if (!aForward && currentPopup) {
  1.3227 +        // The starting content was in a popup, yet no other popups were
  1.3228 +        // found. Move onto the starting content's document.
  1.3229 +        nextFrame = doc->GetWindow();
  1.3230 +      }
  1.3231 +    }
  1.3232 +
  1.3233 +    // Look for the next or previous document.
  1.3234 +    if (!nextFrame) {
  1.3235 +      if (aForward) {
  1.3236 +        GetNextDocShell(curItem, getter_AddRefs(nextItem));
  1.3237 +        if (!nextItem) {
  1.3238 +          // wrap around to the beginning, which is the top of the tree
  1.3239 +          startDocShell->GetRootTreeItem(getter_AddRefs(nextItem));
  1.3240 +        }
  1.3241 +      }
  1.3242 +      else {
  1.3243 +        GetPreviousDocShell(curItem, getter_AddRefs(nextItem));
  1.3244 +        if (!nextItem) {
  1.3245 +          // wrap around to the end, which is the last item in the tree
  1.3246 +          nsCOMPtr<nsIDocShellTreeItem> rootItem;
  1.3247 +          startDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
  1.3248 +          GetLastDocShell(rootItem, getter_AddRefs(nextItem));
  1.3249 +        }
  1.3250 +
  1.3251 +        // When going back to the previous document, check for any focusable
  1.3252 +        // popups in that previous document first.
  1.3253 +        checkPopups = true;
  1.3254 +      }
  1.3255 +
  1.3256 +      curItem = nextItem;
  1.3257 +      nextFrame = do_GetInterface(nextItem);
  1.3258 +    }
  1.3259 +
  1.3260 +    if (!nextFrame)
  1.3261 +      return nullptr;
  1.3262 +
  1.3263 +    // Clear currentPopup for the next iteration
  1.3264 +    currentPopup = nullptr;
  1.3265 +
  1.3266 +    // If event handling is suppressed, move on to the next document. Set
  1.3267 +    // content to null so that the popup check will be skipped on the next
  1.3268 +    // loop iteration.
  1.3269 +    doc = nextFrame->GetExtantDoc();
  1.3270 +    if (!doc || doc->EventHandlingSuppressed()) {
  1.3271 +      content = nullptr;
  1.3272 +      continue;
  1.3273 +    }
  1.3274 +
  1.3275 +    if (checkPopups) {
  1.3276 +      // When iterating backwards, check the panels of the previous document
  1.3277 +      // first. If a panel exists that has focusable content, focus that.
  1.3278 +      // Otherwise, continue on to focus the document.
  1.3279 +      nsIContent* popupContent = GetNextTabbablePanel(doc, nullptr, false);
  1.3280 +      if (popupContent)
  1.3281 +        return popupContent;
  1.3282 +    }
  1.3283 +
  1.3284 +    content = GetRootForFocus(nextFrame, doc, true, true);
  1.3285 +    if (content && !GetRootForFocus(nextFrame, doc, false, false)) {
  1.3286 +      // if the found content is in a chrome shell or a frameset, navigate
  1.3287 +      // forward one tabbable item so that the first item is focused. Note
  1.3288 +      // that we always go forward and not back here.
  1.3289 +      nsCOMPtr<nsIContent> nextFocus;
  1.3290 +      Element* rootElement = doc->GetRootElement();
  1.3291 +      nsIPresShell* presShell = doc->GetShell();
  1.3292 +      if (presShell) {
  1.3293 +        nsresult rv = GetNextTabbableContent(presShell, rootElement,
  1.3294 +                                             nullptr, rootElement,
  1.3295 +                                             true, 1, false,
  1.3296 +                                             getter_AddRefs(nextFocus));
  1.3297 +        return NS_SUCCEEDED(rv) ? nextFocus.get() : nullptr;
  1.3298 +      }
  1.3299 +    }
  1.3300 +
  1.3301 +  } while (!content);
  1.3302 +
  1.3303 +  return content;
  1.3304 +}
  1.3305 +
  1.3306 +void
  1.3307 +nsFocusManager::GetFocusInSelection(nsPIDOMWindow* aWindow,
  1.3308 +                                    nsIContent* aStartSelection,
  1.3309 +                                    nsIContent* aEndSelection,
  1.3310 +                                    nsIContent** aFocusedContent)
  1.3311 +{
  1.3312 +  *aFocusedContent = nullptr;
  1.3313 +
  1.3314 +  nsCOMPtr<nsIContent> testContent = aStartSelection;
  1.3315 +  nsCOMPtr<nsIContent> nextTestContent = aEndSelection;
  1.3316 +
  1.3317 +  nsCOMPtr<nsIContent> currentFocus = aWindow->GetFocusedNode();
  1.3318 +
  1.3319 +  // We now have the correct start node in selectionContent!
  1.3320 +  // Search for focusable elements, starting with selectionContent
  1.3321 +
  1.3322 +  // Method #1: Keep going up while we look - an ancestor might be focusable
  1.3323 +  // We could end the loop earlier, such as when we're no longer
  1.3324 +  // in the same frame, by comparing selectionContent->GetPrimaryFrame()
  1.3325 +  // with a variable holding the starting selectionContent
  1.3326 +  while (testContent) {
  1.3327 +    // Keep testing while selectionContent is equal to something,
  1.3328 +    // eventually we'll run out of ancestors
  1.3329 +
  1.3330 +    nsCOMPtr<nsIURI> uri;
  1.3331 +    if (testContent == currentFocus ||
  1.3332 +        testContent->IsLink(getter_AddRefs(uri))) {
  1.3333 +      NS_ADDREF(*aFocusedContent = testContent);
  1.3334 +      return;
  1.3335 +    }
  1.3336 +
  1.3337 +    // Get the parent
  1.3338 +    testContent = testContent->GetParent();
  1.3339 +
  1.3340 +    if (!testContent) {
  1.3341 +      // We run this loop again, checking the ancestor chain of the selection's end point
  1.3342 +      testContent = nextTestContent;
  1.3343 +      nextTestContent = nullptr;
  1.3344 +    }
  1.3345 +  }
  1.3346 +
  1.3347 +  // We couldn't find an anchor that was an ancestor of the selection start
  1.3348 +  // Method #2: look for anchor in selection's primary range (depth first search)
  1.3349 +
  1.3350 +  // Turn into nodes so that we can use GetNextSibling() and GetFirstChild()
  1.3351 +  nsCOMPtr<nsIDOMNode> selectionNode(do_QueryInterface(aStartSelection));
  1.3352 +  nsCOMPtr<nsIDOMNode> endSelectionNode(do_QueryInterface(aEndSelection));
  1.3353 +  nsCOMPtr<nsIDOMNode> testNode;
  1.3354 +
  1.3355 +  do {
  1.3356 +    testContent = do_QueryInterface(selectionNode);
  1.3357 +
  1.3358 +    // We're looking for any focusable link that could be part of the
  1.3359 +    // main document's selection.
  1.3360 +    nsCOMPtr<nsIURI> uri;
  1.3361 +    if (testContent == currentFocus ||
  1.3362 +        testContent->IsLink(getter_AddRefs(uri))) {
  1.3363 +      NS_ADDREF(*aFocusedContent = testContent);
  1.3364 +      return;
  1.3365 +    }
  1.3366 +
  1.3367 +    selectionNode->GetFirstChild(getter_AddRefs(testNode));
  1.3368 +    if (testNode) {
  1.3369 +      selectionNode = testNode;
  1.3370 +      continue;
  1.3371 +    }
  1.3372 +
  1.3373 +    if (selectionNode == endSelectionNode)
  1.3374 +      break;
  1.3375 +    selectionNode->GetNextSibling(getter_AddRefs(testNode));
  1.3376 +    if (testNode) {
  1.3377 +      selectionNode = testNode;
  1.3378 +      continue;
  1.3379 +    }
  1.3380 +
  1.3381 +    do {
  1.3382 +      selectionNode->GetParentNode(getter_AddRefs(testNode));
  1.3383 +      if (!testNode || testNode == endSelectionNode) {
  1.3384 +        selectionNode = nullptr;
  1.3385 +        break;
  1.3386 +      }
  1.3387 +      testNode->GetNextSibling(getter_AddRefs(selectionNode));
  1.3388 +      if (selectionNode)
  1.3389 +        break;
  1.3390 +      selectionNode = testNode;
  1.3391 +    } while (true);
  1.3392 +  }
  1.3393 +  while (selectionNode && selectionNode != endSelectionNode);
  1.3394 +}
  1.3395 +
  1.3396 +class PointerUnlocker : public nsRunnable
  1.3397 +{
  1.3398 +public:
  1.3399 +  PointerUnlocker()
  1.3400 +  {
  1.3401 +    MOZ_ASSERT(!PointerUnlocker::sActiveUnlocker);
  1.3402 +    PointerUnlocker::sActiveUnlocker = this;
  1.3403 +  }
  1.3404 +
  1.3405 +  ~PointerUnlocker()
  1.3406 +  {
  1.3407 +    if (PointerUnlocker::sActiveUnlocker == this) {
  1.3408 +      PointerUnlocker::sActiveUnlocker = nullptr;
  1.3409 +    }
  1.3410 +  }
  1.3411 +
  1.3412 +  NS_IMETHOD Run()
  1.3413 +  {
  1.3414 +    if (PointerUnlocker::sActiveUnlocker == this) {
  1.3415 +      PointerUnlocker::sActiveUnlocker = nullptr;
  1.3416 +    }
  1.3417 +    NS_ENSURE_STATE(nsFocusManager::GetFocusManager());
  1.3418 +    nsPIDOMWindow* focused =
  1.3419 +      nsFocusManager::GetFocusManager()->GetFocusedWindow();
  1.3420 +    nsCOMPtr<nsIDocument> pointerLockedDoc =
  1.3421 +      do_QueryReferent(EventStateManager::sPointerLockedDoc);
  1.3422 +    if (pointerLockedDoc &&
  1.3423 +        !nsContentUtils::IsInPointerLockContext(focused)) {
  1.3424 +      nsIDocument::UnlockPointer();
  1.3425 +    }
  1.3426 +    return NS_OK;
  1.3427 +  }
  1.3428 +
  1.3429 +  static PointerUnlocker* sActiveUnlocker;
  1.3430 +};
  1.3431 +
  1.3432 +PointerUnlocker*
  1.3433 +PointerUnlocker::sActiveUnlocker = nullptr;
  1.3434 +
  1.3435 +void
  1.3436 +nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindow* aWindow)
  1.3437 +{
  1.3438 +  if (!PointerUnlocker::sActiveUnlocker &&
  1.3439 +      nsContentUtils::IsInPointerLockContext(mFocusedWindow) &&
  1.3440 +      !nsContentUtils::IsInPointerLockContext(aWindow)) {
  1.3441 +    nsCOMPtr<nsIRunnable> runnable = new PointerUnlocker();
  1.3442 +    NS_DispatchToCurrentThread(runnable);
  1.3443 +  }
  1.3444 +  mFocusedWindow = aWindow;
  1.3445 +}
  1.3446 +
  1.3447 +void
  1.3448 +nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
  1.3449 +{
  1.3450 +  if (!sInstance) {
  1.3451 +    return;
  1.3452 +  }
  1.3453 +
  1.3454 +  if (sInstance->mActiveWindow) {
  1.3455 +    sInstance->mActiveWindow->
  1.3456 +      MarkUncollectableForCCGeneration(aGeneration);
  1.3457 +  }
  1.3458 +  if (sInstance->mFocusedWindow) {
  1.3459 +    sInstance->mFocusedWindow->
  1.3460 +      MarkUncollectableForCCGeneration(aGeneration);
  1.3461 +  }
  1.3462 +  if (sInstance->mWindowBeingLowered) {
  1.3463 +    sInstance->mWindowBeingLowered->
  1.3464 +      MarkUncollectableForCCGeneration(aGeneration);
  1.3465 +  }
  1.3466 +  if (sInstance->mFocusedContent) {
  1.3467 +    sInstance->mFocusedContent->OwnerDoc()->
  1.3468 +      MarkUncollectableForCCGeneration(aGeneration);
  1.3469 +  }
  1.3470 +  if (sInstance->mFirstBlurEvent) {
  1.3471 +    sInstance->mFirstBlurEvent->OwnerDoc()->
  1.3472 +      MarkUncollectableForCCGeneration(aGeneration);
  1.3473 +  }
  1.3474 +  if (sInstance->mFirstFocusEvent) {
  1.3475 +    sInstance->mFirstFocusEvent->OwnerDoc()->
  1.3476 +      MarkUncollectableForCCGeneration(aGeneration);
  1.3477 +  }
  1.3478 +  if (sInstance->mMouseDownEventHandlingDocument) {
  1.3479 +    sInstance->mMouseDownEventHandlingDocument->
  1.3480 +      MarkUncollectableForCCGeneration(aGeneration);
  1.3481 +  }
  1.3482 +}
  1.3483 +
  1.3484 +nsresult
  1.3485 +NS_NewFocusManager(nsIFocusManager** aResult)
  1.3486 +{
  1.3487 +  NS_IF_ADDREF(*aResult = nsFocusManager::GetFocusManager());
  1.3488 +  return NS_OK;
  1.3489 +}

mercurial