diff --git a/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index df2d00cf7470..d30137fa6a91 100644 --- a/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -496,30 +496,96 @@ void BlockFormattingContext::resolve_used_height_if_treated_as_auto(Box const& b height = max(height, calculate_inner_height(box, available_space, computed_values.min_height())); } + // https://quirks.spec.whatwg.org/#the-html-element-fills-the-viewport-quirk + // 3.6. The html element fills the viewport quirk + // In quirks mode, if the document element element matches the following conditions: if (box.document().in_quirks_mode() + // - element is an html element. && box.dom_node() && box.dom_node()->is_html_html_element() - && box.computed_values().height().is_auto()) { - // 3.6. The html element fills the viewport quirk - // https://quirks.spec.whatwg.org/#the-html-element-fills-the-viewport-quirk - // FIXME: Handle vertical writing mode. + // - The computed value of the width property of element is auto and element has a vertical writing mode, + && ((box.computed_values().width().is_auto() && box.text_flow_direction() == Gfx::Orientation::Vertical) + // or the computed value of the height property of element is auto and element has a horizontal writing mode. + || (box.computed_values().height().is_auto() && box.text_flow_direction() == Gfx::Orientation::Horizontal))) { + // ...then element must have its border box size in the block flow direction set using the following algorithm: // 1. Let margins be sum of the used values of the margin-left and margin-right properties of element // if element has a vertical writing mode, otherwise let margins be the sum of the used values of // the margin-top and margin-bottom properties of element. - auto margins = box_state.margin_top + box_state.margin_bottom; + CSSPixels margins; + if (box.text_flow_direction() == Gfx::Orientation::Vertical) { + margins = box_state.margin_left + box_state.margin_right; + } else { + margins = box_state.margin_top + box_state.margin_bottom; + } // 2. Let size be the size of the initial containing block in the block flow direction minus margins. auto size = box_state.containing_block_used_values()->content_height() - margins; // 3. Return the bigger value of size and the normal border box size the element would have // according to the CSS specification. - height = max(size, height); + // NOTE: We deal in content box size, not border box size so we need to subtract borders and padding here. + CSSPixels borders; + if (box.text_flow_direction() == Gfx::Orientation::Vertical) { + borders = computed_values.border_left().width + computed_values.border_right().width + computed_values.padding().left().to_px(box, box_state.containing_block_used_values()->content_width()) + computed_values.padding().right().to_px(box, box_state.containing_block_used_values()->content_width()); + } else { + borders = computed_values.border_top().width + computed_values.border_bottom().width + computed_values.padding().top().to_px(box, box_state.containing_block_used_values()->content_height()) + computed_values.padding().bottom().to_px(box, box_state.containing_block_used_values()->content_height()); + } + height = max(size - borders, height); // NOTE: The height of the root element when affected by this quirk is considered to be definite. box_state.set_has_definite_height(true); } + // https://quirks.spec.whatwg.org/#the-body-element-fills-the-html-element-quirk + // 3.7. The body element fills the html element quirk + + // In quirks mode, if the document’s body element body is not null and if it matches the following conditions: + if (box.document().in_quirks_mode() + && box.dom_node() + && box.dom_node() == box.document().body() + // - The computed value of the width property of body is auto and body has a vertical writing mode, + && ((box.computed_values().width().is_auto() && box.text_flow_direction() == Gfx::Orientation::Vertical) + // or the computed value of the height property of body is auto and body has a horizontal writing mode. + || (box.computed_values().height().is_auto() && box.text_flow_direction() == Gfx::Orientation::Horizontal)) + // - The computed value of the position property of body is not absolute or fixed. + && !(box.computed_values().position() == CSS::Positioning::Absolute || box.computed_values().position() == CSS::Positioning::Fixed) + // - The computed value of the float property of body is none. + && box.computed_values().float_() == CSS::Float::None + // - body is not an inline-level element. + && !box.is_inline() + // - body is not a multi-column spanning element. + // FIXME: Implement this. Note: multiple columns seem to be required for this, so just reading the computed value of column-span is not enough. + ) { + // ...then body must have its border box size in the block flow direction set using the following algorithm: + + // 1. Let margins be the sum of the used values of the margin-left and margin-right properties of body if body has a vertical writing mode, otherwise let margins be the sum of the + // used values of the margin-top and margin-bottom properties of body. + CSSPixels margins; + if (box.text_flow_direction() == Gfx::Orientation::Vertical) { + margins = box_state.margin_left + box_state.margin_right; + } else { + margins = box_state.margin_top + box_state.margin_bottom; + } + + // 2. Let size be the size of body’s parent element’s content box in the block flow direction minus margins. + auto size = box_state.containing_block_used_values()->content_height() - margins; + + // 3. Return the bigger value of size and the normal border box size the element would have according to the CSS specification. + // NOTE: We deal in content box size, not border box size so we need to subtract borders and padding here. + CSSPixels borders; + if (box.text_flow_direction() == Gfx::Orientation::Vertical) { + borders = computed_values.border_left().width + computed_values.border_right().width + computed_values.padding().left().to_px(box, box_state.containing_block_used_values()->content_width()) + computed_values.padding().right().to_px(box, box_state.containing_block_used_values()->content_width()); + } else { + borders = computed_values.border_top().width + computed_values.border_bottom().width + computed_values.padding().top().to_px(box, box_state.containing_block_used_values()->content_height()) + computed_values.padding().bottom().to_px(box, box_state.containing_block_used_values()->content_height()); + } + height = max(size - borders, height); + + // What should happen if the html and the body have different writing modes? + // NOTE: Since that is probably rare, we'll just treat the body's height as definite for now when affected by this quirk. + box_state.set_has_definite_height(true); + } + box_state.set_content_height(height); } diff --git a/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Libraries/LibWeb/Layout/BlockFormattingContext.h index c4ffd7a5125b..c291ffc83c80 100644 --- a/Libraries/LibWeb/Layout/BlockFormattingContext.h +++ b/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -81,6 +81,8 @@ class BlockFormattingContext : public FormattingContext { void measure_scrollable_overflow(Box const&, CSSPixels& bottom_edge, CSSPixels& right_edge) const; + Gfx::Orientation block_flow_direction() const; + enum class FloatSide { Left, Right, diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index d513d84db76c..17672a9cd5cf 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -1280,4 +1280,20 @@ CSS::UserSelect Node::user_select_used_value() const return computed_value; } +// https://drafts.csswg.org/css-writing-modes-4/#text-flow +Gfx::Orientation Node::text_flow_direction() const +{ + switch (computed_values().writing_mode()) { + case CSS::WritingMode::HorizontalTb: + return Gfx::Orientation::Horizontal; + case CSS::WritingMode::VerticalRl: + case CSS::WritingMode::VerticalLr: + case CSS::WritingMode::SidewaysRl: + case CSS::WritingMode::SidewaysLr: + return Gfx::Orientation::Vertical; + default: + VERIFY_NOT_REACHED(); + } +} + } diff --git a/Libraries/LibWeb/Layout/Node.h b/Libraries/LibWeb/Layout/Node.h index fcb87cd09a9a..16efc97ba6cb 100644 --- a/Libraries/LibWeb/Layout/Node.h +++ b/Libraries/LibWeb/Layout/Node.h @@ -191,6 +191,9 @@ class Node // https://drafts.csswg.org/css-ui/#propdef-user-select CSS::UserSelect user_select_used_value() const; + // https://drafts.csswg.org/css-writing-modes-4/#text-flow + Gfx::Orientation text_flow_direction() const; + [[nodiscard]] bool has_been_wrapped_in_table_wrapper() const { return m_has_been_wrapped_in_table_wrapper; } void set_has_been_wrapped_in_table_wrapper(bool value) { m_has_been_wrapped_in_table_wrapper = value; } diff --git a/Libraries/LibWeb/Painting/BackgroundPainting.cpp b/Libraries/LibWeb/Painting/BackgroundPainting.cpp index 8a8c456afc50..9a1037000aa1 100644 --- a/Libraries/LibWeb/Painting/BackgroundPainting.cpp +++ b/Libraries/LibWeb/Painting/BackgroundPainting.cpp @@ -34,7 +34,7 @@ static RefPtr compute_text_clip_paths(PaintContext& context, Painta fragment_absolute_rect.x().to_float(), fragment_absolute_rect.y().to_float() + fragment.baseline().to_float(), } * scale; - display_list_recorder.draw_text_run(baseline_start, *glyph_run, Gfx::Color::Black, fragment_absolute_device_rect.to_type(), scale, fragment.orientation()); + display_list_recorder.draw_text_run(baseline_start, *glyph_run, Gfx::Color::Black, fragment_absolute_device_rect.to_type(), scale, paintable.layout_node().text_flow_direction()); }; paintable.for_each_in_inclusive_subtree([&](auto& paintable) { diff --git a/Libraries/LibWeb/Painting/PaintableBox.cpp b/Libraries/LibWeb/Painting/PaintableBox.cpp index 6e4d6dbd0c8e..5c6dab2a25a8 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -692,14 +692,14 @@ void paint_text_fragment(PaintContext& context, TextPaintable const& paintable, fragment_absolute_rect.x().to_float(), fragment_absolute_rect.y().to_float() + fragment.baseline().to_float(), } * scale; - painter.draw_text_run(baseline_start, *glyph_run, paintable.computed_values().webkit_text_fill_color(), fragment_absolute_device_rect.to_type(), scale, fragment.orientation()); + painter.draw_text_run(baseline_start, *glyph_run, paintable.computed_values().webkit_text_fill_color(), fragment_absolute_device_rect.to_type(), scale, paintable.layout_node().text_flow_direction()); auto selection_rect = context.enclosing_device_rect(fragment.selection_rect()).to_type(); if (!selection_rect.is_empty()) { painter.fill_rect(selection_rect, CSS::SystemColor::highlight(paintable.computed_values().color_scheme())); DisplayListRecorderStateSaver saver(painter); painter.add_clip_rect(selection_rect); - painter.draw_text_run(baseline_start, *glyph_run, CSS::SystemColor::highlight_text(paintable.computed_values().color_scheme()), fragment_absolute_device_rect.to_type(), scale, fragment.orientation()); + painter.draw_text_run(baseline_start, *glyph_run, CSS::SystemColor::highlight_text(paintable.computed_values().color_scheme()), fragment_absolute_device_rect.to_type(), scale, paintable.layout_node().text_flow_direction()); } paint_text_decoration(context, paintable, fragment); diff --git a/Libraries/LibWeb/Painting/PaintableFragment.cpp b/Libraries/LibWeb/Painting/PaintableFragment.cpp index 147776675c0b..4d65ea526a82 100644 --- a/Libraries/LibWeb/Painting/PaintableFragment.cpp +++ b/Libraries/LibWeb/Painting/PaintableFragment.cpp @@ -42,7 +42,7 @@ int PaintableFragment::text_index_at(CSSPixelPoint position) const return 0; CSSPixels relative_inline_offset = [&]() { - switch (orientation()) { + switch (layout_node().text_flow_direction()) { case Gfx::Orientation::Horizontal: return position.x() - absolute_rect().x(); case Gfx::Orientation::Vertical: @@ -102,7 +102,7 @@ CSSPixelRect PaintableFragment::range_rect(size_t start_offset, size_t end_offse auto pixel_width_of_selection = CSSPixels::nearest_value_for(font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment))) + 1; auto rect = absolute_rect(); - switch (orientation()) { + switch (layout_node().text_flow_direction()) { case Gfx::Orientation::Horizontal: rect.set_x(rect.x() + pixel_distance_to_first_selected_character); rect.set_width(pixel_width_of_selection); @@ -128,7 +128,7 @@ CSSPixelRect PaintableFragment::range_rect(size_t start_offset, size_t end_offse auto pixel_width_of_selection = CSSPixels::nearest_value_for(font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment))) + 1; auto rect = absolute_rect(); - switch (orientation()) { + switch (layout_node().text_flow_direction()) { case Gfx::Orientation::Horizontal: rect.set_x(rect.x() + pixel_distance_to_first_selected_character); rect.set_width(pixel_width_of_selection); @@ -154,7 +154,7 @@ CSSPixelRect PaintableFragment::range_rect(size_t start_offset, size_t end_offse auto pixel_width_of_selection = CSSPixels::nearest_value_for(font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment))) + 1; auto rect = absolute_rect(); - switch (orientation()) { + switch (layout_node().text_flow_direction()) { case Gfx::Orientation::Horizontal: rect.set_x(rect.x() + pixel_distance_to_first_selected_character); rect.set_width(pixel_width_of_selection); @@ -172,21 +172,6 @@ CSSPixelRect PaintableFragment::range_rect(size_t start_offset, size_t end_offse return {}; } -Gfx::Orientation PaintableFragment::orientation() const -{ - switch (m_writing_mode) { - case CSS::WritingMode::HorizontalTb: - return Gfx::Orientation::Horizontal; - case CSS::WritingMode::VerticalRl: - case CSS::WritingMode::VerticalLr: - case CSS::WritingMode::SidewaysRl: - case CSS::WritingMode::SidewaysLr: - return Gfx::Orientation::Vertical; - default: - VERIFY_NOT_REACHED(); - } -} - CSSPixelRect PaintableFragment::selection_rect() const { if (auto const* focused_element = paintable().document().focused_element(); focused_element && is(*focused_element)) { diff --git a/Tests/LibWeb/Layout/expected/Element-insertAdjacentHTML.txt b/Tests/LibWeb/Layout/expected/Element-insertAdjacentHTML.txt index eb78d5c02865..b17d73b2e561 100644 --- a/Tests/LibWeb/Layout/expected/Element-insertAdjacentHTML.txt +++ b/Tests/LibWeb/Layout/expected/Element-insertAdjacentHTML.txt @@ -1,6 +1,6 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline - BlockContainer at (8,8) content-size 784x211 children: not-inline + BlockContainer at (8,8) content-size 784x584 children: not-inline BlockContainer
at (8,8) content-size 784x17 children: inline frag 0 from TextNode start: 0, length: 38, rect: [8,8 312.4375x17] baseline: 13.296875 "This is inserted before the container." @@ -28,7 +28,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline ViewportPaintable (Viewport<#document>) [0,0 800x600] PaintableWithLines (BlockContainer) [0,0 800x600] - PaintableWithLines (BlockContainer) [8,8 784x211] + PaintableWithLines (BlockContainer) [8,8 784x584] PaintableWithLines (BlockContainer
) [8,8 784x17] TextPaintable (TextNode<#text>) PaintableWithLines (BlockContainer
#container) [28,45 744x137] diff --git a/Tests/LibWeb/Layout/expected/abspos-box-bottom-with-max-height.txt b/Tests/LibWeb/Layout/expected/abspos-box-bottom-with-max-height.txt index 9e0ffe7c4548..3c56fe210df1 100644 --- a/Tests/LibWeb/Layout/expected/abspos-box-bottom-with-max-height.txt +++ b/Tests/LibWeb/Layout/expected/abspos-box-bottom-with-max-height.txt @@ -1,10 +1,10 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline - BlockContainer at (8,8) content-size 784x0 children: inline + BlockContainer at (8,8) content-size 784x584 children: inline BlockContainer
at (8,550) content-size 100x50 positioned [BFC] children: not-inline TextNode <#text> ViewportPaintable (Viewport<#document>) [0,0 800x600] PaintableWithLines (BlockContainer) [0,0 800x600] - PaintableWithLines (BlockContainer) [8,8 784x0] + PaintableWithLines (BlockContainer) [8,8 784x584] PaintableWithLines (BlockContainer
) [8,550 100x50] diff --git a/Tests/LibWeb/Layout/expected/abspos-not-replaced-multiple-auto-variants.txt b/Tests/LibWeb/Layout/expected/abspos-not-replaced-multiple-auto-variants.txt index b4e1dfd50e5a..83ab9dfff5c6 100644 --- a/Tests/LibWeb/Layout/expected/abspos-not-replaced-multiple-auto-variants.txt +++ b/Tests/LibWeb/Layout/expected/abspos-not-replaced-multiple-auto-variants.txt @@ -1,6 +1,6 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline - BlockContainer at (1,1) content-size 798x600 [BFC] children: not-inline - BlockContainer at (10,10) content-size 780x0 children: inline + BlockContainer at (1,1) content-size 798x598 [BFC] children: not-inline + BlockContainer at (10,10) content-size 780x580 children: inline TextNode <#text> BlockContainer at (6,6) content-size 10x10 positioned [BFC] children: not-inline TextNode <#text> @@ -9,9 +9,9 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer at (11,11) content-size 10x10 positioned [BFC] children: not-inline TextNode <#text> -ViewportPaintable (Viewport<#document>) [0,0 800x600] overflow: [0,0 800x602] - PaintableWithLines (BlockContainer) [0,0 800x602] - PaintableWithLines (BlockContainer) [9,9 782x2] +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x600] + PaintableWithLines (BlockContainer) [9,9 782x582] PaintableWithLines (BlockContainer
.only-t-l) [5,5 12x12] PaintableWithLines (BlockContainer
.only-mt-ml) [15,15 12x12] PaintableWithLines (BlockContainer
.both) [10,10 12x12] diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/block-with-hidden-overflow-after-sibling-float.txt b/Tests/LibWeb/Layout/expected/block-and-inline/block-with-hidden-overflow-after-sibling-float.txt index 4e1d63cd6dfc..65d2867236e0 100644 --- a/Tests/LibWeb/Layout/expected/block-and-inline/block-with-hidden-overflow-after-sibling-float.txt +++ b/Tests/LibWeb/Layout/expected/block-and-inline/block-with-hidden-overflow-after-sibling-float.txt @@ -1,6 +1,6 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline - BlockContainer at (8,8) content-size 784x34 children: not-inline + BlockContainer at (8,8) content-size 784x584 children: not-inline BlockContainer
at (8,8) content-size 784x34 children: not-inline BlockContainer <(anonymous)> at (8,8) content-size 784x0 children: inline TextNode <#text> @@ -44,7 +44,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline ViewportPaintable (Viewport<#document>) [0,0 800x600] PaintableWithLines (BlockContainer) [0,0 800x600] - PaintableWithLines (BlockContainer) [8,8 784x34] + PaintableWithLines (BlockContainer) [8,8 784x584] PaintableWithLines (BlockContainer
) [8,8 784x34] PaintableWithLines (BlockContainer(anonymous)) [8,8 784x0] PaintableWithLines (BlockContainer
) [8,8 784x0] diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/box-with-clearance-and-margin-top.txt b/Tests/LibWeb/Layout/expected/block-and-inline/box-with-clearance-and-margin-top.txt index 8bffb30adc75..7c2925a9f0d0 100644 --- a/Tests/LibWeb/Layout/expected/block-and-inline/box-with-clearance-and-margin-top.txt +++ b/Tests/LibWeb/Layout/expected/block-and-inline/box-with-clearance-and-margin-top.txt @@ -1,14 +1,14 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline - BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline - BlockContainer at (8,8) content-size 784x110 children: not-inline + BlockContainer at (0,0) content-size 800x692 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x584 children: not-inline BlockContainer
at (8,8) content-size 784x110 children: not-inline BlockContainer at (8,8) content-size 100x100 floating [BFC] children: not-inline BlockContainer at (8,108) content-size 10x10 children: not-inline BlockContainer at (8,218) content-size 49x49 floating [BFC] children: not-inline -ViewportPaintable (Viewport<#document>) [0,0 800x600] - PaintableWithLines (BlockContainer) [0,0 800x600] - PaintableWithLines (BlockContainer) [8,8 784x110] overflow: [8,8 784x259] +ViewportPaintable (Viewport<#document>) [0,0 800x600] overflow: [0,0 800x692] + PaintableWithLines (BlockContainer) [0,0 800x692] + PaintableWithLines (BlockContainer) [8,8 784x584] PaintableWithLines (BlockContainer
) [8,8 784x110] overflow: [8,8 784x259] PaintableWithLines (BlockContainer
.square.white) [8,8 100x100] PaintableWithLines (BlockContainer
.clearfix) [8,108 10x10] diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/button-baseline-align.txt b/Tests/LibWeb/Layout/expected/block-and-inline/button-baseline-align.txt index 284874c8acd1..c7d84c56008f 100644 --- a/Tests/LibWeb/Layout/expected/block-and-inline/button-baseline-align.txt +++ b/Tests/LibWeb/Layout/expected/block-and-inline/button-baseline-align.txt @@ -1,6 +1,6 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline - BlockContainer at (8,8) content-size 784x48 children: inline + BlockContainer at (8,8) content-size 784x584 children: inline frag 0 from BlockContainer start: 0, length: 0, rect: [8,8 61.1875x48] baseline: 36 BlockContainer at (8,8) content-size 61.1875x48 inline-block [BFC] children: inline frag 0 from BlockContainer start: 0, length: 0, rect: [9,27 17.828125x22] baseline: 18 @@ -23,7 +23,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline ViewportPaintable (Viewport<#document>) [0,0 800x600] PaintableWithLines (BlockContainer) [0,0 800x600] - PaintableWithLines (BlockContainer) [8,8 784x48] + PaintableWithLines (BlockContainer) [8,8 784x584] PaintableWithLines (BlockContainer
.ib) [8,8 61.1875x48] PaintableWithLines (BlockContainer
.label) [8,26 19.828125x24] TextPaintable (TextNode<#text>) diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/button-image-only.txt b/Tests/LibWeb/Layout/expected/block-and-inline/button-image-only.txt index 820fc197eafb..e7490b75fb2c 100644 --- a/Tests/LibWeb/Layout/expected/block-and-inline/button-image-only.txt +++ b/Tests/LibWeb/Layout/expected/block-and-inline/button-image-only.txt @@ -1,6 +1,6 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline - BlockContainer at (8,8) content-size 784x424 children: inline + BlockContainer at (8,8) content-size 784x584 children: inline frag 0 from BlockContainer start: 0, length: 0, rect: [13,10 420x420] baseline: 422 BlockContainer