Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/base/./../../../dom/base/nsFocusManager.cpp
Warning:line 3566, column 11
Value stored to 'startFrame' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_dom_base9.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/base -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/base -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/dom/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/base -I /root/firefox-clang/dom/battery -I /root/firefox-clang/dom/events -I /root/firefox-clang/dom/media -I /root/firefox-clang/dom/network -I /root/firefox-clang/caps -I /root/firefox-clang/docshell/base -I /root/firefox-clang/dom/base -I /root/firefox-clang/dom/file -I /root/firefox-clang/dom/geolocation -I /root/firefox-clang/dom/html -I /root/firefox-clang/dom/ipc -I /root/firefox-clang/dom/storage -I /root/firefox-clang/dom/svg -I /root/firefox-clang/dom/xml -I /root/firefox-clang/dom/xslt/xpath -I /root/firefox-clang/dom/xul -I /root/firefox-clang/extensions/spellcheck/src -I /root/firefox-clang/gfx/2d -I /root/firefox-clang/image -I /root/firefox-clang/js/xpconnect/loader -I /root/firefox-clang/js/xpconnect/src -I /root/firefox-clang/js/xpconnect/wrappers -I /root/firefox-clang/layout/base -I /root/firefox-clang/layout/forms -I /root/firefox-clang/layout/generic -I /root/firefox-clang/layout/style -I /root/firefox-clang/layout/xul -I /root/firefox-clang/netwerk/base -I /root/firefox-clang/netwerk/protocol/http -I /root/firefox-clang/netwerk/url-classifier -I /root/firefox-clang/parser/htmlparser -I /root/firefox-clang/security/manager/ssl -I /root/firefox-clang/third_party/xsimd/include -I /root/firefox-clang/widget -I /root/firefox-clang/xpcom/build -I /root/firefox-clang/xpcom/ds -I /root/firefox-clang/netwerk/sctp/datachannel -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/telemetry -I /root/firefox-clang/xpcom/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/gtk-3.0/unix-print -I /usr/include/gtk-3.0 -I /usr/include/pango-1.0 -I /usr/include/cloudproviders -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/fribidi -I /usr/include/harfbuzz -I /usr/include/atk-1.0 -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/glycin-2 -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/sysprof-6 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_dom_base9.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#include "nsFocusManager.h"
6
7#include <algorithm>
8
9#include "AncestorIterator.h"
10#include "BrowserChild.h"
11#include "ChildIterator.h"
12#include "ContentParent.h"
13#include "LayoutConstants.h"
14#include "mozilla/AccessibleCaretEventHub.h"
15#include "mozilla/ContentEvents.h"
16#include "mozilla/EventDispatcher.h"
17#include "mozilla/EventStateManager.h"
18#include "mozilla/FocusModel.h"
19#include "mozilla/HTMLEditor.h"
20#include "mozilla/IMEStateManager.h"
21#include "mozilla/LookAndFeel.h"
22#include "mozilla/Maybe.h"
23#include "mozilla/PointerLockManager.h"
24#include "mozilla/Preferences.h"
25#include "mozilla/PresShell.h"
26#include "mozilla/Services.h"
27#include "mozilla/StaticPrefs_accessibility.h"
28#include "mozilla/StaticPrefs_full_screen_api.h"
29#include "mozilla/dom/BrowserBridgeChild.h"
30#include "mozilla/dom/BrowserParent.h"
31#include "mozilla/dom/ContentChild.h"
32#include "mozilla/dom/Document.h"
33#include "mozilla/dom/DocumentInlines.h"
34#include "mozilla/dom/Element.h"
35#include "mozilla/dom/ElementBinding.h"
36#include "mozilla/dom/HTMLAreaElement.h"
37#include "mozilla/dom/HTMLImageElement.h"
38#include "mozilla/dom/HTMLInputElement.h"
39#include "mozilla/dom/HTMLSlotElement.h"
40#include "mozilla/dom/Navigation.h"
41#include "mozilla/dom/Selection.h"
42#include "mozilla/dom/Text.h"
43#include "mozilla/dom/WindowGlobalChild.h"
44#include "mozilla/dom/WindowGlobalParent.h"
45#include "mozilla/dom/XULPopupElement.h"
46#include "mozilla/widget/IMEData.h"
47#include "nsCaret.h"
48#include "nsContentUtils.h"
49#include "nsFrameLoader.h"
50#include "nsFrameLoaderOwner.h"
51#include "nsFrameSelection.h"
52#include "nsFrameTraversal.h"
53#include "nsGkAtoms.h"
54#include "nsHTMLDocument.h"
55#include "nsIAppWindow.h"
56#include "nsIBaseWindow.h"
57#include "nsIContentInlines.h"
58#include "nsIDOMXULMenuListElement.h"
59#include "nsIDocShell.h"
60#include "nsIDocShellTreeOwner.h"
61#include "nsIFormControl.h"
62#include "nsIInterfaceRequestorUtils.h"
63#include "nsIObserverService.h"
64#include "nsIPrincipal.h"
65#include "nsIScriptError.h"
66#include "nsIScriptObjectPrincipal.h"
67#include "nsIWebNavigation.h"
68#include "nsIXULRuntime.h"
69#include "nsLayoutUtils.h"
70#include "nsMenuPopupFrame.h"
71#include "nsNetUtil.h"
72#include "nsPIDOMWindow.h"
73#include "nsQueryObject.h"
74#include "nsRange.h"
75#include "nsTextControlFrame.h"
76#include "nsThreadUtils.h"
77#include "nsXULPopupManager.h"
78
79#ifdef ACCESSIBILITY1
80# include "nsAccessibilityService.h"
81#endif
82
83using namespace mozilla;
84using namespace mozilla::dom;
85using namespace mozilla::widget;
86
87// Two types of focus pr logging are available:
88// 'Focus' for normal focus manager calls
89// 'FocusNavigation' for tab and document navigation
90LazyLogModule gFocusLog("Focus");
91LazyLogModule gFocusNavigationLog("FocusNavigation");
92
93#define LOGFOCUS(args)do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, MOZ_LOG_EXPAND_ARGS
args); } } while (0)
MOZ_LOG(gFocusLog, mozilla::LogLevel::Debug, args)do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, MOZ_LOG_EXPAND_ARGS
args); } } while (0)
94#define LOGFOCUSNAVIGATION(args)do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, MOZ_LOG_EXPAND_ARGS
args); } } while (0)
\
95 MOZ_LOG(gFocusNavigationLog, mozilla::LogLevel::Debug, args)do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, MOZ_LOG_EXPAND_ARGS
args); } } while (0)
96
97#define LOGTAG(log, format, content)if ((__builtin_expect(!!(mozilla::detail::log_test(log, LogLevel
::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if (content
) { content->NodeInfo()->NameAtom()->ToUTF8String(tag
); } do { const ::mozilla::LogModule* moz_real_module = log; if
((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, format, tag.get()); } } while (0); }
\
98 if (MOZ_LOG_TEST(log, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(log, LogLevel::
Debug)), 0))
) { \
99 nsAutoCString tag("(none)"_ns); \
100 if (content) { \
101 content->NodeInfo()->NameAtom()->ToUTF8String(tag); \
102 } \
103 MOZ_LOG(log, LogLevel::Debug, (format, tag.get()))do { const ::mozilla::LogModule* moz_real_module = log; if ((
__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, format, tag.get()); } } while (0)
; \
104 }
105
106#define LOGCONTENT(format, content)if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusLog,
LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(content) { content->NodeInfo()->NameAtom()->ToUTF8String
(tag); } do { const ::mozilla::LogModule* moz_real_module = gFocusLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, format, tag.get()); } } while (0); }
LOGTAG(gFocusLog, format, content)if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusLog,
LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(content) { content->NodeInfo()->NameAtom()->ToUTF8String
(tag); } do { const ::mozilla::LogModule* moz_real_module = gFocusLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, format, tag.get()); } } while (0); }
107#define LOGCONTENTNAVIGATION(format, content)if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusNavigationLog
, LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(content) { content->NodeInfo()->NameAtom()->ToUTF8String
(tag); } do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, format, tag.get()); } } while (0); }
\
108 LOGTAG(gFocusNavigationLog, format, content)if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusNavigationLog
, LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(content) { content->NodeInfo()->NameAtom()->ToUTF8String
(tag); } do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, format, tag.get()); } } while (0); }
109
110struct nsDelayedBlurOrFocusEvent {
111 nsDelayedBlurOrFocusEvent(EventMessage aEventMessage, PresShell* aPresShell,
112 Document* aDocument, EventTarget* aTarget,
113 EventTarget* aRelatedTarget)
114 : mPresShell(aPresShell),
115 mDocument(aDocument),
116 mTarget(aTarget),
117 mEventMessage(aEventMessage),
118 mRelatedTarget(aRelatedTarget) {}
119
120 nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent& aOther)
121 : mPresShell(aOther.mPresShell),
122 mDocument(aOther.mDocument),
123 mTarget(aOther.mTarget),
124 mEventMessage(aOther.mEventMessage) {}
125
126 RefPtr<PresShell> mPresShell;
127 nsCOMPtr<Document> mDocument;
128 nsCOMPtr<EventTarget> mTarget;
129 EventMessage mEventMessage;
130 nsCOMPtr<EventTarget> mRelatedTarget;
131};
132
133inline void ImplCycleCollectionUnlink(nsDelayedBlurOrFocusEvent& aField) {
134 aField.mPresShell = nullptr;
135 aField.mDocument = nullptr;
136 aField.mTarget = nullptr;
137 aField.mRelatedTarget = nullptr;
138}
139
140inline void ImplCycleCollectionTraverse(
141 nsCycleCollectionTraversalCallback& aCallback,
142 nsDelayedBlurOrFocusEvent& aField, const char* aName, uint32_t aFlags = 0) {
143 CycleCollectionNoteChild(
144 aCallback, static_cast<nsIDocumentObserver*>(aField.mPresShell.get()),
145 aName, aFlags);
146 CycleCollectionNoteChild(aCallback, aField.mDocument.get(), aName, aFlags);
147 CycleCollectionNoteChild(aCallback, aField.mTarget.get(), aName, aFlags);
148 CycleCollectionNoteChild(aCallback, aField.mRelatedTarget.get(), aName,
149 aFlags);
150}
151
152static bool IsScopeOwner(const nsIContent* aContent);
153static nsIContent* FindScopeOwner(nsIContent* aContent);
154static nsGenericHTMLElement* GetAssociatedPopoverFromInvoker(
155 const nsIContent* aContent);
156
157static nsIContent* GetOpenPopoverInvoker(const nsIContent* aContent) {
158 if (const auto* popover = Element::FromNode(aContent)) {
159 if (popover->IsPopoverOpen()) {
160 return popover->GetPopoverData()->GetInvoker().get();
161 }
162 }
163 return nullptr;
164}
165
166NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager)nsresult nsFocusManager::QueryInterface(const nsIID& aIID
, void** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!"
, "aInstancePtr", "./../../../dom/base/nsFocusManager.cpp", 166
); MOZ_PretendNoReturn(); } } while (0); nsISupports* foundInterface
; if (TopThreeWordsEquals( aIID, (nsXPCOMCycleCollectionParticipant
::kIID), (nsCycleCollectionISupports::kIID)) && (LowWordEquals
(aIID, (nsXPCOMCycleCollectionParticipant::kIID)) || LowWordEquals
(aIID, (nsCycleCollectionISupports::kIID)))) { if (LowWordEquals
(aIID, (nsXPCOMCycleCollectionParticipant::kIID))) { *aInstancePtr
= nsFocusManager::cycleCollection::GetParticipant(); return NS_OK
; } if (LowWordEquals(aIID, (nsCycleCollectionISupports::kIID
))) { *aInstancePtr = nsFocusManager::cycleCollection::Upcast
(this); return NS_OK; } foundInterface = nullptr; } else
167 NS_INTERFACE_MAP_ENTRY(nsIFocusManager)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsIFocusManager>)) foundInterface
= static_cast<nsIFocusManager*>(this); else
168 NS_INTERFACE_MAP_ENTRY(nsIObserver)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsIObserver>)) foundInterface = static_cast
<nsIObserver*>(this); else
169 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsISupportsWeakReference>)) foundInterface
= static_cast<nsISupportsWeakReference*>(this); else
170 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsISupports>)) foundInterface = static_cast
<nsISupports*>(static_cast<nsIFocusManager*>(this
)); else
171NS_INTERFACE_MAP_ENDfoundInterface = 0; nsresult status; if (!foundInterface) { do
{ static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIID.Equals((nsISupports::kIID)))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIID.Equals((nsISupports::kIID
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!aIID.Equals((nsISupports::kIID))", "./../../../dom/base/nsFocusManager.cpp"
, 171); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIID.Equals((nsISupports::kIID))"
")"); do { MOZ_CrashSequence(__null, 171); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); status = NS_NOINTERFACE
; } else { (foundInterface)->AddRef(); status = NS_OK; } *
aInstancePtr = foundInterface; return status; }
172
173NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager)MozExternalRefCountType nsFocusManager::AddRef(void) { static_assert
(!std::is_destructible_v<nsFocusManager>, "Reference-counted class "
"nsFocusManager" " should not have a public destructor. " "Make this class's destructor non-public"
); do { static_assert( mozilla::detail::AssertionConditionType
<decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0"
" (" "illegal refcnt" ")", "./../../../dom/base/nsFocusManager.cpp"
, 173); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0"
") (" "illegal refcnt" ")"); do { MOZ_CrashSequence(__null, 173
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); _mOwningThread.AssertOwnership("nsFocusManager" " not thread-safe"
); nsISupports* base = nsFocusManager::cycleCollection::Upcast
(this); nsrefcnt count = mRefCnt.incr(base); NS_LogAddRef((this
), (count), ("nsFocusManager"), (uint32_t)(sizeof(*this))); return
count; }
174NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager)MozExternalRefCountType nsFocusManager::Release(void) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(int32_t
(mRefCnt) > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0"
" (" "dup release" ")", "./../../../dom/base/nsFocusManager.cpp"
, 174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0"
") (" "dup release" ")"); do { MOZ_CrashSequence(__null, 174
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); _mOwningThread.AssertOwnership("nsFocusManager" " not thread-safe"
); nsISupports* base = nsFocusManager::cycleCollection::Upcast
(this); nsrefcnt count = mRefCnt.decr(base); if (count == 0) {
NS_CycleCollectableHasRefCntZero(); } NS_LogRelease((this), (
count), ("nsFocusManager")); return count; } void nsFocusManager
::DeleteCycleCollectable(void) { delete (this); }
175
176NS_IMPL_CYCLE_COLLECTION_WEAK(nsFocusManager, mActiveWindow,nsFocusManager::cycleCollection nsFocusManager::_cycleCollectorGlobal
; void nsFocusManager::cycleCollection::Unlink(void* p) { nsFocusManager
* tmp = DowncastCCParticipant<nsFocusManager>(p); ImplCycleCollectionUnlink
(tmp->mActiveWindow); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInContent
); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInChrome
); ImplCycleCollectionUnlink(tmp->mFocusedWindow); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInContent); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInChrome); ImplCycleCollectionUnlink
(tmp->mFocusedElement); ImplCycleCollectionUnlink(tmp->
mWindowBeingLowered); ImplCycleCollectionUnlink(tmp->mDelayedBlurFocusEvents
); tmp->ClearWeakReferences(); (void)tmp; } nsresult nsFocusManager
::cycleCollection::TraverseNative( void* p, nsCycleCollectionTraversalCallback
& cb) { nsFocusManager* tmp = DowncastCCParticipant<nsFocusManager
>(p); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "nsFocusManager"
); ImplCycleCollectionTraverse(cb, tmp->mActiveWindow, "mActiveWindow"
, 0); ImplCycleCollectionTraverse(cb, tmp->mActiveBrowsingContextInContent
, "mActiveBrowsingContextInContent", 0); ImplCycleCollectionTraverse
(cb, tmp->mActiveBrowsingContextInChrome, "mActiveBrowsingContextInChrome"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedWindow,
"mFocusedWindow", 0); ImplCycleCollectionTraverse(cb, tmp->
mFocusedBrowsingContextInContent, "mFocusedBrowsingContextInContent"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedBrowsingContextInChrome
, "mFocusedBrowsingContextInChrome", 0); ImplCycleCollectionTraverse
(cb, tmp->mFocusedElement, "mFocusedElement", 0); ImplCycleCollectionTraverse
(cb, tmp->mWindowBeingLowered, "mWindowBeingLowered", 0); ImplCycleCollectionTraverse
(cb, tmp->mDelayedBlurFocusEvents, "mDelayedBlurFocusEvents"
, 0); (void)tmp; return NS_OK; }
177 mActiveBrowsingContextInContent,nsFocusManager::cycleCollection nsFocusManager::_cycleCollectorGlobal
; void nsFocusManager::cycleCollection::Unlink(void* p) { nsFocusManager
* tmp = DowncastCCParticipant<nsFocusManager>(p); ImplCycleCollectionUnlink
(tmp->mActiveWindow); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInContent
); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInChrome
); ImplCycleCollectionUnlink(tmp->mFocusedWindow); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInContent); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInChrome); ImplCycleCollectionUnlink
(tmp->mFocusedElement); ImplCycleCollectionUnlink(tmp->
mWindowBeingLowered); ImplCycleCollectionUnlink(tmp->mDelayedBlurFocusEvents
); tmp->ClearWeakReferences(); (void)tmp; } nsresult nsFocusManager
::cycleCollection::TraverseNative( void* p, nsCycleCollectionTraversalCallback
& cb) { nsFocusManager* tmp = DowncastCCParticipant<nsFocusManager
>(p); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "nsFocusManager"
); ImplCycleCollectionTraverse(cb, tmp->mActiveWindow, "mActiveWindow"
, 0); ImplCycleCollectionTraverse(cb, tmp->mActiveBrowsingContextInContent
, "mActiveBrowsingContextInContent", 0); ImplCycleCollectionTraverse
(cb, tmp->mActiveBrowsingContextInChrome, "mActiveBrowsingContextInChrome"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedWindow,
"mFocusedWindow", 0); ImplCycleCollectionTraverse(cb, tmp->
mFocusedBrowsingContextInContent, "mFocusedBrowsingContextInContent"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedBrowsingContextInChrome
, "mFocusedBrowsingContextInChrome", 0); ImplCycleCollectionTraverse
(cb, tmp->mFocusedElement, "mFocusedElement", 0); ImplCycleCollectionTraverse
(cb, tmp->mWindowBeingLowered, "mWindowBeingLowered", 0); ImplCycleCollectionTraverse
(cb, tmp->mDelayedBlurFocusEvents, "mDelayedBlurFocusEvents"
, 0); (void)tmp; return NS_OK; }
178 mActiveBrowsingContextInChrome, mFocusedWindow,nsFocusManager::cycleCollection nsFocusManager::_cycleCollectorGlobal
; void nsFocusManager::cycleCollection::Unlink(void* p) { nsFocusManager
* tmp = DowncastCCParticipant<nsFocusManager>(p); ImplCycleCollectionUnlink
(tmp->mActiveWindow); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInContent
); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInChrome
); ImplCycleCollectionUnlink(tmp->mFocusedWindow); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInContent); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInChrome); ImplCycleCollectionUnlink
(tmp->mFocusedElement); ImplCycleCollectionUnlink(tmp->
mWindowBeingLowered); ImplCycleCollectionUnlink(tmp->mDelayedBlurFocusEvents
); tmp->ClearWeakReferences(); (void)tmp; } nsresult nsFocusManager
::cycleCollection::TraverseNative( void* p, nsCycleCollectionTraversalCallback
& cb) { nsFocusManager* tmp = DowncastCCParticipant<nsFocusManager
>(p); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "nsFocusManager"
); ImplCycleCollectionTraverse(cb, tmp->mActiveWindow, "mActiveWindow"
, 0); ImplCycleCollectionTraverse(cb, tmp->mActiveBrowsingContextInContent
, "mActiveBrowsingContextInContent", 0); ImplCycleCollectionTraverse
(cb, tmp->mActiveBrowsingContextInChrome, "mActiveBrowsingContextInChrome"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedWindow,
"mFocusedWindow", 0); ImplCycleCollectionTraverse(cb, tmp->
mFocusedBrowsingContextInContent, "mFocusedBrowsingContextInContent"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedBrowsingContextInChrome
, "mFocusedBrowsingContextInChrome", 0); ImplCycleCollectionTraverse
(cb, tmp->mFocusedElement, "mFocusedElement", 0); ImplCycleCollectionTraverse
(cb, tmp->mWindowBeingLowered, "mWindowBeingLowered", 0); ImplCycleCollectionTraverse
(cb, tmp->mDelayedBlurFocusEvents, "mDelayedBlurFocusEvents"
, 0); (void)tmp; return NS_OK; }
179 mFocusedBrowsingContextInContent,nsFocusManager::cycleCollection nsFocusManager::_cycleCollectorGlobal
; void nsFocusManager::cycleCollection::Unlink(void* p) { nsFocusManager
* tmp = DowncastCCParticipant<nsFocusManager>(p); ImplCycleCollectionUnlink
(tmp->mActiveWindow); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInContent
); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInChrome
); ImplCycleCollectionUnlink(tmp->mFocusedWindow); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInContent); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInChrome); ImplCycleCollectionUnlink
(tmp->mFocusedElement); ImplCycleCollectionUnlink(tmp->
mWindowBeingLowered); ImplCycleCollectionUnlink(tmp->mDelayedBlurFocusEvents
); tmp->ClearWeakReferences(); (void)tmp; } nsresult nsFocusManager
::cycleCollection::TraverseNative( void* p, nsCycleCollectionTraversalCallback
& cb) { nsFocusManager* tmp = DowncastCCParticipant<nsFocusManager
>(p); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "nsFocusManager"
); ImplCycleCollectionTraverse(cb, tmp->mActiveWindow, "mActiveWindow"
, 0); ImplCycleCollectionTraverse(cb, tmp->mActiveBrowsingContextInContent
, "mActiveBrowsingContextInContent", 0); ImplCycleCollectionTraverse
(cb, tmp->mActiveBrowsingContextInChrome, "mActiveBrowsingContextInChrome"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedWindow,
"mFocusedWindow", 0); ImplCycleCollectionTraverse(cb, tmp->
mFocusedBrowsingContextInContent, "mFocusedBrowsingContextInContent"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedBrowsingContextInChrome
, "mFocusedBrowsingContextInChrome", 0); ImplCycleCollectionTraverse
(cb, tmp->mFocusedElement, "mFocusedElement", 0); ImplCycleCollectionTraverse
(cb, tmp->mWindowBeingLowered, "mWindowBeingLowered", 0); ImplCycleCollectionTraverse
(cb, tmp->mDelayedBlurFocusEvents, "mDelayedBlurFocusEvents"
, 0); (void)tmp; return NS_OK; }
180 mFocusedBrowsingContextInChrome, mFocusedElement,nsFocusManager::cycleCollection nsFocusManager::_cycleCollectorGlobal
; void nsFocusManager::cycleCollection::Unlink(void* p) { nsFocusManager
* tmp = DowncastCCParticipant<nsFocusManager>(p); ImplCycleCollectionUnlink
(tmp->mActiveWindow); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInContent
); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInChrome
); ImplCycleCollectionUnlink(tmp->mFocusedWindow); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInContent); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInChrome); ImplCycleCollectionUnlink
(tmp->mFocusedElement); ImplCycleCollectionUnlink(tmp->
mWindowBeingLowered); ImplCycleCollectionUnlink(tmp->mDelayedBlurFocusEvents
); tmp->ClearWeakReferences(); (void)tmp; } nsresult nsFocusManager
::cycleCollection::TraverseNative( void* p, nsCycleCollectionTraversalCallback
& cb) { nsFocusManager* tmp = DowncastCCParticipant<nsFocusManager
>(p); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "nsFocusManager"
); ImplCycleCollectionTraverse(cb, tmp->mActiveWindow, "mActiveWindow"
, 0); ImplCycleCollectionTraverse(cb, tmp->mActiveBrowsingContextInContent
, "mActiveBrowsingContextInContent", 0); ImplCycleCollectionTraverse
(cb, tmp->mActiveBrowsingContextInChrome, "mActiveBrowsingContextInChrome"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedWindow,
"mFocusedWindow", 0); ImplCycleCollectionTraverse(cb, tmp->
mFocusedBrowsingContextInContent, "mFocusedBrowsingContextInContent"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedBrowsingContextInChrome
, "mFocusedBrowsingContextInChrome", 0); ImplCycleCollectionTraverse
(cb, tmp->mFocusedElement, "mFocusedElement", 0); ImplCycleCollectionTraverse
(cb, tmp->mWindowBeingLowered, "mWindowBeingLowered", 0); ImplCycleCollectionTraverse
(cb, tmp->mDelayedBlurFocusEvents, "mDelayedBlurFocusEvents"
, 0); (void)tmp; return NS_OK; }
181 mWindowBeingLowered, mDelayedBlurFocusEvents)nsFocusManager::cycleCollection nsFocusManager::_cycleCollectorGlobal
; void nsFocusManager::cycleCollection::Unlink(void* p) { nsFocusManager
* tmp = DowncastCCParticipant<nsFocusManager>(p); ImplCycleCollectionUnlink
(tmp->mActiveWindow); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInContent
); ImplCycleCollectionUnlink(tmp->mActiveBrowsingContextInChrome
); ImplCycleCollectionUnlink(tmp->mFocusedWindow); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInContent); ImplCycleCollectionUnlink
(tmp->mFocusedBrowsingContextInChrome); ImplCycleCollectionUnlink
(tmp->mFocusedElement); ImplCycleCollectionUnlink(tmp->
mWindowBeingLowered); ImplCycleCollectionUnlink(tmp->mDelayedBlurFocusEvents
); tmp->ClearWeakReferences(); (void)tmp; } nsresult nsFocusManager
::cycleCollection::TraverseNative( void* p, nsCycleCollectionTraversalCallback
& cb) { nsFocusManager* tmp = DowncastCCParticipant<nsFocusManager
>(p); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "nsFocusManager"
); ImplCycleCollectionTraverse(cb, tmp->mActiveWindow, "mActiveWindow"
, 0); ImplCycleCollectionTraverse(cb, tmp->mActiveBrowsingContextInContent
, "mActiveBrowsingContextInContent", 0); ImplCycleCollectionTraverse
(cb, tmp->mActiveBrowsingContextInChrome, "mActiveBrowsingContextInChrome"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedWindow,
"mFocusedWindow", 0); ImplCycleCollectionTraverse(cb, tmp->
mFocusedBrowsingContextInContent, "mFocusedBrowsingContextInContent"
, 0); ImplCycleCollectionTraverse(cb, tmp->mFocusedBrowsingContextInChrome
, "mFocusedBrowsingContextInChrome", 0); ImplCycleCollectionTraverse
(cb, tmp->mFocusedElement, "mFocusedElement", 0); ImplCycleCollectionTraverse
(cb, tmp->mWindowBeingLowered, "mWindowBeingLowered", 0); ImplCycleCollectionTraverse
(cb, tmp->mDelayedBlurFocusEvents, "mDelayedBlurFocusEvents"
, 0); (void)tmp; return NS_OK; }
182
183StaticRefPtr<nsFocusManager> nsFocusManager::sInstance;
184bool nsFocusManager::sTestMode = false;
185uint64_t nsFocusManager::sFocusActionCounter = 0;
186
187static const char* kObservedPrefs[] = {"accessibility.browsewithcaret",
188 "focusmanager.testmode", nullptr};
189
190nsFocusManager::nsFocusManager()
191 : mActionIdForActiveBrowsingContextInContent(0),
192 mActionIdForActiveBrowsingContextInChrome(0),
193 mActionIdForFocusedBrowsingContextInContent(0),
194 mActionIdForFocusedBrowsingContextInChrome(0),
195 mActiveBrowsingContextInContentSetFromOtherProcess(false),
196 mEventHandlingNeedsFlush(false) {}
197
198nsFocusManager::~nsFocusManager() {
199 Preferences::UnregisterCallbacks(nsFocusManager::PrefChanged, kObservedPrefs,
200 this);
201
202 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
203 if (obs) {
204 obs->RemoveObserver(this, "xpcom-shutdown");
205 }
206}
207
208// static
209nsresult nsFocusManager::Init() {
210 sInstance = new nsFocusManager();
211
212 sTestMode = Preferences::GetBool("focusmanager.testmode", false);
213
214 Preferences::RegisterCallbacks(nsFocusManager::PrefChanged, kObservedPrefs,
215 sInstance.get());
216
217 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
218 if (obs) {
219 obs->AddObserver(sInstance, "xpcom-shutdown", true);
220 }
221
222 return NS_OK;
223}
224
225// static
226void nsFocusManager::Shutdown() { sInstance = nullptr; }
227
228// static
229void nsFocusManager::PrefChanged(const char* aPref, void* aSelf) {
230 if (RefPtr<nsFocusManager> fm = static_cast<nsFocusManager*>(aSelf)) {
231 fm->PrefChanged(aPref);
232 }
233}
234
235void nsFocusManager::PrefChanged(const char* aPref) {
236 nsDependentCString pref(aPref);
237 if (pref.EqualsLiteral("accessibility.browsewithcaret")) {
238 UpdateCaretForCaretBrowsingMode();
239 } else if (pref.EqualsLiteral("focusmanager.testmode")) {
240 sTestMode = Preferences::GetBool("focusmanager.testmode", false);
241 }
242}
243
244NS_IMETHODIMPnsresult
245nsFocusManager::Observe(nsISupports* aSubject, const char* aTopic,
246 const char16_t* aData) {
247 if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
248 mActiveWindow = nullptr;
249 mActiveBrowsingContextInContent = nullptr;
250 mActionIdForActiveBrowsingContextInContent = 0;
251 mActionIdForFocusedBrowsingContextInContent = 0;
252 mActiveBrowsingContextInChrome = nullptr;
253 mActionIdForActiveBrowsingContextInChrome = 0;
254 mActionIdForFocusedBrowsingContextInChrome = 0;
255 mFocusedWindow = nullptr;
256 mFocusedBrowsingContextInContent = nullptr;
257 mFocusedBrowsingContextInChrome = nullptr;
258 mFocusedElement = nullptr;
259 mWindowBeingLowered = nullptr;
260 mDelayedBlurFocusEvents.Clear();
261 }
262
263 return NS_OK;
264}
265
266static bool ActionIdComparableAndLower(uint64_t aActionId,
267 uint64_t aReference) {
268 MOZ_ASSERT(aActionId, "Uninitialized action id")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aActionId)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aActionId))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aActionId" " (" "Uninitialized action id"
")", "./../../../dom/base/nsFocusManager.cpp", 268); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aActionId" ") (" "Uninitialized action id"
")"); do { MOZ_CrashSequence(__null, 268); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
269 auto [actionProc, actionId] =
270 nsContentUtils::SplitProcessSpecificId(aActionId);
271 auto [refProc, refId] = nsContentUtils::SplitProcessSpecificId(aReference);
272 return actionProc == refProc && actionId < refId;
273}
274
275// given a frame content node, retrieve the nsIDOMWindow displayed in it
276static nsPIDOMWindowOuter* GetContentWindow(nsIContent* aContent) {
277 if (Document* doc = aContent->GetComposedDoc()) {
278 if (Document* subdoc = doc->GetSubDocumentFor(aContent)) {
279 return subdoc->GetWindow();
280 }
281 }
282 return nullptr;
283}
284
285bool nsFocusManager::IsFocused(nsIContent* aContent) {
286 if (!aContent || !mFocusedElement) {
287 return false;
288 }
289 return aContent == mFocusedElement;
290}
291
292bool nsFocusManager::IsTestMode() { return sTestMode; }
293
294bool nsFocusManager::IsInActiveWindow(BrowsingContext* aBC) const {
295 RefPtr<BrowsingContext> top = aBC->Top();
296 if (XRE_IsParentProcess()) {
297 top = top->Canonical()->TopCrossChromeBoundary();
298 }
299 return IsSameOrAncestor(top, GetActiveBrowsingContext());
300}
301
302// get the current window for the given content node
303static nsPIDOMWindowOuter* GetCurrentWindow(nsIContent* aContent) {
304 Document* doc = aContent->GetComposedDoc();
305 return doc ? doc->GetWindow() : nullptr;
306}
307
308// static
309Element* nsFocusManager::GetFocusedDescendant(
310 nsPIDOMWindowOuter* aWindow, SearchRange aSearchRange,
311 nsPIDOMWindowOuter** aFocusedWindow) {
312 NS_ENSURE_TRUE(aWindow, nullptr)do { if ((__builtin_expect(!!(!(aWindow)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aWindow" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 312); return nullptr
; } } while (false)
;
313
314 *aFocusedWindow = nullptr;
315
316 Element* currentElement = nullptr;
317 nsPIDOMWindowOuter* window = aWindow;
318 for (;;) {
319 *aFocusedWindow = window;
320 currentElement = window->GetFocusedElement();
321 if (!currentElement || aSearchRange == eOnlyCurrentWindow) {
322 break;
323 }
324
325 window = GetContentWindow(currentElement);
326 if (!window) {
327 break;
328 }
329
330 if (aSearchRange == eIncludeAllDescendants) {
331 continue;
332 }
333
334 MOZ_ASSERT(aSearchRange == eIncludeVisibleDescendants)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSearchRange == eIncludeVisibleDescendants)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aSearchRange == eIncludeVisibleDescendants))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aSearchRange == eIncludeVisibleDescendants"
, "./../../../dom/base/nsFocusManager.cpp", 334); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aSearchRange == eIncludeVisibleDescendants"
")"); do { MOZ_CrashSequence(__null, 334); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
335
336 // If the child window doesn't have PresShell, it means the window is
337 // invisible.
338 nsIDocShell* docShell = window->GetDocShell();
339 if (!docShell) {
340 break;
341 }
342 if (!docShell->GetPresShell()) {
343 break;
344 }
345 }
346
347 NS_IF_ADDREF(*aFocusedWindow)ns_if_addref(*aFocusedWindow);
348
349 return currentElement;
350}
351
352// static
353InputContextAction::Cause nsFocusManager::GetFocusMoveActionCause(
354 uint32_t aFlags) {
355 if (aFlags & nsIFocusManager::FLAG_BYTOUCH) {
356 return InputContextAction::CAUSE_TOUCH;
357 } else if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
358 return InputContextAction::CAUSE_MOUSE;
359 } else if (aFlags & nsIFocusManager::FLAG_BYKEY) {
360 return InputContextAction::CAUSE_KEY;
361 } else if (aFlags & nsIFocusManager::FLAG_BYLONGPRESS) {
362 return InputContextAction::CAUSE_LONGPRESS;
363 }
364 return InputContextAction::CAUSE_UNKNOWN;
365}
366
367NS_IMETHODIMPnsresult
368nsFocusManager::GetActiveWindow(mozIDOMWindowProxy** aWindow) {
369 MOZ_ASSERT(XRE_IsParentProcess(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
" (" "Must not be called outside the parent process." ")", "./../../../dom/base/nsFocusManager.cpp"
, 370); AnnotateMozCrashReason("MOZ_ASSERT" "(" "XRE_IsParentProcess()"
") (" "Must not be called outside the parent process." ")");
do { MOZ_CrashSequence(__null, 370); __attribute__((nomerge)
) ::abort(); } while (false); } } while (false)
370 "Must not be called outside the parent process.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
" (" "Must not be called outside the parent process." ")", "./../../../dom/base/nsFocusManager.cpp"
, 370); AnnotateMozCrashReason("MOZ_ASSERT" "(" "XRE_IsParentProcess()"
") (" "Must not be called outside the parent process." ")");
do { MOZ_CrashSequence(__null, 370); __attribute__((nomerge)
) ::abort(); } while (false); } } while (false)
;
371 NS_IF_ADDREF(*aWindow = mActiveWindow)ns_if_addref(*aWindow = mActiveWindow);
372 return NS_OK;
373}
374
375NS_IMETHODIMPnsresult
376nsFocusManager::GetActiveBrowsingContext(BrowsingContext** aBrowsingContext) {
377 NS_IF_ADDREF(*aBrowsingContext = GetActiveBrowsingContext())ns_if_addref(*aBrowsingContext = GetActiveBrowsingContext());
378 return NS_OK;
379}
380
381void nsFocusManager::FocusWindow(nsPIDOMWindowOuter* aWindow,
382 CallerType aCallerType) {
383 if (RefPtr<nsFocusManager> fm = sInstance) {
384 fm->SetFocusedWindowWithCallerType(aWindow, aCallerType);
385 }
386}
387
388NS_IMETHODIMPnsresult
389nsFocusManager::GetFocusedWindow(mozIDOMWindowProxy** aFocusedWindow) {
390 NS_IF_ADDREF(*aFocusedWindow = mFocusedWindow)ns_if_addref(*aFocusedWindow = mFocusedWindow);
391 return NS_OK;
392}
393
394NS_IMETHODIMPnsresult
395nsFocusManager::GetFocusedContentBrowsingContext(
396 BrowsingContext** aBrowsingContext) {
397 MOZ_DIAGNOSTIC_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
" (" "We only have use cases for this in the parent process"
")", "./../../../dom/base/nsFocusManager.cpp", 399); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "XRE_IsParentProcess()" ") (" "We only have use cases for this in the parent process"
")"); do { MOZ_CrashSequence(__null, 399); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
398 XRE_IsParentProcess(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
" (" "We only have use cases for this in the parent process"
")", "./../../../dom/base/nsFocusManager.cpp", 399); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "XRE_IsParentProcess()" ") (" "We only have use cases for this in the parent process"
")"); do { MOZ_CrashSequence(__null, 399); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
399 "We only have use cases for this in the parent process")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
" (" "We only have use cases for this in the parent process"
")", "./../../../dom/base/nsFocusManager.cpp", 399); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "XRE_IsParentProcess()" ") (" "We only have use cases for this in the parent process"
")"); do { MOZ_CrashSequence(__null, 399); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
400 NS_IF_ADDREF(*aBrowsingContext = GetFocusedBrowsingContextInChrome())ns_if_addref(*aBrowsingContext = GetFocusedBrowsingContextInChrome
())
;
401 return NS_OK;
402}
403
404NS_IMETHODIMPnsresult
405nsFocusManager::GetActiveContentBrowsingContext(
406 BrowsingContext** aBrowsingContext) {
407 MOZ_DIAGNOSTIC_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
" (" "We only have use cases for this in the parent process"
")", "./../../../dom/base/nsFocusManager.cpp", 409); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "XRE_IsParentProcess()" ") (" "We only have use cases for this in the parent process"
")"); do { MOZ_CrashSequence(__null, 409); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
408 XRE_IsParentProcess(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
" (" "We only have use cases for this in the parent process"
")", "./../../../dom/base/nsFocusManager.cpp", 409); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "XRE_IsParentProcess()" ") (" "We only have use cases for this in the parent process"
")"); do { MOZ_CrashSequence(__null, 409); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
409 "We only have use cases for this in the parent process")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
" (" "We only have use cases for this in the parent process"
")", "./../../../dom/base/nsFocusManager.cpp", 409); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "XRE_IsParentProcess()" ") (" "We only have use cases for this in the parent process"
")"); do { MOZ_CrashSequence(__null, 409); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
410 NS_IF_ADDREF(*aBrowsingContext = GetActiveBrowsingContextInChrome())ns_if_addref(*aBrowsingContext = GetActiveBrowsingContextInChrome
())
;
411 return NS_OK;
412}
413
414nsresult nsFocusManager::SetFocusedWindowWithCallerType(
415 mozIDOMWindowProxy* aWindowToFocus, CallerType aCallerType) {
416 LOGFOCUS(("<<SetFocusedWindow begin>>"))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<SetFocusedWindow begin>>"
); } } while (0)
;
417
418 nsCOMPtr<nsPIDOMWindowOuter> windowToFocus =
419 nsPIDOMWindowOuter::From(aWindowToFocus);
420 NS_ENSURE_TRUE(windowToFocus, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(windowToFocus)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "windowToFocus" ") failed"
, nullptr, "./../../../dom/base/nsFocusManager.cpp", 420); return
NS_ERROR_FAILURE; } } while (false)
;
421
422 nsCOMPtr<Element> frameElement = windowToFocus->GetFrameElementInternal();
423 Maybe<uint64_t> existingActionId;
424 if (frameElement) {
425 // pass false for aFocusChanged so that the caret does not get updated
426 // and scrolling does not occur.
427 existingActionId = SetFocusInner(frameElement, 0, false, true);
428 } else if (auto* bc = windowToFocus->GetBrowsingContext();
429 bc && !bc->IsTop()) {
430 // No frameElement means windowToFocus is an OOP iframe, so
431 // the above SetFocusInner is not called. That means the focus
432 // of the currently focused BC is not going to be cleared. So
433 // we do that manually here.
434 if (RefPtr<BrowsingContext> focusedBC = GetFocusedBrowsingContext()) {
435 // If focusedBC is an ancestor of bc, blur will be handled
436 // correctly by nsFocusManager::AdjustWindowFocus.
437 if (!IsSameOrAncestor(focusedBC, bc)) {
438 existingActionId.emplace(sInstance->GenerateFocusActionId());
439 Blur(focusedBC, nullptr, true, true, false, existingActionId.value());
440 }
441 }
442 } else {
443 // this is a top-level window. If the window has a child frame focused,
444 // clear the focus. Otherwise, focus should already be in this frame, or
445 // already cleared. This ensures that focus will be in this frame and not
446 // in a child.
447 if (Element* el = windowToFocus->GetFocusedElement()) {
448 if (nsCOMPtr<nsPIDOMWindowOuter> childWindow = GetContentWindow(el)) {
449 ClearFocus(windowToFocus);
450 }
451 }
452 }
453
454 nsCOMPtr<nsPIDOMWindowOuter> rootWindow = windowToFocus->GetPrivateRoot();
455 const uint64_t actionId = existingActionId.isSome()
456 ? existingActionId.value()
457 : sInstance->GenerateFocusActionId();
458 if (rootWindow) {
459 RaiseWindow(rootWindow, aCallerType, actionId);
460 }
461
462 LOGFOCUS(("<<SetFocusedWindow end actionid: %" PRIu64 ">>", actionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<SetFocusedWindow end actionid: %"
"l" "u" ">>", actionId); } } while (0)
;
463
464 return NS_OK;
465}
466
467NS_IMETHODIMPnsresult nsFocusManager::SetFocusedWindow(
468 mozIDOMWindowProxy* aWindowToFocus) {
469 return SetFocusedWindowWithCallerType(aWindowToFocus, CallerType::System);
470}
471
472NS_IMETHODIMPnsresult
473nsFocusManager::GetFocusedElement(Element** aFocusedElement) {
474 RefPtr<Element> focusedElement = mFocusedElement;
475 focusedElement.forget(aFocusedElement);
476 return NS_OK;
477}
478
479uint32_t nsFocusManager::GetLastFocusMethod(nsPIDOMWindowOuter* aWindow) const {
480 nsPIDOMWindowOuter* window = aWindow ? aWindow : mFocusedWindow.get();
481 uint32_t method = window ? window->GetFocusMethod() : 0;
482 NS_ASSERTION((method & METHOD_MASK) == method, "invalid focus method")do { if (!((method & METHOD_MASK) == method)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "invalid focus method", "(method & METHOD_MASK) == method"
, "./../../../dom/base/nsFocusManager.cpp", 482); MOZ_PretendNoReturn
(); } } while (0)
;
483 return method;
484}
485
486NS_IMETHODIMPnsresult
487nsFocusManager::GetLastFocusMethod(mozIDOMWindowProxy* aWindow,
488 uint32_t* aLastFocusMethod) {
489 *aLastFocusMethod = GetLastFocusMethod(nsPIDOMWindowOuter::From(aWindow));
490 return NS_OK;
491}
492
493NS_IMETHODIMPnsresult
494nsFocusManager::SetFocus(Element* aElement, uint32_t aFlags) {
495 LOGFOCUS(("<<SetFocus begin>>"))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<SetFocus begin>>"
); } } while (0)
;
496
497 NS_ENSURE_ARG(aElement)do { if ((__builtin_expect(!!(!(aElement)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aElement" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 497); return NS_ERROR_INVALID_ARG
; } } while (false)
;
498
499 SetFocusInner(aElement, aFlags, true, true);
500
501 LOGFOCUS(("<<SetFocus end>>"))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<SetFocus end>>"
); } } while (0)
;
502
503 return NS_OK;
504}
505
506NS_IMETHODIMPnsresult
507nsFocusManager::ElementIsFocusable(Element* aElement, uint32_t aFlags,
508 bool* aIsFocusable) {
509 NS_ENSURE_TRUE(aElement, NS_ERROR_INVALID_ARG)do { if ((__builtin_expect(!!(!(aElement)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aElement" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 509); return NS_ERROR_INVALID_ARG
; } } while (false)
;
510 *aIsFocusable = !!FlushAndCheckIfFocusable(aElement, aFlags);
511 return NS_OK;
512}
513
514MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMPnsresult
515nsFocusManager::MoveFocus(mozIDOMWindowProxy* aWindow, Element* aStartElement,
516 uint32_t aType, uint32_t aFlags, Element** aElement) {
517 *aElement = nullptr;
518
519 LOGFOCUS(("<<MoveFocus begin Type: %d Flags: %x>>", aType, aFlags))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<MoveFocus begin Type: %d Flags: %x>>"
, aType, aFlags); } } while (0)
;
520
521 if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gFocusLog, LogLevel
::Debug)), 0))
&& mFocusedWindow) {
522 Document* doc = mFocusedWindow->GetExtantDoc();
523 if (doc && doc->GetDocumentURI()) {
524 LOGFOCUS((" Focused Window: %p %s", mFocusedWindow.get(),do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Focused Window: %p %s"
, mFocusedWindow.get(), doc->GetDocumentURI()->GetSpecOrDefault
().get()); } } while (0)
525 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Focused Window: %p %s"
, mFocusedWindow.get(), doc->GetDocumentURI()->GetSpecOrDefault
().get()); } } while (0)
;
526 }
527 }
528
529 LOGCONTENT(" Current Focus: %s", mFocusedElement.get())if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusLog,
LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(mFocusedElement.get()) { mFocusedElement.get()->NodeInfo
()->NameAtom()->ToUTF8String(tag); } do { const ::mozilla
::LogModule* moz_real_module = gFocusLog; if ((__builtin_expect
(!!(mozilla::detail::log_test(moz_real_module, LogLevel::Debug
)), 0))) { mozilla::detail::log_print(moz_real_module, LogLevel
::Debug, " Current Focus: %s", tag.get()); } } while (0); }
;
530
531 // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
532 // the other focus methods is already set, or we're just moving to the root
533 // or caret position.
534 if (aType != MOVEFOCUS_ROOT && aType != MOVEFOCUS_CARET &&
535 (aFlags & METHOD_MASK) == 0) {
536 aFlags |= FLAG_BYMOVEFOCUS;
537 }
538
539 nsCOMPtr<nsPIDOMWindowOuter> window;
540 if (aStartElement) {
541 window = GetCurrentWindow(aStartElement);
542 } else {
543 window = aWindow ? nsPIDOMWindowOuter::From(aWindow) : mFocusedWindow.get();
544 }
545
546 NS_ENSURE_TRUE(window, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(window)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "window" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 546); return NS_ERROR_FAILURE
; } } while (false)
;
547
548 // Flush to ensure that focusability of descendants is computed correctly.
549 if (RefPtr<Document> doc = window->GetExtantDoc()) {
550 doc->FlushPendingNotifications(FlushType::EnsurePresShellInitAndFrames);
551 }
552
553 bool noParentTraversal = aFlags & FLAG_NOPARENTFRAME;
554 nsCOMPtr<nsIContent> newFocus;
555 nsresult rv = DetermineElementToMoveFocus(window, aStartElement, aType,
556 noParentTraversal, true,
557 getter_AddRefs(newFocus));
558 if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
559 return NS_OK;
560 }
561
562 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../dom/base/nsFocusManager.cpp"
, 562); return rv; } } while (false)
;
563
564 LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get())if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusNavigationLog
, LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(newFocus.get()) { newFocus.get()->NodeInfo()->NameAtom
()->ToUTF8String(tag); } do { const ::mozilla::LogModule* moz_real_module
= gFocusNavigationLog; if ((__builtin_expect(!!(mozilla::detail
::log_test(moz_real_module, LogLevel::Debug)), 0))) { mozilla
::detail::log_print(moz_real_module, LogLevel::Debug, "Element to be focused: %s"
, tag.get()); } } while (0); }
;
565
566 if (newFocus && newFocus->IsElement()) {
567 // for caret movement, pass false for the aFocusChanged argument,
568 // otherwise the caret will end up moving to the focus position. This
569 // would be a problem because the caret would move to the beginning of the
570 // focused link making it impossible to navigate the caret over a link.
571 SetFocusInner(MOZ_KnownLive(newFocus->AsElement())(newFocus->AsElement()), aFlags,
572 aType != MOVEFOCUS_CARET, true);
573 *aElement = do_AddRef(newFocus->AsElement()).take();
574 } else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) {
575 // no content was found, so clear the focus for these two types.
576 ClearFocus(window);
577 }
578
579 LOGFOCUS(("<<MoveFocus end>>"))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<MoveFocus end>>"
); } } while (0)
;
580
581 return NS_OK;
582}
583
584NS_IMETHODIMPnsresult
585nsFocusManager::ClearFocus(mozIDOMWindowProxy* aWindow) {
586 LOGFOCUS(("<<ClearFocus begin>>"))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<ClearFocus begin>>"
); } } while (0)
;
587
588 // if the window to clear is the focused window or an ancestor of the
589 // focused window, then blur the existing focused content. Otherwise, the
590 // focus is somewhere else so just update the current node.
591 NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG)do { if ((__builtin_expect(!!(!(aWindow)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aWindow" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 591); return NS_ERROR_INVALID_ARG
; } } while (false)
;
592 nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
593
594 if (IsSameOrAncestor(window, GetFocusedBrowsingContext())) {
595 RefPtr<BrowsingContext> bc = window->GetBrowsingContext();
596 RefPtr<BrowsingContext> focusedBC = GetFocusedBrowsingContext();
597 const bool isAncestor = (focusedBC != bc);
598 RefPtr<BrowsingContext> ancestorBC = isAncestor ? bc : nullptr;
599 if (Blur(focusedBC, ancestorBC, isAncestor, true, false,
600 GenerateFocusActionId())) {
601 // if we are clearing the focus on an ancestor of the focused window,
602 // the ancestor will become the new focused window, so focus it
603 if (isAncestor) {
604 // Intentionally use a new actionId here because the above
605 // Blur() will clear the focus of the ancestors of focusedBC, and
606 // this Focus() call might need to update the focus of those ancestors,
607 // so it needs to have a newer actionId to make that happen.
608 Focus(window, nullptr, 0, true, false, false, true,
609 GenerateFocusActionId());
610 }
611 }
612 } else {
613 window->SetFocusedElement(nullptr);
614 }
615
616 LOGFOCUS(("<<ClearFocus end>>"))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<ClearFocus end>>"
); } } while (0)
;
617
618 return NS_OK;
619}
620
621NS_IMETHODIMPnsresult
622nsFocusManager::GetFocusedElementForWindow(mozIDOMWindowProxy* aWindow,
623 bool aDeep,
624 mozIDOMWindowProxy** aFocusedWindow,
625 Element** aElement) {
626 *aElement = nullptr;
627 if (aFocusedWindow) {
628 *aFocusedWindow = nullptr;
629 }
630
631 NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG)do { if ((__builtin_expect(!!(!(aWindow)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aWindow" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 631); return NS_ERROR_INVALID_ARG
; } } while (false)
;
632 nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
633
634 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
635 RefPtr<Element> focusedElement =
636 GetFocusedDescendant(window,
637 aDeep ? nsFocusManager::eIncludeAllDescendants
638 : nsFocusManager::eOnlyCurrentWindow,
639 getter_AddRefs(focusedWindow));
640
641 focusedElement.forget(aElement);
642
643 if (aFocusedWindow) {
644 NS_IF_ADDREF(*aFocusedWindow = focusedWindow)ns_if_addref(*aFocusedWindow = focusedWindow);
645 }
646
647 return NS_OK;
648}
649
650NS_IMETHODIMPnsresult
651nsFocusManager::MoveCaretToFocus(mozIDOMWindowProxy* aWindow) {
652 nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
653 nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
654 if (dsti) {
655 if (dsti->ItemType() != nsIDocShellTreeItem::typeChrome) {
656 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(dsti);
657 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(docShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "docShell" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 657); return NS_ERROR_FAILURE
; } } while (false)
;
658
659 RefPtr<PresShell> presShell = docShell->GetPresShell();
660 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(presShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "presShell" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 660); return NS_ERROR_FAILURE
; } } while (false)
;
661
662 nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
663 if (RefPtr<Element> focusedElement = window->GetFocusedElement()) {
664 MoveCaretToFocus(presShell, focusedElement);
665 }
666 }
667 }
668
669 return NS_OK;
670}
671
672void nsFocusManager::WindowRaised(mozIDOMWindowProxy* aWindow,
673 uint64_t aActionId) {
674 if (!aWindow) {
675 return;
676 }
677
678 nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
679 BrowsingContext* bc = window->GetBrowsingContext();
680
681 if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gFocusLog, LogLevel
::Debug)), 0))
) {
682 LOGFOCUS(("Window %p Raised [Currently: %p %p] actionid: %" PRIu64, aWindow,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Window %p Raised [Currently: %p %p] actionid: %"
"l" "u", aWindow, mActiveWindow.get(), mFocusedWindow.get(),
aActionId); } } while (0)
683 mActiveWindow.get(), mFocusedWindow.get(), aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Window %p Raised [Currently: %p %p] actionid: %"
"l" "u", aWindow, mActiveWindow.get(), mFocusedWindow.get(),
aActionId); } } while (0)
;
684 Document* doc = window->GetExtantDoc();
685 if (doc && doc->GetDocumentURI()) {
686 LOGFOCUS((" Raised Window: %p %s", aWindow,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Raised Window: %p %s"
, aWindow, doc->GetDocumentURI()->GetSpecOrDefault().get
()); } } while (0)
687 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Raised Window: %p %s"
, aWindow, doc->GetDocumentURI()->GetSpecOrDefault().get
()); } } while (0)
;
688 }
689 if (mActiveWindow) {
690 doc = mActiveWindow->GetExtantDoc();
691 if (doc && doc->GetDocumentURI()) {
692 LOGFOCUS((" Active Window: %p %s", mActiveWindow.get(),do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Active Window: %p %s"
, mActiveWindow.get(), doc->GetDocumentURI()->GetSpecOrDefault
().get()); } } while (0)
693 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Active Window: %p %s"
, mActiveWindow.get(), doc->GetDocumentURI()->GetSpecOrDefault
().get()); } } while (0)
;
694 }
695 }
696 }
697
698 if (XRE_IsParentProcess()) {
699 if (mActiveWindow == window) {
700 // The window is already active, so there is no need to focus anything,
701 // but make sure that the right widget is focused. This is a special case
702 // for Windows because when restoring a minimized window, a second
703 // activation will occur and the top-level widget could be focused instead
704 // of the child we want. We solve this by calling SetFocus to ensure that
705 // what the focus manager thinks should be the current widget is actually
706 // focused.
707 EnsureCurrentWidgetFocused(CallerType::System);
708 return;
709 }
710
711 // lower the existing window, if any. This shouldn't happen usually.
712 if (nsCOMPtr<nsPIDOMWindowOuter> activeWindow = mActiveWindow) {
713 WindowLowered(activeWindow, aActionId);
714 }
715 } else if (bc->IsTop()) {
716 BrowsingContext* active = GetActiveBrowsingContext();
717 if (active == bc && !mActiveBrowsingContextInContentSetFromOtherProcess) {
718 // EnsureCurrentWidgetFocused() should not be necessary with
719 // PuppetWidget.
720 return;
721 }
722
723 if (active && active != bc) {
724 if (active->IsInProcess()) {
725 nsCOMPtr<nsPIDOMWindowOuter> activeWindow = active->GetDOMWindow();
726 WindowLowered(activeWindow, aActionId);
727 }
728 // No else, because trying to lower other-process windows
729 // from here can result in the BrowsingContext no longer
730 // existing in the parent process by the time it deserializes
731 // the IPC message.
732 }
733 }
734
735 nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = window->GetDocShell();
736 // If there's no docShellAsItem, this window must have been closed,
737 // in that case there is no tree owner.
738 if (!docShellAsItem) {
739 return;
740 }
741
742 // set this as the active window
743 if (XRE_IsParentProcess()) {
744 mActiveWindow = window;
745 } else if (bc->IsTop()) {
746 SetActiveBrowsingContextInContent(bc, aActionId,
747 false /* aIsEnteringBFCache */);
748 }
749
750 // ensure that the window is enabled and visible
751 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
752 docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
753 if (nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner)) {
754 bool isEnabled = true;
755 if (NS_SUCCEEDED(baseWindow->GetEnabled(&isEnabled))((bool)(__builtin_expect(!!(!NS_FAILED_impl(baseWindow->GetEnabled
(&isEnabled))), 1)))
&& !isEnabled) {
756 return;
757 }
758
759 baseWindow->SetVisibility(true);
760 }
761
762 if (XRE_IsParentProcess()) {
763 // Unsetting top-level focus upon lowering was inhibited to accommodate
764 // ATOK, so we need to do it here.
765 BrowserParent::UnsetTopLevelWebFocusAll();
766 ActivateOrDeactivate(window, true);
767 }
768
769 // Retrieve the last focused element within the window that was raised.
770 MoveFocusToWindowAfterRaise(window, aActionId);
771}
772
773void nsFocusManager::MoveFocusToWindowAfterRaise(nsPIDOMWindowOuter* aWindow,
774 uint64_t aActionId) {
775 nsCOMPtr<nsPIDOMWindowOuter> currentWindow;
776 RefPtr<Element> currentFocus = GetFocusedDescendant(
777 aWindow, eIncludeAllDescendants, getter_AddRefs(currentWindow));
778
779 NS_ASSERTION(currentWindow, "window raised with no window current")do { if (!(currentWindow)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "window raised with no window current", "currentWindow", "./../../../dom/base/nsFocusManager.cpp"
, 779); MOZ_PretendNoReturn(); } } while (0)
;
780 if (!currentWindow) {
781 return;
782 }
783
784 // We use mFocusedWindow here is basically for the case that iframe navigate
785 // from a.com to b.com for example, so it ends up being loaded in a different
786 // process after Fission, but
787 // currentWindow->GetBrowsingContext() == GetFocusedBrowsingContext() would
788 // still be true because focused browsing context is synced, and we won't
789 // fire a focus event while focusing if we use it as condition.
790 Focus(currentWindow, currentFocus, /* aFlags = */ 0,
791 /* aIsNewDocument = */ currentWindow != mFocusedWindow,
792 /* aFocusChanged = */ false,
793 /* aWindowRaised = */ true, /* aAdjustWidget = */ true, aActionId);
794}
795
796void nsFocusManager::WindowLowered(mozIDOMWindowProxy* aWindow,
797 uint64_t aActionId) {
798 if (!aWindow) {
799 return;
800 }
801
802 nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
803
804 if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gFocusLog, LogLevel
::Debug)), 0))
) {
805 LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Window %p Lowered [Currently: %p %p]"
, aWindow, mActiveWindow.get(), mFocusedWindow.get()); } } while
(0)
806 mActiveWindow.get(), mFocusedWindow.get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Window %p Lowered [Currently: %p %p]"
, aWindow, mActiveWindow.get(), mFocusedWindow.get()); } } while
(0)
;
807 Document* doc = window->GetExtantDoc();
808 if (doc && doc->GetDocumentURI()) {
809 LOGFOCUS((" Lowered Window: %s",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Lowered Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
810 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Lowered Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
;
811 }
812 if (mActiveWindow) {
813 doc = mActiveWindow->GetExtantDoc();
814 if (doc && doc->GetDocumentURI()) {
815 LOGFOCUS((" Active Window: %s",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Active Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
816 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Active Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
;
817 }
818 }
819 }
820
821 if (XRE_IsParentProcess()) {
822 if (mActiveWindow != window) {
823 return;
824 }
825 } else {
826 BrowsingContext* bc = window->GetBrowsingContext();
827 BrowsingContext* active = GetActiveBrowsingContext();
828 if (active != bc->Top()) {
829 return;
830 }
831 }
832
833 // clear the mouse capture as the active window has changed
834 PresShell::ReleaseCapturingContent();
835
836 // In addition, reset the drag state to ensure that we are no longer in
837 // drag-select mode.
838 if (mFocusedWindow) {
839 nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
840 if (docShell) {
841 if (PresShell* presShell = docShell->GetPresShell()) {
842 RefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
843 frameSelection->SetDragState(false);
844 }
845 }
846 }
847
848 if (XRE_IsParentProcess()) {
849 ActivateOrDeactivate(window, false);
850 }
851
852 // keep track of the window being lowered, so that attempts to raise the
853 // window can be prevented until we return. Otherwise, focus can get into
854 // an unusual state.
855 mWindowBeingLowered = window;
856 if (XRE_IsParentProcess()) {
857 mActiveWindow = nullptr;
858 } else {
859 BrowsingContext* bc = window->GetBrowsingContext();
860 if (bc == bc->Top()) {
861 SetActiveBrowsingContextInContent(nullptr, aActionId,
862 false /* aIsEnteringBFCache */);
863 }
864 }
865
866 if (mFocusedWindow) {
867 Blur(nullptr, nullptr, true, true, false, aActionId);
868 }
869
870 mWindowBeingLowered = nullptr;
871}
872
873void nsFocusManager::FocusedElementMayHaveMoved(nsIContent* aContent,
874 nsINode* aOldParent) {
875 if (!aOldParent) {
876 return;
877 }
878
879 if (aOldParent->IsElement() &&
880 !aOldParent->AsElement()->State().HasState(ElementState::FOCUS_WITHIN)) {
881 return;
882 }
883
884 nsPIDOMWindowOuter* window = aContent->OwnerDoc()->GetWindow();
885 if (!window) {
886 return;
887 }
888
889 Element* focusedElement = window->GetFocusedElement();
890 if (!focusedElement) {
891 return;
892 }
893
894 if (!nsContentUtils::ContentIsHostIncludingDescendantOf(focusedElement,
895 aContent)) {
896 return;
897 }
898 if (aOldParent->IsElement()) {
899 // Clear the old ancestor chain.
900 NotifyFocusStateChange(aOldParent->AsElement(), nullptr, 0, false, false);
901 }
902 // XXX This is not very optimal.
903 bool showFocusRing =
904 focusedElement->State().HasState(ElementState::FOCUSRING);
905 // Clear the ancestor chain of focused element.
906 NotifyFocusStateChange(focusedElement, nullptr, 0, false, false);
907 // And set the correct states.
908 NotifyFocusStateChange(focusedElement, nullptr, 0, true, showFocusRing);
909}
910
911void nsFocusManager::ContentInserted(nsIContent* aChild,
912 const ContentInsertInfo& aInfo) {
913 FocusedElementMayHaveMoved(aChild, aInfo.mOldParent);
914}
915
916void nsFocusManager::ContentAppended(nsIContent* aFirstNewContent,
917 const ContentAppendInfo& aInfo) {
918 FocusedElementMayHaveMoved(aFirstNewContent, aInfo.mOldParent);
919}
920
921static void UpdateFocusWithinState(Element* aElement,
922 nsIContent* aCommonAncestor,
923 bool aGettingFocus) {
924 Element* focusedElement = nullptr;
925 Document* document = aElement->GetComposedDoc();
926 if (aElement && document) {
927 if (nsPIDOMWindowOuter* window = document->GetWindow()) {
928 focusedElement = window->GetFocusedElement();
929 }
930 }
931
932 bool focusChanged = false;
933 for (nsIContent* content = aElement; content && content != aCommonAncestor;
934 content = content->GetFlattenedTreeParent()) {
935 Element* element = Element::FromNode(content);
936 if (!element) {
937 continue;
938 }
939
940 if (aGettingFocus) {
941 if (element->State().HasState(ElementState::FOCUS_WITHIN)) {
942 break;
943 }
944
945 element->AddStates(ElementState::FOCUS_WITHIN);
946 } else {
947 element->RemoveStates(ElementState::FOCUS_WITHIN);
948 }
949
950 focusChanged = focusChanged || element == focusedElement;
951 }
952
953 if (focusChanged && document->GetInnerWindow()) {
954 if (RefPtr<Navigation> navigation =
955 document->GetInnerWindow()->Navigation()) {
956 navigation->SetFocusedChangedDuringOngoingNavigation(
957 /* aFocusChangedDuringOngoingNavigation */ true);
958 }
959 }
960}
961
962static void MaybeFixUpFocusWithinState(Element* aElementToFocus,
963 Element* aFocusedElement) {
964 if (!aElementToFocus || aElementToFocus == aFocusedElement ||
965 !aElementToFocus->IsInComposedDoc()) {
966 return;
967 }
968 // Focus was redirected, make sure the :focus-within state remains consistent.
969 auto* commonAncestor = [&]() -> nsIContent* {
970 if (!aFocusedElement ||
971 aElementToFocus->OwnerDoc() != aFocusedElement->OwnerDoc()) {
972 return nullptr;
973 }
974 return nsContentUtils::GetCommonFlattenedTreeAncestor(aFocusedElement,
975 aElementToFocus);
976 }();
977 UpdateFocusWithinState(aElementToFocus, commonAncestor, false);
978}
979
980nsresult nsFocusManager::ContentRemoved(Document* aDocument,
981 nsIContent* aContent,
982 const ContentRemoveInfo& aInfo) {
983 MOZ_ASSERT(aDocument)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aDocument)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aDocument))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aDocument", "./../../../dom/base/nsFocusManager.cpp"
, 983); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDocument" ")"
); do { MOZ_CrashSequence(__null, 983); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
984 MOZ_ASSERT(aContent)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aContent", "./../../../dom/base/nsFocusManager.cpp"
, 984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aContent" ")"
); do { MOZ_CrashSequence(__null, 984); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
985
986 if (auto* prevFocused = aDocument->GetPreviouslyFocusedContent()) {
987 // Fix up sequential focus navigation starting point when its ancestor is
988 // removed. But if we are moving aContent with moveBefore and the focus
989 // navigation starting point is inside it, then we don't need to do
990 // anything.
991 if ((!aInfo.mNewParent || aDocument->WasFocusedElementRemoved()) &&
992 prevFocused->IsInclusiveFlatTreeDescendantOf(aContent)) {
993 aDocument->SetPreviouslyFocusedContent(aContent, true);
994 }
995 }
996
997 if (aInfo.mNewParent) {
998 // Handled upon insertion in ContentAppended/Inserted.
999 return NS_OK;
1000 }
1001
1002 nsPIDOMWindowOuter* windowPtr = aDocument->GetWindow();
1003 if (!windowPtr) {
1004 return NS_OK;
1005 }
1006
1007 bool detachingShadow = false;
1008 Element* focusWithinElement = [&]() -> Element* {
1009 if (auto* el = Element::FromNode(aContent)) {
1010 return el;
1011 }
1012 if (auto* shadow = ShadowRoot::FromNode(aContent)) {
1013 detachingShadow = true;
1014 // Note that we only get here with ShadowRoots for shadow roots of form
1015 // controls that we can un-attach. So if there's a focused element it must
1016 // be inside our shadow tree already.
1017 return shadow->Host();
1018 }
1019 // Removing text / comments / etc can't affect the focus state.
1020 return nullptr;
1021 }();
1022
1023 if (!focusWithinElement) {
1024 return NS_OK;
1025 }
1026
1027 const bool hasFocusWithinInThisDocument =
1028 focusWithinElement->State().HasAtLeastOneOfStates(
1029 ElementState::FOCUS | ElementState::FOCUS_WITHIN);
1030
1031 // if the content is currently focused in the window, or is an
1032 // shadow-including inclusive ancestor of the currently focused element,
1033 // reset the focus within that window.
1034 Element* previousFocusedElementPtr = windowPtr->GetFocusedElement();
1035 if (!previousFocusedElementPtr) {
1036 if (hasFocusWithinInThisDocument) {
1037 // If we're in-between a blur and an incoming focus, we might have stale
1038 // :focus-within in our ancestor chain. Fix it up now.
1039 UpdateFocusWithinState(focusWithinElement, nullptr, false);
1040 }
1041 return NS_OK;
1042 }
1043
1044 if (previousFocusedElementPtr->State().HasState(ElementState::FOCUS)) {
1045 if (!hasFocusWithinInThisDocument) {
1046 // If the focused element has :focus, that means our ancestor should have
1047 // focus-within.
1048 return NS_OK;
1049 }
1050 } else {
1051 if (detachingShadow && previousFocusedElementPtr == focusWithinElement) {
1052 // If we're detaching a shadow tree and the already focused element is not
1053 // inside, we don't need to do anything.
1054 return NS_OK;
1055 }
1056 // Even if there's no :focus state on the node, we need to clear focus,
1057 // previousFocusedElementPtr could be an <iframe> for example.
1058 }
1059
1060 if (!nsContentUtils::ContentIsHostIncludingDescendantOf(
1061 previousFocusedElementPtr, focusWithinElement)) {
1062 return NS_OK;
1063 }
1064
1065 RefPtr previousFocusedElement = previousFocusedElementPtr;
1066 RefPtr window = windowPtr;
1067
1068 RefPtr<Element> newFocusedElement =
1069 detachingShadow && focusWithinElement->IsHTMLElement(nsGkAtoms::input)
1070 ? focusWithinElement
1071 : nullptr;
1072
1073 window->SetFocusedElement(newFocusedElement);
1074
1075 // if this window is currently focused, clear the global focused
1076 // element as well, but don't fire any events.
1077 if (window->GetBrowsingContext() == GetFocusedBrowsingContext()) {
1078 mFocusedElement = newFocusedElement;
1079 } else if (Document* subdoc =
1080 aDocument->GetSubDocumentFor(previousFocusedElement)) {
1081 // Check if the node that was focused is an iframe or similar by looking if
1082 // it has a subdocument. This would indicate that this focused iframe
1083 // and its descendants will be going away. We will need to move the focus
1084 // somewhere else, so just clear the focus in the toplevel window so that no
1085 // element is focused.
1086 //
1087 // The Fission case is handled in FlushAndCheckIfFocusable().
1088 if (nsCOMPtr<nsIDocShell> docShell = subdoc->GetDocShell()) {
1089 nsCOMPtr<nsPIDOMWindowOuter> childWindow = docShell->GetWindow();
1090 if (childWindow &&
1091 IsSameOrAncestor(childWindow, GetFocusedBrowsingContext())) {
1092 if (XRE_IsParentProcess()) {
1093 nsCOMPtr<nsPIDOMWindowOuter> activeWindow = mActiveWindow;
1094 ClearFocus(activeWindow);
1095 } else {
1096 BrowsingContext* active = GetActiveBrowsingContext();
1097 if (active) {
1098 if (active->IsInProcess()) {
1099 nsCOMPtr<nsPIDOMWindowOuter> activeWindow =
1100 active->GetDOMWindow();
1101 ClearFocus(activeWindow);
1102 } else {
1103 mozilla::dom::ContentChild* contentChild =
1104 mozilla::dom::ContentChild::GetSingleton();
1105 MOZ_ASSERT(contentChild)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(contentChild)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(contentChild))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("contentChild", "./../../../dom/base/nsFocusManager.cpp"
, 1105); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contentChild"
")"); do { MOZ_CrashSequence(__null, 1105); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1106 contentChild->SendClearFocus(active);
1107 }
1108 } // no else, because ClearFocus does nothing with nullptr
1109 }
1110 }
1111 }
1112 }
1113
1114 // Notify the editor in case we removed its ancestor limiter.
1115 if (previousFocusedElement->IsEditable()) {
1116 if (nsIDocShell* const docShell = aDocument->GetDocShell()) {
1117 if (HTMLEditor* const htmlEditor = docShell->GetHTMLEditor()) {
1118 Selection* const selection = htmlEditor->GetSelection();
1119 if (selection && selection->GetFrameSelection() &&
1120 previousFocusedElement ==
1121 selection->GetFrameSelection()->GetAncestorLimiter()) {
1122 // The editing host may be being removed right now. So, it's already
1123 // removed from the child chain of the parent node, but it still know
1124 // the parent node. This could cause unexpected result at scheduling
1125 // paint of the caret. Therefore, we should call FinalizeSelection
1126 // after unblocking to run the script.
1127 nsContentUtils::AddScriptRunner(
1128 NewRunnableMethod("HTMLEditor::FinalizeSelection", htmlEditor,
1129 &HTMLEditor::FinalizeSelection));
1130 }
1131 }
1132 }
1133 }
1134
1135 if (!newFocusedElement) {
1136 // Ensure that focus navigation continues from the location of the
1137 // previously-focused element.
1138 aDocument->SetPreviouslyFocusedContent(aContent, true);
1139 NotifyFocusStateChange(previousFocusedElement, nullptr, 0,
1140 /* aGettingFocus = */ false, false);
1141 } else {
1142 // We should already have the right state, which is managed by the <input>
1143 // widget.
1144 MOZ_ASSERT(newFocusedElement->State().HasState(ElementState::FOCUS))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(newFocusedElement->State().HasState(ElementState::
FOCUS))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(newFocusedElement->State().HasState(ElementState::
FOCUS)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("newFocusedElement->State().HasState(ElementState::FOCUS)"
, "./../../../dom/base/nsFocusManager.cpp", 1144); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "newFocusedElement->State().HasState(ElementState::FOCUS)"
")"); do { MOZ_CrashSequence(__null, 1144); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1145 }
1146
1147 // If we changed focused element and the element still has focus, let's
1148 // notify IME of focus. Note that if new focus move has already occurred
1149 // by running script, we should not let IMEStateManager of outdated focus
1150 // change.
1151 if (mFocusedElement == newFocusedElement && mFocusedWindow == window) {
1152 RefPtr<nsPresContext> presContext(aDocument->GetPresContext());
1153 IMEStateManager::OnChangeFocus(presContext, newFocusedElement,
1154 InputContextAction::Cause::CAUSE_UNKNOWN);
1155 }
1156
1157 return NS_OK;
1158}
1159
1160void nsFocusManager::WindowShown(mozIDOMWindowProxy* aWindow,
1161 bool aNeedsFocus) {
1162 if (!aWindow) {
1163 return;
1164 }
1165
1166 nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
1167
1168 if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gFocusLog, LogLevel
::Debug)), 0))
) {
1169 LOGFOCUS(("Window %p Shown [Currently: %p %p]", window.get(),do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Window %p Shown [Currently: %p %p]"
, window.get(), mActiveWindow.get(), mFocusedWindow.get()); }
} while (0)
1170 mActiveWindow.get(), mFocusedWindow.get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Window %p Shown [Currently: %p %p]"
, window.get(), mActiveWindow.get(), mFocusedWindow.get()); }
} while (0)
;
1171 Document* doc = window->GetExtantDoc();
1172 if (doc && doc->GetDocumentURI()) {
1173 LOGFOCUS(("Shown Window: %s",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Shown Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
1174 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Shown Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
;
1175 }
1176
1177 if (mFocusedWindow) {
1178 doc = mFocusedWindow->GetExtantDoc();
1179 if (doc && doc->GetDocumentURI()) {
1180 LOGFOCUS((" Focused Window: %s",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Focused Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
1181 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Focused Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
;
1182 }
1183 }
1184 }
1185
1186 if (XRE_IsParentProcess()) {
1187 if (BrowsingContext* bc = window->GetBrowsingContext()) {
1188 if (bc->IsTop()) {
1189 bc->SetIsActiveBrowserWindow(bc->GetIsActiveBrowserWindow());
1190 }
1191 }
1192 }
1193
1194 if (XRE_IsParentProcess()) {
1195 if (mFocusedWindow != window) {
1196 return;
1197 }
1198 } else {
1199 BrowsingContext* bc = window->GetBrowsingContext();
1200 if (!bc || mFocusedBrowsingContextInContent != bc) {
1201 return;
1202 }
1203 // Sync the window for a newly-created OOP iframe
1204 // Set actionId to zero to signify that it should be ignored.
1205 SetFocusedWindowInternal(window, 0, false);
1206 }
1207
1208 if (aNeedsFocus) {
1209 nsCOMPtr<nsPIDOMWindowOuter> currentWindow;
1210 RefPtr<Element> currentFocus = GetFocusedDescendant(
1211 window, eIncludeAllDescendants, getter_AddRefs(currentWindow));
1212
1213 if (currentWindow) {
1214 Focus(currentWindow, currentFocus, 0, true, false, false, true,
1215 GenerateFocusActionId());
1216 }
1217 } else {
1218 // Sometimes, an element in a window can be focused before the window is
1219 // visible, which would mean that the widget may not be properly focused.
1220 // When the window becomes visible, make sure the right widget is focused.
1221 EnsureCurrentWidgetFocused(CallerType::System);
1222 }
1223}
1224
1225void nsFocusManager::WindowHidden(mozIDOMWindowProxy* aWindow,
1226 uint64_t aActionId, bool aIsEnteringBFCache) {
1227 // if there is no window or it is not the same or an ancestor of the
1228 // currently focused window, just return, as the current focus will not
1229 // be affected.
1230
1231 if (!aWindow) {
1232 return;
1233 }
1234
1235 nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
1236
1237 if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gFocusLog, LogLevel
::Debug)), 0))
) {
1238 LOGFOCUS(("Window %p Hidden [Currently: %p %p] actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Window %p Hidden [Currently: %p %p] actionid: %"
"l" "u", window.get(), mActiveWindow.get(), mFocusedWindow.get
(), aActionId); } } while (0)
1239 window.get(), mActiveWindow.get(), mFocusedWindow.get(),do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Window %p Hidden [Currently: %p %p] actionid: %"
"l" "u", window.get(), mActiveWindow.get(), mFocusedWindow.get
(), aActionId); } } while (0)
1240 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Window %p Hidden [Currently: %p %p] actionid: %"
"l" "u", window.get(), mActiveWindow.get(), mFocusedWindow.get
(), aActionId); } } while (0)
;
1241 nsAutoCString spec;
1242 Document* doc = window->GetExtantDoc();
1243 if (doc && doc->GetDocumentURI()) {
1244 LOGFOCUS((" Hide Window: %s",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Hide Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
1245 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Hide Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
;
1246 }
1247
1248 if (mFocusedWindow) {
1249 doc = mFocusedWindow->GetExtantDoc();
1250 if (doc && doc->GetDocumentURI()) {
1251 LOGFOCUS((" Focused Window: %s",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Focused Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
1252 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Focused Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
;
1253 }
1254 }
1255
1256 if (mActiveWindow) {
1257 doc = mActiveWindow->GetExtantDoc();
1258 if (doc && doc->GetDocumentURI()) {
1259 LOGFOCUS((" Active Window: %s",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Active Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
1260 doc->GetDocumentURI()->GetSpecOrDefault().get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Active Window: %s"
, doc->GetDocumentURI()->GetSpecOrDefault().get()); } }
while (0)
;
1261 }
1262 }
1263 }
1264
1265 if (!IsSameOrAncestor(window, mFocusedWindow)) {
1266 return;
1267 }
1268
1269 // at this point, we know that the window being hidden is either the focused
1270 // window, or an ancestor of the focused window. Either way, the focus is no
1271 // longer valid, so it needs to be updated.
1272
1273 const RefPtr<Element> oldFocusedElement = std::move(mFocusedElement);
1274
1275 nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
1276 if (!focusedDocShell) {
1277 return;
1278 }
1279
1280 const RefPtr<PresShell> presShell = focusedDocShell->GetPresShell();
1281
1282 if (oldFocusedElement && oldFocusedElement->IsInComposedDoc()) {
1283 NotifyFocusStateChange(oldFocusedElement, nullptr, 0, false, false);
1284 window->UpdateCommands(u"focus"_ns);
1285
1286 if (presShell) {
1287 const DebugOnly<uint64_t> actionId =
1288 mActionIdForFocusedBrowsingContextInContent;
1289 RefPtr<Document> composedDoc = oldFocusedElement->GetComposedDoc();
1290 SendFocusOrBlurEvent(eBlur, presShell, composedDoc, oldFocusedElement,
1291 false);
1292 NS_WARNING_ASSERTION(do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1297); } } while (
false)
1293 !XRE_IsContentProcess() ||do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1297); } } while (
false)
1294 !ActionIdComparableAndLower(do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1297); } } while (
false)
1295 actionId, mActionIdForFocusedBrowsingContextInContent),do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1297); } } while (
false)
1296 "A recursive focus move occurred. We might need to stop doing "do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1297); } } while (
false)
1297 "something below.")do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1297); } } while (
false)
;
1298 }
1299 }
1300
1301 {
1302 const DebugOnly<uint64_t> actionId =
1303 mActionIdForFocusedBrowsingContextInContent;
1304 const RefPtr<nsPresContext> focusedPresContext =
1305 presShell ? presShell->GetPresContext() : nullptr;
1306 IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
1307 GetFocusMoveActionCause(0));
1308 NS_WARNING_ASSERTION(do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1313); } } while (
false)
1309 !XRE_IsContentProcess() ||do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1313); } } while (
false)
1310 !ActionIdComparableAndLower(do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1313); } } while (
false)
1311 actionId, mActionIdForFocusedBrowsingContextInContent),do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1313); } } while (
false)
1312 "A recursive focus move occurred. We might need to stop doing "do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1313); } } while (
false)
1313 "something below.")do { if (!(!XRE_IsContentProcess() || !ActionIdComparableAndLower
( actionId, mActionIdForFocusedBrowsingContextInContent))) { NS_DebugBreak
(NS_DEBUG_WARNING, "A recursive focus move occurred. We might need to stop doing "
"something below.", "!XRE_IsContentProcess() || !ActionIdComparableAndLower( actionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 1313); } } while (
false)
;
1314 }
1315 if (presShell) {
1316 SetCaretVisible(presShell, false, nullptr);
1317 }
1318
1319 // If a window is being "hidden" because its BrowsingContext is changing
1320 // remoteness, we don't want to handle docshell destruction by moving focus.
1321 // Instead, the focused browsing context should stay the way it is (so that
1322 // the newly "shown" window in the other process knows to take focus) and
1323 // we should just null out the process-local field.
1324 nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell();
1325 // Check if we're currently hiding a non-remote nsDocShell due to its
1326 // BrowsingContext navigating to become remote. Normally, when a focused
1327 // subframe is hidden, focus is moved to the frame element, but focus should
1328 // stay with the BrowsingContext when performing a process switch. We don't
1329 // need to consider process switches where the hiding docshell is already
1330 // remote (ie. GetEmbedderElement is nullptr), as shifting remoteness to the
1331 // frame element is handled elsewhere.
1332 if (docShellBeingHidden &&
1333 nsDocShell::Cast(docShellBeingHidden)->WillChangeProcess() &&
1334 docShellBeingHidden->GetBrowsingContext()->GetEmbedderElement()) {
1335 if (mFocusedWindow != window) {
1336 // The window being hidden is an ancestor of the focused window.
1337#ifdef DEBUG1
1338 BrowsingContext* ancestor = window->GetBrowsingContext();
1339 BrowsingContext* bc = mFocusedWindow->GetBrowsingContext();
1340 for (;;) {
1341 if (!bc) {
1342 MOZ_ASSERT(false, "Should have found ancestor")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "Should have found ancestor"
")", "./../../../dom/base/nsFocusManager.cpp", 1342); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "Should have found ancestor" ")"
); do { MOZ_CrashSequence(__null, 1342); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1343 }
1344 bc = bc->GetParent();
1345 if (ancestor == bc) {
1346 break;
1347 }
1348 }
1349#endif
1350 // This call adjusts the focused browsing context and window.
1351 // The latter gets nulled out immediately below.
1352 SetFocusedWindowInternal(window, aActionId);
1353 }
1354 mFocusedWindow = nullptr;
1355 window->SetFocusedElement(nullptr);
1356 return;
1357 }
1358
1359 // if the docshell being hidden is being destroyed, then we want to move
1360 // focus somewhere else. Call ClearFocus on the toplevel window, which
1361 // will have the effect of clearing the focus and moving the focused window
1362 // to the toplevel window. But if the window isn't being destroyed, we are
1363 // likely just loading a new document in it, so we want to maintain the
1364 // focused window so that the new document gets properly focused.
1365 bool beingDestroyed = !docShellBeingHidden;
1366 if (docShellBeingHidden) {
1367 docShellBeingHidden->IsBeingDestroyed(&beingDestroyed);
1368 }
1369 if (beingDestroyed) {
1370 // There is usually no need to do anything if a toplevel window is going
1371 // away, as we assume that WindowLowered will be called. However, this may
1372 // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
1373 // a leak. So if the active window is being destroyed, call WindowLowered
1374 // directly.
1375
1376 if (XRE_IsParentProcess()) {
1377 nsCOMPtr<nsPIDOMWindowOuter> activeWindow = mActiveWindow;
1378 if (activeWindow == mFocusedWindow || activeWindow == window) {
1379 WindowLowered(activeWindow, aActionId);
1380 } else {
1381 ClearFocus(activeWindow);
1382 }
1383 } else {
1384 BrowsingContext* active = GetActiveBrowsingContext();
1385 if (active) {
1386 if (nsCOMPtr<nsPIDOMWindowOuter> activeWindow =
1387 active->GetDOMWindow()) {
1388 if ((mFocusedWindow &&
1389 mFocusedWindow->GetBrowsingContext() == active) ||
1390 (window->GetBrowsingContext() == active)) {
1391 WindowLowered(activeWindow, aActionId);
1392 } else {
1393 ClearFocus(activeWindow);
1394 }
1395 } // else do nothing when an out-of-process iframe is torn down
1396 }
1397 }
1398 return;
1399 }
1400
1401 if (!XRE_IsParentProcess() &&
1402 mActiveBrowsingContextInContent ==
1403 docShellBeingHidden->GetBrowsingContext() &&
1404 mActiveBrowsingContextInContent->IsEnteringBFCache()) {
1405 SetActiveBrowsingContextInContent(nullptr, aActionId, aIsEnteringBFCache);
1406 }
1407
1408 // if the window being hidden is an ancestor of the focused window, adjust
1409 // the focused window so that it points to the one being hidden. This
1410 // ensures that the focused window isn't in a chain of frames that doesn't
1411 // exist any more.
1412 if (window != mFocusedWindow) {
1413 nsCOMPtr<nsIDocShellTreeItem> dsti =
1414 mFocusedWindow ? mFocusedWindow->GetDocShell() : nullptr;
1415 if (dsti) {
1416 nsCOMPtr<nsIDocShellTreeItem> parentDsti;
1417 dsti->GetInProcessParent(getter_AddRefs(parentDsti));
1418 if (parentDsti) {
1419 if (nsCOMPtr<nsPIDOMWindowOuter> parentWindow =
1420 parentDsti->GetWindow()) {
1421 parentWindow->SetFocusedElement(nullptr);
1422 }
1423 }
1424 }
1425
1426 SetFocusedWindowInternal(window, aActionId);
1427 }
1428}
1429
1430void nsFocusManager::FireDelayedEvents(Document* aDocument) {
1431 MOZ_ASSERT(aDocument)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aDocument)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aDocument))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aDocument", "./../../../dom/base/nsFocusManager.cpp"
, 1431); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDocument" ")"
); do { MOZ_CrashSequence(__null, 1431); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1432
1433 // fire any delayed focus and blur events in the same order that they were
1434 // added
1435 for (uint32_t i = 0; i < mDelayedBlurFocusEvents.Length(); i++) {
1436 if (mDelayedBlurFocusEvents[i].mDocument == aDocument) {
1437 if (!aDocument->GetInnerWindow() ||
1438 !aDocument->GetInnerWindow()->IsCurrentInnerWindow()) {
1439 // If the document was navigated away from or is defunct, don't bother
1440 // firing events on it. Note the symmetry between this condition and
1441 // the similar one in Document.cpp:FireOrClearDelayedEvents.
1442 mDelayedBlurFocusEvents.RemoveElementAt(i);
1443 --i;
1444 } else if (!aDocument->EventHandlingSuppressed()) {
1445 EventMessage message = mDelayedBlurFocusEvents[i].mEventMessage;
1446 nsCOMPtr<EventTarget> target = mDelayedBlurFocusEvents[i].mTarget;
1447 RefPtr<PresShell> presShell = mDelayedBlurFocusEvents[i].mPresShell;
1448 nsCOMPtr<EventTarget> relatedTarget =
1449 mDelayedBlurFocusEvents[i].mRelatedTarget;
1450 mDelayedBlurFocusEvents.RemoveElementAt(i);
1451
1452 FireFocusOrBlurEvent(message, presShell, target, false, false,
1453 relatedTarget);
1454 --i;
1455 }
1456 }
1457 }
1458}
1459
1460void nsFocusManager::WasNuked(nsPIDOMWindowOuter* aWindow) {
1461 MOZ_ASSERT(aWindow, "Expected non-null window.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aWindow)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aWindow))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aWindow" " (" "Expected non-null window."
")", "./../../../dom/base/nsFocusManager.cpp", 1461); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aWindow" ") (" "Expected non-null window."
")"); do { MOZ_CrashSequence(__null, 1461); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1462 if (aWindow == mActiveWindow) {
1463 // TODO(emilio, bug 1933555): Figure out if we can assert below.
1464 // MOZ_ASSERT_UNREACHABLE("How come we're nuking a window that's still
1465 // active?");
1466 mActiveWindow = nullptr;
1467 SetActiveBrowsingContextInChrome(nullptr, GenerateFocusActionId());
1468 }
1469 if (aWindow == mFocusedWindow) {
1470 mFocusedWindow = nullptr;
1471 SetFocusedBrowsingContext(nullptr, GenerateFocusActionId());
1472 mFocusedElement = nullptr;
1473 }
1474}
1475
1476nsFocusManager::BlurredElementInfo::BlurredElementInfo(Element& aElement)
1477 : mElement(aElement) {}
1478
1479nsFocusManager::BlurredElementInfo::~BlurredElementInfo() = default;
1480
1481// https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
1482static bool ShouldMatchFocusVisible(nsPIDOMWindowOuter* aWindow,
1483 const Element& aElement,
1484 int32_t aFocusFlags) {
1485 // If we were explicitly requested to show the ring, do it.
1486 if (aFocusFlags & nsIFocusManager::FLAG_SHOWRING) {
1487 return true;
1488 }
1489
1490 if (aFocusFlags & nsIFocusManager::FLAG_NOSHOWRING) {
1491 return false;
1492 }
1493
1494 if (aWindow->ShouldShowFocusRing()) {
1495 // The window decision also trumps any other heuristic.
1496 return true;
1497 }
1498
1499 // Any element which supports keyboard input (such as an input element, or any
1500 // other element which may trigger a virtual keyboard to be shown on focus if
1501 // a physical keyboard is not present) should always match :focus-visible when
1502 // focused.
1503 {
1504 if (aElement.IsHTMLElement(nsGkAtoms::textarea) || aElement.IsEditable()) {
1505 return true;
1506 }
1507
1508 if (auto* input = HTMLInputElement::FromNode(aElement)) {
1509 if (input->IsSingleLineTextControl()) {
1510 return true;
1511 }
1512 }
1513 }
1514
1515 switch (nsFocusManager::GetFocusMoveActionCause(aFocusFlags)) {
1516 case InputContextAction::CAUSE_KEY:
1517 // If the user interacts with the page via the keyboard, the currently
1518 // focused element should match :focus-visible (i.e. keyboard usage may
1519 // change whether this pseudo-class matches even if it doesn't affect
1520 // :focus).
1521 return true;
1522 case InputContextAction::CAUSE_UNKNOWN:
1523 // We render outlines if the last "known" focus method was by key or there
1524 // was no previous known focus method, otherwise we don't.
1525 return aWindow->UnknownFocusMethodShouldShowOutline();
1526 case InputContextAction::CAUSE_MOUSE:
1527 case InputContextAction::CAUSE_TOUCH:
1528 case InputContextAction::CAUSE_LONGPRESS:
1529 // If the user interacts with the page via a pointing device, such that
1530 // the focus is moved to a new element which does not support user input,
1531 // the newly focused element should not match :focus-visible.
1532 return false;
1533 case InputContextAction::CAUSE_UNKNOWN_CHROME:
1534 case InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT:
1535 case InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
1536 // TODO(emilio): We could return some of these though, looking at
1537 // UserActivation. We may want to suppress focus rings for unknown /
1538 // programatic focus if the user is interacting with the page but not
1539 // during keyboard input, or such.
1540 MOZ_ASSERT_UNREACHABLE(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"These don't get returned by GetFocusMoveActionCause" ")", "./../../../dom/base/nsFocusManager.cpp"
, 1541); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "These don't get returned by GetFocusMoveActionCause"
")"); do { MOZ_CrashSequence(__null, 1541); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1541 "These don't get returned by GetFocusMoveActionCause")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"These don't get returned by GetFocusMoveActionCause" ")", "./../../../dom/base/nsFocusManager.cpp"
, 1541); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "These don't get returned by GetFocusMoveActionCause"
")"); do { MOZ_CrashSequence(__null, 1541); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1542 break;
1543 }
1544 return false;
1545}
1546
1547/* static */
1548void nsFocusManager::NotifyFocusStateChange(Element* aElement,
1549 Element* aElementToFocus,
1550 int32_t aFlags, bool aGettingFocus,
1551 bool aShouldShowFocusRing) {
1552 MOZ_ASSERT_IF(aElementToFocus, !aGettingFocus)do { if (aElementToFocus) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(!aGettingFocus)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!aGettingFocus))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!aGettingFocus", "./../../../dom/base/nsFocusManager.cpp", 1552
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aGettingFocus" ")"
); do { MOZ_CrashSequence(__null, 1552); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
1553 nsIContent* commonAncestor = nullptr;
1554 if (aElementToFocus) {
1555 commonAncestor = nsContentUtils::GetCommonFlattenedTreeAncestor(
1556 aElement, aElementToFocus);
1557 }
1558
1559 if (aGettingFocus) {
1560 ElementState stateToAdd = ElementState::FOCUS;
1561 if (aShouldShowFocusRing) {
1562 stateToAdd |= ElementState::FOCUSRING;
1563 }
1564 aElement->AddStates(stateToAdd);
1565
1566 for (nsIContent* host = aElement->GetContainingShadowHost(); host;
1567 host = host->GetContainingShadowHost()) {
1568 host->AsElement()->AddStates(ElementState::FOCUS);
1569 }
1570 } else {
1571 constexpr auto kStatesToRemove =
1572 ElementState::FOCUS | ElementState::FOCUSRING;
1573 aElement->RemoveStates(kStatesToRemove);
1574 for (nsIContent* host = aElement->GetContainingShadowHost(); host;
1575 host = host->GetContainingShadowHost()) {
1576 host->AsElement()->RemoveStates(kStatesToRemove);
1577 }
1578 }
1579
1580 // Special case for <input type="checkbox"> and <input type="radio">.
1581 // The other browsers cancel active state when they gets lost focus, but
1582 // does not do it for the other elements such as <button> and <a href="...">.
1583 // Additionally, they may be activated with <label>, but they will get focus
1584 // at `click`, but activated at `mousedown`. Therefore, we need to cancel
1585 // active state at moving focus.
1586 if (RefPtr<nsPresContext> presContext =
1587 aElement->GetPresContext(Element::PresContextFor::eForComposedDoc)) {
1588 RefPtr<EventStateManager> esm = presContext->EventStateManager();
1589 auto* activeInputElement =
1590 HTMLInputElement::FromNodeOrNull(esm->GetActiveContent());
1591 if (activeInputElement &&
1592 (activeInputElement->ControlType() == FormControlType::InputCheckbox ||
1593 activeInputElement->ControlType() == FormControlType::InputRadio) &&
1594 !activeInputElement->State().HasState(ElementState::FOCUS)) {
1595 esm->SetContentState(nullptr, ElementState::ACTIVE);
1596 }
1597 }
1598
1599 UpdateFocusWithinState(aElement, commonAncestor, aGettingFocus);
1600}
1601
1602// static
1603void nsFocusManager::EnsureCurrentWidgetFocused(CallerType aCallerType) {
1604 if (!mFocusedWindow || sTestMode) return;
1605
1606 // get the main child widget for the focused window and ensure that the
1607 // platform knows that this widget is focused.
1608 nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
1609 if (!docShell) {
1610 return;
1611 }
1612 RefPtr<PresShell> presShell = docShell->GetPresShell();
1613 if (!presShell) {
1614 return;
1615 }
1616 nsCOMPtr<nsIWidget> widget = presShell->GetRootWidget();
1617 if (!widget) {
1618 return;
1619 }
1620 widget->SetFocus(nsIWidget::Raise::No, aCallerType);
1621}
1622
1623void nsFocusManager::ActivateOrDeactivate(nsPIDOMWindowOuter* aWindow,
1624 bool aActive) {
1625 MOZ_ASSERT(XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 1625); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 1625); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1626 if (!aWindow) {
1627 return;
1628 }
1629
1630 if (BrowsingContext* bc = aWindow->GetBrowsingContext()) {
1631 MOZ_ASSERT(bc->IsTop())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bc->IsTop())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(bc->IsTop()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("bc->IsTop()"
, "./../../../dom/base/nsFocusManager.cpp", 1631); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "bc->IsTop()" ")"); do { MOZ_CrashSequence
(__null, 1631); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1632
1633 RefPtr<CanonicalBrowsingContext> chromeTop =
1634 bc->Canonical()->TopCrossChromeBoundary();
1635 MOZ_ASSERT(bc == chromeTop)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bc == chromeTop)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(bc == chromeTop))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("bc == chromeTop"
, "./../../../dom/base/nsFocusManager.cpp", 1635); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "bc == chromeTop" ")"); do { MOZ_CrashSequence
(__null, 1635); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1636
1637 chromeTop->SetIsActiveBrowserWindow(aActive);
1638 chromeTop->CallOnTopDescendants(
1639 [aActive](CanonicalBrowsingContext* aBrowsingContext) {
1640 aBrowsingContext->SetIsActiveBrowserWindow(aActive);
1641 return CallState::Continue;
1642 },
1643 CanonicalBrowsingContext::TopDescendantKind::All);
1644 }
1645
1646 if (aWindow->GetExtantDoc()) {
1647 const RefPtr<nsGlobalWindowInner> win =
1648 nsGlobalWindowInner::Cast(aWindow->GetCurrentInnerWindow());
1649 nsContentUtils::DispatchEventOnlyToChrome(
1650 win, win, aActive ? u"activate"_ns : u"deactivate"_ns, CanBubble::eYes,
1651 Cancelable::eYes, nullptr);
1652 }
1653}
1654
1655// Retrieves innerWindowId of the window of the last focused element to
1656// log a warning to the website console.
1657void LogWarningFullscreenWindowRaise(Element* aElement) {
1658 nsCOMPtr<nsFrameLoaderOwner> frameLoaderOwner(do_QueryInterface(aElement));
1659 NS_ENSURE_TRUE_VOID(frameLoaderOwner)do { if ((__builtin_expect(!!(!(frameLoaderOwner)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "frameLoaderOwner" ") failed"
, nullptr, "./../../../dom/base/nsFocusManager.cpp", 1659); return
; } } while (false)
;
1660
1661 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
1662 NS_ENSURE_TRUE_VOID(frameLoaderOwner)do { if ((__builtin_expect(!!(!(frameLoaderOwner)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "frameLoaderOwner" ") failed"
, nullptr, "./../../../dom/base/nsFocusManager.cpp", 1662); return
; } } while (false)
;
1663
1664 RefPtr<BrowsingContext> browsingContext = frameLoader->GetBrowsingContext();
1665 NS_ENSURE_TRUE_VOID(browsingContext)do { if ((__builtin_expect(!!(!(browsingContext)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "browsingContext" ") failed"
, nullptr, "./../../../dom/base/nsFocusManager.cpp", 1665); return
; } } while (false)
;
1666
1667 WindowGlobalParent* windowGlobalParent =
1668 browsingContext->Canonical()->GetCurrentWindowGlobal();
1669 NS_ENSURE_TRUE_VOID(windowGlobalParent)do { if ((__builtin_expect(!!(!(windowGlobalParent)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "windowGlobalParent" ") failed"
, nullptr, "./../../../dom/base/nsFocusManager.cpp", 1669); return
; } } while (false)
;
1670
1671 // Log to console
1672 nsAutoString localizedMsg;
1673 nsTArray<nsString> params;
1674 nsresult rv = nsContentUtils::FormatLocalizedString(
1675 PropertiesFile::DOM_PROPERTIES, "FullscreenExitWindowFocus", params,
1676 localizedMsg);
1677
1678 NS_ENSURE_SUCCESS_VOID(rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS_VOID(%s) failed with "
"result 0x%" "X" "%s%s%s", "rv", static_cast<uint32_t>
(__rv), name ? " (" : "", name ? name : "", name ? ")" : "");
NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../dom/base/nsFocusManager.cpp"
, 1678); return; } } while (false)
;
1679
1680 (void)nsContentUtils::ReportToConsoleByWindowID(
1681 localizedMsg, nsIScriptError::warningFlag, "DOM"_ns,
1682 windowGlobalParent->InnerWindowId(),
1683 SourceLocation(windowGlobalParent->GetDocumentURI()));
1684}
1685
1686// Ensure that when an embedded popup with a noautofocus attribute
1687// like a date picker is opened and focused, the parent page does not blur
1688static bool IsEmeddededInNoautofocusPopup(BrowsingContext& aBc) {
1689 auto* embedder = aBc.GetEmbedderElement();
1690 if (!embedder) {
1691 return false;
1692 }
1693 nsIFrame* f = embedder->GetPrimaryFrame();
1694 if (!f || !f->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1695 return false;
1696 }
1697
1698 nsIFrame* menuPopup =
1699 nsLayoutUtils::GetClosestFrameOfType(f, LayoutFrameType::MenuPopup);
1700 MOZ_ASSERT(menuPopup, "NS_FRAME_IN_POPUP lied?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(menuPopup)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(menuPopup))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("menuPopup" " (" "NS_FRAME_IN_POPUP lied?"
")", "./../../../dom/base/nsFocusManager.cpp", 1700); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "menuPopup" ") (" "NS_FRAME_IN_POPUP lied?"
")"); do { MOZ_CrashSequence(__null, 1700); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1701 return static_cast<nsMenuPopupFrame*>(menuPopup)->PopupElement().GetBoolAttr(
1702 nsGkAtoms::noautofocus);
1703}
1704
1705Maybe<uint64_t> nsFocusManager::SetFocusInner(Element* aNewContent,
1706 int32_t aFlags,
1707 bool aFocusChanged,
1708 bool aAdjustWidget) {
1709 // if the element is not focusable, just return and leave the focus as is
1710 RefPtr<Element> elementToFocus =
1711 FlushAndCheckIfFocusable(aNewContent, aFlags);
1712 if (!elementToFocus) {
1713 return Nothing();
1714 }
1715
1716 const RefPtr<BrowsingContext> focusedBrowsingContext =
1717 GetFocusedBrowsingContext();
1718
1719 // check if the element to focus is a frame (iframe) containing a child
1720 // document. Frames are never directly focused; instead focusing a frame
1721 // means focus what is inside the frame. To do this, the descendant content
1722 // within the frame is retrieved and that will be focused instead.
1723 nsCOMPtr<nsPIDOMWindowOuter> newWindow;
1724 nsCOMPtr<nsPIDOMWindowOuter> subWindow = GetContentWindow(elementToFocus);
1725 if (subWindow) {
1726 elementToFocus = GetFocusedDescendant(subWindow, eIncludeAllDescendants,
1727 getter_AddRefs(newWindow));
1728
1729 // since a window is being refocused, clear aFocusChanged so that the
1730 // caret position isn't updated.
1731 aFocusChanged = false;
1732 }
1733
1734 // unless it was set above, retrieve the window for the element to focus
1735 if (!newWindow) {
1736 newWindow = GetCurrentWindow(elementToFocus);
1737 }
1738
1739 RefPtr<BrowsingContext> newBrowsingContext;
1740 if (newWindow) {
1741 newBrowsingContext = newWindow->GetBrowsingContext();
1742 }
1743
1744 // if the element is already focused, just return. Note that this happens
1745 // after the frame check above so that we compare the element that will be
1746 // focused rather than the frame it is in.
1747 if (!newWindow || (newBrowsingContext == GetFocusedBrowsingContext() &&
1748 elementToFocus == mFocusedElement)) {
1749 return Nothing();
1750 }
1751
1752 MOZ_ASSERT(newBrowsingContext)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(newBrowsingContext)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(newBrowsingContext))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("newBrowsingContext"
, "./../../../dom/base/nsFocusManager.cpp", 1752); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "newBrowsingContext" ")"); do { MOZ_CrashSequence
(__null, 1752); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1753
1754 BrowsingContext* browsingContextToFocus = newBrowsingContext;
1755 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(elementToFocus)) {
1756 // Only look at pre-existing browsing contexts. If this function is
1757 // called during reflow, calling GetBrowsingContext() could cause frame
1758 // loader initialization at a time when it isn't safe.
1759 if (BrowsingContext* bc = flo->GetExtantBrowsingContext()) {
1760 // If focus is already in the subtree rooted at bc, return early
1761 // to match the single-process focus semantics. Otherwise, we'd
1762 // blur and immediately refocus whatever is focused.
1763 BrowsingContext* walk = focusedBrowsingContext;
1764 while (walk) {
1765 if (walk == bc) {
1766 return Nothing();
1767 }
1768 walk = walk->GetParent();
1769 }
1770 browsingContextToFocus = bc;
1771 }
1772 }
1773
1774 // don't allow focus to be placed in docshells or descendants of docshells
1775 // that are being destroyed. Also, ensure that the page hasn't been
1776 // unloaded. The prevents content from being refocused during an unload event.
1777 nsCOMPtr<nsIDocShell> newDocShell = newWindow->GetDocShell();
1778 nsCOMPtr<nsIDocShell> docShell = newDocShell;
1779 while (docShell) {
1780 bool inUnload;
1781 docShell->GetIsInUnload(&inUnload);
1782 if (inUnload) {
1783 return Nothing();
1784 }
1785
1786 bool beingDestroyed;
1787 docShell->IsBeingDestroyed(&beingDestroyed);
1788 if (beingDestroyed) {
1789 return Nothing();
1790 }
1791
1792 BrowsingContext* bc = docShell->GetBrowsingContext();
1793
1794 nsCOMPtr<nsIDocShellTreeItem> parentDsti;
1795 docShell->GetInProcessParent(getter_AddRefs(parentDsti));
1796 docShell = do_QueryInterface(parentDsti);
1797 if (!docShell && !XRE_IsParentProcess()) {
1798 // We don't have an in-process parent, but let's see if we have
1799 // an in-process ancestor or if an out-of-process ancestor
1800 // is discarded.
1801 do {
1802 bc = bc->GetParent();
1803 if (bc && bc->IsDiscarded()) {
1804 return Nothing();
1805 }
1806 } while (bc && !bc->IsInProcess());
1807 if (bc) {
1808 docShell = bc->GetDocShell();
1809 } else {
1810 docShell = nullptr;
1811 }
1812 }
1813 }
1814
1815 bool focusMovesToDifferentBC =
1816 (focusedBrowsingContext != browsingContextToFocus);
1817
1818 if (focusedBrowsingContext && focusMovesToDifferentBC &&
1819 nsContentUtils::IsHandlingKeyBoardEvent() &&
1820 !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
1821 MOZ_ASSERT(browsingContextToFocus,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(browsingContextToFocus)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(browsingContextToFocus))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("browsingContextToFocus"
" (" "BrowsingContext to focus should be non-null." ")", "./../../../dom/base/nsFocusManager.cpp"
, 1822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "browsingContextToFocus"
") (" "BrowsingContext to focus should be non-null." ")"); do
{ MOZ_CrashSequence(__null, 1822); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
1822 "BrowsingContext to focus should be non-null.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(browsingContextToFocus)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(browsingContextToFocus))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("browsingContextToFocus"
" (" "BrowsingContext to focus should be non-null." ")", "./../../../dom/base/nsFocusManager.cpp"
, 1822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "browsingContextToFocus"
") (" "BrowsingContext to focus should be non-null." ")"); do
{ MOZ_CrashSequence(__null, 1822); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1823
1824 nsIPrincipal* focusedPrincipal = nullptr;
1825 nsIPrincipal* newPrincipal = nullptr;
1826
1827 if (XRE_IsParentProcess()) {
1828 if (WindowGlobalParent* focusedWindowGlobalParent =
1829 focusedBrowsingContext->Canonical()->GetCurrentWindowGlobal()) {
1830 focusedPrincipal = focusedWindowGlobalParent->DocumentPrincipal();
1831 }
1832
1833 if (WindowGlobalParent* newWindowGlobalParent =
1834 browsingContextToFocus->Canonical()->GetCurrentWindowGlobal()) {
1835 newPrincipal = newWindowGlobalParent->DocumentPrincipal();
1836 }
1837 } else if (focusedBrowsingContext->IsInProcess() &&
1838 browsingContextToFocus->IsInProcess()) {
1839 nsCOMPtr<nsIScriptObjectPrincipal> focused =
1840 do_QueryInterface(focusedBrowsingContext->GetDOMWindow());
1841 nsCOMPtr<nsIScriptObjectPrincipal> newFocus =
1842 do_QueryInterface(browsingContextToFocus->GetDOMWindow());
1843 MOZ_ASSERT(focused && newFocus,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(focused && newFocus)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(focused && newFocus)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("focused && newFocus"
" (" "BrowsingContext should always have a window here." ")"
, "./../../../dom/base/nsFocusManager.cpp", 1844); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "focused && newFocus" ") (" "BrowsingContext should always have a window here."
")"); do { MOZ_CrashSequence(__null, 1844); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1844 "BrowsingContext should always have a window here.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(focused && newFocus)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(focused && newFocus)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("focused && newFocus"
" (" "BrowsingContext should always have a window here." ")"
, "./../../../dom/base/nsFocusManager.cpp", 1844); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "focused && newFocus" ") (" "BrowsingContext should always have a window here."
")"); do { MOZ_CrashSequence(__null, 1844); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1845 focusedPrincipal = focused->GetPrincipal();
1846 newPrincipal = newFocus->GetPrincipal();
1847 }
1848
1849 if (!focusedPrincipal || !newPrincipal) {
1850 return Nothing();
1851 }
1852
1853 if (!focusedPrincipal->Subsumes(newPrincipal)) {
1854 NS_WARNING("Not allowed to focus the new window!")NS_DebugBreak(NS_DEBUG_WARNING, "Not allowed to focus the new window!"
, nullptr, "./../../../dom/base/nsFocusManager.cpp", 1854)
;
1855 return Nothing();
1856 }
1857 }
1858
1859 // to check if the new element is in the active window, compare the
1860 // new root docshell for the new element with the active window's docshell.
1861 RefPtr<BrowsingContext> newRootBrowsingContext = nullptr;
1862 bool isElementInActiveWindow = false;
1863 if (XRE_IsParentProcess()) {
1864 nsCOMPtr<nsPIDOMWindowOuter> newRootWindow = nullptr;
1865 nsCOMPtr<nsIDocShellTreeItem> dsti = newWindow->GetDocShell();
1866 if (dsti) {
1867 nsCOMPtr<nsIDocShellTreeItem> root;
1868 dsti->GetInProcessRootTreeItem(getter_AddRefs(root));
1869 newRootWindow = root ? root->GetWindow() : nullptr;
1870
1871 isElementInActiveWindow =
1872 (mActiveWindow && newRootWindow == mActiveWindow);
1873 }
1874 if (newRootWindow) {
1875 newRootBrowsingContext = newRootWindow->GetBrowsingContext();
1876 }
1877 } else {
1878 // XXX This is wrong for `<iframe mozbrowser>` and for XUL
1879 // `<browser remote="true">`. See:
1880 // https://searchfox.org/mozilla-central/rev/8a63fc190b39ed6951abb4aef4a56487a43962bc/dom/base/nsFrameLoader.cpp#229-232
1881 newRootBrowsingContext = newBrowsingContext->Top();
1882 // to check if the new element is in the active window, compare the
1883 // new root docshell for the new element with the active window's docshell.
1884 isElementInActiveWindow =
1885 (GetActiveBrowsingContext() == newRootBrowsingContext);
1886 }
1887
1888 // Exit fullscreen if a website focuses another window
1889 if (StaticPrefs::full_screen_api_exit_on_windowRaise() &&
1890 !isElementInActiveWindow && (aFlags & FLAG_RAISE)) {
1891 if (XRE_IsParentProcess()) {
1892 if (Document* doc = mActiveWindow ? mActiveWindow->GetDoc() : nullptr) {
1893 Document::ClearPendingFullscreenRequests(doc);
1894 if (doc->GetFullscreenElement()) {
1895 LogWarningFullscreenWindowRaise(mFocusedElement);
1896 Document::AsyncExitFullscreen(doc);
1897 }
1898 }
1899 } else {
1900 BrowsingContext* activeBrowsingContext = GetActiveBrowsingContext();
1901 if (activeBrowsingContext) {
1902 nsIDocShell* shell = activeBrowsingContext->GetDocShell();
1903 if (shell) {
1904 if (Document* doc = shell->GetDocument()) {
1905 Document::ClearPendingFullscreenRequests(doc);
1906 if (doc->GetFullscreenElement()) {
1907 Document::AsyncExitFullscreen(doc);
1908 }
1909 }
1910 } else {
1911 mozilla::dom::ContentChild* contentChild =
1912 mozilla::dom::ContentChild::GetSingleton();
1913 MOZ_ASSERT(contentChild)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(contentChild)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(contentChild))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("contentChild", "./../../../dom/base/nsFocusManager.cpp"
, 1913); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contentChild"
")"); do { MOZ_CrashSequence(__null, 1913); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1914 contentChild->SendMaybeExitFullscreen(activeBrowsingContext);
1915 }
1916 }
1917 }
1918 }
1919
1920 // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
1921 // shifted away from the current element if the new shell to focus is
1922 // the same or an ancestor shell of the currently focused shell.
1923 bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
1924 IsSameOrAncestor(newWindow, focusedBrowsingContext);
1925
1926 // if the element is in the active window, frame switching is allowed and
1927 // the content is in a visible window, fire blur and focus events.
1928 bool sendFocusEvent =
1929 isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow);
1930
1931 // Don't allow to steal the focus from chrome nodes if the caller cannot
1932 // access them.
1933 if (sendFocusEvent && mFocusedElement &&
1934 mFocusedElement->OwnerDoc() != aNewContent->OwnerDoc() &&
1935 mFocusedElement->NodePrincipal()->IsSystemPrincipal() &&
1936 !nsContentUtils::LegacyIsCallerNativeCode() &&
1937 !nsContentUtils::CanCallerAccess(mFocusedElement)) {
1938 sendFocusEvent = false;
1939 }
1940
1941 LOGCONTENT("Shift Focus: %s", elementToFocus.get())if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusLog,
LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(elementToFocus.get()) { elementToFocus.get()->NodeInfo()
->NameAtom()->ToUTF8String(tag); } do { const ::mozilla
::LogModule* moz_real_module = gFocusLog; if ((__builtin_expect
(!!(mozilla::detail::log_test(moz_real_module, LogLevel::Debug
)), 0))) { mozilla::detail::log_print(moz_real_module, LogLevel
::Debug, "Shift Focus: %s", tag.get()); } } while (0); }
;
1942 LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Flags: %x Current Window: %p New Window: %p Current Element: %p"
, aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedElement
.get()); } } while (0)
1943 aFlags, mFocusedWindow.get(), newWindow.get(),do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Flags: %x Current Window: %p New Window: %p Current Element: %p"
, aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedElement
.get()); } } while (0)
1944 mFocusedElement.get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Flags: %x Current Window: %p New Window: %p Current Element: %p"
, aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedElement
.get()); } } while (0)
;
1945 const uint64_t actionId = GenerateFocusActionId();
1946 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " In Active Window: %d Moves to different BrowsingContext: %d "
"SendFocus: %d actionid: %" "l" "u", isElementInActiveWindow
, focusMovesToDifferentBC, sendFocusEvent, actionId); } } while
(0)
1947 (" In Active Window: %d Moves to different BrowsingContext: %d "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " In Active Window: %d Moves to different BrowsingContext: %d "
"SendFocus: %d actionid: %" "l" "u", isElementInActiveWindow
, focusMovesToDifferentBC, sendFocusEvent, actionId); } } while
(0)
1948 "SendFocus: %d actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " In Active Window: %d Moves to different BrowsingContext: %d "
"SendFocus: %d actionid: %" "l" "u", isElementInActiveWindow
, focusMovesToDifferentBC, sendFocusEvent, actionId); } } while
(0)
1949 isElementInActiveWindow, focusMovesToDifferentBC, sendFocusEvent,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " In Active Window: %d Moves to different BrowsingContext: %d "
"SendFocus: %d actionid: %" "l" "u", isElementInActiveWindow
, focusMovesToDifferentBC, sendFocusEvent, actionId); } } while
(0)
1950 actionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " In Active Window: %d Moves to different BrowsingContext: %d "
"SendFocus: %d actionid: %" "l" "u", isElementInActiveWindow
, focusMovesToDifferentBC, sendFocusEvent, actionId); } } while
(0)
;
1951
1952 if (sendFocusEvent) {
1953 Maybe<BlurredElementInfo> blurredInfo;
1954 if (mFocusedElement) {
1955 blurredInfo.emplace(*mFocusedElement);
1956 }
1957 // return if blurring fails or the focus changes during the blur
1958 if (focusedBrowsingContext) {
1959 // find the common ancestor of the currently focused window and the new
1960 // window. The ancestor will need to have its currently focused node
1961 // cleared once the document has been blurred. Otherwise, we'll be in a
1962 // state where a document is blurred yet the chain of windows above it
1963 // still points to that document.
1964 // For instance, in the following frame tree:
1965 // A
1966 // B C
1967 // D
1968 // D is focused and we want to focus C. Once D has been blurred, we need
1969 // to clear out the focus in A, otherwise A would still maintain that B
1970 // was focused, and B that D was focused.
1971 RefPtr<BrowsingContext> commonAncestor =
1972 focusMovesToDifferentBC
1973 ? GetCommonAncestor(newWindow, focusedBrowsingContext)
1974 : nullptr;
1975
1976 const bool needToClearFocusedElement = [&] {
1977 if (focusedBrowsingContext->IsChrome()) {
1978 // Always reset focused element if focus is currently in chrome
1979 // window, unless we're moving focus to a popup.
1980 return !IsEmeddededInNoautofocusPopup(*browsingContextToFocus);
1981 }
1982 if (focusedBrowsingContext->Top() != browsingContextToFocus->Top()) {
1983 // Only reset focused element if focus moves within the same top-level
1984 // content window.
1985 return false;
1986 }
1987 // XXX for the case that we try to focus an
1988 // already-focused-remote-frame, we would still send blur and focus
1989 // IPC to it, but they will not generate blur or focus event, we don't
1990 // want to reset activeElement on the remote frame.
1991 return focusMovesToDifferentBC || focusedBrowsingContext->IsInProcess();
1992 }();
1993
1994 const bool remainActive =
1995 focusMovesToDifferentBC &&
1996 IsEmeddededInNoautofocusPopup(*browsingContextToFocus);
1997
1998 // TODO: MOZ_KnownLive is required due to bug 1770680
1999 if (!Blur(MOZ_KnownLive(needToClearFocusedElement(needToClearFocusedElement ? focusedBrowsingContext.get() : nullptr
)
2000 ? focusedBrowsingContext.get()(needToClearFocusedElement ? focusedBrowsingContext.get() : nullptr
)
2001 : nullptr)(needToClearFocusedElement ? focusedBrowsingContext.get() : nullptr
)
,
2002 commonAncestor, focusMovesToDifferentBC, aAdjustWidget,
2003 remainActive, actionId, elementToFocus)) {
2004 MaybeFixUpFocusWithinState(elementToFocus, mFocusedElement);
2005 return Some(actionId);
2006 }
2007 }
2008
2009 Focus(newWindow, elementToFocus, aFlags, focusMovesToDifferentBC,
2010 aFocusChanged, false, aAdjustWidget, actionId, blurredInfo);
2011 } else {
2012 // otherwise, for inactive windows and when the caller cannot steal the
2013 // focus, update the node in the window, and raise the window if desired.
2014 if (allowFrameSwitch) {
2015 AdjustWindowFocus(newBrowsingContext, true, IsWindowVisible(newWindow),
2016 actionId, false /* aShouldClearAncestorFocus */,
2017 nullptr /* aAncestorBrowsingContextToFocus */);
2018 }
2019
2020 // set the focus node and method as needed
2021 uint32_t focusMethod =
2022 aFocusChanged ? aFlags & METHODANDRING_MASK
2023 : newWindow->GetFocusMethod() |
2024 (aFlags & (FLAG_SHOWRING | FLAG_NOSHOWRING));
2025 newWindow->SetFocusedElement(elementToFocus, focusMethod);
2026 if (aFocusChanged) {
2027 if (nsCOMPtr<nsIDocShell> docShell = newWindow->GetDocShell()) {
2028 RefPtr<PresShell> presShell = docShell->GetPresShell();
2029 if (presShell && presShell->DidInitialize()) {
2030 ScrollIntoView(presShell, elementToFocus, aFlags);
2031 }
2032 }
2033 }
2034
2035 // update the commands even when inactive so that the attributes for that
2036 // window are up to date.
2037 if (allowFrameSwitch) {
2038 newWindow->UpdateCommands(u"focus"_ns);
2039 }
2040
2041 if (aFlags & FLAG_RAISE) {
2042 if (newRootBrowsingContext) {
2043 if (XRE_IsParentProcess() || newRootBrowsingContext->IsInProcess()) {
2044 nsCOMPtr<nsPIDOMWindowOuter> outerWindow =
2045 newRootBrowsingContext->GetDOMWindow();
2046 RaiseWindow(outerWindow,
2047 aFlags & FLAG_NONSYSTEMCALLER ? CallerType::NonSystem
2048 : CallerType::System,
2049 actionId);
2050 } else {
2051 mozilla::dom::ContentChild* contentChild =
2052 mozilla::dom::ContentChild::GetSingleton();
2053 MOZ_ASSERT(contentChild)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(contentChild)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(contentChild))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("contentChild", "./../../../dom/base/nsFocusManager.cpp"
, 2053); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contentChild"
")"); do { MOZ_CrashSequence(__null, 2053); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2054 contentChild->SendRaiseWindow(newRootBrowsingContext,
2055 aFlags & FLAG_NONSYSTEMCALLER
2056 ? CallerType::NonSystem
2057 : CallerType::System,
2058 actionId);
2059 }
2060 }
2061 }
2062 }
2063 return Some(actionId);
2064}
2065
2066static BrowsingContext* GetParentIgnoreChromeBoundary(BrowsingContext* aBC) {
2067 // Chrome BrowsingContexts are only available in the parent process, so if
2068 // we're in a content process, we only worry about the context tree.
2069 if (XRE_IsParentProcess()) {
2070 return aBC->Canonical()->GetParentCrossChromeBoundary();
2071 }
2072 return aBC->GetParent();
2073}
2074
2075bool nsFocusManager::IsSameOrAncestor(BrowsingContext* aPossibleAncestor,
2076 BrowsingContext* aContext) const {
2077 if (!aPossibleAncestor) {
2078 return false;
2079 }
2080
2081 for (BrowsingContext* bc = aContext; bc;
2082 bc = GetParentIgnoreChromeBoundary(bc)) {
2083 if (bc == aPossibleAncestor) {
2084 return true;
2085 }
2086 }
2087
2088 return false;
2089}
2090
2091bool nsFocusManager::IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor,
2092 nsPIDOMWindowOuter* aWindow) const {
2093 if (aWindow && aPossibleAncestor) {
2094 return IsSameOrAncestor(aPossibleAncestor->GetBrowsingContext(),
2095 aWindow->GetBrowsingContext());
2096 }
2097 return false;
2098}
2099
2100bool nsFocusManager::IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor,
2101 BrowsingContext* aContext) const {
2102 if (aPossibleAncestor) {
2103 return IsSameOrAncestor(aPossibleAncestor->GetBrowsingContext(), aContext);
2104 }
2105 return false;
2106}
2107
2108bool nsFocusManager::IsSameOrAncestor(BrowsingContext* aPossibleAncestor,
2109 nsPIDOMWindowOuter* aWindow) const {
2110 if (aWindow) {
2111 return IsSameOrAncestor(aPossibleAncestor, aWindow->GetBrowsingContext());
2112 }
2113 return false;
2114}
2115
2116mozilla::dom::BrowsingContext* nsFocusManager::GetCommonAncestor(
2117 nsPIDOMWindowOuter* aWindow, mozilla::dom::BrowsingContext* aContext) {
2118 NS_ENSURE_TRUE(aWindow && aContext, nullptr)do { if ((__builtin_expect(!!(!(aWindow && aContext))
, 0))) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aWindow && aContext"
") failed", nullptr, "./../../../dom/base/nsFocusManager.cpp"
, 2118); return nullptr; } } while (false)
;
2119
2120 if (XRE_IsParentProcess()) {
2121 nsCOMPtr<nsIDocShellTreeItem> dsti1 = aWindow->GetDocShell();
2122 NS_ENSURE_TRUE(dsti1, nullptr)do { if ((__builtin_expect(!!(!(dsti1)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "dsti1" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 2122); return nullptr
; } } while (false)
;
2123
2124 nsCOMPtr<nsIDocShellTreeItem> dsti2 = aContext->GetDocShell();
2125 NS_ENSURE_TRUE(dsti2, nullptr)do { if ((__builtin_expect(!!(!(dsti2)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "dsti2" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 2125); return nullptr
; } } while (false)
;
2126
2127 AutoTArray<nsIDocShellTreeItem*, 30> parents1, parents2;
2128 do {
2129 parents1.AppendElement(dsti1);
2130 nsCOMPtr<nsIDocShellTreeItem> parentDsti1;
2131 dsti1->GetInProcessParent(getter_AddRefs(parentDsti1));
2132 dsti1.swap(parentDsti1);
2133 } while (dsti1);
2134 do {
2135 parents2.AppendElement(dsti2);
2136 nsCOMPtr<nsIDocShellTreeItem> parentDsti2;
2137 dsti2->GetInProcessParent(getter_AddRefs(parentDsti2));
2138 dsti2.swap(parentDsti2);
2139 } while (dsti2);
2140
2141 uint32_t pos1 = parents1.Length();
2142 uint32_t pos2 = parents2.Length();
2143 nsIDocShellTreeItem* parent = nullptr;
2144 uint32_t len;
2145 for (len = std::min(pos1, pos2); len > 0; --len) {
2146 nsIDocShellTreeItem* child1 = parents1.ElementAt(--pos1);
2147 nsIDocShellTreeItem* child2 = parents2.ElementAt(--pos2);
2148 if (child1 != child2) {
2149 break;
2150 }
2151 parent = child1;
2152 }
2153
2154 return parent ? parent->GetBrowsingContext() : nullptr;
2155 }
2156
2157 BrowsingContext* bc1 = aWindow->GetBrowsingContext();
2158 NS_ENSURE_TRUE(bc1, nullptr)do { if ((__builtin_expect(!!(!(bc1)), 0))) { NS_DebugBreak(NS_DEBUG_WARNING
, "NS_ENSURE_TRUE(" "bc1" ") failed", nullptr, "./../../../dom/base/nsFocusManager.cpp"
, 2158); return nullptr; } } while (false)
;
2159
2160 BrowsingContext* bc2 = aContext;
2161
2162 AutoTArray<BrowsingContext*, 30> parents1, parents2;
2163 do {
2164 parents1.AppendElement(bc1);
2165 bc1 = bc1->GetParent();
2166 } while (bc1);
2167 do {
2168 parents2.AppendElement(bc2);
2169 bc2 = bc2->GetParent();
2170 } while (bc2);
2171
2172 uint32_t pos1 = parents1.Length();
2173 uint32_t pos2 = parents2.Length();
2174 BrowsingContext* parent = nullptr;
2175 uint32_t len;
2176 for (len = std::min(pos1, pos2); len > 0; --len) {
2177 BrowsingContext* child1 = parents1.ElementAt(--pos1);
2178 BrowsingContext* child2 = parents2.ElementAt(--pos2);
2179 if (child1 != child2) {
2180 break;
2181 }
2182 parent = child1;
2183 }
2184
2185 return parent;
2186}
2187
2188bool nsFocusManager::AdjustInProcessWindowFocus(
2189 BrowsingContext* aBrowsingContext, bool aCheckPermission, bool aIsVisible,
2190 uint64_t aActionId, bool aShouldClearAncestorFocus,
2191 BrowsingContext* aAncestorBrowsingContextToFocus) {
2192 MOZ_ASSERT_IF(aAncestorBrowsingContextToFocus, aShouldClearAncestorFocus)do { if (aAncestorBrowsingContextToFocus) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(aShouldClearAncestorFocus
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aShouldClearAncestorFocus))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("aShouldClearAncestorFocus", "./../../../dom/base/nsFocusManager.cpp"
, 2192); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aShouldClearAncestorFocus"
")"); do { MOZ_CrashSequence(__null, 2192); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2193 if (ActionIdComparableAndLower(aActionId,
2194 mActionIdForFocusedBrowsingContextInContent)) {
2195 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to adjust an in-process BrowsingContext [%p] as "
"focused from another process due to stale action id %" "l" "u"
".", aBrowsingContext, aActionId); } } while (0)
2196 ("Ignored an attempt to adjust an in-process BrowsingContext [%p] as "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to adjust an in-process BrowsingContext [%p] as "
"focused from another process due to stale action id %" "l" "u"
".", aBrowsingContext, aActionId); } } while (0)
2197 "focused from another process due to stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to adjust an in-process BrowsingContext [%p] as "
"focused from another process due to stale action id %" "l" "u"
".", aBrowsingContext, aActionId); } } while (0)
2198 aBrowsingContext, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to adjust an in-process BrowsingContext [%p] as "
"focused from another process due to stale action id %" "l" "u"
".", aBrowsingContext, aActionId); } } while (0)
;
2199 return false;
2200 }
2201
2202 BrowsingContext* bc = aBrowsingContext;
2203 bool needToNotifyOtherProcess = false;
2204 while (bc) {
2205 // get the containing <iframe> or equivalent element so that it can be
2206 // focused below.
2207 nsCOMPtr<Element> frameElement = bc->GetEmbedderElement();
2208 BrowsingContext* parent = bc->GetParent();
2209 if (!parent && XRE_IsParentProcess()) {
2210 CanonicalBrowsingContext* canonical = bc->Canonical();
2211 RefPtr<WindowGlobalParent> embedder =
2212 canonical->GetEmbedderWindowGlobal();
2213 if (embedder) {
2214 parent = embedder->BrowsingContext();
2215 }
2216 }
2217 bc = parent;
2218 if (!bc) {
2219 break;
2220 }
2221 if (!frameElement && XRE_IsContentProcess()) {
2222 needToNotifyOtherProcess = true;
2223 continue;
2224 }
2225
2226 nsCOMPtr<nsPIDOMWindowOuter> window = bc->GetDOMWindow();
2227 MOZ_ASSERT(window)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(window)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(window))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("window", "./../../../dom/base/nsFocusManager.cpp"
, 2227); AnnotateMozCrashReason("MOZ_ASSERT" "(" "window" ")"
); do { MOZ_CrashSequence(__null, 2227); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2228 // if the parent window is visible but the original window was not, then we
2229 // have likely moved up and out from a hidden tab to the browser window, or
2230 // a similar such arrangement. Stop adjusting the current nodes.
2231 if (IsWindowVisible(window) != aIsVisible) {
2232 break;
2233 }
2234
2235 // When aCheckPermission is true, we should check whether the caller can
2236 // access the window or not. If it cannot access, we should stop the
2237 // adjusting.
2238 if (aCheckPermission && !nsContentUtils::LegacyIsCallerNativeCode() &&
2239 !nsContentUtils::CanCallerAccess(window->GetCurrentInnerWindow())) {
2240 break;
2241 }
2242
2243 if (aShouldClearAncestorFocus) {
2244 // This is the BrowsingContext that receives the focus, no need to clear
2245 // its focused element and the rest of the ancestors.
2246 if (window->GetBrowsingContext() == aAncestorBrowsingContextToFocus) {
2247 break;
2248 }
2249
2250 window->SetFocusedElement(nullptr);
2251 continue;
2252 }
2253
2254 if (frameElement != window->GetFocusedElement()) {
2255 window->SetFocusedElement(frameElement);
2256
2257 RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(frameElement);
2258 MOZ_ASSERT(loaderOwner)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(loaderOwner)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(loaderOwner))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("loaderOwner", "./../../../dom/base/nsFocusManager.cpp"
, 2258); AnnotateMozCrashReason("MOZ_ASSERT" "(" "loaderOwner"
")"); do { MOZ_CrashSequence(__null, 2258); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2259 RefPtr<nsFrameLoader> loader = loaderOwner->GetFrameLoader();
2260 if (loader && loader->IsRemoteFrame() &&
2261 GetFocusedBrowsingContext() == bc) {
2262 Blur(nullptr, nullptr, true, true, false, aActionId);
2263 }
2264 }
2265 }
2266 return needToNotifyOtherProcess;
2267}
2268
2269void nsFocusManager::AdjustWindowFocus(
2270 BrowsingContext* aBrowsingContext, bool aCheckPermission, bool aIsVisible,
2271 uint64_t aActionId, bool aShouldClearAncestorFocus,
2272 BrowsingContext* aAncestorBrowsingContextToFocus) {
2273 MOZ_ASSERT_IF(aAncestorBrowsingContextToFocus, aShouldClearAncestorFocus)do { if (aAncestorBrowsingContextToFocus) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(aShouldClearAncestorFocus
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aShouldClearAncestorFocus))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("aShouldClearAncestorFocus", "./../../../dom/base/nsFocusManager.cpp"
, 2273); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aShouldClearAncestorFocus"
")"); do { MOZ_CrashSequence(__null, 2273); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2274 if (AdjustInProcessWindowFocus(aBrowsingContext, aCheckPermission, aIsVisible,
2275 aActionId, aShouldClearAncestorFocus,
2276 aAncestorBrowsingContextToFocus)) {
2277 // Some ancestors of aBrowsingContext isn't in this process, so notify other
2278 // processes to adjust their focused element.
2279 mozilla::dom::ContentChild* contentChild =
2280 mozilla::dom::ContentChild::GetSingleton();
2281 MOZ_ASSERT(contentChild)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(contentChild)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(contentChild))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("contentChild", "./../../../dom/base/nsFocusManager.cpp"
, 2281); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contentChild"
")"); do { MOZ_CrashSequence(__null, 2281); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2282 contentChild->SendAdjustWindowFocus(aBrowsingContext, aIsVisible, aActionId,
2283 aShouldClearAncestorFocus,
2284 aAncestorBrowsingContextToFocus);
2285 }
2286}
2287
2288bool nsFocusManager::IsWindowVisible(nsPIDOMWindowOuter* aWindow) {
2289 if (!aWindow || nsGlobalWindowOuter::Cast(aWindow)->IsFrozen()) {
2290 return false;
2291 }
2292
2293 // Check if the inner window is frozen as well. This can happen when a focus
2294 // change occurs while restoring a previous page.
2295 auto* innerWindow =
2296 nsGlobalWindowInner::Cast(aWindow->GetCurrentInnerWindow());
2297 if (!innerWindow || innerWindow->IsFrozen()) {
2298 return false;
2299 }
2300
2301 nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
2302 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(docShell));
2303 if (!baseWin) {
2304 return false;
2305 }
2306
2307 bool visible = false;
2308 baseWin->GetVisibility(&visible);
2309 return visible;
2310}
2311
2312bool nsFocusManager::IsNonFocusableRoot(nsIContent* aContent) {
2313 MOZ_ASSERT(aContent, "aContent must not be NULL")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aContent" " (" "aContent must not be NULL"
")", "./../../../dom/base/nsFocusManager.cpp", 2313); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aContent" ") (" "aContent must not be NULL"
")"); do { MOZ_CrashSequence(__null, 2313); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2314 MOZ_ASSERT(aContent->IsInComposedDoc(), "aContent must be in a document")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContent->IsInComposedDoc())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContent->IsInComposedDoc
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aContent->IsInComposedDoc()" " (" "aContent must be in a document"
")", "./../../../dom/base/nsFocusManager.cpp", 2314); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aContent->IsInComposedDoc()" ") (" "aContent must be in a document"
")"); do { MOZ_CrashSequence(__null, 2314); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2315
2316 // If the uncomposed document of aContent is in designMode, the root element
2317 // is not focusable.
2318 // NOTE: Most elements whose uncomposed document is in design mode are not
2319 // focusable, just the document is focusable. However, if it's in a
2320 // shadow tree, it may be focus able even if the shadow host is in
2321 // design mode.
2322 // Also, if aContent is not editable and it's not in designMode, it's not
2323 // focusable.
2324 // And in userfocusignored context nothing is focusable.
2325 Document* doc = aContent->GetComposedDoc();
2326 NS_ASSERTION(doc, "aContent must have current document")do { if (!(doc)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aContent must have current document"
, "doc", "./../../../dom/base/nsFocusManager.cpp", 2326); MOZ_PretendNoReturn
(); } } while (0)
;
2327 return aContent == doc->GetRootElement() &&
2328 (aContent->IsInDesignMode() || !aContent->IsEditable());
2329}
2330
2331Element* nsFocusManager::FlushAndCheckIfFocusable(Element* aElement,
2332 uint32_t aFlags) {
2333 if (!aElement) {
2334 return nullptr;
2335 }
2336
2337 nsCOMPtr<Document> doc = aElement->GetComposedDoc();
2338 // can't focus elements that are not in documents
2339 if (!doc) {
2340 LOGCONTENT("Cannot focus %s because content not in document", aElement)if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusLog,
LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(aElement) { aElement->NodeInfo()->NameAtom()->ToUTF8String
(tag); } do { const ::mozilla::LogModule* moz_real_module = gFocusLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "Cannot focus %s because content not in document"
, tag.get()); } } while (0); }
2341 return nullptr;
2342 }
2343
2344 // Make sure that our frames are up to date while ensuring the presshell is
2345 // also initialized in case we come from a script calling focus() early.
2346 mEventHandlingNeedsFlush = false;
2347 doc->FlushPendingNotifications(FlushType::EnsurePresShellInitAndFrames);
2348
2349 return GetTheFocusableArea(aElement, aFlags);
2350}
2351
2352bool nsFocusManager::Blur(BrowsingContext* aBrowsingContextToClear,
2353 BrowsingContext* aAncestorBrowsingContextToFocus,
2354 bool aIsLeavingDocument, bool aAdjustWidget,
2355 bool aRemainActive, uint64_t aActionId,
2356 Element* aElementToFocus) {
2357 if (XRE_IsParentProcess()) {
2358 return BlurImpl(aBrowsingContextToClear, aAncestorBrowsingContextToFocus,
2359 aIsLeavingDocument, aAdjustWidget, aRemainActive,
2360 aElementToFocus, aActionId);
2361 }
2362 mozilla::dom::ContentChild* contentChild =
2363 mozilla::dom::ContentChild::GetSingleton();
2364 MOZ_ASSERT(contentChild)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(contentChild)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(contentChild))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("contentChild", "./../../../dom/base/nsFocusManager.cpp"
, 2364); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contentChild"
")"); do { MOZ_CrashSequence(__null, 2364); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2365 bool windowToClearHandled = false;
2366 bool ancestorWindowToFocusHandled = false;
2367
2368 RefPtr<BrowsingContext> focusedBrowsingContext = GetFocusedBrowsingContext();
2369 if (focusedBrowsingContext && focusedBrowsingContext->IsDiscarded()) {
2370 focusedBrowsingContext = nullptr;
2371 }
2372 if (!focusedBrowsingContext) {
2373 mFocusedElement = nullptr;
2374 return true;
2375 }
2376 if (aBrowsingContextToClear && aBrowsingContextToClear->IsDiscarded()) {
2377 aBrowsingContextToClear = nullptr;
2378 }
2379 if (aAncestorBrowsingContextToFocus &&
2380 aAncestorBrowsingContextToFocus->IsDiscarded()) {
2381 aAncestorBrowsingContextToFocus = nullptr;
2382 }
2383 // XXX should more early returns from BlurImpl be hoisted here to avoid
2384 // processing aBrowsingContextToClear and aAncestorBrowsingContextToFocus in
2385 // other processes when BlurImpl returns early in this process? Or should the
2386 // IPC messages for those be sent by BlurImpl itself, in which case they could
2387 // arrive late?
2388 if (focusedBrowsingContext->IsInProcess()) {
2389 if (aBrowsingContextToClear && !aBrowsingContextToClear->IsInProcess()) {
2390 MOZ_RELEASE_ASSERT(!(aAncestorBrowsingContextToFocus &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus
->IsInProcess()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!(aAncestorBrowsingContextToFocus
&& !aAncestorBrowsingContextToFocus->IsInProcess(
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
" (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")", "./../../../dom/base/nsFocusManager.cpp"
, 2394); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
") (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")"); do { MOZ_CrashSequence(__null, 2394)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
2391 !aAncestorBrowsingContextToFocus->IsInProcess()),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus
->IsInProcess()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!(aAncestorBrowsingContextToFocus
&& !aAncestorBrowsingContextToFocus->IsInProcess(
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
" (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")", "./../../../dom/base/nsFocusManager.cpp"
, 2394); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
") (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")"); do { MOZ_CrashSequence(__null, 2394)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
2392 "Both aBrowsingContextToClear and "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus
->IsInProcess()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!(aAncestorBrowsingContextToFocus
&& !aAncestorBrowsingContextToFocus->IsInProcess(
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
" (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")", "./../../../dom/base/nsFocusManager.cpp"
, 2394); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
") (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")"); do { MOZ_CrashSequence(__null, 2394)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
2393 "aAncestorBrowsingContextToFocus are "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus
->IsInProcess()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!(aAncestorBrowsingContextToFocus
&& !aAncestorBrowsingContextToFocus->IsInProcess(
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
" (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")", "./../../../dom/base/nsFocusManager.cpp"
, 2394); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
") (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")"); do { MOZ_CrashSequence(__null, 2394)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
2394 "out-of-process.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus
->IsInProcess()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!(aAncestorBrowsingContextToFocus
&& !aAncestorBrowsingContextToFocus->IsInProcess(
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
" (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")", "./../../../dom/base/nsFocusManager.cpp"
, 2394); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!(aAncestorBrowsingContextToFocus && !aAncestorBrowsingContextToFocus->IsInProcess())"
") (" "Both aBrowsingContextToClear and " "aAncestorBrowsingContextToFocus are "
"out-of-process." ")"); do { MOZ_CrashSequence(__null, 2394)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
2395 contentChild->SendSetFocusedElement(aBrowsingContextToClear, false);
2396 }
2397 if (aAncestorBrowsingContextToFocus &&
2398 !aAncestorBrowsingContextToFocus->IsInProcess()) {
2399 contentChild->SendSetFocusedElement(aAncestorBrowsingContextToFocus,
2400 true);
2401 }
2402 return BlurImpl(aBrowsingContextToClear, aAncestorBrowsingContextToFocus,
2403 aIsLeavingDocument, aAdjustWidget, aRemainActive,
2404 aElementToFocus, aActionId);
2405 }
2406 if (aBrowsingContextToClear && aBrowsingContextToClear->IsInProcess()) {
2407 nsPIDOMWindowOuter* windowToClear = aBrowsingContextToClear->GetDOMWindow();
2408 MOZ_ASSERT(windowToClear)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(windowToClear)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(windowToClear))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("windowToClear",
"./../../../dom/base/nsFocusManager.cpp", 2408); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "windowToClear" ")"); do { MOZ_CrashSequence
(__null, 2408); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2409 windowToClear->SetFocusedElement(nullptr);
2410 windowToClearHandled = true;
2411 }
2412 if (aAncestorBrowsingContextToFocus &&
2413 aAncestorBrowsingContextToFocus->IsInProcess()) {
2414 nsPIDOMWindowOuter* ancestorWindowToFocus =
2415 aAncestorBrowsingContextToFocus->GetDOMWindow();
2416 MOZ_ASSERT(ancestorWindowToFocus)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ancestorWindowToFocus)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ancestorWindowToFocus))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("ancestorWindowToFocus"
, "./../../../dom/base/nsFocusManager.cpp", 2416); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ancestorWindowToFocus" ")"); do { MOZ_CrashSequence
(__null, 2416); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2417 ancestorWindowToFocus->SetFocusedElement(nullptr, 0, true);
2418 ancestorWindowToFocusHandled = true;
2419 }
2420 // The expectation is that the blurring would eventually result in an IPC
2421 // message doing this anyway, but this doesn't happen if the focus is in OOP
2422 // iframe which won't try to bounce an IPC message to its parent frame.
2423 SetFocusedWindowInternal(nullptr, aActionId);
2424 contentChild->SendBlurToParent(
2425 focusedBrowsingContext, aBrowsingContextToClear,
2426 aAncestorBrowsingContextToFocus, aIsLeavingDocument, aAdjustWidget,
2427 windowToClearHandled, ancestorWindowToFocusHandled, aActionId);
2428 return true;
2429}
2430
2431void nsFocusManager::BlurFromOtherProcess(
2432 mozilla::dom::BrowsingContext* aFocusedBrowsingContext,
2433 mozilla::dom::BrowsingContext* aBrowsingContextToClear,
2434 mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
2435 bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId) {
2436 if (aFocusedBrowsingContext != GetFocusedBrowsingContext()) {
2437 return;
2438 }
2439 BlurImpl(aBrowsingContextToClear, aAncestorBrowsingContextToFocus,
2440 aIsLeavingDocument, aAdjustWidget, /* aRemainActive = */ false,
2441 nullptr, aActionId);
2442}
2443
2444bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
2445 BrowsingContext* aAncestorBrowsingContextToFocus,
2446 bool aIsLeavingDocument, bool aAdjustWidget,
2447 bool aRemainActive, Element* aElementToFocus,
2448 uint64_t aActionId) {
2449 LOGFOCUS(("<<Blur begin actionid: %" PRIu64 ">>", aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<Blur begin actionid: %"
"l" "u" ">>", aActionId); } } while (0)
;
2450
2451 // hold a reference to the focused content, which may be null
2452 RefPtr<Element> element = mFocusedElement;
2453 if (element) {
2454 if (!element->IsInComposedDoc()) {
2455 mFocusedElement = nullptr;
2456 return true;
2457 }
2458 }
2459
2460 RefPtr<BrowsingContext> focusedBrowsingContext = GetFocusedBrowsingContext();
2461 // hold a reference to the focused window
2462 nsCOMPtr<nsPIDOMWindowOuter> window;
2463 if (focusedBrowsingContext) {
2464 window = focusedBrowsingContext->GetDOMWindow();
2465 }
2466 if (!window) {
2467 mFocusedElement = nullptr;
2468 return true;
2469 }
2470
2471 nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
2472 if (!docShell) {
2473 if (XRE_IsContentProcess() &&
2474 ActionIdComparableAndLower(
2475 aActionId, mActionIdForFocusedBrowsingContextInContent)) {
2476 // Unclear if this ever happens.
2477 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused BrowsingContext when "
"docShell is null due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2478 ("Ignored an attempt to null out focused BrowsingContext when "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused BrowsingContext when "
"docShell is null due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2479 "docShell is null due to a stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused BrowsingContext when "
"docShell is null due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2480 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused BrowsingContext when "
"docShell is null due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
;
2481 return true;
2482 }
2483
2484 mFocusedWindow = nullptr;
2485 // Setting focused BrowsingContext to nullptr to avoid leaking in print
2486 // preview.
2487 SetFocusedBrowsingContext(nullptr, aActionId);
2488 mFocusedElement = nullptr;
2489 return true;
2490 }
2491
2492 // Keep a ref to presShell since dispatching the DOM event may cause
2493 // the document to be destroyed.
2494 RefPtr<PresShell> presShell = docShell->GetPresShell();
2495 if (!presShell) {
2496 if (XRE_IsContentProcess() &&
2497 ActionIdComparableAndLower(
2498 aActionId, mActionIdForFocusedBrowsingContextInContent)) {
2499 // Unclear if this ever happens.
2500 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused BrowsingContext when "
"presShell is null due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2501 ("Ignored an attempt to null out focused BrowsingContext when "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused BrowsingContext when "
"presShell is null due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2502 "presShell is null due to a stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused BrowsingContext when "
"presShell is null due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2503 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused BrowsingContext when "
"presShell is null due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
;
2504 return true;
2505 }
2506 mFocusedElement = nullptr;
2507 mFocusedWindow = nullptr;
2508 // Setting focused BrowsingContext to nullptr to avoid leaking in print
2509 // preview.
2510 SetFocusedBrowsingContext(nullptr, aActionId);
2511 return true;
2512 }
2513
2514 const RefPtr<nsPresContext> focusedPresContext =
2515 GetActiveBrowsingContext() ? presShell->GetPresContext() : nullptr;
2516 IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
2517 GetFocusMoveActionCause(0));
2518
2519 // IMEStateManager::OnChangeFocus() may commit extant composition and that
2520 // causes some DOM events so that it may cause moving focus recursively.
2521 if (XRE_IsContentProcess() &&
2522 ActionIdComparableAndLower(aActionId,
2523 mActionIdForFocusedBrowsingContextInContent))
2524 [[unlikely]] {
2525 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2526 ("Ignored an attempt to null out focused element after notifying "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2527 "IMEStateManager due to a stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2528 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
;
2529 return true;
2530 }
2531
2532 // now adjust the actual focus, by clearing the fields in the focus manager
2533 // and in the window.
2534 mFocusedElement = nullptr;
2535 if (aBrowsingContextToClear) {
2536 nsPIDOMWindowOuter* windowToClear = aBrowsingContextToClear->GetDOMWindow();
2537 if (windowToClear) {
2538 windowToClear->SetFocusedElement(nullptr);
2539 }
2540 }
2541
2542 LOGCONTENT("Element %s has been blurred", element.get())if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusLog,
LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(element.get()) { element.get()->NodeInfo()->NameAtom(
)->ToUTF8String(tag); } do { const ::mozilla::LogModule* moz_real_module
= gFocusLog; if ((__builtin_expect(!!(mozilla::detail::log_test
(moz_real_module, LogLevel::Debug)), 0))) { mozilla::detail::
log_print(moz_real_module, LogLevel::Debug, "Element %s has been blurred"
, tag.get()); } } while (0); }
;
2543
2544 // Don't fire blur event on the root content which isn't editable.
2545 bool sendBlurEvent =
2546 element && element->IsInComposedDoc() && !IsNonFocusableRoot(element);
2547 if (element) {
2548 if (!aIsLeavingDocument) {
2549 // Ensure that focus navigation still continues from element.
2550 // If we're leaving the document, we will refocus the element
2551 // when the document is refocused, so we don't need to do this.
2552 element->OwnerDoc()->SetPreviouslyFocusedContent(element);
2553 }
2554 if (sendBlurEvent) {
2555 NotifyFocusStateChange(element, aElementToFocus, 0, false, false);
2556 }
2557
2558 if (!aRemainActive) {
2559 bool windowBeingLowered = !aBrowsingContextToClear &&
2560 !aAncestorBrowsingContextToFocus &&
2561 aIsLeavingDocument && aAdjustWidget;
2562 // If the object being blurred is a remote browser, deactivate remote
2563 // content
2564 if (BrowserParent* remote = BrowserParent::GetFrom(element)) {
2565 MOZ_ASSERT(XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 2565); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 2565); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2566 // Let's deactivate all remote browsers.
2567 BrowsingContext* topLevelBrowsingContext = remote->GetBrowsingContext();
2568 topLevelBrowsingContext->PreOrderWalk([&](BrowsingContext* aContext) {
2569 if (WindowGlobalParent* windowGlobalParent =
2570 aContext->Canonical()->GetCurrentWindowGlobal()) {
2571 if (RefPtr<BrowserParent> browserParent =
2572 windowGlobalParent->GetBrowserParent()) {
2573 browserParent->Deactivate(windowBeingLowered, aActionId);
2574 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s remote browser deactivated %p, %d, actionid: %"
"l" "u", aContext == topLevelBrowsingContext ? "Top-level" :
"OOP iframe", browserParent.get(), windowBeingLowered, aActionId
); } } while (0)
2575 ("%s remote browser deactivated %p, %d, actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s remote browser deactivated %p, %d, actionid: %"
"l" "u", aContext == topLevelBrowsingContext ? "Top-level" :
"OOP iframe", browserParent.get(), windowBeingLowered, aActionId
); } } while (0)
2576 aContext == topLevelBrowsingContext ? "Top-level"do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s remote browser deactivated %p, %d, actionid: %"
"l" "u", aContext == topLevelBrowsingContext ? "Top-level" :
"OOP iframe", browserParent.get(), windowBeingLowered, aActionId
); } } while (0)
2577 : "OOP iframe",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s remote browser deactivated %p, %d, actionid: %"
"l" "u", aContext == topLevelBrowsingContext ? "Top-level" :
"OOP iframe", browserParent.get(), windowBeingLowered, aActionId
); } } while (0)
2578 browserParent.get(), windowBeingLowered, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s remote browser deactivated %p, %d, actionid: %"
"l" "u", aContext == topLevelBrowsingContext ? "Top-level" :
"OOP iframe", browserParent.get(), windowBeingLowered, aActionId
); } } while (0)
;
2579 }
2580 }
2581 });
2582 }
2583
2584 // Same as above but for out-of-process iframes
2585 if (BrowserBridgeChild* bbc = BrowserBridgeChild::GetFrom(element)) {
2586 bbc->Deactivate(windowBeingLowered, aActionId);
2587 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Out-of-process iframe deactivated %p, %d, actionid: %"
"l" "u", bbc, windowBeingLowered, aActionId); } } while (0)
2588 ("Out-of-process iframe deactivated %p, %d, actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Out-of-process iframe deactivated %p, %d, actionid: %"
"l" "u", bbc, windowBeingLowered, aActionId); } } while (0)
2589 bbc, windowBeingLowered, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Out-of-process iframe deactivated %p, %d, actionid: %"
"l" "u", bbc, windowBeingLowered, aActionId); } } while (0)
;
2590 }
2591 }
2592 }
2593
2594 bool result = true;
2595 if (sendBlurEvent) {
2596 // if there is an active window, update commands. If there isn't an active
2597 // window, then this was a blur caused by the active window being lowered,
2598 // so there is no need to update the commands
2599 if (GetActiveBrowsingContext()) {
2600 window->UpdateCommands(u"focus"_ns);
2601 }
2602
2603 RefPtr<Document> doc = element->GetComposedDoc();
2604 SendFocusOrBlurEvent(eBlur, presShell, doc, element, false, false,
2605 aElementToFocus);
2606 }
2607
2608 // if we are leaving the document or the window was lowered, make the caret
2609 // invisible.
2610 if (aIsLeavingDocument || !GetActiveBrowsingContext()) {
2611 SetCaretVisible(presShell, false, nullptr);
2612 }
2613
2614 RefPtr<AccessibleCaretEventHub> eventHub =
2615 presShell->GetAccessibleCaretEventHub();
2616 if (eventHub) {
2617 eventHub->NotifyBlur(aIsLeavingDocument || !GetActiveBrowsingContext());
2618 }
2619
2620 // at this point, it is expected that this window will be still be
2621 // focused, but the focused element will be null, as it was cleared before
2622 // the event. If this isn't the case, then something else was focused during
2623 // the blur event above and we should just return. However, if
2624 // aIsLeavingDocument is set, a new document is desired, so make sure to
2625 // blur the document and window.
2626 if (GetFocusedBrowsingContext() != window->GetBrowsingContext() ||
2627 (mFocusedElement != nullptr && !aIsLeavingDocument)) {
2628 result = false;
2629 } else if (aIsLeavingDocument) {
2630 window->TakeFocus(false, 0);
2631
2632 // clear the focus so that the ancestor frame hierarchy is in the correct
2633 // state. Pass true because aAncestorBrowsingContextToFocus is thought to be
2634 // focused at this point.
2635 if (aAncestorBrowsingContextToFocus) {
2636 nsPIDOMWindowOuter* ancestorWindowToFocus =
2637 aAncestorBrowsingContextToFocus->GetDOMWindow();
2638 if (ancestorWindowToFocus) {
2639 ancestorWindowToFocus->SetFocusedElement(nullptr, 0, true);
2640 }
2641
2642 // When the focus of aBrowsingContextToClear is cleared, it should
2643 // also clear its ancestors's focus because ancestors should no longer
2644 // be considered aBrowsingContextToClear is focused.
2645 //
2646 // We don't need to do this when aBrowsingContextToClear and
2647 // aAncestorBrowsingContextToFocus is equal because ancestors don't
2648 // care about this.
2649 if (aBrowsingContextToClear &&
2650 aBrowsingContextToClear != aAncestorBrowsingContextToFocus) {
2651 AdjustWindowFocus(
2652 aBrowsingContextToClear, false,
2653 IsWindowVisible(aBrowsingContextToClear->GetDOMWindow()), aActionId,
2654 true /* aShouldClearAncestorFocus */,
2655 aAncestorBrowsingContextToFocus);
2656 }
2657 }
2658
2659 SetFocusedWindowInternal(nullptr, aActionId);
2660 mFocusedElement = nullptr;
2661
2662 RefPtr<Document> doc = window->GetExtantDoc();
2663 if (doc) {
2664 SendFocusOrBlurEvent(eBlur, presShell, doc, doc, false);
2665 }
2666 if (!GetFocusedBrowsingContext()) {
2667 RefPtr innerWindow =
2668 nsGlobalWindowInner::Cast(window->GetCurrentInnerWindow());
2669 SendFocusOrBlurEvent(eBlur, presShell, doc, innerWindow, false);
2670 }
2671
2672 // check if a different window was focused
2673 result = (!GetFocusedBrowsingContext() && GetActiveBrowsingContext());
2674 } else if (GetActiveBrowsingContext()) {
2675 // Otherwise, the blur of the element without blurring the document
2676 // occurred normally. Call UpdateCaret to redisplay the caret at the right
2677 // location within the document. This is needed to ensure that the caret
2678 // used for caret browsing is made visible again when an input field is
2679 // blurred.
2680 UpdateCaret(false, true, nullptr);
2681 }
2682
2683 return result;
2684}
2685
2686void nsFocusManager::ActivateRemoteFrameIfNeeded(Element& aElement,
2687 uint64_t aActionId) {
2688 if (BrowserParent* remote = BrowserParent::GetFrom(&aElement)) {
2689 remote->Activate(aActionId);
2690 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Remote browser activated %p, actionid: %"
"l" "u", remote, aActionId); } } while (0)
2691 ("Remote browser activated %p, actionid: %" PRIu64, remote, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Remote browser activated %p, actionid: %"
"l" "u", remote, aActionId); } } while (0)
;
2692 }
2693
2694 // Same as above but for out-of-process iframes
2695 if (BrowserBridgeChild* bbc = BrowserBridgeChild::GetFrom(&aElement)) {
2696 bbc->Activate(aActionId);
2697 LOGFOCUS(("Out-of-process iframe activated %p, actionid: %" PRIu64, bbc,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Out-of-process iframe activated %p, actionid: %"
"l" "u", bbc, aActionId); } } while (0)
2698 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Out-of-process iframe activated %p, actionid: %"
"l" "u", bbc, aActionId); } } while (0)
;
2699 }
2700}
2701
2702void nsFocusManager::FixUpFocusBeforeFrameLoaderChange(Element& aElement,
2703 BrowsingContext* aBc) {
2704 // If focus is out of process we don't need to do anything.
2705 if (!mFocusedWindow || !aBc) {
2706 return;
2707 }
2708 auto* docShell = aBc->GetDocShell();
2709 if (!docShell) {
2710 return;
2711 }
2712 if (!IsSameOrAncestor(docShell->GetWindow(), mFocusedWindow)) {
2713 // The window about to go away is not focused.
2714 return;
2715 }
2716 LOGFOCUS(("About to swap frame loaders on focused in-process window %p",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "About to swap frame loaders on focused in-process window %p"
, mFocusedWindow.get()); } } while (0)
2717 mFocusedWindow.get()))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "About to swap frame loaders on focused in-process window %p"
, mFocusedWindow.get()); } } while (0)
;
2718 mFocusedWindow = GetCurrentWindow(&aElement);
2719 mFocusedElement = &aElement;
2720}
2721
2722void nsFocusManager::FixUpFocusAfterFrameLoaderChange(Element& aElement) {
2723 MOZ_ASSERT(mFocusedElement == &aElement)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFocusedElement == &aElement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mFocusedElement == &aElement
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"mFocusedElement == &aElement", "./../../../dom/base/nsFocusManager.cpp"
, 2723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mFocusedElement == &aElement"
")"); do { MOZ_CrashSequence(__null, 2723); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2724 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nsContentUtils::IsSafeToRunScript())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(nsContentUtils::IsSafeToRunScript
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("nsContentUtils::IsSafeToRunScript()", "./../../../dom/base/nsFocusManager.cpp"
, 2724); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nsContentUtils::IsSafeToRunScript()"
")"); do { MOZ_CrashSequence(__null, 2724); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2725 if (GetContentWindow(&aElement)) {
2726 // This will focus the content window.
2727 SetFocusInner(&aElement, 0, false, false);
2728 } else {
2729 // If we're remote, activate the frame.
2730 ActivateRemoteFrameIfNeeded(aElement, GenerateFocusActionId());
2731 }
2732 RefPtr<nsPresContext> presContext = aElement.OwnerDoc()->GetPresContext();
2733 IMEStateManager::OnChangeFocus(presContext, &aElement,
2734 InputContextAction::CAUSE_UNKNOWN);
2735}
2736
2737void nsFocusManager::Focus(
2738 nsPIDOMWindowOuter* aWindow, Element* aElement, uint32_t aFlags,
2739 bool aIsNewDocument, bool aFocusChanged, bool aWindowRaised,
2740 bool aAdjustWidget, uint64_t aActionId,
2741 const Maybe<BlurredElementInfo>& aBlurredElementInfo) {
2742 LOGFOCUS(("<<Focus begin actionid: %" PRIu64 ">>", aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "<<Focus begin actionid: %"
"l" "u" ">>", aActionId); } } while (0)
;
2743
2744 if (!aWindow) {
2745 return;
2746 }
2747
2748 // Keep a reference to the presShell since dispatching the DOM event may
2749 // cause the document to be destroyed.
2750 nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
2751 if (!docShell) {
2752 return;
2753 }
2754
2755 const RefPtr<PresShell> presShell = docShell->GetPresShell();
2756 if (!presShell) {
2757 return;
2758 }
2759
2760 bool focusInOtherContentProcess = false;
2761 // Keep mochitest-browser-chrome harness happy by ignoring
2762 // focusInOtherContentProcess in the chrome process, because the harness
2763 // expects that.
2764 if (!XRE_IsParentProcess()) {
2765 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(aElement)) {
2766 // Only look at pre-existing browsing contexts. If this function is
2767 // called during reflow, calling GetBrowsingContext() could cause frame
2768 // loader initialization at a time when it isn't safe.
2769 if (BrowsingContext* bc = flo->GetExtantBrowsingContext()) {
2770 focusInOtherContentProcess = !bc->IsInProcess();
2771 }
2772 }
2773
2774 if (ActionIdComparableAndLower(
2775 aActionId, mActionIdForFocusedBrowsingContextInContent)) {
2776 // Unclear if this ever happens.
2777 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to focus an element due to stale action id "
"%" "l" "u" ".", aActionId); } } while (0)
2778 ("Ignored an attempt to focus an element due to stale action id "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to focus an element due to stale action id "
"%" "l" "u" ".", aActionId); } } while (0)
2779 "%" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to focus an element due to stale action id "
"%" "l" "u" ".", aActionId); } } while (0)
2780 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to focus an element due to stale action id "
"%" "l" "u" ".", aActionId); } } while (0)
;
2781 return;
2782 }
2783 }
2784
2785 // If the focus actually changed, set the focus method (mouse, keyboard, etc).
2786 // Otherwise, just get the current focus method and use that. This ensures
2787 // that the method is set during the document and window focus events.
2788 uint32_t focusMethod = aFocusChanged
2789 ? aFlags & METHODANDRING_MASK
2790 : aWindow->GetFocusMethod() |
2791 (aFlags & (FLAG_SHOWRING | FLAG_NOSHOWRING));
2792
2793 if (!IsWindowVisible(aWindow)) {
2794 // if the window isn't visible, for instance because it is a hidden tab,
2795 // update the current focus and scroll it into view but don't do anything
2796 // else
2797 if (aElement) {
2798 aWindow->SetFocusedElement(aElement, focusMethod);
2799 if (aFocusChanged) {
2800 ScrollIntoView(presShell, aElement, aFlags);
2801 }
2802 }
2803 return;
2804 }
2805
2806 LOGCONTENT("Element %s has been focused", aElement)if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusLog,
LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(aElement) { aElement->NodeInfo()->NameAtom()->ToUTF8String
(tag); } do { const ::mozilla::LogModule* moz_real_module = gFocusLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "Element %s has been focused", tag.get());
} } while (0); }
;
2807
2808 if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gFocusLog, LogLevel
::Debug)), 0))
) {
2809 Document* docm = aWindow->GetExtantDoc();
2810 if (docm) {
2811 LOGCONTENT(" from %s", docm->GetRootElement())if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusLog,
LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(docm->GetRootElement()) { docm->GetRootElement()->
NodeInfo()->NameAtom()->ToUTF8String(tag); } do { const
::mozilla::LogModule* moz_real_module = gFocusLog; if ((__builtin_expect
(!!(mozilla::detail::log_test(moz_real_module, LogLevel::Debug
)), 0))) { mozilla::detail::log_print(moz_real_module, LogLevel
::Debug, " from %s", tag.get()); } } while (0); }
;
2812 }
2813 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x actionid: %"
"l" "u" "]", aIsNewDocument, aFocusChanged, aWindowRaised, aFlags
, aActionId); } } while (0)
2814 (" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x actionid: %" PRIu64do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x actionid: %"
"l" "u" "]", aIsNewDocument, aFocusChanged, aWindowRaised, aFlags
, aActionId); } } while (0)
2815 "]",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x actionid: %"
"l" "u" "]", aIsNewDocument, aFocusChanged, aWindowRaised, aFlags
, aActionId); } } while (0)
2816 aIsNewDocument, aFocusChanged, aWindowRaised, aFlags, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x actionid: %"
"l" "u" "]", aIsNewDocument, aFocusChanged, aWindowRaised, aFlags
, aActionId); } } while (0)
;
2817 }
2818
2819 if (aIsNewDocument) {
2820 // if this is a new document, update the parent chain of frames so that
2821 // focus can be traversed from the top level down to the newly focused
2822 // window.
2823 RefPtr<BrowsingContext> bc = aWindow->GetBrowsingContext();
2824 AdjustWindowFocus(bc, false, IsWindowVisible(aWindow), aActionId,
2825 false /* aShouldClearAncestorFocus */,
2826 nullptr /* aAncestorBrowsingContextToFocus */);
2827 }
2828
2829 // indicate that the window has taken focus.
2830 if (aWindow->TakeFocus(true, focusMethod)) {
2831 aIsNewDocument = true;
2832 }
2833
2834 SetFocusedWindowInternal(aWindow, aActionId);
2835
2836 if (aAdjustWidget && !sTestMode) {
2837 if (nsCOMPtr<nsIWidget> widget = presShell->GetRootWidget()) {
2838 widget->SetFocus(nsIWidget::Raise::No, aFlags & FLAG_NONSYSTEMCALLER
2839 ? CallerType::NonSystem
2840 : CallerType::System);
2841 }
2842 }
2843
2844 // if switching to a new document, first fire the focus event on the
2845 // document and then the window.
2846 if (aIsNewDocument) {
2847 RefPtr<Document> doc = aWindow->GetExtantDoc();
2848 // The focus change should be notified to IMEStateManager from here if:
2849 // * the focused element is in design mode or
2850 // * nobody gets focus and the document is in design mode
2851 // since any element whose uncomposed document is in design mode won't
2852 // receive focus event.
2853 if (doc && ((aElement && aElement->IsInDesignMode()) ||
2854 (!aElement && doc->IsInDesignMode()))) {
2855 RefPtr<nsPresContext> presContext = presShell->GetPresContext();
2856 IMEStateManager::OnChangeFocus(presContext, nullptr,
2857 GetFocusMoveActionCause(aFlags));
2858 // IMEStateManager::OnChangeFocus() may commit extant composition and that
2859 // causes some DOM events so that it may cause moving focus recursively.
2860 if (XRE_IsContentProcess() &&
2861 ActionIdComparableAndLower(
2862 aActionId, mActionIdForFocusedBrowsingContextInContent))
2863 [[unlikely]] {
2864 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2865 ("Ignored an attempt to null out focused element after notifying "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2866 "IMEStateManager due to a stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2867 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
;
2868 return;
2869 }
2870 }
2871 if (doc && !focusInOtherContentProcess) {
2872 SendFocusOrBlurEvent(eFocus, presShell, doc, doc, aWindowRaised);
2873 }
2874 if (GetFocusedBrowsingContext() == aWindow->GetBrowsingContext() &&
2875 !mFocusedElement && !focusInOtherContentProcess) {
2876 RefPtr innerWindow =
2877 nsGlobalWindowInner::Cast(aWindow->GetCurrentInnerWindow());
2878 SendFocusOrBlurEvent(eFocus, presShell, doc, innerWindow, aWindowRaised);
2879 }
2880 }
2881
2882 const RefPtr<Element> elementToFocus = [&]() -> Element* {
2883 if (!aElement || !aElement->IsInComposedDoc() ||
2884 aElement->GetComposedDoc() != aWindow->GetExtantDoc()) {
2885 // Element moved documents, don't focus it to prevent redirecting focus to
2886 // the wrong window.
2887 return nullptr;
2888 }
2889 return aElement;
2890 }();
2891
2892 if (elementToFocus) {
2893 // Don't need previously-focused content anymore, since an element is
2894 // focused.
2895 Document* doc = elementToFocus->OwnerDoc();
2896 doc->SetPreviouslyFocusedContent(nullptr);
2897 doc->SetSelectionMoreRecentThanFocus(false);
2898 }
2899
2900 if (elementToFocus && !mFocusedElement &&
2901 GetFocusedBrowsingContext() == aWindow->GetBrowsingContext()) {
2902 mFocusedElement = elementToFocus;
2903
2904 nsIContent* focusedNode = aWindow->GetFocusedElement();
2905 const bool sendFocusEvent = elementToFocus->IsInComposedDoc() &&
2906 !IsNonFocusableRoot(elementToFocus);
2907 const bool isRefocus = focusedNode && focusedNode == elementToFocus;
2908 const bool shouldShowFocusRing =
2909 sendFocusEvent &&
2910 ShouldMatchFocusVisible(aWindow, *elementToFocus, aFlags);
2911
2912 aWindow->SetFocusedElement(elementToFocus, focusMethod, false);
2913
2914 const RefPtr<nsPresContext> presContext = presShell->GetPresContext();
2915 if (sendFocusEvent) {
2916 NotifyFocusStateChange(elementToFocus, nullptr, aFlags,
2917 /* aGettingFocus = */ true, shouldShowFocusRing);
2918
2919 // If this is a remote browser, focus its widget and activate remote
2920 // content. Note that we might no longer be in the same document,
2921 // due to the events we fired above when aIsNewDocument.
2922 if (presShell->GetDocument() == elementToFocus->GetComposedDoc()) {
2923 ActivateRemoteFrameIfNeeded(*elementToFocus, aActionId);
2924 }
2925
2926 IMEStateManager::OnChangeFocus(presContext, elementToFocus,
2927 GetFocusMoveActionCause(aFlags));
2928 // IMEStateManager::OnChangeFocus() may commit extant composition and that
2929 // causes some DOM events so that it may cause moving focus recursively.
2930 if (XRE_IsContentProcess() &&
2931 ActionIdComparableAndLower(
2932 aActionId, mActionIdForFocusedBrowsingContextInContent))
2933 [[unlikely]] {
2934 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2935 ("Ignored an attempt to null out focused element after notifying "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2936 "IMEStateManager due to a stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2937 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
;
2938 return;
2939 }
2940
2941 // as long as this focus wasn't because a window was raised, update the
2942 // commands
2943 // XXXndeakin P2 someone could adjust the focus during the update
2944 if (!aWindowRaised) {
2945 aWindow->UpdateCommands(u"focus"_ns);
2946 }
2947
2948 // If the focused element changed, scroll it into view
2949 if (aFocusChanged) {
2950 ScrollIntoView(presShell, elementToFocus, aFlags);
2951 }
2952
2953 if (!focusInOtherContentProcess) {
2954 RefPtr<Document> composedDocument = elementToFocus->GetComposedDoc();
2955 RefPtr<Element> relatedTargetElement =
2956 aBlurredElementInfo ? aBlurredElementInfo->mElement.get() : nullptr;
2957 SendFocusOrBlurEvent(eFocus, presShell, composedDocument,
2958 elementToFocus, aWindowRaised, isRefocus,
2959 relatedTargetElement);
2960 }
2961 } else {
2962 // We should notify IMEStateManager of actual focused element even if it
2963 // won't get focus event because the other IMEStateManager users do not
2964 // want to depend on this check, but IMEStateManager wants to verify
2965 // passed focused element for avoidng to overrride nested calls.
2966 IMEStateManager::OnChangeFocus(presContext, elementToFocus,
2967 GetFocusMoveActionCause(aFlags));
2968 // IMEStateManager::OnChangeFocus() may commit extant composition and that
2969 // causes some DOM events so that it may cause moving focus recursively.
2970 if (XRE_IsContentProcess() &&
2971 ActionIdComparableAndLower(
2972 aActionId, mActionIdForFocusedBrowsingContextInContent))
2973 [[unlikely]] {
2974 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2975 ("Ignored an attempt to null out focused element after notifying "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2976 "IMEStateManager due to a stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
2977 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
;
2978 return;
2979 }
2980 if (!aWindowRaised) {
2981 aWindow->UpdateCommands(u"focus"_ns);
2982 }
2983 if (aFocusChanged) {
2984 // If the focused element changed, scroll it into view
2985 ScrollIntoView(presShell, elementToFocus, aFlags);
2986 }
2987 }
2988 } else {
2989 // We only need this on this branch, on the branch above
2990 // NotifyFocusStateChange takes care of it.
2991 MaybeFixUpFocusWithinState(elementToFocus, mFocusedElement);
2992 if (!mFocusedElement && mFocusedWindow == aWindow) {
2993 // When there is no focused element, IMEStateManager needs to adjust IME
2994 // enabled state with the document.
2995 RefPtr<nsPresContext> presContext = presShell->GetPresContext();
2996 IMEStateManager::OnChangeFocus(presContext, nullptr,
2997 GetFocusMoveActionCause(aFlags));
2998 // IMEStateManager::OnChangeFocus() may commit extant composition and that
2999 // causes some DOM events so that it may cause moving focus recursively.
3000 if (XRE_IsContentProcess() &&
3001 ActionIdComparableAndLower(
3002 aActionId, mActionIdForFocusedBrowsingContextInContent))
3003 [[unlikely]] {
3004 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
3005 ("Ignored an attempt to null out focused element after notifying "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
3006 "IMEStateManager due to a stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
3007 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to null out focused element after notifying "
"IMEStateManager due to a stale action id %" "l" "u" ".", aActionId
); } } while (0)
;
3008 return;
3009 }
3010 }
3011
3012 if (!aWindowRaised) {
3013 aWindow->UpdateCommands(u"focus"_ns);
3014 }
3015 }
3016
3017 // update the caret visibility and position to match the newly focused
3018 // element. However, don't update the position if this was a focus due to a
3019 // mouse click as the selection code would already have moved the caret as
3020 // needed. If this is a different document than was focused before, also
3021 // update the caret's visibility. If this is the same document, the caret
3022 // visibility should be the same as before so there is no need to update it.
3023 if (mFocusedElement == elementToFocus) {
3024 RefPtr<Element> focusedElement = mFocusedElement;
3025 UpdateCaret(aFocusChanged && !(aFlags & FLAG_BYMOUSE), aIsNewDocument,
3026 focusedElement);
3027 }
3028}
3029
3030class FocusBlurEvent : public Runnable {
3031 public:
3032 FocusBlurEvent(EventTarget* aTarget, EventMessage aEventMessage,
3033 nsPresContext* aContext, bool aWindowRaised, bool aIsRefocus,
3034 EventTarget* aRelatedTarget)
3035 : mozilla::Runnable("FocusBlurEvent"),
3036 mTarget(aTarget),
3037 mContext(aContext),
3038 mEventMessage(aEventMessage),
3039 mWindowRaised(aWindowRaised),
3040 mIsRefocus(aIsRefocus),
3041 mRelatedTarget(aRelatedTarget) {}
3042
3043 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
3044 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODvirtual nsresult Run() override {
3045 InternalFocusEvent event(true, mEventMessage);
3046 event.mFlags.mBubbles = false;
3047 event.mFlags.mCancelable = false;
3048 event.mFromRaise = mWindowRaised;
3049 event.mIsRefocus = mIsRefocus;
3050 event.mRelatedTarget = mRelatedTarget;
3051 return EventDispatcher::Dispatch(mTarget, mContext, &event);
3052 }
3053
3054 const nsCOMPtr<EventTarget> mTarget;
3055 const RefPtr<nsPresContext> mContext;
3056 EventMessage mEventMessage;
3057 bool mWindowRaised;
3058 bool mIsRefocus;
3059 nsCOMPtr<EventTarget> mRelatedTarget;
3060};
3061
3062class FocusInOutEvent : public Runnable {
3063 public:
3064 FocusInOutEvent(EventTarget* aTarget, EventMessage aEventMessage,
3065 nsPresContext* aContext,
3066 nsPIDOMWindowOuter* aOriginalFocusedWindow,
3067 nsIContent* aOriginalFocusedContent,
3068 EventTarget* aRelatedTarget)
3069 : mozilla::Runnable("FocusInOutEvent"),
3070 mTarget(aTarget),
3071 mContext(aContext),
3072 mEventMessage(aEventMessage),
3073 mOriginalFocusedWindow(aOriginalFocusedWindow),
3074 mOriginalFocusedContent(aOriginalFocusedContent),
3075 mRelatedTarget(aRelatedTarget) {}
3076
3077 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
3078 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODvirtual nsresult Run() override {
3079 nsCOMPtr<nsIContent> originalWindowFocus =
3080 mOriginalFocusedWindow ? mOriginalFocusedWindow->GetFocusedElement()
3081 : nullptr;
3082 // Blink does not check that focus is the same after blur, but WebKit does.
3083 // Opt to follow Blink's behavior (see bug 687787).
3084 if (mEventMessage == eFocusOut ||
3085 originalWindowFocus == mOriginalFocusedContent) {
3086 InternalFocusEvent event(true, mEventMessage);
3087 event.mFlags.mBubbles = true;
3088 event.mFlags.mCancelable = false;
3089 event.mRelatedTarget = mRelatedTarget;
3090 return EventDispatcher::Dispatch(mTarget, mContext, &event);
3091 }
3092 return NS_OK;
3093 }
3094
3095 const nsCOMPtr<EventTarget> mTarget;
3096 const RefPtr<nsPresContext> mContext;
3097 EventMessage mEventMessage;
3098 nsCOMPtr<nsPIDOMWindowOuter> mOriginalFocusedWindow;
3099 nsCOMPtr<nsIContent> mOriginalFocusedContent;
3100 nsCOMPtr<EventTarget> mRelatedTarget;
3101};
3102
3103static Document* GetDocumentHelper(EventTarget* aTarget) {
3104 if (!aTarget) {
3105 return nullptr;
3106 }
3107 if (const nsINode* node = nsINode::FromEventTarget(aTarget)) {
3108 return node->OwnerDoc();
3109 }
3110 nsPIDOMWindowInner* win = nsPIDOMWindowInner::FromEventTarget(aTarget);
3111 return win ? win->GetExtantDoc() : nullptr;
3112}
3113
3114void nsFocusManager::FireFocusInOrOutEvent(
3115 EventMessage aEventMessage, PresShell* aPresShell, EventTarget* aTarget,
3116 nsPIDOMWindowOuter* aCurrentFocusedWindow,
3117 nsIContent* aCurrentFocusedContent, EventTarget* aRelatedTarget) {
3118 NS_ASSERTION(aEventMessage == eFocusIn || aEventMessage == eFocusOut,do { if (!(aEventMessage == eFocusIn || aEventMessage == eFocusOut
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Wrong event type for FireFocusInOrOutEvent"
, "aEventMessage == eFocusIn || aEventMessage == eFocusOut", "./../../../dom/base/nsFocusManager.cpp"
, 3119); MOZ_PretendNoReturn(); } } while (0)
3119 "Wrong event type for FireFocusInOrOutEvent")do { if (!(aEventMessage == eFocusIn || aEventMessage == eFocusOut
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Wrong event type for FireFocusInOrOutEvent"
, "aEventMessage == eFocusIn || aEventMessage == eFocusOut", "./../../../dom/base/nsFocusManager.cpp"
, 3119); MOZ_PretendNoReturn(); } } while (0)
;
3120
3121 nsContentUtils::AddScriptRunner(MakeAndAddRef<FocusInOutEvent>(
3122 aTarget, aEventMessage, aPresShell->GetPresContext(),
3123 aCurrentFocusedWindow, aCurrentFocusedContent, aRelatedTarget));
3124}
3125
3126void nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage,
3127 PresShell* aPresShell,
3128 Document* aDocument,
3129 EventTarget* aTarget,
3130 bool aWindowRaised, bool aIsRefocus,
3131 EventTarget* aRelatedTarget) {
3132 MOZ_ASSERT(aEventMessage == eFocus || aEventMessage == eBlur,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aEventMessage == eFocus || aEventMessage == eBlur)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aEventMessage == eFocus || aEventMessage == eBlur)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aEventMessage == eFocus || aEventMessage == eBlur"
" (" "Wrong event type for SendFocusOrBlurEvent" ")", "./../../../dom/base/nsFocusManager.cpp"
, 3133); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aEventMessage == eFocus || aEventMessage == eBlur"
") (" "Wrong event type for SendFocusOrBlurEvent" ")"); do {
MOZ_CrashSequence(__null, 3133); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
3133 "Wrong event type for SendFocusOrBlurEvent")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aEventMessage == eFocus || aEventMessage == eBlur)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aEventMessage == eFocus || aEventMessage == eBlur)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aEventMessage == eFocus || aEventMessage == eBlur"
" (" "Wrong event type for SendFocusOrBlurEvent" ")", "./../../../dom/base/nsFocusManager.cpp"
, 3133); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aEventMessage == eFocus || aEventMessage == eBlur"
") (" "Wrong event type for SendFocusOrBlurEvent" ")"); do {
MOZ_CrashSequence(__null, 3133); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3134
3135 nsCOMPtr<Document> eventTargetDoc = GetDocumentHelper(aTarget);
3136 nsCOMPtr<Document> relatedTargetDoc = GetDocumentHelper(aRelatedTarget);
3137
3138 // set aRelatedTarget to null if it's not in the same document as aTarget
3139 if (eventTargetDoc != relatedTargetDoc) {
3140 aRelatedTarget = nullptr;
3141 }
3142
3143 if (aDocument && aDocument->EventHandlingSuppressed()) {
3144 // if this event was already queued, remove it and append it to the end
3145 mDelayedBlurFocusEvents.RemoveElementsBy([&](const auto& event) {
3146 return event.mEventMessage == aEventMessage &&
3147 event.mPresShell == aPresShell && event.mDocument == aDocument &&
3148 event.mTarget == aTarget && event.mRelatedTarget == aRelatedTarget;
3149 });
3150
3151 mDelayedBlurFocusEvents.EmplaceBack(aEventMessage, aPresShell, aDocument,
3152 aTarget, aRelatedTarget);
3153 return;
3154 }
3155
3156 // If mDelayedBlurFocusEvents queue is not empty, check if there are events
3157 // that belongs to this doc, if yes, fire them first.
3158 if (aDocument && !aDocument->EventHandlingSuppressed() &&
3159 mDelayedBlurFocusEvents.Length()) {
3160 FireDelayedEvents(aDocument);
3161 }
3162
3163 FireFocusOrBlurEvent(aEventMessage, aPresShell, aTarget, aWindowRaised,
3164 aIsRefocus, aRelatedTarget);
3165}
3166
3167void nsFocusManager::FireFocusOrBlurEvent(EventMessage aEventMessage,
3168 PresShell* aPresShell,
3169 EventTarget* aTarget,
3170 bool aWindowRaised, bool aIsRefocus,
3171 EventTarget* aRelatedTarget) {
3172 nsCOMPtr<nsPIDOMWindowOuter> currentWindow = mFocusedWindow;
3173 nsCOMPtr<nsPIDOMWindowInner> targetWindow = do_QueryInterface(aTarget);
3174 nsCOMPtr<Document> targetDocument = do_QueryInterface(aTarget);
3175 nsCOMPtr<nsIContent> currentFocusedContent =
3176 currentWindow ? currentWindow->GetFocusedElement() : nullptr;
3177
3178#ifdef ACCESSIBILITY1
3179 nsAccessibilityService* accService = GetAccService();
3180 if (accService) {
3181 if (aEventMessage == eFocus) {
3182 accService->NotifyOfDOMFocus(aTarget);
3183 } else {
3184 accService->NotifyOfDOMBlur(aTarget);
3185 }
3186 }
3187#endif
3188
3189 aPresShell->ScheduleContentRelevancyUpdate(
3190 ContentRelevancyReason::FocusInSubtree);
3191
3192 nsContentUtils::AddScriptRunner(MakeAndAddRef<FocusBlurEvent>(
3193 aTarget, aEventMessage, aPresShell->GetPresContext(), aWindowRaised,
3194 aIsRefocus, aRelatedTarget));
3195
3196 // Check that the target is not a window or document before firing
3197 // focusin/focusout. Other browsers do not fire focusin/focusout on window,
3198 // despite being required in the spec, so follow their behavior.
3199 //
3200 // As for document, we should not even fire focus/blur, but until then, we
3201 // need this check. targetDocument should be removed once bug 1228802 is
3202 // resolved.
3203 if (!targetWindow && !targetDocument) {
3204 EventMessage focusInOrOutMessage =
3205 aEventMessage == eFocus ? eFocusIn : eFocusOut;
3206 FireFocusInOrOutEvent(focusInOrOutMessage, aPresShell, aTarget,
3207 currentWindow, currentFocusedContent, aRelatedTarget);
3208 }
3209}
3210
3211void nsFocusManager::ScrollIntoView(PresShell* aPresShell, nsIContent* aContent,
3212 uint32_t aFlags) {
3213 if (aFlags & FLAG_NOSCROLL) {
3214 return;
3215 }
3216
3217 // If the noscroll flag isn't set, scroll the newly focused element into view.
3218 const AxisScrollParams axis(WhereToScroll::Center,
3219 WhenToScroll::IfNotVisible);
3220 aPresShell->ScrollContentIntoView(aContent, axis, axis,
3221 ScrollFlags::ScrollOverflowHidden);
3222 // Scroll the input / textarea selection into view, unless focused with the
3223 // mouse, see bug 572649.
3224 if (aFlags & FLAG_BYMOUSE) {
3225 return;
3226 }
3227 if (auto* tc = TextControlElement::FromNode(aContent)) {
3228 tc->ScrollSelectionIntoViewAsync(TextControlElement::ScrollAncestors::Yes);
3229 }
3230}
3231
3232void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,
3233 CallerType aCallerType, uint64_t aActionId) {
3234 // don't raise windows that are already raised or are in the process of
3235 // being lowered
3236
3237 if (!aWindow || aWindow == mWindowBeingLowered) {
3238 return;
3239 }
3240
3241 if (XRE_IsParentProcess()) {
3242 if (aWindow == mActiveWindow) {
3243 if (!mFocusedWindow ||
3244 !IsSameOrAncestor(aWindow->GetBrowsingContext(),
3245 mFocusedWindow->GetBrowsingContext())) {
3246 MoveFocusToWindowAfterRaise(aWindow, aActionId);
3247 }
3248 return;
3249 }
3250 } else {
3251 BrowsingContext* bc = aWindow->GetBrowsingContext();
3252 // TODO: Deeper OOP frame hierarchies are
3253 // https://bugzilla.mozilla.org/show_bug.cgi?id=1661227
3254 if (bc == GetActiveBrowsingContext()) {
3255 return;
3256 }
3257 if (bc == GetFocusedBrowsingContext()) {
3258 return;
3259 }
3260 }
3261
3262 if (sTestMode) {
3263 // In test mode, emulate raising the window. WindowRaised takes
3264 // care of lowering the present active window. This happens in
3265 // a separate runnable to avoid touching multiple windows in
3266 // the current runnable.
3267 NS_DispatchToCurrentThread(NS_NewRunnableFunction(
3268 "nsFocusManager::RaiseWindow",
3269 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1770093)
3270 [self = RefPtr{this}, window = nsCOMPtr{aWindow}]()
3271 MOZ_CAN_RUN_SCRIPT_BOUNDARY -> void {
3272 self->WindowRaised(window, GenerateFocusActionId());
3273 }));
3274 return;
3275 }
3276
3277 if (XRE_IsContentProcess()) {
3278 BrowsingContext* bc = aWindow->GetBrowsingContext();
3279 if (!bc->IsTop()) {
3280 // Assume the raise below will succeed and run the raising synchronously
3281 // in this process to make the focus event that is observable in this
3282 // process fire in the right order relative to mouseup when we are here
3283 // thanks to a mousedown.
3284 WindowRaised(aWindow, aActionId);
3285 }
3286 }
3287
3288 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin =
3289 do_QueryInterface(aWindow->GetDocShell());
3290 if (treeOwnerAsWin) {
3291 nsCOMPtr<nsIWidget> widget;
3292 treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
3293 if (widget) {
3294 widget->SetFocus(nsIWidget::Raise::Yes, aCallerType);
3295 }
3296 }
3297}
3298
3299void nsFocusManager::UpdateCaretForCaretBrowsingMode() {
3300 RefPtr<Element> focusedElement = mFocusedElement;
3301 UpdateCaret(false, true, focusedElement);
3302}
3303
3304void nsFocusManager::UpdateCaret(bool aMoveCaretToFocus, bool aUpdateVisibility,
3305 nsIContent* aContent) {
3306 LOGFOCUS(("Update Caret: %d %d", aMoveCaretToFocus, aUpdateVisibility))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Update Caret: %d %d"
, aMoveCaretToFocus, aUpdateVisibility); } } while (0)
;
3307
3308 if (!mFocusedWindow) {
3309 return;
3310 }
3311
3312 // this is called when a document is focused or when the caretbrowsing
3313 // preference is changed
3314 nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
3315 if (!focusedDocShell) {
3316 return;
3317 }
3318
3319 if (focusedDocShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
3320 return; // Never browse with caret in chrome
3321 }
3322
3323 bool browseWithCaret = StaticPrefs::accessibility_browsewithcaret();
3324
3325 const RefPtr<PresShell> presShell = focusedDocShell->GetPresShell();
3326 if (!presShell) {
3327 return;
3328 }
3329
3330 // If this is an editable document which isn't contentEditable, or a
3331 // contentEditable document and the node to focus is contentEditable,
3332 // return, so that we don't mess with caret visibility.
3333 bool isEditable = false;
3334 focusedDocShell->GetEditable(&isEditable);
3335
3336 if (isEditable) {
3337 Document* doc = presShell->GetDocument();
3338
3339 bool isContentEditableDoc =
3340 doc &&
3341 doc->GetEditingState() == Document::EditingState::eContentEditable;
3342
3343 bool isFocusEditable = aContent && aContent->HasFlag(NODE_IS_EDITABLE);
3344 if (!isContentEditableDoc || isFocusEditable) {
3345 return;
3346 }
3347 }
3348
3349 if (aMoveCaretToFocus) {
3350 MoveCaretToFocus(presShell, aContent);
3351 }
3352
3353 // The above MoveCaretToFocus call may run scripts which
3354 // may clear mFocusWindow
3355 if (!mFocusedWindow) {
3356 return;
3357 }
3358
3359 if (!aUpdateVisibility) {
3360 return;
3361 }
3362
3363 // XXXndeakin this doesn't seem right. It should be checking for this only
3364 // on the nearest ancestor frame which is a chrome frame. But this is
3365 // what the existing code does, so just leave it for now.
3366 if (!browseWithCaret) {
3367 nsCOMPtr<Element> docElement = mFocusedWindow->GetFrameElementInternal();
3368 if (docElement)
3369 browseWithCaret = docElement->AttrValueIs(
3370 kNameSpaceID_None, nsGkAtoms::showcaret, u"true"_ns, eCaseMatters);
3371 }
3372
3373 SetCaretVisible(presShell, browseWithCaret, aContent);
3374}
3375
3376void nsFocusManager::MoveCaretToFocus(PresShell* aPresShell,
3377 nsIContent* aContent) {
3378 if (aContent && aContent->IsEditable()) {
3379 // Caret will be handled by HTMLEditor::OnFocus
3380 return;
3381 }
3382 const auto* textControl = TextControlElement::FromNodeOrNull(aContent);
3383 const bool isTextControl =
3384 textControl && textControl->IsSingleLineTextControlOrTextArea();
3385 // Only change selection on focus if caret browsing is enabled,
3386 // or the element is a single-line text control or textarea.
3387 // Otherwise we don't move it to be consistent with other browsers.
3388 if (!StaticPrefs::accessibility_browsewithcaret() && !isTextControl) {
3389 return;
3390 }
3391 nsCOMPtr<Document> doc = aPresShell->GetDocument();
3392 if (!doc) {
3393 return;
3394 }
3395
3396 RefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
3397 RefPtr<Selection> domSelection = &frameSelection->NormalSelection();
3398 MOZ_ASSERT(domSelection)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(domSelection)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(domSelection))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("domSelection", "./../../../dom/base/nsFocusManager.cpp"
, 3398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "domSelection"
")"); do { MOZ_CrashSequence(__null, 3398); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3399
3400 if (!aContent) {
3401 // If there is no currently focused content, the selection is just cleared.
3402 domSelection->RemoveAllRanges(IgnoreErrors());
3403 return;
3404 }
3405
3406 ErrorResult rv;
3407 RefPtr<nsRange> newRange = doc->CreateRange(rv);
3408 if (NS_WARN_IF(rv.Failed())NS_warn_if_impl(rv.Failed(), "rv.Failed()", "./../../../dom/base/nsFocusManager.cpp"
, 3408)
) {
3409 rv.SuppressException();
3410 domSelection->RemoveAllRanges(IgnoreErrors());
3411 return;
3412 }
3413
3414 // Set the range to the start of the currently focused node
3415 // Make sure it's collapsed
3416 newRange->SelectNodeContents(*aContent, IgnoreErrors());
3417
3418 if (!aContent->GetFirstChild() || aContent->IsHTMLFormControlElement()) {
3419 // If current focus node is a leaf, set range to before the
3420 // node by using the parent as a container.
3421 // This prevents it from appearing as selected.
3422 newRange->SetStartBefore(*aContent, IgnoreErrors());
3423 newRange->SetEndBefore(*aContent, IgnoreErrors());
3424 }
3425 // Adding newRange to the selection first would mark, and then immediately
3426 // unmark, every node in the flattened subtree of its closest common
3427 // inclusive ancestor, so collapse to its start directly. Adding it also set
3428 // the interline position to StartOfNextLine, which is what we want here too:
3429 // the caret belongs at the focused element, not trailing at the end of the
3430 // preceding line when that element starts one.
3431 domSelection->SetInterlinePosition(
3432 Selection::InterlinePosition::StartOfNextLine);
3433 domSelection->CollapseToStartOf(*newRange, IgnoreErrors());
3434}
3435
3436nsresult nsFocusManager::SetCaretVisible(PresShell* aPresShell, bool aVisible,
3437 nsIContent* aContent) {
3438 // When browsing with caret, make sure caret is visible after new focus
3439 // Return early if there is no caret. This can happen for the testcase
3440 // for bug 308025 where a window is closed in a blur handler.
3441 RefPtr<nsCaret> caret = aPresShell->GetOriginalCaret();
3442 if (!caret) {
3443 return NS_OK;
3444 }
3445
3446 bool caretVisible = caret->IsVisible();
3447 if (!aVisible && !caretVisible) {
3448 return NS_OK;
3449 }
3450
3451 RefPtr<nsFrameSelection> frameSelection;
3452 if (aContent) {
3453 NS_ASSERTION(aContent->GetComposedDoc() == aPresShell->GetDocument(),do { if (!(aContent->GetComposedDoc() == aPresShell->GetDocument
())) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Wrong document?", "aContent->GetComposedDoc() == aPresShell->GetDocument()"
, "./../../../dom/base/nsFocusManager.cpp", 3454); MOZ_PretendNoReturn
(); } } while (0)
3454 "Wrong document?")do { if (!(aContent->GetComposedDoc() == aPresShell->GetDocument
())) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Wrong document?", "aContent->GetComposedDoc() == aPresShell->GetDocument()"
, "./../../../dom/base/nsFocusManager.cpp", 3454); MOZ_PretendNoReturn
(); } } while (0)
;
3455 nsIFrame* focusFrame = aContent->GetPrimaryFrame();
3456 if (focusFrame) {
3457 frameSelection = focusFrame->GetFrameSelection();
3458 }
3459 }
3460
3461 RefPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
3462
3463 if (docFrameSelection && caret &&
3464 (frameSelection == docFrameSelection || !aContent)) {
3465 Selection& domSelection = docFrameSelection->NormalSelection();
3466
3467 // First, hide the caret to prevent attempting to show it in
3468 // SetCaretDOMSelection
3469 aPresShell->SetCaretEnabled(false);
3470
3471 // Tell the caret which selection to use
3472 caret->SetSelection(&domSelection);
3473
3474 // In content, we need to set the caret. The only special case is edit
3475 // fields, which have a different frame selection from the document.
3476 // They will take care of making the caret visible themselves.
3477
3478 aPresShell->SetCaretReadOnly(false);
3479 aPresShell->SetCaretEnabled(aVisible);
3480 }
3481
3482 return NS_OK;
3483}
3484
3485void nsFocusManager::GetSelectionLocation(Document* aDocument,
3486 PresShell* aPresShell,
3487 nsIContent** aStartContent,
3488 nsIContent** aEndContent) {
3489 *aStartContent = *aEndContent = nullptr;
3490
3491 nsPresContext* presContext = aPresShell->GetPresContext();
3492 NS_ASSERTION(presContext, "mPresContent is null!!")do { if (!(presContext)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "mPresContent is null!!"
, "presContext", "./../../../dom/base/nsFocusManager.cpp", 3492
); MOZ_PretendNoReturn(); } } while (0)
;
3493
3494 RefPtr<Selection> domSelection =
3495 &aPresShell->ConstFrameSelection()->NormalSelection();
3496 MOZ_ASSERT(domSelection)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(domSelection)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(domSelection))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("domSelection", "./../../../dom/base/nsFocusManager.cpp"
, 3496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "domSelection"
")"); do { MOZ_CrashSequence(__null, 3496); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3497
3498 const nsRange* domRange = domSelection->GetRangeAt(0);
3499 if (!domRange || !domRange->IsPositioned()) {
3500 return;
3501 }
3502 nsIContent* start = nsIContent::FromNode(domRange->GetStartContainer());
3503 nsIContent* end = nsIContent::FromNode(domRange->GetEndContainer());
3504 if (nsIContent* child = domRange->StartRef().GetChildAtOffset()) {
3505 start = child;
3506 }
3507 if (nsIContent* child = domRange->EndRef().GetChildAtOffset()) {
3508 end = child;
3509 }
3510
3511 // Next check to see if our caret is at the very end of a text node. If so,
3512 // the caret is actually sitting in front of the next logical frame's primary
3513 // node - so for this case we need to change the content to that node.
3514 // Note that if the text does not have text frame, we do not need to retreive
3515 // caret frame. This could occur if text frame has only collapsisble white-
3516 // spaces and is around a block boundary or an ancestor of it is invisible.
3517 // XXX If there is a visible text sibling, should we return it in the former
3518 // case?
3519 if (auto* text = Text::FromNodeOrNull(start);
3520 text && text->GetPrimaryFrame() &&
3521 text->TextDataLength() == domRange->StartOffset() &&
3522 domSelection->IsCollapsed()) {
3523 nsIFrame* startFrame = start->GetPrimaryFrame();
3524 // Yes, indeed we were at the end of the last node
3525 const Element* const limiter =
3526 domSelection && domSelection->GetAncestorLimiter()
3527 ? domSelection->GetAncestorLimiter()
3528 : nullptr;
3529 nsFrameIterator frameIterator(presContext, startFrame,
3530 nsFrameIterator::Type::Leaf,
3531 false, // aVisual
3532 false, // aLockInScrollView
3533 true, // aFollowOOFs
3534 false, // aSkipPopupChecks
3535 limiter);
3536
3537 nsIFrame* newCaretFrame = nullptr;
3538 nsIContent* newCaretContent = start;
3539 const bool endOfSelectionInStartNode = start == end;
3540 do {
3541 // Continue getting the next frame until the primary content for the
3542 // frame we are on changes - we don't want to be stuck in the same
3543 // place
3544 frameIterator.Next();
3545 newCaretFrame = frameIterator.CurrentItem();
3546 if (!newCaretFrame) {
3547 break;
3548 }
3549 newCaretContent = newCaretFrame->GetContent();
3550 } while (!newCaretContent || newCaretContent == start);
3551
3552 if (newCaretFrame && newCaretContent) {
3553 // If the caret is exactly at the same position of the new frame,
3554 // then we can use the newCaretFrame and newCaretContent for our
3555 // position
3556 nsRect caretRect;
3557 if (nsIFrame* frame = nsCaret::GetGeometry(domSelection, &caretRect)) {
3558 nsPoint caretWidgetOffset;
3559 nsIWidget* widget = frame->GetNearestWidget(caretWidgetOffset);
3560 caretRect.MoveBy(caretWidgetOffset);
3561 nsPoint newCaretOffset;
3562 nsIWidget* newCaretWidget =
3563 newCaretFrame->GetNearestWidget(newCaretOffset);
3564 if (widget == newCaretWidget && caretRect.TopLeft() == newCaretOffset) {
3565 // The caret is at the start of the new element.
3566 startFrame = newCaretFrame;
Value stored to 'startFrame' is never read
3567 start = newCaretContent;
3568 if (endOfSelectionInStartNode) {
3569 end = newCaretContent; // Ensure end of selection is
3570 // not before start
3571 }
3572 }
3573 }
3574 }
3575 }
3576
3577 NS_IF_ADDREF(*aStartContent = start)ns_if_addref(*aStartContent = start);
3578 NS_IF_ADDREF(*aEndContent = end)ns_if_addref(*aEndContent = end);
3579}
3580
3581// Gets next sibling of aContent in the flat tree,
3582// or its parent's next sibling if it has none, etc.
3583static nsIContent* GetFlatTreeNextNonDescendant(nsIContent& aContent) {
3584 nsIContent* content = &aContent;
3585 for (nsIContent* parent = aContent.GetFlattenedTreeParent(); parent;
3586 content = parent, parent = content->GetFlattenedTreeParent()) {
3587 FlattenedChildIterator iterator(parent);
3588 if (NS_WARN_IF(!iterator.Seek(content))NS_warn_if_impl(!iterator.Seek(content), "!iterator.Seek(content)"
, "./../../../dom/base/nsFocusManager.cpp", 3588)
) {
3589 return nullptr;
3590 }
3591 if (auto* sibling = iterator.GetNextChild()) {
3592 return sibling;
3593 }
3594 }
3595 return nullptr;
3596}
3597
3598void nsFocusManager::GetSequentialFocusNavigationStartingPoint(
3599 Document* aDocument, nsIContent* aFocusedContent, bool aForward,
3600 nsIContent** aStartContent, bool* aConsiderStartContent) {
3601 *aConsiderStartContent = true;
3602 // First consider location of previously-focused content.
3603 if (nsIContent* content = aDocument->GetPreviouslyFocusedContent()) {
3604 if (aDocument->WasFocusedElementRemoved()) {
3605 content = GetFlatTreeNextNonDescendant(*content);
3606 *aConsiderStartContent = aForward;
3607 } else {
3608 *aConsiderStartContent = false;
3609 }
3610 if (content) {
3611 NS_ADDREF(*aStartContent = content)(*aStartContent = content)->AddRef();
3612 return;
3613 }
3614 }
3615 if (aFocusedContent && !aDocument->IsSelectionMoreRecentThanFocus()) {
3616 // If some new element was focused more recently than the selection
3617 // was set, we should start there instead.
3618 NS_ADDREF(*aStartContent = aFocusedContent)(*aStartContent = aFocusedContent)->AddRef();
3619 return;
3620 }
3621 // Now consider selection.
3622 RefPtr<nsIContent> selectionStart, selectionEnd;
3623 GetSelectionLocation(aDocument, aDocument->GetPresShell(),
3624 getter_AddRefs(selectionStart),
3625 getter_AddRefs(selectionEnd));
3626 if (selectionStart) {
3627 // If there is something focused, we only want to start from the selection
3628 // if it's a flat tree descendant of the focused content.
3629 if (!aFocusedContent ||
3630 selectionStart->IsInclusiveFlatTreeDescendantOf(aFocusedContent)) {
3631 NS_ADDREF(*aStartContent = selectionStart)(*aStartContent = selectionStart)->AddRef();
3632 return;
3633 }
3634 }
3635 // Lastly, if we have no better place to start, just start from the focused
3636 // content (if any).
3637 NS_IF_ADDREF(*aStartContent = aFocusedContent)ns_if_addref(*aStartContent = aFocusedContent);
3638}
3639
3640nsresult nsFocusManager::DetermineElementToMoveFocus(
3641 nsPIDOMWindowOuter* aWindow, nsIContent* aStartContent, int32_t aType,
3642 bool aNoParentTraversal, bool aNavigateByKey, nsIContent** aNextContent) {
3643 *aNextContent = nullptr;
3644
3645 // This is used for document navigation only. It will be set to true if we
3646 // start navigating from a starting point. If this starting point is near the
3647 // end of the document (for example, an element on a statusbar), and there
3648 // are no child documents or panels before the end of the document, then we
3649 // will need to ensure that we don't consider the root chrome window when we
3650 // loop around and instead find the next child document/panel, as focus is
3651 // already in that window. This flag will be cleared once we navigate into
3652 // another document.
3653 bool mayFocusRoot = (aStartContent != nullptr);
3654
3655 nsCOMPtr<nsIContent> startContent = aStartContent;
3656 if (!startContent && aType != MOVEFOCUS_CARET) {
3657 if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC) {
3658 // When moving between documents, make sure to get the right
3659 // starting content in a descendant.
3660 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
3661 startContent = GetFocusedDescendant(aWindow, eIncludeAllDescendants,
3662 getter_AddRefs(focusedWindow));
3663 } else if (aType != MOVEFOCUS_LASTDOC) {
3664 // Otherwise, start at the focused node. If MOVEFOCUS_LASTDOC is used,
3665 // then we are document-navigating backwards from chrome to the content
3666 // process, and we don't want to use this so that we start from the end
3667 // of the document.
3668 startContent = aWindow->GetFocusedElement();
3669 }
3670 }
3671
3672 nsCOMPtr<Document> doc;
3673 if (startContent)
3674 doc = startContent->GetComposedDoc();
3675 else
3676 doc = aWindow->GetExtantDoc();
3677 if (!doc) return NS_OK;
3678
3679 // True if we are navigating by document (F6/Shift+F6) or false if we are
3680 // navigating by element (Tab/Shift+Tab).
3681 const bool forDocumentNavigation =
3682 aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC ||
3683 aType == MOVEFOCUS_FIRSTDOC || aType == MOVEFOCUS_LASTDOC;
3684
3685 // If moving to the root or first document, find the root element and return.
3686 if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_FIRSTDOC) {
3687 NS_IF_ADDREF(*aNextContent = GetRootForFocus(aWindow, doc, false, false))ns_if_addref(*aNextContent = GetRootForFocus(aWindow, doc, false
, false))
;
3688 if (!*aNextContent && aType == MOVEFOCUS_FIRSTDOC) {
3689 // When looking for the first document, if the root wasn't focusable,
3690 // find the next focusable document.
3691 aType = MOVEFOCUS_FORWARDDOC;
3692 } else {
3693 return NS_OK;
3694 }
3695 }
3696
3697 // rootElement and presShell may be set to sub-document's ones so that they
3698 // cannot be `const`.
3699 RefPtr<Element> rootElement = doc->GetRootElement();
3700 NS_ENSURE_TRUE(rootElement, NS_OK)do { if ((__builtin_expect(!!(!(rootElement)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "rootElement" ") failed"
, nullptr, "./../../../dom/base/nsFocusManager.cpp", 3700); return
NS_OK; } } while (false)
;
3701
3702 RefPtr<PresShell> presShell = doc->GetPresShell();
3703 NS_ENSURE_TRUE(presShell, NS_OK)do { if ((__builtin_expect(!!(!(presShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "presShell" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 3703); return NS_OK
; } } while (false)
;
3704
3705 if (aType == MOVEFOCUS_FIRST) {
3706 if (!aStartContent) {
3707 startContent = rootElement;
3708 }
3709 return GetNextTabbableContent(presShell, startContent, nullptr,
3710 startContent, true, 1, false, false,
3711 aNavigateByKey, false, aNextContent);
3712 }
3713 if (aType == MOVEFOCUS_LAST) {
3714 if (!aStartContent) {
3715 startContent = rootElement;
3716 }
3717 return GetNextTabbableContent(presShell, startContent, nullptr,
3718 startContent, false, 0, false, false,
3719 aNavigateByKey, false, aNextContent);
3720 }
3721
3722 bool forward = (aType == MOVEFOCUS_FORWARD || aType == MOVEFOCUS_FORWARDDOC ||
3723 aType == MOVEFOCUS_CARET);
3724 bool doNavigation = true;
3725 bool ignoreTabIndex = false;
3726 // when a popup is open, we want to ensure that tab navigation occurs only
3727 // within the most recently opened panel. If a popup is open, its frame will
3728 // be stored in popupFrame.
3729 nsIFrame* popupFrame = nullptr;
3730
3731 int32_t tabIndex = forward ? 1 : 0;
3732 nsCOMPtr<nsIContent> focusedContent = startContent;
3733 bool startingFromDescendantOfFocusedContent = false;
3734 if (startContent) {
3735 nsIFrame* frame = startContent->GetPrimaryFrame();
3736 tabIndex = (frame && !startContent->IsHTMLElement(nsGkAtoms::area))
3737 ? frame->IsFocusable().mTabIndex
3738 : startContent->IsFocusableWithoutStyle().mTabIndex;
3739
3740 if (!aStartContent &&
3741 (aType == MOVEFOCUS_FORWARD || aType == MOVEFOCUS_BACKWARD)) {
3742 bool considerStartingPoint = false;
3743 GetSequentialFocusNavigationStartingPoint(doc, focusedContent, forward,
3744 getter_AddRefs(startContent),
3745 &considerStartingPoint);
3746 // Should always have a starting point, since there is focused content.
3747 MOZ_ASSERT(startContent)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startContent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(startContent))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("startContent", "./../../../dom/base/nsFocusManager.cpp"
, 3747); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startContent"
")"); do { MOZ_CrashSequence(__null, 3747); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3748 if (focusedContent != startContent) {
3749 startingFromDescendantOfFocusedContent = true;
3750 // not starting from focused content - ignore tabindex
3751 ignoreTabIndex = true;
3752 if (considerStartingPoint && startContent->IsElement() &&
3753 startContent->GetPrimaryFrame() &&
3754 startContent->GetPrimaryFrame()->IsFocusable().IsTabbable()) {
3755 NS_ADDREF(*aNextContent = startContent)(*aNextContent = startContent)->AddRef();
3756 return NS_OK;
3757 }
3758 }
3759 }
3760
3761 // if the current element isn't tabbable, ignore the tabindex and just
3762 // look for the next element. The root content won't have a tabindex
3763 // so just treat this as the beginning of the tab order.
3764 if (tabIndex < 0) {
3765 tabIndex = 1;
3766 if (startContent != rootElement) {
3767 ignoreTabIndex = true;
3768 }
3769 }
3770
3771 // check if the focus is currently inside a popup. Elements such as the
3772 // autocomplete widget use the noautofocus attribute to allow the focus to
3773 // remain outside the popup when it is opened.
3774 if (frame) {
3775 popupFrame = nsLayoutUtils::GetClosestFrameOfType(
3776 frame, LayoutFrameType::MenuPopup);
3777 }
3778
3779 if (popupFrame && !forDocumentNavigation) {
3780 // Don't navigate outside of a popup, so pretend that the
3781 // root content is the popup itself
3782 rootElement = popupFrame->GetContent()->AsElement();
3783 NS_ASSERTION(rootElement, "Popup frame doesn't have a content node")do { if (!(rootElement)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Popup frame doesn't have a content node"
, "rootElement", "./../../../dom/base/nsFocusManager.cpp", 3783
); MOZ_PretendNoReturn(); } } while (0)
;
3784 } else if (!forward) {
3785 // If focus moves backward and when current focused node is root
3786 // content or <body> element which is editable by contenteditable
3787 // attribute, focus should move to its parent document.
3788 if (startContent == rootElement) {
3789 doNavigation = false;
3790 } else {
3791 Document* doc = startContent->GetComposedDoc();
3792 if (startContent ==
3793 nsLayoutUtils::GetEditableRootContentByContentEditable(doc)) {
3794 doNavigation = false;
3795 }
3796 }
3797 }
3798 } else {
3799 if (aType != MOVEFOCUS_CARET) {
3800 // if there is no focus, yet a panel is open, focus the first item in
3801 // the panel
3802 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
3803 if (pm) {
3804 popupFrame = pm->GetTopPopup(PopupType::Panel);
3805 }
3806 }
3807 if (popupFrame) {
3808 // When there is a popup open, and no starting content, start the search
3809 // at the topmost popup.
3810 startContent = popupFrame->GetContent();
3811 NS_ASSERTION(startContent, "Popup frame doesn't have a content node")do { if (!(startContent)) { NS_DebugBreak(NS_DEBUG_ASSERTION,
"Popup frame doesn't have a content node", "startContent", "./../../../dom/base/nsFocusManager.cpp"
, 3811); MOZ_PretendNoReturn(); } } while (0)
;
3812 // Unless we are searching for documents, set the root content to the
3813 // popup as well, so that we don't tab-navigate outside the popup.
3814 // When navigating by documents, we start at the popup but can navigate
3815 // outside of it to look for other panels and documents.
3816 if (!forDocumentNavigation) {
3817 rootElement = startContent->AsElement();
3818 }
3819
3820 doc = startContent ? startContent->GetComposedDoc() : nullptr;
3821 } else {
3822 // Otherwise, for content shells, start from the location of the caret.
3823 nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
3824 if (docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
3825 bool considerStartContent = false;
3826 RefPtr<nsIContent> endSelectionContent;
3827 if (aType == MOVEFOCUS_FORWARD || aType == MOVEFOCUS_BACKWARD) {
3828 GetSequentialFocusNavigationStartingPoint(
3829 doc, nullptr, forward, getter_AddRefs(startContent),
3830 &considerStartContent);
3831 } else {
3832 GetSelectionLocation(doc, presShell, getter_AddRefs(startContent),
3833 getter_AddRefs(endSelectionContent));
3834 }
3835 // If starting from a focusable and tabbable element, we want to make it
3836 // focused rather than next/previous one.
3837 if (considerStartContent && startContent && startContent->IsElement() &&
3838 startContent->GetPrimaryFrame() &&
3839 startContent->GetPrimaryFrame()->IsFocusable().IsTabbable()) {
3840 NS_ADDREF(*aNextContent = startContent)(*aNextContent = startContent)->AddRef();
3841 return NS_OK;
3842 }
3843 // If the selection is on the rootElement, then there is no selection
3844 if (startContent == rootElement) {
3845 startContent = nullptr;
3846 }
3847
3848 if (aType == MOVEFOCUS_CARET) {
3849 // GetFocusInSelection finds a focusable link near the caret.
3850 // If there is no start content though, don't do this to avoid
3851 // focusing something unexpected.
3852 if (startContent) {
3853 GetFocusInSelection(aWindow, startContent, endSelectionContent,
3854 aNextContent);
3855 }
3856 return NS_OK;
3857 }
3858
3859 if (startContent) {
3860 // when not starting from focused element, we always want to find the
3861 // next or previous element in the document. So the tabindex on
3862 // elements should be ignored.
3863 ignoreTabIndex = true;
3864 }
3865 }
3866
3867 if (!startContent) {
3868 // otherwise, just use the root content as the starting point
3869 startContent = rootElement;
3870 NS_ENSURE_TRUE(startContent, NS_OK)do { if ((__builtin_expect(!!(!(startContent)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "startContent" ") failed"
, nullptr, "./../../../dom/base/nsFocusManager.cpp", 3870); return
NS_OK; } } while (false)
;
3871 }
3872 }
3873 }
3874
3875 // Check if the starting content is the same as the content assigned to the
3876 // retargetdocumentfocus attribute. Is so, we don't want to start searching
3877 // from there but instead from the beginning of the document. Otherwise, the
3878 // content that appears before the retargetdocumentfocus element will never
3879 // get checked as it will be skipped when the focus is retargetted to it.
3880 if (forDocumentNavigation && nsContentUtils::IsChromeDoc(doc)) {
3881 nsAutoString retarget;
3882
3883 if (rootElement->GetAttr(nsGkAtoms::retargetdocumentfocus, retarget)) {
3884 nsIContent* retargetElement = doc->GetElementById(retarget);
3885 // The common case here is the urlbar where focus is on the anonymous
3886 // input inside the textbox, but the retargetdocumentfocus attribute
3887 // refers to the textbox. The Contains check will return false and the
3888 // IsInclusiveDescendantOf check will return true in this case.
3889 if (retargetElement &&
3890 (retargetElement == startContent ||
3891 (!retargetElement->Contains(startContent) &&
3892 startContent->IsInclusiveDescendantOf(retargetElement)))) {
3893 startContent = rootElement;
3894 }
3895 }
3896 }
3897
3898 NS_ASSERTION(startContent, "starting content not set")do { if (!(startContent)) { NS_DebugBreak(NS_DEBUG_ASSERTION,
"starting content not set", "startContent", "./../../../dom/base/nsFocusManager.cpp"
, 3898); MOZ_PretendNoReturn(); } } while (0)
;
3899
3900 // keep a reference to the starting content. If we find that again, it means
3901 // we've iterated around completely and we don't want to adjust the focus.
3902 // The skipOriginalContentCheck will be set to true only for the first time
3903 // GetNextTabbableContent is called. This ensures that we don't break out
3904 // when nothing is focused to start with. Specifically,
3905 // GetNextTabbableContent first checks the root content -- which happens to
3906 // be the same as the start content -- when nothing is focused and tabbing
3907 // forward. Without skipOriginalContentCheck set to true, we'd end up
3908 // returning right away and focusing nothing. Luckily, GetNextTabbableContent
3909 // will never wrap around on its own, and can only return the original
3910 // content when it is called a second time or later.
3911 bool skipOriginalContentCheck = true;
3912 const nsCOMPtr<nsIContent> originalStartContent = startContent;
3913
3914 LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent.get())if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusNavigationLog
, LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(startContent.get()) { startContent.get()->NodeInfo()->
NameAtom()->ToUTF8String(tag); } do { const ::mozilla::LogModule
* moz_real_module = gFocusNavigationLog; if ((__builtin_expect
(!!(mozilla::detail::log_test(moz_real_module, LogLevel::Debug
)), 0))) { mozilla::detail::log_print(moz_real_module, LogLevel
::Debug, "Focus Navigation Start Content %s", tag.get()); } }
while (0); }
;
3915 LOGFOCUSNAVIGATION((" Forward: %d Tabindex: %d Ignore: %d DocNav: %d",do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Forward: %d Tabindex: %d Ignore: %d DocNav: %d"
, forward, tabIndex, ignoreTabIndex, forDocumentNavigation); }
} while (0)
3916 forward, tabIndex, ignoreTabIndex,do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Forward: %d Tabindex: %d Ignore: %d DocNav: %d"
, forward, tabIndex, ignoreTabIndex, forDocumentNavigation); }
} while (0)
3917 forDocumentNavigation))do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " Forward: %d Tabindex: %d Ignore: %d DocNav: %d"
, forward, tabIndex, ignoreTabIndex, forDocumentNavigation); }
} while (0)
;
3918
3919 while (doc) {
3920 if (doNavigation) {
3921 nsCOMPtr<nsIContent> nextFocus;
3922 // TODO: MOZ_KnownLive is reruired due to bug 1770680
3923 nsresult rv = GetNextTabbableContent(
3924 presShell, rootElement,
3925 MOZ_KnownLive(skipOriginalContentCheck ? nullptr(skipOriginalContentCheck ? nullptr : originalStartContent.get
())
3926 : originalStartContent.get())(skipOriginalContentCheck ? nullptr : originalStartContent.get
())
,
3927 startContent, forward, tabIndex, ignoreTabIndex,
3928 forDocumentNavigation, aNavigateByKey, false,
3929 getter_AddRefs(nextFocus));
3930 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../dom/base/nsFocusManager.cpp"
, 3930); return rv; } } while (false)
;
3931 if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
3932 // Navigation was redirected to a child process, so just return.
3933 return NS_OK;
3934 }
3935
3936 // found a content node to focus.
3937 if (nextFocus) {
3938 if (startingFromDescendantOfFocusedContent && tabIndex >= 0 &&
3939 ignoreTabIndex &&
3940 !nextFocus->IsInclusiveFlatTreeDescendantOf(focusedContent)) {
3941 // We started from the selection inside the focused content,
3942 // but we ended up outside of the focused content's subtree.
3943 // So we should start again without ignoring tab index.
3944 ignoreTabIndex = false;
3945 continue;
3946 }
3947 if (startingFromDescendantOfFocusedContent &&
3948 nextFocus == focusedContent) {
3949 // Got back to the original focused content. This can happen with
3950 // backward navigation if selection is inside the focused element,
3951 // and there is nothing else focusable before it.
3952 if (tabIndex >= 0) {
3953 ignoreTabIndex = false;
3954 }
3955 startContent = nextFocus;
3956 // In certain cases (e.g. only one focusable element in the document)
3957 // we can end up looping around the whole document and reaching the
3958 // focused content again. In these cases, we don't want to loop
3959 // infinitely.
3960 startingFromDescendantOfFocusedContent = false;
3961 continue;
3962 }
3963
3964 LOGCONTENTNAVIGATION("Next Content: %s", nextFocus.get())if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusNavigationLog
, LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(nextFocus.get()) { nextFocus.get()->NodeInfo()->NameAtom
()->ToUTF8String(tag); } do { const ::mozilla::LogModule* moz_real_module
= gFocusNavigationLog; if ((__builtin_expect(!!(mozilla::detail
::log_test(moz_real_module, LogLevel::Debug)), 0))) { mozilla
::detail::log_print(moz_real_module, LogLevel::Debug, "Next Content: %s"
, tag.get()); } } while (0); }
;
3965
3966 // as long as the found node was not the same as the starting node,
3967 // set it as the return value. For document navigation, we can return
3968 // the same element in case there is only one content node that could
3969 // be returned, for example, in a child process document.
3970 if (nextFocus != originalStartContent || forDocumentNavigation) {
3971 nextFocus.forget(aNextContent);
3972 }
3973 return NS_OK;
3974 }
3975
3976 if (popupFrame && !forDocumentNavigation) {
3977 // in a popup, so start again from the beginning of the popup. However,
3978 // if we already started at the beginning, then there isn't anything to
3979 // focus, so just return
3980 if (startContent != rootElement) {
3981 startContent = rootElement;
3982 tabIndex = forward ? 1 : 0;
3983 continue;
3984 }
3985 return NS_OK;
3986 }
3987 }
3988
3989 doNavigation = true;
3990 skipOriginalContentCheck = forDocumentNavigation;
3991 ignoreTabIndex = false;
3992
3993 if (aNoParentTraversal) {
3994 if (startContent == rootElement) {
3995 return NS_OK;
3996 }
3997
3998 startContent = rootElement;
3999 tabIndex = forward ? 1 : 0;
4000 continue;
4001 }
4002
4003 // Reached the beginning or end of the document. Next, navigate up to the
4004 // parent document and try again.
4005 nsCOMPtr<nsPIDOMWindowOuter> piWindow = doc->GetWindow();
4006 NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(piWindow)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "piWindow" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 4006); return NS_ERROR_FAILURE
; } } while (false)
;
4007
4008 nsCOMPtr<nsIDocShell> docShell = piWindow->GetDocShell();
4009 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(docShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "docShell" ") failed", nullptr
, "./../../../dom/base/nsFocusManager.cpp", 4009); return NS_ERROR_FAILURE
; } } while (false)
;
4010
4011 // Get the frame element this window is inside and, from that, get the
4012 // parent document and presshell. If there is no enclosing frame element,
4013 // then this is a top-level, embedded or remote window.
4014 startContent = piWindow->GetFrameElementInternal();
4015 if (startContent) {
4016 doc = startContent->GetComposedDoc();
4017 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(doc)), 0))) { NS_DebugBreak(NS_DEBUG_WARNING
, "NS_ENSURE_TRUE(" "doc" ") failed", nullptr, "./../../../dom/base/nsFocusManager.cpp"
, 4017); return NS_ERROR_FAILURE; } } while (false)
;
4018
4019 rootElement = doc->GetRootElement();
4020 presShell = doc->GetPresShell();
4021
4022 // We can focus the root element now that we have moved to another
4023 // document.
4024 mayFocusRoot = true;
4025
4026 nsIFrame* frame = startContent->GetPrimaryFrame();
4027 if (!frame) {
4028 return NS_OK;
4029 }
4030
4031 tabIndex = frame->IsFocusable().mTabIndex;
4032 if (tabIndex < 0) {
4033 tabIndex = 1;
4034 ignoreTabIndex = true;
4035 }
4036
4037 // if the frame is inside a popup, make sure to scan only within the
4038 // popup. This handles the situation of tabbing amongst elements
4039 // inside an iframe which is itself inside a popup. Otherwise,
4040 // navigation would move outside the popup when tabbing outside the
4041 // iframe.
4042 if (!forDocumentNavigation) {
4043 popupFrame = nsLayoutUtils::GetClosestFrameOfType(
4044 frame, LayoutFrameType::MenuPopup);
4045 if (popupFrame) {
4046 rootElement = popupFrame->GetContent()->AsElement();
4047 NS_ASSERTION(rootElement, "Popup frame doesn't have a content node")do { if (!(rootElement)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Popup frame doesn't have a content node"
, "rootElement", "./../../../dom/base/nsFocusManager.cpp", 4047
); MOZ_PretendNoReturn(); } } while (0)
;
4048 }
4049 }
4050 } else {
4051 if (aNavigateByKey) {
4052 // There is no parent, so move the focus to the parent process.
4053 if (auto* child = BrowserChild::GetFrom(docShell)) {
4054 child->SendMoveFocus(forward, forDocumentNavigation);
4055 // Blur the current element.
4056 RefPtr<BrowsingContext> focusedBC = GetFocusedBrowsingContext();
4057 if (focusedBC && focusedBC->IsInProcess()) {
4058 Blur(focusedBC, nullptr, true, true, false,
4059 GenerateFocusActionId());
4060 } else {
4061 nsCOMPtr<nsPIDOMWindowOuter> window = docShell->GetWindow();
4062 window->SetFocusedElement(nullptr);
4063 }
4064 return NS_OK;
4065 }
4066 }
4067
4068 // If we have reached the end of the top-level document, focus the
4069 // first element in the top-level document. This should always happen
4070 // when navigating by document forwards but when navigating backwards,
4071 // only do this if we started in another document or within a popup frame.
4072 // If the focus started in this window outside a popup however, we should
4073 // continue by looping around to the end again.
4074 if (forDocumentNavigation && (forward || mayFocusRoot || popupFrame)) {
4075 // HTML content documents can have their root element focused by
4076 // pressing F6(a focus ring appears around the entire content area
4077 // frame). This root appears in the tab order before all of the elements
4078 // in the document. Chrome documents however cannot be focused directly,
4079 // so instead we focus the first focusable element within the window.
4080 // For example, the urlbar.
4081 RefPtr<Element> rootElementForFocus =
4082 GetRootForFocus(piWindow, doc, true, true);
4083 return FocusFirst(rootElementForFocus, aNextContent,
4084 true /* aReachedToEndForDocumentNavigation */);
4085 }
4086
4087 // Once we have hit the top-level and have iterated to the end again, we
4088 // just want to break out next time we hit this spot to prevent infinite
4089 // iteration.
4090 mayFocusRoot = true;
4091
4092 // reset the tab index and start again from the beginning or end
4093 startContent = rootElement;
4094 tabIndex = forward ? 1 : 0;
4095 }
4096
4097 // wrapped all the way around and didn't find anything to move the focus
4098 // to, so just break out
4099 if (startContent == originalStartContent) {
4100 break;
4101 }
4102 }
4103
4104 return NS_OK;
4105}
4106
4107uint32_t nsFocusManager::ProgrammaticFocusFlags(const FocusOptions& aOptions) {
4108 uint32_t flags = FLAG_BYJS;
4109 if (aOptions.mPreventScroll) {
4110 flags |= FLAG_NOSCROLL;
4111 }
4112 if (aOptions.mFocusVisible.WasPassed()) {
4113 flags |= aOptions.mFocusVisible.Value() ? FLAG_SHOWRING : FLAG_NOSHOWRING;
4114 }
4115 if (UserActivation::IsHandlingKeyboardInput()) {
4116 flags |= FLAG_BYKEY;
4117 }
4118 // TODO: We could do a similar thing if we're handling mouse input, but that
4119 // changes focusability of some elements so may be more risky.
4120 return flags;
4121}
4122
4123static bool IsHostOrSlot(const nsIContent* aContent) {
4124 return aContent && (aContent->GetShadowRoot() ||
4125 aContent->IsHTMLElement(nsGkAtoms::slot));
4126}
4127
4128// Helper class to iterate contents in scope by traversing flattened tree
4129// in tree order
4130class MOZ_STACK_CLASS ScopedContentTraversal {
4131 public:
4132 ScopedContentTraversal(nsIContent* aStartContent, nsIContent* aOwner)
4133 : mCurrent(aStartContent), mOwner(aOwner) {
4134 MOZ_ASSERT(aStartContent)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStartContent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStartContent))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aStartContent",
"./../../../dom/base/nsFocusManager.cpp", 4134); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aStartContent" ")"); do { MOZ_CrashSequence
(__null, 4134); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4135 MOZ_ASSERT(IsScopeOwner(aOwner))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsScopeOwner(aOwner))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsScopeOwner(aOwner)))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsScopeOwner(aOwner)"
, "./../../../dom/base/nsFocusManager.cpp", 4135); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsScopeOwner(aOwner)" ")"); do { MOZ_CrashSequence
(__null, 4135); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4136 MOZ_ASSERT_IF(aOwner != aStartContent,do { if (aOwner != aStartContent) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(aOwner == FindScopeOwner
(aStartContent))>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aOwner == FindScopeOwner(aStartContent
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aOwner == FindScopeOwner(aStartContent)", "./../../../dom/base/nsFocusManager.cpp"
, 4137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aOwner == FindScopeOwner(aStartContent)"
")"); do { MOZ_CrashSequence(__null, 4137); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
4137 aOwner == FindScopeOwner(aStartContent))do { if (aOwner != aStartContent) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(aOwner == FindScopeOwner
(aStartContent))>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aOwner == FindScopeOwner(aStartContent
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aOwner == FindScopeOwner(aStartContent)", "./../../../dom/base/nsFocusManager.cpp"
, 4137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aOwner == FindScopeOwner(aStartContent)"
")"); do { MOZ_CrashSequence(__null, 4137); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
4138 }
4139
4140 void Next();
4141 void Prev();
4142
4143 void Reset() { SetCurrent(mOwner); }
4144
4145 nsIContent* GetCurrent() const { return mCurrent; }
4146
4147 private:
4148 void SetCurrent(nsIContent* aContent) { mCurrent = aContent; }
4149
4150 nsIContent* mCurrent;
4151 nsIContent* mOwner;
4152};
4153
4154// Get next child from aIter that isn't an open popover with invoker
4155// (Open popover with invoker is handled specially because its position in the
4156// DOM is different from its position in focus navigation)
4157static nsIContent* GetNextNonPopover(StyleChildrenIterator& aIter) {
4158 while (nsIContent* child = aIter.GetNextChild()) {
4159 if (!GetOpenPopoverInvoker(child)) {
4160 return child;
4161 }
4162 }
4163 return nullptr;
4164}
4165
4166void ScopedContentTraversal::Next() {
4167 MOZ_ASSERT(mCurrent)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mCurrent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mCurrent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mCurrent", "./../../../dom/base/nsFocusManager.cpp"
, 4167); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCurrent" ")"
); do { MOZ_CrashSequence(__null, 4167); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4168
4169 // If start with the scope owner, get its first "child" in the scope.
4170 if (mCurrent == mOwner) {
4171 if (IsHostOrSlot(mCurrent)) {
4172 StyleChildrenIterator iter(mCurrent);
4173 SetCurrent(GetNextNonPopover(iter));
4174 return;
4175 }
4176
4177 SetCurrent(GetAssociatedPopoverFromInvoker(mCurrent));
4178 MOZ_ASSERT(mCurrent)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mCurrent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mCurrent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mCurrent", "./../../../dom/base/nsFocusManager.cpp"
, 4178); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCurrent" ")"
); do { MOZ_CrashSequence(__null, 4178); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4179 return;
4180 }
4181
4182 // If mCurrent is a scope owner, don't enter its scope.
4183 if (!IsScopeOwner(mCurrent)) {
4184 StyleChildrenIterator iter(mCurrent);
4185 if (nsIContent* child = GetNextNonPopover(iter)) {
4186 SetCurrent(child);
4187 return;
4188 }
4189 }
4190
4191 nsIContent* current = mCurrent;
4192 while (true) {
4193 // Special case for popover, if current is an open popover and its invoker
4194 // is mOwner, there is no next node can be traversed, END traversal.
4195 if (GetOpenPopoverInvoker(current) == mOwner) {
4196 SetCurrent(nullptr);
4197 return;
4198 }
4199
4200 // Create parent's iterator and move to current
4201 nsIContent* parent = current->GetFlattenedTreeParent();
4202 StyleChildrenIterator parentIter(parent);
4203 parentIter.Seek(current);
4204
4205 // Get next sibling of current which is not an open popover with invoker.
4206 if (nsIContent* next = GetNextNonPopover(parentIter)) {
4207 SetCurrent(next);
4208 return;
4209 }
4210
4211 // If no next sibling and parent is mOwner, END traversal
4212 if (parent == mOwner) {
4213 SetCurrent(nullptr);
4214 return;
4215 }
4216
4217 current = parent;
4218 }
4219}
4220
4221// Get previous child from aIter that isn't an open popover with invoker
4222static nsIContent* GetPreviousNonPopover(StyleChildrenIterator& aIter) {
4223 while (nsIContent* child = aIter.GetPreviousChild()) {
4224 if (!GetOpenPopoverInvoker(child)) {
4225 return child;
4226 }
4227 }
4228 return nullptr;
4229}
4230
4231void ScopedContentTraversal::Prev() {
4232 MOZ_ASSERT(mCurrent)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mCurrent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mCurrent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mCurrent", "./../../../dom/base/nsFocusManager.cpp"
, 4232); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCurrent" ")"
); do { MOZ_CrashSequence(__null, 4232); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4233
4234 nsIContent* parent;
4235 nsIContent* last;
4236 if (mCurrent == mOwner) {
4237 // Get last child of mOwner
4238 if (IsHostOrSlot(mCurrent)) {
4239 StyleChildrenIterator ownerIter(mCurrent, false /* aStartAtBeginning */);
4240 last = GetPreviousNonPopover(ownerIter);
4241 } else {
4242 last = GetAssociatedPopoverFromInvoker(mCurrent);
4243 MOZ_ASSERT(last)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(last)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(last))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("last", "./../../../dom/base/nsFocusManager.cpp"
, 4243); AnnotateMozCrashReason("MOZ_ASSERT" "(" "last" ")");
do { MOZ_CrashSequence(__null, 4243); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4244 }
4245
4246 parent = last;
4247 } else {
4248 parent = GetOpenPopoverInvoker(mCurrent);
4249 if (parent) {
4250 MOZ_ASSERT(parent == mOwner)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(parent == mOwner)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(parent == mOwner))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("parent == mOwner"
, "./../../../dom/base/nsFocusManager.cpp", 4250); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "parent == mOwner" ")"); do { MOZ_CrashSequence
(__null, 4250); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4251 // If current is an open popover, there is no previous node can be
4252 // traversed.
4253 last = nullptr;
4254 } else {
4255 // Create parent's iterator and move to mCurrent
4256 parent = mCurrent->GetFlattenedTreeParent();
4257 StyleChildrenIterator parentIter(parent);
4258 parentIter.Seek(mCurrent);
4259
4260 // Get previous sibling which is not an open popover with invoker.
4261 last = GetPreviousNonPopover(parentIter);
4262 }
4263 }
4264
4265 while (last) {
4266 parent = last;
4267 if (IsScopeOwner(parent)) {
4268 // Skip contents in other scopes
4269 break;
4270 }
4271
4272 // Find last child
4273 StyleChildrenIterator iter(parent, false /* aStartAtBeginning */);
4274 last = GetPreviousNonPopover(iter);
4275 }
4276
4277 // If parent is mOwner and no previous sibling remains, END traversal
4278 SetCurrent(parent == mOwner ? nullptr : parent);
4279}
4280
4281static nsGenericHTMLElement* GetAssociatedPopoverFromInvoker(
4282 const nsIContent* aContent) {
4283 const Element* invoker = Element::FromNode(aContent);
4284 if (!invoker) {
4285 return nullptr;
4286 }
4287 nsGenericHTMLElement* popover = invoker->GetAssociatedPopover();
4288 if (popover && popover->IsPopoverOpen()) {
4289 MOZ_ASSERT(popover->GetPopoverData()->GetInvoker() == invoker)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(popover->GetPopoverData()->GetInvoker() == invoker
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(popover->GetPopoverData()->GetInvoker() == invoker
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"popover->GetPopoverData()->GetInvoker() == invoker", "./../../../dom/base/nsFocusManager.cpp"
, 4289); AnnotateMozCrashReason("MOZ_ASSERT" "(" "popover->GetPopoverData()->GetInvoker() == invoker"
")"); do { MOZ_CrashSequence(__null, 4289); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4290 return popover;
4291 }
4292 return nullptr;
4293}
4294
4295/**
4296 * Returns true if the content is a Document, Host, Slot or Popover invoker,
4297 * see https://html.spec.whatwg.org/#focus-navigation-scope-owner
4298 */
4299static bool IsScopeOwner(const nsIContent* aContent) {
4300 return aContent && (IsHostOrSlot(aContent) || aContent->IsDocument() ||
4301 !!GetAssociatedPopoverFromInvoker(aContent));
4302}
4303
4304/**
4305 * Returns scope owner of aContent.
4306 * A scope owner is either a shadow host, or slot, or a popover invoker.
4307 * See https://html.spec.whatwg.org/#associated-focus-navigation-owner.
4308 */
4309static nsIContent* FindScopeOwner(nsIContent* aContent) {
4310 nsIContent* currentContent = aContent;
4311 while (currentContent) {
4312 // 5. If element is in the popover showing state and has a popover trigger
4313 // set, then return element's popover trigger.
4314 if (nsIContent* invoker = GetOpenPopoverInvoker(currentContent)) {
4315 return invoker;
4316 }
4317
4318 // 2. If element's parent is a shadow host, then return element's assigned
4319 // slot.
4320 // 3. If element's parent is a shadow root, then return the parent's host.
4321 // 4. If element's parent is the document element, then return the parent's
4322 // node document.
4323 nsIContent* parent = currentContent->GetFlattenedTreeParent();
4324 if (IsScopeOwner(parent)) {
4325 return parent;
4326 }
4327
4328 currentContent = parent;
4329 }
4330
4331 // 1. If element's parent is null, then return null.
4332 return nullptr;
4333}
4334
4335/**
4336 * Scope owner elements need to be handled as if they had tabindex 0 even
4337 * when they don't have the attribute. This is a helper method to get the
4338 * right value for focus navigation. If aIsFocusable is passed, it is set to
4339 * true if the element itself is focusable.
4340 */
4341static int32_t ScopeOwnerTabIndexValue(const nsIContent* aContent,
4342 bool* aIsFocusable = nullptr) {
4343 MOZ_ASSERT(IsScopeOwner(aContent))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsScopeOwner(aContent))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsScopeOwner(aContent)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsScopeOwner(aContent)"
, "./../../../dom/base/nsFocusManager.cpp", 4343); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsScopeOwner(aContent)" ")"); do { MOZ_CrashSequence
(__null, 4343); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4344
4345 if (aIsFocusable) {
4346 nsIFrame* frame = aContent->GetPrimaryFrame();
4347 *aIsFocusable = frame && frame->IsFocusable().mTabIndex >= 0;
4348 }
4349
4350 const nsAttrValue* attrVal =
4351 aContent->AsElement()->GetParsedAttr(nsGkAtoms::tabindex);
4352 if (!attrVal) {
4353 return 0;
4354 }
4355
4356 if (attrVal->Type() == nsAttrValue::eInteger) {
4357 return attrVal->GetIntegerValue();
4358 }
4359
4360 return -1;
4361}
4362
4363nsIContent* nsFocusManager::GetNextTabbableContentInScope(
4364 nsIContent* aOwner, nsIContent* aStartContent,
4365 nsIContent* aOriginalStartContent, bool aForward, int32_t aCurrentTabIndex,
4366 bool aIgnoreTabIndex, bool aForDocumentNavigation, bool aNavigateByKey,
4367 bool aSkipOwner, bool aReachedToEndForDocumentNavigation) {
4368 MOZ_ASSERT(aOwner, "aOwner must not be null")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOwner)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aOwner))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aOwner" " (" "aOwner must not be null"
")", "./../../../dom/base/nsFocusManager.cpp", 4368); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aOwner" ") (" "aOwner must not be null" ")"
); do { MOZ_CrashSequence(__null, 4368); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4369 MOZ_ASSERT(IsScopeOwner(aOwner),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsScopeOwner(aOwner))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsScopeOwner(aOwner)))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsScopeOwner(aOwner)"
" (" "Scope owner should be host, slot or popover invoker set"
")", "./../../../dom/base/nsFocusManager.cpp", 4370); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsScopeOwner(aOwner)" ") (" "Scope owner should be host, slot or popover invoker set"
")"); do { MOZ_CrashSequence(__null, 4370); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4370 "Scope owner should be host, slot or popover invoker set")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsScopeOwner(aOwner))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsScopeOwner(aOwner)))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsScopeOwner(aOwner)"
" (" "Scope owner should be host, slot or popover invoker set"
")", "./../../../dom/base/nsFocusManager.cpp", 4370); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsScopeOwner(aOwner)" ") (" "Scope owner should be host, slot or popover invoker set"
")"); do { MOZ_CrashSequence(__null, 4370); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4371
4372 // XXX: Why don't we ignore tabindex when the current tabindex < 0?
4373 MOZ_ASSERT_IF(aCurrentTabIndex < 0, aIgnoreTabIndex)do { if (aCurrentTabIndex < 0) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(aIgnoreTabIndex)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aIgnoreTabIndex))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aIgnoreTabIndex", "./../../../dom/base/nsFocusManager.cpp",
4373); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aIgnoreTabIndex"
")"); do { MOZ_CrashSequence(__null, 4373); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
4374
4375 if (!aSkipOwner && (aForward && aOwner == aStartContent)) {
4376 if (nsIFrame* frame = aOwner->GetPrimaryFrame()) {
4377 auto focusable = frame->IsFocusable();
4378 if (focusable && focusable.mTabIndex >= 0 &&
4379 (!aForDocumentNavigation || aReachedToEndForDocumentNavigation)) {
4380 return aOwner;
4381 }
4382 }
4383 }
4384
4385 //
4386 // Iterate contents in scope
4387 //
4388 ScopedContentTraversal contentTraversal(aStartContent, aOwner);
4389 nsCOMPtr<nsIContent> iterContent;
4390 nsIContent* firstNonChromeOnly =
4391 aStartContent->IsInNativeAnonymousSubtree()
4392 ? aStartContent->FindFirstNonChromeOnlyAccessContent()
4393 : nullptr;
4394 while (true) {
4395 // Iterate tab index to find corresponding contents in scope
4396
4397 while (true) {
4398 // Iterate remaining contents in scope to find next content to focus
4399
4400 // Get next content
4401 aForward ? contentTraversal.Next() : contentTraversal.Prev();
4402 iterContent = contentTraversal.GetCurrent();
4403
4404 if (firstNonChromeOnly && firstNonChromeOnly == iterContent) {
4405 // We just broke out from the native anonymous content, so move
4406 // to the previous/next node of the native anonymous owner.
4407 if (aForward) {
4408 contentTraversal.Next();
4409 } else {
4410 contentTraversal.Prev();
4411 }
4412 iterContent = contentTraversal.GetCurrent();
4413 }
4414 if (!iterContent) {
4415 // Reach the end
4416 break;
4417 }
4418
4419 int32_t tabIndex = 0;
4420 if (IsScopeOwner(iterContent)) {
4421 tabIndex = ScopeOwnerTabIndexValue(iterContent);
4422 } else {
4423 nsIFrame* frame = iterContent->GetPrimaryFrame();
4424 if (!frame) {
4425 continue;
4426 }
4427 tabIndex = frame->IsFocusable().mTabIndex;
4428 }
4429 if (tabIndex < 0 || !(aIgnoreTabIndex || tabIndex == aCurrentTabIndex)) {
4430 continue;
4431 }
4432
4433 if (!IsScopeOwner(iterContent)) {
4434 nsCOMPtr<nsIContent> elementInFrame;
4435 bool checkSubDocument = true;
4436 if (aForDocumentNavigation &&
4437 TryDocumentNavigation(iterContent, &checkSubDocument,
4438 getter_AddRefs(elementInFrame))) {
4439 return elementInFrame;
4440 }
4441 if (!checkSubDocument) {
4442 if (aReachedToEndForDocumentNavigation &&
4443 nsContentUtils::IsChromeDoc(iterContent->GetComposedDoc())) {
4444 // aReachedToEndForDocumentNavigation is true means
4445 // 1. This is a document navigation (i.e, VK_F6, Control + Tab)
4446 // 2. This is the top-level document (Note that we may start from
4447 // a subdocument)
4448 // 3. We've searched through the this top-level document already
4449 if (!GetRootForChildDocument(iterContent)) {
4450 // We'd like to focus the first focusable element of this
4451 // top-level chrome document.
4452 return iterContent;
4453 }
4454 }
4455 continue;
4456 }
4457
4458 if (TryToMoveFocusToSubDocument(iterContent, aOriginalStartContent,
4459 aForward, aForDocumentNavigation,
4460 aNavigateByKey,
4461 aReachedToEndForDocumentNavigation,
4462 getter_AddRefs(elementInFrame))) {
4463 return elementInFrame;
4464 }
4465
4466 // Found content to focus
4467 return iterContent;
4468 }
4469
4470 // Search in scope owned by iterContent
4471 nsIContent* contentToFocus = GetNextTabbableContentInScope(
4472 iterContent, iterContent, aOriginalStartContent, aForward,
4473 aForward ? 1 : 0, aIgnoreTabIndex, aForDocumentNavigation,
4474 aNavigateByKey, false /* aSkipOwner */,
4475 aReachedToEndForDocumentNavigation);
4476 if (contentToFocus) {
4477 return contentToFocus;
4478 }
4479 };
4480
4481 // If already at lowest priority tab (0), end search completely.
4482 // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
4483 if (aCurrentTabIndex == (aForward ? 0 : 1)) {
4484 break;
4485 }
4486
4487 // We've been just trying to find some focusable element, and haven't, so
4488 // bail out.
4489 if (aIgnoreTabIndex) {
4490 break;
4491 }
4492
4493 // Continue looking for next highest priority tabindex
4494 aCurrentTabIndex = GetNextTabIndex(aOwner, aCurrentTabIndex, aForward);
4495 contentTraversal.Reset();
4496 }
4497
4498 // Return scope owner at last for backward navigation if its tabindex
4499 // is non-negative
4500 if (!aSkipOwner && !aForward) {
4501 if (nsIFrame* frame = aOwner->GetPrimaryFrame()) {
4502 auto focusable = frame->IsFocusable();
4503 if (focusable && focusable.mTabIndex >= 0 &&
4504 (!aForDocumentNavigation || aReachedToEndForDocumentNavigation)) {
4505 return aOwner;
4506 }
4507 }
4508 }
4509
4510 return nullptr;
4511}
4512
4513nsIContent* nsFocusManager::GetNextTabbableContentInAncestorScopes(
4514 nsIContent* aStartOwner, nsCOMPtr<nsIContent>& aStartContent /* inout */,
4515 nsIContent* aOriginalStartContent, bool aForward, int32_t* aCurrentTabIndex,
4516 bool* aIgnoreTabIndex, bool aForDocumentNavigation, bool aNavigateByKey,
4517 bool aReachedToEndForDocumentNavigation) {
4518 MOZ_ASSERT(aStartOwner == FindScopeOwner(aStartContent),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStartOwner == FindScopeOwner(aStartContent))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aStartOwner == FindScopeOwner(aStartContent)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("aStartOwner == FindScopeOwner(aStartContent)"
" (" "aStartOwner should be the scope owner of aStartContent"
")", "./../../../dom/base/nsFocusManager.cpp", 4519); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aStartOwner == FindScopeOwner(aStartContent)"
") (" "aStartOwner should be the scope owner of aStartContent"
")"); do { MOZ_CrashSequence(__null, 4519); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4519 "aStartOwner should be the scope owner of aStartContent")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStartOwner == FindScopeOwner(aStartContent))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aStartOwner == FindScopeOwner(aStartContent)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("aStartOwner == FindScopeOwner(aStartContent)"
" (" "aStartOwner should be the scope owner of aStartContent"
")", "./../../../dom/base/nsFocusManager.cpp", 4519); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aStartOwner == FindScopeOwner(aStartContent)"
") (" "aStartOwner should be the scope owner of aStartContent"
")"); do { MOZ_CrashSequence(__null, 4519); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4520 MOZ_ASSERT(IsScopeOwner(aStartOwner),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsScopeOwner(aStartOwner))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsScopeOwner(aStartOwner))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsScopeOwner(aStartOwner)"
" (" "scope owner should be host, slot, or popover" ")", "./../../../dom/base/nsFocusManager.cpp"
, 4521); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsScopeOwner(aStartOwner)"
") (" "scope owner should be host, slot, or popover" ")"); do
{ MOZ_CrashSequence(__null, 4521); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
4521 "scope owner should be host, slot, or popover")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsScopeOwner(aStartOwner))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsScopeOwner(aStartOwner))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsScopeOwner(aStartOwner)"
" (" "scope owner should be host, slot, or popover" ")", "./../../../dom/base/nsFocusManager.cpp"
, 4521); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsScopeOwner(aStartOwner)"
") (" "scope owner should be host, slot, or popover" ")"); do
{ MOZ_CrashSequence(__null, 4521); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
4522
4523 nsCOMPtr<nsIContent> owner = aStartOwner;
4524 nsCOMPtr<nsIContent> startContent = aStartContent;
4525 while (IsScopeOwner(owner)) {
4526 int32_t tabIndex = 0;
4527 if (IsScopeOwner(startContent)) {
4528 tabIndex = ScopeOwnerTabIndexValue(startContent);
4529 } else if (nsIFrame* frame = startContent->GetPrimaryFrame()) {
4530 tabIndex = frame->IsFocusable().mTabIndex;
4531 } else {
4532 tabIndex = startContent->IsFocusableWithoutStyle().mTabIndex;
4533 }
4534 nsIContent* contentToFocus = GetNextTabbableContentInScope(
4535 owner, startContent, aOriginalStartContent, aForward, tabIndex,
4536 tabIndex < 0, aForDocumentNavigation, aNavigateByKey,
4537 false /* aSkipOwner */, aReachedToEndForDocumentNavigation);
4538 if (contentToFocus && contentToFocus != aStartContent) {
4539 return contentToFocus;
4540 }
4541
4542 startContent = owner;
4543 owner = FindScopeOwner(startContent);
4544 }
4545
4546 // If not found in shadow DOM, search from the top level shadow host in light
4547 // DOM
4548 aStartContent = startContent;
4549 if (IsScopeOwner(startContent)) {
4550 *aCurrentTabIndex = ScopeOwnerTabIndexValue(startContent);
4551 } else if (nsIFrame* frame = startContent->GetPrimaryFrame()) {
4552 *aCurrentTabIndex = frame->IsFocusable().mTabIndex;
4553 } else {
4554 *aCurrentTabIndex = startContent->IsFocusableWithoutStyle().mTabIndex;
4555 }
4556
4557 if (*aCurrentTabIndex < 0) {
4558 *aIgnoreTabIndex = true;
4559 }
4560
4561 return nullptr;
4562}
4563
4564static nsIContent* GetTopLevelScopeOwner(nsIContent* aContent) {
4565 nsIContent* topLevelScopeOwner = nullptr;
4566 nsIContent* currentOwner = FindScopeOwner(aContent);
4567 while (currentOwner) {
4568 topLevelScopeOwner = currentOwner;
4569 currentOwner = FindScopeOwner(currentOwner);
4570 }
4571
4572 return topLevelScopeOwner;
4573}
4574
4575static Maybe<nsresult> MaybeDelegateToRemoteFrame(nsIContent* aContent,
4576 bool aNavigateByKey,
4577 bool aForward,
4578 bool aForDocumentNavigation) {
4579 // If this is a remote child browser, call NavigateByKey to have
4580 // the child process continue the navigation. Return a special error
4581 // code to have the caller return early. If the child ends up not
4582 // being focusable in some way, the child process will call back
4583 // into document navigation again by calling MoveFocus.
4584 if (BrowserParent* remote = BrowserParent::GetFrom(aContent)) {
4585 if (aNavigateByKey) {
4586 remote->NavigateByKey(aForward, aForDocumentNavigation);
4587 return Some(NS_SUCCESS_DOM_NO_OPERATION);
4588 }
4589 return Some(NS_OK);
4590 }
4591
4592 // Same as above but for out-of-process iframes
4593 if (auto* bbc = BrowserBridgeChild::GetFrom(aContent)) {
4594 if (aNavigateByKey) {
4595 bbc->NavigateByKey(aForward, aForDocumentNavigation);
4596 return Some(NS_SUCCESS_DOM_NO_OPERATION);
4597 }
4598 return Some(NS_OK);
4599 }
4600
4601 return Nothing();
4602}
4603
4604nsresult nsFocusManager::GetNextTabbableContent(
4605 PresShell* aPresShell, nsIContent* aRootContent,
4606 nsIContent* aOriginalStartContent, nsIContent* aStartContent, bool aForward,
4607 int32_t aCurrentTabIndex, bool aIgnoreTabIndex, bool aForDocumentNavigation,
4608 bool aNavigateByKey, bool aReachedToEndForDocumentNavigation,
4609 nsIContent** aResultContent) {
4610 *aResultContent = nullptr;
4611
4612 if (!aStartContent) {
4613 return NS_OK;
4614 }
4615
4616 nsCOMPtr<nsIContent> startContent = aStartContent;
4617 nsCOMPtr<nsIContent> currentTopLevelScopeOwner =
4618 GetTopLevelScopeOwner(startContent);
4619
4620 LOGCONTENTNAVIGATION("GetNextTabbable: %s", startContent)if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusNavigationLog
, LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(startContent) { startContent->NodeInfo()->NameAtom()->
ToUTF8String(tag); } do { const ::mozilla::LogModule* moz_real_module
= gFocusNavigationLog; if ((__builtin_expect(!!(mozilla::detail
::log_test(moz_real_module, LogLevel::Debug)), 0))) { mozilla
::detail::log_print(moz_real_module, LogLevel::Debug, "GetNextTabbable: %s"
, tag.get()); } } while (0); }
;
4621 LOGFOCUSNAVIGATION((" tabindex: %d", aCurrentTabIndex))do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " tabindex: %d",
aCurrentTabIndex); } } while (0)
;
4622
4623 if (aForward && IsScopeOwner(startContent)) {
4624 // If startContent is a scope owner, search in scope owned by startContent
4625 nsIContent* contentToFocus = GetNextTabbableContentInScope(
4626 startContent, startContent, aOriginalStartContent, aForward, 1,
4627 aIgnoreTabIndex, aForDocumentNavigation, aNavigateByKey,
4628 true /* aSkipOwner */, aReachedToEndForDocumentNavigation);
4629 if (contentToFocus) {
4630 if (auto rv =
4631 MaybeDelegateToRemoteFrame(contentToFocus, aNavigateByKey,
4632 aForward, aForDocumentNavigation)) {
4633 return *rv;
4634 }
4635 NS_ADDREF(*aResultContent = contentToFocus)(*aResultContent = contentToFocus)->AddRef();
4636 return NS_OK;
4637 }
4638 }
4639
4640 // If startContent is in a scope owned by Shadow DOM or popover, search
4641 // from scope including startContent
4642 if (nsCOMPtr<nsIContent> owner = FindScopeOwner(startContent)) {
4643 nsIContent* contentToFocus = GetNextTabbableContentInAncestorScopes(
4644 owner, startContent /* inout */, aOriginalStartContent, aForward,
4645 &aCurrentTabIndex, &aIgnoreTabIndex, aForDocumentNavigation,
4646 aNavigateByKey, aReachedToEndForDocumentNavigation);
4647 if (contentToFocus) {
4648 if (auto rv =
4649 MaybeDelegateToRemoteFrame(contentToFocus, aNavigateByKey,
4650 aForward, aForDocumentNavigation)) {
4651 return *rv;
4652 }
4653 NS_ADDREF(*aResultContent = contentToFocus)(*aResultContent = contentToFocus)->AddRef();
4654 return NS_OK;
4655 }
4656 }
4657
4658 // If we reach here, it means no next tabbable content in shadow DOM or
4659 // popover. We need to continue searching in light DOM, starting at the top
4660 // level shadow host in light DOM (updated startContent) and its tabindex
4661 // (updated aCurrentTabIndex).
4662 MOZ_ASSERT(!FindScopeOwner(startContent),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!FindScopeOwner(startContent))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!FindScopeOwner(startContent
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!FindScopeOwner(startContent)" " (" "startContent should not be owned by Shadow DOM at this point"
")", "./../../../dom/base/nsFocusManager.cpp", 4663); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!FindScopeOwner(startContent)" ") (" "startContent should not be owned by Shadow DOM at this point"
")"); do { MOZ_CrashSequence(__null, 4663); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4663 "startContent should not be owned by Shadow DOM at this point")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!FindScopeOwner(startContent))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!FindScopeOwner(startContent
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!FindScopeOwner(startContent)" " (" "startContent should not be owned by Shadow DOM at this point"
")", "./../../../dom/base/nsFocusManager.cpp", 4663); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!FindScopeOwner(startContent)" ") (" "startContent should not be owned by Shadow DOM at this point"
")"); do { MOZ_CrashSequence(__null, 4663); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4664
4665 nsPresContext* presContext = aPresShell->GetPresContext();
4666
4667 bool getNextFrame = true;
4668 nsCOMPtr<nsIContent> iterStartContent = startContent;
4669 nsIContent* topLevelScopeStartContent = startContent;
4670 // Iterate tab index to find corresponding contents
4671 while (true) {
4672 nsIFrame* frame = iterStartContent->GetPrimaryFrame();
4673 // if there is no frame, look for another content node that has a frame
4674 while (!frame) {
4675 // if the root content doesn't have a frame, just return
4676 if (iterStartContent == aRootContent) {
4677 return NS_OK;
4678 }
4679
4680 // look for the next or previous content node in tree order
4681 iterStartContent = aForward ? iterStartContent->GetNextNode()
4682 : iterStartContent->GetPrevNode();
4683 if (!iterStartContent) {
4684 break;
4685 }
4686
4687 frame = iterStartContent->GetPrimaryFrame();
4688 // Host without frame, enter its scope.
4689 if (!frame && IsScopeOwner(iterStartContent)) {
4690 int32_t tabIndex = ScopeOwnerTabIndexValue(iterStartContent);
4691 if (tabIndex >= 0 &&
4692 (aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
4693 nsIContent* contentToFocus = GetNextTabbableContentInScope(
4694 iterStartContent, iterStartContent, aOriginalStartContent,
4695 aForward, aForward ? 1 : 0, aIgnoreTabIndex,
4696 aForDocumentNavigation, aNavigateByKey, true /* aSkipOwner */,
4697 aReachedToEndForDocumentNavigation);
4698 if (contentToFocus) {
4699 if (auto rv = MaybeDelegateToRemoteFrame(contentToFocus,
4700 aNavigateByKey, aForward,
4701 aForDocumentNavigation)) {
4702 return *rv;
4703 }
4704 NS_ADDREF(*aResultContent = contentToFocus)(*aResultContent = contentToFocus)->AddRef();
4705 return NS_OK;
4706 }
4707 }
4708 }
4709 // we've already skipped over the initial focused content, so we
4710 // don't want to traverse frames.
4711 getNextFrame = false;
4712 }
4713
4714 Maybe<nsFrameIterator> frameIterator;
4715 if (frame) {
4716 // For tab navigation, pass false for aSkipPopupChecks so that we don't
4717 // iterate into or out of a popup. For document naviation pass true to
4718 // ignore these boundaries.
4719 frameIterator.emplace(presContext, frame, nsFrameIterator::Type::PreOrder,
4720 false, // aVisual
4721 false, // aLockInScrollView
4722 true, // aFollowOOFs
4723 aForDocumentNavigation // aSkipPopupChecks
4724 );
4725 MOZ_ASSERT(frameIterator)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameIterator)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frameIterator))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("frameIterator",
"./../../../dom/base/nsFocusManager.cpp", 4725); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameIterator" ")"); do { MOZ_CrashSequence
(__null, 4725); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4726
4727 if (iterStartContent == aRootContent) {
4728 if (!aForward) {
4729 frameIterator->Last();
4730 } else if (aRootContent->IsFocusableWithoutStyle()) {
4731 frameIterator->Next();
4732 }
4733 frame = frameIterator->CurrentItem();
4734 } else if (getNextFrame &&
4735 (!iterStartContent ||
4736 !iterStartContent->IsHTMLElement(nsGkAtoms::area))) {
4737 // Need to do special check in case we're in an imagemap which has
4738 // multiple content nodes per frame, so don't skip over the starting
4739 // frame.
4740 frame = frameIterator->Traverse(aForward);
4741 }
4742 }
4743
4744 nsIContent* oldTopLevelScopeOwner = nullptr;
4745 // Walk frames to find something tabbable matching aCurrentTabIndex
4746 while (frame) {
4747 // Try to find the topmost scope owner, since we want to skip the node
4748 // that is not owned by document in frame traversal.
4749 const nsCOMPtr<nsIContent> currentContent = frame->GetContent();
4750 if (currentTopLevelScopeOwner) {
4751 oldTopLevelScopeOwner = currentTopLevelScopeOwner;
4752 }
4753 currentTopLevelScopeOwner = GetTopLevelScopeOwner(currentContent);
4754
4755 // We handle popover case separately.
4756 if (currentTopLevelScopeOwner &&
4757 currentTopLevelScopeOwner == oldTopLevelScopeOwner) {
4758 // We're within non-document scope, continue.
4759 do {
4760 if (aForward) {
4761 frameIterator->Next();
4762 } else {
4763 frameIterator->Prev();
4764 }
4765 frame = frameIterator->CurrentItem();
4766 // For the usage of GetPrevContinuation, see the comment
4767 // at the end of while (frame) loop.
4768 } while (frame && frame->GetPrevContinuation());
4769 continue;
4770 }
4771
4772 // For document navigation, check if this element is an open panel. Since
4773 // panels aren't focusable (tabIndex would be -1), we'll just assume that
4774 // for document navigation, the tabIndex is 0.
4775 if (aForDocumentNavigation && currentContent && (aCurrentTabIndex == 0) &&
4776 currentContent->IsXULElement(nsGkAtoms::panel)) {
4777 nsMenuPopupFrame* popupFrame = do_QueryFrame(frame);
4778 // Check if the panel is open. Closed panels are ignored since you can't
4779 // focus anything in them.
4780 if (popupFrame && popupFrame->IsOpen()) {
4781 // When moving backward, skip the popup we started in otherwise it
4782 // will be selected again.
4783 bool validPopup = true;
4784 if (!aForward) {
4785 nsIContent* content = topLevelScopeStartContent;
4786 while (content) {
4787 if (content == currentContent) {
4788 validPopup = false;
4789 break;
4790 }
4791
4792 content = content->GetParent();
4793 }
4794 }
4795
4796 if (validPopup) {
4797 // Since a panel isn't focusable itself, find the first focusable
4798 // content within the popup. If there isn't any focusable content
4799 // in the popup, skip this popup and continue iterating through the
4800 // frames. We pass the panel itself (currentContent) as the starting
4801 // and root content, so that we only find content within the panel.
4802 // Note also that we pass false for aForDocumentNavigation since we
4803 // want to locate the first content, not the first document.
4804 nsresult rv = GetNextTabbableContent(
4805 aPresShell, currentContent, nullptr, currentContent, true, 1,
4806 false, false, aNavigateByKey,
4807 aReachedToEndForDocumentNavigation, aResultContent);
4808 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1))) && *aResultContent) {
4809 return rv;
4810 }
4811 }
4812 }
4813 }
4814
4815 // As of now, 2018/04/12, sequential focus navigation is still
4816 // in the obsolete Shadow DOM specification.
4817 // http://w3c.github.io/webcomponents/spec/shadow/#sequential-focus-navigation
4818 // "if ELEMENT is focusable, a shadow host, or a slot element,
4819 // append ELEMENT to NAVIGATION-ORDER."
4820 // and later in "For each element ELEMENT in NAVIGATION-ORDER: "
4821 // hosts and slots are handled before other elements.
4822 if (currentTopLevelScopeOwner) {
4823 bool focusableHostSlot;
4824 int32_t tabIndex = ScopeOwnerTabIndexValue(currentTopLevelScopeOwner,
4825 &focusableHostSlot);
4826 // Host or slot itself isn't focusable or going backwards, enter its
4827 // scope.
4828 if ((!aForward || !focusableHostSlot) && tabIndex >= 0 &&
4829 (aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
4830 nsIContent* contentToFocus = GetNextTabbableContentInScope(
4831 currentTopLevelScopeOwner, currentTopLevelScopeOwner,
4832 aOriginalStartContent, aForward, aForward ? 1 : 0,
4833 aIgnoreTabIndex, aForDocumentNavigation, aNavigateByKey,
4834 true /* aSkipOwner */, aReachedToEndForDocumentNavigation);
4835 if (contentToFocus) {
4836 if (auto rv = MaybeDelegateToRemoteFrame(contentToFocus,
4837 aNavigateByKey, aForward,
4838 aForDocumentNavigation)) {
4839 return *rv;
4840 }
4841 NS_ADDREF(*aResultContent = contentToFocus)(*aResultContent = contentToFocus)->AddRef();
4842 return NS_OK;
4843 }
4844 // If we've wrapped around already, then carry on.
4845 if (aOriginalStartContent &&
4846 currentTopLevelScopeOwner ==
4847 GetTopLevelScopeOwner(aOriginalStartContent)) {
4848 // FIXME: Shouldn't this return null instead? aOriginalStartContent
4849 // isn't focusable after all.
4850 NS_ADDREF(*aResultContent = aOriginalStartContent)(*aResultContent = aOriginalStartContent)->AddRef();
4851 return NS_OK;
4852 }
4853 }
4854 // There is no next tabbable content in currentTopLevelScopeOwner's
4855 // scope. We should continue the loop in order to skip all contents that
4856 // is in currentTopLevelScopeOwner's scope.
4857 continue;
4858 }
4859
4860 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetTopLevelScopeOwner(currentContent))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!GetTopLevelScopeOwner(currentContent)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!GetTopLevelScopeOwner(currentContent)"
" (" "currentContent should be in top-level-scope at this point unless "
"for popover case" ")", "./../../../dom/base/nsFocusManager.cpp"
, 4863); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetTopLevelScopeOwner(currentContent)"
") (" "currentContent should be in top-level-scope at this point unless "
"for popover case" ")"); do { MOZ_CrashSequence(__null, 4863
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
4861 !GetTopLevelScopeOwner(currentContent),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetTopLevelScopeOwner(currentContent))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!GetTopLevelScopeOwner(currentContent)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!GetTopLevelScopeOwner(currentContent)"
" (" "currentContent should be in top-level-scope at this point unless "
"for popover case" ")", "./../../../dom/base/nsFocusManager.cpp"
, 4863); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetTopLevelScopeOwner(currentContent)"
") (" "currentContent should be in top-level-scope at this point unless "
"for popover case" ")"); do { MOZ_CrashSequence(__null, 4863
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
4862 "currentContent should be in top-level-scope at this point unless "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetTopLevelScopeOwner(currentContent))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!GetTopLevelScopeOwner(currentContent)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!GetTopLevelScopeOwner(currentContent)"
" (" "currentContent should be in top-level-scope at this point unless "
"for popover case" ")", "./../../../dom/base/nsFocusManager.cpp"
, 4863); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetTopLevelScopeOwner(currentContent)"
") (" "currentContent should be in top-level-scope at this point unless "
"for popover case" ")"); do { MOZ_CrashSequence(__null, 4863
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
4863 "for popover case")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetTopLevelScopeOwner(currentContent))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!GetTopLevelScopeOwner(currentContent)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!GetTopLevelScopeOwner(currentContent)"
" (" "currentContent should be in top-level-scope at this point unless "
"for popover case" ")", "./../../../dom/base/nsFocusManager.cpp"
, 4863); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetTopLevelScopeOwner(currentContent)"
") (" "currentContent should be in top-level-scope at this point unless "
"for popover case" ")"); do { MOZ_CrashSequence(__null, 4863
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
4864
4865 // TabIndex not set defaults to 0 for form elements, anchors and other
4866 // elements that are normally focusable. Tabindex defaults to -1
4867 // for elements that are not normally focusable.
4868 // The returned computed tabindex from IsFocusable() is as follows:
4869 // clang-format off
4870 // < 0 not tabbable at all
4871 // == 0 in normal tab order (last after positive tabindexed items)
4872 // > 0 can be tabbed to in the order specified by this value
4873 // clang-format on
4874 int32_t tabIndex = frame->IsFocusable().mTabIndex;
4875
4876 LOGCONTENTNAVIGATION("Next Tabbable %s:", frame->GetContent())if ((__builtin_expect(!!(mozilla::detail::log_test(gFocusNavigationLog
, LogLevel::Debug)), 0))) { nsAutoCString tag("(none)"_ns); if
(frame->GetContent()) { frame->GetContent()->NodeInfo
()->NameAtom()->ToUTF8String(tag); } do { const ::mozilla
::LogModule* moz_real_module = gFocusNavigationLog; if ((__builtin_expect
(!!(mozilla::detail::log_test(moz_real_module, LogLevel::Debug
)), 0))) { mozilla::detail::log_print(moz_real_module, LogLevel
::Debug, "Next Tabbable %s:", tag.get()); } } while (0); }
;
4877 LOGFOCUSNAVIGATION(do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " with tabindex: %d expected: %d"
, tabIndex, aCurrentTabIndex); } } while (0)
4878 (" with tabindex: %d expected: %d", tabIndex, aCurrentTabIndex))do { const ::mozilla::LogModule* moz_real_module = gFocusNavigationLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " with tabindex: %d expected: %d"
, tabIndex, aCurrentTabIndex); } } while (0)
;
4879
4880 if (tabIndex >= 0) {
4881 NS_ASSERTION(currentContent,do { if (!(currentContent)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "IsFocusable set a tabindex for a frame with no content", "currentContent"
, "./../../../dom/base/nsFocusManager.cpp", 4882); MOZ_PretendNoReturn
(); } } while (0)
4882 "IsFocusable set a tabindex for a frame with no content")do { if (!(currentContent)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "IsFocusable set a tabindex for a frame with no content", "currentContent"
, "./../../../dom/base/nsFocusManager.cpp", 4882); MOZ_PretendNoReturn
(); } } while (0)
;
4883 if (!aForDocumentNavigation &&
4884 currentContent->IsHTMLElement(nsGkAtoms::img) &&
4885 currentContent->AsElement()->HasAttr(nsGkAtoms::usemap)) {
4886 // This is an image with a map. Image map areas are not traversed by
4887 // nsFrameIterator so look for the next or previous area element.
4888 nsIContent* areaContent = GetNextTabbableMapArea(
4889 aForward, aCurrentTabIndex, currentContent->AsElement(),
4890 iterStartContent);
4891 if (areaContent) {
4892 NS_ADDREF(*aResultContent = areaContent)(*aResultContent = areaContent)->AddRef();
4893 return NS_OK;
4894 }
4895 } else if (aIgnoreTabIndex || aCurrentTabIndex == tabIndex) {
4896 // break out if we've wrapped around to the start again.
4897 if (aOriginalStartContent &&
4898 currentContent == aOriginalStartContent) {
4899 NS_ADDREF(*aResultContent = currentContent)(*aResultContent = currentContent)->AddRef();
4900 return NS_OK;
4901 }
4902
4903 if (auto rv = MaybeDelegateToRemoteFrame(currentContent,
4904 aNavigateByKey, aForward,
4905 aForDocumentNavigation)) {
4906 return *rv;
4907 }
4908
4909 // Next, for document navigation, check if this a non-remote child
4910 // document.
4911 bool checkSubDocument = true;
4912 if (aForDocumentNavigation &&
4913 TryDocumentNavigation(currentContent, &checkSubDocument,
4914 aResultContent)) {
4915 return NS_OK;
4916 }
4917
4918 if (checkSubDocument) {
4919 // found a node with a matching tab index. Check if it is a child
4920 // frame. If so, navigate into the child frame instead.
4921 if (TryToMoveFocusToSubDocument(
4922 currentContent, aOriginalStartContent, aForward,
4923 aForDocumentNavigation, aNavigateByKey,
4924 aReachedToEndForDocumentNavigation, aResultContent)) {
4925 MOZ_ASSERT(*aResultContent)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(*aResultContent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(*aResultContent))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("*aResultContent"
, "./../../../dom/base/nsFocusManager.cpp", 4925); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "*aResultContent" ")"); do { MOZ_CrashSequence
(__null, 4925); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4926 return NS_OK;
4927 }
4928 // otherwise, use this as the next content node to tab to, unless
4929 // this was the element we started on. This would happen for
4930 // instance on an element with child frames, where frame navigation
4931 // could return the original element again. In that case, just skip
4932 // it. Also, if the next content node is the root content, then
4933 // return it. This latter case would happen only if someone made a
4934 // popup focusable.
4935 else if (currentContent == aRootContent ||
4936 currentContent != startContent) {
4937 NS_ADDREF(*aResultContent = currentContent)(*aResultContent = currentContent)->AddRef();
4938 return NS_OK;
4939 }
4940 } else if (currentContent && aReachedToEndForDocumentNavigation &&
4941 nsContentUtils::IsChromeDoc(
4942 currentContent->GetComposedDoc())) {
4943 // aReachedToEndForDocumentNavigation is true means
4944 // 1. This is a document navigation (i.e, VK_F6, Control + Tab)
4945 // 2. This is the top-level document (Note that we may start from
4946 // a subdocument)
4947 // 3. We've searched through the this top-level document already
4948 if (!GetRootForChildDocument(currentContent)) {
4949 // We'd like to focus the first focusable element of this
4950 // top-level chrome document.
4951 if (currentContent == aRootContent ||
4952 currentContent != startContent) {
4953 NS_ADDREF(*aResultContent = currentContent)(*aResultContent = currentContent)->AddRef();
4954 return NS_OK;
4955 }
4956 }
4957 }
4958 }
4959 } else if (aOriginalStartContent &&
4960 currentContent == aOriginalStartContent) {
4961 // not focusable, so return if we have wrapped around to the original
4962 // content. This is necessary in case the original starting content was
4963 // not focusable.
4964 //
4965 // FIXME: Shouldn't this return null instead? currentContent isn't
4966 // focusable after all.
4967 NS_ADDREF(*aResultContent = currentContent)(*aResultContent = currentContent)->AddRef();
4968 return NS_OK;
4969 }
4970
4971 // Move to the next or previous frame, but ignore continuation frames
4972 // since only the first frame should be involved in focusability.
4973 // Otherwise, a loop will occur in the following example:
4974 // <span tabindex="1">...<a/><a/>...</span>
4975 // where the text wraps onto multiple lines. Tabbing from the second
4976 // link can find one of the span's continuation frames between the link
4977 // and the end of the span, and the span would end up getting focused
4978 // again.
4979 do {
4980 if (aForward) {
4981 frameIterator->Next();
4982 } else {
4983 frameIterator->Prev();
4984 }
4985 frame = frameIterator->CurrentItem();
4986 } while (frame && frame->GetPrevContinuation());
4987 }
4988
4989 // If already at lowest priority tab (0), end search completely.
4990 // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
4991 if (aCurrentTabIndex == (aForward ? 0 : 1)) {
4992 break;
4993 }
4994
4995 // continue looking for next highest priority tabindex
4996 aCurrentTabIndex =
4997 GetNextTabIndex(aRootContent, aCurrentTabIndex, aForward);
4998 startContent = iterStartContent = aRootContent;
4999 currentTopLevelScopeOwner = GetTopLevelScopeOwner(startContent);
5000 }
5001
5002 return NS_OK;
5003}
5004
5005bool nsFocusManager::TryDocumentNavigation(nsIContent* aCurrentContent,
5006 bool* aCheckSubDocument,
5007 nsIContent** aResultContent) {
5008 *aCheckSubDocument = true;
5009 if (RefPtr<Element> rootElementForChildDocument =
5010 GetRootForChildDocument(aCurrentContent)) {
5011 // If GetRootForChildDocument returned something then call
5012 // FocusFirst to find the root or first element to focus within
5013 // the child document. If this is a frameset though, skip this and
5014 // fall through to normal tab navigation to iterate into
5015 // the frameset's frames and locate the first focusable frame.
5016 if (!rootElementForChildDocument->IsHTMLElement(nsGkAtoms::frameset)) {
5017 *aCheckSubDocument = false;
5018 (void)FocusFirst(rootElementForChildDocument, aResultContent,
5019 false /* aReachedToEndForDocumentNavigation */);
5020 return *aResultContent != nullptr;
5021 }
5022 } else {
5023 // Set aCheckSubDocument to false, as this was neither a frame
5024 // type element or a child document that was focusable.
5025 *aCheckSubDocument = false;
5026 }
5027
5028 return false;
5029}
5030
5031bool nsFocusManager::TryToMoveFocusToSubDocument(
5032 nsIContent* aCurrentContent, nsIContent* aOriginalStartContent,
5033 bool aForward, bool aForDocumentNavigation, bool aNavigateByKey,
5034 bool aReachedToEndForDocumentNavigation, nsIContent** aResultContent) {
5035 Document* doc = aCurrentContent->GetComposedDoc();
5036 NS_ASSERTION(doc, "content not in document")do { if (!(doc)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "content not in document"
, "doc", "./../../../dom/base/nsFocusManager.cpp", 5036); MOZ_PretendNoReturn
(); } } while (0)
;
5037 Document* subdoc = doc->GetSubDocumentFor(aCurrentContent);
5038 if (subdoc && !subdoc->EventHandlingSuppressed()) {
5039 if (RefPtr<Element> rootElement = subdoc->GetRootElement()) {
5040 if (RefPtr<PresShell> subPresShell = subdoc->GetPresShell()) {
5041 nsresult rv = GetNextTabbableContent(
5042 subPresShell, rootElement, aOriginalStartContent, rootElement,
5043 aForward, (aForward ? 1 : 0), false, aForDocumentNavigation,
5044 aNavigateByKey, aReachedToEndForDocumentNavigation, aResultContent);
5045 NS_ENSURE_SUCCESS(rv, false)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "false", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../dom/base/nsFocusManager.cpp"
, 5045); return false; } } while (false)
;
5046 if (*aResultContent) {
5047 return true;
5048 }
5049 if (rootElement->IsEditable()) {
5050 // Only move to the root element with a valid reason
5051 *aResultContent = rootElement;
5052 NS_ADDREF(*aResultContent)(*aResultContent)->AddRef();
5053 return true;
5054 }
5055 }
5056 }
5057 }
5058 return false;
5059}
5060
5061nsIContent* nsFocusManager::GetNextTabbableMapArea(bool aForward,
5062 int32_t aCurrentTabIndex,
5063 Element* aImageContent,
5064 nsIContent* aStartContent) {
5065 if (aImageContent->IsInComposedDoc()) {
5066 HTMLImageElement* imgElement = HTMLImageElement::FromNode(aImageContent);
5067 // The caller should check the element type, so we can assert here.
5068 MOZ_ASSERT(imgElement)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(imgElement)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(imgElement))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("imgElement", "./../../../dom/base/nsFocusManager.cpp"
, 5068); AnnotateMozCrashReason("MOZ_ASSERT" "(" "imgElement"
")"); do { MOZ_CrashSequence(__null, 5068); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5069
5070 nsCOMPtr<nsIContent> mapContent = imgElement->FindImageMap();
5071 if (!mapContent) {
5072 return nullptr;
5073 }
5074 // First see if the the start content is in this map
5075 Maybe<uint32_t> indexOfStartContent =
5076 mapContent->ComputeIndexOf(aStartContent);
5077 nsIContent* scanStartContent;
5078 Focusable focusable;
5079 if (indexOfStartContent.isNothing() ||
5080 ((focusable = aStartContent->IsFocusableWithoutStyle()) &&
5081 focusable.mTabIndex != aCurrentTabIndex)) {
5082 // If aStartContent is in this map we must start iterating past it.
5083 // We skip the case where aStartContent has tabindex == aStartContent
5084 // since the next tab ordered element might be before it
5085 // (or after for backwards) in the child list.
5086 scanStartContent =
5087 aForward ? mapContent->GetFirstChild() : mapContent->GetLastChild();
5088 } else {
5089 scanStartContent = aForward ? aStartContent->GetNextSibling()
5090 : aStartContent->GetPreviousSibling();
5091 }
5092
5093 for (nsCOMPtr<nsIContent> areaContent = scanStartContent; areaContent;
5094 areaContent = aForward ? areaContent->GetNextSibling()
5095 : areaContent->GetPreviousSibling()) {
5096 focusable = areaContent->IsFocusableWithoutStyle();
5097 if (focusable && focusable.mTabIndex == aCurrentTabIndex) {
5098 return areaContent;
5099 }
5100 }
5101 }
5102
5103 return nullptr;
5104}
5105
5106int32_t nsFocusManager::GetNextTabIndex(nsIContent* aParent,
5107 int32_t aCurrentTabIndex,
5108 bool aForward) {
5109 int32_t tabIndex, childTabIndex;
5110 StyleChildrenIterator iter(aParent);
5111
5112 if (aForward) {
5113 tabIndex = 0;
5114 for (nsIContent* child = iter.GetNextChild(); child;
5115 child = iter.GetNextChild()) {
5116 // Skip child's descendants if child is a shadow host or slot, as they are
5117 // in the focus navigation scope owned by child's shadow root
5118 if (!IsHostOrSlot(child)) {
5119 childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
5120 if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
5121 tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex
5122 : tabIndex;
5123 }
5124 }
5125
5126 nsAutoString tabIndexStr;
5127 if (child->IsElement()) {
5128 child->AsElement()->GetAttr(nsGkAtoms::tabindex, tabIndexStr);
5129 }
5130 nsresult ec;
5131 int32_t val = tabIndexStr.ToInteger(&ec);
5132 if (NS_SUCCEEDED(ec)((bool)(__builtin_expect(!!(!NS_FAILED_impl(ec)), 1))) && val > aCurrentTabIndex && val != tabIndex) {
5133 tabIndex = (tabIndex == 0 || val < tabIndex) ? val : tabIndex;
5134 }
5135 }
5136 } else { /* !aForward */
5137 tabIndex = 1;
5138 for (nsIContent* child = iter.GetNextChild(); child;
5139 child = iter.GetNextChild()) {
5140 // Skip child's descendants if child is a shadow host or slot, as they are
5141 // in the focus navigation scope owned by child's shadow root
5142 if (!IsHostOrSlot(child)) {
5143 childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
5144 if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
5145 (childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
5146 tabIndex = childTabIndex;
5147 }
5148 }
5149
5150 nsAutoString tabIndexStr;
5151 if (child->IsElement()) {
5152 child->AsElement()->GetAttr(nsGkAtoms::tabindex, tabIndexStr);
5153 }
5154 nsresult ec;
5155 int32_t val = tabIndexStr.ToInteger(&ec);
5156 if (NS_SUCCEEDED(ec)((bool)(__builtin_expect(!!(!NS_FAILED_impl(ec)), 1)))) {
5157 if ((aCurrentTabIndex == 0 && val > tabIndex) ||
5158 (val < aCurrentTabIndex && val > tabIndex)) {
5159 tabIndex = val;
5160 }
5161 }
5162 }
5163 }
5164
5165 return tabIndex;
5166}
5167
5168nsresult nsFocusManager::FocusFirst(Element* aRootElement,
5169 nsIContent** aNextContent,
5170 bool aReachedToEndForDocumentNavigation) {
5171 if (!aRootElement) {
5172 return NS_OK;
5173 }
5174
5175 Document* doc = aRootElement->GetComposedDoc();
5176 if (doc) {
5177 if (nsContentUtils::IsChromeDoc(doc)) {
5178 // If the redirectdocumentfocus attribute is set, redirect the focus to a
5179 // specific element. This is primarily used to retarget the focus to the
5180 // urlbar during document navigation.
5181 nsAutoString retarget;
5182
5183 if (aRootElement->GetAttr(nsGkAtoms::retargetdocumentfocus, retarget)) {
5184 RefPtr<Element> element = doc->GetElementById(retarget);
5185 nsCOMPtr<nsIContent> retargetElement =
5186 FlushAndCheckIfFocusable(element, 0);
5187 if (retargetElement) {
5188 retargetElement.forget(aNextContent);
5189 return NS_OK;
5190 }
5191 }
5192 }
5193
5194 nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
5195 if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
5196 // If the found content is in a chrome shell, navigate forward one
5197 // tabbable item so that the first item is focused. Note that we
5198 // always go forward and not back here.
5199 if (RefPtr<PresShell> presShell = doc->GetPresShell()) {
5200 return GetNextTabbableContent(
5201 presShell, aRootElement, nullptr, aRootElement, true, 1, false,
5202 aReachedToEndForDocumentNavigation, true,
5203 aReachedToEndForDocumentNavigation, aNextContent);
5204 }
5205 }
5206 }
5207
5208 NS_ADDREF(*aNextContent = aRootElement)(*aNextContent = aRootElement)->AddRef();
5209 return NS_OK;
5210}
5211
5212Element* nsFocusManager::GetRootForFocus(nsPIDOMWindowOuter* aWindow,
5213 Document* aDocument,
5214 bool aForDocumentNavigation,
5215 bool aCheckVisibility) {
5216 if (!aForDocumentNavigation) {
5217 nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
5218 if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
5219 return nullptr;
5220 }
5221 }
5222
5223 if (aCheckVisibility && !IsWindowVisible(aWindow)) return nullptr;
5224
5225 // If the body is contenteditable, use the editor's root element rather than
5226 // the actual root element.
5227 RefPtr<Element> rootElement =
5228 nsLayoutUtils::GetEditableRootContentByContentEditable(aDocument);
5229 if (!rootElement || !rootElement->GetPrimaryFrame()) {
5230 rootElement = aDocument->GetRootElement();
5231 if (!rootElement) {
5232 return nullptr;
5233 }
5234 }
5235
5236 if (aCheckVisibility && !rootElement->GetPrimaryFrame()) {
5237 return nullptr;
5238 }
5239
5240 // Finally, check if this is a frameset
5241 if (aDocument && aDocument->IsHTMLOrXHTML()) {
5242 Element* htmlChild = aDocument->GetHtmlChildElement(nsGkAtoms::frameset);
5243 if (htmlChild) {
5244 // In document navigation mode, return the frameset so that navigation
5245 // descends into the child frames.
5246 return aForDocumentNavigation ? htmlChild : nullptr;
5247 }
5248 }
5249
5250 return rootElement;
5251}
5252
5253Element* nsFocusManager::GetRootForChildDocument(nsIContent* aContent) {
5254 // Check for elements that represent child documents, that is, browsers,
5255 // editors or frames from a frameset. We don't include iframes since we
5256 // consider them to be an integral part of the same window or page.
5257 if (!aContent || !(aContent->IsXULElement(nsGkAtoms::browser) ||
5258 aContent->IsXULElement(nsGkAtoms::editor) ||
5259 aContent->IsHTMLElement(nsGkAtoms::frame))) {
5260 return nullptr;
5261 }
5262
5263 Document* doc = aContent->GetComposedDoc();
5264 if (!doc) {
5265 return nullptr;
5266 }
5267
5268 Document* subdoc = doc->GetSubDocumentFor(aContent);
5269 if (!subdoc || subdoc->EventHandlingSuppressed()) {
5270 return nullptr;
5271 }
5272
5273 nsCOMPtr<nsPIDOMWindowOuter> window = subdoc->GetWindow();
5274 return GetRootForFocus(window, subdoc, true, true);
5275}
5276
5277static bool IsLink(nsIContent* aContent) {
5278 return aContent->IsElement() && aContent->AsElement()->IsLink();
5279}
5280
5281void nsFocusManager::GetFocusInSelection(nsPIDOMWindowOuter* aWindow,
5282 nsIContent* aStartSelection,
5283 nsIContent* aEndSelection,
5284 nsIContent** aFocusedContent) {
5285 *aFocusedContent = nullptr;
5286
5287 nsCOMPtr<nsIContent> testContent = aStartSelection;
5288 nsCOMPtr<nsIContent> nextTestContent = aEndSelection;
5289
5290 nsCOMPtr<nsIContent> currentFocus = aWindow->GetFocusedElement();
5291
5292 // We now have the correct start node in selectionContent!
5293 // Search for focusable elements, starting with selectionContent
5294
5295 // Method #1: Keep going up while we look - an ancestor might be focusable
5296 // We could end the loop earlier, such as when we're no longer
5297 // in the same frame, by comparing selectionContent->GetPrimaryFrame()
5298 // with a variable holding the starting selectionContent
5299 while (testContent) {
5300 // Keep testing while selectionContent is equal to something,
5301 // eventually we'll run out of ancestors
5302
5303 if (testContent == currentFocus || IsLink(testContent)) {
5304 testContent.forget(aFocusedContent);
5305 return;
5306 }
5307
5308 // Get the parent
5309 testContent = testContent->GetParent();
5310
5311 if (!testContent) {
5312 // We run this loop again, checking the ancestor chain of the selection's
5313 // end point
5314 testContent = nextTestContent;
5315 nextTestContent = nullptr;
5316 }
5317 }
5318
5319 // We couldn't find an anchor that was an ancestor of the selection start
5320 // Method #2: look for anchor in selection's primary range (depth first
5321 // search)
5322
5323 nsCOMPtr<nsIContent> selectionNode = aStartSelection;
5324 nsCOMPtr<nsIContent> endSelectionNode = aEndSelection;
5325 nsCOMPtr<nsIContent> testNode;
5326
5327 do {
5328 testContent = selectionNode;
5329
5330 // We're looking for any focusable link that could be part of the
5331 // main document's selection.
5332 if (testContent == currentFocus || IsLink(testContent)) {
5333 testContent.forget(aFocusedContent);
5334 return;
5335 }
5336
5337 nsIContent* testNode = selectionNode->GetFirstChild();
5338 if (testNode) {
5339 selectionNode = testNode;
5340 continue;
5341 }
5342
5343 if (selectionNode == endSelectionNode) {
5344 break;
5345 }
5346 testNode = selectionNode->GetNextSibling();
5347 if (testNode) {
5348 selectionNode = testNode;
5349 continue;
5350 }
5351
5352 do {
5353 // GetParent is OK here, instead of GetParentNode, because the only case
5354 // where the latter returns something different from the former is when
5355 // GetParentNode is the document. But in that case we would simply get
5356 // null for selectionNode when setting it to testNode->GetNextSibling()
5357 // (because a document has no next sibling). And then the next iteration
5358 // of this loop would get null for GetParentNode anyway, and break out of
5359 // all the loops.
5360 testNode = selectionNode->GetParent();
5361 if (!testNode || testNode == endSelectionNode) {
5362 selectionNode = nullptr;
5363 break;
5364 }
5365 selectionNode = testNode->GetNextSibling();
5366 if (selectionNode) {
5367 break;
5368 }
5369 selectionNode = testNode;
5370 } while (true);
5371 } while (selectionNode && selectionNode != endSelectionNode);
5372}
5373
5374static void MaybeUnlockPointer(BrowsingContext* aCurrentFocusedContext) {
5375 if (!PointerLockManager::IsInLockContext(aCurrentFocusedContext)) {
5376 PointerLockManager::Unlock("FocusChange");
5377 }
5378}
5379
5380class PointerUnlocker : public Runnable {
5381 public:
5382 PointerUnlocker() : mozilla::Runnable("PointerUnlocker") {
5383 MOZ_ASSERT(XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5383); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5383); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5384 MOZ_ASSERT(!PointerUnlocker::sActiveUnlocker)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!PointerUnlocker::sActiveUnlocker)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!PointerUnlocker::sActiveUnlocker
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!PointerUnlocker::sActiveUnlocker", "./../../../dom/base/nsFocusManager.cpp"
, 5384); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!PointerUnlocker::sActiveUnlocker"
")"); do { MOZ_CrashSequence(__null, 5384); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5385 PointerUnlocker::sActiveUnlocker = this;
5386 }
5387
5388 ~PointerUnlocker() {
5389 if (PointerUnlocker::sActiveUnlocker == this) {
5390 PointerUnlocker::sActiveUnlocker = nullptr;
5391 }
5392 }
5393
5394 NS_IMETHODvirtual nsresult Run() override {
5395 if (PointerUnlocker::sActiveUnlocker == this) {
5396 PointerUnlocker::sActiveUnlocker = nullptr;
5397 }
5398 NS_ENSURE_STATE(nsFocusManager::GetFocusManager())do { if ((__builtin_expect(!!(!(nsFocusManager::GetFocusManager
())), 0))) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE("
"nsFocusManager::GetFocusManager()" ") failed", nullptr, "./../../../dom/base/nsFocusManager.cpp"
, 5398); return NS_ERROR_UNEXPECTED; } } while (false)
;
5399 nsPIDOMWindowOuter* focused =
5400 nsFocusManager::GetFocusManager()->GetFocusedWindow();
5401 MaybeUnlockPointer(focused ? focused->GetBrowsingContext() : nullptr);
5402 return NS_OK;
5403 }
5404
5405 static PointerUnlocker* sActiveUnlocker;
5406};
5407
5408PointerUnlocker* PointerUnlocker::sActiveUnlocker = nullptr;
5409
5410void nsFocusManager::SetFocusedBrowsingContext(BrowsingContext* aContext,
5411 uint64_t aActionId) {
5412 if (XRE_IsParentProcess()) {
5413 return;
5414 }
5415 MOZ_ASSERT(!ActionIdComparableAndLower(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInContent
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInContent
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 5416); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInContent)"
")"); do { MOZ_CrashSequence(__null, 5416); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5416 aActionId, mActionIdForFocusedBrowsingContextInContent))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInContent
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInContent
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInContent)"
, "./../../../dom/base/nsFocusManager.cpp", 5416); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInContent)"
")"); do { MOZ_CrashSequence(__null, 5416); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5417 mFocusedBrowsingContextInContent = aContext;
5418 mActionIdForFocusedBrowsingContextInContent = aActionId;
5419 if (aContext) {
5420 // We don't send the unset but instead expect the set from
5421 // elsewhere to take care of it. XXX Is that bad?
5422 MOZ_ASSERT(aContext->IsInProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContext->IsInProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContext->IsInProcess()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aContext->IsInProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5422); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aContext->IsInProcess()" ")"); do { MOZ_CrashSequence
(__null, 5422); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5423 mozilla::dom::ContentChild* contentChild =
5424 mozilla::dom::ContentChild::GetSingleton();
5425 MOZ_ASSERT(contentChild)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(contentChild)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(contentChild))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("contentChild", "./../../../dom/base/nsFocusManager.cpp"
, 5425); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contentChild"
")"); do { MOZ_CrashSequence(__null, 5425); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5426 contentChild->SendSetFocusedBrowsingContext(aContext, aActionId);
5427 }
5428}
5429
5430void nsFocusManager::SetFocusedBrowsingContextFromOtherProcess(
5431 BrowsingContext* aContext, uint64_t aActionId) {
5432 MOZ_ASSERT(!XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!XRE_IsParentProcess()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5432); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5432); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5433 MOZ_ASSERT(aContext)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContext)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContext))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aContext", "./../../../dom/base/nsFocusManager.cpp"
, 5433); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aContext" ")"
); do { MOZ_CrashSequence(__null, 5433); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5434 if (ActionIdComparableAndLower(aActionId,
5435 mActionIdForFocusedBrowsingContextInContent)) {
5436 // Unclear if this ever happens.
5437 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"focused from another process due to stale action id %" "l" "u"
".", aContext, aActionId); } } while (0)
5438 ("Ignored an attempt to set an in-process BrowsingContext [%p] as "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"focused from another process due to stale action id %" "l" "u"
".", aContext, aActionId); } } while (0)
5439 "focused from another process due to stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"focused from another process due to stale action id %" "l" "u"
".", aContext, aActionId); } } while (0)
5440 aContext, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"focused from another process due to stale action id %" "l" "u"
".", aContext, aActionId); } } while (0)
;
5441 return;
5442 }
5443 if (aContext->IsInProcess()) {
5444 // This message has been in transit for long enough that
5445 // the process association of aContext has changed since
5446 // the other content process sent the message, because
5447 // an iframe in that process became an out-of-process
5448 // iframe while the IPC broadcast that we're receiving
5449 // was in-flight. Let's just ignore this.
5450 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"focused from another process, actionid: %" "l" "u" ".", aContext
, aActionId); } } while (0)
5451 ("Ignored an attempt to set an in-process BrowsingContext [%p] as "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"focused from another process, actionid: %" "l" "u" ".", aContext
, aActionId); } } while (0)
5452 "focused from another process, actionid: %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"focused from another process, actionid: %" "l" "u" ".", aContext
, aActionId); } } while (0)
5453 aContext, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"focused from another process, actionid: %" "l" "u" ".", aContext
, aActionId); } } while (0)
;
5454 return;
5455 }
5456 mFocusedBrowsingContextInContent = aContext;
5457 mActionIdForFocusedBrowsingContextInContent = aActionId;
5458 mFocusedElement = nullptr;
5459 mFocusedWindow = nullptr;
5460}
5461
5462bool nsFocusManager::SetFocusedBrowsingContextInChrome(
5463 mozilla::dom::BrowsingContext* aContext, uint64_t aActionId) {
5464 MOZ_ASSERT(aActionId)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aActionId)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aActionId))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aActionId", "./../../../dom/base/nsFocusManager.cpp"
, 5464); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aActionId" ")"
); do { MOZ_CrashSequence(__null, 5464); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5465 if (ProcessPendingFocusedBrowsingContextActionId(aActionId)) {
5466 MOZ_DIAGNOSTIC_ASSERT(!ActionIdComparableAndLower(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInChrome
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInChrome
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInChrome)"
, "./../../../dom/base/nsFocusManager.cpp", 5467); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInChrome)"
")"); do { MOZ_CrashSequence(__null, 5467); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5467 aActionId, mActionIdForFocusedBrowsingContextInChrome))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInChrome
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInChrome
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInChrome)"
, "./../../../dom/base/nsFocusManager.cpp", 5467); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "!ActionIdComparableAndLower( aActionId, mActionIdForFocusedBrowsingContextInChrome)"
")"); do { MOZ_CrashSequence(__null, 5467); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5468 mFocusedBrowsingContextInChrome = aContext;
5469 mActionIdForFocusedBrowsingContextInChrome = aActionId;
5470 return true;
5471 }
5472 return false;
5473}
5474
5475BrowsingContext* nsFocusManager::GetFocusedBrowsingContextInChrome() {
5476 return mFocusedBrowsingContextInChrome;
5477}
5478
5479void nsFocusManager::BrowsingContextDetached(BrowsingContext* aContext) {
5480 if (mFocusedBrowsingContextInChrome == aContext) {
5481 mFocusedBrowsingContextInChrome = nullptr;
5482 // Deliberately not adjusting the corresponding action id, because
5483 // we don't want changes from the past to take effect.
5484 }
5485 if (mActiveBrowsingContextInChrome == aContext) {
5486 mActiveBrowsingContextInChrome = nullptr;
5487 // Deliberately not adjusting the corresponding action id, because
5488 // we don't want changes from the past to take effect.
5489 }
5490}
5491
5492void nsFocusManager::SetActiveBrowsingContextInContent(
5493 mozilla::dom::BrowsingContext* aContext, uint64_t aActionId,
5494 bool aIsEnteringBFCache) {
5495 MOZ_ASSERT(!XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!XRE_IsParentProcess()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5495); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5495); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5496 MOZ_ASSERT(!aContext || aContext->IsInProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aContext || aContext->IsInProcess())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!aContext || aContext->IsInProcess()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!aContext || aContext->IsInProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5496); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!aContext || aContext->IsInProcess()" ")"
); do { MOZ_CrashSequence(__null, 5496); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5497 mozilla::dom::ContentChild* contentChild =
5498 mozilla::dom::ContentChild::GetSingleton();
5499 MOZ_ASSERT(contentChild)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(contentChild)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(contentChild))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("contentChild", "./../../../dom/base/nsFocusManager.cpp"
, 5499); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contentChild"
")"); do { MOZ_CrashSequence(__null, 5499); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5500
5501 if (ActionIdComparableAndLower(aActionId,
5502 mActionIdForActiveBrowsingContextInContent)) {
5503 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"the active browsing context due to a stale action id %" "l"
"u" ".", aContext, aActionId); } } while (0)
5504 ("Ignored an attempt to set an in-process BrowsingContext [%p] as "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"the active browsing context due to a stale action id %" "l"
"u" ".", aContext, aActionId); } } while (0)
5505 "the active browsing context due to a stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"the active browsing context due to a stale action id %" "l"
"u" ".", aContext, aActionId); } } while (0)
5506 aContext, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"the active browsing context due to a stale action id %" "l"
"u" ".", aContext, aActionId); } } while (0)
;
5507 return;
5508 }
5509
5510 if (aContext != mActiveBrowsingContextInContent) {
5511 if (aContext) {
5512 contentChild->SendSetActiveBrowsingContext(aContext, aActionId);
5513 } else if (mActiveBrowsingContextInContent &&
5514 !(BFCacheInParent() && aIsEnteringBFCache)) {
5515 // No need to tell the parent process to update the active browsing
5516 // context to null if we are entering BFCache, because the browsing
5517 // context that is about to show will update it.
5518 //
5519 // We want to sync this over only if this isn't happening
5520 // due to the active BrowsingContext switching processes,
5521 // in which case the BrowserChild has already marked itself
5522 // as destroying.
5523 nsPIDOMWindowOuter* outer =
5524 mActiveBrowsingContextInContent->GetDOMWindow();
5525 if (outer) {
5526 nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow();
5527 if (inner) {
5528 WindowGlobalChild* globalChild = inner->GetWindowGlobalChild();
5529 if (globalChild) {
5530 RefPtr<BrowserChild> browserChild = globalChild->GetBrowserChild();
5531 if (browserChild && !browserChild->IsDestroyed()) {
5532 contentChild->SendUnsetActiveBrowsingContext(
5533 mActiveBrowsingContextInContent, aActionId);
5534 }
5535 }
5536 }
5537 }
5538 }
5539 }
5540 mActiveBrowsingContextInContentSetFromOtherProcess = false;
5541 mActiveBrowsingContextInContent = aContext;
5542 mActionIdForActiveBrowsingContextInContent = aActionId;
5543 MaybeUnlockPointer(aContext);
5544}
5545
5546void nsFocusManager::SetActiveBrowsingContextFromOtherProcess(
5547 BrowsingContext* aContext, uint64_t aActionId) {
5548 MOZ_ASSERT(!XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!XRE_IsParentProcess()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5548); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5548); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5549 MOZ_ASSERT(aContext)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContext)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContext))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aContext", "./../../../dom/base/nsFocusManager.cpp"
, 5549); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aContext" ")"
); do { MOZ_CrashSequence(__null, 5549); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5550 if (ActionIdComparableAndLower(aActionId,
5551 mActionIdForActiveBrowsingContextInContent)) {
5552 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set active BrowsingContext [%p] from "
"another process due to a stale action id %" "l" "u" ".", aContext
, aActionId); } } while (0)
5553 ("Ignored an attempt to set active BrowsingContext [%p] from "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set active BrowsingContext [%p] from "
"another process due to a stale action id %" "l" "u" ".", aContext
, aActionId); } } while (0)
5554 "another process due to a stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set active BrowsingContext [%p] from "
"another process due to a stale action id %" "l" "u" ".", aContext
, aActionId); } } while (0)
5555 aContext, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set active BrowsingContext [%p] from "
"another process due to a stale action id %" "l" "u" ".", aContext
, aActionId); } } while (0)
;
5556 return;
5557 }
5558 if (aContext->IsInProcess()) {
5559 // This message has been in transit for long enough that
5560 // the process association of aContext has changed since
5561 // the other content process sent the message, because
5562 // an iframe in that process became an out-of-process
5563 // iframe while the IPC broadcast that we're receiving
5564 // was in-flight. Let's just ignore this.
5565 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"active from another process. actionid: %" "l" "u", aContext
, aActionId); } } while (0)
5566 ("Ignored an attempt to set an in-process BrowsingContext [%p] as "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"active from another process. actionid: %" "l" "u", aContext
, aActionId); } } while (0)
5567 "active from another process. actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"active from another process. actionid: %" "l" "u", aContext
, aActionId); } } while (0)
5568 aContext, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext [%p] as "
"active from another process. actionid: %" "l" "u", aContext
, aActionId); } } while (0)
;
5569 return;
5570 }
5571 mActiveBrowsingContextInContentSetFromOtherProcess = true;
5572 mActiveBrowsingContextInContent = aContext;
5573 mActionIdForActiveBrowsingContextInContent = aActionId;
5574 MaybeUnlockPointer(aContext);
5575}
5576
5577void nsFocusManager::UnsetActiveBrowsingContextFromOtherProcess(
5578 BrowsingContext* aContext, uint64_t aActionId) {
5579 MOZ_ASSERT(!XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!XRE_IsParentProcess()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5579); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5579); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5580 MOZ_ASSERT(aContext)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContext)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContext))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aContext", "./../../../dom/base/nsFocusManager.cpp"
, 5580); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aContext" ")"
); do { MOZ_CrashSequence(__null, 5580); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5581 if (ActionIdComparableAndLower(aActionId,
5582 mActionIdForActiveBrowsingContextInContent)) {
5583 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to unset the active BrowsingContext [%p] from "
"another process due to stale action id: %" "l" "u" ".", aContext
, aActionId); } } while (0)
5584 ("Ignored an attempt to unset the active BrowsingContext [%p] from "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to unset the active BrowsingContext [%p] from "
"another process due to stale action id: %" "l" "u" ".", aContext
, aActionId); } } while (0)
5585 "another process due to stale action id: %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to unset the active BrowsingContext [%p] from "
"another process due to stale action id: %" "l" "u" ".", aContext
, aActionId); } } while (0)
5586 aContext, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to unset the active BrowsingContext [%p] from "
"another process due to stale action id: %" "l" "u" ".", aContext
, aActionId); } } while (0)
;
5587 return;
5588 }
5589 if (mActiveBrowsingContextInContent == aContext) {
5590 mActiveBrowsingContextInContent = nullptr;
5591 mActionIdForActiveBrowsingContextInContent = aActionId;
5592 MaybeUnlockPointer(nullptr);
5593 } else {
5594 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to unset the active BrowsingContext [%p] from "
"another process. actionid: %" "l" "u", aContext, aActionId)
; } } while (0)
5595 ("Ignored an attempt to unset the active BrowsingContext [%p] from "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to unset the active BrowsingContext [%p] from "
"another process. actionid: %" "l" "u", aContext, aActionId)
; } } while (0)
5596 "another process. actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to unset the active BrowsingContext [%p] from "
"another process. actionid: %" "l" "u", aContext, aActionId)
; } } while (0)
5597 aContext, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to unset the active BrowsingContext [%p] from "
"another process. actionid: %" "l" "u", aContext, aActionId)
; } } while (0)
;
5598 }
5599}
5600
5601void nsFocusManager::ReviseActiveBrowsingContext(
5602 uint64_t aOldActionId, mozilla::dom::BrowsingContext* aContext,
5603 uint64_t aNewActionId) {
5604 MOZ_ASSERT(XRE_IsContentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsContentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsContentProcess()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsContentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5604); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "XRE_IsContentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5604); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5605 if (mActionIdForActiveBrowsingContextInContent == aOldActionId) {
5606 LOGFOCUS(("Revising the active BrowsingContext [%p]. old actionid: %" PRIu64do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Revising the active BrowsingContext [%p]. old actionid: %"
"l" "u" ", new " "actionid: %" "l" "u", aContext, aOldActionId
, aNewActionId); } } while (0)
5607 ", new "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Revising the active BrowsingContext [%p]. old actionid: %"
"l" "u" ", new " "actionid: %" "l" "u", aContext, aOldActionId
, aNewActionId); } } while (0)
5608 "actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Revising the active BrowsingContext [%p]. old actionid: %"
"l" "u" ", new " "actionid: %" "l" "u", aContext, aOldActionId
, aNewActionId); } } while (0)
5609 aContext, aOldActionId, aNewActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Revising the active BrowsingContext [%p]. old actionid: %"
"l" "u" ", new " "actionid: %" "l" "u", aContext, aOldActionId
, aNewActionId); } } while (0)
;
5610 mActiveBrowsingContextInContent = aContext;
5611 mActionIdForActiveBrowsingContextInContent = aNewActionId;
5612 } else {
5613 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored a stale attempt to revise the active BrowsingContext [%p]. "
"old actionid: %" "l" "u" ", new actionid: %" "l" "u", aContext
, aOldActionId, aNewActionId); } } while (0)
5614 ("Ignored a stale attempt to revise the active BrowsingContext [%p]. "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored a stale attempt to revise the active BrowsingContext [%p]. "
"old actionid: %" "l" "u" ", new actionid: %" "l" "u", aContext
, aOldActionId, aNewActionId); } } while (0)
5615 "old actionid: %" PRIu64 ", new actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored a stale attempt to revise the active BrowsingContext [%p]. "
"old actionid: %" "l" "u" ", new actionid: %" "l" "u", aContext
, aOldActionId, aNewActionId); } } while (0)
5616 aContext, aOldActionId, aNewActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored a stale attempt to revise the active BrowsingContext [%p]. "
"old actionid: %" "l" "u" ", new actionid: %" "l" "u", aContext
, aOldActionId, aNewActionId); } } while (0)
;
5617 }
5618}
5619
5620void nsFocusManager::ReviseFocusedBrowsingContext(
5621 uint64_t aOldActionId, mozilla::dom::BrowsingContext* aContext,
5622 uint64_t aNewActionId) {
5623 MOZ_ASSERT(XRE_IsContentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsContentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsContentProcess()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsContentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5623); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "XRE_IsContentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5623); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5624 if (mActionIdForFocusedBrowsingContextInContent == aOldActionId) {
5625 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Revising the focused BrowsingContext [%p]. old actionid: %"
"l" "u" ", new " "actionid: %" "l" "u", aContext, aOldActionId
, aNewActionId); } } while (0)
5626 ("Revising the focused BrowsingContext [%p]. old actionid: %" PRIu64do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Revising the focused BrowsingContext [%p]. old actionid: %"
"l" "u" ", new " "actionid: %" "l" "u", aContext, aOldActionId
, aNewActionId); } } while (0)
5627 ", new "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Revising the focused BrowsingContext [%p]. old actionid: %"
"l" "u" ", new " "actionid: %" "l" "u", aContext, aOldActionId
, aNewActionId); } } while (0)
5628 "actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Revising the focused BrowsingContext [%p]. old actionid: %"
"l" "u" ", new " "actionid: %" "l" "u", aContext, aOldActionId
, aNewActionId); } } while (0)
5629 aContext, aOldActionId, aNewActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Revising the focused BrowsingContext [%p]. old actionid: %"
"l" "u" ", new " "actionid: %" "l" "u", aContext, aOldActionId
, aNewActionId); } } while (0)
;
5630 mFocusedBrowsingContextInContent = aContext;
5631 mActionIdForFocusedBrowsingContextInContent = aNewActionId;
5632 mFocusedElement = nullptr;
5633 } else {
5634 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored a stale attempt to revise the focused BrowsingContext [%p]. "
"old actionid: %" "l" "u" ", new actionid: %" "l" "u", aContext
, aOldActionId, aNewActionId); } } while (0)
5635 ("Ignored a stale attempt to revise the focused BrowsingContext [%p]. "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored a stale attempt to revise the focused BrowsingContext [%p]. "
"old actionid: %" "l" "u" ", new actionid: %" "l" "u", aContext
, aOldActionId, aNewActionId); } } while (0)
5636 "old actionid: %" PRIu64 ", new actionid: %" PRIu64,do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored a stale attempt to revise the focused BrowsingContext [%p]. "
"old actionid: %" "l" "u" ", new actionid: %" "l" "u", aContext
, aOldActionId, aNewActionId); } } while (0)
5637 aContext, aOldActionId, aNewActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored a stale attempt to revise the focused BrowsingContext [%p]. "
"old actionid: %" "l" "u" ", new actionid: %" "l" "u", aContext
, aOldActionId, aNewActionId); } } while (0)
;
5638 }
5639}
5640
5641bool nsFocusManager::SetActiveBrowsingContextInChrome(
5642 mozilla::dom::BrowsingContext* aContext, uint64_t aActionId) {
5643 MOZ_ASSERT(aActionId)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aActionId)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aActionId))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aActionId", "./../../../dom/base/nsFocusManager.cpp"
, 5643); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aActionId" ")"
); do { MOZ_CrashSequence(__null, 5643); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5644 if (ProcessPendingActiveBrowsingContextActionId(aActionId, aContext)) {
5645 MOZ_DIAGNOSTIC_ASSERT(!ActionIdComparableAndLower(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ActionIdComparableAndLower( aActionId, mActionIdForActiveBrowsingContextInChrome
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!ActionIdComparableAndLower( aActionId, mActionIdForActiveBrowsingContextInChrome
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ActionIdComparableAndLower( aActionId, mActionIdForActiveBrowsingContextInChrome)"
, "./../../../dom/base/nsFocusManager.cpp", 5646); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "!ActionIdComparableAndLower( aActionId, mActionIdForActiveBrowsingContextInChrome)"
")"); do { MOZ_CrashSequence(__null, 5646); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5646 aActionId, mActionIdForActiveBrowsingContextInChrome))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ActionIdComparableAndLower( aActionId, mActionIdForActiveBrowsingContextInChrome
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!ActionIdComparableAndLower( aActionId, mActionIdForActiveBrowsingContextInChrome
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ActionIdComparableAndLower( aActionId, mActionIdForActiveBrowsingContextInChrome)"
, "./../../../dom/base/nsFocusManager.cpp", 5646); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "!ActionIdComparableAndLower( aActionId, mActionIdForActiveBrowsingContextInChrome)"
")"); do { MOZ_CrashSequence(__null, 5646); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5647 mActiveBrowsingContextInChrome = aContext;
5648 mActionIdForActiveBrowsingContextInChrome = aActionId;
5649 return true;
5650 }
5651 return false;
5652}
5653
5654uint64_t nsFocusManager::GetActionIdForActiveBrowsingContextInChrome() const {
5655 return mActionIdForActiveBrowsingContextInChrome;
5656}
5657
5658uint64_t nsFocusManager::GetActionIdForFocusedBrowsingContextInChrome() const {
5659 return mActionIdForFocusedBrowsingContextInChrome;
5660}
5661
5662BrowsingContext* nsFocusManager::GetActiveBrowsingContextInChrome() {
5663 return mActiveBrowsingContextInChrome;
5664}
5665
5666void nsFocusManager::InsertNewFocusActionId(uint64_t aActionId) {
5667 LOGFOCUS(("InsertNewFocusActionId %" PRIu64, aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "InsertNewFocusActionId %"
"l" "u", aActionId); } } while (0)
;
5668 MOZ_ASSERT(XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5668); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5668); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5669 MOZ_ASSERT(!mPendingActiveBrowsingContextActions.Contains(aActionId))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mPendingActiveBrowsingContextActions.Contains(aActionId
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mPendingActiveBrowsingContextActions.Contains(aActionId
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mPendingActiveBrowsingContextActions.Contains(aActionId)",
"./../../../dom/base/nsFocusManager.cpp", 5669); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mPendingActiveBrowsingContextActions.Contains(aActionId)"
")"); do { MOZ_CrashSequence(__null, 5669); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5670 mPendingActiveBrowsingContextActions.AppendElement(aActionId);
5671 MOZ_ASSERT(!mPendingFocusedBrowsingContextActions.Contains(aActionId))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mPendingFocusedBrowsingContextActions.Contains(aActionId
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mPendingFocusedBrowsingContextActions.Contains(aActionId
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mPendingFocusedBrowsingContextActions.Contains(aActionId)"
, "./../../../dom/base/nsFocusManager.cpp", 5671); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mPendingFocusedBrowsingContextActions.Contains(aActionId)"
")"); do { MOZ_CrashSequence(__null, 5671); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5672 mPendingFocusedBrowsingContextActions.AppendElement(aActionId);
5673}
5674
5675static void RemoveContentInitiatedActionsUntil(
5676 nsTArray<uint64_t>& aPendingActions,
5677 nsTArray<uint64_t>::index_type aUntil) {
5678 nsTArray<uint64_t>::index_type i = 0;
5679 while (i < aUntil) {
5680 auto [actionProc, actionId] =
5681 nsContentUtils::SplitProcessSpecificId(aPendingActions[i]);
5682 (void)actionId;
5683 if (actionProc) {
5684 aPendingActions.RemoveElementAt(i);
5685 --aUntil;
5686 continue;
5687 }
5688 ++i;
5689 }
5690}
5691
5692bool nsFocusManager::ProcessPendingActiveBrowsingContextActionId(
5693 uint64_t aActionId, bool aSettingToNonNull) {
5694 MOZ_ASSERT(XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5694); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5694); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5695 auto index = mPendingActiveBrowsingContextActions.IndexOf(aActionId);
5696 if (index == nsTArray<uint64_t>::NoIndex) {
5697 return false;
5698 }
5699 // When aSettingToNonNull is true, we need to remove one more
5700 // element to remove the action id itself in addition to
5701 // removing the older ones.
5702 if (aSettingToNonNull) {
5703 index++;
5704 }
5705 auto [actionProc, actionId] =
5706 nsContentUtils::SplitProcessSpecificId(aActionId);
5707 (void)actionId;
5708 if (actionProc) {
5709 // Action from content: We allow parent-initiated actions
5710 // to take precedence over content-initiated ones, so we
5711 // remove only prior content-initiated actions.
5712 RemoveContentInitiatedActionsUntil(mPendingActiveBrowsingContextActions,
5713 index);
5714 } else {
5715 // Action from chrome
5716 mPendingActiveBrowsingContextActions.RemoveElementsAt(0, index);
5717 }
5718 return true;
5719}
5720
5721bool nsFocusManager::ProcessPendingFocusedBrowsingContextActionId(
5722 uint64_t aActionId) {
5723 MOZ_ASSERT(XRE_IsParentProcess())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(XRE_IsParentProcess())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(XRE_IsParentProcess()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("XRE_IsParentProcess()"
, "./../../../dom/base/nsFocusManager.cpp", 5723); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "XRE_IsParentProcess()" ")"); do { MOZ_CrashSequence
(__null, 5723); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5724 auto index = mPendingFocusedBrowsingContextActions.IndexOf(aActionId);
5725 if (index == nsTArray<uint64_t>::NoIndex) {
5726 return false;
5727 }
5728
5729 auto [actionProc, actionId] =
5730 nsContentUtils::SplitProcessSpecificId(aActionId);
5731 (void)actionId;
5732 if (actionProc) {
5733 // Action from content: We allow parent-initiated actions
5734 // to take precedence over content-initiated ones, so we
5735 // remove only prior content-initiated actions.
5736 RemoveContentInitiatedActionsUntil(mPendingFocusedBrowsingContextActions,
5737 index);
5738 } else {
5739 // Action from chrome
5740 mPendingFocusedBrowsingContextActions.RemoveElementsAt(0, index);
5741 }
5742 return true;
5743}
5744
5745// static
5746uint64_t nsFocusManager::GenerateFocusActionId() {
5747 uint64_t id =
5748 nsContentUtils::GenerateProcessSpecificId(++sFocusActionCounter);
5749 if (XRE_IsParentProcess()) {
5750 nsFocusManager* fm = GetFocusManager();
5751 if (fm) {
5752 fm->InsertNewFocusActionId(id);
5753 }
5754 } else {
5755 mozilla::dom::ContentChild* contentChild =
5756 mozilla::dom::ContentChild::GetSingleton();
5757 MOZ_ASSERT(contentChild)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(contentChild)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(contentChild))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("contentChild", "./../../../dom/base/nsFocusManager.cpp"
, 5757); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contentChild"
")"); do { MOZ_CrashSequence(__null, 5757); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5758 contentChild->SendInsertNewFocusActionId(id);
5759 }
5760 LOGFOCUS(("GenerateFocusActionId %" PRIu64, id))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "GenerateFocusActionId %"
"l" "u", id); } } while (0)
;
5761 return id;
5762}
5763
5764static bool IsInPointerLockContext(nsPIDOMWindowOuter* aWin) {
5765 return PointerLockManager::IsInLockContext(aWin ? aWin->GetBrowsingContext()
5766 : nullptr);
5767}
5768
5769void nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow,
5770 uint64_t aActionId,
5771 bool aSyncBrowsingContext) {
5772 if (XRE_IsParentProcess() && !PointerUnlocker::sActiveUnlocker &&
5773 IsInPointerLockContext(mFocusedWindow) &&
5774 !IsInPointerLockContext(aWindow)) {
5775 nsCOMPtr<nsIRunnable> runnable = new PointerUnlocker();
5776 NS_DispatchToCurrentThread(runnable);
5777 }
5778
5779 // Update the last focus time on any affected documents
5780 if (aWindow && aWindow != mFocusedWindow) {
5781 const TimeStamp now(TimeStamp::Now());
5782 for (Document* doc = aWindow->GetExtantDoc(); doc;
5783 doc = doc->GetInProcessParentDocument()) {
5784 doc->SetLastFocusTime(now);
5785 }
5786 }
5787
5788 // This function may be called with zero action id to indicate that the
5789 // action id should be ignored.
5790 if (XRE_IsContentProcess() && aActionId &&
5791 ActionIdComparableAndLower(aActionId,
5792 mActionIdForFocusedBrowsingContextInContent)) {
5793 // Unclear if this ever happens.
5794 LOGFOCUS(do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext as "
"focused due to stale action id %" "l" "u" ".", aActionId); }
} while (0)
5795 ("Ignored an attempt to set an in-process BrowsingContext as "do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext as "
"focused due to stale action id %" "l" "u" ".", aActionId); }
} while (0)
5796 "focused due to stale action id %" PRIu64 ".",do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext as "
"focused due to stale action id %" "l" "u" ".", aActionId); }
} while (0)
5797 aActionId))do { const ::mozilla::LogModule* moz_real_module = gFocusLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignored an attempt to set an in-process BrowsingContext as "
"focused due to stale action id %" "l" "u" ".", aActionId); }
} while (0)
;
5798 return;
5799 }
5800
5801 mFocusedWindow = aWindow;
5802 BrowsingContext* bc = aWindow ? aWindow->GetBrowsingContext() : nullptr;
5803 if (aSyncBrowsingContext) {
5804 MOZ_ASSERT(aActionId,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aActionId)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aActionId))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aActionId" " (" "aActionId must not be zero if aSyncBrowsingContext is true"
")", "./../../../dom/base/nsFocusManager.cpp", 5805); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aActionId" ") (" "aActionId must not be zero if aSyncBrowsingContext is true"
")"); do { MOZ_CrashSequence(__null, 5805); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5805 "aActionId must not be zero if aSyncBrowsingContext is true")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aActionId)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aActionId))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aActionId" " (" "aActionId must not be zero if aSyncBrowsingContext is true"
")", "./../../../dom/base/nsFocusManager.cpp", 5805); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aActionId" ") (" "aActionId must not be zero if aSyncBrowsingContext is true"
")"); do { MOZ_CrashSequence(__null, 5805); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5806 SetFocusedBrowsingContext(bc, aActionId);
5807 } else if (XRE_IsContentProcess()) {
5808 MOZ_ASSERT(mFocusedBrowsingContextInContent == bc,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFocusedBrowsingContextInContent == bc)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(mFocusedBrowsingContextInContent == bc))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mFocusedBrowsingContextInContent == bc"
" (" "Not syncing BrowsingContext even when different." ")",
"./../../../dom/base/nsFocusManager.cpp", 5809); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mFocusedBrowsingContextInContent == bc" ") ("
"Not syncing BrowsingContext even when different." ")"); do {
MOZ_CrashSequence(__null, 5809); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
5809 "Not syncing BrowsingContext even when different.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFocusedBrowsingContextInContent == bc)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(mFocusedBrowsingContextInContent == bc))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mFocusedBrowsingContextInContent == bc"
" (" "Not syncing BrowsingContext even when different." ")",
"./../../../dom/base/nsFocusManager.cpp", 5809); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mFocusedBrowsingContextInContent == bc" ") ("
"Not syncing BrowsingContext even when different." ")"); do {
MOZ_CrashSequence(__null, 5809); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
5810 }
5811}
5812
5813void nsFocusManager::NotifyOfReFocus(Element& aElement) {
5814 nsPIDOMWindowOuter* window = GetCurrentWindow(&aElement);
5815 if (!window || window != mFocusedWindow) {
5816 return;
5817 }
5818 if (!aElement.IsInComposedDoc() || IsNonFocusableRoot(&aElement)) {
5819 return;
5820 }
5821 nsIDocShell* docShell = window->GetDocShell();
5822 if (!docShell) {
5823 return;
5824 }
5825 RefPtr<PresShell> presShell = docShell->GetPresShell();
5826 if (!presShell) {
5827 return;
5828 }
5829 RefPtr<nsPresContext> presContext = presShell->GetPresContext();
5830 if (!presContext) {
5831 return;
5832 }
5833 IMEStateManager::OnReFocus(*presContext, aElement);
5834}
5835
5836void nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration) {
5837 if (!sInstance) {
5838 return;
5839 }
5840
5841 if (sInstance->mActiveWindow) {
5842 sInstance->mActiveWindow->MarkUncollectableForCCGeneration(aGeneration);
5843 }
5844 if (sInstance->mFocusedWindow) {
5845 sInstance->mFocusedWindow->MarkUncollectableForCCGeneration(aGeneration);
5846 }
5847 if (sInstance->mWindowBeingLowered) {
5848 sInstance->mWindowBeingLowered->MarkUncollectableForCCGeneration(
5849 aGeneration);
5850 }
5851 if (sInstance->mFocusedElement) {
5852 sInstance->mFocusedElement->OwnerDoc()->MarkUncollectableForCCGeneration(
5853 aGeneration);
5854 }
5855}
5856
5857bool nsFocusManager::CanSkipFocus(nsIContent* aContent) {
5858 if (!aContent) {
5859 return false;
5860 }
5861
5862 if (mFocusedElement == aContent) {
5863 return true;
5864 }
5865
5866 nsIDocShell* ds = aContent->OwnerDoc()->GetDocShell();
5867 if (!ds) {
5868 return true;
5869 }
5870
5871 if (XRE_IsParentProcess()) {
5872 nsCOMPtr<nsIDocShellTreeItem> root;
5873 ds->GetInProcessRootTreeItem(getter_AddRefs(root));
5874 nsCOMPtr<nsPIDOMWindowOuter> newRootWindow =
5875 root ? root->GetWindow() : nullptr;
5876 if (mActiveWindow != newRootWindow) {
5877 nsPIDOMWindowOuter* outerWindow = aContent->OwnerDoc()->GetWindow();
5878 if (outerWindow && outerWindow->GetFocusedElement() == aContent) {
5879 return true;
5880 }
5881 }
5882 } else {
5883 BrowsingContext* bc = aContent->OwnerDoc()->GetBrowsingContext();
5884 BrowsingContext* top = bc ? bc->Top() : nullptr;
5885 if (GetActiveBrowsingContext() != top) {
5886 nsPIDOMWindowOuter* outerWindow = aContent->OwnerDoc()->GetWindow();
5887 if (outerWindow && outerWindow->GetFocusedElement() == aContent) {
5888 return true;
5889 }
5890 }
5891 }
5892
5893 return false;
5894}
5895
5896static IsFocusableFlags FocusManagerFlagsToIsFocusableFlags(uint32_t aFlags) {
5897 auto flags = IsFocusableFlags(0);
5898 if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
5899 flags |= IsFocusableFlags::WithMouse;
5900 }
5901 return flags;
5902}
5903
5904/* static */
5905Element* nsFocusManager::GetTheFocusableArea(Element* aTarget,
5906 uint32_t aFlags) {
5907 MOZ_ASSERT(aTarget)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aTarget)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aTarget))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aTarget", "./../../../dom/base/nsFocusManager.cpp"
, 5907); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aTarget" ")"
); do { MOZ_CrashSequence(__null, 5907); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5908 nsIFrame* frame = aTarget->GetPrimaryFrame();
5909 if (!frame) {
5910 return nullptr;
5911 }
5912
5913 // If focus target is the document element of its Document.
5914 if (aTarget == aTarget->OwnerDoc()->GetRootElement()) {
5915 // the root content can always be focused,
5916 // except in userfocusignored context.
5917 return aTarget;
5918 }
5919
5920 // If focus target is an area element with one or more shapes that are
5921 // focusable areas.
5922 if (auto* area = HTMLAreaElement::FromNode(aTarget)) {
5923 return IsAreaElementFocusable(*area) ? area : nullptr;
5924 }
5925
5926 // For these 3 steps mentioned in the spec
5927 // 1. If focus target is an element with one or more scrollable regions that
5928 // are focusable areas
5929 // 2. If focus target is a navigable
5930 // 3. If focus target is a navigable container with a non-null content
5931 // navigable
5932 // nsIFrame::IsFocusable will effectively perform the checks for them.
5933 IsFocusableFlags flags = FocusManagerFlagsToIsFocusableFlags(aFlags);
5934 if (frame->IsFocusable(flags)) {
5935 return aTarget;
5936 }
5937
5938 // If focus target is a shadow host whose shadow root's delegates focus is
5939 // true
5940 if (ShadowRoot* root = aTarget->GetShadowRoot()) {
5941 if (root->DelegatesFocus()) {
5942 // If focus target is a shadow-including inclusive ancestor of the
5943 // currently focused area of a top-level browsing context's DOM anchor,
5944 // then return the already-focused element.
5945 if (nsPIDOMWindowInner* innerWindow =
5946 aTarget->OwnerDoc()->GetInnerWindow()) {
5947 if (Element* focusedElement = innerWindow->GetFocusedElement()) {
5948 if (focusedElement->IsShadowIncludingInclusiveDescendantOf(aTarget)) {
5949 return focusedElement;
5950 }
5951 }
5952 }
5953
5954 if (Element* firstFocusable = root->GetFocusDelegate(flags)) {
5955 return firstFocusable;
5956 }
5957 }
5958 }
5959 return nullptr;
5960}
5961
5962/* static */
5963bool nsFocusManager::IsAreaElementFocusable(HTMLAreaElement& aArea) {
5964 nsIFrame* frame = aArea.GetPrimaryFrame();
5965 if (!frame) {
5966 return false;
5967 }
5968 // HTML areas do not have their own frame, and the img frame we get from
5969 // GetPrimaryFrame() is not relevant as to whether it is focusable or
5970 // not, so we have to do all the relevant checks manually for them.
5971 return frame->IsVisibleConsideringAncestors() &&
5972 aArea.IsFocusableWithoutStyle();
5973}
5974
5975nsresult NS_NewFocusManager(nsIFocusManager** aResult) {
5976 NS_IF_ADDREF(*aResult = nsFocusManager::GetFocusManager())ns_if_addref(*aResult = nsFocusManager::GetFocusManager());
5977 return NS_OK;
5978}