diff --git a/components/layout/block.rs b/components/layout/block.rs index ba17b417963d..4a5395b75821 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -20,7 +20,7 @@ use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, Floats, PlacementInfo} use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base}; use flow; -use fragment::{Fragment, ImageFragment, ScannedTextFragment}; +use fragment::{Fragment, ImageFragment, InlineBlockFragment, ScannedTextFragment}; use layout_debug; use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse}; use model::{MarginsCollapseThrough, MaybeAuto, NoCollapsibleMargins, Specified, specified}; @@ -706,7 +706,7 @@ impl BlockFlow { /// and image fragments. fn is_replaced_content(&self) -> bool { match self.fragment.specific { - ScannedTextFragment(_) | ImageFragment(_) => true, + ScannedTextFragment(_) | ImageFragment(_) | InlineBlockFragment(_) => true, _ => false, } } diff --git a/components/layout/construct.rs b/components/layout/construct.rs index e30838acbc5e..d48612375d4a 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -27,6 +27,7 @@ use flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use flow::{Descendants, AbsDescendants}; use flow; use flow_ref::FlowRef; +use fragment::{InlineBlockFragment, InlineBlockFragmentInfo}; use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo}; use fragment::{ImageFragment, ImageFragmentInfo, SpecificFragmentInfo, TableFragment}; use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo}; @@ -266,19 +267,38 @@ impl<'a, 'b> FlowConstructor<'a, 'b> { } } - let mut inline_flow = box InlineFlow::from_fragments((*node).clone(), fragments); - let (ascent, descent) = inline_flow.compute_minimum_ascent_and_descent(self.layout_context.font_context(), &**node.style()); - inline_flow.minimum_block_size_above_baseline = ascent; - inline_flow.minimum_depth_below_baseline = descent; - let mut inline_flow = inline_flow as Box; - TextRunScanner::new().scan_for_runs(self.layout_context.font_context(), inline_flow); - let mut inline_flow = FlowRef::new(inline_flow); - inline_flow.finish(self.layout_context); - - if flow.get().need_anonymous_flow(inline_flow.get()) { - flow_list.push(inline_flow) + // Build a list of all the inline-block fragments before fragments is moved. + let mut inline_block_flows = vec!(); + for f in fragments.fragments.iter() { + match f.specific { + InlineBlockFragment(ref info) => { + inline_block_flows.push(info.flow_ref.clone()); + }, + _ => {} + } + } + + let mut inline_flow_ref = FlowRef::new(box InlineFlow::from_fragments((*node).clone(), fragments)); + + // Add all the inline-block fragments as children of the inline flow. + for inline_block_flow in inline_block_flows.iter() { + inline_flow_ref.add_new_child(inline_block_flow.clone()); + } + + { + let mut inline_flow = inline_flow_ref.get_mut().as_inline(); + let (ascent, descent) = inline_flow.compute_minimum_ascent_and_descent(self.layout_context.font_context(), &**node.style()); + inline_flow.minimum_block_size_above_baseline = ascent; + inline_flow.minimum_depth_below_baseline = descent; + TextRunScanner::new().scan_for_runs(self.layout_context.font_context(), inline_flow); + } + + inline_flow_ref.finish(self.layout_context); + + if flow.get().need_anonymous_flow(inline_flow_ref.get()) { + flow_list.push(inline_flow_ref) } else { - flow.add_new_child(inline_flow) + flow.add_new_child(inline_flow_ref) } } @@ -582,6 +602,27 @@ impl<'a, 'b> FlowConstructor<'a, 'b> { ConstructionItemConstructionResult(construction_item) } + fn build_fragment_for_inline_block(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { + let block_flow_result = self.build_flow_for_block(node); + let block_flow = match block_flow_result { + FlowConstructionResult(block_flow, _) => block_flow, + _ => unreachable!() + }; + + let fragment_info = InlineBlockFragment(InlineBlockFragmentInfo::new(block_flow)); + let mut fragment = Fragment::new_from_specific_info(node, fragment_info); + + let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); + fragment_accumulator.fragments.push(&mut fragment, node.style().clone()); + + let construction_item = InlineFragmentsConstructionItem(InlineFragmentsConstructionResult { + splits: Vec::new(), + fragments: fragment_accumulator.finish(), + abs_descendants: Descendants::new(), + }); + ConstructionItemConstructionResult(construction_item) + } + /// Builds one or more fragments for a node with `display: inline`. This yields an /// `InlineFragmentsConstructionResult`. fn build_fragments_for_inline(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { @@ -840,6 +881,12 @@ impl<'a, 'b> PostorderNodeMutTraversal for FlowConstructor<'a, 'b> { node.set_flow_construction_result(construction_result) } + // Inline-block items contribute inline fragment construction results. + (display::inline_block, float::none, _) => { + let construction_result = self.build_fragment_for_inline_block(node); + node.set_flow_construction_result(construction_result) + } + // Table items contribute table flow construction results. (display::table_caption, _, _) => { let construction_result = self.build_flow_for_table_caption(node); diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 32532003f6ee..ce020f116e95 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -10,8 +10,9 @@ use css::node_style::StyledNode; use construct::FlowConstructor; use context::LayoutContext; use floats::{ClearBoth, ClearLeft, ClearRight, ClearType}; -use flow::Flow; +use flow::{Flow, MutableFlowUtils}; use flow; +use flow_ref::FlowRef; use inline::{InlineFragmentContext, InlineMetrics}; use layout_debug; use model::{Auto, IntrinsicISizes, MaybeAuto, Specified, specified}; @@ -125,6 +126,7 @@ impl> Encodable for Fragment { /// Info specific to the kind of fragment. Keep this enum small. #[deriving(Clone)] pub enum SpecificFragmentInfo { + InlineBlockFragment(InlineBlockFragmentInfo), GenericFragment, ImageFragment(ImageFragmentInfo), IframeFragment(IframeFragmentInfo), @@ -137,6 +139,20 @@ pub enum SpecificFragmentInfo { UnscannedTextFragment(UnscannedTextFragmentInfo), } +/// A fragment that represents an inline-block element. +#[deriving(Clone)] +pub struct InlineBlockFragmentInfo { + pub flow_ref: FlowRef, +} + +impl InlineBlockFragmentInfo { + pub fn new(flow_ref: FlowRef) -> InlineBlockFragmentInfo { + InlineBlockFragmentInfo { + flow_ref: flow_ref, + } + } +} + /// A fragment that represents a replaced content image and its accompanying borders, shadows, etc. #[deriving(Clone)] pub struct ImageFragmentInfo { @@ -454,7 +470,7 @@ impl Fragment { /// replaced elements. fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes { let (use_margins, use_padding) = match self.specific { - GenericFragment | IframeFragment(_) | ImageFragment(_) => (true, true), + GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) => (true, true), TableFragment | TableCellFragment => (false, true), TableWrapperFragment => (true, false), TableRowFragment => (false, false), @@ -1082,7 +1098,7 @@ impl Fragment { text_fragment)) }, GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | TableRowFragment | - TableWrapperFragment => { + TableWrapperFragment | InlineBlockFragment(_) => { // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // should have a real `SERVO_DEBUG` system. debug!("{:?}", self.build_debug_borders_around_fragment(display_list, flow_origin)) @@ -1151,6 +1167,11 @@ impl Fragment { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment | TableWrapperFragment => {} + InlineBlockFragment(ref mut info) => { + let block_flow = info.flow_ref.get_mut().as_block(); + result.minimum_inline_size = geometry::max(result.minimum_inline_size, block_flow.base.intrinsic_inline_sizes.minimum_inline_size); + result.preferred_inline_size = geometry::max(result.preferred_inline_size, block_flow.base.intrinsic_inline_sizes.preferred_inline_size); + }, ImageFragment(ref mut image_fragment_info) => { let image_inline_size = image_fragment_info.image_inline_size(); result.minimum_inline_size = geometry::max(result.minimum_inline_size, image_inline_size); @@ -1191,7 +1212,7 @@ impl Fragment { pub fn content_inline_size(&self) -> Au { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment | - TableWrapperFragment => Au(0), + TableWrapperFragment | InlineBlockFragment(_) => Au(0), ImageFragment(ref image_fragment_info) => { image_fragment_info.computed_inline_size() } @@ -1209,7 +1230,7 @@ impl Fragment { pub fn content_block_size(&self, layout_context: &LayoutContext) -> Au { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment | - TableWrapperFragment => Au(0), + TableWrapperFragment | InlineBlockFragment(_) => Au(0), ImageFragment(ref image_fragment_info) => { image_fragment_info.computed_block_size() } @@ -1246,6 +1267,7 @@ impl Fragment { TableRowFragment | TableWrapperFragment => None, TableColumnFragment(_) => fail!("Table column fragments do not need to split"), UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"), + InlineBlockFragment(_) => fail!("Inline blocks do not get split"), ScannedTextFragment(ref text_fragment_info) => { let mut new_line_pos = self.new_line_pos.clone(); let cur_new_line_pos = new_line_pos.remove(0).unwrap(); @@ -1283,7 +1305,7 @@ impl Fragment { -> Option<(Option, Option, Arc> /* TODO(bjz): remove */)> { match self.specific { GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment => None, + TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) => None, TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"), UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"), ScannedTextFragment(ref text_fragment_info) => { @@ -1390,7 +1412,7 @@ impl Fragment { TableWrapperFragment => return, TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"), UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"), - ImageFragment(_) | ScannedTextFragment(_) => {} + ImageFragment(_) | ScannedTextFragment(_) | InlineBlockFragment(_) => {} }; self.compute_border_padding_margins(container_inline_size); @@ -1400,6 +1422,11 @@ impl Fragment { let noncontent_inline_size = self.border_padding.inline_start_end(); match self.specific { + InlineBlockFragment(ref mut info) => { + let block_flow = info.flow_ref.get_mut().as_block(); + self.border_box.size.inline = block_flow.base.intrinsic_inline_sizes.preferred_inline_size + noncontent_inline_size; + block_flow.base.position.size.inline = self.border_box.size.inline; + } ScannedTextFragment(_) => { // Scanned text fragments will have already had their content inline-sizes assigned by this // point. @@ -1441,7 +1468,7 @@ impl Fragment { TableWrapperFragment => return, TableColumnFragment(_) => fail!("Table column fragments do not have block_size"), UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"), - ImageFragment(_) | ScannedTextFragment(_) => {} + ImageFragment(_) | ScannedTextFragment(_) | InlineBlockFragment(_) => {} } let style_inline_size = self.style().content_inline_size(); @@ -1480,6 +1507,10 @@ impl Fragment { // during flow construction. self.border_box.size.block = self.border_box.size.block + noncontent_block_size } + InlineBlockFragment(ref mut info) => { + let block_flow = info.flow_ref.get_mut().as_block(); + self.border_box.size.block = block_flow.base.position.size.block + noncontent_block_size; + } _ => fail!("should have been handled above"), } } @@ -1501,6 +1532,13 @@ impl Fragment { let line_height = self.calculate_line_height(layout_context); InlineMetrics::from_font_metrics(&text_fragment.run.font_metrics, line_height) } + InlineBlockFragment(ref info) => { + // See CSS 2.1 § 10.8.1. + let block_flow = info.flow_ref.get().as_immutable_block(); + let font_style = text::computed_style_to_font_style(&*self.style); + let font_metrics = text::font_metrics_for_style(layout_context.font_context(), &font_style); + InlineMetrics::from_block_height(&font_metrics, block_flow.base.position.size.block) + } _ => { InlineMetrics { block_size_above_baseline: self.border_box.size.block, @@ -1566,6 +1604,7 @@ impl fmt::Show for Fragment { TableRowFragment => "TableRowFragment", TableWrapperFragment => "TableWrapperFragment", UnscannedTextFragment(_) => "UnscannedTextFragment", + InlineBlockFragment(_) => "InlineBlockFragment", })); try!(write!(f, "bp {}", self.border_padding)); try!(write!(f, " ")); diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 6c22b25746ee..8c86b8fae931 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -9,8 +9,8 @@ use context::LayoutContext; use floats::{FloatLeft, Floats, PlacementInfo}; use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass}; use flow; -use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo}; use layout_debug; +use fragment::{Fragment, InlineBlockFragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo}; use model::IntrinsicISizes; use text; use wrapper::ThreadSafeLayoutNode; @@ -20,6 +20,7 @@ use geom::Rect; use gfx::display_list::ContentLevel; use gfx::font::FontMetrics; use gfx::font_context::FontContext; +use geom::Size2D; use gfx::text::glyph::CharIndex; use servo_util::geometry::Au; use servo_util::geometry; @@ -760,11 +761,18 @@ impl InlineFlow { let rel_offset = fragment.relative_position(&self.base .absolute_position_info .relative_containing_block_size); - drop(fragment.build_display_list(&mut self.base.display_list, + let mut accumulator = fragment.build_display_list(&mut self.base.display_list, layout_context, self.base.abs_position.add_size( &rel_offset.to_physical(self.base.writing_mode)), - ContentLevel)); + ContentLevel); + match fragment.specific { + InlineBlockFragment(ref mut block_flow) => { + let block_flow = block_flow.flow_ref.get_mut(); + accumulator.push_child(&mut self.base.display_list, block_flow); + } + _ => {} + } } // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or @@ -949,17 +957,6 @@ impl Flow for InlineFlow { fragment.assign_replaced_inline_size_if_necessary(inline_size); } } - - assert!(self.base.children.len() == 0, - "InlineFlow: should not have children flows in the current layout implementation."); - - // There are no child contexts, so stop here. - - // TODO(Issue #225): once there are 'inline-block' elements, this won't be - // true. In that case, set the InlineBlockFragment's inline-size to the - // shrink-to-fit inline-size, perform inline flow, and set the block - // flow context's inline-size as the assigned inline-size of the - // 'inline-block' fragment that created this flow before recursing. } /// Calculate and set the block-size of this flow. See CSS 2.1 § 10.6.1. @@ -1118,6 +1115,22 @@ impl Flow for InlineFlow { self.base.floats.translate(LogicalSize::new( self.base.writing_mode, Au::new(0), -self.base.position.size.block)); } + + fn compute_absolute_position(&mut self) { + for f in self.fragments.fragments.mut_iter() { + match f.specific { + InlineBlockFragment(ref mut info) => { + let block_flow = info.flow_ref.get_mut().as_block(); + + // FIXME(#2795): Get the real container size + let container_size = Size2D::zero(); + block_flow.base.abs_position = self.base.abs_position + + f.border_box.start.to_physical(self.base.writing_mode, container_size); + } + _ => {} + } + } + } } impl fmt::Show for InlineFlow { @@ -1166,5 +1179,16 @@ impl InlineMetrics { ascent: font_metrics.ascent, } } + + /// Calculates inline metrics from font metrics and line block-size per CSS 2.1 § 10.8.1. + #[inline] + pub fn from_block_height(font_metrics: &FontMetrics, block_height: Au) -> InlineMetrics { + let leading = block_height - (font_metrics.ascent + font_metrics.descent); + InlineMetrics { + block_size_above_baseline: font_metrics.ascent + leading.scale_by(0.5), + depth_below_baseline: font_metrics.descent + leading.scale_by(0.5), + ascent: font_metrics.ascent + leading.scale_by(0.5), + } + } } diff --git a/tests/ref/basic.list b/tests/ref/basic.list index b54708ebbd0b..7c26bc7b2e1a 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -119,3 +119,6 @@ flaky_gpu,flaky_linux == acid2_noscroll.html acid2_ref_broken.html == iframe/simple.html iframe/simple_ref.html == iframe/multiple_external.html iframe/multiple_external_ref.html == floated_generated_content_a.html floated_generated_content_b.html +== inline_block_margin_a.html inline_block_margin_ref.html +== inline_block_img_a.html inline_block_img_ref.html +== inline_block_baseline_a.html inline_block_baseline_ref.html diff --git a/tests/ref/inline_block_baseline_a.html b/tests/ref/inline_block_baseline_a.html new file mode 100644 index 000000000000..21e75c88583c --- /dev/null +++ b/tests/ref/inline_block_baseline_a.html @@ -0,0 +1,28 @@ + + + + + + +
XX
+ + diff --git a/tests/ref/inline_block_baseline_ref.html b/tests/ref/inline_block_baseline_ref.html new file mode 100644 index 000000000000..728da10f61d2 --- /dev/null +++ b/tests/ref/inline_block_baseline_ref.html @@ -0,0 +1,32 @@ + + + + + + +
+
+ + diff --git a/tests/ref/inline_block_img_a.html b/tests/ref/inline_block_img_a.html new file mode 100644 index 000000000000..a00221117151 --- /dev/null +++ b/tests/ref/inline_block_img_a.html @@ -0,0 +1,36 @@ + + + + + +
XX
+ diff --git a/tests/ref/inline_block_img_ref.html b/tests/ref/inline_block_img_ref.html new file mode 100644 index 000000000000..011220e1f273 --- /dev/null +++ b/tests/ref/inline_block_img_ref.html @@ -0,0 +1,47 @@ + + + + + + +
+
+ +
+
+ + diff --git a/tests/ref/inline_block_margin_a.html b/tests/ref/inline_block_margin_a.html new file mode 100644 index 000000000000..2c1a0b90d168 --- /dev/null +++ b/tests/ref/inline_block_margin_a.html @@ -0,0 +1,28 @@ + + + + + + +
XX
+ + diff --git a/tests/ref/inline_block_margin_ref.html b/tests/ref/inline_block_margin_ref.html new file mode 100644 index 000000000000..0727d9cccdfe --- /dev/null +++ b/tests/ref/inline_block_margin_ref.html @@ -0,0 +1,31 @@ + + + + + + +
+
+ +