layout: Fix several bugs relating to inline borders, padding, and

margins.

* The code that attempted to strip out borders that span multiple
  fragments in the same element could go wrong if fragments were
  stripped out due to text clumping or whitespace stripping. This patch
  rewrites that code to maintain flags in the inline fragment context
  specifying whether the node is the beginning or end of the element.
  Not only is this easier to maintain, it's closer in spirit to what roc
  originally suggested two years ago: it's isomorphic to "begin element,
  end element" markers for inline layout.

* Padding and margins for spans containing inline-blocks are now
  properly handled via a division of labor between the `InlineBlock`
  fragment and the `BlockFlow` that represents the inline-block.

* Unscanned text fragments may not be joined together into a text run if
  borders, padding, or margins separate them.

Because Servo now matches the rendering of Gecko and WebKit on the
`input_button_margins_a` reftest, I had to modify it to add some
vertical alignment.

The combined effect of all of these fixes places "Advertising" on the
right place on google.com.
This commit is contained in:
Patrick Walton 2015-08-13 09:49:40 -07:00
parent 3f9b6f8586
commit ee8741b7a8
11 changed files with 319 additions and 130 deletions

View file

@ -27,7 +27,8 @@ use fragment::{InlineAbsoluteHypotheticalFragmentInfo, TableColumnFragmentInfo};
use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo}; use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
use fragment::{WhitespaceStrippingResult}; use fragment::{WhitespaceStrippingResult};
use incremental::{RECONSTRUCT_FLOW, RestyleDamage}; use incremental::{RECONSTRUCT_FLOW, RestyleDamage};
use inline::{InlineFlow, InlineFragmentNodeInfo}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, InlineFragmentNodeFlags};
use inline::{InlineFragmentNodeInfo, LAST_FRAGMENT_OF_ELEMENT};
use list_item::{ListItemFlow, ListStyleTypeContent}; use list_item::{ListItemFlow, ListStyleTypeContent};
use multicol::MulticolFlow; use multicol::MulticolFlow;
use parallel; use parallel;
@ -217,20 +218,7 @@ impl InlineFragmentsAccumulator {
address: node.opaque(), address: node.opaque(),
pseudo: node.get_pseudo_element_type().strip(), pseudo: node.get_pseudo_element_type().strip(),
style: node.style().clone(), style: node.style().clone(),
}), flags: InlineFragmentNodeFlags::empty(),
bidi_control_chars: None,
restyle_damage: node.restyle_damage(),
}
}
fn from_inline_node_and_style(node: &ThreadSafeLayoutNode, style: Arc<ComputedValues>)
-> InlineFragmentsAccumulator {
InlineFragmentsAccumulator {
fragments: IntermediateInlineFragments::new(),
enclosing_node: Some(InlineFragmentNodeInfo {
address: node.opaque(),
pseudo: node.get_pseudo_element_type().strip(),
style: style,
}), }),
bidi_control_chars: None, bidi_control_chars: None,
restyle_damage: node.restyle_damage(), restyle_damage: node.restyle_damage(),
@ -254,15 +242,16 @@ impl InlineFragmentsAccumulator {
restyle_damage, restyle_damage,
} = self; } = self;
if let Some(enclosing_node) = enclosing_node { if let Some(enclosing_node) = enclosing_node {
let frag_len = fragments.fragments.len(); let fragment_count = fragments.fragments.len();
for (idx, frag) in fragments.fragments.iter_mut().enumerate() { for (index, fragment) in fragments.fragments.iter_mut().enumerate() {
let mut enclosing_node = enclosing_node.clone();
// frag is first inline fragment in the inline node if index == 0 {
let is_first = idx == 0; enclosing_node.flags.insert(FIRST_FRAGMENT_OF_ELEMENT)
// frag is the last inline fragment in the inline node }
let is_last = idx == frag_len - 1; if index == fragment_count - 1 {
enclosing_node.flags.insert(LAST_FRAGMENT_OF_ELEMENT)
frag.add_inline_context_style(enclosing_node.clone(), is_first, is_last); }
fragment.add_inline_context_style(enclosing_node);
} }
// Control characters are later discarded in transform_text, so they don't affect the // Control characters are later discarded in transform_text, so they don't affect the
@ -596,6 +585,7 @@ impl<'a> FlowConstructor<'a> {
let fragment_info = SpecificFragmentInfo::UnscannedText( let fragment_info = SpecificFragmentInfo::UnscannedText(
UnscannedTextFragmentInfo::from_text(" ".to_owned())); UnscannedTextFragmentInfo::from_text(" ".to_owned()));
properties::modify_style_for_replaced_content(&mut whitespace_style); properties::modify_style_for_replaced_content(&mut whitespace_style);
properties::modify_style_for_text(&mut whitespace_style);
let fragment = Fragment::from_opaque_node_and_style(whitespace_node, let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_pseudo, whitespace_pseudo,
whitespace_style, whitespace_style,
@ -863,6 +853,7 @@ impl<'a> FlowConstructor<'a> {
let fragment_info = SpecificFragmentInfo::UnscannedText( let fragment_info = SpecificFragmentInfo::UnscannedText(
UnscannedTextFragmentInfo::from_text(" ".to_owned())); UnscannedTextFragmentInfo::from_text(" ".to_owned()));
properties::modify_style_for_replaced_content(&mut whitespace_style); properties::modify_style_for_replaced_content(&mut whitespace_style);
properties::modify_style_for_text(&mut whitespace_style);
let fragment = Fragment::from_opaque_node_and_style(whitespace_node, let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_pseudo, whitespace_pseudo,
whitespace_style, whitespace_style,
@ -966,8 +957,7 @@ impl<'a> FlowConstructor<'a> {
node.restyle_damage(), node.restyle_damage(),
fragment_info); fragment_info);
let mut fragment_accumulator = let mut fragment_accumulator = InlineFragmentsAccumulator::new();
InlineFragmentsAccumulator::from_inline_node_and_style(node, modified_style);
fragment_accumulator.fragments.fragments.push_back(fragment); fragment_accumulator.fragments.fragments.push_back(fragment);
fragment_accumulator.fragments.absolute_descendants.push_descendants(abs_descendants); fragment_accumulator.fragments.absolute_descendants.push_descendants(abs_descendants);
@ -1718,12 +1708,36 @@ pub fn strip_ignorable_whitespace_from_start(this: &mut LinkedList<Fragment>) {
this.pop_front().unwrap()) this.pop_front().unwrap())
} }
WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => { WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => {
this.pop_front(); let removed_fragment = this.pop_front().unwrap();
if let Some(ref mut remaining_fragment) = this.front_mut() {
if let Some(ref mut inline_context_of_remaining_fragment) =
remaining_fragment.inline_context {
if let Some(ref inline_context_of_removed_fragment) =
removed_fragment.inline_context {
for (i, inline_context_node_from_removed_fragment) in
inline_context_of_removed_fragment.nodes.iter().enumerate() {
if i >= inline_context_of_remaining_fragment.nodes.len() {
break
}
if !inline_context_node_from_removed_fragment.flags.contains(
FIRST_FRAGMENT_OF_ELEMENT) {
continue
}
if inline_context_node_from_removed_fragment.address !=
inline_context_of_remaining_fragment.nodes[i].address {
continue
}
inline_context_of_remaining_fragment.nodes[i].flags.insert(
FIRST_FRAGMENT_OF_ELEMENT);
}
}
}
}
} }
} }
} }
linked_list::prepend_from(this, linked_list::prepend_from(this,
&mut leading_fragments_consisting_of_solely_bidi_control_characters) &mut leading_fragments_consisting_of_solely_bidi_control_characters);
} }
/// Strips ignorable whitespace from the end of a list of fragments. /// Strips ignorable whitespace from the end of a list of fragments.
@ -1741,7 +1755,10 @@ pub fn strip_ignorable_whitespace_from_end(this: &mut LinkedList<Fragment>) {
this.pop_back().unwrap()) this.pop_back().unwrap())
} }
WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => { WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => {
this.pop_back(); let removed_fragment = this.pop_back().unwrap();
if let Some(ref mut remaining_fragment) = this.back_mut() {
remaining_fragment.meld_with_next_inline_fragment(&removed_fragment);
}
} }
} }
} }
@ -1772,13 +1789,17 @@ fn bidi_control_chars(style: &Arc<ComputedValues>) -> Option<(&'static str, &'st
} }
} }
fn control_chars_to_fragment(node: &InlineFragmentNodeInfo, text: &str, fn control_chars_to_fragment(node: &InlineFragmentNodeInfo,
restyle_damage: RestyleDamage) -> Fragment { text: &str,
restyle_damage: RestyleDamage)
-> Fragment {
let info = SpecificFragmentInfo::UnscannedText( let info = SpecificFragmentInfo::UnscannedText(
UnscannedTextFragmentInfo::from_text(String::from(text))); UnscannedTextFragmentInfo::from_text(String::from(text)));
let mut style = node.style.clone();
properties::modify_style_for_text(&mut style);
Fragment::from_opaque_node_and_style(node.address, Fragment::from_opaque_node_and_style(node.address,
node.pseudo, node.pseudo,
node.style.clone(), style,
restyle_damage, restyle_damage,
info) info)
} }

View file

@ -18,7 +18,7 @@ use flow::{self, BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED};
use flow_ref; use flow_ref;
use fragment::{CoordinateSystem, Fragment, HAS_LAYER, IframeFragmentInfo, ImageFragmentInfo}; use fragment::{CoordinateSystem, Fragment, HAS_LAYER, IframeFragmentInfo, ImageFragmentInfo};
use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo}; use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo};
use inline::InlineFlow; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT};
use list_item::ListItemFlow; use list_item::ListItemFlow;
use model::{self, MaybeAuto, ToGfxMatrix}; use model::{self, MaybeAuto, ToGfxMatrix};
use table_cell::CollapsedBordersForCell; use table_cell::CollapsedBordersForCell;
@ -51,8 +51,8 @@ use style::computed_values::{background_attachment, background_clip, background_
use style::computed_values::{background_repeat, background_size}; use style::computed_values::{background_repeat, background_size};
use style::computed_values::{border_style, image_rendering, overflow_x, position}; use style::computed_values::{border_style, image_rendering, overflow_x, position};
use style::computed_values::{visibility, transform, transform_style}; use style::computed_values::{visibility, transform, transform_style};
use style::properties::ComputedValues;
use style::properties::style_structs::Border; use style::properties::style_structs::Border;
use style::properties::{self, ComputedValues};
use style::values::RGBA; use style::values::RGBA;
use style::values::computed; use style::values::computed;
use style::values::computed::LinearGradient; use style::values::computed::LinearGradient;
@ -951,13 +951,20 @@ impl FragmentDisplayListBuilding for Fragment {
level, level,
&stacking_relative_border_box, &stacking_relative_border_box,
&clip); &clip);
let mut style = node.style.clone();
properties::modify_border_style_for_inline_sides(
&mut style,
node.flags.contains(FIRST_FRAGMENT_OF_ELEMENT),
node.flags.contains(LAST_FRAGMENT_OF_ELEMENT));
self.build_display_list_for_borders_if_applicable( self.build_display_list_for_borders_if_applicable(
&*node.style, &*style,
border_painting_mode, border_painting_mode,
display_list, display_list,
&stacking_relative_border_box, &stacking_relative_border_box,
level, level,
&clip); &clip);
self.build_display_list_for_outline_if_applicable( self.build_display_list_for_outline_if_applicable(
&*node.style, &*node.style,
display_list, display_list,

View file

@ -13,7 +13,8 @@ use flow;
use flow::Flow; use flow::Flow;
use flow_ref::{self, FlowRef}; use flow_ref::{self, FlowRef};
use incremental::{self, RestyleDamage}; use incremental::{self, RestyleDamage};
use inline::{InlineFragmentContext, InlineFragmentNodeInfo, InlineMetrics}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragmentContext, InlineFragmentNodeInfo};
use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT};
use layout_debug; use layout_debug;
use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified}; use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified};
use text; use text;
@ -39,7 +40,7 @@ use style::computed_values::content::ContentItem;
use style::computed_values::{border_collapse, clear, mix_blend_mode, overflow_wrap, overflow_x}; use style::computed_values::{border_collapse, clear, mix_blend_mode, overflow_wrap, overflow_x};
use style::computed_values::{position, text_align, text_decoration, transform_style, white_space}; use style::computed_values::{position, text_align, text_decoration, transform_style, white_space};
use style::computed_values::{word_break, z_index}; use style::computed_values::{word_break, z_index};
use style::properties::{self, ComputedValues}; use style::properties::ComputedValues;
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::computed::{LengthOrPercentageOrNone}; use style::values::computed::{LengthOrPercentageOrNone};
use text::TextRunScanner; use text::TextRunScanner;
@ -194,7 +195,9 @@ impl SpecificFragmentInfo {
SpecificFragmentInfo::Iframe(_) => "SpecificFragmentInfo::Iframe", SpecificFragmentInfo::Iframe(_) => "SpecificFragmentInfo::Iframe",
SpecificFragmentInfo::Image(_) => "SpecificFragmentInfo::Image", SpecificFragmentInfo::Image(_) => "SpecificFragmentInfo::Image",
SpecificFragmentInfo::InlineAbsolute(_) => "SpecificFragmentInfo::InlineAbsolute", SpecificFragmentInfo::InlineAbsolute(_) => "SpecificFragmentInfo::InlineAbsolute",
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => "SpecificFragmentInfo::InlineAbsoluteHypothetical", SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {
"SpecificFragmentInfo::InlineAbsoluteHypothetical"
}
SpecificFragmentInfo::InlineBlock(_) => "SpecificFragmentInfo::InlineBlock", SpecificFragmentInfo::InlineBlock(_) => "SpecificFragmentInfo::InlineBlock",
SpecificFragmentInfo::ScannedText(_) => "SpecificFragmentInfo::ScannedText", SpecificFragmentInfo::ScannedText(_) => "SpecificFragmentInfo::ScannedText",
SpecificFragmentInfo::Table => "SpecificFragmentInfo::Table", SpecificFragmentInfo::Table => "SpecificFragmentInfo::Table",
@ -876,16 +879,10 @@ impl Fragment {
/// Adds a style to the inline context for this fragment. If the inline context doesn't exist /// Adds a style to the inline context for this fragment. If the inline 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, node_info: InlineFragmentNodeInfo) {
mut node_info: InlineFragmentNodeInfo,
first_frag: bool,
last_frag: bool) {
if self.inline_context.is_none() { if self.inline_context.is_none() {
self.inline_context = Some(InlineFragmentContext::new()); self.inline_context = Some(InlineFragmentContext::new());
} }
if !first_frag || !last_frag {
properties::modify_style_for_inline_sides(&mut node_info.style, first_frag, last_frag)
};
self.inline_context.as_mut().unwrap().nodes.push(node_info); self.inline_context.as_mut().unwrap().nodes.push(node_info);
} }
@ -1015,17 +1012,24 @@ impl Fragment {
#[inline] #[inline]
fn border_width(&self) -> LogicalMargin<Au> { fn border_width(&self) -> LogicalMargin<Au> {
let style_border_width = match self.specific { let style_border_width = match self.specific {
SpecificFragmentInfo::ScannedText(_) => LogicalMargin::zero(self.style.writing_mode), SpecificFragmentInfo::ScannedText(_) |
SpecificFragmentInfo::InlineBlock(_) => LogicalMargin::zero(self.style.writing_mode),
_ => self.style().logical_border_width(), _ => self.style().logical_border_width(),
}; };
match self.inline_context { match self.inline_context {
None => style_border_width, None => style_border_width,
Some(ref inline_fragment_context) => { Some(ref inline_fragment_context) => {
inline_fragment_context.nodes inline_fragment_context.nodes.iter().fold(style_border_width, |accumulator, node| {
.iter() let mut this_border_width = node.style.logical_border_width();
.fold(style_border_width, if !node.flags.contains(FIRST_FRAGMENT_OF_ELEMENT) {
|acc, node| acc + node.style.logical_border_width()) this_border_width.inline_start = Au(0)
}
if !node.flags.contains(LAST_FRAGMENT_OF_ELEMENT) {
this_border_width.inline_end = Au(0)
}
accumulator + this_border_width
})
} }
} }
} }
@ -1037,7 +1041,6 @@ impl Fragment {
/// (for example, via constraint solving for blocks). /// (for example, via constraint solving for blocks).
pub fn compute_inline_direction_margins(&mut self, containing_block_inline_size: Au) { pub fn compute_inline_direction_margins(&mut self, containing_block_inline_size: Au) {
match self.specific { match self.specific {
SpecificFragmentInfo::InlineBlock(_) |
SpecificFragmentInfo::Table | SpecificFragmentInfo::Table |
SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableRow | SpecificFragmentInfo::TableRow |
@ -1046,27 +1049,41 @@ impl Fragment {
self.margin.inline_end = Au(0); self.margin.inline_end = Au(0);
return return
} }
_ => {} SpecificFragmentInfo::InlineBlock(_) => {
// Inline-blocks do not take self margins into account but do account for margins
// from outer inline contexts.
self.margin.inline_start = Au(0);
self.margin.inline_end = Au(0);
}
_ => {
let margin = self.style().logical_margin();
self.margin.inline_start =
MaybeAuto::from_style(margin.inline_start,
containing_block_inline_size).specified_or_zero();
self.margin.inline_end =
MaybeAuto::from_style(margin.inline_end,
containing_block_inline_size).specified_or_zero();
}
} }
let margin = self.style().logical_margin();
self.margin.inline_start =
MaybeAuto::from_style(margin.inline_start,
containing_block_inline_size).specified_or_zero();
self.margin.inline_end =
MaybeAuto::from_style(margin.inline_end,
containing_block_inline_size).specified_or_zero();
if let Some(ref inline_context) = self.inline_context { if let Some(ref inline_context) = self.inline_context {
for node in &inline_context.nodes { for node in &inline_context.nodes {
let margin = node.style.logical_margin(); let margin = node.style.logical_margin();
self.margin.inline_start = self.margin.inline_start + let this_inline_start_margin = if !node.flags.contains(FIRST_FRAGMENT_OF_ELEMENT) {
Au(0)
} else {
MaybeAuto::from_style(margin.inline_start, MaybeAuto::from_style(margin.inline_start,
containing_block_inline_size).specified_or_zero(); containing_block_inline_size).specified_or_zero()
self.margin.inline_end = self.margin.inline_end + };
let this_inline_end_margin = if !node.flags.contains(LAST_FRAGMENT_OF_ELEMENT) {
Au(0)
} else {
MaybeAuto::from_style(margin.inline_end, MaybeAuto::from_style(margin.inline_end,
containing_block_inline_size).specified_or_zero(); containing_block_inline_size).specified_or_zero()
};
self.margin.inline_start = self.margin.inline_start + this_inline_start_margin;
self.margin.inline_end = self.margin.inline_end + this_inline_end_margin;
} }
} }
} }
@ -1114,33 +1131,42 @@ impl Fragment {
border_collapse::T::collapse => LogicalMargin::zero(self.style.writing_mode), border_collapse::T::collapse => LogicalMargin::zero(self.style.writing_mode),
}; };
// Compute padding. // Compute padding from the fragment's style.
let padding = match self.specific { //
SpecificFragmentInfo::TableColumn(_) | SpecificFragmentInfo::TableRow | // This is zero in the case of `inline-block` because that padding is applied to the
SpecificFragmentInfo::TableWrapper => LogicalMargin::zero(self.style.writing_mode), // wrapped block, not the fragment.
_ => { let padding_from_style = match self.specific {
let style_padding = match self.specific { SpecificFragmentInfo::TableColumn(_) |
SpecificFragmentInfo::ScannedText(_) => { SpecificFragmentInfo::TableRow |
LogicalMargin::zero(self.style.writing_mode) SpecificFragmentInfo::TableWrapper |
} SpecificFragmentInfo::InlineBlock(_) => LogicalMargin::zero(self.style.writing_mode),
_ => model::padding_from_style(self.style(), containing_block_inline_size), _ => model::padding_from_style(self.style(), containing_block_inline_size),
}; };
match self.inline_context { // Compute padding from the inline fragment context.
None => style_padding, let padding_from_inline_fragment_context = match (&self.specific, &self.inline_context) {
Some(ref inline_fragment_context) => { (_, &None) |
inline_fragment_context.nodes (&SpecificFragmentInfo::TableColumn(_), _) |
.iter() (&SpecificFragmentInfo::TableRow, _) |
.fold(style_padding, |acc, node| { (&SpecificFragmentInfo::TableWrapper, _) => {
acc + model::padding_from_style(&*node.style, LogicalMargin::zero(self.style.writing_mode)
Au(0)) }
}) (_, &Some(ref inline_fragment_context)) => {
let zero_padding = LogicalMargin::zero(self.style.writing_mode);
inline_fragment_context.nodes.iter().fold(zero_padding, |accumulator, node| {
let mut padding = model::padding_from_style(&*node.style, Au(0));
if !node.flags.contains(FIRST_FRAGMENT_OF_ELEMENT) {
padding.inline_start = Au(0)
} }
} if !node.flags.contains(LAST_FRAGMENT_OF_ELEMENT) {
padding.inline_end = Au(0)
}
accumulator + padding
})
} }
}; };
self.border_padding = border + padding self.border_padding = border + padding_from_style + padding_from_inline_fragment_context
} }
// Return offset from original position because of `position: relative`. // Return offset from original position because of `position: relative`.
@ -1335,13 +1361,25 @@ impl Fragment {
if self.is_primary_fragment() { if self.is_primary_fragment() {
if let Some(ref context) = self.inline_context { if let Some(ref context) = self.inline_context {
for node in &context.nodes { for node in &context.nodes {
let border_width = node.style.logical_border_width().inline_start_end(); let mut border_width = node.style.logical_border_width();
let padding_inline_size = let mut padding = model::padding_from_style(&*node.style, Au(0));
model::padding_from_style(&*node.style, Au(0)).inline_start_end(); let mut margin = model::specified_margin_from_style(&*node.style);
let margin_inline_size = if !node.flags.contains(FIRST_FRAGMENT_OF_ELEMENT) {
model::specified_margin_from_style(&*node.style).inline_start_end(); border_width.inline_start = Au(0);
result.surrounding_size = result.surrounding_size + border_width + padding.inline_start = Au(0);
padding_inline_size + margin_inline_size; margin.inline_start = Au(0);
}
if !node.flags.contains(LAST_FRAGMENT_OF_ELEMENT) {
border_width.inline_end = Au(0);
padding.inline_end = Au(0);
margin.inline_end = Au(0);
}
result.surrounding_size =
result.surrounding_size +
border_width.inline_start_end() +
padding.inline_start_end() +
margin.inline_start_end();
} }
} }
} }
@ -1610,11 +1648,14 @@ impl Fragment {
this_info.requires_line_break_afterward_if_wrapping_on_newlines = this_info.requires_line_break_afterward_if_wrapping_on_newlines =
this_info.requires_line_break_afterward_if_wrapping_on_newlines || this_info.requires_line_break_afterward_if_wrapping_on_newlines ||
other_info.requires_line_break_afterward_if_wrapping_on_newlines; other_info.requires_line_break_afterward_if_wrapping_on_newlines;
self.border_padding.inline_end = next_fragment.border_padding.inline_end;
self.border_box.size.inline = this_info.content_size.inline + self.border_box.size.inline = this_info.content_size.inline +
self.border_padding.inline_start_end(); self.border_padding.inline_start_end();
} }
_ => panic!("Can only merge two scanned-text fragments!"), _ => panic!("Can only merge two scanned-text fragments!"),
} }
self.meld_with_next_inline_fragment(&next_fragment);
} }
/// Returns true if this fragment is an unscanned text fragment that consists entirely of /// Returns true if this fragment is an unscanned text fragment that consists entirely of
@ -1871,17 +1912,67 @@ impl Fragment {
} }
} }
/// Returns true if this fragment can merge with another adjacent fragment or false otherwise. /// Returns true if this fragment can merge with another immediately-following fragment or
/// false otherwise.
pub fn can_merge_with_fragment(&self, other: &Fragment) -> bool { pub fn can_merge_with_fragment(&self, other: &Fragment) -> bool {
match (&self.specific, &other.specific) { match (&self.specific, &other.specific) {
(&SpecificFragmentInfo::UnscannedText(ref first_unscanned_text), (&SpecificFragmentInfo::UnscannedText(ref first_unscanned_text),
&SpecificFragmentInfo::UnscannedText(_)) => { &SpecificFragmentInfo::UnscannedText(_)) => {
// FIXME: Should probably use a whitelist of styles that can safely differ (#3165) // FIXME: Should probably use a whitelist of styles that can safely differ (#3165)
if self.style().get_font() != other.style().get_font() ||
self.text_decoration() != other.text_decoration() ||
self.white_space() != other.white_space() {
return false
}
let length = first_unscanned_text.text.len(); let length = first_unscanned_text.text.len();
self.style().get_font() == other.style().get_font() && if length != 0 && first_unscanned_text.text.char_at_reverse(length) == '\n' {
self.text_decoration() == other.text_decoration() && return false
self.white_space() == other.white_space() && }
(length == 0 || first_unscanned_text.text.char_at_reverse(length) != '\n')
// If this node has any styles that have border/padding/margins on the following
// side, then we can't merge with the next fragment.
if let Some(ref inline_context) = self.inline_context {
for inline_context_node in inline_context.nodes.iter() {
if !inline_context_node.flags.contains(LAST_FRAGMENT_OF_ELEMENT) {
continue
}
if inline_context_node.style.logical_margin().inline_end !=
LengthOrPercentageOrAuto::Length(Au(0)) {
return false
}
if inline_context_node.style.logical_padding().inline_end !=
LengthOrPercentage::Length(Au(0)) {
return false
}
if inline_context_node.style.logical_border_width().inline_end != Au(0) {
return false
}
}
}
// If the next fragment has any styles that have border/padding/margins on the
// preceding side, then it can't merge with us.
if let Some(ref inline_context) = other.inline_context {
for inline_context_node in inline_context.nodes.iter() {
if !inline_context_node.flags.contains(FIRST_FRAGMENT_OF_ELEMENT) {
continue
}
if inline_context_node.style.logical_margin().inline_start !=
LengthOrPercentageOrAuto::Length(Au(0)) {
return false
}
if inline_context_node.style.logical_padding().inline_start !=
LengthOrPercentage::Length(Au(0)) {
return false
}
if inline_context_node.style.logical_border_width().inline_start != Au(0) {
return false
}
}
}
true
} }
_ => false, _ => false,
} }
@ -2286,6 +2377,29 @@ impl Fragment {
pub fn is_absolutely_positioned(&self) -> bool { pub fn is_absolutely_positioned(&self) -> bool {
self.style.get_box().position == position::T::absolute self.style.get_box().position == position::T::absolute
} }
pub fn meld_with_next_inline_fragment(&mut self, next_fragment: &Fragment) {
if let Some(ref mut inline_context_of_this_fragment) = self.inline_context {
if let Some(ref inline_context_of_next_fragment) = next_fragment.inline_context {
for (i, inline_context_node_from_next_fragment) in
inline_context_of_next_fragment.nodes.iter().enumerate() {
if i >= inline_context_of_this_fragment.nodes.len() {
continue
}
if !inline_context_node_from_next_fragment.flags.contains(
LAST_FRAGMENT_OF_ELEMENT) {
continue
}
if inline_context_node_from_next_fragment.address !=
inline_context_of_this_fragment.nodes[i].address {
continue
}
inline_context_of_this_fragment.nodes[i].flags.insert(
LAST_FRAGMENT_OF_ELEMENT);
}
}
}
}
} }
impl fmt::Debug for Fragment { impl fmt::Debug for Fragment {
@ -2378,7 +2492,7 @@ impl<'a> InlineStyleIterator<'a> {
} }
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum WhitespaceStrippingResult { pub enum WhitespaceStrippingResult {
RetainFragment, RetainFragment,
FragmentContainedOnlyBidiControlCharacters, FragmentContainedOnlyBidiControlCharacters,

View file

@ -1666,6 +1666,8 @@ impl Flow for InlineFlow {
.early_absolute_position_info .early_absolute_position_info
.relative_containing_block_mode, .relative_containing_block_mode,
CoordinateSystem::Parent); CoordinateSystem::Parent);
let stacking_relative_content_box =
fragment.stacking_relative_content_box(&stacking_relative_border_box);
let clip = fragment.clipping_region_for_children(&self.base.clip, let clip = fragment.clipping_region_for_children(&self.base.clip,
&stacking_relative_border_box, &stacking_relative_border_box,
false); false);
@ -1689,7 +1691,7 @@ impl Flow for InlineFlow {
} }
block_flow.base.stacking_relative_position = block_flow.base.stacking_relative_position =
stacking_relative_border_box.origin; stacking_relative_content_box.origin;
block_flow.base.stacking_relative_position_of_display_port = block_flow.base.stacking_relative_position_of_display_port =
self.base.stacking_relative_position_of_display_port; self.base.stacking_relative_position_of_display_port;
} }
@ -1817,6 +1819,20 @@ pub struct InlineFragmentNodeInfo {
pub address: OpaqueNode, pub address: OpaqueNode,
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub pseudo: PseudoElementType<()>, pub pseudo: PseudoElementType<()>,
pub flags: InlineFragmentNodeFlags,
}
bitflags! {
flags InlineFragmentNodeFlags: u8 {
const FIRST_FRAGMENT_OF_ELEMENT = 0x01,
const LAST_FRAGMENT_OF_ELEMENT = 0x02,
}
}
impl fmt::Debug for InlineFragmentNodeInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.flags.bits())
}
} }
#[derive(Clone)] #[derive(Clone)]

View file

@ -184,7 +184,6 @@ impl TextRunScanner {
}; };
let (mut start_position, mut end_position) = (0, 0); let (mut start_position, mut end_position) = (0, 0);
for character in text.chars() { for character in text.chars() {
// Search for the first font in this font group that contains a glyph for this // Search for the first font in this font group that contains a glyph for this
// character. // character.
@ -286,10 +285,15 @@ impl TextRunScanner {
for (logical_offset, old_fragment) in for (logical_offset, old_fragment) in
mem::replace(&mut self.clump, LinkedList::new()).into_iter().enumerate() { mem::replace(&mut self.clump, LinkedList::new()).into_iter().enumerate() {
loop { loop {
match mappings.peek() { match mappings.peek() {
Some(mapping) if mapping.old_fragment_index == logical_offset => {} Some(mapping) if mapping.old_fragment_index == logical_offset => {}
Some(_) | None => break, Some(_) | None => {
}; if let Some(ref mut last_fragment) = out_fragments.last_mut() {
last_fragment.meld_with_next_inline_fragment(&old_fragment);
}
break;
}
};
let mut mapping = mappings.next().unwrap(); let mut mapping = mappings.next().unwrap();
let run = runs[mapping.text_run_index].clone(); let run = runs[mapping.text_run_index].clone();

View file

@ -6456,15 +6456,15 @@ pub fn modify_style_for_replaced_content(style: &mut Arc<ComputedValues>) {
} }
} }
/// Adjusts borders, padding, and margins as appropriate to account for a fragment's status as the /// Adjusts borders as appropriate to account for a fragment's status as the first or last fragment
/// first or last fragment within the range of an element. /// within the range of an element.
/// ///
/// Specifically, this function sets border/padding/margin widths to zero on the sides for which /// Specifically, this function sets border widths to zero on the sides for which the fragment is
/// the fragment is not outermost. /// not outermost.
#[inline] #[inline]
pub fn modify_style_for_inline_sides(style: &mut Arc<ComputedValues>, pub fn modify_border_style_for_inline_sides(style: &mut Arc<ComputedValues>,
is_first_fragment_of_element: bool, is_first_fragment_of_element: bool,
is_last_fragment_of_element: bool) { is_last_fragment_of_element: bool) {
fn modify_side(style: &mut Arc<ComputedValues>, side: PhysicalSide) { fn modify_side(style: &mut Arc<ComputedValues>, side: PhysicalSide) {
let mut style = Arc::make_mut(style); let mut style = Arc::make_mut(style);
let border = Arc::make_mut(&mut style.border); let border = Arc::make_mut(&mut style.border);
@ -6472,34 +6472,18 @@ pub fn modify_style_for_inline_sides(style: &mut Arc<ComputedValues>,
PhysicalSide::Left => { PhysicalSide::Left => {
border.border_left_width = Au(0); border.border_left_width = Au(0);
border.border_left_style = BorderStyle::none; border.border_left_style = BorderStyle::none;
Arc::make_mut(&mut style.padding).padding_left =
computed::LengthOrPercentage::Length(Au(0));
Arc::make_mut(&mut style.margin).margin_left =
computed::LengthOrPercentageOrAuto::Length(Au(0))
} }
PhysicalSide::Right => { PhysicalSide::Right => {
border.border_right_width = Au(0); border.border_right_width = Au(0);
border.border_right_style = BorderStyle::none; border.border_right_style = BorderStyle::none;
Arc::make_mut(&mut style.padding).padding_right =
computed::LengthOrPercentage::Length(Au(0));
Arc::make_mut(&mut style.margin).margin_right =
computed::LengthOrPercentageOrAuto::Length(Au(0))
} }
PhysicalSide::Bottom => { PhysicalSide::Bottom => {
border.border_bottom_width = Au(0); border.border_bottom_width = Au(0);
border.border_bottom_style = BorderStyle::none; border.border_bottom_style = BorderStyle::none;
Arc::make_mut(&mut style.padding).padding_bottom =
computed::LengthOrPercentage::Length(Au(0));
Arc::make_mut(&mut style.margin).margin_bottom =
computed::LengthOrPercentageOrAuto::Length(Au(0))
} }
PhysicalSide::Top => { PhysicalSide::Top => {
border.border_top_width = Au(0); border.border_top_width = Au(0);
border.border_top_style = BorderStyle::none; border.border_top_style = BorderStyle::none;
Arc::make_mut(&mut style.padding).padding_top =
computed::LengthOrPercentage::Length(Au(0));
Arc::make_mut(&mut style.margin).margin_top =
computed::LengthOrPercentageOrAuto::Length(Au(0))
} }
} }
} }
@ -6534,7 +6518,7 @@ pub fn modify_style_for_outer_inline_block_fragment(style: &mut Arc<ComputedValu
box_style.position = longhands::position::computed_value::T::static_ box_style.position = longhands::position::computed_value::T::static_
} }
/// Adjusts the `position` property as necessary to account for text. /// Adjusts the `position` and `padding` properties as necessary to account for text.
/// ///
/// Text is never directly relatively positioned; it's always contained within an element that is /// Text is never directly relatively positioned; it's always contained within an element that is
/// itself relatively positioned. /// itself relatively positioned.
@ -6550,6 +6534,18 @@ pub fn modify_style_for_text(style: &mut Arc<ComputedValues>) {
position_offsets.bottom = computed::LengthOrPercentageOrAuto::Auto; position_offsets.bottom = computed::LengthOrPercentageOrAuto::Auto;
position_offsets.left = computed::LengthOrPercentageOrAuto::Auto; position_offsets.left = computed::LengthOrPercentageOrAuto::Auto;
} }
if style.padding.padding_top != computed::LengthOrPercentage::Length(Au(0)) ||
style.padding.padding_right != computed::LengthOrPercentage::Length(Au(0)) ||
style.padding.padding_bottom != computed::LengthOrPercentage::Length(Au(0)) ||
style.padding.padding_left != computed::LengthOrPercentage::Length(Au(0)) {
let mut style = Arc::make_unique(style);
let mut padding = Arc::make_unique(&mut style.padding);
padding.padding_top = computed::LengthOrPercentage::Length(Au(0));
padding.padding_right = computed::LengthOrPercentage::Length(Au(0));
padding.padding_bottom = computed::LengthOrPercentage::Length(Au(0));
padding.padding_left = computed::LengthOrPercentage::Length(Au(0));
}
} }
/// Adjusts the `margin` property as necessary to account for the text of an `input` element. /// Adjusts the `margin` property as necessary to account for the text of an `input` element.

View file

@ -224,6 +224,7 @@ flaky_cpu == linebreak_simple_a.html linebreak_simple_b.html
!= list_style_type_a.html list_style_type_ref.html != list_style_type_a.html list_style_type_ref.html
== many_brs_a.html many_brs_ref.html == many_brs_a.html many_brs_ref.html
== margin_a.html margin_b.html == margin_a.html margin_b.html
== margin_padding_inline_block_a.html margin_padding_inline_block_ref.html
== margins_inside_floats_a.html margins_inside_floats_ref.html == margins_inside_floats_a.html margins_inside_floats_ref.html
== marker_block_direction_placement_a.html marker_block_direction_placement_ref.html == marker_block_direction_placement_a.html marker_block_direction_placement_ref.html
== max_width_float_simple_a.html max_width_float_simple_b.html == max_width_float_simple_a.html max_width_float_simple_b.html

View file

@ -5,6 +5,8 @@ body, html {
} }
input { input {
margin-left: 64px; margin-left: 64px;
border: none;
vertical-align: top;
} }
</style> </style>
<input type=button value=Hello> <input type=button value=Hello>

View file

@ -6,6 +6,7 @@ body, html {
input { input {
position: absolute; position: absolute;
left: 64px; left: 64px;
border: none;
} }
</style> </style>
<input type=button value=Hello> <input type=button value=Hello>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<style>
html, body {
margin: 0;
}
</style>
<div><span style="padding-left: 100px; display: inline-block">Boo</span></div>
<div><span style="padding-left: 100px">Foo</span></div>
<div><span style="margin-left: 100px">Foo</span></div>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<style>
html, body {
margin: 0;
}
div {
position: relative;
}
span {
position: relative;
left: 100px;
}
</style>
<div><span>Boo</span></div>
<div><span>Foo</span></div>
<div><span>Foo</span></div>