Bug Summary

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