mirror of
https://github.com/RGBCube/serenity
synced 2025-05-25 10:45:07 +00:00
LibWeb: Implement a slow but functional HTMLCollection :^)
HTMLCollection is an awkward legacy interface from the DOM spec. It provides a live view of a DOM subtree, with some kind of filtering that determines which elements are part of the collection. We now return HTMLCollection objects from these APIs: - getElementsByClassName() - getElementsByName() - getElementsByTagName() This initial implementation does not do any kind of caching, since that is quite a tricky problem, and there will be plenty of time for tricky problems later on when the engine is more mature.
This commit is contained in:
parent
49f3d88baf
commit
e4df1b223f
14 changed files with 207 additions and 55 deletions
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/ScopeGuard.h>
|
||||||
|
#include <LibWeb/Bindings/HTMLCollectionWrapper.h>
|
||||||
|
#include <LibWeb/Bindings/NodeWrapper.h>
|
||||||
|
#include <LibWeb/Bindings/NodeWrapperFactory.h>
|
||||||
|
#include <LibWeb/DOM/Element.h>
|
||||||
|
|
||||||
|
namespace Web::Bindings {
|
||||||
|
|
||||||
|
JS::Value HTMLCollectionWrapper::get(JS::PropertyName const& name, JS::Value receiver, bool without_side_effects) const
|
||||||
|
{
|
||||||
|
auto* item = const_cast<DOM::HTMLCollection&>(impl()).named_item(name.to_string());
|
||||||
|
if (!item)
|
||||||
|
return Base::get(name, receiver, without_side_effects);
|
||||||
|
return JS::Value { wrap(global_object(), *item) };
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::Value HTMLCollectionWrapper::get_by_index(u32 property_index) const
|
||||||
|
{
|
||||||
|
auto* item = const_cast<DOM::HTMLCollection&>(impl()).item(property_index);
|
||||||
|
if (!item)
|
||||||
|
return Base::get_by_index(property_index);
|
||||||
|
return wrap(global_object(), *item);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ set(SOURCES
|
||||||
Bindings/EventListenerWrapper.cpp
|
Bindings/EventListenerWrapper.cpp
|
||||||
Bindings/EventWrapperFactory.cpp
|
Bindings/EventWrapperFactory.cpp
|
||||||
Bindings/EventTargetWrapperFactory.cpp
|
Bindings/EventTargetWrapperFactory.cpp
|
||||||
|
Bindings/HTMLCollectionWrapperCustom.cpp
|
||||||
Bindings/ImageConstructor.cpp
|
Bindings/ImageConstructor.cpp
|
||||||
Bindings/LocationObject.cpp
|
Bindings/LocationObject.cpp
|
||||||
Bindings/MainThreadVM.cpp
|
Bindings/MainThreadVM.cpp
|
||||||
|
@ -48,6 +49,7 @@ set(SOURCES
|
||||||
DOM/Element.cpp
|
DOM/Element.cpp
|
||||||
DOM/ElementFactory.cpp
|
DOM/ElementFactory.cpp
|
||||||
DOM/Event.cpp
|
DOM/Event.cpp
|
||||||
|
DOM/HTMLCollection.cpp
|
||||||
DOM/Range.cpp
|
DOM/Range.cpp
|
||||||
DOM/EventDispatcher.cpp
|
DOM/EventDispatcher.cpp
|
||||||
DOM/EventListener.cpp
|
DOM/EventListener.cpp
|
||||||
|
@ -309,6 +311,7 @@ libweb_js_wrapper(DOM/DOMImplementation)
|
||||||
libweb_js_wrapper(DOM/Element)
|
libweb_js_wrapper(DOM/Element)
|
||||||
libweb_js_wrapper(DOM/Event)
|
libweb_js_wrapper(DOM/Event)
|
||||||
libweb_js_wrapper(DOM/EventTarget)
|
libweb_js_wrapper(DOM/EventTarget)
|
||||||
|
libweb_js_wrapper(DOM/HTMLCollection)
|
||||||
libweb_js_wrapper(DOM/ProcessingInstruction)
|
libweb_js_wrapper(DOM/ProcessingInstruction)
|
||||||
libweb_js_wrapper(DOM/ShadowRoot)
|
libweb_js_wrapper(DOM/ShadowRoot)
|
||||||
libweb_js_wrapper(DOM/Node)
|
libweb_js_wrapper(DOM/Node)
|
||||||
|
|
|
@ -850,6 +850,7 @@ void generate_implementation(const IDL::Interface& interface)
|
||||||
#include <LibWeb/Bindings/EventTargetWrapperFactory.h>
|
#include <LibWeb/Bindings/EventTargetWrapperFactory.h>
|
||||||
#include <LibWeb/Bindings/EventWrapperFactory.h>
|
#include <LibWeb/Bindings/EventWrapperFactory.h>
|
||||||
#include <LibWeb/Bindings/HTMLCanvasElementWrapper.h>
|
#include <LibWeb/Bindings/HTMLCanvasElementWrapper.h>
|
||||||
|
#include <LibWeb/Bindings/HTMLCollectionWrapper.h>
|
||||||
#include <LibWeb/Bindings/HTMLFormElementWrapper.h>
|
#include <LibWeb/Bindings/HTMLFormElementWrapper.h>
|
||||||
#include <LibWeb/Bindings/HTMLHeadElementWrapper.h>
|
#include <LibWeb/Bindings/HTMLHeadElementWrapper.h>
|
||||||
#include <LibWeb/Bindings/HTMLImageElementWrapper.h>
|
#include <LibWeb/Bindings/HTMLImageElementWrapper.h>
|
||||||
|
@ -1191,6 +1192,7 @@ void generate_prototype_implementation(const IDL::Interface& interface)
|
||||||
#include <LibWeb/Bindings/EventWrapperFactory.h>
|
#include <LibWeb/Bindings/EventWrapperFactory.h>
|
||||||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||||
#include <LibWeb/Bindings/HTMLCanvasElementWrapper.h>
|
#include <LibWeb/Bindings/HTMLCanvasElementWrapper.h>
|
||||||
|
#include <LibWeb/Bindings/HTMLCollectionWrapper.h>
|
||||||
#include <LibWeb/Bindings/HTMLFormElementWrapper.h>
|
#include <LibWeb/Bindings/HTMLFormElementWrapper.h>
|
||||||
#include <LibWeb/Bindings/HTMLHeadElementWrapper.h>
|
#include <LibWeb/Bindings/HTMLHeadElementWrapper.h>
|
||||||
#include <LibWeb/Bindings/HTMLImageElementWrapper.h>
|
#include <LibWeb/Bindings/HTMLImageElementWrapper.h>
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <LibWeb/DOM/ElementFactory.h>
|
#include <LibWeb/DOM/ElementFactory.h>
|
||||||
#include <LibWeb/DOM/Event.h>
|
#include <LibWeb/DOM/Event.h>
|
||||||
#include <LibWeb/DOM/ExceptionOr.h>
|
#include <LibWeb/DOM/ExceptionOr.h>
|
||||||
|
#include <LibWeb/DOM/HTMLCollection.h>
|
||||||
#include <LibWeb/DOM/Range.h>
|
#include <LibWeb/DOM/Range.h>
|
||||||
#include <LibWeb/DOM/ShadowRoot.h>
|
#include <LibWeb/DOM/ShadowRoot.h>
|
||||||
#include <LibWeb/DOM/Text.h>
|
#include <LibWeb/DOM/Text.h>
|
||||||
|
@ -484,42 +485,29 @@ void Document::set_hovered_node(Node* node)
|
||||||
invalidate_style();
|
invalidate_style();
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtrVector<Element> Document::get_elements_by_name(const String& name) const
|
NonnullRefPtr<HTMLCollection> Document::get_elements_by_name(String const& name)
|
||||||
{
|
{
|
||||||
NonnullRefPtrVector<Element> elements;
|
return HTMLCollection::create(*this, [name](Element const& element) {
|
||||||
for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
|
return element.name() == name;
|
||||||
if (element.attribute(HTML::AttributeNames::name) == name)
|
|
||||||
elements.append(element);
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
});
|
});
|
||||||
return elements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtrVector<Element> Document::get_elements_by_tag_name(const FlyString& tag_name) const
|
NonnullRefPtr<HTMLCollection> Document::get_elements_by_tag_name(FlyString const& tag_name)
|
||||||
{
|
{
|
||||||
// FIXME: Support "*" for tag_name
|
// FIXME: Support "*" for tag_name
|
||||||
// https://dom.spec.whatwg.org/#concept-getelementsbytagname
|
// https://dom.spec.whatwg.org/#concept-getelementsbytagname
|
||||||
NonnullRefPtrVector<Element> elements;
|
return HTMLCollection::create(*this, [tag_name](Element const& element) {
|
||||||
for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
|
if (element.namespace_() == Namespace::HTML)
|
||||||
if (element.namespace_() == Namespace::HTML
|
return element.local_name().to_lowercase() == tag_name.to_lowercase();
|
||||||
? element.local_name().to_lowercase() == tag_name.to_lowercase()
|
return element.local_name() == tag_name;
|
||||||
: element.local_name() == tag_name) {
|
|
||||||
elements.append(element);
|
|
||||||
}
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
});
|
});
|
||||||
return elements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtrVector<Element> Document::get_elements_by_class_name(const FlyString& class_name) const
|
NonnullRefPtr<HTMLCollection> Document::get_elements_by_class_name(FlyString const& class_name)
|
||||||
{
|
{
|
||||||
NonnullRefPtrVector<Element> elements;
|
return HTMLCollection::create(*this, [class_name, quirks_mode = document().in_quirks_mode()](Element const& element) {
|
||||||
for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
|
return element.has_class(class_name, quirks_mode ? CaseSensitivity::CaseInsensitive : CaseSensitivity::CaseSensitive);
|
||||||
if (element.has_class(class_name, in_quirks_mode() ? CaseSensitivity::CaseInsensitive : CaseSensitivity::CaseSensitive))
|
|
||||||
elements.append(element);
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
});
|
});
|
||||||
return elements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Color Document::link_color() const
|
Color Document::link_color() const
|
||||||
|
|
|
@ -136,9 +136,9 @@ public:
|
||||||
void schedule_style_update();
|
void schedule_style_update();
|
||||||
void schedule_forced_layout();
|
void schedule_forced_layout();
|
||||||
|
|
||||||
NonnullRefPtrVector<Element> get_elements_by_name(const String&) const;
|
NonnullRefPtr<HTMLCollection> get_elements_by_name(String const&);
|
||||||
NonnullRefPtrVector<Element> get_elements_by_tag_name(const FlyString&) const;
|
NonnullRefPtr<HTMLCollection> get_elements_by_tag_name(FlyString const&);
|
||||||
NonnullRefPtrVector<Element> get_elements_by_class_name(const FlyString&) const;
|
NonnullRefPtr<HTMLCollection> get_elements_by_class_name(FlyString const&);
|
||||||
|
|
||||||
const String& source() const { return m_source; }
|
const String& source() const { return m_source; }
|
||||||
void set_source(const String& source) { m_source = source; }
|
void set_source(const String& source) { m_source = source; }
|
||||||
|
|
|
@ -14,9 +14,9 @@ interface Document : Node {
|
||||||
attribute DOMString cookie;
|
attribute DOMString cookie;
|
||||||
|
|
||||||
Element? getElementById(DOMString id);
|
Element? getElementById(DOMString id);
|
||||||
ArrayFromVector getElementsByName(DOMString name);
|
HTMLCollection getElementsByName(DOMString name);
|
||||||
ArrayFromVector getElementsByTagName(DOMString tagName);
|
HTMLCollection getElementsByTagName(DOMString tagName);
|
||||||
ArrayFromVector getElementsByClassName(DOMString className);
|
HTMLCollection getElementsByClassName(DOMString className);
|
||||||
|
|
||||||
Element createElement(DOMString tagName);
|
Element createElement(DOMString tagName);
|
||||||
Element createElementNS(DOMString? namespace, DOMString qualifiedName);
|
Element createElementNS(DOMString? namespace, DOMString qualifiedName);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -14,6 +14,7 @@
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/DOM/Element.h>
|
#include <LibWeb/DOM/Element.h>
|
||||||
#include <LibWeb/DOM/ExceptionOr.h>
|
#include <LibWeb/DOM/ExceptionOr.h>
|
||||||
|
#include <LibWeb/DOM/HTMLCollection.h>
|
||||||
#include <LibWeb/DOM/ShadowRoot.h>
|
#include <LibWeb/DOM/ShadowRoot.h>
|
||||||
#include <LibWeb/DOM/Text.h>
|
#include <LibWeb/DOM/Text.h>
|
||||||
#include <LibWeb/HTML/Parser/HTMLDocumentParser.h>
|
#include <LibWeb/HTML/Parser/HTMLDocumentParser.h>
|
||||||
|
@ -325,31 +326,22 @@ bool Element::is_focused() const
|
||||||
return document().focused_element() == this;
|
return document().focused_element() == this;
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtrVector<Element> Element::get_elements_by_tag_name(const FlyString& tag_name) const
|
NonnullRefPtr<HTMLCollection> Element::get_elements_by_tag_name(FlyString const& tag_name)
|
||||||
{
|
{
|
||||||
// FIXME: Support "*" for tag_name
|
// FIXME: Support "*" for tag_name
|
||||||
// https://dom.spec.whatwg.org/#concept-getelementsbytagname
|
// https://dom.spec.whatwg.org/#concept-getelementsbytagname
|
||||||
NonnullRefPtrVector<Element> elements;
|
return HTMLCollection::create(*this, [tag_name](Element const& element) {
|
||||||
for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
|
if (element.namespace_() == Namespace::HTML)
|
||||||
if (element.namespace_() == Namespace::HTML
|
return element.local_name().to_lowercase() == tag_name.to_lowercase();
|
||||||
? element.local_name().to_lowercase() == tag_name.to_lowercase()
|
return element.local_name() == tag_name;
|
||||||
: element.local_name() == tag_name) {
|
|
||||||
elements.append(element);
|
|
||||||
}
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
});
|
});
|
||||||
return elements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtrVector<Element> Element::get_elements_by_class_name(const FlyString& class_name) const
|
NonnullRefPtr<HTMLCollection> Element::get_elements_by_class_name(FlyString const& class_name)
|
||||||
{
|
{
|
||||||
NonnullRefPtrVector<Element> elements;
|
return HTMLCollection::create(*this, [class_name, quirks_mode = document().in_quirks_mode()](Element const& element) {
|
||||||
for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
|
return element.has_class(class_name, quirks_mode ? CaseSensitivity::CaseInsensitive : CaseSensitivity::CaseSensitive);
|
||||||
if (element.has_class(class_name, m_document->in_quirks_mode() ? CaseSensitivity::CaseInsensitive : CaseSensitivity::CaseSensitive))
|
|
||||||
elements.append(element);
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
});
|
});
|
||||||
return elements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::set_shadow_root(RefPtr<ShadowRoot> shadow_root)
|
void Element::set_shadow_root(RefPtr<ShadowRoot> shadow_root)
|
||||||
|
|
|
@ -82,8 +82,8 @@ public:
|
||||||
bool is_focused() const;
|
bool is_focused() const;
|
||||||
virtual bool is_focusable() const { return false; }
|
virtual bool is_focusable() const { return false; }
|
||||||
|
|
||||||
NonnullRefPtrVector<Element> get_elements_by_tag_name(const FlyString&) const;
|
NonnullRefPtr<HTMLCollection> get_elements_by_tag_name(FlyString const&);
|
||||||
NonnullRefPtrVector<Element> get_elements_by_class_name(const FlyString&) const;
|
NonnullRefPtr<HTMLCollection> get_elements_by_class_name(FlyString const&);
|
||||||
|
|
||||||
ShadowRoot* shadow_root() { return m_shadow_root; }
|
ShadowRoot* shadow_root() { return m_shadow_root; }
|
||||||
const ShadowRoot* shadow_root() const { return m_shadow_root; }
|
const ShadowRoot* shadow_root() const { return m_shadow_root; }
|
||||||
|
|
|
@ -8,8 +8,8 @@ interface Element : Node {
|
||||||
boolean hasAttribute(DOMString qualifiedName);
|
boolean hasAttribute(DOMString qualifiedName);
|
||||||
boolean hasAttributes();
|
boolean hasAttributes();
|
||||||
|
|
||||||
ArrayFromVector getElementsByTagName(DOMString tagName);
|
HTMLCollection getElementsByTagName(DOMString tagName);
|
||||||
ArrayFromVector getElementsByClassName(DOMString className);
|
HTMLCollection getElementsByClassName(DOMString className);
|
||||||
|
|
||||||
[LegacyNullToEmptyString] attribute DOMString innerHTML;
|
[LegacyNullToEmptyString] attribute DOMString innerHTML;
|
||||||
[Reflect] attribute DOMString id;
|
[Reflect] attribute DOMString id;
|
||||||
|
|
61
Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp
Normal file
61
Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibWeb/DOM/Element.h>
|
||||||
|
#include <LibWeb/DOM/HTMLCollection.h>
|
||||||
|
#include <LibWeb/DOM/ParentNode.h>
|
||||||
|
|
||||||
|
namespace Web::DOM {
|
||||||
|
|
||||||
|
HTMLCollection::HTMLCollection(ParentNode& root, Function<bool(Element const&)> filter)
|
||||||
|
: m_root(root)
|
||||||
|
, m_filter(move(filter))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLCollection::~HTMLCollection()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<NonnullRefPtr<Element>> HTMLCollection::collect_matching_elements()
|
||||||
|
{
|
||||||
|
Vector<NonnullRefPtr<Element>> elements;
|
||||||
|
m_root->for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
|
||||||
|
if (m_filter(element))
|
||||||
|
elements.append(element);
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HTMLCollection::length()
|
||||||
|
{
|
||||||
|
return collect_matching_elements().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
Element* HTMLCollection::item(size_t index)
|
||||||
|
{
|
||||||
|
auto elements = collect_matching_elements();
|
||||||
|
if (index >= elements.size())
|
||||||
|
return nullptr;
|
||||||
|
return elements[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Element* HTMLCollection::named_item(FlyString const& name)
|
||||||
|
{
|
||||||
|
if (name.is_null())
|
||||||
|
return nullptr;
|
||||||
|
auto elements = collect_matching_elements();
|
||||||
|
// First look for an "id" attribute match
|
||||||
|
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())
|
||||||
|
return *it;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
Userland/Libraries/LibWeb/DOM/HTMLCollection.h
Normal file
64
Userland/Libraries/LibWeb/DOM/HTMLCollection.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/FlyString.h>
|
||||||
|
#include <AK/Function.h>
|
||||||
|
#include <AK/Noncopyable.h>
|
||||||
|
#include <LibWeb/Bindings/Wrappable.h>
|
||||||
|
#include <LibWeb/Forward.h>
|
||||||
|
|
||||||
|
namespace Web::DOM {
|
||||||
|
|
||||||
|
// NOTE: HTMLCollection is in the DOM namespace because it's part of the DOM specification.
|
||||||
|
|
||||||
|
// This class implements a live, filtered view of a DOM subtree.
|
||||||
|
// When constructing an HTMLCollection, you provide a root node + a filter.
|
||||||
|
// The filter is a simple Function object that answers the question
|
||||||
|
// "is this Element part of the collection?"
|
||||||
|
|
||||||
|
// FIXME: HTMLCollection currently does no caching. It will re-filter on every access!
|
||||||
|
// We should teach it how to cache results. The main challenge is invalidating
|
||||||
|
// these caches, since this needs to happen on various kinds of DOM mutation.
|
||||||
|
|
||||||
|
class HTMLCollection
|
||||||
|
: public RefCounted<HTMLCollection>
|
||||||
|
, public Bindings::Wrappable {
|
||||||
|
AK_MAKE_NONCOPYABLE(HTMLCollection);
|
||||||
|
AK_MAKE_NONMOVABLE(HTMLCollection);
|
||||||
|
|
||||||
|
public:
|
||||||
|
using WrapperType = Bindings::HTMLCollectionWrapper;
|
||||||
|
|
||||||
|
static NonnullRefPtr<HTMLCollection> create(ParentNode& root, Function<bool(Element const&)> filter)
|
||||||
|
{
|
||||||
|
return adopt(*new HTMLCollection(root, move(filter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
~HTMLCollection();
|
||||||
|
|
||||||
|
size_t length();
|
||||||
|
Element* item(size_t index);
|
||||||
|
Element* named_item(FlyString const& name);
|
||||||
|
|
||||||
|
Vector<NonnullRefPtr<Element>> collect_matching_elements();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HTMLCollection(ParentNode& root, Function<bool(Element const&)> filter);
|
||||||
|
|
||||||
|
private:
|
||||||
|
NonnullRefPtr<ParentNode> m_root;
|
||||||
|
Function<bool(Element const&)> m_filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Web::Bindings {
|
||||||
|
|
||||||
|
HTMLCollectionWrapper* wrap(JS::GlobalObject&, DOM::HTMLCollection&);
|
||||||
|
|
||||||
|
}
|
8
Userland/Libraries/LibWeb/DOM/HTMLCollection.idl
Normal file
8
Userland/Libraries/LibWeb/DOM/HTMLCollection.idl
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[CustomGet,CustomGetByIndex]
|
||||||
|
interface HTMLCollection {
|
||||||
|
|
||||||
|
readonly attribute unsigned long length;
|
||||||
|
Element? item(unsigned long index);
|
||||||
|
Element? namedItem(DOMString name);
|
||||||
|
|
||||||
|
};
|
|
@ -42,6 +42,7 @@ class Event;
|
||||||
class EventHandler;
|
class EventHandler;
|
||||||
class EventListener;
|
class EventListener;
|
||||||
class EventTarget;
|
class EventTarget;
|
||||||
|
class HTMLCollection;
|
||||||
class MouseEvent;
|
class MouseEvent;
|
||||||
class Node;
|
class Node;
|
||||||
class ParentNode;
|
class ParentNode;
|
||||||
|
@ -218,6 +219,7 @@ class HTMLBodyElementWrapper;
|
||||||
class HTMLBRElementWrapper;
|
class HTMLBRElementWrapper;
|
||||||
class HTMLButtonElementWrapper;
|
class HTMLButtonElementWrapper;
|
||||||
class HTMLCanvasElementWrapper;
|
class HTMLCanvasElementWrapper;
|
||||||
|
class HTMLCollectionWrapper;
|
||||||
class HTMLDataElementWrapper;
|
class HTMLDataElementWrapper;
|
||||||
class HTMLDataListElementWrapper;
|
class HTMLDataListElementWrapper;
|
||||||
class HTMLDetailsElementWrapper;
|
class HTMLDetailsElementWrapper;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/DOM/Event.h>
|
#include <LibWeb/DOM/Event.h>
|
||||||
|
#include <LibWeb/DOM/HTMLCollection.h>
|
||||||
#include <LibWeb/DOM/Window.h>
|
#include <LibWeb/DOM/Window.h>
|
||||||
#include <LibWeb/HTML/HTMLAnchorElement.h>
|
#include <LibWeb/HTML/HTMLAnchorElement.h>
|
||||||
#include <LibWeb/InProcessWebView.h>
|
#include <LibWeb/InProcessWebView.h>
|
||||||
|
@ -161,9 +162,9 @@ void Frame::scroll_to_anchor(const String& fragment)
|
||||||
auto element = document()->get_element_by_id(fragment);
|
auto element = document()->get_element_by_id(fragment);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
auto candidates = document()->get_elements_by_name(fragment);
|
auto candidates = document()->get_elements_by_name(fragment);
|
||||||
for (auto& candidate : candidates) {
|
for (auto& candidate : candidates->collect_matching_elements()) {
|
||||||
if (is<HTML::HTMLAnchorElement>(candidate)) {
|
if (is<HTML::HTMLAnchorElement>(*candidate)) {
|
||||||
element = downcast<HTML::HTMLAnchorElement>(candidate);
|
element = downcast<HTML::HTMLAnchorElement>(*candidate);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue