1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/TabParent.h"
8 #include "nsFocusManager.h"
10 #include "nsIInterfaceRequestorUtils.h"
11 #include "nsGkAtoms.h"
12 #include "nsContentUtils.h"
13 #include "nsIDocument.h"
14 #include "nsIDOMWindow.h"
15 #include "nsPIDOMWindow.h"
16 #include "nsIDOMElement.h"
17 #include "nsIDOMDocument.h"
18 #include "nsIDOMRange.h"
19 #include "nsIHTMLDocument.h"
20 #include "nsIDocShell.h"
21 #include "nsIDocShellTreeOwner.h"
22 #include "nsLayoutUtils.h"
23 #include "nsIPresShell.h"
24 #include "nsFrameTraversal.h"
25 #include "nsIWebNavigation.h"
27 #include "nsIBaseWindow.h"
28 #include "nsViewManager.h"
29 #include "nsFrameSelection.h"
30 #include "mozilla/dom/Selection.h"
31 #include "nsXULPopupManager.h"
32 #include "nsIScriptObjectPrincipal.h"
33 #include "nsIPrincipal.h"
34 #include "nsIObserverService.h"
35 #include "nsIObjectFrame.h"
36 #include "nsBindingManager.h"
37 #include "nsStyleCoord.h"
39 #include "mozilla/ContentEvents.h"
40 #include "mozilla/dom/Element.h"
41 #include "mozilla/EventDispatcher.h"
42 #include "mozilla/EventStateManager.h"
43 #include "mozilla/EventStates.h"
44 #include "mozilla/IMEStateManager.h"
45 #include "mozilla/LookAndFeel.h"
46 #include "mozilla/Preferences.h"
47 #include "mozilla/Services.h"
51 #include "nsIDOMXULTextboxElement.h"
52 #include "nsIDOMXULMenuListElement.h"
56 #include "nsAccessibilityService.h"
60 #include "nsIScriptError.h"
63 using namespace mozilla
;
64 using namespace mozilla::dom
;
65 using namespace mozilla::widget
;
69 // Two types of focus pr logging are available:
70 // 'Focus' for normal focus manager calls
71 // 'FocusNavigation' for tab and document navigation
72 PRLogModuleInfo
* gFocusLog
;
73 PRLogModuleInfo
* gFocusNavigationLog
;
75 #define LOGFOCUS(args) PR_LOG(gFocusLog, 4, args)
76 #define LOGFOCUSNAVIGATION(args) PR_LOG(gFocusNavigationLog, 4, args)
78 #define LOGTAG(log, format, content) \
80 nsAutoCString tag(NS_LITERAL_CSTRING("(none)")); \
82 content->Tag()->ToUTF8String(tag); \
84 PR_LOG(log, 4, (format, tag.get())); \
87 #define LOGCONTENT(format, content) LOGTAG(gFocusLog, format, content)
88 #define LOGCONTENTNAVIGATION(format, content) LOGTAG(gFocusNavigationLog, format, content)
92 #define LOGFOCUS(args)
93 #define LOGFOCUSNAVIGATION(args)
94 #define LOGCONTENT(format, content)
95 #define LOGCONTENTNAVIGATION(format, content)
99 struct nsDelayedBlurOrFocusEvent
101 nsDelayedBlurOrFocusEvent(uint32_t aType
,
102 nsIPresShell
* aPresShell
,
103 nsIDocument
* aDocument
,
104 EventTarget
* aTarget
)
106 mPresShell(aPresShell
),
107 mDocument(aDocument
),
110 nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent
& aOther
)
111 : mType(aOther
.mType
),
112 mPresShell(aOther
.mPresShell
),
113 mDocument(aOther
.mDocument
),
114 mTarget(aOther
.mTarget
) { }
117 nsCOMPtr
<nsIPresShell
> mPresShell
;
118 nsCOMPtr
<nsIDocument
> mDocument
;
119 nsCOMPtr
<EventTarget
> mTarget
;
122 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager
)
123 NS_INTERFACE_MAP_ENTRY(nsIFocusManager
)
124 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
125 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
126 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIFocusManager
)
129 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager
)
130 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager
)
132 NS_IMPL_CYCLE_COLLECTION(nsFocusManager
,
140 nsFocusManager
* nsFocusManager::sInstance
= nullptr;
141 bool nsFocusManager::sMouseFocusesFormControl
= false;
142 bool nsFocusManager::sTestMode
= false;
144 static const char* kObservedPrefs
[] = {
145 "accessibility.browsewithcaret",
146 "accessibility.tabfocus_applies_to_xul",
147 "accessibility.mouse_focuses_formcontrol",
148 "focusmanager.testmode",
152 nsFocusManager::nsFocusManager()
155 nsFocusManager::~nsFocusManager()
157 Preferences::RemoveObservers(this, kObservedPrefs
);
159 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
161 obs
->RemoveObserver(this, "xpcom-shutdown");
167 nsFocusManager::Init()
169 nsFocusManager
* fm
= new nsFocusManager();
170 NS_ENSURE_TRUE(fm
, NS_ERROR_OUT_OF_MEMORY
);
175 gFocusLog
= PR_NewLogModule("Focus");
176 gFocusNavigationLog
= PR_NewLogModule("FocusNavigation");
179 nsIContent::sTabFocusModelAppliesToXUL
=
180 Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
181 nsIContent::sTabFocusModelAppliesToXUL
);
183 sMouseFocusesFormControl
=
184 Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false);
186 sTestMode
= Preferences::GetBool("focusmanager.testmode", false);
188 Preferences::AddWeakObservers(fm
, kObservedPrefs
);
190 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
192 obs
->AddObserver(fm
, "xpcom-shutdown", true);
200 nsFocusManager::Shutdown()
202 NS_IF_RELEASE(sInstance
);
206 nsFocusManager::Observe(nsISupports
*aSubject
,
208 const char16_t
*aData
)
210 if (!nsCRT::strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
)) {
211 nsDependentString
data(aData
);
212 if (data
.EqualsLiteral("accessibility.browsewithcaret")) {
213 UpdateCaretForCaretBrowsingMode();
215 else if (data
.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
216 nsIContent::sTabFocusModelAppliesToXUL
=
217 Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
218 nsIContent::sTabFocusModelAppliesToXUL
);
220 else if (data
.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
221 sMouseFocusesFormControl
=
222 Preferences::GetBool("accessibility.mouse_focuses_formcontrol",
225 else if (data
.EqualsLiteral("focusmanager.testmode")) {
226 sTestMode
= Preferences::GetBool("focusmanager.testmode", false);
228 } else if (!nsCRT::strcmp(aTopic
, "xpcom-shutdown")) {
229 mActiveWindow
= nullptr;
230 mFocusedWindow
= nullptr;
231 mFocusedContent
= nullptr;
232 mFirstBlurEvent
= nullptr;
233 mFirstFocusEvent
= nullptr;
234 mWindowBeingLowered
= nullptr;
235 mDelayedBlurFocusEvents
.Clear();
236 mMouseButtonEventHandlingDocument
= nullptr;
242 // given a frame content node, retrieve the nsIDOMWindow displayed in it
243 static nsPIDOMWindow
*
244 GetContentWindow(nsIContent
* aContent
)
246 nsIDocument
* doc
= aContent
->GetComposedDoc();
248 nsIDocument
* subdoc
= doc
->GetSubDocumentFor(aContent
);
250 return subdoc
->GetWindow();
256 // get the current window for the given content node
257 static nsPIDOMWindow
*
258 GetCurrentWindow(nsIContent
* aContent
)
260 nsIDocument
* doc
= aContent
->GetComposedDoc();
261 return doc
? doc
->GetWindow() : nullptr;
266 nsFocusManager::GetFocusedDescendant(nsPIDOMWindow
* aWindow
, bool aDeep
,
267 nsPIDOMWindow
** aFocusedWindow
)
269 NS_ENSURE_TRUE(aWindow
, nullptr);
271 *aFocusedWindow
= nullptr;
273 nsIContent
* currentContent
= nullptr;
274 nsPIDOMWindow
* window
= aWindow
->GetOuterWindow();
276 *aFocusedWindow
= window
;
277 currentContent
= window
->GetFocusedNode();
278 if (!currentContent
|| !aDeep
)
281 window
= GetContentWindow(currentContent
);
284 NS_IF_ADDREF(*aFocusedWindow
);
286 return currentContent
;
291 nsFocusManager::GetRedirectedFocus(nsIContent
* aContent
)
294 if (aContent
->IsXUL()) {
295 nsCOMPtr
<nsIDOMNode
> inputField
;
297 nsCOMPtr
<nsIDOMXULTextBoxElement
> textbox
= do_QueryInterface(aContent
);
299 textbox
->GetInputField(getter_AddRefs(inputField
));
302 nsCOMPtr
<nsIDOMXULMenuListElement
> menulist
= do_QueryInterface(aContent
);
304 menulist
->GetInputField(getter_AddRefs(inputField
));
306 else if (aContent
->Tag() == nsGkAtoms::scale
) {
307 nsCOMPtr
<nsIDocument
> doc
= aContent
->GetComposedDoc();
311 nsINodeList
* children
= doc
->BindingManager()->GetAnonymousNodesFor(aContent
);
313 nsIContent
* child
= children
->Item(0);
314 if (child
&& child
->Tag() == nsGkAtoms::slider
)
321 nsCOMPtr
<nsIContent
> retval
= do_QueryInterface(inputField
);
331 InputContextAction::Cause
332 nsFocusManager::GetFocusMoveActionCause(uint32_t aFlags
)
334 if (aFlags
& nsIFocusManager::FLAG_BYMOUSE
) {
335 return InputContextAction::CAUSE_MOUSE
;
336 } else if (aFlags
& nsIFocusManager::FLAG_BYKEY
) {
337 return InputContextAction::CAUSE_KEY
;
339 return InputContextAction::CAUSE_UNKNOWN
;
343 nsFocusManager::GetActiveWindow(nsIDOMWindow
** aWindow
)
345 NS_IF_ADDREF(*aWindow
= mActiveWindow
);
350 nsFocusManager::SetActiveWindow(nsIDOMWindow
* aWindow
)
352 // only top-level windows can be made active
353 nsCOMPtr
<nsPIDOMWindow
> piWindow
= do_QueryInterface(aWindow
);
355 piWindow
= piWindow
->GetOuterWindow();
357 NS_ENSURE_TRUE(piWindow
&& (piWindow
== piWindow
->GetPrivateRoot()),
358 NS_ERROR_INVALID_ARG
);
360 RaiseWindow(piWindow
);
365 nsFocusManager::GetFocusedWindow(nsIDOMWindow
** aFocusedWindow
)
367 NS_IF_ADDREF(*aFocusedWindow
= mFocusedWindow
);
371 NS_IMETHODIMP
nsFocusManager::SetFocusedWindow(nsIDOMWindow
* aWindowToFocus
)
373 LOGFOCUS(("<<SetFocusedWindow begin>>"));
375 nsCOMPtr
<nsPIDOMWindow
> windowToFocus(do_QueryInterface(aWindowToFocus
));
376 NS_ENSURE_TRUE(windowToFocus
, NS_ERROR_FAILURE
);
378 windowToFocus
= windowToFocus
->GetOuterWindow();
380 nsCOMPtr
<Element
> frameElement
= windowToFocus
->GetFrameElementInternal();
382 // pass false for aFocusChanged so that the caret does not get updated
383 // and scrolling does not occur.
384 SetFocusInner(frameElement
, 0, false, true);
387 // this is a top-level window. If the window has a child frame focused,
388 // clear the focus. Otherwise, focus should already be in this frame, or
389 // already cleared. This ensures that focus will be in this frame and not
391 nsIContent
* content
= windowToFocus
->GetFocusedNode();
393 nsCOMPtr
<nsIDOMWindow
> childWindow
= GetContentWindow(content
);
395 ClearFocus(windowToFocus
);
399 nsCOMPtr
<nsPIDOMWindow
> rootWindow
= windowToFocus
->GetPrivateRoot();
401 RaiseWindow(rootWindow
);
403 LOGFOCUS(("<<SetFocusedWindow end>>"));
409 nsFocusManager::GetFocusedElement(nsIDOMElement
** aFocusedElement
)
412 CallQueryInterface(mFocusedContent
, aFocusedElement
);
414 *aFocusedElement
= nullptr;
419 nsFocusManager::GetLastFocusMethod(nsIDOMWindow
* aWindow
, uint32_t* aLastFocusMethod
)
421 // the focus method is stored on the inner window
422 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
423 if (window
&& window
->IsOuterWindow())
424 window
= window
->GetCurrentInnerWindow();
426 window
= mFocusedWindow
;
428 *aLastFocusMethod
= window
? window
->GetFocusMethod() : 0;
430 NS_ASSERTION((*aLastFocusMethod
& FOCUSMETHOD_MASK
) == *aLastFocusMethod
,
431 "invalid focus method");
436 nsFocusManager::SetFocus(nsIDOMElement
* aElement
, uint32_t aFlags
)
438 LOGFOCUS(("<<SetFocus begin>>"));
440 nsCOMPtr
<nsIContent
> newFocus
= do_QueryInterface(aElement
);
441 NS_ENSURE_ARG(newFocus
);
443 SetFocusInner(newFocus
, aFlags
, true, true);
445 LOGFOCUS(("<<SetFocus end>>"));
451 nsFocusManager::ElementIsFocusable(nsIDOMElement
* aElement
, uint32_t aFlags
,
454 NS_ENSURE_TRUE(aElement
, NS_ERROR_INVALID_ARG
);
456 nsCOMPtr
<nsIContent
> aContent
= do_QueryInterface(aElement
);
458 *aIsFocusable
= CheckIfFocusable(aContent
, aFlags
) != nullptr;
464 nsFocusManager::MoveFocus(nsIDOMWindow
* aWindow
, nsIDOMElement
* aStartElement
,
465 uint32_t aType
, uint32_t aFlags
, nsIDOMElement
** aElement
)
470 LOGFOCUS(("<<MoveFocus begin Type: %d Flags: %x>>", aType
, aFlags
));
472 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
) && mFocusedWindow
) {
473 nsIDocument
* doc
= mFocusedWindow
->GetExtantDoc();
474 if (doc
&& doc
->GetDocumentURI()) {
476 doc
->GetDocumentURI()->GetSpec(spec
);
477 LOGFOCUS((" Focused Window: %p %s", mFocusedWindow
.get(), spec
.get()));
481 LOGCONTENT(" Current Focus: %s", mFocusedContent
.get());
484 // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
485 // the other focus methods is already set, or we're just moving to the root
486 // or caret position.
487 if (aType
!= MOVEFOCUS_ROOT
&& aType
!= MOVEFOCUS_CARET
&&
488 (aFlags
& FOCUSMETHOD_MASK
) == 0) {
489 aFlags
|= FLAG_BYMOVEFOCUS
;
492 nsCOMPtr
<nsPIDOMWindow
> window
;
493 nsCOMPtr
<nsIContent
> startContent
;
495 startContent
= do_QueryInterface(aStartElement
);
496 NS_ENSURE_TRUE(startContent
, NS_ERROR_INVALID_ARG
);
498 window
= GetCurrentWindow(startContent
);
501 window
= aWindow
? do_QueryInterface(aWindow
) : mFocusedWindow
;
502 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
503 window
= window
->GetOuterWindow();
506 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
508 bool noParentTraversal
= aFlags
& FLAG_NOPARENTFRAME
;
509 nsCOMPtr
<nsIContent
> newFocus
;
510 nsresult rv
= DetermineElementToMoveFocus(window
, startContent
, aType
, noParentTraversal
,
511 getter_AddRefs(newFocus
));
512 NS_ENSURE_SUCCESS(rv
, rv
);
514 LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus
.get());
517 // for caret movement, pass false for the aFocusChanged argument,
518 // otherwise the caret will end up moving to the focus position. This
519 // would be a problem because the caret would move to the beginning of the
520 // focused link making it impossible to navigate the caret over a link.
521 SetFocusInner(newFocus
, aFlags
, aType
!= MOVEFOCUS_CARET
, true);
522 CallQueryInterface(newFocus
, aElement
);
524 else if (aType
== MOVEFOCUS_ROOT
|| aType
== MOVEFOCUS_CARET
) {
525 // no content was found, so clear the focus for these two types.
529 LOGFOCUS(("<<MoveFocus end>>"));
535 nsFocusManager::ClearFocus(nsIDOMWindow
* aWindow
)
537 LOGFOCUS(("<<ClearFocus begin>>"));
539 // if the window to clear is the focused window or an ancestor of the
540 // focused window, then blur the existing focused content. Otherwise, the
541 // focus is somewhere else so just update the current node.
542 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
543 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
545 window
= window
->GetOuterWindow();
546 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
548 if (IsSameOrAncestor(window
, mFocusedWindow
)) {
549 bool isAncestor
= (window
!= mFocusedWindow
);
550 if (Blur(window
, nullptr, isAncestor
, true)) {
551 // if we are clearing the focus on an ancestor of the focused window,
552 // the ancestor will become the new focused window, so focus it
554 Focus(window
, nullptr, 0, true, false, false, true);
558 window
->SetFocusedNode(nullptr);
561 LOGFOCUS(("<<ClearFocus end>>"));
567 nsFocusManager::GetFocusedElementForWindow(nsIDOMWindow
* aWindow
,
569 nsIDOMWindow
** aFocusedWindow
,
570 nsIDOMElement
** aElement
)
574 *aFocusedWindow
= nullptr;
576 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
577 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
579 window
= window
->GetOuterWindow();
580 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
582 nsCOMPtr
<nsPIDOMWindow
> focusedWindow
;
583 nsCOMPtr
<nsIContent
> focusedContent
=
584 GetFocusedDescendant(window
, aDeep
, getter_AddRefs(focusedWindow
));
586 CallQueryInterface(focusedContent
, aElement
);
589 NS_IF_ADDREF(*aFocusedWindow
= focusedWindow
);
595 nsFocusManager::MoveCaretToFocus(nsIDOMWindow
* aWindow
)
597 nsCOMPtr
<nsIWebNavigation
> webnav
= do_GetInterface(aWindow
);
598 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(webnav
);
600 if (dsti
->ItemType() != nsIDocShellTreeItem::typeChrome
) {
601 nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(dsti
);
602 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
604 // don't move the caret for editable documents
606 docShell
->GetEditable(&isEditable
);
610 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
611 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
613 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
614 nsCOMPtr
<nsIContent
> content
= window
->GetFocusedNode();
616 MoveCaretToFocus(presShell
, content
);
624 nsFocusManager::WindowRaised(nsIDOMWindow
* aWindow
)
626 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
627 NS_ENSURE_TRUE(window
&& window
->IsOuterWindow(), NS_ERROR_INVALID_ARG
);
630 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
631 LOGFOCUS(("Window %p Raised [Currently: %p %p]", aWindow
, mActiveWindow
.get(), mFocusedWindow
.get()));
633 nsIDocument
* doc
= window
->GetExtantDoc();
634 if (doc
&& doc
->GetDocumentURI()) {
635 doc
->GetDocumentURI()->GetSpec(spec
);
636 LOGFOCUS((" Raised Window: %p %s", aWindow
, spec
.get()));
639 doc
= mActiveWindow
->GetExtantDoc();
640 if (doc
&& doc
->GetDocumentURI()) {
641 doc
->GetDocumentURI()->GetSpec(spec
);
642 LOGFOCUS((" Active Window: %p %s", mActiveWindow
.get(), spec
.get()));
648 if (mActiveWindow
== window
) {
649 // The window is already active, so there is no need to focus anything,
650 // but make sure that the right widget is focused. This is a special case
651 // for Windows because when restoring a minimized window, a second
652 // activation will occur and the top-level widget could be focused instead
653 // of the child we want. We solve this by calling SetFocus to ensure that
654 // what the focus manager thinks should be the current widget is actually
656 EnsureCurrentWidgetFocused();
660 // lower the existing window, if any. This shouldn't happen usually.
662 WindowLowered(mActiveWindow
);
664 nsCOMPtr
<nsIDocShellTreeItem
> docShellAsItem
= window
->GetDocShell();
665 // If there's no docShellAsItem, this window must have been closed,
666 // in that case there is no tree owner.
667 NS_ENSURE_TRUE(docShellAsItem
, NS_OK
);
669 // set this as the active window
670 mActiveWindow
= window
;
672 // ensure that the window is enabled and visible
673 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
;
674 docShellAsItem
->GetTreeOwner(getter_AddRefs(treeOwner
));
675 nsCOMPtr
<nsIBaseWindow
> baseWindow
= do_QueryInterface(treeOwner
);
677 bool isEnabled
= true;
678 if (NS_SUCCEEDED(baseWindow
->GetEnabled(&isEnabled
)) && !isEnabled
) {
679 return NS_ERROR_FAILURE
;
683 baseWindow
->SetVisibility(true);
687 // inform the DOM window that it has activated, so that the active attribute
688 // is updated on the window
689 window
->ActivateOrDeactivate(true);
691 // send activate event
692 nsContentUtils::DispatchTrustedEvent(window
->GetExtantDoc(),
694 NS_LITERAL_STRING("activate"),
695 true, true, nullptr);
697 // retrieve the last focused element within the window that was raised
698 nsCOMPtr
<nsPIDOMWindow
> currentWindow
;
699 nsCOMPtr
<nsIContent
> currentFocus
=
700 GetFocusedDescendant(window
, true, getter_AddRefs(currentWindow
));
702 NS_ASSERTION(currentWindow
, "window raised with no window current");
706 nsCOMPtr
<nsIDocShell
> currentDocShell
= currentWindow
->GetDocShell();
708 nsCOMPtr
<nsIPresShell
> presShell
= currentDocShell
->GetPresShell();
710 // disable selection mousedown state on activation
711 // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
712 nsRefPtr
<nsFrameSelection
> frameSelection
= presShell
->FrameSelection();
713 frameSelection
->SetDragState(false);
716 Focus(currentWindow
, currentFocus
, 0, true, false, true, true);
722 nsFocusManager::WindowLowered(nsIDOMWindow
* aWindow
)
724 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
725 NS_ENSURE_TRUE(window
&& window
->IsOuterWindow(), NS_ERROR_INVALID_ARG
);
728 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
729 LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow
, mActiveWindow
.get(), mFocusedWindow
.get()));
731 nsIDocument
* doc
= window
->GetExtantDoc();
732 if (doc
&& doc
->GetDocumentURI()) {
733 doc
->GetDocumentURI()->GetSpec(spec
);
734 LOGFOCUS((" Lowered Window: %s", spec
.get()));
737 doc
= mActiveWindow
->GetExtantDoc();
738 if (doc
&& doc
->GetDocumentURI()) {
739 doc
->GetDocumentURI()->GetSpec(spec
);
740 LOGFOCUS((" Active Window: %s", spec
.get()));
746 if (mActiveWindow
!= window
)
749 // clear the mouse capture as the active window has changed
750 nsIPresShell::SetCapturingContent(nullptr, 0);
752 // inform the DOM window that it has deactivated, so that the active
753 // attribute is updated on the window
754 window
->ActivateOrDeactivate(false);
756 // send deactivate event
757 nsContentUtils::DispatchTrustedEvent(window
->GetExtantDoc(),
759 NS_LITERAL_STRING("deactivate"),
760 true, true, nullptr);
762 // keep track of the window being lowered, so that attempts to raise the
763 // window can be prevented until we return. Otherwise, focus can get into
765 mWindowBeingLowered
= mActiveWindow
;
766 mActiveWindow
= nullptr;
769 Blur(nullptr, nullptr, true, true);
771 mWindowBeingLowered
= nullptr;
777 nsFocusManager::ContentRemoved(nsIDocument
* aDocument
, nsIContent
* aContent
)
779 NS_ENSURE_ARG(aDocument
);
780 NS_ENSURE_ARG(aContent
);
782 nsPIDOMWindow
*window
= aDocument
->GetWindow();
786 // if the content is currently focused in the window, or is an ancestor
787 // of the currently focused element, reset the focus within that window.
788 nsIContent
* content
= window
->GetFocusedNode();
789 if (content
&& nsContentUtils::ContentIsDescendantOf(content
, aContent
)) {
790 bool shouldShowFocusRing
= window
->ShouldShowFocusRing();
791 window
->SetFocusedNode(nullptr);
793 // if this window is currently focused, clear the global focused
794 // element as well, but don't fire any events.
795 if (window
== mFocusedWindow
) {
796 mFocusedContent
= nullptr;
799 // Check if the node that was focused is an iframe or similar by looking
800 // if it has a subdocument. This would indicate that this focused iframe
801 // and its descendants will be going away. We will need to move the
802 // focus somewhere else, so just clear the focus in the toplevel window
803 // so that no element is focused.
804 nsIDocument
* subdoc
= aDocument
->GetSubDocumentFor(content
);
806 nsCOMPtr
<nsIDocShell
> docShell
= subdoc
->GetDocShell();
808 nsCOMPtr
<nsPIDOMWindow
> childWindow
= docShell
->GetWindow();
809 if (childWindow
&& IsSameOrAncestor(childWindow
, mFocusedWindow
)) {
810 ClearFocus(mActiveWindow
);
816 NotifyFocusStateChange(content
, shouldShowFocusRing
, false);
823 nsFocusManager::WindowShown(nsIDOMWindow
* aWindow
, bool aNeedsFocus
)
825 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
826 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
828 window
= window
->GetOuterWindow();
831 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
832 LOGFOCUS(("Window %p Shown [Currently: %p %p]", window
.get(), mActiveWindow
.get(), mFocusedWindow
.get()));
834 nsIDocument
* doc
= window
->GetExtantDoc();
835 if (doc
&& doc
->GetDocumentURI()) {
836 doc
->GetDocumentURI()->GetSpec(spec
);
837 LOGFOCUS(("Shown Window: %s", spec
.get()));
840 if (mFocusedWindow
) {
841 doc
= mFocusedWindow
->GetExtantDoc();
842 if (doc
&& doc
->GetDocumentURI()) {
843 doc
->GetDocumentURI()->GetSpec(spec
);
844 LOGFOCUS((" Focused Window: %s", spec
.get()));
850 if (mFocusedWindow
!= window
)
854 nsCOMPtr
<nsPIDOMWindow
> currentWindow
;
855 nsCOMPtr
<nsIContent
> currentFocus
=
856 GetFocusedDescendant(window
, true, getter_AddRefs(currentWindow
));
858 Focus(currentWindow
, currentFocus
, 0, true, false, false, true);
861 // Sometimes, an element in a window can be focused before the window is
862 // visible, which would mean that the widget may not be properly focused.
863 // When the window becomes visible, make sure the right widget is focused.
864 EnsureCurrentWidgetFocused();
871 nsFocusManager::WindowHidden(nsIDOMWindow
* aWindow
)
873 // if there is no window or it is not the same or an ancestor of the
874 // currently focused window, just return, as the current focus will not
877 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
878 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
880 window
= window
->GetOuterWindow();
883 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
884 LOGFOCUS(("Window %p Hidden [Currently: %p %p]", window
.get(), mActiveWindow
.get(), mFocusedWindow
.get()));
886 nsIDocument
* doc
= window
->GetExtantDoc();
887 if (doc
&& doc
->GetDocumentURI()) {
888 doc
->GetDocumentURI()->GetSpec(spec
);
889 LOGFOCUS((" Hide Window: %s", spec
.get()));
892 if (mFocusedWindow
) {
893 doc
= mFocusedWindow
->GetExtantDoc();
894 if (doc
&& doc
->GetDocumentURI()) {
895 doc
->GetDocumentURI()->GetSpec(spec
);
896 LOGFOCUS((" Focused Window: %s", spec
.get()));
901 doc
= mActiveWindow
->GetExtantDoc();
902 if (doc
&& doc
->GetDocumentURI()) {
903 doc
->GetDocumentURI()->GetSpec(spec
);
904 LOGFOCUS((" Active Window: %s", spec
.get()));
910 if (!IsSameOrAncestor(window
, mFocusedWindow
))
913 // at this point, we know that the window being hidden is either the focused
914 // window, or an ancestor of the focused window. Either way, the focus is no
915 // longer valid, so it needs to be updated.
917 nsCOMPtr
<nsIContent
> oldFocusedContent
= mFocusedContent
.forget();
919 nsCOMPtr
<nsIDocShell
> focusedDocShell
= mFocusedWindow
->GetDocShell();
920 nsCOMPtr
<nsIPresShell
> presShell
= focusedDocShell
->GetPresShell();
922 if (oldFocusedContent
&& oldFocusedContent
->IsInComposedDoc()) {
923 NotifyFocusStateChange(oldFocusedContent
,
924 mFocusedWindow
->ShouldShowFocusRing(),
926 window
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
929 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
,
930 oldFocusedContent
->GetComposedDoc(),
931 oldFocusedContent
, 1, false);
935 nsPresContext
* focusedPresContext
=
936 presShell
? presShell
->GetPresContext() : nullptr;
937 IMEStateManager::OnChangeFocus(focusedPresContext
, nullptr,
938 GetFocusMoveActionCause(0));
940 SetCaretVisible(presShell
, false, nullptr);
943 // if the docshell being hidden is being destroyed, then we want to move
944 // focus somewhere else. Call ClearFocus on the toplevel window, which
945 // will have the effect of clearing the focus and moving the focused window
946 // to the toplevel window. But if the window isn't being destroyed, we are
947 // likely just loading a new document in it, so we want to maintain the
948 // focused window so that the new document gets properly focused.
950 nsCOMPtr
<nsIDocShell
> docShellBeingHidden
= window
->GetDocShell();
951 docShellBeingHidden
->IsBeingDestroyed(&beingDestroyed
);
952 if (beingDestroyed
) {
953 // There is usually no need to do anything if a toplevel window is going
954 // away, as we assume that WindowLowered will be called. However, this may
955 // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
956 // a leak. So if the active window is being destroyed, call WindowLowered
958 NS_ASSERTION(mFocusedWindow
->IsOuterWindow(), "outer window expected");
959 if (mActiveWindow
== mFocusedWindow
|| mActiveWindow
== window
)
960 WindowLowered(mActiveWindow
);
962 ClearFocus(mActiveWindow
);
966 // if the window being hidden is an ancestor of the focused window, adjust
967 // the focused window so that it points to the one being hidden. This
968 // ensures that the focused window isn't in a chain of frames that doesn't
970 if (window
!= mFocusedWindow
) {
971 nsCOMPtr
<nsIDocShellTreeItem
> dsti
=
972 mFocusedWindow
? mFocusedWindow
->GetDocShell() : nullptr;
974 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
975 dsti
->GetParent(getter_AddRefs(parentDsti
));
977 nsCOMPtr
<nsPIDOMWindow
> parentWindow
= parentDsti
->GetWindow();
979 parentWindow
->SetFocusedNode(nullptr);
983 SetFocusedWindowInternal(window
);
990 nsFocusManager::FireDelayedEvents(nsIDocument
* aDocument
)
992 NS_ENSURE_ARG(aDocument
);
994 // fire any delayed focus and blur events in the same order that they were added
995 for (uint32_t i
= 0; i
< mDelayedBlurFocusEvents
.Length(); i
++) {
996 if (mDelayedBlurFocusEvents
[i
].mDocument
== aDocument
) {
997 if (!aDocument
->GetInnerWindow() ||
998 !aDocument
->GetInnerWindow()->IsCurrentInnerWindow()) {
999 // If the document was navigated away from or is defunct, don't bother
1000 // firing events on it. Note the symmetry between this condition and
1001 // the similar one in nsDocument.cpp:FireOrClearDelayedEvents.
1002 mDelayedBlurFocusEvents
.RemoveElementAt(i
);
1004 } else if (!aDocument
->EventHandlingSuppressed()) {
1005 uint32_t type
= mDelayedBlurFocusEvents
[i
].mType
;
1006 nsCOMPtr
<EventTarget
> target
= mDelayedBlurFocusEvents
[i
].mTarget
;
1007 nsCOMPtr
<nsIPresShell
> presShell
= mDelayedBlurFocusEvents
[i
].mPresShell
;
1008 mDelayedBlurFocusEvents
.RemoveElementAt(i
);
1009 SendFocusOrBlurEvent(type
, presShell
, aDocument
, target
, 0, false);
1019 nsFocusManager::FocusPlugin(nsIContent
* aContent
)
1021 NS_ENSURE_ARG(aContent
);
1022 SetFocusInner(aContent
, 0, true, false);
1028 nsFocusManager::NotifyFocusStateChange(nsIContent
* aContent
,
1029 bool aWindowShouldShowFocusRing
,
1032 if (!aContent
->IsElement()) {
1035 EventStates eventState
= NS_EVENT_STATE_FOCUS
;
1036 if (aWindowShouldShowFocusRing
) {
1037 eventState
|= NS_EVENT_STATE_FOCUSRING
;
1039 if (aGettingFocus
) {
1040 aContent
->AsElement()->AddStates(eventState
);
1042 aContent
->AsElement()->RemoveStates(eventState
);
1048 nsFocusManager::EnsureCurrentWidgetFocused()
1050 if (!mFocusedWindow
|| sTestMode
)
1053 // get the main child widget for the focused window and ensure that the
1054 // platform knows that this widget is focused.
1055 nsCOMPtr
<nsIDocShell
> docShell
= mFocusedWindow
->GetDocShell();
1057 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
1059 nsViewManager
* vm
= presShell
->GetViewManager();
1061 nsCOMPtr
<nsIWidget
> widget
;
1062 vm
->GetRootWidget(getter_AddRefs(widget
));
1064 widget
->SetFocus(false);
1071 nsFocusManager::SetFocusInner(nsIContent
* aNewContent
, int32_t aFlags
,
1072 bool aFocusChanged
, bool aAdjustWidget
)
1074 // if the element is not focusable, just return and leave the focus as is
1075 nsCOMPtr
<nsIContent
> contentToFocus
= CheckIfFocusable(aNewContent
, aFlags
);
1076 if (!contentToFocus
)
1079 // check if the element to focus is a frame (iframe) containing a child
1080 // document. Frames are never directly focused; instead focusing a frame
1081 // means focus what is inside the frame. To do this, the descendant content
1082 // within the frame is retrieved and that will be focused instead.
1083 nsCOMPtr
<nsPIDOMWindow
> newWindow
;
1084 nsCOMPtr
<nsPIDOMWindow
> subWindow
= GetContentWindow(contentToFocus
);
1086 contentToFocus
= GetFocusedDescendant(subWindow
, true, getter_AddRefs(newWindow
));
1087 // since a window is being refocused, clear aFocusChanged so that the
1088 // caret position isn't updated.
1089 aFocusChanged
= false;
1092 // unless it was set above, retrieve the window for the element to focus
1094 newWindow
= GetCurrentWindow(contentToFocus
);
1096 // if the element is already focused, just return. Note that this happens
1097 // after the frame check above so that we compare the element that will be
1098 // focused rather than the frame it is in.
1099 if (!newWindow
|| (newWindow
== mFocusedWindow
&& contentToFocus
== mFocusedContent
))
1102 // don't allow focus to be placed in docshells or descendants of docshells
1103 // that are being destroyed. Also, ensure that the page hasn't been
1104 // unloaded. The prevents content from being refocused during an unload event.
1105 nsCOMPtr
<nsIDocShell
> newDocShell
= newWindow
->GetDocShell();
1106 nsCOMPtr
<nsIDocShell
> docShell
= newDocShell
;
1109 docShell
->GetIsInUnload(&inUnload
);
1113 bool beingDestroyed
;
1114 docShell
->IsBeingDestroyed(&beingDestroyed
);
1118 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1119 docShell
->GetParent(getter_AddRefs(parentDsti
));
1120 docShell
= do_QueryInterface(parentDsti
);
1123 // if the new element is in the same window as the currently focused element
1124 bool isElementInFocusedWindow
= (mFocusedWindow
== newWindow
);
1126 if (!isElementInFocusedWindow
&& mFocusedWindow
&& newWindow
&&
1127 nsContentUtils::IsHandlingKeyBoardEvent()) {
1128 nsCOMPtr
<nsIScriptObjectPrincipal
> focused
=
1129 do_QueryInterface(mFocusedWindow
);
1130 nsCOMPtr
<nsIScriptObjectPrincipal
> newFocus
=
1131 do_QueryInterface(newWindow
);
1132 nsIPrincipal
* focusedPrincipal
= focused
->GetPrincipal();
1133 nsIPrincipal
* newPrincipal
= newFocus
->GetPrincipal();
1134 if (!focusedPrincipal
|| !newPrincipal
) {
1137 bool subsumes
= false;
1138 focusedPrincipal
->Subsumes(newPrincipal
, &subsumes
);
1139 if (!subsumes
&& !nsContentUtils::IsCallerChrome()) {
1140 NS_WARNING("Not allowed to focus the new window!");
1145 // to check if the new element is in the active window, compare the
1146 // new root docshell for the new element with the active window's docshell.
1147 bool isElementInActiveWindow
= false;
1149 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= newWindow
->GetDocShell();
1150 nsCOMPtr
<nsPIDOMWindow
> newRootWindow
;
1152 nsCOMPtr
<nsIDocShellTreeItem
> root
;
1153 dsti
->GetRootTreeItem(getter_AddRefs(root
));
1154 newRootWindow
= root
? root
->GetWindow() : nullptr;
1156 isElementInActiveWindow
= (mActiveWindow
&& newRootWindow
== mActiveWindow
);
1159 // Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX
1160 // system. We don't control event dispatch to windowed plugins on non-MacOSX,
1161 // so we can't display the "Press ESC to leave fullscreen mode" warning on
1162 // key input if a windowed plugin is focused, so just exit fullscreen
1163 // to guard against phishing.
1165 nsIDocument
* fullscreenAncestor
;
1166 if (contentToFocus
&&
1167 (fullscreenAncestor
= nsContentUtils::GetFullscreenAncestor(contentToFocus
->OwnerDoc())) &&
1168 nsContentUtils::HasPluginWithUncontrolledEventDispatch(contentToFocus
)) {
1169 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
1170 NS_LITERAL_CSTRING("DOM"),
1171 contentToFocus
->OwnerDoc(),
1172 nsContentUtils::eDOM_PROPERTIES
,
1173 "FocusedWindowedPluginWhileFullScreen");
1174 nsIDocument::ExitFullscreen(fullscreenAncestor
, /* async */ true);
1178 // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
1179 // shifted away from the current element if the new shell to focus is
1180 // the same or an ancestor shell of the currently focused shell.
1181 bool allowFrameSwitch
= !(aFlags
& FLAG_NOSWITCHFRAME
) ||
1182 IsSameOrAncestor(newWindow
, mFocusedWindow
);
1184 // if the element is in the active window, frame switching is allowed and
1185 // the content is in a visible window, fire blur and focus events.
1186 bool sendFocusEvent
=
1187 isElementInActiveWindow
&& allowFrameSwitch
&& IsWindowVisible(newWindow
);
1189 // When the following conditions are true:
1190 // * an element has focus
1191 // * isn't called by trusted event (i.e., called by untrusted event or by js)
1192 // * the focus is moved to another document's element
1193 // we need to check the permission.
1194 if (sendFocusEvent
&& mFocusedContent
&&
1195 mFocusedContent
->OwnerDoc() != aNewContent
->OwnerDoc()) {
1196 // If the caller cannot access the current focused node, the caller should
1197 // not be able to steal focus from it. E.g., When the current focused node
1198 // is in chrome, any web contents should not be able to steal the focus.
1199 nsCOMPtr
<nsIDOMNode
> domNode(do_QueryInterface(mFocusedContent
));
1200 sendFocusEvent
= nsContentUtils::CanCallerAccess(domNode
);
1201 if (!sendFocusEvent
&& mMouseButtonEventHandlingDocument
) {
1202 // However, while mouse button event is handling, the handling document's
1203 // script should be able to steal focus.
1204 domNode
= do_QueryInterface(mMouseButtonEventHandlingDocument
);
1205 sendFocusEvent
= nsContentUtils::CanCallerAccess(domNode
);
1209 LOGCONTENT("Shift Focus: %s", contentToFocus
.get());
1210 LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p",
1211 aFlags
, mFocusedWindow
.get(), newWindow
.get(), mFocusedContent
.get()));
1212 LOGFOCUS((" In Active Window: %d In Focused Window: %d SendFocus: %d",
1213 isElementInActiveWindow
, isElementInFocusedWindow
, sendFocusEvent
));
1215 if (sendFocusEvent
) {
1216 // return if blurring fails or the focus changes during the blur
1217 if (mFocusedWindow
) {
1218 // if the focus is being moved to another element in the same document,
1219 // or to a descendant, pass the existing window to Blur so that the
1220 // current node in the existing window is cleared. If moving to a
1221 // window elsewhere, we want to maintain the current node in the
1222 // window but still blur it.
1223 bool currentIsSameOrAncestor
= IsSameOrAncestor(mFocusedWindow
, newWindow
);
1224 // find the common ancestor of the currently focused window and the new
1225 // window. The ancestor will need to have its currently focused node
1226 // cleared once the document has been blurred. Otherwise, we'll be in a
1227 // state where a document is blurred yet the chain of windows above it
1228 // still points to that document.
1229 // For instance, in the following frame tree:
1233 // D is focused and we want to focus C. Once D has been blurred, we need
1234 // to clear out the focus in A, otherwise A would still maintain that B
1235 // was focused, and B that D was focused.
1236 nsCOMPtr
<nsPIDOMWindow
> commonAncestor
;
1237 if (!isElementInFocusedWindow
)
1238 commonAncestor
= GetCommonAncestor(newWindow
, mFocusedWindow
);
1240 if (!Blur(currentIsSameOrAncestor
? mFocusedWindow
.get() : nullptr,
1241 commonAncestor
, !isElementInFocusedWindow
, aAdjustWidget
))
1245 Focus(newWindow
, contentToFocus
, aFlags
, !isElementInFocusedWindow
,
1246 aFocusChanged
, false, aAdjustWidget
);
1249 // otherwise, for inactive windows and when the caller cannot steal the
1250 // focus, update the node in the window, and raise the window if desired.
1251 if (allowFrameSwitch
)
1252 AdjustWindowFocus(newWindow
, true);
1254 // set the focus node and method as needed
1255 uint32_t focusMethod
= aFocusChanged
? aFlags
& FOCUSMETHODANDRING_MASK
:
1256 newWindow
->GetFocusMethod() | (aFlags
& FLAG_SHOWRING
);
1257 newWindow
->SetFocusedNode(contentToFocus
, focusMethod
);
1258 if (aFocusChanged
) {
1259 nsCOMPtr
<nsIDocShell
> docShell
= newWindow
->GetDocShell();
1261 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
1263 ScrollIntoView(presShell
, contentToFocus
, aFlags
);
1266 // update the commands even when inactive so that the attributes for that
1267 // window are up to date.
1268 if (allowFrameSwitch
)
1269 newWindow
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1271 if (aFlags
& FLAG_RAISE
)
1272 RaiseWindow(newRootWindow
);
1277 nsFocusManager::IsSameOrAncestor(nsPIDOMWindow
* aPossibleAncestor
,
1278 nsPIDOMWindow
* aWindow
)
1280 if (!aWindow
|| !aPossibleAncestor
) {
1284 nsCOMPtr
<nsIDocShellTreeItem
> ancestordsti
= aPossibleAncestor
->GetDocShell();
1285 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= aWindow
->GetDocShell();
1287 if (dsti
== ancestordsti
)
1289 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1290 dsti
->GetParent(getter_AddRefs(parentDsti
));
1291 dsti
.swap(parentDsti
);
1297 already_AddRefed
<nsPIDOMWindow
>
1298 nsFocusManager::GetCommonAncestor(nsPIDOMWindow
* aWindow1
,
1299 nsPIDOMWindow
* aWindow2
)
1301 NS_ENSURE_TRUE(aWindow1
&& aWindow2
, nullptr);
1303 nsCOMPtr
<nsIDocShellTreeItem
> dsti1
= aWindow1
->GetDocShell();
1304 NS_ENSURE_TRUE(dsti1
, nullptr);
1306 nsCOMPtr
<nsIDocShellTreeItem
> dsti2
= aWindow2
->GetDocShell();
1307 NS_ENSURE_TRUE(dsti2
, nullptr);
1309 nsAutoTArray
<nsIDocShellTreeItem
*, 30> parents1
, parents2
;
1311 parents1
.AppendElement(dsti1
);
1312 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti1
;
1313 dsti1
->GetParent(getter_AddRefs(parentDsti1
));
1314 dsti1
.swap(parentDsti1
);
1317 parents2
.AppendElement(dsti2
);
1318 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti2
;
1319 dsti2
->GetParent(getter_AddRefs(parentDsti2
));
1320 dsti2
.swap(parentDsti2
);
1323 uint32_t pos1
= parents1
.Length();
1324 uint32_t pos2
= parents2
.Length();
1325 nsIDocShellTreeItem
* parent
= nullptr;
1327 for (len
= std::min(pos1
, pos2
); len
> 0; --len
) {
1328 nsIDocShellTreeItem
* child1
= parents1
.ElementAt(--pos1
);
1329 nsIDocShellTreeItem
* child2
= parents2
.ElementAt(--pos2
);
1330 if (child1
!= child2
) {
1336 nsCOMPtr
<nsPIDOMWindow
> window
= parent
? parent
->GetWindow() : nullptr;
1337 return window
.forget();
1341 nsFocusManager::AdjustWindowFocus(nsPIDOMWindow
* aWindow
,
1342 bool aCheckPermission
)
1344 bool isVisible
= IsWindowVisible(aWindow
);
1346 nsCOMPtr
<nsPIDOMWindow
> window(aWindow
);
1348 // get the containing <iframe> or equivalent element so that it can be
1350 nsCOMPtr
<Element
> frameElement
= window
->GetFrameElementInternal();
1352 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= window
->GetDocShell();
1355 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1356 dsti
->GetParent(getter_AddRefs(parentDsti
));
1361 window
= parentDsti
->GetWindow();
1363 // if the parent window is visible but aWindow was not, then we have
1364 // likely moved up and out from a hidden tab to the browser window, or a
1365 // similar such arrangement. Stop adjusting the current nodes.
1366 if (IsWindowVisible(window
) != isVisible
)
1369 // When aCheckPermission is true, we should check whether the caller can
1370 // access the window or not. If it cannot access, we should stop the
1372 if (aCheckPermission
&& !nsContentUtils::CanCallerAccess(window
))
1375 window
->SetFocusedNode(frameElement
);
1381 nsFocusManager::IsWindowVisible(nsPIDOMWindow
* aWindow
)
1383 if (!aWindow
|| aWindow
->IsFrozen())
1386 // Check if the inner window is frozen as well. This can happen when a focus change
1387 // occurs while restoring a previous page.
1388 nsPIDOMWindow
* innerWindow
= aWindow
->GetCurrentInnerWindow();
1389 if (!innerWindow
|| innerWindow
->IsFrozen())
1392 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
1393 nsCOMPtr
<nsIBaseWindow
> baseWin(do_QueryInterface(docShell
));
1397 bool visible
= false;
1398 baseWin
->GetVisibility(&visible
);
1403 nsFocusManager::IsNonFocusableRoot(nsIContent
* aContent
)
1405 NS_PRECONDITION(aContent
, "aContent must not be NULL");
1406 NS_PRECONDITION(aContent
->IsInComposedDoc(), "aContent must be in a document");
1408 // If aContent is in designMode, the root element is not focusable.
1409 // NOTE: in designMode, most elements are not focusable, just the document is
1411 // Also, if aContent is not editable but it isn't in designMode, it's not
1413 // And in userfocusignored context nothing is focusable.
1414 nsIDocument
* doc
= aContent
->GetComposedDoc();
1415 NS_ASSERTION(doc
, "aContent must have current document");
1416 return aContent
== doc
->GetRootElement() &&
1417 (doc
->HasFlag(NODE_IS_EDITABLE
) || !aContent
->IsEditable() ||
1418 nsContentUtils::IsUserFocusIgnored(aContent
));
1422 nsFocusManager::CheckIfFocusable(nsIContent
* aContent
, uint32_t aFlags
)
1427 // this is a special case for some XUL elements where an anonymous child is
1428 // actually focusable and not the element itself.
1429 nsIContent
* redirectedFocus
= GetRedirectedFocus(aContent
);
1430 if (redirectedFocus
)
1431 return CheckIfFocusable(redirectedFocus
, aFlags
);
1433 nsCOMPtr
<nsIDocument
> doc
= aContent
->GetComposedDoc();
1434 // can't focus elements that are not in documents
1436 LOGCONTENT("Cannot focus %s because content not in document", aContent
)
1440 // Make sure that our frames are up to date
1441 doc
->FlushPendingNotifications(Flush_Layout
);
1443 nsIPresShell
*shell
= doc
->GetShell();
1447 // the root content can always be focused,
1448 // except in userfocusignored context.
1449 if (aContent
== doc
->GetRootElement())
1450 return nsContentUtils::IsUserFocusIgnored(aContent
) ? nullptr : aContent
;
1452 // cannot focus content in print preview mode. Only the root can be focused.
1453 nsPresContext
* presContext
= shell
->GetPresContext();
1454 if (presContext
&& presContext
->Type() == nsPresContext::eContext_PrintPreview
) {
1455 LOGCONTENT("Cannot focus %s while in print preview", aContent
)
1459 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
1461 LOGCONTENT("Cannot focus %s as it has no frame", aContent
)
1465 if (aContent
->Tag() == nsGkAtoms::area
&& aContent
->IsHTML()) {
1466 // HTML areas do not have their own frame, and the img frame we get from
1467 // GetPrimaryFrame() is not relevant as to whether it is focusable or
1468 // not, so we have to do all the relevant checks manually for them.
1469 return frame
->IsVisibleConsideringAncestors() &&
1470 aContent
->IsFocusable() ? aContent
: nullptr;
1473 // if this is a child frame content node, check if it is visible and
1474 // call the content node's IsFocusable method instead of the frame's
1475 // IsFocusable method. This skips checking the style system and ensures that
1476 // offscreen browsers can still be focused.
1477 nsIDocument
* subdoc
= doc
->GetSubDocumentFor(aContent
);
1478 if (subdoc
&& IsWindowVisible(subdoc
->GetWindow())) {
1479 const nsStyleUserInterface
* ui
= frame
->StyleUserInterface();
1480 int32_t tabIndex
= (ui
->mUserFocus
== NS_STYLE_USER_FOCUS_IGNORE
||
1481 ui
->mUserFocus
== NS_STYLE_USER_FOCUS_NONE
) ? -1 : 0;
1482 return aContent
->IsFocusable(&tabIndex
, aFlags
& FLAG_BYMOUSE
) ? aContent
: nullptr;
1485 return frame
->IsFocusable(nullptr, aFlags
& FLAG_BYMOUSE
) ? aContent
: nullptr;
1489 nsFocusManager::Blur(nsPIDOMWindow
* aWindowToClear
,
1490 nsPIDOMWindow
* aAncestorWindowToFocus
,
1491 bool aIsLeavingDocument
,
1492 bool aAdjustWidgets
)
1494 LOGFOCUS(("<<Blur begin>>"));
1496 // hold a reference to the focused content, which may be null
1497 nsCOMPtr
<nsIContent
> content
= mFocusedContent
;
1499 if (!content
->IsInComposedDoc()) {
1500 mFocusedContent
= nullptr;
1503 if (content
== mFirstBlurEvent
)
1507 // hold a reference to the focused window
1508 nsCOMPtr
<nsPIDOMWindow
> window
= mFocusedWindow
;
1510 mFocusedContent
= nullptr;
1514 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
1516 mFocusedContent
= nullptr;
1520 // Keep a ref to presShell since dispatching the DOM event may cause
1521 // the document to be destroyed.
1522 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
1524 mFocusedContent
= nullptr;
1528 bool clearFirstBlurEvent
= false;
1529 if (!mFirstBlurEvent
) {
1530 mFirstBlurEvent
= content
;
1531 clearFirstBlurEvent
= true;
1534 nsPresContext
* focusedPresContext
=
1535 mActiveWindow
? presShell
->GetPresContext() : nullptr;
1536 IMEStateManager::OnChangeFocus(focusedPresContext
, nullptr,
1537 GetFocusMoveActionCause(0));
1539 // now adjust the actual focus, by clearing the fields in the focus manager
1540 // and in the window.
1541 mFocusedContent
= nullptr;
1542 bool shouldShowFocusRing
= window
->ShouldShowFocusRing();
1544 aWindowToClear
->SetFocusedNode(nullptr);
1546 LOGCONTENT("Element %s has been blurred", content
.get());
1548 // Don't fire blur event on the root content which isn't editable.
1549 bool sendBlurEvent
=
1550 content
&& content
->IsInComposedDoc() && !IsNonFocusableRoot(content
);
1552 if (sendBlurEvent
) {
1553 NotifyFocusStateChange(content
, shouldShowFocusRing
, false);
1556 // if an object/plug-in/remote browser is being blurred, move the system focus
1557 // to the parent window, otherwise events will still get fired at the plugin.
1558 // But don't do this if we are blurring due to the window being lowered,
1559 // otherwise, the parent window can get raised again.
1560 if (mActiveWindow
) {
1561 nsIFrame
* contentFrame
= content
->GetPrimaryFrame();
1562 nsIObjectFrame
* objectFrame
= do_QueryFrame(contentFrame
);
1563 if (aAdjustWidgets
&& objectFrame
&& !sTestMode
) {
1564 // note that the presshell's widget is being retrieved here, not the one
1565 // for the object frame.
1566 nsViewManager
* vm
= presShell
->GetViewManager();
1568 nsCOMPtr
<nsIWidget
> widget
;
1569 vm
->GetRootWidget(getter_AddRefs(widget
));
1571 widget
->SetFocus(false);
1575 // if the object being blurred is a remote browser, deactivate remote content
1576 if (TabParent
* remote
= TabParent::GetFrom(content
)) {
1577 remote
->Deactivate();
1578 LOGFOCUS(("Remote browser deactivated"));
1584 if (sendBlurEvent
) {
1585 // if there is an active window, update commands. If there isn't an active
1586 // window, then this was a blur caused by the active window being lowered,
1587 // so there is no need to update the commands
1589 window
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1591 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
,
1592 content
->GetComposedDoc(), content
, 1, false);
1595 // if we are leaving the document or the window was lowered, make the caret
1597 if (aIsLeavingDocument
|| !mActiveWindow
)
1598 SetCaretVisible(presShell
, false, nullptr);
1600 // at this point, it is expected that this window will be still be
1601 // focused, but the focused content will be null, as it was cleared before
1602 // the event. If this isn't the case, then something else was focused during
1603 // the blur event above and we should just return. However, if
1604 // aIsLeavingDocument is set, a new document is desired, so make sure to
1605 // blur the document and window.
1606 if (mFocusedWindow
!= window
||
1607 (mFocusedContent
!= nullptr && !aIsLeavingDocument
)) {
1610 else if (aIsLeavingDocument
) {
1611 window
->TakeFocus(false, 0);
1613 // clear the focus so that the ancestor frame hierarchy is in the correct
1614 // state. Pass true because aAncestorWindowToFocus is thought to be
1615 // focused at this point.
1616 if (aAncestorWindowToFocus
)
1617 aAncestorWindowToFocus
->SetFocusedNode(nullptr, 0, true);
1619 SetFocusedWindowInternal(nullptr);
1620 mFocusedContent
= nullptr;
1622 // pass 1 for the focus method when calling SendFocusOrBlurEvent just so
1623 // that the check is made for suppressed documents. Check to ensure that
1624 // the document isn't null in case someone closed it during the blur above
1625 nsIDocument
* doc
= window
->GetExtantDoc();
1627 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
, doc
, doc
, 1, false);
1628 if (mFocusedWindow
== nullptr)
1629 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
, doc
, window
, 1, false);
1631 // check if a different window was focused
1632 result
= (mFocusedWindow
== nullptr && mActiveWindow
);
1634 else if (mActiveWindow
) {
1635 // Otherwise, the blur of the element without blurring the document
1636 // occurred normally. Call UpdateCaret to redisplay the caret at the right
1637 // location within the document. This is needed to ensure that the caret
1638 // used for caret browsing is made visible again when an input field is
1640 UpdateCaret(false, true, nullptr);
1643 if (clearFirstBlurEvent
)
1644 mFirstBlurEvent
= nullptr;
1650 nsFocusManager::Focus(nsPIDOMWindow
* aWindow
,
1651 nsIContent
* aContent
,
1653 bool aIsNewDocument
,
1656 bool aAdjustWidgets
)
1658 LOGFOCUS(("<<Focus begin>>"));
1663 if (aContent
&& (aContent
== mFirstFocusEvent
|| aContent
== mFirstBlurEvent
))
1666 // Keep a reference to the presShell since dispatching the DOM event may
1667 // cause the document to be destroyed.
1668 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
1672 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
1676 // If the focus actually changed, set the focus method (mouse, keyboard, etc).
1677 // Otherwise, just get the current focus method and use that. This ensures
1678 // that the method is set during the document and window focus events.
1679 uint32_t focusMethod
= aFocusChanged
? aFlags
& FOCUSMETHODANDRING_MASK
:
1680 aWindow
->GetFocusMethod() | (aFlags
& FLAG_SHOWRING
);
1682 if (!IsWindowVisible(aWindow
)) {
1683 // if the window isn't visible, for instance because it is a hidden tab,
1684 // update the current focus and scroll it into view but don't do anything else
1685 if (CheckIfFocusable(aContent
, aFlags
)) {
1686 aWindow
->SetFocusedNode(aContent
, focusMethod
);
1688 ScrollIntoView(presShell
, aContent
, aFlags
);
1693 bool clearFirstFocusEvent
= false;
1694 if (!mFirstFocusEvent
) {
1695 mFirstFocusEvent
= aContent
;
1696 clearFirstFocusEvent
= true;
1700 LOGCONTENT("Element %s has been focused", aContent
);
1702 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
1703 nsIDocument
* docm
= aWindow
->GetExtantDoc();
1705 LOGCONTENT(" from %s", docm
->GetRootElement());
1707 LOGFOCUS((" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x]",
1708 aIsNewDocument
, aFocusChanged
, aWindowRaised
, aFlags
));
1712 if (aIsNewDocument
) {
1713 // if this is a new document, update the parent chain of frames so that
1714 // focus can be traversed from the top level down to the newly focused
1716 AdjustWindowFocus(aWindow
, false);
1718 // Update the window touch registration to reflect the state of
1719 // the new document that got focus
1720 aWindow
->UpdateTouchState();
1723 // indicate that the window has taken focus.
1724 if (aWindow
->TakeFocus(true, focusMethod
))
1725 aIsNewDocument
= true;
1727 SetFocusedWindowInternal(aWindow
);
1729 // Update the system focus by focusing the root widget. But avoid this
1730 // if 1) aAdjustWidgets is false or 2) aContent is a plugin that has its
1731 // own widget and is either already focused or is about to be focused.
1732 nsCOMPtr
<nsIWidget
> objectFrameWidget
;
1734 nsIFrame
* contentFrame
= aContent
->GetPrimaryFrame();
1735 nsIObjectFrame
* objectFrame
= do_QueryFrame(contentFrame
);
1737 objectFrameWidget
= objectFrame
->GetWidget();
1739 if (aAdjustWidgets
&& !objectFrameWidget
&& !sTestMode
) {
1740 nsViewManager
* vm
= presShell
->GetViewManager();
1742 nsCOMPtr
<nsIWidget
> widget
;
1743 vm
->GetRootWidget(getter_AddRefs(widget
));
1745 widget
->SetFocus(false);
1749 // if switching to a new document, first fire the focus event on the
1750 // document and then the window.
1751 if (aIsNewDocument
) {
1752 nsIDocument
* doc
= aWindow
->GetExtantDoc();
1753 // The focus change should be notified to IMEStateManager from here if
1754 // the focused content is a designMode editor since any content won't
1755 // receive focus event.
1756 if (doc
&& doc
->HasFlag(NODE_IS_EDITABLE
)) {
1757 IMEStateManager::OnChangeFocus(presShell
->GetPresContext(), nullptr,
1758 GetFocusMoveActionCause(aFlags
));
1761 SendFocusOrBlurEvent(NS_FOCUS_CONTENT
, presShell
, doc
,
1762 doc
, aFlags
& FOCUSMETHOD_MASK
, aWindowRaised
);
1763 if (mFocusedWindow
== aWindow
&& mFocusedContent
== nullptr)
1764 SendFocusOrBlurEvent(NS_FOCUS_CONTENT
, presShell
, doc
,
1765 aWindow
, aFlags
& FOCUSMETHOD_MASK
, aWindowRaised
);
1768 // check to ensure that the element is still focusable, and that nothing
1769 // else was focused during the events above.
1770 if (CheckIfFocusable(aContent
, aFlags
) &&
1771 mFocusedWindow
== aWindow
&& mFocusedContent
== nullptr) {
1772 mFocusedContent
= aContent
;
1774 nsIContent
* focusedNode
= aWindow
->GetFocusedNode();
1775 bool isRefocus
= focusedNode
&& focusedNode
->IsEqualNode(aContent
);
1777 aWindow
->SetFocusedNode(aContent
, focusMethod
);
1779 bool sendFocusEvent
=
1780 aContent
&& aContent
->IsInComposedDoc() && !IsNonFocusableRoot(aContent
);
1781 nsPresContext
* presContext
= presShell
->GetPresContext();
1782 if (sendFocusEvent
) {
1783 // if the focused element changed, scroll it into view
1785 ScrollIntoView(presShell
, aContent
, aFlags
);
1787 NotifyFocusStateChange(aContent
, aWindow
->ShouldShowFocusRing(), true);
1789 // if this is an object/plug-in/remote browser, focus its widget. Note that we might
1790 // no longer be in the same document, due to the events we fired above when
1792 if (presShell
->GetDocument() == aContent
->GetComposedDoc()) {
1793 if (aAdjustWidgets
&& objectFrameWidget
&& !sTestMode
)
1794 objectFrameWidget
->SetFocus(false);
1796 // if the object being focused is a remote browser, activate remote content
1797 if (TabParent
* remote
= TabParent::GetFrom(aContent
)) {
1799 LOGFOCUS(("Remote browser activated"));
1803 IMEStateManager::OnChangeFocus(presContext
, aContent
,
1804 GetFocusMoveActionCause(aFlags
));
1806 // as long as this focus wasn't because a window was raised, update the
1808 // XXXndeakin P2 someone could adjust the focus during the update
1810 aWindow
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1812 SendFocusOrBlurEvent(NS_FOCUS_CONTENT
, presShell
,
1813 aContent
->GetComposedDoc(),
1814 aContent
, aFlags
& FOCUSMETHOD_MASK
,
1815 aWindowRaised
, isRefocus
);
1817 IMEStateManager::OnChangeFocus(presContext
, nullptr,
1818 GetFocusMoveActionCause(aFlags
));
1819 if (!aWindowRaised
) {
1820 aWindow
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1825 // If the window focus event (fired above when aIsNewDocument) caused
1826 // the plugin not to be focusable, update the system focus by focusing
1828 if (aAdjustWidgets
&& objectFrameWidget
&&
1829 mFocusedWindow
== aWindow
&& mFocusedContent
== nullptr &&
1831 nsViewManager
* vm
= presShell
->GetViewManager();
1833 nsCOMPtr
<nsIWidget
> widget
;
1834 vm
->GetRootWidget(getter_AddRefs(widget
));
1836 widget
->SetFocus(false);
1840 nsPresContext
* presContext
= presShell
->GetPresContext();
1841 IMEStateManager::OnChangeFocus(presContext
, nullptr,
1842 GetFocusMoveActionCause(aFlags
));
1845 aWindow
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1848 // update the caret visibility and position to match the newly focused
1849 // element. However, don't update the position if this was a focus due to a
1850 // mouse click as the selection code would already have moved the caret as
1851 // needed. If this is a different document than was focused before, also
1852 // update the caret's visibility. If this is the same document, the caret
1853 // visibility should be the same as before so there is no need to update it.
1854 if (mFocusedContent
== aContent
)
1855 UpdateCaret(aFocusChanged
&& !(aFlags
& FLAG_BYMOUSE
), aIsNewDocument
,
1858 if (clearFirstFocusEvent
)
1859 mFirstFocusEvent
= nullptr;
1862 class FocusBlurEvent
: public nsRunnable
1865 FocusBlurEvent(nsISupports
* aTarget
, uint32_t aType
,
1866 nsPresContext
* aContext
, bool aWindowRaised
,
1868 : mTarget(aTarget
), mType(aType
), mContext(aContext
),
1869 mWindowRaised(aWindowRaised
), mIsRefocus(aIsRefocus
) {}
1873 InternalFocusEvent
event(true, mType
);
1874 event
.mFlags
.mBubbles
= false;
1875 event
.fromRaise
= mWindowRaised
;
1876 event
.isRefocus
= mIsRefocus
;
1877 return EventDispatcher::Dispatch(mTarget
, mContext
, &event
);
1880 nsCOMPtr
<nsISupports
> mTarget
;
1882 nsRefPtr
<nsPresContext
> mContext
;
1888 nsFocusManager::SendFocusOrBlurEvent(uint32_t aType
,
1889 nsIPresShell
* aPresShell
,
1890 nsIDocument
* aDocument
,
1891 nsISupports
* aTarget
,
1892 uint32_t aFocusMethod
,
1896 NS_ASSERTION(aType
== NS_FOCUS_CONTENT
|| aType
== NS_BLUR_CONTENT
,
1897 "Wrong event type for SendFocusOrBlurEvent");
1899 nsCOMPtr
<EventTarget
> eventTarget
= do_QueryInterface(aTarget
);
1901 nsCOMPtr
<nsINode
> n
= do_QueryInterface(aTarget
);
1903 nsCOMPtr
<nsPIDOMWindow
> win
= do_QueryInterface(aTarget
);
1904 n
= win
? win
->GetExtantDoc() : nullptr;
1906 bool dontDispatchEvent
= n
&& nsContentUtils::IsUserFocusIgnored(n
);
1908 // for focus events, if this event was from a mouse or key and event
1909 // handling on the document is suppressed, queue the event and fire it
1910 // later. For blur events, a non-zero value would be set for aFocusMethod.
1911 if (aFocusMethod
&& !dontDispatchEvent
&&
1912 aDocument
&& aDocument
->EventHandlingSuppressed()) {
1913 // aFlags is always 0 when aWindowRaised is true so this won't be called
1914 // on a window raise.
1915 NS_ASSERTION(!aWindowRaised
, "aWindowRaised should not be set");
1917 for (uint32_t i
= mDelayedBlurFocusEvents
.Length(); i
> 0; --i
) {
1918 // if this event was already queued, remove it and append it to the end
1919 if (mDelayedBlurFocusEvents
[i
- 1].mType
== aType
&&
1920 mDelayedBlurFocusEvents
[i
- 1].mPresShell
== aPresShell
&&
1921 mDelayedBlurFocusEvents
[i
- 1].mDocument
== aDocument
&&
1922 mDelayedBlurFocusEvents
[i
- 1].mTarget
== eventTarget
) {
1923 mDelayedBlurFocusEvents
.RemoveElementAt(i
- 1);
1927 mDelayedBlurFocusEvents
.AppendElement(
1928 nsDelayedBlurOrFocusEvent(aType
, aPresShell
, aDocument
, eventTarget
));
1932 #ifdef ACCESSIBILITY
1933 nsAccessibilityService
* accService
= GetAccService();
1935 if (aType
== NS_FOCUS_CONTENT
)
1936 accService
->NotifyOfDOMFocus(aTarget
);
1938 accService
->NotifyOfDOMBlur(aTarget
);
1942 if (!dontDispatchEvent
) {
1943 nsContentUtils::AddScriptRunner(
1944 new FocusBlurEvent(aTarget
, aType
, aPresShell
->GetPresContext(),
1945 aWindowRaised
, aIsRefocus
));
1950 nsFocusManager::ScrollIntoView(nsIPresShell
* aPresShell
,
1951 nsIContent
* aContent
,
1954 // if the noscroll flag isn't set, scroll the newly focused element into view
1955 if (!(aFlags
& FLAG_NOSCROLL
))
1956 aPresShell
->ScrollContentIntoView(aContent
,
1957 nsIPresShell::ScrollAxis(
1958 nsIPresShell::SCROLL_MINIMUM
,
1959 nsIPresShell::SCROLL_IF_NOT_VISIBLE
),
1960 nsIPresShell::ScrollAxis(
1961 nsIPresShell::SCROLL_MINIMUM
,
1962 nsIPresShell::SCROLL_IF_NOT_VISIBLE
),
1963 nsIPresShell::SCROLL_OVERFLOW_HIDDEN
);
1968 nsFocusManager::RaiseWindow(nsPIDOMWindow
* aWindow
)
1970 // don't raise windows that are already raised or are in the process of
1972 if (!aWindow
|| aWindow
== mActiveWindow
|| aWindow
== mWindowBeingLowered
)
1976 // In test mode, emulate the existing window being lowered and the new
1977 // window being raised.
1979 WindowLowered(mActiveWindow
);
1980 WindowRaised(aWindow
);
1985 // Windows would rather we focus the child widget, otherwise, the toplevel
1986 // widget will always end up being focused. Fortunately, focusing the child
1987 // widget will also have the effect of raising the window this widget is in.
1988 // But on other platforms, we can just focus the toplevel widget to raise
1990 nsCOMPtr
<nsPIDOMWindow
> childWindow
;
1991 GetFocusedDescendant(aWindow
, true, getter_AddRefs(childWindow
));
1993 childWindow
= aWindow
;
1995 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
1999 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
2003 nsViewManager
* vm
= presShell
->GetViewManager();
2005 nsCOMPtr
<nsIWidget
> widget
;
2006 vm
->GetRootWidget(getter_AddRefs(widget
));
2008 widget
->SetFocus(true);
2011 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
=
2012 do_QueryInterface(aWindow
->GetDocShell());
2013 if (treeOwnerAsWin
) {
2014 nsCOMPtr
<nsIWidget
> widget
;
2015 treeOwnerAsWin
->GetMainWidget(getter_AddRefs(widget
));
2017 widget
->SetFocus(true);
2023 nsFocusManager::UpdateCaretForCaretBrowsingMode()
2025 UpdateCaret(false, true, mFocusedContent
);
2029 nsFocusManager::UpdateCaret(bool aMoveCaretToFocus
,
2030 bool aUpdateVisibility
,
2031 nsIContent
* aContent
)
2033 LOGFOCUS(("Update Caret: %d %d", aMoveCaretToFocus
, aUpdateVisibility
));
2035 if (!mFocusedWindow
)
2038 // this is called when a document is focused or when the caretbrowsing
2039 // preference is changed
2040 nsCOMPtr
<nsIDocShell
> focusedDocShell
= mFocusedWindow
->GetDocShell();
2041 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(focusedDocShell
);
2045 if (dsti
->ItemType() == nsIDocShellTreeItem::typeChrome
) {
2046 return; // Never browse with caret in chrome
2049 bool browseWithCaret
=
2050 Preferences::GetBool("accessibility.browsewithcaret");
2052 nsCOMPtr
<nsIPresShell
> presShell
= focusedDocShell
->GetPresShell();
2056 // If this is an editable document which isn't contentEditable, or a
2057 // contentEditable document and the node to focus is contentEditable,
2058 // return, so that we don't mess with caret visibility.
2059 bool isEditable
= false;
2060 focusedDocShell
->GetEditable(&isEditable
);
2063 nsCOMPtr
<nsIHTMLDocument
> doc
=
2064 do_QueryInterface(presShell
->GetDocument());
2066 bool isContentEditableDoc
=
2067 doc
&& doc
->GetEditingState() == nsIHTMLDocument::eContentEditable
;
2069 bool isFocusEditable
=
2070 aContent
&& aContent
->HasFlag(NODE_IS_EDITABLE
);
2071 if (!isContentEditableDoc
|| isFocusEditable
)
2075 if (!isEditable
&& aMoveCaretToFocus
)
2076 MoveCaretToFocus(presShell
, aContent
);
2078 if (!aUpdateVisibility
)
2081 // XXXndeakin this doesn't seem right. It should be checking for this only
2082 // on the nearest ancestor frame which is a chrome frame. But this is
2083 // what the existing code does, so just leave it for now.
2084 if (!browseWithCaret
) {
2085 nsCOMPtr
<Element
> docElement
=
2086 mFocusedWindow
->GetFrameElementInternal();
2088 browseWithCaret
= docElement
->AttrValueIs(kNameSpaceID_None
,
2089 nsGkAtoms::showcaret
,
2090 NS_LITERAL_STRING("true"),
2094 SetCaretVisible(presShell
, browseWithCaret
, aContent
);
2098 nsFocusManager::MoveCaretToFocus(nsIPresShell
* aPresShell
, nsIContent
* aContent
)
2100 // domDoc is a document interface we can create a range with
2101 nsCOMPtr
<nsIDOMDocument
> domDoc
= do_QueryInterface(aPresShell
->GetDocument());
2103 nsRefPtr
<nsFrameSelection
> frameSelection
= aPresShell
->FrameSelection();
2104 nsCOMPtr
<nsISelection
> domSelection
= frameSelection
->
2105 GetSelection(nsISelectionController::SELECTION_NORMAL
);
2107 nsCOMPtr
<nsIDOMNode
> currentFocusNode(do_QueryInterface(aContent
));
2108 // First clear the selection. This way, if there is no currently focused
2109 // content, the selection will just be cleared.
2110 domSelection
->RemoveAllRanges();
2111 if (currentFocusNode
) {
2112 nsCOMPtr
<nsIDOMRange
> newRange
;
2113 nsresult rv
= domDoc
->CreateRange(getter_AddRefs(newRange
));
2114 if (NS_SUCCEEDED(rv
)) {
2115 // Set the range to the start of the currently focused node
2116 // Make sure it's collapsed
2117 newRange
->SelectNodeContents(currentFocusNode
);
2118 nsCOMPtr
<nsIDOMNode
> firstChild
;
2119 currentFocusNode
->GetFirstChild(getter_AddRefs(firstChild
));
2121 aContent
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
)) {
2122 // If current focus node is a leaf, set range to before the
2123 // node by using the parent as a container.
2124 // This prevents it from appearing as selected.
2125 newRange
->SetStartBefore(currentFocusNode
);
2126 newRange
->SetEndBefore(currentFocusNode
);
2128 domSelection
->AddRange(newRange
);
2129 domSelection
->CollapseToStart();
2137 nsFocusManager::SetCaretVisible(nsIPresShell
* aPresShell
,
2139 nsIContent
* aContent
)
2141 // When browsing with caret, make sure caret is visible after new focus
2142 // Return early if there is no caret. This can happen for the testcase
2143 // for bug 308025 where a window is closed in a blur handler.
2144 nsRefPtr
<nsCaret
> caret
= aPresShell
->GetCaret();
2148 bool caretVisible
= caret
->IsVisible();
2149 if (!aVisible
&& !caretVisible
)
2152 nsRefPtr
<nsFrameSelection
> frameSelection
;
2154 NS_ASSERTION(aContent
->GetComposedDoc() == aPresShell
->GetDocument(),
2156 nsIFrame
*focusFrame
= aContent
->GetPrimaryFrame();
2158 frameSelection
= focusFrame
->GetFrameSelection();
2161 nsRefPtr
<nsFrameSelection
> docFrameSelection
= aPresShell
->FrameSelection();
2163 if (docFrameSelection
&& caret
&&
2164 (frameSelection
== docFrameSelection
|| !aContent
)) {
2165 nsISelection
* domSelection
= docFrameSelection
->
2166 GetSelection(nsISelectionController::SELECTION_NORMAL
);
2168 nsCOMPtr
<nsISelectionController
> selCon(do_QueryInterface(aPresShell
));
2170 return NS_ERROR_FAILURE
;
2172 // First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
2173 selCon
->SetCaretEnabled(false);
2175 // Caret must blink on non-editable elements
2176 caret
->SetIgnoreUserModify(true);
2177 // Tell the caret which selection to use
2178 caret
->SetSelection(domSelection
);
2180 // In content, we need to set the caret. The only special case is edit
2181 // fields, which have a different frame selection from the document.
2182 // They will take care of making the caret visible themselves.
2184 selCon
->SetCaretReadOnly(false);
2185 selCon
->SetCaretEnabled(aVisible
);
2193 nsFocusManager::GetSelectionLocation(nsIDocument
* aDocument
,
2194 nsIPresShell
* aPresShell
,
2195 nsIContent
**aStartContent
,
2196 nsIContent
**aEndContent
)
2198 *aStartContent
= *aEndContent
= nullptr;
2199 nsresult rv
= NS_ERROR_FAILURE
;
2201 nsPresContext
* presContext
= aPresShell
->GetPresContext();
2202 NS_ASSERTION(presContext
, "mPresContent is null!!");
2204 nsRefPtr
<nsFrameSelection
> frameSelection
= aPresShell
->FrameSelection();
2206 nsCOMPtr
<nsISelection
> domSelection
;
2207 if (frameSelection
) {
2208 domSelection
= frameSelection
->
2209 GetSelection(nsISelectionController::SELECTION_NORMAL
);
2212 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
2213 bool isCollapsed
= false;
2214 nsCOMPtr
<nsIContent
> startContent
, endContent
;
2215 int32_t startOffset
= 0;
2217 domSelection
->GetIsCollapsed(&isCollapsed
);
2218 nsCOMPtr
<nsIDOMRange
> domRange
;
2219 rv
= domSelection
->GetRangeAt(0, getter_AddRefs(domRange
));
2221 domRange
->GetStartContainer(getter_AddRefs(startNode
));
2222 domRange
->GetEndContainer(getter_AddRefs(endNode
));
2223 domRange
->GetStartOffset(&startOffset
);
2225 nsIContent
*childContent
= nullptr;
2227 startContent
= do_QueryInterface(startNode
);
2228 if (startContent
&& startContent
->IsElement()) {
2229 NS_ASSERTION(startOffset
>= 0, "Start offset cannot be negative");
2230 childContent
= startContent
->GetChildAt(startOffset
);
2232 startContent
= childContent
;
2236 endContent
= do_QueryInterface(endNode
);
2237 if (endContent
&& endContent
->IsElement()) {
2238 int32_t endOffset
= 0;
2239 domRange
->GetEndOffset(&endOffset
);
2240 NS_ASSERTION(endOffset
>= 0, "End offset cannot be negative");
2241 childContent
= endContent
->GetChildAt(endOffset
);
2243 endContent
= childContent
;
2249 rv
= NS_ERROR_INVALID_ARG
;
2252 nsIFrame
*startFrame
= nullptr;
2254 startFrame
= startContent
->GetPrimaryFrame();
2256 // Next check to see if our caret is at the very end of a node
2257 // If so, the caret is actually sitting in front of the next
2258 // logical frame's primary node - so for this case we need to
2259 // change caretContent to that node.
2261 if (startContent
->NodeType() == nsIDOMNode::TEXT_NODE
) {
2262 nsAutoString nodeValue
;
2263 startContent
->AppendTextTo(nodeValue
);
2265 bool isFormControl
=
2266 startContent
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
);
2268 if (nodeValue
.Length() == (uint32_t)startOffset
&& !isFormControl
&&
2269 startContent
!= aDocument
->GetRootElement()) {
2270 // Yes, indeed we were at the end of the last node
2271 nsCOMPtr
<nsIFrameEnumerator
> frameTraversal
;
2272 nsresult rv
= NS_NewFrameTraversal(getter_AddRefs(frameTraversal
),
2273 presContext
, startFrame
,
2276 false, // aLockInScrollView
2279 NS_ENSURE_SUCCESS(rv
, rv
);
2281 nsIFrame
*newCaretFrame
= nullptr;
2282 nsCOMPtr
<nsIContent
> newCaretContent
= startContent
;
2283 bool endOfSelectionInStartNode(startContent
== endContent
);
2285 // Continue getting the next frame until the primary content for the frame
2286 // we are on changes - we don't want to be stuck in the same place
2287 frameTraversal
->Next();
2288 newCaretFrame
= static_cast<nsIFrame
*>(frameTraversal
->CurrentItem());
2289 if (nullptr == newCaretFrame
)
2291 newCaretContent
= newCaretFrame
->GetContent();
2292 } while (!newCaretContent
|| newCaretContent
== startContent
);
2294 if (newCaretFrame
&& newCaretContent
) {
2295 // If the caret is exactly at the same position of the new frame,
2296 // then we can use the newCaretFrame and newCaretContent for our position
2298 nsIFrame
*frame
= nsCaret::GetGeometry(domSelection
, &caretRect
);
2300 nsPoint caretWidgetOffset
;
2301 nsIWidget
*widget
= frame
->GetNearestWidget(caretWidgetOffset
);
2302 caretRect
.MoveBy(caretWidgetOffset
);
2303 nsPoint newCaretOffset
;
2304 nsIWidget
*newCaretWidget
= newCaretFrame
->GetNearestWidget(newCaretOffset
);
2305 if (widget
== newCaretWidget
&& caretRect
.y
== newCaretOffset
.y
&&
2306 caretRect
.x
== newCaretOffset
.x
) {
2307 // The caret is at the start of the new element.
2308 startFrame
= newCaretFrame
;
2309 startContent
= newCaretContent
;
2310 if (endOfSelectionInStartNode
) {
2311 endContent
= newCaretContent
; // Ensure end of selection is not before start
2321 *aStartContent
= startContent
;
2322 *aEndContent
= endContent
;
2323 NS_IF_ADDREF(*aStartContent
);
2324 NS_IF_ADDREF(*aEndContent
);
2330 nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow
* aWindow
,
2331 nsIContent
* aStartContent
,
2332 int32_t aType
, bool aNoParentTraversal
,
2333 nsIContent
** aNextContent
)
2335 *aNextContent
= nullptr;
2337 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
2341 nsCOMPtr
<nsIContent
> startContent
= aStartContent
;
2342 if (!startContent
&& aType
!= MOVEFOCUS_CARET
) {
2343 if (aType
== MOVEFOCUS_FORWARDDOC
|| aType
== MOVEFOCUS_BACKWARDDOC
) {
2344 // When moving between documents, make sure to get the right
2345 // starting content in a descendant.
2346 nsCOMPtr
<nsPIDOMWindow
> focusedWindow
;
2347 startContent
= GetFocusedDescendant(aWindow
, true, getter_AddRefs(focusedWindow
));
2350 startContent
= aWindow
->GetFocusedNode();
2354 nsCOMPtr
<nsIDocument
> doc
;
2356 doc
= startContent
->GetComposedDoc();
2358 doc
= aWindow
->GetExtantDoc();
2362 LookAndFeel::GetInt(LookAndFeel::eIntID_TabFocusModel
,
2363 &nsIContent::sTabFocusModel
);
2365 if (aType
== MOVEFOCUS_ROOT
) {
2366 NS_IF_ADDREF(*aNextContent
= GetRootForFocus(aWindow
, doc
, false, false));
2369 if (aType
== MOVEFOCUS_FORWARDDOC
) {
2370 NS_IF_ADDREF(*aNextContent
= GetNextTabbableDocument(startContent
, true));
2373 if (aType
== MOVEFOCUS_BACKWARDDOC
) {
2374 NS_IF_ADDREF(*aNextContent
= GetNextTabbableDocument(startContent
, false));
2378 nsIContent
* rootContent
= doc
->GetRootElement();
2379 NS_ENSURE_TRUE(rootContent
, NS_OK
);
2381 nsIPresShell
*presShell
= doc
->GetShell();
2382 NS_ENSURE_TRUE(presShell
, NS_OK
);
2384 if (aType
== MOVEFOCUS_FIRST
) {
2386 startContent
= rootContent
;
2387 return GetNextTabbableContent(presShell
, startContent
,
2388 nullptr, startContent
,
2389 true, 1, false, aNextContent
);
2391 if (aType
== MOVEFOCUS_LAST
) {
2393 startContent
= rootContent
;
2394 return GetNextTabbableContent(presShell
, startContent
,
2395 nullptr, startContent
,
2396 false, 0, false, aNextContent
);
2399 bool forward
= (aType
== MOVEFOCUS_FORWARD
|| aType
== MOVEFOCUS_CARET
);
2400 bool doNavigation
= true;
2401 bool ignoreTabIndex
= false;
2402 // when a popup is open, we want to ensure that tab navigation occurs only
2403 // within the most recently opened panel. If a popup is open, its frame will
2404 // be stored in popupFrame.
2405 nsIFrame
* popupFrame
= nullptr;
2407 int32_t tabIndex
= forward
? 1 : 0;
2409 nsIFrame
* frame
= startContent
->GetPrimaryFrame();
2410 if (startContent
->Tag() == nsGkAtoms::area
&&
2411 startContent
->IsHTML())
2412 startContent
->IsFocusable(&tabIndex
);
2414 frame
->IsFocusable(&tabIndex
, 0);
2416 startContent
->IsFocusable(&tabIndex
);
2418 // if the current element isn't tabbable, ignore the tabindex and just
2419 // look for the next element. The root content won't have a tabindex
2420 // so just treat this as the beginning of the tab order.
2423 if (startContent
!= rootContent
)
2424 ignoreTabIndex
= true;
2427 // check if the focus is currently inside a popup. Elements such as the
2428 // autocomplete widget use the noautofocus attribute to allow the focus to
2429 // remain outside the popup when it is opened.
2431 popupFrame
= nsLayoutUtils::GetClosestFrameOfType(frame
,
2432 nsGkAtoms::menuPopupFrame
);
2436 // Don't navigate outside of a popup, so pretend that the
2437 // root content is the popup itself
2438 rootContent
= popupFrame
->GetContent();
2439 NS_ASSERTION(rootContent
, "Popup frame doesn't have a content node");
2441 else if (!forward
) {
2442 // If focus moves backward and when current focused node is root
2443 // content or <body> element which is editable by contenteditable
2444 // attribute, focus should move to its parent document.
2445 if (startContent
== rootContent
) {
2446 doNavigation
= false;
2448 nsIDocument
* doc
= startContent
->GetComposedDoc();
2450 nsLayoutUtils::GetEditableRootContentByContentEditable(doc
)) {
2451 doNavigation
= false;
2458 if (aType
!= MOVEFOCUS_CARET
) {
2459 // if there is no focus, yet a panel is open, focus the first item in
2461 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
2463 popupFrame
= pm
->GetTopPopup(ePopupTypePanel
);
2467 rootContent
= popupFrame
->GetContent();
2468 NS_ASSERTION(rootContent
, "Popup frame doesn't have a content node");
2469 startContent
= rootContent
;
2472 // Otherwise, for content shells, start from the location of the caret.
2473 if (docShell
->ItemType() != nsIDocShellTreeItem::typeChrome
) {
2474 nsCOMPtr
<nsIContent
> endSelectionContent
;
2475 GetSelectionLocation(doc
, presShell
,
2476 getter_AddRefs(startContent
),
2477 getter_AddRefs(endSelectionContent
));
2478 // If the selection is on the rootContent, then there is no selection
2479 if (startContent
== rootContent
) {
2480 startContent
= nullptr;
2483 if (aType
== MOVEFOCUS_CARET
) {
2484 // GetFocusInSelection finds a focusable link near the caret.
2485 // If there is no start content though, don't do this to avoid
2486 // focusing something unexpected.
2488 GetFocusInSelection(aWindow
, startContent
,
2489 endSelectionContent
, aNextContent
);
2495 // when starting from a selection, we always want to find the next or
2496 // previous element in the document. So the tabindex on elements
2497 // should be ignored.
2498 ignoreTabIndex
= true;
2502 if (!startContent
) {
2503 // otherwise, just use the root content as the starting point
2504 startContent
= rootContent
;
2505 NS_ENSURE_TRUE(startContent
, NS_OK
);
2510 NS_ASSERTION(startContent
, "starting content not set");
2512 // keep a reference to the starting content. If we find that again, it means
2513 // we've iterated around completely and we don't want to adjust the focus.
2514 // The skipOriginalContentCheck will be set to true only for the first time
2515 // GetNextTabbableContent is called. This ensures that we don't break out
2516 // when nothing is focused to start with. Specifically,
2517 // GetNextTabbableContent first checks the root content -- which happens to
2518 // be the same as the start content -- when nothing is focused and tabbing
2519 // forward. Without skipOriginalContentCheck set to true, we'd end up
2520 // returning right away and focusing nothing. Luckily, GetNextTabbableContent
2521 // will never wrap around on its own, and can only return the original
2522 // content when it is called a second time or later.
2523 bool skipOriginalContentCheck
= true;
2524 nsIContent
* originalStartContent
= startContent
;
2526 LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent
.get());
2527 LOGFOCUSNAVIGATION((" Tabindex: %d Ignore: %d", tabIndex
, ignoreTabIndex
));
2531 nsCOMPtr
<nsIContent
> nextFocus
;
2532 nsresult rv
= GetNextTabbableContent(presShell
, rootContent
,
2533 skipOriginalContentCheck
? nullptr : originalStartContent
,
2534 startContent
, forward
,
2535 tabIndex
, ignoreTabIndex
,
2536 getter_AddRefs(nextFocus
));
2537 NS_ENSURE_SUCCESS(rv
, rv
);
2539 // found a content node to focus.
2541 LOGCONTENTNAVIGATION("Next Content: %s", nextFocus
.get());
2543 // as long as the found node was not the same as the starting node,
2544 // set it as the return value.
2545 if (nextFocus
!= originalStartContent
)
2546 NS_ADDREF(*aNextContent
= nextFocus
);
2551 // in a popup, so start again from the beginning of the popup. However,
2552 // if we already started at the beginning, then there isn't anything to
2553 // focus, so just return
2554 if (startContent
!= rootContent
) {
2555 startContent
= rootContent
;
2556 tabIndex
= forward
? 1 : 0;
2563 doNavigation
= true;
2564 skipOriginalContentCheck
= false;
2565 ignoreTabIndex
= false;
2567 if (aNoParentTraversal
) {
2568 if (startContent
== rootContent
)
2571 startContent
= rootContent
;
2572 tabIndex
= forward
? 1 : 0;
2576 // reached the beginning or end of the document. Traverse up to the parent
2577 // document and try again.
2578 nsCOMPtr
<nsIDocShellTreeItem
> docShellParent
;
2579 docShell
->GetParent(getter_AddRefs(docShellParent
));
2580 if (docShellParent
) {
2581 // move up to the parent shell and try again from there.
2583 // first, get the frame element this window is inside.
2584 nsCOMPtr
<nsPIDOMWindow
> piWindow
= docShell
->GetWindow();
2585 NS_ENSURE_TRUE(piWindow
, NS_ERROR_FAILURE
);
2587 // Next, retrieve the parent docshell, document and presshell.
2588 docShell
= do_QueryInterface(docShellParent
);
2589 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
2591 nsCOMPtr
<nsPIDOMWindow
> piParentWindow
= docShellParent
->GetWindow();
2592 NS_ENSURE_TRUE(piParentWindow
, NS_ERROR_FAILURE
);
2593 doc
= piParentWindow
->GetExtantDoc();
2594 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
2596 presShell
= doc
->GetShell();
2598 rootContent
= doc
->GetRootElement();
2599 startContent
= piWindow
->GetFrameElementInternal();
2601 nsIFrame
* frame
= startContent
->GetPrimaryFrame();
2605 frame
->IsFocusable(&tabIndex
, 0);
2608 ignoreTabIndex
= true;
2611 // if the frame is inside a popup, make sure to scan only within the
2612 // popup. This handles the situation of tabbing amongst elements
2613 // inside an iframe which is itself inside a popup. Otherwise,
2614 // navigation would move outside the popup when tabbing outside the
2616 popupFrame
= nsLayoutUtils::GetClosestFrameOfType(frame
,
2617 nsGkAtoms::menuPopupFrame
);
2619 rootContent
= popupFrame
->GetContent();
2620 NS_ASSERTION(rootContent
, "Popup frame doesn't have a content node");
2624 startContent
= rootContent
;
2625 tabIndex
= forward
? 1 : 0;
2629 // no parent, so call the tree owner. This will tell the embedder that
2630 // it should take the focus.
2632 docShell
->TabToTreeOwner(forward
, &tookFocus
);
2633 // if the tree owner, took the focus, blur the current content
2635 nsCOMPtr
<nsPIDOMWindow
> window
= docShell
->GetWindow();
2636 if (window
->GetFocusedNode() == mFocusedContent
)
2637 Blur(mFocusedWindow
, nullptr, true, true);
2639 window
->SetFocusedNode(nullptr);
2643 // reset the tab index and start again from the beginning or end
2644 startContent
= rootContent
;
2645 tabIndex
= forward
? 1 : 0;
2648 // wrapped all the way around and didn't find anything to move the focus
2649 // to, so just break out
2650 if (startContent
== originalStartContent
)
2658 nsFocusManager::GetNextTabbableContent(nsIPresShell
* aPresShell
,
2659 nsIContent
* aRootContent
,
2660 nsIContent
* aOriginalStartContent
,
2661 nsIContent
* aStartContent
,
2663 int32_t aCurrentTabIndex
,
2664 bool aIgnoreTabIndex
,
2665 nsIContent
** aResultContent
)
2667 *aResultContent
= nullptr;
2669 nsCOMPtr
<nsIContent
> startContent
= aStartContent
;
2673 LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent
);
2674 LOGFOCUSNAVIGATION((" tabindex: %d", aCurrentTabIndex
));
2676 nsPresContext
* presContext
= aPresShell
->GetPresContext();
2678 bool getNextFrame
= true;
2679 nsCOMPtr
<nsIContent
> iterStartContent
= aStartContent
;
2681 nsIFrame
* startFrame
= iterStartContent
->GetPrimaryFrame();
2682 // if there is no frame, look for another content node that has a frame
2684 // if the root content doesn't have a frame, just return
2685 if (iterStartContent
== aRootContent
)
2688 // look for the next or previous content node in tree order
2689 iterStartContent
= aForward
? iterStartContent
->GetNextNode() : iterStartContent
->GetPreviousContent();
2690 // we've already skipped over the initial focused content, so we
2691 // don't want to traverse frames.
2692 getNextFrame
= false;
2693 if (iterStartContent
)
2696 // otherwise, as a last attempt, just look at the root content
2697 iterStartContent
= aRootContent
;
2701 nsCOMPtr
<nsIFrameEnumerator
> frameTraversal
;
2702 nsresult rv
= NS_NewFrameTraversal(getter_AddRefs(frameTraversal
),
2703 presContext
, startFrame
,
2706 false, // aLockInScrollView
2709 NS_ENSURE_SUCCESS(rv
, rv
);
2711 if (iterStartContent
== aRootContent
) {
2713 frameTraversal
->Last();
2714 } else if (aRootContent
->IsFocusable()) {
2715 frameTraversal
->Next();
2718 else if (getNextFrame
&&
2719 (!iterStartContent
|| iterStartContent
->Tag() != nsGkAtoms::area
||
2720 !iterStartContent
->IsHTML())) {
2721 // Need to do special check in case we're in an imagemap which has multiple
2722 // content nodes per frame, so don't skip over the starting frame.
2724 frameTraversal
->Next();
2726 frameTraversal
->Prev();
2729 // Walk frames to find something tabbable matching mCurrentTabIndex
2730 nsIFrame
* frame
= static_cast<nsIFrame
*>(frameTraversal
->CurrentItem());
2732 // TabIndex not set defaults to 0 for form elements, anchors and other
2733 // elements that are normally focusable. Tabindex defaults to -1
2734 // for elements that are not normally focusable.
2735 // The returned computed tabindex from IsFocusable() is as follows:
2736 // < 0 not tabbable at all
2737 // == 0 in normal tab order (last after positive tabindexed items)
2738 // > 0 can be tabbed to in the order specified by this value
2741 frame
->IsFocusable(&tabIndex
, 0);
2743 LOGCONTENTNAVIGATION("Next Tabbable %s:", frame
->GetContent());
2744 LOGFOCUSNAVIGATION((" with tabindex: %d expected: %d", tabIndex
, aCurrentTabIndex
));
2746 nsIContent
* currentContent
= frame
->GetContent();
2747 if (tabIndex
>= 0) {
2748 NS_ASSERTION(currentContent
, "IsFocusable set a tabindex for a frame with no content");
2749 if (currentContent
->Tag() == nsGkAtoms::img
&&
2750 currentContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::usemap
)) {
2751 // This is an image with a map. Image map areas are not traversed by
2752 // nsIFrameTraversal so look for the next or previous area element.
2753 nsIContent
*areaContent
=
2754 GetNextTabbableMapArea(aForward
, aCurrentTabIndex
,
2755 currentContent
, iterStartContent
);
2757 NS_ADDREF(*aResultContent
= areaContent
);
2761 else if (aIgnoreTabIndex
|| aCurrentTabIndex
== tabIndex
) {
2762 // break out if we've wrapped around to the start again.
2763 if (aOriginalStartContent
&& currentContent
== aOriginalStartContent
) {
2764 NS_ADDREF(*aResultContent
= currentContent
);
2768 // found a node with a matching tab index. Check if it is a child
2769 // frame. If so, navigate into the child frame instead.
2770 nsIDocument
* doc
= currentContent
->GetComposedDoc();
2771 NS_ASSERTION(doc
, "content not in document");
2772 nsIDocument
* subdoc
= doc
->GetSubDocumentFor(currentContent
);
2774 if (!subdoc
->EventHandlingSuppressed()) {
2776 // when tabbing forward into a frame, return the root
2777 // frame so that the canvas becomes focused.
2778 nsCOMPtr
<nsPIDOMWindow
> subframe
= subdoc
->GetWindow();
2780 // If the subframe body is editable by contenteditable,
2781 // we should set the editor's root element rather than the
2782 // actual root element. Otherwise, we should set the focus
2783 // to the root content.
2785 nsLayoutUtils::GetEditableRootContentByContentEditable(subdoc
);
2786 if (!*aResultContent
||
2787 !((*aResultContent
)->GetPrimaryFrame())) {
2789 GetRootForFocus(subframe
, subdoc
, false, true);
2791 if (*aResultContent
) {
2792 NS_ADDREF(*aResultContent
);
2797 Element
* rootElement
= subdoc
->GetRootElement();
2798 nsIPresShell
* subShell
= subdoc
->GetShell();
2799 if (rootElement
&& subShell
) {
2800 rv
= GetNextTabbableContent(subShell
, rootElement
,
2801 aOriginalStartContent
, rootElement
,
2802 aForward
, (aForward
? 1 : 0),
2803 false, aResultContent
);
2804 NS_ENSURE_SUCCESS(rv
, rv
);
2805 if (*aResultContent
)
2810 // otherwise, use this as the next content node to tab to, unless
2811 // this was the element we started on. This would happen for
2812 // instance on an element with child frames, where frame navigation
2813 // could return the original element again. In that case, just skip
2814 // it. Also, if the next content node is the root content, then
2815 // return it. This latter case would happen only if someone made a
2817 // Also, when going backwards, check to ensure that the focus
2818 // wouldn't be redirected. Otherwise, for example, when an input in
2819 // a textbox is focused, the enclosing textbox would be found and
2820 // the same inner input would be returned again.
2821 else if (currentContent
== aRootContent
||
2822 (currentContent
!= startContent
&&
2823 (aForward
|| !GetRedirectedFocus(currentContent
)))) {
2824 NS_ADDREF(*aResultContent
= currentContent
);
2829 else if (aOriginalStartContent
&& currentContent
== aOriginalStartContent
) {
2830 // not focusable, so return if we have wrapped around to the original
2831 // content. This is necessary in case the original starting content was
2833 NS_ADDREF(*aResultContent
= currentContent
);
2837 // Move to the next or previous frame, but ignore continuation frames
2838 // since only the first frame should be involved in focusability.
2839 // Otherwise, a loop will occur in the following example:
2840 // <span tabindex="1">...<a/><a/>...</span>
2841 // where the text wraps onto multiple lines. Tabbing from the second
2842 // link can find one of the span's continuation frames between the link
2843 // and the end of the span, and the span would end up getting focused
2847 frameTraversal
->Next();
2849 frameTraversal
->Prev();
2850 frame
= static_cast<nsIFrame
*>(frameTraversal
->CurrentItem());
2851 } while (frame
&& frame
->GetPrevContinuation());
2854 // If already at lowest priority tab (0), end search completely.
2855 // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
2856 if (aCurrentTabIndex
== (aForward
? 0 : 1)) {
2857 // if going backwards, the canvas should be focused once the beginning
2858 // has been reached.
2860 nsCOMPtr
<nsPIDOMWindow
> window
= GetCurrentWindow(aRootContent
);
2861 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
2862 NS_IF_ADDREF(*aResultContent
=
2863 GetRootForFocus(window
, aRootContent
->GetComposedDoc(),
2869 // continue looking for next highest priority tabindex
2870 aCurrentTabIndex
= GetNextTabIndex(aRootContent
, aCurrentTabIndex
, aForward
);
2871 startContent
= iterStartContent
= aRootContent
;
2878 nsFocusManager::GetNextTabbableMapArea(bool aForward
,
2879 int32_t aCurrentTabIndex
,
2880 nsIContent
* aImageContent
,
2881 nsIContent
* aStartContent
)
2883 nsAutoString useMap
;
2884 aImageContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::usemap
, useMap
);
2886 nsCOMPtr
<nsIDocument
> doc
= aImageContent
->GetComposedDoc();
2888 nsCOMPtr
<nsIContent
> mapContent
= doc
->FindImageMap(useMap
);
2891 uint32_t count
= mapContent
->GetChildCount();
2892 // First see if the the start content is in this map
2894 int32_t index
= mapContent
->IndexOf(aStartContent
);
2896 if (index
< 0 || (aStartContent
->IsFocusable(&tabIndex
) &&
2897 tabIndex
!= aCurrentTabIndex
)) {
2898 // If aStartContent is in this map we must start iterating past it.
2899 // We skip the case where aStartContent has tabindex == aStartContent
2900 // since the next tab ordered element might be before it
2901 // (or after for backwards) in the child list.
2902 index
= aForward
? -1 : (int32_t)count
;
2905 // GetChildAt will return nullptr if our index < 0 or index >= count
2906 nsCOMPtr
<nsIContent
> areaContent
;
2907 while ((areaContent
= mapContent
->GetChildAt(aForward
? ++index
: --index
)) != nullptr) {
2908 if (areaContent
->IsFocusable(&tabIndex
) && tabIndex
== aCurrentTabIndex
) {
2918 nsFocusManager::GetNextTabIndex(nsIContent
* aParent
,
2919 int32_t aCurrentTabIndex
,
2922 int32_t tabIndex
, childTabIndex
;
2926 for (nsIContent
* child
= aParent
->GetFirstChild();
2928 child
= child
->GetNextSibling()) {
2929 childTabIndex
= GetNextTabIndex(child
, aCurrentTabIndex
, aForward
);
2930 if (childTabIndex
> aCurrentTabIndex
&& childTabIndex
!= tabIndex
) {
2931 tabIndex
= (tabIndex
== 0 || childTabIndex
< tabIndex
) ? childTabIndex
: tabIndex
;
2934 nsAutoString tabIndexStr
;
2935 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
, tabIndexStr
);
2937 int32_t val
= tabIndexStr
.ToInteger(&ec
);
2938 if (NS_SUCCEEDED (ec
) && val
> aCurrentTabIndex
&& val
!= tabIndex
) {
2939 tabIndex
= (tabIndex
== 0 || val
< tabIndex
) ? val
: tabIndex
;
2943 else { /* !aForward */
2945 for (nsIContent
* child
= aParent
->GetFirstChild();
2947 child
= child
->GetNextSibling()) {
2948 childTabIndex
= GetNextTabIndex(child
, aCurrentTabIndex
, aForward
);
2949 if ((aCurrentTabIndex
== 0 && childTabIndex
> tabIndex
) ||
2950 (childTabIndex
< aCurrentTabIndex
&& childTabIndex
> tabIndex
)) {
2951 tabIndex
= childTabIndex
;
2954 nsAutoString tabIndexStr
;
2955 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
, tabIndexStr
);
2957 int32_t val
= tabIndexStr
.ToInteger(&ec
);
2958 if (NS_SUCCEEDED (ec
)) {
2959 if ((aCurrentTabIndex
== 0 && val
> tabIndex
) ||
2960 (val
< aCurrentTabIndex
&& val
> tabIndex
) ) {
2971 nsFocusManager::GetRootForFocus(nsPIDOMWindow
* aWindow
,
2972 nsIDocument
* aDocument
,
2973 bool aIsForDocNavigation
,
2974 bool aCheckVisibility
)
2976 // the root element's canvas may be focused as long as the document is in a
2977 // a non-chrome shell and does not contain a frameset.
2978 if (aIsForDocNavigation
) {
2979 nsCOMPtr
<Element
> docElement
= aWindow
->GetFrameElementInternal();
2980 // document navigation skips iframes and frames that are specifically non-focusable
2982 if (docElement
->Tag() == nsGkAtoms::iframe
)
2985 nsIFrame
* frame
= docElement
->GetPrimaryFrame();
2986 if (!frame
|| !frame
->IsFocusable(nullptr, 0))
2990 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
2991 if (docShell
->ItemType() == nsIDocShellTreeItem::typeChrome
) {
2996 if (aCheckVisibility
&& !IsWindowVisible(aWindow
))
2999 Element
*rootElement
= aDocument
->GetRootElement();
3004 if (aCheckVisibility
&& !rootElement
->GetPrimaryFrame()) {
3008 // Finally, check if this is a frameset
3009 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(aDocument
);
3010 if (htmlDoc
&& aDocument
->GetHtmlChildElement(nsGkAtoms::frameset
)) {
3018 nsFocusManager::GetLastDocShell(nsIDocShellTreeItem
* aItem
,
3019 nsIDocShellTreeItem
** aResult
)
3023 nsCOMPtr
<nsIDocShellTreeItem
> curItem
= aItem
;
3025 int32_t childCount
= 0;
3026 curItem
->GetChildCount(&childCount
);
3029 NS_ADDREF(*aResult
);
3034 curItem
->GetChildAt(childCount
- 1, getter_AddRefs(curItem
));
3039 nsFocusManager::GetNextDocShell(nsIDocShellTreeItem
* aItem
,
3040 nsIDocShellTreeItem
** aResult
)
3044 int32_t childCount
= 0;
3045 aItem
->GetChildCount(&childCount
);
3047 aItem
->GetChildAt(0, aResult
);
3052 nsCOMPtr
<nsIDocShellTreeItem
> curItem
= aItem
;
3054 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
3055 curItem
->GetParent(getter_AddRefs(parentItem
));
3059 // Note that we avoid using GetChildOffset() here because docshell
3060 // child offsets can't be trusted to be correct. bug 162283.
3061 nsCOMPtr
<nsIDocShellTreeItem
> iterItem
;
3063 parentItem
->GetChildCount(&childCount
);
3064 for (int32_t index
= 0; index
< childCount
; ++index
) {
3065 parentItem
->GetChildAt(index
, getter_AddRefs(iterItem
));
3066 if (iterItem
== curItem
) {
3068 if (index
< childCount
) {
3069 parentItem
->GetChildAt(index
, aResult
);
3077 curItem
= parentItem
;
3082 nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem
* aItem
,
3083 nsIDocShellTreeItem
** aResult
)
3087 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
3088 aItem
->GetParent(getter_AddRefs(parentItem
));
3092 // Note that we avoid using GetChildOffset() here because docshell
3093 // child offsets can't be trusted to be correct. bug 162283.
3094 int32_t childCount
= 0;
3095 parentItem
->GetChildCount(&childCount
);
3096 nsCOMPtr
<nsIDocShellTreeItem
> prevItem
, iterItem
;
3097 for (int32_t index
= 0; index
< childCount
; ++index
) {
3098 parentItem
->GetChildAt(index
, getter_AddRefs(iterItem
));
3099 if (iterItem
== aItem
)
3101 prevItem
= iterItem
;
3105 GetLastDocShell(prevItem
, aResult
);
3107 NS_ADDREF(*aResult
= parentItem
);
3111 nsFocusManager::GetNextTabbablePanel(nsIDocument
* aDocument
, nsIFrame
* aCurrentPopup
, bool aForward
)
3113 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
3117 // Iterate through the array backwards if aForward is false.
3118 nsTArray
<nsIFrame
*> popups
;
3119 pm
->GetVisiblePopups(popups
);
3120 int32_t i
= aForward
? 0 : popups
.Length() - 1;
3121 int32_t end
= aForward
? popups
.Length() : -1;
3123 for (; i
!= end
; aForward
? i
++ : i
--) {
3124 nsIFrame
* popupFrame
= popups
[i
];
3125 if (aCurrentPopup
) {
3126 // If the current popup is set, then we need to skip over this popup and
3127 // wait until the currently focused popup is found. Once found, the
3128 // current popup will be cleared so that the next popup is used.
3129 if (aCurrentPopup
== popupFrame
)
3130 aCurrentPopup
= nullptr;
3134 // Skip over non-panels
3135 if (popupFrame
->GetContent()->Tag() != nsGkAtoms::panel
||
3136 (aDocument
&& popupFrame
->GetContent()->GetComposedDoc() != aDocument
)) {
3140 // Find the first focusable content within the popup. If there isn't any
3141 // focusable content in the popup, skip to the next popup.
3142 nsIPresShell
* presShell
= popupFrame
->PresContext()->GetPresShell();
3144 nsCOMPtr
<nsIContent
> nextFocus
;
3145 nsIContent
* popup
= popupFrame
->GetContent();
3146 nsresult rv
= GetNextTabbableContent(presShell
, popup
,
3149 getter_AddRefs(nextFocus
));
3150 if (NS_SUCCEEDED(rv
) && nextFocus
) {
3151 return nextFocus
.get();
3160 nsFocusManager::GetNextTabbableDocument(nsIContent
* aStartContent
, bool aForward
)
3162 // If currentPopup is set, then the starting content is in a panel.
3163 nsIFrame
* currentPopup
= nullptr;
3164 nsCOMPtr
<nsIDocument
> doc
;
3165 nsCOMPtr
<nsIDocShell
> startDocShell
;
3167 if (aStartContent
) {
3168 doc
= aStartContent
->GetComposedDoc();
3170 startDocShell
= doc
->GetWindow()->GetDocShell();
3173 // Check if the starting content is inside a panel. Document navigation
3174 // must start from this panel instead of the document root.
3175 nsIContent
* content
= aStartContent
;
3177 if (content
->NodeInfo()->Equals(nsGkAtoms::panel
, kNameSpaceID_XUL
)) {
3178 currentPopup
= content
->GetPrimaryFrame();
3181 content
= content
->GetParent();
3184 else if (mFocusedWindow
) {
3185 startDocShell
= mFocusedWindow
->GetDocShell();
3186 doc
= mFocusedWindow
->GetExtantDoc();
3187 } else if (mActiveWindow
) {
3188 startDocShell
= mActiveWindow
->GetDocShell();
3189 doc
= mActiveWindow
->GetExtantDoc();
3195 // perform a depth first search (preorder) of the docshell tree
3196 // looking for an HTML Frame or a chrome document
3197 nsIContent
* content
= aStartContent
;
3198 nsCOMPtr
<nsIDocShellTreeItem
> curItem
= startDocShell
.get();
3199 nsCOMPtr
<nsIDocShellTreeItem
> nextItem
;
3201 // If moving forward, check for a panel in the starting document. If one
3202 // exists with focusable content, return that content instead of the next
3203 // document. If currentPopup is set, then, another panel may exist. If no
3204 // such panel exists, then continue on to check the next document.
3205 // When moving backwards, and the starting content is in a panel, then
3206 // check for additional panels in the starting document. If the starting
3207 // content is not in a panel, move back to the previous document and check
3208 // for panels there.
3210 bool checkPopups
= false;
3211 nsCOMPtr
<nsPIDOMWindow
> nextFrame
= nullptr;
3213 if (doc
&& (aForward
|| currentPopup
)) {
3214 nsIContent
* popupContent
= GetNextTabbablePanel(doc
, currentPopup
, aForward
);
3216 return popupContent
;
3218 if (!aForward
&& currentPopup
) {
3219 // The starting content was in a popup, yet no other popups were
3220 // found. Move onto the starting content's document.
3221 nextFrame
= doc
->GetWindow();
3225 // Look for the next or previous document.
3228 GetNextDocShell(curItem
, getter_AddRefs(nextItem
));
3230 // wrap around to the beginning, which is the top of the tree
3231 startDocShell
->GetRootTreeItem(getter_AddRefs(nextItem
));
3235 GetPreviousDocShell(curItem
, getter_AddRefs(nextItem
));
3237 // wrap around to the end, which is the last item in the tree
3238 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
3239 startDocShell
->GetRootTreeItem(getter_AddRefs(rootItem
));
3240 GetLastDocShell(rootItem
, getter_AddRefs(nextItem
));
3243 // When going back to the previous document, check for any focusable
3244 // popups in that previous document first.
3249 nextFrame
= nextItem
? nextItem
->GetWindow() : nullptr;
3255 // Clear currentPopup for the next iteration
3256 currentPopup
= nullptr;
3258 // If event handling is suppressed, move on to the next document. Set
3259 // content to null so that the popup check will be skipped on the next
3261 doc
= nextFrame
->GetExtantDoc();
3262 if (!doc
|| doc
->EventHandlingSuppressed()) {
3268 // When iterating backwards, check the panels of the previous document
3269 // first. If a panel exists that has focusable content, focus that.
3270 // Otherwise, continue on to focus the document.
3271 nsIContent
* popupContent
= GetNextTabbablePanel(doc
, nullptr, false);
3273 return popupContent
;
3276 content
= GetRootForFocus(nextFrame
, doc
, true, true);
3277 if (content
&& !GetRootForFocus(nextFrame
, doc
, false, false)) {
3278 // if the found content is in a chrome shell or a frameset, navigate
3279 // forward one tabbable item so that the first item is focused. Note
3280 // that we always go forward and not back here.
3281 nsCOMPtr
<nsIContent
> nextFocus
;
3282 Element
* rootElement
= doc
->GetRootElement();
3283 nsIPresShell
* presShell
= doc
->GetShell();
3285 nsresult rv
= GetNextTabbableContent(presShell
, rootElement
,
3286 nullptr, rootElement
,
3288 getter_AddRefs(nextFocus
));
3289 return NS_SUCCEEDED(rv
) ? nextFocus
.get() : nullptr;
3299 nsFocusManager::GetFocusInSelection(nsPIDOMWindow
* aWindow
,
3300 nsIContent
* aStartSelection
,
3301 nsIContent
* aEndSelection
,
3302 nsIContent
** aFocusedContent
)
3304 *aFocusedContent
= nullptr;
3306 nsCOMPtr
<nsIContent
> testContent
= aStartSelection
;
3307 nsCOMPtr
<nsIContent
> nextTestContent
= aEndSelection
;
3309 nsCOMPtr
<nsIContent
> currentFocus
= aWindow
->GetFocusedNode();
3311 // We now have the correct start node in selectionContent!
3312 // Search for focusable elements, starting with selectionContent
3314 // Method #1: Keep going up while we look - an ancestor might be focusable
3315 // We could end the loop earlier, such as when we're no longer
3316 // in the same frame, by comparing selectionContent->GetPrimaryFrame()
3317 // with a variable holding the starting selectionContent
3318 while (testContent
) {
3319 // Keep testing while selectionContent is equal to something,
3320 // eventually we'll run out of ancestors
3322 nsCOMPtr
<nsIURI
> uri
;
3323 if (testContent
== currentFocus
||
3324 testContent
->IsLink(getter_AddRefs(uri
))) {
3325 NS_ADDREF(*aFocusedContent
= testContent
);
3330 testContent
= testContent
->GetParent();
3333 // We run this loop again, checking the ancestor chain of the selection's end point
3334 testContent
= nextTestContent
;
3335 nextTestContent
= nullptr;
3339 // We couldn't find an anchor that was an ancestor of the selection start
3340 // Method #2: look for anchor in selection's primary range (depth first search)
3342 // Turn into nodes so that we can use GetNextSibling() and GetFirstChild()
3343 nsCOMPtr
<nsIDOMNode
> selectionNode(do_QueryInterface(aStartSelection
));
3344 nsCOMPtr
<nsIDOMNode
> endSelectionNode(do_QueryInterface(aEndSelection
));
3345 nsCOMPtr
<nsIDOMNode
> testNode
;
3348 testContent
= do_QueryInterface(selectionNode
);
3350 // We're looking for any focusable link that could be part of the
3351 // main document's selection.
3352 nsCOMPtr
<nsIURI
> uri
;
3353 if (testContent
== currentFocus
||
3354 testContent
->IsLink(getter_AddRefs(uri
))) {
3355 NS_ADDREF(*aFocusedContent
= testContent
);
3359 selectionNode
->GetFirstChild(getter_AddRefs(testNode
));
3361 selectionNode
= testNode
;
3365 if (selectionNode
== endSelectionNode
)
3367 selectionNode
->GetNextSibling(getter_AddRefs(testNode
));
3369 selectionNode
= testNode
;
3374 selectionNode
->GetParentNode(getter_AddRefs(testNode
));
3375 if (!testNode
|| testNode
== endSelectionNode
) {
3376 selectionNode
= nullptr;
3379 testNode
->GetNextSibling(getter_AddRefs(selectionNode
));
3382 selectionNode
= testNode
;
3385 while (selectionNode
&& selectionNode
!= endSelectionNode
);
3388 class PointerUnlocker
: public nsRunnable
3393 MOZ_ASSERT(!PointerUnlocker::sActiveUnlocker
);
3394 PointerUnlocker::sActiveUnlocker
= this;
3399 if (PointerUnlocker::sActiveUnlocker
== this) {
3400 PointerUnlocker::sActiveUnlocker
= nullptr;
3406 if (PointerUnlocker::sActiveUnlocker
== this) {
3407 PointerUnlocker::sActiveUnlocker
= nullptr;
3409 NS_ENSURE_STATE(nsFocusManager::GetFocusManager());
3410 nsPIDOMWindow
* focused
=
3411 nsFocusManager::GetFocusManager()->GetFocusedWindow();
3412 nsCOMPtr
<nsIDocument
> pointerLockedDoc
=
3413 do_QueryReferent(EventStateManager::sPointerLockedDoc
);
3414 if (pointerLockedDoc
&&
3415 !nsContentUtils::IsInPointerLockContext(focused
)) {
3416 nsIDocument::UnlockPointer();
3421 static PointerUnlocker
* sActiveUnlocker
;
3425 PointerUnlocker::sActiveUnlocker
= nullptr;
3428 nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindow
* aWindow
)
3430 if (!PointerUnlocker::sActiveUnlocker
&&
3431 nsContentUtils::IsInPointerLockContext(mFocusedWindow
) &&
3432 !nsContentUtils::IsInPointerLockContext(aWindow
)) {
3433 nsCOMPtr
<nsIRunnable
> runnable
= new PointerUnlocker();
3434 NS_DispatchToCurrentThread(runnable
);
3436 mFocusedWindow
= aWindow
;
3440 nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration
)
3446 if (sInstance
->mActiveWindow
) {
3447 sInstance
->mActiveWindow
->
3448 MarkUncollectableForCCGeneration(aGeneration
);
3450 if (sInstance
->mFocusedWindow
) {
3451 sInstance
->mFocusedWindow
->
3452 MarkUncollectableForCCGeneration(aGeneration
);
3454 if (sInstance
->mWindowBeingLowered
) {
3455 sInstance
->mWindowBeingLowered
->
3456 MarkUncollectableForCCGeneration(aGeneration
);
3458 if (sInstance
->mFocusedContent
) {
3459 sInstance
->mFocusedContent
->OwnerDoc()->
3460 MarkUncollectableForCCGeneration(aGeneration
);
3462 if (sInstance
->mFirstBlurEvent
) {
3463 sInstance
->mFirstBlurEvent
->OwnerDoc()->
3464 MarkUncollectableForCCGeneration(aGeneration
);
3466 if (sInstance
->mFirstFocusEvent
) {
3467 sInstance
->mFirstFocusEvent
->OwnerDoc()->
3468 MarkUncollectableForCCGeneration(aGeneration
);
3470 if (sInstance
->mMouseButtonEventHandlingDocument
) {
3471 sInstance
->mMouseButtonEventHandlingDocument
->
3472 MarkUncollectableForCCGeneration(aGeneration
);
3477 NS_NewFocusManager(nsIFocusManager
** aResult
)
3479 NS_IF_ADDREF(*aResult
= nsFocusManager::GetFocusManager());