diff --git a/Libraries/LibWeb/Rust/src/layout/line_builder.rs b/Libraries/LibWeb/Rust/src/layout/line_builder.rs index f6b22ad95e660..e8a0fb396b825 100644 --- a/Libraries/LibWeb/Rust/src/layout/line_builder.rs +++ b/Libraries/LibWeb/Rust/src/layout/line_builder.rs @@ -31,6 +31,16 @@ struct FragmentAlignmentSnapshot { block_offset: CssPixels, } +// https://drafts.csswg.org/css2/#line-height +#[derive(Clone, Copy)] +struct LineRelativeAlignedSubtree { + root: Node, + alignment: u8, + block_start: CssPixels, + block_end: CssPixels, + shift: CssPixels, +} + pub(crate) struct LineBuilder<'builder, 'context, 'pass> { context: &'builder InlineFormattingContext<'context, 'pass>, available_inline_size_for_current_line: AvailableSize, @@ -468,9 +478,25 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { CssPixels::nearest_value_for_f32(style.font_ascent()) + (line_height - typographic) / 2 } + // The style of the box that an inline-level box is aligned against, which is its parent inline box, or the block + // container for a box the containing block holds directly. + fn parent_style(&self, node: Node) -> StyleValues<'_> { + let parent = if node == self.context().containing_block { + NodeSlotId::INVALID + } else { + self.context().parent_node(node) + }; + if parent.is_invalid() { + self.containing_style() + } else { + self.context().style(parent) + } + } + fn block_offset_for_alignment( &self, style: StyleValues, + parent_style: StyleValues, metrics: VerticalAlignMetrics, line_box_baseline: CssPixels, ) -> CssPixels { @@ -479,9 +505,10 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { if !style.vertical_align_is_keyword() { return alphabetic - style.vertical_align_value().to_px(metrics.line_height); } + // FIXME: middle, sub and super are defined against the parent inline box's font metrics too, but we use the + // block container's instead. match style.vertical_align_keyword() { vertical_align::BASELINE => alphabetic, - vertical_align::TOP => self.current_block_offset + metrics.effective_box_block_start_offset, vertical_align::MIDDLE => { let x_height = CssPixels::nearest_value_for_f32(self.containing_style().font_x_height()); self.current_block_offset @@ -494,11 +521,60 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { } vertical_align::SUB => alphabetic + self.containing_style().font_size() / 5, vertical_align::SUPER => alphabetic - self.containing_style().font_size() / 3, - vertical_align::BOTTOM | vertical_align::TEXT_BOTTOM | vertical_align::TEXT_TOP => alphabetic, + // A top- or bottom-aligned box is the root of an aligned subtree, laid out on the baseline here and + // shifted into place once the final line box size is known. These arms therefore only see boxes that + // form no aligned subtree: the containing block itself, whose alignment does not apply to the line box + // it establishes, and every box in a writing mode below. + // FIXME: Line box alignment still uses physical vertical metrics in non-horizontal writing modes. + vertical_align::TOP if self.writing_mode == writing_mode::HORIZONTAL_TB => alphabetic, + vertical_align::TOP => self.current_block_offset + metrics.effective_box_block_start_offset, + vertical_align::BOTTOM => alphabetic, + vertical_align::TEXT_TOP => { + self.current_block_offset + line_box_baseline + - CssPixels::nearest_value_for_f32(parent_style.font_ascent()) + + metrics.effective_box_block_start_offset + } + vertical_align::TEXT_BOTTOM => { + self.current_block_offset + + line_box_baseline + + CssPixels::nearest_value_for_f32(parent_style.font_descent()) + - metrics.block_size + - metrics.effective_box_block_end_offset + } _ => unreachable!("invalid vertical-align keyword"), } } + // https://drafts.csswg.org/css2/#line-height + fn line_relative_aligned_subtree_root(&self, style_source: Node) -> Option<(Node, u8)> { + // FIXME: Line box alignment still uses physical vertical metrics in non-horizontal writing modes, where + // top and bottom are approximated without forming aligned subtrees. + if self.writing_mode != writing_mode::HORIZONTAL_TB { + return None; + } + + // An aligned subtree contains its inline-level box and the aligned subtrees of all children except any + // descendant subtree whose root is itself aligned to the line box. Walking outwards therefore stops at the + // nearest ancestor aligned to the line box, and at the first ancestor that is not an inline box of this + // formatting context. + let containing_block = self.context().containing_block; + let mut node = style_source; + while !node.is_invalid() && node != containing_block { + if let Some(alignment) = line_relative_alignment(self.context().style(node)) { + return Some((node, alignment)); + } + let parent = self.context().parent_node(node); + if !parent.is_invalid() + && parent != containing_block + && !self.context().facts(parent).is_fragmented_inline() + { + break; + } + node = parent; + } + None + } + fn inline_box_alignment_metrics(&self, node: Node) -> VerticalAlignMetrics { let style = self.context().style(node); let used = self.context().used(node); @@ -511,6 +587,7 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { } } + // https://drafts.csswg.org/css2/#line-height pub(crate) fn update_last_line(&mut self) { if !self.last_line_needs_update { return; @@ -555,9 +632,13 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { } let strut_baseline = Self::baseline_for_style(containing_style, containing_style.line_height()); - let mut should_align_strut = false; + let mut should_align_strut_to_line_box_baseline = false; let mut line_box_baseline = strut_baseline; let fragment_count = self.line(line_index).fragments.len(); + let has_line_relative_aligned_subtree = (0..fragment_count).any(|fragment_index| { + let style_source = self.line(line_index).fragments[fragment_index].style_source; + self.line_relative_aligned_subtree_root(style_source).is_some() + }); for fragment_index in 0..fragment_count { let (node, style_source, content_baselines) = { let fragment = &self.line(line_index).fragments[fragment_index]; @@ -585,30 +666,38 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { ) }; self.line_mut(line_index).fragments[fragment_index].baseline = fragment_baseline; + if self.line_relative_aligned_subtree_root(style_source).is_some() { + continue; + } let adjusted_baseline = if style.vertical_align_is_keyword() { fragment_baseline } else { fragment_baseline + style.vertical_align_value().to_px(style.line_height()) }; if adjusted_baseline > line_box_baseline { - if !self.context().facts(node).is_text_node() { - should_align_strut |= style.display().is_inline_outside() - && style.display().is_flex_inside() - && style.vertical_align_is_keyword() - && style.vertical_align_keyword() == vertical_align::BASELINE; + // A line box holding an aligned subtree can extend past the strut on both sides, so the strut has to + // be placed relative to the line box baseline instead of the line box block start. + // FIXME: That is true of every line box, but the line box block start is currently derived both here + // and from max_block_size_on_current_line, and those two disagree once the strut moves. + if !self.context().facts(node).is_text_node() && style.vertical_align_is_keyword() { + should_align_strut_to_line_box_baseline |= has_line_relative_aligned_subtree + || (style.display().is_inline_outside() + && style.display().is_flex_inside() + && style.vertical_align_keyword() == vertical_align::BASELINE); } line_box_baseline = adjusted_baseline; } } let strut_start = self.current_block_offset; - let strut_end = if should_align_strut { + let strut_end = if should_align_strut_to_line_box_baseline { self.current_block_offset + line_box_baseline + (containing_style.line_height() - strut_baseline) } else { self.current_block_offset + containing_style.line_height() }; let mut earliest = strut_start; let mut latest = strut_end; + let mut aligned_subtrees: Vec = Vec::new(); for fragment_index in 0..fragment_count { let mut snapshot = { @@ -639,12 +728,16 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { metrics.effective_box_block_end_offset = used.margin_bottom.get() + used.border_box_bottom(false); } let new_inline_offset = inline_offset + snapshot.inline_offset; - let mut new_block_offset = self.block_offset_for_alignment(style, metrics, line_box_baseline); - let own_alignment_is_line_relative = style.vertical_align_is_keyword() - && matches!( - style.vertical_align_keyword(), - vertical_align::TOP | vertical_align::BOTTOM - ); + let own_alignment_is_line_relative = line_relative_alignment(style).is_some(); + let aligned_subtree = self.line_relative_aligned_subtree_root(snapshot.style_source); + let alignment_style = if aligned_subtree.is_some_and(|(root, _)| root == snapshot.style_source) { + style.with_vertical_align_keyword(vertical_align::BASELINE) + } else { + style + }; + let parent_style = self.parent_style(snapshot.style_source); + let mut new_block_offset = + self.block_offset_for_alignment(alignment_style, parent_style, metrics, line_box_baseline); let containing_block = self.context().containing_block; let mut ancestor = if snapshot.style_source == containing_block { NodeSlotId::INVALID @@ -657,23 +750,26 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { && ancestor != containing_block { let ancestor_style = self.context().style(ancestor); - if ancestor_style.vertical_align_is_keyword() { - if ancestor_style.vertical_align_keyword() == vertical_align::BASELINE { - ancestor = self.context().parent_node(ancestor); - continue; - } - if matches!( - ancestor_style.vertical_align_keyword(), - vertical_align::TOP | vertical_align::BOTTOM - ) { - break; - } + if line_relative_alignment(ancestor_style).is_some() { + break; + } + if ancestor_style.vertical_align_is_keyword() + && ancestor_style.vertical_align_keyword() == vertical_align::BASELINE + { + ancestor = self.context().parent_node(ancestor); + continue; } let ancestor_metrics = self.inline_box_alignment_metrics(ancestor); + let ancestor_parent_style = self.parent_style(ancestor); let baseline_style = ancestor_style.with_vertical_align_keyword(vertical_align::BASELINE); new_block_offset += - self.block_offset_for_alignment(ancestor_style, ancestor_metrics, line_box_baseline) - - self.block_offset_for_alignment(baseline_style, ancestor_metrics, line_box_baseline); + self.block_offset_for_alignment(ancestor_style, ancestor_parent_style, ancestor_metrics, line_box_baseline) + - self.block_offset_for_alignment( + baseline_style, + ancestor_parent_style, + ancestor_metrics, + line_box_baseline, + ); ancestor = self.context().parent_node(ancestor); } { @@ -708,8 +804,23 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { if !style.vertical_align_is_keyword() { inline_box_end += style.vertical_align_value().to_px(style.line_height()); } - earliest = earliest.min(inline_box_start); - latest = latest.max(inline_box_end); + if let Some((root, alignment)) = aligned_subtree { + if let Some(subtree) = aligned_subtrees.iter_mut().find(|subtree| subtree.root == root) { + subtree.block_start = subtree.block_start.min(inline_box_start); + subtree.block_end = subtree.block_end.max(inline_box_end); + } else { + aligned_subtrees.push(LineRelativeAlignedSubtree { + root, + alignment, + block_start: inline_box_start, + block_end: inline_box_end, + shift: CssPixels::default(), + }); + } + } else { + earliest = earliest.min(inline_box_start); + latest = latest.max(inline_box_end); + } if self.context().facts(snapshot.layout_node).is_text_node() && self.writing_mode == writing_mode::HORIZONTAL_TB @@ -723,18 +834,66 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { } } + // Top- and bottom-aligned subtrees do not participate in choosing the line box baseline. Once all other boxes + // have been positioned, grow the line box to the minimum size that can contain every aligned subtree. + let maximum_block_size = |alignment| { + aligned_subtrees + .iter() + .filter(|subtree| subtree.alignment == alignment) + .map(|subtree| subtree.block_end - subtree.block_start) + .max() + .unwrap_or_default() + }; + latest = latest.max(earliest + maximum_block_size(vertical_align::TOP)); + earliest = earliest.min(latest - maximum_block_size(vertical_align::BOTTOM)); + + let normal_subtree_shift = if has_line_relative_aligned_subtree { + self.current_block_offset - earliest + } else { + CssPixels::default() + }; + for subtree in &mut aligned_subtrees { + subtree.shift = if subtree.alignment == vertical_align::TOP { + self.current_block_offset - subtree.block_start + } else { + self.current_block_offset + latest - earliest - subtree.block_end + }; + } + + if has_line_relative_aligned_subtree { + for fragment_index in 0..fragment_count { + let style_source = self.line(line_index).fragments[fragment_index].style_source; + let shift = self + .line_relative_aligned_subtree_root(style_source) + .and_then(|(root, _)| aligned_subtree_shift(&aligned_subtrees, root)) + .unwrap_or(normal_subtree_shift); + self.line_mut(line_index).fragments[fragment_index].block_offset += shift; + } + } + let current_block_offset = self.current_block_offset; + let marker_count = self.line(line_index).static_position_markers.len(); + for marker_index in 0..marker_count { + // Static position markers are resolved against the inline box that contains them, so they move with that + // box's aligned subtree. + let box_ = self.line(line_index).static_position_markers[marker_index].box_; + let shift = self + .line_relative_aligned_subtree_root(self.context().parent_node(box_)) + .and_then(|(root, _)| aligned_subtree_shift(&aligned_subtrees, root)) + .unwrap_or(normal_subtree_shift); + let mut line = self.line_mut(line_index); + let marker = &mut line.static_position_markers[marker_index]; + marker.inline_offset += inline_offset; + marker.block_offset += block_offset + current_block_offset + shift; + } + { let mut line = self.line_mut(line_index); - for marker in &mut line.static_position_markers { - marker.inline_offset += inline_offset; - marker.block_offset += block_offset + current_block_offset; - } line.block_length = latest - earliest; line.block_end = current_block_offset + line.block_length; - line.baseline = line_box_baseline; + line.baseline = line_box_baseline + normal_subtree_shift; } - self.should_advance_to_last_line_box_block_end = should_align_strut; + self.should_advance_to_last_line_box_block_end = should_align_strut_to_line_box_baseline; } pub(crate) fn remove_last_line_if_empty(&mut self) { @@ -788,6 +947,21 @@ impl<'builder, 'context, 'pass> LineBuilder<'builder, 'context, 'pass> { } } +// https://drafts.csswg.org/css2/#propdef-vertical-align +fn line_relative_alignment(style: StyleValues) -> Option { + let keyword = style.vertical_align_is_keyword().then(|| style.vertical_align_keyword())?; + matches!(keyword, vertical_align::TOP | vertical_align::BOTTOM).then_some(keyword) +} + +// Returns None for a root with no fragment on this line, which happens when its inline box is fragmented across +// lines; such a line only holds content that moves with the rest of the line box. +fn aligned_subtree_shift(subtrees: &[LineRelativeAlignedSubtree], root: Node) -> Option { + subtrees + .iter() + .find(|subtree| subtree.root == root) + .map(|subtree| subtree.shift) +} + pub(crate) fn normal_line_height(style: StyleValues) -> CssPixels { let ascent = style.font_ascent().round_ties_even() as i64; let descent = style.font_descent().round_ties_even() as i64; diff --git a/Tests/LibWeb/Layout/expected/abspos-inline-containing-block-first-last-line-rule.txt b/Tests/LibWeb/Layout/expected/abspos-inline-containing-block-first-last-line-rule.txt index 9425614414ec5..0280fc7022739 100644 --- a/Tests/LibWeb/Layout/expected/abspos-inline-containing-block-first-last-line-rule.txt +++ b/Tests/LibWeb/Layout/expected/abspos-inline-containing-block-first-last-line-rule.txt @@ -1,6 +1,6 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline - BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 409 0+0+0] [BFC] children: not-inline - BlockContainer at [0,8] [0+0+0 800 0+0+0] [0+0+0 393 0+0+0] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 404 0+0+0] [BFC] children: not-inline + BlockContainer at [0,8] [0+0+0 800 0+0+0] [0+0+0 388 0+0+0] children: not-inline BlockContainer at [8,8] [8+0+0 130 0+0+662] [8+0+0 60 0+0+8] children: inline TextNode <#text> (not painted) InlineNode at [8,10] [0+0+0 126.28125 0+0+0] [0+0+0 56 0+0+0] @@ -69,54 +69,54 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children BlockContainer <(anonymous)> at [0,220] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline TextNode <#text> (not painted) TextNode <#text> (not painted) - BlockContainer at [8,220] [8+0+0 300 0+0+492] [8+0+0 65 0+0+8] children: inline + BlockContainer at [8,220] [8+0+0 300 0+0+492] [8+0+0 60 0+0+8] children: inline TextNode <#text> (not painted) - InlineNode at [8,267] [0+0+0 46.875 0+0+0] [0+0+0 60 0+0+0] - frag 0 from TextNode start: 0, length: 1, rect: [8,267 9.703125x16] baseline: 12.796875 + InlineNode at [8,262] [0+0+0 46.875 0+0+0] [0+0+0 60 0+0+0] + frag 0 from TextNode start: 0, length: 1, rect: [8,262 9.703125x16] baseline: 12.796875 "x" frag 1 from BlockContainer start: 0, length: 0, rect: [17.703125,220 30x60] baseline: 60 - frag 2 from TextNode start: 0, length: 1, rect: [47.703125,267 7.171875x16] baseline: 12.796875 + frag 2 from TextNode start: 0, length: 1, rect: [47.703125,262 7.171875x16] baseline: 12.796875 "y" TextNode <#text> (not painted) BlockContainer at [17.703125,220] inline-block [0+0+0 30 0+0+0] [0+0+0 60 0+0+0] [BFC] children: not-inline TextNode <#text> (not painted) - BlockContainer at [8,267] positioned [0+0+0 46.875 0+0+0] [0+0+0 60 0+0+0] [BFC] children: not-inline + BlockContainer at [8,262] positioned [0+0+0 46.875 0+0+0] [0+0+0 60 0+0+0] [BFC] children: not-inline TextNode <#text> (not painted) - BlockContainer <(anonymous)> at [0,293] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline + BlockContainer <(anonymous)> at [0,288] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline TextNode <#text> (not painted) TextNode <#text> (not painted) - TableWrapper <(anonymous)> at [0,293] [0+0+0 204 0+0+596] [0+0+0 20 0+0+0] [BFC] children: not-inline - Box at [0,293] table-box [0+0+0 204 0+0+0] [0+0+0 20 0+0+0] [TFC] children: not-inline - Box at [2,295] table-row-group [0+0+0 200 0+0+0] [0+0+0 16 0+0+0] children: not-inline - Box at [2,295] table-row [0+0+0 200 0+0+0] [0+0+0 16 0+0+0] children: not-inline - BlockContainer
at [2,295] table-cell [0+0+0 200 0+0+0] [0+0+0 16 0+0+0] [BFC] children: inline - InlineNode at [2,295] [0+0+0 66.21875 0+0+0] [0+0+0 16 0+0+0] - frag 0 from TextNode start: 0, length: 9, rect: [2,295 66.21875x16] baseline: 12.796875 + TableWrapper <(anonymous)> at [0,288] [0+0+0 204 0+0+596] [0+0+0 20 0+0+0] [BFC] children: not-inline + Box at [0,288] table-box [0+0+0 204 0+0+0] [0+0+0 20 0+0+0] [TFC] children: not-inline + Box at [2,290] table-row-group [0+0+0 200 0+0+0] [0+0+0 16 0+0+0] children: not-inline + Box at [2,290] table-row [0+0+0 200 0+0+0] [0+0+0 16 0+0+0] children: not-inline + BlockContainer
at [2,290] table-cell [0+0+0 200 0+0+0] [0+0+0 16 0+0+0] [BFC] children: inline + InlineNode at [2,290] [0+0+0 66.21875 0+0+0] [0+0+0 16 0+0+0] + frag 0 from TextNode start: 0, length: 9, rect: [2,290 66.21875x16] baseline: 12.796875 "cell text" TextNode <#text> (not painted) - BlockContainer at [2,295] positioned [0+0+0 66.21875 0+0+0] [0+0+0 16 0+0+0] [BFC] children: not-inline - BlockContainer <(anonymous)> at [0,313] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline + BlockContainer at [2,290] positioned [0+0+0 66.21875 0+0+0] [0+0+0 16 0+0+0] [BFC] children: not-inline + BlockContainer <(anonymous)> at [0,308] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline TextNode <#text> (not painted) TextNode <#text> (not painted) - BlockContainer at [8,321] [8+0+0 300 0+0+492] [8+0+0 80 0+0+8] children: inline + BlockContainer at [8,316] [8+0+0 300 0+0+492] [8+0+0 80 0+0+8] children: inline TextNode <#text> (not painted) - InlineNode at [8,323] [0+0+0 6.859375 0+0+0] [0+0+0 76 0+0+0] - frag 0 from TextNode start: 0, length: 1, rect: [8,323 6.859375x16] baseline: 12.796875 + InlineNode at [8,318] [0+0+0 6.859375 0+0+0] [0+0+0 76 0+0+0] + frag 0 from TextNode start: 0, length: 1, rect: [8,318 6.859375x16] baseline: 12.796875 "t" - frag 0 from Box start: 0, length: 0, rect: [8,341 300x40] baseline: 0 - frag 0 from TextNode start: 0, length: 1, rect: [8,383 6.859375x16] baseline: 12.796875 + frag 0 from Box start: 0, length: 0, rect: [8,336 300x40] baseline: 0 + frag 0 from TextNode start: 0, length: 1, rect: [8,378 6.859375x16] baseline: 12.796875 "t" TextNode <#text> (not painted) - Box
at [8,341] flex-container(row) [0+0+0 300 0+0+0] [0+0+0 40 0+0+0] [FFC] children: not-inline - BlockContainer at [8,323] positioned [0+0+0 6.859375 0+0+0] [0+0+0 76 0+0+0] [BFC] children: not-inline + Box
at [8,336] flex-container(row) [0+0+0 300 0+0+0] [0+0+0 40 0+0+0] [FFC] children: not-inline + BlockContainer at [8,318] positioned [0+0+0 6.859375 0+0+0] [0+0+0 76 0+0+0] [BFC] children: not-inline TextNode <#text> (not painted) TextNode <#text> (not painted) - BlockContainer <(anonymous)> at [0,409] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline + BlockContainer <(anonymous)> at [0,404] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline TextNode <#text> (not painted) ViewportPaintable (Viewport<#document>) [0,0 800x600] - PaintableWithLines (BlockContainer) [0,0 800x409] - PaintableWithLines (BlockContainer) [0,8 800x393] + PaintableWithLines (BlockContainer) [0,0 800x404] + PaintableWithLines (BlockContainer) [0,8 800x388] PaintableWithLines (BlockContainer
.frame) [8,8 130x60] InlinePaintable (InlineNode.rel) [8,10 126.28125x56] PaintableWithLines (BlockContainer.cb) [8,10 17.4375x56] @@ -134,24 +134,24 @@ ViewportPaintable (Viewport<#document>) [0,0 800x600] InlinePaintable (InlineNode.rel) [227.5625,194 56.71875x16] PaintableWithLines (BlockContainer.cb) [231.5625,194 44.71875x16] PaintableWithLines (BlockContainer(anonymous)) [0,220 800x0] - PaintableWithLines (BlockContainer
.frame) [8,220 300x65] - InlinePaintable (InlineNode.rel) [8,267 46.875x60] + PaintableWithLines (BlockContainer
.frame) [8,220 300x60] + InlinePaintable (InlineNode.rel) [8,262 46.875x60] PaintableWithLines (BlockContainer) [17.703125,220 30x60] - PaintableWithLines (BlockContainer.cb) [8,267 46.875x60] - PaintableWithLines (BlockContainer(anonymous)) [0,293 800x0] - PaintableWithLines (TableWrapper(anonymous)) [0,293 204x20] - Paintable (Box) [0,293 204x20] - Paintable (Box) [2,295 200x16] - Paintable (Box) [2,295 200x16] - PaintableWithLines (BlockContainer
) [2,295 200x16] - InlinePaintable (InlineNode.rel) [2,295 66.21875x16] - PaintableWithLines (BlockContainer.cb) [2,295 66.21875x16] - PaintableWithLines (BlockContainer(anonymous)) [0,313 800x0] - PaintableWithLines (BlockContainer
.frame) [8,321 300x80] - InlinePaintable (InlineNode.rel) [8,323 6.859375x76] - Paintable (Box
) [8,341 300x40] - PaintableWithLines (BlockContainer
.cb) [8,323 6.859375x76] - PaintableWithLines (BlockContainer(anonymous)) [0,409 800x0] + PaintableWithLines (BlockContainer.cb) [8,262 46.875x60] + PaintableWithLines (BlockContainer(anonymous)) [0,288 800x0] + PaintableWithLines (TableWrapper(anonymous)) [0,288 204x20] + Paintable (Box) [0,288 204x20] + Paintable (Box) [2,290 200x16] + Paintable (Box) [2,290 200x16] + PaintableWithLines (BlockContainer
) [2,290 200x16] + InlinePaintable (InlineNode.rel) [2,290 66.21875x16] + PaintableWithLines (BlockContainer.cb) [2,290 66.21875x16] + PaintableWithLines (BlockContainer(anonymous)) [0,308 800x0] + PaintableWithLines (BlockContainer
.frame) [8,316 300x80] + InlinePaintable (InlineNode.rel) [8,318 6.859375x76] + Paintable (Box
) [8,336 300x40] + PaintableWithLines (BlockContainer
.cb) [8,318 6.859375x76] + PaintableWithLines (BlockContainer(anonymous)) [0,404 800x0] SC for Viewport<#document> [0,0 800x600] (z-index: auto) - SC for BlockContainer [0,0 800x409] (z-index: auto) + SC for BlockContainer [0,0 800x404] (z-index: auto) diff --git a/Tests/LibWeb/Layout/expected/vertical-align-bottom-atomic-inline-line-height.txt b/Tests/LibWeb/Layout/expected/vertical-align-bottom-atomic-inline-line-height.txt new file mode 100644 index 0000000000000..63c398738a6c6 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/vertical-align-bottom-atomic-inline-line-height.txt @@ -0,0 +1,38 @@ +Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 62 0+0+0] [BFC] children: not-inline + BlockContainer at [8,8] [8+0+0 784 0+0+8] [8+0+0 46 0+0+8] children: not-inline + BlockContainer at [8,8] [0+0+0 784 0+0+0] [0+0+0 23 0+0+0] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [8,11 20x20] baseline: 16 + "A" + frag 1 from BlockContainer start: 0, length: 0, rect: [28,8 20x23] baseline: 23 + frag 2 from TextNode start: 0, length: 1, rect: [48,11 20x20] baseline: 16 + "B" + TextNode <#text> (not painted) + BlockContainer at [28,8] inline-block [0+0+0 20 0+0+0] [0+0+0 23 0+0+0] [BFC] children: not-inline + TextNode <#text> (not painted) + BlockContainer <(anonymous)> at [8,31] [0+0+0 784 0+0+0] [0+0+0 0 0+0+0] children: inline + TextNode <#text> (not painted) + BlockContainer at [8,31] [0+0+0 784 0+0+0] [0+0+0 23 0+0+0] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [8,34 20x20] baseline: 16 + "A" + frag 1 from Box start: 0, length: 0, rect: [28,31 20x23] baseline: 23 + frag 2 from TextNode start: 0, length: 1, rect: [48,34 20x20] baseline: 16 + "B" + TextNode <#text> (not painted) + Box at [28,31] flex-container(row) [0+0+0 20 0+0+0] [0+0+0 23 0+0+0] [FFC] children: not-inline + TextNode <#text> (not painted) + BlockContainer <(anonymous)> at [8,54] [0+0+0 784 0+0+0] [0+0+0 0 0+0+0] children: inline + TextNode <#text> (not painted) + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x62] + PaintableWithLines (BlockContainer) [8,8 784x46] + PaintableWithLines (BlockContainer
.line) [8,8 784x23] + PaintableWithLines (BlockContainer.box.block) [28,8 20x23] + PaintableWithLines (BlockContainer(anonymous)) [8,31 784x0] + PaintableWithLines (BlockContainer
.line) [8,31 784x23] + Paintable (Box.box.flex) [28,31 20x23] + PaintableWithLines (BlockContainer(anonymous)) [8,54 784x0] + +SC for Viewport<#document> [0,0 800x600] (z-index: auto) + SC for BlockContainer [0,0 800x62] (z-index: auto) diff --git a/Tests/LibWeb/Layout/expected/vertical-align-line-relative-subtrees.txt b/Tests/LibWeb/Layout/expected/vertical-align-line-relative-subtrees.txt new file mode 100644 index 0000000000000..f571e1aa84760 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/vertical-align-line-relative-subtrees.txt @@ -0,0 +1,67 @@ +Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 372 0+0+0] [BFC] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 372 0+0+0] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 44 0+0+0] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [0,0 20x20] baseline: 16 + "A" + frag 1 from TextNode start: 0, length: 1, rect: [80,0 20x20] baseline: 16 + "D" + TextNode <#text> (not painted) + InlineNode at [20,24] [0+0+0 60 0+0+0] [0+0+0 40 0+0+0] + frag 0 from TextNode start: 0, length: 1, rect: [20,24 20x20] baseline: 16 + "B" + frag 1 from BlockContainer start: 0, length: 0, rect: [40,0 20x40] baseline: 40 + frag 2 from TextNode start: 0, length: 1, rect: [60,24 20x20] baseline: 16 + "C" + TextNode <#text> (not painted) + BlockContainer at [40,0] inline-block [0+0+0 20 0+0+0] [0+0+0 40 0+0+0] [BFC] children: not-inline + TextNode <#text> (not painted) + TextNode <#text> (not painted) + BlockContainer <(anonymous)> at [0,44] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline + TextNode <#text> (not painted) + BlockContainer at [0,44] [0+0+0 800 0+0+0] [0+0+0 64 0+0+0] children: inline + frag 0 from BlockContainer start: 0, length: 0, rect: [0,44 20x60] baseline: 60 + frag 1 from BlockContainer start: 0, length: 0, rect: [20,83 20x25] baseline: 25 + BlockContainer at [0,44] inline-block [0+0+0 20 0+0+0] [0+0+0 60 0+0+0] [BFC] children: not-inline + BlockContainer at [20,83] inline-block [0+0+0 20 0+0+0] [0+0+0 25 0+0+0] [BFC] children: not-inline + BlockContainer <(anonymous)> at [0,108] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline + TextNode <#text> (not painted) + BlockContainer at [0,108] [0+0+0 800 0+0+0] [0+0+0 64 0+0+0] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [0,152 20x20] baseline: 16 + "A" + frag 1 from BlockContainer start: 0, length: 0, rect: [20,108 20x60] baseline: 60 + TextNode <#text> (not painted) + BlockContainer at [20,108] inline-block [0+0+0 20 0+0+0] [0+0+0 60 0+0+0] [BFC] children: not-inline + BlockContainer <(anonymous)> at [0,172] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline + TextNode <#text> (not painted) + BlockContainer at [0,172] [0+0+0 800 0+0+0] [0+0+0 200 0+0+0] children: inline + InlineNode at [758,172] [0+0+0 40 0+0+0] [0+0+0 30 0+0+0] + frag 0 from TextNode start: 0, length: 1, rect: [758,172 20x20] baseline: 16 + "x" + frag 1 from BlockContainer start: 0, length: 0, rect: [760,192 40x10] baseline: 0 + TextNode <#text> (not painted) + BlockContainer at [760,192] inline-block [0+0+0 10 0+0+0] [0+0+0 40 0+0+0] [BFC] children: not-inline + BlockContainer <(anonymous)> at [0,372] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline + TextNode <#text> (not painted) + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x372] + PaintableWithLines (BlockContainer) [0,0 800x372] + PaintableWithLines (BlockContainer
.line) [0,0 800x44] + InlinePaintable (InlineNode.subtree) [20,24 60x40] + PaintableWithLines (BlockContainer.sub-atomic) [40,0 20x40] + PaintableWithLines (BlockContainer(anonymous)) [0,44 800x0] + PaintableWithLines (BlockContainer
.line) [0,44 800x64] + PaintableWithLines (BlockContainer.tall) [0,44 20x60] + PaintableWithLines (BlockContainer.bottom) [20,83 20x25] + PaintableWithLines (BlockContainer(anonymous)) [0,108 800x0] + PaintableWithLines (BlockContainer
.line.subtree) [0,108 800x64] + PaintableWithLines (BlockContainer.tall) [20,108 20x60] + PaintableWithLines (BlockContainer(anonymous)) [0,172 800x0] + PaintableWithLines (BlockContainer
.vertical) [0,172 800x200] + InlinePaintable (InlineNode.middle) [758,172 40x30] + PaintableWithLines (BlockContainer.vertical-top) [760,192 10x40] + PaintableWithLines (BlockContainer(anonymous)) [0,372 800x0] + +SC for Viewport<#document> [0,0 800x600] (z-index: auto) + SC for BlockContainer [0,0 800x372] (z-index: auto) diff --git a/Tests/LibWeb/Layout/expected/vertical-align-text-top-bottom.txt b/Tests/LibWeb/Layout/expected/vertical-align-text-top-bottom.txt new file mode 100644 index 0000000000000..9649548ae1fe5 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/vertical-align-text-top-bottom.txt @@ -0,0 +1,43 @@ +Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 94 0+0+0] [BFC] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 94 0+0+0] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 40 0+0+0] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [0,10 20x20] baseline: 16 + "A" + frag 1 from BlockContainer start: 0, length: 0, rect: [20,10 20x10] baseline: 20 + frag 2 from BlockContainer start: 0, length: 0, rect: [40,20 20x10] baseline: 6 + TextNode <#text> (not painted) + BlockContainer at [20,10] inline-block [0+0+0 20 0+0+0] [0+0+0 10 0+0+0] [BFC] children: not-inline + BlockContainer at [40,20] inline-block [0+0+0 20 0+0+0] [0+0+0 10 0+0+0] [BFC] children: not-inline + BlockContainer <(anonymous)> at [0,40] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline + TextNode <#text> (not painted) + BlockContainer at [0,40] [0+0+0 800 0+0+0] [0+0+0 54 0+0+0] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [0,64 20x20] baseline: 16 + "A" + TextNode <#text> (not painted) + InlineNode at [20,48] [0+0+0 80 0+0+0] [0+0+0 40 0+0+0] + frag 0 from TextNode start: 0, length: 1, rect: [20,48 40x40] baseline: 32 + "B" + frag 1 from BlockContainer start: 0, length: 0, rect: [60,48 20x10] baseline: 40 + frag 2 from BlockContainer start: 0, length: 0, rect: [80,78 20x10] baseline: 6 + TextNode <#text> (not painted) + BlockContainer at [60,48] inline-block [0+0+0 20 0+0+0] [0+0+0 10 0+0+0] [BFC] children: not-inline + BlockContainer at [80,78] inline-block [0+0+0 20 0+0+0] [0+0+0 10 0+0+0] [BFC] children: not-inline + BlockContainer <(anonymous)> at [0,94] [0+0+0 800 0+0+0] [0+0+0 0 0+0+0] children: inline + TextNode <#text> (not painted) + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x94] + PaintableWithLines (BlockContainer) [0,0 800x94] + PaintableWithLines (BlockContainer
.line) [0,0 800x40] + PaintableWithLines (BlockContainer.box.text-top) [20,10 20x10] + PaintableWithLines (BlockContainer.box.text-bottom) [40,20 20x10] + PaintableWithLines (BlockContainer(anonymous)) [0,40 800x0] + PaintableWithLines (BlockContainer
.line) [0,40 800x54] + InlinePaintable (InlineNode.parent) [20,48 80x40] + PaintableWithLines (BlockContainer.box.text-top) [60,48 20x10] + PaintableWithLines (BlockContainer.box.text-bottom) [80,78 20x10] + PaintableWithLines (BlockContainer(anonymous)) [0,94 800x0] + +SC for Viewport<#document> [0,0 800x600] (z-index: auto) + SC for BlockContainer [0,0 800x94] (z-index: auto) diff --git a/Tests/LibWeb/Layout/input/vertical-align-bottom-atomic-inline-line-height.html b/Tests/LibWeb/Layout/input/vertical-align-bottom-atomic-inline-line-height.html new file mode 100644 index 0000000000000..146e595370510 --- /dev/null +++ b/Tests/LibWeb/Layout/input/vertical-align-bottom-atomic-inline-line-height.html @@ -0,0 +1,25 @@ + + +
AB
+
AB
diff --git a/Tests/LibWeb/Layout/input/vertical-align-line-relative-subtrees.html b/Tests/LibWeb/Layout/input/vertical-align-line-relative-subtrees.html new file mode 100644 index 0000000000000..defcb6ca25dd3 --- /dev/null +++ b/Tests/LibWeb/Layout/input/vertical-align-line-relative-subtrees.html @@ -0,0 +1,54 @@ + + + +
ABCD
+ +
+ +
A
+ +
x
diff --git a/Tests/LibWeb/Layout/input/vertical-align-text-top-bottom.html b/Tests/LibWeb/Layout/input/vertical-align-text-top-bottom.html new file mode 100644 index 0000000000000..925bc6a6141cf --- /dev/null +++ b/Tests/LibWeb/Layout/input/vertical-align-text-top-bottom.html @@ -0,0 +1,30 @@ + + + +
A
+
AB
diff --git a/Tests/LibWeb/Ref/expected/wpt-import/css/CSS2/linebox/vertical-align-nested-top-001-ref.html b/Tests/LibWeb/Ref/expected/wpt-import/css/CSS2/linebox/vertical-align-nested-top-001-ref.html new file mode 100644 index 0000000000000..1fd155e38013e --- /dev/null +++ b/Tests/LibWeb/Ref/expected/wpt-import/css/CSS2/linebox/vertical-align-nested-top-001-ref.html @@ -0,0 +1,14 @@ + + + + XX + diff --git a/Tests/LibWeb/Ref/expected/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-padding-ref.html b/Tests/LibWeb/Ref/expected/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-padding-ref.html new file mode 100644 index 0000000000000..f855bab6a2f4a --- /dev/null +++ b/Tests/LibWeb/Ref/expected/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-padding-ref.html @@ -0,0 +1,38 @@ + + + +
+ + Next + + +
+
+ + Next + + +
+ diff --git a/Tests/LibWeb/Ref/input/wpt-import/css/CSS2/linebox/vertical-align-nested-top-001.html b/Tests/LibWeb/Ref/input/wpt-import/css/CSS2/linebox/vertical-align-nested-top-001.html new file mode 100644 index 0000000000000..b8acc3f34a7c1 --- /dev/null +++ b/Tests/LibWeb/Ref/input/wpt-import/css/CSS2/linebox/vertical-align-nested-top-001.html @@ -0,0 +1,20 @@ + + + + + + + XX + diff --git a/Tests/LibWeb/Ref/input/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-padding.html b/Tests/LibWeb/Ref/input/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-padding.html new file mode 100644 index 0000000000000..86895549b120f --- /dev/null +++ b/Tests/LibWeb/Ref/input/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-padding.html @@ -0,0 +1,49 @@ + + + + + + +
+ + Next + + +
+
+ + Next + + +
+ diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-001.txt b/Tests/LibWeb/Text/expected/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-001.txt new file mode 100644 index 0000000000000..31df970161394 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-001.txt @@ -0,0 +1,25 @@ +Harness status: OK + +Found 20 tests + +20 Pass +Pass top+ +Pass top+top +Pass top+text-top +Pass top+bottom +Pass top+text-bottom +Pass text-top+ +Pass text-top+top +Pass text-top+text-top +Pass text-top+bottom +Pass text-top+text-bottom +Pass text-bottom+ +Pass text-bottom+top +Pass text-bottom+text-top +Pass text-bottom+bottom +Pass text-bottom+text-bottom +Pass bottom+ +Pass bottom+top +Pass bottom+text-top +Pass bottom+bottom +Pass bottom+text-bottom \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-001.html b/Tests/LibWeb/Text/input/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-001.html new file mode 100644 index 0000000000000..e1b6268ce9781 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/css/CSS2/linebox/vertical-align-top-bottom-001.html @@ -0,0 +1,87 @@ + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +