LibWeb: Make layout nodes refcounted

Move the layout tree from GC allocation to refcounted ownership so
removed layout and paint subtrees are destroyed synchronously instead
of waiting for the next GC sweep. This dramatically reduces GC memory
usage peaks after layout tree churn and makes it easier for memory use
to fall back after large document updates.

Update layout factories, tree traversal, SVG layout node creation,
paintable back-pointers, and pseudo-element layout links to use RefPtr
ownership.

Make display: contents follow the same shape as Blink and WebKit: the
element itself does not create a layout node, and its children are
flattened into the nearest layout parent. Wrap direct non-whitespace
text in an anonymous inline node when the boxless element contributes
inherited style to that text.

Use an internal inline wrapper for display: contents pseudo-elements
so generated content can still participate in layout, painting, hit
testing, and pseudo-element queries. Keep CSSOM reporting the computed
display value from the pseudo style, not the internal wrapper.

Remove the retained out-of-tree layout node list and its testing hook,
since the flattened model does not need a side owner for boxless
elements. Add coverage for inherited text style, dynamic insertion
order, pseudo-element hit testing, and computed style queries.
This commit is contained in:
Andreas Kling 2026-06-07 17:50:33 +02:00 committed by Andreas Kling
parent 0b94af1109
commit 9340d2d1a3
173 changed files with 1240 additions and 806 deletions

View file

@ -34,6 +34,7 @@
#include <LibWeb/HTML/Navigable.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/Layout/Node.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/BoxModelMetrics.h>
#include <LibWeb/Painting/PaintableBox.h>
@ -603,6 +604,11 @@ Optional<StyleProperty> CSSStyleProperties::get_direct_property(PropertyNameAndI
layout_node = abstract_element.layout_node();
}
if (auto pseudo_element = abstract_element.pseudo_element(); layout_node && pseudo_element.has_value()) {
if (auto pseudo_style = abstract_element.element().computed_properties(*pseudo_element); pseudo_style && pseudo_style->display().is_contents())
layout_node = nullptr;
}
// FIXME: Somehow get custom properties if there's no layout node.
if (property_name_and_id.is_custom_property()) {
if (auto maybe_value = abstract_element.get_custom_property(property_name_and_id.name())) {
@ -642,6 +648,37 @@ Optional<StyleProperty> CSSStyleProperties::get_direct_property(PropertyNameAndI
// ancestor-dependent selectors still match for no `layout_node`
// queries (for example `.outer .inner .target`).
auto style = abstract_element.document().style_computer().compute_style_with_seeded_ancestors(abstract_element);
if (first_is_one_of(property_id,
PropertyID::BackgroundColor,
PropertyID::BorderBottomColor,
PropertyID::BorderLeftColor,
PropertyID::BorderRightColor,
PropertyID::BorderTopColor,
PropertyID::CaretColor,
PropertyID::Color,
PropertyID::OutlineColor,
PropertyID::TextDecorationColor)) {
auto color_scheme = style->color_scheme(abstract_element.document().page().preferred_color_scheme(), abstract_element.document().supported_color_schemes());
ColorResolutionContext color_resolution_context {
.color_scheme = color_scheme,
.current_color = CSS::InitialValues::color(),
.calculation_resolution_context = {},
};
color_resolution_context.current_color = style->color(PropertyID::Color, color_resolution_context);
auto const& value = style->property(property_id);
Optional<Color> color;
if (property_id == PropertyID::CaretColor && value.is_keyword() && value.to_keyword() == Keyword::Auto)
color = style->color(PropertyID::Color, color_resolution_context);
else if (value.has_color())
color = value.to_color(color_resolution_context).value();
if (color.has_value()) {
return StyleProperty {
.property_id = property_id,
.value = ColorStyleValue::create_from_color(*color, ColorSyntax::Modern),
};
}
}
return StyleProperty {
.property_id = property_id,
.value = style->property(property_id),

View file

@ -1426,9 +1426,8 @@ RefPtr<StyleValue const> interpolate_box_shadow(DOM::Element& element, Calculati
// NB: Called during style interpolation.
ColorResolutionContext color_resolution_context {};
if (auto node = element.unsafe_layout_node()) {
color_resolution_context = ColorResolutionContext::for_layout_node_with_style(*element.unsafe_layout_node());
}
if (auto* node = element.unsafe_layout_node())
color_resolution_context = ColorResolutionContext::for_layout_node_with_style(*node);
for (size_t i = 0; i < from_shadows.size(); i++) {
auto const& from_shadow = from_shadows[i]->as_shadow();
@ -1740,9 +1739,8 @@ static RefPtr<StyleValue const> interpolate_value_impl(DOM::Element& element, Ca
case StyleValue::Type::Color: {
// NB: Called during style interpolation.
ColorResolutionContext color_resolution_context {};
if (auto node = element.unsafe_layout_node()) {
color_resolution_context = ColorResolutionContext::for_layout_node_with_style(*element.unsafe_layout_node());
}
if (auto* node = element.unsafe_layout_node())
color_resolution_context = ColorResolutionContext::for_layout_node_with_style(*node);
if (auto interpolated = interpolate_color(from, to, delta, {}, color_resolution_context))
return interpolated;

View file

@ -73,14 +73,14 @@ AbstractElement::TreeCountingFunctionResolutionContext AbstractElement::tree_cou
};
}
GC::Ptr<Layout::NodeWithStyle> AbstractElement::layout_node()
Layout::NodeWithStyle* AbstractElement::layout_node()
{
if (m_pseudo_element.has_value())
return m_element->pseudo_element_layout_node(*m_pseudo_element);
return m_element->layout_node();
}
GC::Ptr<Layout::NodeWithStyle> AbstractElement::unsafe_layout_node()
Layout::NodeWithStyle* AbstractElement::unsafe_layout_node()
{
if (m_pseudo_element.has_value())
return m_element->pseudo_element_unsafe_layout_node(*m_pseudo_element);
@ -110,7 +110,7 @@ Optional<AbstractElement> AbstractElement::element_to_inherit_style_from() const
Optional<AbstractElement> AbstractElement::walk_layout_tree(WalkMethod walk_method)
{
// NB: Called during style recalculation.
GC::Ptr<Layout::Node> node = unsafe_layout_node();
Layout::Node* node = unsafe_layout_node();
if (!node)
return OptionalNone {};

View file

@ -26,11 +26,11 @@ public:
Element const& element() const { return m_element; }
Optional<CSS::PseudoElement> pseudo_element() const { return m_pseudo_element; }
GC::Ptr<Layout::NodeWithStyle> layout_node();
GC::Ptr<Layout::NodeWithStyle const> layout_node() const { return const_cast<AbstractElement*>(this)->layout_node(); }
Layout::NodeWithStyle* layout_node();
Layout::NodeWithStyle const* layout_node() const { return const_cast<AbstractElement*>(this)->layout_node(); }
GC::Ptr<Layout::NodeWithStyle> unsafe_layout_node();
GC::Ptr<Layout::NodeWithStyle const> unsafe_layout_node() const { return const_cast<AbstractElement*>(this)->unsafe_layout_node(); }
Layout::NodeWithStyle* unsafe_layout_node();
Layout::NodeWithStyle const* unsafe_layout_node() const { return const_cast<AbstractElement*>(this)->unsafe_layout_node(); }
struct TreeCountingFunctionResolutionContext {
size_t sibling_count;

View file

@ -672,7 +672,6 @@ void Document::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_pending_css_import_rules);
visitor.visit(m_page);
visitor.visit(m_window);
visitor.visit(m_layout_root);
visitor.visit(m_style_sheets);
visitor.visit(m_hovered_node);
visitor.visit(m_inspected_node);
@ -725,7 +724,6 @@ void Document::visit_edges(Cell::Visitor& visitor)
for (auto& resize_observer : m_resize_observers)
visitor.visit(resize_observer);
visitor.visit(m_svg_roots_needing_relayout);
visitor.visit(m_query_containers_needing_container_query_evaluation_after_layout);
visitor.visit(m_shared_resource_requests);
@ -1353,6 +1351,7 @@ void Document::tear_down_layout_tree()
{
if (m_layout_root)
m_layout_root->prepare_subtree_for_detach_from_layout_tree();
m_hit_test_display_list = nullptr;
m_layout_root = nullptr;
m_paintable = nullptr;
m_needs_full_layout_tree_update = true;
@ -1634,7 +1633,7 @@ void Document::invalidate_layout_tree(InvalidateLayoutTreeReason reason)
void Document::mark_svg_root_as_needing_relayout(Layout::SVGSVGBox& svg_root)
{
m_svg_roots_needing_relayout.set(svg_root);
m_svg_roots_needing_relayout.set(svg_root.make_weak_ptr<Layout::SVGSVGBox>());
}
void Document::set_needs_container_query_evaluation_after_layout(Element const& query_container)
@ -1799,8 +1798,10 @@ void Document::update_layout(UpdateLayoutReason reason)
// Partial SVG relayout
if (!needs_layout_tree_rebuild && !svg_roots_to_relayout.is_empty() && !m_layout_root->needs_layout_update()) {
for (auto const& svg_root : svg_roots_to_relayout)
for (auto const& svg_root : svg_roots_to_relayout) {
if (svg_root)
relayout_svg_root(*svg_root);
}
invalidate_stacking_context_tree();
set_needs_to_record_display_list();
@ -2761,7 +2762,7 @@ void Document::clear_grid_highlighted_node(GC::Ptr<Node> node)
node->set_needs_repaint();
}
GC::Ptr<Layout::Node> Document::highlighted_layout_node()
Layout::Node* Document::highlighted_layout_node()
{
if (!m_highlighted_node)
return nullptr;
@ -7919,16 +7920,18 @@ Vector<GC::Root<Range>> Document::find_matching_text(String const& query, CaseSe
match_start_position = &text_block.positions[i + 1];
auto start_position = match_index.value() - match_start_position->start_offset + match_start_position->dom_offset_within_node;
auto& start_dom_node = match_start_position->dom_node;
auto start_dom_node = match_start_position->dom_node.ptr();
VERIFY(start_dom_node);
auto* match_end_position = match_start_position;
for (; i < text_block.positions.size() - 1 && (match_index.value() + utf16_query.length_in_code_units() > text_block.positions[i + 1].start_offset); ++i)
match_end_position = &text_block.positions[i + 1];
auto& end_dom_node = match_end_position->dom_node;
auto end_dom_node = match_end_position->dom_node.ptr();
VERIFY(end_dom_node);
auto end_position = match_index.value() + utf16_query.length_in_code_units() - match_end_position->start_offset + match_end_position->dom_offset_within_node;
matches.append(Range::create(start_dom_node, start_position, end_dom_node, end_position));
matches.append(Range::create(*start_dom_node, start_position, *end_dom_node, end_position));
match_start_position = match_end_position;
offset = match_index.value() + utf16_query.length_in_code_units() + 1;
if (offset >= text_view.length_in_code_units())

View file

@ -14,6 +14,7 @@
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/HashTable.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
@ -316,8 +317,8 @@ public:
void set_highlighted_node(GC::Ptr<Node>, Optional<CSS::PseudoElement>);
GC::Ptr<Node const> highlighted_node() const { return m_highlighted_node; }
GC::Ptr<Layout::Node> highlighted_layout_node();
GC::Ptr<Layout::Node const> highlighted_layout_node() const { return const_cast<Document*>(this)->highlighted_layout_node(); }
Layout::Node* highlighted_layout_node();
Layout::Node const* highlighted_layout_node() const { return const_cast<Document*>(this)->highlighted_layout_node(); }
void set_flexbox_highlighted_node(GC::Ptr<Node>, Painting::FlexboxInspectorOverlayOptions);
void clear_flexbox_highlighted_node(GC::Ptr<Node>);
void set_grid_highlighted_node(GC::Ptr<Node>, Painting::GridInspectorOverlayOptions);
@ -1221,7 +1222,7 @@ private:
GC::Ptr<HTML::Window> m_window;
GC::Ptr<Layout::Viewport> m_layout_root;
RefPtr<Layout::Viewport> m_layout_root;
GC::Ptr<Node> m_hovered_node;
GC::Ptr<Node> m_inspected_node;
@ -1360,7 +1361,7 @@ private:
bool m_is_running_update_layout { false };
HashTable<GC::Ref<Layout::SVGSVGBox>> m_svg_roots_needing_relayout;
HashTable<WeakPtr<Layout::SVGSVGBox>> m_svg_roots_needing_relayout;
bool m_needs_animated_style_update { false };

View file

@ -806,7 +806,7 @@ Optional<GC::RootVector<GC::Ref<DOM::Element>>> Element::get_the_attribute_assoc
return elements;
}
GC::Ptr<Layout::Node> Element::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> Element::create_layout_node(CSS::ComputedProperties const& style)
{
if (local_name() == "noscript" && document().is_scripting_enabled())
return nullptr;
@ -815,23 +815,26 @@ GC::Ptr<Layout::Node> Element::create_layout_node(CSS::ComputedProperties const&
return create_layout_node_for_display_type(document(), display, style, this);
}
GC::Ptr<Layout::NodeWithStyle> Element::create_layout_node_for_display_type(DOM::Document& document, CSS::Display const& display, CSS::ComputedProperties const& style, Element* element)
RefPtr<Layout::NodeWithStyle> Element::create_layout_node_for_display_type(DOM::Document& document, CSS::Display const& display, CSS::ComputedProperties const& style, Element* element)
{
if (display.is_none())
return {};
if (display.is_contents())
return {};
if (display.is_table_inside() || display.is_table_row_group() || display.is_table_header_group() || display.is_table_footer_group() || display.is_table_row())
return document.heap().allocate<Layout::Box>(document, element, style);
return make_ref_counted<Layout::Box>(document, element, style);
if (display.is_list_item())
return document.heap().allocate<Layout::ListItemBox>(document, element, style);
return make_ref_counted<Layout::ListItemBox>(document, element, style);
if (display.is_table_cell())
return document.heap().allocate<Layout::BlockContainer>(document, element, style);
return make_ref_counted<Layout::BlockContainer>(document, element, 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 document.heap().allocate<Layout::BlockContainer>(document, element, style);
return make_ref_counted<Layout::BlockContainer>(document, element, style);
}
if (display.is_math_inside()) {
@ -839,35 +842,35 @@ GC::Ptr<Layout::NodeWithStyle> Element::create_layout_node_for_display_type(DOM:
// MathML elements with a computed display value equal to block math or inline math control box generation
// and layout according to their tag name, as described in the relevant sections.
// FIXME: Figure out what kind of node we should make for them. For now, we'll stick with a generic Box.
return document.heap().allocate<Layout::BlockContainer>(document, element, style);
return make_ref_counted<Layout::BlockContainer>(document, element, style);
}
if (display.is_inline_outside()) {
if (display.is_flow_root_inside())
return document.heap().allocate<Layout::BlockContainer>(document, element, style);
return make_ref_counted<Layout::BlockContainer>(document, element, style);
if (display.is_flow_inside())
return document.heap().allocate<Layout::InlineNode>(document, element, style);
return make_ref_counted<Layout::InlineNode>(document, element, style);
if (display.is_flex_inside())
return document.heap().allocate<Layout::Box>(document, element, style);
return make_ref_counted<Layout::Box>(document, element, style);
if (display.is_grid_inside())
return document.heap().allocate<Layout::Box>(document, element, style);
return make_ref_counted<Layout::Box>(document, element, style);
dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Support display: {}", display.to_string());
return document.heap().allocate<Layout::InlineNode>(document, element, style);
return make_ref_counted<Layout::InlineNode>(document, element, style);
}
if (display.is_flex_inside() || display.is_grid_inside())
return document.heap().allocate<Layout::Box>(document, element, style);
return make_ref_counted<Layout::Box>(document, element, style);
if (display.is_flow_inside() || display.is_flow_root_inside() || display.is_contents())
return document.heap().allocate<Layout::BlockContainer>(document, element, style);
if (display.is_flow_inside() || display.is_flow_root_inside())
return make_ref_counted<Layout::BlockContainer>(document, element, style);
dbgln("FIXME: CSS display '{}' not implemented yet.", display.to_string());
// FIXME: We don't actually support `display: block ruby`, this is just a hack to prevent a crash
if (display.is_ruby_inside())
return document.heap().allocate<Layout::BlockContainer>(document, element, style);
return make_ref_counted<Layout::BlockContainer>(document, element, style);
return document.heap().allocate<Layout::InlineNode>(document, element, style);
return make_ref_counted<Layout::InlineNode>(document, element, style);
}
void Element::apply_presentational_hints(Vector<CSS::StyleProperty>& properties) const
@ -1844,7 +1847,7 @@ void Element::children_changed(ChildrenChangedMetadata const& metadata)
}
}
void Element::set_synthetic_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS::PseudoElement pseudo_element, GC::Ptr<Layout::NodeWithStyle> pseudo_element_node)
void Element::set_synthetic_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS::PseudoElement pseudo_element, Layout::NodeWithStyle* pseudo_element_node)
{
auto existing_pseudo_element = get_synthetic_pseudo_element(pseudo_element);
if (!existing_pseudo_element.has_value() && !pseudo_element_node)
@ -1857,14 +1860,14 @@ void Element::set_synthetic_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS:
ensure_synthetic_pseudo_element(pseudo_element).set_layout_node(move(pseudo_element_node));
}
GC::Ptr<Layout::NodeWithStyle> Element::pseudo_element_layout_node(CSS::PseudoElement pseudo_element) const
Layout::NodeWithStyle* Element::pseudo_element_layout_node(CSS::PseudoElement pseudo_element) const
{
if (auto element_data = get_pseudo_element(pseudo_element); element_data.has_value())
return element_data->layout_node();
return nullptr;
}
GC::Ptr<Layout::NodeWithStyle> Element::pseudo_element_unsafe_layout_node(CSS::PseudoElement pseudo_element) const
Layout::NodeWithStyle* Element::pseudo_element_unsafe_layout_node(CSS::PseudoElement pseudo_element) const
{
if (auto element_data = get_pseudo_element(pseudo_element); element_data.has_value())
return element_data->unsafe_layout_node();
@ -2041,6 +2044,8 @@ void Element::clear_synthetic_pseudo_element_layout_nodes()
return TraversalDecision::Continue;
});
layout_node->prepare_subtree_for_detach_from_layout_tree();
if (layout_node->parent())
layout_node->remove();
}
pseudo_element.set_layout_node(nullptr);
});
@ -3607,22 +3612,22 @@ void Element::for_each_attribute(Function<void(FlyString const&, String const&)>
});
}
GC::Ptr<Layout::NodeWithStyle> Element::layout_node()
Layout::NodeWithStyle* Element::layout_node()
{
return static_cast<Layout::NodeWithStyle*>(Node::layout_node());
}
GC::Ptr<Layout::NodeWithStyle const> Element::layout_node() const
Layout::NodeWithStyle const* Element::layout_node() const
{
return static_cast<Layout::NodeWithStyle const*>(Node::layout_node());
}
GC::Ptr<Layout::NodeWithStyle> Element::unsafe_layout_node()
Layout::NodeWithStyle* Element::unsafe_layout_node()
{
return static_cast<Layout::NodeWithStyle*>(Node::unsafe_layout_node());
}
GC::Ptr<Layout::NodeWithStyle const> Element::unsafe_layout_node() const
Layout::NodeWithStyle const* Element::unsafe_layout_node() const
{
return static_cast<Layout::NodeWithStyle const*>(Node::unsafe_layout_node());
}

View file

@ -202,11 +202,11 @@ public:
Optional<CSS::PseudoElement> associated_shadow_host_pseudo_element() const { return m_associated_shadow_host_pseudo_element; }
void set_associated_shadow_host_pseudo_element(CSS::PseudoElement pseudo_element);
GC::Ptr<Layout::NodeWithStyle> layout_node();
GC::Ptr<Layout::NodeWithStyle const> layout_node() const;
Layout::NodeWithStyle* layout_node();
Layout::NodeWithStyle const* layout_node() const;
GC::Ptr<Layout::NodeWithStyle> unsafe_layout_node();
GC::Ptr<Layout::NodeWithStyle const> unsafe_layout_node() const;
Layout::NodeWithStyle* unsafe_layout_node();
Layout::NodeWithStyle const* unsafe_layout_node() const;
RefPtr<CSS::ComputedProperties> computed_properties(Optional<CSS::PseudoElement> = {});
RefPtr<CSS::ComputedProperties const> computed_properties(Optional<CSS::PseudoElement> = {}) const;
@ -361,7 +361,7 @@ public:
[[nodiscard]] Vector<CSSPixelRect> client_rects_assuming_layout_clean() const;
[[nodiscard]] CSSPixelRect bounding_client_rect_assuming_layout_clean() const;
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&);
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&);
virtual void adjust_computed_style(CSS::ComputedProperties&) { }
virtual void did_receive_focus() { }
@ -369,7 +369,7 @@ public:
bool should_indicate_focus() const;
virtual bool is_focusable() const override;
static GC::Ptr<Layout::NodeWithStyle> create_layout_node_for_display_type(DOM::Document&, CSS::Display const&, CSS::ComputedProperties const&, Element*);
static RefPtr<Layout::NodeWithStyle> create_layout_node_for_display_type(DOM::Document&, CSS::Display const&, CSS::ComputedProperties const&, Element*);
void clear_removed_attributes_for_style_invalidation() { m_removed_attributes_for_style_invalidation.clear(); }
bool has_removed_attribute_for_style_invalidation(FlyString const& attribute_name) const
@ -382,10 +382,10 @@ public:
m_removed_attributes_for_style_invalidation.append(attribute_name);
}
void set_synthetic_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS::PseudoElement, GC::Ptr<Layout::NodeWithStyle>);
void set_synthetic_pseudo_element_node(Badge<Layout::TreeBuilder>, CSS::PseudoElement, Layout::NodeWithStyle*);
GC::Ptr<Layout::NodeWithStyle> pseudo_element_layout_node(CSS::PseudoElement) const;
GC::Ptr<Layout::NodeWithStyle> pseudo_element_unsafe_layout_node(CSS::PseudoElement) const;
Layout::NodeWithStyle* pseudo_element_layout_node(CSS::PseudoElement) const;
Layout::NodeWithStyle* pseudo_element_unsafe_layout_node(CSS::PseudoElement) const;
bool has_synthetic_pseudo_elements() const;
template<OneOf<Layout::TreeBuilder, Document, Node> T>

View file

@ -137,7 +137,6 @@ void Node::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_document);
visitor.visit(m_child_nodes);
visitor.visit(m_layout_node);
if (m_registered_observer_list) {
visitor.visit(*m_registered_observer_list);
}
@ -750,7 +749,7 @@ void Node::insert_before(GC::Ref<Node> node, GC::Ptr<Node> child, bool suppress_
if (is_connected()) {
// NB: Called during DOM insertion, layout is not up to date.
if (unsafe_layout_node() && unsafe_layout_node()->display().is_contents() && parent_element()) {
if (auto* element = as_if<Element>(*this); element && element->computed_properties() && element->computed_properties()->display().is_contents() && parent_element()) {
parent_element()->set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::NodeInsertBeforeWithDisplayContents);
}
set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::NodeInsertBefore);
@ -1673,7 +1672,7 @@ GC::Ptr<Node> Node::editing_host()
return {};
}
void Node::set_layout_node(Badge<Layout::Node>, GC::Ref<Layout::Node> layout_node)
void Node::set_layout_node(Badge<Layout::Node>, Layout::Node& layout_node)
{
m_layout_node = layout_node;
}
@ -1758,7 +1757,7 @@ void Node::set_needs_layout_tree_update(bool value, SetNeedsLayoutTreeUpdateReas
// If the layout node has an anonymous parent, rebuild from the nearest non-anonymous ancestor.
// FIXME: This is not optimal, and we should figure out how to rebuild a smaller part of the tree.
if (layout_node->parent() && layout_node->parent()->is_anonymous()) {
GC::Ptr<Layout::Node> ancestor = layout_node->parent();
auto* ancestor = layout_node->parent();
while (ancestor && ancestor->is_anonymous())
ancestor = ancestor->parent();
if (ancestor)

View file

@ -328,8 +328,8 @@ public:
Layout::Node const* layout_node() const;
Layout::Node* layout_node();
Layout::Node const* unsafe_layout_node() const { return m_layout_node; }
Layout::Node* unsafe_layout_node() { return m_layout_node; }
Layout::Node const* unsafe_layout_node() const { return m_layout_node.ptr(); }
Layout::Node* unsafe_layout_node() { return m_layout_node.ptr(); }
RefPtr<Painting::PaintableBox const> paintable_box() const;
RefPtr<Painting::PaintableBox> paintable_box();
@ -348,7 +348,7 @@ public:
void set_needs_layout_update(SetNeedsLayoutReason);
void clear_layout_node_and_paintable(Badge<Document>);
void set_layout_node(Badge<Layout::Node>, GC::Ref<Layout::Node>);
void set_layout_node(Badge<Layout::Node>, Layout::Node&);
void detach_layout_node(Badge<Layout::TreeBuilder>);
virtual bool is_child_allowed(Node const&) const { return true; }
@ -507,7 +507,7 @@ protected:
virtual size_t external_memory_size() const override;
GC::Ptr<Document> m_document;
GC::Ptr<Layout::Node> m_layout_node;
WeakPtr<Layout::Node> m_layout_node;
WeakPtr<Painting::Paintable> m_paintable;
NodeType m_type { NodeType::INVALID };
bool m_needs_layout_tree_update { false };

View file

@ -20,11 +20,15 @@ void SyntheticPseudoElement::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_layout_node);
if (m_counters_set)
m_counters_set->visit_edges(visitor);
}
void SyntheticPseudoElement::set_layout_node(Layout::NodeWithStyle* value)
{
m_layout_node = value;
}
Optional<CSS::CountersSet const&> SyntheticPseudoElement::counters_set() const
{
if (!m_counters_set)
@ -50,12 +54,12 @@ void SyntheticPseudoElementTreeNode::visit_edges(JS::Cell::Visitor& visitor)
TreeNode::visit_edges(visitor);
}
GC::Ptr<Layout::NodeWithStyle> ElementReferencePseudoElement::layout_node() const
Layout::NodeWithStyle* ElementReferencePseudoElement::layout_node() const
{
return m_referenced_element->layout_node();
}
GC::Ptr<Layout::NodeWithStyle> ElementReferencePseudoElement::unsafe_layout_node() const
Layout::NodeWithStyle* ElementReferencePseudoElement::unsafe_layout_node() const
{
return m_referenced_element->unsafe_layout_node();
}

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/OwnPtr.h>
#include <AK/WeakPtr.h>
#include <LibGC/CellAllocator.h>
#include <LibJS/Heap/Cell.h>
#include <LibWeb/CSS/ComputedProperties.h>
@ -23,8 +24,8 @@ class WEB_API PseudoElement : public JS::Cell {
GC_DECLARE_ALLOCATOR(PseudoElement);
public:
virtual GC::Ptr<Layout::NodeWithStyle> layout_node() const = 0;
virtual GC::Ptr<Layout::NodeWithStyle> unsafe_layout_node() const = 0;
virtual Layout::NodeWithStyle* layout_node() const = 0;
virtual Layout::NodeWithStyle* unsafe_layout_node() const = 0;
virtual RefPtr<CSS::ComputedProperties> computed_properties() const = 0;
@ -36,9 +37,9 @@ class WEB_API SyntheticPseudoElement : public PseudoElement {
GC_CELL(SyntheticPseudoElement, PseudoElement);
GC_DECLARE_ALLOCATOR(SyntheticPseudoElement);
GC::Ptr<Layout::NodeWithStyle> layout_node() const override { return m_layout_node; }
GC::Ptr<Layout::NodeWithStyle> unsafe_layout_node() const override { return m_layout_node; }
void set_layout_node(GC::Ptr<Layout::NodeWithStyle> value) { m_layout_node = value; }
Layout::NodeWithStyle* layout_node() const override { return m_layout_node.ptr(); }
Layout::NodeWithStyle* unsafe_layout_node() const override { return m_layout_node.ptr(); }
void set_layout_node(Layout::NodeWithStyle*);
RefPtr<CSS::ComputedProperties> computed_properties() const override { return m_computed_properties; }
void set_computed_properties(RefPtr<CSS::ComputedProperties> value) { m_computed_properties = value; }
@ -57,7 +58,7 @@ class WEB_API SyntheticPseudoElement : public PseudoElement {
virtual void visit_edges(JS::Cell::Visitor&) override;
private:
GC::Ptr<Layout::NodeWithStyle> m_layout_node;
WeakPtr<Layout::NodeWithStyle> m_layout_node;
RefPtr<CSS::ComputedProperties> m_computed_properties;
RefPtr<CSS::CustomPropertyData const> m_custom_property_data;
OwnPtr<CSS::CountersSet> m_counters_set;
@ -84,8 +85,8 @@ class WEB_API ElementReferencePseudoElement : public PseudoElement {
{
}
GC::Ptr<Layout::NodeWithStyle> layout_node() const override;
GC::Ptr<Layout::NodeWithStyle> unsafe_layout_node() const override;
Layout::NodeWithStyle* layout_node() const override;
Layout::NodeWithStyle* unsafe_layout_node() const override;
RefPtr<CSS::ComputedProperties> computed_properties() const override;

View file

@ -37,9 +37,9 @@ void HTMLAudioElement::adjust_computed_style(CSS::ComputedProperties& style)
style.set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::None)));
}
GC::Ptr<Layout::Node> HTMLAudioElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLAudioElement::create_layout_node(CSS::ComputedProperties const& style)
{
return heap().allocate<Layout::AudioBox>(document(), *this, style);
return make_ref_counted<Layout::AudioBox>(document(), *this, style);
}
Layout::AudioBox* HTMLAudioElement::layout_node()

View file

@ -29,7 +29,7 @@ private:
virtual void initialize(JS::Realm&) override;
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
};
}

View file

@ -29,9 +29,9 @@ void HTMLBRElement::initialize(JS::Realm& realm)
Base::initialize(realm);
}
GC::Ptr<Layout::Node> HTMLBRElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLBRElement::create_layout_node(CSS::ComputedProperties const& style)
{
return heap().allocate<Layout::BreakNode>(document(), *this, style);
return make_ref_counted<Layout::BreakNode>(document(), *this, style);
}
bool HTMLBRElement::is_presentational_hint(FlyString const& name) const

View file

@ -17,7 +17,7 @@ class HTMLBRElement final : public HTMLElement {
public:
virtual ~HTMLBRElement() override;
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual bool is_presentational_hint(FlyString const&) const override;
virtual void apply_presentational_hints(Vector<CSS::StyleProperty>&) const override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;

View file

@ -239,9 +239,9 @@ void HTMLCanvasElement::attribute_changed(FlyString const& local_name, Optional<
}
}
GC::Ptr<Layout::Node> HTMLCanvasElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLCanvasElement::create_layout_node(CSS::ComputedProperties const& style)
{
return heap().allocate<Layout::CanvasBox>(document(), *this, style);
return make_ref_counted<Layout::CanvasBox>(document(), *this, style);
}
void HTMLCanvasElement::adjust_computed_style(CSS::ComputedProperties& style)

View file

@ -68,7 +68,7 @@ private:
virtual bool is_presentational_hint(FlyString const&) const override;
virtual void apply_presentational_hints(Vector<CSS::StyleProperty>&) const override;
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
template<typename ContextType>

View file

@ -92,9 +92,9 @@ Layout::FieldSetBox* HTMLFieldSetElement::layout_node()
return static_cast<Layout::FieldSetBox*>(Node::layout_node());
}
GC::Ptr<Layout::Node> HTMLFieldSetElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLFieldSetElement::create_layout_node(CSS::ComputedProperties const& style)
{
return heap().allocate<Layout::FieldSetBox>(document(), *this, style);
return make_ref_counted<Layout::FieldSetBox>(document(), *this, style);
}
}

View file

@ -42,7 +42,7 @@ public:
virtual Optional<ARIA::Role> default_role() const override { return ARIA::Role::group; }
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
Layout::FieldSetBox* layout_node();
Layout::FieldSetBox const* layout_node() const;

View file

@ -40,9 +40,9 @@ void HTMLIFrameElement::initialize(JS::Realm& realm)
Base::initialize(realm);
}
GC::Ptr<Layout::Node> HTMLIFrameElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLIFrameElement::create_layout_node(CSS::ComputedProperties const& style)
{
return heap().allocate<Layout::NavigableContainerViewport>(document(), *this, style);
return make_ref_counted<Layout::NavigableContainerViewport>(document(), *this, style);
}
void HTMLIFrameElement::adjust_computed_style(CSS::ComputedProperties& style)

View file

@ -23,7 +23,7 @@ class HTMLIFrameElement final
public:
virtual ~HTMLIFrameElement() override;
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
// ^EventTarget

View file

@ -133,7 +133,7 @@ static void reset_intrinsic_size_caches_after_image_data_change(Layout::ImageBox
static void set_needs_layout_update_or_repaint_after_image_data_change(HTMLImageElement& image_element, DOM::SetNeedsLayoutReason reason)
{
auto layout_node = image_element.unsafe_layout_node();
auto* image_box = as_if<Layout::ImageBox>(layout_node.ptr());
auto* image_box = as_if<Layout::ImageBox>(layout_node);
if (!image_box || image_element_dimensions_may_depend_on_intrinsic_size(*image_box)) {
image_element.set_needs_layout_update(reason);
return;
@ -207,7 +207,6 @@ void HTMLImageElement::adopted_from(DOM::Document& old_document)
void HTMLImageElement::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
image_provider_visit_edges(visitor);
visitor.visit(m_current_request);
visitor.visit(m_pending_request);
visitor.visit(m_document_observer);
@ -296,9 +295,9 @@ void HTMLImageElement::form_associated_element_attribute_changed(FlyString const
}
}
GC::Ptr<Layout::Node> HTMLImageElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLImageElement::create_layout_node(CSS::ComputedProperties const& style)
{
return heap().allocate<Layout::ImageBox>(document(), *this, style, *this);
return make_ref_counted<Layout::ImageBox>(document(), *this, style, *this);
}
void HTMLImageElement::adjust_computed_style(CSS::ComputedProperties& style)

View file

@ -136,7 +136,7 @@ private:
// https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element:dimension-attributes
virtual bool supports_dimension_attributes() const override { return true; }
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void did_set_viewport_rect(CSSPixelRect const&) override;

View file

@ -107,7 +107,6 @@ void HTMLInputElement::initialize(JS::Realm& realm)
void HTMLInputElement::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
image_provider_visit_edges(visitor);
visitor.visit(m_inner_text_element);
visitor.visit(m_text_node);
visitor.visit(m_placeholder_element);
@ -132,7 +131,7 @@ void HTMLInputElement::set_being_activated(bool activated)
set_needs_repaint();
}
GC::Ptr<Layout::Node> HTMLInputElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLInputElement::create_layout_node(CSS::ComputedProperties const& style)
{
if (type_state() == TypeAttributeState::Hidden)
return nullptr;
@ -140,7 +139,7 @@ GC::Ptr<Layout::Node> HTMLInputElement::create_layout_node(CSS::ComputedProperti
// NOTE: Image inputs are `appearance: none` per the default UA style,
// but we still need to create an ImageBox for them, or no image will get loaded.
if (type_state() == TypeAttributeState::ImageButton) {
return heap().allocate<Layout::ImageBox>(document(), *this, style, *this);
return make_ref_counted<Layout::ImageBox>(document(), *this, style, *this);
}
// https://drafts.csswg.org/css-ui/#appearance-switching
@ -156,18 +155,18 @@ GC::Ptr<Layout::Node> HTMLInputElement::create_layout_node(CSS::ComputedProperti
case TypeAttributeState::SubmitButton:
case TypeAttributeState::Button:
case TypeAttributeState::ResetButton:
return heap().allocate<Layout::BlockContainer>(document(), this, style);
return make_ref_counted<Layout::BlockContainer>(document(), this, style);
case TypeAttributeState::Checkbox:
return heap().allocate<Layout::CheckBox>(document(), *this, style);
return make_ref_counted<Layout::CheckBox>(document(), *this, style);
case TypeAttributeState::RadioButton:
return heap().allocate<Layout::RadioButton>(document(), *this, style);
return make_ref_counted<Layout::RadioButton>(document(), *this, style);
case TypeAttributeState::Range:
return heap().allocate<Layout::RangeInputBox>(document(), *this, style);
return make_ref_counted<Layout::RangeInputBox>(document(), *this, style);
case TypeAttributeState::Color:
case TypeAttributeState::FileUpload:
return Element::create_layout_node_for_display_type(document(), style.display(), style, this);
default:
return heap().allocate<Layout::TextInputBox>(document(), *this, style);
return make_ref_counted<Layout::TextInputBox>(document(), *this, style);
}
}

View file

@ -64,7 +64,7 @@ class WEB_API HTMLInputElement final
public:
virtual ~HTMLInputElement() override;
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void set_being_activated(bool) override;

View file

@ -40,9 +40,9 @@ HTMLFormElement* HTMLLegendElement::form()
return nullptr;
}
GC::Ptr<Layout::Node> HTMLLegendElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLLegendElement::create_layout_node(CSS::ComputedProperties const& style)
{
return heap().allocate<Layout::LegendBox>(document(), *this, style);
return make_ref_counted<Layout::LegendBox>(document(), *this, style);
}
Layout::LegendBox* HTMLLegendElement::layout_node()

View file

@ -20,7 +20,7 @@ public:
HTMLFormElement* form();
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
Layout::LegendBox* layout_node();
Layout::LegendBox const* layout_node() const;

View file

@ -80,7 +80,6 @@ void HTMLObjectElement::initialize(JS::Realm& realm)
void HTMLObjectElement::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
image_provider_visit_edges(visitor);
visitor.visit(m_resource_request);
visitor.visit(m_document_observer);
}
@ -194,16 +193,16 @@ void HTMLObjectElement::set_data(String const& data)
set_attribute_value(HTML::AttributeNames::data, data);
}
GC::Ptr<Layout::Node> HTMLObjectElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLObjectElement::create_layout_node(CSS::ComputedProperties const& style)
{
switch (m_representation) {
case Representation::Children:
return NavigableContainer::create_layout_node(style);
case Representation::ContentNavigable:
return heap().allocate<Layout::NavigableContainerViewport>(document(), *this, style);
return make_ref_counted<Layout::NavigableContainerViewport>(document(), *this, style);
case Representation::Image:
if (image_data())
return heap().allocate<Layout::ImageBox>(document(), *this, style, *this);
return make_ref_counted<Layout::ImageBox>(document(), *this, style, *this);
break;
default:
break;

View file

@ -62,7 +62,7 @@ private:
virtual bool is_presentational_hint(FlyString const&) const override;
virtual void apply_presentational_hints(Vector<CSS::StyleProperty>&) const override;
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
bool has_ancestor_media_element_or_object_element_not_showing_fallback_content() const;

View file

@ -482,9 +482,9 @@ Optional<String> HTMLTextAreaElement::placeholder_value() const
return get_attribute_value(HTML::AttributeNames::placeholder);
}
GC::Ptr<Layout::Node> HTMLTextAreaElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLTextAreaElement::create_layout_node(CSS::ComputedProperties const& style)
{
return heap().allocate<Layout::TextAreaBox>(document(), *this, style);
return make_ref_counted<Layout::TextAreaBox>(document(), *this, style);
}
}

View file

@ -154,7 +154,7 @@ private:
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
void set_raw_value(Utf16String);

View file

@ -68,9 +68,9 @@ void HTMLVideoElement::attribute_changed(FlyString const& name, Optional<String>
}
}
GC::Ptr<Layout::Node> HTMLVideoElement::create_layout_node(CSS::ComputedProperties const& style)
RefPtr<Layout::Node> HTMLVideoElement::create_layout_node(CSS::ComputedProperties const& style)
{
return heap().allocate<Layout::VideoBox>(document(), *this, style);
return make_ref_counted<Layout::VideoBox>(document(), *this, style);
}
Layout::VideoBox* HTMLVideoElement::layout_node()

View file

@ -74,7 +74,7 @@ private:
// https://html.spec.whatwg.org/multipage/media.html#the-video-element:dimension-attributes
virtual bool supports_dimension_attributes() const override { return true; }
virtual GC::Ptr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
WebIDL::ExceptionOr<void> determine_element_poster_frame(Optional<String> const& poster);

View file

@ -312,7 +312,7 @@ CSSPixelRect IntersectionObserver::root_intersection_rectangle() const
document = &intersection_root.get<GC::Ref<DOM::Element>>()->document();
}
if (m_document && document->origin().is_same_origin(m_document->origin())) {
if (auto layout_node = intersection_root.visit([&](auto& node) -> GC::Ptr<Layout::Node> { return node->layout_node(); })) {
if (auto layout_node = intersection_root.visit([&](auto& node) -> Layout::Node* { return node->layout_node(); })) {
rect.inflate(
m_root_margin[0].to_px(*layout_node, rect.height()),
m_root_margin[1].to_px(*layout_node, rect.width()),

View file

@ -10,8 +10,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(AudioBox);
AudioBox::AudioBox(DOM::Document& document, DOM::Element& element, CSS::ComputedProperties const& style)
: ReplacedBox(document, element, style)
{

View file

@ -17,6 +17,8 @@ class AudioBox final : public ReplacedBox {
GC_DECLARE_ALLOCATOR(AudioBox);
public:
AudioBox(DOM::Document&, DOM::Element&, CSS::ComputedProperties const&);
HTML::HTMLAudioElement& dom_node();
HTML::HTMLAudioElement const& dom_node() const;
@ -28,7 +30,6 @@ private:
// Treat the audio element as if it was not a replaced element, sizing based on its content.
// Thus, it can fit to the shadow DOM controls, instead of having a hardcoded height.
virtual bool has_auto_content_box_size() const override { return false; }
AudioBox(DOM::Document&, DOM::Element&, CSS::ComputedProperties const&);
};
}

View file

@ -9,8 +9,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(BlockContainer);
BlockContainer::BlockContainer(DOM::Document& document, DOM::Node* node, CSS::ComputedProperties const& style)
: Box(document, node, style)
{

View file

@ -13,8 +13,7 @@ namespace Web::Layout {
// https://www.w3.org/TR/css-display/#block-container
class BlockContainer : public Box {
GC_CELL(BlockContainer, Box);
GC_DECLARE_ALLOCATOR(BlockContainer);
LAYOUT_NODE(BlockContainer, Box);
public:
BlockContainer(DOM::Document&, DOM::Node*, CSS::ComputedProperties const&);

View file

@ -773,7 +773,8 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
auto static_position_x = CSSPixels(0);
auto static_position_y = m_y_offset_of_current_block_container.value();
if (box.display_before_box_type_transformation().is_inline_outside()) {
auto const* sibling = as_if<Box>(box.previous_sibling());
auto sibling_ref = box.previous_sibling();
auto const* sibling = as_if<Box>(sibling_ref.ptr());
if (sibling && sibling->is_anonymous() && sibling->children_are_inline()) {
auto const& sibling_state = m_state.get(*sibling);
if (auto const& inline_end_static_position_rect = sibling_state.inline_end_static_position_rect(); inline_end_static_position_rect.has_value()) {
@ -1576,7 +1577,7 @@ BlockFormattingContext::SpaceUsedAndContainingMarginForFloats BlockFormattingCon
+ floating_box.used_values.content_width()
+ floating_box.used_values.margin_box_right();
space_and_containing_margin.left_total_containing_margin = offset_from_containing_block_chain_margins_between_here_and_root;
space_and_containing_margin.matching_left_float_box = floating_box.box;
space_and_containing_margin.matching_left_float_box = &floating_box.box;
break;
}
}
@ -1593,7 +1594,7 @@ BlockFormattingContext::SpaceUsedAndContainingMarginForFloats BlockFormattingCon
space_and_containing_margin.right_used_space = floating_box.offset_from_edge
+ floating_box.used_values.margin_box_left();
space_and_containing_margin.right_total_containing_margin = offset_from_containing_block_chain_margins_between_here_and_root;
space_and_containing_margin.matching_right_float_box = floating_box.box;
space_and_containing_margin.matching_right_float_box = &floating_box.box;
break;
}
}
@ -1648,7 +1649,7 @@ CSSPixels BlockFormattingContext::greatest_child_width(Box const& box) const
CSSPixels extra_width_from_left_floats = 0;
for (auto& left_float : m_left_floats.all_boxes) {
// NOTE: Floats directly affect the automatic size of their containing block, but only indirectly anything above in the tree.
if (left_float->box->containing_block() != &box)
if (left_float->box.containing_block() != &box)
continue;
if (line_top < left_float->bottom_margin_edge && line_bottom > left_float->top_margin_edge) {
extra_width_from_left_floats = max(extra_width_from_left_floats, left_float->offset_from_edge + left_float->used_values.content_width() + left_float->used_values.margin_box_right());
@ -1657,7 +1658,7 @@ CSSPixels BlockFormattingContext::greatest_child_width(Box const& box) const
CSSPixels extra_width_from_right_floats = 0;
for (auto& right_float : m_right_floats.all_boxes) {
// NOTE: Floats directly affect the automatic size of their containing block, but only indirectly anything above in the tree.
if (right_float->box->containing_block() != &box)
if (right_float->box.containing_block() != &box)
continue;
if (line_top < right_float->bottom_margin_edge && line_bottom > right_float->top_margin_edge) {
extra_width_from_right_floats = max(extra_width_from_right_floats, right_float->offset_from_edge + right_float->used_values.margin_box_left());

View file

@ -77,7 +77,7 @@ public:
void reset_margin_state() { m_margin_state.reset(); }
struct FloatingBox {
GC::Ref<Box const> box;
Box const& box;
LayoutState::UsedValues& used_values;

View file

@ -15,8 +15,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(Box);
Box::Box(DOM::Document& document, DOM::Node* node, CSS::ComputedProperties const& style)
: NodeWithStyleAndBoxModelMetrics(document, node, style)
{
@ -42,12 +40,6 @@ CSS::SizeWithAspectRatio Box::auto_content_box_size() const
return compute_auto_content_box_size();
}
void Box::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_contained_abspos_children);
}
RefPtr<Painting::Paintable> Box::create_paintable() const
{
return Painting::PaintableBox::create(*this);

View file

@ -55,11 +55,9 @@ public:
virtual RefPtr<Painting::Paintable> create_paintable() const override;
void add_contained_abspos_child(GC::Ref<Node> child) { m_contained_abspos_children.append(child); }
void add_contained_abspos_child(Node& child) { m_contained_abspos_children.append(child.make_weak_ptr()); }
void clear_contained_abspos_children() { m_contained_abspos_children.clear(); }
Vector<GC::Ref<Node>> const& contained_abspos_children() const { return m_contained_abspos_children; }
virtual void visit_edges(Cell::Visitor&) override;
Vector<WeakPtr<Node>> const& contained_abspos_children() const { return m_contained_abspos_children; }
IntrinsicSizes& cached_intrinsic_sizes() const
{
@ -69,15 +67,16 @@ public:
}
void reset_cached_intrinsic_sizes() const { m_cached_intrinsic_sizes.clear(); }
protected:
Box(DOM::Document&, DOM::Node*, CSS::ComputedProperties const&);
Box(DOM::Document&, DOM::Node*, NonnullOwnPtr<CSS::ComputedValues>);
protected:
virtual CSS::SizeWithAspectRatio compute_auto_content_box_size() const { return natural_size(); }
private:
virtual bool is_box() const final { return true; }
Vector<GC::Ref<Node>> m_contained_abspos_children;
Vector<WeakPtr<Node>> m_contained_abspos_children;
OwnPtr<IntrinsicSizes> mutable m_cached_intrinsic_sizes;
};

View file

@ -10,8 +10,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(BreakNode);
BreakNode::BreakNode(DOM::Document& document, HTML::HTMLBRElement& element, CSS::ComputedProperties const& style)
: Layout::NodeWithStyleAndBoxModelMetrics(document, &element, style)
{

View file

@ -12,8 +12,7 @@
namespace Web::Layout {
class BreakNode final : public NodeWithStyleAndBoxModelMetrics {
GC_CELL(BreakNode, NodeWithStyleAndBoxModelMetrics);
GC_DECLARE_ALLOCATOR(BreakNode);
LAYOUT_NODE(BreakNode, NodeWithStyleAndBoxModelMetrics);
public:
BreakNode(DOM::Document&, HTML::HTMLBRElement&, CSS::ComputedProperties const&);

View file

@ -9,8 +9,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(CanvasBox);
CanvasBox::CanvasBox(DOM::Document& document, HTML::HTMLCanvasElement& element, CSS::ComputedProperties const& style)
: ReplacedBox(document, element, style)
{

View file

@ -12,8 +12,7 @@
namespace Web::Layout {
class CanvasBox final : public ReplacedBox {
GC_CELL(CanvasBox, ReplacedBox);
GC_DECLARE_ALLOCATOR(CanvasBox);
LAYOUT_NODE(CanvasBox, ReplacedBox);
public:
CanvasBox(DOM::Document&, HTML::HTMLCanvasElement&, CSS::ComputedProperties const&);

View file

@ -11,8 +11,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(CheckBox);
CheckBox::CheckBox(DOM::Document& document, HTML::HTMLInputElement& element, CSS::ComputedProperties const& style)
: ReplacedBox(document, element, style)
{

View file

@ -12,8 +12,7 @@
namespace Web::Layout {
class CheckBox final : public ReplacedBox {
GC_CELL(CheckBox, ReplacedBox);
GC_DECLARE_ALLOCATOR(CheckBox);
LAYOUT_NODE(CheckBox, ReplacedBox);
public:
CheckBox(DOM::Document&, HTML::HTMLInputElement&, CSS::ComputedProperties const&);

View file

@ -10,8 +10,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(FieldSetBox);
FieldSetBox::FieldSetBox(DOM::Document& document, DOM::Element& element, CSS::ComputedProperties const& style)
: BlockContainer(document, &element, style)
{

View file

@ -14,8 +14,7 @@
namespace Web::Layout {
class FieldSetBox final : public BlockContainer {
GC_CELL(FieldSetBox, BlockContainer);
GC_DECLARE_ALLOCATOR(FieldSetBox);
LAYOUT_NODE(FieldSetBox, BlockContainer);
public:
FieldSetBox(DOM::Document&, DOM::Element&, CSS::ComputedProperties const&);

View file

@ -145,9 +145,9 @@ void FlexFormattingContext::run(AvailableSpace const& available_space)
// different formula with intrinsic sizes), and items with aspect-ratio (transferred_size_suggestion
// complicates the calculation).
auto can_skip_automatic_minimum_size_for_item = [&](FlexItem const& item) -> bool {
if (item.box->is_scroll_container())
if (item.box.is_scroll_container())
return false;
if (item.box->is_replaced_box() || item.box->has_preferred_aspect_ratio())
if (item.box.is_replaced_box() || item.box.has_preferred_aspect_ratio())
return false;
if (!item.used_flex_basis_is_definite)
return false;
@ -308,51 +308,51 @@ void FlexFormattingContext::populate_specified_margins(FlexItem& item, CSS::Flex
{
auto width_of_containing_block = m_flex_container_state.content_width();
item.used_values.padding_left = item.box->computed_values().padding().left().to_px_or_zero(item.box, width_of_containing_block);
item.used_values.padding_right = item.box->computed_values().padding().right().to_px_or_zero(item.box, width_of_containing_block);
item.used_values.padding_top = item.box->computed_values().padding().top().to_px_or_zero(item.box, width_of_containing_block);
item.used_values.padding_bottom = item.box->computed_values().padding().bottom().to_px_or_zero(item.box, width_of_containing_block);
item.used_values.padding_left = item.box.computed_values().padding().left().to_px_or_zero(item.box, width_of_containing_block);
item.used_values.padding_right = item.box.computed_values().padding().right().to_px_or_zero(item.box, width_of_containing_block);
item.used_values.padding_top = item.box.computed_values().padding().top().to_px_or_zero(item.box, width_of_containing_block);
item.used_values.padding_bottom = item.box.computed_values().padding().bottom().to_px_or_zero(item.box, width_of_containing_block);
if (main_axis_is_horizontal()) {
item.borders.main_before = item.box->computed_values().border_left().width;
item.borders.main_after = item.box->computed_values().border_right().width;
item.borders.cross_before = item.box->computed_values().border_top().width;
item.borders.cross_after = item.box->computed_values().border_bottom().width;
item.borders.main_before = item.box.computed_values().border_left().width;
item.borders.main_after = item.box.computed_values().border_right().width;
item.borders.cross_before = item.box.computed_values().border_top().width;
item.borders.cross_after = item.box.computed_values().border_bottom().width;
item.padding.main_before = item.box->computed_values().padding().left().to_px_or_zero(item.box, width_of_containing_block);
item.padding.main_after = item.box->computed_values().padding().right().to_px_or_zero(item.box, width_of_containing_block);
item.padding.cross_before = item.box->computed_values().padding().top().to_px_or_zero(item.box, width_of_containing_block);
item.padding.cross_after = item.box->computed_values().padding().bottom().to_px_or_zero(item.box, width_of_containing_block);
item.padding.main_before = item.box.computed_values().padding().left().to_px_or_zero(item.box, width_of_containing_block);
item.padding.main_after = item.box.computed_values().padding().right().to_px_or_zero(item.box, width_of_containing_block);
item.padding.cross_before = item.box.computed_values().padding().top().to_px_or_zero(item.box, width_of_containing_block);
item.padding.cross_after = item.box.computed_values().padding().bottom().to_px_or_zero(item.box, width_of_containing_block);
item.margins.main_before = item.box->computed_values().margin().left().to_px_or_zero(item.box, width_of_containing_block);
item.margins.main_after = item.box->computed_values().margin().right().to_px_or_zero(item.box, width_of_containing_block);
item.margins.cross_before = item.box->computed_values().margin().top().to_px_or_zero(item.box, width_of_containing_block);
item.margins.cross_after = item.box->computed_values().margin().bottom().to_px_or_zero(item.box, width_of_containing_block);
item.margins.main_before = item.box.computed_values().margin().left().to_px_or_zero(item.box, width_of_containing_block);
item.margins.main_after = item.box.computed_values().margin().right().to_px_or_zero(item.box, width_of_containing_block);
item.margins.cross_before = item.box.computed_values().margin().top().to_px_or_zero(item.box, width_of_containing_block);
item.margins.cross_after = item.box.computed_values().margin().bottom().to_px_or_zero(item.box, width_of_containing_block);
item.margins.main_before_is_auto = item.box->computed_values().margin().left().is_auto();
item.margins.main_after_is_auto = item.box->computed_values().margin().right().is_auto();
item.margins.cross_before_is_auto = item.box->computed_values().margin().top().is_auto();
item.margins.cross_after_is_auto = item.box->computed_values().margin().bottom().is_auto();
item.margins.main_before_is_auto = item.box.computed_values().margin().left().is_auto();
item.margins.main_after_is_auto = item.box.computed_values().margin().right().is_auto();
item.margins.cross_before_is_auto = item.box.computed_values().margin().top().is_auto();
item.margins.cross_after_is_auto = item.box.computed_values().margin().bottom().is_auto();
} else {
item.borders.main_before = item.box->computed_values().border_top().width;
item.borders.main_after = item.box->computed_values().border_bottom().width;
item.borders.cross_before = item.box->computed_values().border_left().width;
item.borders.cross_after = item.box->computed_values().border_right().width;
item.borders.main_before = item.box.computed_values().border_top().width;
item.borders.main_after = item.box.computed_values().border_bottom().width;
item.borders.cross_before = item.box.computed_values().border_left().width;
item.borders.cross_after = item.box.computed_values().border_right().width;
item.padding.main_before = item.used_values.padding_top;
item.padding.main_after = item.used_values.padding_bottom;
item.padding.cross_before = item.used_values.padding_left;
item.padding.cross_after = item.used_values.padding_right;
item.margins.main_before = item.box->computed_values().margin().top().to_px_or_zero(item.box, width_of_containing_block);
item.margins.main_after = item.box->computed_values().margin().bottom().to_px_or_zero(item.box, width_of_containing_block);
item.margins.cross_before = item.box->computed_values().margin().left().to_px_or_zero(item.box, width_of_containing_block);
item.margins.cross_after = item.box->computed_values().margin().right().to_px_or_zero(item.box, width_of_containing_block);
item.margins.main_before = item.box.computed_values().margin().top().to_px_or_zero(item.box, width_of_containing_block);
item.margins.main_after = item.box.computed_values().margin().bottom().to_px_or_zero(item.box, width_of_containing_block);
item.margins.cross_before = item.box.computed_values().margin().left().to_px_or_zero(item.box, width_of_containing_block);
item.margins.cross_after = item.box.computed_values().margin().right().to_px_or_zero(item.box, width_of_containing_block);
item.margins.main_before_is_auto = item.box->computed_values().margin().top().is_auto();
item.margins.main_after_is_auto = item.box->computed_values().margin().bottom().is_auto();
item.margins.cross_before_is_auto = item.box->computed_values().margin().left().is_auto();
item.margins.cross_after_is_auto = item.box->computed_values().margin().right().is_auto();
item.margins.main_before_is_auto = item.box.computed_values().margin().top().is_auto();
item.margins.main_after_is_auto = item.box.computed_values().margin().bottom().is_auto();
item.margins.cross_before_is_auto = item.box.computed_values().margin().left().is_auto();
item.margins.cross_after_is_auto = item.box.computed_values().margin().right().is_auto();
}
}
@ -589,7 +589,7 @@ void FlexFormattingContext::determine_available_space_for_items(AvailableSpace c
// https://drafts.csswg.org/css-flexbox-1/#propdef-flex-basis
CSS::FlexBasis FlexFormattingContext::used_flex_basis_for_item(FlexItem const& item) const
{
auto flex_basis = item.box->computed_values().flex_basis();
auto flex_basis = item.box.computed_values().flex_basis();
if (flex_basis.has<CSS::Size>() && flex_basis.get<CSS::Size>().is_auto()) {
// https://drafts.csswg.org/css-flexbox-1/#valdef-flex-basis-auto
@ -709,7 +709,7 @@ void FlexFormattingContext::determine_flex_base_size(FlexItem& item)
// AD-HOC: If we're sizing the flex container under a min-content constraint in the main axis,
// non-replaced flex items resolve percentages in the main axis to 0.
// https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution
if (item.box->is_replaced_box()
if (item.box.is_replaced_box()
&& m_available_space_for_items->main.is_min_content()
&& computed_main_size(item.box).contains_percentage()) {
return CSSPixels(0);
@ -719,7 +719,7 @@ void FlexFormattingContext::determine_flex_base_size(FlexItem& item)
// - an intrinsic aspect ratio,
// - a used flex basis of content, and
// - a definite cross size,
if (item.box->has_preferred_aspect_ratio()
if (item.box.has_preferred_aspect_ratio()
&& item.used_flex_basis->has<CSS::FlexBasisContent>()
&& has_definite_cross_size(item)) {
// flex_base_size is calculated from definite cross size and intrinsic aspect ratio
@ -730,7 +730,7 @@ void FlexFormattingContext::determine_flex_base_size(FlexItem& item)
return adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(
item.box,
calculate_main_size_from_cross_size_and_aspect_ratio(inner_cross_size(item), item.box->preferred_aspect_ratio().value()),
calculate_main_size_from_cross_size_and_aspect_ratio(inner_cross_size(item), item.box.preferred_aspect_ratio().value()),
computed_cross_min_size(item.box),
computed_cross_max_size(item.box));
}
@ -800,7 +800,7 @@ void FlexFormattingContext::determine_flex_base_size(FlexItem& item)
// - using stretch-fit main size if the flex basis is indefinite, there is no
// intrinsic size and no cross size to resolve the ratio against.
// - in response to cross size min/max constraints.
auto auto_size = item.box->auto_content_box_size();
auto auto_size = item.box.auto_content_box_size();
if (auto_size.has_aspect_ratio()) {
if (!item.used_flex_basis_is_definite && !auto_size.has_width() && !auto_size.has_height() && !has_definite_cross_size(item) && has_definite_main_size(m_flex_container_state)) {
item.flex_base_size = inner_main_size(m_flex_container_state);
@ -817,7 +817,7 @@ CSSPixels FlexFormattingContext::automatic_minimum_size(FlexItem const& item) co
// To provide a more reasonable default minimum size for flex items,
// the used value of a main axis automatic minimum size on a flex item that is not a scroll container is its content-based minimum size;
// for scroll containers the automatic minimum size is zero, as usual.
if (!item.box->is_scroll_container())
if (!item.box.is_scroll_container())
return content_based_minimum_size(item);
return 0;
}
@ -839,7 +839,7 @@ CSSPixels FlexFormattingContext::content_size_suggestion(FlexItem const& item) c
{
auto suggestion = calculate_min_content_main_size(item);
if (item.box->has_preferred_aspect_ratio()) {
if (item.box.has_preferred_aspect_ratio()) {
suggestion = adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(item.box, suggestion, computed_cross_min_size(item.box), computed_cross_max_size(item.box));
}
@ -852,8 +852,8 @@ Optional<CSSPixels> FlexFormattingContext::transferred_size_suggestion(FlexItem
// If the item has a preferred aspect ratio and its preferred cross size is definite,
// then the transferred size suggestion is that size
// (clamped by its minimum and maximum cross sizes if they are definite), converted through the aspect ratio.
if (item.box->has_preferred_aspect_ratio() && has_definite_cross_size(item)) {
auto aspect_ratio = item.box->preferred_aspect_ratio().value();
if (item.box.has_preferred_aspect_ratio() && has_definite_cross_size(item)) {
auto aspect_ratio = item.box.preferred_aspect_ratio().value();
return adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(
item.box,
calculate_main_size_from_cross_size_and_aspect_ratio(inner_cross_size(item), aspect_ratio),
@ -871,7 +871,7 @@ CSSPixels FlexFormattingContext::content_based_minimum_size(FlexItem const& item
auto unclamped_size = [&] {
// The content-based minimum size of a flex item differs depending on whether the flex item is replaced or not:
// -> For replaced elements
if (item.box->is_replaced_box()) {
if (item.box.is_replaced_box()) {
// Use the smaller of the content size suggestion and the transferred size suggestion (if one exists),
// capped by the specified size suggestion (if one exists).
auto size = content_size_suggestion(item);
@ -1005,9 +1005,9 @@ void FlexFormattingContext::resolve_flexible_lengths_for_line(FlexLine& line)
for (FlexItem& item : line.items) {
if (used_flex_factor == FlexFactor::FlexGrowFactor) {
item.flex_factor = item.box->computed_values().flex_grow();
item.flex_factor = item.box.computed_values().flex_grow();
} else if (used_flex_factor == FlexFactor::FlexShrinkFactor) {
item.flex_factor = item.box->computed_values().flex_shrink();
item.flex_factor = item.box.computed_values().flex_shrink();
}
// Freeze, setting its target main size to its hypothetical main size…
// - any item that has a flex factor of zero
@ -1239,7 +1239,7 @@ void FlexFormattingContext::save_flex_layout_data() const
cross_start = cross_start.has_value() ? min(cross_start.value(), item_cross_start) : item_cross_start;
FlexLayoutItem layout_item;
if (auto* dom_node = item.box->dom_node())
if (auto* dom_node = item.box.dom_node())
layout_item.node_id = dom_node->unique_id();
layout_item.main_axis_direction = main_axis_direction;
layout_item.cross_axis_direction = cross_axis_direction;
@ -1254,12 +1254,12 @@ void FlexFormattingContext::save_flex_layout_data() const
? FlexLayoutClampState::ClampedToMin
: item.is_max_violation ? FlexLayoutClampState::ClampedToMax
: FlexLayoutClampState::Unclamped;
layout_item.flex_basis = serialize_flex_basis(item.box->computed_values().flex_basis());
layout_item.flex_basis = serialize_flex_basis(item.box.computed_values().flex_basis());
layout_item.main_size_property = MUST(String::formatted("{}", computed_main_size(item.box)));
layout_item.main_min_size_property = MUST(String::formatted("{}", computed_main_min_size(item.box)));
layout_item.main_max_size_property = MUST(String::formatted("{}", computed_main_max_size(item.box)));
layout_item.flex_grow = item.box->computed_values().flex_grow();
layout_item.flex_shrink = item.box->computed_values().flex_shrink();
layout_item.flex_grow = item.box.computed_values().flex_grow();
layout_item.flex_shrink = item.box.computed_values().flex_shrink();
layout_line.items.append(move(layout_item));
}
@ -1287,10 +1287,10 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem&
return;
}
if (item.box->has_preferred_aspect_ratio()) {
auto auto_size = item.box->auto_content_box_size();
if (item.box.has_preferred_aspect_ratio()) {
auto auto_size = item.box.auto_content_box_size();
if (item.used_flex_basis_is_definite || (auto_size.has_width() && auto_size.has_height())) {
item.hypothetical_cross_size = css_clamp(calculate_cross_size_from_main_size_and_aspect_ratio(item.main_size.value(), item.box->preferred_aspect_ratio().value()), clamp_min, clamp_max);
item.hypothetical_cross_size = css_clamp(calculate_cross_size_from_main_size_and_aspect_ratio(item.main_size.value(), item.box.preferred_aspect_ratio().value()), clamp_min, clamp_max);
return;
}
item.hypothetical_cross_size = css_clamp(inner_cross_size(m_flex_container_state), clamp_min, clamp_max);
@ -1604,7 +1604,7 @@ void FlexFormattingContext::dump_items() const
dbgln("{} flex-line #{}:", flex_container().debug_description(), i);
for (size_t j = 0; j < m_flex_lines[i].items.size(); ++j) {
auto& item = m_flex_lines[i].items[j];
dbgln("{} flex-item #{}: {} (main:{}, cross:{})", flex_container().debug_description(), j, item.box->debug_description(), item.main_size.value_or(-1), item.cross_size.value_or(-1));
dbgln("{} flex-item #{}: {} (main:{}, cross:{})", flex_container().debug_description(), j, item.box.debug_description(), item.main_size.value_or(-1), item.cross_size.value_or(-1));
}
}
}
@ -1880,15 +1880,15 @@ void FlexFormattingContext::copy_dimensions_from_flex_items_to_boxes()
for (auto& item : m_flex_items) {
auto const& box = item.box;
item.used_values.margin_left = box->computed_values().margin().left().to_px_or_zero(box, m_flex_container_state.content_width());
item.used_values.margin_right = box->computed_values().margin().right().to_px_or_zero(box, m_flex_container_state.content_width());
item.used_values.margin_top = box->computed_values().margin().top().to_px_or_zero(box, m_flex_container_state.content_width());
item.used_values.margin_bottom = box->computed_values().margin().bottom().to_px_or_zero(box, m_flex_container_state.content_width());
item.used_values.margin_left = box.computed_values().margin().left().to_px_or_zero(box, m_flex_container_state.content_width());
item.used_values.margin_right = box.computed_values().margin().right().to_px_or_zero(box, m_flex_container_state.content_width());
item.used_values.margin_top = box.computed_values().margin().top().to_px_or_zero(box, m_flex_container_state.content_width());
item.used_values.margin_bottom = box.computed_values().margin().bottom().to_px_or_zero(box, m_flex_container_state.content_width());
item.used_values.border_left = box->computed_values().border_left().width;
item.used_values.border_right = box->computed_values().border_right().width;
item.used_values.border_top = box->computed_values().border_top().width;
item.used_values.border_bottom = box->computed_values().border_bottom().width;
item.used_values.border_left = box.computed_values().border_left().width;
item.used_values.border_right = box.computed_values().border_right().width;
item.used_values.border_top = box.computed_values().border_top().width;
item.used_values.border_bottom = box.computed_values().border_bottom().width;
set_main_size(item, item.main_size.value());
set_cross_size(item, item.cross_size.value());
@ -1946,10 +1946,10 @@ CSSPixels FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container
CSSPixels result = contribution - outer_flex_base_size;
if (result > 0) {
if (item.box->computed_values().flex_grow() >= 1) {
result.scale_by(1 / item.box->computed_values().flex_grow());
if (item.box.computed_values().flex_grow() >= 1) {
result.scale_by(1 / item.box.computed_values().flex_grow());
} else {
result.scale_by(item.box->computed_values().flex_grow());
result.scale_by(item.box.computed_values().flex_grow());
}
} else if (result < 0) {
if (item.scaled_flex_shrink_factor == 0)
@ -1978,8 +1978,8 @@ CSSPixels FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container
float sum_of_flex_shrink_factors = 0;
for (auto& item : flex_line.items) {
greatest_desired_flex_fraction = max(greatest_desired_flex_fraction, item.desired_flex_fraction);
sum_of_flex_grow_factors += item.box->computed_values().flex_grow();
sum_of_flex_shrink_factors += item.box->computed_values().flex_shrink();
sum_of_flex_grow_factors += item.box.computed_values().flex_grow();
sum_of_flex_shrink_factors += item.box.computed_values().flex_shrink();
}
float chosen_flex_fraction = greatest_desired_flex_fraction;
@ -2005,7 +2005,7 @@ CSSPixels FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container
for (auto& item : flex_line.items) {
double product = 0;
if (item.desired_flex_fraction > 0)
product = flex_line.chosen_flex_fraction * static_cast<double>(item.box->computed_values().flex_grow());
product = flex_line.chosen_flex_fraction * static_cast<double>(item.box.computed_values().flex_grow());
else if (item.desired_flex_fraction < 0)
product = flex_line.chosen_flex_fraction * item.scaled_flex_shrink_factor;
auto result = item.flex_base_size + CSSPixels::nearest_value_for(product);
@ -2184,7 +2184,7 @@ CSSPixels FlexFormattingContext::calculate_cross_min_content_contribution(FlexIt
return cross_axis_is_horizontal() ? get_pixel_width(item, computed_cross_size(item.box)) : get_pixel_height(item, computed_cross_size(item.box));
}();
if (cross_size_auto && item.box->has_preferred_aspect_ratio())
if (cross_size_auto && item.box.has_preferred_aspect_ratio())
size = adjust_cross_size_through_aspect_ratio_for_main_size_min_max_constraints(item.box, size, computed_main_min_size(item.box), computed_main_max_size(item.box));
auto const& computed_min_size = this->computed_cross_min_size(item.box);
@ -2207,7 +2207,7 @@ CSSPixels FlexFormattingContext::calculate_cross_max_content_contribution(FlexIt
return cross_axis_is_horizontal() ? get_pixel_width(item, computed_cross_size(item.box)) : get_pixel_height(item, computed_cross_size(item.box));
}();
if (cross_size_auto && item.box->has_preferred_aspect_ratio())
if (cross_size_auto && item.box.has_preferred_aspect_ratio())
size = adjust_cross_size_through_aspect_ratio_for_main_size_min_max_constraints(item.box, size, computed_main_min_size(item.box), computed_main_max_size(item.box));
auto const& computed_min_size = this->computed_cross_min_size(item.box);
@ -2223,7 +2223,7 @@ CSSPixels FlexFormattingContext::calculate_cross_max_content_contribution(FlexIt
CSSPixels FlexFormattingContext::calculate_width_to_use_when_determining_intrinsic_height_of_item(FlexItem const& item) const
{
auto const& box = *item.box;
auto const& box = item.box;
auto computed_width = box.computed_values().width();
auto const& computed_min_width = box.computed_values().min_width();
auto const& computed_max_width = box.computed_values().max_width();

View file

@ -55,7 +55,7 @@ private:
};
struct FlexItem {
GC::Ref<Box> box;
Box& box;
LayoutState::UsedValues& used_values;
Optional<CSS::FlexBasis> used_flex_basis {};
bool used_flex_basis_is_definite { false };

View file

@ -1468,12 +1468,12 @@ static Optional<CSSPixelRect> compute_inline_containing_block_rect(InlineNode co
}
}
for (auto const* child = node.first_child(); child; child = child->next_sibling()) {
for (auto child = node.first_child(); child; child = child->next_sibling()) {
if (child->is_absolutely_positioned() || child->is_floating())
continue;
auto const* child_used_values = state.try_get(*child);
auto child_offset = child_used_values ? offset + child_used_values->offset : offset;
auto const* box_child = as_if<Box>(child);
auto const* box_child = as_if<Box>(child.ptr());
if (box_child && !box_child->is_anonymous()) {
auto const* dom = box_child->dom_node();
if (!dom || !inline_dom_node->is_inclusive_ancestor_of(*dom))
@ -1752,6 +1752,8 @@ void FormattingContext::layout_absolutely_positioned_children()
if (m_layout_mode != LayoutMode::Normal)
return;
for (auto& child : context_box().contained_abspos_children()) {
if (!child)
continue;
auto& box = as<Box>(*child);
layout_absolutely_positioned_element(box);
}
@ -1879,7 +1881,7 @@ void FormattingContext::layout_absolutely_positioned_element(Box& box)
auto static_position = m_state.get(box).static_position();
auto const* static_position_cb = box.static_position_containing_block();
auto actual_containing_block = box.containing_block();
if (static_position_cb && static_position_cb != actual_containing_block.ptr()) {
if (static_position_cb && static_position_cb != actual_containing_block) {
auto offset = m_state.get(*static_position_cb).cumulative_offset() - m_state.get(*actual_containing_block).cumulative_offset();
static_position += offset;
}
@ -2535,7 +2537,7 @@ Box const* FormattingContext::box_child_to_derive_baseline_from(Box const& box)
if (!box.has_children() || box.children_are_inline())
return nullptr;
// Find the last in-flow child that has a baseline (either directly via line boxes, or via its descendants).
for (auto const* child = box.last_child(); child; child = child->previous_sibling()) {
for (auto child = box.last_child(); child; child = child->previous_sibling()) {
auto const* child_box = as_if<Box>(*child);
if (!child_box)
continue;

View file

@ -194,8 +194,8 @@ protected:
// Each block in the containing chain adds its own margin and we store the total here.
CSSPixels left_total_containing_margin;
CSSPixels right_total_containing_margin;
GC::Ptr<Box const> matching_left_float_box;
GC::Ptr<Box const> matching_right_float_box;
Box const* matching_left_float_box { nullptr };
Box const* matching_right_float_box { nullptr };
};
struct ShrinkToFitResult {
@ -239,7 +239,7 @@ protected:
LayoutMode m_layout_mode;
FormattingContext* m_parent { nullptr };
GC::Ref<Box const> m_context_box;
Box const& m_context_box;
LayoutState& m_state;
};
@ -253,7 +253,7 @@ public:
for (int i = 0; i < s_depth; ++i)
indent_builder.append("| "sv);
auto intrinsic_marker = fc.m_layout_mode == LayoutMode::IntrinsicSizing ? " [intrinsic]"sv : ""sv;
dbgln("{}|- {} <{}> run({}){}", indent_builder.string_view(), FormattingContext::type_name(fc.m_type), fc.m_context_box->debug_description(), available_space, intrinsic_marker);
dbgln("{}|- {} <{}> run({}){}", indent_builder.string_view(), FormattingContext::type_name(fc.m_type), fc.m_context_box.debug_description(), available_space, intrinsic_marker);
++s_depth;
}

View file

@ -300,7 +300,7 @@ GridFormattingContext const* GridFormattingContext::parent_grid_formatting_conte
GridItem const* GridFormattingContext::grid_item_for_box(Box const& box) const
{
for (auto const& item : m_grid_items) {
if (item.box.ptr() == &box)
if (&item.box == &box)
return &item;
}
return nullptr;
@ -316,7 +316,7 @@ GridItem const* GridFormattingContext::parent_grid_item() const
bool GridFormattingContext::grid_item_is_subgridded_in_axis(GridItem const& item, GridDimension dimension) const
{
if (!item.box->display().is_grid_inside())
if (!item.box.display().is_grid_inside())
return false;
auto const& grid_template = dimension == GridDimension::Column
@ -1942,7 +1942,7 @@ void GridFormattingContext::place_grid_items()
// flex items), which are then assigned to predefined areas in the grid. They can be explicitly
// placed using coordinates through the grid-placement properties or implicitly placed into
// empty areas using auto-placement.
HashMap<int, Vector<GC::Ref<Box const>>> order_item_bucket;
HashMap<int, Vector<Box const&>> order_item_bucket;
grid_container().for_each_child_of_type<Box>([&](Box& child_box) {
if (can_skip_is_anonymous_text_run(child_box))
return IterationDecision::Continue;
@ -1973,7 +1973,7 @@ void GridFormattingContext::place_grid_items()
auto& boxes_to_place = order_item_bucket.get(key).value();
for (size_t i = 0; i < boxes_to_place.size(); i++) {
auto const& child_box = boxes_to_place[i];
auto const& computed_values = child_box->computed_values();
auto const& computed_values = child_box.computed_values();
if (is_auto_positioned_track(computed_values.grid_row_start(), computed_values.grid_row_end())
|| is_auto_positioned_track(computed_values.grid_column_start(), computed_values.grid_column_end()))
continue;
@ -1988,7 +1988,7 @@ void GridFormattingContext::place_grid_items()
auto& boxes_to_place = order_item_bucket.get(key).value();
for (size_t i = 0; i < boxes_to_place.size(); i++) {
auto const& child_box = boxes_to_place[i];
auto const& computed_values = child_box->computed_values();
auto const& computed_values = child_box.computed_values();
if (is_auto_positioned_track(computed_values.grid_row_start(), computed_values.grid_row_end()))
continue;
place_item_with_row_position(child_box);
@ -2037,7 +2037,7 @@ void GridFormattingContext::place_grid_items()
auto& boxes_to_place = order_item_bucket.get(key).value();
for (size_t i = 0; i < boxes_to_place.size(); i++) {
auto const& child_box = boxes_to_place[i];
auto const& computed_values = child_box->computed_values();
auto const& computed_values = child_box.computed_values();
if (!is_auto_positioned_track(computed_values.grid_column_start(), computed_values.grid_column_end())) {
// 4.1.1 / 4.2.1: Item with definite column position.
@ -2222,7 +2222,7 @@ void GridFormattingContext::resolve_grid_item_sizes(GridDimension dimension)
result.margin_start = absorbed_margin_space;
} else if (end_is_auto) {
result.margin_end = absorbed_margin_space;
} else if (css_size.is_auto() && !item.box->is_replaced_box()) {
} else if (css_size.is_auto() && !item.box.is_replaced_box()) {
result.size += free_space_left_for_margins;
}
@ -2274,15 +2274,15 @@ void GridFormattingContext::resolve_grid_item_sizes(GridDimension dimension)
};
ItemAlignment used_alignment;
auto hint = item.box->auto_content_box_size();
auto hint = item.box.auto_content_box_size();
bool has_replaced_size_hint_in_this_axis = false;
if (dimension == GridDimension::Column) {
has_replaced_size_hint_in_this_axis = hint.has_width() || (hint.has_height() && item.box->has_preferred_aspect_ratio());
has_replaced_size_hint_in_this_axis = hint.has_width() || (hint.has_height() && item.box.has_preferred_aspect_ratio());
} else {
has_replaced_size_hint_in_this_axis = hint.has_height() || (hint.has_width() && item.box->has_preferred_aspect_ratio());
has_replaced_size_hint_in_this_axis = hint.has_height() || (hint.has_width() && item.box.has_preferred_aspect_ratio());
}
if (item.box->is_replaced_box() && has_replaced_size_hint_in_this_axis) {
if (item.box.is_replaced_box() && has_replaced_size_hint_in_this_axis) {
auto tentative_size = tentative_size_for_replaced_element(preferred_size);
used_alignment = try_compute_size(tentative_size, item.preferred_size(dimension));
} else {
@ -2303,7 +2303,7 @@ void GridFormattingContext::resolve_grid_item_sizes(GridDimension dimension)
.margin_end = item.used_margin_end(dimension),
.size = stretched_size
};
} else if (dimension == GridDimension::Column && is<TableWrapper>(*item.box)) {
} else if (dimension == GridDimension::Column && is<TableWrapper>(item.box)) {
// CSS Grid lays out each grid item into its grid-area containing block before alignment. For
// display:table, the anonymous table wrapper is the grid item, while table layout computes the inner
// table's border-box width, so resolve the wrapper width with the same grid-area basis used later.
@ -2317,11 +2317,11 @@ void GridFormattingContext::resolve_grid_item_sizes(GridDimension dimension)
CSSPixels fit_content_size;
if (dimension == GridDimension::Column) {
fit_content_size = calculate_fit_content_width(item.box, available_space);
} else if (preferred_size.is_auto() && item.box->has_preferred_aspect_ratio() && *item.box->preferred_aspect_ratio() != 0 && item.used_values.has_definite_width()) {
} else if (preferred_size.is_auto() && item.box.has_preferred_aspect_ratio() && *item.box.preferred_aspect_ratio() != 0 && item.used_values.has_definite_width()) {
// NB: When the item has a preferred aspect ratio and a definite width, resolve the
// height through the aspect ratio instead of using fit-content sizing, which would
// incorrectly use the available width (grid area width) instead of the item's width.
fit_content_size = item.used_values.content_width() / *item.box->preferred_aspect_ratio();
fit_content_size = item.used_values.content_width() / *item.box.preferred_aspect_ratio();
} else {
fit_content_size = calculate_fit_content_height(item.box, available_space);
}
@ -2578,7 +2578,7 @@ CSSPixels GridFormattingContext::grid_container_size_for_track_alignment(GridDim
void GridFormattingContext::resolve_items_box_metrics(GridDimension dimension)
{
for (auto& item : m_grid_items) {
auto& computed_values = item.box->computed_values();
auto& computed_values = item.box.computed_values();
CSSPixels containing_block_width = containing_block_size_for_item(item, GridDimension::Column);
if (dimension == GridDimension::Column) {
@ -2688,7 +2688,7 @@ CSSPixelRect GridFormattingContext::get_grid_area_rect(GridItem const& grid_item
};
auto place_into_track_formed_by_last_line_and_grid_container_padding_edge = [&](GridDimension dimension) {
VERIFY(grid_item.box->is_absolutely_positioned());
VERIFY(grid_item.box.is_absolutely_positioned());
auto const& tracks_and_gaps = dimension == GridDimension::Column ? m_grid_columns_and_gaps : m_grid_rows_and_gaps;
CSSPixels offset = 0;
for (auto const& row_track : tracks_and_gaps) {
@ -2784,7 +2784,7 @@ void GridFormattingContext::run(AvailableSpace const& available_space)
collapse_auto_fit_tracks_if_needed(GridDimension::Row);
for (auto& item : m_grid_items) {
auto& computed_values = item.box->computed_values();
auto& computed_values = item.box.computed_values();
// NOTE: As the containing blocks of grid items are created by implicit grid areas that are not present in the
// layout tree, the initial value of has_definite_width/height computed by LayoutState::UsedValues::set_node
@ -2844,7 +2844,7 @@ void GridFormattingContext::run(AvailableSpace const& available_space)
for (auto& grid_item : m_grid_items) {
auto const grid_area_rect = get_grid_area_rect(grid_item);
if (is<TableWrapper>(*grid_item.box)) {
if (is<TableWrapper>(grid_item.box)) {
// Track spacing can expand the final grid area after the earlier width pass. Recompute the wrapper width
// against that final area and store it so the real table layout resolves percentages against the grid area.
resolve_table_wrapper_grid_item_width(grid_item, grid_area_rect.width());
@ -3417,7 +3417,7 @@ bool GridFormattingContext::should_treat_preferred_size_as_auto_for_intrinsic_co
// When a non-replaced grid item's percentage preferred size contributes to
// sizing tracks in the same axis, the percentage is cyclic and behaves as
// the property's initial value for intrinsic contribution calculations.
return !item.box->is_replaced_box() && item.preferred_size(dimension).contains_percentage();
return !item.box.is_replaced_box() && item.preferred_size(dimension).contains_percentage();
}
CSSPixels GridFormattingContext::calculate_min_content_size(GridItem const& item, GridDimension dimension) const
@ -3448,7 +3448,7 @@ CSSPixels GridFormattingContext::containing_block_size_for_item(GridItem const&
Box const& GridFormattingContext::table_box_inside_table_wrapper(GridItem const& item) const
{
Optional<Box const&> table_box;
item.box->for_each_in_subtree_of_type<Box>([&](Box const& child_box) {
item.box.for_each_in_subtree_of_type<Box>([&](Box const& child_box) {
if (child_box.display().is_table_inside()) {
table_box = child_box;
return TraversalDecision::Break;
@ -3461,7 +3461,7 @@ Box const& GridFormattingContext::table_box_inside_table_wrapper(GridItem const&
void GridFormattingContext::resolve_table_wrapper_grid_item_width(GridItem& item, CSSPixels containing_block_width)
{
VERIFY(is<TableWrapper>(*item.box));
VERIFY(is<TableWrapper>(item.box));
auto table_wrapper_containing_block_width = non_cyclic_containing_block_width_for_table_wrapper(item, containing_block_width);
auto available_space = AvailableSpace {
@ -3487,7 +3487,7 @@ void GridFormattingContext::resolve_table_wrapper_grid_item_width(GridItem& item
if (!item.minimum_size(GridDimension::Column).is_auto())
table_wrapper_width = max(table_wrapper_width, calculate_inner_width(item.box, available_space.width, item.minimum_size(GridDimension::Column)));
auto const& computed_values = item.box->computed_values();
auto const& computed_values = item.box.computed_values();
item.used_values.margin_left = computed_values.margin().left().to_px_or_zero(grid_container(), table_wrapper_containing_block_width);
item.used_values.margin_right = computed_values.margin().right().to_px_or_zero(grid_container(), table_wrapper_containing_block_width);
@ -3585,7 +3585,7 @@ CSSPixels GridFormattingContext::calculate_min_content_contribution(GridItem con
// width contribution is 0 because its content can overflow and scroll horizontally.
// This does NOT apply to the row dimension — scroll containers must still contribute
// their content height, otherwise grids with height:min-content collapse rows to 0.
if (dimension == GridDimension::Column && item.box->is_scroll_container()) {
if (dimension == GridDimension::Column && item.box.is_scroll_container()) {
min_content_size = 0;
} else {
min_content_size = calculate_min_content_size(item, dimension);
@ -3738,7 +3738,7 @@ Optional<CSSPixels> GridFormattingContext::specified_size_suggestion(GridItem co
// https://www.w3.org/TR/css-grid-1/#specified-size-suggestion
// If the items preferred size in the relevant axis is definite, then the specified size suggestion is that size.
// It is otherwise undefined.
if (!item.box->is_replaced_box() && item.preferred_size(dimension).contains_percentage())
if (!item.box.is_replaced_box() && item.preferred_size(dimension).contains_percentage())
return {};
auto has_definite_preferred_size = dimension == GridDimension::Column ? item.used_values.has_definite_width() : item.used_values.has_definite_height();
@ -3757,7 +3757,7 @@ Optional<CSSPixels> GridFormattingContext::transferred_size_suggestion(GridItem
// If the item has a preferred aspect ratio and its preferred size in the opposite axis is definite, then the transferred
// size suggestion is that size (clamped by the opposite-axis minimum and maximum sizes if they are definite), converted
// through the aspect ratio. It is otherwise undefined.
if (!item.box->preferred_aspect_ratio().has_value()) {
if (!item.box.preferred_aspect_ratio().has_value()) {
return {};
}
@ -3765,7 +3765,7 @@ Optional<CSSPixels> GridFormattingContext::transferred_size_suggestion(GridItem
if (preferred_size_in_opposite_axis.is_length()) {
auto opposite_axis_size = preferred_size_in_opposite_axis.length().to_px(item.box);
// FIXME: Clamp by opposite-axis minimum and maximum sizes if they are definite
return opposite_axis_size * item.box->preferred_aspect_ratio().value();
return opposite_axis_size * item.box.preferred_aspect_ratio().value();
}
return {};
@ -3823,7 +3823,7 @@ CSSPixels GridFormattingContext::content_based_minimum_size(GridItem const& item
// against zero (and considered definite).
// FIXME: "compressible replaced element" includes more elements than is_replaced_box().
auto const& preferred_size = item.preferred_size(dimension);
if (item.box->is_replaced_box() && (preferred_size.is_percentage() || maximum_size.is_percentage())) {
if (item.box.is_replaced_box() && (preferred_size.is_percentage() || maximum_size.is_percentage())) {
// NOTE: Implements "for this purpose, any indefinite percentages in these sizes are resolved
// against zero (and considered definite)." part.
result = 0;
@ -3854,7 +3854,7 @@ CSSPixels GridFormattingContext::automatic_minimum_size(GridItem const& item, Gr
if (track.min_track_sizing_function.is_auto(available_size))
spans_auto_tracks = true;
}
if (spans_auto_tracks && !item.box->is_scroll_container() && (item_track_span == 1 || !spans_flexible_tracks)) {
if (spans_auto_tracks && !item.box.is_scroll_container() && (item_track_span == 1 || !spans_flexible_tracks)) {
return content_based_minimum_size(item, dimension);
}

View file

@ -26,7 +26,7 @@ struct GridPosition {
};
struct GridItem {
GC::Ref<Box const> box;
Box const& box;
LayoutState::UsedValues& used_values;
// Position and span are empty if the item is auto-placed which could only be the case for abspos items
@ -62,7 +62,7 @@ struct GridItem {
CSS::ComputedValues const& computed_values() const
{
return box->computed_values();
return box.computed_values();
}
CSS::Size const& minimum_size(GridDimension dimension) const

View file

@ -6,6 +6,8 @@
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/DecodedImageData.h>
#include <LibWeb/HTML/HTMLInputElement.h>
#include <LibWeb/HTML/HTMLObjectElement.h>
#include <LibWeb/Layout/ImageBox.h>
#include <LibWeb/Layout/ImageProvider.h>
#include <LibWeb/Painting/ImagePaintable.h>
@ -13,29 +15,52 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(ImageBox);
static ImageProvider const& image_provider_for_element(DOM::Element const& element)
{
if (auto const* image = as_if<HTML::HTMLImageElement>(element))
return *image;
if (auto const* input = as_if<HTML::HTMLInputElement>(element))
return *input;
if (auto const* object = as_if<HTML::HTMLObjectElement>(element))
return *object;
VERIFY_NOT_REACHED();
}
ImageBox::ImageBox(DOM::Document& document, GC::Ptr<DOM::Element> element, CSS::ComputedProperties const& style, ImageProvider const& image_provider)
: ReplacedBox(document, element, style)
, m_image_provider(image_provider)
{
VERIFY(element);
VERIFY(&image_provider == &image_provider_for_element(*element));
}
ImageBox::ImageBox(DOM::Document& document, CSS::ComputedProperties const& style, NonnullOwnPtr<ImageProvider> image_provider)
: ReplacedBox(document, nullptr, style)
, m_owned_image_provider(move(image_provider))
{
}
ImageProvider const& ImageBox::image_provider() const
{
if (m_owned_image_provider)
return *m_owned_image_provider;
auto element = dom_node();
VERIFY(element);
return image_provider_for_element(*element);
}
ImageBox::~ImageBox() = default;
void ImageBox::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
m_image_provider.image_provider_visit_edges(visitor);
}
CSS::SizeWithAspectRatio ImageBox::natural_size() const
{
if (m_image_provider.is_image_available()) {
auto const& image_provider = this->image_provider();
if (image_provider.is_image_available()) {
return {
.width = m_image_provider.intrinsic_width(),
.height = m_image_provider.intrinsic_height(),
.aspect_ratio = m_image_provider.intrinsic_aspect_ratio()
.width = image_provider.intrinsic_width(),
.height = image_provider.intrinsic_height(),
.aspect_ratio = image_provider.intrinsic_aspect_ratio()
};
}
@ -66,7 +91,7 @@ void ImageBox::dom_node_did_update_alt_text(Badge<ImageProvider>)
bool ImageBox::renders_as_alt_text() const
{
return !m_image_provider.is_image_available();
return !image_provider().is_image_available();
}
RefPtr<Painting::Paintable> ImageBox::create_paintable() const

View file

@ -6,33 +6,36 @@
#pragma once
#include <AK/OwnPtr.h>
#include <LibWeb/HTML/HTMLImageElement.h>
#include <LibWeb/Layout/ReplacedBox.h>
namespace Web::Layout {
class ImageBox final : public ReplacedBox {
GC_CELL(ImageBox, ReplacedBox);
GC_DECLARE_ALLOCATOR(ImageBox);
LAYOUT_NODE(ImageBox, ReplacedBox);
public:
ImageBox(DOM::Document&, GC::Ptr<DOM::Element>, CSS::ComputedProperties const&, ImageProvider const&);
ImageBox(DOM::Document&, CSS::ComputedProperties const&, NonnullOwnPtr<ImageProvider>);
virtual ~ImageBox() override;
bool renders_as_alt_text() const;
virtual RefPtr<Painting::Paintable> create_paintable() const override;
auto const& image_provider() const { return m_image_provider; }
auto& image_provider() { return m_image_provider; }
ImageProvider const& image_provider() const;
ImageProvider& image_provider()
{
return const_cast<ImageProvider&>(const_cast<ImageBox const&>(*this).image_provider());
}
void dom_node_did_update_alt_text(Badge<ImageProvider>);
private:
virtual void visit_edges(Visitor&) override;
virtual CSS::SizeWithAspectRatio natural_size() const override;
ImageProvider const& m_image_provider;
OwnPtr<ImageProvider> m_owned_image_provider;
mutable Optional<CSSPixels> m_cached_alt_text_width;
};

View file

@ -39,11 +39,6 @@ public:
virtual void set_visible_in_viewport(bool) = 0;
virtual void layout_node_was_detached() const { }
virtual void image_provider_visit_edges(GC::Cell::Visitor& visitor) const
{
visitor.visit(to_html_element());
}
protected:
virtual GC::Ptr<DOM::Element const> to_html_element() const = 0;
static void did_update_alt_text(ImageBox&);

View file

@ -252,9 +252,9 @@ void InlineFormattingContext::apply_text_overflow_ellipsis(Vector<LineBox>& line
// NB: When inline children are wrapped in anonymous blocks (e.g. due to floats), we look past the anonymous
// wrapper to the actual element that has text-overflow and overflow set.
GC::Ref<Box const> block = containing_block();
Box const* block = &containing_block();
if (block->is_anonymous())
block = *block->non_anonymous_containing_block();
block = block->non_anonymous_containing_block();
// FIXME: Support the <string>, fade, and fade() values, as well as the two-value syntax.
auto const& block_values = block->computed_values();
@ -345,8 +345,8 @@ void InlineFormattingContext::generate_line_boxes()
auto& line_boxes = m_containing_block_used_values.line_boxes;
line_boxes.clear_with_capacity();
auto direction = m_context_box->computed_values().direction();
auto writing_mode = m_context_box->computed_values().writing_mode();
auto direction = m_context_box.computed_values().direction();
auto writing_mode = m_context_box.computed_values().writing_mode();
InlineLevelIterator iterator(*this, m_state, containing_block(), m_containing_block_used_values, m_layout_mode);
LineBuilder line_builder(*this, m_state, m_containing_block_used_values, direction, writing_mode);
@ -570,10 +570,10 @@ StaticPositionRect InlineFormattingContext::calculate_static_position_rect(Box c
// We're calculating the position for an absolutely positioned box in an IFC.
// Walk backwards through previous siblings to find the most recent one with a line box fragment.
LineBoxFragment const* last_fragment = nullptr;
for (auto const* sibling = box.previous_sibling(); sibling && !last_fragment; sibling = sibling->previous_sibling()) {
for (auto sibling = box.previous_sibling(); sibling && !last_fragment; sibling = sibling->previous_sibling()) {
for (auto const& line_box : m_containing_block_used_values.line_boxes) {
for (auto const& fragment : line_box.fragments()) {
if (&fragment.layout_node() == sibling)
if (&fragment.layout_node() == sibling.ptr())
last_fragment = &fragment;
}
}

View file

@ -79,7 +79,7 @@ void InlineLevelIterator::enter_node_with_box_model_metrics(Layout::NodeWithStyl
// Now's our chance to resolve the inset properties for this node.
m_inline_formatting_context.compute_inset(node, m_inline_formatting_context.content_box_rect(m_containing_block_used_values).size());
m_box_model_node_stack.append(node);
m_box_model_node_stack.append(&node);
}
void InlineLevelIterator::exit_node_with_box_model_metrics()
@ -87,7 +87,7 @@ void InlineLevelIterator::exit_node_with_box_model_metrics()
if (!m_extra_trailing_metrics.has_value())
m_extra_trailing_metrics = ExtraBoxMetrics {};
auto& node = m_box_model_node_stack.last();
auto& node = *m_box_model_node_stack.last();
auto& used_values = m_layout_state.get_mutable(node);
m_extra_trailing_metrics->margin += used_values.margin_right;
@ -136,7 +136,7 @@ void InlineLevelIterator::compute_next()
if (m_next_node == nullptr)
return;
do {
m_next_node = next_inline_node_in_pre_order(*m_next_node, m_containing_block);
m_next_node = next_inline_node_in_pre_order(*m_next_node, &m_containing_block);
if (m_next_node && m_next_node->is_svg_mask_box()) {
// NOTE: It is possible to encounter SVGMaskBox nodes while doing layout of formatting context established by <foreignObject> with a mask.
// We should skip and let SVGFormattingContext take care of them.
@ -206,7 +206,7 @@ Gfx::GlyphRun::TextType InlineLevelIterator::resolve_text_direction_from_context
auto last_known_direction = m_text_node_context->last_known_direction;
if (last_known_direction.has_value() && next_known_direction.has_value() && *last_known_direction != *next_known_direction) {
switch (m_containing_block->computed_values().direction()) {
switch (m_containing_block.computed_values().direction()) {
case CSS::Direction::Ltr:
return Gfx::GlyphRun::TextType::Ltr;
case CSS::Direction::Rtl:

View file

@ -30,7 +30,7 @@ public:
FloatingElement,
};
Type type {};
GC::Ptr<Layout::Node const> node {};
Layout::Node const* node { nullptr };
RefPtr<Gfx::GlyphRun> glyph_run {};
size_t offset_in_node { 0 };
size_t length_in_node { 0 };
@ -73,10 +73,10 @@ private:
Layout::InlineFormattingContext& m_inline_formatting_context;
Layout::LayoutState& m_layout_state;
GC::Ref<BlockContainer const> m_containing_block;
BlockContainer const& m_containing_block;
LayoutState::UsedValues const& m_containing_block_used_values;
GC::Ptr<Layout::Node const> m_current_node;
GC::Ptr<Layout::Node const> m_next_node;
Layout::Node const* m_current_node { nullptr };
Layout::Node const* m_next_node { nullptr };
LayoutMode const m_layout_mode;
struct TextNodeContext {
@ -99,7 +99,7 @@ private:
Optional<ExtraBoxMetrics> m_extra_leading_metrics;
Optional<ExtraBoxMetrics> m_extra_trailing_metrics;
Vector<GC::Ref<NodeWithStyleAndBoxModelMetrics const>> m_box_model_node_stack;
Vector<NodeWithStyleAndBoxModelMetrics const*> m_box_model_node_stack;
// Pre-generated items for O(1) iteration and lookahead.
Vector<Item> m_items;

View file

@ -14,8 +14,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(InlineNode);
InlineNode::InlineNode(DOM::Document& document, DOM::Element* element, CSS::ComputedProperties const& style)
: Layout::NodeWithStyleAndBoxModelMetrics(document, element, style)
{

View file

@ -11,8 +11,7 @@
namespace Web::Layout {
class InlineNode final : public NodeWithStyleAndBoxModelMetrics {
GC_CELL(InlineNode, NodeWithStyleAndBoxModelMetrics);
GC_DECLARE_ALLOCATOR(InlineNode);
LAYOUT_NODE(InlineNode, NodeWithStyleAndBoxModelMetrics);
public:
InlineNode(DOM::Document&, DOM::Element*, CSS::ComputedProperties const&);

View file

@ -425,7 +425,7 @@ static void build_paint_tree(Node& node, RefPtr<Painting::Paintable> parent_pain
if (node.dom_node())
node.dom_node()->set_paintable(paintable);
}
for (auto* child = node.first_child(); child; child = child->next_sibling()) {
for (auto child = node.first_child(); child; child = child->next_sibling()) {
build_paint_tree(*child, node.first_paintable());
}
}
@ -744,8 +744,8 @@ void LayoutState::commit(Box& root)
auto const* box = as_if<Box>(used_values.node());
if (!box || !box->paintable_box())
return;
if (auto containing_block = box->containing_block())
contained_boxes_map.ensure(containing_block.ptr()).append(box);
if (auto const* containing_block = box->containing_block())
contained_boxes_map.ensure(containing_block).append(box);
});
// Measure overflow in scroll containers.

View file

@ -229,9 +229,9 @@ struct LayoutState {
}
void add_floating_descendant(Box const& box) { ensure_rare_data().floating_descendants.set(&box); }
HashTable<GC::Ptr<Box const>> const& floating_descendants() const
HashTable<Box const*> const& floating_descendants() const
{
static auto const& empty = *new HashTable<GC::Ptr<Box const>>;
static auto const& empty = *new HashTable<Box const*>;
return m_rare ? m_rare->floating_descendants : empty;
}
@ -349,7 +349,7 @@ struct LayoutState {
flex_layout_data = make<FlexLayoutData>(*other.flex_layout_data);
}
HashTable<GC::Ptr<Box const>> floating_descendants;
HashTable<Box const*> floating_descendants;
Optional<Painting::PaintableBox::TableCellCoordinates> table_cell_coordinates;
Optional<Gfx::Path> computed_svg_path;
OwnPtr<GridLayoutData> grid_layout_data;
@ -370,7 +370,7 @@ struct LayoutState {
return *m_rare;
}
GC::Ptr<Layout::NodeWithStyle const> m_node { nullptr };
Layout::NodeWithStyle const* m_node { nullptr };
UsedValues const* m_containing_block_used_values { nullptr };
Optional<CSSPixelPoint> m_cumulative_offset;
@ -410,7 +410,7 @@ private:
void resolve_relative_positions();
PagedStore<UsedValues> m_used_values_store;
GC::Ptr<Layout::NodeWithStyle const> m_subtree_root;
Layout::NodeWithStyle const* m_subtree_root { nullptr };
bool m_should_collect_devtools_layout_data { false };
};

View file

@ -8,8 +8,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(LegendBox);
LegendBox::LegendBox(DOM::Document& document, DOM::Element& element, CSS::ComputedProperties const& style)
: BlockContainer(document, &element, style)
{

View file

@ -12,8 +12,7 @@
namespace Web::Layout {
class LegendBox final : public BlockContainer {
GC_CELL(LegendBox, BlockContainer);
GC_DECLARE_ALLOCATOR(LegendBox);
LAYOUT_NODE(LegendBox, BlockContainer);
public:
LegendBox(DOM::Document&, DOM::Element&, CSS::ComputedProperties const&);

View file

@ -6,6 +6,8 @@
#pragma once
#include <AK/Assertions.h>
#include <AK/WeakPtr.h>
#include <LibGC/Ptr.h>
#include <LibGfx/Rect.h>
#include <LibGfx/TextLayout.h>
@ -20,7 +22,11 @@ class LineBoxFragment {
public:
LineBoxFragment(Node const& layout_node, size_t start, size_t length, CSSPixels inline_offset, CSSPixels block_offset, CSSPixels inline_length, CSSPixels block_length, CSSPixels border_box_top, CSS::Direction, CSS::WritingMode, RefPtr<Gfx::GlyphRun>);
Node const& layout_node() const { return m_layout_node; }
Node const& layout_node() const
{
VERIFY(m_layout_node);
return *m_layout_node;
}
size_t start() const { return m_start; }
size_t length_in_code_units() const { return m_length_in_code_units; }
@ -65,7 +71,7 @@ private:
void append_glyph_run_ltr(RefPtr<Gfx::GlyphRun> const&, CSSPixels run_width);
void append_glyph_run_rtl(RefPtr<Gfx::GlyphRun> const&, CSSPixels run_width);
GC::Ref<Node const> m_layout_node;
WeakPtr<Node const> m_layout_node;
size_t m_start { 0 };
size_t m_length_in_code_units { 0 };
CSSPixels m_inline_offset;

View file

@ -10,8 +10,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(ListItemBox);
ListItemBox::ListItemBox(DOM::Document& document, DOM::Element* element, CSS::ComputedProperties const& style)
: Layout::BlockContainer(document, element, style)
{
@ -19,15 +17,9 @@ ListItemBox::ListItemBox(DOM::Document& document, DOM::Element* element, CSS::Co
ListItemBox::~ListItemBox() = default;
void ListItemBox::visit_edges(Cell::Visitor& visitor)
void ListItemBox::set_marker(ListItemMarkerBox* marker)
{
Base::visit_edges(visitor);
visitor.visit(m_marker);
}
void ListItemBox::set_marker(GC::Ptr<ListItemMarkerBox> marker)
{
m_marker = move(marker);
m_marker = marker;
}
}

View file

@ -12,8 +12,7 @@
namespace Web::Layout {
class ListItemBox final : public BlockContainer {
GC_CELL(ListItemBox, BlockContainer);
GC_DECLARE_ALLOCATOR(ListItemBox);
LAYOUT_NODE(ListItemBox, BlockContainer);
public:
ListItemBox(DOM::Document&, DOM::Element*, CSS::ComputedProperties const&);
@ -23,14 +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(GC::Ptr<ListItemMarkerBox>);
void set_marker(ListItemMarkerBox*);
private:
virtual bool is_list_item_box() const override { return true; }
virtual void visit_edges(Cell::Visitor&) override;
GC::Ptr<ListItemMarkerBox> m_marker;
WeakPtr<ListItemMarkerBox> m_marker;
};
template<>

View file

@ -12,8 +12,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(ListItemMarkerBox);
ListItemMarkerBox::ListItemMarkerBox(DOM::Document& document, CSS::ListStyleType style_type, CSS::ListStylePosition style_position, GC::Ref<DOM::Element> list_item_element, CSS::ComputedProperties const& style)
: Box(document, nullptr, style)
, m_list_style_type(style_type)
@ -41,6 +39,8 @@ bool ListItemMarkerBox::counter_style_is_rendered_with_custom_image(RefPtr<CSS::
Optional<String> ListItemMarkerBox::text() const
{
VERIFY(m_list_item_element);
// https://drafts.csswg.org/css-lists-3/#text-markers
auto index = m_list_item_element->ordinal_value();
@ -61,7 +61,7 @@ Optional<String> ListItemMarkerBox::text() const
return {};
// NB: Fallback to decimal if the counter style does not exist is handled within generate_a_counter_representation()
auto counter_representation = CSS::generate_a_counter_representation(counter_style, DOM::AbstractElement { m_list_item_element }.style_scope(), index);
auto counter_representation = CSS::generate_a_counter_representation(counter_style, DOM::AbstractElement { *m_list_item_element }.style_scope(), index);
if (!counter_style)
return MUST(String::formatted("{}. ", counter_representation));
@ -109,10 +109,4 @@ CSSPixels ListItemMarkerBox::relative_size() const
VERIFY_NOT_REACHED();
}
void ListItemMarkerBox::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_list_item_element);
}
}

View file

@ -13,8 +13,7 @@
namespace Web::Layout {
class ListItemMarkerBox final : public Box {
GC_CELL(ListItemMarkerBox, Box);
GC_DECLARE_ALLOCATOR(ListItemMarkerBox);
LAYOUT_NODE(ListItemMarkerBox, Box);
public:
static bool counter_style_is_rendered_with_custom_image(RefPtr<CSS::CounterStyle const> const& counter_style);
@ -32,14 +31,12 @@ public:
CSSPixels relative_size() const;
private:
virtual void visit_edges(Cell::Visitor&) override;
virtual bool is_list_item_marker_box() const final { return true; }
virtual bool can_have_children() const override { return false; }
CSS::ListStyleType m_list_style_type;
CSS::ListStylePosition m_list_style_position { CSS::ListStylePosition::Outside };
GC::Ref<DOM::Element> m_list_item_element;
GC::Weak<DOM::Element> m_list_item_element;
};
template<>

View file

@ -15,8 +15,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(NavigableContainerViewport);
NavigableContainerViewport::NavigableContainerViewport(DOM::Document& document, HTML::NavigableContainer& element, CSS::ComputedProperties const& style)
: ReplacedBox(document, element, style)
{

View file

@ -12,8 +12,7 @@
namespace Web::Layout {
class NavigableContainerViewport final : public ReplacedBox {
GC_CELL(NavigableContainerViewport, ReplacedBox);
GC_DECLARE_ALLOCATOR(NavigableContainerViewport);
LAYOUT_NODE(NavigableContainerViewport, ReplacedBox);
public:
NavigableContainerViewport(DOM::Document&, HTML::NavigableContainer&, CSS::ComputedProperties const&);

View file

@ -55,7 +55,11 @@ Node::Node(DOM::Document& document, DOM::Node* node, AttachToDOMNode attach_to_d
node->set_layout_node({}, *this);
}
Node::~Node() = default;
Node::~Node()
{
for (auto& paintable : m_paintable)
paintable->detach_from_layout_node({});
}
static void invalidate_paint_caches(Node& node)
{
@ -82,16 +86,6 @@ void Node::prepare_subtree_for_detach_from_layout_tree()
});
}
void Node::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_dom_node);
visitor.visit(m_containing_block);
visitor.visit(m_inline_containing_block_if_applicable);
visitor.visit(m_pseudo_element_generator);
TreeNode::visit_edges(visitor);
}
// https://www.w3.org/TR/css-display-3/#out-of-flow
bool Node::is_out_of_flow(FormattingContext const& formatting_context) const
{
@ -269,14 +263,14 @@ bool Node::establishes_a_fixed_positioning_containing_block() const
return false;
}
static GC::Ptr<Box> nearest_ancestor_capable_of_forming_a_containing_block(Node& node)
static Box* nearest_ancestor_capable_of_forming_a_containing_block(Node& node)
{
for (auto* ancestor = node.parent(); ancestor; ancestor = ancestor->parent()) {
if (ancestor->is_block_container()
|| ancestor->display().is_flex_inside()
|| ancestor->display().is_grid_inside()
|| ancestor->is_replaced_box_with_children()) {
return as<Box>(ancestor);
return static_cast<Box*>(ancestor);
}
}
return nullptr;
@ -357,7 +351,7 @@ void Node::recompute_containing_block(Badge<DOM::Document>)
|| computed_values.filter().has_filters() || will_change.has_property(CSS::PropertyID::Filter)
|| computed_values.backdrop_filter().has_filters() || will_change.has_property(CSS::PropertyID::BackdropFilter);
if (inline_establishes_cb) {
m_inline_containing_block_if_applicable = const_cast<InlineNode*>(static_cast<InlineNode const*>(layout_node.ptr()));
m_inline_containing_block_if_applicable = &as<InlineNode>(*layout_node);
break;
}
}
@ -655,14 +649,8 @@ void NodeWithStyle::ImageObserver::image_style_value_did_update(CSS::ImageStyleV
}
}
void NodeWithStyle::ImageObserver::visit_edges(JS::Cell::Visitor& visitor) const
NodeWithStyle::~NodeWithStyle()
{
(void)visitor;
}
void NodeWithStyle::finalize()
{
Base::finalize();
clear_image_observers();
}
@ -697,11 +685,6 @@ void NodeWithStyle::rebuild_image_observers()
m_image_observers = move(new_observers);
}
void NodeWithStyle::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
}
void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
{
auto& computed_values = mutable_computed_values();
@ -1219,9 +1202,9 @@ bool Node::is_atomic_inline() const
return display.is_inline_outside() && !display.is_flow_inside();
}
GC::Ref<NodeWithStyle> NodeWithStyle::create_anonymous_wrapper() const
NonnullRefPtr<NodeWithStyle> NodeWithStyle::create_anonymous_wrapper() const
{
auto wrapper = heap().allocate<BlockContainer>(const_cast<DOM::Document&>(document()), nullptr, computed_values().clone_inherited_values());
auto wrapper = adopt_ref(*new BlockContainer(const_cast<DOM::Document&>(document()), nullptr, computed_values().clone_inherited_values()));
wrapper->mutable_computed_values().set_display(CSS::Display(CSS::DisplayOutside::Block, CSS::DisplayInside::Flow));
propagate_non_inherit_values(*wrapper);
// CSS 2.2 9.2.1.1 creates anonymous block boxes, but 9.4.1 states inline-block creates a BFC.
@ -1354,6 +1337,7 @@ DOM::Node const* Node::dom_node() const
{
if (m_anonymous)
return nullptr;
VERIFY(m_dom_node);
return m_dom_node.ptr();
}
@ -1361,28 +1345,39 @@ DOM::Node* Node::dom_node()
{
if (m_anonymous)
return nullptr;
VERIFY(m_dom_node);
return m_dom_node.ptr();
}
DOM::Element const* Node::pseudo_element_generator() const
{
VERIFY(m_generated_for.has_value());
VERIFY(m_pseudo_element_generator);
return m_pseudo_element_generator.ptr();
}
DOM::Element* Node::pseudo_element_generator()
{
VERIFY(m_generated_for.has_value());
VERIFY(m_pseudo_element_generator);
return m_pseudo_element_generator.ptr();
}
void Node::set_generated_for(CSS::PseudoElement type, DOM::Element& element)
{
m_generated_for = type;
m_pseudo_element_generator = element;
}
DOM::Document& Node::document()
{
VERIFY(m_dom_node);
return m_dom_node->document();
}
DOM::Document const& Node::document() const
{
VERIFY(m_dom_node);
return m_dom_node->document();
}
@ -1614,12 +1609,6 @@ void NodeWithStyleAndBoxModelMetrics::propagate_style_along_continuation(CSS::Co
continuation->apply_style(computed_style);
}
void NodeWithStyleAndBoxModelMetrics::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_continuation_of_node);
}
void Node::set_needs_layout_update(DOM::SetNeedsLayoutReason reason)
{
if (m_needs_layout_update)

View file

@ -10,19 +10,30 @@
#include <AK/DoublyLinkedList.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/RefCounted.h>
#include <AK/Vector.h>
#include <AK/WeakPtr.h>
#include <AK/Weakable.h>
#include <LibGC/Cell.h>
#include <LibGC/Weak.h>
#include <LibJS/Heap/Cell.h>
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
#include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
#include <LibWeb/Export.h>
#include <LibWeb/Forward.h>
#include <LibWeb/Painting/DisplayListRecordingContext.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/TreeNode.h>
#include <LibWeb/RefCountedTreeNode.h>
namespace Web::Layout {
#define LAYOUT_NODE(class_, base_class) \
public: \
using Base = base_class; \
virtual StringView class_name() const override \
{ \
return #class_##sv; \
}
class InlineNode;
enum class LayoutMode {
@ -37,12 +48,15 @@ enum class LayoutMode {
};
class WEB_API Node
: public JS::Cell
, public TreeNode<Node> {
GC_CELL(Node, JS::Cell);
: public RefCounted<Node>
, public Weakable<Node>
, public RefCountedTreeNode<Node> {
public:
using Base = RefCountedTreeNode<Node>;
virtual ~Node();
virtual StringView class_name() const { return "Node"sv; }
bool is_anonymous() const;
DOM::Node const* dom_node() const;
@ -60,11 +74,7 @@ public:
bool is_generated_for_before_pseudo_element() const { return m_generated_for == CSS::PseudoElement::Before; }
bool is_generated_for_after_pseudo_element() const { return m_generated_for == CSS::PseudoElement::After; }
bool is_generated_for_backdrop_pseudo_element() const { return m_generated_for == CSS::PseudoElement::Backdrop; }
void set_generated_for(CSS::PseudoElement type, DOM::Element& element)
{
m_generated_for = type;
m_pseudo_element_generator = &element;
}
void set_generated_for(CSS::PseudoElement type, DOM::Element&);
using PaintableList = DoublyLinkedList<NonnullRefPtr<Painting::Paintable>>;
@ -176,15 +186,15 @@ public:
return true;
}
[[nodiscard]] GC::Ptr<Box const> containing_block() const { return m_containing_block; }
[[nodiscard]] GC::Ptr<Box> containing_block() { return m_containing_block; }
[[nodiscard]] Box const* containing_block() const { return m_containing_block; }
[[nodiscard]] Box* containing_block() { return m_containing_block; }
// Returns the inline node that actually establishes the containing block for this absolutely
// positioned element, if applicable. This is needed because m_containing_block can only hold
// a Box*, but CSS allows inline elements (like a <span> with position:relative) to establish
// containing blocks for their absolutely positioned descendants.
// See the large FIXME comment in FormattingContext.cpp for full context.
[[nodiscard]] GC::Ptr<InlineNode const> inline_containing_block_if_applicable() const { return m_inline_containing_block_if_applicable; }
[[nodiscard]] InlineNode const* inline_containing_block_if_applicable() const { return m_inline_containing_block_if_applicable; }
void recompute_containing_block(Badge<DOM::Document>);
@ -260,24 +270,22 @@ public:
protected:
Node(DOM::Document&, DOM::Node*, AttachToDOMNode = AttachToDOMNode::Yes);
virtual void visit_edges(Cell::Visitor&) override;
private:
friend class NodeWithStyle;
GC::Ref<DOM::Node> m_dom_node;
GC::Weak<DOM::Node> m_dom_node;
PaintableList m_paintable;
GC::Ptr<Box> m_containing_block;
Box* m_containing_block { nullptr };
// For absolutely positioned elements, if there's an inline element (like a <span> with
// position:relative) that should be the containing block but can't be stored in m_containing_block
// (because it's not a Box), we store it here. This happens when a block element is inside an
// inline element - the layout tree restructures so the block becomes a sibling of the inline,
// but the CSS containing block relationship is based on the DOM structure.
GC::Ptr<InlineNode> m_inline_containing_block_if_applicable;
InlineNode const* m_inline_containing_block_if_applicable { nullptr };
GC::Ptr<DOM::Element> m_pseudo_element_generator;
GC::Weak<DOM::Element> m_pseudo_element_generator;
bool m_anonymous { false };
bool m_has_style { false };
@ -297,12 +305,10 @@ private:
};
class WEB_API NodeWithStyle : public Node {
GC_CELL(NodeWithStyle, Node);
LAYOUT_NODE(NodeWithStyle, Node);
public:
virtual ~NodeWithStyle() override = default;
static constexpr bool OVERRIDES_FINALIZE = true;
virtual ~NodeWithStyle() override;
class ImageObserver final : public CSS::ImageStyleValue::Client {
public:
@ -310,10 +316,9 @@ public:
virtual ~ImageObserver() override;
virtual void image_style_value_did_update(CSS::ImageStyleValue&) override;
void visit_edges(JS::Cell::Visitor&) const;
private:
GC::Weak<NodeWithStyle> m_owner;
WeakPtr<NodeWithStyle> m_owner;
NonnullRefPtr<CSS::ImageStyleValue const> m_image;
};
@ -328,15 +333,13 @@ public:
CSS::AbstractImageStyleValue const* list_style_image() const { return m_list_style_image; }
CSS::StyleScope const& style_scope() const;
GC::Ref<NodeWithStyle> create_anonymous_wrapper() const;
NonnullRefPtr<NodeWithStyle> create_anonymous_wrapper() const;
void transfer_table_box_computed_values_to_wrapper_computed_values(CSS::ComputedValues& wrapper_computed_values);
bool is_body() const { return m_is_body; }
bool is_scroll_container() const;
virtual void visit_edges(Cell::Visitor& visitor) override;
void set_computed_values(NonnullOwnPtr<CSS::ComputedValues>);
u32 layout_index() const { return m_layout_index; }
@ -348,7 +351,6 @@ protected:
private:
virtual bool is_node_with_style() const final { return true; }
virtual void finalize() override;
void reset_table_box_computed_values_used_by_wrapper_to_init_values();
void propagate_non_inherit_values(NodeWithStyle& target_node) const;
@ -366,18 +368,16 @@ template<>
inline bool Node::fast_is<NodeWithStyle>() const { return is_node_with_style(); }
class NodeWithStyleAndBoxModelMetrics : public NodeWithStyle {
GC_CELL(NodeWithStyleAndBoxModelMetrics, NodeWithStyle);
LAYOUT_NODE(NodeWithStyleAndBoxModelMetrics, NodeWithStyle);
public:
GC::Ptr<NodeWithStyleAndBoxModelMetrics> continuation_of_node() const { return m_continuation_of_node; }
void set_continuation_of_node(Badge<TreeBuilder>, GC::Ptr<NodeWithStyleAndBoxModelMetrics> node) { m_continuation_of_node = node; }
NodeWithStyleAndBoxModelMetrics* continuation_of_node() const { return m_continuation_of_node.ptr(); }
void set_continuation_of_node(Badge<TreeBuilder>, NodeWithStyleAndBoxModelMetrics* node) { m_continuation_of_node = node; }
bool should_create_inline_continuation() const;
void propagate_style_along_continuation(CSS::ComputedProperties const&) const;
virtual void visit_edges(Cell::Visitor& visitor) override;
protected:
NodeWithStyleAndBoxModelMetrics(DOM::Document&, DOM::Node*, CSS::ComputedProperties const&);
@ -389,7 +389,7 @@ protected:
private:
virtual bool is_node_with_style_and_box_model_metrics() const final { return true; }
GC::Ptr<NodeWithStyleAndBoxModelMetrics> m_continuation_of_node;
WeakPtr<NodeWithStyleAndBoxModelMetrics> m_continuation_of_node;
};
template<>
@ -430,12 +430,12 @@ inline CSS::ImmutableComputedValues const& Node::computed_values() const
inline NodeWithStyle const* Node::parent() const
{
return static_cast<NodeWithStyle const*>(TreeNode<Node>::parent());
return static_cast<NodeWithStyle const*>(Base::parent().ptr());
}
inline NodeWithStyle* Node::parent()
{
return static_cast<NodeWithStyle*>(TreeNode<Node>::parent());
return static_cast<NodeWithStyle*>(Base::parent().ptr());
}
inline Gfx::Font const& NodeWithStyle::first_available_font() const

View file

@ -12,8 +12,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(RadioButton);
RadioButton::RadioButton(DOM::Document& document, HTML::HTMLInputElement& element, CSS::ComputedProperties const& style)
: ReplacedBox(document, element, style)
{

View file

@ -12,8 +12,7 @@
namespace Web::Layout {
class RadioButton final : public ReplacedBox {
GC_CELL(RadioButton, ReplacedBox);
GC_DECLARE_ALLOCATOR(RadioButton);
LAYOUT_NODE(RadioButton, ReplacedBox);
public:
RadioButton(DOM::Document&, HTML::HTMLInputElement&, CSS::ComputedProperties const&);

View file

@ -8,8 +8,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(RangeInputBox);
RangeInputBox::RangeInputBox(DOM::Document& document, GC::Ptr<DOM::Element> element, CSS::ComputedProperties const& style)
: BlockContainer(document, element, style)
{

View file

@ -12,8 +12,7 @@
namespace Web::Layout {
class RangeInputBox final : public BlockContainer {
GC_CELL(RangeInputBox, BlockContainer);
GC_DECLARE_ALLOCATOR(RangeInputBox);
LAYOUT_NODE(RangeInputBox, BlockContainer);
public:
RangeInputBox(DOM::Document&, GC::Ptr<DOM::Element>, CSS::ComputedProperties const&);

View file

@ -12,7 +12,7 @@
namespace Web::Layout {
class ReplacedBox : public Box {
GC_CELL(ReplacedBox, Box);
LAYOUT_NODE(ReplacedBox, Box);
public:
ReplacedBox(DOM::Document&, GC::Ptr<DOM::Element>, CSS::ComputedProperties const&);

View file

@ -8,8 +8,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(SVGBox);
SVGBox::SVGBox(DOM::Document& document, SVG::SVGElement& element, CSS::ComputedProperties const& style)
: Box(document, &element, style)
{

View file

@ -13,8 +13,7 @@
namespace Web::Layout {
class SVGBox : public Box {
GC_CELL(SVGBox, Box);
GC_DECLARE_ALLOCATOR(SVGBox);
LAYOUT_NODE(SVGBox, Box);
public:
SVGBox(DOM::Document&, SVG::SVGElement&, CSS::ComputedProperties const&);

View file

@ -10,8 +10,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(SVGClipBox);
SVGClipBox::SVGClipBox(DOM::Document& document, SVG::SVGClipPathElement& element, CSS::ComputedProperties const& style)
: SVGBox(document, element, style)
{

View file

@ -13,8 +13,7 @@
namespace Web::Layout {
class SVGClipBox final : public SVGBox {
GC_CELL(SVGClipBox, SVGBox);
GC_DECLARE_ALLOCATOR(SVGClipBox);
LAYOUT_NODE(SVGClipBox, SVGBox);
public:
SVGClipBox(DOM::Document&, SVG::SVGClipPathElement&, CSS::ComputedProperties const&);

View file

@ -10,8 +10,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(SVGForeignObjectBox);
SVGForeignObjectBox::SVGForeignObjectBox(DOM::Document& document, SVG::SVGForeignObjectElement& element, CSS::ComputedProperties const& style)
: BlockContainer(document, &element, style)
{

View file

@ -14,8 +14,7 @@
namespace Web::Layout {
class SVGForeignObjectBox final : public BlockContainer {
GC_CELL(SVGForeignObjectBox, BlockContainer);
GC_DECLARE_ALLOCATOR(SVGForeignObjectBox);
LAYOUT_NODE(SVGForeignObjectBox, BlockContainer);
public:
SVGForeignObjectBox(DOM::Document&, SVG::SVGForeignObjectElement&, CSS::ComputedProperties const&);

View file

@ -12,8 +12,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(SVGGeometryBox);
SVGGeometryBox::SVGGeometryBox(DOM::Document& document, SVG::SVGGeometryElement& element, CSS::ComputedProperties const& style)
: SVGGraphicsBox(document, element, style)
{

View file

@ -13,8 +13,7 @@
namespace Web::Layout {
class SVGGeometryBox final : public SVGGraphicsBox {
GC_CELL(SVGGeometryBox, SVGGraphicsBox);
GC_DECLARE_ALLOCATOR(SVGGeometryBox);
LAYOUT_NODE(SVGGeometryBox, SVGGraphicsBox);
public:
SVGGeometryBox(DOM::Document&, SVG::SVGGeometryElement&, CSS::ComputedProperties const&);

View file

@ -10,8 +10,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(SVGGraphicsBox);
SVGGraphicsBox::SVGGraphicsBox(DOM::Document& document, SVG::SVGGraphicsElement& element, CSS::ComputedProperties const& style)
: SVGBox(document, element, style)
{

View file

@ -14,8 +14,7 @@
namespace Web::Layout {
class WEB_API SVGGraphicsBox : public SVGBox {
GC_CELL(SVGGraphicsBox, SVGBox);
GC_DECLARE_ALLOCATOR(SVGGraphicsBox);
LAYOUT_NODE(SVGGraphicsBox, SVGBox);
public:
SVGGraphicsBox(DOM::Document&, SVG::SVGGraphicsElement&, CSS::ComputedProperties const&);

View file

@ -11,8 +11,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(SVGImageBox);
SVGImageBox::SVGImageBox(DOM::Document& document, SVG::SVGGraphicsElement& element, CSS::ComputedProperties const& style)
: SVGGraphicsBox(document, element, style)
{

View file

@ -13,8 +13,7 @@
namespace Web::Layout {
class SVGImageBox : public SVGGraphicsBox {
GC_CELL(SVGImageBox, SVGGraphicsBox);
GC_DECLARE_ALLOCATOR(SVGImageBox);
LAYOUT_NODE(SVGImageBox, SVGGraphicsBox);
public:
SVGImageBox(DOM::Document&, SVG::SVGGraphicsElement&, CSS::ComputedProperties const&);

View file

@ -10,8 +10,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(SVGMaskBox);
SVGMaskBox::SVGMaskBox(DOM::Document& document, SVG::SVGMaskElement& element, CSS::ComputedProperties const& style)
: SVGGraphicsBox(document, element, style)
{

View file

@ -13,8 +13,7 @@
namespace Web::Layout {
class SVGMaskBox : public SVGGraphicsBox {
GC_CELL(SVGMaskBox, SVGGraphicsBox);
GC_DECLARE_ALLOCATOR(SVGMaskBox);
LAYOUT_NODE(SVGMaskBox, SVGGraphicsBox);
public:
SVGMaskBox(DOM::Document&, SVG::SVGMaskElement&, CSS::ComputedProperties const&);

View file

@ -9,8 +9,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(SVGPatternBox);
SVGPatternBox::SVGPatternBox(DOM::Document& document, SVG::SVGPatternElement& element, CSS::ComputedProperties const& style)
: SVGBox(document, element, style)
{

View file

@ -12,8 +12,7 @@
namespace Web::Layout {
class SVGPatternBox final : public SVGBox {
GC_CELL(SVGPatternBox, SVGBox);
GC_DECLARE_ALLOCATOR(SVGPatternBox);
LAYOUT_NODE(SVGPatternBox, SVGBox);
public:
SVGPatternBox(DOM::Document&, SVG::SVGPatternElement&, CSS::ComputedProperties const&);

View file

@ -13,8 +13,6 @@
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(SVGSVGBox);
SVGSVGBox::SVGSVGBox(DOM::Document& document, SVG::SVGSVGElement& element, CSS::ComputedProperties const& style)
: ReplacedBox(document, element, style)
{

Some files were not shown because too many files have changed in this diff Show more