From 94306cd1834c7f1594eeb44119ef9f7fadadd611 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 27 Apr 2016 12:38:30 -0700 Subject: [PATCH 01/12] layout: Speculate that the inline sizes of floats with percentage inline sizes are nonzero. This is a bit of a hack. --- components/layout/floats.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/components/layout/floats.rs b/components/layout/floats.rs index bd91acb657d..e4489f4288d 100644 --- a/components/layout/floats.rs +++ b/components/layout/floats.rs @@ -11,6 +11,7 @@ use std::fmt; use std::i32; use style::computed_values::float; use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; +use style::values::computed::LengthOrPercentageOrAuto; /// The kind of float: left or right. #[derive(Clone, RustcEncodable, Debug, Copy)] @@ -490,14 +491,30 @@ impl SpeculatedFloatPlacement { } let base_flow = flow::base(flow); + if !base_flow.flags.is_float() { + return + } + + let mut float_inline_size = base_flow.intrinsic_inline_sizes.preferred_inline_size; + if float_inline_size == Au(0) { + if flow.is_block_like() { + // Hack: If the size of the float is a percentage, then there's no way we can guess + // at its size now. So just pick an arbitrary nonzero value (in this case, 1px) so + // that the layout traversal logic will know that objects later in the document + // might flow around this float. + if let LengthOrPercentageOrAuto::Percentage(percentage) = + flow.as_block().fragment.style.content_inline_size() { + if percentage > 0.0 { + float_inline_size = Au::from_px(1) + } + } + } + } + match base_flow.flags.float_kind() { float::T::none => {} - float::T::left => { - self.left = self.left + base_flow.intrinsic_inline_sizes.preferred_inline_size - } - float::T::right => { - self.right = self.right + base_flow.intrinsic_inline_sizes.preferred_inline_size - } + float::T::left => self.left = self.left + float_inline_size, + float::T::right => self.right = self.right + float_inline_size, } } From 8823f87276cf19ec52cb5e61118a7c17ffaf53a6 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 27 Apr 2016 12:40:58 -0700 Subject: [PATCH 02/12] layout: Don't pretend inline fragment sizes are zero when placing them between floats. --- components/gfx/text/text_run.rs | 9 ++++++++- components/layout/fragment.rs | 11 +++++++++++ components/layout/inline.rs | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index 10cced11166..7dd0b027839 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -272,7 +272,14 @@ impl<'a> TextRun { }) } - /// Returns the index of the first glyph run containing the given byte index. + pub fn minimum_splittable_inline_size(&self, range: &Range) -> Au { + match self.natural_word_slices_in_range(range).next() { + None => Au(0), + Some(slice) => self.advance_for_range(&slice.range), + } + } + + /// Returns the index of the first glyph run containing the given character index. fn index_of_first_glyph_run_containing(&self, index: ByteIndex) -> Option { let self_ptr = self as *const TextRun; INDEX_OF_FIRST_GLYPH_RUN_CACHE.with(|index_of_first_glyph_run_cache| { diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index f90a18a438d..67ced630250 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -1471,6 +1471,17 @@ impl Fragment { result } + /// Returns the narrowest inline-size that the first splittable part of this fragment could + /// possibly be split to. (In most cases, this returns the inline-size of the first word in + /// this fragment.) + pub fn minimum_splittable_inline_size(&self) -> Au { + match self.specific { + SpecificFragmentInfo::ScannedText(ref text) => { + text.run.minimum_splittable_inline_size(&text.range) + } + _ => Au(0), + } + } /// TODO: What exactly does this function return? Why is it Au(0) for /// `SpecificFragmentInfo::Generic`? diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 95c3c85454a..17fdfc82937 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -444,7 +444,7 @@ impl LineBreaker { // Initially, pretend a splittable fragment has zero inline-size. We will move it later if // it has nonzero inline-size and that causes problems. let placement_inline_size = if first_fragment.can_split() { - Au(0) + first_fragment.minimum_splittable_inline_size() } else { first_fragment.margin_box_inline_size() + self.indentation_for_pending_fragment() }; From 04f05349b11ba18334ff689f31ba3abed27f7199 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 27 Apr 2016 12:44:43 -0700 Subject: [PATCH 03/12] =?UTF-8?q?layout:=20Check=20flow=20descendants=20of?= =?UTF-8?q?=20inline=20block=20fragments=20to=20find=20their=20baselines?= =?UTF-8?q?=20when=20aligning=20inline=20fragments=20per=20CSS=202.1=20?= =?UTF-8?q?=C2=A7=2010.8.1.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/layout/block.rs | 127 ++++++++++++++++++++++----------- components/layout/flow.rs | 44 ++++++++---- components/layout/flow_list.rs | 6 ++ components/layout/fragment.rs | 45 +++++++++--- components/layout/inline.rs | 19 ++++- components/style/values.rs | 15 ++++ 6 files changed, 191 insertions(+), 65 deletions(-) diff --git a/components/layout/block.rs b/components/layout/block.rs index 987a3038e9b..c6fedc043bb 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -33,7 +33,8 @@ use display_list_builder::BlockFlowDisplayListBuilding; use display_list_builder::{BorderPaintingMode, DisplayListBuildState, FragmentDisplayListBuilding}; use euclid::{Point2D, Rect, Size2D}; use floats::{ClearType, FloatKind, Floats, PlacementInfo}; -use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT, INLINE_POSITION_IS_STATIC}; +use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT}; +use flow::{CONTAINS_TEXT_OR_REPLACED_FRAGMENTS, INLINE_POSITION_IS_STATIC}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow}; use flow::{NEEDS_LAYER, PostorderFlowTraversal, PreorderFlowTraversal, FragmentationContext}; @@ -1477,57 +1478,85 @@ impl BlockFlow { pub fn bubble_inline_sizes_for_block(&mut self, consult_children: bool) { let _scope = layout_debug_scope!("block::bubble_inline_sizes {:x}", self.base.debug_id()); - let flags = self.base.flags; - - // Find the maximum inline-size from children. - let mut computation = self.fragment.compute_intrinsic_inline_sizes(); - let (mut left_float_width, mut right_float_width) = (Au(0), Au(0)); - let (mut left_float_width_accumulator, mut right_float_width_accumulator) = (Au(0), Au(0)); - for kid in self.base.child_iter_mut() { - let is_absolutely_positioned = - flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED); - let child_base = flow::mut_base(kid); - let float_kind = child_base.flags.float_kind(); - if !is_absolutely_positioned && consult_children { - computation.content_intrinsic_sizes.minimum_inline_size = - max(computation.content_intrinsic_sizes.minimum_inline_size, - child_base.intrinsic_inline_sizes.minimum_inline_size); - - if child_base.flags.contains(CLEARS_LEFT) { - left_float_width = max(left_float_width, left_float_width_accumulator); - left_float_width_accumulator = Au(0) - } - if child_base.flags.contains(CLEARS_RIGHT) { - right_float_width = max(right_float_width, right_float_width_accumulator); - right_float_width_accumulator = Au(0) - } - - match float_kind { - float::T::none => { - computation.content_intrinsic_sizes.preferred_inline_size = - max(computation.content_intrinsic_sizes.preferred_inline_size, - child_base.intrinsic_inline_sizes.preferred_inline_size); - } - float::T::left => { - left_float_width_accumulator = left_float_width_accumulator + - child_base.intrinsic_inline_sizes.preferred_inline_size; - } - float::T::right => { - right_float_width_accumulator = right_float_width_accumulator + - child_base.intrinsic_inline_sizes.preferred_inline_size; - } + let mut flags = self.base.flags; + if self.definitely_has_zero_block_size() { + // This is kind of a hack for Acid2. But it's a harmless one, because (a) this behavior + // is unspecified; (b) it matches the behavior one would intuitively expect, since + // floats don't flow around blocks that take up no space in the block direction. + flags.remove(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS); + } else if self.fragment.is_text_or_replaced() { + flags.insert(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS); + } else { + flags.remove(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS); + for kid in self.base.children.iter() { + if flow::base(kid).flags.contains(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS) { + flags.insert(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS); + break } } } + // Find the maximum inline-size from children. + // + // See: https://lists.w3.org/Archives/Public/www-style/2014Nov/0085.html + // + // FIXME(pcwalton): This doesn't exactly follow that algorithm at the moment. // FIXME(pcwalton): This should consider all float descendants, not just children. - // FIXME(pcwalton): This is not well-spec'd; INTRINSIC specifies to do this, but CSS-SIZING - // says not to. In practice, Gecko and WebKit both do this. + let mut computation = self.fragment.compute_intrinsic_inline_sizes(); + let (mut left_float_width, mut right_float_width) = (Au(0), Au(0)); + let (mut left_float_width_accumulator, mut right_float_width_accumulator) = (Au(0), Au(0)); + let mut preferred_inline_size_of_children_without_text_or_replaced_fragments = Au(0); + for kid in self.base.child_iter_mut() { + if flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) || !consult_children { + continue + } + + let child_base = flow::mut_base(kid); + let float_kind = child_base.flags.float_kind(); + computation.content_intrinsic_sizes.minimum_inline_size = + max(computation.content_intrinsic_sizes.minimum_inline_size, + child_base.intrinsic_inline_sizes.minimum_inline_size); + + if child_base.flags.contains(CLEARS_LEFT) { + left_float_width = max(left_float_width, left_float_width_accumulator); + left_float_width_accumulator = Au(0) + } + if child_base.flags.contains(CLEARS_RIGHT) { + right_float_width = max(right_float_width, right_float_width_accumulator); + right_float_width_accumulator = Au(0) + } + + match (float_kind, child_base.flags.contains(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS)) { + (float::T::none, true) => { + computation.content_intrinsic_sizes.preferred_inline_size = + max(computation.content_intrinsic_sizes.preferred_inline_size, + child_base.intrinsic_inline_sizes.preferred_inline_size); + } + (float::T::none, false) => { + preferred_inline_size_of_children_without_text_or_replaced_fragments = max( + preferred_inline_size_of_children_without_text_or_replaced_fragments, + child_base.intrinsic_inline_sizes.preferred_inline_size) + } + (float::T::left, _) => { + left_float_width_accumulator = left_float_width_accumulator + + child_base.intrinsic_inline_sizes.preferred_inline_size; + } + (float::T::right, _) => { + right_float_width_accumulator = right_float_width_accumulator + + child_base.intrinsic_inline_sizes.preferred_inline_size; + } + } + } + left_float_width = max(left_float_width, left_float_width_accumulator); right_float_width = max(right_float_width, right_float_width_accumulator); + + computation.content_intrinsic_sizes.preferred_inline_size = + computation.content_intrinsic_sizes.preferred_inline_size + left_float_width + + right_float_width; computation.content_intrinsic_sizes.preferred_inline_size = max(computation.content_intrinsic_sizes.preferred_inline_size, - left_float_width + right_float_width); + preferred_inline_size_of_children_without_text_or_replaced_fragments); self.base.intrinsic_inline_sizes = computation.finish(); self.base.flags = flags @@ -1630,6 +1659,18 @@ impl BlockFlow { FormattingContextType::None | FormattingContextType::Other => {} } } + + fn definitely_has_zero_block_size(&self) -> bool { + if !self.fragment.style.content_block_size().is_definitely_zero() { + return false + } + let border_width = self.fragment.border_width(); + if border_width.block_start != Au(0) || border_width.block_end != Au(0) { + return false + } + let padding = self.fragment.style.logical_padding(); + padding.block_start.is_definitely_zero() && padding.block_end.is_definitely_zero() + } } impl Flow for BlockFlow { diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 9ca27c352ce..a77f636415b 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -512,6 +512,8 @@ pub trait ImmutableFlowUtils { /// Returns true if floats might flow through this flow, as determined by the float placement /// speculation pass. fn floats_might_flow_through(self) -> bool; + + fn baseline_offset_of_last_line_box_in_flow(self) -> Option; } pub trait MutableFlowUtils { @@ -634,45 +636,48 @@ bitflags! { #[doc = "Whether this flow must have its own layer. Even if this flag is not set, it might"] #[doc = "get its own layer if it's deemed to be likely to overlap flows with their own"] #[doc = "layer."] - const NEEDS_LAYER = 0b0000_0000_0000_0010_0000, + const NEEDS_LAYER = 0b0000_0000_0000_0000_0010_0000, #[doc = "Whether this flow is absolutely positioned. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const IS_ABSOLUTELY_POSITIONED = 0b0000_0000_0000_0100_0000, + const IS_ABSOLUTELY_POSITIONED = 0b0000_0000_0000_0000_0100_0000, #[doc = "Whether this flow clears to the left. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const CLEARS_LEFT = 0b0000_0000_0000_1000_0000, + const CLEARS_LEFT = 0b0000_0000_0000_0000_1000_0000, #[doc = "Whether this flow clears to the right. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const CLEARS_RIGHT = 0b0000_0000_0001_0000_0000, + const CLEARS_RIGHT = 0b0000_0000_0000_0001_0000_0000, #[doc = "Whether this flow is left-floated. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const FLOATS_LEFT = 0b0000_0000_0010_0000_0000, + const FLOATS_LEFT = 0b0000_0000_0000_0010_0000_0000, #[doc = "Whether this flow is right-floated. This is checked all over layout, so a"] #[doc = "virtual call is too expensive."] - const FLOATS_RIGHT = 0b0000_0000_0100_0000_0000, + const FLOATS_RIGHT = 0b0000_0000_0000_0100_0000_0000, #[doc = "Text alignment. \ NB: If you update this, update `TEXT_ALIGN_SHIFT` below."] - const TEXT_ALIGN = 0b0000_0111_1000_0000_0000, + const TEXT_ALIGN = 0b0000_0000_0111_1000_0000_0000, #[doc = "Whether this flow has a fragment with `counter-reset` or `counter-increment` \ styles."] - const AFFECTS_COUNTERS = 0b0000_1000_0000_0000_0000, + const AFFECTS_COUNTERS = 0b0000_0000_1000_0000_0000_0000, #[doc = "Whether this flow's descendants have fragments that affect `counter-reset` or \ `counter-increment` styles."] - const HAS_COUNTER_AFFECTING_CHILDREN = 0b0001_0000_0000_0000_0000, + const HAS_COUNTER_AFFECTING_CHILDREN = 0b0000_0001_0000_0000_0000_0000, #[doc = "Whether this flow behaves as though it had `position: static` for the purposes \ of positioning in the inline direction. This is set for flows with `position: \ static` and `position: relative` as well as absolutely-positioned flows with \ unconstrained positions in the inline direction."] - const INLINE_POSITION_IS_STATIC = 0b0010_0000_0000_0000_0000, + const INLINE_POSITION_IS_STATIC = 0b0000_0010_0000_0000_0000_0000, #[doc = "Whether this flow behaves as though it had `position: static` for the purposes \ of positioning in the block direction. This is set for flows with `position: \ static` and `position: relative` as well as absolutely-positioned flows with \ unconstrained positions in the block direction."] - const BLOCK_POSITION_IS_STATIC = 0b0100_0000_0000_0000_0000, + const BLOCK_POSITION_IS_STATIC = 0b0000_0100_0000_0000_0000_0000, /// Whether any ancestor is a fragmentation container - const CAN_BE_FRAGMENTED = 0b1000_0000_0000_0000_0000, + const CAN_BE_FRAGMENTED = 0b0000_1000_0000_0000_0000_0000, + + /// Whether this flow contains any text and/or replaced fragments. + const CONTAINS_TEXT_OR_REPLACED_FRAGMENTS = 0b0001_0000_0000_0000_0000_0000, } } @@ -1387,6 +1392,21 @@ impl<'a> ImmutableFlowUtils for &'a Flow { } self.as_block().formatting_context_type() == FormattingContextType::None } + + fn baseline_offset_of_last_line_box_in_flow(self) -> Option { + for kid in base(self).children.iter().rev() { + if kid.is_inline_flow() { + return kid.as_inline().baseline_offset_of_last_line() + } + if kid.is_block_like() && + kid.as_block().formatting_context_type() == FormattingContextType::None { + if let Some(baseline_offset) = kid.baseline_offset_of_last_line_box_in_flow() { + return Some(base(kid).position.start.b + baseline_offset) + } + } + } + None + } } impl<'a> MutableFlowUtils for &'a mut Flow { diff --git a/components/layout/flow_list.rs b/components/layout/flow_list.rs index 85657db8acd..5a0d389f5da 100644 --- a/components/layout/flow_list.rs +++ b/components/layout/flow_list.rs @@ -111,6 +111,12 @@ impl<'a> Iterator for FlowListIterator<'a> { } } +impl<'a> DoubleEndedIterator for FlowListIterator<'a> { + fn next_back(&mut self) -> Option<&'a Flow> { + self.it.next_back().map(|x| &**x) + } +} + impl<'a> Iterator for MutFlowListIterator<'a> { type Item = &'a mut Flow; #[inline] diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 67ced630250..2b7527af822 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -1077,7 +1077,7 @@ impl Fragment { /// Returns the sum of the inline-sizes of all the borders of this fragment. Note that this /// can be expensive to compute, so if possible use the `border_padding` field instead. #[inline] - fn border_width(&self) -> LogicalMargin { + pub fn border_width(&self) -> LogicalMargin { let style_border_width = match self.specific { SpecificFragmentInfo::ScannedText(_) | SpecificFragmentInfo::InlineBlock(_) => LogicalMargin::zero(self.style.writing_mode), @@ -1983,14 +1983,19 @@ impl Fragment { } SpecificFragmentInfo::InlineBlock(ref info) => { // See CSS 2.1 § 10.8.1. - let block_flow = info.flow_ref.as_block(); - let font_style = self.style.get_font_arc(); - let font_metrics = text::font_metrics_for_style(&mut layout_context.font_context(), - font_style); - InlineMetrics::from_block_height(&font_metrics, - block_flow.base.position.size.block, - block_flow.fragment.margin.block_start, - block_flow.fragment.margin.block_end) + let flow = &info.flow_ref; + let block_flow = flow.as_block(); + let baseline_offset = match flow.baseline_offset_of_last_line_box_in_flow() { + Some(baseline_offset) => baseline_offset, + None => block_flow.fragment.border_box.size.block, + }; + let start_margin = block_flow.fragment.margin.block_start; + let end_margin = block_flow.fragment.margin.block_end; + let depth_below_baseline = flow::base(&**flow).position.size.block - + baseline_offset + end_margin; + InlineMetrics::new(baseline_offset + start_margin, + depth_below_baseline, + baseline_offset) } SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | SpecificFragmentInfo::InlineAbsolute(_) => { @@ -2552,6 +2557,28 @@ impl Fragment { pub fn layer_id_for_overflow_scroll(&self) -> LayerId { LayerId::new_of_type(LayerType::OverflowScroll, self.node.id() as usize) } + + pub fn is_text_or_replaced(&self) -> bool { + match self.specific { + SpecificFragmentInfo::Generic | + SpecificFragmentInfo::InlineAbsolute(_) | + SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | + SpecificFragmentInfo::InlineBlock(_) | + SpecificFragmentInfo::Multicol | + SpecificFragmentInfo::MulticolColumn | + SpecificFragmentInfo::Table | + SpecificFragmentInfo::TableCell | + SpecificFragmentInfo::TableColumn(_) | + SpecificFragmentInfo::TableRow | + SpecificFragmentInfo::TableWrapper => false, + SpecificFragmentInfo::Canvas(_) | + SpecificFragmentInfo::GeneratedContent(_) | + SpecificFragmentInfo::Iframe(_) | + SpecificFragmentInfo::Image(_) | + SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::UnscannedText(_) => true + } + } } impl fmt::Debug for Fragment { diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 17fdfc82937..05c0d524912 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -11,7 +11,8 @@ use display_list_builder::DisplayListBuildState; use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding}; use euclid::{Point2D, Size2D}; use floats::{FloatKind, Floats, PlacementInfo}; -use flow::{EarlyAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow}; +use flow::{CONTAINS_TEXT_OR_REPLACED_FRAGMENTS, EarlyAbsolutePositionInfo, MutableFlowUtils}; +use flow::{OpaqueFlow}; use flow::{self, BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, IS_ABSOLUTELY_POSITIONED}; use flow_ref; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow}; @@ -1275,6 +1276,16 @@ impl InlineFlow { } } } + + pub fn baseline_offset_of_last_line(&self) -> Option { + match self.lines.last() { + None => None, + Some(ref last_line) => { + Some(last_line.bounds.start.b + last_line.bounds.size.block - + last_line.inline_metrics.depth_below_baseline) + } + } + } } impl Flow for InlineFlow { @@ -1300,6 +1311,8 @@ impl Flow for InlineFlow { flow::mut_base(kid).floats = Floats::new(writing_mode); } + self.base.flags.remove(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS); + let mut intrinsic_sizes_for_flow = IntrinsicISizesContribution::new(); let mut intrinsic_sizes_for_inline_run = IntrinsicISizesContribution::new(); let mut intrinsic_sizes_for_nonbroken_run = IntrinsicISizesContribution::new(); @@ -1358,6 +1371,10 @@ impl Flow for InlineFlow { } fragment.restyle_damage.remove(BUBBLE_ISIZES); + + if fragment.is_text_or_replaced() { + self.base.flags.insert(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS); + } } // Flush any remaining nonbroken-run and inline-run intrinsic sizes. diff --git a/components/style/values.rs b/components/style/values.rs index 77a521469ad..2f295bd8775 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -1837,6 +1837,21 @@ pub mod computed { Auto, Calc(CalcLengthOrPercentage), } + + impl LengthOrPercentageOrAuto { + /// Returns true if the computed value is absolute 0 or 0%. + /// + /// (Returns false for calc() values, even if ones that may resolve to zero.) + #[inline] + pub fn is_definitely_zero(&self) -> bool { + use self::LengthOrPercentageOrAuto::*; + match *self { + Length(Au(0)) | Percentage(0.0) => true, + Length(_) | Percentage(_) | Calc(_) | Auto => false + } + } + } + impl fmt::Debug for LengthOrPercentageOrAuto { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { From b7c967404481d39599a665b7bcbb755a6929a5b2 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 27 Apr 2016 12:47:50 -0700 Subject: [PATCH 04/12] layout: Consider vertical alignment of fragments during line breaking. This makes the line breaker determine the final block positions of each line rather than doing it in a separate pass afterward. Not only does this simplify the code, it makes `vertical-align` and float placement interact properly. --- components/layout/fragment.rs | 22 +- components/layout/inline.rs | 424 ++++++++++++---------------------- 2 files changed, 171 insertions(+), 275 deletions(-) diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 2b7527af822..9d90a9fca07 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -11,7 +11,7 @@ use canvas_traits::CanvasMsg; use context::LayoutContext; use euclid::{Point2D, Rect, Size2D}; use floats::ClearType; -use flow::{self, Flow}; +use flow::{self, Flow, ImmutableFlowUtils}; use flow_ref::{self, FlowRef}; use gfx; use gfx::display_list::{BLUR_INFLATION_FACTOR, FragmentType, OpaqueNode, StackingContextId}; @@ -38,7 +38,7 @@ use std::sync::{Arc, Mutex}; use style::computed_values::content::ContentItem; use style::computed_values::{border_collapse, clear, display, mix_blend_mode, overflow_wrap}; use style::computed_values::{overflow_x, position, text_decoration, transform_style}; -use style::computed_values::{white_space, word_break, z_index}; +use style::computed_values::{vertical_align, white_space, word_break, z_index}; use style::dom::TRestyleDamage; use style::logical_geometry::{LogicalMargin, LogicalRect, LogicalSize, WritingMode}; use style::properties::{ComputedValues, ServoComputedValues}; @@ -2558,6 +2558,24 @@ impl Fragment { LayerId::new_of_type(LayerType::OverflowScroll, self.node.id() as usize) } + /// Returns true if any of the inline styles associated with this fragment have + /// `vertical-align` set to `top` or `bottom`. + pub fn is_vertically_aligned_to_top_or_bottom(&self) -> bool { + match self.style.get_box().vertical_align { + vertical_align::T::top | vertical_align::T::bottom => return true, + _ => {} + } + if let Some(ref inline_context) = self.inline_context { + for node in &inline_context.nodes { + match node.style.get_box().vertical_align { + vertical_align::T::top | vertical_align::T::bottom => return true, + _ => {} + } + } + } + false + } + pub fn is_text_or_replaced(&self) -> bool { match self.specific { SpecificFragmentInfo::Generic | diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 05c0d524912..f48a7a0c632 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -27,7 +27,7 @@ use range::{Range, RangeIndex}; use std::cmp::max; use std::collections::VecDeque; use std::sync::Arc; -use std::{fmt, isize, mem}; +use std::{fmt, i32, isize, mem}; use style::computed_values::{display, overflow_x, position, text_align, text_justify}; use style::computed_values::{text_overflow, vertical_align, white_space}; use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; @@ -419,13 +419,23 @@ impl LineBreaker { // block-size. This might not be the case with some weird text fonts. fn new_inline_metrics_for_line(&self, new_fragment: &Fragment, layout_context: &LayoutContext) -> InlineMetrics { - self.pending_line.inline_metrics.max(&new_fragment.inline_metrics(layout_context)) + if !new_fragment.is_vertically_aligned_to_top_or_bottom() { + let fragment_inline_metrics = new_fragment.inline_metrics(layout_context); + self.pending_line.inline_metrics.max(&fragment_inline_metrics) + } else { + self.pending_line.inline_metrics + } } fn new_block_size_for_line(&self, new_fragment: &Fragment, layout_context: &LayoutContext) -> Au { - max(self.pending_line.bounds.size.block, - self.new_inline_metrics_for_line(new_fragment, layout_context).block_size()) + let new_block_size = if new_fragment.is_vertically_aligned_to_top_or_bottom() { + max(new_fragment.inline_metrics(layout_context).block_size(), + self.minimum_block_size_above_baseline + self.minimum_depth_below_baseline) + } else { + self.new_inline_metrics_for_line(new_fragment, layout_context).block_size() + }; + max(self.pending_line.bounds.size.block, new_block_size) } /// Computes the position of a line that has only the provided fragment. Returns the bounding @@ -856,105 +866,6 @@ impl InlineFlow { flow } - /// Returns the distance from the baseline for the logical block-start inline-start corner of - /// this fragment, taking into account the value of the CSS `vertical-align` property. - /// Negative values mean "toward the logical block-start" and positive values mean "toward the - /// logical block-end". - /// - /// The extra boolean is set if and only if `largest_block_size_for_top_fragments` and/or - /// `largest_block_size_for_bottom_fragments` were updated. That is, if the box has a `top` or - /// `bottom` value for `vertical-align`, true is returned. - fn distance_from_baseline(fragment: &Fragment, - ascent: Au, - parent_text_block_start: Au, - parent_text_block_end: Au, - block_size_above_baseline: &mut Au, - depth_below_baseline: &mut Au, - largest_block_size_for_top_fragments: &mut Au, - largest_block_size_for_bottom_fragments: &mut Au, - layout_context: &LayoutContext) - -> (Au, bool) { - let (mut offset_from_baseline, mut largest_size_updated) = (Au(0), false); - for style in fragment.inline_styles() { - // Ignore `vertical-align` values for table cells. - let box_style = style.get_box(); - match box_style.display { - display::T::inline | display::T::block | display::T::inline_block => {} - _ => continue, - } - - match box_style.vertical_align { - vertical_align::T::baseline => {} - vertical_align::T::middle => { - // TODO: x-height value should be used from font info. - // TODO: Doing nothing here passes our current reftests but doesn't work in - // all situations. Add vertical align reftests and fix this. - }, - vertical_align::T::sub => { - let sub_offset = (parent_text_block_start + parent_text_block_end) - .scale_by(FONT_SUBSCRIPT_OFFSET_RATIO); - offset_from_baseline = offset_from_baseline + sub_offset - }, - vertical_align::T::super_ => { - let super_offset = (parent_text_block_start + parent_text_block_end) - .scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO); - offset_from_baseline = offset_from_baseline - super_offset - }, - vertical_align::T::text_top => { - let fragment_block_size = *block_size_above_baseline + - *depth_below_baseline; - let prev_depth_below_baseline = *depth_below_baseline; - *block_size_above_baseline = parent_text_block_start; - *depth_below_baseline = fragment_block_size - *block_size_above_baseline; - offset_from_baseline = offset_from_baseline + *depth_below_baseline - - prev_depth_below_baseline - }, - vertical_align::T::text_bottom => { - let fragment_block_size = *block_size_above_baseline + - *depth_below_baseline; - let prev_depth_below_baseline = *depth_below_baseline; - *depth_below_baseline = parent_text_block_end; - *block_size_above_baseline = fragment_block_size - *depth_below_baseline; - offset_from_baseline = offset_from_baseline + *depth_below_baseline - - prev_depth_below_baseline - }, - vertical_align::T::top => { - if !largest_size_updated { - largest_size_updated = true; - *largest_block_size_for_top_fragments = - max(*largest_block_size_for_top_fragments, - *block_size_above_baseline + *depth_below_baseline); - offset_from_baseline = offset_from_baseline + - *block_size_above_baseline - } - }, - vertical_align::T::bottom => { - if !largest_size_updated { - largest_size_updated = true; - *largest_block_size_for_bottom_fragments = - max(*largest_block_size_for_bottom_fragments, - *block_size_above_baseline + *depth_below_baseline); - offset_from_baseline = offset_from_baseline - *depth_below_baseline - } - }, - vertical_align::T::LengthOrPercentage(LengthOrPercentage::Length(length)) => { - offset_from_baseline = offset_from_baseline - length - } - vertical_align::T::LengthOrPercentage(LengthOrPercentage::Percentage(p)) => { - let line_height = fragment.calculate_line_height(layout_context); - let percent_offset = line_height.scale_by(p); - offset_from_baseline = offset_from_baseline - percent_offset - } - vertical_align::T::LengthOrPercentage(LengthOrPercentage::Calc(calc)) => { - let line_height = fragment.calculate_line_height(layout_context); - let percent_offset = line_height.scale_by(calc.percentage()); - offset_from_baseline = offset_from_baseline - percent_offset - calc.length() - } - } - } - (offset_from_baseline - ascent, largest_size_updated) - } - /// Sets fragment positions in the inline direction based on alignment for one line. This /// performs text justification if mandated by the style. fn set_inline_fragment_positions(fragments: &mut InlineFragments, @@ -1120,13 +1031,12 @@ impl InlineFlow { } } - /// Sets final fragment positions in the block direction for one line. Assumes that the - /// fragment positions were initially set to the distance from the baseline first. + /// Sets final fragment positions in the block direction for one line. fn set_block_fragment_positions(fragments: &mut InlineFragments, line: &Line, - line_distance_from_flow_block_start: Au, - baseline_distance_from_block_start: Au, - largest_depth_below_baseline: Au) { + minimum_block_size_above_baseline: Au, + minimum_depth_below_baseline: Au, + layout_context: &LayoutContext) { for fragment_index in line.range.each_index() { // If any of the inline styles say `top` or `bottom`, adjust the vertical align // appropriately. @@ -1134,42 +1044,63 @@ impl InlineFlow { // FIXME(#5624, pcwalton): This passes our current reftests but isn't the right thing // to do. let fragment = fragments.get_mut(fragment_index.to_usize()); - let mut vertical_align = vertical_align::T::baseline; + let fragment_inline_metrics = fragment.inline_metrics(layout_context); + let mut block_start = line.bounds.start.b + + line.inline_metrics.block_size_above_baseline - + fragment_inline_metrics.ascent; + for style in fragment.inline_styles() { - match (style.get_box().display, style.get_box().vertical_align) { - (display::T::inline, vertical_align::T::top) | - (display::T::block, vertical_align::T::top) | - (display::T::inline_block, vertical_align::T::top) => { - vertical_align = vertical_align::T::top; - break + match style.get_box().vertical_align { + vertical_align::T::baseline => {} + vertical_align::T::middle => {} + vertical_align::T::sub => { + let sub_offset = + (minimum_block_size_above_baseline + + minimum_depth_below_baseline).scale_by(FONT_SUBSCRIPT_OFFSET_RATIO); + block_start = block_start + sub_offset } - (display::T::inline, vertical_align::T::bottom) | - (display::T::block, vertical_align::T::bottom) | - (display::T::inline_block, vertical_align::T::bottom) => { - vertical_align = vertical_align::T::bottom; - break + vertical_align::T::super_ => { + let super_offset = + (minimum_block_size_above_baseline + + minimum_depth_below_baseline).scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO); + block_start = block_start - super_offset + } + vertical_align::T::text_top => { + block_start = line.bounds.start.b + + line.inline_metrics.block_size_above_baseline - + minimum_block_size_above_baseline + } + vertical_align::T::text_bottom => { + block_start = line.bounds.start.b + + line.inline_metrics.block_size_above_baseline + + minimum_depth_below_baseline - + fragment.border_box.size.block + } + vertical_align::T::top => { + block_start = line.bounds.start.b + } + vertical_align::T::bottom => { + block_start = line.bounds.start.b + line.bounds.size.block - + fragment.border_box.size.block + } + vertical_align::T::LengthOrPercentage(LengthOrPercentage::Length(length)) => { + block_start = block_start - length + } + vertical_align::T::LengthOrPercentage(LengthOrPercentage::Percentage( + percentage)) => { + let line_height = fragment.calculate_line_height(layout_context); + let length = line_height.scale_by(percentage); + block_start = block_start - length + } + vertical_align::T::LengthOrPercentage(LengthOrPercentage::Calc(calc)) => { + let line_height = fragment.calculate_line_height(layout_context); + let percentage_length = line_height.scale_by(calc.percentage()); + block_start = block_start - percentage_length - calc.length() } - _ => {} - } - } - - match vertical_align { - vertical_align::T::top => { - fragment.border_box.start.b = fragment.border_box.start.b + - line_distance_from_flow_block_start - } - vertical_align::T::bottom => { - fragment.border_box.start.b = fragment.border_box.start.b + - line_distance_from_flow_block_start + - baseline_distance_from_block_start + - largest_depth_below_baseline; - } - _ => { - fragment.border_box.start.b = fragment.border_box.start.b + - line_distance_from_flow_block_start + baseline_distance_from_block_start } } + fragment.border_box.start.b = block_start; fragment.update_late_computed_block_position_if_necessary(); } } @@ -1193,31 +1124,88 @@ impl InlineFlow { let line_height = text::line_height_from_style(style, &font_metrics); let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height); - let mut block_size_above_baseline = inline_metrics.block_size_above_baseline; - let mut depth_below_baseline = inline_metrics.depth_below_baseline; + let mut block_size_above_baseline = Au(0); + let mut depth_below_baseline = Au(i32::MIN); + let mut largest_block_size_for_top_fragments = Au(0); + let mut largest_block_size_for_bottom_fragments = Au(0); + + // We use `vertical_align::T::baseline` here because `vertical-align` must not apply to + // the inside of inline blocks. + update_inline_metrics(&inline_metrics, + style.get_box().display, + vertical_align::T::baseline, + &mut block_size_above_baseline, + &mut depth_below_baseline, + &mut largest_block_size_for_top_fragments, + &mut largest_block_size_for_bottom_fragments); // According to CSS 2.1 § 10.8, `line-height` of any inline element specifies the minimal // height of line boxes within the element. for frag in &self.fragments.fragments { - match frag.inline_context { - Some(ref inline_context) => { - for node in &inline_context.nodes { - let font_style = node.style.get_font_arc(); - let font_metrics = text::font_metrics_for_style(font_context, font_style); - let line_height = text::line_height_from_style(&*node.style, &font_metrics); - let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, - line_height); - block_size_above_baseline = max(block_size_above_baseline, - inline_metrics.block_size_above_baseline); - depth_below_baseline = max(depth_below_baseline, - inline_metrics.depth_below_baseline); - } + if let Some(ref inline_context) = frag.inline_context { + for node in &inline_context.nodes { + let font_style = node.style.get_font_arc(); + let font_metrics = text::font_metrics_for_style(font_context, font_style); + let line_height = text::line_height_from_style(&*node.style, &font_metrics); + let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, + line_height); + + update_inline_metrics(&inline_metrics, + node.style.get_box().display, + node.style.get_box().vertical_align, + &mut block_size_above_baseline, + &mut depth_below_baseline, + &mut largest_block_size_for_top_fragments, + &mut largest_block_size_for_bottom_fragments); + } - None => {} } } - (block_size_above_baseline, depth_below_baseline) + block_size_above_baseline = + max(block_size_above_baseline, + largest_block_size_for_bottom_fragments - max(depth_below_baseline, Au(0))); + depth_below_baseline = + max(depth_below_baseline, + largest_block_size_for_top_fragments - block_size_above_baseline); + + return (block_size_above_baseline, depth_below_baseline); + + fn update_inline_metrics(inline_metrics: &InlineMetrics, + display_value: display::T, + vertical_align_value: vertical_align::T, + block_size_above_baseline: &mut Au, + depth_below_baseline: &mut Au, + largest_block_size_for_top_fragments: &mut Au, + largest_block_size_for_bottom_fragments: &mut Au) { + match (display_value, vertical_align_value) { + (display::T::inline, vertical_align::T::top) | + (display::T::block, vertical_align::T::top) | + (display::T::inline_block, vertical_align::T::top) if + inline_metrics.block_size_above_baseline >= Au(0) => { + *largest_block_size_for_top_fragments = + max(*largest_block_size_for_top_fragments, + inline_metrics.block_size_above_baseline + + inline_metrics.depth_below_baseline) + } + (display::T::inline, vertical_align::T::bottom) | + (display::T::block, vertical_align::T::bottom) | + (display::T::inline_block, vertical_align::T::bottom) if + inline_metrics.depth_below_baseline >= Au(0) => { + *largest_block_size_for_bottom_fragments = + max(*largest_block_size_for_bottom_fragments, + inline_metrics.block_size_above_baseline + + inline_metrics.depth_below_baseline) + } + _ => { + *block_size_above_baseline = + max(*block_size_above_baseline, + inline_metrics.block_size_above_baseline); + *depth_below_baseline = max(*depth_below_baseline, + inline_metrics.depth_below_baseline); + } + } + } } fn update_restyle_damage(&mut self) { @@ -1468,11 +1456,8 @@ impl Flow for InlineFlow { // Now, go through each line and lay out the fragments inside. - let mut line_distance_from_flow_block_start = Au(0); let line_count = self.lines.len(); - for line_index in 0..line_count { - let line = &mut self.lines[line_index]; - + for (line_index, line) in self.lines.iter_mut().enumerate() { // Lay out fragments in the inline direction, and justify them if necessary. InlineFlow::set_inline_fragment_positions(&mut self.fragments, line, @@ -1480,108 +1465,17 @@ impl Flow for InlineFlow { indentation, line_index + 1 == line_count); - // Set the block-start position of the current line. - // `line_height_offset` is updated at the end of the previous loop. - line.bounds.start.b = line_distance_from_flow_block_start; - - // Calculate the distance from the baseline to the block-start and block-end of the - // line. - let mut largest_block_size_above_baseline = self.minimum_block_size_above_baseline; - let mut largest_depth_below_baseline = self.minimum_depth_below_baseline; - - // Calculate the largest block-size among fragments with 'top' and 'bottom' values - // respectively. - let (mut largest_block_size_for_top_fragments, - mut largest_block_size_for_bottom_fragments) = (Au(0), Au(0)); - - for fragment_index in line.range.each_index() { - let fragment = &mut self.fragments.fragments[fragment_index.to_usize()]; - - let InlineMetrics { - mut block_size_above_baseline, - mut depth_below_baseline, - ascent - } = fragment.inline_metrics(layout_context); - - // To calculate text-top and text-bottom value when `vertical-align` is involved, - // we should find the top and bottom of the content area of the parent fragment. - // "Content area" is defined in CSS 2.1 § 10.6.1. - // - // TODO: We should extract em-box info from the font size of the parent and - // calculate the distances from the baseline to the block-start and the block-end - // of the parent's content area. - - // We should calculate the distance from baseline to the top of parent's content - // area. But for now we assume it's the font size. - // - // CSS 2.1 does not state which font to use. This version of the code uses - // the parent's font. - - // Calculate the final block-size above the baseline for this fragment. - // - // The no-update flag decides whether `largest_block_size_for_top_fragments` and - // `largest_block_size_for_bottom_fragments` are to be updated or not. This will be - // set if and only if the fragment has `vertical-align` set to `top` or `bottom`. - let (distance_from_baseline, no_update_flag) = - InlineFlow::distance_from_baseline( - fragment, - ascent, - self.minimum_block_size_above_baseline, - self.minimum_depth_below_baseline, - &mut block_size_above_baseline, - &mut depth_below_baseline, - &mut largest_block_size_for_top_fragments, - &mut largest_block_size_for_bottom_fragments, - layout_context); - - // Unless the current fragment has `vertical-align` set to `top` or `bottom`, - // `largest_block_size_above_baseline` and `largest_depth_below_baseline` are - // updated. - if !no_update_flag { - largest_block_size_above_baseline = max(block_size_above_baseline, - largest_block_size_above_baseline); - largest_depth_below_baseline = max(depth_below_baseline, - largest_depth_below_baseline); - } - - // Temporarily use `fragment.border_box.start.b` to mean "the distance from the - // baseline". We will assign the real value later. - fragment.border_box.start.b = distance_from_baseline - } - - // Calculate the distance from the baseline to the top of the largest fragment with a - // value for `bottom`. Then, if necessary, update `largest_block-size_above_baseline`. - largest_block_size_above_baseline = - max(largest_block_size_above_baseline, - largest_block_size_for_bottom_fragments - largest_depth_below_baseline); - - // Calculate the distance from baseline to the bottom of the largest fragment with a - // value for `top`. Then, if necessary, update `largest_depth_below_baseline`. - largest_depth_below_baseline = - max(largest_depth_below_baseline, - largest_block_size_for_top_fragments - largest_block_size_above_baseline); - - // Now, the distance from the logical block-start of the line to the baseline can be - // computed as `largest_block-size_above_baseline`. - let baseline_distance_from_block_start = largest_block_size_above_baseline; - - // Compute the final positions in the block direction of each fragment. Recall that - // `fragment.border_box.start.b` was set to the distance from the baseline above. + // Compute the final positions in the block direction of each fragment. InlineFlow::set_block_fragment_positions(&mut self.fragments, line, - line_distance_from_flow_block_start, - baseline_distance_from_block_start, - largest_depth_below_baseline); + self.minimum_block_size_above_baseline, + self.minimum_depth_below_baseline, + layout_context); - // This is used to set the block-start position of the next line in the next loop. - line.bounds.size.block = largest_block_size_above_baseline + - largest_depth_below_baseline; - line_distance_from_flow_block_start = line_distance_from_flow_block_start + - line.bounds.size.block; - - // We're no longer on the first line, so set indentation to zero. + // This is used to set the block-start position of the next line in the next iteration + // of the loop. We're no longer on the first line, so set indentation to zero. indentation = Au(0) - } // End of `lines.iter_mut()` loop. + } // Assign block sizes for any inline-block descendants. let thread_id = self.base.thread_id; @@ -1968,22 +1862,6 @@ impl InlineMetrics { } } - /// Calculates inline metrics from font metrics and line block-size per CSS 2.1 § 10.8.1. - #[inline] - pub fn from_block_height(font_metrics: &FontMetrics, - block_height: Au, - block_start_margin: Au, - block_end_margin: Au) - -> InlineMetrics { - let leading = block_height + block_start_margin + block_end_margin - - (font_metrics.ascent + font_metrics.descent); - InlineMetrics { - block_size_above_baseline: font_metrics.ascent + leading.scale_by(0.5), - depth_below_baseline: font_metrics.descent + leading.scale_by(0.5), - ascent: font_metrics.ascent + leading.scale_by(0.5) - block_start_margin, - } - } - pub fn block_size(&self) -> Au { self.block_size_above_baseline + self.depth_below_baseline } From 9e1935c2eda5a3b16c7e7daef469274aebf0b76f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 27 Apr 2016 16:48:07 -0700 Subject: [PATCH 05/12] =?UTF-8?q?layout:=20Make=20the=20hypothetical=20box?= =?UTF-8?q?=20for=20`display:=20inline-block`=20with=20`position:=20absolu?= =?UTF-8?q?te`=20inline=20per=20CSS=202.1=20=C2=A7=2010.3.7.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/layout/construct.rs | 12 +++++----- tests/wpt/mozilla/meta/MANIFEST.json | 24 +++++++++++++++++++ .../inline_block_absolute_hypothetical_a.html | 12 ++++++++++ ...nline_block_absolute_hypothetical_ref.html | 11 +++++++++ 4 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 tests/wpt/mozilla/tests/css/inline_block_absolute_hypothetical_a.html create mode 100644 tests/wpt/mozilla/tests/css/inline_block_absolute_hypothetical_ref.html diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 873be2b2743..7985fac3f73 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -1519,11 +1519,10 @@ impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal { let style = node.style(self.style_context()); - let munged_display = if style.get_box()._servo_display_for_hypothetical_box == - display::T::inline { - display::T::inline - } else { - style.get_box().display + let original_display = style.get_box()._servo_display_for_hypothetical_box; + let munged_display = match original_display { + display::T::inline | display::T::inline_block => original_display, + _ => style.get_box().display, }; (munged_display, style.get_box().float, style.get_box().position) } @@ -1577,7 +1576,8 @@ impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal { + (display::T::inline, _, position::T::absolute) | + (display::T::inline_block, _, position::T::absolute) => { let construction_result = self.build_fragment_for_absolutely_positioned_inline(node); self.set_flow_construction_result(node, construction_result) diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index a4604810b29..4d17eef015a 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -2280,6 +2280,18 @@ "url": "/_mozilla/css/inline_background_a.html" } ], + "css/inline_block_absolute_hypothetical_a.html": [ + { + "path": "css/inline_block_absolute_hypothetical_a.html", + "references": [ + [ + "/_mozilla/css/inline_block_absolute_hypothetical_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/inline_block_absolute_hypothetical_a.html" + } + ], "css/inline_block_baseline_a.html": [ { "path": "css/inline_block_baseline_a.html", @@ -9044,6 +9056,18 @@ "url": "/_mozilla/css/inline_background_a.html" } ], + "css/inline_block_absolute_hypothetical_a.html": [ + { + "path": "css/inline_block_absolute_hypothetical_a.html", + "references": [ + [ + "/_mozilla/css/inline_block_absolute_hypothetical_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/inline_block_absolute_hypothetical_a.html" + } + ], "css/inline_block_baseline_a.html": [ { "path": "css/inline_block_baseline_a.html", diff --git a/tests/wpt/mozilla/tests/css/inline_block_absolute_hypothetical_a.html b/tests/wpt/mozilla/tests/css/inline_block_absolute_hypothetical_a.html new file mode 100644 index 00000000000..612a922a4f9 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/inline_block_absolute_hypothetical_a.html @@ -0,0 +1,12 @@ + + + + +Hello world! + diff --git a/tests/wpt/mozilla/tests/css/inline_block_absolute_hypothetical_ref.html b/tests/wpt/mozilla/tests/css/inline_block_absolute_hypothetical_ref.html new file mode 100644 index 00000000000..2dd7f39da41 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/inline_block_absolute_hypothetical_ref.html @@ -0,0 +1,11 @@ + + + +Hello world! + From 0198a9dda84cf3e07603982489a1d55c415f9da9 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 27 Apr 2016 12:50:43 -0700 Subject: [PATCH 06/12] layout: Include float information in flows' debug output. --- components/layout/flow.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/layout/flow.rs b/components/layout/flow.rs index a77f636415b..05e5814d0e0 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -980,9 +980,11 @@ impl fmt::Debug for BaseFlow { }; write!(f, - "sc={:?} pos={:?}, floatspec-in={:?}, floatspec-out={:?}, overflow={:?}{}{}{}", + "sc={:?} pos={:?}, {}{} floatspec-in={:?}, floatspec-out={:?}, overflow={:?}{}{}{}", self.stacking_context_id, self.position, + if self.flags.contains(FLOATS_LEFT) { "FL" } else { "" }, + if self.flags.contains(FLOATS_RIGHT) { "FR" } else { "" }, self.speculated_float_placement_in, self.speculated_float_placement_out, self.overflow, From 4ae6a68e35934c634e5a2487eb9ac9eed5c81fcc Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 27 Apr 2016 12:51:31 -0700 Subject: [PATCH 07/12] layout: Fix non-vertical-align-correct wording in a comment. --- components/layout/block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/layout/block.rs b/components/layout/block.rs index c6fedc043bb..53094ffd311 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -1474,7 +1474,7 @@ impl BlockFlow { } } - /// Computes intrinsic widths for a block. + /// Computes intrinsic inline sizes for a block. pub fn bubble_inline_sizes_for_block(&mut self, consult_children: bool) { let _scope = layout_debug_scope!("block::bubble_inline_sizes {:x}", self.base.debug_id()); From d2511e33c676e57f270d5fe0bf9c3a079bd6ace1 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 27 Apr 2016 12:54:05 -0700 Subject: [PATCH 08/12] layout: When printing out debug info for the float list, include information about each float. --- components/layout/floats.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/layout/floats.rs b/components/layout/floats.rs index e4489f4288d..9a5d1521d58 100644 --- a/components/layout/floats.rs +++ b/components/layout/floats.rs @@ -79,7 +79,11 @@ impl FloatList { impl fmt::Debug for FloatList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "max_block_start={:?} floats={}", self.max_block_start, self.floats.len()) + try!(write!(f, "max_block_start={:?} floats={}", self.max_block_start, self.floats.len())); + for float in self.floats.iter() { + try!(write!(f, " {:?}", float)); + } + Ok(()) } } From 2bef5606bdec51daf2f5b942cb126e091bc8cc3c Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 27 Apr 2016 12:55:01 -0700 Subject: [PATCH 09/12] layout: Minor whitespace cleanup. --- components/layout/construct.rs | 5 +++-- components/layout/fragment.rs | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 7985fac3f73..dc6ca7b3cb1 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -686,7 +686,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> /// FIXME(pcwalton): It is not clear to me that there isn't a cleaner way to handle /// `