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 "nsIEditor.h"
16 #include "nsPIDOMWindow.h"
17 #include "nsIDOMElement.h"
18 #include "nsIDOMDocument.h"
19 #include "nsIDOMRange.h"
20 #include "nsIHTMLDocument.h"
21 #include "nsIDocShell.h"
22 #include "nsIDocShellTreeOwner.h"
23 #include "nsLayoutUtils.h"
24 #include "nsIPresShell.h"
25 #include "nsFrameTraversal.h"
26 #include "nsIWebNavigation.h"
28 #include "nsIBaseWindow.h"
29 #include "nsIXULWindow.h"
30 #include "nsViewManager.h"
31 #include "nsFrameSelection.h"
32 #include "mozilla/dom/Selection.h"
33 #include "nsXULPopupManager.h"
34 #include "nsIScriptObjectPrincipal.h"
35 #include "nsIPrincipal.h"
36 #include "nsIObserverService.h"
37 #include "nsIObjectFrame.h"
38 #include "nsBindingManager.h"
39 #include "nsStyleCoord.h"
40 #include "SelectionCarets.h"
42 #include "mozilla/ContentEvents.h"
43 #include "mozilla/dom/Element.h"
44 #include "mozilla/EventDispatcher.h"
45 #include "mozilla/EventStateManager.h"
46 #include "mozilla/EventStates.h"
47 #include "mozilla/IMEStateManager.h"
48 #include "mozilla/LookAndFeel.h"
49 #include "mozilla/Preferences.h"
50 #include "mozilla/Services.h"
51 #include "mozilla/unused.h"
55 #include "nsIDOMXULTextboxElement.h"
56 #include "nsIDOMXULMenuListElement.h"
60 #include "nsAccessibilityService.h"
64 #include "nsIScriptError.h"
67 using namespace mozilla
;
68 using namespace mozilla::dom
;
69 using namespace mozilla::widget
;
73 // Two types of focus pr logging are available:
74 // 'Focus' for normal focus manager calls
75 // 'FocusNavigation' for tab and document navigation
76 PRLogModuleInfo
* gFocusLog
;
77 PRLogModuleInfo
* gFocusNavigationLog
;
79 #define LOGFOCUS(args) PR_LOG(gFocusLog, 4, args)
80 #define LOGFOCUSNAVIGATION(args) PR_LOG(gFocusNavigationLog, 4, args)
82 #define LOGTAG(log, format, content) \
84 nsAutoCString tag(NS_LITERAL_CSTRING("(none)")); \
86 content->Tag()->ToUTF8String(tag); \
88 PR_LOG(log, 4, (format, tag.get())); \
91 #define LOGCONTENT(format, content) LOGTAG(gFocusLog, format, content)
92 #define LOGCONTENTNAVIGATION(format, content) LOGTAG(gFocusNavigationLog, format, content)
96 #define LOGFOCUS(args)
97 #define LOGFOCUSNAVIGATION(args)
98 #define LOGCONTENT(format, content)
99 #define LOGCONTENTNAVIGATION(format, content)
103 struct nsDelayedBlurOrFocusEvent
105 nsDelayedBlurOrFocusEvent(uint32_t aType
,
106 nsIPresShell
* aPresShell
,
107 nsIDocument
* aDocument
,
108 EventTarget
* aTarget
)
110 mPresShell(aPresShell
),
111 mDocument(aDocument
),
114 nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent
& aOther
)
115 : mType(aOther
.mType
),
116 mPresShell(aOther
.mPresShell
),
117 mDocument(aOther
.mDocument
),
118 mTarget(aOther
.mTarget
) { }
121 nsCOMPtr
<nsIPresShell
> mPresShell
;
122 nsCOMPtr
<nsIDocument
> mDocument
;
123 nsCOMPtr
<EventTarget
> mTarget
;
126 inline void ImplCycleCollectionUnlink(nsDelayedBlurOrFocusEvent
& aField
)
128 aField
.mPresShell
= nullptr;
129 aField
.mDocument
= nullptr;
130 aField
.mTarget
= nullptr;
134 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
135 nsDelayedBlurOrFocusEvent
& aField
,
139 CycleCollectionNoteChild(aCallback
, aField
.mPresShell
.get(), aName
, aFlags
);
140 CycleCollectionNoteChild(aCallback
, aField
.mDocument
.get(), aName
, aFlags
);
141 CycleCollectionNoteChild(aCallback
, aField
.mTarget
.get(), aName
, aFlags
);
144 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager
)
145 NS_INTERFACE_MAP_ENTRY(nsIFocusManager
)
146 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
147 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
148 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIFocusManager
)
151 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager
)
152 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager
)
154 NS_IMPL_CYCLE_COLLECTION(nsFocusManager
,
161 mDelayedBlurFocusEvents
,
162 mMouseButtonEventHandlingDocument
)
164 nsFocusManager
* nsFocusManager::sInstance
= nullptr;
165 bool nsFocusManager::sMouseFocusesFormControl
= false;
166 bool nsFocusManager::sTestMode
= false;
168 static const char* kObservedPrefs
[] = {
169 "accessibility.browsewithcaret",
170 "accessibility.tabfocus_applies_to_xul",
171 "accessibility.mouse_focuses_formcontrol",
172 "focusmanager.testmode",
176 nsFocusManager::nsFocusManager()
177 : mParentFocusType(ParentFocusType_Ignore
)
180 nsFocusManager::~nsFocusManager()
182 Preferences::RemoveObservers(this, kObservedPrefs
);
184 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
186 obs
->RemoveObserver(this, "xpcom-shutdown");
192 nsFocusManager::Init()
194 nsFocusManager
* fm
= new nsFocusManager();
195 NS_ENSURE_TRUE(fm
, NS_ERROR_OUT_OF_MEMORY
);
200 gFocusLog
= PR_NewLogModule("Focus");
201 gFocusNavigationLog
= PR_NewLogModule("FocusNavigation");
204 nsIContent::sTabFocusModelAppliesToXUL
=
205 Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
206 nsIContent::sTabFocusModelAppliesToXUL
);
208 sMouseFocusesFormControl
=
209 Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false);
211 sTestMode
= Preferences::GetBool("focusmanager.testmode", false);
213 Preferences::AddWeakObservers(fm
, kObservedPrefs
);
215 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
217 obs
->AddObserver(fm
, "xpcom-shutdown", true);
225 nsFocusManager::Shutdown()
227 NS_IF_RELEASE(sInstance
);
231 nsFocusManager::Observe(nsISupports
*aSubject
,
233 const char16_t
*aData
)
235 if (!nsCRT::strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
)) {
236 nsDependentString
data(aData
);
237 if (data
.EqualsLiteral("accessibility.browsewithcaret")) {
238 UpdateCaretForCaretBrowsingMode();
240 else if (data
.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
241 nsIContent::sTabFocusModelAppliesToXUL
=
242 Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
243 nsIContent::sTabFocusModelAppliesToXUL
);
245 else if (data
.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
246 sMouseFocusesFormControl
=
247 Preferences::GetBool("accessibility.mouse_focuses_formcontrol",
250 else if (data
.EqualsLiteral("focusmanager.testmode")) {
251 sTestMode
= Preferences::GetBool("focusmanager.testmode", false);
253 } else if (!nsCRT::strcmp(aTopic
, "xpcom-shutdown")) {
254 mActiveWindow
= nullptr;
255 mFocusedWindow
= nullptr;
256 mFocusedContent
= nullptr;
257 mFirstBlurEvent
= nullptr;
258 mFirstFocusEvent
= nullptr;
259 mWindowBeingLowered
= nullptr;
260 mDelayedBlurFocusEvents
.Clear();
261 mMouseButtonEventHandlingDocument
= nullptr;
267 // given a frame content node, retrieve the nsIDOMWindow displayed in it
268 static nsPIDOMWindow
*
269 GetContentWindow(nsIContent
* aContent
)
271 nsIDocument
* doc
= aContent
->GetComposedDoc();
273 nsIDocument
* subdoc
= doc
->GetSubDocumentFor(aContent
);
275 return subdoc
->GetWindow();
281 // get the current window for the given content node
282 static nsPIDOMWindow
*
283 GetCurrentWindow(nsIContent
* aContent
)
285 nsIDocument
* doc
= aContent
->GetComposedDoc();
286 return doc
? doc
->GetWindow() : nullptr;
291 nsFocusManager::GetFocusedDescendant(nsPIDOMWindow
* aWindow
, bool aDeep
,
292 nsPIDOMWindow
** aFocusedWindow
)
294 NS_ENSURE_TRUE(aWindow
, nullptr);
296 *aFocusedWindow
= nullptr;
298 nsIContent
* currentContent
= nullptr;
299 nsPIDOMWindow
* window
= aWindow
->GetOuterWindow();
301 *aFocusedWindow
= window
;
302 currentContent
= window
->GetFocusedNode();
303 if (!currentContent
|| !aDeep
)
306 window
= GetContentWindow(currentContent
);
309 NS_IF_ADDREF(*aFocusedWindow
);
311 return currentContent
;
316 nsFocusManager::GetRedirectedFocus(nsIContent
* aContent
)
319 if (aContent
->IsXUL()) {
320 nsCOMPtr
<nsIDOMNode
> inputField
;
322 nsCOMPtr
<nsIDOMXULTextBoxElement
> textbox
= do_QueryInterface(aContent
);
324 textbox
->GetInputField(getter_AddRefs(inputField
));
327 nsCOMPtr
<nsIDOMXULMenuListElement
> menulist
= do_QueryInterface(aContent
);
329 menulist
->GetInputField(getter_AddRefs(inputField
));
331 else if (aContent
->Tag() == nsGkAtoms::scale
) {
332 nsCOMPtr
<nsIDocument
> doc
= aContent
->GetComposedDoc();
336 nsINodeList
* children
= doc
->BindingManager()->GetAnonymousNodesFor(aContent
);
338 nsIContent
* child
= children
->Item(0);
339 if (child
&& child
->Tag() == nsGkAtoms::slider
)
346 nsCOMPtr
<nsIContent
> retval
= do_QueryInterface(inputField
);
356 InputContextAction::Cause
357 nsFocusManager::GetFocusMoveActionCause(uint32_t aFlags
)
359 if (aFlags
& nsIFocusManager::FLAG_BYMOUSE
) {
360 return InputContextAction::CAUSE_MOUSE
;
361 } else if (aFlags
& nsIFocusManager::FLAG_BYKEY
) {
362 return InputContextAction::CAUSE_KEY
;
364 return InputContextAction::CAUSE_UNKNOWN
;
368 nsFocusManager::GetActiveWindow(nsIDOMWindow
** aWindow
)
370 NS_IF_ADDREF(*aWindow
= mActiveWindow
);
375 nsFocusManager::SetActiveWindow(nsIDOMWindow
* aWindow
)
377 // only top-level windows can be made active
378 nsCOMPtr
<nsPIDOMWindow
> piWindow
= do_QueryInterface(aWindow
);
380 piWindow
= piWindow
->GetOuterWindow();
382 NS_ENSURE_TRUE(piWindow
&& (piWindow
== piWindow
->GetPrivateRoot()),
383 NS_ERROR_INVALID_ARG
);
385 RaiseWindow(piWindow
);
390 nsFocusManager::GetFocusedWindow(nsIDOMWindow
** aFocusedWindow
)
392 NS_IF_ADDREF(*aFocusedWindow
= mFocusedWindow
);
396 NS_IMETHODIMP
nsFocusManager::SetFocusedWindow(nsIDOMWindow
* aWindowToFocus
)
398 LOGFOCUS(("<<SetFocusedWindow begin>>"));
400 nsCOMPtr
<nsPIDOMWindow
> windowToFocus(do_QueryInterface(aWindowToFocus
));
401 NS_ENSURE_TRUE(windowToFocus
, NS_ERROR_FAILURE
);
403 windowToFocus
= windowToFocus
->GetOuterWindow();
405 nsCOMPtr
<Element
> frameElement
= windowToFocus
->GetFrameElementInternal();
407 // pass false for aFocusChanged so that the caret does not get updated
408 // and scrolling does not occur.
409 SetFocusInner(frameElement
, 0, false, true);
412 // this is a top-level window. If the window has a child frame focused,
413 // clear the focus. Otherwise, focus should already be in this frame, or
414 // already cleared. This ensures that focus will be in this frame and not
416 nsIContent
* content
= windowToFocus
->GetFocusedNode();
418 nsCOMPtr
<nsIDOMWindow
> childWindow
= GetContentWindow(content
);
420 ClearFocus(windowToFocus
);
424 nsCOMPtr
<nsPIDOMWindow
> rootWindow
= windowToFocus
->GetPrivateRoot();
426 RaiseWindow(rootWindow
);
428 LOGFOCUS(("<<SetFocusedWindow end>>"));
434 nsFocusManager::GetFocusedElement(nsIDOMElement
** aFocusedElement
)
437 CallQueryInterface(mFocusedContent
, aFocusedElement
);
439 *aFocusedElement
= nullptr;
444 nsFocusManager::GetLastFocusMethod(nsIDOMWindow
* aWindow
, uint32_t* aLastFocusMethod
)
446 // the focus method is stored on the inner window
447 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
448 if (window
&& window
->IsOuterWindow())
449 window
= window
->GetCurrentInnerWindow();
451 window
= mFocusedWindow
;
453 *aLastFocusMethod
= window
? window
->GetFocusMethod() : 0;
455 NS_ASSERTION((*aLastFocusMethod
& FOCUSMETHOD_MASK
) == *aLastFocusMethod
,
456 "invalid focus method");
461 nsFocusManager::SetFocus(nsIDOMElement
* aElement
, uint32_t aFlags
)
463 LOGFOCUS(("<<SetFocus begin>>"));
465 nsCOMPtr
<nsIContent
> newFocus
= do_QueryInterface(aElement
);
466 NS_ENSURE_ARG(newFocus
);
468 SetFocusInner(newFocus
, aFlags
, true, true);
470 LOGFOCUS(("<<SetFocus end>>"));
476 nsFocusManager::ElementIsFocusable(nsIDOMElement
* aElement
, uint32_t aFlags
,
479 NS_ENSURE_TRUE(aElement
, NS_ERROR_INVALID_ARG
);
481 nsCOMPtr
<nsIContent
> aContent
= do_QueryInterface(aElement
);
483 *aIsFocusable
= CheckIfFocusable(aContent
, aFlags
) != nullptr;
489 nsFocusManager::MoveFocus(nsIDOMWindow
* aWindow
, nsIDOMElement
* aStartElement
,
490 uint32_t aType
, uint32_t aFlags
, nsIDOMElement
** aElement
)
495 LOGFOCUS(("<<MoveFocus begin Type: %d Flags: %x>>", aType
, aFlags
));
497 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
) && mFocusedWindow
) {
498 nsIDocument
* doc
= mFocusedWindow
->GetExtantDoc();
499 if (doc
&& doc
->GetDocumentURI()) {
501 doc
->GetDocumentURI()->GetSpec(spec
);
502 LOGFOCUS((" Focused Window: %p %s", mFocusedWindow
.get(), spec
.get()));
506 LOGCONTENT(" Current Focus: %s", mFocusedContent
.get());
509 // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
510 // the other focus methods is already set, or we're just moving to the root
511 // or caret position.
512 if (aType
!= MOVEFOCUS_ROOT
&& aType
!= MOVEFOCUS_CARET
&&
513 (aFlags
& FOCUSMETHOD_MASK
) == 0) {
514 aFlags
|= FLAG_BYMOVEFOCUS
;
517 nsCOMPtr
<nsPIDOMWindow
> window
;
518 nsCOMPtr
<nsIContent
> startContent
;
520 startContent
= do_QueryInterface(aStartElement
);
521 NS_ENSURE_TRUE(startContent
, NS_ERROR_INVALID_ARG
);
523 window
= GetCurrentWindow(startContent
);
526 window
= aWindow
? do_QueryInterface(aWindow
) : mFocusedWindow
;
527 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
528 window
= window
->GetOuterWindow();
531 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
533 bool noParentTraversal
= aFlags
& FLAG_NOPARENTFRAME
;
534 nsCOMPtr
<nsIContent
> newFocus
;
535 nsresult rv
= DetermineElementToMoveFocus(window
, startContent
, aType
, noParentTraversal
,
536 getter_AddRefs(newFocus
));
537 NS_ENSURE_SUCCESS(rv
, rv
);
539 LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus
.get());
542 // for caret movement, pass false for the aFocusChanged argument,
543 // otherwise the caret will end up moving to the focus position. This
544 // would be a problem because the caret would move to the beginning of the
545 // focused link making it impossible to navigate the caret over a link.
546 SetFocusInner(newFocus
, aFlags
, aType
!= MOVEFOCUS_CARET
, true);
547 CallQueryInterface(newFocus
, aElement
);
549 else if (aType
== MOVEFOCUS_ROOT
|| aType
== MOVEFOCUS_CARET
) {
550 // no content was found, so clear the focus for these two types.
554 LOGFOCUS(("<<MoveFocus end>>"));
560 nsFocusManager::ClearFocus(nsIDOMWindow
* aWindow
)
562 LOGFOCUS(("<<ClearFocus begin>>"));
564 // if the window to clear is the focused window or an ancestor of the
565 // focused window, then blur the existing focused content. Otherwise, the
566 // focus is somewhere else so just update the current node.
567 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
568 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
570 window
= window
->GetOuterWindow();
571 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
573 if (IsSameOrAncestor(window
, mFocusedWindow
)) {
574 bool isAncestor
= (window
!= mFocusedWindow
);
575 if (Blur(window
, nullptr, isAncestor
, true)) {
576 // if we are clearing the focus on an ancestor of the focused window,
577 // the ancestor will become the new focused window, so focus it
579 Focus(window
, nullptr, 0, true, false, false, true);
583 window
->SetFocusedNode(nullptr);
586 LOGFOCUS(("<<ClearFocus end>>"));
592 nsFocusManager::GetFocusedElementForWindow(nsIDOMWindow
* aWindow
,
594 nsIDOMWindow
** aFocusedWindow
,
595 nsIDOMElement
** aElement
)
599 *aFocusedWindow
= nullptr;
601 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
602 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
604 window
= window
->GetOuterWindow();
605 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
607 nsCOMPtr
<nsPIDOMWindow
> focusedWindow
;
608 nsCOMPtr
<nsIContent
> focusedContent
=
609 GetFocusedDescendant(window
, aDeep
, getter_AddRefs(focusedWindow
));
611 CallQueryInterface(focusedContent
, aElement
);
614 NS_IF_ADDREF(*aFocusedWindow
= focusedWindow
);
620 nsFocusManager::MoveCaretToFocus(nsIDOMWindow
* aWindow
)
622 nsCOMPtr
<nsIWebNavigation
> webnav
= do_GetInterface(aWindow
);
623 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(webnav
);
625 if (dsti
->ItemType() != nsIDocShellTreeItem::typeChrome
) {
626 nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(dsti
);
627 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
629 // don't move the caret for editable documents
631 docShell
->GetEditable(&isEditable
);
635 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
636 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
638 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aWindow
));
639 nsCOMPtr
<nsIContent
> content
= window
->GetFocusedNode();
641 MoveCaretToFocus(presShell
, content
);
649 nsFocusManager::WindowRaised(nsIDOMWindow
* aWindow
)
651 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
652 NS_ENSURE_TRUE(window
&& window
->IsOuterWindow(), NS_ERROR_INVALID_ARG
);
655 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
656 LOGFOCUS(("Window %p Raised [Currently: %p %p]", aWindow
, mActiveWindow
.get(), mFocusedWindow
.get()));
658 nsIDocument
* doc
= window
->GetExtantDoc();
659 if (doc
&& doc
->GetDocumentURI()) {
660 doc
->GetDocumentURI()->GetSpec(spec
);
661 LOGFOCUS((" Raised Window: %p %s", aWindow
, spec
.get()));
664 doc
= mActiveWindow
->GetExtantDoc();
665 if (doc
&& doc
->GetDocumentURI()) {
666 doc
->GetDocumentURI()->GetSpec(spec
);
667 LOGFOCUS((" Active Window: %p %s", mActiveWindow
.get(), spec
.get()));
673 if (mActiveWindow
== window
) {
674 // The window is already active, so there is no need to focus anything,
675 // but make sure that the right widget is focused. This is a special case
676 // for Windows because when restoring a minimized window, a second
677 // activation will occur and the top-level widget could be focused instead
678 // of the child we want. We solve this by calling SetFocus to ensure that
679 // what the focus manager thinks should be the current widget is actually
681 EnsureCurrentWidgetFocused();
685 // lower the existing window, if any. This shouldn't happen usually.
687 WindowLowered(mActiveWindow
);
689 nsCOMPtr
<nsIDocShellTreeItem
> docShellAsItem
= window
->GetDocShell();
690 // If there's no docShellAsItem, this window must have been closed,
691 // in that case there is no tree owner.
692 NS_ENSURE_TRUE(docShellAsItem
, NS_OK
);
694 // set this as the active window
695 mActiveWindow
= window
;
697 // ensure that the window is enabled and visible
698 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
;
699 docShellAsItem
->GetTreeOwner(getter_AddRefs(treeOwner
));
700 nsCOMPtr
<nsIBaseWindow
> baseWindow
= do_QueryInterface(treeOwner
);
702 bool isEnabled
= true;
703 if (NS_SUCCEEDED(baseWindow
->GetEnabled(&isEnabled
)) && !isEnabled
) {
704 return NS_ERROR_FAILURE
;
708 baseWindow
->SetVisibility(true);
712 // If this is a parent or single process window, send the activate event.
713 // Events for child process windows will be sent when ParentActivated
715 if (mParentFocusType
== ParentFocusType_Ignore
) {
716 ActivateOrDeactivate(window
, true);
719 // retrieve the last focused element within the window that was raised
720 nsCOMPtr
<nsPIDOMWindow
> currentWindow
;
721 nsCOMPtr
<nsIContent
> currentFocus
=
722 GetFocusedDescendant(window
, true, getter_AddRefs(currentWindow
));
724 NS_ASSERTION(currentWindow
, "window raised with no window current");
728 nsCOMPtr
<nsIDocShell
> currentDocShell
= currentWindow
->GetDocShell();
730 nsCOMPtr
<nsIPresShell
> presShell
= currentDocShell
->GetPresShell();
732 // disable selection mousedown state on activation
733 // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
734 nsRefPtr
<nsFrameSelection
> frameSelection
= presShell
->FrameSelection();
735 frameSelection
->SetDragState(false);
738 // If there is no nsIXULWindow, then this is an embedded or child process window.
739 // Pass false for aWindowRaised so that commands get updated.
740 nsCOMPtr
<nsIXULWindow
> xulWin(do_GetInterface(baseWindow
));
741 Focus(currentWindow
, currentFocus
, 0, true, false, xulWin
!= nullptr, true);
747 nsFocusManager::WindowLowered(nsIDOMWindow
* aWindow
)
749 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
750 NS_ENSURE_TRUE(window
&& window
->IsOuterWindow(), NS_ERROR_INVALID_ARG
);
753 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
754 LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow
, mActiveWindow
.get(), mFocusedWindow
.get()));
756 nsIDocument
* doc
= window
->GetExtantDoc();
757 if (doc
&& doc
->GetDocumentURI()) {
758 doc
->GetDocumentURI()->GetSpec(spec
);
759 LOGFOCUS((" Lowered Window: %s", spec
.get()));
762 doc
= mActiveWindow
->GetExtantDoc();
763 if (doc
&& doc
->GetDocumentURI()) {
764 doc
->GetDocumentURI()->GetSpec(spec
);
765 LOGFOCUS((" Active Window: %s", spec
.get()));
771 if (mActiveWindow
!= window
)
774 // clear the mouse capture as the active window has changed
775 nsIPresShell::SetCapturingContent(nullptr, 0);
777 // If this is a parent or single process window, send the deactivate event.
778 // Events for child process windows will be sent when ParentActivated
780 if (mParentFocusType
== ParentFocusType_Ignore
) {
781 ActivateOrDeactivate(window
, false);
784 // keep track of the window being lowered, so that attempts to raise the
785 // window can be prevented until we return. Otherwise, focus can get into
787 mWindowBeingLowered
= mActiveWindow
;
788 mActiveWindow
= nullptr;
791 Blur(nullptr, nullptr, true, true);
793 mWindowBeingLowered
= nullptr;
799 nsFocusManager::ContentRemoved(nsIDocument
* aDocument
, nsIContent
* aContent
)
801 NS_ENSURE_ARG(aDocument
);
802 NS_ENSURE_ARG(aContent
);
804 nsPIDOMWindow
*window
= aDocument
->GetWindow();
808 // if the content is currently focused in the window, or is an ancestor
809 // of the currently focused element, reset the focus within that window.
810 nsIContent
* content
= window
->GetFocusedNode();
811 if (content
&& nsContentUtils::ContentIsDescendantOf(content
, aContent
)) {
812 bool shouldShowFocusRing
= window
->ShouldShowFocusRing();
813 window
->SetFocusedNode(nullptr);
815 // if this window is currently focused, clear the global focused
816 // element as well, but don't fire any events.
817 if (window
== mFocusedWindow
) {
818 mFocusedContent
= nullptr;
820 // Check if the node that was focused is an iframe or similar by looking
821 // if it has a subdocument. This would indicate that this focused iframe
822 // and its descendants will be going away. We will need to move the
823 // focus somewhere else, so just clear the focus in the toplevel window
824 // so that no element is focused.
825 nsIDocument
* subdoc
= aDocument
->GetSubDocumentFor(content
);
827 nsCOMPtr
<nsIDocShell
> docShell
= subdoc
->GetDocShell();
829 nsCOMPtr
<nsPIDOMWindow
> childWindow
= docShell
->GetWindow();
830 if (childWindow
&& IsSameOrAncestor(childWindow
, mFocusedWindow
)) {
831 ClearFocus(mActiveWindow
);
837 // Notify the editor in case we removed its ancestor limiter.
838 if (content
->IsEditable()) {
839 nsCOMPtr
<nsIDocShell
> docShell
= aDocument
->GetDocShell();
841 nsCOMPtr
<nsIEditor
> editor
;
842 docShell
->GetEditor(getter_AddRefs(editor
));
844 nsCOMPtr
<nsISelection
> s
;
845 editor
->GetSelection(getter_AddRefs(s
));
846 nsCOMPtr
<nsISelectionPrivate
> selection
= do_QueryInterface(s
);
848 nsCOMPtr
<nsIContent
> limiter
;
849 selection
->GetAncestorLimiter(getter_AddRefs(limiter
));
850 if (limiter
== content
) {
851 editor
->FinalizeSelection();
858 NotifyFocusStateChange(content
, shouldShowFocusRing
, false);
865 nsFocusManager::WindowShown(nsIDOMWindow
* aWindow
, bool aNeedsFocus
)
867 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
868 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
870 window
= window
->GetOuterWindow();
873 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
874 LOGFOCUS(("Window %p Shown [Currently: %p %p]", window
.get(), mActiveWindow
.get(), mFocusedWindow
.get()));
876 nsIDocument
* doc
= window
->GetExtantDoc();
877 if (doc
&& doc
->GetDocumentURI()) {
878 doc
->GetDocumentURI()->GetSpec(spec
);
879 LOGFOCUS(("Shown Window: %s", spec
.get()));
882 if (mFocusedWindow
) {
883 doc
= mFocusedWindow
->GetExtantDoc();
884 if (doc
&& doc
->GetDocumentURI()) {
885 doc
->GetDocumentURI()->GetSpec(spec
);
886 LOGFOCUS((" Focused Window: %s", spec
.get()));
892 if (mFocusedWindow
!= window
)
896 nsCOMPtr
<nsPIDOMWindow
> currentWindow
;
897 nsCOMPtr
<nsIContent
> currentFocus
=
898 GetFocusedDescendant(window
, true, getter_AddRefs(currentWindow
));
900 Focus(currentWindow
, currentFocus
, 0, true, false, false, true);
903 // Sometimes, an element in a window can be focused before the window is
904 // visible, which would mean that the widget may not be properly focused.
905 // When the window becomes visible, make sure the right widget is focused.
906 EnsureCurrentWidgetFocused();
909 if (mParentFocusType
== ParentFocusType_Active
) {
910 ActivateOrDeactivate(window
, true);
917 nsFocusManager::WindowHidden(nsIDOMWindow
* aWindow
)
919 // if there is no window or it is not the same or an ancestor of the
920 // currently focused window, just return, as the current focus will not
923 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
924 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
926 window
= window
->GetOuterWindow();
929 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
930 LOGFOCUS(("Window %p Hidden [Currently: %p %p]", window
.get(), mActiveWindow
.get(), mFocusedWindow
.get()));
932 nsIDocument
* doc
= window
->GetExtantDoc();
933 if (doc
&& doc
->GetDocumentURI()) {
934 doc
->GetDocumentURI()->GetSpec(spec
);
935 LOGFOCUS((" Hide Window: %s", spec
.get()));
938 if (mFocusedWindow
) {
939 doc
= mFocusedWindow
->GetExtantDoc();
940 if (doc
&& doc
->GetDocumentURI()) {
941 doc
->GetDocumentURI()->GetSpec(spec
);
942 LOGFOCUS((" Focused Window: %s", spec
.get()));
947 doc
= mActiveWindow
->GetExtantDoc();
948 if (doc
&& doc
->GetDocumentURI()) {
949 doc
->GetDocumentURI()->GetSpec(spec
);
950 LOGFOCUS((" Active Window: %s", spec
.get()));
956 if (!IsSameOrAncestor(window
, mFocusedWindow
))
959 // at this point, we know that the window being hidden is either the focused
960 // window, or an ancestor of the focused window. Either way, the focus is no
961 // longer valid, so it needs to be updated.
963 nsCOMPtr
<nsIContent
> oldFocusedContent
= mFocusedContent
.forget();
965 nsCOMPtr
<nsIDocShell
> focusedDocShell
= mFocusedWindow
->GetDocShell();
966 nsCOMPtr
<nsIPresShell
> presShell
= focusedDocShell
->GetPresShell();
968 if (oldFocusedContent
&& oldFocusedContent
->IsInComposedDoc()) {
969 NotifyFocusStateChange(oldFocusedContent
,
970 mFocusedWindow
->ShouldShowFocusRing(),
972 window
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
975 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
,
976 oldFocusedContent
->GetComposedDoc(),
977 oldFocusedContent
, 1, false);
981 nsPresContext
* focusedPresContext
=
982 presShell
? presShell
->GetPresContext() : nullptr;
983 IMEStateManager::OnChangeFocus(focusedPresContext
, nullptr,
984 GetFocusMoveActionCause(0));
986 SetCaretVisible(presShell
, false, nullptr);
989 // if the docshell being hidden is being destroyed, then we want to move
990 // focus somewhere else. Call ClearFocus on the toplevel window, which
991 // will have the effect of clearing the focus and moving the focused window
992 // to the toplevel window. But if the window isn't being destroyed, we are
993 // likely just loading a new document in it, so we want to maintain the
994 // focused window so that the new document gets properly focused.
996 nsCOMPtr
<nsIDocShell
> docShellBeingHidden
= window
->GetDocShell();
997 docShellBeingHidden
->IsBeingDestroyed(&beingDestroyed
);
998 if (beingDestroyed
) {
999 // There is usually no need to do anything if a toplevel window is going
1000 // away, as we assume that WindowLowered will be called. However, this may
1001 // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
1002 // a leak. So if the active window is being destroyed, call WindowLowered
1004 NS_ASSERTION(mFocusedWindow
->IsOuterWindow(), "outer window expected");
1005 if (mActiveWindow
== mFocusedWindow
|| mActiveWindow
== window
)
1006 WindowLowered(mActiveWindow
);
1008 ClearFocus(mActiveWindow
);
1012 // if the window being hidden is an ancestor of the focused window, adjust
1013 // the focused window so that it points to the one being hidden. This
1014 // ensures that the focused window isn't in a chain of frames that doesn't
1016 if (window
!= mFocusedWindow
) {
1017 nsCOMPtr
<nsIDocShellTreeItem
> dsti
=
1018 mFocusedWindow
? mFocusedWindow
->GetDocShell() : nullptr;
1020 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1021 dsti
->GetParent(getter_AddRefs(parentDsti
));
1023 nsCOMPtr
<nsPIDOMWindow
> parentWindow
= parentDsti
->GetWindow();
1025 parentWindow
->SetFocusedNode(nullptr);
1029 SetFocusedWindowInternal(window
);
1036 nsFocusManager::FireDelayedEvents(nsIDocument
* aDocument
)
1038 NS_ENSURE_ARG(aDocument
);
1040 // fire any delayed focus and blur events in the same order that they were added
1041 for (uint32_t i
= 0; i
< mDelayedBlurFocusEvents
.Length(); i
++) {
1042 if (mDelayedBlurFocusEvents
[i
].mDocument
== aDocument
) {
1043 if (!aDocument
->GetInnerWindow() ||
1044 !aDocument
->GetInnerWindow()->IsCurrentInnerWindow()) {
1045 // If the document was navigated away from or is defunct, don't bother
1046 // firing events on it. Note the symmetry between this condition and
1047 // the similar one in nsDocument.cpp:FireOrClearDelayedEvents.
1048 mDelayedBlurFocusEvents
.RemoveElementAt(i
);
1050 } else if (!aDocument
->EventHandlingSuppressed()) {
1051 uint32_t type
= mDelayedBlurFocusEvents
[i
].mType
;
1052 nsCOMPtr
<EventTarget
> target
= mDelayedBlurFocusEvents
[i
].mTarget
;
1053 nsCOMPtr
<nsIPresShell
> presShell
= mDelayedBlurFocusEvents
[i
].mPresShell
;
1054 mDelayedBlurFocusEvents
.RemoveElementAt(i
);
1055 SendFocusOrBlurEvent(type
, presShell
, aDocument
, target
, 0, false);
1065 nsFocusManager::FocusPlugin(nsIContent
* aContent
)
1067 NS_ENSURE_ARG(aContent
);
1068 SetFocusInner(aContent
, 0, true, false);
1073 nsFocusManager::ParentActivated(nsIDOMWindow
* aWindow
, bool aActive
)
1075 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aWindow
);
1076 NS_ENSURE_TRUE(window
, NS_ERROR_INVALID_ARG
);
1078 window
= window
->GetOuterWindow();
1080 mParentFocusType
= aActive
? ParentFocusType_Active
: ParentFocusType_Inactive
;
1081 ActivateOrDeactivate(window
, aActive
);
1087 nsFocusManager::NotifyFocusStateChange(nsIContent
* aContent
,
1088 bool aWindowShouldShowFocusRing
,
1091 if (!aContent
->IsElement()) {
1094 EventStates eventState
= NS_EVENT_STATE_FOCUS
;
1095 if (aWindowShouldShowFocusRing
) {
1096 eventState
|= NS_EVENT_STATE_FOCUSRING
;
1098 if (aGettingFocus
) {
1099 aContent
->AsElement()->AddStates(eventState
);
1101 aContent
->AsElement()->RemoveStates(eventState
);
1107 nsFocusManager::EnsureCurrentWidgetFocused()
1109 if (!mFocusedWindow
|| sTestMode
)
1112 // get the main child widget for the focused window and ensure that the
1113 // platform knows that this widget is focused.
1114 nsCOMPtr
<nsIDocShell
> docShell
= mFocusedWindow
->GetDocShell();
1116 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
1118 nsViewManager
* vm
= presShell
->GetViewManager();
1120 nsCOMPtr
<nsIWidget
> widget
;
1121 vm
->GetRootWidget(getter_AddRefs(widget
));
1123 widget
->SetFocus(false);
1130 ActivateOrDeactivateChild(TabParent
* aParent
, void* aArg
)
1132 bool active
= static_cast<bool>(aArg
);
1133 unused
<< aParent
->SendParentActivated(active
);
1137 nsFocusManager::ActivateOrDeactivate(nsPIDOMWindow
* aWindow
, bool aActive
)
1143 // Inform the DOM window that it has activated or deactivated, so that
1144 // the active attribute is updated on the window.
1145 aWindow
->ActivateOrDeactivate(aActive
);
1147 // Send the activate event.
1148 nsContentUtils::DispatchTrustedEvent(aWindow
->GetExtantDoc(),
1150 aActive
? NS_LITERAL_STRING("activate") :
1151 NS_LITERAL_STRING("deactivate"),
1152 true, true, nullptr);
1154 // Look for any remote child frames, iterate over them and send the activation notification.
1155 nsContentUtils::CallOnAllRemoteChildren(aWindow
, ActivateOrDeactivateChild
,
1160 nsFocusManager::SetFocusInner(nsIContent
* aNewContent
, int32_t aFlags
,
1161 bool aFocusChanged
, bool aAdjustWidget
)
1163 // if the element is not focusable, just return and leave the focus as is
1164 nsCOMPtr
<nsIContent
> contentToFocus
= CheckIfFocusable(aNewContent
, aFlags
);
1165 if (!contentToFocus
)
1168 // check if the element to focus is a frame (iframe) containing a child
1169 // document. Frames are never directly focused; instead focusing a frame
1170 // means focus what is inside the frame. To do this, the descendant content
1171 // within the frame is retrieved and that will be focused instead.
1172 nsCOMPtr
<nsPIDOMWindow
> newWindow
;
1173 nsCOMPtr
<nsPIDOMWindow
> subWindow
= GetContentWindow(contentToFocus
);
1175 contentToFocus
= GetFocusedDescendant(subWindow
, true, getter_AddRefs(newWindow
));
1176 // since a window is being refocused, clear aFocusChanged so that the
1177 // caret position isn't updated.
1178 aFocusChanged
= false;
1181 // unless it was set above, retrieve the window for the element to focus
1183 newWindow
= GetCurrentWindow(contentToFocus
);
1185 // if the element is already focused, just return. Note that this happens
1186 // after the frame check above so that we compare the element that will be
1187 // focused rather than the frame it is in.
1188 if (!newWindow
|| (newWindow
== mFocusedWindow
&& contentToFocus
== mFocusedContent
))
1191 // don't allow focus to be placed in docshells or descendants of docshells
1192 // that are being destroyed. Also, ensure that the page hasn't been
1193 // unloaded. The prevents content from being refocused during an unload event.
1194 nsCOMPtr
<nsIDocShell
> newDocShell
= newWindow
->GetDocShell();
1195 nsCOMPtr
<nsIDocShell
> docShell
= newDocShell
;
1198 docShell
->GetIsInUnload(&inUnload
);
1202 bool beingDestroyed
;
1203 docShell
->IsBeingDestroyed(&beingDestroyed
);
1207 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1208 docShell
->GetParent(getter_AddRefs(parentDsti
));
1209 docShell
= do_QueryInterface(parentDsti
);
1212 // if the new element is in the same window as the currently focused element
1213 bool isElementInFocusedWindow
= (mFocusedWindow
== newWindow
);
1215 if (!isElementInFocusedWindow
&& mFocusedWindow
&& newWindow
&&
1216 nsContentUtils::IsHandlingKeyBoardEvent()) {
1217 nsCOMPtr
<nsIScriptObjectPrincipal
> focused
=
1218 do_QueryInterface(mFocusedWindow
);
1219 nsCOMPtr
<nsIScriptObjectPrincipal
> newFocus
=
1220 do_QueryInterface(newWindow
);
1221 nsIPrincipal
* focusedPrincipal
= focused
->GetPrincipal();
1222 nsIPrincipal
* newPrincipal
= newFocus
->GetPrincipal();
1223 if (!focusedPrincipal
|| !newPrincipal
) {
1226 bool subsumes
= false;
1227 focusedPrincipal
->Subsumes(newPrincipal
, &subsumes
);
1228 if (!subsumes
&& !nsContentUtils::IsCallerChrome()) {
1229 NS_WARNING("Not allowed to focus the new window!");
1234 // to check if the new element is in the active window, compare the
1235 // new root docshell for the new element with the active window's docshell.
1236 bool isElementInActiveWindow
= false;
1238 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= newWindow
->GetDocShell();
1239 nsCOMPtr
<nsPIDOMWindow
> newRootWindow
;
1241 nsCOMPtr
<nsIDocShellTreeItem
> root
;
1242 dsti
->GetRootTreeItem(getter_AddRefs(root
));
1243 newRootWindow
= root
? root
->GetWindow() : nullptr;
1245 isElementInActiveWindow
= (mActiveWindow
&& newRootWindow
== mActiveWindow
);
1248 // Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX
1249 // system. We don't control event dispatch to windowed plugins on non-MacOSX,
1250 // so we can't display the "Press ESC to leave fullscreen mode" warning on
1251 // key input if a windowed plugin is focused, so just exit fullscreen
1252 // to guard against phishing.
1254 nsIDocument
* fullscreenAncestor
;
1255 if (contentToFocus
&&
1256 (fullscreenAncestor
= nsContentUtils::GetFullscreenAncestor(contentToFocus
->OwnerDoc())) &&
1257 nsContentUtils::HasPluginWithUncontrolledEventDispatch(contentToFocus
)) {
1258 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
1259 NS_LITERAL_CSTRING("DOM"),
1260 contentToFocus
->OwnerDoc(),
1261 nsContentUtils::eDOM_PROPERTIES
,
1262 "FocusedWindowedPluginWhileFullScreen");
1263 nsIDocument::ExitFullscreen(fullscreenAncestor
, /* async */ true);
1267 // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
1268 // shifted away from the current element if the new shell to focus is
1269 // the same or an ancestor shell of the currently focused shell.
1270 bool allowFrameSwitch
= !(aFlags
& FLAG_NOSWITCHFRAME
) ||
1271 IsSameOrAncestor(newWindow
, mFocusedWindow
);
1273 // if the element is in the active window, frame switching is allowed and
1274 // the content is in a visible window, fire blur and focus events.
1275 bool sendFocusEvent
=
1276 isElementInActiveWindow
&& allowFrameSwitch
&& IsWindowVisible(newWindow
);
1278 // When the following conditions are true:
1279 // * an element has focus
1280 // * isn't called by trusted event (i.e., called by untrusted event or by js)
1281 // * the focus is moved to another document's element
1282 // we need to check the permission.
1283 if (sendFocusEvent
&& mFocusedContent
&&
1284 mFocusedContent
->OwnerDoc() != aNewContent
->OwnerDoc()) {
1285 // If the caller cannot access the current focused node, the caller should
1286 // not be able to steal focus from it. E.g., When the current focused node
1287 // is in chrome, any web contents should not be able to steal the focus.
1288 nsCOMPtr
<nsIDOMNode
> domNode(do_QueryInterface(mFocusedContent
));
1289 sendFocusEvent
= nsContentUtils::CanCallerAccess(domNode
);
1290 if (!sendFocusEvent
&& mMouseButtonEventHandlingDocument
) {
1291 // However, while mouse button event is handling, the handling document's
1292 // script should be able to steal focus.
1293 domNode
= do_QueryInterface(mMouseButtonEventHandlingDocument
);
1294 sendFocusEvent
= nsContentUtils::CanCallerAccess(domNode
);
1298 LOGCONTENT("Shift Focus: %s", contentToFocus
.get());
1299 LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p",
1300 aFlags
, mFocusedWindow
.get(), newWindow
.get(), mFocusedContent
.get()));
1301 LOGFOCUS((" In Active Window: %d In Focused Window: %d SendFocus: %d",
1302 isElementInActiveWindow
, isElementInFocusedWindow
, sendFocusEvent
));
1304 if (sendFocusEvent
) {
1305 // return if blurring fails or the focus changes during the blur
1306 if (mFocusedWindow
) {
1307 // if the focus is being moved to another element in the same document,
1308 // or to a descendant, pass the existing window to Blur so that the
1309 // current node in the existing window is cleared. If moving to a
1310 // window elsewhere, we want to maintain the current node in the
1311 // window but still blur it.
1312 bool currentIsSameOrAncestor
= IsSameOrAncestor(mFocusedWindow
, newWindow
);
1313 // find the common ancestor of the currently focused window and the new
1314 // window. The ancestor will need to have its currently focused node
1315 // cleared once the document has been blurred. Otherwise, we'll be in a
1316 // state where a document is blurred yet the chain of windows above it
1317 // still points to that document.
1318 // For instance, in the following frame tree:
1322 // D is focused and we want to focus C. Once D has been blurred, we need
1323 // to clear out the focus in A, otherwise A would still maintain that B
1324 // was focused, and B that D was focused.
1325 nsCOMPtr
<nsPIDOMWindow
> commonAncestor
;
1326 if (!isElementInFocusedWindow
)
1327 commonAncestor
= GetCommonAncestor(newWindow
, mFocusedWindow
);
1329 if (!Blur(currentIsSameOrAncestor
? mFocusedWindow
.get() : nullptr,
1330 commonAncestor
, !isElementInFocusedWindow
, aAdjustWidget
))
1334 Focus(newWindow
, contentToFocus
, aFlags
, !isElementInFocusedWindow
,
1335 aFocusChanged
, false, aAdjustWidget
);
1338 // otherwise, for inactive windows and when the caller cannot steal the
1339 // focus, update the node in the window, and raise the window if desired.
1340 if (allowFrameSwitch
)
1341 AdjustWindowFocus(newWindow
, true);
1343 // set the focus node and method as needed
1344 uint32_t focusMethod
= aFocusChanged
? aFlags
& FOCUSMETHODANDRING_MASK
:
1345 newWindow
->GetFocusMethod() | (aFlags
& FLAG_SHOWRING
);
1346 newWindow
->SetFocusedNode(contentToFocus
, focusMethod
);
1347 if (aFocusChanged
) {
1348 nsCOMPtr
<nsIDocShell
> docShell
= newWindow
->GetDocShell();
1350 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
1352 ScrollIntoView(presShell
, contentToFocus
, aFlags
);
1355 // update the commands even when inactive so that the attributes for that
1356 // window are up to date.
1357 if (allowFrameSwitch
)
1358 newWindow
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1360 if (aFlags
& FLAG_RAISE
)
1361 RaiseWindow(newRootWindow
);
1366 nsFocusManager::IsSameOrAncestor(nsPIDOMWindow
* aPossibleAncestor
,
1367 nsPIDOMWindow
* aWindow
)
1369 if (!aWindow
|| !aPossibleAncestor
) {
1373 nsCOMPtr
<nsIDocShellTreeItem
> ancestordsti
= aPossibleAncestor
->GetDocShell();
1374 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= aWindow
->GetDocShell();
1376 if (dsti
== ancestordsti
)
1378 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1379 dsti
->GetParent(getter_AddRefs(parentDsti
));
1380 dsti
.swap(parentDsti
);
1386 already_AddRefed
<nsPIDOMWindow
>
1387 nsFocusManager::GetCommonAncestor(nsPIDOMWindow
* aWindow1
,
1388 nsPIDOMWindow
* aWindow2
)
1390 NS_ENSURE_TRUE(aWindow1
&& aWindow2
, nullptr);
1392 nsCOMPtr
<nsIDocShellTreeItem
> dsti1
= aWindow1
->GetDocShell();
1393 NS_ENSURE_TRUE(dsti1
, nullptr);
1395 nsCOMPtr
<nsIDocShellTreeItem
> dsti2
= aWindow2
->GetDocShell();
1396 NS_ENSURE_TRUE(dsti2
, nullptr);
1398 nsAutoTArray
<nsIDocShellTreeItem
*, 30> parents1
, parents2
;
1400 parents1
.AppendElement(dsti1
);
1401 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti1
;
1402 dsti1
->GetParent(getter_AddRefs(parentDsti1
));
1403 dsti1
.swap(parentDsti1
);
1406 parents2
.AppendElement(dsti2
);
1407 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti2
;
1408 dsti2
->GetParent(getter_AddRefs(parentDsti2
));
1409 dsti2
.swap(parentDsti2
);
1412 uint32_t pos1
= parents1
.Length();
1413 uint32_t pos2
= parents2
.Length();
1414 nsIDocShellTreeItem
* parent
= nullptr;
1416 for (len
= std::min(pos1
, pos2
); len
> 0; --len
) {
1417 nsIDocShellTreeItem
* child1
= parents1
.ElementAt(--pos1
);
1418 nsIDocShellTreeItem
* child2
= parents2
.ElementAt(--pos2
);
1419 if (child1
!= child2
) {
1425 nsCOMPtr
<nsPIDOMWindow
> window
= parent
? parent
->GetWindow() : nullptr;
1426 return window
.forget();
1430 nsFocusManager::AdjustWindowFocus(nsPIDOMWindow
* aWindow
,
1431 bool aCheckPermission
)
1433 bool isVisible
= IsWindowVisible(aWindow
);
1435 nsCOMPtr
<nsPIDOMWindow
> window(aWindow
);
1437 // get the containing <iframe> or equivalent element so that it can be
1439 nsCOMPtr
<Element
> frameElement
= window
->GetFrameElementInternal();
1441 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= window
->GetDocShell();
1444 nsCOMPtr
<nsIDocShellTreeItem
> parentDsti
;
1445 dsti
->GetParent(getter_AddRefs(parentDsti
));
1450 window
= parentDsti
->GetWindow();
1452 // if the parent window is visible but aWindow was not, then we have
1453 // likely moved up and out from a hidden tab to the browser window, or a
1454 // similar such arrangement. Stop adjusting the current nodes.
1455 if (IsWindowVisible(window
) != isVisible
)
1458 // When aCheckPermission is true, we should check whether the caller can
1459 // access the window or not. If it cannot access, we should stop the
1461 if (aCheckPermission
&& !nsContentUtils::CanCallerAccess(window
))
1464 window
->SetFocusedNode(frameElement
);
1470 nsFocusManager::IsWindowVisible(nsPIDOMWindow
* aWindow
)
1472 if (!aWindow
|| aWindow
->IsFrozen())
1475 // Check if the inner window is frozen as well. This can happen when a focus change
1476 // occurs while restoring a previous page.
1477 nsPIDOMWindow
* innerWindow
= aWindow
->GetCurrentInnerWindow();
1478 if (!innerWindow
|| innerWindow
->IsFrozen())
1481 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
1482 nsCOMPtr
<nsIBaseWindow
> baseWin(do_QueryInterface(docShell
));
1486 bool visible
= false;
1487 baseWin
->GetVisibility(&visible
);
1492 nsFocusManager::IsNonFocusableRoot(nsIContent
* aContent
)
1494 NS_PRECONDITION(aContent
, "aContent must not be NULL");
1495 NS_PRECONDITION(aContent
->IsInComposedDoc(), "aContent must be in a document");
1497 // If aContent is in designMode, the root element is not focusable.
1498 // NOTE: in designMode, most elements are not focusable, just the document is
1500 // Also, if aContent is not editable but it isn't in designMode, it's not
1502 // And in userfocusignored context nothing is focusable.
1503 nsIDocument
* doc
= aContent
->GetComposedDoc();
1504 NS_ASSERTION(doc
, "aContent must have current document");
1505 return aContent
== doc
->GetRootElement() &&
1506 (doc
->HasFlag(NODE_IS_EDITABLE
) || !aContent
->IsEditable() ||
1507 nsContentUtils::IsUserFocusIgnored(aContent
));
1511 nsFocusManager::CheckIfFocusable(nsIContent
* aContent
, uint32_t aFlags
)
1516 // this is a special case for some XUL elements where an anonymous child is
1517 // actually focusable and not the element itself.
1518 nsIContent
* redirectedFocus
= GetRedirectedFocus(aContent
);
1519 if (redirectedFocus
)
1520 return CheckIfFocusable(redirectedFocus
, aFlags
);
1522 nsCOMPtr
<nsIDocument
> doc
= aContent
->GetComposedDoc();
1523 // can't focus elements that are not in documents
1525 LOGCONTENT("Cannot focus %s because content not in document", aContent
)
1529 // Make sure that our frames are up to date
1530 doc
->FlushPendingNotifications(Flush_Layout
);
1532 nsIPresShell
*shell
= doc
->GetShell();
1536 // the root content can always be focused,
1537 // except in userfocusignored context.
1538 if (aContent
== doc
->GetRootElement())
1539 return nsContentUtils::IsUserFocusIgnored(aContent
) ? nullptr : aContent
;
1541 // cannot focus content in print preview mode. Only the root can be focused.
1542 nsPresContext
* presContext
= shell
->GetPresContext();
1543 if (presContext
&& presContext
->Type() == nsPresContext::eContext_PrintPreview
) {
1544 LOGCONTENT("Cannot focus %s while in print preview", aContent
)
1548 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
1550 LOGCONTENT("Cannot focus %s as it has no frame", aContent
)
1554 if (aContent
->Tag() == nsGkAtoms::area
&& aContent
->IsHTML()) {
1555 // HTML areas do not have their own frame, and the img frame we get from
1556 // GetPrimaryFrame() is not relevant as to whether it is focusable or
1557 // not, so we have to do all the relevant checks manually for them.
1558 return frame
->IsVisibleConsideringAncestors() &&
1559 aContent
->IsFocusable() ? aContent
: nullptr;
1562 // if this is a child frame content node, check if it is visible and
1563 // call the content node's IsFocusable method instead of the frame's
1564 // IsFocusable method. This skips checking the style system and ensures that
1565 // offscreen browsers can still be focused.
1566 nsIDocument
* subdoc
= doc
->GetSubDocumentFor(aContent
);
1567 if (subdoc
&& IsWindowVisible(subdoc
->GetWindow())) {
1568 const nsStyleUserInterface
* ui
= frame
->StyleUserInterface();
1569 int32_t tabIndex
= (ui
->mUserFocus
== NS_STYLE_USER_FOCUS_IGNORE
||
1570 ui
->mUserFocus
== NS_STYLE_USER_FOCUS_NONE
) ? -1 : 0;
1571 return aContent
->IsFocusable(&tabIndex
, aFlags
& FLAG_BYMOUSE
) ? aContent
: nullptr;
1574 return frame
->IsFocusable(nullptr, aFlags
& FLAG_BYMOUSE
) ? aContent
: nullptr;
1578 nsFocusManager::Blur(nsPIDOMWindow
* aWindowToClear
,
1579 nsPIDOMWindow
* aAncestorWindowToFocus
,
1580 bool aIsLeavingDocument
,
1581 bool aAdjustWidgets
)
1583 LOGFOCUS(("<<Blur begin>>"));
1585 // hold a reference to the focused content, which may be null
1586 nsCOMPtr
<nsIContent
> content
= mFocusedContent
;
1588 if (!content
->IsInComposedDoc()) {
1589 mFocusedContent
= nullptr;
1592 if (content
== mFirstBlurEvent
)
1596 // hold a reference to the focused window
1597 nsCOMPtr
<nsPIDOMWindow
> window
= mFocusedWindow
;
1599 mFocusedContent
= nullptr;
1603 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
1605 mFocusedContent
= nullptr;
1609 // Keep a ref to presShell since dispatching the DOM event may cause
1610 // the document to be destroyed.
1611 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
1613 mFocusedContent
= nullptr;
1617 bool clearFirstBlurEvent
= false;
1618 if (!mFirstBlurEvent
) {
1619 mFirstBlurEvent
= content
;
1620 clearFirstBlurEvent
= true;
1623 nsPresContext
* focusedPresContext
=
1624 mActiveWindow
? presShell
->GetPresContext() : nullptr;
1625 IMEStateManager::OnChangeFocus(focusedPresContext
, nullptr,
1626 GetFocusMoveActionCause(0));
1628 // now adjust the actual focus, by clearing the fields in the focus manager
1629 // and in the window.
1630 mFocusedContent
= nullptr;
1631 bool shouldShowFocusRing
= window
->ShouldShowFocusRing();
1633 aWindowToClear
->SetFocusedNode(nullptr);
1635 LOGCONTENT("Element %s has been blurred", content
.get());
1637 // Don't fire blur event on the root content which isn't editable.
1638 bool sendBlurEvent
=
1639 content
&& content
->IsInComposedDoc() && !IsNonFocusableRoot(content
);
1641 if (sendBlurEvent
) {
1642 NotifyFocusStateChange(content
, shouldShowFocusRing
, false);
1645 // if an object/plug-in/remote browser is being blurred, move the system focus
1646 // to the parent window, otherwise events will still get fired at the plugin.
1647 // But don't do this if we are blurring due to the window being lowered,
1648 // otherwise, the parent window can get raised again.
1649 if (mActiveWindow
) {
1650 nsIFrame
* contentFrame
= content
->GetPrimaryFrame();
1651 nsIObjectFrame
* objectFrame
= do_QueryFrame(contentFrame
);
1652 if (aAdjustWidgets
&& objectFrame
&& !sTestMode
) {
1653 // note that the presshell's widget is being retrieved here, not the one
1654 // for the object frame.
1655 nsViewManager
* vm
= presShell
->GetViewManager();
1657 nsCOMPtr
<nsIWidget
> widget
;
1658 vm
->GetRootWidget(getter_AddRefs(widget
));
1660 widget
->SetFocus(false);
1665 // if the object being blurred is a remote browser, deactivate remote content
1666 if (TabParent
* remote
= TabParent::GetFrom(content
)) {
1667 remote
->Deactivate();
1668 LOGFOCUS(("Remote browser deactivated"));
1673 if (sendBlurEvent
) {
1674 // if there is an active window, update commands. If there isn't an active
1675 // window, then this was a blur caused by the active window being lowered,
1676 // so there is no need to update the commands
1678 window
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1680 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
,
1681 content
->GetComposedDoc(), content
, 1, false);
1684 // if we are leaving the document or the window was lowered, make the caret
1686 if (aIsLeavingDocument
|| !mActiveWindow
) {
1687 SetCaretVisible(presShell
, false, nullptr);
1690 nsRefPtr
<SelectionCarets
> selectionCarets
= presShell
->GetSelectionCarets();
1691 if (selectionCarets
) {
1692 selectionCarets
->NotifyBlur(aIsLeavingDocument
|| !mActiveWindow
);
1695 // at this point, it is expected that this window will be still be
1696 // focused, but the focused content will be null, as it was cleared before
1697 // the event. If this isn't the case, then something else was focused during
1698 // the blur event above and we should just return. However, if
1699 // aIsLeavingDocument is set, a new document is desired, so make sure to
1700 // blur the document and window.
1701 if (mFocusedWindow
!= window
||
1702 (mFocusedContent
!= nullptr && !aIsLeavingDocument
)) {
1705 else if (aIsLeavingDocument
) {
1706 window
->TakeFocus(false, 0);
1708 // clear the focus so that the ancestor frame hierarchy is in the correct
1709 // state. Pass true because aAncestorWindowToFocus is thought to be
1710 // focused at this point.
1711 if (aAncestorWindowToFocus
)
1712 aAncestorWindowToFocus
->SetFocusedNode(nullptr, 0, true);
1714 SetFocusedWindowInternal(nullptr);
1715 mFocusedContent
= nullptr;
1717 // pass 1 for the focus method when calling SendFocusOrBlurEvent just so
1718 // that the check is made for suppressed documents. Check to ensure that
1719 // the document isn't null in case someone closed it during the blur above
1720 nsIDocument
* doc
= window
->GetExtantDoc();
1722 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
, doc
, doc
, 1, false);
1723 if (mFocusedWindow
== nullptr)
1724 SendFocusOrBlurEvent(NS_BLUR_CONTENT
, presShell
, doc
, window
, 1, false);
1726 // check if a different window was focused
1727 result
= (mFocusedWindow
== nullptr && mActiveWindow
);
1729 else if (mActiveWindow
) {
1730 // Otherwise, the blur of the element without blurring the document
1731 // occurred normally. Call UpdateCaret to redisplay the caret at the right
1732 // location within the document. This is needed to ensure that the caret
1733 // used for caret browsing is made visible again when an input field is
1735 UpdateCaret(false, true, nullptr);
1738 if (clearFirstBlurEvent
)
1739 mFirstBlurEvent
= nullptr;
1745 nsFocusManager::Focus(nsPIDOMWindow
* aWindow
,
1746 nsIContent
* aContent
,
1748 bool aIsNewDocument
,
1751 bool aAdjustWidgets
)
1753 LOGFOCUS(("<<Focus begin>>"));
1758 if (aContent
&& (aContent
== mFirstFocusEvent
|| aContent
== mFirstBlurEvent
))
1761 // Keep a reference to the presShell since dispatching the DOM event may
1762 // cause the document to be destroyed.
1763 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
1767 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
1771 // If the focus actually changed, set the focus method (mouse, keyboard, etc).
1772 // Otherwise, just get the current focus method and use that. This ensures
1773 // that the method is set during the document and window focus events.
1774 uint32_t focusMethod
= aFocusChanged
? aFlags
& FOCUSMETHODANDRING_MASK
:
1775 aWindow
->GetFocusMethod() | (aFlags
& FLAG_SHOWRING
);
1777 if (!IsWindowVisible(aWindow
)) {
1778 // if the window isn't visible, for instance because it is a hidden tab,
1779 // update the current focus and scroll it into view but don't do anything else
1780 if (CheckIfFocusable(aContent
, aFlags
)) {
1781 aWindow
->SetFocusedNode(aContent
, focusMethod
);
1783 ScrollIntoView(presShell
, aContent
, aFlags
);
1788 bool clearFirstFocusEvent
= false;
1789 if (!mFirstFocusEvent
) {
1790 mFirstFocusEvent
= aContent
;
1791 clearFirstFocusEvent
= true;
1795 LOGCONTENT("Element %s has been focused", aContent
);
1797 if (PR_LOG_TEST(gFocusLog
, PR_LOG_DEBUG
)) {
1798 nsIDocument
* docm
= aWindow
->GetExtantDoc();
1800 LOGCONTENT(" from %s", docm
->GetRootElement());
1802 LOGFOCUS((" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x]",
1803 aIsNewDocument
, aFocusChanged
, aWindowRaised
, aFlags
));
1807 if (aIsNewDocument
) {
1808 // if this is a new document, update the parent chain of frames so that
1809 // focus can be traversed from the top level down to the newly focused
1811 AdjustWindowFocus(aWindow
, false);
1813 // Update the window touch registration to reflect the state of
1814 // the new document that got focus
1815 aWindow
->UpdateTouchState();
1818 // indicate that the window has taken focus.
1819 if (aWindow
->TakeFocus(true, focusMethod
))
1820 aIsNewDocument
= true;
1822 SetFocusedWindowInternal(aWindow
);
1824 // Update the system focus by focusing the root widget. But avoid this
1825 // if 1) aAdjustWidgets is false or 2) aContent is a plugin that has its
1826 // own widget and is either already focused or is about to be focused.
1827 nsCOMPtr
<nsIWidget
> objectFrameWidget
;
1829 nsIFrame
* contentFrame
= aContent
->GetPrimaryFrame();
1830 nsIObjectFrame
* objectFrame
= do_QueryFrame(contentFrame
);
1832 objectFrameWidget
= objectFrame
->GetWidget();
1834 if (aAdjustWidgets
&& !objectFrameWidget
&& !sTestMode
) {
1835 nsViewManager
* vm
= presShell
->GetViewManager();
1837 nsCOMPtr
<nsIWidget
> widget
;
1838 vm
->GetRootWidget(getter_AddRefs(widget
));
1840 widget
->SetFocus(false);
1844 // if switching to a new document, first fire the focus event on the
1845 // document and then the window.
1846 if (aIsNewDocument
) {
1847 nsIDocument
* doc
= aWindow
->GetExtantDoc();
1848 // The focus change should be notified to IMEStateManager from here if
1849 // the focused content is a designMode editor since any content won't
1850 // receive focus event.
1851 if (doc
&& doc
->HasFlag(NODE_IS_EDITABLE
)) {
1852 IMEStateManager::OnChangeFocus(presShell
->GetPresContext(), nullptr,
1853 GetFocusMoveActionCause(aFlags
));
1856 SendFocusOrBlurEvent(NS_FOCUS_CONTENT
, presShell
, doc
,
1857 doc
, aFlags
& FOCUSMETHOD_MASK
, aWindowRaised
);
1858 if (mFocusedWindow
== aWindow
&& mFocusedContent
== nullptr)
1859 SendFocusOrBlurEvent(NS_FOCUS_CONTENT
, presShell
, doc
,
1860 aWindow
, aFlags
& FOCUSMETHOD_MASK
, aWindowRaised
);
1863 // check to ensure that the element is still focusable, and that nothing
1864 // else was focused during the events above.
1865 if (CheckIfFocusable(aContent
, aFlags
) &&
1866 mFocusedWindow
== aWindow
&& mFocusedContent
== nullptr) {
1867 mFocusedContent
= aContent
;
1869 nsIContent
* focusedNode
= aWindow
->GetFocusedNode();
1870 bool isRefocus
= focusedNode
&& focusedNode
->IsEqualNode(aContent
);
1872 aWindow
->SetFocusedNode(aContent
, focusMethod
);
1874 bool sendFocusEvent
=
1875 aContent
&& aContent
->IsInComposedDoc() && !IsNonFocusableRoot(aContent
);
1876 nsPresContext
* presContext
= presShell
->GetPresContext();
1877 if (sendFocusEvent
) {
1878 // if the focused element changed, scroll it into view
1880 ScrollIntoView(presShell
, aContent
, aFlags
);
1882 NotifyFocusStateChange(aContent
, aWindow
->ShouldShowFocusRing(), true);
1884 // if this is an object/plug-in/remote browser, focus its widget. Note that we might
1885 // no longer be in the same document, due to the events we fired above when
1887 if (presShell
->GetDocument() == aContent
->GetComposedDoc()) {
1888 if (aAdjustWidgets
&& objectFrameWidget
&& !sTestMode
)
1889 objectFrameWidget
->SetFocus(false);
1891 // if the object being focused is a remote browser, activate remote content
1892 if (TabParent
* remote
= TabParent::GetFrom(aContent
)) {
1894 LOGFOCUS(("Remote browser activated"));
1898 IMEStateManager::OnChangeFocus(presContext
, aContent
,
1899 GetFocusMoveActionCause(aFlags
));
1901 // as long as this focus wasn't because a window was raised, update the
1903 // XXXndeakin P2 someone could adjust the focus during the update
1905 aWindow
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1907 SendFocusOrBlurEvent(NS_FOCUS_CONTENT
, presShell
,
1908 aContent
->GetComposedDoc(),
1909 aContent
, aFlags
& FOCUSMETHOD_MASK
,
1910 aWindowRaised
, isRefocus
);
1912 IMEStateManager::OnChangeFocus(presContext
, nullptr,
1913 GetFocusMoveActionCause(aFlags
));
1914 if (!aWindowRaised
) {
1915 aWindow
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1920 // If the window focus event (fired above when aIsNewDocument) caused
1921 // the plugin not to be focusable, update the system focus by focusing
1923 if (aAdjustWidgets
&& objectFrameWidget
&&
1924 mFocusedWindow
== aWindow
&& mFocusedContent
== nullptr &&
1926 nsViewManager
* vm
= presShell
->GetViewManager();
1928 nsCOMPtr
<nsIWidget
> widget
;
1929 vm
->GetRootWidget(getter_AddRefs(widget
));
1931 widget
->SetFocus(false);
1935 nsPresContext
* presContext
= presShell
->GetPresContext();
1936 IMEStateManager::OnChangeFocus(presContext
, nullptr,
1937 GetFocusMoveActionCause(aFlags
));
1940 aWindow
->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
1943 // update the caret visibility and position to match the newly focused
1944 // element. However, don't update the position if this was a focus due to a
1945 // mouse click as the selection code would already have moved the caret as
1946 // needed. If this is a different document than was focused before, also
1947 // update the caret's visibility. If this is the same document, the caret
1948 // visibility should be the same as before so there is no need to update it.
1949 if (mFocusedContent
== aContent
)
1950 UpdateCaret(aFocusChanged
&& !(aFlags
& FLAG_BYMOUSE
), aIsNewDocument
,
1953 if (clearFirstFocusEvent
)
1954 mFirstFocusEvent
= nullptr;
1957 class FocusBlurEvent
: public nsRunnable
1960 FocusBlurEvent(nsISupports
* aTarget
, uint32_t aType
,
1961 nsPresContext
* aContext
, bool aWindowRaised
,
1963 : mTarget(aTarget
), mType(aType
), mContext(aContext
),
1964 mWindowRaised(aWindowRaised
), mIsRefocus(aIsRefocus
) {}
1968 InternalFocusEvent
event(true, mType
);
1969 event
.mFlags
.mBubbles
= false;
1970 event
.fromRaise
= mWindowRaised
;
1971 event
.isRefocus
= mIsRefocus
;
1972 return EventDispatcher::Dispatch(mTarget
, mContext
, &event
);
1975 nsCOMPtr
<nsISupports
> mTarget
;
1977 nsRefPtr
<nsPresContext
> mContext
;
1983 nsFocusManager::SendFocusOrBlurEvent(uint32_t aType
,
1984 nsIPresShell
* aPresShell
,
1985 nsIDocument
* aDocument
,
1986 nsISupports
* aTarget
,
1987 uint32_t aFocusMethod
,
1991 NS_ASSERTION(aType
== NS_FOCUS_CONTENT
|| aType
== NS_BLUR_CONTENT
,
1992 "Wrong event type for SendFocusOrBlurEvent");
1994 nsCOMPtr
<EventTarget
> eventTarget
= do_QueryInterface(aTarget
);
1996 nsCOMPtr
<nsINode
> n
= do_QueryInterface(aTarget
);
1998 nsCOMPtr
<nsPIDOMWindow
> win
= do_QueryInterface(aTarget
);
1999 n
= win
? win
->GetExtantDoc() : nullptr;
2001 bool dontDispatchEvent
= n
&& nsContentUtils::IsUserFocusIgnored(n
);
2003 // for focus events, if this event was from a mouse or key and event
2004 // handling on the document is suppressed, queue the event and fire it
2005 // later. For blur events, a non-zero value would be set for aFocusMethod.
2006 if (aFocusMethod
&& !dontDispatchEvent
&&
2007 aDocument
&& aDocument
->EventHandlingSuppressed()) {
2008 // aFlags is always 0 when aWindowRaised is true so this won't be called
2009 // on a window raise.
2010 NS_ASSERTION(!aWindowRaised
, "aWindowRaised should not be set");
2012 for (uint32_t i
= mDelayedBlurFocusEvents
.Length(); i
> 0; --i
) {
2013 // if this event was already queued, remove it and append it to the end
2014 if (mDelayedBlurFocusEvents
[i
- 1].mType
== aType
&&
2015 mDelayedBlurFocusEvents
[i
- 1].mPresShell
== aPresShell
&&
2016 mDelayedBlurFocusEvents
[i
- 1].mDocument
== aDocument
&&
2017 mDelayedBlurFocusEvents
[i
- 1].mTarget
== eventTarget
) {
2018 mDelayedBlurFocusEvents
.RemoveElementAt(i
- 1);
2022 mDelayedBlurFocusEvents
.AppendElement(
2023 nsDelayedBlurOrFocusEvent(aType
, aPresShell
, aDocument
, eventTarget
));
2027 #ifdef ACCESSIBILITY
2028 nsAccessibilityService
* accService
= GetAccService();
2030 if (aType
== NS_FOCUS_CONTENT
)
2031 accService
->NotifyOfDOMFocus(aTarget
);
2033 accService
->NotifyOfDOMBlur(aTarget
);
2037 if (!dontDispatchEvent
) {
2038 nsContentUtils::AddScriptRunner(
2039 new FocusBlurEvent(aTarget
, aType
, aPresShell
->GetPresContext(),
2040 aWindowRaised
, aIsRefocus
));
2045 nsFocusManager::ScrollIntoView(nsIPresShell
* aPresShell
,
2046 nsIContent
* aContent
,
2049 // if the noscroll flag isn't set, scroll the newly focused element into view
2050 if (!(aFlags
& FLAG_NOSCROLL
))
2051 aPresShell
->ScrollContentIntoView(aContent
,
2052 nsIPresShell::ScrollAxis(
2053 nsIPresShell::SCROLL_MINIMUM
,
2054 nsIPresShell::SCROLL_IF_NOT_VISIBLE
),
2055 nsIPresShell::ScrollAxis(
2056 nsIPresShell::SCROLL_MINIMUM
,
2057 nsIPresShell::SCROLL_IF_NOT_VISIBLE
),
2058 nsIPresShell::SCROLL_OVERFLOW_HIDDEN
);
2063 nsFocusManager::RaiseWindow(nsPIDOMWindow
* aWindow
)
2065 // don't raise windows that are already raised or are in the process of
2067 if (!aWindow
|| aWindow
== mActiveWindow
|| aWindow
== mWindowBeingLowered
)
2071 // In test mode, emulate the existing window being lowered and the new
2072 // window being raised.
2074 WindowLowered(mActiveWindow
);
2075 WindowRaised(aWindow
);
2080 // Windows would rather we focus the child widget, otherwise, the toplevel
2081 // widget will always end up being focused. Fortunately, focusing the child
2082 // widget will also have the effect of raising the window this widget is in.
2083 // But on other platforms, we can just focus the toplevel widget to raise
2085 nsCOMPtr
<nsPIDOMWindow
> childWindow
;
2086 GetFocusedDescendant(aWindow
, true, getter_AddRefs(childWindow
));
2088 childWindow
= aWindow
;
2090 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
2094 nsCOMPtr
<nsIPresShell
> presShell
= docShell
->GetPresShell();
2098 nsViewManager
* vm
= presShell
->GetViewManager();
2100 nsCOMPtr
<nsIWidget
> widget
;
2101 vm
->GetRootWidget(getter_AddRefs(widget
));
2103 widget
->SetFocus(true);
2106 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
=
2107 do_QueryInterface(aWindow
->GetDocShell());
2108 if (treeOwnerAsWin
) {
2109 nsCOMPtr
<nsIWidget
> widget
;
2110 treeOwnerAsWin
->GetMainWidget(getter_AddRefs(widget
));
2112 widget
->SetFocus(true);
2118 nsFocusManager::UpdateCaretForCaretBrowsingMode()
2120 UpdateCaret(false, true, mFocusedContent
);
2124 nsFocusManager::UpdateCaret(bool aMoveCaretToFocus
,
2125 bool aUpdateVisibility
,
2126 nsIContent
* aContent
)
2128 LOGFOCUS(("Update Caret: %d %d", aMoveCaretToFocus
, aUpdateVisibility
));
2130 if (!mFocusedWindow
)
2133 // this is called when a document is focused or when the caretbrowsing
2134 // preference is changed
2135 nsCOMPtr
<nsIDocShell
> focusedDocShell
= mFocusedWindow
->GetDocShell();
2136 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= do_QueryInterface(focusedDocShell
);
2140 if (dsti
->ItemType() == nsIDocShellTreeItem::typeChrome
) {
2141 return; // Never browse with caret in chrome
2144 bool browseWithCaret
=
2145 Preferences::GetBool("accessibility.browsewithcaret");
2147 nsCOMPtr
<nsIPresShell
> presShell
= focusedDocShell
->GetPresShell();
2151 // If this is an editable document which isn't contentEditable, or a
2152 // contentEditable document and the node to focus is contentEditable,
2153 // return, so that we don't mess with caret visibility.
2154 bool isEditable
= false;
2155 focusedDocShell
->GetEditable(&isEditable
);
2158 nsCOMPtr
<nsIHTMLDocument
> doc
=
2159 do_QueryInterface(presShell
->GetDocument());
2161 bool isContentEditableDoc
=
2162 doc
&& doc
->GetEditingState() == nsIHTMLDocument::eContentEditable
;
2164 bool isFocusEditable
=
2165 aContent
&& aContent
->HasFlag(NODE_IS_EDITABLE
);
2166 if (!isContentEditableDoc
|| isFocusEditable
)
2170 if (!isEditable
&& aMoveCaretToFocus
)
2171 MoveCaretToFocus(presShell
, aContent
);
2173 if (!aUpdateVisibility
)
2176 // XXXndeakin this doesn't seem right. It should be checking for this only
2177 // on the nearest ancestor frame which is a chrome frame. But this is
2178 // what the existing code does, so just leave it for now.
2179 if (!browseWithCaret
) {
2180 nsCOMPtr
<Element
> docElement
=
2181 mFocusedWindow
->GetFrameElementInternal();
2183 browseWithCaret
= docElement
->AttrValueIs(kNameSpaceID_None
,
2184 nsGkAtoms::showcaret
,
2185 NS_LITERAL_STRING("true"),
2189 SetCaretVisible(presShell
, browseWithCaret
, aContent
);
2193 nsFocusManager::MoveCaretToFocus(nsIPresShell
* aPresShell
, nsIContent
* aContent
)
2195 // domDoc is a document interface we can create a range with
2196 nsCOMPtr
<nsIDOMDocument
> domDoc
= do_QueryInterface(aPresShell
->GetDocument());
2198 nsRefPtr
<nsFrameSelection
> frameSelection
= aPresShell
->FrameSelection();
2199 nsCOMPtr
<nsISelection
> domSelection
= frameSelection
->
2200 GetSelection(nsISelectionController::SELECTION_NORMAL
);
2202 nsCOMPtr
<nsIDOMNode
> currentFocusNode(do_QueryInterface(aContent
));
2203 // First clear the selection. This way, if there is no currently focused
2204 // content, the selection will just be cleared.
2205 domSelection
->RemoveAllRanges();
2206 if (currentFocusNode
) {
2207 nsCOMPtr
<nsIDOMRange
> newRange
;
2208 nsresult rv
= domDoc
->CreateRange(getter_AddRefs(newRange
));
2209 if (NS_SUCCEEDED(rv
)) {
2210 // Set the range to the start of the currently focused node
2211 // Make sure it's collapsed
2212 newRange
->SelectNodeContents(currentFocusNode
);
2213 nsCOMPtr
<nsIDOMNode
> firstChild
;
2214 currentFocusNode
->GetFirstChild(getter_AddRefs(firstChild
));
2216 aContent
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
)) {
2217 // If current focus node is a leaf, set range to before the
2218 // node by using the parent as a container.
2219 // This prevents it from appearing as selected.
2220 newRange
->SetStartBefore(currentFocusNode
);
2221 newRange
->SetEndBefore(currentFocusNode
);
2223 domSelection
->AddRange(newRange
);
2224 domSelection
->CollapseToStart();
2232 nsFocusManager::SetCaretVisible(nsIPresShell
* aPresShell
,
2234 nsIContent
* aContent
)
2236 // When browsing with caret, make sure caret is visible after new focus
2237 // Return early if there is no caret. This can happen for the testcase
2238 // for bug 308025 where a window is closed in a blur handler.
2239 nsRefPtr
<nsCaret
> caret
= aPresShell
->GetCaret();
2243 bool caretVisible
= caret
->IsVisible();
2244 if (!aVisible
&& !caretVisible
)
2247 nsRefPtr
<nsFrameSelection
> frameSelection
;
2249 NS_ASSERTION(aContent
->GetComposedDoc() == aPresShell
->GetDocument(),
2251 nsIFrame
*focusFrame
= aContent
->GetPrimaryFrame();
2253 frameSelection
= focusFrame
->GetFrameSelection();
2256 nsRefPtr
<nsFrameSelection
> docFrameSelection
= aPresShell
->FrameSelection();
2258 if (docFrameSelection
&& caret
&&
2259 (frameSelection
== docFrameSelection
|| !aContent
)) {
2260 nsISelection
* domSelection
= docFrameSelection
->
2261 GetSelection(nsISelectionController::SELECTION_NORMAL
);
2263 nsCOMPtr
<nsISelectionController
> selCon(do_QueryInterface(aPresShell
));
2265 return NS_ERROR_FAILURE
;
2267 // First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
2268 selCon
->SetCaretEnabled(false);
2270 // Caret must blink on non-editable elements
2271 caret
->SetIgnoreUserModify(true);
2272 // Tell the caret which selection to use
2273 caret
->SetSelection(domSelection
);
2275 // In content, we need to set the caret. The only special case is edit
2276 // fields, which have a different frame selection from the document.
2277 // They will take care of making the caret visible themselves.
2279 selCon
->SetCaretReadOnly(false);
2280 selCon
->SetCaretEnabled(aVisible
);
2288 nsFocusManager::GetSelectionLocation(nsIDocument
* aDocument
,
2289 nsIPresShell
* aPresShell
,
2290 nsIContent
**aStartContent
,
2291 nsIContent
**aEndContent
)
2293 *aStartContent
= *aEndContent
= nullptr;
2294 nsresult rv
= NS_ERROR_FAILURE
;
2296 nsPresContext
* presContext
= aPresShell
->GetPresContext();
2297 NS_ASSERTION(presContext
, "mPresContent is null!!");
2299 nsRefPtr
<nsFrameSelection
> frameSelection
= aPresShell
->FrameSelection();
2301 nsCOMPtr
<nsISelection
> domSelection
;
2302 if (frameSelection
) {
2303 domSelection
= frameSelection
->
2304 GetSelection(nsISelectionController::SELECTION_NORMAL
);
2307 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
2308 bool isCollapsed
= false;
2309 nsCOMPtr
<nsIContent
> startContent
, endContent
;
2310 int32_t startOffset
= 0;
2312 domSelection
->GetIsCollapsed(&isCollapsed
);
2313 nsCOMPtr
<nsIDOMRange
> domRange
;
2314 rv
= domSelection
->GetRangeAt(0, getter_AddRefs(domRange
));
2316 domRange
->GetStartContainer(getter_AddRefs(startNode
));
2317 domRange
->GetEndContainer(getter_AddRefs(endNode
));
2318 domRange
->GetStartOffset(&startOffset
);
2320 nsIContent
*childContent
= nullptr;
2322 startContent
= do_QueryInterface(startNode
);
2323 if (startContent
&& startContent
->IsElement()) {
2324 NS_ASSERTION(startOffset
>= 0, "Start offset cannot be negative");
2325 childContent
= startContent
->GetChildAt(startOffset
);
2327 startContent
= childContent
;
2331 endContent
= do_QueryInterface(endNode
);
2332 if (endContent
&& endContent
->IsElement()) {
2333 int32_t endOffset
= 0;
2334 domRange
->GetEndOffset(&endOffset
);
2335 NS_ASSERTION(endOffset
>= 0, "End offset cannot be negative");
2336 childContent
= endContent
->GetChildAt(endOffset
);
2338 endContent
= childContent
;
2344 rv
= NS_ERROR_INVALID_ARG
;
2347 nsIFrame
*startFrame
= nullptr;
2349 startFrame
= startContent
->GetPrimaryFrame();
2351 // Next check to see if our caret is at the very end of a node
2352 // If so, the caret is actually sitting in front of the next
2353 // logical frame's primary node - so for this case we need to
2354 // change caretContent to that node.
2356 if (startContent
->NodeType() == nsIDOMNode::TEXT_NODE
) {
2357 nsAutoString nodeValue
;
2358 startContent
->AppendTextTo(nodeValue
);
2360 bool isFormControl
=
2361 startContent
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
);
2363 if (nodeValue
.Length() == (uint32_t)startOffset
&& !isFormControl
&&
2364 startContent
!= aDocument
->GetRootElement()) {
2365 // Yes, indeed we were at the end of the last node
2366 nsCOMPtr
<nsIFrameEnumerator
> frameTraversal
;
2367 nsresult rv
= NS_NewFrameTraversal(getter_AddRefs(frameTraversal
),
2368 presContext
, startFrame
,
2371 false, // aLockInScrollView
2374 NS_ENSURE_SUCCESS(rv
, rv
);
2376 nsIFrame
*newCaretFrame
= nullptr;
2377 nsCOMPtr
<nsIContent
> newCaretContent
= startContent
;
2378 bool endOfSelectionInStartNode(startContent
== endContent
);
2380 // Continue getting the next frame until the primary content for the frame
2381 // we are on changes - we don't want to be stuck in the same place
2382 frameTraversal
->Next();
2383 newCaretFrame
= static_cast<nsIFrame
*>(frameTraversal
->CurrentItem());
2384 if (nullptr == newCaretFrame
)
2386 newCaretContent
= newCaretFrame
->GetContent();
2387 } while (!newCaretContent
|| newCaretContent
== startContent
);
2389 if (newCaretFrame
&& newCaretContent
) {
2390 // If the caret is exactly at the same position of the new frame,
2391 // then we can use the newCaretFrame and newCaretContent for our position
2393 nsIFrame
*frame
= nsCaret::GetGeometry(domSelection
, &caretRect
);
2395 nsPoint caretWidgetOffset
;
2396 nsIWidget
*widget
= frame
->GetNearestWidget(caretWidgetOffset
);
2397 caretRect
.MoveBy(caretWidgetOffset
);
2398 nsPoint newCaretOffset
;
2399 nsIWidget
*newCaretWidget
= newCaretFrame
->GetNearestWidget(newCaretOffset
);
2400 if (widget
== newCaretWidget
&& caretRect
.y
== newCaretOffset
.y
&&
2401 caretRect
.x
== newCaretOffset
.x
) {
2402 // The caret is at the start of the new element.
2403 startFrame
= newCaretFrame
;
2404 startContent
= newCaretContent
;
2405 if (endOfSelectionInStartNode
) {
2406 endContent
= newCaretContent
; // Ensure end of selection is not before start
2416 *aStartContent
= startContent
;
2417 *aEndContent
= endContent
;
2418 NS_IF_ADDREF(*aStartContent
);
2419 NS_IF_ADDREF(*aEndContent
);
2425 nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow
* aWindow
,
2426 nsIContent
* aStartContent
,
2427 int32_t aType
, bool aNoParentTraversal
,
2428 nsIContent
** aNextContent
)
2430 *aNextContent
= nullptr;
2432 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
2436 nsCOMPtr
<nsIContent
> startContent
= aStartContent
;
2437 if (!startContent
&& aType
!= MOVEFOCUS_CARET
) {
2438 if (aType
== MOVEFOCUS_FORWARDDOC
|| aType
== MOVEFOCUS_BACKWARDDOC
) {
2439 // When moving between documents, make sure to get the right
2440 // starting content in a descendant.
2441 nsCOMPtr
<nsPIDOMWindow
> focusedWindow
;
2442 startContent
= GetFocusedDescendant(aWindow
, true, getter_AddRefs(focusedWindow
));
2445 startContent
= aWindow
->GetFocusedNode();
2449 nsCOMPtr
<nsIDocument
> doc
;
2451 doc
= startContent
->GetComposedDoc();
2453 doc
= aWindow
->GetExtantDoc();
2457 LookAndFeel::GetInt(LookAndFeel::eIntID_TabFocusModel
,
2458 &nsIContent::sTabFocusModel
);
2460 if (aType
== MOVEFOCUS_ROOT
) {
2461 NS_IF_ADDREF(*aNextContent
= GetRootForFocus(aWindow
, doc
, false, false));
2464 if (aType
== MOVEFOCUS_FORWARDDOC
) {
2465 NS_IF_ADDREF(*aNextContent
= GetNextTabbableDocument(startContent
, true));
2468 if (aType
== MOVEFOCUS_BACKWARDDOC
) {
2469 NS_IF_ADDREF(*aNextContent
= GetNextTabbableDocument(startContent
, false));
2473 nsIContent
* rootContent
= doc
->GetRootElement();
2474 NS_ENSURE_TRUE(rootContent
, NS_OK
);
2476 nsIPresShell
*presShell
= doc
->GetShell();
2477 NS_ENSURE_TRUE(presShell
, NS_OK
);
2479 if (aType
== MOVEFOCUS_FIRST
) {
2481 startContent
= rootContent
;
2482 return GetNextTabbableContent(presShell
, startContent
,
2483 nullptr, startContent
,
2484 true, 1, false, aNextContent
);
2486 if (aType
== MOVEFOCUS_LAST
) {
2488 startContent
= rootContent
;
2489 return GetNextTabbableContent(presShell
, startContent
,
2490 nullptr, startContent
,
2491 false, 0, false, aNextContent
);
2494 bool forward
= (aType
== MOVEFOCUS_FORWARD
|| aType
== MOVEFOCUS_CARET
);
2495 bool doNavigation
= true;
2496 bool ignoreTabIndex
= false;
2497 // when a popup is open, we want to ensure that tab navigation occurs only
2498 // within the most recently opened panel. If a popup is open, its frame will
2499 // be stored in popupFrame.
2500 nsIFrame
* popupFrame
= nullptr;
2502 int32_t tabIndex
= forward
? 1 : 0;
2504 nsIFrame
* frame
= startContent
->GetPrimaryFrame();
2505 if (startContent
->Tag() == nsGkAtoms::area
&&
2506 startContent
->IsHTML())
2507 startContent
->IsFocusable(&tabIndex
);
2509 frame
->IsFocusable(&tabIndex
, 0);
2511 startContent
->IsFocusable(&tabIndex
);
2513 // if the current element isn't tabbable, ignore the tabindex and just
2514 // look for the next element. The root content won't have a tabindex
2515 // so just treat this as the beginning of the tab order.
2518 if (startContent
!= rootContent
)
2519 ignoreTabIndex
= true;
2522 // check if the focus is currently inside a popup. Elements such as the
2523 // autocomplete widget use the noautofocus attribute to allow the focus to
2524 // remain outside the popup when it is opened.
2526 popupFrame
= nsLayoutUtils::GetClosestFrameOfType(frame
,
2527 nsGkAtoms::menuPopupFrame
);
2531 // Don't navigate outside of a popup, so pretend that the
2532 // root content is the popup itself
2533 rootContent
= popupFrame
->GetContent();
2534 NS_ASSERTION(rootContent
, "Popup frame doesn't have a content node");
2536 else if (!forward
) {
2537 // If focus moves backward and when current focused node is root
2538 // content or <body> element which is editable by contenteditable
2539 // attribute, focus should move to its parent document.
2540 if (startContent
== rootContent
) {
2541 doNavigation
= false;
2543 nsIDocument
* doc
= startContent
->GetComposedDoc();
2545 nsLayoutUtils::GetEditableRootContentByContentEditable(doc
)) {
2546 doNavigation
= false;
2553 if (aType
!= MOVEFOCUS_CARET
) {
2554 // if there is no focus, yet a panel is open, focus the first item in
2556 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
2558 popupFrame
= pm
->GetTopPopup(ePopupTypePanel
);
2562 rootContent
= popupFrame
->GetContent();
2563 NS_ASSERTION(rootContent
, "Popup frame doesn't have a content node");
2564 startContent
= rootContent
;
2567 // Otherwise, for content shells, start from the location of the caret.
2568 if (docShell
->ItemType() != nsIDocShellTreeItem::typeChrome
) {
2569 nsCOMPtr
<nsIContent
> endSelectionContent
;
2570 GetSelectionLocation(doc
, presShell
,
2571 getter_AddRefs(startContent
),
2572 getter_AddRefs(endSelectionContent
));
2573 // If the selection is on the rootContent, then there is no selection
2574 if (startContent
== rootContent
) {
2575 startContent
= nullptr;
2578 if (aType
== MOVEFOCUS_CARET
) {
2579 // GetFocusInSelection finds a focusable link near the caret.
2580 // If there is no start content though, don't do this to avoid
2581 // focusing something unexpected.
2583 GetFocusInSelection(aWindow
, startContent
,
2584 endSelectionContent
, aNextContent
);
2590 // when starting from a selection, we always want to find the next or
2591 // previous element in the document. So the tabindex on elements
2592 // should be ignored.
2593 ignoreTabIndex
= true;
2597 if (!startContent
) {
2598 // otherwise, just use the root content as the starting point
2599 startContent
= rootContent
;
2600 NS_ENSURE_TRUE(startContent
, NS_OK
);
2605 NS_ASSERTION(startContent
, "starting content not set");
2607 // keep a reference to the starting content. If we find that again, it means
2608 // we've iterated around completely and we don't want to adjust the focus.
2609 // The skipOriginalContentCheck will be set to true only for the first time
2610 // GetNextTabbableContent is called. This ensures that we don't break out
2611 // when nothing is focused to start with. Specifically,
2612 // GetNextTabbableContent first checks the root content -- which happens to
2613 // be the same as the start content -- when nothing is focused and tabbing
2614 // forward. Without skipOriginalContentCheck set to true, we'd end up
2615 // returning right away and focusing nothing. Luckily, GetNextTabbableContent
2616 // will never wrap around on its own, and can only return the original
2617 // content when it is called a second time or later.
2618 bool skipOriginalContentCheck
= true;
2619 nsIContent
* originalStartContent
= startContent
;
2621 LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent
.get());
2622 LOGFOCUSNAVIGATION((" Tabindex: %d Ignore: %d", tabIndex
, ignoreTabIndex
));
2626 nsCOMPtr
<nsIContent
> nextFocus
;
2627 nsresult rv
= GetNextTabbableContent(presShell
, rootContent
,
2628 skipOriginalContentCheck
? nullptr : originalStartContent
,
2629 startContent
, forward
,
2630 tabIndex
, ignoreTabIndex
,
2631 getter_AddRefs(nextFocus
));
2632 NS_ENSURE_SUCCESS(rv
, rv
);
2634 // found a content node to focus.
2636 LOGCONTENTNAVIGATION("Next Content: %s", nextFocus
.get());
2638 // as long as the found node was not the same as the starting node,
2639 // set it as the return value.
2640 if (nextFocus
!= originalStartContent
)
2641 NS_ADDREF(*aNextContent
= nextFocus
);
2646 // in a popup, so start again from the beginning of the popup. However,
2647 // if we already started at the beginning, then there isn't anything to
2648 // focus, so just return
2649 if (startContent
!= rootContent
) {
2650 startContent
= rootContent
;
2651 tabIndex
= forward
? 1 : 0;
2658 doNavigation
= true;
2659 skipOriginalContentCheck
= false;
2660 ignoreTabIndex
= false;
2662 if (aNoParentTraversal
) {
2663 if (startContent
== rootContent
)
2666 startContent
= rootContent
;
2667 tabIndex
= forward
? 1 : 0;
2671 // reached the beginning or end of the document. Traverse up to the parent
2672 // document and try again.
2673 nsCOMPtr
<nsIDocShellTreeItem
> docShellParent
;
2674 docShell
->GetParent(getter_AddRefs(docShellParent
));
2675 if (docShellParent
) {
2676 // move up to the parent shell and try again from there.
2678 // first, get the frame element this window is inside.
2679 nsCOMPtr
<nsPIDOMWindow
> piWindow
= docShell
->GetWindow();
2680 NS_ENSURE_TRUE(piWindow
, NS_ERROR_FAILURE
);
2682 // Next, retrieve the parent docshell, document and presshell.
2683 docShell
= do_QueryInterface(docShellParent
);
2684 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
2686 nsCOMPtr
<nsPIDOMWindow
> piParentWindow
= docShellParent
->GetWindow();
2687 NS_ENSURE_TRUE(piParentWindow
, NS_ERROR_FAILURE
);
2688 doc
= piParentWindow
->GetExtantDoc();
2689 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
2691 presShell
= doc
->GetShell();
2693 rootContent
= doc
->GetRootElement();
2694 startContent
= piWindow
->GetFrameElementInternal();
2696 nsIFrame
* frame
= startContent
->GetPrimaryFrame();
2700 frame
->IsFocusable(&tabIndex
, 0);
2703 ignoreTabIndex
= true;
2706 // if the frame is inside a popup, make sure to scan only within the
2707 // popup. This handles the situation of tabbing amongst elements
2708 // inside an iframe which is itself inside a popup. Otherwise,
2709 // navigation would move outside the popup when tabbing outside the
2711 popupFrame
= nsLayoutUtils::GetClosestFrameOfType(frame
,
2712 nsGkAtoms::menuPopupFrame
);
2714 rootContent
= popupFrame
->GetContent();
2715 NS_ASSERTION(rootContent
, "Popup frame doesn't have a content node");
2719 startContent
= rootContent
;
2720 tabIndex
= forward
? 1 : 0;
2724 // no parent, so call the tree owner. This will tell the embedder that
2725 // it should take the focus.
2727 docShell
->TabToTreeOwner(forward
, &tookFocus
);
2728 // if the tree owner, took the focus, blur the current content
2730 nsCOMPtr
<nsPIDOMWindow
> window
= docShell
->GetWindow();
2731 if (window
->GetFocusedNode() == mFocusedContent
)
2732 Blur(mFocusedWindow
, nullptr, true, true);
2734 window
->SetFocusedNode(nullptr);
2738 // reset the tab index and start again from the beginning or end
2739 startContent
= rootContent
;
2740 tabIndex
= forward
? 1 : 0;
2743 // wrapped all the way around and didn't find anything to move the focus
2744 // to, so just break out
2745 if (startContent
== originalStartContent
)
2753 nsFocusManager::GetNextTabbableContent(nsIPresShell
* aPresShell
,
2754 nsIContent
* aRootContent
,
2755 nsIContent
* aOriginalStartContent
,
2756 nsIContent
* aStartContent
,
2758 int32_t aCurrentTabIndex
,
2759 bool aIgnoreTabIndex
,
2760 nsIContent
** aResultContent
)
2762 *aResultContent
= nullptr;
2764 nsCOMPtr
<nsIContent
> startContent
= aStartContent
;
2768 LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent
);
2769 LOGFOCUSNAVIGATION((" tabindex: %d", aCurrentTabIndex
));
2771 nsPresContext
* presContext
= aPresShell
->GetPresContext();
2773 bool getNextFrame
= true;
2774 nsCOMPtr
<nsIContent
> iterStartContent
= aStartContent
;
2776 nsIFrame
* startFrame
= iterStartContent
->GetPrimaryFrame();
2777 // if there is no frame, look for another content node that has a frame
2779 // if the root content doesn't have a frame, just return
2780 if (iterStartContent
== aRootContent
)
2783 // look for the next or previous content node in tree order
2784 iterStartContent
= aForward
? iterStartContent
->GetNextNode() : iterStartContent
->GetPreviousContent();
2785 // we've already skipped over the initial focused content, so we
2786 // don't want to traverse frames.
2787 getNextFrame
= false;
2788 if (iterStartContent
)
2791 // otherwise, as a last attempt, just look at the root content
2792 iterStartContent
= aRootContent
;
2796 nsCOMPtr
<nsIFrameEnumerator
> frameTraversal
;
2797 nsresult rv
= NS_NewFrameTraversal(getter_AddRefs(frameTraversal
),
2798 presContext
, startFrame
,
2801 false, // aLockInScrollView
2804 NS_ENSURE_SUCCESS(rv
, rv
);
2806 if (iterStartContent
== aRootContent
) {
2808 frameTraversal
->Last();
2809 } else if (aRootContent
->IsFocusable()) {
2810 frameTraversal
->Next();
2813 else if (getNextFrame
&&
2814 (!iterStartContent
|| iterStartContent
->Tag() != nsGkAtoms::area
||
2815 !iterStartContent
->IsHTML())) {
2816 // Need to do special check in case we're in an imagemap which has multiple
2817 // content nodes per frame, so don't skip over the starting frame.
2819 frameTraversal
->Next();
2821 frameTraversal
->Prev();
2824 // Walk frames to find something tabbable matching mCurrentTabIndex
2825 nsIFrame
* frame
= static_cast<nsIFrame
*>(frameTraversal
->CurrentItem());
2827 // TabIndex not set defaults to 0 for form elements, anchors and other
2828 // elements that are normally focusable. Tabindex defaults to -1
2829 // for elements that are not normally focusable.
2830 // The returned computed tabindex from IsFocusable() is as follows:
2831 // < 0 not tabbable at all
2832 // == 0 in normal tab order (last after positive tabindexed items)
2833 // > 0 can be tabbed to in the order specified by this value
2836 frame
->IsFocusable(&tabIndex
, 0);
2838 LOGCONTENTNAVIGATION("Next Tabbable %s:", frame
->GetContent());
2839 LOGFOCUSNAVIGATION((" with tabindex: %d expected: %d", tabIndex
, aCurrentTabIndex
));
2841 nsIContent
* currentContent
= frame
->GetContent();
2842 if (tabIndex
>= 0) {
2843 NS_ASSERTION(currentContent
, "IsFocusable set a tabindex for a frame with no content");
2844 if (currentContent
->Tag() == nsGkAtoms::img
&&
2845 currentContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::usemap
)) {
2846 // This is an image with a map. Image map areas are not traversed by
2847 // nsIFrameTraversal so look for the next or previous area element.
2848 nsIContent
*areaContent
=
2849 GetNextTabbableMapArea(aForward
, aCurrentTabIndex
,
2850 currentContent
, iterStartContent
);
2852 NS_ADDREF(*aResultContent
= areaContent
);
2856 else if (aIgnoreTabIndex
|| aCurrentTabIndex
== tabIndex
) {
2857 // break out if we've wrapped around to the start again.
2858 if (aOriginalStartContent
&& currentContent
== aOriginalStartContent
) {
2859 NS_ADDREF(*aResultContent
= currentContent
);
2863 // found a node with a matching tab index. Check if it is a child
2864 // frame. If so, navigate into the child frame instead.
2865 nsIDocument
* doc
= currentContent
->GetComposedDoc();
2866 NS_ASSERTION(doc
, "content not in document");
2867 nsIDocument
* subdoc
= doc
->GetSubDocumentFor(currentContent
);
2869 if (!subdoc
->EventHandlingSuppressed()) {
2871 // when tabbing forward into a frame, return the root
2872 // frame so that the canvas becomes focused.
2873 nsCOMPtr
<nsPIDOMWindow
> subframe
= subdoc
->GetWindow();
2875 // If the subframe body is editable by contenteditable,
2876 // we should set the editor's root element rather than the
2877 // actual root element. Otherwise, we should set the focus
2878 // to the root content.
2880 nsLayoutUtils::GetEditableRootContentByContentEditable(subdoc
);
2881 if (!*aResultContent
||
2882 !((*aResultContent
)->GetPrimaryFrame())) {
2884 GetRootForFocus(subframe
, subdoc
, false, true);
2886 if (*aResultContent
) {
2887 NS_ADDREF(*aResultContent
);
2892 Element
* rootElement
= subdoc
->GetRootElement();
2893 nsIPresShell
* subShell
= subdoc
->GetShell();
2894 if (rootElement
&& subShell
) {
2895 rv
= GetNextTabbableContent(subShell
, rootElement
,
2896 aOriginalStartContent
, rootElement
,
2897 aForward
, (aForward
? 1 : 0),
2898 false, aResultContent
);
2899 NS_ENSURE_SUCCESS(rv
, rv
);
2900 if (*aResultContent
)
2905 // otherwise, use this as the next content node to tab to, unless
2906 // this was the element we started on. This would happen for
2907 // instance on an element with child frames, where frame navigation
2908 // could return the original element again. In that case, just skip
2909 // it. Also, if the next content node is the root content, then
2910 // return it. This latter case would happen only if someone made a
2912 // Also, when going backwards, check to ensure that the focus
2913 // wouldn't be redirected. Otherwise, for example, when an input in
2914 // a textbox is focused, the enclosing textbox would be found and
2915 // the same inner input would be returned again.
2916 else if (currentContent
== aRootContent
||
2917 (currentContent
!= startContent
&&
2918 (aForward
|| !GetRedirectedFocus(currentContent
)))) {
2919 NS_ADDREF(*aResultContent
= currentContent
);
2924 else if (aOriginalStartContent
&& currentContent
== aOriginalStartContent
) {
2925 // not focusable, so return if we have wrapped around to the original
2926 // content. This is necessary in case the original starting content was
2928 NS_ADDREF(*aResultContent
= currentContent
);
2932 // Move to the next or previous frame, but ignore continuation frames
2933 // since only the first frame should be involved in focusability.
2934 // Otherwise, a loop will occur in the following example:
2935 // <span tabindex="1">...<a/><a/>...</span>
2936 // where the text wraps onto multiple lines. Tabbing from the second
2937 // link can find one of the span's continuation frames between the link
2938 // and the end of the span, and the span would end up getting focused
2942 frameTraversal
->Next();
2944 frameTraversal
->Prev();
2945 frame
= static_cast<nsIFrame
*>(frameTraversal
->CurrentItem());
2946 } while (frame
&& frame
->GetPrevContinuation());
2949 // If already at lowest priority tab (0), end search completely.
2950 // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
2951 if (aCurrentTabIndex
== (aForward
? 0 : 1)) {
2952 // if going backwards, the canvas should be focused once the beginning
2953 // has been reached.
2955 nsCOMPtr
<nsPIDOMWindow
> window
= GetCurrentWindow(aRootContent
);
2956 NS_ENSURE_TRUE(window
, NS_ERROR_FAILURE
);
2957 NS_IF_ADDREF(*aResultContent
=
2958 GetRootForFocus(window
, aRootContent
->GetComposedDoc(),
2964 // continue looking for next highest priority tabindex
2965 aCurrentTabIndex
= GetNextTabIndex(aRootContent
, aCurrentTabIndex
, aForward
);
2966 startContent
= iterStartContent
= aRootContent
;
2973 nsFocusManager::GetNextTabbableMapArea(bool aForward
,
2974 int32_t aCurrentTabIndex
,
2975 nsIContent
* aImageContent
,
2976 nsIContent
* aStartContent
)
2978 nsAutoString useMap
;
2979 aImageContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::usemap
, useMap
);
2981 nsCOMPtr
<nsIDocument
> doc
= aImageContent
->GetComposedDoc();
2983 nsCOMPtr
<nsIContent
> mapContent
= doc
->FindImageMap(useMap
);
2986 uint32_t count
= mapContent
->GetChildCount();
2987 // First see if the the start content is in this map
2989 int32_t index
= mapContent
->IndexOf(aStartContent
);
2991 if (index
< 0 || (aStartContent
->IsFocusable(&tabIndex
) &&
2992 tabIndex
!= aCurrentTabIndex
)) {
2993 // If aStartContent is in this map we must start iterating past it.
2994 // We skip the case where aStartContent has tabindex == aStartContent
2995 // since the next tab ordered element might be before it
2996 // (or after for backwards) in the child list.
2997 index
= aForward
? -1 : (int32_t)count
;
3000 // GetChildAt will return nullptr if our index < 0 or index >= count
3001 nsCOMPtr
<nsIContent
> areaContent
;
3002 while ((areaContent
= mapContent
->GetChildAt(aForward
? ++index
: --index
)) != nullptr) {
3003 if (areaContent
->IsFocusable(&tabIndex
) && tabIndex
== aCurrentTabIndex
) {
3013 nsFocusManager::GetNextTabIndex(nsIContent
* aParent
,
3014 int32_t aCurrentTabIndex
,
3017 int32_t tabIndex
, childTabIndex
;
3021 for (nsIContent
* child
= aParent
->GetFirstChild();
3023 child
= child
->GetNextSibling()) {
3024 childTabIndex
= GetNextTabIndex(child
, aCurrentTabIndex
, aForward
);
3025 if (childTabIndex
> aCurrentTabIndex
&& childTabIndex
!= tabIndex
) {
3026 tabIndex
= (tabIndex
== 0 || childTabIndex
< tabIndex
) ? childTabIndex
: tabIndex
;
3029 nsAutoString tabIndexStr
;
3030 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
, tabIndexStr
);
3032 int32_t val
= tabIndexStr
.ToInteger(&ec
);
3033 if (NS_SUCCEEDED (ec
) && val
> aCurrentTabIndex
&& val
!= tabIndex
) {
3034 tabIndex
= (tabIndex
== 0 || val
< tabIndex
) ? val
: tabIndex
;
3038 else { /* !aForward */
3040 for (nsIContent
* child
= aParent
->GetFirstChild();
3042 child
= child
->GetNextSibling()) {
3043 childTabIndex
= GetNextTabIndex(child
, aCurrentTabIndex
, aForward
);
3044 if ((aCurrentTabIndex
== 0 && childTabIndex
> tabIndex
) ||
3045 (childTabIndex
< aCurrentTabIndex
&& childTabIndex
> tabIndex
)) {
3046 tabIndex
= childTabIndex
;
3049 nsAutoString tabIndexStr
;
3050 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
, tabIndexStr
);
3052 int32_t val
= tabIndexStr
.ToInteger(&ec
);
3053 if (NS_SUCCEEDED (ec
)) {
3054 if ((aCurrentTabIndex
== 0 && val
> tabIndex
) ||
3055 (val
< aCurrentTabIndex
&& val
> tabIndex
) ) {
3066 nsFocusManager::GetRootForFocus(nsPIDOMWindow
* aWindow
,
3067 nsIDocument
* aDocument
,
3068 bool aIsForDocNavigation
,
3069 bool aCheckVisibility
)
3071 // the root element's canvas may be focused as long as the document is in a
3072 // a non-chrome shell and does not contain a frameset.
3073 if (aIsForDocNavigation
) {
3074 nsCOMPtr
<Element
> docElement
= aWindow
->GetFrameElementInternal();
3075 // document navigation skips iframes and frames that are specifically non-focusable
3077 if (docElement
->Tag() == nsGkAtoms::iframe
)
3080 nsIFrame
* frame
= docElement
->GetPrimaryFrame();
3081 if (!frame
|| !frame
->IsFocusable(nullptr, 0))
3085 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
3086 if (docShell
->ItemType() == nsIDocShellTreeItem::typeChrome
) {
3091 if (aCheckVisibility
&& !IsWindowVisible(aWindow
))
3094 Element
*rootElement
= aDocument
->GetRootElement();
3099 if (aCheckVisibility
&& !rootElement
->GetPrimaryFrame()) {
3103 // Finally, check if this is a frameset
3104 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(aDocument
);
3105 if (htmlDoc
&& aDocument
->GetHtmlChildElement(nsGkAtoms::frameset
)) {
3113 nsFocusManager::GetLastDocShell(nsIDocShellTreeItem
* aItem
,
3114 nsIDocShellTreeItem
** aResult
)
3118 nsCOMPtr
<nsIDocShellTreeItem
> curItem
= aItem
;
3120 int32_t childCount
= 0;
3121 curItem
->GetChildCount(&childCount
);
3124 NS_ADDREF(*aResult
);
3129 curItem
->GetChildAt(childCount
- 1, getter_AddRefs(curItem
));
3134 nsFocusManager::GetNextDocShell(nsIDocShellTreeItem
* aItem
,
3135 nsIDocShellTreeItem
** aResult
)
3139 int32_t childCount
= 0;
3140 aItem
->GetChildCount(&childCount
);
3142 aItem
->GetChildAt(0, aResult
);
3147 nsCOMPtr
<nsIDocShellTreeItem
> curItem
= aItem
;
3149 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
3150 curItem
->GetParent(getter_AddRefs(parentItem
));
3154 // Note that we avoid using GetChildOffset() here because docshell
3155 // child offsets can't be trusted to be correct. bug 162283.
3156 nsCOMPtr
<nsIDocShellTreeItem
> iterItem
;
3158 parentItem
->GetChildCount(&childCount
);
3159 for (int32_t index
= 0; index
< childCount
; ++index
) {
3160 parentItem
->GetChildAt(index
, getter_AddRefs(iterItem
));
3161 if (iterItem
== curItem
) {
3163 if (index
< childCount
) {
3164 parentItem
->GetChildAt(index
, aResult
);
3172 curItem
= parentItem
;
3177 nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem
* aItem
,
3178 nsIDocShellTreeItem
** aResult
)
3182 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
3183 aItem
->GetParent(getter_AddRefs(parentItem
));
3187 // Note that we avoid using GetChildOffset() here because docshell
3188 // child offsets can't be trusted to be correct. bug 162283.
3189 int32_t childCount
= 0;
3190 parentItem
->GetChildCount(&childCount
);
3191 nsCOMPtr
<nsIDocShellTreeItem
> prevItem
, iterItem
;
3192 for (int32_t index
= 0; index
< childCount
; ++index
) {
3193 parentItem
->GetChildAt(index
, getter_AddRefs(iterItem
));
3194 if (iterItem
== aItem
)
3196 prevItem
= iterItem
;
3200 GetLastDocShell(prevItem
, aResult
);
3202 NS_ADDREF(*aResult
= parentItem
);
3206 nsFocusManager::GetNextTabbablePanel(nsIDocument
* aDocument
, nsIFrame
* aCurrentPopup
, bool aForward
)
3208 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
3212 // Iterate through the array backwards if aForward is false.
3213 nsTArray
<nsIFrame
*> popups
;
3214 pm
->GetVisiblePopups(popups
);
3215 int32_t i
= aForward
? 0 : popups
.Length() - 1;
3216 int32_t end
= aForward
? popups
.Length() : -1;
3218 for (; i
!= end
; aForward
? i
++ : i
--) {
3219 nsIFrame
* popupFrame
= popups
[i
];
3220 if (aCurrentPopup
) {
3221 // If the current popup is set, then we need to skip over this popup and
3222 // wait until the currently focused popup is found. Once found, the
3223 // current popup will be cleared so that the next popup is used.
3224 if (aCurrentPopup
== popupFrame
)
3225 aCurrentPopup
= nullptr;
3229 // Skip over non-panels
3230 if (popupFrame
->GetContent()->Tag() != nsGkAtoms::panel
||
3231 (aDocument
&& popupFrame
->GetContent()->GetComposedDoc() != aDocument
)) {
3235 // Find the first focusable content within the popup. If there isn't any
3236 // focusable content in the popup, skip to the next popup.
3237 nsIPresShell
* presShell
= popupFrame
->PresContext()->GetPresShell();
3239 nsCOMPtr
<nsIContent
> nextFocus
;
3240 nsIContent
* popup
= popupFrame
->GetContent();
3241 nsresult rv
= GetNextTabbableContent(presShell
, popup
,
3244 getter_AddRefs(nextFocus
));
3245 if (NS_SUCCEEDED(rv
) && nextFocus
) {
3246 return nextFocus
.get();
3255 nsFocusManager::GetNextTabbableDocument(nsIContent
* aStartContent
, bool aForward
)
3257 // If currentPopup is set, then the starting content is in a panel.
3258 nsIFrame
* currentPopup
= nullptr;
3259 nsCOMPtr
<nsIDocument
> doc
;
3260 nsCOMPtr
<nsIDocShell
> startDocShell
;
3262 if (aStartContent
) {
3263 doc
= aStartContent
->GetComposedDoc();
3265 startDocShell
= doc
->GetWindow()->GetDocShell();
3268 // Check if the starting content is inside a panel. Document navigation
3269 // must start from this panel instead of the document root.
3270 nsIContent
* content
= aStartContent
;
3272 if (content
->NodeInfo()->Equals(nsGkAtoms::panel
, kNameSpaceID_XUL
)) {
3273 currentPopup
= content
->GetPrimaryFrame();
3276 content
= content
->GetParent();
3279 else if (mFocusedWindow
) {
3280 startDocShell
= mFocusedWindow
->GetDocShell();
3281 doc
= mFocusedWindow
->GetExtantDoc();
3282 } else if (mActiveWindow
) {
3283 startDocShell
= mActiveWindow
->GetDocShell();
3284 doc
= mActiveWindow
->GetExtantDoc();
3290 // perform a depth first search (preorder) of the docshell tree
3291 // looking for an HTML Frame or a chrome document
3292 nsIContent
* content
= aStartContent
;
3293 nsCOMPtr
<nsIDocShellTreeItem
> curItem
= startDocShell
.get();
3294 nsCOMPtr
<nsIDocShellTreeItem
> nextItem
;
3296 // If moving forward, check for a panel in the starting document. If one
3297 // exists with focusable content, return that content instead of the next
3298 // document. If currentPopup is set, then, another panel may exist. If no
3299 // such panel exists, then continue on to check the next document.
3300 // When moving backwards, and the starting content is in a panel, then
3301 // check for additional panels in the starting document. If the starting
3302 // content is not in a panel, move back to the previous document and check
3303 // for panels there.
3305 bool checkPopups
= false;
3306 nsCOMPtr
<nsPIDOMWindow
> nextFrame
= nullptr;
3308 if (doc
&& (aForward
|| currentPopup
)) {
3309 nsIContent
* popupContent
= GetNextTabbablePanel(doc
, currentPopup
, aForward
);
3311 return popupContent
;
3313 if (!aForward
&& currentPopup
) {
3314 // The starting content was in a popup, yet no other popups were
3315 // found. Move onto the starting content's document.
3316 nextFrame
= doc
->GetWindow();
3320 // Look for the next or previous document.
3323 GetNextDocShell(curItem
, getter_AddRefs(nextItem
));
3325 // wrap around to the beginning, which is the top of the tree
3326 startDocShell
->GetRootTreeItem(getter_AddRefs(nextItem
));
3330 GetPreviousDocShell(curItem
, getter_AddRefs(nextItem
));
3332 // wrap around to the end, which is the last item in the tree
3333 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
3334 startDocShell
->GetRootTreeItem(getter_AddRefs(rootItem
));
3335 GetLastDocShell(rootItem
, getter_AddRefs(nextItem
));
3338 // When going back to the previous document, check for any focusable
3339 // popups in that previous document first.
3344 nextFrame
= nextItem
? nextItem
->GetWindow() : nullptr;
3350 // Clear currentPopup for the next iteration
3351 currentPopup
= nullptr;
3353 // If event handling is suppressed, move on to the next document. Set
3354 // content to null so that the popup check will be skipped on the next
3356 doc
= nextFrame
->GetExtantDoc();
3357 if (!doc
|| doc
->EventHandlingSuppressed()) {
3363 // When iterating backwards, check the panels of the previous document
3364 // first. If a panel exists that has focusable content, focus that.
3365 // Otherwise, continue on to focus the document.
3366 nsIContent
* popupContent
= GetNextTabbablePanel(doc
, nullptr, false);
3368 return popupContent
;
3371 content
= GetRootForFocus(nextFrame
, doc
, true, true);
3372 if (content
&& !GetRootForFocus(nextFrame
, doc
, false, false)) {
3373 // if the found content is in a chrome shell or a frameset, navigate
3374 // forward one tabbable item so that the first item is focused. Note
3375 // that we always go forward and not back here.
3376 nsCOMPtr
<nsIContent
> nextFocus
;
3377 Element
* rootElement
= doc
->GetRootElement();
3378 nsIPresShell
* presShell
= doc
->GetShell();
3380 nsresult rv
= GetNextTabbableContent(presShell
, rootElement
,
3381 nullptr, rootElement
,
3383 getter_AddRefs(nextFocus
));
3384 return NS_SUCCEEDED(rv
) ? nextFocus
.get() : nullptr;
3394 nsFocusManager::GetFocusInSelection(nsPIDOMWindow
* aWindow
,
3395 nsIContent
* aStartSelection
,
3396 nsIContent
* aEndSelection
,
3397 nsIContent
** aFocusedContent
)
3399 *aFocusedContent
= nullptr;
3401 nsCOMPtr
<nsIContent
> testContent
= aStartSelection
;
3402 nsCOMPtr
<nsIContent
> nextTestContent
= aEndSelection
;
3404 nsCOMPtr
<nsIContent
> currentFocus
= aWindow
->GetFocusedNode();
3406 // We now have the correct start node in selectionContent!
3407 // Search for focusable elements, starting with selectionContent
3409 // Method #1: Keep going up while we look - an ancestor might be focusable
3410 // We could end the loop earlier, such as when we're no longer
3411 // in the same frame, by comparing selectionContent->GetPrimaryFrame()
3412 // with a variable holding the starting selectionContent
3413 while (testContent
) {
3414 // Keep testing while selectionContent is equal to something,
3415 // eventually we'll run out of ancestors
3417 nsCOMPtr
<nsIURI
> uri
;
3418 if (testContent
== currentFocus
||
3419 testContent
->IsLink(getter_AddRefs(uri
))) {
3420 NS_ADDREF(*aFocusedContent
= testContent
);
3425 testContent
= testContent
->GetParent();
3428 // We run this loop again, checking the ancestor chain of the selection's end point
3429 testContent
= nextTestContent
;
3430 nextTestContent
= nullptr;
3434 // We couldn't find an anchor that was an ancestor of the selection start
3435 // Method #2: look for anchor in selection's primary range (depth first search)
3437 // Turn into nodes so that we can use GetNextSibling() and GetFirstChild()
3438 nsCOMPtr
<nsIDOMNode
> selectionNode(do_QueryInterface(aStartSelection
));
3439 nsCOMPtr
<nsIDOMNode
> endSelectionNode(do_QueryInterface(aEndSelection
));
3440 nsCOMPtr
<nsIDOMNode
> testNode
;
3443 testContent
= do_QueryInterface(selectionNode
);
3445 // We're looking for any focusable link that could be part of the
3446 // main document's selection.
3447 nsCOMPtr
<nsIURI
> uri
;
3448 if (testContent
== currentFocus
||
3449 testContent
->IsLink(getter_AddRefs(uri
))) {
3450 NS_ADDREF(*aFocusedContent
= testContent
);
3454 selectionNode
->GetFirstChild(getter_AddRefs(testNode
));
3456 selectionNode
= testNode
;
3460 if (selectionNode
== endSelectionNode
)
3462 selectionNode
->GetNextSibling(getter_AddRefs(testNode
));
3464 selectionNode
= testNode
;
3469 selectionNode
->GetParentNode(getter_AddRefs(testNode
));
3470 if (!testNode
|| testNode
== endSelectionNode
) {
3471 selectionNode
= nullptr;
3474 testNode
->GetNextSibling(getter_AddRefs(selectionNode
));
3477 selectionNode
= testNode
;
3480 while (selectionNode
&& selectionNode
!= endSelectionNode
);
3483 class PointerUnlocker
: public nsRunnable
3488 MOZ_ASSERT(!PointerUnlocker::sActiveUnlocker
);
3489 PointerUnlocker::sActiveUnlocker
= this;
3494 if (PointerUnlocker::sActiveUnlocker
== this) {
3495 PointerUnlocker::sActiveUnlocker
= nullptr;
3501 if (PointerUnlocker::sActiveUnlocker
== this) {
3502 PointerUnlocker::sActiveUnlocker
= nullptr;
3504 NS_ENSURE_STATE(nsFocusManager::GetFocusManager());
3505 nsPIDOMWindow
* focused
=
3506 nsFocusManager::GetFocusManager()->GetFocusedWindow();
3507 nsCOMPtr
<nsIDocument
> pointerLockedDoc
=
3508 do_QueryReferent(EventStateManager::sPointerLockedDoc
);
3509 if (pointerLockedDoc
&&
3510 !nsContentUtils::IsInPointerLockContext(focused
)) {
3511 nsIDocument::UnlockPointer();
3516 static PointerUnlocker
* sActiveUnlocker
;
3520 PointerUnlocker::sActiveUnlocker
= nullptr;
3523 nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindow
* aWindow
)
3525 if (!PointerUnlocker::sActiveUnlocker
&&
3526 nsContentUtils::IsInPointerLockContext(mFocusedWindow
) &&
3527 !nsContentUtils::IsInPointerLockContext(aWindow
)) {
3528 nsCOMPtr
<nsIRunnable
> runnable
= new PointerUnlocker();
3529 NS_DispatchToCurrentThread(runnable
);
3531 mFocusedWindow
= aWindow
;
3535 nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration
)
3541 if (sInstance
->mActiveWindow
) {
3542 sInstance
->mActiveWindow
->
3543 MarkUncollectableForCCGeneration(aGeneration
);
3545 if (sInstance
->mFocusedWindow
) {
3546 sInstance
->mFocusedWindow
->
3547 MarkUncollectableForCCGeneration(aGeneration
);
3549 if (sInstance
->mWindowBeingLowered
) {
3550 sInstance
->mWindowBeingLowered
->
3551 MarkUncollectableForCCGeneration(aGeneration
);
3553 if (sInstance
->mFocusedContent
) {
3554 sInstance
->mFocusedContent
->OwnerDoc()->
3555 MarkUncollectableForCCGeneration(aGeneration
);
3557 if (sInstance
->mFirstBlurEvent
) {
3558 sInstance
->mFirstBlurEvent
->OwnerDoc()->
3559 MarkUncollectableForCCGeneration(aGeneration
);
3561 if (sInstance
->mFirstFocusEvent
) {
3562 sInstance
->mFirstFocusEvent
->OwnerDoc()->
3563 MarkUncollectableForCCGeneration(aGeneration
);
3565 if (sInstance
->mMouseButtonEventHandlingDocument
) {
3566 sInstance
->mMouseButtonEventHandlingDocument
->
3567 MarkUncollectableForCCGeneration(aGeneration
);
3572 NS_NewFocusManager(nsIFocusManager
** aResult
)
3574 NS_IF_ADDREF(*aResult
= nsFocusManager::GetFocusManager());