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 +}