Auto merge of #5623 - pcwalton:nested-inline-vertical-align, r=glennw

This allows things like `<sup><span>Foo</span></sup>` to work and
improves Wikipedia.

r? @glennw
This commit is contained in:
bors-servo 2015-04-09 17:20:23 -05:00
commit d7b6961104
9 changed files with 248 additions and 98 deletions

View file

@ -651,7 +651,7 @@ impl<'a> FlowConstructor<'a> {
/// `InlineFragmentsConstructionResult` if this node consisted entirely of ignorable /// `InlineFragmentsConstructionResult` if this node consisted entirely of ignorable
/// whitespace. /// whitespace.
fn build_fragments_for_nonreplaced_inline_content(&mut self, node: &ThreadSafeLayoutNode) fn build_fragments_for_nonreplaced_inline_content(&mut self, node: &ThreadSafeLayoutNode)
-> ConstructionResult { -> ConstructionResult {
let mut opt_inline_block_splits: LinkedList<InlineBlockSplit> = LinkedList::new(); let mut opt_inline_block_splits: LinkedList<InlineBlockSplit> = LinkedList::new();
let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node);
let mut abs_descendants = Descendants::new(); let mut abs_descendants = Descendants::new();
@ -760,13 +760,10 @@ impl<'a> FlowConstructor<'a> {
node.restyle_damage())) node.restyle_damage()))
} }
// If the value of `display` property is not `inline`, then we have a situation like // Modify the style as necessary. (See the comment in
// `<div style="position:absolute">foo bar baz</div>`. The fragments for `foo`, `bar`, and // `properties::modify_style_for_replaced_content()`.)
// `baz` had better not be absolutely positioned!
let mut style = (*node.style()).clone(); let mut style = (*node.style()).clone();
if style.get_box().display != display::T::inline { properties::modify_style_for_replaced_content(&mut style);
style = Arc::new(properties::make_inline(&*style))
}
// If this is generated content, then we need to initialize the accumulator with the // 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 // fragment corresponding to that content. Otherwise, just initialize with the ordinary

View file

@ -42,9 +42,8 @@ use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use string_cache::Atom; use string_cache::Atom;
use style::computed_values::content::ContentItem; use style::computed_values::content::ContentItem;
use style::computed_values::{clear, mix_blend_mode, overflow_wrap}; use style::computed_values::{clear, mix_blend_mode, overflow_wrap, position, text_align};
use style::computed_values::{position, text_align, text_decoration, vertical_align, white_space}; use style::computed_values::{text_decoration, white_space, word_break};
use style::computed_values::{word_break};
use style::node::{TElement, TNode}; use style::node::{TElement, TNode};
use style::properties::{ComputedValues, cascade_anonymous, make_border}; use style::properties::{ComputedValues, cascade_anonymous, make_border};
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
@ -108,8 +107,8 @@ pub struct Fragment {
/// Info specific to the kind of fragment. Keep this enum small. /// Info specific to the kind of fragment. Keep this enum small.
pub specific: SpecificFragmentInfo, pub specific: SpecificFragmentInfo,
/// Holds the style context information for fragments /// Holds the style context information for fragments that are part of an inline formatting
/// that are part of an inline formatting context. /// context.
pub inline_context: Option<InlineFragmentContext>, pub inline_context: Option<InlineFragmentContext>,
/// How damaged this fragment is since last reflow. /// How damaged this fragment is since last reflow.
@ -815,8 +814,8 @@ impl Fragment {
self.restyle_damage | self.specific.restyle_damage() self.restyle_damage | self.specific.restyle_damage()
} }
/// Adds a style to the inline context for this fragment. If the inline /// Adds a style to the inline context for this fragment. If the inline context doesn't exist
/// context doesn't exist yet, it will be created. /// yet, it will be created.
pub fn add_inline_context_style(&mut self, pub fn add_inline_context_style(&mut self,
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
first_frag: bool, first_frag: bool,
@ -1070,16 +1069,14 @@ impl Fragment {
LogicalSize::zero(self.style.writing_mode) LogicalSize::zero(self.style.writing_mode)
}; };
match self.inline_context { if let Some(ref inline_fragment_context) = self.inline_context {
None => {} for style in inline_fragment_context.styles.iter() {
Some(ref inline_fragment_context) => { if style.get_box().position == position::T::relative {
for style in inline_fragment_context.styles.iter() { rel_pos = rel_pos + from_style(&**style, containing_block_size);
if style.get_box().position == position::T::relative {
rel_pos = rel_pos + from_style(&**style, containing_block_size);
}
} }
}, }
} }
rel_pos rel_pos
} }
@ -1108,10 +1105,6 @@ impl Fragment {
self.style().get_inheritedtext().text_align 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 { pub fn white_space(&self) -> white_space::T {
self.style().get_inheritedtext().white_space self.style().get_inheritedtext().white_space
} }
@ -1218,16 +1211,13 @@ impl Fragment {
// Take borders and padding for parent inline fragments into account, if necessary. // Take borders and padding for parent inline fragments into account, if necessary.
if self.is_primary_fragment() { if self.is_primary_fragment() {
match self.inline_context { if let Some(ref context) = self.inline_context {
None => {} for style in context.styles.iter() {
Some(ref context) => { let border_width = style.logical_border_width().inline_start_end();
for style in context.styles.iter() { let padding_inline_size =
let border_width = style.logical_border_width().inline_start_end(); model::padding_from_style(&**style, Au(0)).inline_start_end();
let padding_inline_size = result.surrounding_size = result.surrounding_size + border_width +
model::padding_from_style(&**style, Au(0)).inline_start_end(); padding_inline_size;
result.surrounding_size = result.surrounding_size + border_width +
padding_inline_size;
}
} }
} }
} }
@ -1955,6 +1945,10 @@ impl Fragment {
scanned_text_fragment_info.range.adjust_by(CharIndex(leading_whitespace_character_count), scanned_text_fragment_info.range.adjust_by(CharIndex(leading_whitespace_character_count),
-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 { impl fmt::Debug for Fragment {
@ -2009,3 +2003,40 @@ pub enum CoordinateSystem {
Own, 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,
}
}
}

View file

@ -24,13 +24,14 @@ use gfx::text::glyph::CharIndex;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use std::cmp::max; use std::cmp::max;
use std::fmt; use std::fmt;
use std::iter;
use std::mem; use std::mem;
use std::num::ToPrimitive; use std::num::ToPrimitive;
use std::ops::{Add, Sub, Mul, Div, Rem, Neg, Shl, Shr, Not, BitOr, BitAnd, BitXor}; use std::ops::{Add, Sub, Mul, Div, Rem, Neg, Shl, Shr, Not, BitOr, BitAnd, BitXor};
use std::sync::Arc; use std::sync::Arc;
use std::u16; use std::u16;
use style::computed_values::{overflow_x, text_align, text_justify, text_overflow, vertical_align}; use style::computed_values::{display, overflow_x, text_align, text_justify, text_overflow};
use style::computed_values::{white_space}; use style::computed_values::{vertical_align, white_space};
use style::properties::ComputedValues; use style::properties::ComputedValues;
use util::geometry::{Au, MAX_AU, ZERO_RECT}; use util::geometry::{Au, MAX_AU, ZERO_RECT};
use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; use util::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
@ -809,59 +810,80 @@ impl InlineFlow {
largest_block_size_for_bottom_fragments: &mut Au, largest_block_size_for_bottom_fragments: &mut Au,
layout_context: &LayoutContext) layout_context: &LayoutContext)
-> (Au, bool) { -> (Au, bool) {
match fragment.vertical_align() { let (mut offset_from_baseline, mut largest_size_updated) = (Au(0), false);
vertical_align::T::baseline => (-ascent, false), for style in fragment.inline_styles() {
vertical_align::T::middle => { // Ignore `vertical-align` values for table cells.
// TODO: x-height value should be used from font info. let box_style = style.get_box();
// TODO: The code below passes our current reftests but doesn't work in all if box_style.display != display::T::inline &&
// situations. Add vertical align reftests and fix this. box_style.display != display::T::block {
(-ascent, false) continue
}, }
vertical_align::T::sub => {
let sub_offset = (parent_text_block_start + parent_text_block_end) match box_style.vertical_align {
.scale_by(FONT_SUBSCRIPT_OFFSET_RATIO); vertical_align::T::baseline => {}
(sub_offset - ascent, false) vertical_align::T::middle => {
}, // TODO: x-height value should be used from font info.
vertical_align::T::super_ => { // TODO: Doing nothing here passes our current reftests but doesn't work in
let super_offset = (parent_text_block_start + parent_text_block_end) // all situations. Add vertical align reftests and fix this.
.scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO); },
(-super_offset - ascent, false) vertical_align::T::sub => {
}, let sub_offset = (parent_text_block_start + parent_text_block_end)
vertical_align::T::text_top => { .scale_by(FONT_SUBSCRIPT_OFFSET_RATIO);
let fragment_block_size = *block_size_above_baseline + *depth_below_baseline; offset_from_baseline = offset_from_baseline + sub_offset
let prev_depth_below_baseline = *depth_below_baseline; },
*block_size_above_baseline = parent_text_block_start; vertical_align::T::super_ => {
*depth_below_baseline = fragment_block_size - *block_size_above_baseline; let super_offset = (parent_text_block_start + parent_text_block_end)
(*depth_below_baseline - prev_depth_below_baseline - ascent, false) .scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO);
}, offset_from_baseline = offset_from_baseline - super_offset
vertical_align::T::text_bottom => { },
let fragment_block_size = *block_size_above_baseline + *depth_below_baseline; vertical_align::T::text_top => {
let prev_depth_below_baseline = *depth_below_baseline; let fragment_block_size = *block_size_above_baseline +
*depth_below_baseline = parent_text_block_end; *depth_below_baseline;
*block_size_above_baseline = fragment_block_size - *depth_below_baseline; let prev_depth_below_baseline = *depth_below_baseline;
(*depth_below_baseline - prev_depth_below_baseline - ascent, false) *block_size_above_baseline = parent_text_block_start;
}, *depth_below_baseline = fragment_block_size - *block_size_above_baseline;
vertical_align::T::top => { offset_from_baseline = offset_from_baseline + *depth_below_baseline -
*largest_block_size_for_top_fragments = prev_depth_below_baseline
max(*largest_block_size_for_top_fragments, },
*block_size_above_baseline + *depth_below_baseline); vertical_align::T::text_bottom => {
let offset_top = *block_size_above_baseline - ascent; let fragment_block_size = *block_size_above_baseline +
(offset_top, true) *depth_below_baseline;
}, let prev_depth_below_baseline = *depth_below_baseline;
vertical_align::T::bottom => { *depth_below_baseline = parent_text_block_end;
*largest_block_size_for_bottom_fragments = *block_size_above_baseline = fragment_block_size - *depth_below_baseline;
max(*largest_block_size_for_bottom_fragments, offset_from_baseline = offset_from_baseline + *depth_below_baseline -
*block_size_above_baseline + *depth_below_baseline); prev_depth_below_baseline
let offset_bottom = -(*depth_below_baseline + ascent); },
(offset_bottom, true) vertical_align::T::top => {
}, if !largest_size_updated {
vertical_align::T::Length(length) => (-(length + ascent), false), largest_size_updated = true;
vertical_align::T::Percentage(p) => { *largest_block_size_for_top_fragments =
let line_height = fragment.calculate_line_height(layout_context); max(*largest_block_size_for_top_fragments,
let percent_offset = line_height.scale_by(p); *block_size_above_baseline + *depth_below_baseline);
(-(percent_offset + ascent), false) 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 /// Sets fragment positions in the inline direction based on alignment for one line. This
@ -989,22 +1011,46 @@ impl InlineFlow {
baseline_distance_from_block_start: Au, baseline_distance_from_block_start: Au,
largest_depth_below_baseline: Au) { largest_depth_below_baseline: Au) {
for fragment_index in range(line.range.begin(), line.range.end()) { 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()); 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 => { vertical_align::T::top => {
fragment.border_box.start.b = fragment.border_box.start.b + fragment.border_box.start.b = fragment.border_box.start.b +
line_distance_from_flow_block_start line_distance_from_flow_block_start
} }
vertical_align::T::bottom => { vertical_align::T::bottom => {
fragment.border_box.start.b = fragment.border_box.start.b + fragment.border_box.start.b = fragment.border_box.start.b +
line_distance_from_flow_block_start + baseline_distance_from_block_start + line_distance_from_flow_block_start +
largest_depth_below_baseline baseline_distance_from_block_start +
largest_depth_below_baseline;
} }
_ => { _ => {
fragment.border_box.start.b = fragment.border_box.start.b + fragment.border_box.start.b = fragment.border_box.start.b +
line_distance_from_flow_block_start + baseline_distance_from_block_start line_distance_from_flow_block_start + baseline_distance_from_block_start
} }
} }
fragment.update_late_computed_block_position_if_necessary(); fragment.update_late_computed_block_position_if_necessary();
} }
} }

View file

@ -5239,13 +5239,27 @@ pub fn cascade_anonymous(parent_style: &ComputedValues) -> ComputedValues {
result 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 `<div style="position: absolute">foo bar baz</div>` (in which `foo`, `bar`,
/// and `baz` must not be absolutely-positioned) and cases like `<sup>Foo</sup>` (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] #[inline]
pub fn make_inline(style: &ComputedValues) -> ComputedValues { pub fn modify_style_for_replaced_content(style: &mut Arc<ComputedValues>) {
let mut style = (*style).clone(); // Reset `position` to handle cases like `<div style="position: absolute">foo bar baz</div>`.
style.box_.make_unique().display = longhands::display::computed_value::T::inline; if style.box_.display != longhands::display::computed_value::T::inline {
style.box_.make_unique().position = longhands::position::computed_value::T::static_; let mut style = style.make_unique();
style 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 `<sup>foo</sup>`.
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. /// Sets `border_${side}_width` to the passed in values.

View file

@ -311,8 +311,10 @@ experimental == rtl_simple.html rtl_simple_ref.html
== upper_id_attr.html upper_id_attr_ref.html == upper_id_attr.html upper_id_attr_ref.html
flaky_cpu,experimental == vertical-lr-blocks.html vertical-lr-blocks_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_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_sub_a.html vertical_align_sub_ref.html
== vertical_align_super_a.html vertical_align_super_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_bottom_a.html vertical_align_text_bottom_ref.html
== vertical_align_text_top_a.html vertical_align_text_top_ref.html == vertical_align_text_top_a.html vertical_align_text_top_ref.html
== vertical_align_top_a.html vertical_align_top_ref.html == vertical_align_top_a.html vertical_align_top_ref.html

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<!--
Tests that layout doesn't treat `vertical-align` on table cells as though it were
`vertical-align` on inlines.
-->
<style>
td {
vertical-align: top;
}
</style>
</head>
<body>
<table>
<tr><td>What? <a>What?</a></td></tr>
</table>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<!--
Tests that layout doesn't treat `vertical-align` on table cells as though it were
`vertical-align` on inlines.
-->
</head>
<body>
<table>
<tr><td>What? <a>What?</a></td></tr>
</table>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<!-- Tests that `vertical-align: baseline` inside `vertical-align: super` is laid out properly. -->
<style>
span {
vertical-align: baseline;
}
</style>
</head>
<body>
<p><sup><span>Yo</span></sup></p>
</body>
</html>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<!-- Tests that `vertical-align: baseline` inside `vertical-align: super` is laid out properly. -->
</head>
<body>
<p><sup>Yo</sup></p>
</body>
</html>