1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 18:18:12 +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

@ -683,7 +683,7 @@ void Document::tear_down_layout_tree()
// Gather up all the layout nodes in a vector and detach them from parents
// while the vector keeps them alive.
NonnullRefPtrVector<Layout::Node> layout_nodes;
Vector<JS::Handle<Layout::Node>> layout_nodes;
m_layout_root->for_each_in_inclusive_subtree([&](auto& layout_node) {
layout_nodes.append(layout_node);
@ -691,8 +691,8 @@ void Document::tear_down_layout_tree()
});
for (auto& layout_node : layout_nodes) {
if (layout_node.parent())
layout_node.parent()->remove_child(layout_node);
if (layout_node->parent())
layout_node->parent()->remove_child(*layout_node);
}
m_layout_root = nullptr;
@ -816,7 +816,7 @@ void Document::update_layout()
if (!m_layout_root) {
m_next_layout_node_serial_id = 0;
Layout::TreeBuilder tree_builder;
m_layout_root = static_ptr_cast<Layout::InitialContainingBlock>(tree_builder.build(*this));
m_layout_root = verify_cast<Layout::InitialContainingBlock>(*tree_builder.build(*this));
}
Layout::LayoutState layout_state;

View file

@ -469,7 +469,7 @@ private:
JS::GCPtr<HTML::Window> m_window;
RefPtr<Layout::InitialContainingBlock> m_layout_root;
JS::GCPtr<Layout::InitialContainingBlock> m_layout_root;
Optional<Color> m_link_color;
Optional<Color> m_active_link_color;

View file

@ -75,6 +75,8 @@ void Element::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_inline_style.ptr());
visitor.visit(m_class_list.ptr());
visitor.visit(m_shadow_root.ptr());
for (auto& pseudo_element_layout_node : m_pseudo_element_nodes)
visitor.visit(pseudo_element_layout_node);
}
// https://dom.spec.whatwg.org/#dom-element-getattribute
@ -272,7 +274,7 @@ bool Element::has_class(FlyString const& class_name, CaseSensitivity case_sensit
}
}
RefPtr<Layout::Node> Element::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> Element::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
if (local_name() == "noscript" && document().is_scripting_enabled())
return nullptr;
@ -281,41 +283,41 @@ RefPtr<Layout::Node> Element::create_layout_node(NonnullRefPtr<CSS::StylePropert
return create_layout_node_for_display_type(document(), display, move(style), this);
}
RefPtr<Layout::Node> Element::create_layout_node_for_display_type(DOM::Document& document, CSS::Display const& display, NonnullRefPtr<CSS::StyleProperties> style, Element* element)
JS::GCPtr<Layout::Node> Element::create_layout_node_for_display_type(DOM::Document& document, CSS::Display const& display, NonnullRefPtr<CSS::StyleProperties> style, Element* element)
{
if (display.is_table_inside())
return adopt_ref(*new Layout::TableBox(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::TableBox>(document, element, move(style));
if (display.is_list_item())
return adopt_ref(*new Layout::ListItemBox(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::ListItemBox>(document, element, move(style));
if (display.is_table_row())
return adopt_ref(*new Layout::TableRowBox(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::TableRowBox>(document, element, move(style));
if (display.is_table_cell())
return adopt_ref(*new Layout::TableCellBox(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::TableCellBox>(document, element, move(style));
if (display.is_table_row_group() || display.is_table_header_group() || display.is_table_footer_group())
return adopt_ref(*new Layout::TableRowGroupBox(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::TableRowGroupBox>(document, element, move(style));
if (display.is_table_column() || display.is_table_column_group() || display.is_table_caption()) {
// FIXME: This is just an incorrect placeholder until we improve table layout support.
return adopt_ref(*new Layout::BlockContainer(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::BlockContainer>(document, element, move(style));
}
if (display.is_inline_outside()) {
if (display.is_flow_root_inside())
return adopt_ref(*new Layout::BlockContainer(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::BlockContainer>(document, element, move(style));
if (display.is_flow_inside())
return adopt_ref(*new Layout::InlineNode(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::InlineNode>(document, element, move(style));
if (display.is_flex_inside())
return adopt_ref(*new Layout::BlockContainer(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::BlockContainer>(document, element, move(style));
dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Support display: {}", display.to_string());
return adopt_ref(*new Layout::InlineNode(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::InlineNode>(document, element, move(style));
}
if (display.is_flow_inside() || display.is_flow_root_inside() || display.is_flex_inside() || display.is_grid_inside())
return adopt_ref(*new Layout::BlockContainer(document, element, move(style)));
return document.heap().allocate_without_realm<Layout::BlockContainer>(document, element, move(style));
TODO();
}
@ -709,14 +711,14 @@ void Element::children_changed()
set_needs_style_update(true);
}
void Element::set_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS::Selector::PseudoElement pseudo_element, RefPtr<Layout::Node> pseudo_element_node)
void Element::set_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS::Selector::PseudoElement pseudo_element, JS::GCPtr<Layout::Node> pseudo_element_node)
{
m_pseudo_element_nodes[to_underlying(pseudo_element)] = pseudo_element_node->make_weak_ptr();
m_pseudo_element_nodes[to_underlying(pseudo_element)] = pseudo_element_node;
}
RefPtr<Layout::Node> Element::get_pseudo_element_node(CSS::Selector::PseudoElement pseudo_element) const
JS::GCPtr<Layout::Node> Element::get_pseudo_element_node(CSS::Selector::PseudoElement pseudo_element) const
{
return m_pseudo_element_nodes[to_underlying(pseudo_element)].strong_ref();
return m_pseudo_element_nodes[to_underlying(pseudo_element)];
}
void Element::clear_pseudo_element_nodes(Badge<Layout::TreeBuilder>)

View file

@ -142,15 +142,15 @@ public:
JS::NonnullGCPtr<Geometry::DOMRect> get_bounding_client_rect() const;
JS::NonnullGCPtr<Geometry::DOMRectList> get_client_rects() const;
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>);
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>);
virtual void did_receive_focus() { }
virtual void did_lose_focus() { }
static RefPtr<Layout::Node> create_layout_node_for_display_type(DOM::Document&, CSS::Display const&, NonnullRefPtr<CSS::StyleProperties>, Element*);
static JS::GCPtr<Layout::Node> create_layout_node_for_display_type(DOM::Document&, CSS::Display const&, NonnullRefPtr<CSS::StyleProperties>, Element*);
void set_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS::Selector::PseudoElement, RefPtr<Layout::Node>);
RefPtr<Layout::Node> get_pseudo_element_node(CSS::Selector::PseudoElement) const;
void set_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS::Selector::PseudoElement, JS::GCPtr<Layout::Node>);
JS::GCPtr<Layout::Node> get_pseudo_element_node(CSS::Selector::PseudoElement) const;
void clear_pseudo_element_nodes(Badge<Layout::TreeBuilder>);
void serialize_pseudo_elements_as_json(JsonArraySerializer<StringBuilder>& children_array) const;
@ -188,7 +188,7 @@ private:
Vector<FlyString> m_classes;
Array<WeakPtr<Layout::Node>, CSS::Selector::PseudoElementCount> m_pseudo_element_nodes;
Array<JS::GCPtr<Layout::Node>, CSS::Selector::PseudoElementCount> m_pseudo_element_nodes;
};
template<>

View file

@ -92,6 +92,8 @@ void Node::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_previous_sibling.ptr());
visitor.visit(m_child_nodes);
visitor.visit(m_layout_node);
for (auto& registered_observer : m_registered_observer_list)
visitor.visit(registered_observer);
}
@ -823,11 +825,16 @@ bool Node::is_editable() const
return parent() && parent()->is_editable();
}
void Node::set_layout_node(Badge<Layout::Node>, Layout::Node* layout_node) const
void Node::set_layout_node(Badge<Layout::Node>, JS::NonnullGCPtr<Layout::Node> layout_node)
{
m_layout_node = layout_node;
}
void Node::detach_layout_node(Badge<DOM::Document>)
{
m_layout_node = nullptr;
}
EventTarget* Node::get_parent(Event const&)
{
// FIXME: returns the nodes assigned slot, if node is assigned, and nodes parent otherwise.

View file

@ -156,7 +156,8 @@ public:
Painting::PaintableBox const* paint_box() const;
Painting::Paintable const* paintable() const;
void set_layout_node(Badge<Layout::Node>, Layout::Node*) const;
void set_layout_node(Badge<Layout::Node>, JS::NonnullGCPtr<Layout::Node>);
void detach_layout_node(Badge<DOM::Document>);
virtual bool is_child_allowed(Node const&) const { return true; }
@ -623,7 +624,7 @@ protected:
virtual void visit_edges(Cell::Visitor&) override;
JS::GCPtr<Document> m_document;
mutable WeakPtr<Layout::Node> m_layout_node;
JS::GCPtr<Layout::Node> m_layout_node;
NodeType m_type { NodeType::INVALID };
bool m_needs_style_update { false };
bool m_child_needs_style_update { false };

View file

@ -503,7 +503,7 @@ String BrowsingContext::selected_text() const
auto selection = layout_root->selection().normalized();
if (selection.start().layout_node == selection.end().layout_node) {
if (selection.start().layout_node.ptr() == selection.end().layout_node) {
if (!is<Layout::TextNode>(*selection.start().layout_node))
return "";
return verify_cast<Layout::TextNode>(*selection.start().layout_node).text_for_rendering().substring(selection.start().index_in_node, selection.end().index_in_node - selection.start().index_in_node);
@ -518,7 +518,7 @@ String BrowsingContext::selected_text() const
// Middle nodes
layout_node = layout_node->next_in_pre_order();
while (layout_node && layout_node != selection.end().layout_node) {
while (layout_node && layout_node.ptr() != selection.end().layout_node) {
if (is<Layout::TextNode>(*layout_node))
builder.append(verify_cast<Layout::TextNode>(*layout_node).text_for_rendering());
else if (is<Layout::BreakNode>(*layout_node) || is<Layout::BlockContainer>(*layout_node))
@ -528,7 +528,7 @@ String BrowsingContext::selected_text() const
}
// End node
VERIFY(layout_node == selection.end().layout_node);
VERIFY(layout_node.ptr() == selection.end().layout_node);
if (is<Layout::TextNode>(*layout_node)) {
auto& text = verify_cast<Layout::TextNode>(*layout_node).text_for_rendering();
builder.append(text.substring(0, selection.end().index_in_node));
@ -573,7 +573,13 @@ void BrowsingContext::select_all()
last_layout_node_index_in_node = text_for_rendering.length() - 1;
}
layout_root->set_selection({ { first_layout_node, 0 }, { last_layout_node, last_layout_node_index_in_node } });
auto start = Layout::LayoutPosition {
JS::make_handle(const_cast<Layout::Node*>(first_layout_node)), 0
};
auto end = Layout::LayoutPosition {
JS::make_handle(const_cast<Layout::Node*>(last_layout_node)), last_layout_node_index_in_node
};
layout_root->set_selection({ move(start), move(end) });
}
void BrowsingContext::register_viewport_client(ViewportClient& client)

View file

@ -18,9 +18,9 @@ HTMLBRElement::HTMLBRElement(DOM::Document& document, DOM::QualifiedName qualifi
HTMLBRElement::~HTMLBRElement() = default;
RefPtr<Layout::Node> HTMLBRElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> HTMLBRElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return adopt_ref(*new Layout::BreakNode(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::BreakNode>(document(), *this, move(style));
}
}

View file

@ -16,7 +16,7 @@ class HTMLBRElement final : public HTMLElement {
public:
virtual ~HTMLBRElement() override;
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
private:
HTMLBRElement(DOM::Document&, DOM::QualifiedName);

View file

@ -78,9 +78,9 @@ void HTMLCanvasElement::set_height(unsigned value)
reset_context_to_default_state();
}
RefPtr<Layout::Node> HTMLCanvasElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> HTMLCanvasElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return adopt_ref(*new Layout::CanvasBox(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::CanvasBox>(document(), *this, move(style));
}
HTMLCanvasElement::HasOrCreatedContext HTMLCanvasElement::create_2d_context()

View file

@ -42,7 +42,7 @@ private:
virtual void visit_edges(Cell::Visitor&) override;
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
enum class HasOrCreatedContext {
No,

View file

@ -22,9 +22,9 @@ HTMLIFrameElement::HTMLIFrameElement(DOM::Document& document, DOM::QualifiedName
HTMLIFrameElement::~HTMLIFrameElement() = default;
RefPtr<Layout::Node> HTMLIFrameElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> HTMLIFrameElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return adopt_ref(*new Layout::FrameBox(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::FrameBox>(document(), *this, move(style));
}
void HTMLIFrameElement::parse_attribute(FlyString const& name, String const& value)

View file

@ -16,7 +16,7 @@ class HTMLIFrameElement final : public BrowsingContextContainer {
public:
virtual ~HTMLIFrameElement() override;
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#will-lazy-load-element-steps
bool will_lazy_load_element() const;

View file

@ -85,9 +85,9 @@ void HTMLImageElement::parse_attribute(FlyString const& name, String const& valu
}
}
RefPtr<Layout::Node> HTMLImageElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> HTMLImageElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return adopt_ref(*new Layout::ImageBox(document(), *this, move(style), m_image_loader));
return heap().allocate_without_realm<Layout::ImageBox>(document(), *this, move(style), m_image_loader);
}
Gfx::Bitmap const* HTMLImageElement::bitmap() const

View file

@ -50,7 +50,7 @@ private:
void animate();
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
ImageLoader m_image_loader;
};

View file

@ -51,21 +51,21 @@ void HTMLInputElement::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_selected_files);
}
RefPtr<Layout::Node> HTMLInputElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> HTMLInputElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
if (type_state() == TypeAttributeState::Hidden)
return nullptr;
if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton || type_state() == TypeAttributeState::FileUpload)
return adopt_ref(*new Layout::ButtonBox(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::ButtonBox>(document(), *this, move(style));
if (type_state() == TypeAttributeState::Checkbox)
return adopt_ref(*new Layout::CheckBox(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::CheckBox>(document(), *this, move(style));
if (type_state() == TypeAttributeState::RadioButton)
return adopt_ref(*new Layout::RadioButton(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::RadioButton>(document(), *this, move(style));
return adopt_ref(*new Layout::BlockContainer(document(), this, move(style)));
return heap().allocate_without_realm<Layout::BlockContainer>(document(), this, move(style));
}
void HTMLInputElement::set_checked(bool checked, ChangeSource change_source)

View file

@ -48,7 +48,7 @@ class HTMLInputElement final
public:
virtual ~HTMLInputElement() override;
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
enum class TypeAttributeState {
#define __ENUMERATE_HTML_INPUT_TYPE_ATTRIBUTE(_, state) state,

View file

@ -18,9 +18,9 @@ HTMLLabelElement::HTMLLabelElement(DOM::Document& document, DOM::QualifiedName q
HTMLLabelElement::~HTMLLabelElement() = default;
RefPtr<Layout::Node> HTMLLabelElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> HTMLLabelElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return adopt_ref(*new Layout::Label(document(), this, move(style)));
return heap().allocate_without_realm<Layout::Label>(document(), this, move(style));
}
}

View file

@ -16,7 +16,7 @@ class HTMLLabelElement final : public HTMLElement {
public:
virtual ~HTMLLabelElement() override;
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
String for_() const { return attribute(HTML::AttributeNames::for_); }

View file

@ -39,7 +39,7 @@ String HTMLObjectElement::data() const
return document().parse_url(data).to_string();
}
RefPtr<Layout::Node> HTMLObjectElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> HTMLObjectElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
switch (m_representation) {
case Representation::Children:
@ -49,7 +49,7 @@ RefPtr<Layout::Node> HTMLObjectElement::create_layout_node(NonnullRefPtr<CSS::St
return nullptr;
case Representation::Image:
if (m_image_loader.has_value() && m_image_loader->has_image())
return adopt_ref(*new Layout::ImageBox(document(), *this, move(style), *m_image_loader));
return heap().allocate_without_realm<Layout::ImageBox>(document(), *this, move(style), *m_image_loader);
break;
default:
break;

View file

@ -46,7 +46,7 @@ public:
private:
HTMLObjectElement(DOM::Document&, DOM::QualifiedName);
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
bool has_ancestor_media_element_or_object_element_not_showing_fallback_content() const;

View file

@ -23,11 +23,11 @@ HTMLProgressElement::HTMLProgressElement(DOM::Document& document, DOM::Qualified
HTMLProgressElement::~HTMLProgressElement() = default;
RefPtr<Layout::Node> HTMLProgressElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> HTMLProgressElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
if (style->appearance().value_or(CSS::Appearance::Auto) == CSS::Appearance::None)
return HTMLElement::create_layout_node(style);
return adopt_ref(*new Layout::Progress(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::Progress>(document(), *this, move(style));
}
bool HTMLProgressElement::using_system_appearance() const

View file

@ -16,7 +16,7 @@ class HTMLProgressElement final : public HTMLElement {
public:
virtual ~HTMLProgressElement() override;
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
double value() const;
void set_value(double);

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;
};

View file

@ -637,7 +637,7 @@ bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_poin
if (!document->layout_node())
return false;
NonnullRefPtr<Layout::InitialContainingBlock> layout_root = *document->layout_node();
JS::NonnullGCPtr<Layout::InitialContainingBlock> layout_root = *document->layout_node();
if (key == KeyCode::Key_Tab) {
if (modifiers & KeyModifier::Mod_Shift)

View file

@ -71,6 +71,12 @@ Gfx::FloatPoint PaintableBox::effective_offset() const
{
Gfx::FloatPoint offset;
if (m_containing_line_box_fragment.has_value()) {
// FIXME: This is a hack to deal with situations where the layout tree has been garbage collected.
// We could avoid this by making the paintable tree garbage collected as well.
if (!containing_block() || !containing_block()->paint_box())
return offset;
auto const& fragment = containing_block()->paint_box()->line_boxes()[m_containing_line_box_fragment->line_box_index].fragments()[m_containing_line_box_fragment->fragment_index];
offset = fragment.offset();
} else {

View file

@ -19,7 +19,7 @@ SVGClipPathElement::~SVGClipPathElement()
{
}
RefPtr<Layout::Node> SVGClipPathElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties>)
JS::GCPtr<Layout::Node> SVGClipPathElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties>)
{
return nullptr;
}

View file

@ -16,7 +16,7 @@ class SVGClipPathElement final : public SVGElement {
public:
virtual ~SVGClipPathElement();
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
private:
SVGClipPathElement(DOM::Document&, DOM::QualifiedName);

View file

@ -19,7 +19,7 @@ SVGDefsElement::~SVGDefsElement()
{
}
RefPtr<Layout::Node> SVGDefsElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties>)
JS::GCPtr<Layout::Node> SVGDefsElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties>)
{
return nullptr;
}

View file

@ -16,7 +16,7 @@ class SVGDefsElement final : public SVGGraphicsElement {
public:
virtual ~SVGDefsElement();
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
private:
SVGDefsElement(DOM::Document&, DOM::QualifiedName);

View file

@ -16,9 +16,9 @@ SVGGElement::SVGGElement(DOM::Document& document, DOM::QualifiedName qualified_n
{
}
RefPtr<Layout::Node> SVGGElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> SVGGElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return adopt_ref(*new Layout::SVGGraphicsBox(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::SVGGraphicsBox>(document(), *this, move(style));
}
}

View file

@ -16,7 +16,7 @@ class SVGGElement final : public SVGGraphicsElement {
public:
virtual ~SVGGElement() override = default;
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
private:
SVGGElement(DOM::Document&, DOM::QualifiedName);

View file

@ -16,9 +16,9 @@ SVGGeometryElement::SVGGeometryElement(DOM::Document& document, DOM::QualifiedNa
set_prototype(&Bindings::cached_web_prototype(realm(), "SVGGeometryElement"));
}
RefPtr<Layout::Node> SVGGeometryElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> SVGGeometryElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return adopt_ref(*new Layout::SVGGeometryBox(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::SVGGeometryBox>(document(), *this, move(style));
}
float SVGGeometryElement::get_total_length()

View file

@ -16,7 +16,7 @@ class SVGGeometryElement : public SVGGraphicsElement {
WEB_PLATFORM_OBJECT(SVGGeometryElement, SVGGraphicsElement);
public:
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual Gfx::Path& get_path() = 0;

View file

@ -23,9 +23,9 @@ SVGSVGElement::SVGSVGElement(DOM::Document& document, DOM::QualifiedName qualifi
set_prototype(&Bindings::cached_web_prototype(realm(), "SVGSVGElement"));
}
RefPtr<Layout::Node> SVGSVGElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
JS::GCPtr<Layout::Node> SVGSVGElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return adopt_ref(*new Layout::SVGSVGBox(document(), *this, move(style)));
return heap().allocate_without_realm<Layout::SVGSVGBox>(document(), *this, move(style));
}
void SVGSVGElement::apply_presentational_hints(CSS::StyleProperties& style) const

View file

@ -16,7 +16,7 @@ class SVGSVGElement final : public SVGGraphicsElement {
WEB_PLATFORM_OBJECT(SVGSVGElement, SVGGraphicsElement);
public:
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual void apply_presentational_hints(CSS::StyleProperties&) const override;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -7,54 +7,27 @@
#pragma once
#include <AK/Assertions.h>
#include <AK/NonnullRefPtr.h>
#include <AK/TypeCasts.h>
#include <AK/Weakable.h>
#include <LibJS/Heap/GCPtr.h>
#include <LibWeb/Forward.h>
namespace Web {
template<typename T>
class TreeNode : public Weakable<T> {
class TreeNode {
public:
void ref()
{
VERIFY(!m_in_removed_last_ref);
if constexpr (!IsBaseOf<DOM::Node, T>) {
// NOTE: DOM::Document is allowed to survive with 0 ref count, if one of its descendant nodes are alive.
VERIFY(m_ref_count);
}
++m_ref_count;
}
void unref()
{
VERIFY(!m_in_removed_last_ref);
VERIFY(m_ref_count);
if (!--m_ref_count) {
if constexpr (IsBaseOf<DOM::Node, T>) {
m_in_removed_last_ref = true;
static_cast<T*>(this)->removed_last_ref();
} else {
delete static_cast<T*>(this);
}
return;
}
}
int ref_count() const { return m_ref_count; }
T* parent() { return m_parent; }
const T* parent() const { return m_parent; }
T const* parent() const { return m_parent; }
bool has_children() const { return m_first_child; }
T* next_sibling() { return m_next_sibling; }
T* previous_sibling() { return m_previous_sibling; }
T* first_child() { return m_first_child; }
T* last_child() { return m_last_child; }
const T* next_sibling() const { return m_next_sibling; }
const T* previous_sibling() const { return m_previous_sibling; }
const T* first_child() const { return m_first_child; }
const T* last_child() const { return m_last_child; }
T const* next_sibling() const { return m_next_sibling; }
T const* previous_sibling() const { return m_previous_sibling; }
T const* first_child() const { return m_first_child; }
T const* last_child() const { return m_last_child; }
size_t child_count() const
{
@ -75,7 +48,7 @@ public:
return nullptr;
}
const T* child_at_index(int index) const
T const* child_at_index(int index) const
{
return const_cast<TreeNode*>(this)->child_at_index(index);
}
@ -90,7 +63,7 @@ public:
return index;
}
Optional<size_t> index_of_child(const T& search_child)
Optional<size_t> index_of_child(T const& search_child)
{
VERIFY(search_child.parent() == this);
size_t index = 0;
@ -106,7 +79,7 @@ public:
}
template<typename ChildType>
Optional<size_t> index_of_child(const T& search_child)
Optional<size_t> index_of_child(T const& search_child)
{
VERIFY(search_child.parent() == this);
size_t index = 0;
@ -130,12 +103,12 @@ public:
bool is_following(TreeNode const&) const;
void append_child(NonnullRefPtr<T> node);
void prepend_child(NonnullRefPtr<T> node);
void insert_before(NonnullRefPtr<T> node, RefPtr<T> child);
void remove_child(NonnullRefPtr<T> node);
void append_child(JS::NonnullGCPtr<T> node);
void prepend_child(JS::NonnullGCPtr<T> node);
void insert_before(JS::NonnullGCPtr<T> node, JS::GCPtr<T> child);
void remove_child(JS::NonnullGCPtr<T> node);
bool is_child_allowed(const T&) const { return true; }
bool is_child_allowed(T const&) const { return true; }
T* next_in_pre_order()
{
@ -230,7 +203,7 @@ public:
template<typename Callback>
IterationDecision for_each_in_inclusive_subtree(Callback callback) const
{
if (callback(static_cast<const T&>(*this)) == IterationDecision::Break)
if (callback(static_cast<T 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)
@ -254,7 +227,7 @@ public:
template<typename U, typename Callback>
IterationDecision for_each_in_inclusive_subtree_of_type(Callback callback)
{
if (is<U>(static_cast<const T&>(*this))) {
if (is<U>(static_cast<T const&>(*this))) {
if (callback(static_cast<U&>(*this)) == IterationDecision::Break)
return IterationDecision::Break;
}
@ -268,8 +241,8 @@ public:
template<typename U, typename Callback>
IterationDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
{
if (is<U>(static_cast<const T&>(*this))) {
if (callback(static_cast<const U&>(*this)) == IterationDecision::Break)
if (is<U>(static_cast<T const&>(*this))) {
if (callback(static_cast<U const&>(*this)) == IterationDecision::Break)
return IterationDecision::Break;
}
for (auto* child = first_child(); child; child = child->next_sibling()) {
@ -348,7 +321,7 @@ public:
}
template<typename U>
const U* next_sibling_of_type() const
U const* next_sibling_of_type() const
{
return const_cast<TreeNode*>(this)->template next_sibling_of_type<U>();
}
@ -364,7 +337,7 @@ public:
}
template<typename U>
const U* previous_sibling_of_type() const
U const* previous_sibling_of_type() const
{
return const_cast<TreeNode*>(this)->template previous_sibling_of_type<U>();
}
@ -380,13 +353,13 @@ public:
}
template<typename U>
const U* first_child_of_type() const
U const* first_child_of_type() const
{
return const_cast<TreeNode*>(this)->template first_child_of_type<U>();
}
template<typename U>
const U* last_child_of_type() const
U const* last_child_of_type() const
{
return const_cast<TreeNode*>(this)->template last_child_of_type<U>();
}
@ -418,7 +391,7 @@ public:
}
template<typename U>
const U* first_ancestor_of_type() const
U const* first_ancestor_of_type() const
{
return const_cast<TreeNode*>(this)->template first_ancestor_of_type<U>();
}
@ -442,25 +415,21 @@ public:
return false;
}
~TreeNode()
{
VERIFY(!m_parent);
T* next_child = nullptr;
for (auto* child = m_first_child; child; child = next_child) {
next_child = child->m_next_sibling;
child->m_parent = nullptr;
child->unref();
}
}
~TreeNode() = default;
protected:
TreeNode() = default;
bool m_deletion_has_begun { false };
bool m_in_removed_last_ref { false };
void visit_edges(JS::Cell::Visitor& visitor)
{
visitor.visit(m_parent);
visitor.visit(m_first_child);
visitor.visit(m_last_child);
visitor.visit(m_next_sibling);
visitor.visit(m_previous_sibling);
}
private:
int m_ref_count { 1 };
T* m_parent { nullptr };
T* m_first_child { nullptr };
T* m_last_child { nullptr };
@ -469,7 +438,7 @@ private:
};
template<typename T>
inline void TreeNode<T>::remove_child(NonnullRefPtr<T> node)
inline void TreeNode<T>::remove_child(JS::NonnullGCPtr<T> node)
{
VERIFY(node->m_parent == this);
@ -488,12 +457,10 @@ inline void TreeNode<T>::remove_child(NonnullRefPtr<T> node)
node->m_next_sibling = nullptr;
node->m_previous_sibling = nullptr;
node->m_parent = nullptr;
node->unref();
}
template<typename T>
inline void TreeNode<T>::append_child(NonnullRefPtr<T> node)
inline void TreeNode<T>::append_child(JS::NonnullGCPtr<T> node)
{
VERIFY(!node->m_parent);
@ -507,11 +474,10 @@ inline void TreeNode<T>::append_child(NonnullRefPtr<T> node)
m_last_child = node.ptr();
if (!m_first_child)
m_first_child = m_last_child;
[[maybe_unused]] auto& rc = node.leak_ref();
}
template<typename T>
inline void TreeNode<T>::insert_before(NonnullRefPtr<T> node, RefPtr<T> child)
inline void TreeNode<T>::insert_before(JS::NonnullGCPtr<T> node, JS::GCPtr<T> child)
{
if (!child)
return append_child(move(node));
@ -531,11 +497,10 @@ inline void TreeNode<T>::insert_before(NonnullRefPtr<T> node, RefPtr<T> child)
child->m_previous_sibling = node;
node->m_parent = static_cast<T*>(this);
[[maybe_unused]] auto& rc = node.leak_ref();
}
template<typename T>
inline void TreeNode<T>::prepend_child(NonnullRefPtr<T> node)
inline void TreeNode<T>::prepend_child(JS::NonnullGCPtr<T> node)
{
VERIFY(!node->m_parent);
@ -550,7 +515,6 @@ inline void TreeNode<T>::prepend_child(NonnullRefPtr<T> node)
if (!m_last_child)
m_last_child = m_first_child;
node->inserted_into(static_cast<T&>(*this));
[[maybe_unused]] auto& rc = node.leak_ref();
static_cast<T*>(this)->children_changed();
}

View file

@ -354,7 +354,7 @@ Messages::WebContentServer::InspectDomNodeResponse ConnectionFromClient::inspect
if (pseudo_element.has_value()) {
auto pseudo_element_node = element.get_pseudo_element_node(pseudo_element.value());
if (pseudo_element_node.is_null())
if (!pseudo_element_node)
return { false, "", "", "", "" };
// FIXME: Pseudo-elements only exist as Layout::Nodes, which don't have style information