mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:07: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:
parent
83c5ff57d8
commit
268b9c5d90
72 changed files with 258 additions and 207 deletions
|
@ -683,7 +683,7 @@ void Document::tear_down_layout_tree()
|
||||||
// Gather up all the layout nodes in a vector and detach them from parents
|
// Gather up all the layout nodes in a vector and detach them from parents
|
||||||
// while the vector keeps them alive.
|
// 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) {
|
m_layout_root->for_each_in_inclusive_subtree([&](auto& layout_node) {
|
||||||
layout_nodes.append(layout_node);
|
layout_nodes.append(layout_node);
|
||||||
|
@ -691,8 +691,8 @@ void Document::tear_down_layout_tree()
|
||||||
});
|
});
|
||||||
|
|
||||||
for (auto& layout_node : layout_nodes) {
|
for (auto& layout_node : layout_nodes) {
|
||||||
if (layout_node.parent())
|
if (layout_node->parent())
|
||||||
layout_node.parent()->remove_child(layout_node);
|
layout_node->parent()->remove_child(*layout_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_layout_root = nullptr;
|
m_layout_root = nullptr;
|
||||||
|
@ -816,7 +816,7 @@ void Document::update_layout()
|
||||||
if (!m_layout_root) {
|
if (!m_layout_root) {
|
||||||
m_next_layout_node_serial_id = 0;
|
m_next_layout_node_serial_id = 0;
|
||||||
Layout::TreeBuilder tree_builder;
|
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;
|
Layout::LayoutState layout_state;
|
||||||
|
|
|
@ -469,7 +469,7 @@ private:
|
||||||
|
|
||||||
JS::GCPtr<HTML::Window> m_window;
|
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_link_color;
|
||||||
Optional<Color> m_active_link_color;
|
Optional<Color> m_active_link_color;
|
||||||
|
|
|
@ -75,6 +75,8 @@ void Element::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_inline_style.ptr());
|
visitor.visit(m_inline_style.ptr());
|
||||||
visitor.visit(m_class_list.ptr());
|
visitor.visit(m_class_list.ptr());
|
||||||
visitor.visit(m_shadow_root.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
|
// 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())
|
if (local_name() == "noscript" && document().is_scripting_enabled())
|
||||||
return nullptr;
|
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);
|
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())
|
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())
|
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())
|
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())
|
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())
|
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()) {
|
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.
|
// 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_inline_outside()) {
|
||||||
if (display.is_flow_root_inside())
|
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())
|
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())
|
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());
|
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())
|
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();
|
TODO();
|
||||||
}
|
}
|
||||||
|
@ -709,14 +711,14 @@ void Element::children_changed()
|
||||||
set_needs_style_update(true);
|
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>)
|
void Element::clear_pseudo_element_nodes(Badge<Layout::TreeBuilder>)
|
||||||
|
|
|
@ -142,15 +142,15 @@ public:
|
||||||
JS::NonnullGCPtr<Geometry::DOMRect> get_bounding_client_rect() const;
|
JS::NonnullGCPtr<Geometry::DOMRect> get_bounding_client_rect() const;
|
||||||
JS::NonnullGCPtr<Geometry::DOMRectList> get_client_rects() 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_receive_focus() { }
|
||||||
virtual void did_lose_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>);
|
void set_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS::Selector::PseudoElement, JS::GCPtr<Layout::Node>);
|
||||||
RefPtr<Layout::Node> get_pseudo_element_node(CSS::Selector::PseudoElement) const;
|
JS::GCPtr<Layout::Node> get_pseudo_element_node(CSS::Selector::PseudoElement) const;
|
||||||
void clear_pseudo_element_nodes(Badge<Layout::TreeBuilder>);
|
void clear_pseudo_element_nodes(Badge<Layout::TreeBuilder>);
|
||||||
void serialize_pseudo_elements_as_json(JsonArraySerializer<StringBuilder>& children_array) const;
|
void serialize_pseudo_elements_as_json(JsonArraySerializer<StringBuilder>& children_array) const;
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ private:
|
||||||
|
|
||||||
Vector<FlyString> m_classes;
|
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<>
|
template<>
|
||||||
|
|
|
@ -92,6 +92,8 @@ void Node::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_previous_sibling.ptr());
|
visitor.visit(m_previous_sibling.ptr());
|
||||||
visitor.visit(m_child_nodes);
|
visitor.visit(m_child_nodes);
|
||||||
|
|
||||||
|
visitor.visit(m_layout_node);
|
||||||
|
|
||||||
for (auto& registered_observer : m_registered_observer_list)
|
for (auto& registered_observer : m_registered_observer_list)
|
||||||
visitor.visit(registered_observer);
|
visitor.visit(registered_observer);
|
||||||
}
|
}
|
||||||
|
@ -823,11 +825,16 @@ bool Node::is_editable() const
|
||||||
return parent() && parent()->is_editable();
|
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;
|
m_layout_node = layout_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Node::detach_layout_node(Badge<DOM::Document>)
|
||||||
|
{
|
||||||
|
m_layout_node = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
EventTarget* Node::get_parent(Event const&)
|
EventTarget* Node::get_parent(Event const&)
|
||||||
{
|
{
|
||||||
// FIXME: returns the node’s assigned slot, if node is assigned, and node’s parent otherwise.
|
// FIXME: returns the node’s assigned slot, if node is assigned, and node’s parent otherwise.
|
||||||
|
|
|
@ -156,7 +156,8 @@ public:
|
||||||
Painting::PaintableBox const* paint_box() const;
|
Painting::PaintableBox const* paint_box() const;
|
||||||
Painting::Paintable const* paintable() 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; }
|
virtual bool is_child_allowed(Node const&) const { return true; }
|
||||||
|
|
||||||
|
@ -623,7 +624,7 @@ protected:
|
||||||
virtual void visit_edges(Cell::Visitor&) override;
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
JS::GCPtr<Document> m_document;
|
JS::GCPtr<Document> m_document;
|
||||||
mutable WeakPtr<Layout::Node> m_layout_node;
|
JS::GCPtr<Layout::Node> m_layout_node;
|
||||||
NodeType m_type { NodeType::INVALID };
|
NodeType m_type { NodeType::INVALID };
|
||||||
bool m_needs_style_update { false };
|
bool m_needs_style_update { false };
|
||||||
bool m_child_needs_style_update { false };
|
bool m_child_needs_style_update { false };
|
||||||
|
|
|
@ -503,7 +503,7 @@ String BrowsingContext::selected_text() const
|
||||||
|
|
||||||
auto selection = layout_root->selection().normalized();
|
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))
|
if (!is<Layout::TextNode>(*selection.start().layout_node))
|
||||||
return "";
|
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);
|
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
|
// Middle nodes
|
||||||
layout_node = layout_node->next_in_pre_order();
|
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))
|
if (is<Layout::TextNode>(*layout_node))
|
||||||
builder.append(verify_cast<Layout::TextNode>(*layout_node).text_for_rendering());
|
builder.append(verify_cast<Layout::TextNode>(*layout_node).text_for_rendering());
|
||||||
else if (is<Layout::BreakNode>(*layout_node) || is<Layout::BlockContainer>(*layout_node))
|
else if (is<Layout::BreakNode>(*layout_node) || is<Layout::BlockContainer>(*layout_node))
|
||||||
|
@ -528,7 +528,7 @@ String BrowsingContext::selected_text() const
|
||||||
}
|
}
|
||||||
|
|
||||||
// End node
|
// End node
|
||||||
VERIFY(layout_node == selection.end().layout_node);
|
VERIFY(layout_node.ptr() == selection.end().layout_node);
|
||||||
if (is<Layout::TextNode>(*layout_node)) {
|
if (is<Layout::TextNode>(*layout_node)) {
|
||||||
auto& text = verify_cast<Layout::TextNode>(*layout_node).text_for_rendering();
|
auto& text = verify_cast<Layout::TextNode>(*layout_node).text_for_rendering();
|
||||||
builder.append(text.substring(0, selection.end().index_in_node));
|
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;
|
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)
|
void BrowsingContext::register_viewport_client(ViewportClient& client)
|
||||||
|
|
|
@ -18,9 +18,9 @@ HTMLBRElement::HTMLBRElement(DOM::Document& document, DOM::QualifiedName qualifi
|
||||||
|
|
||||||
HTMLBRElement::~HTMLBRElement() = default;
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class HTMLBRElement final : public HTMLElement {
|
||||||
public:
|
public:
|
||||||
virtual ~HTMLBRElement() override;
|
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:
|
private:
|
||||||
HTMLBRElement(DOM::Document&, DOM::QualifiedName);
|
HTMLBRElement(DOM::Document&, DOM::QualifiedName);
|
||||||
|
|
|
@ -78,9 +78,9 @@ void HTMLCanvasElement::set_height(unsigned value)
|
||||||
reset_context_to_default_state();
|
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()
|
HTMLCanvasElement::HasOrCreatedContext HTMLCanvasElement::create_2d_context()
|
||||||
|
|
|
@ -42,7 +42,7 @@ private:
|
||||||
|
|
||||||
virtual void visit_edges(Cell::Visitor&) override;
|
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 {
|
enum class HasOrCreatedContext {
|
||||||
No,
|
No,
|
||||||
|
|
|
@ -22,9 +22,9 @@ HTMLIFrameElement::HTMLIFrameElement(DOM::Document& document, DOM::QualifiedName
|
||||||
|
|
||||||
HTMLIFrameElement::~HTMLIFrameElement() = default;
|
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)
|
void HTMLIFrameElement::parse_attribute(FlyString const& name, String const& value)
|
||||||
|
|
|
@ -16,7 +16,7 @@ class HTMLIFrameElement final : public BrowsingContextContainer {
|
||||||
public:
|
public:
|
||||||
virtual ~HTMLIFrameElement() override;
|
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
|
// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#will-lazy-load-element-steps
|
||||||
bool will_lazy_load_element() const;
|
bool will_lazy_load_element() const;
|
||||||
|
|
|
@ -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
|
Gfx::Bitmap const* HTMLImageElement::bitmap() const
|
||||||
|
|
|
@ -50,7 +50,7 @@ private:
|
||||||
|
|
||||||
void animate();
|
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;
|
ImageLoader m_image_loader;
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,21 +51,21 @@ void HTMLInputElement::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_selected_files);
|
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)
|
if (type_state() == TypeAttributeState::Hidden)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton || type_state() == TypeAttributeState::FileUpload)
|
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)
|
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)
|
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)
|
void HTMLInputElement::set_checked(bool checked, ChangeSource change_source)
|
||||||
|
|
|
@ -48,7 +48,7 @@ class HTMLInputElement final
|
||||||
public:
|
public:
|
||||||
virtual ~HTMLInputElement() override;
|
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 {
|
enum class TypeAttributeState {
|
||||||
#define __ENUMERATE_HTML_INPUT_TYPE_ATTRIBUTE(_, state) state,
|
#define __ENUMERATE_HTML_INPUT_TYPE_ATTRIBUTE(_, state) state,
|
||||||
|
|
|
@ -18,9 +18,9 @@ HTMLLabelElement::HTMLLabelElement(DOM::Document& document, DOM::QualifiedName q
|
||||||
|
|
||||||
HTMLLabelElement::~HTMLLabelElement() = default;
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class HTMLLabelElement final : public HTMLElement {
|
||||||
public:
|
public:
|
||||||
virtual ~HTMLLabelElement() override;
|
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_); }
|
String for_() const { return attribute(HTML::AttributeNames::for_); }
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ String HTMLObjectElement::data() const
|
||||||
return document().parse_url(data).to_string();
|
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) {
|
switch (m_representation) {
|
||||||
case Representation::Children:
|
case Representation::Children:
|
||||||
|
@ -49,7 +49,7 @@ RefPtr<Layout::Node> HTMLObjectElement::create_layout_node(NonnullRefPtr<CSS::St
|
||||||
return nullptr;
|
return nullptr;
|
||||||
case Representation::Image:
|
case Representation::Image:
|
||||||
if (m_image_loader.has_value() && m_image_loader->has_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;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -46,7 +46,7 @@ public:
|
||||||
private:
|
private:
|
||||||
HTMLObjectElement(DOM::Document&, DOM::QualifiedName);
|
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;
|
bool has_ancestor_media_element_or_object_element_not_showing_fallback_content() const;
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,11 @@ HTMLProgressElement::HTMLProgressElement(DOM::Document& document, DOM::Qualified
|
||||||
|
|
||||||
HTMLProgressElement::~HTMLProgressElement() = default;
|
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)
|
if (style->appearance().value_or(CSS::Appearance::Auto) == CSS::Appearance::None)
|
||||||
return HTMLElement::create_layout_node(style);
|
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
|
bool HTMLProgressElement::using_system_appearance() const
|
||||||
|
|
|
@ -16,7 +16,7 @@ class HTMLProgressElement final : public HTMLElement {
|
||||||
public:
|
public:
|
||||||
virtual ~HTMLProgressElement() override;
|
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;
|
double value() const;
|
||||||
void set_value(double);
|
void set_value(double);
|
||||||
|
|
|
@ -13,6 +13,8 @@ namespace Web::Layout {
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-display/#block-container
|
// https://www.w3.org/TR/css-display/#block-container
|
||||||
class BlockContainer : public Box {
|
class BlockContainer : public Box {
|
||||||
|
JS_CELL(BlockContainer, Box);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BlockContainer(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
|
BlockContainer(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
BlockContainer(DOM::Document&, DOM::Node*, CSS::ComputedValues);
|
BlockContainer(DOM::Document&, DOM::Node*, CSS::ComputedValues);
|
||||||
|
|
|
@ -18,6 +18,8 @@ struct LineBoxFragmentCoordinate {
|
||||||
};
|
};
|
||||||
|
|
||||||
class Box : public NodeWithStyleAndBoxModelMetrics {
|
class Box : public NodeWithStyleAndBoxModelMetrics {
|
||||||
|
JS_CELL(Box, NodeWithStyleAndBoxModelMetrics);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Painting::PaintableBox const* paint_box() const;
|
Painting::PaintableBox const* paint_box() const;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class BreakNode final : public NodeWithStyleAndBoxModelMetrics {
|
class BreakNode final : public NodeWithStyleAndBoxModelMetrics {
|
||||||
|
JS_CELL(BreakNode, NodeWithStyleAndBoxModelMetrics);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BreakNode(DOM::Document&, HTML::HTMLBRElement&, NonnullRefPtr<CSS::StyleProperties>);
|
BreakNode(DOM::Document&, HTML::HTMLBRElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~BreakNode() override;
|
virtual ~BreakNode() override;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class ButtonBox : public FormAssociatedLabelableNode {
|
class ButtonBox : public FormAssociatedLabelableNode {
|
||||||
|
JS_CELL(ButtonBox, FormAssociatedLabelableNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ButtonBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
|
ButtonBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~ButtonBox() override;
|
virtual ~ButtonBox() override;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class CanvasBox : public ReplacedBox {
|
class CanvasBox : public ReplacedBox {
|
||||||
|
JS_CELL(CanvasBox, ReplacedBox);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CanvasBox(DOM::Document&, HTML::HTMLCanvasElement&, NonnullRefPtr<CSS::StyleProperties>);
|
CanvasBox(DOM::Document&, HTML::HTMLCanvasElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~CanvasBox() override;
|
virtual ~CanvasBox() override;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class CheckBox : public FormAssociatedLabelableNode {
|
class CheckBox : public FormAssociatedLabelableNode {
|
||||||
|
JS_CELL(CheckBox, FormAssociatedLabelableNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CheckBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
|
CheckBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~CheckBox() override;
|
virtual ~CheckBox() override;
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class FormAssociatedLabelableNode : public LabelableNode {
|
class FormAssociatedLabelableNode : public LabelableNode {
|
||||||
|
JS_CELL(FormAssociatedLabelableNode, LabelableNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const HTML::FormAssociatedElement& dom_node() const { return dynamic_cast<const HTML::FormAssociatedElement&>(LabelableNode::dom_node()); }
|
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()); }
|
HTML::FormAssociatedElement& dom_node() { return dynamic_cast<HTML::FormAssociatedElement&>(LabelableNode::dom_node()); }
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class FrameBox final : public ReplacedBox {
|
class FrameBox final : public ReplacedBox {
|
||||||
|
JS_CELL(FrameBox, ReplacedBox);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FrameBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
|
FrameBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~FrameBox() override;
|
virtual ~FrameBox() override;
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace Web::Layout {
|
||||||
class ImageBox
|
class ImageBox
|
||||||
: public ReplacedBox
|
: public ReplacedBox
|
||||||
, public HTML::BrowsingContext::ViewportClient {
|
, public HTML::BrowsingContext::ViewportClient {
|
||||||
|
JS_CELL(ImageBox, ReplacedBox);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ImageBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>, ImageLoader const&);
|
ImageBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>, ImageLoader const&);
|
||||||
virtual ~ImageBox() override;
|
virtual ~ImageBox() override;
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class InitialContainingBlock final : public BlockContainer {
|
class InitialContainingBlock final : public BlockContainer {
|
||||||
|
JS_CELL(InitialContainingBlock, BlockContainer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit InitialContainingBlock(DOM::Document&, NonnullRefPtr<CSS::StyleProperties>);
|
explicit InitialContainingBlock(DOM::Document&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~InitialContainingBlock() override;
|
virtual ~InitialContainingBlock() override;
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class InlineNode : public NodeWithStyleAndBoxModelMetrics {
|
class InlineNode : public NodeWithStyleAndBoxModelMetrics {
|
||||||
|
JS_CELL(InlineNode, NodeWithStyleAndBoxModelMetrics);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InlineNode(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
InlineNode(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~InlineNode() override;
|
virtual ~InlineNode() override;
|
||||||
|
|
|
@ -38,9 +38,6 @@ void Label::handle_mouseup_on_label(Badge<Painting::TextPaintable>, Gfx::IntPoin
|
||||||
if (!m_tracking_mouse || button != GUI::MouseButton::Primary)
|
if (!m_tracking_mouse || button != GUI::MouseButton::Primary)
|
||||||
return;
|
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) {
|
if (auto* control = labeled_control(); control) {
|
||||||
bool is_inside_control = enclosing_int_rect(control->paint_box()->absolute_rect()).contains(position);
|
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);
|
bool is_inside_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position);
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class Label final : public BlockContainer {
|
class Label final : public BlockContainer {
|
||||||
|
JS_CELL(Label, BlockContainer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Label(DOM::Document&, HTML::HTMLLabelElement*, NonnullRefPtr<CSS::StyleProperties>);
|
Label(DOM::Document&, HTML::HTMLLabelElement*, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~Label() override;
|
virtual ~Label() override;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class LabelableNode : public ReplacedBox {
|
class LabelableNode : public ReplacedBox {
|
||||||
|
JS_CELL(LabelableNode, ReplacedBox);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Painting::LabelablePaintable* paintable();
|
Painting::LabelablePaintable* paintable();
|
||||||
Painting::LabelablePaintable const* paintable() const;
|
Painting::LabelablePaintable const* paintable() const;
|
||||||
|
|
|
@ -23,7 +23,7 @@ LayoutRange LayoutRange::normalized() const
|
||||||
{
|
{
|
||||||
if (!is_valid())
|
if (!is_valid())
|
||||||
return {};
|
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)
|
if (m_start.index_in_node < m_end.index_in_node)
|
||||||
return *this;
|
return *this;
|
||||||
return { m_end, m_start };
|
return { m_end, m_start };
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace Web::Layout {
|
||||||
class Node;
|
class Node;
|
||||||
|
|
||||||
struct LayoutPosition {
|
struct LayoutPosition {
|
||||||
RefPtr<Node> layout_node;
|
JS::Handle<Layout::Node> layout_node;
|
||||||
int index_in_node { 0 };
|
int index_in_node { 0 };
|
||||||
|
|
||||||
DOM::Position to_dom_position() const;
|
DOM::Position to_dom_position() const;
|
||||||
|
|
|
@ -17,7 +17,13 @@ ListItemBox::ListItemBox(DOM::Document& document, DOM::Element* element, Nonnull
|
||||||
|
|
||||||
ListItemBox::~ListItemBox() = default;
|
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);
|
m_marker = move(marker);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class ListItemBox final : public BlockContainer {
|
class ListItemBox final : public BlockContainer {
|
||||||
|
JS_CELL(ListItemBox, BlockContainer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListItemBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
ListItemBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~ListItemBox() override;
|
virtual ~ListItemBox() override;
|
||||||
|
@ -20,10 +22,12 @@ public:
|
||||||
DOM::Element const& dom_node() const { return static_cast<DOM::Element const&>(*BlockContainer::dom_node()); }
|
DOM::Element const& dom_node() const { return static_cast<DOM::Element const&>(*BlockContainer::dom_node()); }
|
||||||
|
|
||||||
ListItemMarkerBox const* marker() const { return m_marker; }
|
ListItemMarkerBox const* marker() const { return m_marker; }
|
||||||
void set_marker(RefPtr<ListItemMarkerBox>);
|
void set_marker(JS::GCPtr<ListItemMarkerBox>);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RefPtr<ListItemMarkerBox> m_marker;
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
|
JS::GCPtr<ListItemMarkerBox> m_marker;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class ListItemMarkerBox final : public Box {
|
class ListItemMarkerBox final : public Box {
|
||||||
|
JS_CELL(ListItemMarkerBox, Box);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ListItemMarkerBox(DOM::Document&, CSS::ListStyleType, size_t index, NonnullRefPtr<CSS::StyleProperties>);
|
explicit ListItemMarkerBox(DOM::Document&, CSS::ListStyleType, size_t index, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~ListItemMarkerBox() override;
|
virtual ~ListItemMarkerBox() override;
|
||||||
|
|
|
@ -15,24 +15,26 @@
|
||||||
#include <LibWeb/Layout/Node.h>
|
#include <LibWeb/Layout/Node.h>
|
||||||
#include <LibWeb/Layout/TextNode.h>
|
#include <LibWeb/Layout/TextNode.h>
|
||||||
#include <LibWeb/Platform/FontPlugin.h>
|
#include <LibWeb/Platform/FontPlugin.h>
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
Node::Node(DOM::Document& document, DOM::Node* node)
|
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_anonymous(node == nullptr)
|
||||||
{
|
{
|
||||||
m_serial_id = document.next_layout_node_serial_id({});
|
m_serial_id = document.next_layout_node_serial_id({});
|
||||||
|
|
||||||
if (node)
|
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)
|
Base::visit_edges(visitor);
|
||||||
m_dom_node->set_layout_node({}, nullptr);
|
visitor.visit(m_dom_node);
|
||||||
|
TreeNode::visit_edges(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-display-3/#out-of-flow
|
// 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());
|
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
|
String Node::debug_description() const
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.append(class_name().substring_view(13));
|
builder.append(class_name());
|
||||||
if (dom_node()) {
|
if (dom_node()) {
|
||||||
builder.appendff("<{}>", dom_node()->node_name());
|
builder.appendff("<{}>", dom_node()->node_name());
|
||||||
if (dom_node()->is_element()) {
|
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();
|
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));
|
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_font = m_font;
|
||||||
wrapper->m_line_height = m_line_height;
|
wrapper->m_line_height = m_line_height;
|
||||||
return wrapper;
|
return *wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::set_paintable(RefPtr<Painting::Paintable> paintable)
|
void Node::set_paintable(RefPtr<Painting::Paintable> paintable)
|
||||||
|
|
|
@ -31,7 +31,12 @@ enum class LayoutMode {
|
||||||
IntrinsicSizing,
|
IntrinsicSizing,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Node : public TreeNode<Node> {
|
class Node
|
||||||
|
: public JS::Cell
|
||||||
|
, public TreeNode<Node>
|
||||||
|
, public Weakable<Node> {
|
||||||
|
JS_CELL(Node, JS::Cell);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~Node();
|
virtual ~Node();
|
||||||
|
|
||||||
|
@ -61,7 +66,6 @@ public:
|
||||||
|
|
||||||
bool is_root_element() const;
|
bool is_root_element() const;
|
||||||
|
|
||||||
String class_name() const;
|
|
||||||
String debug_description() const;
|
String debug_description() const;
|
||||||
|
|
||||||
bool has_style() const { return m_has_style; }
|
bool has_style() const { return m_has_style; }
|
||||||
|
@ -140,10 +144,12 @@ public:
|
||||||
protected:
|
protected:
|
||||||
Node(DOM::Document&, DOM::Node*);
|
Node(DOM::Document&, DOM::Node*);
|
||||||
|
|
||||||
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class NodeWithStyle;
|
friend class NodeWithStyle;
|
||||||
|
|
||||||
JS::Handle<DOM::Node> m_dom_node;
|
JS::NonnullGCPtr<DOM::Node> m_dom_node;
|
||||||
RefPtr<Painting::Paintable> m_paintable;
|
RefPtr<Painting::Paintable> m_paintable;
|
||||||
|
|
||||||
size_t m_serial_id { 0 };
|
size_t m_serial_id { 0 };
|
||||||
|
@ -159,6 +165,8 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
class NodeWithStyle : public Node {
|
class NodeWithStyle : public Node {
|
||||||
|
JS_CELL(NodeWithStyle, Node);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~NodeWithStyle() override = default;
|
virtual ~NodeWithStyle() override = default;
|
||||||
|
|
||||||
|
@ -171,7 +179,7 @@ public:
|
||||||
Vector<CSS::BackgroundLayerData> const& background_layers() const { return computed_values().background_layers(); }
|
Vector<CSS::BackgroundLayerData> const& background_layers() const { return computed_values().background_layers(); }
|
||||||
const CSS::AbstractImageStyleValue* list_style_image() const { return m_list_style_image; }
|
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:
|
protected:
|
||||||
NodeWithStyle(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
|
NodeWithStyle(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
|
@ -185,6 +193,8 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
class NodeWithStyleAndBoxModelMetrics : public NodeWithStyle {
|
class NodeWithStyleAndBoxModelMetrics : public NodeWithStyle {
|
||||||
|
JS_CELL(NodeWithStyleAndBoxModelMetrics, NodeWithStyle);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BoxModelMetrics& box_model() { return m_box_model; }
|
BoxModelMetrics& box_model() { return m_box_model; }
|
||||||
BoxModelMetrics const& box_model() const { return m_box_model; }
|
BoxModelMetrics const& box_model() const { return m_box_model; }
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class Progress : public LabelableNode {
|
class Progress : public LabelableNode {
|
||||||
|
JS_CELL(Progress, LabelableNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Progress(DOM::Document&, HTML::HTMLProgressElement&, NonnullRefPtr<CSS::StyleProperties>);
|
Progress(DOM::Document&, HTML::HTMLProgressElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~Progress() override;
|
virtual ~Progress() override;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class RadioButton final : public FormAssociatedLabelableNode {
|
class RadioButton final : public FormAssociatedLabelableNode {
|
||||||
|
JS_CELL(RadioButton, FormAssociatedLabelableNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RadioButton(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
|
RadioButton(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~RadioButton() override;
|
virtual ~RadioButton() override;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class ReplacedBox : public Box {
|
class ReplacedBox : public Box {
|
||||||
|
JS_CELL(ReplacedBox, Box);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ReplacedBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
|
ReplacedBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~ReplacedBox() override;
|
virtual ~ReplacedBox() override;
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class SVGBox : public Box {
|
class SVGBox : public Box {
|
||||||
|
JS_CELL(SVGBox, Box);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SVGBox(DOM::Document&, SVG::SVGElement&, NonnullRefPtr<CSS::StyleProperties>);
|
SVGBox(DOM::Document&, SVG::SVGElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~SVGBox() override = default;
|
virtual ~SVGBox() override = default;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class SVGGeometryBox final : public SVGGraphicsBox {
|
class SVGGeometryBox final : public SVGGraphicsBox {
|
||||||
|
JS_CELL(SVGGeometryBox, SVGGraphicsBox);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SVGGeometryBox(DOM::Document&, SVG::SVGGeometryElement&, NonnullRefPtr<CSS::StyleProperties>);
|
SVGGeometryBox(DOM::Document&, SVG::SVGGeometryElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~SVGGeometryBox() override = default;
|
virtual ~SVGGeometryBox() override = default;
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class SVGGraphicsBox : public SVGBox {
|
class SVGGraphicsBox : public SVGBox {
|
||||||
|
JS_CELL(SVGGraphicsBox, SVGBox);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SVGGraphicsBox(DOM::Document&, SVG::SVGGraphicsElement&, NonnullRefPtr<CSS::StyleProperties>);
|
SVGGraphicsBox(DOM::Document&, SVG::SVGGraphicsElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~SVGGraphicsBox() override = default;
|
virtual ~SVGGraphicsBox() override = default;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class SVGSVGBox final : public ReplacedBox {
|
class SVGSVGBox final : public ReplacedBox {
|
||||||
|
JS_CELL(SVGSVGBox, ReplacedBox);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SVGSVGBox(DOM::Document&, SVG::SVGSVGElement&, NonnullRefPtr<CSS::StyleProperties>);
|
SVGSVGBox(DOM::Document&, SVG::SVGSVGElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~SVGSVGBox() override = default;
|
virtual ~SVGSVGBox() override = default;
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class TableBox final : public Layout::BlockContainer {
|
class TableBox final : public Layout::BlockContainer {
|
||||||
|
JS_CELL(TableBox, BlockContainer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TableBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
TableBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
TableBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
|
TableBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class TableCellBox final : public BlockContainer {
|
class TableCellBox final : public BlockContainer {
|
||||||
|
JS_CELL(TableCellBox, BlockContainer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TableCellBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
TableCellBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
TableCellBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
|
TableCellBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class TableRowBox final : public Box {
|
class TableRowBox final : public Box {
|
||||||
|
JS_CELL(TableRowBox, Box);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TableRowBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
TableRowBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
TableRowBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
|
TableRowBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
class TableRowGroupBox final : public BlockContainer {
|
class TableRowGroupBox final : public BlockContainer {
|
||||||
|
JS_CELL(TableRowGroupBox, BlockContainer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TableRowGroupBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
TableRowGroupBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
||||||
virtual ~TableRowGroupBox() override;
|
virtual ~TableRowGroupBox() override;
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace Web::Layout {
|
||||||
class LineBoxFragment;
|
class LineBoxFragment;
|
||||||
|
|
||||||
class TextNode : public Node {
|
class TextNode : public Node {
|
||||||
|
JS_CELL(TextNode, Node);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TextNode(DOM::Document&, DOM::Text&);
|
TextNode(DOM::Document&, DOM::Text&);
|
||||||
virtual ~TextNode() override;
|
virtual ~TextNode() override;
|
||||||
|
|
|
@ -31,7 +31,7 @@ TreeBuilder::TreeBuilder() = default;
|
||||||
|
|
||||||
static bool has_inline_or_in_flow_block_children(Layout::Node const& layout_node)
|
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())
|
if (child->is_inline())
|
||||||
return true;
|
return true;
|
||||||
if (!child->is_floating() && !child->is_absolutely_positioned())
|
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())
|
if (layout_node.children_are_inline())
|
||||||
return false;
|
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())
|
if (child->is_inline())
|
||||||
continue;
|
continue;
|
||||||
if (!child->is_floating() && !child->is_absolutely_positioned())
|
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).
|
// Parent block has inline-level children (our siblings).
|
||||||
// First move these siblings into an anonymous wrapper block.
|
// First move these siblings into an anonymous wrapper block.
|
||||||
NonnullRefPtrVector<Layout::Node> children;
|
Vector<JS::Handle<Layout::Node>> children;
|
||||||
while (RefPtr<Layout::Node> child = layout_parent.first_child()) {
|
while (JS::GCPtr<Layout::Node> child = layout_parent.first_child()) {
|
||||||
layout_parent.remove_child(*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.append_child(layout_parent.create_anonymous_wrapper());
|
||||||
layout_parent.set_children_are_inline(false);
|
layout_parent.set_children_are_inline(false);
|
||||||
for (auto& child : children) {
|
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);
|
layout_parent.last_child()->set_children_are_inline(true);
|
||||||
// Then it's safe to insert this block into parent.
|
// 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
|
// FIXME: Handle images, and multiple values
|
||||||
if (pseudo_element_content.type == CSS::ContentData::Type::String) {
|
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 = 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);
|
text_node->set_generated(true);
|
||||||
push_parent(verify_cast<NodeWithStyle>(*pseudo_element_node));
|
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();
|
pop_parent();
|
||||||
} else {
|
} else {
|
||||||
TODO();
|
TODO();
|
||||||
|
@ -195,7 +195,7 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
|
||||||
|
|
||||||
auto& document = dom_node.document();
|
auto& document = dom_node.document();
|
||||||
auto& style_computer = document.style_computer();
|
auto& style_computer = document.style_computer();
|
||||||
RefPtr<Layout::Node> layout_node;
|
JS::GCPtr<Layout::Node> layout_node;
|
||||||
RefPtr<CSS::StyleProperties> style;
|
RefPtr<CSS::StyleProperties> style;
|
||||||
CSS::Display display;
|
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)) {
|
} else if (is<DOM::Document>(dom_node)) {
|
||||||
style = style_computer.create_document_style();
|
style = style_computer.create_document_style();
|
||||||
display = style->display();
|
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)) {
|
} 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);
|
display = CSS::Display(CSS::Display::Outside::Inline, CSS::Display::Inside::Flow);
|
||||||
} else if (is<DOM::ShadowRoot>(dom_node)) {
|
} 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);
|
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);
|
auto& element = static_cast<DOM::Element&>(dom_node);
|
||||||
int child_index = layout_node->parent()->index_of_child<ListItemBox>(*layout_node).value();
|
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 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);
|
static_cast<ListItemBox&>(*layout_node).set_marker(list_item_marker);
|
||||||
element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::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)) {
|
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());
|
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:
|
// 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.
|
// Children of a table-column.
|
||||||
for_each_in_tree_with_internal_display<CSS::Display::Internal::TableColumn>(root, [&](Box& 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
|
// - whose immediate sibling, if any, is a table-non-root box
|
||||||
|
|
||||||
for (auto& box : to_remove)
|
for (auto& box : to_remove)
|
||||||
box.parent()->remove_child(box);
|
box->parent()->remove_child(*box);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_table_track(CSS::Display display)
|
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>
|
template<typename Matcher, typename Callback>
|
||||||
static void for_each_sequence_of_consecutive_children_matching(NodeWithStyle& parent, Matcher matcher, Callback 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 {
|
auto sequence_is_all_ignorable_whitespace = [&]() -> bool {
|
||||||
for (auto& node : sequence) {
|
for (auto& node : sequence) {
|
||||||
if (!is_ignorable_whitespace(node))
|
if (!is_ignorable_whitespace(*node))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
Node* next_sibling = nullptr;
|
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();
|
next_sibling = child->next_sibling();
|
||||||
if (matcher(*child)) {
|
if (matcher(*child)) {
|
||||||
sequence.append(*child);
|
sequence.append(*child);
|
||||||
|
@ -452,21 +452,21 @@ static void for_each_sequence_of_consecutive_children_matching(NodeWithStyle& pa
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename WrapperBoxType>
|
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());
|
VERIFY(!sequence.is_empty());
|
||||||
auto& parent = *sequence.first().parent();
|
auto& parent = *sequence.first()->parent();
|
||||||
auto computed_values = parent.computed_values().clone_inherited_values();
|
auto computed_values = parent.computed_values().clone_inherited_values();
|
||||||
static_cast<CSS::MutableComputedValues&>(computed_values).set_display(WrapperBoxType::static_display());
|
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) {
|
for (auto& child : sequence) {
|
||||||
parent.remove_child(child);
|
parent.remove_child(*child);
|
||||||
wrapper->append_child(child);
|
wrapper->append_child(*child);
|
||||||
}
|
}
|
||||||
if (nearest_sibling)
|
if (nearest_sibling)
|
||||||
parent.insert_before(move(wrapper), *nearest_sibling);
|
parent.insert_before(*wrapper, *nearest_sibling);
|
||||||
else
|
else
|
||||||
parent.append_child(move(wrapper));
|
parent.append_child(*wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreeBuilder::generate_missing_child_wrappers(NodeWithStyle& root)
|
void TreeBuilder::generate_missing_child_wrappers(NodeWithStyle& root)
|
||||||
|
|
|
@ -17,7 +17,7 @@ class TreeBuilder {
|
||||||
public:
|
public:
|
||||||
TreeBuilder();
|
TreeBuilder();
|
||||||
|
|
||||||
RefPtr<Layout::Node> build(DOM::Node&);
|
JS::GCPtr<Layout::Node> build(DOM::Node&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Context {
|
struct Context {
|
||||||
|
@ -47,7 +47,7 @@ private:
|
||||||
void insert_node_into_inline_or_block_ancestor(Layout::Node&, CSS::Display, AppendOrPrepend);
|
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);
|
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;
|
Vector<Layout::NodeWithStyle&> m_ancestor_stack;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -637,7 +637,7 @@ bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_poin
|
||||||
if (!document->layout_node())
|
if (!document->layout_node())
|
||||||
return false;
|
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 (key == KeyCode::Key_Tab) {
|
||||||
if (modifiers & KeyModifier::Mod_Shift)
|
if (modifiers & KeyModifier::Mod_Shift)
|
||||||
|
|
|
@ -71,6 +71,12 @@ Gfx::FloatPoint PaintableBox::effective_offset() const
|
||||||
{
|
{
|
||||||
Gfx::FloatPoint offset;
|
Gfx::FloatPoint offset;
|
||||||
if (m_containing_line_box_fragment.has_value()) {
|
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];
|
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();
|
offset = fragment.offset();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class SVGClipPathElement final : public SVGElement {
|
||||||
public:
|
public:
|
||||||
virtual ~SVGClipPathElement();
|
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:
|
private:
|
||||||
SVGClipPathElement(DOM::Document&, DOM::QualifiedName);
|
SVGClipPathElement(DOM::Document&, DOM::QualifiedName);
|
||||||
|
|
|
@ -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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class SVGDefsElement final : public SVGGraphicsElement {
|
||||||
public:
|
public:
|
||||||
virtual ~SVGDefsElement();
|
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:
|
private:
|
||||||
SVGDefsElement(DOM::Document&, DOM::QualifiedName);
|
SVGDefsElement(DOM::Document&, DOM::QualifiedName);
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class SVGGElement final : public SVGGraphicsElement {
|
||||||
public:
|
public:
|
||||||
virtual ~SVGGElement() override = default;
|
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:
|
private:
|
||||||
SVGGElement(DOM::Document&, DOM::QualifiedName);
|
SVGGElement(DOM::Document&, DOM::QualifiedName);
|
||||||
|
|
|
@ -16,9 +16,9 @@ SVGGeometryElement::SVGGeometryElement(DOM::Document& document, DOM::QualifiedNa
|
||||||
set_prototype(&Bindings::cached_web_prototype(realm(), "SVGGeometryElement"));
|
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()
|
float SVGGeometryElement::get_total_length()
|
||||||
|
|
|
@ -16,7 +16,7 @@ class SVGGeometryElement : public SVGGraphicsElement {
|
||||||
WEB_PLATFORM_OBJECT(SVGGeometryElement, SVGGraphicsElement);
|
WEB_PLATFORM_OBJECT(SVGGeometryElement, SVGGraphicsElement);
|
||||||
|
|
||||||
public:
|
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;
|
virtual Gfx::Path& get_path() = 0;
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,9 @@ SVGSVGElement::SVGSVGElement(DOM::Document& document, DOM::QualifiedName qualifi
|
||||||
set_prototype(&Bindings::cached_web_prototype(realm(), "SVGSVGElement"));
|
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
|
void SVGSVGElement::apply_presentational_hints(CSS::StyleProperties& style) const
|
||||||
|
|
|
@ -16,7 +16,7 @@ class SVGSVGElement final : public SVGGraphicsElement {
|
||||||
WEB_PLATFORM_OBJECT(SVGSVGElement, SVGGraphicsElement);
|
WEB_PLATFORM_OBJECT(SVGSVGElement, SVGGraphicsElement);
|
||||||
|
|
||||||
public:
|
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;
|
virtual void apply_presentational_hints(CSS::StyleProperties&) const override;
|
||||||
|
|
||||||
|
|
|
@ -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
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -7,54 +7,27 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/Assertions.h>
|
#include <AK/Assertions.h>
|
||||||
#include <AK/NonnullRefPtr.h>
|
|
||||||
#include <AK/TypeCasts.h>
|
#include <AK/TypeCasts.h>
|
||||||
#include <AK/Weakable.h>
|
#include <LibJS/Heap/GCPtr.h>
|
||||||
#include <LibWeb/Forward.h>
|
#include <LibWeb/Forward.h>
|
||||||
|
|
||||||
namespace Web {
|
namespace Web {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class TreeNode : public Weakable<T> {
|
class TreeNode {
|
||||||
public:
|
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; }
|
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; }
|
bool has_children() const { return m_first_child; }
|
||||||
T* next_sibling() { return m_next_sibling; }
|
T* next_sibling() { return m_next_sibling; }
|
||||||
T* previous_sibling() { return m_previous_sibling; }
|
T* previous_sibling() { return m_previous_sibling; }
|
||||||
T* first_child() { return m_first_child; }
|
T* first_child() { return m_first_child; }
|
||||||
T* last_child() { return m_last_child; }
|
T* last_child() { return m_last_child; }
|
||||||
const T* next_sibling() const { return m_next_sibling; }
|
T const* next_sibling() const { return m_next_sibling; }
|
||||||
const T* previous_sibling() const { return m_previous_sibling; }
|
T const* previous_sibling() const { return m_previous_sibling; }
|
||||||
const T* first_child() const { return m_first_child; }
|
T const* first_child() const { return m_first_child; }
|
||||||
const T* last_child() const { return m_last_child; }
|
T const* last_child() const { return m_last_child; }
|
||||||
|
|
||||||
size_t child_count() const
|
size_t child_count() const
|
||||||
{
|
{
|
||||||
|
@ -75,7 +48,7 @@ public:
|
||||||
return nullptr;
|
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);
|
return const_cast<TreeNode*>(this)->child_at_index(index);
|
||||||
}
|
}
|
||||||
|
@ -90,7 +63,7 @@ public:
|
||||||
return index;
|
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);
|
VERIFY(search_child.parent() == this);
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
|
@ -106,7 +79,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ChildType>
|
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);
|
VERIFY(search_child.parent() == this);
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
|
@ -130,12 +103,12 @@ public:
|
||||||
|
|
||||||
bool is_following(TreeNode const&) const;
|
bool is_following(TreeNode const&) const;
|
||||||
|
|
||||||
void append_child(NonnullRefPtr<T> node);
|
void append_child(JS::NonnullGCPtr<T> node);
|
||||||
void prepend_child(NonnullRefPtr<T> node);
|
void prepend_child(JS::NonnullGCPtr<T> node);
|
||||||
void insert_before(NonnullRefPtr<T> node, RefPtr<T> child);
|
void insert_before(JS::NonnullGCPtr<T> node, JS::GCPtr<T> child);
|
||||||
void remove_child(NonnullRefPtr<T> node);
|
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()
|
T* next_in_pre_order()
|
||||||
{
|
{
|
||||||
|
@ -230,7 +203,7 @@ public:
|
||||||
template<typename Callback>
|
template<typename Callback>
|
||||||
IterationDecision for_each_in_inclusive_subtree(Callback callback) const
|
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;
|
return IterationDecision::Break;
|
||||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||||
if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
|
if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
|
||||||
|
@ -254,7 +227,7 @@ public:
|
||||||
template<typename U, typename Callback>
|
template<typename U, typename Callback>
|
||||||
IterationDecision for_each_in_inclusive_subtree_of_type(Callback 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)
|
if (callback(static_cast<U&>(*this)) == IterationDecision::Break)
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
}
|
}
|
||||||
|
@ -268,8 +241,8 @@ public:
|
||||||
template<typename U, typename Callback>
|
template<typename U, typename Callback>
|
||||||
IterationDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
|
IterationDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
|
||||||
{
|
{
|
||||||
if (is<U>(static_cast<const T&>(*this))) {
|
if (is<U>(static_cast<T const&>(*this))) {
|
||||||
if (callback(static_cast<const U&>(*this)) == IterationDecision::Break)
|
if (callback(static_cast<U const&>(*this)) == IterationDecision::Break)
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
}
|
}
|
||||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||||
|
@ -348,7 +321,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename U>
|
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>();
|
return const_cast<TreeNode*>(this)->template next_sibling_of_type<U>();
|
||||||
}
|
}
|
||||||
|
@ -364,7 +337,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename U>
|
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>();
|
return const_cast<TreeNode*>(this)->template previous_sibling_of_type<U>();
|
||||||
}
|
}
|
||||||
|
@ -380,13 +353,13 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename U>
|
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>();
|
return const_cast<TreeNode*>(this)->template first_child_of_type<U>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename 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>();
|
return const_cast<TreeNode*>(this)->template last_child_of_type<U>();
|
||||||
}
|
}
|
||||||
|
@ -418,7 +391,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename U>
|
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>();
|
return const_cast<TreeNode*>(this)->template first_ancestor_of_type<U>();
|
||||||
}
|
}
|
||||||
|
@ -442,25 +415,21 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
~TreeNode()
|
~TreeNode() = default;
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TreeNode() = default;
|
TreeNode() = default;
|
||||||
|
|
||||||
bool m_deletion_has_begun { false };
|
void visit_edges(JS::Cell::Visitor& visitor)
|
||||||
bool m_in_removed_last_ref { false };
|
{
|
||||||
|
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:
|
private:
|
||||||
int m_ref_count { 1 };
|
|
||||||
T* m_parent { nullptr };
|
T* m_parent { nullptr };
|
||||||
T* m_first_child { nullptr };
|
T* m_first_child { nullptr };
|
||||||
T* m_last_child { nullptr };
|
T* m_last_child { nullptr };
|
||||||
|
@ -469,7 +438,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
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);
|
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_next_sibling = nullptr;
|
||||||
node->m_previous_sibling = nullptr;
|
node->m_previous_sibling = nullptr;
|
||||||
node->m_parent = nullptr;
|
node->m_parent = nullptr;
|
||||||
|
|
||||||
node->unref();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
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);
|
VERIFY(!node->m_parent);
|
||||||
|
|
||||||
|
@ -507,11 +474,10 @@ inline void TreeNode<T>::append_child(NonnullRefPtr<T> node)
|
||||||
m_last_child = node.ptr();
|
m_last_child = node.ptr();
|
||||||
if (!m_first_child)
|
if (!m_first_child)
|
||||||
m_first_child = m_last_child;
|
m_first_child = m_last_child;
|
||||||
[[maybe_unused]] auto& rc = node.leak_ref();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
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)
|
if (!child)
|
||||||
return append_child(move(node));
|
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;
|
child->m_previous_sibling = node;
|
||||||
|
|
||||||
node->m_parent = static_cast<T*>(this);
|
node->m_parent = static_cast<T*>(this);
|
||||||
[[maybe_unused]] auto& rc = node.leak_ref();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
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);
|
VERIFY(!node->m_parent);
|
||||||
|
|
||||||
|
@ -550,7 +515,6 @@ inline void TreeNode<T>::prepend_child(NonnullRefPtr<T> node)
|
||||||
if (!m_last_child)
|
if (!m_last_child)
|
||||||
m_last_child = m_first_child;
|
m_last_child = m_first_child;
|
||||||
node->inserted_into(static_cast<T&>(*this));
|
node->inserted_into(static_cast<T&>(*this));
|
||||||
[[maybe_unused]] auto& rc = node.leak_ref();
|
|
||||||
|
|
||||||
static_cast<T*>(this)->children_changed();
|
static_cast<T*>(this)->children_changed();
|
||||||
}
|
}
|
||||||
|
|
|
@ -354,7 +354,7 @@ Messages::WebContentServer::InspectDomNodeResponse ConnectionFromClient::inspect
|
||||||
|
|
||||||
if (pseudo_element.has_value()) {
|
if (pseudo_element.has_value()) {
|
||||||
auto pseudo_element_node = element.get_pseudo_element_node(pseudo_element.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, "", "", "", "" };
|
return { false, "", "", "", "" };
|
||||||
|
|
||||||
// FIXME: Pseudo-elements only exist as Layout::Nodes, which don't have style information
|
// FIXME: Pseudo-elements only exist as Layout::Nodes, which don't have style information
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue