diff options
author | Andrew Kaster <akaster@serenityos.org> | 2022-10-02 14:42:47 -0600 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-10-07 21:17:50 +0100 |
commit | a0d5724a58a0ce130a4712fc4a8bfa4b9636fd3b (patch) | |
tree | d747ecd220c6aaba5ea7914fa1bec84c23ca3217 /Userland | |
parent | f08a979b96e92680dd0305e6d9b9d979d1e33602 (diff) | |
download | serenity-a0d5724a58a0ce130a4712fc4a8bfa4b9636fd3b.zip |
LibWeb: Add initial implementation of Element.blur()
This implementation includes a first cut at run the unfocusing steps
from the spec, with many things left unimplemented.
The viewport related spec steps in particular don't seem to map to
LibWeb concepts, which makes figuring out if things are properly focused
much more difficult.
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLElement.cpp | 92 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLElement.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLElement.idl | 2 |
3 files changed, 94 insertions, 2 deletions
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 <LibJS/Parser.h> #include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/IDLEventListener.h> +#include <LibWeb/DOM/ShadowRoot.h> #include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/HTML/BrowsingContextContainer.h> #include <LibWeb/HTML/EventHandler.h> #include <LibWeb/HTML/HTMLAnchorElement.h> +#include <LibWeb/HTML/HTMLAreaElement.h> #include <LibWeb/HTML/HTMLBodyElement.h> #include <LibWeb/HTML/HTMLElement.h> +#include <LibWeb/HTML/VisibilityState.h> #include <LibWeb/HTML/Window.h> #include <LibWeb/Layout/Box.h> #include <LibWeb/Layout/BreakNode.h> @@ -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<JS::Handle<DOM::Node>> old_chain, Vector<JS::Handle<DOM::Node>> new_chain, DOM::Node& new_focus_target) +static void run_focus_update_steps(Vector<JS::Handle<DOM::Node>> old_chain, Vector<JS::Handle<DOM::Node>> 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<DOM::Element>(node) && static_cast<DOM::Element*>(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<DOM::Element*>(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<DOM::Document>(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; |