Browse Source

LibWeb: Implement HTMLFormControlsCollection

This collection is used in the form element for when element lookup
yields multiple elements.
Shannon Booth 1 year ago
parent
commit
27dd2a40ad

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -153,6 +153,7 @@ set(SOURCES
     DOM/EventDispatcher.cpp
     DOM/EventTarget.cpp
     DOM/HTMLCollection.cpp
+    DOM/HTMLFormControlsCollection.cpp
     DOM/IDLEventListener.cpp
     DOM/LiveNodeList.cpp
     DOM/MutationObserver.cpp

+ 78 - 0
Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.cpp

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/HTMLCollection.h>
+#include <LibWeb/DOM/HTMLFormControlsCollection.h>
+#include <LibWeb/DOM/ParentNode.h>
+
+namespace Web::DOM {
+
+JS::NonnullGCPtr<HTMLFormControlsCollection> HTMLFormControlsCollection::create(ParentNode& root, Scope scope, Function<bool(Element const&)> filter)
+{
+    return root.heap().allocate<HTMLFormControlsCollection>(root.realm(), root, scope, move(filter));
+}
+
+HTMLFormControlsCollection::HTMLFormControlsCollection(ParentNode& root, Scope scope, Function<bool(Element const&)> filter)
+    : HTMLCollection(root, scope, move(filter))
+{
+}
+
+HTMLFormControlsCollection::~HTMLFormControlsCollection() = default;
+
+void HTMLFormControlsCollection::initialize(JS::Realm& realm)
+{
+    Base::initialize(realm);
+    set_prototype(&Bindings::ensure_web_prototype<Bindings::HTMLFormControlsCollectionPrototype>(realm, "HTMLFormControlsCollection"));
+}
+
+// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmlformcontrolscollection-nameditem
+Variant<Empty, Element*, JS::Handle<RadioNodeList>> HTMLFormControlsCollection::named_item_or_radio_node_list(FlyString const& name)
+{
+    // 1. If name is the empty string, return null and stop the algorithm.
+    if (name.is_empty())
+        return {};
+
+    auto const deprecated_name = name.to_deprecated_fly_string();
+
+    // 2. If, at the time the method is called, there is exactly one node in the collection that has either an id attribute or a name attribute equal to name, then return that node and stop the algorithm.
+    // 3. Otherwise, if there are no nodes in the collection that have either an id attribute or a name attribute equal to name, then return null and stop the algorithm.
+    Element* matching_element = nullptr;
+    bool multiple_matching = false;
+
+    auto collection = collect_matching_elements();
+    for (auto const& element : collection) {
+        if (element->attribute(HTML::AttributeNames::id) != deprecated_name && element->name() != deprecated_name)
+            continue;
+
+        if (matching_element) {
+            multiple_matching = true;
+            break;
+        }
+
+        matching_element = element;
+    }
+
+    if (!matching_element)
+        return {};
+
+    if (!multiple_matching)
+        return matching_element;
+
+    // 4. Otherwise, create a new RadioNodeList object representing a live view of the HTMLFormControlsCollection object, further filtered so that the only nodes in the
+    //    RadioNodeList object are those that have either an id attribute or a name attribute equal to name. The nodes in the RadioNodeList object must be sorted in tree
+    //    order. Return that RadioNodeList object.
+    return JS::make_handle(RadioNodeList::create(realm(), root(), LiveNodeList::Scope::Descendants, [deprecated_name](Node const& node) {
+        if (!is<Element>(node))
+            return false;
+
+        auto const& element = verify_cast<Element>(node);
+        return element.attribute(HTML::AttributeNames::id) == deprecated_name || element.name() == deprecated_name;
+    }));
+}
+
+}

+ 31 - 0
Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.h

@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/HTMLCollection.h>
+#include <LibWeb/DOM/RadioNodeList.h>
+
+namespace Web::DOM {
+
+class HTMLFormControlsCollection : public HTMLCollection {
+    WEB_PLATFORM_OBJECT(HTMLFormControlsCollection, HTMLCollection);
+
+public:
+    [[nodiscard]] static JS::NonnullGCPtr<HTMLFormControlsCollection> create(ParentNode& root, Scope, Function<bool(Element const&)> filter);
+
+    virtual ~HTMLFormControlsCollection() override;
+
+    Variant<Empty, Element*, JS::Handle<RadioNodeList>> named_item_or_radio_node_list(FlyString const& name);
+
+protected:
+    virtual void initialize(JS::Realm&) override;
+
+private:
+    HTMLFormControlsCollection(ParentNode& root, Scope, Function<bool(Element const&)> filter);
+};
+
+}

+ 5 - 0
Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.idl

@@ -0,0 +1,5 @@
+// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#htmlformcontrolscollection
+[Exposed=Window, UseNewAKString]
+interface HTMLFormControlsCollection : HTMLCollection {
+      [ImplementedAs=named_item_or_radio_node_list] getter (RadioNodeList or Element)? namedItem(DOMString name); // shadows inherited namedItem()
+};

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -225,6 +225,7 @@ class Event;
 class EventHandler;
 class EventTarget;
 class HTMLCollection;
+class HTMLFormControlsCollection;
 class IDLEventListener;
 class LiveNodeList;
 class MutationObserver;

+ 2 - 1
Userland/Libraries/LibWeb/idl_files.cmake

@@ -42,8 +42,9 @@ libweb_js_bindings(DOM/Element)
 libweb_js_bindings(DOM/Event)
 libweb_js_bindings(DOM/EventTarget)
 libweb_js_bindings(DOM/HTMLCollection)
-libweb_js_bindings(DOM/MutationRecord)
+libweb_js_bindings(DOM/HTMLFormControlsCollection)
 libweb_js_bindings(DOM/MutationObserver)
+libweb_js_bindings(DOM/MutationRecord)
 libweb_js_bindings(DOM/NamedNodeMap)
 libweb_js_bindings(DOM/Node)
 libweb_js_bindings(DOM/NodeFilter)