dom/base/nsFocusManager.cpp

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

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

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

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

mercurial