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:
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
|
||||
// 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>)
|
||||
|
|
|
@ -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<>
|
||||
|
|
|
@ -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 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::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 };
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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_); }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -18,6 +18,8 @@ struct LineBoxFragmentCoordinate {
|
|||
};
|
||||
|
||||
class Box : public NodeWithStyleAndBoxModelMetrics {
|
||||
JS_CELL(Box, NodeWithStyleAndBoxModelMetrics);
|
||||
|
||||
public:
|
||||
Painting::PaintableBox const* paint_box() const;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()); }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
namespace Web::Layout {
|
||||
|
||||
class LabelableNode : public ReplacedBox {
|
||||
JS_CELL(LabelableNode, ReplacedBox);
|
||||
|
||||
public:
|
||||
Painting::LabelablePaintable* paintable();
|
||||
Painting::LabelablePaintable const* paintable() const;
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue