Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibWeb: Improve html/body size quirks #3271

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 72 additions & 6 deletions Libraries/LibWeb/Layout/BlockFormattingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/Layout/BlockFormattingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 16 additions & 0 deletions Libraries/LibWeb/Layout/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}

}
3 changes: 3 additions & 0 deletions Libraries/LibWeb/Layout/Node.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWeb/Painting/BackgroundPainting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ static RefPtr<DisplayList> 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<int>(), scale, fragment.orientation());
display_list_recorder.draw_text_run(baseline_start, *glyph_run, Gfx::Color::Black, fragment_absolute_device_rect.to_type<int>(), scale, paintable.layout_node().text_flow_direction());
};

paintable.for_each_in_inclusive_subtree([&](auto& paintable) {
Expand Down
4 changes: 2 additions & 2 deletions Libraries/LibWeb/Painting/PaintableBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(), scale, fragment.orientation());
painter.draw_text_run(baseline_start, *glyph_run, paintable.computed_values().webkit_text_fill_color(), fragment_absolute_device_rect.to_type<int>(), scale, paintable.layout_node().text_flow_direction());

auto selection_rect = context.enclosing_device_rect(fragment.selection_rect()).to_type<int>();
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<int>(), 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<int>(), scale, paintable.layout_node().text_flow_direction());
}

paint_text_decoration(context, paintable, fragment);
Expand Down
23 changes: 4 additions & 19 deletions Libraries/LibWeb/Painting/PaintableFragment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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<HTML::FormAssociatedTextControlElement>(*focused_element)) {
Expand Down
4 changes: 2 additions & 2 deletions Tests/LibWeb/Layout/expected/Element-insertAdjacentHTML.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x211 children: not-inline
BlockContainer <body> at (8,8) content-size 784x584 children: not-inline
BlockContainer <div> 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."
Expand Down Expand Up @@ -28,7 +28,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline

ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x211]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x584]
PaintableWithLines (BlockContainer<DIV>) [8,8 784x17]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer<DIV>#container) [28,45 744x137]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x0 children: inline
BlockContainer <body> at (8,8) content-size 784x584 children: inline
BlockContainer <div> at (8,550) content-size 100x50 positioned [BFC] children: not-inline
TextNode <#text>

ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x0]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x584]
PaintableWithLines (BlockContainer<DIV>) [8,550 100x50]
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (1,1) content-size 798x600 [BFC] children: not-inline
BlockContainer <body> at (10,10) content-size 780x0 children: inline
BlockContainer <html> at (1,1) content-size 798x598 [BFC] children: not-inline
BlockContainer <body> at (10,10) content-size 780x580 children: inline
TextNode <#text>
BlockContainer <div.only-t-l> at (6,6) content-size 10x10 positioned [BFC] children: not-inline
TextNode <#text>
Expand All @@ -9,9 +9,9 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <div.both> 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<HTML>) [0,0 800x602]
PaintableWithLines (BlockContainer<BODY>) [9,9 782x2]
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
PaintableWithLines (BlockContainer<BODY>) [9,9 782x582]
PaintableWithLines (BlockContainer<DIV>.only-t-l) [5,5 12x12]
PaintableWithLines (BlockContainer<DIV>.only-mt-ml) [15,15 12x12]
PaintableWithLines (BlockContainer<DIV>.both) [10,10 12x12]
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x34 children: not-inline
BlockContainer <body> at (8,8) content-size 784x584 children: not-inline
BlockContainer <div> at (8,8) content-size 784x34 children: not-inline
BlockContainer <(anonymous)> at (8,8) content-size 784x0 children: inline
TextNode <#text>
Expand Down Expand Up @@ -44,7 +44,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline

ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x34]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x584]
PaintableWithLines (BlockContainer<DIV>) [8,8 784x34]
PaintableWithLines (BlockContainer(anonymous)) [8,8 784x0]
PaintableWithLines (BlockContainer<DIV>) [8,8 784x0]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x110 children: not-inline
BlockContainer <html> at (0,0) content-size 800x692 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x584 children: not-inline
BlockContainer <div> at (8,8) content-size 784x110 children: not-inline
BlockContainer <div.square.white> at (8,8) content-size 100x100 floating [BFC] children: not-inline
BlockContainer <div.clearfix> at (8,108) content-size 10x10 children: not-inline
BlockContainer <div.square.black> at (8,218) content-size 49x49 floating [BFC] children: not-inline

ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x110] overflow: [8,8 784x259]
ViewportPaintable (Viewport<#document>) [0,0 800x600] overflow: [0,0 800x692]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x692]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x584]
PaintableWithLines (BlockContainer<DIV>) [8,8 784x110] overflow: [8,8 784x259]
PaintableWithLines (BlockContainer<DIV>.square.white) [8,8 100x100]
PaintableWithLines (BlockContainer<DIV>.clearfix) [8,108 10x10]
Expand Down
Loading
Loading