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