From deda7c899525eeb989ab79d5d9e24d685569c84d Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Mon, 29 Mar 2021 12:04:26 -0400 Subject: [PATCH] LibWeb: Compute position of relative block elements Section 10.3 "Calculating widths and margins" indicates that the 'left' and 'right' properties of relatively positioned elements should be set in accordance with the rules of section 9.4.3. --- .../LibWeb/Layout/BlockFormattingContext.cpp | 37 +++++++++++++++++++ .../LibWeb/Layout/BlockFormattingContext.h | 1 + 2 files changed, 38 insertions(+) diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 0a5918b2e7..fe767a037b 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -351,6 +351,40 @@ void BlockFormattingContext::compute_height(Box& box) box.set_height(height); } +void BlockFormattingContext::compute_position(Box& box) +{ + // 9.4.3 Relative positioning + // Once a box has been laid out according to the normal flow or floated, it may be shifted relative to this position. + + auto& box_model = box.box_model(); + auto& computed_values = box.computed_values(); + float width_of_containing_block = box.width_of_logical_containing_block(); + + auto specified_left = computed_values.offset().left.resolved_or_zero(box, width_of_containing_block); + auto specified_right = computed_values.offset().right.resolved_or_zero(box, width_of_containing_block); + + if (specified_left.is_auto() && specified_right.is_auto()) { + // If both 'left' and 'right' are 'auto' (their initial values), the used values are '0' (i.e., the boxes stay in their original position). + box_model.offset.left = 0; + box_model.offset.right = 0; + } else if (specified_left.is_auto()) { + // If 'left' is 'auto', its used value is minus the value of 'right' (i.e., the boxes move to the left by the value of 'right'). + box_model.offset.right = specified_right.to_px(box); + box_model.offset.left = 0 - box_model.offset.right; + } else if (specified_right.is_auto()) { + // If 'right' is specified as 'auto', its used value is minus the value of 'left'. + box_model.offset.left = specified_left.to_px(box); + box_model.offset.right = 0 - box_model.offset.left; + } else { + // If neither 'left' nor 'right' is 'auto', the position is over-constrained, and one of them has to be ignored. + // If the 'direction' property of the containing block is 'ltr', the value of 'left' wins and 'right' becomes -'left'. + // If 'direction' of the containing block is 'rtl', 'right' wins and 'left' is ignored. + // FIXME: Check direction (assuming 'ltr' for now). + box_model.offset.left = specified_left.to_px(box); + box_model.offset.right = 0 - box_model.offset.left; + } +} + void BlockFormattingContext::layout_inline_children(Box& box, LayoutMode layout_mode) { InlineFormattingContext context(box, this); @@ -380,6 +414,9 @@ void BlockFormattingContext::layout_block_level_children(Box& box, LayoutMode la else if (is(child_box)) place_block_level_non_replaced_element_in_normal_flow(child_box, box); + if (child_box.computed_values().position() == CSS::Position::Relative) + compute_position(child_box); // Note: Shifting position should occur after the above layout. + // FIXME: This should be factored differently. It's uncool that we mutate the tree *during* layout! // Instead, we should generate the marker box during the tree build. if (is(child_box)) diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h index c5b73c20fa..7cc5d6fac5 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -47,6 +47,7 @@ public: protected: void compute_width(Box&); void compute_height(Box&); + void compute_position(Box&); private: virtual bool is_block_formatting_context() const final { return true; }