Merge pull request #3134 from glennw/inline-background

Add support for backgrounds on inline elements. Fix fixup() by removing it.
This commit is contained in:
Lars Bergstrom 2014-08-27 10:24:49 -05:00
commit 370192451f
8 changed files with 155 additions and 375 deletions

View file

@ -1107,8 +1107,7 @@ impl BlockFlow {
let rel_offset = let rel_offset =
self.fragment.relative_position(&self.base self.fragment.relative_position(&self.base
.absolute_position_info .absolute_position_info
.relative_containing_block_size, .relative_containing_block_size);
None);
// FIXME(#2795): Get the real container size // FIXME(#2795): Get the real container size
let container_size = Size2D::zero(); let container_size = Size2D::zero();
@ -1120,8 +1119,7 @@ impl BlockFlow {
layout_context, layout_context,
self.base.abs_position + (offset + rel_offset).to_physical( self.base.abs_position + (offset + rel_offset).to_physical(
self.base.writing_mode, container_size), self.base.writing_mode, container_size),
background_border_level, background_border_level);
None);
let mut child_layers = DList::new(); let mut child_layers = DList::new();
for kid in self.base.child_iter() { for kid in self.base.child_iter() {
@ -1470,7 +1468,7 @@ impl Flow for BlockFlow {
flags.union_floated_descendants_flags(child_base.flags); flags.union_floated_descendants_flags(child_base.flags);
} }
let fragment_intrinsic_inline_sizes = self.fragment.intrinsic_inline_sizes(None); let fragment_intrinsic_inline_sizes = self.fragment.intrinsic_inline_sizes();
intrinsic_inline_sizes.minimum_inline_size = geometry::max(intrinsic_inline_sizes.minimum_inline_size, intrinsic_inline_sizes.minimum_inline_size = geometry::max(intrinsic_inline_sizes.minimum_inline_size,
fragment_intrinsic_inline_sizes.minimum_inline_size); fragment_intrinsic_inline_sizes.minimum_inline_size);
intrinsic_inline_sizes.preferred_inline_size = geometry::max(intrinsic_inline_sizes.preferred_inline_size, intrinsic_inline_sizes.preferred_inline_size = geometry::max(intrinsic_inline_sizes.preferred_inline_size,
@ -1624,8 +1622,7 @@ impl Flow for BlockFlow {
let relative_offset = let relative_offset =
self.fragment.relative_position(&self.base self.fragment.relative_position(&self.base
.absolute_position_info .absolute_position_info
.relative_containing_block_size, .relative_containing_block_size);
None);
if self.is_positioned() { if self.is_positioned() {
self.base.absolute_position_info.absolute_containing_block_position = self.base.absolute_position_info.absolute_containing_block_position =
self.base.abs_position self.base.abs_position
@ -1811,7 +1808,7 @@ pub trait ISizeAndMarginsComputer {
let containing_block_inline_size = self.containing_block_inline_size(block, parent_flow_inline_size, ctx); let containing_block_inline_size = self.containing_block_inline_size(block, parent_flow_inline_size, ctx);
let computed_inline_size = self.initial_computed_inline_size(block, parent_flow_inline_size, ctx); let computed_inline_size = self.initial_computed_inline_size(block, parent_flow_inline_size, ctx);
block.fragment.compute_border_padding_margins(containing_block_inline_size, None); block.fragment.compute_border_padding_margins(containing_block_inline_size);
let style = block.fragment.style(); let style = block.fragment.style();
@ -2257,7 +2254,7 @@ impl ISizeAndMarginsComputer for AbsoluteReplaced {
-> MaybeAuto { -> MaybeAuto {
let containing_block_inline_size = block.containing_block_size(ctx.shared.screen_size).inline; let containing_block_inline_size = block.containing_block_size(ctx.shared.screen_size).inline;
let fragment = block.fragment(); let fragment = block.fragment();
fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size, None); fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size);
// For replaced absolute flow, the rest of the constraint solving will // For replaced absolute flow, the rest of the constraint solving will
// take inline-size to be specified as the value computed here. // take inline-size to be specified as the value computed here.
Specified(fragment.content_inline_size()) Specified(fragment.content_inline_size())
@ -2306,7 +2303,7 @@ impl ISizeAndMarginsComputer for BlockReplaced {
_: &LayoutContext) _: &LayoutContext)
-> MaybeAuto { -> MaybeAuto {
let fragment = block.fragment(); let fragment = block.fragment();
fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, None); fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
// For replaced block flow, the rest of the constraint solving will // For replaced block flow, the rest of the constraint solving will
// take inline-size to be specified as the value computed here. // take inline-size to be specified as the value computed here.
Specified(fragment.content_inline_size()) Specified(fragment.content_inline_size())
@ -2362,7 +2359,7 @@ impl ISizeAndMarginsComputer for FloatReplaced {
_: &LayoutContext) _: &LayoutContext)
-> MaybeAuto { -> MaybeAuto {
let fragment = block.fragment(); let fragment = block.fragment();
fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, None); fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
// For replaced block flow, the rest of the constraint solving will // For replaced block flow, the rest of the constraint solving will
// take inline-size to be specified as the value computed here. // take inline-size to be specified as the value computed here.
Specified(fragment.content_inline_size()) Specified(fragment.content_inline_size())

View file

@ -32,7 +32,7 @@ use fragment::{ImageFragment, ImageFragmentInfo, SpecificFragmentInfo, TableFrag
use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo}; use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo};
use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment}; use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment};
use fragment::{UnscannedTextFragmentInfo}; use fragment::{UnscannedTextFragmentInfo};
use inline::{FragmentIndex, InlineFragments, InlineFlow}; use inline::{InlineFragments, InlineFlow};
use parallel; use parallel;
use table_wrapper::TableWrapperFlow; use table_wrapper::TableWrapperFlow;
use table::TableFlow; use table::TableFlow;
@ -57,7 +57,6 @@ use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstruc
use script::dom::node::{TextNodeTypeId}; use script::dom::node::{TextNodeTypeId};
use script::dom::htmlobjectelement::is_image_data; use script::dom::htmlobjectelement::is_image_data;
use servo_util::namespace; use servo_util::namespace;
use servo_util::range::Range;
use std::mem; use std::mem;
use std::sync::atomics::Relaxed; use std::sync::atomics::Relaxed;
use style::ComputedValues; use style::ComputedValues;
@ -140,37 +139,40 @@ struct InlineFragmentsAccumulator {
/// The list of fragments. /// The list of fragments.
fragments: InlineFragments, fragments: InlineFragments,
/// Whether we've created a range to enclose all the fragments. This will be true if the outer node /// Whether we've created a range to enclose all the fragments. This will be Some() if the outer node
/// is an inline and false otherwise. /// is an inline and None otherwise.
has_enclosing_range: bool, enclosing_style: Option<Arc<ComputedValues>>,
} }
impl InlineFragmentsAccumulator { impl InlineFragmentsAccumulator {
fn new() -> InlineFragmentsAccumulator { fn new() -> InlineFragmentsAccumulator {
InlineFragmentsAccumulator { InlineFragmentsAccumulator {
fragments: InlineFragments::new(), fragments: InlineFragments::new(),
has_enclosing_range: false, enclosing_style: None,
} }
} }
fn from_inline_node(node: &ThreadSafeLayoutNode) -> InlineFragmentsAccumulator { fn from_inline_node(node: &ThreadSafeLayoutNode) -> InlineFragmentsAccumulator {
let mut fragments = InlineFragments::new(); let fragments = InlineFragments::new();
fragments.push_range(node.style().clone(), Range::empty());
InlineFragmentsAccumulator { InlineFragmentsAccumulator {
fragments: fragments, fragments: fragments,
has_enclosing_range: true, enclosing_style: Some(node.style().clone()),
} }
} }
fn finish(self) -> InlineFragments { fn finish(self) -> InlineFragments {
let InlineFragmentsAccumulator { let InlineFragmentsAccumulator {
fragments: mut fragments, fragments: mut fragments,
has_enclosing_range enclosing_style
} = self; } = self;
if has_enclosing_range { match enclosing_style {
let len = FragmentIndex(fragments.len() as int); Some(enclosing_style) => {
fragments.get_mut_range(FragmentIndex(0)).range.extend_to(len); for frag in fragments.fragments.mut_iter() {
frag.add_inline_context_style(enclosing_style.clone());
}
}
None => {}
} }
fragments fragments
} }
@ -374,10 +376,10 @@ impl<'a, 'b> FlowConstructor<'a, 'b> {
// Add whitespace results. They will be stripped out later on when // Add whitespace results. They will be stripped out later on when
// between block elements, and retained when between inline elements. // between block elements, and retained when between inline elements.
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string())); let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
let fragment = Fragment::from_opaque_node_and_style(whitespace_node, let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_style.clone(), whitespace_style.clone(),
fragment_info); fragment_info);
inline_fragment_accumulator.fragments.push(fragment, whitespace_style); inline_fragment_accumulator.fragments.push(&mut fragment, whitespace_style);
} }
ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(_)) => { ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(_)) => {
// TODO: Implement anonymous table objects for missing parents // TODO: Implement anonymous table objects for missing parents
@ -525,10 +527,10 @@ impl<'a, 'b> FlowConstructor<'a, 'b> {
=> { => {
// Instantiate the whitespace fragment. // Instantiate the whitespace fragment.
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string())); let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
let fragment = Fragment::from_opaque_node_and_style(whitespace_node, let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
whitespace_style.clone(), whitespace_style.clone(),
fragment_info); fragment_info);
fragment_accumulator.fragments.push(fragment, whitespace_style) fragment_accumulator.fragments.push(&mut fragment, whitespace_style)
} }
ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(_)) => { ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(_)) => {
// TODO: Implement anonymous table objects for missing parents // TODO: Implement anonymous table objects for missing parents
@ -570,7 +572,7 @@ impl<'a, 'b> FlowConstructor<'a, 'b> {
} }
let mut fragments = InlineFragments::new(); let mut fragments = InlineFragments::new();
fragments.push(Fragment::new(self, node), node.style().clone()); fragments.push(&mut Fragment::new(self, node), node.style().clone());
let construction_item = InlineFragmentsConstructionItem(InlineFragmentsConstructionResult { let construction_item = InlineFragmentsConstructionItem(InlineFragmentsConstructionResult {
splits: Vec::new(), splits: Vec::new(),

View file

@ -99,6 +99,10 @@ pub struct Fragment {
/// ///
/// FIXME(#2260, pcwalton): This is very inefficient; remove. /// FIXME(#2260, pcwalton): This is very inefficient; remove.
pub new_line_pos: Vec<CharIndex>, pub new_line_pos: Vec<CharIndex>,
/// Holds the style context information for fragments
/// that are part of an inline formatting context.
pub inline_context: Option<InlineFragmentContext>,
} }
/// Info specific to the kind of fragment. Keep this enum small. /// Info specific to the kind of fragment. Keep this enum small.
@ -330,6 +334,7 @@ impl Fragment {
margin: LogicalMargin::zero(writing_mode), margin: LogicalMargin::zero(writing_mode),
specific: constructor.build_specific_fragment_info_for_node(node), specific: constructor.build_specific_fragment_info_for_node(node),
new_line_pos: vec!(), new_line_pos: vec!(),
inline_context: None,
} }
} }
@ -345,6 +350,7 @@ impl Fragment {
margin: LogicalMargin::zero(writing_mode), margin: LogicalMargin::zero(writing_mode),
specific: specific, specific: specific,
new_line_pos: vec!(), new_line_pos: vec!(),
inline_context: None,
} }
} }
@ -369,6 +375,7 @@ impl Fragment {
margin: LogicalMargin::zero(writing_mode), margin: LogicalMargin::zero(writing_mode),
specific: specific, specific: specific,
new_line_pos: vec!(), new_line_pos: vec!(),
inline_context: None,
} }
} }
@ -386,6 +393,7 @@ impl Fragment {
margin: LogicalMargin::zero(writing_mode), margin: LogicalMargin::zero(writing_mode),
specific: specific, specific: specific,
new_line_pos: vec!(), new_line_pos: vec!(),
inline_context: None,
} }
} }
@ -407,9 +415,19 @@ impl Fragment {
margin: self.margin, margin: self.margin,
specific: specific, specific: specific,
new_line_pos: self.new_line_pos.clone(), new_line_pos: self.new_line_pos.clone(),
inline_context: self.inline_context.clone(),
} }
} }
/// 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<ComputedValues>) {
if self.inline_context.is_none() {
self.inline_context = Some(InlineFragmentContext::new());
}
self.inline_context.get_mut_ref().styles.push(style.clone());
}
/// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text or /// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text or
/// replaced elements. /// replaced elements.
fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes { fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes {
@ -444,7 +462,7 @@ impl Fragment {
}; };
// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK? // FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
let border = self.border_width(None); let border = self.border_width();
let surround_inline_size = margin_inline_start + margin_inline_end + padding_inline_start + padding_inline_end + let surround_inline_size = margin_inline_start + margin_inline_end + padding_inline_start + padding_inline_end +
border.inline_start_end(); border.inline_start_end();
@ -465,13 +483,12 @@ impl Fragment {
/// it should only be called during intrinsic inline-size computation or computation of /// it should only be called during intrinsic inline-size computation or computation of
/// `border_padding`. Other consumers of this information should simply consult that field. /// `border_padding`. Other consumers of this information should simply consult that field.
#[inline] #[inline]
fn border_width(&self, inline_fragment_context: Option<InlineFragmentContext>) fn border_width(&self) -> LogicalMargin<Au> {
-> LogicalMargin<Au> { match self.inline_context {
match inline_fragment_context {
None => self.style().logical_border_width(), None => self.style().logical_border_width(),
Some(inline_fragment_context) => { Some(ref inline_fragment_context) => {
let zero = LogicalMargin::zero(self.style.writing_mode); let zero = LogicalMargin::zero(self.style.writing_mode);
inline_fragment_context.ranges().fold(zero, |acc, range| acc + range.border()) inline_fragment_context.styles.iter().fold(zero, |acc, style| acc + style.logical_border_width())
} }
} }
} }
@ -480,8 +497,7 @@ impl Fragment {
/// style. After this call, the `border_padding` and the vertical direction of the `margin` /// style. After this call, the `border_padding` and the vertical direction of the `margin`
/// field will be correct. /// field will be correct.
pub fn compute_border_padding_margins(&mut self, pub fn compute_border_padding_margins(&mut self,
containing_block_inline_size: Au, containing_block_inline_size: Au) {
inline_fragment_context: Option<InlineFragmentContext>) {
// Compute vertical margins. Note that this value will be ignored by layout if the style // Compute vertical margins. Note that this value will be ignored by layout if the style
// specifies `auto`. // specifies `auto`.
match self.specific { match self.specific {
@ -500,19 +516,19 @@ impl Fragment {
} }
// Compute border. // Compute border.
let border = self.border_width(inline_fragment_context); let border = self.border_width();
// Compute padding. // Compute padding.
let padding = match self.specific { let padding = match self.specific {
TableColumnFragment(_) | TableRowFragment | TableColumnFragment(_) | TableRowFragment |
TableWrapperFragment => LogicalMargin::zero(self.style.writing_mode), TableWrapperFragment => LogicalMargin::zero(self.style.writing_mode),
_ => { _ => {
match inline_fragment_context { match self.inline_context {
None => model::padding_from_style(self.style(), containing_block_inline_size), None => model::padding_from_style(self.style(), containing_block_inline_size),
Some(inline_fragment_context) => { Some(ref inline_fragment_context) => {
let zero = LogicalMargin::zero(self.style.writing_mode); let zero = LogicalMargin::zero(self.style.writing_mode);
inline_fragment_context.ranges() inline_fragment_context.styles.iter()
.fold(zero, |acc, range| acc + range.padding()) .fold(zero, |acc, style| acc + model::padding_from_style(&**style, Au(0)))
} }
} }
} }
@ -523,8 +539,7 @@ impl Fragment {
// Return offset from original position because of `position: relative`. // Return offset from original position because of `position: relative`.
pub fn relative_position(&self, pub fn relative_position(&self,
containing_block_size: &LogicalSize<Au>, containing_block_size: &LogicalSize<Au>)
inline_fragment_context: Option<InlineFragmentContext>)
-> LogicalSize<Au> { -> LogicalSize<Au> {
fn from_style(style: &ComputedValues, container_size: &LogicalSize<Au>) fn from_style(style: &ComputedValues, container_size: &LogicalSize<Au>)
-> LogicalSize<Au> { -> LogicalSize<Au> {
@ -544,16 +559,16 @@ impl Fragment {
// Go over the ancestor fragments and add all relative offsets (if any). // Go over the ancestor fragments and add all relative offsets (if any).
let mut rel_pos = LogicalSize::zero(self.style.writing_mode); let mut rel_pos = LogicalSize::zero(self.style.writing_mode);
match inline_fragment_context { match self.inline_context {
None => { None => {
if self.style().get_box().position == position::relative { if self.style().get_box().position == position::relative {
rel_pos = rel_pos + from_style(self.style(), containing_block_size); rel_pos = rel_pos + from_style(self.style(), containing_block_size);
} }
} }
Some(inline_fragment_context) => { Some(ref inline_fragment_context) => {
for range in inline_fragment_context.ranges() { for style in inline_fragment_context.styles.iter() {
if range.style.get_box().position == position::relative { if style.get_box().position == position::relative {
rel_pos = rel_pos + from_style(&*range.style, containing_block_size); rel_pos = rel_pos + from_style(&**style, containing_block_size);
} }
} }
}, },
@ -634,6 +649,7 @@ impl Fragment {
/// Adds the display items necessary to paint the background of this fragment to the display /// Adds the display items necessary to paint the background of this fragment to the display
/// list if necessary. /// list if necessary.
pub fn build_display_list_for_background_if_applicable(&self, pub fn build_display_list_for_background_if_applicable(&self,
style: &ComputedValues,
list: &mut DisplayList, list: &mut DisplayList,
layout_context: &LayoutContext, layout_context: &LayoutContext,
level: StackingLevel, level: StackingLevel,
@ -642,7 +658,6 @@ impl Fragment {
// needed. We could use display list optimization to clean this up, but it still seems // needed. We could use display list optimization to clean this up, but it still seems
// inefficient. What we really want is something like "nearest ancestor element that // inefficient. What we really want is something like "nearest ancestor element that
// doesn't have a fragment". // doesn't have a fragment".
let style = self.style();
let background_color = style.resolve_color(style.get_background().background_color); let background_color = style.resolve_color(style.get_background().background_color);
if !background_color.alpha.approx_eq(&0.0) { if !background_color.alpha.approx_eq(&0.0) {
let display_item = box SolidColorDisplayItem { let display_item = box SolidColorDisplayItem {
@ -742,11 +757,9 @@ impl Fragment {
pub fn build_display_list_for_borders_if_applicable(&self, pub fn build_display_list_for_borders_if_applicable(&self,
list: &mut DisplayList, list: &mut DisplayList,
abs_bounds: &Rect<Au>, abs_bounds: &Rect<Au>,
level: StackingLevel, level: StackingLevel) {
inline_fragment_context:
Option<InlineFragmentContext>) {
// Fast path. // Fast path.
let border = self.border_width(inline_fragment_context); let border = self.border_width();
if border.is_zero() { if border.is_zero() {
return return
} }
@ -844,8 +857,7 @@ impl Fragment {
display_list: &mut DisplayList, display_list: &mut DisplayList,
layout_context: &LayoutContext, layout_context: &LayoutContext,
flow_origin: Point2D<Au>, flow_origin: Point2D<Au>,
background_and_border_level: BackgroundAndBorderLevel, background_and_border_level: BackgroundAndBorderLevel)
inline_fragment_context: Option<InlineFragmentContext>)
-> ChildDisplayListAccumulator { -> ChildDisplayListAccumulator {
// FIXME(#2795): Get the real container size // FIXME(#2795): Get the real container size
let container_size = Size2D::zero(); let container_size = Size2D::zero();
@ -886,18 +898,31 @@ impl Fragment {
display_list.push(PseudoDisplayItemClass(base_display_item)); display_list.push(PseudoDisplayItemClass(base_display_item));
// Add the background to the list, if applicable. // Add the background to the list, if applicable.
self.build_display_list_for_background_if_applicable(display_list, match self.inline_context {
layout_context, Some(ref inline_context) => {
level, for style in inline_context.styles.iter().rev() {
&absolute_fragment_bounds); self.build_display_list_for_background_if_applicable(&**style,
display_list,
layout_context,
level,
&absolute_fragment_bounds);
}
}
None => {
self.build_display_list_for_background_if_applicable(&*self.style,
display_list,
layout_context,
level,
&absolute_fragment_bounds);
}
}
// Add a border, if applicable. // Add a border, if applicable.
// //
// TODO: Outlines. // TODO: Outlines.
self.build_display_list_for_borders_if_applicable(display_list, self.build_display_list_for_borders_if_applicable(display_list,
&absolute_fragment_bounds, &absolute_fragment_bounds,
level, level);
inline_fragment_context);
} }
// Add a clip, if applicable. // Add a clip, if applicable.
@ -944,8 +969,8 @@ impl Fragment {
// FIXME(#2263, pcwalton): This is a bit of an abuse of the logging infrastructure. // FIXME(#2263, pcwalton): This is a bit of an abuse of the logging infrastructure.
// We should have a real `SERVO_DEBUG` system. // We should have a real `SERVO_DEBUG` system.
debug!("{:?}", self.build_debug_borders_around_text_fragments(display_list, debug!("{:?}", self.build_debug_borders_around_text_fragments(display_list,
flow_origin, flow_origin,
text_fragment)) text_fragment))
}, },
GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | TableRowFragment | GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | TableRowFragment |
TableWrapperFragment => { TableWrapperFragment => {
@ -1017,7 +1042,7 @@ impl Fragment {
} }
/// Returns the intrinsic inline-sizes of this fragment. /// Returns the intrinsic inline-sizes of this fragment.
pub fn intrinsic_inline_sizes(&mut self, inline_fragment_context: Option<InlineFragmentContext>) pub fn intrinsic_inline_sizes(&mut self)
-> IntrinsicISizes { -> IntrinsicISizes {
let mut result = self.style_specified_intrinsic_inline_size(); let mut result = self.style_specified_intrinsic_inline_size();
@ -1046,12 +1071,12 @@ 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.
match inline_fragment_context { match self.inline_context {
None => {} None => {}
Some(context) => { Some(ref context) => {
for range in context.ranges() { for style in context.styles.iter() {
let border_width = range.border().inline_start_end(); let border_width = style.logical_border_width().inline_start_end();
let padding_inline_size = range.padding().inline_start_end(); let padding_inline_size = model::padding_from_style(&**style, Au(0)).inline_start_end();
result.minimum_inline_size = result.minimum_inline_size + border_width + padding_inline_size; result.minimum_inline_size = result.minimum_inline_size + border_width + padding_inline_size;
result.preferred_inline_size = result.preferred_inline_size + border_width + padding_inline_size; result.preferred_inline_size = result.preferred_inline_size + border_width + padding_inline_size;
} }
@ -1265,9 +1290,7 @@ impl Fragment {
/// Assigns replaced inline-size, padding, and margins for this fragment only if it is replaced /// Assigns replaced inline-size, padding, and margins for this fragment only if it is replaced
/// content per CSS 2.1 § 10.3.2. /// content per CSS 2.1 § 10.3.2.
pub fn assign_replaced_inline_size_if_necessary(&mut self, pub fn assign_replaced_inline_size_if_necessary(&mut self,
container_inline_size: Au, container_inline_size: Au) {
inline_fragment_context:
Option<InlineFragmentContext>) {
match self.specific { match self.specific {
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment | GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment |
TableWrapperFragment => return, TableWrapperFragment => return,
@ -1276,7 +1299,7 @@ impl Fragment {
ImageFragment(_) | ScannedTextFragment(_) => {} ImageFragment(_) | ScannedTextFragment(_) => {}
}; };
self.compute_border_padding_margins(container_inline_size, inline_fragment_context); self.compute_border_padding_margins(container_inline_size);
let style_inline_size = self.style().content_inline_size(); let style_inline_size = self.style().content_inline_size();
let style_block_size = self.style().content_block_size(); let style_block_size = self.style().content_block_size();

View file

@ -11,7 +11,6 @@ use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
use flow; use flow;
use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo}; use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
use model::IntrinsicISizes; use model::IntrinsicISizes;
use model;
use text; use text;
use wrapper::ThreadSafeLayoutNode; use wrapper::ThreadSafeLayoutNode;
@ -23,14 +22,12 @@ use gfx::font_context::FontContext;
use gfx::text::glyph::CharIndex; use gfx::text::glyph::CharIndex;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use servo_util::logical_geometry::{LogicalRect, LogicalMargin, LogicalSize}; use servo_util::logical_geometry::{LogicalRect, LogicalSize};
use servo_util::range; use servo_util::range;
use servo_util::range::{EachIndex, Range, RangeIndex, IntRangeIndex}; use servo_util::range::{EachIndex, Range, RangeIndex, IntRangeIndex};
use std::iter::Enumerate;
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use std::num; use std::num;
use std::slice::{Items, MutItems};
use std::u16; use std::u16;
use style::computed_values::{text_align, vertical_align, white_space}; use style::computed_values::{text_align, vertical_align, white_space};
use style::ComputedValues; use style::ComputedValues;
@ -342,7 +339,7 @@ impl LineBreaker {
} }
} }
old_fragments.fixup(mem::replace(&mut self.new_fragments, vec![])); old_fragments.fragments = mem::replace(&mut self.new_fragments, vec![]);
flow.fragments = old_fragments; flow.fragments = old_fragments;
flow.lines = mem::replace(&mut self.lines, Vec::new()); flow.lines = mem::replace(&mut self.lines, Vec::new());
} }
@ -627,43 +624,10 @@ impl LineBreaker {
} }
} }
/// Iterator over fragments.
pub struct FragmentIterator<'a> {
iter: Enumerate<Items<'a,Fragment>>,
ranges: &'a Vec<InlineFragmentRange>,
}
impl<'a> Iterator<(&'a Fragment, InlineFragmentContext<'a>)> for FragmentIterator<'a> {
#[inline]
fn next(&mut self) -> Option<(&'a Fragment, InlineFragmentContext<'a>)> {
self.iter.next().map(|(i, fragment)| {
(fragment, InlineFragmentContext::new(self.ranges, FragmentIndex(i as int)))
})
}
}
/// Mutable iterator over fragments.
pub struct MutFragmentIterator<'a> {
iter: Enumerate<MutItems<'a,Fragment>>,
ranges: &'a Vec<InlineFragmentRange>,
}
impl<'a> Iterator<(&'a mut Fragment, InlineFragmentContext<'a>)> for MutFragmentIterator<'a> {
#[inline]
fn next(&mut self) -> Option<(&'a mut Fragment, InlineFragmentContext<'a>)> {
self.iter.next().map(|(i, fragment)| {
(fragment, InlineFragmentContext::new(self.ranges, FragmentIndex(i as int)))
})
}
}
/// Represents a list of inline fragments, including element ranges. /// Represents a list of inline fragments, including element ranges.
pub struct InlineFragments { pub struct InlineFragments {
/// The fragments themselves. /// The fragments themselves.
pub fragments: Vec<Fragment>, pub fragments: Vec<Fragment>,
/// Tracks the elements that made up the fragments above. This is used to
/// recover the DOM structure from the `fragments` when it's needed.
pub ranges: Vec<InlineFragmentRange>,
} }
impl InlineFragments { impl InlineFragments {
@ -671,7 +635,6 @@ impl InlineFragments {
pub fn new() -> InlineFragments { pub fn new() -> InlineFragments {
InlineFragments { InlineFragments {
fragments: vec![], fragments: vec![],
ranges: vec![],
} }
} }
@ -686,35 +649,14 @@ impl InlineFragments {
} }
/// Pushes a new inline fragment. /// Pushes a new inline fragment.
pub fn push(&mut self, fragment: Fragment, style: Arc<ComputedValues>) { pub fn push(&mut self, fragment: &mut Fragment, style: Arc<ComputedValues>) {
self.ranges.push(InlineFragmentRange::new( fragment.add_inline_context_style(style);
style, Range::new(FragmentIndex(self.fragments.len() as int), FragmentIndex(1)), self.fragments.push(fragment.clone());
));
self.fragments.push(fragment)
} }
/// Merges another set of inline fragments with this one. /// Merges another set of inline fragments with this one.
pub fn push_all(&mut self, InlineFragments { fragments, ranges }: InlineFragments) { pub fn push_all(&mut self, fragments: InlineFragments) {
let adjustment = FragmentIndex(self.fragments.len() as int); self.fragments.push_all_move(fragments.fragments);
self.push_all_ranges(ranges, adjustment);
self.fragments.push_all_move(fragments);
}
/// Returns an iterator that iterates over all fragments along with the appropriate context.
pub fn iter<'a>(&'a self) -> FragmentIterator<'a> {
FragmentIterator {
iter: self.fragments.as_slice().iter().enumerate(),
ranges: &self.ranges,
}
}
/// Returns an iterator that iterates over all fragments along with the appropriate context and
/// allows those fragments to be mutated.
pub fn mut_iter<'a>(&'a mut self) -> MutFragmentIterator<'a> {
MutFragmentIterator {
iter: self.fragments.as_mut_slice().mut_iter().enumerate(),
ranges: &self.ranges,
}
} }
/// A convenience function to return the fragment at a given index. /// A convenience function to return the fragment at a given index.
@ -727,134 +669,6 @@ impl InlineFragments {
self.fragments.get_mut(index) self.fragments.get_mut(index)
} }
/// Adds the given node to the fragment map.
pub fn push_range(&mut self, style: Arc<ComputedValues>, range: Range<FragmentIndex>) {
self.ranges.push(InlineFragmentRange::new(style, range))
}
/// Pushes the ranges in a fragment map, adjusting indices as necessary.
fn push_all_ranges(&mut self, ranges: Vec<InlineFragmentRange>, adjustment: FragmentIndex) {
for other_range in ranges.move_iter() {
let InlineFragmentRange {
style: other_style,
range: mut other_range
} = other_range;
other_range.shift_by(adjustment);
self.push_range(other_style, other_range)
}
}
/// Returns the range with the given index.
pub fn get_mut_range<'a>(&'a mut self, index: FragmentIndex) -> &'a mut InlineFragmentRange {
self.ranges.get_mut(index.to_uint())
}
/// Rebuilds the list after the fragments have been split or deleted (for example, for line
/// breaking). This assumes that the overall structure of the DOM has not changed; if the
/// DOM has changed, then the flow constructor will need to do more complicated surgery than
/// this function can provide.
///
/// FIXME(#2267, pcwalton): It would be more efficient to not have to clone fragments all the time;
/// i.e. if `self.fragments` contained less info than the entire range of fragments. See
/// `layout::construct::strip_ignorable_whitespace_from_start` for an example of some code that
/// needlessly has to clone fragments.
pub fn fixup(&mut self, new_fragments: Vec<Fragment>) {
// TODO(pcwalton): Post Rust upgrade, use `with_capacity` here.
let old_list = mem::replace(&mut self.ranges, vec![]);
let mut worklist = vec![]; // FIXME(#2269, pcwalton): was smallvec4
let mut old_list_iter = old_list.move_iter().peekable();
{ // Enter a new scope so that new_fragments_iter's borrow is released
let mut new_fragments_iter = new_fragments.iter().enumerate().peekable();
// FIXME(#2270, pcwalton): I don't think this will work if multiple old fragments
// correspond to the same node.
for (i, old_fragment) in self.fragments.iter().enumerate() {
let old_fragment_index = FragmentIndex(i as int);
// Find the start of the corresponding new fragment.
let new_fragment_start = match new_fragments_iter.peek() {
Some(&(index, new_fragment)) if new_fragment.node == old_fragment.node => {
// We found the start of the corresponding new fragment.
FragmentIndex(index as int)
}
Some(_) | None => {
// The old fragment got deleted entirely.
continue
}
};
drop(new_fragments_iter.next());
// Eat any additional fragments that the old fragment got split into.
loop {
match new_fragments_iter.peek() {
Some(&(_, new_fragment)) if new_fragment.node == old_fragment.node => {}
Some(_) | None => break,
}
drop(new_fragments_iter.next());
}
// Find all ranges that started at this old fragment and add them onto the worklist.
loop {
match old_list_iter.peek() {
None => break,
Some(fragment_range) => {
if fragment_range.range.begin() > old_fragment_index {
// We haven't gotten to the appropriate old fragment yet, so stop.
break
}
// Note that it can be the case that `fragment_range.range.begin() < i`.
// This is OK, as it corresponds to the case in which a fragment got
// deleted entirely (e.g. ignorable whitespace got nuked). In that case we
// want to keep the range, but shorten it.
}
};
let InlineFragmentRange {
style: style,
range: old_range,
} = old_list_iter.next().unwrap();
worklist.push(InlineFragmentFixupWorkItem {
style: style,
new_start_index: new_fragment_start,
old_end_index: old_range.end(),
});
}
// Pop off any ranges that ended at this fragment.
loop {
match worklist.as_slice().last() {
None => break,
Some(last_work_item) => {
if last_work_item.old_end_index > old_fragment_index + FragmentIndex(1) {
// Haven't gotten to it yet.
break
}
}
}
let new_last_index = match new_fragments_iter.peek() {
None => {
// At the end.
FragmentIndex(new_fragments.len() as int)
}
Some(&(index, _)) => {
FragmentIndex(index as int)
},
};
let InlineFragmentFixupWorkItem {
style,
new_start_index,
..
} = worklist.pop().unwrap();
let range = Range::new(new_start_index, new_last_index - new_start_index);
self.ranges.push(InlineFragmentRange::new(style, range))
}
}
}
self.fragments = new_fragments;
}
/// Strips ignorable whitespace from the start of a list of fragments. /// Strips ignorable whitespace from the start of a list of fragments.
pub fn strip_ignorable_whitespace_from_start(&mut self) { pub fn strip_ignorable_whitespace_from_start(&mut self) {
if self.is_empty() { return }; // Fast path if self.is_empty() { return }; // Fast path
@ -874,7 +688,7 @@ impl InlineFragments {
new_fragments.push(fragment); new_fragments.push(fragment);
} }
self.fixup(new_fragments); self.fragments = new_fragments;
} }
/// Strips ignorable whitespace from the end of a list of fragments. /// Strips ignorable whitespace from the end of a list of fragments.
@ -889,7 +703,8 @@ impl InlineFragments {
drop(new_fragments.pop()); drop(new_fragments.pop());
} }
self.fixup(new_fragments);
self.fragments = new_fragments;
} }
} }
@ -936,17 +751,15 @@ impl InlineFlow {
// not recurse on a line if nothing in it can intersect the dirty region. // not recurse on a line if nothing in it can intersect the dirty region.
debug!("Flow: building display list for {:u} inline fragments", self.fragments.len()); debug!("Flow: building display list for {:u} inline fragments", self.fragments.len());
for (fragment, context) in self.fragments.mut_iter() { for fragment in self.fragments.fragments.mut_iter() {
let rel_offset = fragment.relative_position(&self.base let rel_offset = fragment.relative_position(&self.base
.absolute_position_info .absolute_position_info
.relative_containing_block_size, .relative_containing_block_size);
Some(context));
drop(fragment.build_display_list(&mut self.base.display_list, drop(fragment.build_display_list(&mut self.base.display_list,
layout_context, layout_context,
self.base.abs_position.add_size( self.base.abs_position.add_size(
&rel_offset.to_physical(self.base.writing_mode)), &rel_offset.to_physical(self.base.writing_mode)),
ContentLevel, ContentLevel));
Some(context)));
} }
// TODO(#225): Should `inline-block` elements have flows as children of the inline flow or // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
@ -1095,11 +908,11 @@ impl Flow for InlineFlow {
} }
let mut intrinsic_inline_sizes = IntrinsicISizes::new(); let mut intrinsic_inline_sizes = IntrinsicISizes::new();
for (fragment, context) in self.fragments.mut_iter() { for fragment in self.fragments.fragments.mut_iter() {
debug!("Flow: measuring {}", *fragment); debug!("Flow: measuring {}", *fragment);
let fragment_intrinsic_inline_sizes = let fragment_intrinsic_inline_sizes =
fragment.intrinsic_inline_sizes(Some(context)); fragment.intrinsic_inline_sizes();
intrinsic_inline_sizes.minimum_inline_size = geometry::max( intrinsic_inline_sizes.minimum_inline_size = geometry::max(
intrinsic_inline_sizes.minimum_inline_size, intrinsic_inline_sizes.minimum_inline_size,
fragment_intrinsic_inline_sizes.minimum_inline_size); fragment_intrinsic_inline_sizes.minimum_inline_size);
@ -1123,9 +936,8 @@ impl Flow for InlineFlow {
{ {
let inline_size = self.base.position.size.inline; let inline_size = self.base.position.size.inline;
let this = &mut *self; let this = &mut *self;
for (fragment, context) in this.fragments.mut_iter() { for fragment in this.fragments.fragments.mut_iter() {
fragment.assign_replaced_inline_size_if_necessary(inline_size, fragment.assign_replaced_inline_size_if_necessary(inline_size);
Some(context))
} }
} }
@ -1157,7 +969,7 @@ impl Flow for InlineFlow {
debug!("assign_block_size_inline: floats in: {:?}", self.base.floats); debug!("assign_block_size_inline: floats in: {:?}", self.base.floats);
// assign block-size for inline fragments // assign block-size for inline fragments
for (fragment, _) in self.fragments.mut_iter() { for fragment in self.fragments.fragments.mut_iter() {
fragment.assign_replaced_block_size_if_necessary(); fragment.assign_replaced_block_size_if_necessary();
} }
@ -1305,7 +1117,7 @@ impl Flow for InlineFlow {
impl fmt::Show for InlineFlow { impl fmt::Show for InlineFlow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "InlineFlow")); try!(write!(f, "InlineFlow"));
for (i, (fragment, _)) in self.fragments.iter().enumerate() { for (i, fragment) in self.fragments.fragments.iter().enumerate() {
if i == 0 { if i == 0 {
try!(write!(f, ": {}", fragment)) try!(write!(f, ": {}", fragment))
} else { } else {
@ -1316,102 +1128,15 @@ impl fmt::Show for InlineFlow {
} }
} }
/// Information that inline flows keep about a single nested element. This is used to recover the #[deriving(Clone)]
/// DOM structure from the flat fragment list when it's needed. pub struct InlineFragmentContext {
pub struct InlineFragmentRange { pub styles: Vec<Arc<ComputedValues>>,
/// The style of the DOM node that this range refers to.
pub style: Arc<ComputedValues>,
/// The range, in indices into the fragment list.
pub range: Range<FragmentIndex>,
} }
impl InlineFragmentRange { impl InlineFragmentContext {
/// Creates a new fragment range from the given values. pub fn new() -> InlineFragmentContext {
fn new(style: Arc<ComputedValues>, range: Range<FragmentIndex>) -> InlineFragmentRange {
InlineFragmentRange {
style: style,
range: range,
}
}
/// Returns the dimensions of the border in this fragment range.
#[inline]
pub fn border(&self) -> LogicalMargin<Au> {
self.style.logical_border_width()
}
/// Returns the dimensions of the padding in this fragment range.
#[inline]
pub fn padding(&self) -> LogicalMargin<Au> {
// FIXME(#2266, pcwalton): Is Au(0) right here for the containing block?
model::padding_from_style(&*self.style, Au(0))
}
}
struct InlineFragmentFixupWorkItem {
style: Arc<ComputedValues>,
new_start_index: FragmentIndex,
old_end_index: FragmentIndex,
}
/// The type of an iterator over fragment ranges in the fragment map.
pub struct RangeIterator<'a> {
iter: Items<'a,InlineFragmentRange>,
index: FragmentIndex,
is_first: bool,
}
impl<'a> Iterator<&'a InlineFragmentRange> for RangeIterator<'a> {
fn next(&mut self) -> Option<&'a InlineFragmentRange> {
if !self.is_first {
// Yield the next fragment range if it contains the index
self.iter.next().and_then(|frag_range| {
if frag_range.range.contains(self.index) { Some(frag_range) } else { None }
})
} else {
// Find the first fragment range that contains the index if it exists
let index = self.index;
let first = self.iter.by_ref().find(|frag_range| {
frag_range.range.contains(index)
});
self.is_first = false; // We have made our first iteration
first
}
}
}
/// The context that an inline fragment appears in. This allows the fragment map to be passed in
/// conveniently to various fragment functions.
pub struct InlineFragmentContext<'a> {
ranges: &'a Vec<InlineFragmentRange>,
index: FragmentIndex,
}
impl<'a> InlineFragmentContext<'a> {
pub fn new<'a>(ranges: &'a Vec<InlineFragmentRange>, index: FragmentIndex) -> InlineFragmentContext<'a> {
InlineFragmentContext { InlineFragmentContext {
ranges: ranges, styles: vec!()
index: index,
}
}
/// Iterates over all ranges that contain the fragment at context's index, outermost first.
#[inline(always)]
pub fn ranges(&self) -> RangeIterator<'a> {
// TODO: It would be more straightforward to return an existing iterator
// rather defining our own `RangeIterator`, but this requires unboxed
// closures in order to satisfy the borrow checker:
//
// ~~~rust
// let index = self.index;
// self.ranges.iter()
// .skip_while(|fr| fr.range.contains(index))
// .take_while(|fr| fr.range.contains(index))
// ~~~
RangeIterator {
iter: self.ranges.iter(),
index: self.index,
is_first: true,
} }
} }
} }

View file

@ -75,7 +75,7 @@ impl TextRunScanner {
debug!("TextRunScanner: swapping out fragments."); debug!("TextRunScanner: swapping out fragments.");
fragments.fixup(new_fragments); fragments.fragments = new_fragments;
} }
/// A "clump" is a range of inline flow leaves that can be merged together into a single /// A "clump" is a range of inline flow leaves that can be merged together into a single

View file

@ -103,3 +103,5 @@ experimental == vertical-lr-blocks.html vertical-lr-blocks_ref.html
# FIXME: use the real test when pixel-snapping for scrolling is fixed. # FIXME: use the real test when pixel-snapping for scrolling is fixed.
#== ../html/acid2.html#top acid2_ref_broken.html #== ../html/acid2.html#top acid2_ref_broken.html
flaky_gpu,flaky_linux == acid2_noscroll.html acid2_ref_broken.html flaky_gpu,flaky_linux == acid2_noscroll.html acid2_ref_broken.html
!= inline_background_a.html inline_background_ref.html

View file

@ -0,0 +1,17 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<style type="text/css">
.white {
color: white;
}
.bggreen {
background-color: green;
}
body {
margin: 0;
}
</style>
</head>
<body><span class="bggreen white">White text on a green background</span></body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<style type="text/css">
.white {
color: white;
}
body {
margin: 0;
}
</style>
</head>
<body><span class="white">White text on a green background</span></body>
</html>