From 185848e40becf486fec084781e2ea9a426970eee Mon Sep 17 00:00:00 2001 From: Sean Feng Date: Thu, 6 Jul 2023 15:33:33 +0000 Subject: [PATCH] Bug 1811129 - Implement the new dialog initial focus algorithm r=emilio The main changes of the new algorithm are * Make the dialog focusing steps look at sequentially focusable elements instead of any focusable element. * Make the dialog element itself get focus if it has the autofocus attribute set. * Make the dialog element itself get focus as a fallback instead of focus being "reset" to the body element. Spec PR (merged): https://github.com/whatwg/html/pull/8199 Differential Revision: https://phabricator.services.mozilla.com/D181263 --- dom/base/FragmentOrElement.cpp | 27 ++++++++++++++----- dom/html/HTMLDialogElement.cpp | 6 ++++- dom/html/HTMLDialogElement.h | 2 ++ .../child-sequential-focus.html.ini | 12 --------- .../dialog-focus-shadow.html.ini | 20 -------------- .../dialog-showModal.html.ini | 5 ---- .../show-modal-focusing-steps.html.ini | 5 ---- 7 files changed, 28 insertions(+), 49 deletions(-) delete mode 100644 testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html.ini delete mode 100644 testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html.ini delete mode 100644 testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html.ini delete mode 100644 testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html.ini diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index dc4e43064a9ba9..e5a49ad5dbec0b 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1036,9 +1036,14 @@ Element* nsIContent::GetFocusDelegate(bool aWithMouse, whereToLook = root; } - auto IsFocusable = [&](Element* aElement) { + auto IsFocusable = [&](Element* aElement) -> nsIFrame::Focusable { nsIFrame* frame = aElement->GetPrimaryFrame(); - return frame && frame->IsFocusable(aWithMouse); + + if (!frame) { + return {}; + } + + return frame->IsFocusable(aWithMouse); }; Element* potentialFocus = nullptr; @@ -1059,10 +1064,20 @@ Element* nsIContent::GetFocusDelegate(bool aWithMouse, // Found an autofocus candidate. return el; } - } else if (!potentialFocus && IsFocusable(el)) { - // This element could be the one if we can't find an - // autofocus candidate which has the precedence. - potentialFocus = el; + } else if (!potentialFocus) { + if (nsIFrame::Focusable focusable = IsFocusable(el)) { + if (IsHTMLElement(nsGkAtoms::dialog)) { + if (focusable.mTabIndex >= 0) { + // If focusTarget is a dialog element and descendant is sequentially + // focusable, then set focusableArea to descendant. + potentialFocus = el; + } + } else { + // This element could be the one if we can't find an + // autofocus candidate which has the precedence. + potentialFocus = el; + } + } } if (!autofocus && potentialFocus) { diff --git a/dom/html/HTMLDialogElement.cpp b/dom/html/HTMLDialogElement.cpp index a243f3b72dda68..b9cb6a79b51c8e 100644 --- a/dom/html/HTMLDialogElement.cpp +++ b/dom/html/HTMLDialogElement.cpp @@ -159,7 +159,9 @@ void HTMLDialogElement::FocusDialog() { doc->FlushPendingNotifications(FlushType::Frames); } - RefPtr control = GetFocusDelegate(false /* aWithMouse */); + RefPtr control = HasAttr(nsGkAtoms::autofocus) + ? this + : GetFocusDelegate(false /* aWithMouse */); // If there isn't one of those either, then let control be subject. if (!control) { @@ -169,6 +171,8 @@ void HTMLDialogElement::FocusDialog() { FocusCandidate(*control, IsInTopLayer()); } +int32_t HTMLDialogElement::TabIndexDefault() { return 0; } + void HTMLDialogElement::QueueCancelDialog() { // queues an element task on the user interaction task source OwnerDoc() diff --git a/dom/html/HTMLDialogElement.h b/dom/html/HTMLDialogElement.h index 4f5f0c8abaebd9..82f21423879b24 100644 --- a/dom/html/HTMLDialogElement.h +++ b/dom/html/HTMLDialogElement.h @@ -49,6 +49,8 @@ class HTMLDialogElement final : public nsGenericHTMLElement { MOZ_CAN_RUN_SCRIPT_BOUNDARY void FocusDialog(); + int32_t TabIndexDefault() override; + nsString mReturnValue; protected: diff --git a/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html.ini b/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html.ini deleted file mode 100644 index 6f8c4561a74dec..00000000000000 --- a/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html.ini +++ /dev/null @@ -1,12 +0,0 @@ -[child-sequential-focus.html] - [dialog element with autofocus should get initial focus.] - expected: FAIL - - [Only keyboard-focusable elements should get dialog initial focus.] - expected: FAIL - - [Only keyboard-focusable elements should get dialog initial focus including in subtrees.] - expected: FAIL - - [Only keyboard-focusable elements should get dialog initial focus including in nested buttons.] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html.ini b/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html.ini deleted file mode 100644 index 863e564527a689..00000000000000 --- a/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html.ini +++ /dev/null @@ -1,20 +0,0 @@ -[dialog-focus-shadow.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [show: No autofocus, no delegatesFocus, no siblings] - expected: FAIL - - [showModal: No autofocus, no delegatesFocus, no siblings] - expected: FAIL - - [show: Autofocus on shadow host, no delegatesFocus, no siblings] - expected: FAIL - - [showModal: Autofocus on shadow host, no delegatesFocus, no siblings] - expected: FAIL - - [show: Autofocus inside shadow tree, no delegatesFocus, no siblings] - expected: FAIL - - [showModal: Autofocus inside shadow tree, no delegatesFocus, no siblings] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html.ini b/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html.ini deleted file mode 100644 index c7357edd249894..00000000000000 --- a/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[dialog-showModal.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [opening dialog without focusable children] - expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html.ini b/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html.ini deleted file mode 100644 index f0d673d2c2145c..00000000000000 --- a/testing/web-platform/meta/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[show-modal-focusing-steps.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [focus when a modal dialog is opened] - expected: FAIL