1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:37: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:
Andreas Kling 2022-08-28 13:42:07 +02:00
parent bb547ce1c4
commit 6f433c8656
445 changed files with 4797 additions and 4268 deletions

View file

@ -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 childs 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 ranges root is not equal to nodes root, or if bp is after the ranges end, set ranges 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 ranges 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 ranges root is not equal to nodes root, or if bp is before the ranges start, set ranges 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 ranges 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 ranges start to boundary point (parent, index).
m_start_container = *parent;
m_start_container = parent;
m_start_offset = index;
// 5. Set ranges 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 containers 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 ranges start nodes 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 ranges 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 nodes 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 ranges 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 ranges 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 ranges start node if referenceNode is null, and referenceNodes 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 ranges start nodes 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 ranges 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 thiss 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 nodes 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();