diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 0426e66f3131..82dd677eda33 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -758,6 +758,8 @@ set(SOURCES Layout/TableFormattingContext.cpp Layout/TableGrid.cpp Layout/TableWrapper.cpp + Layout/TextAreaBox.cpp + Layout/TextInputBox.cpp Layout/TextNode.cpp Layout/TreeBuilder.cpp Layout/VideoBox.cpp diff --git a/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Libraries/LibWeb/CSS/Parser/Parser.cpp index 480c61b7233b..4facfa7b0a74 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -1857,7 +1857,7 @@ LengthOrCalculated Parser::parse_as_sizes_attribute(DOM::Element const& element, // Should this use some of the methods from FormattingContext? auto concrete_size = run_default_sizing_algorithm( img->width(), img->height(), - img->natural_width(), img->natural_height(), img->intrinsic_aspect_ratio(), + { img->natural_width(), img->natural_height(), img->intrinsic_aspect_ratio() }, // NOTE: https://html.spec.whatwg.org/multipage/rendering.html#img-contain-size CSSPixelSize { 300, 150 }); size = Length::make_px(concrete_size.width()); diff --git a/Libraries/LibWeb/CSS/Sizing.cpp b/Libraries/LibWeb/CSS/Sizing.cpp index 74d58c7c8a94..a604486e6c50 100644 --- a/Libraries/LibWeb/CSS/Sizing.cpp +++ b/Libraries/LibWeb/CSS/Sizing.cpp @@ -12,8 +12,7 @@ namespace Web::CSS { // https://drafts.csswg.org/css-images/#default-sizing CSSPixelSize run_default_sizing_algorithm( Optional specified_width, Optional specified_height, - Optional natural_width, Optional natural_height, - Optional natural_aspect_ratio, + SizeWithAspectRatio const& natural, CSSPixelSize default_size) { // If the specified size is a definite width and height, the concrete object size is given that width and height. @@ -24,18 +23,18 @@ CSSPixelSize run_default_sizing_algorithm( if (specified_width.has_value() || specified_height.has_value()) { // 1. If the object has a natural aspect ratio, // the missing dimension of the concrete object size is calculated using that aspect ratio and the present dimension. - if (natural_aspect_ratio.has_value() && !natural_aspect_ratio->might_be_saturated()) { + if (natural.has_aspect_ratio() && !natural.aspect_ratio->might_be_saturated()) { if (specified_width.has_value()) - return CSSPixelSize { specified_width.value(), (CSSPixels(1) / natural_aspect_ratio.value()) * specified_width.value() }; + return CSSPixelSize { specified_width.value(), (CSSPixels(1) / natural.aspect_ratio.value()) * specified_width.value() }; if (specified_height.has_value()) - return CSSPixelSize { specified_height.value() * natural_aspect_ratio.value(), specified_height.value() }; + return CSSPixelSize { specified_height.value() * natural.aspect_ratio.value(), specified_height.value() }; } // 2. Otherwise, if the missing dimension is present in the object’s natural dimensions, // the missing dimension is taken from the object’s natural dimensions. - if (specified_height.has_value() && natural_width.has_value()) - return CSSPixelSize { natural_width.value(), specified_height.value() }; - if (specified_width.has_value() && natural_height.has_value()) - return CSSPixelSize { specified_width.value(), natural_height.value() }; + if (specified_height.has_value() && natural.has_width()) + return CSSPixelSize { natural.width.value(), specified_height.value() }; + if (specified_width.has_value() && natural.has_height()) + return CSSPixelSize { specified_width.value(), natural.height.value() }; // 3. Otherwise, the missing dimension of the concrete object size is taken from the default object size. if (specified_height.has_value()) return CSSPixelSize { default_size.width(), specified_height.value() }; @@ -45,8 +44,8 @@ CSSPixelSize run_default_sizing_algorithm( } // If the specified size has no constraints: // 1. If the object has a natural height or width, its size is resolved as if its natural dimensions were given as the specified size. - if (natural_width.has_value() || natural_height.has_value()) - return run_default_sizing_algorithm(natural_width, natural_height, natural_width, natural_height, natural_aspect_ratio, default_size); + if (natural.has_width() || natural.has_height()) + return run_default_sizing_algorithm(natural.width, natural.height, natural, default_size); // FIXME: 2. Otherwise, its size is resolved as a contain constraint against the default object size. return default_size; } diff --git a/Libraries/LibWeb/CSS/Sizing.h b/Libraries/LibWeb/CSS/Sizing.h index f1cc8e006b30..c5da1b592581 100644 --- a/Libraries/LibWeb/CSS/Sizing.h +++ b/Libraries/LibWeb/CSS/Sizing.h @@ -10,11 +10,19 @@ namespace Web::CSS { +struct SizeWithAspectRatio { + Optional width; + Optional height; + Optional aspect_ratio; + bool has_width() const { return width.has_value(); } + bool has_height() const { return height.has_value(); } + bool has_aspect_ratio() const { return aspect_ratio.has_value(); } +}; + // https://drafts.csswg.org/css-images/#default-sizing CSSPixelSize run_default_sizing_algorithm( Optional specified_width, Optional specified_height, - Optional natural_width, Optional natural_height, - Optional natural_aspect_ratio, + SizeWithAspectRatio const& natural_size, CSSPixelSize default_size); } diff --git a/Libraries/LibWeb/CSS/StyleValues/CursorStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/CursorStyleValue.cpp index 7d3622104e63..7d2573c3cade 100644 --- a/Libraries/LibWeb/CSS/StyleValues/CursorStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/CursorStyleValue.cpp @@ -75,7 +75,7 @@ Optional CursorStyleValue::make_image_cursor(Layout::NodeWithS // 32x32 is selected arbitrarily. // FIXME: Ask the OS for the default size? CSSPixelSize const default_cursor_size { 32, 32 }; - auto cursor_css_size = run_default_sizing_algorithm({}, {}, image.natural_width(), image.natural_height(), image.natural_aspect_ratio(), default_cursor_size); + auto cursor_css_size = run_default_sizing_algorithm({}, {}, { image.natural_width(), image.natural_height(), image.natural_aspect_ratio() }, default_cursor_size); // FIXME: How do we determine what cursor sizes the OS allows? // We don't multiply by the pixel ratio, because we want to use the image's actual pixel size. DevicePixelSize cursor_device_size { cursor_css_size.to_type().to_rounded() }; diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 863551ed7843..8fd0f7b065b9 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -118,16 +119,28 @@ GC::Ptr HTMLInputElement::create_layout_node(GC::Refdisplay(), style, this); } - if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton) - return heap().allocate(document(), this, move(style)); + switch (type_state()) { - if (type_state() == TypeAttributeState::Checkbox) + case TypeAttributeState::SubmitButton: + case TypeAttributeState::Button: + case TypeAttributeState::ResetButton: + return heap().allocate(document(), this, move(style)); + case TypeAttributeState::Checkbox: return heap().allocate(document(), *this, move(style)); - - if (type_state() == TypeAttributeState::RadioButton) + case TypeAttributeState::RadioButton: return heap().allocate(document(), *this, move(style)); - - return Element::create_layout_node_for_display_type(document(), style->display(), style, this); + case TypeAttributeState::Text: + case TypeAttributeState::Search: + case TypeAttributeState::URL: + case TypeAttributeState::Telephone: + case TypeAttributeState::Email: + case TypeAttributeState::Password: + case TypeAttributeState::Number: + // FIXME: text padding issues + return heap().allocate(document(), *this, move(style)); + default: + return Element::create_layout_node_for_display_type(document(), style->display(), style, this); + } } void HTMLInputElement::adjust_computed_style(CSS::ComputedProperties& style) diff --git a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index 06f15dc40bcb..cde2a5fddeb9 100644 --- a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -51,11 +52,6 @@ void HTMLTextAreaElement::adjust_computed_style(CSS::ComputedProperties& style) // This is required for the internal shadow tree to work correctly in layout. if (style.display().is_inline_outside() && style.display().is_flow_inside()) style.set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::InlineBlock))); - - if (style.property(CSS::PropertyID::Width).has_auto()) - style.set_property(CSS::PropertyID::Width, CSS::LengthStyleValue::create(CSS::Length(cols(), CSS::LengthUnit::Ch))); - if (style.property(CSS::PropertyID::Height).has_auto()) - style.set_property(CSS::PropertyID::Height, CSS::LengthStyleValue::create(CSS::Length(rows(), CSS::LengthUnit::Lh))); } void HTMLTextAreaElement::initialize(JS::Realm& realm) @@ -466,4 +462,9 @@ bool HTMLTextAreaElement::is_mutable() const return enabled() && !has_attribute(AttributeNames::readonly); } +GC::Ptr HTMLTextAreaElement::create_layout_node(GC::Ref style) +{ + return heap().allocate(document(), *this, style); +} + } diff --git a/Libraries/LibWeb/HTML/HTMLTextAreaElement.h b/Libraries/LibWeb/HTML/HTMLTextAreaElement.h index a8a58710e012..58e8c2705423 100644 --- a/Libraries/LibWeb/HTML/HTMLTextAreaElement.h +++ b/Libraries/LibWeb/HTML/HTMLTextAreaElement.h @@ -139,6 +139,7 @@ class WEB_API HTMLTextAreaElement final virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; + virtual GC::Ptr create_layout_node(GC::Ref) override; void set_raw_value(Utf16String); diff --git a/Libraries/LibWeb/Layout/AudioBox.cpp b/Libraries/LibWeb/Layout/AudioBox.cpp index 72cd6732cf65..aa771e45cdb8 100644 --- a/Libraries/LibWeb/Layout/AudioBox.cpp +++ b/Libraries/LibWeb/Layout/AudioBox.cpp @@ -15,8 +15,6 @@ GC_DEFINE_ALLOCATOR(AudioBox); AudioBox::AudioBox(DOM::Document& document, DOM::Element& element, GC::Ref style) : ReplacedBox(document, element, move(style)) { - set_natural_width(300); - set_natural_height(40); } HTML::HTMLAudioElement& AudioBox::dom_node() @@ -34,15 +32,11 @@ GC::Ptr AudioBox::create_paintable() const return Painting::AudioPaintable::create(*this); } -void AudioBox::prepare_for_replaced_layout() +CSS::SizeWithAspectRatio AudioBox::natural_size() const { - if (dom_node().should_paint()) { - set_natural_width(300); - set_natural_height(40); - } else { - set_natural_width(0); - set_natural_height(0); - } + if (dom_node().should_paint()) + return { 300, 40, {} }; + return { 0, 0, {} }; } } diff --git a/Libraries/LibWeb/Layout/AudioBox.h b/Libraries/LibWeb/Layout/AudioBox.h index 2d403cc2b632..5ef656924f9a 100644 --- a/Libraries/LibWeb/Layout/AudioBox.h +++ b/Libraries/LibWeb/Layout/AudioBox.h @@ -17,14 +17,13 @@ class AudioBox final : public ReplacedBox { GC_DECLARE_ALLOCATOR(AudioBox); public: - virtual void prepare_for_replaced_layout() override; - HTML::HTMLAudioElement& dom_node(); HTML::HTMLAudioElement const& dom_node() const; virtual GC::Ptr create_paintable() const override; private: + virtual CSS::SizeWithAspectRatio natural_size() const override; AudioBox(DOM::Document&, DOM::Element&, GC::Ref); }; diff --git a/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 24de463dbd7c..a0e7dd8e7545 100644 --- a/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -189,11 +189,6 @@ void BlockFormattingContext::compute_width(Box const& box, AvailableSpace const& } if (box_is_sized_as_replaced_element(box, available_space)) { - // FIXME: This should not be done *by* ReplacedBox - if (auto* replaced = as_if(box)) { - // FIXME: This const_cast is gross. - const_cast(*replaced).prepare_for_replaced_layout(); - } compute_width_for_block_level_replaced_element_in_normal_flow(box, available_space); if (box.is_floating()) { // 10.3.6 Floating, replaced elements: @@ -803,7 +798,7 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain resolve_used_height_if_not_treated_as_auto(box, available_space_for_height_resolution); // NOTE: Flex containers with `auto` height are treated as `max-content`, so we can compute their height early. - if (box.is_replaced_box() || box.display().is_flex_inside()) { + if (box.has_intrinsic_content_box_size() || box.display().is_flex_inside()) { resolve_used_height_if_treated_as_auto(box, available_space_for_height_resolution); } @@ -1083,7 +1078,7 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer resolve_used_height_if_not_treated_as_auto(box, available_space); // NOTE: Flex containers with `auto` height are treated as `max-content`, so we can compute their height early. - if (box.is_replaced_box() || box.display().is_flex_inside()) { + if (box.has_intrinsic_content_box_size() || box.display().is_flex_inside()) { resolve_used_height_if_treated_as_auto(box, available_space); } diff --git a/Libraries/LibWeb/Layout/Box.cpp b/Libraries/LibWeb/Layout/Box.cpp index b0b25255d4e5..b4d2063025e2 100644 --- a/Libraries/LibWeb/Layout/Box.cpp +++ b/Libraries/LibWeb/Layout/Box.cpp @@ -29,32 +29,16 @@ Box::~Box() { } -Optional Box::natural_width() const +CSS::SizeWithAspectRatio Box::intrinsic_content_box_size() const { - // https://drafts.csswg.org/css-contain-2/#containment-size + // https://www.w3.org/TR/css-contain-2/#containment-size // Replaced elements must be treated as having a natural width and height of 0 and no natural aspect // ratio. if (has_size_containment()) - return 0; - return m_natural_width; -} -Optional Box::natural_height() const -{ - // https://drafts.csswg.org/css-contain-2/#containment-size - // Replaced elements must be treated as having a natural width and height of 0 and no natural aspect - // ratio. - if (has_size_containment()) - return 0; - return m_natural_height; -} -Optional Box::natural_aspect_ratio() const -{ - // https://drafts.csswg.org/css-contain-2/#containment-size - // Replaced elements must be treated as having a natural width and height of 0 and no natural aspect - // ratio. - if (has_size_containment()) - return {}; - return m_natural_aspect_ratio; + return { 0, 0, {} }; + + // Return the content-box intrinsic size + return compute_intrinsic_content_box_size(); } void Box::visit_edges(Cell::Visitor& visitor) @@ -80,18 +64,22 @@ Painting::PaintableBox const* Box::paintable_box() const Optional Box::preferred_aspect_ratio() const { - auto computed_aspect_ratio = computed_values().aspect_ratio(); - if (computed_aspect_ratio.use_natural_aspect_ratio_if_available && natural_aspect_ratio().has_value()) - return natural_aspect_ratio(); + auto const& aspect = computed_values().aspect_ratio(); + + // https://www.w3.org/TR/css-contain-2/#containment-size - if (!computed_aspect_ratio.preferred_ratio.has_value()) - return {}; + if (!has_size_containment() && aspect.use_natural_aspect_ratio_if_available) { + if (auto intrinsic = intrinsic_content_box_size(); intrinsic.has_aspect_ratio()) + return intrinsic.aspect_ratio; + } - auto ratio = computed_aspect_ratio.preferred_ratio.release_value(); - if (ratio.is_degenerate()) - return {}; + if (aspect.preferred_ratio.has_value()) { + auto ratio = aspect.preferred_ratio.value(); + if (!ratio.is_degenerate()) + return CSSPixelFraction(ratio.numerator(), ratio.denominator()); + } - return CSSPixelFraction(ratio.numerator(), ratio.denominator()); + return {}; } } diff --git a/Libraries/LibWeb/Layout/Box.h b/Libraries/LibWeb/Layout/Box.h index ef0728645188..4b08ebe581c0 100644 --- a/Libraries/LibWeb/Layout/Box.h +++ b/Libraries/LibWeb/Layout/Box.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -18,7 +19,7 @@ struct LineBoxFragmentCoordinate { size_t fragment_index { 0 }; }; -struct IntrinsicSizes { +struct IntrinsicMinMaxCache { Optional min_content_width; Optional max_content_width; HashMap> min_content_height; @@ -33,17 +34,9 @@ class WEB_API Box : public NodeWithStyleAndBoxModelMetrics { Painting::PaintableBox* paintable_box(); // https://www.w3.org/TR/css-images-3/#natural-dimensions - Optional natural_width() const; - Optional natural_height() const; - Optional natural_aspect_ratio() const; - - bool has_natural_width() const { return natural_width().has_value(); } - bool has_natural_height() const { return natural_height().has_value(); } - bool has_natural_aspect_ratio() const { return natural_aspect_ratio().has_value(); } - - void set_natural_width(Optional width) { m_natural_width = width; } - void set_natural_height(Optional height) { m_natural_height = height; } - void set_natural_aspect_ratio(Optional ratio) { m_natural_aspect_ratio = ratio; } + virtual CSS::SizeWithAspectRatio natural_size() const { return {}; } + CSS::SizeWithAspectRatio intrinsic_content_box_size() const; + virtual bool has_intrinsic_content_box_size() const { return false; } // https://www.w3.org/TR/css-sizing-4/#preferred-aspect-ratio Optional preferred_aspect_ratio() const; @@ -61,28 +54,25 @@ class WEB_API Box : public NodeWithStyleAndBoxModelMetrics { virtual void visit_edges(Cell::Visitor&) override; - IntrinsicSizes& cached_intrinsic_sizes() const + IntrinsicMinMaxCache& intrinsic_min_max_cache() const { - if (!m_cached_intrinsic_sizes) - m_cached_intrinsic_sizes = make(); - return *m_cached_intrinsic_sizes; + if (!m_intrinsic_min_max_cache) + m_intrinsic_min_max_cache = make(); + return *m_intrinsic_min_max_cache; } - void reset_cached_intrinsic_sizes() const { m_cached_intrinsic_sizes.clear(); } + void reset_cached_intrinsic_sizes() const { m_intrinsic_min_max_cache.clear(); } protected: Box(DOM::Document&, DOM::Node*, GC::Ref); Box(DOM::Document&, DOM::Node*, NonnullOwnPtr); + virtual CSS::SizeWithAspectRatio compute_intrinsic_content_box_size() const { return natural_size(); } private: virtual bool is_box() const final { return true; } - Optional m_natural_width; - Optional m_natural_height; - Optional m_natural_aspect_ratio; - Vector> m_contained_abspos_children; - OwnPtr mutable m_cached_intrinsic_sizes; + OwnPtr mutable m_intrinsic_min_max_cache; }; template<> diff --git a/Libraries/LibWeb/Layout/CanvasBox.cpp b/Libraries/LibWeb/Layout/CanvasBox.cpp index 603bcc7cb158..7e5d2dab8f31 100644 --- a/Libraries/LibWeb/Layout/CanvasBox.cpp +++ b/Libraries/LibWeb/Layout/CanvasBox.cpp @@ -18,10 +18,13 @@ CanvasBox::CanvasBox(DOM::Document& document, HTML::HTMLCanvasElement& element, CanvasBox::~CanvasBox() = default; -void CanvasBox::prepare_for_replaced_layout() +CSS::SizeWithAspectRatio CanvasBox::compute_intrinsic_content_box_size() const { - set_natural_width(dom_node().width()); - set_natural_height(dom_node().height()); + auto width = dom_node().width(); + auto height = dom_node().height(); + if (width == 0 || height == 0) + return { width, height, {} }; + return { width, height, CSSPixelFraction(width, height) }; } GC::Ptr CanvasBox::create_paintable() const diff --git a/Libraries/LibWeb/Layout/CanvasBox.h b/Libraries/LibWeb/Layout/CanvasBox.h index 9ac48b42df4f..8ca57343f84a 100644 --- a/Libraries/LibWeb/Layout/CanvasBox.h +++ b/Libraries/LibWeb/Layout/CanvasBox.h @@ -19,11 +19,12 @@ class CanvasBox final : public ReplacedBox { CanvasBox(DOM::Document&, HTML::HTMLCanvasElement&, GC::Ref); virtual ~CanvasBox() override; - virtual void prepare_for_replaced_layout() override; - HTML::HTMLCanvasElement const& dom_node() const { return static_cast(*ReplacedBox::dom_node()); } virtual GC::Ptr create_paintable() const override; + +private: + virtual CSS::SizeWithAspectRatio compute_intrinsic_content_box_size() const override; }; } diff --git a/Libraries/LibWeb/Layout/CheckBox.cpp b/Libraries/LibWeb/Layout/CheckBox.cpp index 865b8af040fe..6027cdb8770c 100644 --- a/Libraries/LibWeb/Layout/CheckBox.cpp +++ b/Libraries/LibWeb/Layout/CheckBox.cpp @@ -16,8 +16,6 @@ GC_DEFINE_ALLOCATOR(CheckBox); CheckBox::CheckBox(DOM::Document& document, HTML::HTMLInputElement& element, GC::Ref style) : FormAssociatedLabelableNode(document, element, move(style)) { - set_natural_width(13); - set_natural_height(13); } CheckBox::~CheckBox() = default; diff --git a/Libraries/LibWeb/Layout/CheckBox.h b/Libraries/LibWeb/Layout/CheckBox.h index 2670322757ee..071ced86b683 100644 --- a/Libraries/LibWeb/Layout/CheckBox.h +++ b/Libraries/LibWeb/Layout/CheckBox.h @@ -20,6 +20,7 @@ class CheckBox final : public FormAssociatedLabelableNode { virtual ~CheckBox() override; private: + virtual CSS::SizeWithAspectRatio compute_intrinsic_content_box_size() const override { return { 13, 13, {} }; } virtual GC::Ptr create_paintable() const override; }; diff --git a/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Libraries/LibWeb/Layout/FlexFormattingContext.cpp index 4237a3e9a82c..ac6c17fc3c07 100644 --- a/Libraries/LibWeb/Layout/FlexFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/FlexFormattingContext.cpp @@ -96,10 +96,6 @@ void FlexFormattingContext::run(AvailableSpace const& available_space) // 3. Determine the flex base size and hypothetical main size of each item for (auto& item : m_flex_items) { - if (item.box->is_replaced_box()) { - // FIXME: Get rid of prepare_for_replaced_layout() and make replaced elements figure out their intrinsic size lazily. - static_cast(*item.box).prepare_for_replaced_layout(); - } determine_flex_base_size(item); } @@ -744,8 +740,9 @@ 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. - if (item.box->has_natural_aspect_ratio()) { - if (!item.used_flex_basis_is_definite && !item.box->has_natural_width() && !item.box->has_natural_height() && !has_definite_cross_size(item)) { + auto intrinsic = item.box->intrinsic_content_box_size(); // ? + if (intrinsic.has_aspect_ratio()) { + if (!item.used_flex_basis_is_definite && !intrinsic.has_width() && !intrinsic.has_height() && !has_definite_cross_size(item)) { item.flex_base_size = inner_main_size(m_flex_container_state); } item.flex_base_size = adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(child_box, item.flex_base_size, computed_cross_min_size(child_box), computed_cross_max_size(child_box)); @@ -1156,14 +1153,14 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem& } if (item.box->has_preferred_aspect_ratio()) { - if (item.used_flex_basis_is_definite || (item.box->has_natural_width() && item.box->has_natural_height())) { + auto intrinsic = item.box->intrinsic_content_box_size(); + if (item.used_flex_basis_is_definite || (intrinsic.has_width() && intrinsic.has_height())) { item.hypothetical_cross_size = calculate_cross_size_from_main_size_and_aspect_ratio(item.main_size.value(), item.box->preferred_aspect_ratio().value()); return; } item.hypothetical_cross_size = inner_cross_size(m_flex_container_state); return; } - auto computed_cross_size = this->computed_cross_size(item.box); if (computed_cross_size.is_min_content()) { @@ -2011,13 +2008,14 @@ bool FlexFormattingContext::should_treat_cross_max_size_as_none(Box const& box) CSSPixels FlexFormattingContext::calculate_cross_min_content_contribution(FlexItem const& item, bool resolve_percentage_min_max_sizes) const { + bool cross_size_auto = should_treat_cross_size_as_auto(item.box); auto size = [&] { - if (should_treat_cross_size_as_auto(item.box)) + if (cross_size_auto) return calculate_min_content_cross_size(item); return !is_row_layout() ? get_pixel_width(item, computed_cross_size(item.box)) : get_pixel_height(item, computed_cross_size(item.box)); }(); - if (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); @@ -2033,13 +2031,14 @@ CSSPixels FlexFormattingContext::calculate_cross_min_content_contribution(FlexIt CSSPixels FlexFormattingContext::calculate_cross_max_content_contribution(FlexItem const& item, bool resolve_percentage_min_max_sizes) const { + bool cross_size_auto = should_treat_cross_size_as_auto(item.box); auto size = [&] { - if (should_treat_cross_size_as_auto(item.box)) + if (cross_size_auto) return calculate_max_content_cross_size(item); return !is_row_layout() ? get_pixel_width(item, computed_cross_size(item.box)) : get_pixel_height(item, computed_cross_size(item.box)); }(); - if (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); diff --git a/Libraries/LibWeb/Layout/FormattingContext.cpp b/Libraries/LibWeb/Layout/FormattingContext.cpp index 1a02d69e1c75..b6872b13af7b 100644 --- a/Libraries/LibWeb/Layout/FormattingContext.cpp +++ b/Libraries/LibWeb/Layout/FormattingContext.cpp @@ -514,8 +514,9 @@ CSSPixels FormattingContext::tentative_width_for_replaced_element(Box const& box // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width, // then that intrinsic width is the used value of 'width'. - if (computed_height.is_auto() && computed_width.is_auto() && box.has_natural_width()) - return box.natural_width().value(); + auto intrinsic = box.intrinsic_content_box_size(); + if (computed_height.is_auto() && computed_width.is_auto() && intrinsic.has_width()) + return intrinsic.width.value(); // If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width, // but does have an intrinsic height and intrinsic ratio; @@ -523,7 +524,7 @@ CSSPixels FormattingContext::tentative_width_for_replaced_element(Box const& box // 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value of 'width' is: // // (used height) * (intrinsic ratio) - if ((computed_height.is_auto() && computed_width.is_auto() && !box.has_natural_width() && box.has_natural_height() && box.has_preferred_aspect_ratio()) + if ((computed_height.is_auto() && computed_width.is_auto() && !intrinsic.has_width() && intrinsic.has_height() && box.has_preferred_aspect_ratio()) || (computed_width.is_auto() && !computed_height.is_auto() && box.has_preferred_aspect_ratio())) { return compute_height_for_replaced_element(box, available_space) * box.preferred_aspect_ratio().value(); } @@ -532,13 +533,13 @@ CSSPixels FormattingContext::tentative_width_for_replaced_element(Box const& box // then the used value of 'width' is undefined in CSS 2.2. However, it is suggested that, if the containing block's width does not itself // depend on the replaced element's width, then the used value of 'width' is calculated from the constraint equation used for block-level, // non-replaced elements in normal flow. - if (computed_height.is_auto() && computed_width.is_auto() && !box.has_natural_width() && !box.has_natural_height() && box.has_preferred_aspect_ratio()) { + if (computed_height.is_auto() && computed_width.is_auto() && !intrinsic.has_width() && !intrinsic.has_height() && box.has_preferred_aspect_ratio()) { return calculate_stretch_fit_width(box, available_space.width); } // Otherwise, if 'width' has a computed value of 'auto', and the element has an intrinsic width, then that intrinsic width is the used value of 'width'. - if (computed_width.is_auto() && box.has_natural_width()) - return box.natural_width().value(); + if (computed_width.is_auto() && intrinsic.has_width()) + return intrinsic.width.value(); // Otherwise, if 'width' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'width' becomes 300px. // If 300px is too wide to fit the device, UAs should use the width of the largest rectangle that has a 2:1 ratio and fits the device instead. @@ -608,10 +609,11 @@ CSSPixels FormattingContext::compute_width_for_replaced_element(Box const& box, // https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-height CSSPixels FormattingContext::tentative_height_for_replaced_element(Box const& box, CSS::Size const& computed_height, AvailableSpace const& available_space) const { + auto intrinsic = box.intrinsic_content_box_size(); // If 'height' and 'width' both have computed values of 'auto' and the element also has // an intrinsic height, then that intrinsic height is the used value of 'height'. - if (should_treat_width_as_auto(box, available_space) && should_treat_height_as_auto(box, available_space) && box.has_natural_height()) - return box.natural_height().value(); + if (should_treat_width_as_auto(box, available_space) && should_treat_height_as_auto(box, available_space) && intrinsic.has_height()) + return intrinsic.height.value(); // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is: // @@ -620,8 +622,8 @@ CSSPixels FormattingContext::tentative_height_for_replaced_element(Box const& bo return m_state.get(box).content_width() / box.preferred_aspect_ratio().value(); // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic height, then that intrinsic height is the used value of 'height'. - if (computed_height.is_auto() && box.has_natural_height()) - return box.natural_height().value(); + if (computed_height.is_auto() && intrinsic.has_height()) + return intrinsic.height.value(); // Otherwise, if 'height' has a computed value of 'auto', but none of the conditions above are met, // then the used value of 'height' must be set to the height of the largest rectangle that has a 2:1 ratio, has a height not greater than 150px, @@ -651,15 +653,15 @@ CSSPixels FormattingContext::compute_height_for_replaced_element(Box const& box, // use the algorithm under 'Minimum and maximum widths' // https://www.w3.org/TR/CSS22/visudet.html#min-max-widths // to find the used width and height. - if ((computed_width.is_auto() && computed_height.is_auto() && box.has_preferred_aspect_ratio()) + if ((computed_width.is_auto() && computed_height.is_auto() && box.has_preferred_aspect_ratio())) { // NOTE: This is a special case where calling tentative_width_for_replaced_element() would call us right back, // and we'd end up in an infinite loop. So we need to handle this case separately. - && !(!box.has_natural_width() && box.has_natural_height())) { - CSSPixels w = tentative_width_for_replaced_element(box, computed_width, available_space); - CSSPixels h = used_height; - used_height = solve_replaced_size_constraint(w, h, box, available_space).height(); + if (auto intrinsic = box.intrinsic_content_box_size(); intrinsic.has_width() || !intrinsic.has_height()) { + CSSPixels w = tentative_width_for_replaced_element(box, computed_width, available_space); + CSSPixels h = used_height; + used_height = solve_replaced_size_constraint(w, h, box, available_space).height(); + } } - // 2. If this tentative height is greater than 'max-height', the rules above are applied again, // but this time using the value of 'max-height' as the computed value for 'height'. if (!should_treat_max_height_as_none(box, available_space.height)) { @@ -868,10 +870,6 @@ void FormattingContext::compute_width_for_absolutely_positioned_replaced_element // but the rest of section 10.3.7 is replaced by the following rules: // 1. The used value of 'width' is determined as for inline replaced elements. - if (auto const* replaced = as_if(box)) { - // FIXME: This const_cast is gross. - const_cast(*replaced).prepare_for_replaced_layout(); - } auto width = compute_width_for_replaced_element(box, available_space); auto width_of_containing_block = available_space.width.to_px_or_zero(); @@ -1454,11 +1452,10 @@ CSSPixels FormattingContext::calculate_min_content_width(Layout::Box const& box) if (auto const& max_width = box.computed_values().max_width(); max_width.is_percentage()) return max_width.to_px(box, 0); } + if (auto intrinsic = box.intrinsic_content_box_size(); intrinsic.has_width()) + return intrinsic.width.value(); - if (box.has_natural_width()) - return *box.natural_width(); - - auto& cache = box.cached_intrinsic_sizes().min_content_width; + auto& cache = box.intrinsic_min_max_cache().min_content_width; if (cache.has_value()) return cache.value(); @@ -1484,10 +1481,11 @@ CSSPixels FormattingContext::calculate_min_content_width(Layout::Box const& box) CSSPixels FormattingContext::calculate_max_content_width(Layout::Box const& box) const { - if (box.has_natural_width()) - return *box.natural_width(); - auto& cache = box.cached_intrinsic_sizes().max_content_width; + if (auto intrinsic = box.intrinsic_content_box_size(); intrinsic.has_width()) + return intrinsic.width.value(); + + auto& cache = box.intrinsic_min_max_cache().max_content_width; if (cache.has_value()) return cache.value(); @@ -1526,13 +1524,13 @@ CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box if (box.is_block_container() || box.display().is_table_inside()) return calculate_max_content_height(box, width); - if (box.has_natural_height()) { - if (box.has_natural_aspect_ratio()) - return width / *box.natural_aspect_ratio(); - return *box.natural_height(); + if (auto intrinsic = box.intrinsic_content_box_size(); intrinsic.has_height()) { + if (intrinsic.has_aspect_ratio()) + return width / intrinsic.aspect_ratio.value(); + return intrinsic.height.value(); } - auto& cache = box.cached_intrinsic_sizes().min_content_height.ensure(width); + auto& cache = box.intrinsic_min_max_cache().min_content_height.ensure(width); if (cache.has_value()) return cache.value(); @@ -1557,10 +1555,10 @@ CSSPixels FormattingContext::calculate_max_content_height(Layout::Box const& box if (box.has_preferred_aspect_ratio()) return width / *box.preferred_aspect_ratio(); - if (box.has_natural_height()) - return *box.natural_height(); + if (auto intrinsic = box.intrinsic_content_box_size(); intrinsic.has_height()) + return intrinsic.height.value(); - auto& cache_slot = box.cached_intrinsic_sizes().max_content_height.ensure(width); + auto& cache_slot = box.intrinsic_min_max_cache().max_content_height.ensure(width); if (cache_slot.has_value()) return cache_slot.value(); @@ -1713,7 +1711,7 @@ bool FormattingContext::should_treat_width_as_auto(Box const& box, AvailableSpac // AD-HOC: If the box has a preferred aspect ratio and an intrinsic keyword for width... if (box.has_preferred_aspect_ratio() && computed_width.is_intrinsic_sizing_constraint()) { // If the box has no natural height to resolve the aspect ratio, we treat the width as auto. - if (!box.has_natural_height()) + if (!box.intrinsic_content_box_size().has_height()) return true; // If the box has definite height, we can resolve the width through the aspect ratio. if (m_state.get(box).has_definite_height()) @@ -1745,7 +1743,7 @@ bool FormattingContext::should_treat_height_as_auto(Box const& box, AvailableSpa // AD-HOC: If the box has a preferred aspect ratio and an intrinsic keyword for height... if (box.has_preferred_aspect_ratio() && computed_height.is_intrinsic_sizing_constraint()) { // If the box has no natural width to resolve the aspect ratio, we treat the height as auto. - if (!box.has_natural_width()) + if (!box.intrinsic_content_box_size().has_width()) return true; // If the box has definite width, we can resolve the height through the aspect ratio. if (m_state.get(box).has_definite_width()) @@ -1782,7 +1780,7 @@ CSSPixelRect FormattingContext::absolute_content_rect(Box const& box) const Box const* FormattingContext::box_child_to_derive_baseline_from(Box const& box) const { - if (!box.has_children() || box.children_are_inline()) + if (!box.has_children() || box.children_are_inline() || box.is_textarea_box()) return nullptr; // To find the baseline of a box, we first look for the last in-flow child with at least one line box. auto const* last_box_child = box.last_child_of_type(); @@ -1832,11 +1830,14 @@ CSSPixels FormattingContext::box_baseline(Box const& box) const auto const& overflow_x = box.computed_values().overflow_x(); auto const& overflow_y = box.computed_values().overflow_y(); - if (!box_state.line_boxes.is_empty() && overflow_x == CSS::Overflow::Visible && overflow_y == CSS::Overflow::Visible) - return box_state.margin_box_top() + box_state.offset.y() + box_state.line_boxes.last().baseline(); - if (auto const* child_box = box_child_to_derive_baseline_from(box)) { - return box_state.margin_box_top() + box_state.offset.y() + box_baseline(*child_box); + if (!box_state.line_boxes.is_empty() && overflow_x == CSS::Overflow::Visible && overflow_y == CSS::Overflow::Visible) { + auto const& last = box_state.line_boxes.last(); + return box_state.margin_box_top() + box_state.offset.y() + last.baseline() + last.bottom() - last.height(); } + + if (auto const* child_box = box_child_to_derive_baseline_from(box)) + return box_state.margin_box_top() + box_state.offset.y() + box_baseline(*child_box); + // If none of the children have a baseline set, the bottom margin edge of the box is used. return box_state.margin_box_height(); } @@ -1903,7 +1904,7 @@ bool FormattingContext::box_is_sized_as_replaced_element(Box const& box, Availab if (is(box)) return true; - if (box.has_preferred_aspect_ratio()) { + if (box.has_preferred_aspect_ratio() || box.has_intrinsic_content_box_size()) { // From CSS2: // If height and width both have computed values of auto and the element has an intrinsic ratio but no intrinsic height or width, // then the used value of width is undefined in CSS 2. @@ -1912,10 +1913,12 @@ bool FormattingContext::box_is_sized_as_replaced_element(Box const& box, Availab // AD-HOC: If box has preferred aspect ratio but width and height are not specified, then we should // size it as a normal box to match other browsers. + + auto intrinsic = box.intrinsic_content_box_size(); if (should_treat_width_as_auto(box, available_space) && should_treat_height_as_auto(box, available_space) - && !box.has_natural_width() - && !box.has_natural_height()) { + && !intrinsic.has_width() + && !intrinsic.has_height()) { return false; } return true; diff --git a/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Libraries/LibWeb/Layout/GridFormattingContext.cpp index 801c534fbe0e..f6dccbc88933 100644 --- a/Libraries/LibWeb/Layout/GridFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/GridFormattingContext.cpp @@ -1716,7 +1716,7 @@ void GridFormattingContext::resolve_grid_item_sizes(GridDimension dimension) }; ItemAlignment used_alignment; - if (item.box->is_replaced_box() && item.box->has_natural_width()) { + if (item.box->is_replaced_box() && item.box->intrinsic_content_box_size().has_width()) { auto width = tentative_size_for_replaced_element(preferred_size); used_alignment = try_compute_size(width, item.preferred_size(dimension)); } else { @@ -2023,12 +2023,6 @@ void GridFormattingContext::run(AvailableSpace const& available_space) item.used_values.set_indefinite_content_width(); if (!computed_values.height().is_length()) item.used_values.set_indefinite_content_height(); - - if (item.box->is_replaced_box()) { - auto& replaced_box = static_cast(*item.box); - // FIXME: This const_cast is gross. - const_cast(replaced_box).prepare_for_replaced_layout(); - } } // Do the first pass of resolving grid items box metrics to compute values that are independent of a track width diff --git a/Libraries/LibWeb/Layout/ImageBox.cpp b/Libraries/LibWeb/Layout/ImageBox.cpp index 30dd660e1caf..c6eaa81e9c25 100644 --- a/Libraries/LibWeb/Layout/ImageBox.cpp +++ b/Libraries/LibWeb/Layout/ImageBox.cpp @@ -29,33 +29,33 @@ void ImageBox::visit_edges(JS::Cell::Visitor& visitor) m_image_provider.image_provider_visit_edges(visitor); } -void ImageBox::prepare_for_replaced_layout() +CSS::SizeWithAspectRatio ImageBox::natural_size() const { - set_natural_width(m_image_provider.intrinsic_width()); - set_natural_height(m_image_provider.intrinsic_height()); - set_natural_aspect_ratio(m_image_provider.intrinsic_aspect_ratio()); + if (m_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() + }; - if (renders_as_alt_text()) { - String alt; - if (auto element = dom_node()) - alt = element->get_attribute_value(HTML::AttributeNames::alt); + String alt; + if (auto element = dom_node()) + alt = element->get_attribute_value(HTML::AttributeNames::alt); + if (alt.is_empty()) + return { 0, 0, {} }; - if (alt.is_empty()) { - set_natural_width(0); - set_natural_height(0); - } else { - auto font = Platform::FontPlugin::the().default_font(12); - CSSPixels alt_text_width = m_cached_alt_text_width.ensure([&] { - return CSSPixels::nearest_value_for(font->width(Utf16String::from_utf8(alt))); - }); - set_natural_width(alt_text_width + 16); - set_natural_height(CSSPixels::nearest_value_for(font->pixel_size()) + 16); - } - } + auto font = Platform::FontPlugin::the().default_font(12); + CSSPixels alt_text_width = m_cached_alt_text_width.ensure([&] { + return CSSPixels::nearest_value_for(font->width(Utf16String::from_utf8(alt))); + }); + auto width = alt_text_width + 16; + auto height = CSSPixels::nearest_value_for(font->pixel_size()) + 16; - if (!has_natural_width() && !has_natural_height()) { - // FIXME: Do something. - } + Optional aspect_ratio; + if (height > 0) + aspect_ratio = CSSPixelFraction(width, height); + + return { width, height, aspect_ratio }; } void ImageBox::dom_node_did_update_alt_text(Badge) diff --git a/Libraries/LibWeb/Layout/ImageBox.h b/Libraries/LibWeb/Layout/ImageBox.h index 1f61b0739d5e..7efbb2215951 100644 --- a/Libraries/LibWeb/Layout/ImageBox.h +++ b/Libraries/LibWeb/Layout/ImageBox.h @@ -19,8 +19,6 @@ class ImageBox final : public ReplacedBox { ImageBox(DOM::Document&, GC::Ptr, GC::Ref, ImageProvider const&); virtual ~ImageBox() override; - virtual void prepare_for_replaced_layout() override; - bool renders_as_alt_text() const; virtual GC::Ptr create_paintable() const override; @@ -32,10 +30,11 @@ class ImageBox final : public ReplacedBox { private: virtual void visit_edges(Visitor&) override; + virtual CSS::SizeWithAspectRatio natural_size() const override; ImageProvider const& m_image_provider; - Optional m_cached_alt_text_width; + mutable Optional m_cached_alt_text_width; }; } diff --git a/Libraries/LibWeb/Layout/InlineLevelIterator.cpp b/Libraries/LibWeb/Layout/InlineLevelIterator.cpp index ffc24a8fa907..ffdba7de282c 100644 --- a/Libraries/LibWeb/Layout/InlineLevelIterator.cpp +++ b/Libraries/LibWeb/Layout/InlineLevelIterator.cpp @@ -363,12 +363,6 @@ Optional InlineLevelIterator::next_without_lookahead( return next_without_lookahead(); } - if (is(*m_current_node)) { - auto const& replaced_box = static_cast(*m_current_node); - // FIXME: This const_cast is gross. - const_cast(replaced_box).prepare_for_replaced_layout(); - } - auto const& box = as(*m_current_node); auto const& box_state = m_layout_state.get(box); m_inline_formatting_context.dimension_box_on_line(box, m_layout_mode); diff --git a/Libraries/LibWeb/Layout/NavigableContainerViewport.cpp b/Libraries/LibWeb/Layout/NavigableContainerViewport.cpp index 42030c1fa8f5..3b763a4b1f57 100644 --- a/Libraries/LibWeb/Layout/NavigableContainerViewport.cpp +++ b/Libraries/LibWeb/Layout/NavigableContainerViewport.cpp @@ -24,22 +24,20 @@ NavigableContainerViewport::NavigableContainerViewport(DOM::Document& document, NavigableContainerViewport::~NavigableContainerViewport() = default; -void NavigableContainerViewport::prepare_for_replaced_layout() +CSS::SizeWithAspectRatio NavigableContainerViewport::natural_size() const { - if (is(dom_node())) { - if (auto const* content_document = dom_node().content_document_without_origin_check()) { - if (auto const* root_element = content_document->document_element(); root_element && root_element->is_svg_svg_element()) { - auto natural_metrics = SVG::SVGSVGElement::negotiate_natural_metrics(static_cast(*root_element)); - set_natural_width(natural_metrics.width); - set_natural_height(natural_metrics.height); - set_natural_aspect_ratio(natural_metrics.aspect_ratio); - return; - } + if (!is(dom_node())) + return {}; + + if (auto const* content_document = dom_node().content_document_without_origin_check()) { + if (auto const* root = content_document->document_element(); + root && root->is_svg_svg_element()) { + + auto metrics = SVG::SVGSVGElement::negotiate_natural_metrics(static_cast(*root)); + return { metrics.width, metrics.height, metrics.aspect_ratio }; } } - // FIXME: Do proper error checking, etc. - set_natural_width(dom_node().get_attribute_value(HTML::AttributeNames::width).to_number().value_or(300)); - set_natural_height(dom_node().get_attribute_value(HTML::AttributeNames::height).to_number().value_or(150)); + return {}; } void NavigableContainerViewport::did_set_content_size() diff --git a/Libraries/LibWeb/Layout/NavigableContainerViewport.h b/Libraries/LibWeb/Layout/NavigableContainerViewport.h index bc756d1a9f18..cefa91cbed75 100644 --- a/Libraries/LibWeb/Layout/NavigableContainerViewport.h +++ b/Libraries/LibWeb/Layout/NavigableContainerViewport.h @@ -19,14 +19,13 @@ class NavigableContainerViewport final : public ReplacedBox { NavigableContainerViewport(DOM::Document&, HTML::NavigableContainer&, GC::Ref); virtual ~NavigableContainerViewport() override; - virtual void prepare_for_replaced_layout() override; - [[nodiscard]] HTML::NavigableContainer const& dom_node() const { return as(*ReplacedBox::dom_node()); } [[nodiscard]] HTML::NavigableContainer& dom_node() { return as(*ReplacedBox::dom_node()); } virtual GC::Ptr create_paintable() const override; private: + virtual CSS::SizeWithAspectRatio natural_size() const override; virtual void did_set_content_size() override; }; diff --git a/Libraries/LibWeb/Layout/Node.h b/Libraries/LibWeb/Layout/Node.h index 55f4ed23b2fc..b7583a05a9c6 100644 --- a/Libraries/LibWeb/Layout/Node.h +++ b/Libraries/LibWeb/Layout/Node.h @@ -116,6 +116,7 @@ class WEB_API Node virtual bool is_svg_foreign_object_box() const { return false; } virtual bool is_label() const { return false; } virtual bool is_replaced_box() const { return false; } + virtual bool is_textarea_box() const { return false; } virtual bool is_list_item_box() const { return false; } virtual bool is_list_item_marker_box() const { return false; } virtual bool is_fieldset_box() const { return false; } diff --git a/Libraries/LibWeb/Layout/RadioButton.cpp b/Libraries/LibWeb/Layout/RadioButton.cpp index 58d179ca0766..9e230e6dc9c9 100644 --- a/Libraries/LibWeb/Layout/RadioButton.cpp +++ b/Libraries/LibWeb/Layout/RadioButton.cpp @@ -17,9 +17,6 @@ GC_DEFINE_ALLOCATOR(RadioButton); RadioButton::RadioButton(DOM::Document& document, HTML::HTMLInputElement& element, GC::Ref style) : FormAssociatedLabelableNode(document, element, move(style)) { - set_natural_width(12); - set_natural_height(12); - set_natural_aspect_ratio(1); } RadioButton::~RadioButton() = default; diff --git a/Libraries/LibWeb/Layout/RadioButton.h b/Libraries/LibWeb/Layout/RadioButton.h index 461faa37d9f1..bdc45e305f68 100644 --- a/Libraries/LibWeb/Layout/RadioButton.h +++ b/Libraries/LibWeb/Layout/RadioButton.h @@ -20,6 +20,7 @@ class RadioButton final : public FormAssociatedLabelableNode { virtual ~RadioButton() override; private: + CSS::SizeWithAspectRatio compute_intrinsic_content_box_size() const override { return { 12, 12, {} }; } virtual GC::Ptr create_paintable() const override; }; diff --git a/Libraries/LibWeb/Layout/ReplacedBox.h b/Libraries/LibWeb/Layout/ReplacedBox.h index b3ccdee2f4a5..e6f3512fb27b 100644 --- a/Libraries/LibWeb/Layout/ReplacedBox.h +++ b/Libraries/LibWeb/Layout/ReplacedBox.h @@ -21,12 +21,11 @@ class ReplacedBox : public Box { GC::Ptr dom_node() const { return as(Node::dom_node()); } GC::Ptr dom_node() { return as(Node::dom_node()); } - virtual void prepare_for_replaced_layout() { } - virtual bool can_have_children() const override { return false; } private: virtual bool is_replaced_box() const final { return true; } + virtual bool has_intrinsic_content_box_size() const override { return true; } }; template<> diff --git a/Libraries/LibWeb/Layout/SVGSVGBox.cpp b/Libraries/LibWeb/Layout/SVGSVGBox.cpp index 0f198a15e384..6cdabf30e347 100644 --- a/Libraries/LibWeb/Layout/SVGSVGBox.cpp +++ b/Libraries/LibWeb/Layout/SVGSVGBox.cpp @@ -25,12 +25,10 @@ GC::Ptr SVGSVGBox::create_paintable() const return Painting::SVGSVGPaintable::create(*this); } -void SVGSVGBox::prepare_for_replaced_layout() +CSS::SizeWithAspectRatio SVGSVGBox::natural_size() const { - auto natural_metrics = SVG::SVGSVGElement::negotiate_natural_metrics(dom_node()); - set_natural_width(natural_metrics.width); - set_natural_height(natural_metrics.height); - set_natural_aspect_ratio(natural_metrics.aspect_ratio); + auto metrics = SVG::SVGSVGElement::negotiate_natural_metrics(dom_node()); + return { metrics.width, metrics.height, metrics.aspect_ratio }; } } diff --git a/Libraries/LibWeb/Layout/SVGSVGBox.h b/Libraries/LibWeb/Layout/SVGSVGBox.h index d631aff2040d..acb9f9520c02 100644 --- a/Libraries/LibWeb/Layout/SVGSVGBox.h +++ b/Libraries/LibWeb/Layout/SVGSVGBox.h @@ -26,12 +26,10 @@ class SVGSVGBox final : public ReplacedBox { virtual GC::Ptr create_paintable() const override; - virtual void prepare_for_replaced_layout() override; - private: + // virtual CSS::SizeWithAspectRatio compute_intrinsic_box_size() const override; + virtual CSS::SizeWithAspectRatio natural_size() const override; virtual bool is_svg_svg_box() const final { return true; } - - [[nodiscard]] Optional calculate_intrinsic_aspect_ratio() const; }; template<> diff --git a/Libraries/LibWeb/Layout/TextAreaBox.cpp b/Libraries/LibWeb/Layout/TextAreaBox.cpp new file mode 100644 index 000000000000..49c302fc48d8 --- /dev/null +++ b/Libraries/LibWeb/Layout/TextAreaBox.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2025, Jonathan Gamble + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Web::Layout { + +TextAreaBox::TextAreaBox(DOM::Document& document, GC::Ptr element, GC::Ref style) + : BlockContainer(document, element, move(style)) +{ +} + +CSS::SizeWithAspectRatio TextAreaBox::compute_intrinsic_content_box_size() const +{ + return { + .width = CSS::Length(dom_node().cols(), CSS::LengthUnit::Ch).to_px(*this), + .height = CSS::Length(dom_node().rows(), CSS::LengthUnit::Lh).to_px(*this), + .aspect_ratio = {} + }; +} + +} diff --git a/Libraries/LibWeb/Layout/TextAreaBox.h b/Libraries/LibWeb/Layout/TextAreaBox.h new file mode 100644 index 000000000000..f05ef4707778 --- /dev/null +++ b/Libraries/LibWeb/Layout/TextAreaBox.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2025, Jonathan Gamble + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::Layout { + +class TextAreaBox : public BlockContainer { + GC_CELL(TextAreaBox, BlockContainer); + +public: + TextAreaBox(DOM::Document&, GC::Ptr, GC::Ref); + + HTML::HTMLTextAreaElement const& dom_node() const { return static_cast(*Box::dom_node()); } + + virtual ~TextAreaBox() override = default; + +private: + virtual CSS::SizeWithAspectRatio compute_intrinsic_content_box_size() const override; + virtual bool has_intrinsic_content_box_size() const override { return true; } + virtual bool is_textarea_box() const override { return true; } +}; + +} diff --git a/Libraries/LibWeb/Layout/TextInputBox.cpp b/Libraries/LibWeb/Layout/TextInputBox.cpp new file mode 100644 index 000000000000..04e6fe93562c --- /dev/null +++ b/Libraries/LibWeb/Layout/TextInputBox.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2025, Jonathan Gamble + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Web::Layout { + +TextInputBox::TextInputBox(DOM::Document& document, GC::Ptr element, GC::Ref style) + : BlockContainer(document, element, move(style)) +{ +} + +CSS::SizeWithAspectRatio TextInputBox::compute_intrinsic_content_box_size() const +{ + return { + .width = CSS::Length(dom_node().size(), CSS::LengthUnit::Ch).to_px(*this), + // line-height + 2px because of padding in HTMLInputElement.cpp shadow DOM css + .height = computed_values().line_height() + CSSPixels(2), + .aspect_ratio = {} + }; +} + +} diff --git a/Libraries/LibWeb/Layout/TextInputBox.h b/Libraries/LibWeb/Layout/TextInputBox.h new file mode 100644 index 000000000000..b34fe032143b --- /dev/null +++ b/Libraries/LibWeb/Layout/TextInputBox.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2025, Jonathan Gamble + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::Layout { + +class TextInputBox : public BlockContainer { + GC_CELL(TextInputBox, BlockContainer); + +public: + TextInputBox(DOM::Document&, GC::Ptr, GC::Ref); + + HTML::HTMLInputElement const& dom_node() const { return static_cast(*Box::dom_node()); } + + virtual ~TextInputBox() override = default; + +private: + virtual CSS::SizeWithAspectRatio compute_intrinsic_content_box_size() const override; + virtual bool has_intrinsic_content_box_size() const override { return true; } +}; + +} diff --git a/Libraries/LibWeb/Layout/VideoBox.cpp b/Libraries/LibWeb/Layout/VideoBox.cpp index add74daaa274..254fd3d01550 100644 --- a/Libraries/LibWeb/Layout/VideoBox.cpp +++ b/Libraries/LibWeb/Layout/VideoBox.cpp @@ -37,18 +37,9 @@ HTML::HTMLVideoElement const& VideoBox::dom_node() const return static_cast(*ReplacedBox::dom_node()); } -void VideoBox::prepare_for_replaced_layout() +CSS::SizeWithAspectRatio VideoBox::natural_size() const { - CSSPixels width = dom_node().video_width(); - set_natural_width(width); - - CSSPixels height = dom_node().video_height(); - set_natural_height(height); - - if (width != 0 && height != 0) - set_natural_aspect_ratio(width / height); - else - set_natural_aspect_ratio({}); + return { dom_node().video_width(), dom_node().video_height(), {} }; } void VideoBox::did_set_viewport_rect(CSSPixelRect const&) diff --git a/Libraries/LibWeb/Layout/VideoBox.h b/Libraries/LibWeb/Layout/VideoBox.h index 063752de864d..fd040940604e 100644 --- a/Libraries/LibWeb/Layout/VideoBox.h +++ b/Libraries/LibWeb/Layout/VideoBox.h @@ -19,8 +19,6 @@ class VideoBox final GC_DECLARE_ALLOCATOR(VideoBox); public: - virtual void prepare_for_replaced_layout() override; - HTML::HTMLVideoElement& dom_node(); HTML::HTMLVideoElement const& dom_node() const; @@ -28,6 +26,7 @@ class VideoBox final private: VideoBox(DOM::Document&, DOM::Element&, GC::Ref); + virtual CSS::SizeWithAspectRatio natural_size() const override; // ^Document::ViewportClient virtual void did_set_viewport_rect(CSSPixelRect const&) final; diff --git a/Libraries/LibWeb/Painting/BackgroundPainting.cpp b/Libraries/LibWeb/Painting/BackgroundPainting.cpp index 2dae07d77a47..eba6e84eddbf 100644 --- a/Libraries/LibWeb/Painting/BackgroundPainting.cpp +++ b/Libraries/LibWeb/Painting/BackgroundPainting.cpp @@ -376,7 +376,7 @@ ResolvedBackground resolve_background_layers(Vector co } auto concrete_image_size = CSS::run_default_sizing_algorithm( specified_width, specified_height, - image.natural_width(), image.natural_height(), image.natural_aspect_ratio(), + { image.natural_width(), image.natural_height(), image.natural_aspect_ratio() }, background_positioning_area.size()); // If any of these are zero, the NaNs will pop up in the painting code. diff --git a/Libraries/LibWeb/SVG/SVGSVGElement.cpp b/Libraries/LibWeb/SVG/SVGSVGElement.cpp index e709480a527d..86102b19d2fe 100644 --- a/Libraries/LibWeb/SVG/SVGSVGElement.cpp +++ b/Libraries/LibWeb/SVG/SVGSVGElement.cpp @@ -252,11 +252,11 @@ GC::Ref SVGSVGElement::create_svg_transform() const return SVGTransform::create(realm()); } -SVGSVGElement::NaturalMetrics SVGSVGElement::negotiate_natural_metrics(SVG::SVGSVGElement const& svg_root) +CSS::SizeWithAspectRatio SVGSVGElement::negotiate_natural_metrics(SVG::SVGSVGElement const& svg_root) { // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS - NaturalMetrics natural_metrics; + CSS::SizeWithAspectRatio natural_metrics; // The intrinsic dimensions must also be determined from the width and height sizing properties. // If either width or height are not specified, the used value is the initial value 'auto'. diff --git a/Libraries/LibWeb/SVG/SVGSVGElement.h b/Libraries/LibWeb/SVG/SVGSVGElement.h index 22038dc61e98..7fb9c6dcdda3 100644 --- a/Libraries/LibWeb/SVG/SVGSVGElement.h +++ b/Libraries/LibWeb/SVG/SVGSVGElement.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -75,13 +76,7 @@ class SVGSVGElement final : public SVGGraphicsElement [[nodiscard]] RefPtr width_style_value_from_attribute() const; [[nodiscard]] RefPtr height_style_value_from_attribute() const; - struct NaturalMetrics { - Optional width; - Optional height; - Optional aspect_ratio; - }; - - static NaturalMetrics negotiate_natural_metrics(SVGSVGElement const&); + static CSS::SizeWithAspectRatio negotiate_natural_metrics(SVGSVGElement const&); private: SVGSVGElement(DOM::Document&, DOM::QualifiedName); diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/inline-block-contained-by-abspos-element.txt b/Tests/LibWeb/Layout/expected/block-and-inline/inline-block-contained-by-abspos-element.txt index c355e656d6e7..73bee9fa6bb4 100644 --- a/Tests/LibWeb/Layout/expected/block-and-inline/inline-block-contained-by-abspos-element.txt +++ b/Tests/LibWeb/Layout/expected/block-and-inline/inline-block-contained-by-abspos-element.txt @@ -3,7 +3,7 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not- BlockContainer at [10,10] [8+1+0 780 0+1+8] [8+1+0 2 0+1+8] children: not-inline BlockContainer
at [11,11] positioned [0+1+0 500 0+1+278] [0+1+0 0 0+1+0] children: not-inline BlockContainer

at [12,45.5] positioned [0+1+0 498 0+1+0] [33.5+1+0 344 0+1+33.5] [BFC] children: inline - frag 0 from BlockContainer start: 0, length: 0, rect: [13,46.5 496x342] baseline: 44.484375 + frag 0 from BlockContainer start: 0, length: 0, rect: [13,46.5 496x342] baseline: 329.484375 BlockContainer at [13,46.5] inline-block [0+1+0 496 0+1+0] [0+1+0 342 0+1+0] [BFC] children: inline frag 0 from TextNode start: 0, length: 9, rect: [13,46.5 246.515625x57] baseline: 43.484375 "Skew is a" diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/treat-percentage-width-as-auto-in-non-replaced-min-content-sizing.txt b/Tests/LibWeb/Layout/expected/block-and-inline/treat-percentage-width-as-auto-in-non-replaced-min-content-sizing.txt index 995d3f58b3e6..be4fe4c33a39 100644 --- a/Tests/LibWeb/Layout/expected/block-and-inline/treat-percentage-width-as-auto-in-non-replaced-min-content-sizing.txt +++ b/Tests/LibWeb/Layout/expected/block-and-inline/treat-percentage-width-as-auto-in-non-replaced-min-content-sizing.txt @@ -1,6 +1,6 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not-inline BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 66 0+0+0] [BFC] children: inline - frag 0 from BlockContainer start: 0, length: 0, rect: [8,8 38.1875x50] baseline: 21.796875 + frag 0 from BlockContainer start: 0, length: 0, rect: [8,8 38.1875x50] baseline: 39.796875 BlockContainer at [8,8] inline-block [8+0+0 38.1875 0+0+8] [8+0+0 50 0+0+8] [BFC] children: not-inline BlockContainer
at [8,8] [0+0+0 38.1875 0+0+0] [0+0+0 20 0+0+0] children: inline frag 0 from TextNode start: 0, length: 4, rect: [8,8 31.140625x18] baseline: 13.796875 diff --git a/Tests/LibWeb/Layout/expected/css-line-height-zero.txt b/Tests/LibWeb/Layout/expected/css-line-height-zero.txt index f087f3d62cad..ac60214db5d9 100644 --- a/Tests/LibWeb/Layout/expected/css-line-height-zero.txt +++ b/Tests/LibWeb/Layout/expected/css-line-height-zero.txt @@ -1,7 +1,7 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not-inline BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 84 0+0+0] [BFC] children: not-inline BlockContainer at [8,8] [8+0+0 784 0+0+8] [8+0+0 68 0+0+8] children: not-inline - BlockContainer at [9,9] [0+1+0 200 0+1+582] [0+1+0 20 0+1+0] children: not-inline + TextInputBox at [9,9] [0+1+0 200 0+1+582] [0+1+0 20 0+1+0] children: not-inline Box
at [11,10] flex-container(row) [0+0+2 196 2+0+0] [0+0+1 18 1+0+0] [FFC] children: not-inline BlockContainer
at [11,10] flex-item [0+0+0 196 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline frag 0 from TextNode start: 0, length: 11, rect: [11,10 91.953125x18] baseline: 13.796875 @@ -9,7 +9,7 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not- TextNode <#text> (not painted) BlockContainer <(anonymous)> at [8,30] [0+0+0 784 0+0+0] [0+0+0 0 0+0+0] children: inline TextNode <#text> (not painted) - BlockContainer at [9,31] [0+1+0 200 0+1+582] [0+1+0 16 0+1+0] children: not-inline + TextInputBox at [9,31] [0+1+0 200 0+1+582] [0+1+0 16 0+1+0] children: not-inline Box
at [11,32] flex-container(row) [0+0+2 196 2+0+0] [0+0+1 16 1+0+0] [FFC] children: not-inline BlockContainer
at [11,40] flex-item [0+0+0 196 0+0+0] [0+0+0 0 0+0+0] [BFC] children: inline frag 0 from TextNode start: 0, length: 11, rect: [11,40 91.953125x0] baseline: 4.796875 @@ -17,7 +17,7 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not- TextNode <#text> (not painted) BlockContainer <(anonymous)> at [8,48] [0+0+0 784 0+0+0] [0+0+0 0 0+0+0] children: inline TextNode <#text> (not painted) - BlockContainer at [9,49] [0+1+0 200 0+1+582] [0+1+0 26 0+1+0] children: not-inline + TextInputBox at [9,49] [0+1+0 200 0+1+582] [0+1+0 26 0+1+0] children: not-inline Box
at [11,50] flex-container(row) [0+0+2 196 2+0+0] [0+0+1 24 1+0+0] [FFC] children: not-inline BlockContainer
at [11,50] flex-item [0+0+0 196 0+0+0] [0+0+0 24 0+0+0] [BFC] children: inline frag 0 from TextNode start: 0, length: 11, rect: [11,50 91.953125x24] baseline: 16.796875 @@ -29,17 +29,17 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not- ViewportPaintable (Viewport<#document>) [0,0 800x600] PaintableWithLines (BlockContainer) [0,0 800x84] PaintableWithLines (BlockContainer) [8,8 784x68] - PaintableWithLines (BlockContainer#a) [8,8 202x22] + PaintableWithLines (TextInputBox#a) [8,8 202x22] PaintableBox (Box
) [9,9 200x20] PaintableWithLines (BlockContainer
) [11,10 196x18] TextPaintable (TextNode<#text>) PaintableWithLines (BlockContainer(anonymous)) [8,30 784x0] - PaintableWithLines (BlockContainer#b) [8,30 202x18] overflow: [9,31 200x18] + PaintableWithLines (TextInputBox#b) [8,30 202x18] overflow: [9,31 200x18] PaintableBox (Box
) [9,31 200x18] PaintableWithLines (BlockContainer
) [11,40 196x0] TextPaintable (TextNode<#text>) PaintableWithLines (BlockContainer(anonymous)) [8,48 784x0] - PaintableWithLines (BlockContainer#c) [8,48 202x28] + PaintableWithLines (TextInputBox#c) [8,48 202x28] PaintableBox (Box
) [9,49 200x26] PaintableWithLines (BlockContainer
) [11,50 196x24] TextPaintable (TextNode<#text>) diff --git a/Tests/LibWeb/Layout/expected/empty-editable-shows-cursor.txt b/Tests/LibWeb/Layout/expected/empty-editable-shows-cursor.txt index 2a3ebec554bc..92b3c147eaac 100644 --- a/Tests/LibWeb/Layout/expected/empty-editable-shows-cursor.txt +++ b/Tests/LibWeb/Layout/expected/empty-editable-shows-cursor.txt @@ -1,34 +1,34 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not-inline - BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 52 0+0+0] [BFC] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 59 0+0+0] [BFC] children: not-inline BlockContainer <(anonymous)> at [0,0] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline TextNode <#text> (not painted) - BlockContainer at [8,8] [8+0+0 784 0+0+8] [8+0+0 36 0+0+8] children: not-inline + BlockContainer at [8,8] [8+0+0 784 0+0+8] [8+0+0 43 0+0+8] children: not-inline BlockContainer <(anonymous)> at [8,8] [0+0+0 784 0+0+0] [0+0+0 0 0+0+0] children: inline TextNode <#text> (not painted) BlockContainer
at [8,8] [0+0+0 784 0+0+0] [0+0+0 0 0+0+0] children: not-inline - BlockContainer <(anonymous)> at [8,8] [0+0+0 784 0+0+0] [0+0+0 36 0+0+0] children: inline - frag 0 from BlockContainer start: 0, length: 0, rect: [9,9 200x20] baseline: 14.796875 - frag 1 from TextNode start: 0, length: 1, rect: [210,9 8x18] baseline: 13.796875 + BlockContainer <(anonymous)> at [8,8] [0+0+0 784 0+0+0] [0+0+0 43 0+0+0] children: inline + frag 0 from TextInputBox start: 0, length: 0, rect: [9,30 200x20] baseline: 14.796875 + frag 1 from TextNode start: 0, length: 1, rect: [210,30 8x18] baseline: 13.796875 " " - frag 2 from BlockContainer start: 0, length: 0, rect: [221,11 160x30] baseline: 14.390625 - frag 3 from TextNode start: 0, length: 1, rect: [384,9 8x18] baseline: 13.796875 + frag 2 from TextAreaBox start: 0, length: 0, rect: [221,11 160x30] baseline: 36 + frag 3 from TextNode start: 0, length: 1, rect: [384,30 8x18] baseline: 13.796875 " " - frag 4 from BlockContainer start: 0, length: 0, rect: [393,9 200x20] baseline: 14.796875 - frag 5 from TextNode start: 0, length: 1, rect: [594,9 8x18] baseline: 13.796875 + frag 4 from TextInputBox start: 0, length: 0, rect: [393,30 200x20] baseline: 14.796875 + frag 5 from TextNode start: 0, length: 1, rect: [594,30 8x18] baseline: 13.796875 " " - frag 6 from BlockContainer start: 0, length: 0, rect: [605,11 160x30] baseline: 14.390625 + frag 6 from TextAreaBox start: 0, length: 0, rect: [605,11 160x30] baseline: 36 TextNode <#text> (not painted) - BlockContainer at [9,9] inline-block [0+1+0 200 0+1+0] [0+1+0 20 0+1+0] [BFC] children: not-inline - Box
at [11,10] flex-container(row) [0+0+2 196 2+0+0] [0+0+1 18 1+0+0] [FFC] children: not-inline - BlockContainer
at [11,10] flex-item [0+0+0 0 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline - frag 0 from TextNode start: 0, length: 0, rect: [11,10 0x18] baseline: 13.796875 + TextInputBox at [9,30] inline-block [0+1+0 200 0+1+0] [0+1+0 20 0+1+0] [BFC] children: not-inline + Box
at [11,31] flex-container(row) [0+0+2 196 2+0+0] [0+0+1 18 1+0+0] [FFC] children: not-inline + BlockContainer
at [11,31] flex-item [0+0+0 0 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline + frag 0 from TextNode start: 0, length: 0, rect: [11,31 0x18] baseline: 13.796875 TextNode <#text> (not painted) - BlockContainer
at [11,10] flex-item [0+0+0 196 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline - frag 0 from TextNode start: 0, length: 5, rect: [11,10 36.84375x18] baseline: 13.796875 + BlockContainer
at [11,31] flex-item [0+0+0 196 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline + frag 0 from TextNode start: 0, length: 5, rect: [11,31 36.84375x18] baseline: 13.796875 "hello" TextNode <#text> (not painted) TextNode <#text> (not painted) - BlockContainer
+ + + diff --git a/Tests/LibWeb/Ref/expected/wpt-import/css/CSS2/visudet/inline-block-baseline-001-ref.xht b/Tests/LibWeb/Ref/expected/wpt-import/css/CSS2/visudet/inline-block-baseline-001-ref.xht new file mode 100644 index 000000000000..9f14c821b332 --- /dev/null +++ b/Tests/LibWeb/Ref/expected/wpt-import/css/CSS2/visudet/inline-block-baseline-001-ref.xht @@ -0,0 +1,22 @@ + + + + + + Vertical-align: baseline of an inline-block depends on 'overflow' + + + + + + + + +

+ All the words are aligned on the same baseline. +

+ + diff --git a/Tests/LibWeb/Ref/input/wpt-import/css/CSS2/visudet/inline-block-baseline-002.xht b/Tests/LibWeb/Ref/input/wpt-import/css/CSS2/visudet/inline-block-baseline-002.xht new file mode 100644 index 000000000000..7dfb7642fc01 --- /dev/null +++ b/Tests/LibWeb/Ref/input/wpt-import/css/CSS2/visudet/inline-block-baseline-002.xht @@ -0,0 +1,27 @@ + + + + + + Vertical-align: baseline of an inline-block depends on 'overflow' + + + + + + + + + + + + +

+ All the  
words
are aligned on the same baseline. +

+ + diff --git a/Tests/LibWeb/Ref/input/wpt-import/css/css-grid/alignment/grid-align-baseline-005.html b/Tests/LibWeb/Ref/input/wpt-import/css/css-grid/alignment/grid-align-baseline-005.html new file mode 100644 index 000000000000..397c96f999fb --- /dev/null +++ b/Tests/LibWeb/Ref/input/wpt-import/css/css-grid/alignment/grid-align-baseline-005.html @@ -0,0 +1,8 @@ + + + +

Test passes if there is a filled green square and no red.

+
+ +

+
diff --git a/Tests/LibWeb/Ref/input/wpt-import/css/css-grid/grid-items/percentage-size-indefinite-replaced.html b/Tests/LibWeb/Ref/input/wpt-import/css/css-grid/grid-items/percentage-size-indefinite-replaced.html new file mode 100644 index 000000000000..b887228cad5c --- /dev/null +++ b/Tests/LibWeb/Ref/input/wpt-import/css/css-grid/grid-items/percentage-size-indefinite-replaced.html @@ -0,0 +1,8 @@ + + + + +

Test passes if there is a filled green square.

+
+ +
diff --git a/Tests/LibWeb/Ref/input/wpt-import/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-dimension-attributes.html b/Tests/LibWeb/Ref/input/wpt-import/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-dimension-attributes.html new file mode 100644 index 000000000000..b2a697eb3b62 --- /dev/null +++ b/Tests/LibWeb/Ref/input/wpt-import/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-dimension-attributes.html @@ -0,0 +1,29 @@ + +Canvas width and height attributes are used as the surface size, and also to infer aspect ratio + + + + + + +

Test passes if there is a filled green square and no red.

+
+ + + + +
diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-flexbox/canvas-dynamic-change-001.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-flexbox/canvas-dynamic-change-001.txt index 6f72beac63d7..d314312166c7 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-flexbox/canvas-dynamic-change-001.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-flexbox/canvas-dynamic-change-001.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail .flexbox 1 \ No newline at end of file +1 Pass +Pass .flexbox 1 \ No newline at end of file