diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 06adb14cbd3..30893551f3a 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -3418,6 +3418,24 @@ bool Document::has_focus() const return true; } +// https://html.spec.whatwg.org/multipage/interaction.html#allow-focus-steps +bool Document::allow_focus() const +{ + // The allow focus steps, given a Document object target, are as follows: + + // 1. If target is allowed to use the "focus-without-user-activation" feature, then return true. + if (is_allowed_to_use_feature(PolicyControlledFeature::FocusWithoutUserActivation)) + return true; + + // FIXME: 2. If any of the following are true: + // - target's relevant global object has transient user activation; or + // - target's node navigable's container, if any, is marked as locked for focus, + // then return true. + + // 3. Return false. + return false; +} + void Document::set_parser(Badge, HTML::HTMLParser& parser) { m_parser = parser; @@ -4295,6 +4313,9 @@ bool Document::is_allowed_to_use_feature(PolicyControlledFeature feature) const if (PermissionsPolicy::AutoplayAllowlist::the().is_allowed_for_origin(*this, origin()) == PermissionsPolicy::Decision::Enabled) return true; break; + case PolicyControlledFeature::FocusWithoutUserActivation: + // FIXME: Implement allowlist for this. + return true; } // 4. Return false. diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index cdd782aaf4b..3227961a75c 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -154,8 +154,9 @@ struct ElementCreationOptions { Optional is; }; -enum class PolicyControlledFeature { +enum class PolicyControlledFeature : u8 { Autoplay, + FocusWithoutUserActivation, }; class Document @@ -552,6 +553,8 @@ public: bool has_focus() const; + bool allow_focus() const; + void set_parser(Badge, HTML::HTMLParser&); void detach_parser(Badge); diff --git a/Libraries/LibWeb/HTML/HTMLDialogElement.cpp b/Libraries/LibWeb/HTML/HTMLDialogElement.cpp index 6eead2d5be5..6d7e5a44c92 100644 --- a/Libraries/LibWeb/HTML/HTMLDialogElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLDialogElement.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2024, Sam Atkins + * Copyright (c) 2024-2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -16,6 +16,7 @@ #include #include #include +#include namespace Web::HTML { @@ -406,18 +407,32 @@ void HTMLDialogElement::set_close_watcher() // https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-focusing-steps void HTMLDialogElement::run_dialog_focusing_steps() { - // 1. Let control be null + // 1. If the allow focus steps given subject's node document return false, then return. + if (!document().allow_focus()) + return; + + // 2. Let control be null GC::Ptr control = nullptr; - // FIXME 2. If subject has the autofocus attribute, then set control to subject. - // FIXME 3. If control is null, then set control to the focus delegate of subject. + // FIXME 3. If subject has the autofocus attribute, then set control to subject. + // FIXME 4. If control is null, then set control to the focus delegate of subject. - // 4. If control is null, then set control to subject. + // 5. If control is null, then set control to subject. if (!control) control = this; - // 5. Run the focusing steps for control. + // 6. Run the focusing steps for control. run_focusing_steps(control); + + // 7. Let topDocument be control's node navigable's top-level traversable's active document. + auto top_document = control->navigable()->top_level_traversable()->active_document(); + + // 8. If control's node document's origin is not the same as the origin of topDocument, then return. + if (!control->document().origin().is_same_origin(top_document->origin())) + return; + + // FIXME: 9. Empty topDocument's autofocus candidates. + // FIXME: 10. Set topDocument's autofocus processed flag to true. } } diff --git a/Libraries/LibWeb/HTML/HTMLOrSVGElement.cpp b/Libraries/LibWeb/HTML/HTMLOrSVGElement.cpp index 9170ffbb44b..825fd3935d6 100644 --- a/Libraries/LibWeb/HTML/HTMLOrSVGElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLOrSVGElement.cpp @@ -26,24 +26,28 @@ GC::Ref HTMLOrSVGElement::dataset() template void HTMLOrSVGElement::focus() { - // FIXME: below are the focus(options) steps, also implement focus() + // 1. If the allow focus steps given the element's node document return false, then return. + if (!static_cast(this)->document().allow_focus()) + return; - // 1. If the element is marked as locked for focus, then return. + // 2. If the element is marked as locked for focus, then return. if (m_locked_for_focus) return; - // 2. Mark the element as locked for focus. + // 3. Mark the element as locked for focus. m_locked_for_focus = true; - // 3. Run the focusing steps for the element. + // 4. Run the focusing steps for the element. run_focusing_steps(static_cast(this)); - // FIXME: 4. If the value of the preventScroll dictionary member of options is false, + // FIXME: 5. If the value of the focusVisible dictionary member of options is true, or is not present but in an implementation-defined way the user agent determines it would be best to do so, then indicate focus. + + // FIXME: 6. If the value of the preventScroll dictionary member of options is false, // then scroll the element into view with scroll behavior "auto", // block flow direction position set to an implementation-defined value, // and inline base direction position set to an implementation-defined value. - // 5. Unmark the element as locked for focus. + // 7. Unmark the element as locked for focus. m_locked_for_focus = false; } diff --git a/Libraries/LibWeb/HTML/Window.cpp b/Libraries/LibWeb/HTML/Window.cpp index 98398578c71..8f805567d51 100644 --- a/Libraries/LibWeb/HTML/Window.cpp +++ b/Libraries/LibWeb/HTML/Window.cpp @@ -910,12 +910,16 @@ void Window::focus() if (!current) return; - // 3. Run the focusing steps with current. + // 3. If the allow focus steps given current's active document return false, then return. + if (!document()->allow_focus()) + return; + + // 4. Run the focusing steps with current. // FIXME: We should pass in the browsing context itself instead of the active document, however the focusing steps don't currently accept browsing contexts. // Passing in a browsing context always makes it resolve to its active document for focus, so this is fine for now. run_focusing_steps(current->active_document()); - // FIXME: 4. If current is a top-level traversable, user agents are encouraged to trigger some sort of notification to + // FIXME: 5. If current is a top-level traversable, user agents are encouraged to trigger some sort of notification to // indicate to the user that the page is attempting to gain focus. }