Ver código fonte

LibWeb: Move absolute positioning up to FormattingContext

It seems like both BFC and IFC can have absolutely positioned children.
It's a bit strange, but consider the following HTML:

<html><body>foobar<img style="position: absolute"></body></html>

In such a document, the <img> element is an absolutely positioned child
of a block-level element (<body>) with no block-level children.
An IFC is established for <body>, and needs to handle layout for <img>.
Andreas Kling 4 anos atrás
pai
commit
0ecefbff57

+ 6 - 212
Libraries/LibWeb/Layout/BlockFormattingContext.cpp

@@ -76,7 +76,7 @@ void BlockFormattingContext::run(Box& box, LayoutMode layout_mode)
 
         box.for_each_child_of_type<Box>([&](auto& child_box) {
             if (child_box.is_absolutely_positioned()) {
-                layout_absolutely_positioned_child(child_box);
+                layout_absolutely_positioned_element(child_box);
             }
             return IterationDecision::Continue;
         });
@@ -85,6 +85,11 @@ void BlockFormattingContext::run(Box& box, LayoutMode layout_mode)
 
 void BlockFormattingContext::compute_width(Box& box)
 {
+    if (box.is_absolutely_positioned()) {
+        compute_width_for_absolutely_positioned_element(box);
+        return;
+    }
+
     if (is<ReplacedBox>(box)) {
         // FIXME: This should not be done *by* ReplacedBox
         auto& replaced = downcast<ReplacedBox>(box);
@@ -93,11 +98,6 @@ void BlockFormattingContext::compute_width(Box& box)
         return;
     }
 
-    if (box.is_absolutely_positioned()) {
-        compute_width_for_absolutely_positioned_block(box);
-        return;
-    }
-
     if (box.is_floating()) {
         compute_width_for_floating_box(box);
         return;
@@ -272,146 +272,6 @@ void BlockFormattingContext::compute_height_for_block_level_replaced_element_in_
     box.set_height(compute_height_for_replaced_element(box));
 }
 
-void BlockFormattingContext::compute_width_for_absolutely_positioned_block(Box& box)
-{
-    auto& containing_block = *box.containing_block();
-    auto& computed_values = box.computed_values();
-    auto zero_value = CSS::Length::make_px(0);
-
-    auto margin_left = CSS::Length::make_auto();
-    auto margin_right = CSS::Length::make_auto();
-    const auto border_left = computed_values.border_left().width;
-    const auto border_right = computed_values.border_right().width;
-    const auto padding_left = computed_values.padding().left.resolved_or_zero(box, containing_block.width());
-    const auto padding_right = computed_values.padding().right.resolved_or_zero(box, containing_block.width());
-
-    auto try_compute_width = [&](const auto& a_width) {
-        margin_left = computed_values.margin().left.resolved_or_zero(box, containing_block.width());
-        margin_right = computed_values.margin().right.resolved_or_zero(box, containing_block.width());
-
-        auto left = computed_values.offset().left.resolved_or_auto(box, containing_block.width());
-        auto right = computed_values.offset().right.resolved_or_auto(box, containing_block.width());
-        auto width = a_width;
-
-        auto solve_for_left = [&] {
-            return CSS::Length(containing_block.width() - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px);
-        };
-
-        auto solve_for_width = [&] {
-            return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px);
-        };
-
-        auto solve_for_right = [&] {
-            return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box), CSS::Length::Type::Px);
-        };
-
-        // If all three of 'left', 'width', and 'right' are 'auto':
-        if (left.is_auto() && width.is_auto() && right.is_auto()) {
-            // First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
-            if (margin_left.is_auto())
-                margin_left = CSS::Length::make_px(0);
-            if (margin_right.is_auto())
-                margin_right = CSS::Length::make_px(0);
-            // Then, if the 'direction' property of the element establishing the static-position containing block
-            // is 'ltr' set 'left' to the static position and apply rule number three below;
-            // otherwise, set 'right' to the static position and apply rule number one below.
-            // FIXME: This is very hackish.
-            left = CSS::Length::make_px(0);
-            goto Rule3;
-        }
-
-        if (!left.is_auto() && !width.is_auto() && !right.is_auto()) {
-            // FIXME: This should be solved in a more complicated way.
-            return width;
-        }
-
-        if (margin_left.is_auto())
-            margin_left = CSS::Length::make_px(0);
-        if (margin_right.is_auto())
-            margin_right = CSS::Length::make_px(0);
-
-        // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto',
-        //    then the width is shrink-to-fit. Then solve for 'left'
-        if (left.is_auto() && width.is_auto() && !right.is_auto()) {
-            auto result = calculate_shrink_to_fit_widths(box);
-            solve_for_left();
-            auto available_width = solve_for_width();
-            width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px);
-        }
-
-        // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto',
-        //    then if the 'direction' property of the element establishing
-        //    the static-position containing block is 'ltr' set 'left'
-        //    to the static position, otherwise set 'right' to the static position.
-        //    Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
-        else if (left.is_auto() && right.is_auto() && !width.is_auto()) {
-            // FIXME: Check direction
-            // FIXME: Use the static-position containing block
-            left = zero_value;
-            right = solve_for_right();
-        }
-
-        // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto',
-        //    then the width is shrink-to-fit. Then solve for 'right'
-        else if (width.is_auto() && right.is_auto() && !left.is_auto()) {
-        Rule3:
-            auto result = calculate_shrink_to_fit_widths(box);
-            auto available_width = solve_for_width();
-            width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px);
-            right = solve_for_right();
-        }
-
-        // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
-        else if (left.is_auto() && !width.is_auto() && !right.is_auto()) {
-            left = solve_for_left();
-        }
-
-        // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
-        else if (width.is_auto() && !left.is_auto() && !right.is_auto()) {
-            width = solve_for_width();
-        }
-
-        // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
-        else if (right.is_auto() && !left.is_auto() && !width.is_auto()) {
-            right = solve_for_right();
-        }
-
-        return width;
-    };
-
-    auto specified_width = computed_values.width().resolved_or_auto(box, containing_block.width());
-
-    // 1. The tentative used width is calculated (without 'min-width' and 'max-width')
-    auto used_width = try_compute_width(specified_width);
-
-    // 2. The tentative used width is greater than 'max-width', the rules above are applied again,
-    //    but this time using the computed value of 'max-width' as the computed value for 'width'.
-    auto specified_max_width = computed_values.max_width().resolved_or_auto(box, containing_block.width());
-    if (!specified_max_width.is_auto()) {
-        if (used_width.to_px(box) > specified_max_width.to_px(box)) {
-            used_width = try_compute_width(specified_max_width);
-        }
-    }
-
-    // 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
-    //    but this time using the value of 'min-width' as the computed value for 'width'.
-    auto specified_min_width = computed_values.min_width().resolved_or_auto(box, containing_block.width());
-    if (!specified_min_width.is_auto()) {
-        if (used_width.to_px(box) < specified_min_width.to_px(box)) {
-            used_width = try_compute_width(specified_min_width);
-        }
-    }
-
-    box.set_width(used_width.to_px(box));
-
-    box.box_model().margin.left = margin_left.to_px(box);
-    box.box_model().margin.right = margin_right.to_px(box);
-    box.box_model().border.left = border_left;
-    box.box_model().border.right = border_right;
-    box.box_model().padding.left = padding_left.to_px(box);
-    box.box_model().padding.right = padding_right.to_px(box);
-}
-
 void BlockFormattingContext::compute_height(Box& box)
 {
     if (is<ReplacedBox>(box)) {
@@ -703,70 +563,4 @@ void BlockFormattingContext::layout_floating_child(Box& box, Box& containing_blo
     box.set_offset(x, box.effective_offset().y());
 }
 
-void BlockFormattingContext::layout_absolutely_positioned_child(Box& box)
-{
-    auto& containing_block = context_box();
-    auto& box_model = box.box_model();
-
-    auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
-
-    compute_width(box);
-    layout_inside(box, LayoutMode::Default);
-    compute_height(box);
-
-    box_model.margin.left = box.computed_values().margin().left.resolved_or_auto(box, containing_block.width()).to_px(box);
-    box_model.margin.top = box.computed_values().margin().top.resolved_or_auto(box, containing_block.height()).to_px(box);
-    box_model.margin.right = box.computed_values().margin().right.resolved_or_auto(box, containing_block.width()).to_px(box);
-    box_model.margin.bottom = box.computed_values().margin().bottom.resolved_or_auto(box, containing_block.height()).to_px(box);
-
-    box_model.border.left = box.computed_values().border_left().width;
-    box_model.border.right = box.computed_values().border_right().width;
-    box_model.border.top = box.computed_values().border_top().width;
-    box_model.border.bottom = box.computed_values().border_bottom().width;
-
-    box_model.offset.left = box.computed_values().offset().left.resolved_or_auto(box, containing_block.width()).to_px(box);
-    box_model.offset.top = box.computed_values().offset().top.resolved_or_auto(box, containing_block.height()).to_px(box);
-    box_model.offset.right = box.computed_values().offset().right.resolved_or_auto(box, containing_block.width()).to_px(box);
-    box_model.offset.bottom = box.computed_values().offset().bottom.resolved_or_auto(box, containing_block.height()).to_px(box);
-
-    if (box.computed_values().offset().left.is_auto() && specified_width.is_auto() && box.computed_values().offset().right.is_auto()) {
-        if (box.computed_values().margin().left.is_auto())
-            box_model.margin.left = 0;
-        if (box.computed_values().margin().right.is_auto())
-            box_model.margin.right = 0;
-    }
-
-    Gfx::FloatPoint used_offset;
-
-    if (!box.computed_values().offset().left.is_auto()) {
-        float x_offset = box_model.offset.left
-            + box_model.border_box().left;
-        used_offset.set_x(x_offset + box_model.margin.left);
-    } else if (!box.computed_values().offset().right.is_auto()) {
-        float x_offset = 0
-            - box_model.offset.right
-            - box_model.border_box().right;
-        used_offset.set_x(containing_block.width() + x_offset - box.width() - box_model.margin.right);
-    } else {
-        float x_offset = box_model.margin_box().left;
-        used_offset.set_x(x_offset);
-    }
-
-    if (!box.computed_values().offset().top.is_auto()) {
-        float y_offset = box_model.offset.top
-            + box_model.border_box().top;
-        used_offset.set_y(y_offset + box_model.margin.top);
-    } else if (!box.computed_values().offset().bottom.is_auto()) {
-        float y_offset = 0
-            - box_model.offset.bottom
-            - box_model.border_box().bottom;
-        used_offset.set_y(containing_block.height() + y_offset - box.height() - box_model.margin.bottom);
-    } else {
-        float y_offset = box_model.margin_box().top;
-        used_offset.set_y(y_offset);
-    }
-
-    box.set_offset(used_offset);
-}
-
 }

+ 0 - 2
Libraries/LibWeb/Layout/BlockFormattingContext.h

@@ -51,7 +51,6 @@ protected:
 private:
     virtual bool is_block_formatting_context() const final { return true; }
 
-    void compute_width_for_absolutely_positioned_block(Box&);
     void compute_width_for_floating_box(Box&);
 
     void compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox&);
@@ -65,7 +64,6 @@ private:
     void place_block_level_replaced_element_in_normal_flow(Box& child, Box& container);
     void place_block_level_non_replaced_element_in_normal_flow(Box& child, Box& container);
 
-    void layout_absolutely_positioned_child(Box&);
     void layout_floating_child(Box&, Box& containing_block);
 
     Vector<Box*> m_left_floating_boxes;

+ 264 - 0
Libraries/LibWeb/Layout/FormattingContext.cpp

@@ -195,6 +195,22 @@ float FormattingContext::tentative_width_for_replaced_element(const ReplacedBox&
     return used_width;
 }
 
+void FormattingContext::compute_width_for_absolutely_positioned_element(Box& box)
+{
+    if (is<ReplacedBox>(box))
+        compute_width_for_absolutely_positioned_replaced_element(downcast<ReplacedBox>(box));
+    else
+        compute_width_for_absolutely_positioned_non_replaced_element(box);
+}
+
+void FormattingContext::compute_height_for_absolutely_positioned_element(Box& box)
+{
+    if (is<ReplacedBox>(box))
+        compute_height_for_absolutely_positioned_replaced_element(downcast<ReplacedBox>(box));
+    else
+        compute_height_for_absolutely_positioned_non_replaced_element(box);
+}
+
 float FormattingContext::compute_width_for_replaced_element(const ReplacedBox& box)
 {
     // 10.3.4 Block-level, replaced elements in normal flow...
@@ -279,4 +295,252 @@ float FormattingContext::compute_height_for_replaced_element(const ReplacedBox&
     return used_height;
 }
 
+void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_element(Box& box)
+{
+    auto& containing_block = *box.containing_block();
+    auto& computed_values = box.computed_values();
+    auto zero_value = CSS::Length::make_px(0);
+
+    auto margin_left = CSS::Length::make_auto();
+    auto margin_right = CSS::Length::make_auto();
+    const auto border_left = computed_values.border_left().width;
+    const auto border_right = computed_values.border_right().width;
+    const auto padding_left = computed_values.padding().left.resolved_or_zero(box, containing_block.width());
+    const auto padding_right = computed_values.padding().right.resolved_or_zero(box, containing_block.width());
+
+    auto try_compute_width = [&](const auto& a_width) {
+        margin_left = computed_values.margin().left.resolved_or_zero(box, containing_block.width());
+        margin_right = computed_values.margin().right.resolved_or_zero(box, containing_block.width());
+
+        auto left = computed_values.offset().left.resolved_or_auto(box, containing_block.width());
+        auto right = computed_values.offset().right.resolved_or_auto(box, containing_block.width());
+        auto width = a_width;
+
+        auto solve_for_left = [&] {
+            return CSS::Length(containing_block.width() - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px);
+        };
+
+        auto solve_for_width = [&] {
+            return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px);
+        };
+
+        auto solve_for_right = [&] {
+            return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box), CSS::Length::Type::Px);
+        };
+
+        // If all three of 'left', 'width', and 'right' are 'auto':
+        if (left.is_auto() && width.is_auto() && right.is_auto()) {
+            // First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
+            if (margin_left.is_auto())
+                margin_left = CSS::Length::make_px(0);
+            if (margin_right.is_auto())
+                margin_right = CSS::Length::make_px(0);
+            // Then, if the 'direction' property of the element establishing the static-position containing block
+            // is 'ltr' set 'left' to the static position and apply rule number three below;
+            // otherwise, set 'right' to the static position and apply rule number one below.
+            // FIXME: This is very hackish.
+            left = CSS::Length::make_px(0);
+            goto Rule3;
+        }
+
+        if (!left.is_auto() && !width.is_auto() && !right.is_auto()) {
+            // FIXME: This should be solved in a more complicated way.
+            return width;
+        }
+
+        if (margin_left.is_auto())
+            margin_left = CSS::Length::make_px(0);
+        if (margin_right.is_auto())
+            margin_right = CSS::Length::make_px(0);
+
+        // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto',
+        //    then the width is shrink-to-fit. Then solve for 'left'
+        if (left.is_auto() && width.is_auto() && !right.is_auto()) {
+            auto result = calculate_shrink_to_fit_widths(box);
+            solve_for_left();
+            auto available_width = solve_for_width();
+            width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px);
+        }
+
+        // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto',
+        //    then if the 'direction' property of the element establishing
+        //    the static-position containing block is 'ltr' set 'left'
+        //    to the static position, otherwise set 'right' to the static position.
+        //    Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
+        else if (left.is_auto() && right.is_auto() && !width.is_auto()) {
+            // FIXME: Check direction
+            // FIXME: Use the static-position containing block
+            left = zero_value;
+            right = solve_for_right();
+        }
+
+        // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto',
+        //    then the width is shrink-to-fit. Then solve for 'right'
+        else if (width.is_auto() && right.is_auto() && !left.is_auto()) {
+        Rule3:
+            auto result = calculate_shrink_to_fit_widths(box);
+            auto available_width = solve_for_width();
+            width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px);
+            right = solve_for_right();
+        }
+
+        // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
+        else if (left.is_auto() && !width.is_auto() && !right.is_auto()) {
+            left = solve_for_left();
+        }
+
+        // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
+        else if (width.is_auto() && !left.is_auto() && !right.is_auto()) {
+            width = solve_for_width();
+        }
+
+        // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
+        else if (right.is_auto() && !left.is_auto() && !width.is_auto()) {
+            right = solve_for_right();
+        }
+
+        return width;
+    };
+
+    auto specified_width = computed_values.width().resolved_or_auto(box, containing_block.width());
+
+    // 1. The tentative used width is calculated (without 'min-width' and 'max-width')
+    auto used_width = try_compute_width(specified_width);
+
+    // 2. The tentative used width is greater than 'max-width', the rules above are applied again,
+    //    but this time using the computed value of 'max-width' as the computed value for 'width'.
+    auto specified_max_width = computed_values.max_width().resolved_or_auto(box, containing_block.width());
+    if (!specified_max_width.is_auto()) {
+        if (used_width.to_px(box) > specified_max_width.to_px(box)) {
+            used_width = try_compute_width(specified_max_width);
+        }
+    }
+
+    // 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
+    //    but this time using the value of 'min-width' as the computed value for 'width'.
+    auto specified_min_width = computed_values.min_width().resolved_or_auto(box, containing_block.width());
+    if (!specified_min_width.is_auto()) {
+        if (used_width.to_px(box) < specified_min_width.to_px(box)) {
+            used_width = try_compute_width(specified_min_width);
+        }
+    }
+
+    box.set_width(used_width.to_px(box));
+
+    box.box_model().margin.left = margin_left.to_px(box);
+    box.box_model().margin.right = margin_right.to_px(box);
+    box.box_model().border.left = border_left;
+    box.box_model().border.right = border_right;
+    box.box_model().padding.left = padding_left.to_px(box);
+    box.box_model().padding.right = padding_right.to_px(box);
+}
+
+void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(ReplacedBox& box)
+{
+    // FIXME: Implement this.
+    return compute_width_for_absolutely_positioned_non_replaced_element(box);
+}
+
+void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box& box)
+{
+    auto& computed_values = box.computed_values();
+    auto& containing_block = *box.containing_block();
+
+    CSS::Length specified_height;
+
+    if (computed_values.height().is_percentage() && !containing_block.computed_values().height().is_absolute()) {
+        specified_height = CSS::Length::make_auto();
+    } else {
+        specified_height = computed_values.height().resolved_or_auto(box, containing_block.height());
+    }
+
+    auto specified_max_height = computed_values.max_height().resolved_or_auto(box, containing_block.height());
+
+    box.box_model().margin.top = computed_values.margin().top.resolved_or_zero(box, containing_block.width()).to_px(box);
+    box.box_model().margin.bottom = computed_values.margin().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
+    box.box_model().border.top = computed_values.border_top().width;
+    box.box_model().border.bottom = computed_values.border_bottom().width;
+    box.box_model().padding.top = computed_values.padding().top.resolved_or_zero(box, containing_block.width()).to_px(box);
+    box.box_model().padding.bottom = computed_values.padding().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
+
+    if (!specified_height.is_auto()) {
+        float used_height = specified_height.to_px(box);
+        if (!specified_max_height.is_auto())
+            used_height = min(used_height, specified_max_height.to_px(box));
+        box.set_height(used_height);
+    }
+}
+
+void FormattingContext::layout_absolutely_positioned_element(Box& box)
+{
+    auto& containing_block = context_box();
+    auto& box_model = box.box_model();
+
+    auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
+
+    compute_width_for_absolutely_positioned_element(box);
+    layout_inside(box, LayoutMode::Default);
+    compute_height_for_absolutely_positioned_element(box);
+
+    box_model.margin.left = box.computed_values().margin().left.resolved_or_auto(box, containing_block.width()).to_px(box);
+    box_model.margin.top = box.computed_values().margin().top.resolved_or_auto(box, containing_block.height()).to_px(box);
+    box_model.margin.right = box.computed_values().margin().right.resolved_or_auto(box, containing_block.width()).to_px(box);
+    box_model.margin.bottom = box.computed_values().margin().bottom.resolved_or_auto(box, containing_block.height()).to_px(box);
+
+    box_model.border.left = box.computed_values().border_left().width;
+    box_model.border.right = box.computed_values().border_right().width;
+    box_model.border.top = box.computed_values().border_top().width;
+    box_model.border.bottom = box.computed_values().border_bottom().width;
+
+    box_model.offset.left = box.computed_values().offset().left.resolved_or_auto(box, containing_block.width()).to_px(box);
+    box_model.offset.top = box.computed_values().offset().top.resolved_or_auto(box, containing_block.height()).to_px(box);
+    box_model.offset.right = box.computed_values().offset().right.resolved_or_auto(box, containing_block.width()).to_px(box);
+    box_model.offset.bottom = box.computed_values().offset().bottom.resolved_or_auto(box, containing_block.height()).to_px(box);
+
+    if (box.computed_values().offset().left.is_auto() && specified_width.is_auto() && box.computed_values().offset().right.is_auto()) {
+        if (box.computed_values().margin().left.is_auto())
+            box_model.margin.left = 0;
+        if (box.computed_values().margin().right.is_auto())
+            box_model.margin.right = 0;
+    }
+
+    Gfx::FloatPoint used_offset;
+
+    if (!box.computed_values().offset().left.is_auto()) {
+        float x_offset = box_model.offset.left
+            + box_model.border_box().left;
+        used_offset.set_x(x_offset + box_model.margin.left);
+    } else if (!box.computed_values().offset().right.is_auto()) {
+        float x_offset = 0
+            - box_model.offset.right
+            - box_model.border_box().right;
+        used_offset.set_x(containing_block.width() + x_offset - box.width() - box_model.margin.right);
+    } else {
+        float x_offset = box_model.margin_box().left;
+        used_offset.set_x(x_offset);
+    }
+
+    if (!box.computed_values().offset().top.is_auto()) {
+        float y_offset = box_model.offset.top
+            + box_model.border_box().top;
+        used_offset.set_y(y_offset + box_model.margin.top);
+    } else if (!box.computed_values().offset().bottom.is_auto()) {
+        float y_offset = 0
+            - box_model.offset.bottom
+            - box_model.border_box().bottom;
+        used_offset.set_y(containing_block.height() + y_offset - box.height() - box_model.margin.bottom);
+    } else {
+        float y_offset = box_model.margin_box().top;
+        used_offset.set_y(y_offset);
+    }
+
+    box.set_offset(used_offset);
+}
+
+void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox& box)
+{
+    // FIXME: Implement this.
+    return compute_height_for_absolutely_positioned_non_replaced_element(box);
+}
+
 }

+ 8 - 0
Libraries/LibWeb/Layout/FormattingContext.h

@@ -63,6 +63,14 @@ protected:
 
     ShrinkToFitResult calculate_shrink_to_fit_widths(Box&);
 
+    void layout_absolutely_positioned_element(Box&);
+    void compute_width_for_absolutely_positioned_element(Box&);
+    void compute_width_for_absolutely_positioned_non_replaced_element(Box&);
+    void compute_width_for_absolutely_positioned_replaced_element(ReplacedBox&);
+    void compute_height_for_absolutely_positioned_element(Box&);
+    void compute_height_for_absolutely_positioned_non_replaced_element(Box&);
+    void compute_height_for_absolutely_positioned_replaced_element(ReplacedBox&);
+
     FormattingContext* m_parent { nullptr };
     Box* m_context_box { nullptr };
 };

+ 3 - 1
Libraries/LibWeb/Layout/InlineFormattingContext.cpp

@@ -95,8 +95,10 @@ void InlineFormattingContext::run(Box&, LayoutMode layout_mode)
     containing_block().line_boxes().clear();
     containing_block().for_each_child([&](auto& child) {
         ASSERT(child.is_inline());
-        if (child.is_absolutely_positioned())
+        if (is<Box>(child) && child.is_absolutely_positioned()) {
+            layout_absolutely_positioned_element(downcast<Box>(child));
             return;
+        }
 
         child.split_into_lines(*this, layout_mode);
     });