commit 2e045b27f806a34b4145862990fb80bff1a3d954
parent a71c51bcb81ca1b32f60b36510338cc0ee2e82fa
Author: Andreas Farre <farre@mozilla.com>
Date: Tue, 4 Nov 2025 11:53:47 +0000
Bug 1997917 - Make sure to notify Navigation API about focus changes. r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D271067
Diffstat:
6 files changed, 41 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"
@@ -914,6 +915,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);
@@ -925,10 +935,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/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