diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 6f8ef66edf6..5ebdc225a7a 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -651,7 +651,7 @@ impl<'a> FlowConstructor<'a> { /// `InlineFragmentsConstructionResult` if this node consisted entirely of ignorable /// whitespace. fn build_fragments_for_nonreplaced_inline_content(&mut self, node: &ThreadSafeLayoutNode) - -> ConstructionResult { + -> ConstructionResult { let mut opt_inline_block_splits: LinkedList = LinkedList::new(); let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); let mut abs_descendants = Descendants::new(); @@ -760,13 +760,10 @@ impl<'a> FlowConstructor<'a> { node.restyle_damage())) } - // If the value of `display` property is not `inline`, then we have a situation like - // `
foo bar baz
`. The fragments for `foo`, `bar`, and - // `baz` had better not be absolutely positioned! + // Modify the style as necessary. (See the comment in + // `properties::modify_style_for_replaced_content()`.) let mut style = (*node.style()).clone(); - if style.get_box().display != display::T::inline { - style = Arc::new(properties::make_inline(&*style)) - } + properties::modify_style_for_replaced_content(&mut style); // If this is generated content, then we need to initialize the accumulator with the // fragment corresponding to that content. Otherwise, just initialize with the ordinary diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index b766b27752e..df34bf39234 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -42,9 +42,8 @@ use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use string_cache::Atom; use style::computed_values::content::ContentItem; -use style::computed_values::{clear, mix_blend_mode, overflow_wrap}; -use style::computed_values::{position, text_align, text_decoration, vertical_align, white_space}; -use style::computed_values::{word_break}; +use style::computed_values::{clear, mix_blend_mode, overflow_wrap, position, text_align}; +use style::computed_values::{text_decoration, white_space, word_break}; use style::node::{TElement, TNode}; use style::properties::{ComputedValues, cascade_anonymous, make_border}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; @@ -108,8 +107,8 @@ pub struct Fragment { /// Info specific to the kind of fragment. Keep this enum small. pub specific: SpecificFragmentInfo, - /// Holds the style context information for fragments - /// that are part of an inline formatting context. + /// Holds the style context information for fragments that are part of an inline formatting + /// context. pub inline_context: Option, /// How damaged this fragment is since last reflow. @@ -815,8 +814,8 @@ impl Fragment { self.restyle_damage | self.specific.restyle_damage() } - /// Adds a style to the inline context for this fragment. If the inline - /// context doesn't exist yet, it will be created. + /// Adds a style to the inline context for this fragment. If the inline context doesn't exist + /// yet, it will be created. pub fn add_inline_context_style(&mut self, style: Arc, first_frag: bool, @@ -1070,16 +1069,14 @@ impl Fragment { LogicalSize::zero(self.style.writing_mode) }; - match self.inline_context { - None => {} - Some(ref inline_fragment_context) => { - for style in inline_fragment_context.styles.iter() { - if style.get_box().position == position::T::relative { - rel_pos = rel_pos + from_style(&**style, containing_block_size); - } + if let Some(ref inline_fragment_context) = self.inline_context { + for style in inline_fragment_context.styles.iter() { + if style.get_box().position == position::T::relative { + rel_pos = rel_pos + from_style(&**style, containing_block_size); } - }, + } } + rel_pos } @@ -1108,10 +1105,6 @@ impl Fragment { self.style().get_inheritedtext().text_align } - pub fn vertical_align(&self) -> vertical_align::T { - self.style().get_box().vertical_align - } - pub fn white_space(&self) -> white_space::T { self.style().get_inheritedtext().white_space } @@ -1218,16 +1211,13 @@ impl Fragment { // Take borders and padding for parent inline fragments into account, if necessary. if self.is_primary_fragment() { - match self.inline_context { - None => {} - Some(ref context) => { - for style in context.styles.iter() { - let border_width = style.logical_border_width().inline_start_end(); - let padding_inline_size = - model::padding_from_style(&**style, Au(0)).inline_start_end(); - result.surrounding_size = result.surrounding_size + border_width + - padding_inline_size; - } + if let Some(ref context) = self.inline_context { + for style in context.styles.iter() { + let border_width = style.logical_border_width().inline_start_end(); + let padding_inline_size = + model::padding_from_style(&**style, Au(0)).inline_start_end(); + result.surrounding_size = result.surrounding_size + border_width + + padding_inline_size; } } } @@ -1986,6 +1976,10 @@ impl Fragment { scanned_text_fragment_info.range.adjust_by(CharIndex(leading_whitespace_character_count), -CharIndex(leading_whitespace_character_count)); } + + pub fn inline_styles<'a>(&'a self) -> InlineStyleIterator<'a> { + InlineStyleIterator::new(self) + } } impl fmt::Debug for Fragment { @@ -2040,3 +2034,40 @@ pub enum CoordinateSystem { Own, } +pub struct InlineStyleIterator<'a> { + fragment: &'a Fragment, + inline_style_index: usize, + primary_style_yielded: bool, +} + +impl<'a> Iterator for InlineStyleIterator<'a> { + type Item = &'a ComputedValues; + + fn next(&mut self) -> Option<&'a ComputedValues> { + if !self.primary_style_yielded { + self.primary_style_yielded = true; + return Some(&*self.fragment.style) + } + let inline_context = match self.fragment.inline_context { + None => return None, + Some(ref inline_context) => inline_context, + }; + let inline_style_index = self.inline_style_index; + if inline_style_index == inline_context.styles.len() { + return None + } + self.inline_style_index += 1; + Some(&*inline_context.styles[inline_style_index]) + } +} + +impl<'a> InlineStyleIterator<'a> { + fn new<'b>(fragment: &'b Fragment) -> InlineStyleIterator<'b> { + InlineStyleIterator { + fragment: fragment, + inline_style_index: 0, + primary_style_yielded: false, + } + } +} + diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 1f9e59dba2e..aa3d0d91bda 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -24,13 +24,14 @@ use gfx::text::glyph::CharIndex; use gfx::text::text_run::TextRun; use std::cmp::max; use std::fmt; +use std::iter; use std::mem; use std::num::ToPrimitive; use std::ops::{Add, Sub, Mul, Div, Rem, Neg, Shl, Shr, Not, BitOr, BitAnd, BitXor}; use std::sync::Arc; use std::u16; -use style::computed_values::{overflow_x, text_align, text_justify, text_overflow, vertical_align}; -use style::computed_values::{white_space}; +use style::computed_values::{display, overflow_x, text_align, text_justify, text_overflow}; +use style::computed_values::{vertical_align, white_space}; use style::properties::ComputedValues; use util::geometry::{Au, MAX_AU, ZERO_RECT}; use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; @@ -782,59 +783,80 @@ impl InlineFlow { largest_block_size_for_bottom_fragments: &mut Au, layout_context: &LayoutContext) -> (Au, bool) { - match fragment.vertical_align() { - vertical_align::T::baseline => (-ascent, false), - vertical_align::T::middle => { - // TODO: x-height value should be used from font info. - // TODO: The code below passes our current reftests but doesn't work in all - // situations. Add vertical align reftests and fix this. - (-ascent, false) - }, - vertical_align::T::sub => { - let sub_offset = (parent_text_block_start + parent_text_block_end) - .scale_by(FONT_SUBSCRIPT_OFFSET_RATIO); - (sub_offset - ascent, false) - }, - vertical_align::T::super_ => { - let super_offset = (parent_text_block_start + parent_text_block_end) - .scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO); - (-super_offset - ascent, false) - }, - 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; - (*depth_below_baseline - prev_depth_below_baseline - ascent, false) - }, - 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; - (*depth_below_baseline - prev_depth_below_baseline - ascent, false) - }, - vertical_align::T::top => { - *largest_block_size_for_top_fragments = - max(*largest_block_size_for_top_fragments, - *block_size_above_baseline + *depth_below_baseline); - let offset_top = *block_size_above_baseline - ascent; - (offset_top, true) - }, - vertical_align::T::bottom => { - *largest_block_size_for_bottom_fragments = - max(*largest_block_size_for_bottom_fragments, - *block_size_above_baseline + *depth_below_baseline); - let offset_bottom = -(*depth_below_baseline + ascent); - (offset_bottom, true) - }, - vertical_align::T::Length(length) => (-(length + ascent), false), - vertical_align::T::Percentage(p) => { - let line_height = fragment.calculate_line_height(layout_context); - let percent_offset = line_height.scale_by(p); - (-(percent_offset + ascent), false) + 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(); + if box_style.display != display::T::inline && + box_style.display != display::T::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::Length(length) => { + offset_from_baseline = offset_from_baseline - length + } + vertical_align::T::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 + } } } + (offset_from_baseline - ascent, largest_size_updated) } /// Sets fragment positions in the inline direction based on alignment for one line. This @@ -962,22 +984,46 @@ impl InlineFlow { baseline_distance_from_block_start: Au, largest_depth_below_baseline: Au) { for fragment_index in range(line.range.begin(), line.range.end()) { + // If any of the inline styles say `top` or `bottom`, adjust the vertical align + // appropriately. + // + // 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()); - match fragment.vertical_align() { + let mut vertical_align = vertical_align::T::baseline; + 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) => { + vertical_align = vertical_align::T::top; + break + } + (display::T::inline, vertical_align::T::bottom) | + (display::T::block, vertical_align::T::bottom) => { + vertical_align = vertical_align::T::bottom; + break + } + _ => {} + } + } + + 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 + 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.update_late_computed_block_position_if_necessary(); } } diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 2b2c74a2495..26e0bdb0f44 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -5239,13 +5239,27 @@ pub fn cascade_anonymous(parent_style: &ComputedValues) -> ComputedValues { result } -/// Sets `display` to `inline` and `position` to `static`. +/// Alters the given style to accommodate replaced content. This is called in flow construction. It +/// handles cases like `
foo bar baz
` (in which `foo`, `bar`, +/// and `baz` must not be absolutely-positioned) and cases like `Foo` (in which the +/// `vertical-align: top` style of `sup` must not propagate down into `Foo`). +/// +/// FIXME(#5625, pcwalton): It would probably be cleaner and faster to do this in the cascade. #[inline] -pub fn make_inline(style: &ComputedValues) -> ComputedValues { - let mut style = (*style).clone(); - style.box_.make_unique().display = longhands::display::computed_value::T::inline; - style.box_.make_unique().position = longhands::position::computed_value::T::static_; - style +pub fn modify_style_for_replaced_content(style: &mut Arc) { + // Reset `position` to handle cases like `
foo bar baz
`. + if style.box_.display != longhands::display::computed_value::T::inline { + let mut style = style.make_unique(); + style.box_.make_unique().display = longhands::display::computed_value::T::inline; + style.box_.make_unique().position = longhands::position::computed_value::T::static_; + } + + // Reset `vertical-align` to handle cases like `foo`. + if style.box_.vertical_align != longhands::vertical_align::computed_value::T::baseline { + let mut style = style.make_unique(); + style.box_.make_unique().vertical_align = + longhands::vertical_align::computed_value::T::baseline + } } /// Sets `border_${side}_width` to the passed in values. diff --git a/tests/ref/basic.list b/tests/ref/basic.list index c1189069cc6..384b5d6225d 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -310,8 +310,10 @@ experimental == rtl_simple.html rtl_simple_ref.html == upper_id_attr.html upper_id_attr_ref.html flaky_cpu,experimental == vertical-lr-blocks.html vertical-lr-blocks_ref.html == vertical_align_bottom_a.html vertical_align_bottom_ref.html +== vertical_align_inside_table_a.html vertical_align_inside_table_ref.html == vertical_align_sub_a.html vertical_align_sub_ref.html == vertical_align_super_a.html vertical_align_super_ref.html +== vertical_align_super_nested_a.html vertical_align_super_nested_ref.html == vertical_align_text_bottom_a.html vertical_align_text_bottom_ref.html == vertical_align_text_top_a.html vertical_align_text_top_ref.html == vertical_align_top_a.html vertical_align_top_ref.html diff --git a/tests/ref/vertical_align_inside_table_a.html b/tests/ref/vertical_align_inside_table_a.html new file mode 100644 index 00000000000..03408aad19a --- /dev/null +++ b/tests/ref/vertical_align_inside_table_a.html @@ -0,0 +1,20 @@ + + + + + + + + + +
What? What?
+ + + diff --git a/tests/ref/vertical_align_inside_table_ref.html b/tests/ref/vertical_align_inside_table_ref.html new file mode 100644 index 00000000000..96694013209 --- /dev/null +++ b/tests/ref/vertical_align_inside_table_ref.html @@ -0,0 +1,15 @@ + + + + + + + + +
What? What?
+ + + diff --git a/tests/ref/vertical_align_super_nested_a.html b/tests/ref/vertical_align_super_nested_a.html new file mode 100644 index 00000000000..494a3940d1b --- /dev/null +++ b/tests/ref/vertical_align_super_nested_a.html @@ -0,0 +1,15 @@ + + + + + + + +

Yo

+ + + diff --git a/tests/ref/vertical_align_super_nested_ref.html b/tests/ref/vertical_align_super_nested_ref.html new file mode 100644 index 00000000000..c4165536541 --- /dev/null +++ b/tests/ref/vertical_align_super_nested_ref.html @@ -0,0 +1,10 @@ + + + + + + +

Yo

+ + +