layout: Right-to-left support for other layout modes (#33375)

This change removes the `effective_writing_mode` concept and tries to
properly implement right-to-left layout support for all non-inline
writing modes. In general, what needs to happen is that rectangles
need to be converted to physical rectangles using the containing block.
A right-to-left rectangle's inline start is on the right physical side
of the containing block. Likewise a positive inline offset in
right-to-left text is a negative physical one.

The implementation here is pretty good for most layout modes, but floats
are still a bit in process. Currently, floats are processed in the
logical layout of the block container, but there still might be issues
with float interaction with mixed RTL and LTR.

While this does move us closer to supporting vertical writing modes,
this is still unsupported.

New failures:
 - Vertical writing mode not supported:
   - `/css/CSS2/floats/floats-placement-vertical-001b.xht`
   - `/css/CSS2/floats/floats-placement-vertical-001c.xht`
 - Absolutes inlines should avoid floats (#33323)
   - `/css/css-position/position-absolute-dynamic-static-position-floats-004.html`
 - No support for grid
   - `/css/css-align/self-alignment/self-align-safe-unsafe-grid-003.html`
   - `/css/css-position/static-position/inline-level-absolute-in-block-level-context-009.html`
   - `/css/css-position/static-position/inline-level-absolute-in-block-level-context-010.html`
 - Cannot reproduce these locally on any platform. Very mysterious:
   - `/css/css-tables/row-group-margin-border-padding.html`
   - `/css/css-tables/row-margin-border-padding.html`
 - Exposes bugs we have related to hanging whitespace in preserved
   whitespace inlines:
   - `/css/css-text/white-space/trailing-space-and-text-alignment-rtl-003.html`
   - `/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-023.html`

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Rakhi Sharma <atbrakhi@igalia.com>
This commit is contained in:
Martin Robinson 2024-09-11 06:40:11 -07:00 committed by GitHub
parent bc8d8b62c3
commit 027fc53e2f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
99 changed files with 946 additions and 867 deletions

View file

@ -707,7 +707,6 @@ impl<'a> BuilderForBoxFragment<'a> {
painting_area_override: None, painting_area_override: None,
positioning_area_override: Some( positioning_area_override: Some(
positioning_area positioning_area
.to_physical(self.fragment.style.effective_writing_mode())
.translate(self.containing_block.origin.to_vector()) .translate(self.containing_block.origin.to_vector())
.to_webrender(), .to_webrender(),
), ),

View file

@ -171,13 +171,13 @@ impl FlexItemLayoutResult {
let flags = fragment_info.flags; let flags = fragment_info.flags;
let containing_block = flex_context.containing_block; let containing_block = flex_context.containing_block;
let container_writing_mode = containing_block.effective_writing_mode(); let container_writing_mode = containing_block.style.writing_mode;
let style = item.box_.style(); let style = item.box_.style();
let mut fragment = BoxFragment::new( let mut fragment = BoxFragment::new(
fragment_info, fragment_info,
style.clone(), style.clone(),
self.fragments, self.fragments,
content_rect.to_physical(container_writing_mode), content_rect.to_physical(Some(flex_context.containing_block)),
flex_context flex_context
.sides_to_flow_relative(item.padding) .sides_to_flow_relative(item.padding)
.to_physical(container_writing_mode), .to_physical(container_writing_mode),
@ -199,7 +199,7 @@ impl FlexItemLayoutResult {
if style.clone_position() == Position::Relative { if style.clone_position() == Position::Relative {
fragment.content_rect.origin += relative_adjustement(style, containing_block) fragment.content_rect.origin += relative_adjustement(style, containing_block)
.to_physical_size(containing_block.effective_writing_mode()) .to_physical_size(containing_block.style.writing_mode)
} }
(fragment, self.positioning_context) (fragment, self.positioning_context)
@ -409,7 +409,7 @@ impl FlexContainer {
let mut sum_of_flex_shrink_factors = 0.0; let mut sum_of_flex_shrink_factors = 0.0;
let mut item_infos = vec![]; let mut item_infos = vec![];
let container_is_horizontal = self.style.effective_writing_mode().is_horizontal(); let container_is_horizontal = self.style.writing_mode.is_horizontal();
for kid in self.children.iter() { for kid in self.children.iter() {
let kid = &mut *kid.borrow_mut(); let kid = &mut *kid.borrow_mut();
match kid { match kid {
@ -806,8 +806,8 @@ impl FlexContainer {
all_baselines.last = line_all_baselines.last; all_baselines.last = line_all_baselines.last;
} }
let physical_line_position = flow_relative_line_position let physical_line_position =
.to_physical_size(self.style.effective_writing_mode()); flow_relative_line_position.to_physical_size(self.style.writing_mode);
for (fragment, _) in &mut final_line_layout.item_fragments { for (fragment, _) in &mut final_line_layout.item_fragments {
fragment.content_rect.origin += physical_line_position; fragment.content_rect.origin += physical_line_position;
} }
@ -912,12 +912,13 @@ impl FlexContainer {
start_corner: LogicalVec2::zero(), start_corner: LogicalVec2::zero(),
size: self.config.flex_axis.vec2_to_flow_relative(container_size), size: self.config.flex_axis.vec2_to_flow_relative(container_size),
} }
.to_physical(containing_block.effective_writing_mode()); .to_physical(Some(containing_block));
let hoisted_box = AbsolutelyPositionedBox::to_hoisted( let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
absolutely_positioned_box, absolutely_positioned_box,
static_position_rect, static_position_rect,
logical_alignment, logical_alignment,
self.config.writing_mode,
); );
let hoisted_fragment = hoisted_box.fragment.clone(); let hoisted_fragment = hoisted_box.fragment.clone();
positioning_context.push(hoisted_box); positioning_context.push(hoisted_box);
@ -995,14 +996,8 @@ fn allocate_free_cross_space_for_flex_line(
impl<'a> FlexItem<'a> { impl<'a> FlexItem<'a> {
fn new(flex_context: &FlexContext, box_: &'a mut FlexItemBox) -> Self { fn new(flex_context: &FlexContext, box_: &'a mut FlexItemBox) -> Self {
let containing_block = flex_context.containing_block; let containing_block = flex_context.containing_block;
let parent_writing_mode = containing_block.effective_writing_mode(); let parent_writing_mode = containing_block.style.writing_mode;
let item_writing_mode = box_.style().effective_writing_mode(); let item_writing_mode = box_.style().writing_mode;
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
assert_eq!(
parent_writing_mode, item_writing_mode,
"Mixed writing modes are not supported yet"
);
let container_is_horizontal = parent_writing_mode.is_horizontal(); let container_is_horizontal = parent_writing_mode.is_horizontal();
let item_is_horizontal = item_writing_mode.is_horizontal(); let item_is_horizontal = item_writing_mode.is_horizontal();
@ -1697,15 +1692,6 @@ impl FlexItem<'_> {
) )
}); });
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
let container_writing_mode = containing_block.effective_writing_mode();
assert_eq!(
container_writing_mode,
self.box_.style().effective_writing_mode(),
"Mixed writing modes are not supported yet"
);
// … and also the items inline axis.
let cross_size = match used_cross_size_override { let cross_size = match used_cross_size_override {
Some(s) => AuOrAuto::LengthPercentage(s), Some(s) => AuOrAuto::LengthPercentage(s),
None => self.content_box_size.cross.map(|cross_size| { None => self.content_box_size.cross.map(|cross_size| {
@ -1717,14 +1703,11 @@ impl FlexItem<'_> {
}; };
let ifc = &mut self.box_.independent_formatting_context; let ifc = &mut self.box_.independent_formatting_context;
let item_writing_mode = ifc.style().effective_writing_mode(); let item_writing_mode = ifc.style().writing_mode;
let item_is_horizontal = item_writing_mode.is_horizontal(); let item_is_horizontal = item_writing_mode.is_horizontal();
let flex_axis = flex_context.config.flex_axis; let flex_axis = flex_context.config.flex_axis;
let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis( let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
containing_block containing_block.style.writing_mode.is_horizontal(),
.style
.effective_writing_mode()
.is_horizontal(),
item_is_horizontal, item_is_horizontal,
flex_axis, flex_axis,
); );
@ -1769,6 +1752,7 @@ impl FlexItem<'_> {
) )
}; };
let container_writing_mode = containing_block.style.writing_mode;
match ifc { match ifc {
IndependentFormattingContext::Replaced(replaced) => { IndependentFormattingContext::Replaced(replaced) => {
let size = replaced let size = replaced
@ -1820,7 +1804,7 @@ impl FlexItem<'_> {
let item_writing_mode_is_orthogonal_to_container_writing_mode = let item_writing_mode_is_orthogonal_to_container_writing_mode =
flex_context.config.writing_mode.is_horizontal() != flex_context.config.writing_mode.is_horizontal() !=
non_replaced.style.effective_writing_mode().is_horizontal(); non_replaced.style.writing_mode.is_horizontal();
let has_compatible_baseline = match flex_context.config.flex_axis { let has_compatible_baseline = match flex_context.config.flex_axis {
FlexAxis::Row => !item_writing_mode_is_orthogonal_to_container_writing_mode, FlexAxis::Row => !item_writing_mode_is_orthogonal_to_container_writing_mode,
FlexAxis::Column => item_writing_mode_is_orthogonal_to_container_writing_mode, FlexAxis::Column => item_writing_mode_is_orthogonal_to_container_writing_mode,
@ -2013,7 +1997,7 @@ impl FlexItemBox {
let flex_axis = config.flex_axis; let flex_axis = config.flex_axis;
let main_start_cross_start = config.main_start_cross_start_sides_are; let main_start_cross_start = config.main_start_cross_start_sides_are;
let style = self.style().clone(); let style = self.style().clone();
let item_writing_mode = style.effective_writing_mode(); let item_writing_mode = style.writing_mode;
let item_is_horizontal = item_writing_mode.is_horizontal(); let item_is_horizontal = item_writing_mode.is_horizontal();
let cross_axis_is_item_block_axis = let cross_axis_is_item_block_axis =
cross_axis_is_item_block_axis(container_is_horizontal, item_is_horizontal, flex_axis); cross_axis_is_item_block_axis(container_is_horizontal, item_is_horizontal, flex_axis);

View file

@ -17,7 +17,6 @@ use crate::cell::ArcRefCell;
use crate::formatting_contexts::IndependentFormattingContext; use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::BaseFragmentInfo; use crate::fragment_tree::BaseFragmentInfo;
use crate::positioned::AbsolutelyPositionedBox; use crate::positioned::AbsolutelyPositionedBox;
use crate::style_ext::ComputedValuesExt;
mod construct; mod construct;
mod geom; mod geom;
@ -69,7 +68,7 @@ impl FlexContainerConfig {
FlexContainerConfig { FlexContainerConfig {
container_is_single_line, container_is_single_line,
writing_mode: container_style.effective_writing_mode(), writing_mode: container_style.writing_mode,
flex_axis, flex_axis,
flex_direction, flex_direction,
flex_direction_is_reversed, flex_direction_is_reversed,

View file

@ -16,6 +16,7 @@ use euclid::num::Zero;
use serde::Serialize; use serde::Serialize;
use servo_arc::Arc; use servo_arc::Arc;
use style::computed_values::float::T as FloatProperty; use style::computed_values::float::T as FloatProperty;
use style::computed_values::position::T as Position;
use style::logical_geometry::WritingMode; use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::Clear; use style::values::computed::Clear;
@ -26,8 +27,8 @@ use crate::dom::NodeExt;
use crate::dom_traversal::{Contents, NodeAndStyleInfo}; use crate::dom_traversal::{Contents, NodeAndStyleInfo};
use crate::formatting_contexts::IndependentFormattingContext; use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin}; use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin};
use crate::geom::{LogicalRect, LogicalVec2, ToLogical}; use crate::geom::{LogicalRect, LogicalVec2, PhysicalPoint, PhysicalRect, ToLogical};
use crate::positioned::PositioningContext; use crate::positioned::{relative_adjustement, PositioningContext};
use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside, PaddingBorderMargin};
use crate::{ContainingBlock, IndefiniteContainingBlock}; use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -412,7 +413,7 @@ impl FloatContext {
// The object fits perfectly here. Place it. // The object fits perfectly here. Place it.
match object.side { match object.side {
FloatSide::Left => { FloatSide::InlineStart => {
let left_object_edge = match first_band.left { let left_object_edge = match first_band.left {
Some(band_left) => band_left.max(self.containing_block_info.inline_start), Some(band_left) => band_left.max(self.containing_block_info.inline_start),
None => self.containing_block_info.inline_start, None => self.containing_block_info.inline_start,
@ -422,7 +423,7 @@ impl FloatContext {
block: first_band.top.max(ceiling), block: first_band.top.max(ceiling),
} }
}, },
FloatSide::Right => { FloatSide::InlineEnd => {
let right_object_edge = match first_band.right { let right_object_edge = match first_band.right {
Some(band_right) => band_right.min(self.containing_block_info.inline_end), Some(band_right) => band_right.min(self.containing_block_info.inline_end),
None => self.containing_block_info.inline_end, None => self.containing_block_info.inline_end,
@ -441,8 +442,8 @@ impl FloatContext {
let ceiling = self.ceiling(); let ceiling = self.ceiling();
let new_float_origin = self.place_object(new_float, ceiling); let new_float_origin = self.place_object(new_float, ceiling);
let new_float_extent = match new_float.side { let new_float_extent = match new_float.side {
FloatSide::Left => new_float_origin.inline + new_float.size.inline, FloatSide::InlineStart => new_float_origin.inline + new_float.size.inline,
FloatSide::Right => new_float_origin.inline, FloatSide::InlineEnd => new_float_origin.inline,
}; };
let new_float_rect = LogicalRect { let new_float_rect = LogicalRect {
@ -459,11 +460,11 @@ impl FloatContext {
// Update clear. // Update clear.
match new_float.side { match new_float.side {
FloatSide::Left => { FloatSide::InlineStart => {
self.clear_left_position self.clear_left_position
.max_assign(new_float_rect.max_block_position()); .max_assign(new_float_rect.max_block_position());
}, },
FloatSide::Right => { FloatSide::InlineEnd => {
self.clear_right_position self.clear_right_position
.max_assign(new_float_rect.max_block_position()); .max_assign(new_float_rect.max_block_position());
}, },
@ -513,8 +514,8 @@ pub struct PlacementInfo {
/// See CSS 2.1 § 9.5.1: <https://www.w3.org/TR/CSS2/visuren.html#float-position> /// See CSS 2.1 § 9.5.1: <https://www.w3.org/TR/CSS2/visuren.html#float-position>
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum FloatSide { pub enum FloatSide {
Left, InlineStart,
Right, InlineEnd,
} }
/// Internal data structure that describes a nonoverlapping vertical region in which floats may be /// Internal data structure that describes a nonoverlapping vertical region in which floats may be
@ -536,11 +537,18 @@ pub struct FloatBand {
} }
impl FloatSide { impl FloatSide {
fn from_style(style: &ComputedValues) -> Option<FloatSide> { fn from_style_and_container_writing_mode(
match style.get_box().float { style: &ComputedValues,
FloatProperty::None => None, container_writing_mode: WritingMode,
FloatProperty::Left => Some(FloatSide::Left), ) -> Option<FloatSide> {
FloatProperty::Right => Some(FloatSide::Right), match (style.get_box().float, container_writing_mode.is_bidi_ltr()) {
(FloatProperty::None, _) => None,
(FloatProperty::Left, true) | (FloatProperty::Right, false) => {
Some(FloatSide::InlineStart)
},
(FloatProperty::Right, true) | (FloatProperty::Left, false) => {
Some(FloatSide::InlineEnd)
},
} }
} }
} }
@ -549,7 +557,7 @@ impl FloatBand {
/// Determines whether an object fits in a band. Returns true if the object fits. /// Determines whether an object fits in a band. Returns true if the object fits.
fn object_fits(&self, object: &PlacementInfo, walls: &ContainingBlockPositionInfo) -> bool { fn object_fits(&self, object: &PlacementInfo, walls: &ContainingBlockPositionInfo) -> bool {
match object.side { match object.side {
FloatSide::Left => { FloatSide::InlineStart => {
// Compute a candidate left position for the object. // Compute a candidate left position for the object.
let candidate_left = match self.left { let candidate_left = match self.left {
None => walls.inline_start, None => walls.inline_start,
@ -570,7 +578,7 @@ impl FloatBand {
} }
}, },
FloatSide::Right => { FloatSide::InlineEnd => {
// Compute a candidate right position for the object. // Compute a candidate right position for the object.
let candidate_right = match self.right { let candidate_right = match self.right {
None => walls.inline_end, None => walls.inline_end,
@ -694,13 +702,13 @@ impl FloatBandNode {
let mut new_band = self.band; let mut new_band = self.band;
if self.band.top >= range.start && self.band.top < range.end { if self.band.top >= range.start && self.band.top < range.end {
match side { match side {
FloatSide::Left => { FloatSide::InlineStart => {
new_band.left = match new_band.left { new_band.left = match new_band.left {
Some(old_value) => Some(std::cmp::max(old_value, new_value)), Some(old_value) => Some(std::cmp::max(old_value, new_value)),
None => Some(new_value), None => Some(new_value),
}; };
}, },
FloatSide::Right => { FloatSide::InlineEnd => {
new_band.right = match new_band.right { new_band.right = match new_band.right {
Some(old_value) => Some(std::cmp::min(old_value, new_value)), Some(old_value) => Some(std::cmp::min(old_value, new_value)),
None => Some(new_value), None => Some(new_value),
@ -984,23 +992,22 @@ impl FloatBox {
); );
children = replaced.contents.make_fragments( children = replaced.contents.make_fragments(
&replaced.style, &replaced.style,
content_size content_size.to_physical_size(containing_block.style.writing_mode),
.to_physical_size(containing_block.effective_writing_mode()),
) )
}, },
}; };
let content_rect = LogicalRect { let containing_block_writing_mode = containing_block.style.writing_mode;
start_corner: LogicalVec2::zero(), let content_rect = PhysicalRect::new(
size: content_size, PhysicalPoint::zero(),
}; content_size.to_physical_size(containing_block_writing_mode),
);
let containing_block_writing_mode = containing_block.effective_writing_mode();
BoxFragment::new( BoxFragment::new(
self.contents.base_fragment_info(), self.contents.base_fragment_info(),
style.clone(), style.clone(),
children, children,
content_rect.to_physical(containing_block_writing_mode), content_rect,
pbm.padding.to_physical(containing_block_writing_mode), pbm.padding.to_physical(containing_block_writing_mode),
pbm.border.to_physical(containing_block_writing_mode), pbm.border.to_physical(containing_block_writing_mode),
margin.to_physical(containing_block_writing_mode), margin.to_physical(containing_block_writing_mode),
@ -1203,7 +1210,7 @@ impl SequentialLayoutState {
pub(crate) fn place_float_fragment( pub(crate) fn place_float_fragment(
&mut self, &mut self,
box_fragment: &mut BoxFragment, box_fragment: &mut BoxFragment,
container_writing_mode: WritingMode, containing_block: &ContainingBlock,
margins_collapsing_with_parent_containing_block: CollapsedMargin, margins_collapsing_with_parent_containing_block: CollapsedMargin,
block_offset_from_containing_block_top: Au, block_offset_from_containing_block_top: Au,
) { ) {
@ -1218,19 +1225,35 @@ impl SequentialLayoutState {
block_start_of_containing_block_in_bfc + block_offset_from_containing_block_top, block_start_of_containing_block_in_bfc + block_offset_from_containing_block_top,
); );
let pbm_sums = (box_fragment.padding + box_fragment.border + box_fragment.margin) let container_writing_mode = containing_block.style.writing_mode;
let logical_float_size = box_fragment
.content_rect
.size
.to_logical(container_writing_mode);
let pbm_sums = box_fragment
.padding_border_margin()
.to_logical(container_writing_mode); .to_logical(container_writing_mode);
let content_rect = &box_fragment.content_rect.to_logical(container_writing_mode);
let margin_box_start_corner = self.floats.add_float(&PlacementInfo { let margin_box_start_corner = self.floats.add_float(&PlacementInfo {
size: content_rect.size + pbm_sums.sum(), size: logical_float_size + pbm_sums.sum(),
side: FloatSide::from_style(&box_fragment.style).expect("Float box wasn't floated!"), side: FloatSide::from_style_and_container_writing_mode(
&box_fragment.style,
container_writing_mode,
)
.expect("Float box wasn't floated!"),
clear: box_fragment.style.get_box().clear, clear: box_fragment.style.get_box().clear,
}); });
// Re-calculate relative adjustment so that it is not lost when the BoxFragment's
// `content_rect` is overwritten below.
let relative_offset = match box_fragment.style.clone_position() {
Position::Relative => relative_adjustement(&box_fragment.style, containing_block),
_ => LogicalVec2::zero(),
};
// This is the position of the float in the float-containing block formatting context. We add the // This is the position of the float in the float-containing block formatting context. We add the
// existing start corner here because we may have already gotten some relative positioning offset. // existing start corner here because we may have already gotten some relative positioning offset.
let new_position_in_bfc = let new_position_in_bfc =
margin_box_start_corner + pbm_sums.start_offset() + content_rect.start_corner; margin_box_start_corner + pbm_sums.start_offset() + relative_offset;
// This is the position of the float relative to the containing block start. // This is the position of the float relative to the containing block start.
let new_position_in_containing_block = LogicalVec2 { let new_position_in_containing_block = LogicalVec2 {
@ -1238,7 +1261,13 @@ impl SequentialLayoutState {
block: new_position_in_bfc.block - block_start_of_containing_block_in_bfc, block: new_position_in_bfc.block - block_start_of_containing_block_in_bfc,
}; };
box_fragment.content_rect.origin = box_fragment.content_rect = LogicalRect {
new_position_in_containing_block.to_physical_point(container_writing_mode); start_corner: new_position_in_containing_block,
size: box_fragment
.content_rect
.size
.to_logical(container_writing_mode),
}
.to_physical(Some(&containing_block));
} }
} }

View file

@ -5,6 +5,7 @@
use app_units::Au; use app_units::Au;
use bitflags::bitflags; use bitflags::bitflags;
use fonts::{FontMetrics, GlyphStore}; use fonts::{FontMetrics, GlyphStore};
use itertools::Either;
use servo_arc::Arc; use servo_arc::Arc;
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
use style::properties::ComputedValues; use style::properties::ComputedValues;
@ -14,7 +15,6 @@ use style::values::generics::font::LineHeight;
use style::values::specified::align::AlignFlags; use style::values::specified::align::AlignFlags;
use style::values::specified::box_::DisplayOutside; use style::values::specified::box_::DisplayOutside;
use style::values::specified::text::TextDecorationLine; use style::values::specified::text::TextDecorationLine;
use style::values::Either;
use style::Zero; use style::Zero;
use unicode_bidi::{BidiInfo, Level}; use unicode_bidi::{BidiInfo, Level};
use webrender_api::FontInstanceKey; use webrender_api::FontInstanceKey;
@ -25,10 +25,11 @@ use crate::cell::ArcRefCell;
use crate::fragment_tree::{ use crate::fragment_tree::{
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, Fragment, TextFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, Fragment, TextFragment,
}; };
use crate::geom::{LogicalRect, LogicalVec2}; use crate::geom::{AuOrAuto, LogicalRect, LogicalVec2, PhysicalRect, ToLogical};
use crate::positioned::{ use crate::positioned::{
relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength,
}; };
use crate::ContainingBlock;
pub(super) struct LineMetrics { pub(super) struct LineMetrics {
/// The block offset of the line start in the containing /// The block offset of the line start in the containing
@ -49,10 +50,10 @@ bitflags! {
const HAD_ANY_LINE_ITEMS = 1 << 0; const HAD_ANY_LINE_ITEMS = 1 << 0;
/// Whether or not the starting inline border, padding, or margin of the inline box /// Whether or not the starting inline border, padding, or margin of the inline box
/// was encountered. /// was encountered.
const HAD_START_PBM = 1 << 2; const HAD_LEFT_PBM = 1 << 2;
/// Whether or not the ending inline border, padding, or margin of the inline box /// Whether or not the ending inline border, padding, or margin of the inline box
/// was encountered. /// was encountered.
const HAD_END_PBM = 1 << 3; const HAD_RIGHT_PBM = 1 << 3;
/// Whether or not any floats were encountered while laying out this inline box. /// Whether or not any floats were encountered while laying out this inline box.
const HAD_ANY_FLOATS = 1 << 4; const HAD_ANY_FLOATS = 1 << 4;
} }
@ -66,16 +67,21 @@ pub(super) struct LineItemLayoutInlineContainerState {
/// that is currently being laid out. /// that is currently being laid out.
pub identifier: Option<InlineBoxIdentifier>, pub identifier: Option<InlineBoxIdentifier>,
/// The fragments that are laid out into this inline container on a line. /// The fragments and their logical rectangle relative within the current inline box (or
pub fragments: Vec<Fragment>, /// line). These logical rectangles will be converted into physical ones and the Fragment's
/// `content_rect` will be updated once the inline box's final size is known in
/// [`LineItemLayout::end_inline_box`].
pub fragments: Vec<(Fragment, LogicalRect<Au>)>,
/// The current inline adavnce of the layout in the coordinates of this inline box. /// The current inline advance of the layout in the coordinates of this inline box.
pub inline_advance: Au, pub inline_advance: Au,
/// Flags which track various features during layout. /// Flags which track various features during layout.
flags: LineLayoutInlineContainerFlags, flags: LineLayoutInlineContainerFlags,
/// The offset of the parent, relative to the start position of the line. /// The offset of the parent, relative to the start position of the line, not including
/// any inline start and end borders which are only processed when the inline box is
/// finished.
pub parent_offset: LogicalVec2<Au>, pub parent_offset: LogicalVec2<Au>,
/// The block offset of the parent's baseline relative to the block start of the line. This /// The block offset of the parent's baseline relative to the block start of the line. This
@ -117,7 +123,7 @@ impl LineItemLayoutInlineContainerState {
None, None,
LogicalVec2::zero(), LogicalVec2::zero(),
baseline_offset, baseline_offset,
Either::Second(PositioningContextLength::zero()), Either::Right(PositioningContextLength::zero()),
); );
state.inline_advance = starting_inline_advance; state.inline_advance = starting_inline_advance;
state state
@ -208,8 +214,8 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
// TODO: This level needs either to be last_level, or if there were // TODO: This level needs either to be last_level, or if there were
// unicode characters inserted for the inline box, we need to get the // unicode characters inserted for the inline box, we need to get the
// level from them. // level from them.
LineItem::StartInlineBoxPaddingBorderMargin(_) => last_level, LineItem::LeftInlineBoxPaddingBorderMargin(_) => last_level,
LineItem::EndInlineBoxPaddingBorderMargin(_) => last_level, LineItem::RightInlineBoxPaddingBorderMargin(_) => last_level,
LineItem::Atomic(_, atomic) => atomic.bidi_level, LineItem::Atomic(_, atomic) => atomic.bidi_level,
LineItem::AbsolutelyPositioned(..) => last_level, LineItem::AbsolutelyPositioned(..) => last_level,
LineItem::Float(..) => { LineItem::Float(..) => {
@ -227,7 +233,25 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels)); sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels));
} }
for item in line_items.into_iter().by_ref() { // `BidiInfo::reorder_visual` will reorder the contents of the line so that they
// are in the correct order as if one was looking at the line from left-to-right.
// During this layout we do not lay out from left to right. Instead we lay out
// from inline-start to inline-end. If the overall line contents have been flipped
// for BiDi, flip them again so that they are in line start-to-end order rather
// than left-to-right order.
let line_item_iterator = if self
.layout
.containing_block
.style
.writing_mode
.is_bidi_ltr()
{
Either::Left(line_items.into_iter())
} else {
Either::Right(line_items.into_iter().rev())
};
for item in line_item_iterator.into_iter().by_ref() {
// When preparing to lay out a new line item, start and end inline boxes, so that the current // When preparing to lay out a new line item, start and end inline boxes, so that the current
// inline box state reflects the item's parent. Items in the line are not necessarily in tree // inline box state reflects the item's parent. Items in the line are not necessarily in tree
// order due to BiDi and other reordering so the inline box of the item could potentially be // order due to BiDi and other reordering so the inline box of the item could potentially be
@ -238,15 +262,15 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
.flags .flags
.insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS); .insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS);
match item { match item {
LineItem::StartInlineBoxPaddingBorderMargin(_) => { LineItem::LeftInlineBoxPaddingBorderMargin(_) => {
self.current_state self.current_state
.flags .flags
.insert(LineLayoutInlineContainerFlags::HAD_START_PBM); .insert(LineLayoutInlineContainerFlags::HAD_LEFT_PBM);
}, },
LineItem::EndInlineBoxPaddingBorderMargin(_) => { LineItem::RightInlineBoxPaddingBorderMargin(_) => {
self.current_state self.current_state
.flags .flags
.insert(LineLayoutInlineContainerFlags::HAD_END_PBM); .insert(LineLayoutInlineContainerFlags::HAD_RIGHT_PBM);
}, },
LineItem::TextRun(_, text_run) => self.layout_text_run(text_run), LineItem::TextRun(_, text_run) => self.layout_text_run(text_run),
LineItem::Atomic(_, atomic) => self.layout_atomic(atomic), LineItem::Atomic(_, atomic) => self.layout_atomic(atomic),
@ -257,11 +281,29 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
// Move back to the root of the inline box tree, so that all boxes are ended. // Move back to the root of the inline box tree, so that all boxes are ended.
self.prepare_layout_for_inline_box(None); self.prepare_layout_for_inline_box(None);
std::mem::take(&mut self.current_state.fragments)
let fragments_and_rectangles = std::mem::take(&mut self.current_state.fragments);
fragments_and_rectangles
.into_iter()
.map(|(mut fragment, logical_rect)| {
if matches!(fragment, Fragment::Float(_)) {
return fragment;
}
// We do not know the actual physical position of a logically laid out inline element, until
// we know the width of the containing inline block. This step converts the logical rectangle
// into a physical one based on the inline formatting context width.
if let Some(content_rect) = fragment.content_rect_mut() {
*content_rect = logical_rect.to_physical(Some(self.layout.containing_block))
}
fragment
})
.collect()
} }
fn current_positioning_context_mut(&mut self) -> &mut PositioningContext { fn current_positioning_context_mut(&mut self) -> &mut PositioningContext {
if let Either::First(ref mut positioning_context) = self if let Either::Left(ref mut positioning_context) = self
.current_state .current_state
.positioning_context_or_start_offset_in_parent .positioning_context_or_start_offset_in_parent
{ {
@ -272,8 +314,8 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
.rev() .rev()
.find_map( .find_map(
|state| match state.positioning_context_or_start_offset_in_parent { |state| match state.positioning_context_or_start_offset_in_parent {
Either::First(ref mut positioning_context) => Some(positioning_context), Either::Left(ref mut positioning_context) => Some(positioning_context),
Either::Second(_) => None, Either::Right(_) => None,
}, },
) )
.unwrap_or(self.layout.positioning_context) .unwrap_or(self.layout.positioning_context)
@ -292,8 +334,8 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
let positioning_context_or_start_offset_in_parent = let positioning_context_or_start_offset_in_parent =
match PositioningContext::new_for_style(style) { match PositioningContext::new_for_style(style) {
Some(positioning_context) => Either::First(positioning_context), Some(positioning_context) => Either::Left(positioning_context),
None => Either::Second(self.current_positioning_context_mut().len()), None => Either::Right(self.current_positioning_context_mut().len()),
}; };
let parent_offset = LogicalVec2 { let parent_offset = LogicalVec2 {
@ -316,7 +358,7 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
fn end_inline_box(&mut self) { fn end_inline_box(&mut self) {
let outer_state = self.state_stack.pop().expect("Ended unknown inline box"); let outer_state = self.state_stack.pop().expect("Ended unknown inline box");
let mut inner_state = std::mem::replace(&mut self.current_state, outer_state); let inner_state = std::mem::replace(&mut self.current_state, outer_state);
let identifier = inner_state.identifier.expect("Ended unknown inline box"); let identifier = inner_state.identifier.expect("Ended unknown inline box");
let inline_box_state = let inline_box_state =
@ -328,12 +370,24 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
let mut border = inline_box_state.pbm.border; let mut border = inline_box_state.pbm.border;
let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero); let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero);
let had_start = inner_state let had_left = inner_state
.flags .flags
.contains(LineLayoutInlineContainerFlags::HAD_START_PBM); .contains(LineLayoutInlineContainerFlags::HAD_LEFT_PBM);
let had_end = inner_state let had_right = inner_state
.flags .flags
.contains(LineLayoutInlineContainerFlags::HAD_END_PBM); .contains(LineLayoutInlineContainerFlags::HAD_RIGHT_PBM);
let (had_start, had_end) = if self
.layout
.containing_block
.style
.writing_mode
.is_bidi_ltr()
{
(had_left, had_right)
} else {
(had_right, had_left)
};
if !had_start { if !had_start {
padding.inline_start = Au::zero(); padding.inline_start = Au::zero();
@ -368,14 +422,6 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
}, },
}; };
let ifc_writing_mode = self.layout.containing_block.effective_writing_mode();
for fragment in inner_state.fragments.iter_mut() {
if let Fragment::Float(box_fragment) = fragment {
box_fragment.content_rect.origin -=
pbm_sums.start_offset().to_physical_size(ifc_writing_mode);
}
}
// Relative adjustment should not affect the rest of line layout, so we can // Relative adjustment should not affect the rest of line layout, so we can
// do it right before creating the Fragment. // do it right before creating the Fragment.
let style = &inline_box.style; let style = &inline_box.style;
@ -383,11 +429,40 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
content_rect.start_corner += relative_adjustement(style, self.layout.containing_block); content_rect.start_corner += relative_adjustement(style, self.layout.containing_block);
} }
let ifc_writing_mode = self.layout.containing_block.style.writing_mode;
let inline_box_containing_block = ContainingBlock {
inline_size: content_rect.size.inline,
block_size: AuOrAuto::Auto,
style: &self.layout.containing_block.style,
};
let fragments = inner_state
.fragments
.into_iter()
.map(|(mut fragment, logical_rect)| {
let is_float = matches!(fragment, Fragment::Float(_));
if let Some(content_rect) = fragment.content_rect_mut() {
if is_float {
content_rect.origin -=
pbm_sums.start_offset().to_physical_size(ifc_writing_mode);
} else {
// We do not know the actual physical position of a logically laid out inline element, until
// we know the width of the containing inline block. This step converts the logical rectangle
// into a physical one now that we've computed inline size of the containing inline block above.
*content_rect = logical_rect.to_physical(Some(&inline_box_containing_block))
}
}
fragment
})
.collect();
// Previously all the fragment's children were positioned relative to the linebox,
// but they need to be made relative to this fragment.
let physical_content_rect = content_rect.to_physical(Some(self.layout.containing_block));
let mut fragment = BoxFragment::new( let mut fragment = BoxFragment::new(
inline_box.base_fragment_info, inline_box.base_fragment_info,
style.clone(), style.clone(),
inner_state.fragments, fragments,
content_rect.to_physical(ifc_writing_mode), physical_content_rect,
padding.to_physical(ifc_writing_mode), padding.to_physical(ifc_writing_mode),
border.to_physical(ifc_writing_mode), border.to_physical(ifc_writing_mode),
margin.to_physical(ifc_writing_mode), margin.to_physical(ifc_writing_mode),
@ -395,28 +470,36 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
CollapsedBlockMargins::zero(), CollapsedBlockMargins::zero(),
); );
let offset_from_parent_ifc = LogicalVec2 {
inline: pbm_sums.inline_start + self.current_state.inline_advance,
block: content_rect.start_corner.block,
}
.to_physical_vector(self.layout.containing_block.style.writing_mode);
match inner_state.positioning_context_or_start_offset_in_parent { match inner_state.positioning_context_or_start_offset_in_parent {
Either::First(mut positioning_context) => { Either::Left(mut positioning_context) => {
positioning_context positioning_context
.layout_collected_children(self.layout.layout_context, &mut fragment); .layout_collected_children(self.layout.layout_context, &mut fragment);
positioning_context.adjust_static_position_of_hoisted_fragments_with_offset( positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
&fragment.content_rect.origin.to_vector(), &offset_from_parent_ifc,
PositioningContextLength::zero(), PositioningContextLength::zero(),
); );
self.current_positioning_context_mut() self.current_positioning_context_mut()
.append(positioning_context); .append(positioning_context);
}, },
Either::Second(start_offset) => { Either::Right(start_offset) => {
self.current_positioning_context_mut() self.current_positioning_context_mut()
.adjust_static_position_of_hoisted_fragments_with_offset( .adjust_static_position_of_hoisted_fragments_with_offset(
&fragment.content_rect.origin.to_vector(), &offset_from_parent_ifc,
start_offset, start_offset,
); );
}, },
} }
self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum(); self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
self.current_state.fragments.push(Fragment::Box(fragment)); self.current_state
.fragments
.push((Fragment::Box(fragment), content_rect));
} }
fn calculate_inline_box_block_start( fn calculate_inline_box_block_start(
@ -477,8 +560,7 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
text_item.font_metrics.ascent - text_item.font_metrics.ascent -
self.current_state.parent_offset.block, self.current_state.parent_offset.block,
}; };
let content_rect = LogicalRect {
let rect = LogicalRect {
start_corner, start_corner,
size: LogicalVec2 { size: LogicalVec2 {
block: text_item.font_metrics.line_gap, block: text_item.font_metrics.line_gap,
@ -487,29 +569,37 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
}; };
self.current_state.inline_advance += inline_advance; self.current_state.inline_advance += inline_advance;
self.current_state self.current_state.fragments.push((
.fragments Fragment::Text(TextFragment {
.push(Fragment::Text(TextFragment {
base: text_item.base_fragment_info.into(), base: text_item.base_fragment_info.into(),
parent_style: text_item.parent_style, parent_style: text_item.parent_style,
rect: rect.to_physical(self.layout.containing_block.effective_writing_mode()), rect: PhysicalRect::zero(),
font_metrics: text_item.font_metrics, font_metrics: text_item.font_metrics,
font_key: text_item.font_key, font_key: text_item.font_key,
glyphs: text_item.text, glyphs: text_item.text,
text_decoration_line: text_item.text_decoration_line, text_decoration_line: text_item.text_decoration_line,
justification_adjustment: self.justification_adjustment, justification_adjustment: self.justification_adjustment,
})); }),
content_rect,
));
} }
fn layout_atomic(&mut self, mut atomic: AtomicLineItem) { fn layout_atomic(&mut self, atomic: AtomicLineItem) {
// The initial `start_corner` of the Fragment is only the PaddingBorderMargin sum start // The initial `start_corner` of the Fragment is only the PaddingBorderMargin sum start
// offset, which is the sum of the start component of the padding, border, and margin. // offset, which is the sum of the start component of the padding, border, and margin.
// This needs to be added to the calculated block and inline positions. // This needs to be added to the calculated block and inline positions.
// Make the final result relative to the parent box. // Make the final result relative to the parent box.
let ifc_writing_mode = self.layout.containing_block.style.writing_mode;
let padding_border_margin_sides = atomic
.fragment
.padding_border_margin()
.to_logical(ifc_writing_mode);
let mut atomic_offset = LogicalVec2 { let mut atomic_offset = LogicalVec2 {
inline: self.current_state.inline_advance, inline: self.current_state.inline_advance + padding_border_margin_sides.inline_start,
block: atomic.calculate_block_start(&self.line_metrics) - block: atomic.calculate_block_start(&self.line_metrics) -
self.current_state.parent_offset.block, self.current_state.parent_offset.block +
padding_border_margin_sides.block_start,
}; };
if atomic.fragment.style.clone_position().is_relative() { if atomic.fragment.style.clone_position().is_relative() {
@ -517,14 +607,25 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
relative_adjustement(&atomic.fragment.style, self.layout.containing_block); relative_adjustement(&atomic.fragment.style, self.layout.containing_block);
} }
let ifc_writing_mode = self.layout.containing_block.effective_writing_mode(); // Reconstruct a logical rectangle relative to the inline box container that will be used
atomic.fragment.content_rect.origin += atomic_offset.to_physical_size(ifc_writing_mode); // after the inline box is procesed to find a final physical rectangle.
let content_rect = LogicalRect {
start_corner: atomic_offset,
size: atomic
.fragment
.content_rect
.size
.to_logical(ifc_writing_mode),
};
if let Some(mut positioning_context) = atomic.positioning_context { if let Some(mut positioning_context) = atomic.positioning_context {
let physical_rect_as_if_in_root =
content_rect.to_physical(Some(self.layout.containing_block));
positioning_context.adjust_static_position_of_hoisted_fragments_with_offset( positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
&atomic.fragment.content_rect.origin.to_vector(), &physical_rect_as_if_in_root.origin.to_vector(),
PositioningContextLength::zero(), PositioningContextLength::zero(),
); );
self.current_positioning_context_mut() self.current_positioning_context_mut()
.append(positioning_context); .append(positioning_context);
} }
@ -532,7 +633,7 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
self.current_state.inline_advance += atomic.size.inline; self.current_state.inline_advance += atomic.size.inline;
self.current_state self.current_state
.fragments .fragments
.push(Fragment::Box(atomic.fragment)); .push((Fragment::Box(atomic.fragment), content_rect));
} }
fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) { fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
@ -572,7 +673,7 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
start_corner: initial_start_corner, start_corner: initial_start_corner,
size: LogicalVec2::zero(), size: LogicalVec2::zero(),
} }
.to_physical(self.layout.containing_block.effective_writing_mode()); .to_physical(Some(self.layout.containing_block));
let hoisted_box = AbsolutelyPositionedBox::to_hoisted( let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
absolute.absolutely_positioned_box.clone(), absolute.absolutely_positioned_box.clone(),
@ -581,12 +682,15 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
inline: AlignFlags::START, inline: AlignFlags::START,
block: AlignFlags::START, block: AlignFlags::START,
}, },
self.layout.containing_block.style.writing_mode,
); );
let hoisted_fragment = hoisted_box.fragment.clone(); let hoisted_fragment = hoisted_box.fragment.clone();
self.current_positioning_context_mut().push(hoisted_box); self.current_positioning_context_mut().push(hoisted_box);
self.current_state self.current_state.fragments.push((
.fragments Fragment::AbsoluteOrFixedPositioned(hoisted_fragment),
.push(Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)); LogicalRect::zero(),
));
} }
fn layout_float(&mut self, mut float: FloatLineItem) { fn layout_float(&mut self, mut float: FloatLineItem) {
@ -604,16 +708,17 @@ impl<'layout_data, 'layout> LineItemLayout<'layout_data, 'layout> {
block: self.line_metrics.block_offset + self.current_state.parent_offset.block, block: self.line_metrics.block_offset + self.current_state.parent_offset.block,
}; };
float.fragment.content_rect.origin -= distance_from_parent_to_ifc float.fragment.content_rect.origin -= distance_from_parent_to_ifc
.to_physical_size(self.layout.containing_block.effective_writing_mode()); .to_physical_size(self.layout.containing_block.style.writing_mode);
self.current_state self.current_state
.fragments .fragments
.push(Fragment::Float(float.fragment)); .push((Fragment::Float(float.fragment), LogicalRect::zero()));
} }
} }
pub(super) enum LineItem { pub(super) enum LineItem {
StartInlineBoxPaddingBorderMargin(InlineBoxIdentifier), LeftInlineBoxPaddingBorderMargin(InlineBoxIdentifier),
EndInlineBoxPaddingBorderMargin(InlineBoxIdentifier), RightInlineBoxPaddingBorderMargin(InlineBoxIdentifier),
TextRun(Option<InlineBoxIdentifier>, TextRunLineItem), TextRun(Option<InlineBoxIdentifier>, TextRunLineItem),
Atomic(Option<InlineBoxIdentifier>, AtomicLineItem), Atomic(Option<InlineBoxIdentifier>, AtomicLineItem),
AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem), AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem),
@ -623,8 +728,8 @@ pub(super) enum LineItem {
impl LineItem { impl LineItem {
fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> { fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
match self { match self {
LineItem::StartInlineBoxPaddingBorderMargin(identifier) => Some(*identifier), LineItem::LeftInlineBoxPaddingBorderMargin(identifier) => Some(*identifier),
LineItem::EndInlineBoxPaddingBorderMargin(identifier) => Some(*identifier), LineItem::RightInlineBoxPaddingBorderMargin(identifier) => Some(*identifier),
LineItem::TextRun(identifier, _) => *identifier, LineItem::TextRun(identifier, _) => *identifier,
LineItem::Atomic(identifier, _) => *identifier, LineItem::Atomic(identifier, _) => *identifier,
LineItem::AbsolutelyPositioned(identifier, _) => *identifier, LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
@ -634,8 +739,8 @@ impl LineItem {
pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool { pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
match self { match self {
LineItem::StartInlineBoxPaddingBorderMargin(_) => true, LineItem::LeftInlineBoxPaddingBorderMargin(_) => true,
LineItem::EndInlineBoxPaddingBorderMargin(_) => true, LineItem::RightInlineBoxPaddingBorderMargin(_) => true,
LineItem::TextRun(_, ref mut item) => item.trim_whitespace_at_end(whitespace_trimmed), LineItem::TextRun(_, ref mut item) => item.trim_whitespace_at_end(whitespace_trimmed),
LineItem::Atomic(..) => false, LineItem::Atomic(..) => false,
LineItem::AbsolutelyPositioned(..) => true, LineItem::AbsolutelyPositioned(..) => true,
@ -645,8 +750,8 @@ impl LineItem {
pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool { pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
match self { match self {
LineItem::StartInlineBoxPaddingBorderMargin(_) => true, LineItem::LeftInlineBoxPaddingBorderMargin(_) => true,
LineItem::EndInlineBoxPaddingBorderMargin(_) => true, LineItem::RightInlineBoxPaddingBorderMargin(_) => true,
LineItem::TextRun(_, ref mut item) => item.trim_whitespace_at_start(whitespace_trimmed), LineItem::TextRun(_, ref mut item) => item.trim_whitespace_at_start(whitespace_trimmed),
LineItem::Atomic(..) => false, LineItem::Atomic(..) => false,
LineItem::AbsolutelyPositioned(..) => true, LineItem::AbsolutelyPositioned(..) => true,

View file

@ -123,7 +123,7 @@ use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags,
PositioningFragment, PositioningFragment,
}; };
use crate::geom::{LogicalRect, LogicalVec2, PhysicalRect, ToLogical}; use crate::geom::{LogicalRect, LogicalVec2, PhysicalPoint, PhysicalRect, ToLogical};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
@ -719,7 +719,7 @@ impl<'layout_dta> InlineFormattingContextLayout<'layout_dta> {
inline_box_state.pbm.margin.inline_start.auto_is(Au::zero); inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
self.current_line_segment self.current_line_segment
.line_items .line_items
.push(LineItem::StartInlineBoxPaddingBorderMargin( .push(LineItem::LeftInlineBoxPaddingBorderMargin(
inline_box.identifier, inline_box.identifier,
)); ));
} }
@ -764,7 +764,7 @@ impl<'layout_dta> InlineFormattingContextLayout<'layout_dta> {
self.current_line_segment.inline_size += pbm_end; self.current_line_segment.inline_size += pbm_end;
self.current_line_segment self.current_line_segment
.line_items .line_items
.push(LineItem::EndInlineBoxPaddingBorderMargin( .push(LineItem::RightInlineBoxPaddingBorderMargin(
inline_box_state.identifier, inline_box_state.identifier,
)) ))
} }
@ -872,30 +872,34 @@ impl<'layout_dta> InlineFormattingContextLayout<'layout_dta> {
self.baselines.first.get_or_insert(baseline); self.baselines.first.get_or_insert(baseline);
self.baselines.last = Some(baseline); self.baselines.last = Some(baseline);
let line_rect = LogicalRect { // The inline part of this start offset was taken into account when determining
// The inline part of this start offset was taken into account when determining // the inline start of the line in `calculate_inline_start_for_current_line` so
// the inline start of the line in `calculate_inline_start_for_current_line` so // we do not need to include it in the `start_corner` of the line's main Fragment.
// we do not need to include it in the `start_corner` of the line's main Fragment. let start_corner = LogicalVec2 {
start_corner: LogicalVec2 { inline: Au::zero(),
inline: Au::zero(), block: block_start_position,
block: block_start_position, };
},
let logical_origin_in_physical_coordinates =
start_corner.to_physical_vector(self.containing_block.style.writing_mode);
self.positioning_context
.adjust_static_position_of_hoisted_fragments_with_offset(
&logical_origin_in_physical_coordinates,
start_positioning_context_length,
);
let physical_line_rect = LogicalRect {
start_corner,
size: LogicalVec2 { size: LogicalVec2 {
inline: self.containing_block.inline_size, inline: self.containing_block.inline_size,
block: effective_block_advance.resolve(), block: effective_block_advance.resolve(),
}, },
}; }
.to_physical(Some(self.containing_block));
let line_rect = line_rect.to_physical(self.containing_block.effective_writing_mode());
self.positioning_context
.adjust_static_position_of_hoisted_fragments_with_offset(
&line_rect.origin.to_vector(),
start_positioning_context_length,
);
self.fragments self.fragments
.push(Fragment::Positioning(PositioningFragment::new_anonymous( .push(Fragment::Positioning(PositioningFragment::new_anonymous(
line_rect, fragments, physical_line_rect,
fragments,
))); )));
} }
@ -909,9 +913,9 @@ impl<'layout_dta> InlineFormattingContextLayout<'layout_dta> {
last_line_or_forced_line_break: bool, last_line_or_forced_line_break: bool,
) -> (Au, Au) { ) -> (Au, Au) {
enum TextAlign { enum TextAlign {
Left, Start,
Center, Center,
Right, End,
} }
let style = self.containing_block.style; let style = self.containing_block.style;
let mut text_align_keyword = style.clone_text_align(); let mut text_align_keyword = style.clone_text_align();
@ -932,24 +936,24 @@ impl<'layout_dta> InlineFormattingContextLayout<'layout_dta> {
} }
let text_align = match text_align_keyword { let text_align = match text_align_keyword {
TextAlignKeyword::Start => { TextAlignKeyword::Start => TextAlign::Start,
if style.writing_mode.line_left_is_inline_start() {
TextAlign::Left
} else {
TextAlign::Right
}
},
TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center, TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
TextAlignKeyword::End => { TextAlignKeyword::End => TextAlign::End,
TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
if style.writing_mode.line_left_is_inline_start() { if style.writing_mode.line_left_is_inline_start() {
TextAlign::Right TextAlign::Start
} else { } else {
TextAlign::Left TextAlign::End
} }
}, },
TextAlignKeyword::Left | TextAlignKeyword::MozLeft => TextAlign::Left, TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
TextAlignKeyword::Right | TextAlignKeyword::MozRight => TextAlign::Right, if style.writing_mode.line_left_is_inline_start() {
TextAlignKeyword::Justify => TextAlign::Left, TextAlign::End
} else {
TextAlign::Start
}
},
TextAlignKeyword::Justify => TextAlign::Start,
}; };
let (line_start, available_space) = match self.current_line.placement_among_floats.get() { let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
@ -970,8 +974,8 @@ impl<'layout_dta> InlineFormattingContextLayout<'layout_dta> {
let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent; let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
let adjusted_line_start = line_start + let adjusted_line_start = line_start +
match text_align { match text_align {
TextAlign::Left => text_indent, TextAlign::Start => text_indent,
TextAlign::Right => (available_space - line_length).max(text_indent), TextAlign::End => (available_space - line_length).max(text_indent),
TextAlign::Center => (available_space - line_length + text_indent) TextAlign::Center => (available_space - line_length + text_indent)
.scale_by(0.5) .scale_by(0.5)
.max(text_indent), .max(text_indent),
@ -1015,7 +1019,7 @@ impl<'layout_dta> InlineFormattingContextLayout<'layout_dta> {
state.current_containing_block_offset(); state.current_containing_block_offset();
state.place_float_fragment( state.place_float_fragment(
fragment, fragment,
self.containing_block.effective_writing_mode(), self.containing_block,
CollapsedMargin::zero(), CollapsedMargin::zero(),
block_offset_from_containining_block_top, block_offset_from_containining_block_top,
); );
@ -1034,11 +1038,12 @@ impl<'layout_dta> InlineFormattingContextLayout<'layout_dta> {
float_item: &mut FloatLineItem, float_item: &mut FloatLineItem,
line_inline_size_without_trailing_whitespace: Au, line_inline_size_without_trailing_whitespace: Au,
) { ) {
let margin_box = float_item let logical_margin_rect_size = float_item
.fragment .fragment
.margin_rect() .margin_rect()
.to_logical(self.containing_block.effective_writing_mode()); .size
let inline_size = margin_box.size.inline.max(Au::zero()); .to_logical(self.containing_block.style.writing_mode);
let inline_size = logical_margin_rect_size.inline.max(Au::zero());
let available_inline_size = match self.current_line.placement_among_floats.get() { let available_inline_size = match self.current_line.placement_among_floats.get() {
Some(placement_among_floats) => placement_among_floats.size.inline, Some(placement_among_floats) => placement_among_floats.size.inline,
@ -1902,36 +1907,22 @@ impl IndependentFormattingContext {
bidi_level: Level, bidi_level: Level,
) { ) {
let style = self.style(); let style = self.style();
let container_writing_mode = layout.containing_block.style.effective_writing_mode(); let container_writing_mode = layout.containing_block.style.writing_mode;
let pbm = style.padding_border_margin(layout.containing_block); let pbm = style.padding_border_margin(layout.containing_block);
let margin = pbm.margin.auto_is(Au::zero); let margin = pbm.margin.auto_is(Au::zero);
let pbm_sums = pbm.padding + pbm.border + margin; let pbm_sums = pbm.padding + pbm.border + margin;
let pbm_physical_origin = pbm_sums
.start_offset()
.to_physical_point(container_writing_mode);
let mut child_positioning_context = None;
// We need to know the inline size of the atomic before deciding whether to do the line break. // We need to know the inline size of the atomic before deciding whether to do the line break.
let fragment = match self { let (fragments, content_rect, baselines, mut child_positioning_context) = match self {
IndependentFormattingContext::Replaced(replaced) => { IndependentFormattingContext::Replaced(replaced) => {
let size = replaced let size = replaced
.contents .contents
.used_size_as_if_inline_element(layout.containing_block, &replaced.style, &pbm) .used_size_as_if_inline_element(layout.containing_block, &replaced.style, &pbm)
.to_physical_size(container_writing_mode); .to_physical_size(container_writing_mode);
let fragments = replaced.contents.make_fragments(&replaced.style, size); let fragments = replaced.contents.make_fragments(&replaced.style, size);
let content_rect = PhysicalRect::new(pbm_physical_origin, size);
BoxFragment::new( let content_rect = PhysicalRect::new(PhysicalPoint::zero(), size);
replaced.base_fragment_info, (fragments, content_rect, None, None)
replaced.style.clone(),
fragments,
content_rect,
pbm.padding.to_physical(container_writing_mode),
pbm.border.to_physical(container_writing_mode),
margin.to_physical(container_writing_mode),
None, /* clearance */
CollapsedBlockMargins::zero(),
)
}, },
IndependentFormattingContext::NonReplaced(non_replaced) => { IndependentFormattingContext::NonReplaced(non_replaced) => {
let box_size = non_replaced let box_size = non_replaced
@ -2010,7 +2001,7 @@ impl IndependentFormattingContext {
}; };
let content_rect = PhysicalRect::new( let content_rect = PhysicalRect::new(
pbm_physical_origin, PhysicalPoint::zero(),
LogicalVec2 { LogicalVec2 {
block: block_size, block: block_size,
inline: inline_size, inline: inline_size,
@ -2018,32 +2009,50 @@ impl IndependentFormattingContext {
.to_physical_size(container_writing_mode), .to_physical_size(container_writing_mode),
); );
let mut fragment = BoxFragment::new( (
non_replaced.base_fragment_info,
non_replaced.style.clone(),
independent_layout.fragments, independent_layout.fragments,
content_rect, content_rect,
pbm.padding.to_physical(container_writing_mode), Some(independent_layout.baselines),
pbm.border.to_physical(container_writing_mode), Some(positioning_context),
margin.to_physical(container_writing_mode),
None,
CollapsedBlockMargins::zero(),
) )
.with_baselines(independent_layout.baselines);
if fragment
.style
.establishes_containing_block_for_absolute_descendants(fragment.base.flags)
{
positioning_context
.layout_collected_children(layout.layout_context, &mut fragment);
}
child_positioning_context = Some(positioning_context);
fragment
}, },
}; };
// Offset the content rectangle by the physical offset of the padding, border, and margin.
let pbm_physical_offset = pbm_sums
.start_offset()
.to_physical_size(container_writing_mode);
let content_rect = content_rect.translate(pbm_physical_offset.to_vector());
let fragment = BoxFragment::new(
self.base_fragment_info(),
self.style().clone(),
fragments,
content_rect,
pbm.padding.to_physical(container_writing_mode),
pbm.border.to_physical(container_writing_mode),
margin.to_physical(container_writing_mode),
None, /* clearance */
CollapsedBlockMargins::zero(),
);
// Apply baselines if necessary.
let mut fragment = match baselines {
Some(baselines) => fragment.with_baselines(baselines),
None => fragment,
};
// Lay out absolutely positioned children if this new atomic establishes a containing block
// for absolutes.
if let Some(positioning_context) = child_positioning_context.as_mut() {
if fragment
.style
.establishes_containing_block_for_absolute_descendants(fragment.base.flags)
{
positioning_context.layout_collected_children(layout.layout_context, &mut fragment);
}
}
if layout.text_wrap_mode == TextWrapMode::Wrap && if layout.text_wrap_mode == TextWrapMode::Wrap &&
!layout !layout
.ifc .ifc
@ -2055,8 +2064,8 @@ impl IndependentFormattingContext {
let size = pbm_sums.sum() + let size = pbm_sums.sum() +
fragment fragment
.content_rect .content_rect
.to_logical(container_writing_mode) .size
.size; .to_logical(container_writing_mode);
let baseline_offset = self let baseline_offset = self
.pick_baseline(&fragment.baselines(container_writing_mode)) .pick_baseline(&fragment.baselines(container_writing_mode))
.map(|baseline| pbm_sums.block_start + baseline) .map(|baseline| pbm_sums.block_start + baseline)

View file

@ -12,7 +12,6 @@ use serde::Serialize;
use servo_arc::Arc; use servo_arc::Arc;
use style::computed_values::clear::T as Clear; use style::computed_values::clear::T as Clear;
use style::computed_values::float::T as Float; use style::computed_values::float::T as Float;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Length, Size}; use style::values::computed::{Length, Size};
use style::values::specified::align::AlignFlags; use style::values::specified::align::AlignFlags;
@ -32,6 +31,7 @@ use crate::fragment_tree::{
}; };
use crate::geom::{ use crate::geom::{
AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalRect, PhysicalSides, ToLogical, AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalRect, PhysicalSides, ToLogical,
ToLogicalWithContainingBlock,
}; };
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
@ -245,7 +245,6 @@ impl OutsideMarker {
collapsible_with_parent_start_margin.unwrap_or(CollapsibleWithParentStartMargin(false)), collapsible_with_parent_start_margin.unwrap_or(CollapsibleWithParentStartMargin(false)),
); );
let containing_block_writing_mode = containing_block.effective_writing_mode();
let max_inline_size = let max_inline_size =
flow_layout flow_layout
.fragments .fragments
@ -265,7 +264,7 @@ impl OutsideMarker {
); );
}, },
} }
.to_logical(containing_block_writing_mode) .to_logical(&containing_block_for_children)
.max_inline_position(), .max_inline_position(),
) )
}); });
@ -297,7 +296,7 @@ impl OutsideMarker {
base_fragment_info, base_fragment_info,
self.marker_style.clone(), self.marker_style.clone(),
flow_layout.fragments, flow_layout.fragments,
content_rect.to_physical(containing_block_writing_mode), content_rect.to_physical(Some(containing_block)),
PhysicalSides::zero(), PhysicalSides::zero(),
PhysicalSides::zero(), PhysicalSides::zero(),
PhysicalSides::zero(), PhysicalSides::zero(),
@ -534,7 +533,7 @@ fn layout_block_level_children(
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
) -> FlowLayout { ) -> FlowLayout {
let mut placement_state = let mut placement_state =
PlacementState::new(collapsible_with_parent_start_margin, containing_block.style); PlacementState::new(collapsible_with_parent_start_margin, containing_block);
let fragments = match sequential_layout_state { let fragments = match sequential_layout_state {
Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially( Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
@ -705,6 +704,9 @@ impl BlockLevelBox {
}, },
}, },
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
// The static position of zero here is incorrect, however we do not know
// the correct positioning until later, in place_block_level_fragment, and
// this value will be adjusted there.
let hoisted_box = AbsolutelyPositionedBox::to_hoisted( let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
box_.clone(), box_.clone(),
// This is incorrect, however we do not know the correct positioning // This is incorrect, however we do not know the correct positioning
@ -715,6 +717,7 @@ impl BlockLevelBox {
inline: AlignFlags::START, inline: AlignFlags::START,
block: AlignFlags::START, block: AlignFlags::START,
}, },
containing_block.style.writing_mode,
); );
let hoisted_fragment = hoisted_box.fragment.clone(); let hoisted_fragment = hoisted_box.fragment.clone();
positioning_context.push(hoisted_box); positioning_context.push(hoisted_box);
@ -934,12 +937,12 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
}, },
}; };
let containing_block_writing_mode = containing_block.effective_writing_mode(); let containing_block_writing_mode = containing_block.style.writing_mode;
BoxFragment::new( BoxFragment::new(
base_fragment_info, base_fragment_info,
style.clone(), style.clone(),
flow_layout.fragments, flow_layout.fragments,
content_rect.to_physical(containing_block_writing_mode), content_rect.to_physical(Some(containing_block)),
pbm.padding.to_physical(containing_block_writing_mode), pbm.padding.to_physical(containing_block_writing_mode),
pbm.border.to_physical(containing_block_writing_mode), pbm.border.to_physical(containing_block_writing_mode),
margin.to_physical(containing_block_writing_mode), margin.to_physical(containing_block_writing_mode),
@ -1019,12 +1022,12 @@ impl NonReplacedFormattingContext {
}; };
let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
let containing_block_writing_mode = containing_block.effective_writing_mode(); let containing_block_writing_mode = containing_block.style.writing_mode;
BoxFragment::new( BoxFragment::new(
self.base_fragment_info, self.base_fragment_info,
self.style.clone(), self.style.clone(),
layout.fragments, layout.fragments,
content_rect.to_physical(containing_block_writing_mode), content_rect.to_physical(Some(containing_block)),
pbm.padding.to_physical(containing_block_writing_mode), pbm.padding.to_physical(containing_block_writing_mode),
pbm.border.to_physical(containing_block_writing_mode), pbm.border.to_physical(containing_block_writing_mode),
margin.to_physical(containing_block_writing_mode), margin.to_physical(containing_block_writing_mode),
@ -1269,12 +1272,12 @@ impl NonReplacedFormattingContext {
}; };
let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
let containing_block_writing_mode = containing_block.effective_writing_mode(); let containing_block_writing_mode = containing_block.style.writing_mode;
BoxFragment::new( BoxFragment::new(
self.base_fragment_info, self.base_fragment_info,
self.style.clone(), self.style.clone(),
layout.fragments, layout.fragments,
content_rect.to_physical(containing_block_writing_mode), content_rect.to_physical(Some(containing_block)),
pbm.padding.to_physical(containing_block_writing_mode), pbm.padding.to_physical(containing_block_writing_mode),
pbm.border.to_physical(containing_block_writing_mode), pbm.border.to_physical(containing_block_writing_mode),
margin.to_physical(containing_block_writing_mode), margin.to_physical(containing_block_writing_mode),
@ -1303,7 +1306,7 @@ fn layout_in_flow_replaced_block_level(
let effective_margin_inline_start; let effective_margin_inline_start;
let (margin_block_start, margin_block_end) = solve_block_margins_for_in_flow_block_level(&pbm); let (margin_block_start, margin_block_end) = solve_block_margins_for_in_flow_block_level(&pbm);
let containing_block_writing_mode = containing_block.effective_writing_mode(); let containing_block_writing_mode = containing_block.style.writing_mode;
let physical_content_size = content_size.to_physical_size(containing_block_writing_mode); let physical_content_size = content_size.to_physical_size(containing_block_writing_mode);
let fragments = replaced.make_fragments(style, physical_content_size); let fragments = replaced.make_fragments(style, physical_content_size);
@ -1371,11 +1374,12 @@ fn layout_in_flow_replaced_block_level(
clearance.unwrap_or_else(Au::zero), clearance.unwrap_or_else(Au::zero),
inline: pbm.padding.inline_start + pbm.border.inline_start + effective_margin_inline_start, inline: pbm.padding.inline_start + pbm.border.inline_start + effective_margin_inline_start,
}; };
let content_rect = LogicalRect {
start_corner,
size: content_size,
}
.to_physical(Some(containing_block));
let content_rect = PhysicalRect::new(
start_corner.to_physical_point(containing_block_writing_mode),
physical_content_size,
);
let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
BoxFragment::new( BoxFragment::new(
@ -1455,9 +1459,12 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
}; };
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
assert_eq!( assert_eq!(
containing_block.effective_writing_mode(), containing_block.style.writing_mode.is_horizontal(),
containing_block_for_children.effective_writing_mode(), containing_block_for_children
"Mixed writing modes are not supported yet" .style
.writing_mode
.is_horizontal(),
"Vertical writing modes are not supported yet"
); );
ContainingBlockPaddingAndBorder { ContainingBlockPaddingAndBorder {
containing_block: containing_block_for_children, containing_block: containing_block_for_children,
@ -1510,16 +1517,8 @@ fn justify_self_alignment(containing_block: &ContainingBlock, free_space: Au) ->
debug_assert!(free_space >= Au::zero()); debug_assert!(free_space >= Au::zero());
match style.clone_text_align() { match style.clone_text_align() {
TextAlignKeyword::MozCenter => free_space / 2, TextAlignKeyword::MozCenter => free_space / 2,
TextAlignKeyword::MozLeft TextAlignKeyword::MozLeft if !style.writing_mode.line_left_is_inline_start() => free_space,
if !style.effective_writing_mode().line_left_is_inline_start() => TextAlignKeyword::MozRight if style.writing_mode.line_left_is_inline_start() => free_space,
{
free_space
},
TextAlignKeyword::MozRight
if style.effective_writing_mode().line_left_is_inline_start() =>
{
free_space
},
_ => Au::zero(), _ => Au::zero(),
} }
} }
@ -1643,7 +1642,7 @@ fn solve_clearance_and_inline_margins_avoiding_floats(
/// ///
/// In parallel mode, this placement is done after all child blocks are laid out. In /// In parallel mode, this placement is done after all child blocks are laid out. In
/// sequential mode, this is done right after each block is laid out. /// sequential mode, this is done right after each block is laid out.
struct PlacementState { struct PlacementState<'container> {
next_in_flow_margin_collapses_with_parent_start_margin: bool, next_in_flow_margin_collapses_with_parent_start_margin: bool,
last_in_flow_margin_collapses_with_parent_end_margin: bool, last_in_flow_margin_collapses_with_parent_end_margin: bool,
start_margin: CollapsedMargin, start_margin: CollapsedMargin,
@ -1658,17 +1657,18 @@ struct PlacementState {
/// position of the placement. /// position of the placement.
marker_block_size: Option<Au>, marker_block_size: Option<Au>,
/// The [`WritingMode`] of the containing fragment where these fragments are being laid out. /// The [`ContainingBlock`] of the container into which this [`PlacementState`] is laying out
writing_mode: WritingMode, /// fragments. This is used to convert between physical and logical geometry.
containing_block: &'container ContainingBlock<'container>,
} }
impl PlacementState { impl<'container> PlacementState<'container> {
fn new( fn new(
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
containing_block_style: &ComputedValues, containing_block: &'container ContainingBlock<'container>,
) -> PlacementState { ) -> PlacementState {
let is_inline_block_context = let is_inline_block_context =
containing_block_style.get_box().clone_display() == Display::InlineBlock; containing_block.style.get_box().clone_display() == Display::InlineBlock;
PlacementState { PlacementState {
next_in_flow_margin_collapses_with_parent_start_margin: next_in_flow_margin_collapses_with_parent_start_margin:
collapsible_with_parent_start_margin.0, collapsible_with_parent_start_margin.0,
@ -1679,7 +1679,7 @@ impl PlacementState {
inflow_baselines: Baselines::default(), inflow_baselines: Baselines::default(),
is_inline_block_context, is_inline_block_context,
marker_block_size: None, marker_block_size: None,
writing_mode: containing_block_style.effective_writing_mode(), containing_block,
} }
} }
@ -1708,9 +1708,10 @@ impl PlacementState {
let box_block_offset = box_fragment let box_block_offset = box_fragment
.content_rect .content_rect
.origin .origin
.to_logical(self.writing_mode) .to_logical(self.containing_block)
.block; .block;
let box_fragment_baselines = box_fragment.baselines(self.writing_mode); let box_fragment_baselines =
box_fragment.baselines(self.containing_block.style.writing_mode);
if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment_baselines.first) { if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment_baselines.first) {
self.inflow_baselines.first = Some(first + box_block_offset); self.inflow_baselines.first = Some(first + box_block_offset);
} }
@ -1746,7 +1747,7 @@ impl PlacementState {
fragment fragment
.content_rect .content_rect
.size .size
.to_logical(self.writing_mode) .to_logical(self.containing_block.style.writing_mode)
.block, .block,
); );
return; return;
@ -1755,8 +1756,8 @@ impl PlacementState {
let fragment_block_margins = &fragment.block_margins_collapsed_with_children; let fragment_block_margins = &fragment.block_margins_collapsed_with_children;
let mut fragment_block_size = fragment let mut fragment_block_size = fragment
.border_rect() .border_rect()
.to_logical(self.writing_mode)
.size .size
.to_logical(self.containing_block.style.writing_mode)
.block; .block;
// We use `last_in_flow_margin_collapses_with_parent_end_margin` to implement // We use `last_in_flow_margin_collapses_with_parent_end_margin` to implement
@ -1798,7 +1799,7 @@ impl PlacementState {
inline: Au::zero(), inline: Au::zero(),
block: self.current_margin.solve() + self.current_block_direction_position, block: self.current_margin.solve() + self.current_block_direction_position,
} }
.to_physical_size(self.writing_mode); .to_physical_size(self.containing_block.style.writing_mode);
if fragment_block_margins.collapsed_through { if fragment_block_margins.collapsed_through {
// `fragment_block_size` is typically zero when collapsing through, // `fragment_block_size` is typically zero when collapsing through,
@ -1823,7 +1824,7 @@ impl PlacementState {
}, },
size: LogicalVec2::zero(), size: LogicalVec2::zero(),
} }
.to_physical(self.writing_mode); .to_physical(Some(self.containing_block));
}, },
Fragment::Float(box_fragment) => { Fragment::Float(box_fragment) => {
let sequential_layout_state = sequential_layout_state let sequential_layout_state = sequential_layout_state
@ -1832,7 +1833,7 @@ impl PlacementState {
self.current_block_direction_position + self.current_margin.solve(); self.current_block_direction_position + self.current_margin.solve();
sequential_layout_state.place_float_fragment( sequential_layout_state.place_float_fragment(
box_fragment, box_fragment,
self.writing_mode, self.containing_block,
self.start_margin, self.start_margin,
block_offset_from_containing_block_top, block_offset_from_containing_block_top,
); );

View file

@ -16,9 +16,7 @@ use style::Zero;
use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment}; use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment};
use crate::cell::ArcRefCell; use crate::cell::ArcRefCell;
use crate::formatting_contexts::Baselines; use crate::formatting_contexts::Baselines;
use crate::geom::{ use crate::geom::{AuOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, ToLogical};
AuOrAuto, LogicalRect, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, ToLogical,
};
use crate::style_ext::ComputedValuesExt; use crate::style_ext::ComputedValuesExt;
/// Describes how a [`BoxFragment`] paints its background. /// Describes how a [`BoxFragment`] paints its background.
@ -36,7 +34,7 @@ pub(crate) enum BackgroundMode {
pub(crate) struct ExtraBackground { pub(crate) struct ExtraBackground {
pub style: ServoArc<ComputedValues>, pub style: ServoArc<ComputedValues>,
pub rect: LogicalRect<Au>, pub rect: PhysicalRect<Au>,
} }
#[derive(Serialize)] #[derive(Serialize)]
@ -165,16 +163,15 @@ impl BoxFragment {
/// Get the baselines for this [`BoxFragment`] if they are compatible with the given [`WritingMode`]. /// Get the baselines for this [`BoxFragment`] if they are compatible with the given [`WritingMode`].
/// If they are not compatible, [`Baselines::default()`] is returned. /// If they are not compatible, [`Baselines::default()`] is returned.
pub fn baselines(&self, writing_mode: WritingMode) -> Baselines { pub fn baselines(&self, writing_mode: WritingMode) -> Baselines {
let mut baselines = if writing_mode.is_horizontal() == let mut baselines =
self.style.effective_writing_mode().is_horizontal() if writing_mode.is_horizontal() == self.style.writing_mode.is_horizontal() {
{ self.baselines
self.baselines } else {
} else { // If the writing mode of the container requesting baselines is not
// If the writing mode of the container requesting baselines is not // compatible, ensure that the baselines established by this fragment are
// compatible, ensure that the baselines established by this fragment are // not used.
// not used. Baselines::default()
Baselines::default() };
};
// From the https://drafts.csswg.org/css-align-3/#baseline-export section on "block containers": // From the https://drafts.csswg.org/css-align-3/#baseline-export section on "block containers":
// > However, for legacy reasons if its baseline-source is auto (the initial // > However, for legacy reasons if its baseline-source is auto (the initial
@ -185,12 +182,12 @@ impl BoxFragment {
// This applies even if there is no baseline set, so we unconditionally set the value here // This applies even if there is no baseline set, so we unconditionally set the value here
// and ignore anything that is set via [`Self::with_baselines`]. // and ignore anything that is set via [`Self::with_baselines`].
if self.style.establishes_scroll_container() { if self.style.establishes_scroll_container() {
let content_rect = self.content_rect.to_logical(writing_mode); let content_rect_size = self.content_rect.size.to_logical(writing_mode);
let padding = self.padding.to_logical(writing_mode); let padding = self.padding.to_logical(writing_mode);
let border = self.border.to_logical(writing_mode); let border = self.border.to_logical(writing_mode);
let margin = self.margin.to_logical(writing_mode); let margin = self.margin.to_logical(writing_mode);
baselines.last = Some( baselines.last = Some(
content_rect.size.block + padding.block_end + border.block_end + margin.block_end, content_rect_size.block + padding.block_end + border.block_end + margin.block_end,
) )
} }
baselines baselines
@ -229,6 +226,10 @@ impl BoxFragment {
self.border_rect().outer_rect(self.margin) self.border_rect().outer_rect(self.margin)
} }
pub(crate) fn padding_border_margin(&self) -> PhysicalSides<Au> {
self.margin + self.border + self.padding
}
pub fn print(&self, tree: &mut PrintTree) { pub fn print(&self, tree: &mut PrintTree) {
tree.new_level(format!( tree.new_level(format!(
"Box\ "Box\

View file

@ -109,6 +109,17 @@ impl Fragment {
Fragment::Float(fragment) => &fragment.base, Fragment::Float(fragment) => &fragment.base,
}) })
} }
pub(crate) fn content_rect_mut(&mut self) -> Option<&mut PhysicalRect<Au>> {
match self {
Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
Some(&mut box_fragment.content_rect)
},
Fragment::Positioning(_) | Fragment::AbsoluteOrFixedPositioned(_) => None,
Fragment::Text(text_fragment) => Some(&mut text_fragment.rect),
Fragment::Image(image_fragment) => Some(&mut image_fragment.rect),
Fragment::IFrame(iframe_fragment) => Some(&mut iframe_fragment.rect),
}
}
pub fn tag(&self) -> Option<Tag> { pub fn tag(&self) -> Option<Tag> {
self.base().and_then(|base| base.tag) self.base().and_then(|base| base.tag)

View file

@ -4,6 +4,7 @@
use app_units::Au; use app_units::Au;
use serde::Serialize; use serde::Serialize;
use style::logical_geometry::WritingMode;
use style::values::specified::align::AlignFlags; use style::values::specified::align::AlignFlags;
use super::Fragment; use super::Fragment;
@ -26,17 +27,24 @@ pub(crate) struct HoistedSharedFragment {
/// These values are dependent on the layout mode (currently only interesting for /// These values are dependent on the layout mode (currently only interesting for
/// flexbox). /// flexbox).
pub resolved_alignment: LogicalVec2<AlignFlags>, pub resolved_alignment: LogicalVec2<AlignFlags>,
/// This is the [`WritingMode`] of the original parent of the element that created this
/// hoisted absolutely-positioned fragment. This helps to interpret the offset for
/// static positioning. If the writing mode is right-to-left or bottom-to-top, the static
/// offset needs to be adjusted by the absolutely positioned element's inline size.
pub original_parent_writing_mode: WritingMode,
} }
impl HoistedSharedFragment { impl HoistedSharedFragment {
pub(crate) fn new( pub(crate) fn new(
static_position_rect: PhysicalRect<Au>, static_position_rect: PhysicalRect<Au>,
resolved_alignment: LogicalVec2<AlignFlags>, resolved_alignment: LogicalVec2<AlignFlags>,
original_parent_writing_mode: WritingMode,
) -> Self { ) -> Self {
HoistedSharedFragment { HoistedSharedFragment {
fragment: None, fragment: None,
static_position_rect, static_position_rect,
resolved_alignment, resolved_alignment,
original_parent_writing_mode,
} }
} }
} }

View file

@ -70,19 +70,6 @@ impl<T: Clone> LogicalVec2<T> {
} }
} }
pub fn from_physical_point(physical_point: &PhysicalPoint<T>, mode: WritingMode) -> Self {
// https://drafts.csswg.org/css-writing-modes/#logical-to-physical
let (i, b) = if mode.is_horizontal() {
(&physical_point.x, &physical_point.y)
} else {
(&physical_point.y, &physical_point.x)
};
LogicalVec2 {
inline: i.clone(),
block: b.clone(),
}
}
pub fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalVec2<U> { pub fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalVec2<U> {
LogicalVec2 { LogicalVec2 {
inline: f(&self.inline), inline: f(&self.inline),
@ -271,15 +258,48 @@ impl<T: Clone> LogicalVec2<T> {
}; };
PhysicalSize::new(x.clone(), y.clone()) PhysicalSize::new(x.clone(), y.clone())
} }
}
pub fn to_physical_point(&self, mode: WritingMode) -> PhysicalPoint<T> { impl<T: Copy + Neg<Output = T>> LogicalVec2<T> {
// https://drafts.csswg.org/css-writing-modes/#logical-to-physical pub fn to_physical_vector(&self, mode: WritingMode) -> PhysicalVec<T> {
let (x, y) = if mode.is_horizontal() { if mode.is_horizontal() {
(&self.inline, &self.block) if mode.is_bidi_ltr() {
PhysicalVec::new(self.inline, self.block)
} else {
PhysicalVec::new(-self.inline, self.block)
}
} else { } else {
(&self.block, &self.inline) if mode.is_inline_tb() {
}; PhysicalVec::new(self.block, self.inline)
PhysicalPoint::new(x.clone(), y.clone()) } else {
PhysicalVec::new(-self.block, self.inline)
}
}
}
}
impl LogicalVec2<Au> {
#[inline]
pub fn to_physical_point(
&self,
containing_block: Option<&ContainingBlock>,
) -> PhysicalPoint<Au> {
let mode = containing_block.map_or_else(WritingMode::horizontal_tb, |containing_block| {
containing_block.style.writing_mode
});
if mode.is_vertical() {
// TODO: Bottom-to-top writing modes are not supported yet.
PhysicalPoint::new(self.block, self.inline)
} else {
let y = self.block;
let x = match containing_block {
Some(containing_block) if !mode.is_bidi_ltr() => {
containing_block.inline_size - self.inline
},
_ => self.inline,
};
PhysicalPoint::new(x, y)
}
} }
} }
@ -540,15 +560,36 @@ impl<T> LogicalRect<T> {
}, },
} }
} }
}
pub fn to_physical(&self, mode: WritingMode) -> PhysicalRect<T> impl LogicalRect<Au> {
where pub fn to_physical<'a>(
T: Copy, &self,
{ containing_block: Option<&ContainingBlock<'a>>,
PhysicalRect::new( ) -> PhysicalRect<Au> {
self.start_corner.to_physical_point(mode), let mode = containing_block.map_or_else(WritingMode::horizontal_tb, |containing_block| {
self.size.to_physical_size(mode), containing_block.style.writing_mode
) });
let (x, y, width, height) = if mode.is_vertical() {
// TODO: Bottom-to-top writing modes are not supported.
(
self.start_corner.block,
self.start_corner.inline,
self.size.block,
self.size.inline,
)
} else {
let y = self.start_corner.block;
let x = match containing_block {
Some(containing_block) if !mode.is_bidi_ltr() => {
containing_block.inline_size - self.max_inline_position()
},
_ => self.start_corner.inline,
};
(x, y, self.size.inline, self.size.block)
};
PhysicalRect::new(PhysicalPoint::new(x, y), PhysicalSize::new(width, height))
} }
} }
@ -592,32 +633,74 @@ pub(crate) trait ToLogical<Unit, LogicalType> {
fn to_logical(&self, writing_mode: WritingMode) -> LogicalType; fn to_logical(&self, writing_mode: WritingMode) -> LogicalType;
} }
impl<Unit: Copy> ToLogical<Unit, LogicalRect<Unit>> for PhysicalRect<Unit> {
fn to_logical(&self, writing_mode: WritingMode) -> LogicalRect<Unit> {
LogicalRect {
start_corner: LogicalVec2::from_physical_size(
&PhysicalSize::new(self.origin.x, self.origin.y),
writing_mode,
),
size: LogicalVec2::from_physical_size(&self.size, writing_mode),
}
}
}
impl<Unit: Copy> ToLogical<Unit, LogicalVec2<Unit>> for PhysicalSize<Unit> { impl<Unit: Copy> ToLogical<Unit, LogicalVec2<Unit>> for PhysicalSize<Unit> {
fn to_logical(&self, writing_mode: WritingMode) -> LogicalVec2<Unit> { fn to_logical(&self, writing_mode: WritingMode) -> LogicalVec2<Unit> {
LogicalVec2::from_physical_size(self, writing_mode) LogicalVec2::from_physical_size(self, writing_mode)
} }
} }
impl<Unit: Copy> ToLogical<Unit, LogicalVec2<Unit>> for PhysicalPoint<Unit> {
fn to_logical(&self, writing_mode: WritingMode) -> LogicalVec2<Unit> {
LogicalVec2::from_physical_point(self, writing_mode)
}
}
impl<Unit: Copy> ToLogical<Unit, LogicalSides<Unit>> for PhysicalSides<Unit> { impl<Unit: Copy> ToLogical<Unit, LogicalSides<Unit>> for PhysicalSides<Unit> {
fn to_logical(&self, writing_mode: WritingMode) -> LogicalSides<Unit> { fn to_logical(&self, writing_mode: WritingMode) -> LogicalSides<Unit> {
LogicalSides::from_physical(self, writing_mode) LogicalSides::from_physical(self, writing_mode)
} }
} }
pub(crate) trait ToLogicalWithContainingBlock<LogicalType> {
fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalType;
}
impl ToLogicalWithContainingBlock<LogicalVec2<Au>> for PhysicalPoint<Au> {
fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalVec2<Au> {
let writing_mode = containing_block.style.writing_mode;
// TODO: Bottom-to-top and right-to-left vertical writing modes are not supported yet.
if writing_mode.is_vertical() {
LogicalVec2 {
inline: self.y,
block: self.x,
}
} else {
LogicalVec2 {
inline: if writing_mode.is_bidi_ltr() {
self.x
} else {
containing_block.inline_size - self.x
},
block: self.y,
}
}
}
}
impl ToLogicalWithContainingBlock<LogicalRect<Au>> for PhysicalRect<Au> {
fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalRect<Au> {
let inline_start;
let block_start;
let inline;
let block;
let writing_mode = containing_block.style.writing_mode;
if writing_mode.is_vertical() {
// TODO: Bottom-to-top and right-to-left vertical writing modes are not supported yet.
inline = self.size.height;
block = self.size.width;
block_start = self.origin.x;
inline_start = self.origin.y;
} else {
inline = self.size.width;
block = self.size.height;
block_start = self.origin.y;
if writing_mode.is_bidi_ltr() {
inline_start = self.origin.x;
} else {
inline_start = containing_block.inline_size - (self.origin.x + self.size.width);
}
}
LogicalRect {
start_corner: LogicalVec2 {
inline: inline_start,
block: block_start,
},
size: LogicalVec2 { inline, block },
}
}
}

View file

@ -29,7 +29,6 @@ use app_units::Au;
pub use flow::BoxTree; pub use flow::BoxTree;
pub use fragment_tree::FragmentTree; pub use fragment_tree::FragmentTree;
use geom::AuOrAuto; use geom::AuOrAuto;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style_ext::{Clamp, ComputedValuesExt}; use style_ext::{Clamp, ComputedValuesExt};
@ -107,12 +106,6 @@ pub struct ContainingBlock<'a> {
style: &'a ComputedValues, style: &'a ComputedValues,
} }
impl<'a> ContainingBlock<'a> {
pub(crate) fn effective_writing_mode(&self) -> WritingMode {
self.style.effective_writing_mode()
}
}
impl<'a> TryFrom<&'_ IndefiniteContainingBlock<'a>> for ContainingBlock<'a> { impl<'a> TryFrom<&'_ IndefiniteContainingBlock<'a>> for ContainingBlock<'a> {
type Error = &'static str; type Error = &'static str;
@ -135,12 +128,6 @@ struct DefiniteContainingBlock<'a> {
style: &'a ComputedValues, style: &'a ComputedValues,
} }
impl<'a> DefiniteContainingBlock<'a> {
pub(crate) fn effective_writing_mode(&self) -> WritingMode {
self.style.effective_writing_mode()
}
}
impl<'a> From<&'_ DefiniteContainingBlock<'a>> for ContainingBlock<'a> { impl<'a> From<&'_ DefiniteContainingBlock<'a>> for ContainingBlock<'a> {
fn from(definite: &DefiniteContainingBlock<'a>) -> Self { fn from(definite: &DefiniteContainingBlock<'a>) -> Self {
ContainingBlock { ContainingBlock {

View file

@ -7,6 +7,7 @@ use rayon::iter::IntoParallelRefMutIterator;
use rayon::prelude::{IndexedParallelIterator, ParallelIterator}; use rayon::prelude::{IndexedParallelIterator, ParallelIterator};
use serde::Serialize; use serde::Serialize;
use style::computed_values::position::T as Position; use style::computed_values::position::T as Position;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::specified::align::{AlignFlags, AxisDirection}; use style::values::specified::align::{AlignFlags, AxisDirection};
use style::values::specified::text::TextDecorationLine; use style::values::specified::text::TextDecorationLine;
@ -22,7 +23,7 @@ use crate::fragment_tree::{
}; };
use crate::geom::{ use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint, AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint,
PhysicalRect, PhysicalVec, ToLogical, PhysicalRect, PhysicalVec, ToLogical, ToLogicalWithContainingBlock,
}; };
use crate::style_ext::{ComputedValuesExt, DisplayInside}; use crate::style_ext::{ComputedValuesExt, DisplayInside};
use crate::{ContainingBlock, DefiniteContainingBlock, IndefiniteContainingBlock}; use crate::{ContainingBlock, DefiniteContainingBlock, IndefiniteContainingBlock};
@ -73,11 +74,13 @@ impl AbsolutelyPositionedBox {
absolutely_positioned_box: ArcRefCell<Self>, absolutely_positioned_box: ArcRefCell<Self>,
static_position_rectangle: PhysicalRect<Au>, static_position_rectangle: PhysicalRect<Au>,
resolved_alignment: LogicalVec2<AlignFlags>, resolved_alignment: LogicalVec2<AlignFlags>,
original_parent_writing_mode: WritingMode,
) -> HoistedAbsolutelyPositionedBox { ) -> HoistedAbsolutelyPositionedBox {
HoistedAbsolutelyPositionedBox { HoistedAbsolutelyPositionedBox {
fragment: ArcRefCell::new(HoistedSharedFragment::new( fragment: ArcRefCell::new(HoistedSharedFragment::new(
static_position_rectangle, static_position_rectangle,
resolved_alignment, resolved_alignment,
original_parent_writing_mode,
)), )),
absolutely_positioned_box, absolutely_positioned_box,
} }
@ -211,7 +214,7 @@ impl PositioningContext {
if style.clone_position() == Position::Relative { if style.clone_position() == Position::Relative {
new_fragment.content_rect.origin += relative_adjustement(style, containing_block) new_fragment.content_rect.origin += relative_adjustement(style, containing_block)
.to_physical_size(containing_block.effective_writing_mode()) .to_physical_vector(containing_block.style.writing_mode)
} }
new_fragment new_fragment
@ -233,7 +236,7 @@ impl PositioningContext {
let containing_block = DefiniteContainingBlock { let containing_block = DefiniteContainingBlock {
size: padding_rect size: padding_rect
.size .size
.to_logical(new_fragment.style.effective_writing_mode()), .to_logical(new_fragment.style.writing_mode),
style: &new_fragment.style, style: &new_fragment.style,
}; };
@ -376,7 +379,7 @@ impl PositioningContext {
} }
/// A data structure which stores the size of a positioning context. /// A data structure which stores the size of a positioning context.
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct PositioningContextLength { pub(crate) struct PositioningContextLength {
/// The number of boxes that will be hoisted the the nearest positioned ancestor for /// The number of boxes that will be hoisted the the nearest positioned ancestor for
/// layout. /// layout.
@ -452,9 +455,10 @@ impl HoistedAbsolutelyPositionedBox {
let cbis = containing_block.size.inline; let cbis = containing_block.size.inline;
let cbbs = containing_block.size.block; let cbbs = containing_block.size.block;
let mut absolutely_positioned_box = self.absolutely_positioned_box.borrow_mut(); let mut absolutely_positioned_box = self.absolutely_positioned_box.borrow_mut();
let containing_block_writing_mode = containing_block.style.effective_writing_mode(); let containing_block_writing_mode = containing_block.style.writing_mode;
let style = absolutely_positioned_box.context.style().clone(); let style = absolutely_positioned_box.context.style().clone();
let pbm = style.padding_border_margin(&containing_block.into()); let pbm = style.padding_border_margin(&containing_block.into());
let indefinite_containing_block = containing_block.into();
let computed_size = match &absolutely_positioned_box.context { let computed_size = match &absolutely_positioned_box.context {
IndependentFormattingContext::Replaced(replaced) => { IndependentFormattingContext::Replaced(replaced) => {
@ -470,17 +474,16 @@ impl HoistedAbsolutelyPositionedBox {
block: AuOrAuto::LengthPercentage(used_size.block), block: AuOrAuto::LengthPercentage(used_size.block),
} }
}, },
IndependentFormattingContext::NonReplaced(..) => { IndependentFormattingContext::NonReplaced(non_replaced) => non_replaced
style.content_box_size(&containing_block.into(), &pbm) .style
}, .content_box_size(&indefinite_containing_block, &pbm),
}; };
let shared_fragment = self.fragment.borrow(); let shared_fragment = self.fragment.borrow();
let static_position_rect = shared_fragment let static_position_rect = shared_fragment
.static_position_rect .static_position_rect
.to_logical(containing_block_writing_mode); .to_logical(&indefinite_containing_block);
let indefinite_containing_block = containing_block.into();
let box_offset = style.box_offsets(&indefinite_containing_block); let box_offset = style.box_offsets(&indefinite_containing_block);
// When the "static-position rect" doesn't come into play, we do not do any alignment // When the "static-position rect" doesn't come into play, we do not do any alignment
@ -504,6 +507,8 @@ impl HoistedAbsolutelyPositionedBox {
box_offsets: inline_box_offsets, box_offsets: inline_box_offsets,
static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Inline), static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Inline),
alignment: inline_alignment, alignment: inline_alignment,
flip_anchor: !(shared_fragment.original_parent_writing_mode.is_bidi_ltr() ==
indefinite_containing_block.style.writing_mode.is_bidi_ltr()),
}; };
// When the "static-position rect" doesn't come into play, we re-resolve "align-self" // When the "static-position rect" doesn't come into play, we re-resolve "align-self"
@ -526,6 +531,7 @@ impl HoistedAbsolutelyPositionedBox {
box_offsets: block_box_offsets, box_offsets: block_box_offsets,
static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Block), static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Block),
alignment: block_alignment, alignment: block_alignment,
flip_anchor: false,
}; };
let overconstrained = LogicalVec2 { let overconstrained = LogicalVec2 {
inline: inline_axis_solver.is_overconstrained_for_size(computed_size.inline), inline: inline_axis_solver.is_overconstrained_for_size(computed_size.inline),
@ -535,8 +541,7 @@ impl HoistedAbsolutelyPositionedBox {
let mut inline_axis = inline_axis_solver.solve_for_size(computed_size.inline); let mut inline_axis = inline_axis_solver.solve_for_size(computed_size.inline);
let mut block_axis = block_axis_solver.solve_for_size(computed_size.block); let mut block_axis = block_axis_solver.solve_for_size(computed_size.block);
let mut positioning_context = let mut positioning_context = PositioningContext::new_for_style(&style).unwrap();
PositioningContext::new_for_style(absolutely_positioned_box.context.style()).unwrap();
let mut new_fragment = { let mut new_fragment = {
let content_size: LogicalVec2<Au>; let content_size: LogicalVec2<Au>;
let fragments; let fragments;
@ -544,10 +549,9 @@ impl HoistedAbsolutelyPositionedBox {
IndependentFormattingContext::Replaced(replaced) => { IndependentFormattingContext::Replaced(replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
let style = &replaced.style;
content_size = computed_size.auto_is(|| unreachable!()); content_size = computed_size.auto_is(|| unreachable!());
fragments = replaced.contents.make_fragments( fragments = replaced.contents.make_fragments(
style, &style,
content_size.to_physical_size(containing_block_writing_mode), content_size.to_physical_size(containing_block_writing_mode),
); );
}, },
@ -558,8 +562,7 @@ impl HoistedAbsolutelyPositionedBox {
.style .style
.content_min_box_size(&indefinite_containing_block, &pbm) .content_min_box_size(&indefinite_containing_block, &pbm)
.map(|t| t.map(Au::from).auto_is(Au::zero)); .map(|t| t.map(Au::from).auto_is(Au::zero));
let max_size = non_replaced let max_size = style
.style
.content_max_box_size(&containing_block.into(), &pbm) .content_max_box_size(&containing_block.into(), &pbm)
.map(|t| t.map(Au::from)); .map(|t| t.map(Au::from));
@ -622,13 +625,16 @@ impl HoistedAbsolutelyPositionedBox {
let containing_block_for_children = ContainingBlock { let containing_block_for_children = ContainingBlock {
inline_size, inline_size,
block_size: size, block_size: size,
style: &non_replaced.style, style: &style,
}; };
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
assert_eq!( assert_eq!(
containing_block.effective_writing_mode(), containing_block.style.writing_mode.is_horizontal(),
containing_block_for_children.effective_writing_mode(), containing_block_for_children
"Mixed writing modes are not supported yet" .style
.writing_mode
.is_horizontal(),
"Mixed horizontal and vertical writing modes are not supported yet"
); );
// Clear the context since we will lay out the same descendants // Clear the context since we will lay out the same descendants
@ -732,13 +738,13 @@ impl HoistedAbsolutelyPositionedBox {
inline_axis_solver.solve_alignment(margin_box_rect, &mut content_rect); inline_axis_solver.solve_alignment(margin_box_rect, &mut content_rect);
let physical_overconstrained = let physical_overconstrained =
overconstrained.to_physical_size(containing_block.effective_writing_mode()); overconstrained.to_physical_size(containing_block.style.writing_mode);
BoxFragment::new_with_overconstrained( BoxFragment::new_with_overconstrained(
absolutely_positioned_box.context.base_fragment_info(), absolutely_positioned_box.context.base_fragment_info(),
absolutely_positioned_box.context.style().clone(), style,
fragments, fragments,
content_rect.to_physical(containing_block_writing_mode), content_rect.to_physical(Some(&containing_block.into())),
pbm.padding.to_physical(containing_block_writing_mode), pbm.padding.to_physical(containing_block_writing_mode),
pbm.border.to_physical(containing_block_writing_mode), pbm.border.to_physical(containing_block_writing_mode),
margin.to_physical(containing_block_writing_mode), margin.to_physical(containing_block_writing_mode),
@ -826,8 +832,8 @@ struct AbsoluteAxisSolver<'a> {
box_offsets: AbsoluteBoxOffsets<'a>, box_offsets: AbsoluteBoxOffsets<'a>,
static_position_rect_axis: RectAxis, static_position_rect_axis: RectAxis,
alignment: AlignFlags, alignment: AlignFlags,
flip_anchor: bool,
} }
impl<'a> AbsoluteAxisSolver<'a> { impl<'a> AbsoluteAxisSolver<'a> {
/// This unifies some of the parts in common in: /// This unifies some of the parts in common in:
/// ///
@ -846,7 +852,11 @@ impl<'a> AbsoluteAxisSolver<'a> {
self.box_offsets.end.non_auto(), self.box_offsets.end.non_auto(),
) { ) {
(None, None) => AxisResult { (None, None) => AxisResult {
anchor: Anchor::Start(self.static_position_rect_axis.origin), anchor: if self.flip_anchor {
Anchor::End(self.containing_size - self.static_position_rect_axis.origin)
} else {
Anchor::Start(self.static_position_rect_axis.origin)
},
size: computed_size, size: computed_size,
margin_start: self.computed_margin_start.auto_is(Au::zero), margin_start: self.computed_margin_start.auto_is(Au::zero),
margin_end: self.computed_margin_end.auto_is(Au::zero), margin_end: self.computed_margin_end.auto_is(Au::zero),

View file

@ -32,7 +32,6 @@ use style::values::generics::font::LineHeight;
use style_traits::{ParsingMode, ToCss}; use style_traits::{ParsingMode, ToCss};
use crate::fragment_tree::{BoxFragment, Fragment, FragmentFlags, FragmentTree, Tag}; use crate::fragment_tree::{BoxFragment, Fragment, FragmentFlags, FragmentTree, Tag};
use crate::style_ext::ComputedValuesExt;
pub fn process_content_box_request( pub fn process_content_box_request(
requested_node: OpaqueNode, requested_node: OpaqueNode,
@ -136,7 +135,7 @@ pub fn process_resolved_style_request<'dom>(
return style.computed_value_to_string(PropertyDeclarationId::Custom(name)); return style.computed_value_to_string(PropertyDeclarationId::Custom(name));
}, },
} }
.to_physical(style.effective_writing_mode()); .to_physical(style.writing_mode);
let computed_style = let computed_style =
|| style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id)); || style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id));

View file

@ -235,7 +235,7 @@ impl ReplacedContent {
fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> LogicalVec2<Option<Au>> { fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> LogicalVec2<Option<Au>> {
let intrinsic_size = PhysicalSize::new(self.intrinsic.width, self.intrinsic.height); let intrinsic_size = PhysicalSize::new(self.intrinsic.width, self.intrinsic.height);
LogicalVec2::from_physical_size(&intrinsic_size, style.effective_writing_mode()) LogicalVec2::from_physical_size(&intrinsic_size, style.writing_mode)
} }
fn inline_size_over_block_size_intrinsic_ratio( fn inline_size_over_block_size_intrinsic_ratio(
@ -243,7 +243,7 @@ impl ReplacedContent {
style: &ComputedValues, style: &ComputedValues,
) -> Option<CSSFloat> { ) -> Option<CSSFloat> {
self.intrinsic.ratio.map(|width_over_height| { self.intrinsic.ratio.map(|width_over_height| {
if style.effective_writing_mode().is_vertical() { if style.writing_mode.is_vertical() {
1. / width_over_height 1. / width_over_height
} else { } else {
width_over_height width_over_height
@ -351,7 +351,7 @@ impl ReplacedContent {
style.preferred_aspect_ratio( style.preferred_aspect_ratio(
self.inline_size_over_block_size_intrinsic_ratio(style), self.inline_size_over_block_size_intrinsic_ratio(style),
containing_block.try_into().ok().as_ref(), containing_block.try_into().ok().as_ref(),
containing_block.style.effective_writing_mode(), containing_block.style.writing_mode,
) )
} }
@ -398,7 +398,7 @@ impl ReplacedContent {
min_box_size: LogicalVec2<Au>, min_box_size: LogicalVec2<Au>,
max_box_size: LogicalVec2<Option<Au>>, max_box_size: LogicalVec2<Option<Au>>,
) -> LogicalVec2<Au> { ) -> LogicalVec2<Au> {
let mode = style.effective_writing_mode(); let mode = style.writing_mode;
let intrinsic_size = self.flow_relative_intrinsic_size(style); let intrinsic_size = self.flow_relative_intrinsic_size(style);
let intrinsic_ratio = self.preferred_aspect_ratio(&containing_block.into(), style); let intrinsic_ratio = self.preferred_aspect_ratio(&containing_block.into(), style);

View file

@ -139,13 +139,6 @@ impl PaddingBorderMargin {
padding_border_sums: LogicalVec2::zero(), padding_border_sums: LogicalVec2::zero(),
} }
} }
pub(crate) fn border_padding_start(&self) -> LogicalVec2<Au> {
LogicalVec2 {
inline: self.border.inline_start + self.padding.inline_start,
block: self.border.block_start + self.padding.block_start,
}
}
} }
/// Resolved `aspect-ratio` property with respect to a specific element. Depends /// Resolved `aspect-ratio` property with respect to a specific element. Depends
@ -186,7 +179,6 @@ impl AspectRatio {
} }
pub(crate) trait ComputedValuesExt { pub(crate) trait ComputedValuesExt {
fn effective_writing_mode(&self) -> WritingMode;
fn box_offsets( fn box_offsets(
&self, &self,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
@ -291,10 +283,6 @@ pub(crate) trait ComputedValuesExt {
} }
impl ComputedValuesExt for ComputedValues { impl ComputedValuesExt for ComputedValues {
fn effective_writing_mode(&self) -> WritingMode {
WritingMode::horizontal_tb()
}
fn box_offsets( fn box_offsets(
&self, &self,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
@ -307,7 +295,7 @@ impl ComputedValuesExt for ComputedValues {
position.bottom.as_ref(), position.bottom.as_ref(),
position.left.as_ref(), position.left.as_ref(),
), ),
containing_block.effective_writing_mode(), containing_block.style.writing_mode,
) )
} }
@ -362,7 +350,7 @@ impl ComputedValuesExt for ComputedValues {
pbm: &PaddingBorderMargin, pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto> { ) -> LogicalVec2<AuOrAuto> {
let box_size = self let box_size = self
.box_size(containing_block.effective_writing_mode()) .box_size(containing_block.style.writing_mode)
.percentages_relative_to(containing_block); .percentages_relative_to(containing_block);
self.content_box_size_for_box_size(box_size, pbm) self.content_box_size_for_box_size(box_size, pbm)
} }
@ -393,7 +381,7 @@ impl ComputedValuesExt for ComputedValues {
pbm: &PaddingBorderMargin, pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto> { ) -> LogicalVec2<AuOrAuto> {
let box_size = self let box_size = self
.min_box_size(containing_block.effective_writing_mode()) .min_box_size(containing_block.style.writing_mode)
.percentages_relative_to(containing_block); .percentages_relative_to(containing_block);
self.content_min_box_size_for_min_size(box_size, pbm) self.content_min_box_size_for_min_size(box_size, pbm)
} }
@ -423,7 +411,7 @@ impl ComputedValuesExt for ComputedValues {
pbm: &PaddingBorderMargin, pbm: &PaddingBorderMargin,
) -> LogicalVec2<Option<Au>> { ) -> LogicalVec2<Option<Au>> {
let max_box_size = self let max_box_size = self
.max_box_size(containing_block.effective_writing_mode()) .max_box_size(containing_block.style.writing_mode)
.percentages_relative_to(containing_block); .percentages_relative_to(containing_block);
self.content_max_box_size_for_max_size(max_box_size, pbm) self.content_max_box_size_for_max_size(max_box_size, pbm)
@ -495,23 +483,10 @@ impl ComputedValuesExt for ComputedValues {
} }
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin { fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin {
let cbis = containing_block.inline_size; self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
let padding = self containing_block.style.writing_mode,
.padding(containing_block.effective_writing_mode()) containing_block.inline_size,
.percentages_relative_to(cbis); )
let border = self.border_width(containing_block.effective_writing_mode());
let margin = self
.margin(containing_block.effective_writing_mode())
.percentages_relative_to(cbis);
PaddingBorderMargin {
padding_border_sums: LogicalVec2 {
inline: (padding.inline_sum() + border.inline_sum()),
block: (padding.block_sum() + border.block_sum()),
},
padding,
border,
margin,
}
} }
fn padding_border_margin_for_intrinsic_size( fn padding_border_margin_for_intrinsic_size(

View file

@ -28,8 +28,8 @@ use crate::fragment_tree::{
PositioningFragment, PositioningFragment,
}; };
use crate::geom::{ use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalSides, AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint,
ToLogical, PhysicalRect, PhysicalSides, ToLogical, ToLogicalWithContainingBlock,
}; };
use crate::positioned::{relative_adjustement, PositioningContext, PositioningContextLength}; use crate::positioned::{relative_adjustement, PositioningContext, PositioningContextLength};
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
@ -1135,14 +1135,14 @@ impl<'a> TableLayout<'a> {
let border: LogicalSides<Au> = self let border: LogicalSides<Au> = self
.get_collapsed_borders_for_cell(cell, coordinates) .get_collapsed_borders_for_cell(cell, coordinates)
.unwrap_or_else(|| { .unwrap_or_else(|| {
cell.style.border_width( cell.style
containing_block_for_table.effective_writing_mode(), .border_width(containing_block_for_table.style.writing_mode)
) })
}); .into();
let padding: LogicalSides<Au> = cell let padding: LogicalSides<Au> = cell
.style .style
.padding(containing_block_for_table.effective_writing_mode()) .padding(containing_block_for_table.style.writing_mode)
.percentages_relative_to(self.basis_for_cell_padding_percentage); .percentages_relative_to(self.basis_for_cell_padding_percentage);
let inline_border_padding_sum = border.inline_sum() + padding.inline_sum(); let inline_border_padding_sum = border.inline_sum() + padding.inline_sum();
@ -1541,7 +1541,7 @@ impl<'a> TableLayout<'a> {
style: containing_block.style, style: containing_block.style,
}; };
let mut box_fragment = context.layout_in_flow_block_level( let box_fragment = context.layout_in_flow_block_level(
layout_context, layout_context,
positioning_context positioning_context
.as_mut() .as_mut()
@ -1550,16 +1550,6 @@ impl<'a> TableLayout<'a> {
None, /* sequential_layout_state */ None, /* sequential_layout_state */
); );
let margin_offset = LogicalVec2 {
inline: Au::zero(),
block: box_fragment
.block_margins_collapsed_with_children
.start
.solve(),
}
.to_physical_size(containing_block.effective_writing_mode());
box_fragment.content_rect.origin += margin_offset;
if let Some(positioning_context) = positioning_context.take() { if let Some(positioning_context) = positioning_context.take() {
parent_positioning_context.append(positioning_context); parent_positioning_context.append(positioning_context);
} }
@ -1576,10 +1566,10 @@ impl<'a> TableLayout<'a> {
containing_block_for_children: &ContainingBlock, containing_block_for_children: &ContainingBlock,
containing_block_for_table: &ContainingBlock, containing_block_for_table: &ContainingBlock,
) -> IndependentLayout { ) -> IndependentLayout {
let writing_mode = containing_block_for_children.effective_writing_mode(); let table_writing_mode = containing_block_for_children.style.writing_mode;
let grid_min_max = self.compute_grid_min_max(layout_context, writing_mode); let grid_min_max = self.compute_grid_min_max(layout_context, table_writing_mode);
let caption_minimum_inline_size = let caption_minimum_inline_size =
self.compute_caption_minimum_inline_size(layout_context, writing_mode); self.compute_caption_minimum_inline_size(layout_context, table_writing_mode);
self.compute_table_width( self.compute_table_width(
containing_block_for_children, containing_block_for_children,
containing_block_for_table, containing_block_for_table,
@ -1596,10 +1586,23 @@ impl<'a> TableLayout<'a> {
// //
// TODO: This is a pretty large hack. It would be nicer to actually have the grid sized properly, // TODO: This is a pretty large hack. It would be nicer to actually have the grid sized properly,
// but it works for now. // but it works for now.
//
// Get the padding, border, and margin of the table using the inline size of the table's containing
// block but in the writing of the table itself.
// TODO: This is broken for orthoganol flows, because the inline size of the parent isn't necessarily
// the inline size of the table.
let containing_block_for_logical_conversion = ContainingBlock {
inline_size: self.table_width,
block_size: containing_block_for_table.block_size,
style: containing_block_for_children.style,
};
let table_pbm = self let table_pbm = self
.table .table
.style .style
.padding_border_margin(containing_block_for_table); .padding_border_margin_with_writing_mode_and_containing_block_inline_size(
table_writing_mode,
containing_block_for_table.inline_size,
);
let offset_from_wrapper = -table_pbm.padding - table_pbm.border; let offset_from_wrapper = -table_pbm.padding - table_pbm.border;
let mut current_block_offset = offset_from_wrapper.block_start; let mut current_block_offset = offset_from_wrapper.block_start;
@ -1609,7 +1612,6 @@ impl<'a> TableLayout<'a> {
content_inline_size_for_table: None, content_inline_size_for_table: None,
baselines: Baselines::default(), baselines: Baselines::default(),
}; };
let table_writing_mode = containing_block_for_children.effective_writing_mode();
table_layout table_layout
.fragments .fragments
@ -1627,12 +1629,23 @@ impl<'a> TableLayout<'a> {
positioning_context, positioning_context,
); );
let caption_offset = LogicalVec2 { // The caption is not placed yet. Construct a rectangle for it in the adjusted containing block
inline: offset_from_wrapper.inline_start, // for the table children and only then convert the result to physical geometry.
block: current_block_offset, let caption_pbm = caption_fragment
.padding_border_margin()
.to_logical(table_writing_mode);
caption_fragment.content_rect = LogicalRect {
start_corner: LogicalVec2 {
inline: offset_from_wrapper.inline_start + caption_pbm.inline_start,
block: current_block_offset + caption_pbm.block_start,
},
size: caption_fragment
.content_rect
.size
.to_logical(table_writing_mode),
} }
.to_physical_size(table_writing_mode); .to_physical(Some(&containing_block_for_logical_conversion));
caption_fragment.content_rect.origin += caption_offset;
current_block_offset += caption_fragment current_block_offset += caption_fragment
.margin_rect() .margin_rect()
.size .size
@ -1652,22 +1665,37 @@ impl<'a> TableLayout<'a> {
layout_context, layout_context,
&table_pbm, &table_pbm,
positioning_context, positioning_context,
&containing_block_for_logical_conversion,
containing_block_for_children, containing_block_for_children,
containing_block_for_table, containing_block_for_table,
); );
// Take the baseline of the grid fragment, after adjusting it to be in the coordinate system // Take the baseline of the grid fragment, after adjusting it to be in the coordinate system
// of the table wrapper. // of the table wrapper.
let logical_grid_content_rect = grid_fragment.content_rect.to_logical(table_writing_mode); let logical_grid_content_rect = grid_fragment
table_layout.baselines = grid_fragment .content_rect
.baselines(table_writing_mode) .to_logical(&containing_block_for_logical_conversion);
.offset(current_block_offset + logical_grid_content_rect.start_corner.block); let grid_pbm = grid_fragment
.padding_border_margin()
.to_logical(table_writing_mode);
table_layout.baselines = grid_fragment.baselines(table_writing_mode).offset(
current_block_offset +
logical_grid_content_rect.start_corner.block +
grid_pbm.block_start,
);
grid_fragment.content_rect.origin += LogicalVec2 { grid_fragment.content_rect = LogicalRect {
inline: offset_from_wrapper.inline_start, start_corner: LogicalVec2 {
block: current_block_offset, inline: offset_from_wrapper.inline_start + grid_pbm.inline_start,
block: current_block_offset + grid_pbm.block_start,
},
size: grid_fragment
.content_rect
.size
.to_logical(table_writing_mode),
} }
.to_physical_size(table_writing_mode); .to_physical(Some(&containing_block_for_logical_conversion));
current_block_offset += grid_fragment current_block_offset += grid_fragment
.border_rect() .border_rect()
.size .size
@ -1698,12 +1726,23 @@ impl<'a> TableLayout<'a> {
positioning_context, positioning_context,
); );
let caption_offset = LogicalVec2 { // The caption is not placed yet. Construct a rectangle for it in the adjusted containing block
inline: offset_from_wrapper.inline_start, // for the table children and only then convert the result to physical geometry.
block: current_block_offset, let caption_pbm = caption_fragment
.padding_border_margin()
.to_logical(table_writing_mode);
caption_fragment.content_rect = LogicalRect {
start_corner: LogicalVec2 {
inline: offset_from_wrapper.inline_start + caption_pbm.inline_start,
block: current_block_offset + caption_pbm.block_start,
},
size: caption_fragment
.content_rect
.size
.to_logical(table_writing_mode),
} }
.to_physical_size(containing_block_for_children.effective_writing_mode()); .to_physical(Some(&containing_block_for_logical_conversion));
caption_fragment.content_rect.origin += caption_offset;
current_block_offset += caption_fragment current_block_offset += caption_fragment
.margin_rect() .margin_rect()
.size .size
@ -1729,6 +1768,7 @@ impl<'a> TableLayout<'a> {
layout_context: &LayoutContext, layout_context: &LayoutContext,
table_pbm: &PaddingBorderMargin, table_pbm: &PaddingBorderMargin,
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
containing_block_for_logical_conversion: &ContainingBlock,
containing_block_for_children: &ContainingBlock, containing_block_for_children: &ContainingBlock,
containing_block_for_table: &ContainingBlock, containing_block_for_table: &ContainingBlock,
) -> BoxFragment { ) -> BoxFragment {
@ -1738,8 +1778,8 @@ impl<'a> TableLayout<'a> {
containing_block_for_children, containing_block_for_children,
positioning_context, positioning_context,
); );
let writing_mode = containing_block_for_children.effective_writing_mode(); let table_writing_mode = containing_block_for_children.style.writing_mode;
let first_layout_row_heights = self.do_first_row_layout(writing_mode); let first_layout_row_heights = self.do_first_row_layout(table_writing_mode);
self.compute_table_height_and_final_row_heights( self.compute_table_height_and_final_row_heights(
first_layout_row_heights, first_layout_row_heights,
containing_block_for_children, containing_block_for_children,
@ -1749,16 +1789,15 @@ impl<'a> TableLayout<'a> {
assert_eq!(self.table.size.height, self.row_sizes.len()); assert_eq!(self.table.size.height, self.row_sizes.len());
assert_eq!(self.table.size.width, self.distributed_column_widths.len()); assert_eq!(self.table.size.width, self.distributed_column_widths.len());
let table_writing_mode = containing_block_for_children.effective_writing_mode();
if self.table.size.width == 0 && self.table.size.height == 0 { if self.table.size.width == 0 && self.table.size.height == 0 {
let content_rect = LogicalRect { let content_rect = LogicalRect {
start_corner: table_pbm.border_padding_start(), start_corner: LogicalVec2::zero(),
size: LogicalVec2 { size: LogicalVec2 {
inline: self.table_width, inline: self.table_width,
block: self.final_table_height, block: self.final_table_height,
}, },
} }
.to_physical(table_writing_mode); .to_physical(Some(containing_block_for_logical_conversion));
return BoxFragment::new( return BoxFragment::new(
self.table.grid_base_fragment_info, self.table.grid_base_fragment_info,
self.table.grid_style.clone(), self.table.grid_style.clone(),
@ -1804,8 +1843,12 @@ impl<'a> TableLayout<'a> {
} }
let table_row = &self.table.rows[row_index]; let table_row = &self.table.rows[row_index];
let mut row_fragment_layout = let mut row_fragment_layout = RowFragmentLayout::new(
RowFragmentLayout::new(table_row, row_index, &table_and_track_dimensions); table_row,
row_index,
&table_and_track_dimensions,
&self.table.style,
);
let old_row_group_index = row_group_fragment_layout let old_row_group_index = row_group_fragment_layout
.as_ref() .as_ref()
@ -1816,6 +1859,7 @@ impl<'a> TableLayout<'a> {
table_fragments.push(Fragment::Box(old_row_group_layout.finish( table_fragments.push(Fragment::Box(old_row_group_layout.finish(
layout_context, layout_context,
positioning_context, positioning_context,
containing_block_for_logical_conversion,
containing_block_for_children, containing_block_for_children,
))); )));
} }
@ -1837,31 +1881,21 @@ impl<'a> TableLayout<'a> {
continue; continue;
} }
// The PositioningContext for cells is, in order or preference, the PositioningContext of the row,
// the PositioningContext of the row group, or the PositioningContext of the table.
let row_group_positioning_context = row_group_fragment_layout
.as_mut()
.and_then(|layout| layout.positioning_context.as_mut());
let positioning_context_for_cells = row_fragment_layout
.positioning_context
.as_mut()
.or(row_group_positioning_context)
.unwrap_or(positioning_context);
self.do_final_cell_layout( self.do_final_cell_layout(
row_index, row_index,
column_index, column_index,
&table_and_track_dimensions, &table_and_track_dimensions,
&row_fragment_layout.rect,
positioning_context_for_cells,
&mut baselines, &mut baselines,
&mut row_fragment_layout.fragments, &mut row_fragment_layout,
row_group_fragment_layout.as_mut(),
positioning_context,
); );
} }
let row_fragment = Fragment::Box(row_fragment_layout.finish( let row_fragment = Fragment::Box(row_fragment_layout.finish(
layout_context, layout_context,
positioning_context, positioning_context,
containing_block_for_logical_conversion,
containing_block_for_children, containing_block_for_children,
&mut row_group_fragment_layout, &mut row_group_fragment_layout,
)); ));
@ -1876,18 +1910,19 @@ impl<'a> TableLayout<'a> {
table_fragments.push(Fragment::Box(row_group_layout.finish( table_fragments.push(Fragment::Box(row_group_layout.finish(
layout_context, layout_context,
positioning_context, positioning_context,
containing_block_for_logical_conversion,
containing_block_for_children, containing_block_for_children,
))); )));
} }
let content_rect = LogicalRect { let content_rect = LogicalRect {
start_corner: table_pbm.border_padding_start(), start_corner: LogicalVec2::zero(),
size: LogicalVec2 { size: LogicalVec2 {
inline: table_and_track_dimensions.table_rect.max_inline_position(), inline: table_and_track_dimensions.table_rect.max_inline_position(),
block: table_and_track_dimensions.table_rect.max_block_position(), block: table_and_track_dimensions.table_rect.max_block_position(),
}, },
} }
.to_physical(table_writing_mode); .to_physical(Some(containing_block_for_logical_conversion));
BoxFragment::new( BoxFragment::new(
self.table.grid_base_fragment_info, self.table.grid_base_fragment_info,
self.table.grid_style.clone(), self.table.grid_style.clone(),
@ -1930,17 +1965,26 @@ impl<'a> TableLayout<'a> {
col_group.style.get_inherited_box().visibility == Visibility::Collapse col_group.style.get_inherited_box().visibility == Visibility::Collapse
} }
#[allow(clippy::too_many_arguments)]
fn do_final_cell_layout( fn do_final_cell_layout(
&mut self, &mut self,
row_index: usize, row_index: usize,
column_index: usize, column_index: usize,
dimensions: &TableAndTrackDimensions, dimensions: &TableAndTrackDimensions,
row_rect: &LogicalRect<Au>,
positioning_context: &mut PositioningContext,
baselines: &mut Baselines, baselines: &mut Baselines,
cell_fragments: &mut Vec<Fragment>, row_fragment_layout: &mut RowFragmentLayout,
row_group_fragment_layout: Option<&mut RowGroupFragmentLayout>,
positioning_context_for_table: &mut PositioningContext,
) { ) {
// The PositioningContext for cells is, in order or preference, the PositioningContext of the row,
// the PositioningContext of the row group, or the PositioningContext of the table.
let row_group_positioning_context =
row_group_fragment_layout.and_then(|layout| layout.positioning_context.as_mut());
let positioning_context = row_fragment_layout
.positioning_context
.as_mut()
.or(row_group_positioning_context)
.unwrap_or(positioning_context_for_table);
let layout = match self.cells_laid_out[row_index][column_index].take() { let layout = match self.cells_laid_out[row_index][column_index].take() {
Some(layout) => layout, Some(layout) => layout,
None => { None => {
@ -1955,7 +1999,8 @@ impl<'a> TableLayout<'a> {
}, },
}; };
let row_block_offset = row_rect.start_corner.block; // If this cell has baseline alignment, it can adjust the table's overall baseline.
let row_block_offset = row_fragment_layout.rect.start_corner.block;
let row_baseline = self.row_baselines[row_index]; let row_baseline = self.row_baselines[row_index];
if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline && !layout.is_empty() { if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline && !layout.is_empty() {
let baseline = row_block_offset + row_baseline; let baseline = row_block_offset + row_baseline;
@ -1969,21 +2014,43 @@ impl<'a> TableLayout<'a> {
cell.rowspan, cell.rowspan,
cell.colspan, cell.colspan,
); );
row_relative_cell_rect.start_corner -= row_rect.start_corner; row_relative_cell_rect.start_corner -= row_fragment_layout.rect.start_corner;
let mut fragment = cell.create_fragment( let mut fragment = cell.create_fragment(
layout, layout,
row_relative_cell_rect, row_relative_cell_rect,
row_baseline, row_baseline,
positioning_context, positioning_context,
&self.table.style, &self.table.style,
&row_fragment_layout.containing_block,
); );
// Make a table part rectangle relative to the row fragment for the purposes of
// drawing extra backgrounds.
//
// This rectangle is an offset between the row fragment and the other table
// part rectangle (row group, column, column group). Everything between them
// is laid out in a left-to-right fashion, but respecting the veritcality of
// the writing mode. This is why below, only the axes are flipped, but the
// rectangle is not flipped for RTL.
let make_relative_to_row_start = |mut rect: LogicalRect<Au>| {
rect.start_corner -= row_fragment_layout.rect.start_corner;
let writing_mode = row_fragment_layout.containing_block.style.writing_mode;
PhysicalRect::new(
if !writing_mode.is_vertical() {
PhysicalPoint::new(rect.start_corner.inline, rect.start_corner.block)
} else {
PhysicalPoint::new(rect.start_corner.block, rect.start_corner.inline)
},
rect.size.to_physical_size(writing_mode),
)
};
let column = self.table.columns.get(column_index); let column = self.table.columns.get(column_index);
let column_group = column let column_group = column
.and_then(|column| column.group_index) .and_then(|column| column.group_index)
.and_then(|index| self.table.column_groups.get(index)); .and_then(|index| self.table.column_groups.get(index));
if let Some(column_group) = column_group { if let Some(column_group) = column_group {
let mut rect = dimensions.get_column_group_rect(column_group); let rect = make_relative_to_row_start(dimensions.get_column_group_rect(column_group));
rect.start_corner -= row_rect.start_corner;
fragment.add_extra_background(ExtraBackground { fragment.add_extra_background(ExtraBackground {
style: column_group.style.clone(), style: column_group.style.clone(),
rect, rect,
@ -1991,8 +2058,7 @@ impl<'a> TableLayout<'a> {
} }
if let Some(column) = column { if let Some(column) = column {
if !column.is_anonymous { if !column.is_anonymous {
let mut rect = dimensions.get_column_rect(column_index); let rect = make_relative_to_row_start(dimensions.get_column_rect(column_index));
rect.start_corner -= row_rect.start_corner;
fragment.add_extra_background(ExtraBackground { fragment.add_extra_background(ExtraBackground {
style: column.style.clone(), style: column.style.clone(),
rect, rect,
@ -2004,24 +2070,20 @@ impl<'a> TableLayout<'a> {
.and_then(|row| row.group_index) .and_then(|row| row.group_index)
.and_then(|index| self.table.row_groups.get(index)); .and_then(|index| self.table.row_groups.get(index));
if let Some(row_group) = row_group { if let Some(row_group) = row_group {
let mut rect = dimensions.get_row_group_rect(row_group); let rect = make_relative_to_row_start(dimensions.get_row_group_rect(row_group));
rect.start_corner -= row_rect.start_corner;
fragment.add_extra_background(ExtraBackground { fragment.add_extra_background(ExtraBackground {
style: row_group.style.clone(), style: row_group.style.clone(),
rect, rect,
}) })
} }
if let Some(row) = row { if let Some(row) = row {
let mut rect = *row_rect; let rect = make_relative_to_row_start(row_fragment_layout.rect);
rect.start_corner = LogicalVec2::zero();
fragment.add_extra_background(ExtraBackground { fragment.add_extra_background(ExtraBackground {
style: row.style.clone(), style: row.style.clone(),
rect, rect,
}) })
} }
cell_fragments.push(Fragment::Box(fragment)); row_fragment_layout.fragments.push(Fragment::Box(fragment));
// If this cell has baseline alignment, it can adjust the table's overall baseline.
} }
fn make_fragments_for_columns_and_column_groups( fn make_fragments_for_columns_and_column_groups(
@ -2029,14 +2091,13 @@ impl<'a> TableLayout<'a> {
dimensions: &TableAndTrackDimensions, dimensions: &TableAndTrackDimensions,
fragments: &mut Vec<Fragment>, fragments: &mut Vec<Fragment>,
) { ) {
let table_writing_mode = self.table.style.effective_writing_mode();
for column_group in self.table.column_groups.iter() { for column_group in self.table.column_groups.iter() {
if !column_group.is_empty() { if !column_group.is_empty() {
fragments.push(Fragment::Positioning(PositioningFragment::new_empty( fragments.push(Fragment::Positioning(PositioningFragment::new_empty(
column_group.base_fragment_info, column_group.base_fragment_info,
dimensions dimensions
.get_column_group_rect(column_group) .get_column_group_rect(column_group)
.to_physical(table_writing_mode), .to_physical(None),
column_group.style.clone(), column_group.style.clone(),
))); )));
} }
@ -2045,9 +2106,7 @@ impl<'a> TableLayout<'a> {
for (column_index, column) in self.table.columns.iter().enumerate() { for (column_index, column) in self.table.columns.iter().enumerate() {
fragments.push(Fragment::Positioning(PositioningFragment::new_empty( fragments.push(Fragment::Positioning(PositioningFragment::new_empty(
column.base_fragment_info, column.base_fragment_info,
dimensions dimensions.get_column_rect(column_index).to_physical(None),
.get_column_rect(column_index)
.to_physical(table_writing_mode),
column.style.clone(), column.style.clone(),
))); )));
} }
@ -2117,16 +2176,29 @@ impl<'a> TableLayout<'a> {
struct RowFragmentLayout<'a> { struct RowFragmentLayout<'a> {
row: &'a TableTrack, row: &'a TableTrack,
rect: LogicalRect<Au>, rect: LogicalRect<Au>,
containing_block: ContainingBlock<'a>,
positioning_context: Option<PositioningContext>, positioning_context: Option<PositioningContext>,
fragments: Vec<Fragment>, fragments: Vec<Fragment>,
} }
impl<'a> RowFragmentLayout<'a> { impl<'a> RowFragmentLayout<'a> {
fn new(table_row: &'a TableTrack, index: usize, dimensions: &TableAndTrackDimensions) -> Self { fn new(
table_row: &'a TableTrack,
index: usize,
dimensions: &TableAndTrackDimensions,
table_style: &'a ComputedValues,
) -> Self {
let rect = dimensions.get_row_rect(index);
let containing_block = ContainingBlock {
inline_size: rect.size.inline,
block_size: AuOrAuto::LengthPercentage(rect.size.inline),
style: table_style,
};
Self { Self {
row: table_row, row: table_row,
rect: dimensions.get_row_rect(index), rect,
positioning_context: PositioningContext::new_for_style(&table_row.style), positioning_context: PositioningContext::new_for_style(&table_row.style),
containing_block,
fragments: Vec::new(), fragments: Vec::new(),
} }
} }
@ -2134,23 +2206,40 @@ impl<'a> RowFragmentLayout<'a> {
mut self, mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
table_positioning_context: &mut PositioningContext, table_positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock, containing_block_for_logical_conversion: &ContainingBlock,
containing_block_for_children: &ContainingBlock,
row_group_fragment_layout: &mut Option<RowGroupFragmentLayout>, row_group_fragment_layout: &mut Option<RowGroupFragmentLayout>,
) -> BoxFragment { ) -> BoxFragment {
if self.positioning_context.is_some() { if self.positioning_context.is_some() {
self.rect.start_corner += relative_adjustement(&self.row.style, containing_block); self.rect.start_corner +=
relative_adjustement(&self.row.style, containing_block_for_children);
} }
if let Some(ref row_group_layout) = row_group_fragment_layout { let (inline_size, block_size) =
self.rect.start_corner -= row_group_layout.rect.start_corner; if let Some(ref row_group_layout) = row_group_fragment_layout {
} self.rect.start_corner -= row_group_layout.rect.start_corner;
(
row_group_layout.rect.size.inline,
AuOrAuto::LengthPercentage(row_group_layout.rect.size.block),
)
} else {
(
containing_block_for_logical_conversion.inline_size,
containing_block_for_logical_conversion.block_size,
)
};
let row_group_containing_block = ContainingBlock {
inline_size,
block_size,
style: containing_block_for_logical_conversion.style,
};
let mut row_fragment = BoxFragment::new( let mut row_fragment = BoxFragment::new(
self.row.base_fragment_info, self.row.base_fragment_info,
self.row.style.clone(), self.row.style.clone(),
self.fragments, self.fragments,
self.rect self.rect.to_physical(Some(&row_group_containing_block)),
.to_physical(containing_block.effective_writing_mode()),
PhysicalSides::zero(), /* padding */ PhysicalSides::zero(), /* padding */
PhysicalSides::zero(), /* border */ PhysicalSides::zero(), /* border */
PhysicalSides::zero(), /* margin */ PhysicalSides::zero(), /* margin */
@ -2203,10 +2292,12 @@ impl RowGroupFragmentLayout {
mut self, mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
table_positioning_context: &mut PositioningContext, table_positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock, containing_block_for_logical_conversion: &ContainingBlock,
containing_block_for_children: &ContainingBlock,
) -> BoxFragment { ) -> BoxFragment {
if self.positioning_context.is_some() { if self.positioning_context.is_some() {
self.rect.start_corner += relative_adjustement(&self.style, containing_block); self.rect.start_corner +=
relative_adjustement(&self.style, containing_block_for_children);
} }
let mut row_group_fragment = BoxFragment::new( let mut row_group_fragment = BoxFragment::new(
@ -2214,7 +2305,7 @@ impl RowGroupFragmentLayout {
self.style, self.style,
self.fragments, self.fragments,
self.rect self.rect
.to_physical(containing_block.effective_writing_mode()), .to_physical(Some(containing_block_for_logical_conversion)),
PhysicalSides::zero(), /* padding */ PhysicalSides::zero(), /* padding */
PhysicalSides::zero(), /* border */ PhysicalSides::zero(), /* border */
PhysicalSides::zero(), /* margin */ PhysicalSides::zero(), /* margin */
@ -2414,7 +2505,7 @@ impl Table {
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> ContentSizes {
let writing_mode = containing_block_for_children.style.effective_writing_mode(); let writing_mode = containing_block_for_children.style.writing_mode;
let mut layout = TableLayout::new(self); let mut layout = TableLayout::new(self);
let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode); let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode);
@ -2536,6 +2627,7 @@ impl TableSlotCell {
cell_baseline: Au, cell_baseline: Au,
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
table_style: &ComputedValues, table_style: &ComputedValues,
containing_block: &ContainingBlock,
) -> BoxFragment { ) -> BoxFragment {
// This must be scoped to this function because it conflicts with euclid's Zero. // This must be scoped to this function because it conflicts with euclid's Zero.
use style::Zero as StyleZero; use style::Zero as StyleZero;
@ -2570,7 +2662,7 @@ impl TableSlotCell {
block: vertical_align_offset, block: vertical_align_offset,
}; };
let vertical_align_fragment = PositioningFragment::new_anonymous( let vertical_align_fragment = PositioningFragment::new_anonymous(
vertical_align_fragment_rect.to_physical(table_style.effective_writing_mode()), vertical_align_fragment_rect.to_physical(None),
layout.layout.fragments, layout.layout.fragments,
); );
@ -2581,8 +2673,7 @@ impl TableSlotCell {
// TODO(mrobinson): This is correct for absolutes that are direct children of the table // TODO(mrobinson): This is correct for absolutes that are direct children of the table
// cell, but wrong for absolute fragments that are more deeply nested in the hierarchy of // cell, but wrong for absolute fragments that are more deeply nested in the hierarchy of
// fragments. // fragments.
let physical_cell_rect = let physical_cell_rect = cell_content_rect.to_physical(Some(containing_block));
cell_content_rect.to_physical(table_style.effective_writing_mode());
layout layout
.positioning_context .positioning_context
.adjust_static_position_of_hoisted_fragments_with_offset( .adjust_static_position_of_hoisted_fragments_with_offset(
@ -2596,12 +2687,8 @@ impl TableSlotCell {
self.style.clone(), self.style.clone(),
vec![Fragment::Positioning(vertical_align_fragment)], vec![Fragment::Positioning(vertical_align_fragment)],
physical_cell_rect, physical_cell_rect,
layout layout.padding.to_physical(table_style.writing_mode),
.padding layout.border.to_physical(table_style.writing_mode),
.to_physical(table_style.effective_writing_mode()),
layout
.border
.to_physical(table_style.effective_writing_mode()),
PhysicalSides::zero(), /* margin */ PhysicalSides::zero(), /* margin */
None, /* clearance */ None, /* clearance */
CollapsedBlockMargins::zero(), CollapsedBlockMargins::zero(),

View file

@ -83,9 +83,9 @@ impl Arbitrary for FloatRangeInput {
FloatRangeInput { FloatRangeInput {
start_index, start_index,
side: if is_left { side: if is_left {
FloatSide::Left FloatSide::InlineStart
} else { } else {
FloatSide::Right FloatSide::InlineEnd
}, },
length, length,
} }
@ -185,8 +185,8 @@ fn check_node_range_setting(
) { ) {
if node.band.top >= block_range.start && node.band.top < block_range.end { if node.band.top >= block_range.start && node.band.top < block_range.end {
match side { match side {
FloatSide::Left => assert!(node.band.left.unwrap() >= value), FloatSide::InlineStart => assert!(node.band.left.unwrap() >= value),
FloatSide::Right => assert!(node.band.right.unwrap() <= value), FloatSide::InlineEnd => assert!(node.band.right.unwrap() <= value),
} }
} }
@ -357,9 +357,9 @@ impl Arbitrary for FloatInput {
block: Au::from_f32_px(height as f32), block: Au::from_f32_px(height as f32),
}, },
side: if is_left { side: if is_left {
FloatSide::Left FloatSide::InlineStart
} else { } else {
FloatSide::Right FloatSide::InlineEnd
}, },
clear: new_clear(clear), clear: new_clear(clear),
}, },
@ -504,10 +504,10 @@ impl FloatPlacement {
fn check_floats_rule_1(placement: &FloatPlacement) { fn check_floats_rule_1(placement: &FloatPlacement) {
for placed_float in &placement.placed_floats { for placed_float in &placement.placed_floats {
match placed_float.info.side { match placed_float.info.side {
FloatSide::Left => assert!( FloatSide::InlineStart => assert!(
placed_float.origin.inline >= placed_float.containing_block_info.inline_start placed_float.origin.inline >= placed_float.containing_block_info.inline_start
), ),
FloatSide::Right => { FloatSide::InlineEnd => {
assert!( assert!(
placed_float.rect().max_inline_position() <= placed_float.rect().max_inline_position() <=
placed_float.containing_block_info.inline_end placed_float.containing_block_info.inline_end
@ -526,19 +526,20 @@ fn check_floats_rule_2(placement: &FloatPlacement) {
for (this_float_index, this_float) in placement.placed_floats.iter().enumerate() { for (this_float_index, this_float) in placement.placed_floats.iter().enumerate() {
for prev_float in &placement.placed_floats[0..this_float_index] { for prev_float in &placement.placed_floats[0..this_float_index] {
match (this_float.info.side, prev_float.info.side) { match (this_float.info.side, prev_float.info.side) {
(FloatSide::Left, FloatSide::Left) => { (FloatSide::InlineStart, FloatSide::InlineStart) => {
assert!( assert!(
this_float.origin.inline >= prev_float.rect().max_inline_position() || this_float.origin.inline >= prev_float.rect().max_inline_position() ||
this_float.origin.block >= prev_float.rect().max_block_position() this_float.origin.block >= prev_float.rect().max_block_position()
); );
}, },
(FloatSide::Right, FloatSide::Right) => { (FloatSide::InlineEnd, FloatSide::InlineEnd) => {
assert!( assert!(
this_float.rect().max_inline_position() <= prev_float.origin.inline || this_float.rect().max_inline_position() <= prev_float.origin.inline ||
this_float.origin.block >= prev_float.rect().max_block_position() this_float.origin.block >= prev_float.rect().max_block_position()
); );
}, },
(FloatSide::Left, FloatSide::Right) | (FloatSide::Right, FloatSide::Left) => {}, (FloatSide::InlineStart, FloatSide::InlineEnd) |
(FloatSide::InlineEnd, FloatSide::InlineStart) => {},
} }
} }
} }
@ -568,13 +569,14 @@ fn check_floats_rule_3(placement: &FloatPlacement) {
} }
match (this_float.info.side, other_float.info.side) { match (this_float.info.side, other_float.info.side) {
(FloatSide::Left, FloatSide::Right) => { (FloatSide::InlineStart, FloatSide::InlineEnd) => {
assert!(this_float.rect().max_inline_position() <= other_float.origin.inline); assert!(this_float.rect().max_inline_position() <= other_float.origin.inline);
}, },
(FloatSide::Right, FloatSide::Left) => { (FloatSide::InlineEnd, FloatSide::InlineStart) => {
assert!(this_float.origin.inline >= other_float.rect().max_inline_position()); assert!(this_float.origin.inline >= other_float.rect().max_inline_position());
}, },
(FloatSide::Left, FloatSide::Left) | (FloatSide::Right, FloatSide::Right) => {}, (FloatSide::InlineStart, FloatSide::InlineStart) |
(FloatSide::InlineEnd, FloatSide::InlineEnd) => {},
} }
} }
} }
@ -616,14 +618,14 @@ fn check_floats_rule_7(placement: &FloatPlacement) {
for (placed_float_index, placed_float) in placement.placed_floats.iter().enumerate() { for (placed_float_index, placed_float) in placement.placed_floats.iter().enumerate() {
// Only consider floats that stick out. // Only consider floats that stick out.
match placed_float.info.side { match placed_float.info.side {
FloatSide::Left => { FloatSide::InlineStart => {
if placed_float.rect().max_inline_position() <= if placed_float.rect().max_inline_position() <=
placed_float.containing_block_info.inline_end placed_float.containing_block_info.inline_end
{ {
continue; continue;
} }
}, },
FloatSide::Right => { FloatSide::InlineEnd => {
if placed_float.origin.inline >= placed_float.containing_block_info.inline_start { if placed_float.origin.inline >= placed_float.containing_block_info.inline_start {
continue; continue;
} }
@ -686,8 +688,8 @@ fn check_floats_rule_9(floats_and_perturbations: Vec<(FloatInput, u32)>) {
let placed_float = &mut placement.placed_floats[float_index]; let placed_float = &mut placement.placed_floats[float_index];
let perturbation = Au::from_f32_px(perturbation as f32); let perturbation = Au::from_f32_px(perturbation as f32);
match placed_float.info.side { match placed_float.info.side {
FloatSide::Left => placed_float.origin.inline -= perturbation, FloatSide::InlineStart => placed_float.origin.inline -= perturbation,
FloatSide::Right => placed_float.origin.inline += perturbation, FloatSide::InlineEnd => placed_float.origin.inline += perturbation,
} }
} }
@ -737,8 +739,8 @@ fn check_floats_rule_10(placement: &FloatPlacement) {
} }
match this_float.info.clear { match this_float.info.clear {
Clear::Left => assert_ne!(other_float.info.side, FloatSide::Left), Clear::Left => assert_ne!(other_float.info.side, FloatSide::InlineStart),
Clear::Right => assert_ne!(other_float.info.side, FloatSide::Right), Clear::Right => assert_ne!(other_float.info.side, FloatSide::InlineEnd),
Clear::Both => assert!(false), Clear::Both => assert!(false),
Clear::None => unreachable!(), Clear::None => unreachable!(),
} }

View file

@ -0,0 +1,2 @@
[floats-placement-vertical-001b.xht]
expected: FAIL

View file

@ -0,0 +1,2 @@
[floats-placement-vertical-001c.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-right-100.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-right-101.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-right-102.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[padding-right-061.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[padding-right-062.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[padding-right-073.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-in-inline-margins-002a.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-in-inline-margins-002b.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-non-replaced-width-002.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-non-replaced-width-005.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-non-replaced-width-012.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-non-replaced-width-021.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-non-replaced-width-022.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-non-replaced-width-023.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-non-replaced-width-024.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-replaced-width-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-replaced-width-020.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-replaced-width-034.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-replaced-width-071.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[absolute-replaced-width-076.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[abspos-024.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[abspos-block-level-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[position-relative-010.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[position-relative-038.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[relpos-calcs-006.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[right-113.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text-align-white-space-005.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text-indent-rtl-001.xht]
expected: FAIL

View file

@ -17,20 +17,20 @@
[.item 12] [.item 12]
expected: FAIL expected: FAIL
[.item 19] [.item 13]
expected: FAIL expected: FAIL
[.item 20] [.item 14]
expected: FAIL expected: FAIL
[.item 21] [.item 15]
expected: FAIL expected: FAIL
[.item 22] [.item 16]
expected: FAIL expected: FAIL
[.item 23] [.item 17]
expected: FAIL expected: FAIL
[.item 24] [.item 18]
expected: FAIL expected: FAIL

View file

@ -17,20 +17,20 @@
[.item 6] [.item 6]
expected: FAIL expected: FAIL
[.item 13] [.item 19]
expected: FAIL expected: FAIL
[.item 14] [.item 20]
expected: FAIL expected: FAIL
[.item 15] [.item 21]
expected: FAIL expected: FAIL
[.item 16] [.item 22]
expected: FAIL expected: FAIL
[.item 17] [.item 23]
expected: FAIL expected: FAIL
[.item 18] [.item 24]
expected: FAIL expected: FAIL

View file

@ -17,20 +17,20 @@
[.item 12] [.item 12]
expected: FAIL expected: FAIL
[.item 13] [.item 19]
expected: FAIL expected: FAIL
[.item 14] [.item 20]
expected: FAIL expected: FAIL
[.item 15] [.item 21]
expected: FAIL expected: FAIL
[.item 16] [.item 22]
expected: FAIL expected: FAIL
[.item 17] [.item 23]
expected: FAIL expected: FAIL
[.item 18] [.item 24]
expected: FAIL expected: FAIL

View file

@ -17,20 +17,20 @@
[.item 12] [.item 12]
expected: FAIL expected: FAIL
[.item 13] [.item 19]
expected: FAIL expected: FAIL
[.item 14] [.item 20]
expected: FAIL expected: FAIL
[.item 15] [.item 21]
expected: FAIL expected: FAIL
[.item 16] [.item 22]
expected: FAIL expected: FAIL
[.item 17] [.item 23]
expected: FAIL expected: FAIL
[.item 18] [.item 24]
expected: FAIL expected: FAIL

View file

@ -0,0 +1,2 @@
[self-align-safe-unsafe-grid-003.html]
expected: FAIL

View file

@ -1,19 +1,10 @@
[position-absolute-001.html] [position-absolute-001.html]
[.flexbox 38]
expected: FAIL
[.flexbox 39] [.flexbox 39]
expected: FAIL expected: FAIL
[.flexbox 34] [.flexbox 34]
expected: FAIL expected: FAIL
[.flexbox 36]
expected: FAIL
[.flexbox 37]
expected: FAIL
[.flexbox 89] [.flexbox 89]
expected: FAIL expected: FAIL
@ -35,18 +26,6 @@
[.flexbox 86] [.flexbox 86]
expected: FAIL expected: FAIL
[.flexbox 16]
expected: FAIL
[.flexbox 17]
expected: FAIL
[.flexbox 18]
expected: FAIL
[.flexbox 19]
expected: FAIL
[.flexbox 108] [.flexbox 108]
expected: FAIL expected: FAIL
@ -59,9 +38,6 @@
[.flexbox 96] [.flexbox 96]
expected: FAIL expected: FAIL
[.flexbox 97]
expected: FAIL
[.flexbox 95] [.flexbox 95]
expected: FAIL expected: FAIL
@ -98,9 +74,6 @@
[.flexbox 64] [.flexbox 64]
expected: FAIL expected: FAIL
[.flexbox 69]
expected: FAIL
[.flexbox 68] [.flexbox 68]
expected: FAIL expected: FAIL
@ -140,9 +113,6 @@
[.flexbox 115] [.flexbox 115]
expected: FAIL expected: FAIL
[.flexbox 116]
expected: FAIL
[.flexbox 117] [.flexbox 117]
expected: FAIL expected: FAIL
@ -164,9 +134,6 @@
[.flexbox 41] [.flexbox 41]
expected: FAIL expected: FAIL
[.flexbox 40]
expected: FAIL
[.flexbox 43] [.flexbox 43]
expected: FAIL expected: FAIL
@ -194,12 +161,6 @@
[.flexbox 104] [.flexbox 104]
expected: FAIL expected: FAIL
[.flexbox 6]
expected: FAIL
[.flexbox 9]
expected: FAIL
[.flexbox 58] [.flexbox 58]
expected: FAIL expected: FAIL
@ -224,32 +185,32 @@
[.flexbox 55] [.flexbox 55]
expected: FAIL expected: FAIL
[.flexbox 27]
expected: FAIL
[.flexbox 24] [.flexbox 24]
expected: FAIL expected: FAIL
[.flexbox 20]
expected: FAIL
[.flexbox 120] [.flexbox 120]
expected: FAIL expected: FAIL
[.flexbox 7]
expected: FAIL
[.flexbox 26]
expected: FAIL
[.flexbox 52] [.flexbox 52]
expected: FAIL expected: FAIL
[.flexbox 71] [.flexbox 71]
expected: FAIL expected: FAIL
[.flexbox 87] [.flexbox 29]
expected: FAIL expected: FAIL
[.flexbox 106] [.flexbox 47]
expected: FAIL
[.flexbox 57]
expected: FAIL
[.flexbox 66]
expected: FAIL
[.flexbox 76]
expected: FAIL
[.flexbox 109]
expected: FAIL expected: FAIL

View file

@ -2,9 +2,6 @@
[.flexbox 5] [.flexbox 5]
expected: FAIL expected: FAIL
[.flexbox 4]
expected: FAIL
[.flexbox 8] [.flexbox 8]
expected: FAIL expected: FAIL

View file

@ -1,7 +1,4 @@
[position-absolute-003.html] [position-absolute-003.html]
[.rect 13]
expected: FAIL
[.rect 15] [.rect 15]
expected: FAIL expected: FAIL
@ -17,9 +14,6 @@
[.rect 5] [.rect 5]
expected: FAIL expected: FAIL
[.rect 14]
expected: FAIL
[.rect 16] [.rect 16]
expected: FAIL expected: FAIL

View file

@ -190,3 +190,15 @@
[.flexbox 95] [.flexbox 95]
expected: FAIL expected: FAIL
[.flexbox 38]
expected: FAIL
[.flexbox 46]
expected: FAIL
[.flexbox 54]
expected: FAIL
[.flexbox 62]
expected: FAIL

View file

@ -862,3 +862,57 @@
[.flexbox 432] [.flexbox 432]
expected: FAIL expected: FAIL
[.flexbox 152]
expected: FAIL
[.flexbox 166]
expected: FAIL
[.flexbox 168]
expected: FAIL
[.flexbox 179]
expected: FAIL
[.flexbox 187]
expected: FAIL
[.flexbox 189]
expected: FAIL
[.flexbox 200]
expected: FAIL
[.flexbox 214]
expected: FAIL
[.flexbox 216]
expected: FAIL
[.flexbox 227]
expected: FAIL
[.flexbox 235]
expected: FAIL
[.flexbox 237]
expected: FAIL
[.flexbox 247]
expected: FAIL
[.flexbox 249]
expected: FAIL
[.flexbox 263]
expected: FAIL
[.flexbox 274]
expected: FAIL
[.flexbox 276]
expected: FAIL
[.flexbox 284]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flex-direction-row-vertical.html]
expected: FAIL

View file

@ -8,15 +8,9 @@
[.flexbox 4] [.flexbox 4]
expected: FAIL expected: FAIL
[.flexbox 7]
expected: FAIL
[.flexbox 2] [.flexbox 2]
expected: FAIL expected: FAIL
[.flexbox 8]
expected: FAIL
[.flexbox 1] [.flexbox 1]
expected: FAIL expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-align-self-vert-rtl-001.xhtml]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-align-self-vert-rtl-002.xhtml]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-align-self-vert-rtl-003.xhtml]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-align-self-vert-rtl-004.xhtml]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-mbp-horiz-001-rtl-reverse.xhtml]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-mbp-horiz-001-rtl.xhtml]
expected: FAIL

View file

@ -1,24 +1,3 @@
[flexbox_justifycontent-rtl-001.html] [flexbox_justifycontent-rtl-001.html]
[.container > div 1] [.container > div 11]
expected: FAIL
[.container > div 2]
expected: FAIL
[.container > div 5]
expected: FAIL
[.container > div 7]
expected: FAIL
[.container > div 8]
expected: FAIL
[.container > div 9]
expected: FAIL
[.container > div 10]
expected: FAIL
[.container > div 12]
expected: FAIL expected: FAIL

View file

@ -1,36 +0,0 @@
[flexbox_justifycontent-rtl-002.html]
[.container > div 1]
expected: FAIL
[.container > div 2]
expected: FAIL
[.container > div 3]
expected: FAIL
[.container > div 4]
expected: FAIL
[.container > div 5]
expected: FAIL
[.container > div 6]
expected: FAIL
[.container > div 7]
expected: FAIL
[.container > div 8]
expected: FAIL
[.container > div 9]
expected: FAIL
[.container > div 10]
expected: FAIL
[.container > div 11]
expected: FAIL
[.container > div 12]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox_rtl-direction.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[gap-001-rtl.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[gap-004-rtl.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[gap-007-rtl.html]
expected: FAIL

View file

@ -1,16 +1,4 @@
[logical-box-border-color.html] [logical-box-border-color.html]
[Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.] [Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.]
expected: FAIL expected: FAIL

View file

@ -1,13 +1,4 @@
[logical-box-border-radius.html] [logical-box-border-radius.html]
[Test that logical border-*-radius properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-radius properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-radius properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that logical border-*-radius properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.] [Test that logical border-*-radius properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.]
expected: FAIL expected: FAIL

View file

@ -1,16 +1,4 @@
[logical-box-border-style.html] [logical-box-border-style.html]
[Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.] [Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.]
expected: FAIL expected: FAIL

View file

@ -1,16 +1,4 @@
[logical-box-border-width.html] [logical-box-border-width.html]
[Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.] [Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.]
expected: FAIL expected: FAIL

View file

@ -1,16 +1,4 @@
[logical-box-inset.html] [logical-box-inset.html]
[Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.] [Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.]
expected: FAIL expected: FAIL

View file

@ -1,16 +1,4 @@
[logical-box-margin.html] [logical-box-margin.html]
[Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.] [Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.]
expected: FAIL expected: FAIL

View file

@ -1,16 +1,4 @@
[logical-box-padding.html] [logical-box-padding.html]
[Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
expected: FAIL
[Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.] [Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.]
expected: FAIL expected: FAIL

View file

@ -0,0 +1,2 @@
[position-absolute-dynamic-static-position-floats-004.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[inline-level-absolute-in-block-level-context-007.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[inline-level-absolute-in-block-level-context-009.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[inline-level-absolute-in-block-level-context-010.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[row-group-margin-border-padding.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[row-margin-border-padding.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[trailing-space-and-text-alignment-rtl-002.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[trailing-space-and-text-alignment-rtl-003.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[white-space-pre-wrap-trailing-spaces-023.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[word-break-normal-ar-000.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[getClientRects-inline.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-direction.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-row-direction.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-row-group-direction.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[textarea-padding-iend-overlaps-content-001.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[textarea-padding-istart-moves-content-001.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[bdi-neutral-wrapped.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[direction_style_caching.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[rtl_body.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[rtl_margin_a.html]
expected: FAIL