1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 21:37:35 +00:00

LibWeb: Make the layout tree GC-allocated

This removes a set of complex reference cycles between DOM, layout tree
and browsing context.

It also makes lifetimes much easier to reason about, as the DOM and
layout trees are now free to keep each other alive.
This commit is contained in:
Andreas Kling 2022-10-17 14:41:50 +02:00
parent 83c5ff57d8
commit 268b9c5d90
72 changed files with 258 additions and 207 deletions

View file

@ -13,6 +13,8 @@ namespace Web::Layout {
// https://www.w3.org/TR/css-display/#block-container
class BlockContainer : public Box {
JS_CELL(BlockContainer, Box);
public:
BlockContainer(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
BlockContainer(DOM::Document&, DOM::Node*, CSS::ComputedValues);

View file

@ -18,6 +18,8 @@ struct LineBoxFragmentCoordinate {
};
class Box : public NodeWithStyleAndBoxModelMetrics {
JS_CELL(Box, NodeWithStyleAndBoxModelMetrics);
public:
Painting::PaintableBox const* paint_box() const;

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class BreakNode final : public NodeWithStyleAndBoxModelMetrics {
JS_CELL(BreakNode, NodeWithStyleAndBoxModelMetrics);
public:
BreakNode(DOM::Document&, HTML::HTMLBRElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~BreakNode() override;

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class ButtonBox : public FormAssociatedLabelableNode {
JS_CELL(ButtonBox, FormAssociatedLabelableNode);
public:
ButtonBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~ButtonBox() override;

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class CanvasBox : public ReplacedBox {
JS_CELL(CanvasBox, ReplacedBox);
public:
CanvasBox(DOM::Document&, HTML::HTMLCanvasElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~CanvasBox() override;

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class CheckBox : public FormAssociatedLabelableNode {
JS_CELL(CheckBox, FormAssociatedLabelableNode);
public:
CheckBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~CheckBox() override;

View file

@ -14,6 +14,8 @@
namespace Web::Layout {
class FormAssociatedLabelableNode : public LabelableNode {
JS_CELL(FormAssociatedLabelableNode, LabelableNode);
public:
const HTML::FormAssociatedElement& dom_node() const { return dynamic_cast<const HTML::FormAssociatedElement&>(LabelableNode::dom_node()); }
HTML::FormAssociatedElement& dom_node() { return dynamic_cast<HTML::FormAssociatedElement&>(LabelableNode::dom_node()); }

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class FrameBox final : public ReplacedBox {
JS_CELL(FrameBox, ReplacedBox);
public:
FrameBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~FrameBox() override;

View file

@ -15,6 +15,8 @@ namespace Web::Layout {
class ImageBox
: public ReplacedBox
, public HTML::BrowsingContext::ViewportClient {
JS_CELL(ImageBox, ReplacedBox);
public:
ImageBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>, ImageLoader const&);
virtual ~ImageBox() override;

View file

@ -13,6 +13,8 @@
namespace Web::Layout {
class InitialContainingBlock final : public BlockContainer {
JS_CELL(InitialContainingBlock, BlockContainer);
public:
explicit InitialContainingBlock(DOM::Document&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~InitialContainingBlock() override;

View file

@ -11,6 +11,8 @@
namespace Web::Layout {
class InlineNode : public NodeWithStyleAndBoxModelMetrics {
JS_CELL(InlineNode, NodeWithStyleAndBoxModelMetrics);
public:
InlineNode(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
virtual ~InlineNode() override;

View file

@ -38,9 +38,6 @@ void Label::handle_mouseup_on_label(Badge<Painting::TextPaintable>, Gfx::IntPoin
if (!m_tracking_mouse || button != GUI::MouseButton::Primary)
return;
// NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node.
NonnullRefPtr protect = *this;
if (auto* control = labeled_control(); control) {
bool is_inside_control = enclosing_int_rect(control->paint_box()->absolute_rect()).contains(position);
bool is_inside_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position);

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class Label final : public BlockContainer {
JS_CELL(Label, BlockContainer);
public:
Label(DOM::Document&, HTML::HTMLLabelElement*, NonnullRefPtr<CSS::StyleProperties>);
virtual ~Label() override;

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class LabelableNode : public ReplacedBox {
JS_CELL(LabelableNode, ReplacedBox);
public:
Painting::LabelablePaintable* paintable();
Painting::LabelablePaintable const* paintable() const;

View file

@ -23,7 +23,7 @@ LayoutRange LayoutRange::normalized() const
{
if (!is_valid())
return {};
if (m_start.layout_node == m_end.layout_node) {
if (m_start.layout_node.ptr() == m_end.layout_node.ptr()) {
if (m_start.index_in_node < m_end.index_in_node)
return *this;
return { m_end, m_start };

View file

@ -16,7 +16,7 @@ namespace Web::Layout {
class Node;
struct LayoutPosition {
RefPtr<Node> layout_node;
JS::Handle<Layout::Node> layout_node;
int index_in_node { 0 };
DOM::Position to_dom_position() const;

View file

@ -17,7 +17,13 @@ ListItemBox::ListItemBox(DOM::Document& document, DOM::Element* element, Nonnull
ListItemBox::~ListItemBox() = default;
void ListItemBox::set_marker(RefPtr<ListItemMarkerBox> marker)
void ListItemBox::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_marker);
}
void ListItemBox::set_marker(JS::GCPtr<ListItemMarkerBox> marker)
{
m_marker = move(marker);
}

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class ListItemBox final : public BlockContainer {
JS_CELL(ListItemBox, BlockContainer);
public:
ListItemBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
virtual ~ListItemBox() override;
@ -20,10 +22,12 @@ public:
DOM::Element const& dom_node() const { return static_cast<DOM::Element const&>(*BlockContainer::dom_node()); }
ListItemMarkerBox const* marker() const { return m_marker; }
void set_marker(RefPtr<ListItemMarkerBox>);
void set_marker(JS::GCPtr<ListItemMarkerBox>);
private:
RefPtr<ListItemMarkerBox> m_marker;
virtual void visit_edges(Cell::Visitor&) override;
JS::GCPtr<ListItemMarkerBox> m_marker;
};
}

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class ListItemMarkerBox final : public Box {
JS_CELL(ListItemMarkerBox, Box);
public:
explicit ListItemMarkerBox(DOM::Document&, CSS::ListStyleType, size_t index, NonnullRefPtr<CSS::StyleProperties>);
virtual ~ListItemMarkerBox() override;

View file

@ -15,24 +15,26 @@
#include <LibWeb/Layout/Node.h>
#include <LibWeb/Layout/TextNode.h>
#include <LibWeb/Platform/FontPlugin.h>
#include <typeinfo>
namespace Web::Layout {
Node::Node(DOM::Document& document, DOM::Node* node)
: m_dom_node(node ? node : &document)
: m_dom_node(node ? *node : document)
, m_anonymous(node == nullptr)
{
m_serial_id = document.next_layout_node_serial_id({});
if (node)
node->set_layout_node({}, this);
node->set_layout_node({}, *this);
}
Node::~Node()
Node::~Node() = default;
void Node::visit_edges(Cell::Visitor& visitor)
{
if (!is_anonymous() && m_dom_node->layout_node() == this)
m_dom_node->set_layout_node({}, nullptr);
Base::visit_edges(visitor);
visitor.visit(m_dom_node);
TreeNode::visit_edges(visitor);
}
// https://www.w3.org/TR/css-display-3/#out-of-flow
@ -558,16 +560,10 @@ bool Node::is_root_element() const
return is<HTML::HTMLHtmlElement>(*dom_node());
}
String Node::class_name() const
{
auto const* mangled_name = typeid(*this).name();
return demangle({ mangled_name, strlen(mangled_name) });
}
String Node::debug_description() const
{
StringBuilder builder;
builder.append(class_name().substring_view(13));
builder.append(class_name());
if (dom_node()) {
builder.appendff("<{}>", dom_node()->node_name());
if (dom_node()->is_element()) {
@ -604,13 +600,13 @@ bool Node::is_inline_block() const
return display.is_inline_outside() && display.is_flow_root_inside();
}
NonnullRefPtr<NodeWithStyle> NodeWithStyle::create_anonymous_wrapper() const
JS::NonnullGCPtr<NodeWithStyle> NodeWithStyle::create_anonymous_wrapper() const
{
auto wrapper = adopt_ref(*new BlockContainer(const_cast<DOM::Document&>(document()), nullptr, m_computed_values.clone_inherited_values()));
auto wrapper = heap().allocate_without_realm<BlockContainer>(const_cast<DOM::Document&>(document()), nullptr, m_computed_values.clone_inherited_values());
static_cast<CSS::MutableComputedValues&>(wrapper->m_computed_values).set_display(CSS::Display(CSS::Display::Outside::Block, CSS::Display::Inside::Flow));
wrapper->m_font = m_font;
wrapper->m_line_height = m_line_height;
return wrapper;
return *wrapper;
}
void Node::set_paintable(RefPtr<Painting::Paintable> paintable)

View file

@ -31,7 +31,12 @@ enum class LayoutMode {
IntrinsicSizing,
};
class Node : public TreeNode<Node> {
class Node
: public JS::Cell
, public TreeNode<Node>
, public Weakable<Node> {
JS_CELL(Node, JS::Cell);
public:
virtual ~Node();
@ -61,7 +66,6 @@ public:
bool is_root_element() const;
String class_name() const;
String debug_description() const;
bool has_style() const { return m_has_style; }
@ -140,10 +144,12 @@ public:
protected:
Node(DOM::Document&, DOM::Node*);
virtual void visit_edges(Cell::Visitor&) override;
private:
friend class NodeWithStyle;
JS::Handle<DOM::Node> m_dom_node;
JS::NonnullGCPtr<DOM::Node> m_dom_node;
RefPtr<Painting::Paintable> m_paintable;
size_t m_serial_id { 0 };
@ -159,6 +165,8 @@ private:
};
class NodeWithStyle : public Node {
JS_CELL(NodeWithStyle, Node);
public:
virtual ~NodeWithStyle() override = default;
@ -171,7 +179,7 @@ public:
Vector<CSS::BackgroundLayerData> const& background_layers() const { return computed_values().background_layers(); }
const CSS::AbstractImageStyleValue* list_style_image() const { return m_list_style_image; }
NonnullRefPtr<NodeWithStyle> create_anonymous_wrapper() const;
JS::NonnullGCPtr<NodeWithStyle> create_anonymous_wrapper() const;
protected:
NodeWithStyle(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
@ -185,6 +193,8 @@ private:
};
class NodeWithStyleAndBoxModelMetrics : public NodeWithStyle {
JS_CELL(NodeWithStyleAndBoxModelMetrics, NodeWithStyle);
public:
BoxModelMetrics& box_model() { return m_box_model; }
BoxModelMetrics const& box_model() const { return m_box_model; }

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class Progress : public LabelableNode {
JS_CELL(Progress, LabelableNode);
public:
Progress(DOM::Document&, HTML::HTMLProgressElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~Progress() override;

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class RadioButton final : public FormAssociatedLabelableNode {
JS_CELL(RadioButton, FormAssociatedLabelableNode);
public:
RadioButton(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~RadioButton() override;

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class ReplacedBox : public Box {
JS_CELL(ReplacedBox, Box);
public:
ReplacedBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~ReplacedBox() override;

View file

@ -13,6 +13,8 @@
namespace Web::Layout {
class SVGBox : public Box {
JS_CELL(SVGBox, Box);
public:
SVGBox(DOM::Document&, SVG::SVGElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~SVGBox() override = default;

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class SVGGeometryBox final : public SVGGraphicsBox {
JS_CELL(SVGGeometryBox, SVGGraphicsBox);
public:
SVGGeometryBox(DOM::Document&, SVG::SVGGeometryElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~SVGGeometryBox() override = default;

View file

@ -13,6 +13,8 @@
namespace Web::Layout {
class SVGGraphicsBox : public SVGBox {
JS_CELL(SVGGraphicsBox, SVGBox);
public:
SVGGraphicsBox(DOM::Document&, SVG::SVGGraphicsElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~SVGGraphicsBox() override = default;

View file

@ -12,6 +12,8 @@
namespace Web::Layout {
class SVGSVGBox final : public ReplacedBox {
JS_CELL(SVGSVGBox, ReplacedBox);
public:
SVGSVGBox(DOM::Document&, SVG::SVGSVGElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~SVGSVGBox() override = default;

View file

@ -11,6 +11,8 @@
namespace Web::Layout {
class TableBox final : public Layout::BlockContainer {
JS_CELL(TableBox, BlockContainer);
public:
TableBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
TableBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);

View file

@ -11,6 +11,8 @@
namespace Web::Layout {
class TableCellBox final : public BlockContainer {
JS_CELL(TableCellBox, BlockContainer);
public:
TableCellBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
TableCellBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);

View file

@ -11,6 +11,8 @@
namespace Web::Layout {
class TableRowBox final : public Box {
JS_CELL(TableRowBox, Box);
public:
TableRowBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
TableRowBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);

View file

@ -11,6 +11,8 @@
namespace Web::Layout {
class TableRowGroupBox final : public BlockContainer {
JS_CELL(TableRowGroupBox, BlockContainer);
public:
TableRowGroupBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
virtual ~TableRowGroupBox() override;

View file

@ -15,6 +15,8 @@ namespace Web::Layout {
class LineBoxFragment;
class TextNode : public Node {
JS_CELL(TextNode, Node);
public:
TextNode(DOM::Document&, DOM::Text&);
virtual ~TextNode() override;

View file

@ -31,7 +31,7 @@ TreeBuilder::TreeBuilder() = default;
static bool has_inline_or_in_flow_block_children(Layout::Node const& layout_node)
{
for (auto const* child = layout_node.first_child(); child; child = child->next_sibling()) {
for (auto child = layout_node.first_child(); child; child = child->next_sibling()) {
if (child->is_inline())
return true;
if (!child->is_floating() && !child->is_absolutely_positioned())
@ -44,7 +44,7 @@ static bool has_in_flow_block_children(Layout::Node const& layout_node)
{
if (layout_node.children_are_inline())
return false;
for (auto const* child = layout_node.first_child(); child; child = child->next_sibling()) {
for (auto child = layout_node.first_child(); child; child = child->next_sibling()) {
if (child->is_inline())
continue;
if (!child->is_floating() && !child->is_absolutely_positioned())
@ -95,15 +95,15 @@ static Layout::Node& insertion_parent_for_block_node(Layout::NodeWithStyle& layo
// Parent block has inline-level children (our siblings).
// First move these siblings into an anonymous wrapper block.
NonnullRefPtrVector<Layout::Node> children;
while (RefPtr<Layout::Node> child = layout_parent.first_child()) {
Vector<JS::Handle<Layout::Node>> children;
while (JS::GCPtr<Layout::Node> child = layout_parent.first_child()) {
layout_parent.remove_child(*child);
children.append(child.release_nonnull());
children.append(*child);
}
layout_parent.append_child(layout_parent.create_anonymous_wrapper());
layout_parent.set_children_are_inline(false);
for (auto& child : children) {
layout_parent.last_child()->append_child(child);
layout_parent.last_child()->append_child(*child);
}
layout_parent.last_child()->set_children_are_inline(true);
// Then it's safe to insert this block into parent.
@ -166,10 +166,10 @@ void TreeBuilder::create_pseudo_element_if_needed(DOM::Element& element, CSS::Se
// FIXME: Handle images, and multiple values
if (pseudo_element_content.type == CSS::ContentData::Type::String) {
auto* text = document.heap().allocate<DOM::Text>(document.realm(), document, pseudo_element_content.data);
auto text_node = adopt_ref(*new TextNode(document, *text));
auto text_node = document.heap().allocate_without_realm<Layout::TextNode>(document, *text);
text_node->set_generated(true);
push_parent(verify_cast<NodeWithStyle>(*pseudo_element_node));
insert_node_into_inline_or_block_ancestor(text_node, text_node->display(), AppendOrPrepend::Append);
insert_node_into_inline_or_block_ancestor(*text_node, text_node->display(), AppendOrPrepend::Append);
pop_parent();
} else {
TODO();
@ -195,7 +195,7 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
auto& document = dom_node.document();
auto& style_computer = document.style_computer();
RefPtr<Layout::Node> layout_node;
JS::GCPtr<Layout::Node> layout_node;
RefPtr<CSS::StyleProperties> style;
CSS::Display display;
@ -211,12 +211,12 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
} else if (is<DOM::Document>(dom_node)) {
style = style_computer.create_document_style();
display = style->display();
layout_node = adopt_ref(*new Layout::InitialContainingBlock(static_cast<DOM::Document&>(dom_node), *style));
layout_node = document.heap().allocate_without_realm<Layout::InitialContainingBlock>(static_cast<DOM::Document&>(dom_node), *style);
} else if (is<DOM::Text>(dom_node)) {
layout_node = adopt_ref(*new Layout::TextNode(document, static_cast<DOM::Text&>(dom_node)));
layout_node = document.heap().allocate_without_realm<Layout::TextNode>(document, static_cast<DOM::Text&>(dom_node));
display = CSS::Display(CSS::Display::Outside::Inline, CSS::Display::Inside::Flow);
} else if (is<DOM::ShadowRoot>(dom_node)) {
layout_node = adopt_ref(*new Layout::BlockContainer(document, &static_cast<DOM::ShadowRoot&>(dom_node), CSS::ComputedValues {}));
layout_node = document.heap().allocate_without_realm<Layout::BlockContainer>(document, &static_cast<DOM::ShadowRoot&>(dom_node), CSS::ComputedValues {});
display = CSS::Display(CSS::Display::Outside::Block, CSS::Display::Inside::FlowRoot);
}
@ -256,10 +256,10 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
auto& element = static_cast<DOM::Element&>(dom_node);
int child_index = layout_node->parent()->index_of_child<ListItemBox>(*layout_node).value();
auto marker_style = style_computer.compute_style(element, CSS::Selector::PseudoElement::Marker);
auto list_item_marker = adopt_ref(*new ListItemMarkerBox(document, layout_node->computed_values().list_style_type(), child_index + 1, *marker_style));
auto list_item_marker = document.heap().allocate_without_realm<ListItemMarkerBox>(document, layout_node->computed_values().list_style_type(), child_index + 1, *marker_style);
static_cast<ListItemBox&>(*layout_node).set_marker(list_item_marker);
element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::Marker, list_item_marker);
layout_node->append_child(move(list_item_marker));
layout_node->append_child(*list_item_marker);
}
if (is<HTML::HTMLProgressElement>(dom_node)) {
@ -287,7 +287,7 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
}
}
RefPtr<Node> TreeBuilder::build(DOM::Node& dom_node)
JS::GCPtr<Layout::Node> TreeBuilder::build(DOM::Node& dom_node)
{
VERIFY(dom_node.is_document());
@ -333,7 +333,7 @@ void TreeBuilder::remove_irrelevant_boxes(NodeWithStyle& root)
{
// The following boxes are discarded as if they were display:none:
NonnullRefPtrVector<Node> to_remove;
Vector<JS::Handle<Node>> to_remove;
// Children of a table-column.
for_each_in_tree_with_internal_display<CSS::Display::Internal::TableColumn>(root, [&](Box& table_column) {
@ -358,7 +358,7 @@ void TreeBuilder::remove_irrelevant_boxes(NodeWithStyle& root)
// - whose immediate sibling, if any, is a table-non-root box
for (auto& box : to_remove)
box.parent()->remove_child(box);
box->parent()->remove_child(*box);
}
static bool is_table_track(CSS::Display display)
@ -424,18 +424,18 @@ static bool is_ignorable_whitespace(Layout::Node const& node)
template<typename Matcher, typename Callback>
static void for_each_sequence_of_consecutive_children_matching(NodeWithStyle& parent, Matcher matcher, Callback callback)
{
NonnullRefPtrVector<Node> sequence;
Vector<JS::Handle<Node>> sequence;
auto sequence_is_all_ignorable_whitespace = [&]() -> bool {
for (auto& node : sequence) {
if (!is_ignorable_whitespace(node))
if (!is_ignorable_whitespace(*node))
return false;
}
return true;
};
Node* next_sibling = nullptr;
for (auto* child = parent.first_child(); child; child = next_sibling) {
for (auto child = parent.first_child(); child; child = next_sibling) {
next_sibling = child->next_sibling();
if (matcher(*child)) {
sequence.append(*child);
@ -452,21 +452,21 @@ static void for_each_sequence_of_consecutive_children_matching(NodeWithStyle& pa
}
template<typename WrapperBoxType>
static void wrap_in_anonymous(NonnullRefPtrVector<Node>& sequence, Node* nearest_sibling)
static void wrap_in_anonymous(Vector<JS::Handle<Node>>& sequence, Node* nearest_sibling)
{
VERIFY(!sequence.is_empty());
auto& parent = *sequence.first().parent();
auto& parent = *sequence.first()->parent();
auto computed_values = parent.computed_values().clone_inherited_values();
static_cast<CSS::MutableComputedValues&>(computed_values).set_display(WrapperBoxType::static_display());
auto wrapper = adopt_ref(*new WrapperBoxType(parent.document(), nullptr, move(computed_values)));
auto wrapper = parent.heap().template allocate_without_realm<WrapperBoxType>(parent.document(), nullptr, move(computed_values));
for (auto& child : sequence) {
parent.remove_child(child);
wrapper->append_child(child);
parent.remove_child(*child);
wrapper->append_child(*child);
}
if (nearest_sibling)
parent.insert_before(move(wrapper), *nearest_sibling);
parent.insert_before(*wrapper, *nearest_sibling);
else
parent.append_child(move(wrapper));
parent.append_child(*wrapper);
}
void TreeBuilder::generate_missing_child_wrappers(NodeWithStyle& root)

View file

@ -17,7 +17,7 @@ class TreeBuilder {
public:
TreeBuilder();
RefPtr<Layout::Node> build(DOM::Node&);
JS::GCPtr<Layout::Node> build(DOM::Node&);
private:
struct Context {
@ -47,7 +47,7 @@ private:
void insert_node_into_inline_or_block_ancestor(Layout::Node&, CSS::Display, AppendOrPrepend);
void create_pseudo_element_if_needed(DOM::Element&, CSS::Selector::PseudoElement, AppendOrPrepend);
RefPtr<Layout::Node> m_layout_root;
JS::GCPtr<Layout::Node> m_layout_root;
Vector<Layout::NodeWithStyle&> m_ancestor_stack;
};