commit 0c9992521b1f82785bf7c9d5136d152b483302d7
parent e8c7b719c8c02a5a6eb235cac6f7f6bde9e691cb
Author: Andreas Farre <farre@mozilla.com>
Date: Wed, 5 Nov 2025 08:21:40 +0000
Bug 1997917 - Make sure to notify Navigation API about focus changes. r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D271067
Diffstat:
7 files changed, 42 insertions(+), 41 deletions(-)
diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
@@ -39,6 +39,7 @@
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLSlotElement.h"
+#include "mozilla/dom/Navigation.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/Text.h"
#include "mozilla/dom/WindowGlobalChild.h"
@@ -913,6 +914,15 @@ void nsFocusManager::ContentAppended(nsIContent* aFirstNewContent,
static void UpdateFocusWithinState(Element* aElement,
nsIContent* aCommonAncestor,
bool aGettingFocus) {
+ Element* focusedElement = nullptr;
+ Document* document = aElement->GetComposedDoc();
+ if (aElement && document) {
+ if (nsPIDOMWindowOuter* window = document->GetWindow()) {
+ focusedElement = window->GetFocusedElement();
+ }
+ }
+
+ bool focusChanged = false;
for (nsIContent* content = aElement; content && content != aCommonAncestor;
content = content->GetFlattenedTreeParent()) {
Element* element = Element::FromNode(content);
@@ -924,10 +934,21 @@ static void UpdateFocusWithinState(Element* aElement,
if (element->State().HasState(ElementState::FOCUS_WITHIN)) {
break;
}
+
element->AddStates(ElementState::FOCUS_WITHIN);
} else {
element->RemoveStates(ElementState::FOCUS_WITHIN);
}
+
+ focusChanged = focusChanged || element == focusedElement;
+ }
+
+ if (focusChanged && document->GetInnerWindow()) {
+ if (RefPtr<Navigation> navigation =
+ document->GetInnerWindow()->Navigation()) {
+ navigation->SetFocusedChangedDuringOngoingNavigation(
+ /* aFocusChangedDuringOngoingNavigation */ true);
+ }
}
}
diff --git a/dom/events/NavigateEvent.cpp b/dom/events/NavigateEvent.cpp
@@ -15,6 +15,7 @@
#include "mozilla/dom/Navigation.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "nsDocShell.h"
+#include "nsFocusManager.h"
#include "nsGlobalWindowInner.h"
extern mozilla::LazyLogModule gNavigationAPILog;
@@ -409,8 +410,25 @@ void NavigateEvent::PotentiallyResetFocus() {
// Step 11, step 12
FocusOptions options;
- LOG_FMT("Set focus for {}", *focusTarget->AsNode());
- focusTarget->Focus(options, CallerType::NonSystem, IgnoredErrorResult());
+ options.mPreventScroll = true;
+ focusTarget = nsFocusManager::GetTheFocusableArea(
+ focusTarget, nsFocusManager::ProgrammaticFocusFlags(options));
+
+ if (focusTarget) {
+ LOG_FMT("Reset focus to {}", *focusTarget->AsNode());
+ focusTarget->Focus(options, CallerType::NonSystem, IgnoredErrorResult());
+ } else if (RefPtr<nsIFocusManager> focusManager =
+ nsFocusManager::GetFocusManager()) {
+ if (nsPIDOMWindowOuter* window = document->GetWindow()) {
+ // Now focus the document itself if focus is on an element within it.
+ nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
+ focusManager->GetFocusedWindow(getter_AddRefs(focusedWindow));
+ if (SameCOMIdentity(window, focusedWindow)) {
+ LOG_FMT("Reset focus to document viewport");
+ focusManager->ClearFocus(focusedWindow);
+ }
+ }
+ }
}
// https://html.spec.whatwg.org/#potentially-process-scroll-behavior
diff --git a/dom/events/NavigateEvent.h b/dom/events/NavigateEvent.h
@@ -117,6 +117,7 @@ class NavigateEvent final : public Event {
void PerformSharedChecks(ErrorResult& aRv);
private:
+ MOZ_CAN_RUN_SCRIPT
void PotentiallyResetFocus();
MOZ_CAN_RUN_SCRIPT
diff --git a/testing/web-platform/meta/navigation-api/focus-reset/autofocus.html.ini b/testing/web-platform/meta/navigation-api/focus-reset/autofocus.html.ini
@@ -1,12 +1,3 @@
[autofocus.html]
- [An element with autofocus, present before navigation but disabled before finished, does not get focused]
- expected: FAIL
-
- [An element with autofocus, present before navigation but with its autofocus attribute removed before finished, does not get focused]
- expected: FAIL
-
[An element with autofocus, introduced between committed and finished, gets focused]
expected: FAIL
-
- [An element with autofocus, introduced after finished, does not get focused]
- expected: FAIL
diff --git a/testing/web-platform/meta/navigation-api/focus-reset/basic.html.ini b/testing/web-platform/meta/navigation-api/focus-reset/basic.html.ini
@@ -1,15 +0,0 @@
-[basic.html]
- [Resets the focus when no focusReset option is provided]
- expected: FAIL
-
- [Resets the focus when focusReset is explicitly set to undefined]
- expected: FAIL
-
- [Resets the focus when no focusReset option is provided (nontrivial fulfilled promise)]
- expected: FAIL
-
- [Resets the focus when no focusReset option is provided (rejected promise)]
- expected: FAIL
-
- [Resets the focus when focusReset is explicitly set to 'after-transition']
- expected: FAIL
diff --git a/testing/web-platform/meta/navigation-api/focus-reset/focus-reset-timing.html.ini b/testing/web-platform/meta/navigation-api/focus-reset/focus-reset-timing.html.ini
@@ -1,6 +0,0 @@
-[focus-reset-timing.html]
- [Focus should be reset before navigatesuccess]
- expected: FAIL
-
- [Focus should be reset before navigateerror]
- expected: FAIL
diff --git a/testing/web-platform/meta/navigation-api/focus-reset/multiple-intercept.html.ini b/testing/web-platform/meta/navigation-api/focus-reset/multiple-intercept.html.ini
@@ -1,9 +0,0 @@
-[multiple-intercept.html]
- [(not provided) + after-transition]
- expected: FAIL
-
- [after-transition + (not provided)]
- expected: FAIL
-
- [manual + after-transition]
- expected: FAIL