diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp index f0a877a84b..bd25760637 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -9,12 +9,15 @@ #include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -225,7 +228,7 @@ void HTMLElement::parse_attribute(FlyString const& name, String const& value) } // https://html.spec.whatwg.org/multipage/interaction.html#focus-update-steps -static void run_focus_update_steps(Vector> old_chain, Vector> new_chain, DOM::Node& new_focus_target) +static void run_focus_update_steps(Vector> old_chain, Vector> new_chain, DOM::Node* new_focus_target) { // 1. If the last entry in old chain and the last entry in new chain are the same, // pop the last entry from old chain and the last entry from new chain and redo this step. @@ -405,7 +408,83 @@ static void run_focusing_steps(DOM::Node* new_focus_target, DOM::Node* fallback_ auto new_chain = focus_chain(new_focus_target); // 8. Run the focus update steps with old chain, new chain, and new focus target respectively. - run_focus_update_steps(old_chain, new_chain, *new_focus_target); + run_focus_update_steps(old_chain, new_chain, new_focus_target); +} + +// https://html.spec.whatwg.org/multipage/interaction.html#unfocusing-steps +static void run_unfocusing_steps(DOM::Node* old_focus_target) +{ + // NOTE: The unfocusing steps do not always result in the focus changing, even when applied to the currently focused + // area of a top-level browsing context. For example, if the currently focused area of a top-level browsing context + // is a viewport, then it will usually keep its focus regardless until another focusable area is explicitly focused + // with the focusing steps. + + auto is_shadow_host = [](DOM::Node* node) { + return is(node) && static_cast(node)->shadow_root() != nullptr; + }; + + // 1. If old focus target is a shadow host whose shadow root's delegates focus is true, and old focus target's + // shadow root is a shadow-including inclusive ancestor of the currently focused area of a top-level browsing + // context's DOM anchor, then set old focus target to that currently focused area of a top-level browsing + // context. + if (is_shadow_host(old_focus_target)) { + auto* shadow_root = static_cast(old_focus_target)->shadow_root(); + if (shadow_root->delegates_focus()) { + auto& top_level_browsing_context = old_focus_target->document().browsing_context()->top_level_browsing_context(); + if (auto currently_focused_area = top_level_browsing_context.currently_focused_area()) { + if (shadow_root->is_shadow_including_ancestor_of(*currently_focused_area)) { + old_focus_target = currently_focused_area; + } + } + } + } + + // FIXME: 2. If old focus target is inert, then return. + + // FIXME: 3. If old focus target is an area element and one of its shapes is the currently focused area of a + // top-level browsing context, or, if old focus target is an element with one or more scrollable regions, and one + // of them is the currently focused area of a top-level browsing context, then let old focus target be that + // currently focused area of a top-level browsing context. + + // NOTE: HTMLAreaElement is currently missing the shapes property + + auto& top_level_browsing_context = old_focus_target->document().browsing_context()->top_level_browsing_context(); + + // 4. Let old chain be the current focus chain of the top-level browsing context in which old focus target finds itself. + auto old_chain = focus_chain(top_level_browsing_context.currently_focused_area()); + + // 5. If old focus target is not one of the entries in old chain, then return. + for (auto& node : old_chain) { + if (old_focus_target != node) { + return; + } + } + + // 6. If old focus target is not a focusable area, then return. + if (!old_focus_target->is_focusable()) + return; + + // 7. Let topDocument be old chain's last entry. + auto* top_document = verify_cast(old_chain.last().ptr()); + + // 8. If topDocument's browsing context has system focus, then run the focusing steps for topDocument's viewport. + if (top_document->browsing_context()->system_visibility_state() == VisibilityState::Visible) { + // FIXME: run the focusing steps for topDocument's viewport (??) + } else { + // FIXME: Otherwise, apply any relevant platform-specific conventions for removing system focus from + // topDocument's browsing context, and run the focus update steps with old chain, an empty list, and null + // respectively. + + // What? It already doesn't have system focus, what possible platform-specific conventions are there? + + run_focus_update_steps(old_chain, {}, nullptr); + } + + // FIXME: When the currently focused area of a top-level browsing context is somehow unfocused without another + // element being explicitly focused in its stead, the user agent must immediately run the unfocusing steps for that + // object. + + // What? How are we supposed to detect when something is "somehow unfocused without another element being explicitly focused"? } // https://html.spec.whatwg.org/multipage/interaction.html#dom-focus @@ -480,4 +559,13 @@ void HTMLElement::click() m_click_in_progress = false; } +// https://html.spec.whatwg.org/multipage/interaction.html#dom-blur +void HTMLElement::blur() +{ + // The blur() method, when invoked, should run the unfocusing steps for the element on which the method was called. + run_unfocusing_steps(this); + + // User agents may selectively or uniformly ignore calls to this method for usability reasons. +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.h b/Userland/Libraries/LibWeb/HTML/HTMLElement.h index 68fdb4c1ff..72097c8462 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.h @@ -44,6 +44,8 @@ public: void click(); + void blur(); + bool fire_a_synthetic_pointer_event(FlyString const& type, DOM::Element& target, bool not_trusted); // https://html.spec.whatwg.org/multipage/forms.html#category-label diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl index 9abdbdd538..81ac8c4597 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl @@ -15,6 +15,8 @@ interface HTMLElement : Element { // FIXME: Support the optional FocusOptions parameter. undefined focus(); + undefined blur(); + [LegacyNullToEmptyString] attribute DOMString innerText; readonly attribute long offsetTop;