diff --git a/Userland/Libraries/LibWeb/Bindings/HTMLCollectionWrapperCustom.cpp b/Userland/Libraries/LibWeb/Bindings/HTMLCollectionWrapperCustom.cpp deleted file mode 100644 index 1d4f92551f..0000000000 --- a/Userland/Libraries/LibWeb/Bindings/HTMLCollectionWrapperCustom.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Web::Bindings { - -JS::Value HTMLCollectionWrapper::internal_get(JS::PropertyName const& property_name, JS::Value receiver) const -{ - if (property_name.is_symbol()) - return Base::internal_get(property_name, receiver); - DOM::Element* item = nullptr; - if (property_name.is_string()) - item = const_cast(impl()).named_item(property_name.to_string()); - else if (property_name.is_number()) - item = const_cast(impl()).item(property_name.as_number()); - if (!item) - return Base::internal_get(property_name, receiver); - return wrap(global_object(), *item); -} - -} diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h index 7427e0f604..04096c8ffc 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h @@ -56,6 +56,8 @@ #include #include #include +#include +#include #include #include #include @@ -278,6 +280,7 @@ ADD_WINDOW_OBJECT_INTERFACE(HTMLBRElement) \ ADD_WINDOW_OBJECT_INTERFACE(HTMLButtonElement) \ ADD_WINDOW_OBJECT_INTERFACE(HTMLCanvasElement) \ + ADD_WINDOW_OBJECT_INTERFACE(HTMLCollection) \ ADD_WINDOW_OBJECT_INTERFACE(HTMLDataElement) \ ADD_WINDOW_OBJECT_INTERFACE(HTMLDataListElement) \ ADD_WINDOW_OBJECT_INTERFACE(HTMLDetailsElement) \ diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 3e09f4beb6..80ebbac1d9 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -3,7 +3,6 @@ set(SOURCES Bindings/EventListenerWrapper.cpp Bindings/EventTargetWrapperFactory.cpp Bindings/EventWrapperFactory.cpp - Bindings/HTMLCollectionWrapperCustom.cpp Bindings/IDLAbstractOperations.cpp Bindings/ImageConstructor.cpp Bindings/LocationObject.cpp diff --git a/Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp b/Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp index 86b26a087c..64520f439e 100644 --- a/Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp +++ b/Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Andreas Kling + * Copyright (c) 2021, Luke Wilde * * SPDX-License-Identifier: BSD-2-Clause */ @@ -7,6 +8,7 @@ #include #include #include +#include namespace Web::DOM { @@ -20,7 +22,7 @@ HTMLCollection::~HTMLCollection() { } -Vector> HTMLCollection::collect_matching_elements() +Vector> HTMLCollection::collect_matching_elements() const { Vector> elements; m_root->for_each_in_inclusive_subtree_of_type([&](auto& element) { @@ -31,31 +33,82 @@ Vector> HTMLCollection::collect_matching_elements() return elements; } +// https://dom.spec.whatwg.org/#dom-htmlcollection-length size_t HTMLCollection::length() { + // The length getter steps are to return the number of nodes represented by the collection. return collect_matching_elements().size(); } -Element* HTMLCollection::item(size_t index) +// https://dom.spec.whatwg.org/#dom-htmlcollection-item +Element* HTMLCollection::item(size_t index) const { + // The item(index) method steps are to return the indexth element in the collection. If there is no indexth element in the collection, then the method must return null. auto elements = collect_matching_elements(); if (index >= elements.size()) return nullptr; return elements[index]; } -Element* HTMLCollection::named_item(FlyString const& name) +// https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem-key +Element* HTMLCollection::named_item(FlyString const& name) const { - if (name.is_null()) + // 1. If key is the empty string, return null. + if (name.is_empty()) return nullptr; auto elements = collect_matching_elements(); - // First look for an "id" attribute match + // 2. Return the first element in the collection for which at least one of the following is true: + // - it has an ID which is key; if (auto it = elements.find_if([&](auto& entry) { return entry->attribute(HTML::AttributeNames::id) == name; }); it != elements.end()) return *it; - // Then look for a "name" attribute match - if (auto it = elements.find_if([&](auto& entry) { return entry->name() == name; }); it != elements.end()) + // - it is in the HTML namespace and has a name attribute whose value is key; + if (auto it = elements.find_if([&](auto& entry) { return entry->namespace_() == Namespace::HTML && entry->name() == name; }); it != elements.end()) return *it; + // or null if there is no such element. return nullptr; } +// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-names +Vector HTMLCollection::supported_property_names() const +{ + // 1. Let result be an empty list. + Vector result; + + // 2. For each element represented by the collection, in tree order: + auto elements = collect_matching_elements(); + + for (auto& element : elements) { + // 1. If element has an ID which is not in result, append element’s ID to result. + if (element->has_attribute(HTML::AttributeNames::id)) { + auto id = element->attribute(HTML::AttributeNames::id); + + if (!result.contains_slow(id)) + result.append(id); + } + + // 2. If element is in the HTML namespace and has a name attribute whose value is neither the empty string nor is in result, append element’s name attribute value to result. + if (element->namespace_() == Namespace::HTML && element->has_attribute(HTML::AttributeNames::name)) { + auto name = element->attribute(HTML::AttributeNames::name); + + if (!name.is_empty() && !result.contains_slow(name)) + result.append(name); + } + } + + // 3. Return result. + return result; +} + +// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-indices%E2%91%A1 +bool HTMLCollection::is_supported_property_index(u32 index) const +{ + // The object’s supported property indices are the numbers in the range zero to one less than the number of elements represented by the collection. + // If there are no such elements, then there are no supported property indices. + auto elements = collect_matching_elements(); + if (elements.is_empty()) + return false; + + return index < elements.size(); +} + } diff --git a/Userland/Libraries/LibWeb/DOM/HTMLCollection.h b/Userland/Libraries/LibWeb/DOM/HTMLCollection.h index 8ee6c69d53..999b3f9e72 100644 --- a/Userland/Libraries/LibWeb/DOM/HTMLCollection.h +++ b/Userland/Libraries/LibWeb/DOM/HTMLCollection.h @@ -42,10 +42,13 @@ public: ~HTMLCollection(); size_t length(); - Element* item(size_t index); - Element* named_item(FlyString const& name); + Element* item(size_t index) const; + Element* named_item(FlyString const& name) const; - Vector> collect_matching_elements(); + Vector> collect_matching_elements() const; + + Vector supported_property_names() const; + bool is_supported_property_index(u32) const; protected: HTMLCollection(ParentNode& root, Function filter); diff --git a/Userland/Libraries/LibWeb/DOM/HTMLCollection.idl b/Userland/Libraries/LibWeb/DOM/HTMLCollection.idl index 73bcf070f1..ab641f6bf6 100644 --- a/Userland/Libraries/LibWeb/DOM/HTMLCollection.idl +++ b/Userland/Libraries/LibWeb/DOM/HTMLCollection.idl @@ -1,8 +1,8 @@ -[CustomGet] +[Exposed=Window, LegacyUnenumerableNamedProperties] interface HTMLCollection { readonly attribute unsigned long length; - Element? item(unsigned long index); - Element? namedItem(DOMString name); + getter Element? item(unsigned long index); + getter Element? namedItem(DOMString name); };