mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 15:27:35 +00:00
LibWeb+LibJS: Make the EventTarget hierarchy (incl. DOM) GC-allocated
This is a monster patch that turns all EventTargets into GC-allocated PlatformObjects. Their C++ wrapper classes are removed, and the LibJS garbage collector is now responsible for their lifetimes. There's a fair amount of hacks and band-aids in this patch, and we'll have a lot of cleanup to do after this.
This commit is contained in:
parent
bb547ce1c4
commit
6f433c8656
445 changed files with 4797 additions and 4268 deletions
|
@ -10,8 +10,8 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-abortcontroller-abortcontroller
|
||||
AbortController::AbortController()
|
||||
: m_signal(AbortSignal::create())
|
||||
AbortController::AbortController(HTML::Window& window)
|
||||
: m_signal(JS::make_handle(*AbortSignal::create_with_global_object(window)))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/Weakable.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/Bindings/Wrappable.h>
|
||||
#include <LibWeb/DOM/AbortSignal.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
@ -24,28 +23,23 @@ class AbortController final
|
|||
public:
|
||||
using WrapperType = Bindings::AbortControllerWrapper;
|
||||
|
||||
static NonnullRefPtr<AbortController> create()
|
||||
static NonnullRefPtr<AbortController> create_with_global_object(HTML::Window& window)
|
||||
{
|
||||
return adopt_ref(*new AbortController());
|
||||
}
|
||||
|
||||
static NonnullRefPtr<AbortController> create_with_global_object(Bindings::WindowObject&)
|
||||
{
|
||||
return AbortController::create();
|
||||
return adopt_ref(*new AbortController(window));
|
||||
}
|
||||
|
||||
virtual ~AbortController() override = default;
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-abortcontroller-signal
|
||||
NonnullRefPtr<AbortSignal> signal() const { return m_signal; }
|
||||
JS::NonnullGCPtr<AbortSignal> signal() const { return *m_signal; }
|
||||
|
||||
void abort(JS::Value reason);
|
||||
|
||||
private:
|
||||
AbortController();
|
||||
explicit AbortController(HTML::Window&);
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortcontroller-signal
|
||||
NonnullRefPtr<AbortSignal> m_signal;
|
||||
JS::Handle<AbortSignal> m_signal;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/AbortSignalWrapper.h>
|
||||
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
|
||||
#include <LibWeb/Bindings/Wrapper.h>
|
||||
#include <LibWeb/DOM/AbortSignal.h>
|
||||
|
@ -14,14 +13,14 @@
|
|||
|
||||
namespace Web::DOM {
|
||||
|
||||
AbortSignal::AbortSignal()
|
||||
: EventTarget()
|
||||
JS::NonnullGCPtr<AbortSignal> AbortSignal::create_with_global_object(HTML::Window& window)
|
||||
{
|
||||
return *window.heap().allocate<AbortSignal>(window.realm(), window);
|
||||
}
|
||||
|
||||
JS::Object* AbortSignal::create_wrapper(JS::Realm& realm)
|
||||
AbortSignal::AbortSignal(HTML::Window& window)
|
||||
: EventTarget(window.realm())
|
||||
{
|
||||
return wrap(realm, *this);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal-add
|
||||
|
@ -38,10 +37,6 @@ void AbortSignal::add_abort_algorithm(Function<void()> abort_algorithm)
|
|||
// https://dom.spec.whatwg.org/#abortsignal-signal-abort
|
||||
void AbortSignal::signal_abort(JS::Value reason)
|
||||
{
|
||||
VERIFY(wrapper());
|
||||
auto& vm = wrapper()->vm();
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. If signal is aborted, then return.
|
||||
if (aborted())
|
||||
return;
|
||||
|
@ -50,7 +45,7 @@ void AbortSignal::signal_abort(JS::Value reason)
|
|||
if (!reason.is_undefined())
|
||||
m_abort_reason = reason;
|
||||
else
|
||||
m_abort_reason = wrap(realm, AbortError::create("Aborted without reason"));
|
||||
m_abort_reason = wrap(realm(), AbortError::create("Aborted without reason"));
|
||||
|
||||
// 3. For each algorithm in signal’s abort algorithms: run algorithm.
|
||||
for (auto& algorithm : m_abort_algorithms)
|
||||
|
@ -60,7 +55,7 @@ void AbortSignal::signal_abort(JS::Value reason)
|
|||
m_abort_algorithms.clear();
|
||||
|
||||
// 5. Fire an event named abort at signal.
|
||||
dispatch_event(*Event::create(verify_cast<Bindings::WindowObject>(wrapper()->global_object()), HTML::EventNames::abort));
|
||||
dispatch_event(*Event::create(global_object(), HTML::EventNames::abort));
|
||||
}
|
||||
|
||||
void AbortSignal::set_onabort(Bindings::CallbackType* event_handler)
|
||||
|
@ -85,6 +80,7 @@ JS::ThrowCompletionOr<void> AbortSignal::throw_if_aborted() const
|
|||
|
||||
void AbortSignal::visit_edges(JS::Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_abort_reason);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,26 +16,11 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal
|
||||
class AbortSignal final
|
||||
: public RefCounted<AbortSignal>
|
||||
, public Weakable<AbortSignal>
|
||||
, public EventTarget
|
||||
, public Bindings::Wrappable {
|
||||
class AbortSignal final : public EventTarget {
|
||||
WEB_PLATFORM_OBJECT(AbortSignal, EventTarget);
|
||||
|
||||
public:
|
||||
using WrapperType = Bindings::AbortSignalWrapper;
|
||||
|
||||
using RefCounted::ref;
|
||||
using RefCounted::unref;
|
||||
|
||||
static NonnullRefPtr<AbortSignal> create()
|
||||
{
|
||||
return adopt_ref(*new AbortSignal());
|
||||
}
|
||||
|
||||
static NonnullRefPtr<AbortSignal> create_with_global_object(Bindings::WindowObject&)
|
||||
{
|
||||
return AbortSignal::create();
|
||||
}
|
||||
static JS::NonnullGCPtr<AbortSignal> create_with_global_object(HTML::Window&);
|
||||
|
||||
virtual ~AbortSignal() override = default;
|
||||
|
||||
|
@ -55,15 +40,10 @@ public:
|
|||
|
||||
JS::ThrowCompletionOr<void> throw_if_aborted() const;
|
||||
|
||||
void visit_edges(JS::Cell::Visitor&);
|
||||
|
||||
// ^EventTarget
|
||||
virtual void ref_event_target() override { ref(); }
|
||||
virtual void unref_event_target() override { unref(); }
|
||||
virtual JS::Object* create_wrapper(JS::Realm&) override;
|
||||
|
||||
private:
|
||||
AbortSignal();
|
||||
explicit AbortSignal(HTML::Window&);
|
||||
|
||||
virtual void visit_edges(JS::Cell::Visitor&) override;
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal-abort-reason
|
||||
// An AbortSignal object has an associated abort reason, which is a JavaScript value. It is undefined unless specified otherwise.
|
||||
|
@ -75,3 +55,5 @@ private:
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(AbortSignal, Web::DOM)
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
*/
|
||||
|
||||
#include <LibWeb/Bindings/AbstractRangePrototype.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/AbstractRange.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
AbstractRange::AbstractRange(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset)
|
||||
: Bindings::PlatformObject(start_container.document().preferred_window_object().ensure_web_prototype<Bindings::AbstractRangePrototype>("AbstractRange"))
|
||||
: Bindings::PlatformObject(start_container.document().window().ensure_web_prototype<Bindings::AbstractRangePrototype>("AbstractRange"))
|
||||
, m_start_container(start_container)
|
||||
, m_start_offset(start_offset)
|
||||
, m_end_container(end_container)
|
||||
|
@ -22,4 +22,11 @@ AbstractRange::AbstractRange(Node& start_container, u32 start_offset, Node& end_
|
|||
|
||||
AbstractRange::~AbstractRange() = default;
|
||||
|
||||
void AbstractRange::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_start_container.ptr());
|
||||
visitor.visit(m_end_container.ptr());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,19 +13,17 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
class AbstractRange : public Bindings::PlatformObject {
|
||||
JS_OBJECT(AbstractRange, Bindings::PlatformObject);
|
||||
WEB_PLATFORM_OBJECT(AbstractRange, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
virtual ~AbstractRange() override;
|
||||
|
||||
AbstractRange& impl() { return *this; }
|
||||
|
||||
Node* start_container() { return m_start_container; }
|
||||
Node const* start_container() const { return m_start_container; }
|
||||
Node* start_container() { return m_start_container.ptr(); }
|
||||
Node const* start_container() const { return m_start_container.ptr(); }
|
||||
unsigned start_offset() const { return m_start_offset; }
|
||||
|
||||
Node* end_container() { return m_end_container; }
|
||||
Node const* end_container() const { return m_end_container; }
|
||||
Node* end_container() { return m_end_container.ptr(); }
|
||||
Node const* end_container() const { return m_end_container.ptr(); }
|
||||
unsigned end_offset() const { return m_end_offset; }
|
||||
|
||||
// https://dom.spec.whatwg.org/#range-collapsed
|
||||
|
@ -38,10 +36,12 @@ public:
|
|||
protected:
|
||||
AbstractRange(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset);
|
||||
|
||||
NonnullRefPtr<Node> m_start_container;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
JS::NonnullGCPtr<Node> m_start_container;
|
||||
u32 m_start_offset;
|
||||
|
||||
NonnullRefPtr<Node> m_end_container;
|
||||
JS::NonnullGCPtr<Node> m_end_container;
|
||||
u32 m_end_offset;
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/AttributePrototype.h>
|
||||
#include <LibWeb/DOM/Attribute.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
|
@ -12,9 +13,9 @@
|
|||
|
||||
namespace Web::DOM {
|
||||
|
||||
NonnullRefPtr<Attribute> Attribute::create(Document& document, FlyString local_name, String value, Element const* owner_element)
|
||||
JS::NonnullGCPtr<Attribute> Attribute::create(Document& document, FlyString local_name, String value, Element const* owner_element)
|
||||
{
|
||||
return adopt_ref(*new Attribute(document, move(local_name), move(value), owner_element));
|
||||
return *document.heap().allocate<Attribute>(document.realm(), document, move(local_name), move(value), owner_element);
|
||||
}
|
||||
|
||||
Attribute::Attribute(Document& document, FlyString local_name, String value, Element const* owner_element)
|
||||
|
@ -23,16 +24,23 @@ Attribute::Attribute(Document& document, FlyString local_name, String value, Ele
|
|||
, m_value(move(value))
|
||||
, m_owner_element(owner_element)
|
||||
{
|
||||
set_prototype(&window().ensure_web_prototype<Bindings::AttributePrototype>("Attribute"));
|
||||
}
|
||||
|
||||
void Attribute::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_owner_element.ptr());
|
||||
}
|
||||
|
||||
Element* Attribute::owner_element()
|
||||
{
|
||||
return m_owner_element;
|
||||
return m_owner_element.ptr();
|
||||
}
|
||||
|
||||
Element const* Attribute::owner_element() const
|
||||
{
|
||||
return m_owner_element;
|
||||
return m_owner_element.ptr();
|
||||
}
|
||||
|
||||
void Attribute::set_owner_element(Element const* owner_element)
|
||||
|
|
|
@ -15,10 +15,10 @@ namespace Web::DOM {
|
|||
|
||||
// https://dom.spec.whatwg.org/#attr
|
||||
class Attribute final : public Node {
|
||||
public:
|
||||
using WrapperType = Bindings::AttributeWrapper;
|
||||
WEB_PLATFORM_OBJECT(Attribute, Node);
|
||||
|
||||
static NonnullRefPtr<Attribute> create(Document&, FlyString local_name, String value, Element const* = nullptr);
|
||||
public:
|
||||
static JS::NonnullGCPtr<Attribute> create(Document&, FlyString local_name, String value, Element const* = nullptr);
|
||||
|
||||
virtual ~Attribute() override = default;
|
||||
|
||||
|
@ -44,12 +44,16 @@ public:
|
|||
private:
|
||||
Attribute(Document&, FlyString local_name, String value, Element const*);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
QualifiedName m_qualified_name;
|
||||
String m_value;
|
||||
WeakPtr<Element> m_owner_element;
|
||||
JS::GCPtr<Element> m_owner_element;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool Node::fast_is<Attribute>() const { return is_attribute(); }
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(Attribute, Web::DOM)
|
||||
|
|
|
@ -4,17 +4,18 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/CDATASectionPrototype.h>
|
||||
#include <LibWeb/DOM/CDATASection.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
CDATASection::CDATASection(Document& document, String const& data)
|
||||
: Text(document, NodeType::CDATA_SECTION_NODE, data)
|
||||
{
|
||||
set_prototype(&window().ensure_web_prototype<Bindings::CDATASectionPrototype>("CDATASection"));
|
||||
}
|
||||
|
||||
CDATASection::~CDATASection()
|
||||
{
|
||||
}
|
||||
CDATASection::~CDATASection() = default;
|
||||
|
||||
}
|
||||
|
|
|
@ -12,17 +12,21 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
class CDATASection final : public Text {
|
||||
public:
|
||||
using WrapperType = Bindings::CDATASectionWrapper;
|
||||
WEB_PLATFORM_OBJECT(Text, CDATASection);
|
||||
|
||||
CDATASection(Document&, String const&);
|
||||
public:
|
||||
virtual ~CDATASection() override;
|
||||
|
||||
// ^Node
|
||||
virtual FlyString node_name() const override { return "#cdata-section"; }
|
||||
|
||||
private:
|
||||
CDATASection(Document&, String const&);
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool Node::fast_is<CDATASection>() const { return is_cdata_section(); }
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(CDATASection, Web::DOM)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/CharacterDataPrototype.h>
|
||||
#include <LibWeb/DOM/CharacterData.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/MutationType.h>
|
||||
|
@ -16,6 +17,7 @@ CharacterData::CharacterData(Document& document, NodeType type, String const& da
|
|||
: Node(document, type)
|
||||
, m_data(data)
|
||||
{
|
||||
set_prototype(&window().ensure_web_prototype<Bindings::CharacterDataPrototype>("CharacterData"));
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-characterdata-data
|
||||
|
|
|
@ -17,9 +17,9 @@ class CharacterData
|
|||
: public Node
|
||||
, public ChildNode<CharacterData>
|
||||
, public NonDocumentTypeChildNode<CharacterData> {
|
||||
public:
|
||||
using WrapperType = Bindings::CharacterDataWrapper;
|
||||
WEB_PLATFORM_OBJECT(CharacterData, Node);
|
||||
|
||||
public:
|
||||
virtual ~CharacterData() override = default;
|
||||
|
||||
String const& data() const { return m_data; }
|
||||
|
@ -41,3 +41,4 @@ private:
|
|||
};
|
||||
|
||||
}
|
||||
WRAPPER_HACK(CharacterData, Web::DOM)
|
||||
|
|
|
@ -16,7 +16,7 @@ template<typename NodeType>
|
|||
class ChildNode {
|
||||
public:
|
||||
// https://dom.spec.whatwg.org/#dom-childnode-before
|
||||
ExceptionOr<void> before(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
|
||||
ExceptionOr<void> before(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
||||
{
|
||||
auto* node = static_cast<NodeType*>(this);
|
||||
|
||||
|
@ -46,7 +46,7 @@ public:
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-childnode-after
|
||||
ExceptionOr<void> after(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
|
||||
ExceptionOr<void> after(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
||||
{
|
||||
auto* node = static_cast<NodeType*>(this);
|
||||
|
||||
|
@ -70,7 +70,7 @@ public:
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-childnode-replacewith
|
||||
ExceptionOr<void> replace_with(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
|
||||
ExceptionOr<void> replace_with(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
||||
{
|
||||
auto* node = static_cast<NodeType*>(this);
|
||||
|
||||
|
@ -117,7 +117,7 @@ protected:
|
|||
ChildNode() = default;
|
||||
|
||||
private:
|
||||
RefPtr<Node> viable_previous_sibling_for_insertion(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes) const
|
||||
JS::GCPtr<Node> viable_previous_sibling_for_insertion(Vector<Variant<JS::Handle<Node>, String>> const& nodes) const
|
||||
{
|
||||
auto* node = static_cast<NodeType const*>(this);
|
||||
|
||||
|
@ -125,11 +125,11 @@ private:
|
|||
bool contained_in_nodes = false;
|
||||
|
||||
for (auto const& node_or_string : nodes) {
|
||||
if (!node_or_string.template has<NonnullRefPtr<Node>>())
|
||||
if (!node_or_string.template has<JS::Handle<Node>>())
|
||||
continue;
|
||||
|
||||
auto node_in_vector = node_or_string.template get<NonnullRefPtr<Node>>();
|
||||
if (node_in_vector.ptr() == previous_sibling) {
|
||||
auto node_in_vector = node_or_string.template get<JS::Handle<Node>>();
|
||||
if (node_in_vector.cell() == previous_sibling) {
|
||||
contained_in_nodes = true;
|
||||
break;
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ private:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Node> viable_nest_sibling_for_insertion(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes) const
|
||||
JS::GCPtr<Node> viable_nest_sibling_for_insertion(Vector<Variant<JS::Handle<Node>, String>> const& nodes) const
|
||||
{
|
||||
auto* node = static_cast<NodeType const*>(this);
|
||||
|
||||
|
@ -150,11 +150,11 @@ private:
|
|||
bool contained_in_nodes = false;
|
||||
|
||||
for (auto const& node_or_string : nodes) {
|
||||
if (!node_or_string.template has<NonnullRefPtr<Node>>())
|
||||
if (!node_or_string.template has<JS::Handle<Node>>())
|
||||
continue;
|
||||
|
||||
auto node_in_vector = node_or_string.template get<NonnullRefPtr<Node>>();
|
||||
if (node_in_vector.ptr() == next_sibling) {
|
||||
auto& node_in_vector = node_or_string.template get<JS::Handle<Node>>();
|
||||
if (node_in_vector.cell() == next_sibling) {
|
||||
contained_in_nodes = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@ Comment::Comment(Document& document, String const& data)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-comment-comment
|
||||
NonnullRefPtr<Comment> Comment::create_with_global_object(Bindings::WindowObject& window, String const& data)
|
||||
JS::NonnullGCPtr<Comment> Comment::create_with_global_object(HTML::Window& window, String const& data)
|
||||
{
|
||||
return make_ref_counted<Comment>(window.impl().associated_document(), data);
|
||||
return *window.heap().allocate<Comment>(window.realm(), window.associated_document(), data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,18 +12,21 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
class Comment final : public CharacterData {
|
||||
public:
|
||||
using WrapperType = Bindings::CommentWrapper;
|
||||
WEB_PLATFORM_OBJECT(Comment, CharacterData);
|
||||
|
||||
explicit Comment(Document&, String const&);
|
||||
public:
|
||||
static JS::NonnullGCPtr<Comment> create_with_global_object(HTML::Window&, String const& data);
|
||||
virtual ~Comment() override = default;
|
||||
|
||||
virtual FlyString node_name() const override { return "#comment"; }
|
||||
|
||||
static NonnullRefPtr<Comment> create_with_global_object(Bindings::WindowObject& window, String const& data);
|
||||
private:
|
||||
explicit Comment(Document&, String const&);
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool Node::fast_is<Comment>() const { return is_comment(); }
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(Comment, Web::DOM)
|
||||
|
|
|
@ -6,28 +6,28 @@
|
|||
*/
|
||||
|
||||
#include <LibWeb/Bindings/CustomEventPrototype.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/CustomEvent.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
CustomEvent* CustomEvent::create(Bindings::WindowObject& window_object, FlyString const& event_name, CustomEventInit const& event_init)
|
||||
CustomEvent* CustomEvent::create(HTML::Window& window_object, FlyString const& event_name, CustomEventInit const& event_init)
|
||||
{
|
||||
return window_object.heap().allocate<CustomEvent>(window_object.realm(), window_object, event_name, event_init);
|
||||
}
|
||||
|
||||
CustomEvent* CustomEvent::create_with_global_object(Bindings::WindowObject& window_object, FlyString const& event_name, CustomEventInit const& event_init)
|
||||
CustomEvent* CustomEvent::create_with_global_object(HTML::Window& window_object, FlyString const& event_name, CustomEventInit const& event_init)
|
||||
{
|
||||
return create(window_object, event_name, event_init);
|
||||
}
|
||||
|
||||
CustomEvent::CustomEvent(Bindings::WindowObject& window_object, FlyString const& event_name)
|
||||
CustomEvent::CustomEvent(HTML::Window& window_object, FlyString const& event_name)
|
||||
: Event(window_object, event_name)
|
||||
{
|
||||
set_prototype(&window_object.ensure_web_prototype<Bindings::CustomEventPrototype>("CustomEvent"));
|
||||
}
|
||||
|
||||
CustomEvent::CustomEvent(Bindings::WindowObject& window_object, FlyString const& event_name, CustomEventInit const& event_init)
|
||||
CustomEvent::CustomEvent(HTML::Window& window_object, FlyString const& event_name, CustomEventInit const& event_init)
|
||||
: Event(window_object, event_name, event_init)
|
||||
, m_detail(event_init.detail)
|
||||
{
|
||||
|
|
|
@ -17,19 +17,17 @@ struct CustomEventInit : public EventInit {
|
|||
|
||||
// https://dom.spec.whatwg.org/#customevent
|
||||
class CustomEvent : public Event {
|
||||
JS_OBJECT(CustomEvent, Event);
|
||||
WEB_PLATFORM_OBJECT(CustomEvent, Event);
|
||||
|
||||
public:
|
||||
static CustomEvent* create(Bindings::WindowObject&, FlyString const& event_name, CustomEventInit const& event_init = {});
|
||||
static CustomEvent* create_with_global_object(Bindings::WindowObject&, FlyString const& event_name, CustomEventInit const& event_init);
|
||||
static CustomEvent* create(HTML::Window&, FlyString const& event_name, CustomEventInit const& event_init = {});
|
||||
static CustomEvent* create_with_global_object(HTML::Window&, FlyString const& event_name, CustomEventInit const& event_init);
|
||||
|
||||
CustomEvent(Bindings::WindowObject&, FlyString const& event_name);
|
||||
CustomEvent(Bindings::WindowObject&, FlyString const& event_name, CustomEventInit const& event_init);
|
||||
CustomEvent(HTML::Window&, FlyString const& event_name);
|
||||
CustomEvent(HTML::Window&, FlyString const& event_name, CustomEventInit const& event_init);
|
||||
|
||||
virtual ~CustomEvent() override;
|
||||
|
||||
CustomEvent& impl() { return *this; }
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-customevent-detail
|
||||
JS::Value detail() const { return m_detail; }
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ void DOMEventListener::visit_edges(Cell::Visitor& visitor)
|
|||
{
|
||||
Cell::visit_edges(visitor);
|
||||
visitor.visit(callback.ptr());
|
||||
visitor.visit(signal.ptr());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
JS::GCPtr<IDLEventListener> callback;
|
||||
|
||||
// signal (null or an AbortSignal object)
|
||||
RefPtr<DOM::AbortSignal> signal;
|
||||
JS::GCPtr<DOM::AbortSignal> signal;
|
||||
|
||||
// capture (a boolean, initially false)
|
||||
bool capture { false };
|
||||
|
|
|
@ -111,7 +111,7 @@ public:
|
|||
}
|
||||
|
||||
// JS constructor has message first, name second
|
||||
static NonnullRefPtr<DOMException> create_with_global_object(Bindings::WindowObject&, FlyString const& message, FlyString const& name)
|
||||
static NonnullRefPtr<DOMException> create_with_global_object(HTML::Window&, FlyString const& message, FlyString const& name)
|
||||
{
|
||||
return adopt_ref(*new DOMException(name, message));
|
||||
}
|
||||
|
|
|
@ -6,25 +6,26 @@
|
|||
*/
|
||||
|
||||
#include <LibWeb/Bindings/DOMImplementationPrototype.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/DOM/DOMImplementation.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/DocumentType.h>
|
||||
#include <LibWeb/DOM/ElementFactory.h>
|
||||
#include <LibWeb/DOM/Text.h>
|
||||
#include <LibWeb/HTML/Origin.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Namespace.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
JS::NonnullGCPtr<DOMImplementation> DOMImplementation::create(Document& document)
|
||||
{
|
||||
auto& window_object = document.preferred_window_object();
|
||||
return *window_object.heap().allocate<DOMImplementation>(window_object.realm(), document);
|
||||
auto& window = document.window();
|
||||
return *window.heap().allocate<DOMImplementation>(document.realm(), document);
|
||||
}
|
||||
|
||||
DOMImplementation::DOMImplementation(Document& document)
|
||||
: PlatformObject(document.preferred_window_object().ensure_web_prototype<Bindings::DOMImplementationPrototype>("DOMImplementation"))
|
||||
: PlatformObject(document.window().ensure_web_prototype<Bindings::DOMImplementationPrototype>("DOMImplementation"))
|
||||
, m_document(document)
|
||||
{
|
||||
}
|
||||
|
@ -38,23 +39,23 @@ void DOMImplementation::visit_edges(Cell::Visitor& visitor)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-domimplementation-createdocument
|
||||
ExceptionOr<NonnullRefPtr<Document>> DOMImplementation::create_document(String const& namespace_, String const& qualified_name, RefPtr<DocumentType> doctype) const
|
||||
ExceptionOr<JS::NonnullGCPtr<Document>> DOMImplementation::create_document(String const& namespace_, String const& qualified_name, JS::GCPtr<DocumentType> doctype) const
|
||||
{
|
||||
// FIXME: This should specifically be an XML document.
|
||||
auto xml_document = Document::create();
|
||||
auto xml_document = Document::create(Bindings::main_thread_internal_window_object());
|
||||
|
||||
xml_document->set_ready_for_post_load_tasks(true);
|
||||
|
||||
RefPtr<Element> element;
|
||||
JS::GCPtr<Element> element;
|
||||
|
||||
if (!qualified_name.is_empty())
|
||||
element = TRY(xml_document->create_element_ns(namespace_, qualified_name /* FIXME: and an empty dictionary */));
|
||||
|
||||
if (doctype)
|
||||
xml_document->append_child(doctype.release_nonnull());
|
||||
xml_document->append_child(*doctype);
|
||||
|
||||
if (element)
|
||||
xml_document->append_child(element.release_nonnull());
|
||||
xml_document->append_child(*element);
|
||||
|
||||
xml_document->set_origin(document().origin());
|
||||
|
||||
|
@ -69,16 +70,16 @@ ExceptionOr<NonnullRefPtr<Document>> DOMImplementation::create_document(String c
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
|
||||
NonnullRefPtr<Document> DOMImplementation::create_html_document(String const& title) const
|
||||
JS::NonnullGCPtr<Document> DOMImplementation::create_html_document(String const& title) const
|
||||
{
|
||||
auto html_document = Document::create();
|
||||
auto html_document = Document::create(Bindings::main_thread_internal_window_object());
|
||||
|
||||
html_document->set_content_type("text/html");
|
||||
html_document->set_ready_for_post_load_tasks(true);
|
||||
|
||||
auto doctype = adopt_ref(*new DocumentType(html_document));
|
||||
auto doctype = heap().allocate<DocumentType>(realm(), html_document);
|
||||
doctype->set_name("html");
|
||||
html_document->append_child(doctype);
|
||||
html_document->append_child(*doctype);
|
||||
|
||||
auto html_element = create_element(html_document, HTML::TagNames::html, Namespace::HTML);
|
||||
html_document->append_child(html_element);
|
||||
|
@ -90,8 +91,8 @@ NonnullRefPtr<Document> DOMImplementation::create_html_document(String const& ti
|
|||
auto title_element = create_element(html_document, HTML::TagNames::title, Namespace::HTML);
|
||||
head_element->append_child(title_element);
|
||||
|
||||
auto text_node = adopt_ref(*new Text(html_document, title));
|
||||
title_element->append_child(text_node);
|
||||
auto text_node = heap().allocate<Text>(realm(), html_document, title);
|
||||
title_element->append_child(*text_node);
|
||||
}
|
||||
|
||||
auto body_element = create_element(html_document, HTML::TagNames::body, Namespace::HTML);
|
||||
|
@ -103,7 +104,7 @@ NonnullRefPtr<Document> DOMImplementation::create_html_document(String const& ti
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype
|
||||
ExceptionOr<NonnullRefPtr<DocumentType>> DOMImplementation::create_document_type(String const& qualified_name, String const& public_id, String const& system_id)
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentType>> DOMImplementation::create_document_type(String const& qualified_name, String const& public_id, String const& system_id)
|
||||
{
|
||||
TRY(Document::validate_qualified_name(qualified_name));
|
||||
auto document_type = DocumentType::create(document());
|
||||
|
|
|
@ -7,30 +7,29 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
class DOMImplementation final : public Bindings::PlatformObject {
|
||||
JS_OBJECT(DOMImplementation, Bindings::PlatformObject);
|
||||
WEB_PLATFORM_OBJECT(DOMImplementation, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
static JS::NonnullGCPtr<DOMImplementation> create(Document&);
|
||||
explicit DOMImplementation(Document&);
|
||||
virtual ~DOMImplementation();
|
||||
|
||||
DOMImplementation& impl() { return *this; }
|
||||
|
||||
ExceptionOr<NonnullRefPtr<Document>> create_document(String const&, String const&, RefPtr<DocumentType>) const;
|
||||
NonnullRefPtr<Document> create_html_document(String const& title) const;
|
||||
ExceptionOr<NonnullRefPtr<DocumentType>> create_document_type(String const& qualified_name, String const& public_id, String const& system_id);
|
||||
ExceptionOr<JS::NonnullGCPtr<Document>> create_document(String const&, String const&, JS::GCPtr<DocumentType>) const;
|
||||
JS::NonnullGCPtr<Document> create_html_document(String const& title) const;
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentType>> create_document_type(String const& qualified_name, String const& public_id, String const& system_id);
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
|
||||
bool has_feature() const { return true; }
|
||||
|
||||
private:
|
||||
explicit DOMImplementation(Document&);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
Document& document() { return m_document; }
|
||||
|
@ -41,7 +40,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace Web::Bindings {
|
||||
inline JS::Object* wrap(JS::Realm&, Web::DOM::DOMImplementation& object) { return &object; }
|
||||
using DOMImplementationWrapper = Web::DOM::DOMImplementation;
|
||||
}
|
||||
WRAPPER_HACK(DOMImplementation, Web::DOM)
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibWeb/Bindings/DOMTokenListPrototype.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/DOMException.h>
|
||||
#include <LibWeb/DOM/DOMTokenList.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -56,13 +56,13 @@ namespace Web::DOM {
|
|||
|
||||
DOMTokenList* DOMTokenList::create(Element const& associated_element, FlyString associated_attribute)
|
||||
{
|
||||
auto& realm = associated_element.document().preferred_window_object().realm();
|
||||
auto& realm = associated_element.document().window().realm();
|
||||
return realm.heap().allocate<DOMTokenList>(realm, associated_element, move(associated_attribute));
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#ref-for-domtokenlist%E2%91%A0%E2%91%A2
|
||||
DOMTokenList::DOMTokenList(Element const& associated_element, FlyString associated_attribute)
|
||||
: Bindings::LegacyPlatformObject(associated_element.document().preferred_window_object().ensure_web_prototype<Bindings::DOMTokenListPrototype>("DOMTokenList"))
|
||||
: Bindings::LegacyPlatformObject(associated_element.document().window().ensure_web_prototype<Bindings::DOMTokenListPrototype>("DOMTokenList"))
|
||||
, m_associated_element(associated_element)
|
||||
, m_associated_attribute(move(associated_attribute))
|
||||
{
|
||||
|
@ -225,7 +225,7 @@ String DOMTokenList::value() const
|
|||
// https://dom.spec.whatwg.org/#ref-for-concept-element-attributes-set-value%E2%91%A2
|
||||
void DOMTokenList::set_value(String value)
|
||||
{
|
||||
auto associated_element = m_associated_element.strong_ref();
|
||||
JS::GCPtr<DOM::Element> associated_element = m_associated_element.ptr();
|
||||
if (!associated_element)
|
||||
return;
|
||||
|
||||
|
@ -244,7 +244,7 @@ ExceptionOr<void> DOMTokenList::validate_token(StringView token) const
|
|||
// https://dom.spec.whatwg.org/#concept-dtl-update
|
||||
void DOMTokenList::run_update_steps()
|
||||
{
|
||||
auto associated_element = m_associated_element.strong_ref();
|
||||
JS::GCPtr<DOM::Element> associated_element = m_associated_element.ptr();
|
||||
if (!associated_element)
|
||||
return;
|
||||
|
||||
|
|
|
@ -20,15 +20,12 @@ namespace Web::DOM {
|
|||
|
||||
// https://dom.spec.whatwg.org/#domtokenlist
|
||||
class DOMTokenList final : public Bindings::LegacyPlatformObject {
|
||||
JS_OBJECT(DOMTokenList, Bindings::LegacyPlatformObject);
|
||||
WEB_PLATFORM_OBJECT(DOMTokenList, Bindings::LegacyPlatformObject);
|
||||
|
||||
public:
|
||||
static DOMTokenList* create(Element const& associated_element, FlyString associated_attribute);
|
||||
DOMTokenList(Element const& associated_element, FlyString associated_attribute);
|
||||
~DOMTokenList() = default;
|
||||
|
||||
DOMTokenList& impl() { return *this; }
|
||||
|
||||
void associated_attribute_changed(StringView value);
|
||||
|
||||
virtual bool is_supported_property_index(u32 index) const override;
|
||||
|
@ -46,6 +43,8 @@ public:
|
|||
void set_value(String value);
|
||||
|
||||
private:
|
||||
DOMTokenList(Element const& associated_element, FlyString associated_attribute);
|
||||
|
||||
ExceptionOr<void> validate_token(StringView token) const;
|
||||
void run_update_steps();
|
||||
|
||||
|
@ -56,7 +55,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace Web::Bindings {
|
||||
inline JS::Object* wrap(JS::Realm&, Web::DOM::DOMTokenList& object) { return &object; }
|
||||
using DOMTokenListWrapper = Web::DOM::DOMTokenList;
|
||||
}
|
||||
WRAPPER_HACK(DOMTokenList, Web::DOM)
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
#include <LibJS/Interpreter.h>
|
||||
#include <LibJS/Parser.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibWeb/Bindings/DocumentPrototype.h>
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/CSS/MediaQueryList.h>
|
||||
#include <LibWeb/CSS/MediaQueryListEvent.h>
|
||||
#include <LibWeb/CSS/StyleComputer.h>
|
||||
#include <LibWeb/Cookie/ParsedCookie.h>
|
||||
|
@ -100,7 +101,7 @@ static NonnullRefPtr<HTML::BrowsingContext> obtain_a_browsing_context_to_use_for
|
|||
VERIFY(browsing_context.page());
|
||||
auto new_browsing_context = HTML::BrowsingContext::create_a_new_browsing_context(*browsing_context.page(), nullptr, nullptr);
|
||||
|
||||
// FIXME: 4. If navigationCOOP's value is "same-origin-plus-COEP", then set newBrowsingContext's group's
|
||||
// FIXME: 4. If navigationCOOP's value is "same-origin-plurs-COEP", then set newBrowsingContext's group's
|
||||
// cross-origin isolation mode to either "logical" or "concrete". The choice of which is implementation-defined.
|
||||
|
||||
// 5. If sandboxFlags is not empty, then:
|
||||
|
@ -120,7 +121,7 @@ static NonnullRefPtr<HTML::BrowsingContext> obtain_a_browsing_context_to_use_for
|
|||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#initialise-the-document-object
|
||||
NonnullRefPtr<Document> Document::create_and_initialize(Type type, String content_type, HTML::NavigationParams navigation_params)
|
||||
JS::NonnullGCPtr<Document> Document::create_and_initialize(Type type, String content_type, HTML::NavigationParams navigation_params)
|
||||
{
|
||||
// 1. Let browsingContext be the result of the obtaining a browsing context to use for a navigation response
|
||||
// given navigationParams's browsing context, navigationParams's final sandboxing flag set,
|
||||
|
@ -142,7 +143,7 @@ NonnullRefPtr<Document> Document::create_and_initialize(Type type, String conten
|
|||
creation_url = navigation_params.request->current_url();
|
||||
}
|
||||
|
||||
RefPtr<HTML::Window> window;
|
||||
JS::GCPtr<HTML::Window> window;
|
||||
|
||||
// 5. If browsingContext is still on its initial about:blank Document,
|
||||
// and navigationParams's history handling is "replace",
|
||||
|
@ -168,14 +169,12 @@ NonnullRefPtr<Document> Document::create_and_initialize(Type type, String conten
|
|||
// 5. Let realm execution context be the result of creating a new JavaScript realm given agent and the following customizations:
|
||||
auto realm_execution_context = Bindings::create_a_new_javascript_realm(
|
||||
Bindings::main_thread_vm(),
|
||||
[&](JS::Realm& realm) -> JS::GlobalObject* {
|
||||
[&](JS::Realm& realm) -> JS::Object* {
|
||||
// - For the global object, create a new Window object.
|
||||
window = HTML::Window::create();
|
||||
auto* global_object = realm.heap().allocate_without_realm<Bindings::WindowObject>(realm, *window);
|
||||
VERIFY(window->wrapper() == global_object);
|
||||
return global_object;
|
||||
window = HTML::Window::create(realm);
|
||||
return window;
|
||||
},
|
||||
[](JS::Realm&) -> JS::GlobalObject* {
|
||||
[](JS::Realm&) -> JS::Object* {
|
||||
// FIXME: - For the global this binding, use browsingContext's WindowProxy object.
|
||||
return nullptr;
|
||||
});
|
||||
|
@ -223,7 +222,7 @@ NonnullRefPtr<Document> Document::create_and_initialize(Type type, String conten
|
|||
// FIXME: and cross-origin opener policy is navigationParams's cross-origin opener policy,
|
||||
// FIXME: load timing info is loadTimingInfo,
|
||||
// FIXME: and navigation id is navigationParams's id.
|
||||
auto document = Document::create();
|
||||
auto document = Document::create(*window);
|
||||
document->m_type = type;
|
||||
document->m_content_type = content_type;
|
||||
document->set_origin(navigation_params.origin);
|
||||
|
@ -266,24 +265,25 @@ NonnullRefPtr<Document> Document::create_and_initialize(Type type, String conten
|
|||
return document;
|
||||
}
|
||||
|
||||
NonnullRefPtr<Document> Document::create_with_global_object(Bindings::WindowObject&)
|
||||
JS::NonnullGCPtr<Document> Document::create_with_global_object(HTML::Window& window)
|
||||
{
|
||||
return Document::create();
|
||||
return Document::create(window);
|
||||
}
|
||||
|
||||
NonnullRefPtr<Document> Document::create(AK::URL const& url)
|
||||
JS::NonnullGCPtr<Document> Document::create(HTML::Window& window, AK::URL const& url)
|
||||
{
|
||||
return adopt_ref(*new Document(url));
|
||||
auto& realm = window.realm();
|
||||
return *realm.heap().allocate<Document>(realm, window, url);
|
||||
}
|
||||
|
||||
Document::Document(const AK::URL& url)
|
||||
: ParentNode(*this, NodeType::DOCUMENT_NODE)
|
||||
Document::Document(HTML::Window& window, const AK::URL& url)
|
||||
: ParentNode(window.realm(), *this, NodeType::DOCUMENT_NODE)
|
||||
, m_style_computer(make<CSS::StyleComputer>(*this))
|
||||
, m_url(url)
|
||||
, m_window(HTML::Window::create_with_document(*this))
|
||||
, m_window(window)
|
||||
, m_history(HTML::History::create(*this))
|
||||
{
|
||||
m_style_sheets = JS::make_handle(CSS::StyleSheetList::create(*this));
|
||||
set_prototype(&window.ensure_web_prototype<Bindings::DocumentPrototype>("Document"));
|
||||
|
||||
HTML::main_thread_event_loop().register_document({}, *this);
|
||||
|
||||
|
@ -296,63 +296,33 @@ Document::Document(const AK::URL& url)
|
|||
});
|
||||
}
|
||||
|
||||
Document::~Document() = default;
|
||||
|
||||
void Document::removed_last_ref()
|
||||
Document::~Document()
|
||||
{
|
||||
VERIFY(!ref_count());
|
||||
VERIFY(!m_deletion_has_begun);
|
||||
|
||||
if (m_referencing_node_count) {
|
||||
// The document has reached ref_count==0 but still has nodes keeping it alive.
|
||||
// At this point, sever all the node links we control.
|
||||
// If nodes remain elsewhere (e.g JS wrappers), they will keep the document alive.
|
||||
|
||||
// NOTE: This makes sure we stay alive across for the duration of the cleanup below.
|
||||
increment_referencing_node_count();
|
||||
|
||||
m_focused_element = nullptr;
|
||||
m_hovered_node = nullptr;
|
||||
m_pending_parsing_blocking_script = nullptr;
|
||||
m_inspected_node = nullptr;
|
||||
m_scripts_to_execute_when_parsing_has_finished.clear();
|
||||
m_scripts_to_execute_as_soon_as_possible.clear();
|
||||
m_associated_inert_template_document = nullptr;
|
||||
|
||||
m_interpreter = nullptr;
|
||||
|
||||
{
|
||||
// Gather up all the descendants of this document and prune them from the tree.
|
||||
// FIXME: This could definitely be more elegant.
|
||||
NonnullRefPtrVector<Node> descendants;
|
||||
for_each_in_inclusive_subtree([&](auto& node) {
|
||||
if (&node != this)
|
||||
descendants.append(node);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
for (auto& node : descendants) {
|
||||
VERIFY(&node.document() == this);
|
||||
VERIFY(!node.is_document());
|
||||
if (node.parent()) {
|
||||
// We need to suppress mutation observers so that they don't try and queue a microtask for this Document which is in the process of dying,
|
||||
// which will cause an `!m_in_removed_last_ref` assertion failure when it tries to ref this Document.
|
||||
node.remove(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_in_removed_last_ref = false;
|
||||
decrement_referencing_node_count();
|
||||
return;
|
||||
}
|
||||
|
||||
m_in_removed_last_ref = false;
|
||||
m_deletion_has_begun = true;
|
||||
|
||||
HTML::main_thread_event_loop().unregister_document({}, *this);
|
||||
}
|
||||
|
||||
delete this;
|
||||
void Document::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_window.ptr());
|
||||
visitor.visit(m_style_sheets.ptr());
|
||||
visitor.visit(m_hovered_node.ptr());
|
||||
visitor.visit(m_inspected_node.ptr());
|
||||
visitor.visit(m_active_favicon.ptr());
|
||||
visitor.visit(m_focused_element.ptr());
|
||||
visitor.visit(m_active_element.ptr());
|
||||
visitor.visit(m_implementation.ptr());
|
||||
visitor.visit(m_current_script.ptr());
|
||||
visitor.visit(m_associated_inert_template_document.ptr());
|
||||
visitor.visit(m_pending_parsing_blocking_script.ptr());
|
||||
|
||||
for (auto& script : m_scripts_to_execute_when_parsing_has_finished)
|
||||
visitor.visit(script.ptr());
|
||||
for (auto& script : m_scripts_to_execute_as_soon_as_possible)
|
||||
visitor.visit(script.ptr());
|
||||
|
||||
for (auto& node_iterator : m_node_iterators)
|
||||
visitor.visit(node_iterator);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-write
|
||||
|
@ -642,14 +612,14 @@ void Document::set_title(String const& title)
|
|||
if (!head_element)
|
||||
return;
|
||||
|
||||
RefPtr<HTML::HTMLTitleElement> title_element = head_element->first_child_of_type<HTML::HTMLTitleElement>();
|
||||
JS::GCPtr<HTML::HTMLTitleElement> title_element = head_element->first_child_of_type<HTML::HTMLTitleElement>();
|
||||
if (!title_element) {
|
||||
title_element = static_ptr_cast<HTML::HTMLTitleElement>(create_element(HTML::TagNames::title).release_value());
|
||||
title_element = &static_cast<HTML::HTMLTitleElement&>(*create_element(HTML::TagNames::title).release_value());
|
||||
head_element->append_child(*title_element);
|
||||
}
|
||||
|
||||
title_element->remove_all_children(true);
|
||||
title_element->append_child(adopt_ref(*new Text(*this, title)));
|
||||
title_element->append_child(*heap().allocate<Text>(realm(), *this, title));
|
||||
|
||||
if (auto* page = this->page()) {
|
||||
if (browsing_context() == &page->top_level_browsing_context())
|
||||
|
@ -725,13 +695,13 @@ Vector<CSS::BackgroundLayerData> const* Document::background_layers() const
|
|||
return &body_layout_node->background_layers();
|
||||
}
|
||||
|
||||
RefPtr<HTML::HTMLBaseElement> Document::first_base_element_with_href_in_tree_order() const
|
||||
JS::GCPtr<HTML::HTMLBaseElement> Document::first_base_element_with_href_in_tree_order() const
|
||||
{
|
||||
RefPtr<HTML::HTMLBaseElement> base_element;
|
||||
JS::GCPtr<HTML::HTMLBaseElement> base_element;
|
||||
|
||||
for_each_in_subtree_of_type<HTML::HTMLBaseElement>([&base_element](HTML::HTMLBaseElement const& base_element_in_tree) {
|
||||
if (base_element_in_tree.has_attribute(HTML::AttributeNames::href)) {
|
||||
base_element = base_element_in_tree;
|
||||
base_element = &base_element_in_tree;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
|
||||
|
@ -909,7 +879,7 @@ Layout::InitialContainingBlock* Document::layout_node()
|
|||
|
||||
void Document::set_inspected_node(Node* node)
|
||||
{
|
||||
if (m_inspected_node == node)
|
||||
if (m_inspected_node.ptr() == node)
|
||||
return;
|
||||
|
||||
if (m_inspected_node && m_inspected_node->layout_node())
|
||||
|
@ -943,10 +913,10 @@ static Node* find_common_ancestor(Node* a, Node* b)
|
|||
|
||||
void Document::set_hovered_node(Node* node)
|
||||
{
|
||||
if (m_hovered_node == node)
|
||||
if (m_hovered_node.ptr() == node)
|
||||
return;
|
||||
|
||||
RefPtr<Node> old_hovered_node = move(m_hovered_node);
|
||||
JS::GCPtr<Node> old_hovered_node = move(m_hovered_node);
|
||||
m_hovered_node = node;
|
||||
|
||||
if (auto* common_ancestor = find_common_ancestor(old_hovered_node, m_hovered_node))
|
||||
|
@ -1077,13 +1047,6 @@ HTML::EnvironmentSettingsObject& Document::relevant_settings_object()
|
|||
return verify_cast<HTML::EnvironmentSettingsObject>(*realm().host_defined());
|
||||
}
|
||||
|
||||
JS::Realm& Document::realm()
|
||||
{
|
||||
VERIFY(m_window);
|
||||
VERIFY(m_window->wrapper());
|
||||
return m_window->wrapper()->shape().realm();
|
||||
}
|
||||
|
||||
JS::Interpreter& Document::interpreter()
|
||||
{
|
||||
if (!m_interpreter) {
|
||||
|
@ -1115,7 +1078,7 @@ JS::Value Document::run_javascript(StringView source, StringView filename)
|
|||
|
||||
// https://dom.spec.whatwg.org/#dom-document-createelement
|
||||
// FIXME: This only implements step 6 of the algorithm and does not take in options.
|
||||
DOM::ExceptionOr<NonnullRefPtr<Element>> Document::create_element(String const& tag_name)
|
||||
DOM::ExceptionOr<JS::NonnullGCPtr<Element>> Document::create_element(String const& tag_name)
|
||||
{
|
||||
if (!is_valid_name(tag_name))
|
||||
return DOM::InvalidCharacterError::create("Invalid character in tag name.");
|
||||
|
@ -1127,7 +1090,7 @@ DOM::ExceptionOr<NonnullRefPtr<Element>> Document::create_element(String const&
|
|||
// https://dom.spec.whatwg.org/#dom-document-createelementns
|
||||
// https://dom.spec.whatwg.org/#internal-createelementns-steps
|
||||
// FIXME: This only implements step 4 of the algorithm and does not take in options.
|
||||
DOM::ExceptionOr<NonnullRefPtr<Element>> Document::create_element_ns(String const& namespace_, String const& qualified_name)
|
||||
DOM::ExceptionOr<JS::NonnullGCPtr<Element>> Document::create_element_ns(String const& namespace_, String const& qualified_name)
|
||||
{
|
||||
// 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
|
||||
auto extracted_qualified_name = TRY(validate_and_extract(namespace_, qualified_name));
|
||||
|
@ -1139,19 +1102,19 @@ DOM::ExceptionOr<NonnullRefPtr<Element>> Document::create_element_ns(String cons
|
|||
return DOM::create_element(*this, extracted_qualified_name.local_name(), extracted_qualified_name.namespace_(), extracted_qualified_name.prefix());
|
||||
}
|
||||
|
||||
NonnullRefPtr<DocumentFragment> Document::create_document_fragment()
|
||||
JS::NonnullGCPtr<DocumentFragment> Document::create_document_fragment()
|
||||
{
|
||||
return adopt_ref(*new DocumentFragment(*this));
|
||||
return *heap().allocate<DocumentFragment>(realm(), *this);
|
||||
}
|
||||
|
||||
NonnullRefPtr<Text> Document::create_text_node(String const& data)
|
||||
JS::NonnullGCPtr<Text> Document::create_text_node(String const& data)
|
||||
{
|
||||
return adopt_ref(*new Text(*this, data));
|
||||
return *heap().allocate<Text>(realm(), *this, data);
|
||||
}
|
||||
|
||||
NonnullRefPtr<Comment> Document::create_comment(String const& data)
|
||||
JS::NonnullGCPtr<Comment> Document::create_comment(String const& data)
|
||||
{
|
||||
return adopt_ref(*new Comment(*this, data));
|
||||
return *heap().allocate<Comment>(realm(), *this, data);
|
||||
}
|
||||
|
||||
JS::NonnullGCPtr<Range> Document::create_range()
|
||||
|
@ -1162,7 +1125,7 @@ JS::NonnullGCPtr<Range> Document::create_range()
|
|||
// https://dom.spec.whatwg.org/#dom-document-createevent
|
||||
DOM::ExceptionOr<JS::NonnullGCPtr<Event>> Document::create_event(String const& interface)
|
||||
{
|
||||
auto& window_object = preferred_window_object();
|
||||
auto& window_object = window();
|
||||
|
||||
// NOTE: This is named event here, since we do step 5 and 6 as soon as possible for each case.
|
||||
// 1. Let constructor be null.
|
||||
|
@ -1238,33 +1201,36 @@ void Document::set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement
|
|||
m_pending_parsing_blocking_script = script;
|
||||
}
|
||||
|
||||
NonnullRefPtr<HTML::HTMLScriptElement> Document::take_pending_parsing_blocking_script(Badge<HTML::HTMLParser>)
|
||||
JS::NonnullGCPtr<HTML::HTMLScriptElement> Document::take_pending_parsing_blocking_script(Badge<HTML::HTMLParser>)
|
||||
{
|
||||
return m_pending_parsing_blocking_script.release_nonnull();
|
||||
VERIFY(m_pending_parsing_blocking_script);
|
||||
auto script = m_pending_parsing_blocking_script;
|
||||
m_pending_parsing_blocking_script = nullptr;
|
||||
return *script;
|
||||
}
|
||||
|
||||
void Document::add_script_to_execute_when_parsing_has_finished(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
|
||||
{
|
||||
m_scripts_to_execute_when_parsing_has_finished.append(script);
|
||||
m_scripts_to_execute_when_parsing_has_finished.append(JS::make_handle(script));
|
||||
}
|
||||
|
||||
NonnullRefPtrVector<HTML::HTMLScriptElement> Document::take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLParser>)
|
||||
Vector<JS::Handle<HTML::HTMLScriptElement>> Document::take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLParser>)
|
||||
{
|
||||
return move(m_scripts_to_execute_when_parsing_has_finished);
|
||||
}
|
||||
|
||||
void Document::add_script_to_execute_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
|
||||
{
|
||||
m_scripts_to_execute_as_soon_as_possible.append(script);
|
||||
m_scripts_to_execute_as_soon_as_possible.append(JS::make_handle(script));
|
||||
}
|
||||
|
||||
NonnullRefPtrVector<HTML::HTMLScriptElement> Document::take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLParser>)
|
||||
Vector<JS::Handle<HTML::HTMLScriptElement>> Document::take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLParser>)
|
||||
{
|
||||
return move(m_scripts_to_execute_as_soon_as_possible);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-document-importnode
|
||||
ExceptionOr<NonnullRefPtr<Node>> Document::import_node(NonnullRefPtr<Node> node, bool deep)
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> Document::import_node(JS::NonnullGCPtr<Node> node, bool deep)
|
||||
{
|
||||
// 1. If node is a document or shadow root, then throw a "NotSupportedError" DOMException.
|
||||
if (is<Document>(*node) || is<ShadowRoot>(*node))
|
||||
|
@ -1299,10 +1265,11 @@ void Document::adopt_node(Node& node)
|
|||
|
||||
// Transfer NodeIterators rooted at `node` from old_document to this document.
|
||||
Vector<NodeIterator&> node_iterators_to_transfer;
|
||||
for (auto* node_iterator : old_document.m_node_iterators) {
|
||||
if (node_iterator->root() == &node)
|
||||
for (auto node_iterator : old_document.m_node_iterators) {
|
||||
if (node_iterator->root().ptr() == &node)
|
||||
node_iterators_to_transfer.append(*node_iterator);
|
||||
}
|
||||
|
||||
for (auto& node_iterator : node_iterators_to_transfer) {
|
||||
old_document.m_node_iterators.remove(&node_iterator);
|
||||
m_node_iterators.set(&node_iterator);
|
||||
|
@ -1311,7 +1278,7 @@ void Document::adopt_node(Node& node)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-document-adoptnode
|
||||
ExceptionOr<NonnullRefPtr<Node>> Document::adopt_node_binding(NonnullRefPtr<Node> node)
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> Document::adopt_node_binding(JS::NonnullGCPtr<Node> node)
|
||||
{
|
||||
if (is<Document>(*node))
|
||||
return DOM::NotSupportedError::create("Cannot adopt a document into a document");
|
||||
|
@ -1350,7 +1317,7 @@ bool Document::is_editable() const
|
|||
|
||||
void Document::set_focused_element(Element* element)
|
||||
{
|
||||
if (m_focused_element == element)
|
||||
if (m_focused_element.ptr() == element)
|
||||
return;
|
||||
|
||||
if (m_focused_element) {
|
||||
|
@ -1371,7 +1338,7 @@ void Document::set_focused_element(Element* element)
|
|||
|
||||
void Document::set_active_element(Element* element)
|
||||
{
|
||||
if (m_active_element == element)
|
||||
if (m_active_element.ptr() == element)
|
||||
return;
|
||||
|
||||
m_active_element = element;
|
||||
|
@ -1410,7 +1377,7 @@ void Document::update_readiness(HTML::DocumentReadyState readiness_value)
|
|||
// FIXME: 3. Otherwise, if readinessValue is "interactive", and document's load timing info's DOM interactive time is 0, then set document's load timing info's DOM interactive time to now.
|
||||
|
||||
// 3. Fire an event named readystatechange at document.
|
||||
dispatch_event(*Event::create(preferred_window_object(), HTML::EventNames::readystatechange));
|
||||
dispatch_event(*Event::create(window(), HTML::EventNames::readystatechange));
|
||||
}
|
||||
|
||||
Page* Document::page()
|
||||
|
@ -1451,7 +1418,7 @@ void Document::completely_finish_loading()
|
|||
// Otherwise, if container is non-null, then queue an element task on the DOM manipulation task source given container to fire an event named load at container.
|
||||
else if (container) {
|
||||
container->queue_an_element_task(HTML::Task::Source::DOMManipulation, [container, this]() mutable {
|
||||
container->dispatch_event(*DOM::Event::create(preferred_window_object(), HTML::EventNames::load));
|
||||
container->dispatch_event(*DOM::Event::create(window(), HTML::EventNames::load));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1531,7 +1498,7 @@ Bindings::LocationObject* Document::location()
|
|||
if (!is_fully_active())
|
||||
return nullptr;
|
||||
|
||||
return window().wrapper()->location_object();
|
||||
return window().location_object();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#dom-document-hidden
|
||||
|
@ -1562,14 +1529,14 @@ void Document::run_the_resize_steps()
|
|||
return;
|
||||
m_last_viewport_size = viewport_size;
|
||||
|
||||
window().dispatch_event(*DOM::Event::create(preferred_window_object(), UIEvents::EventNames::resize));
|
||||
window().dispatch_event(*DOM::Event::create(window(), UIEvents::EventNames::resize));
|
||||
|
||||
update_layout();
|
||||
}
|
||||
|
||||
void Document::add_media_query_list(NonnullRefPtr<CSS::MediaQueryList>& media_query_list)
|
||||
void Document::add_media_query_list(JS::NonnullGCPtr<CSS::MediaQueryList> media_query_list)
|
||||
{
|
||||
m_media_query_lists.append(media_query_list);
|
||||
m_media_query_lists.append(*media_query_list);
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes
|
||||
|
@ -1590,7 +1557,7 @@ void Document::evaluate_media_queries_and_report_changes()
|
|||
// and its matches attribute initialized to target’s matches state.
|
||||
if (media_query_list_ptr.is_null())
|
||||
continue;
|
||||
auto media_query_list = media_query_list_ptr.strong_ref();
|
||||
JS::GCPtr<CSS::MediaQueryList> media_query_list = media_query_list_ptr.ptr();
|
||||
bool did_match = media_query_list->matches();
|
||||
bool now_matches = media_query_list->evaluate();
|
||||
|
||||
|
@ -1598,7 +1565,7 @@ void Document::evaluate_media_queries_and_report_changes()
|
|||
CSS::MediaQueryListEventInit init;
|
||||
init.media = media_query_list->media();
|
||||
init.matches = now_matches;
|
||||
auto event = CSS::MediaQueryListEvent::create(preferred_window_object(), HTML::EventNames::change, init);
|
||||
auto event = CSS::MediaQueryListEvent::create(window(), HTML::EventNames::change, init);
|
||||
event->set_is_trusted(true);
|
||||
media_query_list->dispatch_event(*event);
|
||||
}
|
||||
|
@ -1624,9 +1591,9 @@ void Document::evaluate_media_rules()
|
|||
|
||||
DOMImplementation* Document::implementation()
|
||||
{
|
||||
if (!m_implementation.cell())
|
||||
m_implementation = JS::make_handle(*DOMImplementation::create(*this));
|
||||
return m_implementation.cell();
|
||||
if (!m_implementation)
|
||||
m_implementation = DOMImplementation::create(*this);
|
||||
return m_implementation;
|
||||
}
|
||||
|
||||
bool Document::has_focus() const
|
||||
|
@ -1821,7 +1788,7 @@ void Document::check_favicon_after_loading_link_resource()
|
|||
for (auto i = favicon_link_elements->length(); i-- > 0;) {
|
||||
auto favicon_element = favicon_link_elements->item(i);
|
||||
|
||||
if (favicon_element == m_active_element)
|
||||
if (favicon_element == m_active_element.ptr())
|
||||
return;
|
||||
|
||||
// If the user agent tries to use an icon but that icon is determined, upon closer examination,
|
||||
|
@ -1838,14 +1805,19 @@ void Document::check_favicon_after_loading_link_resource()
|
|||
|
||||
void Document::set_window(Badge<HTML::BrowsingContext>, HTML::Window& window)
|
||||
{
|
||||
m_window = window;
|
||||
m_window = &window;
|
||||
}
|
||||
|
||||
Bindings::WindowObject& Document::preferred_window_object() const
|
||||
CSS::StyleSheetList& Document::style_sheets()
|
||||
{
|
||||
if (m_window && m_window->wrapper())
|
||||
return const_cast<Bindings::WindowObject&>(*m_window->wrapper());
|
||||
return Bindings::main_thread_internal_window_object();
|
||||
if (!m_style_sheets)
|
||||
m_style_sheets = CSS::StyleSheetList::create(*this);
|
||||
return *m_style_sheets;
|
||||
}
|
||||
|
||||
CSS::StyleSheetList const& Document::style_sheets() const
|
||||
{
|
||||
return const_cast<Document*>(this)->style_sheets();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <AK/WeakPtr.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/CSS/CSSStyleSheet.h>
|
||||
#include <LibWeb/CSS/StyleComputer.h>
|
||||
#include <LibWeb/CSS/StyleSheetList.h>
|
||||
|
@ -31,6 +30,7 @@
|
|||
#include <LibWeb/HTML/History.h>
|
||||
#include <LibWeb/HTML/Origin.h>
|
||||
#include <LibWeb/HTML/Scripting/Environments.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
|
@ -44,25 +44,20 @@ class Document
|
|||
: public ParentNode
|
||||
, public NonElementParentNode<Document>
|
||||
, public HTML::GlobalEventHandlers {
|
||||
public:
|
||||
using WrapperType = Bindings::DocumentWrapper;
|
||||
WEB_PLATFORM_OBJECT(Document, ParentNode);
|
||||
|
||||
public:
|
||||
enum class Type {
|
||||
XML,
|
||||
HTML
|
||||
};
|
||||
|
||||
static NonnullRefPtr<Document> create_and_initialize(Type, String content_type, HTML::NavigationParams);
|
||||
static JS::NonnullGCPtr<Document> create_and_initialize(Type, String content_type, HTML::NavigationParams);
|
||||
|
||||
static NonnullRefPtr<Document> create(AK::URL const& url = "about:blank"sv);
|
||||
static NonnullRefPtr<Document> create_with_global_object(Bindings::WindowObject&);
|
||||
static JS::NonnullGCPtr<Document> create(HTML::Window&, AK::URL const& url = "about:blank"sv);
|
||||
static JS::NonnullGCPtr<Document> create_with_global_object(HTML::Window&);
|
||||
virtual ~Document() override;
|
||||
|
||||
// NOTE: This returns the web-facing window object if there is one,
|
||||
// otherwise it returns the internal window object.
|
||||
// FIXME: Remove this when Document is a JS::Object.
|
||||
Bindings::WindowObject& preferred_window_object() const;
|
||||
|
||||
size_t next_layout_node_serial_id(Badge<Layout::Node>) { return m_next_layout_node_serial_id++; }
|
||||
size_t layout_node_count() const { return m_next_layout_node_serial_id; }
|
||||
|
||||
|
@ -80,7 +75,7 @@ public:
|
|||
AK::URL fallback_base_url() const;
|
||||
AK::URL base_url() const;
|
||||
|
||||
RefPtr<HTML::HTMLBaseElement> first_base_element_with_href_in_tree_order() const;
|
||||
JS::GCPtr<HTML::HTMLBaseElement> first_base_element_with_href_in_tree_order() const;
|
||||
|
||||
String url_string() const { return m_url.to_string(); }
|
||||
String document_uri() const { return m_url.to_string(); }
|
||||
|
@ -96,20 +91,20 @@ public:
|
|||
CSS::StyleComputer& style_computer() { return *m_style_computer; }
|
||||
const CSS::StyleComputer& style_computer() const { return *m_style_computer; }
|
||||
|
||||
CSS::StyleSheetList& style_sheets() { return *m_style_sheets; }
|
||||
const CSS::StyleSheetList& style_sheets() const { return *m_style_sheets; }
|
||||
CSS::StyleSheetList& style_sheets();
|
||||
CSS::StyleSheetList const& style_sheets() const;
|
||||
|
||||
CSS::StyleSheetList* style_sheets_for_bindings() { return m_style_sheets.cell(); }
|
||||
CSS::StyleSheetList* style_sheets_for_bindings() { return &style_sheets(); }
|
||||
|
||||
virtual FlyString node_name() const override { return "#document"; }
|
||||
|
||||
void set_hovered_node(Node*);
|
||||
Node* hovered_node() { return m_hovered_node; }
|
||||
Node const* hovered_node() const { return m_hovered_node; }
|
||||
Node* hovered_node() { return m_hovered_node.ptr(); }
|
||||
Node const* hovered_node() const { return m_hovered_node.ptr(); }
|
||||
|
||||
void set_inspected_node(Node*);
|
||||
Node* inspected_node() { return m_inspected_node; }
|
||||
Node const* inspected_node() const { return m_inspected_node; }
|
||||
Node* inspected_node() { return m_inspected_node.ptr(); }
|
||||
Node const* inspected_node() const { return m_inspected_node.ptr(); }
|
||||
|
||||
Element* document_element();
|
||||
Element const* document_element() const;
|
||||
|
@ -193,30 +188,29 @@ public:
|
|||
void set_source(String const& source) { m_source = source; }
|
||||
|
||||
HTML::EnvironmentSettingsObject& relevant_settings_object();
|
||||
JS::Realm& realm();
|
||||
JS::Interpreter& interpreter();
|
||||
|
||||
JS::Value run_javascript(StringView source, StringView filename = "(unknown)"sv);
|
||||
|
||||
ExceptionOr<NonnullRefPtr<Element>> create_element(String const& tag_name);
|
||||
ExceptionOr<NonnullRefPtr<Element>> create_element_ns(String const& namespace_, String const& qualified_name);
|
||||
NonnullRefPtr<DocumentFragment> create_document_fragment();
|
||||
NonnullRefPtr<Text> create_text_node(String const& data);
|
||||
NonnullRefPtr<Comment> create_comment(String const& data);
|
||||
ExceptionOr<JS::NonnullGCPtr<Element>> create_element(String const& tag_name);
|
||||
ExceptionOr<JS::NonnullGCPtr<Element>> create_element_ns(String const& namespace_, String const& qualified_name);
|
||||
JS::NonnullGCPtr<DocumentFragment> create_document_fragment();
|
||||
JS::NonnullGCPtr<Text> create_text_node(String const& data);
|
||||
JS::NonnullGCPtr<Comment> create_comment(String const& data);
|
||||
ExceptionOr<JS::NonnullGCPtr<Event>> create_event(String const& interface);
|
||||
JS::NonnullGCPtr<Range> create_range();
|
||||
|
||||
void set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement*);
|
||||
HTML::HTMLScriptElement* pending_parsing_blocking_script() { return m_pending_parsing_blocking_script; }
|
||||
NonnullRefPtr<HTML::HTMLScriptElement> take_pending_parsing_blocking_script(Badge<HTML::HTMLParser>);
|
||||
HTML::HTMLScriptElement* pending_parsing_blocking_script() { return m_pending_parsing_blocking_script.ptr(); }
|
||||
JS::NonnullGCPtr<HTML::HTMLScriptElement> take_pending_parsing_blocking_script(Badge<HTML::HTMLParser>);
|
||||
|
||||
void add_script_to_execute_when_parsing_has_finished(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement&);
|
||||
NonnullRefPtrVector<HTML::HTMLScriptElement> take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLParser>);
|
||||
NonnullRefPtrVector<HTML::HTMLScriptElement>& scripts_to_execute_when_parsing_has_finished() { return m_scripts_to_execute_when_parsing_has_finished; }
|
||||
Vector<JS::Handle<HTML::HTMLScriptElement>> take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLParser>);
|
||||
Vector<JS::Handle<HTML::HTMLScriptElement>>& scripts_to_execute_when_parsing_has_finished() { return m_scripts_to_execute_when_parsing_has_finished; }
|
||||
|
||||
void add_script_to_execute_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement&);
|
||||
NonnullRefPtrVector<HTML::HTMLScriptElement> take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLParser>);
|
||||
NonnullRefPtrVector<HTML::HTMLScriptElement>& scripts_to_execute_as_soon_as_possible() { return m_scripts_to_execute_as_soon_as_possible; }
|
||||
Vector<JS::Handle<HTML::HTMLScriptElement>> take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLParser>);
|
||||
Vector<JS::Handle<HTML::HTMLScriptElement>>& scripts_to_execute_as_soon_as_possible() { return m_scripts_to_execute_as_soon_as_possible; }
|
||||
|
||||
QuirksMode mode() const { return m_quirks_mode; }
|
||||
bool in_quirks_mode() const { return m_quirks_mode == QuirksMode::Yes; }
|
||||
|
@ -228,9 +222,9 @@ public:
|
|||
// https://dom.spec.whatwg.org/#xml-document
|
||||
bool is_xml_document() const { return m_type == Type::XML; }
|
||||
|
||||
ExceptionOr<NonnullRefPtr<Node>> import_node(NonnullRefPtr<Node> node, bool deep);
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> import_node(JS::NonnullGCPtr<Node> node, bool deep);
|
||||
void adopt_node(Node&);
|
||||
ExceptionOr<NonnullRefPtr<Node>> adopt_node_binding(NonnullRefPtr<Node>);
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> adopt_node_binding(JS::NonnullGCPtr<Node>);
|
||||
|
||||
DocumentType const* doctype() const;
|
||||
String const& compat_mode() const;
|
||||
|
@ -238,39 +232,26 @@ public:
|
|||
void set_editable(bool editable) { m_editable = editable; }
|
||||
virtual bool is_editable() const final;
|
||||
|
||||
Element* focused_element() { return m_focused_element; }
|
||||
Element const* focused_element() const { return m_focused_element; }
|
||||
Element* focused_element() { return m_focused_element.ptr(); }
|
||||
Element const* focused_element() const { return m_focused_element.ptr(); }
|
||||
|
||||
void set_focused_element(Element*);
|
||||
|
||||
Element const* active_element() const { return m_active_element; }
|
||||
Element const* active_element() const { return m_active_element.ptr(); }
|
||||
|
||||
void set_active_element(Element*);
|
||||
|
||||
bool created_for_appropriate_template_contents() const { return m_created_for_appropriate_template_contents; }
|
||||
void set_created_for_appropriate_template_contents(bool value) { m_created_for_appropriate_template_contents = value; }
|
||||
|
||||
Document* associated_inert_template_document() { return m_associated_inert_template_document; }
|
||||
Document const* associated_inert_template_document() const { return m_associated_inert_template_document; }
|
||||
void set_associated_inert_template_document(Document& document) { m_associated_inert_template_document = document; }
|
||||
Document* associated_inert_template_document() { return m_associated_inert_template_document.ptr(); }
|
||||
Document const* associated_inert_template_document() const { return m_associated_inert_template_document.ptr(); }
|
||||
void set_associated_inert_template_document(Document& document) { m_associated_inert_template_document = &document; }
|
||||
|
||||
String ready_state() const;
|
||||
void update_readiness(HTML::DocumentReadyState);
|
||||
|
||||
void ref_from_node(Badge<Node>)
|
||||
{
|
||||
increment_referencing_node_count();
|
||||
}
|
||||
|
||||
void unref_from_node(Badge<Node>)
|
||||
{
|
||||
decrement_referencing_node_count();
|
||||
}
|
||||
|
||||
void removed_last_ref();
|
||||
|
||||
HTML::Window& window() { return *m_window; }
|
||||
HTML::Window const& window() const { return *m_window; }
|
||||
HTML::Window& window() const { return const_cast<HTML::Window&>(*m_window); }
|
||||
|
||||
void set_window(Badge<HTML::BrowsingContext>, HTML::Window&);
|
||||
|
||||
|
@ -280,7 +261,7 @@ public:
|
|||
ExceptionOr<Document*> open(String const& = "", String const& = "");
|
||||
ExceptionOr<void> close();
|
||||
|
||||
HTML::Window* default_view() { return m_window; }
|
||||
HTML::Window* default_view() { return m_window.ptr(); }
|
||||
|
||||
String const& content_type() const { return m_content_type; }
|
||||
void set_content_type(String const& content_type) { m_content_type = content_type; }
|
||||
|
@ -302,8 +283,8 @@ public:
|
|||
|
||||
DOMImplementation* implementation();
|
||||
|
||||
RefPtr<HTML::HTMLScriptElement> current_script() const { return m_current_script; }
|
||||
void set_current_script(Badge<HTML::HTMLScriptElement>, RefPtr<HTML::HTMLScriptElement> script) { m_current_script = move(script); }
|
||||
JS::GCPtr<HTML::HTMLScriptElement> current_script() const { return m_current_script.ptr(); }
|
||||
void set_current_script(Badge<HTML::HTMLScriptElement>, JS::GCPtr<HTML::HTMLScriptElement> script) { m_current_script = move(script); }
|
||||
|
||||
u32 ignore_destructive_writes_counter() const { return m_ignore_destructive_writes_counter; }
|
||||
void increment_ignore_destructive_writes_counter() { m_ignore_destructive_writes_counter++; }
|
||||
|
@ -335,7 +316,7 @@ public:
|
|||
void run_the_resize_steps();
|
||||
|
||||
void evaluate_media_queries_and_report_changes();
|
||||
void add_media_query_list(NonnullRefPtr<CSS::MediaQueryList>&);
|
||||
void add_media_query_list(JS::NonnullGCPtr<CSS::MediaQueryList>);
|
||||
|
||||
bool has_focus() const;
|
||||
|
||||
|
@ -359,15 +340,13 @@ public:
|
|||
template<typename Callback>
|
||||
void for_each_node_iterator(Callback callback)
|
||||
{
|
||||
for (auto* node_iterator : m_node_iterators)
|
||||
for (auto& node_iterator : m_node_iterators)
|
||||
callback(*node_iterator);
|
||||
}
|
||||
|
||||
bool needs_full_style_update() const { return m_needs_full_style_update; }
|
||||
void set_needs_full_style_update(bool b) { m_needs_full_style_update = b; }
|
||||
|
||||
bool in_removed_last_ref() const { return m_in_removed_last_ref; }
|
||||
|
||||
bool has_active_favicon() const { return m_active_favicon; }
|
||||
void check_favicon_after_loading_link_resource();
|
||||
|
||||
|
@ -375,8 +354,11 @@ public:
|
|||
bool is_initial_about_blank() const { return m_is_initial_about_blank; }
|
||||
void set_is_initial_about_blank(bool b) { m_is_initial_about_blank = b; }
|
||||
|
||||
protected:
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
private:
|
||||
explicit Document(const AK::URL&);
|
||||
Document(HTML::Window&, AK::URL const&);
|
||||
|
||||
// ^HTML::GlobalEventHandlers
|
||||
virtual EventTarget& global_event_handlers_to_event_target(FlyString const&) final { return *this; }
|
||||
|
@ -387,36 +369,17 @@ private:
|
|||
|
||||
ExceptionOr<void> run_the_document_write_steps(String);
|
||||
|
||||
void increment_referencing_node_count()
|
||||
{
|
||||
VERIFY(!m_deletion_has_begun);
|
||||
++m_referencing_node_count;
|
||||
}
|
||||
|
||||
void decrement_referencing_node_count()
|
||||
{
|
||||
VERIFY(!m_deletion_has_begun);
|
||||
VERIFY(m_referencing_node_count);
|
||||
--m_referencing_node_count;
|
||||
if (!m_referencing_node_count && !ref_count()) {
|
||||
m_deletion_has_begun = true;
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned m_referencing_node_count { 0 };
|
||||
|
||||
size_t m_next_layout_node_serial_id { 0 };
|
||||
|
||||
OwnPtr<CSS::StyleComputer> m_style_computer;
|
||||
JS::Handle<CSS::StyleSheetList> m_style_sheets;
|
||||
RefPtr<Node> m_hovered_node;
|
||||
RefPtr<Node> m_inspected_node;
|
||||
RefPtr<Node> m_active_favicon;
|
||||
JS::GCPtr<CSS::StyleSheetList> m_style_sheets;
|
||||
JS::GCPtr<Node> m_hovered_node;
|
||||
JS::GCPtr<Node> m_inspected_node;
|
||||
JS::GCPtr<Node> m_active_favicon;
|
||||
WeakPtr<HTML::BrowsingContext> m_browsing_context;
|
||||
AK::URL m_url;
|
||||
|
||||
RefPtr<HTML::Window> m_window;
|
||||
JS::GCPtr<HTML::Window> m_window;
|
||||
|
||||
RefPtr<Layout::InitialContainingBlock> m_layout_root;
|
||||
|
||||
|
@ -434,9 +397,9 @@ private:
|
|||
|
||||
OwnPtr<JS::Interpreter> m_interpreter;
|
||||
|
||||
RefPtr<HTML::HTMLScriptElement> m_pending_parsing_blocking_script;
|
||||
NonnullRefPtrVector<HTML::HTMLScriptElement> m_scripts_to_execute_when_parsing_has_finished;
|
||||
NonnullRefPtrVector<HTML::HTMLScriptElement> m_scripts_to_execute_as_soon_as_possible;
|
||||
JS::GCPtr<HTML::HTMLScriptElement> m_pending_parsing_blocking_script;
|
||||
Vector<JS::Handle<HTML::HTMLScriptElement>> m_scripts_to_execute_when_parsing_has_finished;
|
||||
Vector<JS::Handle<HTML::HTMLScriptElement>> m_scripts_to_execute_as_soon_as_possible;
|
||||
|
||||
QuirksMode m_quirks_mode { QuirksMode::No };
|
||||
|
||||
|
@ -445,11 +408,11 @@ private:
|
|||
|
||||
bool m_editable { false };
|
||||
|
||||
WeakPtr<Element> m_focused_element;
|
||||
WeakPtr<Element> m_active_element;
|
||||
JS::GCPtr<Element> m_focused_element;
|
||||
JS::GCPtr<Element> m_active_element;
|
||||
|
||||
bool m_created_for_appropriate_template_contents { false };
|
||||
RefPtr<Document> m_associated_inert_template_document;
|
||||
JS::GCPtr<Document> m_associated_inert_template_document;
|
||||
|
||||
HTML::DocumentReadyState m_readiness { HTML::DocumentReadyState::Loading };
|
||||
String m_content_type { "application/xml" };
|
||||
|
@ -457,8 +420,8 @@ private:
|
|||
|
||||
bool m_ready_for_post_load_tasks { false };
|
||||
|
||||
JS::Handle<DOMImplementation> m_implementation;
|
||||
RefPtr<HTML::HTMLScriptElement> m_current_script;
|
||||
JS::GCPtr<DOMImplementation> m_implementation;
|
||||
JS::GCPtr<HTML::HTMLScriptElement> m_current_script;
|
||||
|
||||
bool m_should_invalidate_styles_on_attribute_changes { true };
|
||||
|
||||
|
@ -506,3 +469,5 @@ private:
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(Document, Web::DOM)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/DocumentFragmentPrototype.h>
|
||||
#include <LibWeb/DOM/DocumentFragment.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
|
@ -12,12 +13,24 @@ namespace Web::DOM {
|
|||
DocumentFragment::DocumentFragment(Document& document)
|
||||
: ParentNode(document, NodeType::DOCUMENT_FRAGMENT_NODE)
|
||||
{
|
||||
set_prototype(&window().ensure_web_prototype<Bindings::DocumentFragmentPrototype>("DocumentFragment"));
|
||||
}
|
||||
|
||||
void DocumentFragment::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_host.ptr());
|
||||
}
|
||||
|
||||
void DocumentFragment::set_host(Web::DOM::Element* element)
|
||||
{
|
||||
m_host = element;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-documentfragment-documentfragment
|
||||
NonnullRefPtr<DocumentFragment> DocumentFragment::create_with_global_object(Bindings::WindowObject& window)
|
||||
JS::NonnullGCPtr<DocumentFragment> DocumentFragment::create_with_global_object(HTML::Window& window)
|
||||
{
|
||||
return make_ref_counted<DocumentFragment>(window.impl().associated_document());
|
||||
return *window.heap().allocate<DocumentFragment>(window.realm(), window.associated_document());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,27 +16,33 @@ namespace Web::DOM {
|
|||
class DocumentFragment
|
||||
: public ParentNode
|
||||
, public NonElementParentNode<DocumentFragment> {
|
||||
WEB_PLATFORM_OBJECT(DocumentFragment, ParentNode);
|
||||
|
||||
public:
|
||||
using WrapperType = Bindings::DocumentFragmentWrapper;
|
||||
static JS::NonnullGCPtr<DocumentFragment> create_with_global_object(HTML::Window& window);
|
||||
|
||||
static NonnullRefPtr<DocumentFragment> create_with_global_object(Bindings::WindowObject& window);
|
||||
|
||||
explicit DocumentFragment(Document& document);
|
||||
virtual ~DocumentFragment() override = default;
|
||||
|
||||
virtual FlyString node_name() const override { return "#document-fragment"; }
|
||||
|
||||
Element* host() { return m_host; }
|
||||
Element const* host() const { return m_host; }
|
||||
Element* host() { return m_host.ptr(); }
|
||||
Element const* host() const { return m_host.ptr(); }
|
||||
|
||||
void set_host(Element* host) { m_host = host; }
|
||||
void set_host(Element*);
|
||||
|
||||
protected:
|
||||
explicit DocumentFragment(Document& document);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
private:
|
||||
// https://dom.spec.whatwg.org/#concept-documentfragment-host
|
||||
WeakPtr<Element> m_host;
|
||||
JS::GCPtr<Element> m_host;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool Node::fast_is<DocumentFragment>() const { return is_document_fragment(); }
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(DocumentFragment, Web::DOM)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
DocumentLoadEventDelayer::DocumentLoadEventDelayer(Document& document)
|
||||
: m_document(document)
|
||||
: m_document(JS::make_handle(document))
|
||||
{
|
||||
m_document->increment_number_of_things_delaying_the_load_event({});
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
@ -21,7 +21,7 @@ public:
|
|||
~DocumentLoadEventDelayer();
|
||||
|
||||
private:
|
||||
NonnullRefPtr<Document> m_document;
|
||||
JS::Handle<Document> m_document;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/DocumentTypePrototype.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/DocumentType.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
JS::NonnullGCPtr<DocumentType> DocumentType::create(Document& document)
|
||||
{
|
||||
return *document.heap().allocate<DocumentType>(document.realm(), document);
|
||||
}
|
||||
|
||||
DocumentType::DocumentType(Document& document)
|
||||
: Node(document, NodeType::DOCUMENT_TYPE_NODE)
|
||||
{
|
||||
set_prototype(&window().ensure_web_prototype<Bindings::DocumentTypePrototype>("DocumentType"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,15 +15,11 @@ namespace Web::DOM {
|
|||
class DocumentType final
|
||||
: public Node
|
||||
, public ChildNode<DocumentType> {
|
||||
WEB_PLATFORM_OBJECT(DocumentType, Node);
|
||||
|
||||
public:
|
||||
using WrapperType = Bindings::DocumentTypeWrapper;
|
||||
static JS::NonnullGCPtr<DocumentType> create(Document&);
|
||||
|
||||
static NonnullRefPtr<DocumentType> create(Document& document)
|
||||
{
|
||||
return adopt_ref(*new DocumentType(document));
|
||||
}
|
||||
|
||||
explicit DocumentType(Document&);
|
||||
virtual ~DocumentType() override = default;
|
||||
|
||||
virtual FlyString node_name() const override { return "#doctype"; }
|
||||
|
@ -38,6 +34,8 @@ public:
|
|||
void set_system_id(String const& system_id) { m_system_id = system_id; }
|
||||
|
||||
private:
|
||||
explicit DocumentType(Document&);
|
||||
|
||||
String m_name;
|
||||
String m_public_id;
|
||||
String m_system_id;
|
||||
|
@ -47,3 +45,5 @@ template<>
|
|||
inline bool Node::fast_is<DocumentType>() const { return is_document_type(); }
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(DocumentType, Web::DOM)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibWeb/Bindings/ElementPrototype.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/CSS/PropertyID.h>
|
||||
#include <LibWeb/CSS/ResolvedCSSStyleDeclaration.h>
|
||||
|
@ -45,13 +46,24 @@ namespace Web::DOM {
|
|||
Element::Element(Document& document, DOM::QualifiedName qualified_name)
|
||||
: ParentNode(document, NodeType::ELEMENT_NODE)
|
||||
, m_qualified_name(move(qualified_name))
|
||||
, m_attributes(JS::make_handle(NamedNodeMap::create(*this)))
|
||||
, m_attributes(NamedNodeMap::create(*this))
|
||||
{
|
||||
set_prototype(&document.window().ensure_web_prototype<Bindings::ElementPrototype>("Element"));
|
||||
|
||||
make_html_uppercased_qualified_name();
|
||||
}
|
||||
|
||||
Element::~Element() = default;
|
||||
|
||||
void Element::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_attributes.ptr());
|
||||
visitor.visit(m_inline_style.ptr());
|
||||
visitor.visit(m_class_list.ptr());
|
||||
visitor.visit(m_shadow_root.ptr());
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-element-getattribute
|
||||
String Element::get_attribute(FlyString const& name) const
|
||||
{
|
||||
|
@ -295,7 +307,7 @@ RefPtr<Layout::Node> Element::create_layout_node_for_display_type(DOM::Document&
|
|||
|
||||
CSS::CSSStyleDeclaration const* Element::inline_style() const
|
||||
{
|
||||
return m_inline_style.cell();
|
||||
return m_inline_style.ptr();
|
||||
}
|
||||
|
||||
void Element::parse_attribute(FlyString const& name, String const& value)
|
||||
|
@ -307,13 +319,13 @@ void Element::parse_attribute(FlyString const& name, String const& value)
|
|||
for (auto& new_class : new_classes) {
|
||||
m_classes.unchecked_append(new_class);
|
||||
}
|
||||
if (m_class_list.cell())
|
||||
if (m_class_list)
|
||||
m_class_list->associated_attribute_changed(value);
|
||||
} else if (name == HTML::AttributeNames::style) {
|
||||
// https://drafts.csswg.org/cssom/#ref-for-cssstyledeclaration-updating-flag
|
||||
if (m_inline_style.cell() && m_inline_style->is_updating())
|
||||
if (m_inline_style && m_inline_style->is_updating())
|
||||
return;
|
||||
m_inline_style = JS::make_handle(parse_css_style_attribute(CSS::Parser::ParsingContext(document()), value, *this));
|
||||
m_inline_style = parse_css_style_attribute(CSS::Parser::ParsingContext(document()), value, *this);
|
||||
set_needs_style_update(true);
|
||||
}
|
||||
}
|
||||
|
@ -321,8 +333,8 @@ void Element::parse_attribute(FlyString const& name, String const& value)
|
|||
void Element::did_remove_attribute(FlyString const& name)
|
||||
{
|
||||
if (name == HTML::AttributeNames::style) {
|
||||
if (m_inline_style.cell()) {
|
||||
m_inline_style = {};
|
||||
if (m_inline_style) {
|
||||
m_inline_style = nullptr;
|
||||
set_needs_style_update(true);
|
||||
}
|
||||
}
|
||||
|
@ -414,9 +426,9 @@ NonnullRefPtr<CSS::StyleProperties> Element::resolved_css_values()
|
|||
|
||||
DOMTokenList* Element::class_list()
|
||||
{
|
||||
if (!m_class_list.cell())
|
||||
m_class_list = JS::make_handle(DOMTokenList::create(*this, HTML::AttributeNames::class_));
|
||||
return m_class_list.cell();
|
||||
if (!m_class_list)
|
||||
m_class_list = DOMTokenList::create(*this, HTML::AttributeNames::class_);
|
||||
return m_class_list;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-element-matches
|
||||
|
@ -495,7 +507,7 @@ NonnullRefPtr<HTMLCollection> Element::get_elements_by_class_name(FlyString cons
|
|||
});
|
||||
}
|
||||
|
||||
void Element::set_shadow_root(RefPtr<ShadowRoot> shadow_root)
|
||||
void Element::set_shadow_root(JS::GCPtr<ShadowRoot> shadow_root)
|
||||
{
|
||||
if (m_shadow_root == shadow_root)
|
||||
return;
|
||||
|
@ -509,9 +521,9 @@ void Element::set_shadow_root(RefPtr<ShadowRoot> shadow_root)
|
|||
|
||||
CSS::CSSStyleDeclaration* Element::style_for_bindings()
|
||||
{
|
||||
if (m_inline_style.is_null())
|
||||
m_inline_style = JS::make_handle(CSS::ElementInlineCSSStyleDeclaration::create(*this, {}, {}));
|
||||
return m_inline_style.cell();
|
||||
if (!m_inline_style)
|
||||
m_inline_style = CSS::ElementInlineCSSStyleDeclaration::create(*this, {}, {});
|
||||
return m_inline_style;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#element-html-uppercased-qualified-name
|
||||
|
@ -527,7 +539,7 @@ void Element::make_html_uppercased_qualified_name()
|
|||
// https://html.spec.whatwg.org/multipage/webappapis.html#queue-an-element-task
|
||||
void Element::queue_an_element_task(HTML::Task::Source source, Function<void()> steps)
|
||||
{
|
||||
auto task = HTML::Task::create(source, &document(), [strong_this = NonnullRefPtr(*this), steps = move(steps)] {
|
||||
auto task = HTML::Task::create(source, &document(), [strong_this = JS::make_handle(*this), steps = move(steps)] {
|
||||
steps();
|
||||
});
|
||||
HTML::main_thread_event_loop().task_queue().add(move(task));
|
||||
|
|
|
@ -29,10 +29,9 @@ class Element
|
|||
: public ParentNode
|
||||
, public ChildNode<Element>
|
||||
, public NonDocumentTypeChildNode<Element> {
|
||||
WEB_PLATFORM_OBJECT(Element, ParentNode);
|
||||
|
||||
public:
|
||||
using WrapperType = Bindings::ElementWrapper;
|
||||
|
||||
Element(Document&, DOM::QualifiedName);
|
||||
virtual ~Element() override;
|
||||
|
||||
|
@ -59,7 +58,7 @@ public:
|
|||
void remove_attribute(FlyString const& name);
|
||||
DOM::ExceptionOr<bool> toggle_attribute(FlyString const& name, Optional<bool> force);
|
||||
size_t attribute_list_size() const { return m_attributes->length(); }
|
||||
NamedNodeMap const* attributes() const { return m_attributes.cell(); }
|
||||
NamedNodeMap const* attributes() const { return m_attributes.ptr(); }
|
||||
Vector<String> get_attribute_names() const;
|
||||
|
||||
DOMTokenList* class_list();
|
||||
|
@ -115,9 +114,9 @@ public:
|
|||
|
||||
NonnullRefPtr<HTMLCollection> get_elements_by_class_name(FlyString const&);
|
||||
|
||||
ShadowRoot* shadow_root() { return m_shadow_root; }
|
||||
ShadowRoot const* shadow_root() const { return m_shadow_root; }
|
||||
void set_shadow_root(RefPtr<ShadowRoot>);
|
||||
ShadowRoot* shadow_root() { return m_shadow_root.ptr(); }
|
||||
ShadowRoot const* shadow_root() const { return m_shadow_root.ptr(); }
|
||||
void set_shadow_root(JS::GCPtr<ShadowRoot>);
|
||||
|
||||
void set_custom_properties(HashMap<FlyString, CSS::StyleProperty> custom_properties) { m_custom_properties = move(custom_properties); }
|
||||
HashMap<FlyString, CSS::StyleProperty> const& custom_properties() const { return m_custom_properties; }
|
||||
|
@ -145,23 +144,24 @@ public:
|
|||
protected:
|
||||
virtual void children_changed() override;
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
private:
|
||||
void make_html_uppercased_qualified_name();
|
||||
|
||||
QualifiedName m_qualified_name;
|
||||
String m_html_uppercased_qualified_name;
|
||||
JS::Handle<NamedNodeMap> m_attributes;
|
||||
|
||||
JS::Handle<CSS::ElementInlineCSSStyleDeclaration> m_inline_style;
|
||||
JS::NonnullGCPtr<NamedNodeMap> m_attributes;
|
||||
JS::GCPtr<CSS::ElementInlineCSSStyleDeclaration> m_inline_style;
|
||||
JS::GCPtr<DOMTokenList> m_class_list;
|
||||
JS::GCPtr<ShadowRoot> m_shadow_root;
|
||||
|
||||
RefPtr<CSS::StyleProperties> m_computed_css_values;
|
||||
HashMap<FlyString, CSS::StyleProperty> m_custom_properties;
|
||||
|
||||
JS::Handle<DOMTokenList> m_class_list;
|
||||
Vector<FlyString> m_classes;
|
||||
|
||||
RefPtr<ShadowRoot> m_shadow_root;
|
||||
|
||||
Array<RefPtr<Layout::Node>, CSS::Selector::PseudoElementCount> m_pseudo_element_nodes;
|
||||
};
|
||||
|
||||
|
@ -171,3 +171,5 @@ inline bool Node::fast_is<Element>() const { return is_element(); }
|
|||
ExceptionOr<QualifiedName> validate_and_extract(FlyString namespace_, FlyString qualified_name);
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(Element, Web::DOM)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2020, Luke Wilde <lukew@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/ElementFactory.h>
|
||||
#include <LibWeb/HTML/HTMLAnchorElement.h>
|
||||
#include <LibWeb/HTML/HTMLAreaElement.h>
|
||||
|
@ -93,7 +94,7 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-create-element
|
||||
NonnullRefPtr<Element> create_element(Document& document, FlyString local_name, FlyString namespace_, FlyString prefix)
|
||||
JS::NonnullGCPtr<Element> create_element(Document& document, FlyString local_name, FlyString namespace_, FlyString prefix)
|
||||
{
|
||||
// 1. If prefix was not given, let prefix be null.
|
||||
// NOTE: This is already taken care of by `prefix` having a default value.
|
||||
|
@ -113,182 +114,183 @@ NonnullRefPtr<Element> create_element(Document& document, FlyString local_name,
|
|||
// then set result’s custom element state to "undefined".
|
||||
// 8. Return result.
|
||||
|
||||
auto& realm = document.realm();
|
||||
auto lowercase_tag_name = local_name.to_lowercase();
|
||||
|
||||
auto qualified_name = QualifiedName { local_name, prefix, namespace_ };
|
||||
if (lowercase_tag_name == HTML::TagNames::a)
|
||||
return adopt_ref(*new HTML::HTMLAnchorElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLAnchorElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::area)
|
||||
return adopt_ref(*new HTML::HTMLAreaElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLAreaElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::audio)
|
||||
return adopt_ref(*new HTML::HTMLAudioElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLAudioElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::base)
|
||||
return adopt_ref(*new HTML::HTMLBaseElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLBaseElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::blink)
|
||||
return adopt_ref(*new HTML::HTMLBlinkElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLBlinkElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::body)
|
||||
return adopt_ref(*new HTML::HTMLBodyElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLBodyElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::br)
|
||||
return adopt_ref(*new HTML::HTMLBRElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLBRElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::button)
|
||||
return adopt_ref(*new HTML::HTMLButtonElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLButtonElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::canvas)
|
||||
return adopt_ref(*new HTML::HTMLCanvasElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLCanvasElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::data)
|
||||
return adopt_ref(*new HTML::HTMLDataElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLDataElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::datalist)
|
||||
return adopt_ref(*new HTML::HTMLDataListElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLDataListElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::details)
|
||||
return adopt_ref(*new HTML::HTMLDetailsElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLDetailsElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::dialog)
|
||||
return adopt_ref(*new HTML::HTMLDialogElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLDialogElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::dir)
|
||||
return adopt_ref(*new HTML::HTMLDirectoryElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLDirectoryElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::div)
|
||||
return adopt_ref(*new HTML::HTMLDivElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLDivElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::dl)
|
||||
return adopt_ref(*new HTML::HTMLDListElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLDListElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::embed)
|
||||
return adopt_ref(*new HTML::HTMLEmbedElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLEmbedElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::fieldset)
|
||||
return adopt_ref(*new HTML::HTMLFieldSetElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLFieldSetElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::font)
|
||||
return adopt_ref(*new HTML::HTMLFontElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLFontElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::form)
|
||||
return adopt_ref(*new HTML::HTMLFormElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLFormElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::frame)
|
||||
return adopt_ref(*new HTML::HTMLFrameElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLFrameElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::frameset)
|
||||
return adopt_ref(*new HTML::HTMLFrameSetElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLFrameSetElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::head)
|
||||
return adopt_ref(*new HTML::HTMLHeadElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLHeadElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name.is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6))
|
||||
return adopt_ref(*new HTML::HTMLHeadingElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLHeadingElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::hr)
|
||||
return adopt_ref(*new HTML::HTMLHRElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLHRElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::html)
|
||||
return adopt_ref(*new HTML::HTMLHtmlElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLHtmlElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::iframe)
|
||||
return adopt_ref(*new HTML::HTMLIFrameElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLIFrameElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::img)
|
||||
return adopt_ref(*new HTML::HTMLImageElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLImageElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::input)
|
||||
return adopt_ref(*new HTML::HTMLInputElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLInputElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::label)
|
||||
return adopt_ref(*new HTML::HTMLLabelElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLLabelElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::legend)
|
||||
return adopt_ref(*new HTML::HTMLLegendElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLLegendElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::li)
|
||||
return adopt_ref(*new HTML::HTMLLIElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLLIElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::link)
|
||||
return adopt_ref(*new HTML::HTMLLinkElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLLinkElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::map)
|
||||
return adopt_ref(*new HTML::HTMLMapElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLMapElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::marquee)
|
||||
return adopt_ref(*new HTML::HTMLMarqueeElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLMarqueeElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::menu)
|
||||
return adopt_ref(*new HTML::HTMLMenuElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLMenuElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::meta)
|
||||
return adopt_ref(*new HTML::HTMLMetaElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLMetaElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::meter)
|
||||
return adopt_ref(*new HTML::HTMLMeterElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLMeterElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name.is_one_of(HTML::TagNames::ins, HTML::TagNames::del))
|
||||
return adopt_ref(*new HTML::HTMLModElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLModElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::object)
|
||||
return adopt_ref(*new HTML::HTMLObjectElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLObjectElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::ol)
|
||||
return adopt_ref(*new HTML::HTMLOListElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLOListElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::optgroup)
|
||||
return adopt_ref(*new HTML::HTMLOptGroupElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLOptGroupElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::option)
|
||||
return adopt_ref(*new HTML::HTMLOptionElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLOptionElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::output)
|
||||
return adopt_ref(*new HTML::HTMLOutputElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLOutputElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::p)
|
||||
return adopt_ref(*new HTML::HTMLParagraphElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLParagraphElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::param)
|
||||
return adopt_ref(*new HTML::HTMLParamElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLParamElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::picture)
|
||||
return adopt_ref(*new HTML::HTMLPictureElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLPictureElement>(realm, document, move(qualified_name));
|
||||
// NOTE: The obsolete elements "listing" and "xmp" are explicitly mapped to HTMLPreElement in the specification.
|
||||
if (lowercase_tag_name.is_one_of(HTML::TagNames::pre, HTML::TagNames::listing, HTML::TagNames::xmp))
|
||||
return adopt_ref(*new HTML::HTMLPreElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLPreElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::progress)
|
||||
return adopt_ref(*new HTML::HTMLProgressElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLProgressElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name.is_one_of(HTML::TagNames::blockquote, HTML::TagNames::q))
|
||||
return adopt_ref(*new HTML::HTMLQuoteElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLQuoteElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::script)
|
||||
return adopt_ref(*new HTML::HTMLScriptElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLScriptElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::select)
|
||||
return adopt_ref(*new HTML::HTMLSelectElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLSelectElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::slot)
|
||||
return adopt_ref(*new HTML::HTMLSlotElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLSlotElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::source)
|
||||
return adopt_ref(*new HTML::HTMLSourceElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLSourceElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::span)
|
||||
return adopt_ref(*new HTML::HTMLSpanElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLSpanElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::style)
|
||||
return adopt_ref(*new HTML::HTMLStyleElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLStyleElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::caption)
|
||||
return adopt_ref(*new HTML::HTMLTableCaptionElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTableCaptionElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name.is_one_of(Web::HTML::TagNames::td, Web::HTML::TagNames::th))
|
||||
return adopt_ref(*new HTML::HTMLTableCellElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTableCellElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name.is_one_of(HTML::TagNames::colgroup, HTML::TagNames::col))
|
||||
return adopt_ref(*new HTML::HTMLTableColElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTableColElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::table)
|
||||
return adopt_ref(*new HTML::HTMLTableElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTableElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::tr)
|
||||
return adopt_ref(*new HTML::HTMLTableRowElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTableRowElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name.is_one_of(HTML::TagNames::tbody, HTML::TagNames::thead, HTML::TagNames::tfoot))
|
||||
return adopt_ref(*new HTML::HTMLTableSectionElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTableSectionElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::template_)
|
||||
return adopt_ref(*new HTML::HTMLTemplateElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTemplateElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::textarea)
|
||||
return adopt_ref(*new HTML::HTMLTextAreaElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTextAreaElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::time)
|
||||
return adopt_ref(*new HTML::HTMLTimeElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTimeElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::title)
|
||||
return adopt_ref(*new HTML::HTMLTitleElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTitleElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::track)
|
||||
return adopt_ref(*new HTML::HTMLTrackElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLTrackElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::ul)
|
||||
return adopt_ref(*new HTML::HTMLUListElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLUListElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == HTML::TagNames::video)
|
||||
return adopt_ref(*new HTML::HTMLVideoElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLVideoElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name.is_one_of(
|
||||
HTML::TagNames::article, HTML::TagNames::section, HTML::TagNames::nav, HTML::TagNames::aside, HTML::TagNames::hgroup, HTML::TagNames::header, HTML::TagNames::footer, HTML::TagNames::address, HTML::TagNames::dt, HTML::TagNames::dd, HTML::TagNames::figure, HTML::TagNames::figcaption, HTML::TagNames::main, HTML::TagNames::em, HTML::TagNames::strong, HTML::TagNames::small, HTML::TagNames::s, HTML::TagNames::cite, HTML::TagNames::dfn, HTML::TagNames::abbr, HTML::TagNames::ruby, HTML::TagNames::rt, HTML::TagNames::rp, HTML::TagNames::code, HTML::TagNames::var, HTML::TagNames::samp, HTML::TagNames::kbd, HTML::TagNames::sub, HTML::TagNames::sup, HTML::TagNames::i, HTML::TagNames::b, HTML::TagNames::u, HTML::TagNames::mark, HTML::TagNames::bdi, HTML::TagNames::bdo, HTML::TagNames::wbr, HTML::TagNames::summary, HTML::TagNames::noscript,
|
||||
// Obsolete
|
||||
HTML::TagNames::acronym, HTML::TagNames::basefont, HTML::TagNames::big, HTML::TagNames::center, HTML::TagNames::nobr, HTML::TagNames::noembed, HTML::TagNames::noframes, HTML::TagNames::plaintext, HTML::TagNames::rb, HTML::TagNames::rtc, HTML::TagNames::strike, HTML::TagNames::tt))
|
||||
return adopt_ref(*new HTML::HTMLElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::svg)
|
||||
return adopt_ref(*new SVG::SVGSVGElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGSVGElement>(realm, document, move(qualified_name));
|
||||
// FIXME: Support SVG's mixedCase tag names properly.
|
||||
if (lowercase_tag_name.equals_ignoring_case(SVG::TagNames::clipPath))
|
||||
return adopt_ref(*new SVG::SVGClipPathElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGClipPathElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::circle)
|
||||
return adopt_ref(*new SVG::SVGCircleElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGCircleElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name.equals_ignoring_case(SVG::TagNames::defs))
|
||||
return adopt_ref(*new SVG::SVGDefsElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGDefsElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::ellipse)
|
||||
return adopt_ref(*new SVG::SVGEllipseElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGEllipseElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::line)
|
||||
return adopt_ref(*new SVG::SVGLineElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGLineElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::path)
|
||||
return adopt_ref(*new SVG::SVGPathElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGPathElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::polygon)
|
||||
return adopt_ref(*new SVG::SVGPolygonElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGPolygonElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::polyline)
|
||||
return adopt_ref(*new SVG::SVGPolylineElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGPolylineElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::rect)
|
||||
return adopt_ref(*new SVG::SVGRectElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGRectElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::g)
|
||||
return adopt_ref(*new SVG::SVGGElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGGElement>(realm, document, move(qualified_name));
|
||||
if (lowercase_tag_name == SVG::TagNames::text)
|
||||
return adopt_ref(*new SVG::SVGTextContentElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<SVG::SVGTextContentElement>(realm, document, move(qualified_name));
|
||||
|
||||
// FIXME: If name is a valid custom element name, then return HTMLElement.
|
||||
|
||||
return adopt_ref(*new HTML::HTMLUnknownElement(document, move(qualified_name)));
|
||||
return *realm.heap().allocate<HTML::HTMLUnknownElement>(realm, document, move(qualified_name));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@
|
|||
|
||||
namespace Web::DOM {
|
||||
|
||||
NonnullRefPtr<Element> create_element(Document&, FlyString local_name, FlyString namespace_, FlyString prefix = {});
|
||||
JS::NonnullGCPtr<Element> create_element(Document&, FlyString local_name, FlyString namespace_, FlyString prefix = {});
|
||||
|
||||
}
|
||||
|
|
|
@ -8,31 +8,31 @@
|
|||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibWeb/Bindings/EventPrototype.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/Event.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/DOM/ShadowRoot.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
JS::NonnullGCPtr<Event> Event::create(Bindings::WindowObject& window_object, FlyString const& event_name, EventInit const& event_init)
|
||||
JS::NonnullGCPtr<Event> Event::create(HTML::Window& window_object, FlyString const& event_name, EventInit const& event_init)
|
||||
{
|
||||
return *window_object.heap().allocate<Event>(window_object.realm(), window_object, event_name, event_init);
|
||||
}
|
||||
|
||||
JS::NonnullGCPtr<Event> Event::create_with_global_object(Bindings::WindowObject& window_object, FlyString const& event_name, EventInit const& event_init)
|
||||
JS::NonnullGCPtr<Event> Event::create_with_global_object(HTML::Window& window_object, FlyString const& event_name, EventInit const& event_init)
|
||||
{
|
||||
return create(window_object, event_name, event_init);
|
||||
}
|
||||
|
||||
Event::Event(Bindings::WindowObject& window_object, FlyString const& type)
|
||||
Event::Event(HTML::Window& window_object, FlyString const& type)
|
||||
: PlatformObject(window_object.ensure_web_prototype<Bindings::EventPrototype>("Event"))
|
||||
, m_type(type)
|
||||
, m_initialized(true)
|
||||
{
|
||||
}
|
||||
|
||||
Event::Event(Bindings::WindowObject& window_object, FlyString const& type, EventInit const& event_init)
|
||||
Event::Event(HTML::Window& window_object, FlyString const& type, EventInit const& event_init)
|
||||
: PlatformObject(window_object.ensure_web_prototype<Bindings::EventPrototype>("Event"))
|
||||
, m_type(type)
|
||||
, m_bubbles(event_init.bubbles)
|
||||
|
@ -43,7 +43,7 @@ Event::Event(Bindings::WindowObject& window_object, FlyString const& type, Event
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-event-path-append
|
||||
void Event::append_to_path(EventTarget& invocation_target, RefPtr<EventTarget> shadow_adjusted_target, RefPtr<EventTarget> related_target, TouchTargetList& touch_targets, bool slot_in_closed_tree)
|
||||
void Event::append_to_path(EventTarget& invocation_target, JS::GCPtr<EventTarget> shadow_adjusted_target, JS::GCPtr<EventTarget> related_target, TouchTargetList& touch_targets, bool slot_in_closed_tree)
|
||||
{
|
||||
// 1. Let invocationTargetInShadowTree be false.
|
||||
bool invocation_target_in_shadow_tree = false;
|
||||
|
@ -120,10 +120,10 @@ double Event::time_stamp() const
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-event-composedpath
|
||||
NonnullRefPtrVector<EventTarget> Event::composed_path() const
|
||||
Vector<JS::Handle<EventTarget>> Event::composed_path() const
|
||||
{
|
||||
// 1. Let composedPath be an empty list.
|
||||
NonnullRefPtrVector<EventTarget> composed_path;
|
||||
Vector<JS::Handle<EventTarget>> composed_path;
|
||||
|
||||
// 2. Let path be this’s path. (NOTE: Not necessary)
|
||||
|
||||
|
@ -136,7 +136,7 @@ NonnullRefPtrVector<EventTarget> Event::composed_path() const
|
|||
// 5. Append currentTarget to composedPath.
|
||||
// NOTE: If path is not empty, then the event is being dispatched and will have a currentTarget.
|
||||
VERIFY(m_current_target);
|
||||
composed_path.append(*m_current_target);
|
||||
composed_path.append(const_cast<EventTarget*>(m_current_target.ptr()));
|
||||
|
||||
// 6. Let currentTargetIndex be 0.
|
||||
size_t current_target_index = 0;
|
||||
|
@ -182,7 +182,7 @@ NonnullRefPtrVector<EventTarget> Event::composed_path() const
|
|||
// 2. If currentHiddenLevel is less than or equal to maxHiddenLevel, then prepend path[index]'s invocation target to composedPath.
|
||||
if (current_hidden_level <= max_hidden_level) {
|
||||
VERIFY(path_entry.invocation_target);
|
||||
composed_path.prepend(*path_entry.invocation_target);
|
||||
composed_path.prepend(const_cast<EventTarget*>(path_entry.invocation_target.ptr()));
|
||||
}
|
||||
|
||||
// 3. If path[index]'s slot-in-closed-tree is true, then:
|
||||
|
@ -214,7 +214,7 @@ NonnullRefPtrVector<EventTarget> Event::composed_path() const
|
|||
// 2. If currentHiddenLevel is less than or equal to maxHiddenLevel, then append path[index]'s invocation target to composedPath.
|
||||
if (current_hidden_level <= max_hidden_level) {
|
||||
VERIFY(path_entry.invocation_target);
|
||||
composed_path.append(*path_entry.invocation_target);
|
||||
composed_path.append(const_cast<EventTarget*>(path_entry.invocation_target.ptr()));
|
||||
}
|
||||
|
||||
// 3. If path[index]'s root-of-closed-tree is true, then:
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
#include <AK/FlyString.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
|
@ -20,7 +20,7 @@ struct EventInit {
|
|||
};
|
||||
|
||||
class Event : public Bindings::PlatformObject {
|
||||
JS_OBJECT(Event, Bindings::PlatformObject);
|
||||
WEB_PLATFORM_OBJECT(Event, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
enum Phase : u16 {
|
||||
|
@ -30,13 +30,14 @@ public:
|
|||
BubblingPhase = 3,
|
||||
};
|
||||
|
||||
using TouchTargetList = Vector<RefPtr<EventTarget>>;
|
||||
// FIXME: These need explicit marking somehow.
|
||||
using TouchTargetList = Vector<JS::GCPtr<EventTarget>>;
|
||||
|
||||
struct PathEntry {
|
||||
RefPtr<EventTarget> invocation_target;
|
||||
JS::GCPtr<EventTarget> invocation_target;
|
||||
bool invocation_target_in_shadow_tree { false };
|
||||
RefPtr<EventTarget> shadow_adjusted_target;
|
||||
RefPtr<EventTarget> related_target;
|
||||
JS::GCPtr<EventTarget> shadow_adjusted_target;
|
||||
JS::GCPtr<EventTarget> related_target;
|
||||
TouchTargetList touch_target_list;
|
||||
bool root_of_closed_tree { false };
|
||||
bool slot_in_closed_tree { false };
|
||||
|
@ -45,28 +46,26 @@ public:
|
|||
|
||||
using Path = Vector<PathEntry>;
|
||||
|
||||
static JS::NonnullGCPtr<Event> create(Bindings::WindowObject&, FlyString const& event_name, EventInit const& event_init = {});
|
||||
static JS::NonnullGCPtr<Event> create_with_global_object(Bindings::WindowObject&, FlyString const& event_name, EventInit const& event_init);
|
||||
static JS::NonnullGCPtr<Event> create(HTML::Window&, FlyString const& event_name, EventInit const& event_init = {});
|
||||
static JS::NonnullGCPtr<Event> create_with_global_object(HTML::Window&, FlyString const& event_name, EventInit const& event_init);
|
||||
|
||||
Event(Bindings::WindowObject&, FlyString const& type);
|
||||
Event(Bindings::WindowObject&, FlyString const& type, EventInit const& event_init);
|
||||
Event(HTML::Window&, FlyString const& type);
|
||||
Event(HTML::Window&, FlyString const& type, EventInit const& event_init);
|
||||
|
||||
virtual ~Event() = default;
|
||||
|
||||
Event& impl() { return *this; }
|
||||
|
||||
double time_stamp() const;
|
||||
|
||||
FlyString const& type() const { return m_type; }
|
||||
void set_type(StringView type) { m_type = type; }
|
||||
|
||||
RefPtr<EventTarget> target() const { return m_target; }
|
||||
JS::GCPtr<EventTarget> target() const { return m_target; }
|
||||
void set_target(EventTarget* target) { m_target = target; }
|
||||
|
||||
// NOTE: This is intended for the JS bindings.
|
||||
RefPtr<EventTarget> src_element() const { return target(); }
|
||||
JS::GCPtr<EventTarget> src_element() const { return target(); }
|
||||
|
||||
RefPtr<EventTarget> related_target() const { return m_related_target; }
|
||||
JS::GCPtr<EventTarget> related_target() const { return m_related_target; }
|
||||
void set_related_target(EventTarget* related_target) { m_related_target = related_target; }
|
||||
|
||||
bool should_stop_propagation() const { return m_stop_propagation; }
|
||||
|
@ -96,7 +95,7 @@ public:
|
|||
u16 event_phase() const { return m_phase; }
|
||||
void set_phase(Phase phase) { m_phase = phase; }
|
||||
|
||||
RefPtr<EventTarget> current_target() const { return m_current_target; }
|
||||
JS::GCPtr<EventTarget> current_target() const { return m_current_target; }
|
||||
void set_current_target(EventTarget* current_target) { m_current_target = current_target; }
|
||||
|
||||
bool return_value() const { return !m_cancelled; }
|
||||
|
@ -106,7 +105,7 @@ public:
|
|||
set_cancelled_flag();
|
||||
}
|
||||
|
||||
void append_to_path(EventTarget&, RefPtr<EventTarget>, RefPtr<EventTarget>, TouchTargetList&, bool);
|
||||
void append_to_path(EventTarget&, JS::GCPtr<EventTarget>, JS::GCPtr<EventTarget>, TouchTargetList&, bool);
|
||||
Path& path() { return m_path; }
|
||||
Path const& path() const { return m_path; }
|
||||
void clear_path() { m_path.clear(); }
|
||||
|
@ -143,16 +142,16 @@ public:
|
|||
|
||||
void set_time_stamp(double time_stamp) { m_time_stamp = time_stamp; }
|
||||
|
||||
NonnullRefPtrVector<EventTarget> composed_path() const;
|
||||
Vector<JS::Handle<EventTarget>> composed_path() const;
|
||||
|
||||
protected:
|
||||
void initialize_event(String const&, bool, bool);
|
||||
|
||||
private:
|
||||
FlyString m_type;
|
||||
RefPtr<EventTarget> m_target;
|
||||
RefPtr<EventTarget> m_related_target;
|
||||
RefPtr<EventTarget> m_current_target;
|
||||
JS::GCPtr<EventTarget> m_target;
|
||||
JS::GCPtr<EventTarget> m_related_target;
|
||||
JS::GCPtr<EventTarget> m_current_target;
|
||||
|
||||
Phase m_phase { None };
|
||||
|
||||
|
@ -179,7 +178,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace Web::Bindings {
|
||||
inline JS::Object* wrap(JS::Realm&, Web::DOM::Event& object) { return &object; }
|
||||
using EventWrapper = Web::DOM::Event;
|
||||
}
|
||||
WRAPPER_HACK(Event, Web::DOM)
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibWeb/Bindings/EventTargetWrapper.h>
|
||||
#include <LibWeb/Bindings/EventTargetWrapperFactory.h>
|
||||
#include <LibWeb/Bindings/IDLAbstractOperations.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/AbortSignal.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Event.h>
|
||||
|
@ -93,8 +90,8 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<JS::Handle<DOM::DOMEvent
|
|||
Event* current_event = nullptr;
|
||||
|
||||
// 8. If global is a Window object, then:
|
||||
if (is<Bindings::WindowObject>(global)) {
|
||||
auto& bindings_window_global = verify_cast<Bindings::WindowObject>(global);
|
||||
if (is<HTML::Window>(global)) {
|
||||
auto& bindings_window_global = verify_cast<HTML::Window>(global);
|
||||
auto& window_impl = bindings_window_global.impl();
|
||||
|
||||
// 1. Set currentEvent to global’s current event.
|
||||
|
@ -111,8 +108,8 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<JS::Handle<DOM::DOMEvent
|
|||
|
||||
// 10. Call a user object’s operation with listener’s callback, "handleEvent", « event », and event’s currentTarget attribute value. If this throws an exception, then:
|
||||
// FIXME: These should be wrapped for us in call_user_object_operation, but it currently doesn't do that.
|
||||
auto* this_value = Bindings::wrap(realm, *event.current_target());
|
||||
auto* wrapped_event = Bindings::wrap(realm, event);
|
||||
auto* this_value = event.current_target().ptr();
|
||||
auto* wrapped_event = &event;
|
||||
auto result = Bindings::IDL::call_user_object_operation(callback, "handleEvent", this_value, wrapped_event);
|
||||
|
||||
// If this throws an exception, then:
|
||||
|
@ -127,8 +124,8 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<JS::Handle<DOM::DOMEvent
|
|||
event.set_in_passive_listener(false);
|
||||
|
||||
// 12. If global is a Window object, then set global’s current event to currentEvent.
|
||||
if (is<Bindings::WindowObject>(global)) {
|
||||
auto& bindings_window_global = verify_cast<Bindings::WindowObject>(global);
|
||||
if (is<HTML::Window>(global)) {
|
||||
auto& bindings_window_global = verify_cast<HTML::Window>(global);
|
||||
auto& window_impl = bindings_window_global.impl();
|
||||
window_impl.set_current_event(current_event);
|
||||
}
|
||||
|
@ -146,17 +143,17 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<JS::Handle<DOM::DOMEvent
|
|||
void EventDispatcher::invoke(Event::PathEntry& struct_, Event& event, Event::Phase phase)
|
||||
{
|
||||
auto last_valid_shadow_adjusted_target = event.path().last_matching([&struct_](auto& entry) {
|
||||
return entry.index <= struct_.index && !entry.shadow_adjusted_target.is_null();
|
||||
return entry.index <= struct_.index && entry.shadow_adjusted_target;
|
||||
});
|
||||
|
||||
VERIFY(last_valid_shadow_adjusted_target.has_value());
|
||||
|
||||
// 1. Set event’s target to the shadow-adjusted target of the last struct in event’s path,
|
||||
// that is either struct or preceding struct, whose shadow-adjusted target is non-null.
|
||||
event.set_target(last_valid_shadow_adjusted_target.value().shadow_adjusted_target);
|
||||
event.set_target(last_valid_shadow_adjusted_target.value().shadow_adjusted_target.ptr());
|
||||
|
||||
// 2. Set event’s relatedTarget to struct’s relatedTarget.
|
||||
event.set_related_target(struct_.related_target);
|
||||
event.set_related_target(struct_.related_target.ptr());
|
||||
|
||||
// 3. Set event’s touch target list to struct’s touch target list.
|
||||
event.set_touch_target_list(struct_.touch_target_list);
|
||||
|
@ -166,7 +163,7 @@ void EventDispatcher::invoke(Event::PathEntry& struct_, Event& event, Event::Pha
|
|||
return;
|
||||
|
||||
// 5. Initialize event’s currentTarget attribute to struct’s invocation target.
|
||||
event.set_current_target(struct_.invocation_target);
|
||||
event.set_current_target(struct_.invocation_target.ptr());
|
||||
|
||||
// 6. Let listeners be a clone of event’s currentTarget attribute value’s event listener list.
|
||||
// NOTE: This avoids event listeners added after this point from being run. Note that removal still has an effect due to the removed field.
|
||||
|
@ -205,25 +202,25 @@ void EventDispatcher::invoke(Event::PathEntry& struct_, Event& event, Event::Pha
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-event-dispatch
|
||||
bool EventDispatcher::dispatch(NonnullRefPtr<EventTarget> target, Event& event, bool legacy_target_override)
|
||||
bool EventDispatcher::dispatch(JS::NonnullGCPtr<EventTarget> target, Event& event, bool legacy_target_override)
|
||||
{
|
||||
// 1. Set event’s dispatch flag.
|
||||
event.set_dispatched(true);
|
||||
|
||||
// 2. Let targetOverride be target, if legacy target override flag is not given, and target’s associated Document otherwise. [HTML]
|
||||
// NOTE: legacy target override flag is only used by HTML and only when target is a Window object.
|
||||
RefPtr<EventTarget> target_override;
|
||||
JS::GCPtr<EventTarget> target_override;
|
||||
if (!legacy_target_override) {
|
||||
target_override = target;
|
||||
} else {
|
||||
target_override = verify_cast<HTML::Window>(*target).associated_document();
|
||||
target_override = &verify_cast<HTML::Window>(*target).associated_document();
|
||||
}
|
||||
|
||||
// 3. Let activationTarget be null.
|
||||
RefPtr<EventTarget> activation_target;
|
||||
JS::GCPtr<EventTarget> activation_target;
|
||||
|
||||
// 4. Let relatedTarget be the result of retargeting event’s relatedTarget against target.
|
||||
RefPtr<EventTarget> related_target = retarget(event.related_target(), target);
|
||||
JS::GCPtr<EventTarget> related_target = retarget(event.related_target(), target);
|
||||
|
||||
bool clear_targets = false;
|
||||
// 5. If target is not relatedTarget or target is event’s relatedTarget, then:
|
||||
|
@ -285,12 +282,12 @@ bool EventDispatcher::dispatch(NonnullRefPtr<EventTarget> target, Event& event,
|
|||
|
||||
}
|
||||
// 7. Otherwise, if parent is relatedTarget, then set parent to null.
|
||||
else if (related_target == parent) {
|
||||
else if (related_target.ptr() == parent) {
|
||||
parent = nullptr;
|
||||
}
|
||||
// 8. Otherwise, set target to parent and then:
|
||||
else {
|
||||
target = *parent;
|
||||
target = parent;
|
||||
|
||||
// 1. If isActivationEvent is true, activationTarget is null, and target has activation behavior, then set activationTarget to target.
|
||||
if (is_activation_event && !activation_target && target->activation_behavior)
|
||||
|
@ -311,7 +308,7 @@ bool EventDispatcher::dispatch(NonnullRefPtr<EventTarget> target, Event& event,
|
|||
|
||||
// 10. Let clearTargetsStruct be the last struct in event’s path whose shadow-adjusted target is non-null.
|
||||
auto clear_targets_struct = event.path().last_matching([](auto& entry) {
|
||||
return !entry.shadow_adjusted_target.is_null();
|
||||
return entry.shadow_adjusted_target;
|
||||
});
|
||||
|
||||
VERIFY(clear_targets_struct.has_value());
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Web::DOM {
|
|||
|
||||
class EventDispatcher {
|
||||
public:
|
||||
static bool dispatch(NonnullRefPtr<EventTarget>, Event&, bool legacy_target_override = false);
|
||||
static bool dispatch(JS::NonnullGCPtr<EventTarget>, Event&, bool legacy_target_override = false);
|
||||
|
||||
private:
|
||||
static void invoke(Event::PathEntry&, Event&, Event::Phase);
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
#include <LibJS/Runtime/ObjectEnvironment.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibWeb/Bindings/DocumentWrapper.h>
|
||||
#include <LibWeb/Bindings/EventTargetWrapperFactory.h>
|
||||
#include <LibWeb/Bindings/EventTargetPrototype.h>
|
||||
#include <LibWeb/Bindings/IDLAbstractOperations.h>
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/DOM/AbortSignal.h>
|
||||
|
@ -36,9 +35,32 @@
|
|||
|
||||
namespace Web::DOM {
|
||||
|
||||
EventTarget::EventTarget() = default;
|
||||
EventTarget::EventTarget(JS::Realm& realm)
|
||||
: PlatformObject(realm)
|
||||
{
|
||||
}
|
||||
|
||||
EventTarget::~EventTarget() = default;
|
||||
|
||||
void EventTarget::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
|
||||
for (auto& event_listener : m_event_listener_list)
|
||||
visitor.visit(event_listener.ptr());
|
||||
|
||||
for (auto& it : m_event_handler_map)
|
||||
visitor.visit(it.value.ptr());
|
||||
}
|
||||
|
||||
Vector<JS::Handle<DOMEventListener>> EventTarget::event_listener_list()
|
||||
{
|
||||
Vector<JS::Handle<DOMEventListener>> list;
|
||||
for (auto& listener : m_event_listener_list)
|
||||
list.append(*listener);
|
||||
return list;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-flatten-options
|
||||
static bool flatten_event_listener_options(Variant<EventListenerOptions, bool> const& options)
|
||||
{
|
||||
|
@ -64,7 +86,7 @@ struct FlattenedAddEventListenerOptions {
|
|||
bool capture { false };
|
||||
bool passive { false };
|
||||
bool once { false };
|
||||
RefPtr<AbortSignal> signal;
|
||||
JS::GCPtr<AbortSignal> signal;
|
||||
};
|
||||
|
||||
// https://dom.spec.whatwg.org/#event-flatten-more
|
||||
|
@ -78,7 +100,7 @@ static FlattenedAddEventListenerOptions flatten_add_event_listener_options(Varia
|
|||
bool passive = false;
|
||||
|
||||
// 3. Let signal be null.
|
||||
RefPtr<AbortSignal> signal;
|
||||
JS::GCPtr<AbortSignal> signal;
|
||||
|
||||
// 4. If options is a dictionary, then:
|
||||
if (options.has<AddEventListenerOptions>()) {
|
||||
|
@ -90,11 +112,11 @@ static FlattenedAddEventListenerOptions flatten_add_event_listener_options(Varia
|
|||
|
||||
// 2. If options["signal"] exists, then set signal to options["signal"].
|
||||
if (add_event_listener_options.signal.has_value())
|
||||
signal = add_event_listener_options.signal.value();
|
||||
signal = add_event_listener_options.signal.value().ptr();
|
||||
}
|
||||
|
||||
// 5. Return capture, passive, once, and signal.
|
||||
return FlattenedAddEventListenerOptions { .capture = capture, .passive = passive, .once = once, .signal = signal };
|
||||
return FlattenedAddEventListenerOptions { .capture = capture, .passive = passive, .once = once, .signal = signal.ptr() };
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
|
||||
|
@ -145,11 +167,11 @@ void EventTarget::add_an_event_listener(DOMEventListener& listener)
|
|||
&& entry->capture == listener.capture;
|
||||
});
|
||||
if (it == m_event_listener_list.end())
|
||||
m_event_listener_list.append(JS::make_handle(listener));
|
||||
m_event_listener_list.append(listener);
|
||||
|
||||
// 5. If listener’s signal is not null, then add the following abort steps to it:
|
||||
if (listener.signal) {
|
||||
listener.signal->add_abort_algorithm([strong_event_target = NonnullRefPtr(*this), listener = JS::make_handle(&listener)]() mutable {
|
||||
listener.signal->add_abort_algorithm([strong_event_target = JS::make_handle(*this), listener = JS::make_handle(&listener)]() mutable {
|
||||
// 1. Remove an event listener with eventTarget and listener.
|
||||
strong_event_target->remove_an_event_listener(*listener);
|
||||
});
|
||||
|
@ -193,12 +215,12 @@ void EventTarget::remove_an_event_listener(DOMEventListener& listener)
|
|||
|
||||
// 2. Set listener’s removed to true and remove listener from eventTarget’s event listener list.
|
||||
listener.removed = true;
|
||||
m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.cell() == &listener; });
|
||||
m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.ptr() == &listener; });
|
||||
}
|
||||
|
||||
void EventTarget::remove_from_event_listener_list(DOMEventListener& listener)
|
||||
{
|
||||
m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.cell() == &listener; });
|
||||
m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.ptr() == &listener; });
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
|
||||
|
@ -313,17 +335,17 @@ Bindings::CallbackType* EventTarget::get_current_value_of_event_handler(FlyStrin
|
|||
if (event_handler->value.has<String>()) {
|
||||
// 1. If eventTarget is an element, then let element be eventTarget, and document be element's node document.
|
||||
// Otherwise, eventTarget is a Window object, let element be null, and document be eventTarget's associated Document.
|
||||
RefPtr<Element> element;
|
||||
RefPtr<Document> document;
|
||||
JS::GCPtr<Element> element;
|
||||
JS::GCPtr<Document> document;
|
||||
|
||||
if (is<Element>(this)) {
|
||||
auto* element_event_target = verify_cast<Element>(this);
|
||||
element = element_event_target;
|
||||
document = element_event_target->document();
|
||||
document = &element_event_target->document();
|
||||
} else {
|
||||
VERIFY(is<HTML::Window>(this));
|
||||
auto* window_event_target = verify_cast<HTML::Window>(this);
|
||||
document = window_event_target->associated_document();
|
||||
document = &window_event_target->associated_document();
|
||||
}
|
||||
|
||||
VERIFY(document);
|
||||
|
@ -338,7 +360,7 @@ Bindings::CallbackType* EventTarget::get_current_value_of_event_handler(FlyStrin
|
|||
// FIXME: 4. Let location be the location where the script body originated, as given by eventHandler's value.
|
||||
|
||||
// 5. If element is not null and element has a form owner, let form owner be that form owner. Otherwise, let form owner be null.
|
||||
RefPtr<HTML::HTMLFormElement> form_owner;
|
||||
JS::GCPtr<HTML::HTMLFormElement> form_owner;
|
||||
if (is<HTML::FormAssociatedElement>(element.ptr())) {
|
||||
auto* form_associated_element = dynamic_cast<HTML::FormAssociatedElement*>(element.ptr());
|
||||
VERIFY(form_associated_element);
|
||||
|
@ -491,7 +513,7 @@ void EventTarget::set_event_handler_attribute(FlyString const& name, Bindings::C
|
|||
// unique call to activate_event_handler.
|
||||
event_target->activate_event_handler(name, *new_event_handler);
|
||||
|
||||
handler_map.set(name, JS::make_handle(new_event_handler));
|
||||
handler_map.set(name, new_event_handler);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -515,21 +537,7 @@ void EventTarget::activate_event_handler(FlyString const& name, HTML::EventHandl
|
|||
if (event_handler.listener)
|
||||
return;
|
||||
|
||||
JS::Realm* realm = nullptr;
|
||||
// See step 3.1. in get_current_value_of_event_handler(), which explains these assumptions.
|
||||
if (event_handler.value.has<Bindings::CallbackType*>()) {
|
||||
realm = &event_handler.value.get<Bindings::CallbackType*>()->callback_context.realm();
|
||||
} else if (is<Element>(this)) {
|
||||
realm = &verify_cast<Element>(*this).document().realm();
|
||||
} else {
|
||||
auto& window = verify_cast<HTML::Window>(*this);
|
||||
// If an element attribute is set on a <body> element before any script is run, Window::wrapper() will be null.
|
||||
// Force creation of the global object via the Document::interpreter() lazy initialization mechanism.
|
||||
if (window.wrapper() == nullptr)
|
||||
window.associated_document().interpreter();
|
||||
realm = &window.wrapper()->shape().realm();
|
||||
}
|
||||
VERIFY(realm);
|
||||
JS::Realm& realm = shape().realm();
|
||||
|
||||
// 4. Let callback be the result of creating a Web IDL EventListener instance representing a reference to a function of one argument that executes the steps of the event handler processing algorithm, given eventTarget, name, and its argument.
|
||||
// The EventListener's callback context can be arbitrary; it does not impact the steps of the event handler processing algorithm. [DOM]
|
||||
|
@ -540,28 +548,28 @@ void EventTarget::activate_event_handler(FlyString const& name, HTML::EventHandl
|
|||
// location.reload();
|
||||
// The body element is no longer in the DOM and there is no variable holding onto it. However, the onunload handler is still called, meaning the callback keeps the body element alive.
|
||||
auto callback_function = JS::NativeFunction::create(
|
||||
*realm, [event_target = NonnullRefPtr(*this), name](JS::VM& vm) mutable -> JS::ThrowCompletionOr<JS::Value> {
|
||||
realm, [event_target = JS::make_handle(*this), name](JS::VM& vm) mutable -> JS::ThrowCompletionOr<JS::Value> {
|
||||
// The event dispatcher should only call this with one argument.
|
||||
VERIFY(vm.argument_count() == 1);
|
||||
|
||||
// The argument must be an object and it must be an EventWrapper.
|
||||
// The argument must be an object and it must be an Event.
|
||||
auto event_wrapper_argument = vm.argument(0);
|
||||
VERIFY(event_wrapper_argument.is_object());
|
||||
auto& event_wrapper = verify_cast<Bindings::EventWrapper>(event_wrapper_argument.as_object());
|
||||
auto& event_wrapper = verify_cast<DOM::Event>(event_wrapper_argument.as_object());
|
||||
auto& event = event_wrapper.impl();
|
||||
|
||||
TRY(event_target->process_event_handler_for_event(name, event));
|
||||
return JS::js_undefined();
|
||||
},
|
||||
0, "", realm);
|
||||
0, "", &realm);
|
||||
|
||||
// NOTE: As per the spec, the callback context is arbitrary.
|
||||
auto* callback = realm->heap().allocate_without_realm<Bindings::CallbackType>(*callback_function, verify_cast<HTML::EnvironmentSettingsObject>(*realm->host_defined()));
|
||||
auto* callback = realm.heap().allocate_without_realm<Bindings::CallbackType>(*callback_function, verify_cast<HTML::EnvironmentSettingsObject>(*realm.host_defined()));
|
||||
|
||||
// 5. Let listener be a new event listener whose type is the event handler event type corresponding to eventHandler and callback is callback.
|
||||
auto* listener = realm->heap().allocate_without_realm<DOMEventListener>();
|
||||
auto* listener = realm.heap().allocate_without_realm<DOMEventListener>();
|
||||
listener->type = name;
|
||||
listener->callback = IDLEventListener::create(*realm, *callback).ptr();
|
||||
listener->callback = IDLEventListener::create(realm, *callback).ptr();
|
||||
|
||||
// 6. Add an event listener with eventTarget and listener.
|
||||
add_an_event_listener(*listener);
|
||||
|
@ -723,7 +731,7 @@ void EventTarget::element_event_handler_attribute_changed(FlyString const& local
|
|||
// 6. Activate an event handler given eventTarget and name.
|
||||
event_target->activate_event_handler(local_name, *new_event_handler);
|
||||
|
||||
handler_map.set(local_name, JS::make_handle(new_event_handler));
|
||||
handler_map.set(local_name, new_event_handler);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/DOM/DOMEventListener.h>
|
||||
#include <LibWeb/DOM/ExceptionOr.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
@ -17,16 +18,12 @@
|
|||
|
||||
namespace Web::DOM {
|
||||
|
||||
class EventTarget {
|
||||
AK_MAKE_NONCOPYABLE(EventTarget);
|
||||
AK_MAKE_NONMOVABLE(EventTarget);
|
||||
class EventTarget : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(EventTarget, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
virtual ~EventTarget();
|
||||
|
||||
void ref() { ref_event_target(); }
|
||||
void unref() { unref_event_target(); }
|
||||
|
||||
virtual bool is_focusable() const { return false; }
|
||||
|
||||
void add_event_listener(FlyString const& type, IDLEventListener* callback, Variant<AddEventListenerOptions, bool> const& options);
|
||||
|
@ -39,16 +36,13 @@ public:
|
|||
virtual bool dispatch_event(Event&);
|
||||
ExceptionOr<bool> dispatch_event_binding(Event&);
|
||||
|
||||
virtual JS::Object* create_wrapper(JS::Realm&) = 0;
|
||||
|
||||
virtual EventTarget* get_parent(Event const&) { return nullptr; }
|
||||
|
||||
void add_an_event_listener(DOMEventListener&);
|
||||
void remove_an_event_listener(DOMEventListener&);
|
||||
void remove_from_event_listener_list(DOMEventListener&);
|
||||
|
||||
auto& event_listener_list() { return m_event_listener_list; }
|
||||
auto const& event_listener_list() const { return m_event_listener_list; }
|
||||
Vector<JS::Handle<DOMEventListener>> event_listener_list();
|
||||
|
||||
Function<void(Event const&)> activation_behavior;
|
||||
|
||||
|
@ -61,19 +55,18 @@ public:
|
|||
void set_event_handler_attribute(FlyString const& name, Bindings::CallbackType*);
|
||||
|
||||
protected:
|
||||
EventTarget();
|
||||
|
||||
virtual void ref_event_target() = 0;
|
||||
virtual void unref_event_target() = 0;
|
||||
explicit EventTarget(JS::Realm&);
|
||||
|
||||
void element_event_handler_attribute_changed(FlyString const& local_name, String const& value);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
private:
|
||||
Vector<JS::Handle<DOMEventListener>> m_event_listener_list;
|
||||
Vector<JS::NonnullGCPtr<DOMEventListener>> m_event_listener_list;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-map
|
||||
// Spec Note: The order of the entries of event handler map could be arbitrary. It is not observable through any algorithms that operate on the map.
|
||||
HashMap<FlyString, JS::Handle<HTML::EventHandler>> m_event_handler_map;
|
||||
HashMap<FlyString, JS::GCPtr<HTML::EventHandler>> m_event_handler_map;
|
||||
|
||||
Bindings::CallbackType* get_current_value_of_event_handler(FlyString const& name);
|
||||
void activate_event_handler(FlyString const& name, HTML::EventHandler& event_handler);
|
||||
|
@ -84,3 +77,5 @@ private:
|
|||
bool is_window_reflecting_body_element_event_handler(FlyString const& name);
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(EventTarget, Web::DOM)
|
||||
|
|
|
@ -13,19 +13,19 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
HTMLCollection::HTMLCollection(ParentNode& root, Function<bool(Element const&)> filter)
|
||||
: m_root(root)
|
||||
: m_root(JS::make_handle(root))
|
||||
, m_filter(move(filter))
|
||||
{
|
||||
}
|
||||
|
||||
HTMLCollection::~HTMLCollection() = default;
|
||||
|
||||
Vector<NonnullRefPtr<Element>> HTMLCollection::collect_matching_elements() const
|
||||
JS::MarkedVector<Element*> HTMLCollection::collect_matching_elements() const
|
||||
{
|
||||
Vector<NonnullRefPtr<Element>> elements;
|
||||
JS::MarkedVector<Element*> elements(m_root->heap());
|
||||
m_root->for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
|
||||
if (m_filter(element))
|
||||
elements.append(element);
|
||||
elements.append(const_cast<Element*>(&element));
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return elements;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
#include <LibWeb/Bindings/Wrappable.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
|
@ -45,7 +46,7 @@ public:
|
|||
Element* item(size_t index) const;
|
||||
Element* named_item(FlyString const& name) const;
|
||||
|
||||
Vector<NonnullRefPtr<Element>> collect_matching_elements() const;
|
||||
JS::MarkedVector<Element*> collect_matching_elements() const;
|
||||
|
||||
Vector<String> supported_property_names() const;
|
||||
bool is_supported_property_index(u32) const;
|
||||
|
@ -53,10 +54,10 @@ public:
|
|||
protected:
|
||||
HTMLCollection(ParentNode& root, Function<bool(Element const&)> filter);
|
||||
|
||||
NonnullRefPtr<ParentNode> root() { return m_root; }
|
||||
JS::NonnullGCPtr<ParentNode> root() { return *m_root; }
|
||||
|
||||
private:
|
||||
NonnullRefPtr<ParentNode> m_root;
|
||||
JS::Handle<ParentNode> m_root;
|
||||
Function<bool(Element const&)> m_filter;
|
||||
};
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/IDLEventListener.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
|
|
|
@ -22,15 +22,13 @@ struct EventListenerOptions {
|
|||
struct AddEventListenerOptions : public EventListenerOptions {
|
||||
bool passive { false };
|
||||
bool once { false };
|
||||
Optional<NonnullRefPtr<AbortSignal>> signal;
|
||||
Optional<JS::NonnullGCPtr<AbortSignal>> signal;
|
||||
};
|
||||
|
||||
class IDLEventListener final : public JS::Object {
|
||||
JS_OBJECT(IDLEventListener, JS::Object);
|
||||
|
||||
public:
|
||||
IDLEventListener& impl() { return *this; }
|
||||
|
||||
static JS::NonnullGCPtr<IDLEventListener> create(JS::Realm&, JS::NonnullGCPtr<Bindings::CallbackType>);
|
||||
IDLEventListener(JS::Realm&, JS::NonnullGCPtr<Bindings::CallbackType>);
|
||||
|
||||
|
|
|
@ -10,17 +10,17 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
LiveNodeList::LiveNodeList(Node& root, Function<bool(Node const&)> filter)
|
||||
: m_root(root)
|
||||
: m_root(JS::make_handle(root))
|
||||
, m_filter(move(filter))
|
||||
{
|
||||
}
|
||||
|
||||
NonnullRefPtrVector<Node> LiveNodeList::collection() const
|
||||
JS::MarkedVector<Node*> LiveNodeList::collection() const
|
||||
{
|
||||
NonnullRefPtrVector<Node> nodes;
|
||||
JS::MarkedVector<Node*> nodes(m_root->heap());
|
||||
m_root->for_each_in_inclusive_subtree([&](auto& node) {
|
||||
if (m_filter(node))
|
||||
nodes.append(node);
|
||||
nodes.append(const_cast<Node*>(&node));
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
@ -40,7 +40,7 @@ Node const* LiveNodeList::item(u32 index) const
|
|||
auto nodes = collection();
|
||||
if (index >= nodes.size())
|
||||
return nullptr;
|
||||
return &nodes[index];
|
||||
return nodes[index];
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-indices
|
||||
|
|
|
@ -29,9 +29,9 @@ public:
|
|||
private:
|
||||
LiveNodeList(Node& root, Function<bool(Node const&)> filter);
|
||||
|
||||
NonnullRefPtrVector<Node> collection() const;
|
||||
JS::MarkedVector<Node*> collection() const;
|
||||
|
||||
NonnullRefPtr<Node> m_root;
|
||||
JS::Handle<Node> m_root;
|
||||
Function<bool(Node const&)> m_filter;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
*/
|
||||
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/MutationObserver.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-mutationobserver-mutationobserver
|
||||
MutationObserver::MutationObserver(Bindings::WindowObject& window_object, JS::Handle<Bindings::CallbackType> callback)
|
||||
MutationObserver::MutationObserver(HTML::Window& window_object, JS::Handle<Bindings::CallbackType> callback)
|
||||
: m_callback(move(callback))
|
||||
{
|
||||
// 1. Set this’s callback to callback.
|
||||
|
@ -83,7 +83,7 @@ ExceptionOr<void> MutationObserver::observe(Node& target, MutationObserverInit o
|
|||
target.add_registered_observer(new_registered_observer);
|
||||
|
||||
// 2. Append target to this’s node list.
|
||||
m_node_list.append(target.make_weak_ptr());
|
||||
m_node_list.append(target.make_weak_ptr<Node>());
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
|
@ -34,7 +34,7 @@ class MutationObserver final
|
|||
public:
|
||||
using WrapperType = Bindings::MutationObserverWrapper;
|
||||
|
||||
static NonnullRefPtr<MutationObserver> create_with_global_object(Bindings::WindowObject& window_object, Bindings::CallbackType* callback)
|
||||
static NonnullRefPtr<MutationObserver> create_with_global_object(HTML::Window& window_object, Bindings::CallbackType* callback)
|
||||
{
|
||||
return adopt_ref(*new MutationObserver(window_object, JS::make_handle(callback)));
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
MutationObserver(Bindings::WindowObject& window_object, JS::Handle<Bindings::CallbackType> callback);
|
||||
MutationObserver(HTML::Window& window_object, JS::Handle<Bindings::CallbackType> callback);
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-mo-callback
|
||||
JS::Handle<Bindings::CallbackType> m_callback;
|
||||
|
|
|
@ -14,11 +14,11 @@ class MutationRecordImpl final : public MutationRecord {
|
|||
public:
|
||||
MutationRecordImpl(FlyString const& type, Node& target, NodeList& added_nodes, NodeList& removed_nodes, Node* previous_sibling, Node* next_sibling, String const& attribute_name, String const& attribute_namespace, String const& old_value)
|
||||
: m_type(type)
|
||||
, m_target(target)
|
||||
, m_target(JS::make_handle(target))
|
||||
, m_added_nodes(added_nodes)
|
||||
, m_removed_nodes(removed_nodes)
|
||||
, m_previous_sibling(previous_sibling)
|
||||
, m_next_sibling(next_sibling)
|
||||
, m_previous_sibling(JS::make_handle(previous_sibling))
|
||||
, m_next_sibling(JS::make_handle(next_sibling))
|
||||
, m_attribute_name(attribute_name)
|
||||
, m_attribute_namespace(attribute_namespace)
|
||||
, m_old_value(old_value)
|
||||
|
@ -28,22 +28,22 @@ public:
|
|||
virtual ~MutationRecordImpl() override = default;
|
||||
|
||||
virtual FlyString const& type() const override { return m_type; }
|
||||
virtual Node const* target() const override { return m_target; }
|
||||
virtual Node const* target() const override { return m_target.ptr(); }
|
||||
virtual NodeList const* added_nodes() const override { return m_added_nodes; }
|
||||
virtual NodeList const* removed_nodes() const override { return m_removed_nodes; }
|
||||
virtual Node const* previous_sibling() const override { return m_previous_sibling; }
|
||||
virtual Node const* next_sibling() const override { return m_next_sibling; }
|
||||
virtual Node const* previous_sibling() const override { return m_previous_sibling.ptr(); }
|
||||
virtual Node const* next_sibling() const override { return m_next_sibling.ptr(); }
|
||||
virtual String const& attribute_name() const override { return m_attribute_name; }
|
||||
virtual String const& attribute_namespace() const override { return m_attribute_namespace; }
|
||||
virtual String const& old_value() const override { return m_old_value; }
|
||||
|
||||
private:
|
||||
FlyString m_type;
|
||||
NonnullRefPtr<Node> m_target;
|
||||
JS::Handle<Node> m_target;
|
||||
NonnullRefPtr<NodeList> m_added_nodes;
|
||||
NonnullRefPtr<NodeList> m_removed_nodes;
|
||||
RefPtr<Node> m_previous_sibling;
|
||||
RefPtr<Node> m_next_sibling;
|
||||
JS::Handle<Node> m_previous_sibling;
|
||||
JS::Handle<Node> m_next_sibling;
|
||||
String m_attribute_name;
|
||||
String m_attribute_namespace;
|
||||
String m_old_value;
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
*/
|
||||
|
||||
#include <LibWeb/Bindings/NamedNodeMapPrototype.h>
|
||||
#include <LibWeb/Bindings/NodeWrapper.h>
|
||||
#include <LibWeb/Bindings/NodeWrapperFactory.h>
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/Attribute.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/NamedNodeMap.h>
|
||||
|
@ -16,18 +13,26 @@
|
|||
|
||||
namespace Web::DOM {
|
||||
|
||||
NamedNodeMap* NamedNodeMap::create(Element& element)
|
||||
JS::NonnullGCPtr<NamedNodeMap> NamedNodeMap::create(Element& element)
|
||||
{
|
||||
auto& realm = element.document().preferred_window_object().realm();
|
||||
return realm.heap().allocate<NamedNodeMap>(realm, element);
|
||||
auto& realm = element.realm();
|
||||
return *realm.heap().allocate<NamedNodeMap>(realm, element);
|
||||
}
|
||||
|
||||
NamedNodeMap::NamedNodeMap(Element& element)
|
||||
: Bindings::LegacyPlatformObject(element.document().preferred_window_object().ensure_web_prototype<Bindings::NamedNodeMapPrototype>("NamedNodeMap"))
|
||||
: Bindings::LegacyPlatformObject(element.document().window().ensure_web_prototype<Bindings::NamedNodeMapPrototype>("NamedNodeMap"))
|
||||
, m_element(element)
|
||||
{
|
||||
}
|
||||
|
||||
void NamedNodeMap::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_element.ptr());
|
||||
for (auto& attribute : m_attributes)
|
||||
visitor.visit(attribute.ptr());
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-indices%E2%91%A3
|
||||
bool NamedNodeMap::is_supported_property_index(u32 index) const
|
||||
{
|
||||
|
@ -42,8 +47,8 @@ Vector<String> NamedNodeMap::supported_property_names() const
|
|||
names.ensure_capacity(m_attributes.size());
|
||||
|
||||
for (auto const& attribute : m_attributes) {
|
||||
if (!names.contains_slow(attribute.name()))
|
||||
names.append(attribute.name());
|
||||
if (!names.contains_slow(attribute->name()))
|
||||
names.append(attribute->name());
|
||||
}
|
||||
|
||||
// 2. If this NamedNodeMap object’s element is in the HTML namespace and its node document is an HTML document, then for each name in names:
|
||||
|
@ -66,7 +71,7 @@ Attribute const* NamedNodeMap::item(u32 index) const
|
|||
return nullptr;
|
||||
|
||||
// 2. Otherwise, return this’s attribute list[index].
|
||||
return &m_attributes[index];
|
||||
return m_attributes[index].ptr();
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-namednodemap-getnameditem
|
||||
|
@ -114,11 +119,11 @@ Attribute const* NamedNodeMap::get_attribute(StringView qualified_name, size_t*
|
|||
// 2. Return the first attribute in element’s attribute list whose qualified name is qualifiedName; otherwise null.
|
||||
for (auto const& attribute : m_attributes) {
|
||||
if (compare_as_lowercase) {
|
||||
if (attribute.name().equals_ignoring_case(qualified_name))
|
||||
return &attribute;
|
||||
if (attribute->name().equals_ignoring_case(qualified_name))
|
||||
return attribute.ptr();
|
||||
} else {
|
||||
if (attribute.name() == qualified_name)
|
||||
return &attribute;
|
||||
if (attribute->name() == qualified_name)
|
||||
return attribute.ptr();
|
||||
}
|
||||
|
||||
if (item_index)
|
||||
|
@ -191,7 +196,7 @@ void NamedNodeMap::append_attribute(Attribute& attribute)
|
|||
// https://dom.spec.whatwg.org/#concept-element-attributes-remove
|
||||
void NamedNodeMap::remove_attribute_at_index(size_t attribute_index)
|
||||
{
|
||||
NonnullRefPtr<Attribute> attribute = m_attributes.at(attribute_index);
|
||||
JS::NonnullGCPtr<Attribute> attribute = m_attributes.at(attribute_index);
|
||||
|
||||
// 1. Handle attribute changes for attribute with attribute’s element, attribute’s value, and null.
|
||||
VERIFY(attribute->owner_element());
|
||||
|
@ -225,7 +230,7 @@ JS::Value NamedNodeMap::item_value(size_t index) const
|
|||
auto const* node = item(index);
|
||||
if (!node)
|
||||
return JS::js_undefined();
|
||||
return Bindings::wrap(shape().realm(), const_cast<Attribute&>(*node));
|
||||
return node;
|
||||
}
|
||||
|
||||
JS::Value NamedNodeMap::named_item_value(FlyString const& name) const
|
||||
|
@ -233,7 +238,7 @@ JS::Value NamedNodeMap::named_item_value(FlyString const& name) const
|
|||
auto const* node = get_named_item(name);
|
||||
if (!node)
|
||||
return JS::js_undefined();
|
||||
return Bindings::wrap(shape().realm(), const_cast<Attribute&>(*node));
|
||||
return node;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,15 +18,12 @@ namespace Web::DOM {
|
|||
|
||||
// https://dom.spec.whatwg.org/#interface-namednodemap
|
||||
class NamedNodeMap : public Bindings::LegacyPlatformObject {
|
||||
JS_OBJECT(NamedNodeMap, Bindings::LegacyPlatformObject);
|
||||
WEB_PLATFORM_OBJECT(NamedNodeMap, Bindings::LegacyPlatformObject);
|
||||
|
||||
public:
|
||||
static NamedNodeMap* create(Element&);
|
||||
explicit NamedNodeMap(Element&);
|
||||
static JS::NonnullGCPtr<NamedNodeMap> create(Element&);
|
||||
~NamedNodeMap() = default;
|
||||
|
||||
NamedNodeMap& impl() { return *this; }
|
||||
|
||||
virtual bool is_supported_property_index(u32 index) const override;
|
||||
virtual Vector<String> supported_property_names() const override;
|
||||
virtual JS::Value item_value(size_t index) const override;
|
||||
|
@ -50,18 +47,19 @@ public:
|
|||
Attribute const* remove_attribute(StringView qualified_name);
|
||||
|
||||
private:
|
||||
Element& associated_element() { return m_element; }
|
||||
Element const& associated_element() const { return m_element; }
|
||||
explicit NamedNodeMap(Element&);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
Element& associated_element() { return *m_element; }
|
||||
Element const& associated_element() const { return *m_element; }
|
||||
|
||||
void remove_attribute_at_index(size_t attribute_index);
|
||||
|
||||
DOM::Element& m_element;
|
||||
NonnullRefPtrVector<Attribute> m_attributes;
|
||||
JS::NonnullGCPtr<DOM::Element> m_element;
|
||||
Vector<JS::NonnullGCPtr<Attribute>> m_attributes;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Web::Bindings {
|
||||
inline JS::Object* wrap(JS::Realm&, Web::DOM::NamedNodeMap& object) { return &object; }
|
||||
using NamedNodeMapWrapper = Web::DOM::NamedNodeMap;
|
||||
}
|
||||
WRAPPER_HACK(NamedNodeMap, Web::DOM)
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
#include <LibJS/AST.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/Bindings/NodeWrapper.h>
|
||||
#include <LibWeb/Bindings/NodeWrapperFactory.h>
|
||||
#include <LibWeb/Bindings/NodePrototype.h>
|
||||
#include <LibWeb/DOM/Comment.h>
|
||||
#include <LibWeb/DOM/DocumentType.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
|
@ -23,7 +22,9 @@
|
|||
#include <LibWeb/DOM/LiveNodeList.h>
|
||||
#include <LibWeb/DOM/MutationType.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/DOM/NodeIterator.h>
|
||||
#include <LibWeb/DOM/ProcessingInstruction.h>
|
||||
#include <LibWeb/DOM/Range.h>
|
||||
#include <LibWeb/DOM/ShadowRoot.h>
|
||||
#include <LibWeb/DOM/StaticNodeList.h>
|
||||
#include <LibWeb/HTML/BrowsingContextContainer.h>
|
||||
|
@ -58,28 +59,38 @@ Node* Node::from_id(i32 node_id)
|
|||
return s_node_directory.get(node_id).value_or(nullptr);
|
||||
}
|
||||
|
||||
Node::Node(Document& document, NodeType type)
|
||||
: EventTarget()
|
||||
Node::Node(JS::Realm& realm, Document& document, NodeType type)
|
||||
: EventTarget(realm)
|
||||
, m_document(&document)
|
||||
, m_type(type)
|
||||
, m_id(allocate_node_id(this))
|
||||
{
|
||||
if (!is_document())
|
||||
m_document->ref_from_node({});
|
||||
}
|
||||
|
||||
Node::Node(Document& document, NodeType type)
|
||||
: Node(document.realm(), document, type)
|
||||
{
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{
|
||||
VERIFY(m_deletion_has_begun);
|
||||
if (layout_node() && layout_node()->parent())
|
||||
layout_node()->parent()->remove_child(*layout_node());
|
||||
|
||||
if (!is_document())
|
||||
m_document->unref_from_node({});
|
||||
|
||||
deallocate_node_id(m_id);
|
||||
}
|
||||
|
||||
void Node::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_document.ptr());
|
||||
visitor.visit(m_parent.ptr());
|
||||
visitor.visit(m_first_child.ptr());
|
||||
visitor.visit(m_last_child.ptr());
|
||||
visitor.visit(m_next_sibling.ptr());
|
||||
visitor.visit(m_previous_sibling.ptr());
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-node-baseuri
|
||||
String Node::base_uri() const
|
||||
{
|
||||
|
@ -291,7 +302,7 @@ Element const* Node::parent_element() const
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
|
||||
ExceptionOr<void> Node::ensure_pre_insertion_validity(NonnullRefPtr<Node> node, RefPtr<Node> child) const
|
||||
ExceptionOr<void> Node::ensure_pre_insertion_validity(JS::NonnullGCPtr<Node> node, JS::GCPtr<Node> child) const
|
||||
{
|
||||
// 1. If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
|
||||
if (!is<Document>(this) && !is<DocumentFragment>(this) && !is<Element>(this))
|
||||
|
@ -342,14 +353,14 @@ ExceptionOr<void> Node::ensure_pre_insertion_validity(NonnullRefPtr<Node> node,
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-node-insert
|
||||
void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool suppress_observers)
|
||||
void Node::insert_before(JS::NonnullGCPtr<Node> node, JS::GCPtr<Node> child, bool suppress_observers)
|
||||
{
|
||||
// 1. Let nodes be node’s children, if node is a DocumentFragment node; otherwise « node ».
|
||||
NonnullRefPtrVector<Node> nodes;
|
||||
Vector<JS::Handle<Node>> nodes;
|
||||
if (is<DocumentFragment>(*node))
|
||||
nodes = node->children_as_vector();
|
||||
else
|
||||
nodes.append(node);
|
||||
nodes.append(JS::make_handle(*node));
|
||||
|
||||
// 2. Let count be nodes’s size.
|
||||
auto count = nodes.size();
|
||||
|
@ -384,7 +395,7 @@ void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool supp
|
|||
}
|
||||
|
||||
// 6. Let previousSibling be child’s previous sibling or parent’s last child if child is null.
|
||||
RefPtr<Node> previous_sibling;
|
||||
JS::GCPtr<Node> previous_sibling;
|
||||
if (child)
|
||||
previous_sibling = child->previous_sibling();
|
||||
else
|
||||
|
@ -394,14 +405,14 @@ void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool supp
|
|||
// FIXME: In tree order
|
||||
for (auto& node_to_insert : nodes) {
|
||||
// 1. Adopt node into parent’s node document.
|
||||
document().adopt_node(node_to_insert);
|
||||
document().adopt_node(*node_to_insert);
|
||||
|
||||
// 2. If child is null, then append node to parent’s children.
|
||||
if (!child)
|
||||
TreeNode<Node>::append_child(node_to_insert);
|
||||
append_child_impl(*node_to_insert);
|
||||
// 3. Otherwise, insert node into parent’s children before child’s index.
|
||||
else
|
||||
TreeNode<Node>::insert_before(node_to_insert, child);
|
||||
insert_before_impl(*node_to_insert, child);
|
||||
|
||||
// FIXME: 4. If parent is a shadow host and node is a slottable, then assign a slot for node.
|
||||
// FIXME: 5. If parent’s root is a shadow root, and parent is a slot whose assigned nodes is the empty list, then run signal a slot change for parent.
|
||||
|
@ -409,7 +420,7 @@ void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool supp
|
|||
|
||||
// FIXME: This should be shadow-including.
|
||||
// 7. For each shadow-including inclusive descendant inclusiveDescendant of node, in shadow-including tree order:
|
||||
node_to_insert.for_each_in_inclusive_subtree([&](Node& inclusive_descendant) {
|
||||
node_to_insert->for_each_in_inclusive_subtree([&](Node& inclusive_descendant) {
|
||||
// 1. Run the insertion steps with inclusiveDescendant.
|
||||
inclusive_descendant.inserted();
|
||||
|
||||
|
@ -427,7 +438,7 @@ void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool supp
|
|||
|
||||
// 8. If suppress observers flag is unset, then queue a tree mutation record for parent with nodes, « », previousSibling, and child.
|
||||
if (!suppress_observers)
|
||||
queue_tree_mutation_record(StaticNodeList::create(move(nodes)), StaticNodeList::create({}), previous_sibling, child);
|
||||
queue_tree_mutation_record(StaticNodeList::create(move(nodes)), StaticNodeList::create({}), previous_sibling.ptr(), child.ptr());
|
||||
|
||||
// 9. Run the children changed steps for parent.
|
||||
children_changed();
|
||||
|
@ -436,7 +447,7 @@ void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool supp
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-node-pre-insert
|
||||
ExceptionOr<NonnullRefPtr<Node>> Node::pre_insert(NonnullRefPtr<Node> node, RefPtr<Node> child)
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> Node::pre_insert(JS::NonnullGCPtr<Node> node, JS::GCPtr<Node> child)
|
||||
{
|
||||
// 1. Ensure pre-insertion validity of node into parent before child.
|
||||
TRY(ensure_pre_insertion_validity(node, child));
|
||||
|
@ -456,14 +467,14 @@ ExceptionOr<NonnullRefPtr<Node>> Node::pre_insert(NonnullRefPtr<Node> node, RefP
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-node-removechild
|
||||
ExceptionOr<NonnullRefPtr<Node>> Node::remove_child(NonnullRefPtr<Node> child)
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> Node::remove_child(JS::NonnullGCPtr<Node> child)
|
||||
{
|
||||
// The removeChild(child) method steps are to return the result of pre-removing child from this.
|
||||
return pre_remove(child);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-node-pre-remove
|
||||
ExceptionOr<NonnullRefPtr<Node>> Node::pre_remove(NonnullRefPtr<Node> child)
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> Node::pre_remove(JS::NonnullGCPtr<Node> child)
|
||||
{
|
||||
// 1. If child’s parent is not parent, then throw a "NotFoundError" DOMException.
|
||||
if (child->parent() != this)
|
||||
|
@ -477,7 +488,7 @@ ExceptionOr<NonnullRefPtr<Node>> Node::pre_remove(NonnullRefPtr<Node> child)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-node-append
|
||||
ExceptionOr<NonnullRefPtr<Node>> Node::append_child(NonnullRefPtr<Node> node)
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> Node::append_child(JS::NonnullGCPtr<Node> node)
|
||||
{
|
||||
// To append a node to a parent, pre-insert node into parent before null.
|
||||
return pre_insert(node, nullptr);
|
||||
|
@ -487,7 +498,7 @@ ExceptionOr<NonnullRefPtr<Node>> Node::append_child(NonnullRefPtr<Node> node)
|
|||
void Node::remove(bool suppress_observers)
|
||||
{
|
||||
// 1. Let parent be node’s parent
|
||||
auto* parent = TreeNode<Node>::parent();
|
||||
auto* parent = this->parent();
|
||||
|
||||
// 2. Assert: parent is non-null.
|
||||
VERIFY(parent);
|
||||
|
@ -525,13 +536,13 @@ void Node::remove(bool suppress_observers)
|
|||
});
|
||||
|
||||
// 9. Let oldPreviousSibling be node’s previous sibling.
|
||||
RefPtr<Node> old_previous_sibling = previous_sibling();
|
||||
JS::GCPtr<Node> old_previous_sibling = previous_sibling();
|
||||
|
||||
// 10. Let oldNextSibling be node’s next sibling.
|
||||
RefPtr<Node> old_next_sibling = next_sibling();
|
||||
JS::GCPtr<Node> old_next_sibling = next_sibling();
|
||||
|
||||
// 11. Remove node from its parent’s children.
|
||||
parent->TreeNode::remove_child(*this);
|
||||
parent->remove_child_impl(*this);
|
||||
|
||||
// FIXME: 12. If node is assigned, then run assign slottables for node’s assigned slot.
|
||||
|
||||
|
@ -576,9 +587,9 @@ void Node::remove(bool suppress_observers)
|
|||
|
||||
// 20. If suppress observers flag is unset, then queue a tree mutation record for parent with « », « node », oldPreviousSibling, and oldNextSibling.
|
||||
if (!suppress_observers) {
|
||||
NonnullRefPtrVector<Node> removed_nodes;
|
||||
removed_nodes.append(*this);
|
||||
parent->queue_tree_mutation_record(StaticNodeList::create({}), StaticNodeList::create(move(removed_nodes)), old_previous_sibling, old_next_sibling);
|
||||
Vector<JS::Handle<Node>> removed_nodes;
|
||||
removed_nodes.append(JS::make_handle(*this));
|
||||
parent->queue_tree_mutation_record(StaticNodeList::create({}), StaticNodeList::create(move(removed_nodes)), old_previous_sibling.ptr(), old_next_sibling.ptr());
|
||||
}
|
||||
|
||||
// 21. Run the children changed steps for parent.
|
||||
|
@ -588,7 +599,7 @@ void Node::remove(bool suppress_observers)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-node-replace
|
||||
ExceptionOr<NonnullRefPtr<Node>> Node::replace_child(NonnullRefPtr<Node> node, NonnullRefPtr<Node> child)
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> Node::replace_child(JS::NonnullGCPtr<Node> node, JS::NonnullGCPtr<Node> child)
|
||||
{
|
||||
// If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
|
||||
if (!is<Document>(this) && !is<DocumentFragment>(this) && !is<Element>(this))
|
||||
|
@ -637,52 +648,52 @@ ExceptionOr<NonnullRefPtr<Node>> Node::replace_child(NonnullRefPtr<Node> node, N
|
|||
}
|
||||
|
||||
// 7. Let referenceChild be child’s next sibling.
|
||||
RefPtr<Node> reference_child = child->next_sibling();
|
||||
JS::GCPtr<Node> reference_child = child->next_sibling();
|
||||
|
||||
// 8. If referenceChild is node, then set referenceChild to node’s next sibling.
|
||||
if (reference_child == node)
|
||||
reference_child = node->next_sibling();
|
||||
|
||||
// 9. Let previousSibling be child’s previous sibling.
|
||||
RefPtr<Node> previous_sibling = child->previous_sibling();
|
||||
JS::GCPtr<Node> previous_sibling = child->previous_sibling();
|
||||
|
||||
// 10. Let removedNodes be the empty set.
|
||||
NonnullRefPtrVector<Node> removed_nodes;
|
||||
Vector<JS::Handle<Node>> removed_nodes;
|
||||
|
||||
// 11. If child’s parent is non-null, then:
|
||||
// NOTE: The above can only be false if child is node.
|
||||
if (child->parent()) {
|
||||
// 1. Set removedNodes to « child ».
|
||||
removed_nodes.append(child);
|
||||
removed_nodes.append(JS::make_handle(*child));
|
||||
|
||||
// 2. Remove child with the suppress observers flag set.
|
||||
child->remove(true);
|
||||
}
|
||||
|
||||
// 12. Let nodes be node’s children if node is a DocumentFragment node; otherwise « node ».
|
||||
NonnullRefPtrVector<Node> nodes;
|
||||
Vector<JS::Handle<Node>> nodes;
|
||||
if (is<DocumentFragment>(*node))
|
||||
nodes = node->children_as_vector();
|
||||
else
|
||||
nodes.append(node);
|
||||
nodes.append(JS::make_handle(*node));
|
||||
|
||||
// 13. Insert node into parent before referenceChild with the suppress observers flag set.
|
||||
insert_before(node, reference_child, true);
|
||||
|
||||
// 14. Queue a tree mutation record for parent with nodes, removedNodes, previousSibling, and referenceChild.
|
||||
queue_tree_mutation_record(StaticNodeList::create(move(nodes)), StaticNodeList::create(move(removed_nodes)), previous_sibling, reference_child);
|
||||
queue_tree_mutation_record(StaticNodeList::create(move(nodes)), StaticNodeList::create(move(removed_nodes)), previous_sibling.ptr(), reference_child.ptr());
|
||||
|
||||
// 15. Return child.
|
||||
return child;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-node-clone
|
||||
NonnullRefPtr<Node> Node::clone_node(Document* document, bool clone_children)
|
||||
JS::NonnullGCPtr<Node> Node::clone_node(Document* document, bool clone_children)
|
||||
{
|
||||
// 1. If document is not given, let document be node’s node document.
|
||||
if (!document)
|
||||
document = m_document;
|
||||
RefPtr<Node> copy;
|
||||
document = m_document.ptr();
|
||||
JS::GCPtr<Node> copy;
|
||||
|
||||
// 2. If node is an element, then:
|
||||
if (is<Element>(this)) {
|
||||
|
@ -703,7 +714,7 @@ NonnullRefPtr<Node> Node::clone_node(Document* document, bool clone_children)
|
|||
else if (is<Document>(this)) {
|
||||
// Document
|
||||
auto document_ = verify_cast<Document>(this);
|
||||
auto document_copy = Document::create(document_->url());
|
||||
auto document_copy = Document::create(Bindings::main_thread_internal_window_object(), document_->url());
|
||||
|
||||
// Set copy’s encoding, content type, URL, origin, type, and mode to those of node.
|
||||
document_copy->set_encoding(document_->encoding());
|
||||
|
@ -716,7 +727,7 @@ NonnullRefPtr<Node> Node::clone_node(Document* document, bool clone_children)
|
|||
} else if (is<DocumentType>(this)) {
|
||||
// DocumentType
|
||||
auto document_type = verify_cast<DocumentType>(this);
|
||||
auto document_type_copy = adopt_ref(*new DocumentType(*document));
|
||||
auto document_type_copy = heap().allocate<DocumentType>(realm(), *document);
|
||||
|
||||
// Set copy’s name, public ID, and system ID to those of node.
|
||||
document_type_copy->set_name(document_type->name());
|
||||
|
@ -733,26 +744,26 @@ NonnullRefPtr<Node> Node::clone_node(Document* document, bool clone_children)
|
|||
auto text = verify_cast<Text>(this);
|
||||
|
||||
// Set copy’s data to that of node.
|
||||
auto text_copy = adopt_ref(*new Text(*document, text->data()));
|
||||
auto text_copy = heap().allocate<Text>(realm(), *document, text->data());
|
||||
copy = move(text_copy);
|
||||
} else if (is<Comment>(this)) {
|
||||
// Comment
|
||||
auto comment = verify_cast<Comment>(this);
|
||||
|
||||
// Set copy’s data to that of node.
|
||||
auto comment_copy = adopt_ref(*new Comment(*document, comment->data()));
|
||||
auto comment_copy = heap().allocate<Comment>(realm(), *document, comment->data());
|
||||
copy = move(comment_copy);
|
||||
} else if (is<ProcessingInstruction>(this)) {
|
||||
// ProcessingInstruction
|
||||
auto processing_instruction = verify_cast<ProcessingInstruction>(this);
|
||||
|
||||
// Set copy’s target and data to those of node.
|
||||
auto processing_instruction_copy = adopt_ref(*new ProcessingInstruction(*document, processing_instruction->data(), processing_instruction->target()));
|
||||
copy = move(processing_instruction_copy);
|
||||
auto processing_instruction_copy = heap().allocate<ProcessingInstruction>(realm(), *document, processing_instruction->data(), processing_instruction->target());
|
||||
copy = processing_instruction_copy;
|
||||
}
|
||||
// Otherwise, Do nothing.
|
||||
else if (is<DocumentFragment>(this)) {
|
||||
copy = adopt_ref(*new DocumentFragment(*document));
|
||||
copy = heap().allocate<DocumentFragment>(realm(), *document);
|
||||
}
|
||||
|
||||
// FIXME: 4. Set copy’s node document and document to copy, if copy is a document, and set copy’s node document to document otherwise.
|
||||
|
@ -768,11 +779,11 @@ NonnullRefPtr<Node> Node::clone_node(Document* document, bool clone_children)
|
|||
}
|
||||
|
||||
// 7. Return copy.
|
||||
return copy.release_nonnull();
|
||||
return *copy;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-node-clonenode
|
||||
ExceptionOr<NonnullRefPtr<Node>> Node::clone_node_binding(bool deep)
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> Node::clone_node_binding(bool deep)
|
||||
{
|
||||
// 1. If this is a shadow root, then throw a "NotSupportedError" DOMException.
|
||||
if (is<ShadowRoot>(*this))
|
||||
|
@ -784,11 +795,9 @@ ExceptionOr<NonnullRefPtr<Node>> Node::clone_node_binding(bool deep)
|
|||
|
||||
void Node::set_document(Badge<Document>, Document& document)
|
||||
{
|
||||
if (m_document == &document)
|
||||
if (m_document.ptr() == &document)
|
||||
return;
|
||||
|
||||
document.ref_from_node({});
|
||||
m_document->unref_from_node({});
|
||||
m_document = &document;
|
||||
|
||||
if (needs_style_update() || child_needs_style_update()) {
|
||||
|
@ -805,21 +814,6 @@ bool Node::is_editable() const
|
|||
return parent() && parent()->is_editable();
|
||||
}
|
||||
|
||||
JS::Object* Node::create_wrapper(JS::Realm& realm)
|
||||
{
|
||||
return wrap(realm, *this);
|
||||
}
|
||||
|
||||
void Node::removed_last_ref()
|
||||
{
|
||||
if (is<Document>(*this)) {
|
||||
verify_cast<Document>(*this).removed_last_ref();
|
||||
return;
|
||||
}
|
||||
m_deletion_has_begun = true;
|
||||
delete this;
|
||||
}
|
||||
|
||||
void Node::set_layout_node(Badge<Layout::Node>, Layout::Node* layout_node) const
|
||||
{
|
||||
m_layout_node = layout_node;
|
||||
|
@ -866,12 +860,12 @@ NonnullRefPtr<NodeList> Node::child_nodes()
|
|||
});
|
||||
}
|
||||
|
||||
NonnullRefPtrVector<Node> Node::children_as_vector() const
|
||||
Vector<JS::Handle<Node>> Node::children_as_vector() const
|
||||
{
|
||||
NonnullRefPtrVector<Node> nodes;
|
||||
Vector<JS::Handle<Node>> nodes;
|
||||
|
||||
for_each_child([&](auto& child) {
|
||||
nodes.append(child);
|
||||
nodes.append(JS::make_handle(child));
|
||||
});
|
||||
|
||||
return nodes;
|
||||
|
@ -879,12 +873,12 @@ NonnullRefPtrVector<Node> Node::children_as_vector() const
|
|||
|
||||
void Node::remove_all_children(bool suppress_observers)
|
||||
{
|
||||
while (RefPtr<Node> child = first_child())
|
||||
while (JS::GCPtr<Node> child = first_child())
|
||||
child->remove(suppress_observers);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
|
||||
u16 Node::compare_document_position(RefPtr<Node> other)
|
||||
u16 Node::compare_document_position(JS::GCPtr<Node> other)
|
||||
{
|
||||
enum Position : u16 {
|
||||
DOCUMENT_POSITION_EQUAL = 0,
|
||||
|
@ -897,7 +891,7 @@ u16 Node::compare_document_position(RefPtr<Node> other)
|
|||
};
|
||||
|
||||
// 1. If this is other, then return zero.
|
||||
if (this == other)
|
||||
if (this == other.ptr())
|
||||
return DOCUMENT_POSITION_EQUAL;
|
||||
|
||||
// 2. Let node1 be other and node2 be this.
|
||||
|
@ -967,7 +961,7 @@ bool Node::is_host_including_inclusive_ancestor_of(Node const& other) const
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-node-ownerdocument
|
||||
RefPtr<Document> Node::owner_document() const
|
||||
JS::GCPtr<Document> Node::owner_document() const
|
||||
{
|
||||
// The ownerDocument getter steps are to return null, if this is a document; otherwise this’s node document.
|
||||
if (is_document())
|
||||
|
@ -1067,7 +1061,7 @@ bool Node::is_scripting_disabled() const
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-node-contains
|
||||
bool Node::contains(RefPtr<Node> other) const
|
||||
bool Node::contains(JS::GCPtr<Node> other) const
|
||||
{
|
||||
// The contains(other) method steps are to return true if other is an inclusive descendant of this; otherwise false (including when other is null).
|
||||
return other && other->is_inclusive_descendant_of(*this);
|
||||
|
@ -1113,13 +1107,13 @@ bool Node::is_shadow_including_inclusive_ancestor_of(Node const& other) const
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-node-replace-all
|
||||
void Node::replace_all(RefPtr<Node> node)
|
||||
void Node::replace_all(JS::GCPtr<Node> node)
|
||||
{
|
||||
// 1. Let removedNodes be parent’s children.
|
||||
auto removed_nodes = children_as_vector();
|
||||
|
||||
// 2. Let addedNodes be the empty set.
|
||||
NonnullRefPtrVector<Node> added_nodes;
|
||||
Vector<JS::Handle<Node>> added_nodes;
|
||||
|
||||
// 3. If node is a DocumentFragment node, then set addedNodes to node’s children.
|
||||
if (node && is<DocumentFragment>(*node)) {
|
||||
|
@ -1127,7 +1121,7 @@ void Node::replace_all(RefPtr<Node> node)
|
|||
}
|
||||
// 4. Otherwise, if node is non-null, set addedNodes to « node ».
|
||||
else if (node) {
|
||||
added_nodes.append(*node);
|
||||
added_nodes.append(JS::make_handle(*node));
|
||||
}
|
||||
|
||||
// 5. Remove all parent’s children, in tree order, with the suppress observers flag set.
|
||||
|
@ -1146,11 +1140,11 @@ void Node::replace_all(RefPtr<Node> node)
|
|||
void Node::string_replace_all(String const& string)
|
||||
{
|
||||
// 1. Let node be null.
|
||||
RefPtr<Node> node;
|
||||
JS::GCPtr<Node> node;
|
||||
|
||||
// 2. If string is not the empty string, then set node to a new Text node whose data is string and node document is parent’s node document.
|
||||
if (!string.is_empty())
|
||||
node = make_ref_counted<Text>(document(), string);
|
||||
node = heap().allocate<Text>(realm(), document(), string);
|
||||
|
||||
// 3. Replace all with node within parent.
|
||||
replace_all(node);
|
||||
|
@ -1267,7 +1261,7 @@ bool Node::in_a_document_tree() const
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-node-getrootnode
|
||||
NonnullRefPtr<Node> Node::get_root_node(GetRootNodeOptions const& options)
|
||||
JS::NonnullGCPtr<Node> Node::get_root_node(GetRootNodeOptions const& options)
|
||||
{
|
||||
// The getRootNode(options) method steps are to return this’s shadow-including root if options["composed"] is true;
|
||||
if (options.composed)
|
||||
|
@ -1332,15 +1326,15 @@ void Node::queue_mutation_record(FlyString const& type, String attribute_name, S
|
|||
OrderedHashMap<NonnullRefPtr<MutationObserver>, String> interested_observers;
|
||||
|
||||
// 2. Let nodes be the inclusive ancestors of target.
|
||||
NonnullRefPtrVector<Node> nodes;
|
||||
nodes.append(*this);
|
||||
Vector<JS::Handle<Node>> nodes;
|
||||
nodes.append(JS::make_handle(*this));
|
||||
|
||||
for (auto* parent_node = parent(); parent_node; parent_node = parent_node->parent())
|
||||
nodes.append(*parent_node);
|
||||
nodes.append(JS::make_handle(*parent_node));
|
||||
|
||||
// 3. For each node in nodes, and then for each registered of node’s registered observer list:
|
||||
for (auto& node : nodes) {
|
||||
for (auto& registered_observer : node.m_registered_observer_list) {
|
||||
for (auto& registered_observer : node->m_registered_observer_list) {
|
||||
// 1. Let options be registered’s options.
|
||||
auto& options = registered_observer.options;
|
||||
|
||||
|
@ -1351,7 +1345,7 @@ void Node::queue_mutation_record(FlyString const& type, String attribute_name, S
|
|||
// - type is "characterData" and options["characterData"] either does not exist or is false
|
||||
// - type is "childList" and options["childList"] is false
|
||||
// then:
|
||||
if (!(&node != this && !options.subtree)
|
||||
if (!(node.ptr() != this && !options.subtree)
|
||||
&& !(type == MutationType::attributes && (!options.attributes.has_value() || !options.attributes.value()))
|
||||
&& !(type == MutationType::attributes && options.attribute_filter.has_value() && (!attribute_namespace.is_null() || !options.attribute_filter->contains_slow(attribute_name)))
|
||||
&& !(type == MutationType::characterData && (!options.character_data.has_value() || !options.character_data.value()))
|
||||
|
@ -1394,4 +1388,99 @@ void Node::queue_tree_mutation_record(NonnullRefPtr<NodeList> added_nodes, Nonnu
|
|||
queue_mutation_record(MutationType::childList, {}, {}, {}, move(added_nodes), move(removed_nodes), previous_sibling, next_sibling);
|
||||
}
|
||||
|
||||
void Node::append_child_impl(JS::NonnullGCPtr<Node> node)
|
||||
{
|
||||
VERIFY(!node->m_parent);
|
||||
|
||||
if (!is_child_allowed(*node))
|
||||
return;
|
||||
|
||||
if (m_last_child)
|
||||
m_last_child->m_next_sibling = node.ptr();
|
||||
node->m_previous_sibling = m_last_child;
|
||||
node->m_parent = this;
|
||||
m_last_child = node.ptr();
|
||||
if (!m_first_child)
|
||||
m_first_child = m_last_child;
|
||||
}
|
||||
|
||||
void Node::insert_before_impl(JS::NonnullGCPtr<Node> node, JS::GCPtr<Node> child)
|
||||
{
|
||||
if (!child)
|
||||
return append_child_impl(move(node));
|
||||
|
||||
VERIFY(!node->m_parent);
|
||||
VERIFY(child->parent() == this);
|
||||
|
||||
node->m_previous_sibling = child->m_previous_sibling;
|
||||
node->m_next_sibling = child;
|
||||
|
||||
if (child->m_previous_sibling)
|
||||
child->m_previous_sibling->m_next_sibling = node;
|
||||
|
||||
if (m_first_child == child)
|
||||
m_first_child = node;
|
||||
|
||||
child->m_previous_sibling = node;
|
||||
|
||||
node->m_parent = this;
|
||||
}
|
||||
|
||||
void Node::remove_child_impl(JS::NonnullGCPtr<Node> node)
|
||||
{
|
||||
VERIFY(node->m_parent.ptr() == this);
|
||||
|
||||
if (m_first_child == node)
|
||||
m_first_child = node->m_next_sibling;
|
||||
|
||||
if (m_last_child == node)
|
||||
m_last_child = node->m_previous_sibling;
|
||||
|
||||
if (node->m_next_sibling)
|
||||
node->m_next_sibling->m_previous_sibling = node->m_previous_sibling;
|
||||
|
||||
if (node->m_previous_sibling)
|
||||
node->m_previous_sibling->m_next_sibling = node->m_next_sibling;
|
||||
|
||||
node->m_next_sibling = nullptr;
|
||||
node->m_previous_sibling = nullptr;
|
||||
node->m_parent = nullptr;
|
||||
}
|
||||
|
||||
bool Node::is_ancestor_of(Node const& other) const
|
||||
{
|
||||
for (auto* ancestor = other.parent(); ancestor; ancestor = ancestor->parent()) {
|
||||
if (ancestor == this)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Node::is_inclusive_ancestor_of(Node const& other) const
|
||||
{
|
||||
return &other == this || is_ancestor_of(other);
|
||||
}
|
||||
|
||||
bool Node::is_descendant_of(Node const& other) const
|
||||
{
|
||||
return other.is_ancestor_of(*this);
|
||||
}
|
||||
|
||||
bool Node::is_inclusive_descendant_of(Node const& other) const
|
||||
{
|
||||
return other.is_inclusive_ancestor_of(*this);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-tree-following
|
||||
bool Node::is_following(Node const& other) const
|
||||
{
|
||||
// An object A is following an object B if A and B are in the same tree and A comes after B in tree order.
|
||||
for (auto* node = previous_in_pre_order(); node; node = node->previous_in_pre_order()) {
|
||||
if (node == &other)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/DOM/ExceptionOr.h>
|
||||
#include <LibWeb/DOM/MutationObserver.h>
|
||||
#include <LibWeb/TreeNode.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
|
@ -40,26 +39,16 @@ struct GetRootNodeOptions {
|
|||
bool composed { false };
|
||||
};
|
||||
|
||||
class Node
|
||||
: public TreeNode<Node>
|
||||
, public EventTarget
|
||||
, public Bindings::Wrappable {
|
||||
class Node : public EventTarget {
|
||||
WEB_PLATFORM_OBJECT(Node, EventTarget);
|
||||
|
||||
public:
|
||||
using WrapperType = Bindings::NodeWrapper;
|
||||
|
||||
using TreeNode<Node>::ref;
|
||||
using TreeNode<Node>::unref;
|
||||
|
||||
ParentNode* parent_or_shadow_host();
|
||||
ParentNode const* parent_or_shadow_host() const { return const_cast<Node*>(this)->parent_or_shadow_host(); }
|
||||
|
||||
// ^EventTarget
|
||||
virtual void ref_event_target() final { ref(); }
|
||||
virtual void unref_event_target() final { unref(); }
|
||||
virtual JS::Object* create_wrapper(JS::Realm&) override;
|
||||
|
||||
virtual ~Node();
|
||||
|
||||
// FIXME: Move cleanup to the regular destructor.
|
||||
void removed_last_ref();
|
||||
|
||||
NodeType type() const { return m_type; }
|
||||
|
@ -94,26 +83,26 @@ public:
|
|||
virtual bool is_html_template_element() const { return false; }
|
||||
virtual bool is_browsing_context_container() const { return false; }
|
||||
|
||||
ExceptionOr<NonnullRefPtr<Node>> pre_insert(NonnullRefPtr<Node>, RefPtr<Node>);
|
||||
ExceptionOr<NonnullRefPtr<Node>> pre_remove(NonnullRefPtr<Node>);
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> pre_insert(JS::NonnullGCPtr<Node>, JS::GCPtr<Node>);
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> pre_remove(JS::NonnullGCPtr<Node>);
|
||||
|
||||
ExceptionOr<NonnullRefPtr<Node>> append_child(NonnullRefPtr<Node>);
|
||||
ExceptionOr<NonnullRefPtr<Node>> remove_child(NonnullRefPtr<Node>);
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> append_child(JS::NonnullGCPtr<Node>);
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> remove_child(JS::NonnullGCPtr<Node>);
|
||||
|
||||
void insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool suppress_observers = false);
|
||||
void insert_before(JS::NonnullGCPtr<Node> node, JS::GCPtr<Node> child, bool suppress_observers = false);
|
||||
void remove(bool suppress_observers = false);
|
||||
void remove_all_children(bool suppress_observers = false);
|
||||
u16 compare_document_position(RefPtr<Node> other);
|
||||
u16 compare_document_position(JS::GCPtr<Node> other);
|
||||
|
||||
ExceptionOr<NonnullRefPtr<Node>> replace_child(NonnullRefPtr<Node> node, NonnullRefPtr<Node> child);
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> replace_child(JS::NonnullGCPtr<Node> node, JS::NonnullGCPtr<Node> child);
|
||||
|
||||
NonnullRefPtr<Node> clone_node(Document* document = nullptr, bool clone_children = false);
|
||||
ExceptionOr<NonnullRefPtr<Node>> clone_node_binding(bool deep);
|
||||
JS::NonnullGCPtr<Node> clone_node(Document* document = nullptr, bool clone_children = false);
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> clone_node_binding(bool deep);
|
||||
|
||||
// NOTE: This is intended for the JS bindings.
|
||||
bool has_child_nodes() const { return has_children(); }
|
||||
NonnullRefPtr<NodeList> child_nodes();
|
||||
NonnullRefPtrVector<Node> children_as_vector() const;
|
||||
Vector<JS::Handle<Node>> children_as_vector() const;
|
||||
|
||||
virtual FlyString node_name() const = 0;
|
||||
|
||||
|
@ -129,7 +118,7 @@ public:
|
|||
Document& document() { return *m_document; }
|
||||
Document const& document() const { return *m_document; }
|
||||
|
||||
RefPtr<Document> owner_document() const;
|
||||
JS::GCPtr<Document> owner_document() const;
|
||||
|
||||
const HTML::HTMLAnchorElement* enclosing_link_element() const;
|
||||
const HTML::HTMLElement* enclosing_html_element() const;
|
||||
|
@ -190,14 +179,14 @@ public:
|
|||
template<typename T>
|
||||
bool fast_is() const = delete;
|
||||
|
||||
ExceptionOr<void> ensure_pre_insertion_validity(NonnullRefPtr<Node> node, RefPtr<Node> child) const;
|
||||
ExceptionOr<void> ensure_pre_insertion_validity(JS::NonnullGCPtr<Node> node, JS::GCPtr<Node> child) const;
|
||||
|
||||
bool is_host_including_inclusive_ancestor_of(Node const&) const;
|
||||
|
||||
bool is_scripting_enabled() const;
|
||||
bool is_scripting_disabled() const;
|
||||
|
||||
bool contains(RefPtr<Node>) const;
|
||||
bool contains(JS::GCPtr<Node>) const;
|
||||
|
||||
// Used for dumping the DOM Tree
|
||||
void serialize_tree_as_json(JsonObjectSerializer<StringBuilder>&) const;
|
||||
|
@ -212,13 +201,13 @@ public:
|
|||
|
||||
String serialize_fragment() const;
|
||||
|
||||
void replace_all(RefPtr<Node>);
|
||||
void replace_all(JS::GCPtr<Node>);
|
||||
void string_replace_all(String const&);
|
||||
|
||||
bool is_same_node(Node const*) const;
|
||||
bool is_equal_node(Node const*) const;
|
||||
|
||||
NonnullRefPtr<Node> get_root_node(GetRootNodeOptions const& options = {});
|
||||
JS::NonnullGCPtr<Node> get_root_node(GetRootNodeOptions const& options = {});
|
||||
|
||||
bool is_uninteresting_whitespace_node() const;
|
||||
|
||||
|
@ -237,10 +226,407 @@ public:
|
|||
template<typename Callback>
|
||||
IterationDecision for_each_shadow_including_descendant(Callback);
|
||||
|
||||
Node* parent() { return m_parent.ptr(); }
|
||||
Node const* parent() const { return m_parent.ptr(); }
|
||||
|
||||
bool has_children() const { return m_first_child; }
|
||||
Node* next_sibling() { return m_next_sibling.ptr(); }
|
||||
Node* previous_sibling() { return m_previous_sibling.ptr(); }
|
||||
Node* first_child() { return m_first_child.ptr(); }
|
||||
Node* last_child() { return m_last_child.ptr(); }
|
||||
Node const* next_sibling() const { return m_next_sibling.ptr(); }
|
||||
Node const* previous_sibling() const { return m_previous_sibling.ptr(); }
|
||||
Node const* first_child() const { return m_first_child.ptr(); }
|
||||
Node const* last_child() const { return m_last_child.ptr(); }
|
||||
|
||||
size_t child_count() const
|
||||
{
|
||||
size_t count = 0;
|
||||
for (auto* child = first_child(); child; child = child->next_sibling())
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
Node* child_at_index(int index)
|
||||
{
|
||||
int count = 0;
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (count == index)
|
||||
return child;
|
||||
++count;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node const* child_at_index(int index) const
|
||||
{
|
||||
return const_cast<Node*>(this)->child_at_index(index);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-tree-index
|
||||
size_t index() const
|
||||
{
|
||||
// The index of an object is its number of preceding siblings, or 0 if it has none.
|
||||
size_t index = 0;
|
||||
for (auto* node = previous_sibling(); node; node = node->previous_sibling())
|
||||
++index;
|
||||
return index;
|
||||
}
|
||||
|
||||
Optional<size_t> index_of_child(Node const& search_child)
|
||||
{
|
||||
VERIFY(search_child.parent() == this);
|
||||
size_t index = 0;
|
||||
auto* child = first_child();
|
||||
VERIFY(child);
|
||||
|
||||
do {
|
||||
if (child == &search_child)
|
||||
return index;
|
||||
index++;
|
||||
} while (child && (child = child->next_sibling()));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename ChildType>
|
||||
Optional<size_t> index_of_child(Node const& search_child)
|
||||
{
|
||||
VERIFY(search_child.parent() == this);
|
||||
size_t index = 0;
|
||||
auto* child = first_child();
|
||||
VERIFY(child);
|
||||
|
||||
do {
|
||||
if (!is<ChildType>(child))
|
||||
continue;
|
||||
if (child == &search_child)
|
||||
return index;
|
||||
index++;
|
||||
} while (child && (child = child->next_sibling()));
|
||||
return {};
|
||||
}
|
||||
|
||||
bool is_ancestor_of(Node const&) const;
|
||||
bool is_inclusive_ancestor_of(Node const&) const;
|
||||
bool is_descendant_of(Node const&) const;
|
||||
bool is_inclusive_descendant_of(Node const&) const;
|
||||
|
||||
bool is_following(Node const&) const;
|
||||
|
||||
void prepend_child(JS::NonnullGCPtr<Node> node);
|
||||
|
||||
Node* next_in_pre_order()
|
||||
{
|
||||
if (first_child())
|
||||
return first_child();
|
||||
Node* node;
|
||||
if (!(node = next_sibling())) {
|
||||
node = parent();
|
||||
while (node && !node->next_sibling())
|
||||
node = node->parent();
|
||||
if (node)
|
||||
node = node->next_sibling();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
Node* next_in_pre_order(Node const* stay_within)
|
||||
{
|
||||
if (first_child())
|
||||
return first_child();
|
||||
|
||||
Node* node = static_cast<Node*>(this);
|
||||
Node* next = nullptr;
|
||||
while (!(next = node->next_sibling())) {
|
||||
node = node->parent();
|
||||
if (!node || node == stay_within)
|
||||
return nullptr;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
Node const* next_in_pre_order() const
|
||||
{
|
||||
return const_cast<Node*>(this)->next_in_pre_order();
|
||||
}
|
||||
|
||||
Node const* next_in_pre_order(Node const* stay_within) const
|
||||
{
|
||||
return const_cast<Node*>(this)->next_in_pre_order(stay_within);
|
||||
}
|
||||
|
||||
Node* previous_in_pre_order()
|
||||
{
|
||||
if (auto* node = previous_sibling()) {
|
||||
while (node->last_child())
|
||||
node = node->last_child();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return parent();
|
||||
}
|
||||
|
||||
Node const* previous_in_pre_order() const
|
||||
{
|
||||
return const_cast<Node*>(this)->previous_in_pre_order();
|
||||
}
|
||||
|
||||
bool is_before(Node const& other) const
|
||||
{
|
||||
if (this == &other)
|
||||
return false;
|
||||
for (auto* node = this; node; node = node->next_in_pre_order()) {
|
||||
if (node == &other)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-tree-preceding (Object A is 'typename U' and Object B is 'this')
|
||||
template<typename U>
|
||||
bool has_preceding_node_of_type_in_tree_order() const
|
||||
{
|
||||
for (auto* node = previous_in_pre_order(); node; node = node->previous_in_pre_order()) {
|
||||
if (is<U>(node))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-tree-following (Object A is 'typename U' and Object B is 'this')
|
||||
template<typename U>
|
||||
bool has_following_node_of_type_in_tree_order() const
|
||||
{
|
||||
for (auto* node = next_in_pre_order(); node; node = node->next_in_pre_order()) {
|
||||
if (is<U>(node))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision for_each_in_inclusive_subtree(Callback callback) const
|
||||
{
|
||||
if (callback(static_cast<Node const&>(*this)) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision for_each_in_inclusive_subtree(Callback callback)
|
||||
{
|
||||
if (callback(static_cast<Node&>(*this)) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename U, typename Callback>
|
||||
IterationDecision for_each_in_inclusive_subtree_of_type(Callback callback)
|
||||
{
|
||||
if (is<U>(static_cast<Node const&>(*this))) {
|
||||
if (callback(static_cast<U&>(*this)) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename U, typename Callback>
|
||||
IterationDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
|
||||
{
|
||||
if (is<U>(static_cast<Node const&>(*this))) {
|
||||
if (callback(static_cast<U const&>(*this)) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision for_each_in_subtree(Callback callback) const
|
||||
{
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision for_each_in_subtree(Callback callback)
|
||||
{
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename U, typename Callback>
|
||||
IterationDecision for_each_in_subtree_of_type(Callback callback)
|
||||
{
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename U, typename Callback>
|
||||
IterationDecision for_each_in_subtree_of_type(Callback callback) const
|
||||
{
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_child(Callback callback) const
|
||||
{
|
||||
return const_cast<Node*>(this)->template for_each_child(move(callback));
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_child(Callback callback)
|
||||
{
|
||||
for (auto* node = first_child(); node; node = node->next_sibling())
|
||||
callback(*node);
|
||||
}
|
||||
|
||||
template<typename U, typename Callback>
|
||||
void for_each_child_of_type(Callback callback)
|
||||
{
|
||||
for (auto* node = first_child(); node; node = node->next_sibling()) {
|
||||
if (is<U>(node))
|
||||
callback(verify_cast<U>(*node));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U, typename Callback>
|
||||
void for_each_child_of_type(Callback callback) const
|
||||
{
|
||||
return const_cast<Node*>(this)->template for_each_child_of_type<U>(move(callback));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U const* next_sibling_of_type() const
|
||||
{
|
||||
return const_cast<Node*>(this)->template next_sibling_of_type<U>();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline U* next_sibling_of_type()
|
||||
{
|
||||
for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
|
||||
if (is<U>(*sibling))
|
||||
return &verify_cast<U>(*sibling);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U const* previous_sibling_of_type() const
|
||||
{
|
||||
return const_cast<Node*>(this)->template previous_sibling_of_type<U>();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U* previous_sibling_of_type()
|
||||
{
|
||||
for (auto* sibling = previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
|
||||
if (is<U>(*sibling))
|
||||
return &verify_cast<U>(*sibling);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U const* first_child_of_type() const
|
||||
{
|
||||
return const_cast<Node*>(this)->template first_child_of_type<U>();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U const* last_child_of_type() const
|
||||
{
|
||||
return const_cast<Node*>(this)->template last_child_of_type<U>();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U* first_child_of_type()
|
||||
{
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (is<U>(*child))
|
||||
return &verify_cast<U>(*child);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U* last_child_of_type()
|
||||
{
|
||||
for (auto* child = last_child(); child; child = child->previous_sibling()) {
|
||||
if (is<U>(*child))
|
||||
return &verify_cast<U>(*child);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
bool has_child_of_type() const
|
||||
{
|
||||
return first_child_of_type<U>() != nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U const* first_ancestor_of_type() const
|
||||
{
|
||||
return const_cast<Node*>(this)->template first_ancestor_of_type<U>();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U* first_ancestor_of_type()
|
||||
{
|
||||
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
|
||||
if (is<U>(*ancestor))
|
||||
return &verify_cast<U>(*ancestor);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool is_parent_of(Node const& other) const
|
||||
{
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (&other == child)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
Node(JS::Realm&, Document&, NodeType);
|
||||
Node(Document&, NodeType);
|
||||
|
||||
Document* m_document { nullptr };
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
JS::GCPtr<Document> m_document;
|
||||
mutable WeakPtr<Layout::Node> m_layout_node;
|
||||
NodeType m_type { NodeType::INVALID };
|
||||
bool m_needs_style_update { false };
|
||||
|
@ -254,6 +640,18 @@ protected:
|
|||
|
||||
private:
|
||||
void queue_tree_mutation_record(NonnullRefPtr<NodeList> added_nodes, NonnullRefPtr<NodeList> removed_nodes, Node* previous_sibling, Node* next_sibling);
|
||||
|
||||
void insert_before_impl(JS::NonnullGCPtr<Node>, JS::GCPtr<Node> child);
|
||||
void append_child_impl(JS::NonnullGCPtr<Node>);
|
||||
void remove_child_impl(JS::NonnullGCPtr<Node>);
|
||||
|
||||
JS::GCPtr<Node> m_parent;
|
||||
JS::GCPtr<Node> m_first_child;
|
||||
JS::GCPtr<Node> m_last_child;
|
||||
JS::GCPtr<Node> m_next_sibling;
|
||||
JS::GCPtr<Node> m_previous_sibling;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(Node, Web::DOM)
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/NodeFilter.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
|
|
|
@ -12,16 +12,13 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
class NodeFilter final : public Bindings::PlatformObject {
|
||||
JS_OBJECT(NodeFilter, Bindings::PlatformObject);
|
||||
WEB_PLATFORM_OBJECT(NodeFilter, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
static JS::NonnullGCPtr<NodeFilter> create(JS::Realm&, Bindings::CallbackType&);
|
||||
NodeFilter(JS::Realm&, Bindings::CallbackType&);
|
||||
|
||||
virtual ~NodeFilter() = default;
|
||||
|
||||
NodeFilter& impl() { return *this; }
|
||||
|
||||
Bindings::CallbackType& callback() { return m_callback; }
|
||||
|
||||
enum Result {
|
||||
|
@ -31,14 +28,13 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
NodeFilter(JS::Realm&, Bindings::CallbackType&);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
Bindings::CallbackType& m_callback;
|
||||
};
|
||||
|
||||
inline JS::Object* wrap(JS::Realm&, Web::DOM::NodeFilter& filter)
|
||||
{
|
||||
return &filter.callback().callback;
|
||||
}
|
||||
|
||||
}
|
||||
WRAPPER_HACK(NodeFilter, Web::DOM)
|
||||
|
|
|
@ -7,15 +7,14 @@
|
|||
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
|
||||
#include <LibWeb/Bindings/IDLAbstractOperations.h>
|
||||
#include <LibWeb/Bindings/NodeIteratorPrototype.h>
|
||||
#include <LibWeb/Bindings/NodeWrapper.h>
|
||||
#include <LibWeb/Bindings/NodeWrapperFactory.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/DOM/NodeIterator.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
NodeIterator::NodeIterator(Node& root)
|
||||
: PlatformObject(root.document().preferred_window_object().ensure_web_prototype<Bindings::NodeIteratorPrototype>("NodeIterator"))
|
||||
: PlatformObject(root.document().window().ensure_web_prototype<Bindings::NodeIteratorPrototype>("NodeIterator"))
|
||||
, m_root(root)
|
||||
, m_reference({ root })
|
||||
{
|
||||
|
@ -31,6 +30,11 @@ void NodeIterator::visit_edges(Cell::Visitor& visitor)
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_filter.ptr());
|
||||
visitor.visit(m_root.ptr());
|
||||
visitor.visit(m_reference.node.ptr());
|
||||
|
||||
if (m_traversal_pointer.has_value())
|
||||
visitor.visit(m_traversal_pointer->node.ptr());
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-document-createnodeiterator
|
||||
|
@ -39,7 +43,7 @@ JS::NonnullGCPtr<NodeIterator> NodeIterator::create(Node& root, unsigned what_to
|
|||
// 1. Let iterator be a new NodeIterator object.
|
||||
// 2. Set iterator’s root and iterator’s reference to root.
|
||||
// 3. Set iterator’s pointer before reference to true.
|
||||
auto& window_object = root.document().preferred_window_object();
|
||||
auto& window_object = root.document().window();
|
||||
auto* iterator = window_object.heap().allocate<NodeIterator>(window_object.realm(), root);
|
||||
|
||||
// 4. Set iterator’s whatToShow to whatToShow.
|
||||
|
@ -60,13 +64,13 @@ void NodeIterator::detach()
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-nodeiterator-traverse
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> NodeIterator::traverse(Direction direction)
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> NodeIterator::traverse(Direction direction)
|
||||
{
|
||||
// 1. Let node be iterator’s reference.
|
||||
// 2. Let beforeNode be iterator’s pointer before reference.
|
||||
m_traversal_pointer = m_reference;
|
||||
|
||||
RefPtr<Node> candidate;
|
||||
JS::GCPtr<Node> candidate;
|
||||
|
||||
// 3. While true:
|
||||
while (true) {
|
||||
|
@ -79,7 +83,7 @@ JS::ThrowCompletionOr<RefPtr<Node>> NodeIterator::traverse(Direction direction)
|
|||
auto* next_node = m_traversal_pointer->node->next_in_pre_order(m_root.ptr());
|
||||
if (!next_node)
|
||||
return nullptr;
|
||||
m_traversal_pointer->node = *next_node;
|
||||
m_traversal_pointer->node = next_node;
|
||||
} else {
|
||||
// If beforeNode is true, then set it to false.
|
||||
m_traversal_pointer->is_before_node = false;
|
||||
|
@ -89,12 +93,12 @@ JS::ThrowCompletionOr<RefPtr<Node>> NodeIterator::traverse(Direction direction)
|
|||
// If beforeNode is true, then set node to the first node preceding node in iterator’s iterator collection.
|
||||
// If there is no such node, then return null.
|
||||
if (m_traversal_pointer->is_before_node) {
|
||||
if (m_traversal_pointer->node == m_root.ptr())
|
||||
if (m_traversal_pointer->node.ptr() == m_root.ptr())
|
||||
return nullptr;
|
||||
auto* previous_node = m_traversal_pointer->node->previous_in_pre_order();
|
||||
if (!previous_node)
|
||||
return nullptr;
|
||||
m_traversal_pointer->node = *previous_node;
|
||||
m_traversal_pointer->node = previous_node;
|
||||
} else {
|
||||
// If beforeNode is false, then set it to true.
|
||||
m_traversal_pointer->is_before_node = true;
|
||||
|
@ -161,13 +165,13 @@ JS::ThrowCompletionOr<NodeFilter::Result> NodeIterator::filter(Node& node)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-nodeiterator-nextnode
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> NodeIterator::next_node()
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> NodeIterator::next_node()
|
||||
{
|
||||
return traverse(Direction::Next);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-nodeiterator-previousnode
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> NodeIterator::previous_node()
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> NodeIterator::previous_node()
|
||||
{
|
||||
return traverse(Direction::Previous);
|
||||
}
|
||||
|
@ -189,7 +193,7 @@ void NodeIterator::run_pre_removing_steps_with_node_pointer(Node& to_be_removed_
|
|||
while (node && node->is_descendant_of(to_be_removed_node))
|
||||
node = node->next_in_pre_order(root());
|
||||
if (node)
|
||||
pointer.node = *node;
|
||||
pointer.node = node;
|
||||
return;
|
||||
}
|
||||
if (auto* node = to_be_removed_node.previous_in_pre_order()) {
|
||||
|
@ -213,7 +217,7 @@ void NodeIterator::run_pre_removing_steps_with_node_pointer(Node& to_be_removed_
|
|||
node = node->previous_in_pre_order();
|
||||
}
|
||||
if (node)
|
||||
pointer.node = *node;
|
||||
pointer.node = node;
|
||||
return;
|
||||
}
|
||||
auto* node = to_be_removed_node.next_in_pre_order(root());
|
||||
|
@ -222,7 +226,7 @@ void NodeIterator::run_pre_removing_steps_with_node_pointer(Node& to_be_removed_
|
|||
node = node->previous_in_pre_order();
|
||||
}
|
||||
if (node)
|
||||
pointer.node = *node;
|
||||
pointer.node = node;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#nodeiterator-pre-removing-steps
|
||||
|
|
|
@ -13,31 +13,30 @@ namespace Web::DOM {
|
|||
|
||||
// https://dom.spec.whatwg.org/#nodeiterator
|
||||
class NodeIterator final : public Bindings::PlatformObject {
|
||||
JS_OBJECT(NodeIterator, Bindings::PlatformObject);
|
||||
WEB_PLATFORM_OBJECT(NodeIterator, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
static JS::NonnullGCPtr<NodeIterator> create(Node& root, unsigned what_to_show, JS::GCPtr<NodeFilter>);
|
||||
|
||||
NodeIterator(Node& root);
|
||||
virtual ~NodeIterator() override;
|
||||
|
||||
NodeIterator& impl() { return *this; }
|
||||
|
||||
NonnullRefPtr<Node> root() { return m_root; }
|
||||
NonnullRefPtr<Node> reference_node() { return m_reference.node; }
|
||||
JS::NonnullGCPtr<Node> root() { return m_root; }
|
||||
JS::NonnullGCPtr<Node> reference_node() { return m_reference.node; }
|
||||
bool pointer_before_reference_node() const { return m_reference.is_before_node; }
|
||||
unsigned what_to_show() const { return m_what_to_show; }
|
||||
|
||||
NodeFilter* filter() { return m_filter.ptr(); }
|
||||
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> next_node();
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> previous_node();
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> next_node();
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> previous_node();
|
||||
|
||||
void detach();
|
||||
|
||||
void run_pre_removing_steps(Node&);
|
||||
|
||||
private:
|
||||
explicit NodeIterator(Node& root);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
enum class Direction {
|
||||
|
@ -45,15 +44,15 @@ private:
|
|||
Previous,
|
||||
};
|
||||
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> traverse(Direction);
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> traverse(Direction);
|
||||
|
||||
JS::ThrowCompletionOr<NodeFilter::Result> filter(Node&);
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-root
|
||||
NonnullRefPtr<DOM::Node> m_root;
|
||||
JS::NonnullGCPtr<Node> m_root;
|
||||
|
||||
struct NodePointer {
|
||||
NonnullRefPtr<DOM::Node> node;
|
||||
JS::NonnullGCPtr<Node> node;
|
||||
|
||||
// https://dom.spec.whatwg.org/#nodeiterator-pointer-before-reference
|
||||
bool is_before_node { true };
|
||||
|
@ -72,7 +71,7 @@ private:
|
|||
unsigned m_what_to_show { 0 };
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-filter
|
||||
JS::GCPtr<DOM::NodeFilter> m_filter;
|
||||
JS::GCPtr<NodeFilter> m_filter;
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-active
|
||||
bool m_active { false };
|
||||
|
@ -80,7 +79,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace Web::Bindings {
|
||||
inline JS::Object* wrap(JS::Realm&, Web::DOM::NodeIterator& object) { return &object; }
|
||||
using NodeIteratorWrapper = Web::DOM::NodeIterator;
|
||||
}
|
||||
WRAPPER_HACK(NodeIterator, Web::DOM)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/DocumentFragment.h>
|
||||
#include <LibWeb/DOM/NodeOperations.h>
|
||||
#include <LibWeb/DOM/Text.h>
|
||||
|
@ -13,7 +14,7 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
// https://dom.spec.whatwg.org/#converting-nodes-into-a-node
|
||||
ExceptionOr<NonnullRefPtr<Node>> convert_nodes_to_single_node(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes, DOM::Document& document)
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> convert_nodes_to_single_node(Vector<Variant<JS::Handle<Node>, String>> const& nodes, DOM::Document& document)
|
||||
{
|
||||
// 1. Let node be null.
|
||||
// 2. Replace each string in nodes with a new Text node whose data is the string and node document is document.
|
||||
|
@ -21,18 +22,18 @@ ExceptionOr<NonnullRefPtr<Node>> convert_nodes_to_single_node(Vector<Variant<Non
|
|||
// 4. Otherwise, set node to a new DocumentFragment node whose node document is document, and then append each node in nodes, if any, to it.
|
||||
// 5. Return node.
|
||||
|
||||
auto potentially_convert_string_to_text_node = [&document](Variant<NonnullRefPtr<Node>, String> const& node) -> NonnullRefPtr<Node> {
|
||||
if (node.has<NonnullRefPtr<Node>>())
|
||||
return node.get<NonnullRefPtr<Node>>();
|
||||
auto potentially_convert_string_to_text_node = [&document](Variant<JS::Handle<Node>, String> const& node) -> JS::NonnullGCPtr<Node> {
|
||||
if (node.has<JS::Handle<Node>>())
|
||||
return *node.get<JS::Handle<Node>>();
|
||||
|
||||
return adopt_ref(*new Text(document, node.get<String>()));
|
||||
return *document.heap().allocate<DOM::Text>(document.realm(), document, node.get<String>());
|
||||
};
|
||||
|
||||
if (nodes.size() == 1)
|
||||
return potentially_convert_string_to_text_node(nodes.first());
|
||||
|
||||
// This is NNRP<Node> instead of NNRP<DocumentFragment> to be compatible with the return type.
|
||||
NonnullRefPtr<Node> document_fragment = adopt_ref(*new DocumentFragment(document));
|
||||
// This is NNGCP<Node> instead of NNGCP<DocumentFragment> to be compatible with the return type.
|
||||
JS::NonnullGCPtr<Node> document_fragment = *document.heap().allocate<DOM::DocumentFragment>(document.realm(), document);
|
||||
for (auto& unconverted_node : nodes) {
|
||||
auto node = potentially_convert_string_to_text_node(unconverted_node);
|
||||
(void)TRY(document_fragment->append_child(node));
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
ExceptionOr<NonnullRefPtr<Node>> convert_nodes_to_single_node(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes, DOM::Document& document);
|
||||
ExceptionOr<JS::NonnullGCPtr<Node>> convert_nodes_to_single_node(Vector<Variant<JS::Handle<Node>, String>> const& nodes, DOM::Document& document);
|
||||
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@ namespace Web::DOM {
|
|||
template<typename NodeType>
|
||||
class NonElementParentNode {
|
||||
public:
|
||||
RefPtr<Element> get_element_by_id(FlyString const& id) const
|
||||
JS::GCPtr<Element> get_element_by_id(FlyString const& id) const
|
||||
{
|
||||
RefPtr<Element> found_element;
|
||||
JS::GCPtr<Element> found_element;
|
||||
static_cast<NodeType const*>(this)->template for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
|
||||
if (element.attribute(HTML::AttributeNames::id) == id) {
|
||||
found_element = &element;
|
||||
|
@ -28,7 +28,7 @@ public:
|
|||
});
|
||||
return found_element;
|
||||
}
|
||||
RefPtr<Element> get_element_by_id(FlyString const& id)
|
||||
JS::GCPtr<Element> get_element_by_id(FlyString const& id)
|
||||
{
|
||||
return const_cast<NonElementParentNode const*>(this)->get_element_by_id(id);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
namespace Web::DOM {
|
||||
|
||||
ExceptionOr<RefPtr<Element>> ParentNode::query_selector(StringView selector_text)
|
||||
ExceptionOr<JS::GCPtr<Element>> ParentNode::query_selector(StringView selector_text)
|
||||
{
|
||||
auto maybe_selectors = parse_selector(CSS::Parser::ParsingContext(*this), selector_text);
|
||||
if (!maybe_selectors.has_value())
|
||||
|
@ -23,12 +23,12 @@ ExceptionOr<RefPtr<Element>> ParentNode::query_selector(StringView selector_text
|
|||
|
||||
auto selectors = maybe_selectors.value();
|
||||
|
||||
RefPtr<Element> result;
|
||||
JS::GCPtr<Element> result;
|
||||
// FIXME: This should be shadow-including. https://drafts.csswg.org/selectors-4/#match-a-selector-against-a-tree
|
||||
for_each_in_subtree_of_type<Element>([&](auto& element) {
|
||||
for (auto& selector : selectors) {
|
||||
if (SelectorEngine::matches(selector, element)) {
|
||||
result = element;
|
||||
result = &element;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
}
|
||||
|
@ -46,12 +46,12 @@ ExceptionOr<NonnullRefPtr<NodeList>> ParentNode::query_selector_all(StringView s
|
|||
|
||||
auto selectors = maybe_selectors.value();
|
||||
|
||||
NonnullRefPtrVector<Node> elements;
|
||||
Vector<JS::Handle<Node>> elements;
|
||||
// FIXME: This should be shadow-including. https://drafts.csswg.org/selectors-4/#match-a-selector-against-a-tree
|
||||
for_each_in_subtree_of_type<Element>([&](auto& element) {
|
||||
for (auto& selector : selectors) {
|
||||
if (SelectorEngine::matches(selector, element)) {
|
||||
elements.append(element);
|
||||
elements.append(&element);
|
||||
}
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
|
@ -60,12 +60,12 @@ ExceptionOr<NonnullRefPtr<NodeList>> ParentNode::query_selector_all(StringView s
|
|||
return StaticNodeList::create(move(elements));
|
||||
}
|
||||
|
||||
RefPtr<Element> ParentNode::first_element_child()
|
||||
JS::GCPtr<Element> ParentNode::first_element_child()
|
||||
{
|
||||
return first_child_of_type<Element>();
|
||||
}
|
||||
|
||||
RefPtr<Element> ParentNode::last_element_child()
|
||||
JS::GCPtr<Element> ParentNode::last_element_child()
|
||||
{
|
||||
return last_child_of_type<Element>();
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ NonnullRefPtr<HTMLCollection> ParentNode::get_elements_by_tag_name_ns(FlyString
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-parentnode-prepend
|
||||
ExceptionOr<void> ParentNode::prepend(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
|
||||
ExceptionOr<void> ParentNode::prepend(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
||||
{
|
||||
// 1. Let node be the result of converting nodes into a node given nodes and this’s node document.
|
||||
auto node = TRY(convert_nodes_to_single_node(nodes, document()));
|
||||
|
@ -168,7 +168,7 @@ ExceptionOr<void> ParentNode::prepend(Vector<Variant<NonnullRefPtr<Node>, String
|
|||
return {};
|
||||
}
|
||||
|
||||
ExceptionOr<void> ParentNode::append(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
|
||||
ExceptionOr<void> ParentNode::append(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
||||
{
|
||||
// 1. Let node be the result of converting nodes into a node given nodes and this’s node document.
|
||||
auto node = TRY(convert_nodes_to_single_node(nodes, document()));
|
||||
|
@ -179,7 +179,7 @@ ExceptionOr<void> ParentNode::append(Vector<Variant<NonnullRefPtr<Node>, String>
|
|||
return {};
|
||||
}
|
||||
|
||||
ExceptionOr<void> ParentNode::replace_children(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
|
||||
ExceptionOr<void> ParentNode::replace_children(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
||||
{
|
||||
// 1. Let node be the result of converting nodes into a node given nodes and this’s node document.
|
||||
auto node = TRY(convert_nodes_to_single_node(nodes, document()));
|
||||
|
@ -188,7 +188,7 @@ ExceptionOr<void> ParentNode::replace_children(Vector<Variant<NonnullRefPtr<Node
|
|||
TRY(ensure_pre_insertion_validity(node, nullptr));
|
||||
|
||||
// 3. Replace all with node within this.
|
||||
replace_all(node);
|
||||
replace_all(*node);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,23 +6,24 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullRefPtrVector.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
class ParentNode : public Node {
|
||||
WEB_PLATFORM_OBJECT(ParentNode, Node);
|
||||
|
||||
public:
|
||||
template<typename F>
|
||||
void for_each_child(F) const;
|
||||
template<typename F>
|
||||
void for_each_child(F);
|
||||
|
||||
RefPtr<Element> first_element_child();
|
||||
RefPtr<Element> last_element_child();
|
||||
JS::GCPtr<Element> first_element_child();
|
||||
JS::GCPtr<Element> last_element_child();
|
||||
u32 child_element_count() const;
|
||||
|
||||
ExceptionOr<RefPtr<Element>> query_selector(StringView);
|
||||
ExceptionOr<JS::GCPtr<Element>> query_selector(StringView);
|
||||
ExceptionOr<NonnullRefPtr<NodeList>> query_selector_all(StringView);
|
||||
|
||||
NonnullRefPtr<HTMLCollection> children();
|
||||
|
@ -30,11 +31,16 @@ public:
|
|||
NonnullRefPtr<HTMLCollection> get_elements_by_tag_name(FlyString const&);
|
||||
NonnullRefPtr<HTMLCollection> get_elements_by_tag_name_ns(FlyString const&, FlyString const&);
|
||||
|
||||
ExceptionOr<void> prepend(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes);
|
||||
ExceptionOr<void> append(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes);
|
||||
ExceptionOr<void> replace_children(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes);
|
||||
ExceptionOr<void> prepend(Vector<Variant<JS::Handle<Node>, String>> const& nodes);
|
||||
ExceptionOr<void> append(Vector<Variant<JS::Handle<Node>, String>> const& nodes);
|
||||
ExceptionOr<void> replace_children(Vector<Variant<JS::Handle<Node>, String>> const& nodes);
|
||||
|
||||
protected:
|
||||
ParentNode(JS::Realm& realm, Document& document, NodeType type)
|
||||
: Node(realm, document, type)
|
||||
{
|
||||
}
|
||||
|
||||
ParentNode(Document& document, NodeType type)
|
||||
: Node(document, type)
|
||||
{
|
||||
|
@ -59,3 +65,5 @@ inline void ParentNode::for_each_child(Callback callback)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(ParentNode, Web::DOM)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
Position::Position(Node& node, unsigned offset)
|
||||
: m_node(node)
|
||||
: m_node(JS::make_handle(node))
|
||||
, m_offset(offset)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@ public:
|
|||
Position() = default;
|
||||
Position(Node&, unsigned offset);
|
||||
|
||||
bool is_valid() const { return m_node; }
|
||||
bool is_valid() const { return m_node.ptr(); }
|
||||
|
||||
Node* node() { return m_node; }
|
||||
Node const* node() const { return m_node; }
|
||||
Node* node() { return m_node.cell(); }
|
||||
Node const* node() const { return m_node.cell(); }
|
||||
|
||||
unsigned offset() const { return m_offset; }
|
||||
bool offset_is_at_end_of_node() const;
|
||||
|
@ -31,7 +31,7 @@ public:
|
|||
|
||||
bool operator==(Position const& other) const
|
||||
{
|
||||
return m_node == other.m_node && m_offset == other.m_offset;
|
||||
return m_node.ptr() == other.m_node.ptr() && m_offset == other.m_offset;
|
||||
}
|
||||
|
||||
bool operator!=(Position const& other) const
|
||||
|
@ -42,7 +42,7 @@ public:
|
|||
String to_string() const;
|
||||
|
||||
private:
|
||||
RefPtr<Node> m_node;
|
||||
JS::Handle<Node> m_node;
|
||||
unsigned m_offset { 0 };
|
||||
};
|
||||
|
||||
|
|
|
@ -12,10 +12,9 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
class ProcessingInstruction final : public CharacterData {
|
||||
public:
|
||||
using WrapperType = Bindings::ProcessingInstructionWrapper;
|
||||
WEB_PLATFORM_OBJECT(ProcessingInstruction, CharacterData);
|
||||
|
||||
ProcessingInstruction(Document&, String const& data, String const& target);
|
||||
public:
|
||||
virtual ~ProcessingInstruction() override = default;
|
||||
|
||||
virtual FlyString node_name() const override { return m_target; }
|
||||
|
@ -23,6 +22,8 @@ public:
|
|||
String const& target() const { return m_target; }
|
||||
|
||||
private:
|
||||
ProcessingInstruction(Document&, String const& data, String const& target);
|
||||
|
||||
String m_target;
|
||||
};
|
||||
|
||||
|
@ -30,3 +31,5 @@ template<>
|
|||
inline bool Node::fast_is<ProcessingInstruction>() const { return node_type() == (u16)NodeType::PROCESSING_INSTRUCTION_NODE; }
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(ProcessingInstruction, Web::DOM)
|
||||
|
|
|
@ -32,17 +32,17 @@ JS::NonnullGCPtr<Range> Range::create(HTML::Window& window)
|
|||
|
||||
JS::NonnullGCPtr<Range> Range::create(Document& document)
|
||||
{
|
||||
auto& window_object = document.preferred_window_object();
|
||||
auto& window_object = document.window();
|
||||
return *window_object.heap().allocate<Range>(window_object.realm(), document);
|
||||
}
|
||||
|
||||
JS::NonnullGCPtr<Range> Range::create(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset)
|
||||
{
|
||||
auto& window_object = start_container.document().preferred_window_object();
|
||||
auto& window_object = start_container.document().window();
|
||||
return *window_object.heap().allocate<Range>(window_object.realm(), start_container, start_offset, end_container, end_offset);
|
||||
}
|
||||
|
||||
JS::NonnullGCPtr<Range> Range::create_with_global_object(Bindings::WindowObject& window)
|
||||
JS::NonnullGCPtr<Range> Range::create_with_global_object(HTML::Window& window)
|
||||
{
|
||||
return Range::create(window.impl());
|
||||
}
|
||||
|
@ -50,13 +50,13 @@ JS::NonnullGCPtr<Range> Range::create_with_global_object(Bindings::WindowObject&
|
|||
Range::Range(Document& document)
|
||||
: Range(document, 0, document, 0)
|
||||
{
|
||||
set_prototype(&document.preferred_window_object().ensure_web_prototype<Bindings::RangePrototype>("Range"));
|
||||
set_prototype(&document.window().ensure_web_prototype<Bindings::RangePrototype>("Range"));
|
||||
}
|
||||
|
||||
Range::Range(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset)
|
||||
: AbstractRange(start_container, start_offset, end_container, end_offset)
|
||||
{
|
||||
set_prototype(&start_container.document().preferred_window_object().ensure_web_prototype<Bindings::RangePrototype>("Range"));
|
||||
set_prototype(&start_container.document().window().ensure_web_prototype<Bindings::RangePrototype>("Range"));
|
||||
live_ranges().set(this);
|
||||
}
|
||||
|
||||
|
@ -114,13 +114,13 @@ static RelativeBoundaryPointPosition position_of_boundary_point_relative_to_othe
|
|||
// 4. If nodeA is an ancestor of nodeB:
|
||||
if (node_a.is_ancestor_of(node_b)) {
|
||||
// 1. Let child be nodeB.
|
||||
NonnullRefPtr<Node> child = node_b;
|
||||
JS::NonnullGCPtr<Node> child = node_b;
|
||||
|
||||
// 2. While child is not a child of nodeA, set child to its parent.
|
||||
while (!node_a.is_parent_of(child)) {
|
||||
auto* parent = child->parent();
|
||||
VERIFY(parent);
|
||||
child = *parent;
|
||||
child = parent;
|
||||
}
|
||||
|
||||
// 3. If child’s index is less than offsetA, then return after.
|
||||
|
@ -134,13 +134,6 @@ static RelativeBoundaryPointPosition position_of_boundary_point_relative_to_othe
|
|||
|
||||
ExceptionOr<void> Range::set_start_or_end(Node& node, u32 offset, StartOrEnd start_or_end)
|
||||
{
|
||||
// FIXME: If the incoming node is part of a document that's in the process of being destroyed,
|
||||
// we just ignore this. This prevents us from trying to re-ref a document during its
|
||||
// destruction process. This is a hack and should be replaced with some smarter form
|
||||
// of lifetime management.
|
||||
if (node.document().in_removed_last_ref())
|
||||
return {};
|
||||
|
||||
// To set the start or end of a range to a boundary point (node, offset), run these steps:
|
||||
|
||||
// 1. If node is a doctype, then throw an "InvalidNodeTypeError" DOMException.
|
||||
|
@ -158,12 +151,12 @@ ExceptionOr<void> Range::set_start_or_end(Node& node, u32 offset, StartOrEnd sta
|
|||
|
||||
// 1. If range’s root is not equal to node’s root, or if bp is after the range’s end, set range’s end to bp.
|
||||
if (&root() != &node.root() || position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_end_container, m_end_offset) == RelativeBoundaryPointPosition::After) {
|
||||
m_end_container = node;
|
||||
m_end_container = &node;
|
||||
m_end_offset = offset;
|
||||
}
|
||||
|
||||
// 2. Set range’s start to bp.
|
||||
m_start_container = node;
|
||||
m_start_container = &node;
|
||||
m_start_offset = offset;
|
||||
} else {
|
||||
// -> If these steps were invoked as "set the end"
|
||||
|
@ -171,12 +164,12 @@ ExceptionOr<void> Range::set_start_or_end(Node& node, u32 offset, StartOrEnd sta
|
|||
|
||||
// 1. If range’s root is not equal to node’s root, or if bp is before the range’s start, set range’s start to bp.
|
||||
if (&root() != &node.root() || position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_start_container, m_start_offset) == RelativeBoundaryPointPosition::Before) {
|
||||
m_start_container = node;
|
||||
m_start_container = &node;
|
||||
m_start_offset = offset;
|
||||
}
|
||||
|
||||
// 2. Set range’s end to bp.
|
||||
m_end_container = node;
|
||||
m_end_container = &node;
|
||||
m_end_offset = offset;
|
||||
}
|
||||
|
||||
|
@ -268,10 +261,10 @@ ExceptionOr<i16> Range::compare_boundary_points(u16 how, Range const& source_ran
|
|||
if (&root() != &source_range.root())
|
||||
return WrongDocumentError::create("This range is not in the same tree as the source range.");
|
||||
|
||||
RefPtr<Node> this_point_node;
|
||||
JS::GCPtr<Node> this_point_node;
|
||||
u32 this_point_offset = 0;
|
||||
|
||||
RefPtr<Node> other_point_node;
|
||||
JS::GCPtr<Node> other_point_node;
|
||||
u32 other_point_offset = 0;
|
||||
|
||||
// 3. If how is:
|
||||
|
@ -353,11 +346,11 @@ ExceptionOr<void> Range::select(Node& node)
|
|||
auto index = node.index();
|
||||
|
||||
// 4. Set range’s start to boundary point (parent, index).
|
||||
m_start_container = *parent;
|
||||
m_start_container = parent;
|
||||
m_start_offset = index;
|
||||
|
||||
// 5. Set range’s end to boundary point (parent, index plus 1).
|
||||
m_end_container = *parent;
|
||||
m_end_container = parent;
|
||||
m_end_offset = index + 1;
|
||||
|
||||
return {};
|
||||
|
@ -395,11 +388,11 @@ ExceptionOr<void> Range::select_node_contents(Node const& node)
|
|||
auto length = node.length();
|
||||
|
||||
// 3. Set start to the boundary point (node, 0).
|
||||
m_start_container = node;
|
||||
m_start_container = &node;
|
||||
m_start_offset = 0;
|
||||
|
||||
// 4. Set end to the boundary point (node, length).
|
||||
m_end_container = node;
|
||||
m_end_container = &node;
|
||||
m_end_offset = length;
|
||||
|
||||
return {};
|
||||
|
@ -431,7 +424,7 @@ JS::NonnullGCPtr<Range> Range::normalized() const
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-range-commonancestorcontainer
|
||||
NonnullRefPtr<Node> Range::common_ancestor_container() const
|
||||
JS::NonnullGCPtr<Node> Range::common_ancestor_container() const
|
||||
{
|
||||
// 1. Let container be start node.
|
||||
auto container = m_start_container;
|
||||
|
@ -439,7 +432,7 @@ NonnullRefPtr<Node> Range::common_ancestor_container() const
|
|||
// 2. While container is not an inclusive ancestor of end node, let container be container’s parent.
|
||||
while (!container->is_inclusive_ancestor_of(m_end_container)) {
|
||||
VERIFY(container->parent());
|
||||
container = *container->parent();
|
||||
container = container->parent();
|
||||
}
|
||||
|
||||
// 3. Return container.
|
||||
|
@ -557,26 +550,26 @@ String Range::to_string() const
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-range-extractcontents
|
||||
ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::extract_contents()
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::extract_contents()
|
||||
{
|
||||
return extract();
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-range-extract
|
||||
ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::extract()
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::extract()
|
||||
{
|
||||
// 1. Let fragment be a new DocumentFragment node whose node document is range’s start node’s node document.
|
||||
auto fragment = adopt_ref(*new DocumentFragment(const_cast<Document&>(start_container()->document())));
|
||||
auto* fragment = heap().allocate<DOM::DocumentFragment>(realm(), const_cast<Document&>(start_container()->document()));
|
||||
|
||||
// 2. If range is collapsed, then return fragment.
|
||||
if (collapsed())
|
||||
return fragment;
|
||||
return JS::NonnullGCPtr(*fragment);
|
||||
|
||||
// 3. Let original start node, original start offset, original end node, and original end offset
|
||||
// be range’s start node, start offset, end node, and end offset, respectively.
|
||||
NonnullRefPtr<Node> original_start_node = m_start_container;
|
||||
JS::NonnullGCPtr<Node> original_start_node = m_start_container;
|
||||
auto original_start_offset = m_start_offset;
|
||||
NonnullRefPtr<Node> original_end_node = m_end_container;
|
||||
JS::NonnullGCPtr<Node> original_end_node = m_end_container;
|
||||
auto original_end_offset = m_end_offset;
|
||||
|
||||
// 4. If original start node is original end node and it is a CharacterData node, then:
|
||||
|
@ -596,18 +589,18 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::extract()
|
|||
static_cast<CharacterData&>(*original_start_node).replace_data(original_start_offset, original_end_offset - original_start_offset, "");
|
||||
|
||||
// 5. Return fragment.
|
||||
return fragment;
|
||||
return JS::NonnullGCPtr(*fragment);
|
||||
}
|
||||
|
||||
// 5. Let common ancestor be original start node.
|
||||
NonnullRefPtr<Node> common_ancestor = original_start_node;
|
||||
JS::NonnullGCPtr<Node> common_ancestor = original_start_node;
|
||||
|
||||
// 6. While common ancestor is not an inclusive ancestor of original end node, set common ancestor to its own parent.
|
||||
while (!common_ancestor->is_inclusive_ancestor_of(original_end_node))
|
||||
common_ancestor = *common_ancestor->parent_node();
|
||||
common_ancestor = common_ancestor->parent_node();
|
||||
|
||||
// 7. Let first partially contained child be null.
|
||||
RefPtr<Node> first_partially_contained_child;
|
||||
JS::GCPtr<Node> first_partially_contained_child;
|
||||
|
||||
// 8. If original start node is not an inclusive ancestor of original end node,
|
||||
// set first partially contained child to the first child of common ancestor that is partially contained in range.
|
||||
|
@ -621,7 +614,7 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::extract()
|
|||
}
|
||||
|
||||
// 9. Let last partially contained child be null.
|
||||
RefPtr<Node> last_partially_contained_child;
|
||||
JS::GCPtr<Node> last_partially_contained_child;
|
||||
|
||||
// 10. If original end node is not an inclusive ancestor of original start node,
|
||||
// set last partially contained child to the last child of common ancestor that is partially contained in range.
|
||||
|
@ -635,7 +628,7 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::extract()
|
|||
}
|
||||
|
||||
// 11. Let contained children be a list of all children of common ancestor that are contained in range, in tree order.
|
||||
Vector<NonnullRefPtr<Node>> contained_children;
|
||||
Vector<JS::NonnullGCPtr<Node>> contained_children;
|
||||
for (Node const* node = common_ancestor->first_child(); node; node = node->next_sibling()) {
|
||||
if (contains_node(*node))
|
||||
contained_children.append(*node);
|
||||
|
@ -647,7 +640,7 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::extract()
|
|||
return DOM::HierarchyRequestError::create("Contained child is a DocumentType");
|
||||
}
|
||||
|
||||
RefPtr<Node> new_node;
|
||||
JS::GCPtr<Node> new_node;
|
||||
size_t new_offset = 0;
|
||||
|
||||
// 13. If original start node is an inclusive ancestor of original end node, set new node to original start node and new offset to original start offset.
|
||||
|
@ -658,7 +651,7 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::extract()
|
|||
// 14. Otherwise:
|
||||
else {
|
||||
// 1. Let reference node equal original start node.
|
||||
RefPtr<Node> reference_node = original_start_node;
|
||||
JS::GCPtr<Node> reference_node = original_start_node;
|
||||
|
||||
// 2. While reference node’s parent is not null and is not an inclusive ancestor of original end node, set reference node to its parent.
|
||||
while (reference_node->parent_node() && !reference_node->parent_node()->is_inclusive_ancestor_of(original_end_node))
|
||||
|
@ -746,7 +739,7 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::extract()
|
|||
set_end(*new_node, new_offset);
|
||||
|
||||
// 21. Return fragment.
|
||||
return fragment;
|
||||
return JS::NonnullGCPtr(*fragment);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#contained
|
||||
|
@ -779,23 +772,23 @@ bool Range::partially_contains_node(Node const& node) const
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-range-insertnode
|
||||
ExceptionOr<void> Range::insert_node(NonnullRefPtr<Node> node)
|
||||
ExceptionOr<void> Range::insert_node(JS::NonnullGCPtr<Node> node)
|
||||
{
|
||||
return insert(node);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-range-insert
|
||||
ExceptionOr<void> Range::insert(NonnullRefPtr<Node> node)
|
||||
ExceptionOr<void> Range::insert(JS::NonnullGCPtr<Node> node)
|
||||
{
|
||||
// 1. If range’s start node is a ProcessingInstruction or Comment node, is a Text node whose parent is null, or is node, then throw a "HierarchyRequestError" DOMException.
|
||||
if ((is<ProcessingInstruction>(*m_start_container) || is<Comment>(*m_start_container))
|
||||
|| (is<Text>(*m_start_container) && !m_start_container->parent_node())
|
||||
|| m_start_container == node.ptr()) {
|
||||
|| m_start_container.ptr() == node.ptr()) {
|
||||
return DOM::HierarchyRequestError::create("Range has inappropriate start node for insertion");
|
||||
}
|
||||
|
||||
// 2. Let referenceNode be null.
|
||||
RefPtr<Node> reference_node;
|
||||
JS::GCPtr<Node> reference_node;
|
||||
|
||||
// 3. If range’s start node is a Text node, set referenceNode to that Text node.
|
||||
if (is<Text>(*m_start_container)) {
|
||||
|
@ -807,7 +800,7 @@ ExceptionOr<void> Range::insert(NonnullRefPtr<Node> node)
|
|||
}
|
||||
|
||||
// 5. Let parent be range’s start node if referenceNode is null, and referenceNode’s parent otherwise.
|
||||
RefPtr<Node> parent;
|
||||
JS::GCPtr<Node> parent;
|
||||
if (!reference_node)
|
||||
parent = m_start_container;
|
||||
else
|
||||
|
@ -852,7 +845,7 @@ ExceptionOr<void> Range::insert(NonnullRefPtr<Node> node)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-range-surroundcontents
|
||||
ExceptionOr<void> Range::surround_contents(NonnullRefPtr<Node> new_parent)
|
||||
ExceptionOr<void> Range::surround_contents(JS::NonnullGCPtr<Node> new_parent)
|
||||
{
|
||||
// 1. If a non-Text node is partially contained in this, then throw an "InvalidStateError" DOMException.
|
||||
Node* start_non_text_node = start_container();
|
||||
|
@ -886,26 +879,26 @@ ExceptionOr<void> Range::surround_contents(NonnullRefPtr<Node> new_parent)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-range-clonecontents
|
||||
ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::clone_contents()
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::clone_contents()
|
||||
{
|
||||
return clone_the_contents();
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-range-clone
|
||||
ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::clone_the_contents()
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::clone_the_contents()
|
||||
{
|
||||
// 1. Let fragment be a new DocumentFragment node whose node document is range’s start node’s node document.
|
||||
auto fragment = adopt_ref(*new DocumentFragment(const_cast<Document&>(start_container()->document())));
|
||||
auto* fragment = heap().allocate<DOM::DocumentFragment>(realm(), const_cast<Document&>(start_container()->document()));
|
||||
|
||||
// 2. If range is collapsed, then return fragment.
|
||||
if (collapsed())
|
||||
return fragment;
|
||||
return JS::NonnullGCPtr(*fragment);
|
||||
|
||||
// 3. Let original start node, original start offset, original end node, and original end offset
|
||||
// be range’s start node, start offset, end node, and end offset, respectively.
|
||||
NonnullRefPtr<Node> original_start_node = m_start_container;
|
||||
JS::NonnullGCPtr<Node> original_start_node = m_start_container;
|
||||
auto original_start_offset = m_start_offset;
|
||||
NonnullRefPtr<Node> original_end_node = m_end_container;
|
||||
JS::NonnullGCPtr<Node> original_end_node = m_end_container;
|
||||
auto original_end_offset = m_end_offset;
|
||||
|
||||
// 4. If original start node is original end node and it is a CharacterData node, then:
|
||||
|
@ -922,18 +915,18 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::clone_the_contents()
|
|||
fragment->append_child(clone);
|
||||
|
||||
// 4. Return fragment.
|
||||
return fragment;
|
||||
return JS::NonnullGCPtr(*fragment);
|
||||
}
|
||||
|
||||
// 5. Let common ancestor be original start node.
|
||||
NonnullRefPtr<Node> common_ancestor = original_start_node;
|
||||
JS::NonnullGCPtr<Node> common_ancestor = original_start_node;
|
||||
|
||||
// 6. While common ancestor is not an inclusive ancestor of original end node, set common ancestor to its own parent.
|
||||
while (!common_ancestor->is_inclusive_ancestor_of(original_end_node))
|
||||
common_ancestor = *common_ancestor->parent_node();
|
||||
common_ancestor = common_ancestor->parent_node();
|
||||
|
||||
// 7. Let first partially contained child be null.
|
||||
RefPtr<Node> first_partially_contained_child;
|
||||
JS::GCPtr<Node> first_partially_contained_child;
|
||||
|
||||
// 8. If original start node is not an inclusive ancestor of original end node,
|
||||
// set first partially contained child to the first child of common ancestor that is partially contained in range.
|
||||
|
@ -947,7 +940,7 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::clone_the_contents()
|
|||
}
|
||||
|
||||
// 9. Let last partially contained child be null.
|
||||
RefPtr<Node> last_partially_contained_child;
|
||||
JS::GCPtr<Node> last_partially_contained_child;
|
||||
|
||||
// 10. If original end node is not an inclusive ancestor of original start node,
|
||||
// set last partially contained child to the last child of common ancestor that is partially contained in range.
|
||||
|
@ -961,7 +954,7 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::clone_the_contents()
|
|||
}
|
||||
|
||||
// 11. Let contained children be a list of all children of common ancestor that are contained in range, in tree order.
|
||||
Vector<NonnullRefPtr<Node>> contained_children;
|
||||
Vector<JS::NonnullGCPtr<Node>> contained_children;
|
||||
for (Node const* node = common_ancestor->first_child(); node; node = node->next_sibling()) {
|
||||
if (contains_node(*node))
|
||||
contained_children.append(*node);
|
||||
|
@ -1044,7 +1037,7 @@ ExceptionOr<NonnullRefPtr<DocumentFragment>> Range::clone_the_contents()
|
|||
}
|
||||
|
||||
// 18. Return fragment.
|
||||
return fragment;
|
||||
return JS::NonnullGCPtr(*fragment);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-range-deletecontents
|
||||
|
@ -1055,9 +1048,9 @@ ExceptionOr<void> Range::delete_contents()
|
|||
return {};
|
||||
|
||||
// 2. Let original start node, original start offset, original end node, and original end offset be this’s start node, start offset, end node, and end offset, respectively.
|
||||
NonnullRefPtr<Node> original_start_node = m_start_container;
|
||||
JS::NonnullGCPtr<Node> original_start_node = m_start_container;
|
||||
auto original_start_offset = m_start_offset;
|
||||
NonnullRefPtr<Node> original_end_node = m_end_container;
|
||||
JS::NonnullGCPtr<Node> original_end_node = m_end_container;
|
||||
auto original_end_offset = m_end_offset;
|
||||
|
||||
// 3. If original start node is original end node and it is a CharacterData node, then replace data with node original start node, offset original start offset,
|
||||
|
@ -1068,13 +1061,13 @@ ExceptionOr<void> Range::delete_contents()
|
|||
}
|
||||
|
||||
// 4. Let nodes to remove be a list of all the nodes that are contained in this, in tree order, omitting any node whose parent is also contained in this.
|
||||
Vector<NonnullRefPtr<Node>> nodes_to_remove;
|
||||
JS::MarkedVector<Node*> nodes_to_remove(heap());
|
||||
for (Node const* node = start_container(); node != end_container()->next_in_pre_order(); node = node->next_in_pre_order()) {
|
||||
if (contains_node(*node) && (!node->parent_node() || !contains_node(*node->parent_node())))
|
||||
nodes_to_remove.append(*node);
|
||||
nodes_to_remove.append(const_cast<Node*>(node));
|
||||
}
|
||||
|
||||
RefPtr<Node> new_node;
|
||||
JS::GCPtr<Node> new_node;
|
||||
size_t new_offset = 0;
|
||||
|
||||
// 5. If original start node is an inclusive ancestor of original end node, set new node to original start node and new offset to original start offset.
|
||||
|
@ -1089,7 +1082,7 @@ ExceptionOr<void> Range::delete_contents()
|
|||
|
||||
// 2. While reference node’s parent is not null and is not an inclusive ancestor of original end node, set reference node to its parent.
|
||||
while (reference_node->parent_node() && !reference_node->parent_node()->is_inclusive_ancestor_of(original_end_node))
|
||||
reference_node = *reference_node->parent_node();
|
||||
reference_node = reference_node->parent_node();
|
||||
|
||||
// 3. Set new node to the parent of reference node, and new offset to one plus the index of reference node.
|
||||
new_node = reference_node->parent_node();
|
||||
|
|
|
@ -13,15 +13,13 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
class Range final : public AbstractRange {
|
||||
JS_OBJECT(Range, AbstractRange);
|
||||
WEB_PLATFORM_OBJECT(Range, AbstractRange);
|
||||
|
||||
public:
|
||||
Range& impl() { return *this; }
|
||||
|
||||
static JS::NonnullGCPtr<Range> create(Document&);
|
||||
static JS::NonnullGCPtr<Range> create(HTML::Window&);
|
||||
static JS::NonnullGCPtr<Range> create(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset);
|
||||
static JS::NonnullGCPtr<Range> create_with_global_object(Bindings::WindowObject&);
|
||||
static JS::NonnullGCPtr<Range> create_with_global_object(HTML::Window&);
|
||||
|
||||
explicit Range(Document&);
|
||||
Range(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset);
|
||||
|
@ -53,7 +51,7 @@ public:
|
|||
JS::NonnullGCPtr<Range> normalized() const;
|
||||
JS::NonnullGCPtr<Range> clone_range() const;
|
||||
|
||||
NonnullRefPtr<Node> common_ancestor_container() const;
|
||||
JS::NonnullGCPtr<Node> common_ancestor_container() const;
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-range-detach
|
||||
void detach() const
|
||||
|
@ -67,11 +65,11 @@ public:
|
|||
ExceptionOr<i16> compare_point(Node const&, u32 offset) const;
|
||||
|
||||
ExceptionOr<void> delete_contents();
|
||||
ExceptionOr<NonnullRefPtr<DocumentFragment>> extract_contents();
|
||||
ExceptionOr<NonnullRefPtr<DocumentFragment>> clone_contents();
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> extract_contents();
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> clone_contents();
|
||||
|
||||
ExceptionOr<void> insert_node(NonnullRefPtr<Node>);
|
||||
ExceptionOr<void> surround_contents(NonnullRefPtr<Node> new_parent);
|
||||
ExceptionOr<void> insert_node(JS::NonnullGCPtr<Node>);
|
||||
ExceptionOr<void> surround_contents(JS::NonnullGCPtr<Node> new_parent);
|
||||
|
||||
String to_string() const;
|
||||
|
||||
|
@ -89,9 +87,9 @@ private:
|
|||
ExceptionOr<void> set_start_or_end(Node& node, u32 offset, StartOrEnd start_or_end);
|
||||
ExceptionOr<void> select(Node& node);
|
||||
|
||||
ExceptionOr<NonnullRefPtr<DocumentFragment>> extract();
|
||||
ExceptionOr<NonnullRefPtr<DocumentFragment>> clone_the_contents();
|
||||
ExceptionOr<void> insert(NonnullRefPtr<Node>);
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> extract();
|
||||
ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> clone_the_contents();
|
||||
ExceptionOr<void> insert(JS::NonnullGCPtr<Node>);
|
||||
|
||||
bool contains_node(Node const&) const;
|
||||
bool partially_contains_node(Node const&) const;
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
class ShadowRoot final : public DocumentFragment {
|
||||
public:
|
||||
ShadowRoot(Document&, Element&);
|
||||
WEB_PLATFORM_OBJECT(ShadowRoot, DocumentFragment);
|
||||
|
||||
public:
|
||||
bool closed() const { return m_closed; }
|
||||
|
||||
bool delegates_focus() const { return m_delegates_focus; }
|
||||
|
@ -32,6 +32,8 @@ public:
|
|||
ExceptionOr<void> set_inner_html(String const&);
|
||||
|
||||
private:
|
||||
ShadowRoot(Document&, Element&);
|
||||
|
||||
// ^Node
|
||||
virtual FlyString node_name() const override { return "#shadow-root"; }
|
||||
virtual bool is_shadow_root() const final { return true; }
|
||||
|
@ -52,7 +54,7 @@ inline IterationDecision Node::for_each_shadow_including_descendant(Callback cal
|
|||
return IterationDecision::Break;
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->is_element()) {
|
||||
if (RefPtr<ShadowRoot> shadow_root = static_cast<Element*>(child)->shadow_root()) {
|
||||
if (JS::GCPtr<ShadowRoot> shadow_root = static_cast<Element*>(child)->shadow_root()) {
|
||||
if (shadow_root->for_each_shadow_including_descendant(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
|
@ -64,3 +66,5 @@ inline IterationDecision Node::for_each_shadow_including_descendant(Callback cal
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(ShadowRoot, Web::DOM)
|
||||
|
|
|
@ -8,7 +8,12 @@
|
|||
|
||||
namespace Web::DOM {
|
||||
|
||||
StaticNodeList::StaticNodeList(NonnullRefPtrVector<Node>&& static_nodes)
|
||||
NonnullRefPtr<NodeList> StaticNodeList::create(Vector<JS::Handle<Node>> static_nodes)
|
||||
{
|
||||
return adopt_ref(*new StaticNodeList(move(static_nodes)));
|
||||
}
|
||||
|
||||
StaticNodeList::StaticNodeList(Vector<JS::Handle<Node>> static_nodes)
|
||||
: m_static_nodes(move(static_nodes))
|
||||
{
|
||||
}
|
||||
|
@ -25,7 +30,7 @@ Node const* StaticNodeList::item(u32 index) const
|
|||
// The item(index) method must return the indexth node in the collection. If there is no indexth node in the collection, then the method must return null.
|
||||
if (index >= m_static_nodes.size())
|
||||
return nullptr;
|
||||
return &m_static_nodes[index];
|
||||
return m_static_nodes[index].ptr();
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-indices
|
||||
|
|
|
@ -14,10 +14,7 @@ namespace Web::DOM {
|
|||
|
||||
class StaticNodeList : public NodeList {
|
||||
public:
|
||||
static NonnullRefPtr<NodeList> create(NonnullRefPtrVector<Node> static_nodes)
|
||||
{
|
||||
return adopt_ref(*new StaticNodeList(move(static_nodes)));
|
||||
}
|
||||
static NonnullRefPtr<NodeList> create(Vector<JS::Handle<Node>> static_nodes);
|
||||
|
||||
virtual ~StaticNodeList() override = default;
|
||||
|
||||
|
@ -27,9 +24,9 @@ public:
|
|||
virtual bool is_supported_property_index(u32) const override;
|
||||
|
||||
private:
|
||||
StaticNodeList(NonnullRefPtrVector<Node>&& static_nodes);
|
||||
StaticNodeList(Vector<JS::Handle<Node>> static_nodes);
|
||||
|
||||
NonnullRefPtrVector<Node> m_static_nodes;
|
||||
Vector<JS::Handle<Node>> m_static_nodes;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ namespace Web::DOM {
|
|||
StaticRange::StaticRange(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset)
|
||||
: AbstractRange(start_container, start_offset, end_container, end_offset)
|
||||
{
|
||||
set_prototype(&start_container.document().preferred_window_object().ensure_web_prototype<Bindings::StaticRangePrototype>("StaticRange"));
|
||||
set_prototype(&start_container.document().window().ensure_web_prototype<Bindings::StaticRangePrototype>("StaticRange"));
|
||||
}
|
||||
|
||||
StaticRange::~StaticRange() = default;
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-staticrange-staticrange
|
||||
ExceptionOr<StaticRange*> StaticRange::create_with_global_object(Bindings::WindowObject& window_object, StaticRangeInit& init)
|
||||
ExceptionOr<StaticRange*> StaticRange::create_with_global_object(HTML::Window& window_object, StaticRangeInit& init)
|
||||
{
|
||||
// 1. If init["startContainer"] or init["endContainer"] is a DocumentType or Attr node, then throw an "InvalidNodeTypeError" DOMException.
|
||||
if (is<DocumentType>(*init.start_container) || is<Attribute>(*init.start_container))
|
||||
|
|
|
@ -11,25 +11,23 @@
|
|||
|
||||
namespace Web::DOM {
|
||||
|
||||
// NOTE: We must use RP instead of NNRP here, otherwise the generated code cannot default initialize this struct.
|
||||
// NOTE: We must use GCP instead of NNGCP here, otherwise the generated code cannot default initialize this struct.
|
||||
// They will never be null, as they are marked as required and non-null in the dictionary.
|
||||
struct StaticRangeInit {
|
||||
RefPtr<Node> start_container;
|
||||
JS::GCPtr<Node> start_container;
|
||||
u32 start_offset { 0 };
|
||||
RefPtr<Node> end_container;
|
||||
JS::GCPtr<Node> end_container;
|
||||
u32 end_offset { 0 };
|
||||
};
|
||||
|
||||
class StaticRange final : public AbstractRange {
|
||||
JS_OBJECT(StaticRange, JS::Object);
|
||||
WEB_PLATFORM_OBJECT(StaticRange, JS::Object);
|
||||
|
||||
public:
|
||||
static ExceptionOr<StaticRange*> create_with_global_object(Bindings::WindowObject&, StaticRangeInit& init);
|
||||
static ExceptionOr<StaticRange*> create_with_global_object(HTML::Window&, StaticRangeInit& init);
|
||||
|
||||
StaticRange(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset);
|
||||
virtual ~StaticRange() override;
|
||||
|
||||
StaticRange& impl() { return *this; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/TextPrototype.h>
|
||||
#include <LibWeb/DOM/Range.h>
|
||||
#include <LibWeb/DOM/Text.h>
|
||||
#include <LibWeb/HTML/HTMLInputElement.h>
|
||||
|
@ -15,27 +16,35 @@ namespace Web::DOM {
|
|||
Text::Text(Document& document, String const& data)
|
||||
: CharacterData(document, NodeType::TEXT_NODE, data)
|
||||
{
|
||||
set_prototype(&window().ensure_web_prototype<Bindings::TextPrototype>("Text"));
|
||||
}
|
||||
|
||||
Text::Text(Document& document, NodeType type, String const& data)
|
||||
: CharacterData(document, type, data)
|
||||
{
|
||||
set_prototype(&window().ensure_web_prototype<Bindings::TextPrototype>("Text"));
|
||||
}
|
||||
|
||||
void Text::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_owner_input_element.ptr());
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-text-text
|
||||
NonnullRefPtr<Text> Text::create_with_global_object(Bindings::WindowObject& window, String const& data)
|
||||
JS::NonnullGCPtr<Text> Text::create_with_global_object(HTML::Window& window, String const& data)
|
||||
{
|
||||
return make_ref_counted<Text>(window.impl().associated_document(), data);
|
||||
return *window.heap().allocate<Text>(window.realm(), window.associated_document(), data);
|
||||
}
|
||||
|
||||
void Text::set_owner_input_element(Badge<HTML::HTMLInputElement>, HTML::HTMLInputElement& input_element)
|
||||
{
|
||||
m_owner_input_element = input_element;
|
||||
m_owner_input_element = &input_element;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-text-splittext
|
||||
// https://dom.spec.whatwg.org/#concept-text-split
|
||||
ExceptionOr<NonnullRefPtr<Text>> Text::split_text(size_t offset)
|
||||
ExceptionOr<JS::NonnullGCPtr<Text>> Text::split_text(size_t offset)
|
||||
{
|
||||
// 1. Let length be node’s length.
|
||||
auto length = this->length();
|
||||
|
@ -51,26 +60,26 @@ ExceptionOr<NonnullRefPtr<Text>> Text::split_text(size_t offset)
|
|||
auto new_data = TRY(substring_data(offset, count));
|
||||
|
||||
// 5. Let new node be a new Text node, with the same node document as node. Set new node’s data to new data.
|
||||
auto new_node = adopt_ref(*new Text(document(), new_data));
|
||||
auto new_node = JS::NonnullGCPtr(*heap().allocate<Text>(realm(), document(), new_data));
|
||||
|
||||
// 6. Let parent be node’s parent.
|
||||
RefPtr<Node> parent = this->parent();
|
||||
JS::GCPtr<Node> parent = this->parent();
|
||||
|
||||
// 7. If parent is not null, then:
|
||||
if (parent) {
|
||||
// 1. Insert new node into parent before node’s next sibling.
|
||||
parent->insert_before(new_node, next_sibling());
|
||||
parent->insert_before(*new_node, next_sibling());
|
||||
|
||||
// 2. For each live range whose start node is node and start offset is greater than offset, set its start node to new node and decrease its start offset by offset.
|
||||
for (auto& range : Range::live_ranges()) {
|
||||
if (range->start_container() == this && range->start_offset() > offset)
|
||||
range->set_start(new_node, range->start_offset() - offset);
|
||||
range->set_start(*new_node, range->start_offset() - offset);
|
||||
}
|
||||
|
||||
// 3. For each live range whose end node is node and end offset is greater than offset, set its end node to new node and decrease its end offset by offset.
|
||||
for (auto& range : Range::live_ranges()) {
|
||||
if (range->end_container() == this && range->end_offset() > offset)
|
||||
range->set_end(new_node, range->end_offset() - offset);
|
||||
range->set_end(*new_node, range->end_offset() - offset);
|
||||
}
|
||||
|
||||
// 4. For each live range whose start node is parent and start offset is equal to the index of node plus 1, increase its start offset by 1.
|
||||
|
@ -81,7 +90,7 @@ ExceptionOr<NonnullRefPtr<Text>> Text::split_text(size_t offset)
|
|||
|
||||
// 5. For each live range whose end node is parent and end offset is equal to the index of node plus 1, increase its end offset by 1.
|
||||
for (auto& range : Range::live_ranges()) {
|
||||
if (range->end_container() == parent && range->end_offset() == index() + 1) {
|
||||
if (range->end_container() == parent.ptr() && range->end_offset() == index() + 1) {
|
||||
range->set_end(*range->end_container(), range->end_offset() + 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,12 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
class Text : public CharacterData {
|
||||
public:
|
||||
using WrapperType = Bindings::TextWrapper;
|
||||
WEB_PLATFORM_OBJECT(Text, CharacterData);
|
||||
|
||||
explicit Text(Document&, String const&);
|
||||
public:
|
||||
virtual ~Text() override = default;
|
||||
|
||||
static NonnullRefPtr<Text> create_with_global_object(Bindings::WindowObject& window, String const& data);
|
||||
static JS::NonnullGCPtr<Text> create_with_global_object(HTML::Window& window, String const& data);
|
||||
|
||||
// ^Node
|
||||
virtual FlyString node_name() const override { return "#text"; }
|
||||
|
@ -28,15 +27,18 @@ public:
|
|||
void set_always_editable(bool b) { m_always_editable = b; }
|
||||
|
||||
void set_owner_input_element(Badge<HTML::HTMLInputElement>, HTML::HTMLInputElement&);
|
||||
HTML::HTMLInputElement* owner_input_element() { return m_owner_input_element; }
|
||||
HTML::HTMLInputElement* owner_input_element() { return m_owner_input_element.ptr(); }
|
||||
|
||||
ExceptionOr<NonnullRefPtr<Text>> split_text(size_t offset);
|
||||
ExceptionOr<JS::NonnullGCPtr<Text>> split_text(size_t offset);
|
||||
|
||||
protected:
|
||||
explicit Text(Document&, String const&);
|
||||
Text(Document&, NodeType, String const&);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
private:
|
||||
WeakPtr<HTML::HTMLInputElement> m_owner_input_element;
|
||||
JS::GCPtr<HTML::HTMLInputElement> m_owner_input_element;
|
||||
|
||||
bool m_always_editable { false };
|
||||
};
|
||||
|
@ -45,3 +47,5 @@ template<>
|
|||
inline bool Node::fast_is<Text>() const { return is_text(); }
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(Text, Web::DOM)
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
|
||||
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
|
||||
#include <LibWeb/Bindings/IDLAbstractOperations.h>
|
||||
#include <LibWeb/Bindings/NodeWrapper.h>
|
||||
#include <LibWeb/Bindings/NodeWrapperFactory.h>
|
||||
#include <LibWeb/Bindings/TreeWalkerPrototype.h>
|
||||
#include <LibWeb/Bindings/Wrapper.h>
|
||||
#include <LibWeb/DOM/DOMException.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/DOM/NodeFilter.h>
|
||||
#include <LibWeb/DOM/TreeWalker.h>
|
||||
|
@ -18,7 +17,7 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
TreeWalker::TreeWalker(Node& root)
|
||||
: PlatformObject(root.document().preferred_window_object().ensure_web_prototype<Bindings::TreeWalkerPrototype>("TreeWalker"))
|
||||
: PlatformObject(root.document().window().ensure_web_prototype<Bindings::TreeWalkerPrototype>("TreeWalker"))
|
||||
, m_root(root)
|
||||
, m_current(root)
|
||||
{
|
||||
|
@ -30,6 +29,8 @@ void TreeWalker::visit_edges(Cell::Visitor& visitor)
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_filter.ptr());
|
||||
visitor.visit(m_root.ptr());
|
||||
visitor.visit(m_current.ptr());
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-document-createtreewalker
|
||||
|
@ -37,7 +38,7 @@ JS::NonnullGCPtr<TreeWalker> TreeWalker::create(Node& root, unsigned what_to_sho
|
|||
{
|
||||
// 1. Let walker be a new TreeWalker object.
|
||||
// 2. Set walker’s root and walker’s current to root.
|
||||
auto& window_object = root.document().preferred_window_object();
|
||||
auto& window_object = root.document().window();
|
||||
auto* walker = window_object.heap().allocate<TreeWalker>(window_object.realm(), root);
|
||||
|
||||
// 3. Set walker’s whatToShow to whatToShow.
|
||||
|
@ -51,7 +52,7 @@ JS::NonnullGCPtr<TreeWalker> TreeWalker::create(Node& root, unsigned what_to_sho
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-treewalker-currentnode
|
||||
NonnullRefPtr<Node> TreeWalker::current_node() const
|
||||
JS::NonnullGCPtr<Node> TreeWalker::current_node() const
|
||||
{
|
||||
return *m_current;
|
||||
}
|
||||
|
@ -59,14 +60,14 @@ NonnullRefPtr<Node> TreeWalker::current_node() const
|
|||
// https://dom.spec.whatwg.org/#dom-treewalker-currentnode
|
||||
void TreeWalker::set_current_node(Node& node)
|
||||
{
|
||||
m_current = node;
|
||||
m_current = &node;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-treewalker-parentnode
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::parent_node()
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> TreeWalker::parent_node()
|
||||
{
|
||||
// 1. Let node be this’s current.
|
||||
RefPtr<Node> node = m_current;
|
||||
JS::GCPtr<Node> node = m_current;
|
||||
|
||||
// 2. While node is non-null and is not this’s root:
|
||||
while (node && node != m_root) {
|
||||
|
@ -78,7 +79,7 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::parent_node()
|
|||
if (node) {
|
||||
auto result = TRY(filter(*node));
|
||||
if (result == NodeFilter::FILTER_ACCEPT) {
|
||||
m_current = *node;
|
||||
m_current = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
@ -88,39 +89,39 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::parent_node()
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-treewalker-firstchild
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::first_child()
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> TreeWalker::first_child()
|
||||
{
|
||||
return traverse_children(ChildTraversalType::First);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-treewalker-lastchild
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::last_child()
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> TreeWalker::last_child()
|
||||
{
|
||||
return traverse_children(ChildTraversalType::Last);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-treewalker-previoussibling
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::previous_sibling()
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> TreeWalker::previous_sibling()
|
||||
{
|
||||
return traverse_siblings(SiblingTraversalType::Previous);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-treewalker-nextsibling
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::next_sibling()
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> TreeWalker::next_sibling()
|
||||
{
|
||||
return traverse_siblings(SiblingTraversalType::Next);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-treewalker-previousnode
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::previous_node()
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> TreeWalker::previous_node()
|
||||
{
|
||||
// 1. Let node be this’s current.
|
||||
RefPtr<Node> node = m_current;
|
||||
JS::GCPtr<Node> node = m_current;
|
||||
|
||||
// 2. While node is not this’s root:
|
||||
while (node != m_root) {
|
||||
// 1. Let sibling be node’s previous sibling.
|
||||
RefPtr<Node> sibling = node->previous_sibling();
|
||||
JS::GCPtr<Node> sibling = node->previous_sibling();
|
||||
|
||||
// 2. While sibling is non-null:
|
||||
while (sibling) {
|
||||
|
@ -141,7 +142,7 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::previous_node()
|
|||
|
||||
// 4. If result is FILTER_ACCEPT, then set this’s current to node and return node.
|
||||
if (result == NodeFilter::FILTER_ACCEPT) {
|
||||
m_current = *node;
|
||||
m_current = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -158,7 +159,7 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::previous_node()
|
|||
|
||||
// 5. If the return value of filtering node within this is FILTER_ACCEPT, then set this’s current to node and return node.
|
||||
if (TRY(filter(*node)) == NodeFilter::FILTER_ACCEPT) {
|
||||
m_current = *node;
|
||||
m_current = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
@ -167,10 +168,10 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::previous_node()
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-treewalker-nextnode
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::next_node()
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> TreeWalker::next_node()
|
||||
{
|
||||
// 1. Let node be this’s current.
|
||||
RefPtr<Node> node = m_current;
|
||||
JS::GCPtr<Node> node = m_current;
|
||||
|
||||
// 2. Let result be FILTER_ACCEPT.
|
||||
auto result = NodeFilter::FILTER_ACCEPT;
|
||||
|
@ -187,16 +188,16 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::next_node()
|
|||
|
||||
// 3. If result is FILTER_ACCEPT, then set this’s current to node and return node.
|
||||
if (result == NodeFilter::FILTER_ACCEPT) {
|
||||
m_current = *node;
|
||||
m_current = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Let sibling be null.
|
||||
RefPtr<Node> sibling = nullptr;
|
||||
JS::GCPtr<Node> sibling = nullptr;
|
||||
|
||||
// 3. Let temporary be node.
|
||||
RefPtr<Node> temporary = node;
|
||||
JS::GCPtr<Node> temporary = node;
|
||||
|
||||
// 4. While temporary is non-null:
|
||||
while (temporary) {
|
||||
|
@ -222,7 +223,7 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::next_node()
|
|||
|
||||
// 6. If result is FILTER_ACCEPT, then set this’s current to node and return node.
|
||||
if (result == NodeFilter::FILTER_ACCEPT) {
|
||||
m_current = *node;
|
||||
m_current = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
@ -266,10 +267,10 @@ JS::ThrowCompletionOr<NodeFilter::Result> TreeWalker::filter(Node& node)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traverse-children
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::traverse_children(ChildTraversalType type)
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> TreeWalker::traverse_children(ChildTraversalType type)
|
||||
{
|
||||
// 1. Let node be walker’s current.
|
||||
RefPtr<Node> node = m_current;
|
||||
JS::GCPtr<Node> node = m_current;
|
||||
|
||||
// 2. Set node to node’s first child if type is first, and node’s last child if type is last.
|
||||
node = type == ChildTraversalType::First ? node->first_child() : node->last_child();
|
||||
|
@ -281,18 +282,18 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::traverse_children(ChildTraversal
|
|||
|
||||
// 2. If result is FILTER_ACCEPT, then set walker’s current to node and return node.
|
||||
if (result == NodeFilter::FILTER_ACCEPT) {
|
||||
m_current = *node;
|
||||
m_current = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
// 3. If result is FILTER_SKIP, then:
|
||||
if (result == NodeFilter::FILTER_SKIP) {
|
||||
// 1. Let child be node’s first child if type is first, and node’s last child if type is last.
|
||||
RefPtr<Node> child = type == ChildTraversalType::First ? node->first_child() : node->last_child();
|
||||
JS::GCPtr<Node> child = type == ChildTraversalType::First ? node->first_child() : node->last_child();
|
||||
|
||||
// 2. If child is non-null, then set node to child and continue.
|
||||
if (child) {
|
||||
node = child.release_nonnull();
|
||||
node = child;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -300,23 +301,23 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::traverse_children(ChildTraversal
|
|||
// 4. While node is non-null:
|
||||
while (node) {
|
||||
// 1. Let sibling be node’s next sibling if type is first, and node’s previous sibling if type is last.
|
||||
RefPtr<Node> sibling = type == ChildTraversalType::First ? node->next_sibling() : node->previous_sibling();
|
||||
JS::GCPtr<Node> sibling = type == ChildTraversalType::First ? node->next_sibling() : node->previous_sibling();
|
||||
|
||||
// 2. If sibling is non-null, then set node to sibling and break.
|
||||
if (sibling) {
|
||||
node = sibling.release_nonnull();
|
||||
node = sibling;
|
||||
break;
|
||||
}
|
||||
|
||||
// 3. Let parent be node’s parent.
|
||||
RefPtr<Node> parent = node->parent();
|
||||
JS::GCPtr<Node> parent = node->parent();
|
||||
|
||||
// 4. If parent is null, walker’s root, or walker’s current, then return null.
|
||||
if (!parent || parent == m_root || parent == m_current)
|
||||
return nullptr;
|
||||
|
||||
// 5. Set node to parent.
|
||||
node = parent.release_nonnull();
|
||||
node = parent;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,10 +326,10 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::traverse_children(ChildTraversal
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traverse-siblings
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::traverse_siblings(SiblingTraversalType type)
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> TreeWalker::traverse_siblings(SiblingTraversalType type)
|
||||
{
|
||||
// 1. Let node be walker’s current.
|
||||
RefPtr<Node> node = m_current;
|
||||
JS::GCPtr<Node> node = m_current;
|
||||
|
||||
// 2. If node is root, then return null.
|
||||
if (node == m_root)
|
||||
|
@ -337,7 +338,7 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::traverse_siblings(SiblingTravers
|
|||
// 3. While true:
|
||||
while (true) {
|
||||
// 1. Let sibling be node’s next sibling if type is next, and node’s previous sibling if type is previous.
|
||||
RefPtr<Node> sibling = type == SiblingTraversalType::Next ? node->next_sibling() : node->previous_sibling();
|
||||
JS::GCPtr<Node> sibling = type == SiblingTraversalType::Next ? node->next_sibling() : node->previous_sibling();
|
||||
|
||||
// 2. While sibling is non-null:
|
||||
while (sibling) {
|
||||
|
@ -349,7 +350,7 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::traverse_siblings(SiblingTravers
|
|||
|
||||
// 3. If result is FILTER_ACCEPT, then set walker’s current to node and return node.
|
||||
if (result == NodeFilter::FILTER_ACCEPT) {
|
||||
m_current = *node;
|
||||
m_current = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,61 +12,60 @@ namespace Web::DOM {
|
|||
|
||||
// https://dom.spec.whatwg.org/#treewalker
|
||||
class TreeWalker final : public Bindings::PlatformObject {
|
||||
JS_OBJECT(TreeWalker, JS::Object);
|
||||
WEB_PLATFORM_OBJECT(TreeWalker, JS::Object);
|
||||
|
||||
public:
|
||||
static JS::NonnullGCPtr<TreeWalker> create(Node& root, unsigned what_to_show, JS::GCPtr<NodeFilter>);
|
||||
|
||||
explicit TreeWalker(Node& root);
|
||||
virtual ~TreeWalker() override;
|
||||
|
||||
TreeWalker& impl() { return *this; }
|
||||
|
||||
NonnullRefPtr<Node> current_node() const;
|
||||
JS::NonnullGCPtr<Node> current_node() const;
|
||||
void set_current_node(Node&);
|
||||
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> parent_node();
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> first_child();
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> last_child();
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> previous_sibling();
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> next_sibling();
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> previous_node();
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> next_node();
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> parent_node();
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> first_child();
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> last_child();
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> previous_sibling();
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> next_sibling();
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> previous_node();
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> next_node();
|
||||
|
||||
NonnullRefPtr<Node> root() { return m_root; }
|
||||
JS::NonnullGCPtr<Node> root() { return m_root; }
|
||||
|
||||
NodeFilter* filter() { return m_filter.ptr(); }
|
||||
|
||||
unsigned what_to_show() const { return m_what_to_show; }
|
||||
|
||||
private:
|
||||
explicit TreeWalker(Node& root);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
enum class ChildTraversalType {
|
||||
First,
|
||||
Last,
|
||||
};
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> traverse_children(ChildTraversalType);
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> traverse_children(ChildTraversalType);
|
||||
|
||||
enum class SiblingTraversalType {
|
||||
Next,
|
||||
Previous,
|
||||
};
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> traverse_siblings(SiblingTraversalType);
|
||||
JS::ThrowCompletionOr<JS::GCPtr<Node>> traverse_siblings(SiblingTraversalType);
|
||||
|
||||
JS::ThrowCompletionOr<NodeFilter::Result> filter(Node&);
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-root
|
||||
NonnullRefPtr<DOM::Node> m_root;
|
||||
JS::NonnullGCPtr<Node> m_root;
|
||||
|
||||
// https://dom.spec.whatwg.org/#treewalker-current
|
||||
NonnullRefPtr<DOM::Node> m_current;
|
||||
JS::NonnullGCPtr<Node> m_current;
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-whattoshow
|
||||
unsigned m_what_to_show { 0 };
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-filter
|
||||
JS::GCPtr<DOM::NodeFilter> m_filter;
|
||||
JS::GCPtr<NodeFilter> m_filter;
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-active
|
||||
bool m_active { false };
|
||||
|
@ -74,7 +73,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace Web::Bindings {
|
||||
inline JS::Object* wrap(JS::Realm&, Web::DOM::TreeWalker& object) { return &object; }
|
||||
using TreeWalkerWrapper = Web::DOM::TreeWalker;
|
||||
}
|
||||
WRAPPER_HACK(TreeWalker, Web::DOM)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue