diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 423da95def..db29e53eb1 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/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 diff --git a/Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.cpp b/Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.cpp new file mode 100644 index 0000000000..4fb6064482 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023, Shannon Booth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace Web::DOM { + +JS::NonnullGCPtr HTMLFormControlsCollection::create(ParentNode& root, Scope scope, Function filter) +{ + return root.heap().allocate(root.realm(), root, scope, move(filter)); +} + +HTMLFormControlsCollection::HTMLFormControlsCollection(ParentNode& root, Scope scope, Function filter) + : HTMLCollection(root, scope, move(filter)) +{ +} + +HTMLFormControlsCollection::~HTMLFormControlsCollection() = default; + +void HTMLFormControlsCollection::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + set_prototype(&Bindings::ensure_web_prototype(realm, "HTMLFormControlsCollection")); +} + +// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmlformcontrolscollection-nameditem +Variant> 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(node)) + return false; + + auto const& element = verify_cast(node); + return element.attribute(HTML::AttributeNames::id) == deprecated_name || element.name() == deprecated_name; + })); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.h b/Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.h new file mode 100644 index 0000000000..7fd1e36fac --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023, Shannon Booth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::DOM { + +class HTMLFormControlsCollection : public HTMLCollection { + WEB_PLATFORM_OBJECT(HTMLFormControlsCollection, HTMLCollection); + +public: + [[nodiscard]] static JS::NonnullGCPtr create(ParentNode& root, Scope, Function filter); + + virtual ~HTMLFormControlsCollection() override; + + Variant> named_item_or_radio_node_list(FlyString const& name); + +protected: + virtual void initialize(JS::Realm&) override; + +private: + HTMLFormControlsCollection(ParentNode& root, Scope, Function filter); +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.idl b/Userland/Libraries/LibWeb/DOM/HTMLFormControlsCollection.idl new file mode 100644 index 0000000000..7002b20759 --- /dev/null +++ b/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() +}; diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index de79e66af1..d0ccd64194 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/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; diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index 8f6693fa3c..0bdb3ea689 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/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)