From 5949e3c3e8580e0341f2adefbc483ada7fb83186 Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Tue, 19 Sep 2023 16:40:42 -0600 Subject: [PATCH] LibWeb: Implement named property access AOs on Window These allow accessing embeds, forms, images and objects with a given name attribute, and any element with a given id attribute, as top level properties on the global object. It also allows accessing NavigableContainers by target name as top level properties on the global object. The current implementation feels very expensive. It's likely that these values will need smarter caching in the future. --- .../HTML/Window-named-properties-elements.txt | 6 + .../Window-named-properties-elements.html | 46 ++++++++ Userland/Libraries/LibWeb/HTML/Window.cpp | 108 +++++++++++++++++- Userland/Libraries/LibWeb/HTML/Window.h | 6 + 4 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/HTML/Window-named-properties-elements.txt create mode 100644 Tests/LibWeb/Text/input/HTML/Window-named-properties-elements.html diff --git a/Tests/LibWeb/Text/expected/HTML/Window-named-properties-elements.txt b/Tests/LibWeb/Text/expected/HTML/Window-named-properties-elements.txt new file mode 100644 index 0000000000..f6d3e3948c --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/Window-named-properties-elements.txt @@ -0,0 +1,6 @@ + george true +true +true +true +true +true \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/HTML/Window-named-properties-elements.html b/Tests/LibWeb/Text/input/HTML/Window-named-properties-elements.html new file mode 100644 index 0000000000..4428576c3a --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/Window-named-properties-elements.html @@ -0,0 +1,46 @@ + + + +
+ +
+
+
+
+ diff --git a/Userland/Libraries/LibWeb/HTML/Window.cpp b/Userland/Libraries/LibWeb/HTML/Window.cpp index 35dd0768af..8432e5c93b 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.cpp +++ b/Userland/Libraries/LibWeb/HTML/Window.cpp @@ -28,14 +28,20 @@ #include #include #include +#include #include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include #include #include #include @@ -1440,13 +1446,111 @@ OrderedHashMap> Window::document_tree_child_ // https://html.spec.whatwg.org/#named-access-on-the-window-object Vector Window::supported_property_names() { - return {}; + // The Window object supports named properties. + // The supported property names of a Window object window at any moment consist of the following, + // in tree order according to the element that contributed them, ignoring later duplicates: + + HashTable property_names; + + // - window's document-tree child navigable target name property set; + auto child_navigable_property_set = document_tree_child_navigable_target_name_property_set(); + for (auto& entry : child_navigable_property_set) + property_names.set(entry.key, AK::HashSetExistingEntryBehavior::Keep); + + // - the value of the name content attribute for all embed, form, img, and object elements + // that have a non-empty name content attribute and are in a document tree with window's associated Document as their root; and + // - the value of the id content attribute for all HTML elements that have a non-empty id content attribute + // and are in a document tree with window's associated Document as their root. + associated_document().for_each_in_subtree_of_type([&property_names](auto& element) -> IterationDecision { + if (is(element) || is(element) || is(element) || is(element)) { + if (auto const& name = element.attribute(AttributeNames::name); name.has_value()) + property_names.set(name.value(), AK::HashSetExistingEntryBehavior::Keep); + } + if (auto const& name = element.attribute(AttributeNames::id); name.has_value()) + property_names.set(name.value(), AK::HashSetExistingEntryBehavior::Keep); + return IterationDecision::Continue; + }); + + // FIXME: Many copies here. Would be nice to be able to return Vector + Vector names; + names.ensure_capacity(property_names.size()); + for (auto const& name : property_names) { + names.append(name.to_deprecated_string()); + } + return names; } // https://html.spec.whatwg.org/#named-access-on-the-window-object WebIDL::ExceptionOr Window::named_item_value(DeprecatedFlyString const& name) { - return JS::js_undefined(); + // To determine the value of a named property name in a Window object window, the user agent must return the value obtained using the following steps: + + // 1. Let objects be the list of named objects of window with the name name. + // NOTE: There will be at least one such object, since the algorithm would otherwise not have been invoked by Web IDL. + auto objects = named_objects(name); + + // 2. If objects contains a navigable, then: + if (!objects.navigables.is_empty()) { + // 1. Let container be the first navigable container in window's associated Document's descendants whose content navigable is in objects. + JS::GCPtr container = nullptr; + associated_document().for_each_in_subtree_of_type([&](HTML::NavigableContainer& navigable_container) { + if (!navigable_container.content_navigable()) + return IterationDecision::Continue; + if (objects.navigables.contains_slow(JS::NonnullGCPtr { *navigable_container.content_navigable() })) { + container = navigable_container; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + // 2. Return container's content navigable's active WindowProxy. + VERIFY(container); + return container->content_navigable()->active_window_proxy(); + } + + // 3. Otherwise, if objects has only one element, return that element. + if (objects.elements.size() == 1) + return objects.elements[0]; + + // 4. Otherwise return an HTMLCollection rooted at window's associated Document, + // whose filter matches only named objects of window with the name name. (By definition, these will all be elements.) + return DOM::HTMLCollection::create(associated_document(), DOM::HTMLCollection::Scope::Descendants, [name](auto& element) -> bool { + if ((is(element) || is(element) || is(element) || is(element)) + && (element.attribute(AttributeNames::name) == name.view())) + return true; + return element.attribute(AttributeNames::id) == name.view(); + }); +} + +// https://html.spec.whatwg.org/#dom-window-nameditem-filter +Window::NamedObjects Window::named_objects(StringView name) +{ + // NOTE: Since the Window interface has the [Global] extended attribute, its named properties + // follow the rules for named properties objects rather than legacy platform objects. + + // Named objects of Window object window with the name name, for the purposes of the above algorithm, consist of the following: + NamedObjects objects; + + // document-tree child navigables of window's associated Document whose target name is name; + auto children = associated_document().document_tree_child_navigables(); + for (auto& navigable : children) { + if (navigable->target_name() == name) { + objects.navigables.append(*navigable); + } + } + + // embed, form, img, or object elements that have a name content attribute whose value is name + // and are in a document tree with window's associated Document as their root; and + // HTML elements that have an id content attribute whose value is name and are in a document tree with window's associated Document as their root. + associated_document().for_each_in_subtree_of_type([&objects, &name](auto& element) -> IterationDecision { + if ((is(element) || is(element) || is(element) || is(element)) + && (element.attribute(AttributeNames::name) == name)) + objects.elements.append(element); + else if (element.attribute(AttributeNames::id) == name) + objects.elements.append(element); + return IterationDecision::Continue; + }); + + return objects; } } diff --git a/Userland/Libraries/LibWeb/HTML/Window.h b/Userland/Libraries/LibWeb/HTML/Window.h index c67e02a9d3..5ce5347e0f 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.h +++ b/Userland/Libraries/LibWeb/HTML/Window.h @@ -209,6 +209,12 @@ private: void invoke_idle_callbacks(); + struct [[nodiscard]] NamedObjects { + Vector> navigables; + Vector> elements; + }; + NamedObjects named_objects(StringView name); + // https://html.spec.whatwg.org/multipage/window-object.html#concept-document-window JS::GCPtr m_associated_document;