Repository: servo/servo (language: python)

Issue:
Missing support for some alignment keywords on absolutely positioned elements
https://github.com/servo/servo/blob/11dfbd6f90bc7bc8b8d7485695548b5ad685d0e1/components/layout_2020/positioned.rs#L934-L943

This has some problems:
 - It handles `space-around` and `space-evenly`, which are content distribution keywords, even though here we can only have a self-alignment value
 - It's missing some values

The syntax is for `align-self` is:

```
auto | normal | stretch | <baseline-position> | <overflow-position>? <self-position>
<baseline-position> = [ first | last ]? && baseline
<overflow-position> = unsafe | safe
<self-position> = center | start | end | self-start | self-end | flex-start | flex-end
```

The syntax for `justify-self` also includes `left` and `right`.

So we need to handle:
 - `self-start`: like `start` in the block axis (because we lack `writing-mode`), but in the inline axis we need to check the`direction` of the abspos.
 - `self-end`: like `end` in the block axis (because we lack `writing-mode`), but in the inline axis we need to check the `direction` of the abspos.
 - `left`: we need to check the `direction` of the containing block
 - `right`: we need to check the `direction` of the containing block

`WritingMode` has some methods that can be useful, e.g. if `line_left_is_inline_start()` then `left` should be like `start`. See https://github.com/servo/stylo/blob/main/style/logical_geometry.rs

I think leaving the rest as `start` is fine (I should double-check at some point).

Testcase:

```html
<!DOCTYPE html>
<style>
.wrapper { display: inline-block; position: relative; width: 30px; height: 30px; border: 1px solid; }
.abspos { position: absolute; height: 15px; width: 15px; inset: 0; background: cyan; }
.ltr { direction: ltr }
.rtl { direction: rtl }
</style>
<div class="wrapper ltr"><div class="abspos ltr" style="justify-self: left"></div></div>
<div class="wrapper ltr"><div class="abspos ltr" style="justify-self: right"></div></div>
<div class="wrapper ltr"><div class="abspos rtl" style="justify-self: left"></div></div>
<div class="wrapper ltr"><div class="abspos rtl" style="justify-self: right"></div></div>
<br>
<div class="wrapper rtl"><div class="abspos ltr" style="justify-self: left"></div></div>
<div class="wrapper rtl"><div class="abspos ltr" style="justify-self: right"></div></div>
<div class="wrapper rtl"><div class="abspos rtl" style="justify-self: left"></div></div>
<div class="wrapper rtl"><div class="abspos rtl" style="justify-self: right"></div></div>
<br>
<div class="wrapper ltr"><div class="abspos ltr" style="place-self: self-start"></div></div>
<div class="wrapper ltr"><div class="abspos ltr" style="place-self: self-end"></div></div>
<div class="wrapper ltr"><div class="abspos rtl" style="place-self: self-start"></div></div>
<div class="wrapper ltr"><div class="abspos rtl" style="place-self: self-end"></div></div>
<br>
<div class="wrapper rtl"><div class="abspos ltr" style="place-self: self-start"></div></div>
<div class="wrapper rtl"><div class="abspos ltr" style="place-self: self-end"></div></div>
<div class="wrapper rtl"><div class="abspos rtl" style="place-self: self-start"></div></div>
<div class="wrapper rtl"><div class="abspos rtl" style="place-self: self-end"></div></div>
```

| Expected | Actual |
| - | - |
| ![](https://github.com/user-attachments/assets/66b0c29c-35dd-4ff2-bb03-a4ea01cfb684) | ![](https://github.com/user-attachments/assets/e88380a8-7682-4d7b-a169-200f2e0900dc) |



Discussion / hints:
I'd like to work on this

Fix patch:
diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs
index 397f8707b611b..5f5bfee52e9ec 100644
--- a/components/layout_2020/positioned.rs
+++ b/components/layout_2020/positioned.rs
@@ -632,8 +632,20 @@ impl HoistedAbsolutelyPositionedBox {
 
             let pb = pbm.padding + pbm.border;
             let margin_rect_size = content_size + pbm.padding_border_sums + margin.sum();
-            let inline_origin = inline_axis_solver.origin_for_margin_box(margin_rect_size.inline);
-            let block_origin = block_axis_solver.origin_for_margin_box(margin_rect_size.block);
+            let inline_origin = inline_axis_solver.origin_for_margin_box(
+                AxisDirection::Inline,
+                margin_rect_size.inline,
+                style.writing_mode,
+                shared_fragment.original_parent_writing_mode,
+                containing_block_writing_mode,
+            );
+            let block_origin = block_axis_solver.origin_for_margin_box(
+                AxisDirection::Block,
+                margin_rect_size.block,
+                style.writing_mode,
+                shared_fragment.original_parent_writing_mode,
+                containing_block_writing_mode,
+            );
 
             let content_rect = LogicalRect {
                 start_corner: LogicalVec2 {
@@ -868,12 +880,23 @@ impl<'a> AbsoluteAxisSolver<'a> {
         result
     }
 
-    fn origin_for_margin_box(&self, size: Au) -> Au {
-        let (alignment_container, flip_anchor) = match (
+    fn origin_for_margin_box(
+        &self,
+        axis: AxisDirection,
+        size: Au,
+        self_writing_mode: WritingMode,
+        original_parent_writing_mode: WritingMode,
+        containing_block_writing_mode: WritingMode,
+    ) -> Au {
+        let (alignment_container, alignment_container_writing_mode, flip_anchor) = match (
             self.box_offsets.start.non_auto(),
             self.box_offsets.end.non_auto(),
         ) {
-            (None, None) => (self.static_position_rect_axis, self.flip_anchor),
+            (None, None) => (
+                self.static_position_rect_axis,
+                original_parent_writing_mode,
+                self.flip_anchor,
+            ),
             (Some(start), Some(end)) => {
                 let start = start.to_used_value(self.containing_size);
                 let end = end.to_used_value(self.containing_size);
@@ -881,7 +904,7 @@ impl<'a> AbsoluteAxisSolver<'a> {
                     origin: start,
                     length: self.containing_size - (end + start),
                 };
-                (alignment_container, false)
+                (alignment_container, containing_block_writing_mode, false)
             },
             // If a single offset is auto, for alignment purposes it resolves to the amount
             // that makes the inset-modified containing block be exactly as big as the abspos.
@@ -892,21 +915,49 @@ impl<'a> AbsoluteAxisSolver<'a> {
             },
         };
 
+        assert_eq!(
+            self_writing_mode.is_horizontal(),
+            original_parent_writing_mode.is_horizontal(),
+            "Mixed horizontal and vertical writing modes are not supported yet"
+        );
+        assert_eq!(
+            self_writing_mode.is_horizontal(),
+            containing_block_writing_mode.is_horizontal(),
+            "Mixed horizontal and vertical writing modes are not supported yet"
+        );
+        let self_value_matches_container = || {
+            axis == AxisDirection::Block ||
+                self_writing_mode.is_bidi_ltr() == alignment_container_writing_mode.is_bidi_ltr()
+        };
+
         // Here we resolve the alignment to either start, center, or end.
         // Note we need to handle both self-alignment values (when some inset isn't auto)
         // and distributed alignment values (when both insets are auto).
         // The latter are treated as their fallback alignment.
         let alignment = match self.alignment.value() {
-            // https://drafts.csswg.org/css-align/#valdef-self-position-end
-            // https://drafts.csswg.org/css-align/#valdef-self-position-flex-end
-            AlignFlags::END | AlignFlags::FLEX_END => AlignFlags::END,
             // https://drafts.csswg.org/css-align/#valdef-self-position-center
             // https://drafts.csswg.org/css-align/#valdef-align-content-space-around
             // https://drafts.csswg.org/css-align/#valdef-align-content-space-evenly
             AlignFlags::CENTER | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
                 AlignFlags::CENTER
             },
-            // TODO(#34282): handle missing values: self-start, self-end, left, right.
+            // https://drafts.csswg.org/css-align/#valdef-self-position-self-start
+            AlignFlags::SELF_START if self_value_matches_container() => AlignFlags::START,
+            AlignFlags::SELF_START => AlignFlags::END,
+            // https://drafts.csswg.org/css-align/#valdef-self-position-self-end
+            AlignFlags::SELF_END if self_value_matches_container() => AlignFlags::END,
+            AlignFlags::SELF_END => AlignFlags::START,
+            // https://drafts.csswg.org/css-align/#valdef-justify-content-left
+            AlignFlags::LEFT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::START,
+            AlignFlags::LEFT => AlignFlags::END,
+            // https://drafts.csswg.org/css-align/#valdef-justify-content-right
+            AlignFlags::RIGHT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::END,
+            AlignFlags::RIGHT => AlignFlags::START,
+            // https://drafts.csswg.org/css-align/#valdef-self-position-end
+            // https://drafts.csswg.org/css-align/#valdef-self-position-flex-end
+            AlignFlags::END | AlignFlags::FLEX_END => AlignFlags::END,
+            // https://drafts.csswg.org/css-align/#valdef-self-position-start
+            // https://drafts.csswg.org/css-align/#valdef-self-position-flex-start
             _ => AlignFlags::START,
         };
 


Test patch:
diff --git a/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-htb.html.ini b/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-htb.html.ini
index 9e71a6e7b4fba..fee3c215bacdc 100644
--- a/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-htb.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-htb.html.ini
@@ -2,11 +2,5 @@
   [.item 5]
     expected: FAIL
 
-  [.item 7]
-    expected: FAIL
-
   [.item 13]
     expected: FAIL
-
-  [.item 15]
-    expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-vlr.html.ini b/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-vlr.html.ini
index 2e97d601891a9..95c538d31bb45 100644
--- a/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-vlr.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-vlr.html.ini
@@ -2,11 +2,11 @@
   [.item 5]
     expected: FAIL
 
-  [.item 7]
-    expected: FAIL
-
   [.item 13]
     expected: FAIL
 
   [.item 14]
     expected: FAIL
+
+  [.item 15]
+    expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-vrl.html.ini b/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-vrl.html.ini
index c00f13125fdf5..146577796d56a 100644
--- a/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-vrl.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/align-self-htb-ltr-vrl.html.ini
@@ -2,11 +2,11 @@
   [.item 5]
     expected: FAIL
 
-  [.item 7]
-    expected: FAIL
-
   [.item 13]
     expected: FAIL
 
   [.item 14]
     expected: FAIL
+
+  [.item 15]
+    expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-htb.html.ini b/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-htb.html.ini
index d77f51252992c..0a01395fd1aed 100644
--- a/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-htb.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-htb.html.ini
@@ -2,11 +2,5 @@
   [.item 5]
     expected: FAIL
 
-  [.item 7]
-    expected: FAIL
-
   [.item 13]
     expected: FAIL
-
-  [.item 15]
-    expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-vlr.html.ini b/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-vlr.html.ini
index 65c11d01926f2..8f9553700b77f 100644
--- a/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-vlr.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-vlr.html.ini
@@ -2,11 +2,11 @@
   [.item 5]
     expected: FAIL
 
-  [.item 7]
-    expected: FAIL
-
   [.item 13]
     expected: FAIL
 
   [.item 14]
     expected: FAIL
+
+  [.item 15]
+    expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-vrl.html.ini b/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-vrl.html.ini
index 17e8b81059c33..0ed1525f88b4c 100644
--- a/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-vrl.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/align-self-htb-rtl-vrl.html.ini
@@ -2,11 +2,11 @@
   [.item 5]
     expected: FAIL
 
-  [.item 7]
-    expected: FAIL
-
   [.item 13]
     expected: FAIL
 
   [.item 14]
     expected: FAIL
+
+  [.item 15]
+    expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-htb.html.ini b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-htb.html.ini
index 36cc6bc1497a8..629696646041f 100644
--- a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-htb.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-htb.html.ini
@@ -2,17 +2,5 @@
   [.item 5]
     expected: FAIL
 
-  [.item 7]
-    expected: FAIL
-
-  [.item 9]
-    expected: FAIL
-
   [.item 15]
     expected: FAIL
-
-  [.item 16]
-    expected: FAIL
-
-  [.item 19]
-    expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-vlr.html.ini b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-vlr.html.ini
index b0f5ff3c78801..e0aaa0db74440 100644
--- a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-vlr.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-vlr.html.ini
@@ -2,17 +2,11 @@
   [.item 5]
     expected: FAIL
 
-  [.item 7]
-    expected: FAIL
-
-  [.item 9]
-    expected: FAIL
-
   [.item 15]
     expected: FAIL
 
   [.item 17]
     expected: FAIL
 
-  [.item 19]
+  [.item 16]
     expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-vrl.html.ini b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-vrl.html.ini
index 21d63104f3b06..b671e2a5e6823 100644
--- a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-vrl.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-ltr-vrl.html.ini
@@ -5,14 +5,8 @@
   [.item 6]
     expected: FAIL
 
-  [.item 9]
-    expected: FAIL
-
   [.item 15]
     expected: FAIL
 
-  [.item 16]
-    expected: FAIL
-
-  [.item 19]
+  [.item 7]
     expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-htb.html.ini b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-htb.html.ini
index 9cdebf2ee225a..965006050164c 100644
--- a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-htb.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-htb.html.ini
@@ -2,17 +2,5 @@
   [.item 5]
     expected: FAIL
 
-  [.item 6]
-    expected: FAIL
-
-  [.item 8]
-    expected: FAIL
-
   [.item 15]
     expected: FAIL
-
-  [.item 17]
-    expected: FAIL
-
-  [.item 18]
-    expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-vlr.html.ini b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-vlr.html.ini
index 67309fa98b8ae..fb7560bdcb66e 100644
--- a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-vlr.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-vlr.html.ini
@@ -2,17 +2,11 @@
   [.item 5]
     expected: FAIL
 
-  [.item 6]
-    expected: FAIL
-
-  [.item 8]
-    expected: FAIL
-
   [.item 15]
     expected: FAIL
 
   [.item 16]
     expected: FAIL
 
-  [.item 18]
+  [.item 17]
     expected: FAIL
diff --git a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-vrl.html.ini b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-vrl.html.ini
index 414415ee19156..44bd19585ef13 100644
--- a/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-vrl.html.ini
+++ b/tests/wpt/meta/css/css-align/abspos/justify-self-htb-rtl-vrl.html.ini
@@ -5,14 +5,8 @@
   [.item 7]
     expected: FAIL
 
-  [.item 8]
-    expected: FAIL
-
   [.item 15]
     expected: FAIL
 
-  [.item 17]
-    expected: FAIL
-
-  [.item 18]
+  [.item 6]
     expected: FAIL