diff --git a/components/layout/block.rs b/components/layout/block.rs index 40d5a89244f..66e46c736d7 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -418,7 +418,6 @@ impl<'a> PreorderFlowTraversal for AbsoluteAssignBSizesTraversal<'a> { return true; } - let AbsoluteAssignBSizesTraversal(ref ctx) = *self; block_flow.calculate_abs_block_size_and_margins(*ctx); true @@ -746,48 +745,6 @@ impl BlockFlow { max(self.base.intrinsic_inline_sizes.minimum_inline_size, available_inline_size)) } - /// Collect and update static y-offsets bubbled up by kids. - /// - /// This would essentially give us offsets of all absolutely positioned - /// direct descendants and all fixed descendants, in tree order. - /// - /// Assume that this is called in a bottom-up traversal (specifically, the - /// assign-block-size traversal). So, kids have their flow origin already set. - /// In the case of absolute flow kids, they have their hypothetical box - /// position already set. - fn collect_static_b_offsets_from_kids(&mut self) { - let mut abs_descendant_y_offsets = Vec::new(); - for kid in self.base.child_iter() { - let mut gives_abs_offsets = true; - if kid.is_block_like() { - let kid_block = kid.as_block(); - if kid_block.is_fixed() || kid_block.is_absolutely_positioned() { - // It won't contribute any offsets for descendants because it - // would be the CB for them. - gives_abs_offsets = false; - // Give the offset for the current absolute flow alone. - abs_descendant_y_offsets.push(kid_block.get_hypothetical_block_start_edge()); - } else if kid_block.is_positioned() { - // It won't contribute any offsets because it would be the CB - // for the descendants. - gives_abs_offsets = false; - } - } - - if gives_abs_offsets { - let kid_base = flow::mut_base(kid); - // Avoid copying the offset vector. - let offsets = mem::replace(&mut kid_base.abs_descendants.static_b_offsets, Vec::new()); - // Consume all the static y-offsets bubbled up by kid. - for y_offset in offsets.into_iter() { - // The offsets are wrt the kid flow box. Translate them to current flow. - abs_descendant_y_offsets.push(y_offset + kid_base.position.start.b); - } - } - } - self.base.abs_descendants.static_b_offsets = abs_descendant_y_offsets; - } - /// If this is the root flow, shifts all kids down and adjusts our size to account for /// root flow margins, which should never be collapsed according to CSS § 8.3.1. /// @@ -959,7 +916,7 @@ impl BlockFlow { self.base.flags.set_layers_needed_for_descendants(layers_needed_for_descendants); // Collect various offsets needed by absolutely positioned descendants. - self.collect_static_b_offsets_from_kids(); + (&mut *self as &mut Flow).collect_static_block_offsets_from_children(); // Add in our block-end margin and compute our collapsible margins. let can_collapse_block_end_margin_with_kids = @@ -1296,7 +1253,7 @@ impl BlockFlow { /// During normal layout assign-block-size, the absolute flow's position is /// roughly set to its static position (the position it would have had in /// the normal flow). - fn get_hypothetical_block_start_edge(&self) -> Au { + pub fn get_hypothetical_block_start_edge(&self) -> Au { self.base.position.start.b } @@ -1307,21 +1264,22 @@ impl BlockFlow { /// and the code for block layout is significantly simpler. #[inline(always)] pub fn propagate_assigned_inline_size_to_children(&mut self, - inline_start_content_edge: Au, - content_inline_size: Au, - opt_col_inline_sizes: Option>) { + inline_start_content_edge: Au, + content_inline_size: Au, + opt_col_inline_sizes: Option>) { // Keep track of whether floats could impact each child. let mut inline_start_floats_impact_child = self.base.flags.impacted_by_left_floats(); let mut inline_end_floats_impact_child = self.base.flags.impacted_by_right_floats(); let absolute_static_i_offset = if self.is_positioned() { - // This flow is the containing block. The static X offset will be the inline-start padding - // edge. + // This flow is the containing block. The static inline offset will be the inline-start + // padding edge. self.fragment.border_padding.inline_start - self.fragment.style().logical_border_width().inline_start } else { - // For kids, the inline-start margin edge will be at our inline-start content edge. The current static - // offset is at our inline-start margin edge. So move in to the inline-start content edge. + // For kids, the inline-start margin edge will be at our inline-start content edge. The + // current static offset is at our inline-start margin edge. So move in to the + // inline-start content edge. self.base.absolute_static_i_offset + inline_start_content_edge }; @@ -1355,12 +1313,11 @@ impl BlockFlow { }; for (i, kid) in self.base.child_iter().enumerate() { - flow::mut_base(kid).block_container_explicit_block_size = explicit_content_size; - - if kid.is_block_flow() { - let kid_block = kid.as_block(); - kid_block.base.absolute_static_i_offset = absolute_static_i_offset; - kid_block.base.fixed_static_i_offset = fixed_static_i_offset; + { + let mut kid_base = flow::mut_base(kid); + kid_base.block_container_explicit_block_size = explicit_content_size; + kid_base.absolute_static_i_offset = absolute_static_i_offset; + kid_base.fixed_static_i_offset = fixed_static_i_offset; } match kid.float_kind() { @@ -1824,17 +1781,35 @@ impl Flow for BlockFlow { fn is_absolute_containing_block(&self) -> bool { self.is_positioned() } + + fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { + if self.is_absolutely_positioned() && + self.fragment.style().logical_position().inline_start == LPA_Auto && + self.fragment.style().logical_position().inline_end == LPA_Auto { + self.base.position.start.i = inline_position + } + } + + fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { + if self.is_absolutely_positioned() && + self.fragment.style().logical_position().block_start == LPA_Auto && + self.fragment.style().logical_position().block_end == LPA_Auto { + self.base.position.start.b = block_position + } + } } impl fmt::Show for BlockFlow { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(write!(f, "BlockFlow")); if self.is_float() { - write!(f, "FloatFlow: {}", self.fragment) + try!(write!(f, "(Float)")); } else if self.is_root() { - write!(f, "RootFlow: {}", self.fragment) - } else { - write!(f, "BlockFlow: {}", self.fragment) + try!(write!(f, "(Root)")); + } else if self.is_absolutely_positioned() { + try!(write!(f, "(Absolute)")); } + write!(f, ": {} ({})", self.fragment, self.base) } } diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 818fa1602e6..83185801772 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -27,12 +27,13 @@ use flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use flow::{Descendants, AbsDescendants}; use flow; use flow_ref::FlowRef; -use fragment::{InlineBlockFragment, InlineBlockFragmentInfo, InputFragment}; -use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo}; -use fragment::{ImageFragment, ImageFragmentInfo, SpecificFragmentInfo, TableFragment}; -use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo}; +use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment}; +use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment}; +use fragment::{InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragment}; +use fragment::{InlineBlockFragmentInfo, InputFragment, InputFragmentInfo, SpecificFragmentInfo}; +use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo, TableFragment}; use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment}; -use fragment::{UnscannedTextFragmentInfo, InputFragmentInfo}; +use fragment::{UnscannedTextFragmentInfo}; use inline::{InlineFragments, InlineFlow}; use parallel; use table_wrapper::TableWrapperFlow; @@ -311,14 +312,16 @@ impl<'a> FlowConstructor<'a> { 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()); - }, + InlineBlockFragment(ref info) => inline_block_flows.push(info.flow_ref.clone()), + InlineAbsoluteHypotheticalFragment(ref info) => { + inline_block_flows.push(info.flow_ref.clone()) + } _ => {} } } - let mut inline_flow_ref = FlowRef::new(box InlineFlow::from_fragments((*node).clone(), fragments)); + 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() { @@ -327,7 +330,9 @@ impl<'a> FlowConstructor<'a> { { let 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()); + 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); @@ -362,21 +367,20 @@ impl<'a> FlowConstructor<'a> { } else if flow.get().need_anonymous_flow(kid_flow.get()) { consecutive_siblings.push(kid_flow) } else { - // Flush any inline fragments that we were gathering up. This allows us to handle - // {ib} splits. + // Flush any inline fragments that we were gathering up. This allows us to + // handle {ib} splits. debug!("flushing {} inline box(es) to flow A", inline_fragment_accumulator.fragments.len()); self.flush_inline_fragments_to_flow_or_list( - mem::replace(inline_fragment_accumulator, InlineFragmentsAccumulator::new()), + mem::replace(inline_fragment_accumulator, + InlineFragmentsAccumulator::new()), flow, consecutive_siblings, StripWhitespaceFromStart, node); if !consecutive_siblings.is_empty() { let consecutive_siblings = mem::replace(consecutive_siblings, vec!()); - self.generate_anonymous_missing_child(consecutive_siblings, - flow, - node); + self.generate_anonymous_missing_child(consecutive_siblings, flow, node); } flow.add_new_child(kid_flow); } @@ -431,13 +435,15 @@ impl<'a> FlowConstructor<'a> { inline_fragment_accumulator.fragments.push_all(successor_fragments); abs_descendants.push_descendants(kid_abs_descendants); } - ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node, whitespace_style)) => { + ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node, + whitespace_style)) => { // Add whitespace results. They will be stripped out later on when // 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 mut fragment = Fragment::from_opaque_node_and_style(whitespace_node, - whitespace_style, - fragment_info); + whitespace_style, + fragment_info); inline_fragment_accumulator.fragments.push(&mut fragment); } ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(_)) => { @@ -501,16 +507,16 @@ impl<'a> FlowConstructor<'a> { // The flow is done. flow.finish(self.layout_context); + + // Set up the absolute descendants. let is_positioned = flow.get_mut().as_block().is_positioned(); - let is_fixed_positioned = flow.get_mut().as_block().is_fixed(); let is_absolutely_positioned = flow.get_mut().as_block().is_absolutely_positioned(); if is_positioned { - // This is the CB for all the absolute descendants. + // This is the containing block for all the absolute descendants. flow.set_absolute_descendants(abs_descendants); abs_descendants = Descendants::new(); - - if is_fixed_positioned || is_absolutely_positioned { + if is_absolutely_positioned { // This is now the only absolute flow in the subtree which hasn't yet // reached its CB. abs_descendants.push(flow.clone()); @@ -689,6 +695,31 @@ impl<'a> FlowConstructor<'a> { ConstructionItemConstructionResult(construction_item) } + /// This is an annoying case, because the computed `display` value is `block`, but the + /// hypothetical box is inline. + fn build_fragment_for_absolutely_positioned_inline(&mut self, node: &ThreadSafeLayoutNode) + -> ConstructionResult { + let block_flow_result = self.build_flow_for_nonfloated_block(node); + let (block_flow, abs_descendants) = match block_flow_result { + FlowConstructionResult(block_flow, abs_descendants) => (block_flow, abs_descendants), + _ => unreachable!() + }; + + let fragment_info = InlineAbsoluteHypotheticalFragment( + InlineAbsoluteHypotheticalFragmentInfo::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); + + let construction_item = InlineFragmentsConstructionItem(InlineFragmentsConstructionResult { + splits: Vec::new(), + fragments: fragment_accumulator.finish(), + abs_descendants: abs_descendants, + }); + 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 { @@ -913,7 +944,13 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { } Some(ElementNodeTypeId(_)) => { let style = node.style(); - (style.get_box().display, style.get_box().float, style.get_box().position) + let munged_display = if style.get_box()._servo_display_for_hypothetical_box == + display::inline { + display::inline + } else { + style.get_box().display + }; + (munged_display, style.get_box().float, style.get_box().position) } Some(TextNodeTypeId) => (display::inline, float::none, position::static_), Some(CommentNodeTypeId) | @@ -953,10 +990,18 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { node.set_flow_construction_result(self.build_flow_for_nonfloated_block(node)) } + // Inline items that are absolutely-positioned contribute inline fragment construction + // results with a hypothetical fragment. + (display::inline, _, position::absolute) => { + let construction_result = + self.build_fragment_for_absolutely_positioned_inline(node); + node.set_flow_construction_result(construction_result) + } + // Inline items contribute inline fragment construction results. // // FIXME(pcwalton, #3307): This is not sufficient to handle floated generated content. - (display::inline, _, _) => { + (display::inline, float::none, _) => { let construction_result = self.build_fragments_for_inline(node); node.set_flow_construction_result(construction_result) } diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 47dbf7e76c2..912d1bd6b1f 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -279,7 +279,7 @@ pub trait Flow: fmt::Show + ToString + Sync { self.positioning() == position::absolute || self.is_fixed() } - /// Return true if this is the root of an Absolute flow tree. + /// Return true if this is the root of an absolute flow tree. fn is_root_of_absolute_flow_tree(&self) -> bool { false } @@ -289,6 +289,14 @@ pub trait Flow: fmt::Show + ToString + Sync { false } + /// Updates the inline position of a child flow during the assign-height traversal. At present, + /// this is only used for absolutely-positioned inline-blocks. + fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au); + + /// Updates the block position of a child flow during the assign-height traversal. At present, + /// this is only used for absolutely-positioned inline-blocks. + fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au); + /// Return the dimensions of the containing block generated by this flow for absolutely- /// positioned descendants. For block flows, this is the padding box. fn generated_containing_block_rect(&self) -> LogicalRect { @@ -426,6 +434,16 @@ pub trait MutableFlowUtils { /// Builds the display lists for this flow. fn build_display_list(self, layout_context: &LayoutContext); + + /// Gathers static block-offsets bubbled up by kids. + /// + /// This essentially gives us offsets of all absolutely positioned direct descendants and all + /// fixed descendants, in tree order. + /// + /// This is called in a bottom-up traversal (specifically, the assign-block-size traversal). + /// So, kids have their flow origin already set. In the case of absolute flow kids, they have + /// their hypothetical box position already set. + fn collect_static_block_offsets_from_children(&mut self); } pub trait MutableOwnedFlowUtils { @@ -580,15 +598,15 @@ pub struct Descendants { /// layout. descendant_links: Vec, - /// Static y offsets of all descendants from the start of this flow box. - pub static_b_offsets: Vec, + /// Static block-direction offsets of all descendants from the start of this flow box. + pub static_block_offsets: Vec, } impl Descendants { pub fn new() -> Descendants { Descendants { descendant_links: Vec::new(), - static_b_offsets: Vec::new(), + static_block_offsets: Vec::new(), } } @@ -621,7 +639,7 @@ impl Descendants { let descendant_iter = DescendantIter { iter: self.descendant_links.slice_from_mut(0).iter_mut(), }; - descendant_iter.zip(self.static_b_offsets.slice_from_mut(0).iter_mut()) + descendant_iter.zip(self.static_block_offsets.slice_from_mut(0).iter_mut()) } } @@ -752,6 +770,16 @@ pub struct BaseFlow { pub writing_mode: WritingMode, } +impl fmt::Show for BaseFlow { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, + "CC {}, ADC {}, CADC {}", + self.parallel.children_count.load(SeqCst), + self.abs_descendants.len(), + self.parallel.children_and_absolute_descendant_count.load(SeqCst)) + } +} + impl> Encodable for BaseFlow { fn encode(&self, e: &mut S) -> Result<(), E> { e.emit_struct("base", 0, |e| { @@ -1103,6 +1131,52 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a { } } } + + /// Collect and update static y-offsets bubbled up by kids. + /// + /// This would essentially give us offsets of all absolutely positioned + /// direct descendants and all fixed descendants, in tree order. + /// + /// Assume that this is called in a bottom-up traversal (specifically, the + /// assign-block-size traversal). So, kids have their flow origin already set. + /// In the case of absolute flow kids, they have their hypothetical box + /// position already set. + fn collect_static_block_offsets_from_children(&mut self) { + let mut absolute_descendant_block_offsets = Vec::new(); + for kid in mut_base(*self).child_iter() { + let mut gives_absolute_offsets = true; + if kid.is_block_like() { + let kid_block = kid.as_block(); + if kid_block.is_fixed() || kid_block.is_absolutely_positioned() { + // It won't contribute any offsets for descendants because it would be the + // containing block for them. + gives_absolute_offsets = false; + // Give the offset for the current absolute flow alone. + absolute_descendant_block_offsets.push( + kid_block.get_hypothetical_block_start_edge()); + } else if kid_block.is_positioned() { + // It won't contribute any offsets because it would be the containing block + // for the descendants. + gives_absolute_offsets = false; + } + } + + if gives_absolute_offsets { + let kid_base = mut_base(kid); + // Avoid copying the offset vector. + let offsets = mem::replace(&mut kid_base.abs_descendants.static_block_offsets, + Vec::new()); + // Consume all the static block-offsets bubbled up by kids. + for block_offset in offsets.into_iter() { + // The offsets are with respect to the kid flow's fragment. Translate them to + // that of the current flow. + absolute_descendant_block_offsets.push( + block_offset + kid_base.position.start.b); + } + } + } + mut_base(*self).abs_descendants.static_block_offsets = absolute_descendant_block_offsets + } } impl MutableOwnedFlowUtils for FlowRef { diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 7c2ef5c4a05..fb4b6fd96e5 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -131,6 +131,11 @@ pub enum SpecificFragmentInfo { GenericFragment, IframeFragment(IframeFragmentInfo), ImageFragment(ImageFragmentInfo), + + /// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was + /// declared with `display: inline;`. + InlineAbsoluteHypotheticalFragment(InlineAbsoluteHypotheticalFragmentInfo), + InlineBlockFragment(InlineBlockFragmentInfo), InputFragment(InputFragmentInfo), ScannedTextFragment(ScannedTextFragmentInfo), @@ -142,7 +147,28 @@ pub enum SpecificFragmentInfo { UnscannedTextFragment(UnscannedTextFragmentInfo), } +/// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was declared +/// with `display: inline;`. +/// +/// FIXME(pcwalton): Stop leaking this `FlowRef` to layout; that is not memory safe because layout +/// can clone it. +#[deriving(Clone)] +pub struct InlineAbsoluteHypotheticalFragmentInfo { + pub flow_ref: FlowRef, +} + +impl InlineAbsoluteHypotheticalFragmentInfo { + pub fn new(flow_ref: FlowRef) -> InlineAbsoluteHypotheticalFragmentInfo { + InlineAbsoluteHypotheticalFragmentInfo { + flow_ref: flow_ref, + } + } +} + /// A fragment that represents an inline-block element. +/// +/// FIXME(pcwalton): Stop leaking this `FlowRef` to layout; that is not memory safe because layout +/// can clone it. #[deriving(Clone)] pub struct InlineBlockFragmentInfo { pub flow_ref: FlowRef, @@ -512,8 +538,8 @@ impl Fragment { self.inline_context.as_mut().unwrap().styles.push(style.clone()); } - /// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text or - /// replaced elements. + /// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text + /// or replaced elements. fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes { let (use_margins, use_padding) = match self.specific { GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) | @@ -521,14 +547,16 @@ impl Fragment { TableFragment | TableCellFragment => (false, true), TableWrapperFragment => (true, false), TableRowFragment => (false, false), - ScannedTextFragment(_) | TableColumnFragment(_) | UnscannedTextFragment(_) => { + ScannedTextFragment(_) | TableColumnFragment(_) | UnscannedTextFragment(_) | + InlineAbsoluteHypotheticalFragment(_) => { // Styles are irrelevant for these kinds of fragments. return IntrinsicISizes::new() } }; let style = self.style(); - let inline_size = MaybeAuto::from_style(style.content_inline_size(), Au::new(0)).specified_or_zero(); + let inline_size = MaybeAuto::from_style(style.content_inline_size(), + Au(0)).specified_or_zero(); let margin = style.logical_margin(); let (margin_inline_start, margin_inline_end) = if use_margins { @@ -1147,7 +1175,8 @@ impl Fragment { text_fragment)) } GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) => { + TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) | + InlineAbsoluteHypotheticalFragment(_) => { // 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)) @@ -1216,7 +1245,8 @@ impl Fragment { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | - TableColumnFragment(_) | TableRowFragment | TableWrapperFragment => {} + TableColumnFragment(_) | TableRowFragment | TableWrapperFragment | + InlineAbsoluteHypotheticalFragment(_) => {} InlineBlockFragment(ref mut info) => { let block_flow = info.flow_ref.get_mut().as_block(); result.minimum_inline_size = max(result.minimum_inline_size, @@ -1225,7 +1255,7 @@ impl Fragment { result.preferred_inline_size = max(result.preferred_inline_size, block_flow.base.intrinsic_inline_sizes.preferred_inline_size + block_flow.base.intrinsic_inline_sizes.surround_inline_size); - }, + } ImageFragment(ref mut image_fragment_info) => { let image_inline_size = image_fragment_info.image_inline_size(); result.minimum_inline_size = max(result.minimum_inline_size, image_inline_size); @@ -1283,8 +1313,9 @@ impl Fragment { /// TODO: What exactly does this function return? Why is it Au(0) for GenericFragment? pub fn content_inline_size(&self) -> Au { match self.specific { - GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment | - TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) => Au(0), + GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | + TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | + InputFragment(_) | InlineAbsoluteHypotheticalFragment(_) => Au(0), ImageFragment(ref image_fragment_info) => { image_fragment_info.computed_inline_size() } @@ -1303,7 +1334,7 @@ impl Fragment { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | - InputFragment(_) => Au(0), + InputFragment(_) | InlineAbsoluteHypotheticalFragment(_) => Au(0), ImageFragment(ref image_fragment_info) => { image_fragment_info.computed_block_size() } @@ -1340,7 +1371,9 @@ impl Fragment { TableRowFragment | TableWrapperFragment | InputFragment(_) => 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"), + InlineBlockFragment(_) | InlineAbsoluteHypotheticalFragment(_) => { + fail!("Inline blocks or inline absolute hypothetical fragments 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(); @@ -1378,7 +1411,8 @@ impl Fragment { -> Option<(Option, Option, Arc> /* TODO(bjz): remove */)> { match self.specific { GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) => None, + TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) | + InlineAbsoluteHypotheticalFragment(_) => 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) => { @@ -1487,7 +1521,8 @@ impl Fragment { UnscannedTextFragment(_) => { fail!("Unscanned text fragments should have been scanned by now!") } - ImageFragment(_) | ScannedTextFragment(_) | InlineBlockFragment(_) => {} + ImageFragment(_) | ScannedTextFragment(_) | InlineBlockFragment(_) | + InlineAbsoluteHypotheticalFragment(_) => {} }; self.compute_border_padding_margins(container_inline_size); @@ -1501,6 +1536,15 @@ impl Fragment { let noncontent_inline_size = self.border_padding.inline_start_end(); match self.specific { + InlineAbsoluteHypotheticalFragment(ref mut info) => { + let block_flow = info.flow_ref.get_mut().as_block(); + block_flow.base.position.size.inline = + block_flow.base.intrinsic_inline_sizes.preferred_inline_size + + block_flow.base.intrinsic_inline_sizes.surround_inline_size; + + // This is a hypothetical box, so it takes up no space. + self.border_box.size.inline = Au(0); + } 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 + @@ -1570,7 +1614,8 @@ impl Fragment { UnscannedTextFragment(_) => { fail!("Unscanned text fragments should have been scanned by now!") } - ImageFragment(_) | ScannedTextFragment(_) | InlineBlockFragment(_) => {} + ImageFragment(_) | ScannedTextFragment(_) | InlineBlockFragment(_) | + InlineAbsoluteHypotheticalFragment(_) => {} } let style_block_size = self.style().content_block_size(); @@ -1616,6 +1661,11 @@ impl Fragment { let block_flow = info.flow_ref.get_mut().as_block(); self.border_box.size.block = block_flow.base.position.size.block; } + InlineAbsoluteHypotheticalFragment(ref mut info) => { + // Not the primary fragment, so we do not take the noncontent size into account. + let block_flow = info.flow_ref.get_mut().as_block(); + self.border_box.size.block = block_flow.base.position.size.block; + } _ => fail!("should have been handled above"), } } @@ -1641,7 +1691,16 @@ impl Fragment { // 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); + 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) + } + InlineAbsoluteHypotheticalFragment(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) } _ => { @@ -1704,12 +1763,33 @@ impl Fragment { /// because the corresponding table flow is the primary fragment. fn is_primary_fragment(&self) -> bool { match self.specific { - InlineBlockFragment(_) | TableWrapperFragment => false, + InlineBlockFragment(_) | InlineAbsoluteHypotheticalFragment(_) | + TableWrapperFragment => false, GenericFragment | IframeFragment(_) | ImageFragment(_) | ScannedTextFragment(_) | TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment | UnscannedTextFragment(_) | InputFragment(_) => true, } } + + pub fn update_late_computed_inline_position_if_necessary(&mut self) { + match self.specific { + InlineAbsoluteHypotheticalFragment(ref mut info) => { + let position = self.border_box.start.i; + info.flow_ref.get_mut().update_late_computed_inline_position_if_necessary(position) + } + _ => {} + } + } + + pub fn update_late_computed_block_position_if_necessary(&mut self) { + match self.specific { + InlineAbsoluteHypotheticalFragment(ref mut info) => { + let position = self.border_box.start.b; + info.flow_ref.get_mut().update_late_computed_block_position_if_necessary(position) + } + _ => {} + } + } } impl fmt::Show for Fragment { @@ -1720,6 +1800,9 @@ impl fmt::Show for Fragment { GenericFragment => "GenericFragment", IframeFragment(_) => "IframeFragment", ImageFragment(_) => "ImageFragment", + InlineAbsoluteHypotheticalFragment(_) => "InlineAbsoluteHypotheticalFragment", + InlineBlockFragment(_) => "InlineBlockFragment", + InputFragment(_) => "InputFragment", ScannedTextFragment(_) => "ScannedTextFragment", TableFragment => "TableFragment", TableCellFragment => "TableCellFragment", @@ -1727,8 +1810,6 @@ impl fmt::Show for Fragment { TableRowFragment => "TableRowFragment", TableWrapperFragment => "TableWrapperFragment", UnscannedTextFragment(_) => "UnscannedTextFragment", - InlineBlockFragment(_) => "InlineBlockFragment", - InputFragment(_) => "InputFragment", })); try!(write!(f, "bp {}", self.border_padding)); try!(write!(f, " ")); diff --git a/components/layout/inline.rs b/components/layout/inline.rs index d937a6cef53..86522e1d958 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -7,10 +7,11 @@ use css::node_style::StyledNode; use context::LayoutContext; use floats::{FloatLeft, Floats, PlacementInfo}; -use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass}; +use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils}; use flow; +use fragment::{Fragment, InlineBlockFragment, ScannedTextFragment, ScannedTextFragmentInfo}; +use fragment::{SplitInfo}; use layout_debug; -use fragment::{Fragment, InlineBlockFragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo}; use model::IntrinsicISizes; use text; use wrapper::ThreadSafeLayoutNode; @@ -875,16 +876,16 @@ impl InlineFlow { } } - /// Sets fragment X positions based on alignment for one line. - fn set_horizontal_fragment_positions(fragments: &mut InlineFragments, - line: &Line, - line_align: text_align::T) { + /// Sets fragment positions in the inline direction based on alignment for one line. + fn set_inline_fragment_positions(fragments: &mut InlineFragments, + line: &Line, + line_align: text_align::T) { // Figure out how much inline-size we have. let slack_inline_size = Au::max(Au(0), line.green_zone.inline - line.bounds.size.inline); - // Set the fragment x positions based on that alignment. - let mut offset_x = line.bounds.start.i; - offset_x = offset_x + match line_align { + // Set the fragment inline positions based on that alignment. + let mut offset = line.bounds.start.i; + offset = offset + match line_align { // So sorry, but justified text is more complicated than shuffling line // coordinates. // @@ -897,10 +898,41 @@ impl InlineFlow { for i in each_fragment_index(&line.range) { let fragment = fragments.get_mut(i.to_uint()); let size = fragment.border_box.size; - fragment.border_box = LogicalRect::new( - fragment.style.writing_mode, offset_x, fragment.border_box.start.b, - size.inline, size.block); - offset_x = offset_x + size.inline; + fragment.border_box = LogicalRect::new(fragment.style.writing_mode, + offset, + fragment.border_box.start.b, + size.inline, + size.block); + fragment.update_late_computed_inline_position_if_necessary(); + offset = offset + size.inline; + } + } + + /// Sets final fragment positions in the block direction for one line. Assumes that + /// the fragment positions were initially set to the distance from the baseline first. + fn set_block_fragment_positions(fragments: &mut InlineFragments, + line: &Line, + line_distance_from_flow_block_start: Au, + baseline_distance_from_block_start: Au, + largest_depth_below_baseline: Au) { + for fragment_i in each_fragment_index(&line.range) { + let fragment = fragments.get_mut(fragment_i.to_uint()); + match fragment.vertical_align() { + vertical_align::top => { + fragment.border_box.start.b = fragment.border_box.start.b + + line_distance_from_flow_block_start + } + vertical_align::bottom => { + fragment.border_box.start.b = fragment.border_box.start.b + + line_distance_from_flow_block_start + baseline_distance_from_block_start + + largest_depth_below_baseline + } + _ => { + fragment.border_box.start.b = fragment.border_box.start.b + + line_distance_from_flow_block_start + baseline_distance_from_block_start + } + } + fragment.update_late_computed_block_position_if_necessary(); } } @@ -1014,6 +1046,10 @@ impl Flow for InlineFlow { fn assign_block_size(&mut self, ctx: &LayoutContext) { let _scope = layout_debug_scope!("inline::assign_block_size {:s}", self.base.debug_id()); + // Collect various offsets needed by absolutely positioned inline-block or hypothetical + // absolute descendants. + (&mut *self as &mut Flow).collect_static_block_offsets_from_children(); + // Divide the fragments into lines. // // TODO(#226): Get the CSS `line-block-size` property from the containing block's style to @@ -1040,10 +1076,10 @@ impl Flow for InlineFlow { // Now, go through each line and lay out the fragments inside. let mut line_distance_from_flow_block_start = Au(0); for line in self.lines.iter_mut() { - // Lay out fragments horizontally. - InlineFlow::set_horizontal_fragment_positions(&mut self.fragments, line, text_align); + // Lay out fragments in the inline direction. + InlineFlow::set_inline_fragment_positions(&mut self.fragments, line, text_align); - // Set the block-start y position of the current line. + // Set the block-start position of the current line. // `line_height_offset` is updated at the end of the previous loop. line.bounds.start.b = line_distance_from_flow_block_start; @@ -1053,8 +1089,8 @@ impl Flow for InlineFlow { // Calculate the largest block-size among fragments with 'top' and 'bottom' values // respectively. - let (mut largest_block_size_for_top_fragments, mut largest_block_size_for_bottom_fragments) = - (Au(0), Au(0)); + let (mut largest_block_size_for_top_fragments, + mut largest_block_size_for_bottom_fragments) = (Au(0), Au(0)); for fragment_i in each_fragment_index(&line.range) { let fragment = self.fragments.fragments.get_mut(fragment_i.to_uint()); @@ -1128,24 +1164,11 @@ impl Flow for InlineFlow { // Compute the final positions in the block direction of each fragment. Recall that // `fragment.border_box.start.b` was set to the distance from the baseline above. - for fragment_i in each_fragment_index(&line.range) { - let fragment = self.fragments.get_mut(fragment_i.to_uint()); - match fragment.vertical_align() { - vertical_align::top => { - fragment.border_box.start.b = fragment.border_box.start.b + - line_distance_from_flow_block_start - } - vertical_align::bottom => { - fragment.border_box.start.b = fragment.border_box.start.b + - line_distance_from_flow_block_start + baseline_distance_from_block_start + - largest_depth_below_baseline - } - _ => { - fragment.border_box.start.b = fragment.border_box.start.b + - line_distance_from_flow_block_start + baseline_distance_from_block_start - } - } - } + InlineFlow::set_block_fragment_positions(&mut self.fragments, + line, + line_distance_from_flow_block_start, + baseline_distance_from_block_start, + largest_depth_below_baseline); // This is used to set the block-start y position of the next line in the next loop. line.bounds.size.block = largest_block_size_above_baseline + largest_depth_below_baseline; @@ -1170,13 +1193,18 @@ impl Flow for InlineFlow { // 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); + block_flow.base.abs_position = + self.base.abs_position + + f.border_box.start.to_physical(self.base.writing_mode, container_size); } _ => {} } } } + + fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {} + + fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {} } impl fmt::Show for InlineFlow { @@ -1189,7 +1217,7 @@ impl fmt::Show for InlineFlow { try!(write!(f, ", {}", fragment)) } } - Ok(()) + write!(f, " ({})", self.base) } } diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index 9bd0ea43094..0535998ee65 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -521,8 +521,9 @@ fn compute_absolute_position(unsafe_flow: UnsafeFlow, // Compute the absolute position for the flow. flow.get_mut().compute_absolute_position(); - // Count the number of absolutely-positioned children, so that we can subtract it from - // from `children_and_absolute_descendant_count` to get the number of real children. + // If we are the containing block, count the number of absolutely-positioned children, so + // that we don't double-count them in the `children_and_absolute_descendant_count` + // reference count. let mut absolutely_positioned_child_count = 0u; for kid in flow::child_iter(flow.get_mut()) { if kid.is_absolutely_positioned() { @@ -530,13 +531,12 @@ fn compute_absolute_position(unsafe_flow: UnsafeFlow, } } - // Don't enqueue absolutely positioned children. drop(flow::mut_base(flow.get_mut()).parallel .children_and_absolute_descendant_count .fetch_sub(absolutely_positioned_child_count as int, SeqCst)); - // Possibly enqueue the children. + // Enqueue all non-absolutely-positioned children. for kid in flow::child_iter(flow.get_mut()) { if !kid.is_absolutely_positioned() { had_descendants = true; @@ -559,8 +559,7 @@ fn compute_absolute_position(unsafe_flow: UnsafeFlow, // If there were no more descendants, start building the display list. if !had_descendants { - build_display_list(mut_owned_flow_to_unsafe_flow(flow), - proxy) + build_display_list(mut_owned_flow_to_unsafe_flow(flow), proxy) } } } diff --git a/components/layout/table.rs b/components/layout/table.rs index 92e069f1c5c..6be3d900020 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -315,6 +315,14 @@ impl Flow for TableFlow { fn generated_containing_block_rect(&self) -> LogicalRect { self.block_flow.generated_containing_block_rect() } + + fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { + self.block_flow.update_late_computed_inline_position_if_necessary(inline_position) + } + + fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { + self.block_flow.update_late_computed_block_position_if_necessary(block_position) + } } impl fmt::Show for TableFlow { diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs index 8c1dba3e7ca..ddebb01ce3d 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -12,6 +12,7 @@ use context::LayoutContext; use flow::{TableCaptionFlowClass, FlowClass, Flow}; use wrapper::ThreadSafeLayoutNode; +use servo_util::geometry::Au; use std::fmt; /// A table formatting context. @@ -64,6 +65,14 @@ impl Flow for TableCaptionFlow { fn compute_absolute_position(&mut self) { self.block_flow.compute_absolute_position() } + + fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { + self.block_flow.update_late_computed_inline_position_if_necessary(inline_position) + } + + fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { + self.block_flow.update_late_computed_block_position_if_necessary(block_position) + } } impl fmt::Show for TableCaptionFlow { diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 69ae3d83d81..6aeb6b8f8e9 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -123,6 +123,14 @@ impl Flow for TableCellFlow { fn compute_absolute_position(&mut self) { self.block_flow.compute_absolute_position() } + + fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { + self.block_flow.update_late_computed_inline_position_if_necessary(inline_position) + } + + fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { + self.block_flow.update_late_computed_block_position_if_necessary(block_position) + } } impl fmt::Show for TableCellFlow { diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs index ab206c5da18..ae49c9a78d3 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -80,6 +80,10 @@ impl Flow for TableColGroupFlow { /// Table column do not have block-size. fn assign_block_size(&mut self, _ctx: &LayoutContext) { } + + fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {} + + fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {} } impl fmt::Show for TableColGroupFlow { diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 6420d4db62d..23d392dd2da 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -227,6 +227,14 @@ impl Flow for TableRowFlow { fn compute_absolute_position(&mut self) { self.block_flow.compute_absolute_position() } + + fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { + self.block_flow.update_late_computed_inline_position_if_necessary(inline_position) + } + + fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { + self.block_flow.update_late_computed_block_position_if_necessary(block_position) + } } impl fmt::Show for TableRowFlow { diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index 78233de9c72..c1e56bc5219 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -210,6 +210,14 @@ impl Flow for TableRowGroupFlow { fn compute_absolute_position(&mut self) { self.block_flow.compute_absolute_position() } + + fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { + self.block_flow.update_late_computed_inline_position_if_necessary(inline_position) + } + + fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { + self.block_flow.update_late_computed_block_position_if_necessary(block_position) + } } impl fmt::Show for TableRowGroupFlow { diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index f1f47531afb..d4d6486f425 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -333,6 +333,14 @@ impl Flow for TableWrapperFlow { } impacted } + + fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { + self.block_flow.update_late_computed_inline_position_if_necessary(inline_position) + } + + fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { + self.block_flow.update_late_computed_block_position_if_necessary(block_position) + } } impl fmt::Show for TableWrapperFlow { diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako index c6d70a6d096..96d77aff194 100644 --- a/components/style/properties/mod.rs.mako +++ b/components/style/properties/mod.rs.mako @@ -133,7 +133,7 @@ pub mod longhands { <%def name="longhand(name, no_super=False, derived_from=None, experimental=False)"> <%self:raw_longhand name="${name}" derived_from="${derived_from}" - experimental="${experimental}"> + experimental="${experimental}" no_super="${no_super}"> ${caller.body()} % if derived_from is None: pub fn parse_specified(_input: &[ComponentValue], _base_url: &Url) @@ -235,7 +235,7 @@ pub mod longhands { ${single_keyword("border-top-style", values="none solid double dotted dashed hidden groove ridge inset outset")} % for side in ["right", "bottom", "left"]: - <%self:longhand name="border-${side}-style", no_super="True"> + <%self:longhand name="border-${side}-style"> pub use super::border_top_style::{get_initial_value, parse, to_computed_value}; pub type SpecifiedValue = super::border_top_style::SpecifiedValue; pub mod computed_value { @@ -327,6 +327,25 @@ pub mod longhands { ${single_keyword("float", "none left right")} ${single_keyword("clear", "none left right both")} + <%self:longhand name="-servo-display-for-hypothetical-box" derived_from="display" no_super="True"> + pub use super::computed_as_specified as to_computed_value; + pub use super::display::{SpecifiedValue, get_initial_value}; + pub use super::display::{parse}; + use super::computed; + use super::display; + + pub mod computed_value { + pub type T = super::SpecifiedValue; + } + + #[inline] + pub fn derive_from_display(_: display::computed_value::T, context: &computed::Context) + -> computed_value::T { + context.display + } + + + ${new_style_struct("InheritedBox", is_inherited=True)} ${single_keyword("direction", "ltr rtl", experimental=True)} @@ -1840,7 +1859,7 @@ fn cascade_with_cached_declarations(applicable_declarations: &[DeclarationBlock] % if style_struct.inherited: let mut style_${style_struct.ident} = parent_style.${style_struct.ident}.clone(); % else: - let style_${style_struct.ident} = cached_style.${style_struct.ident}.clone(); + let mut style_${style_struct.ident} = cached_style.${style_struct.ident}.clone(); % endif % endfor diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 2383e36e5e6..662d8f5e58d 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -161,3 +161,4 @@ fragment=top != ../html/acid2.html acid2_ref.html == vertical_align_super_a.html vertical_align_super_ref.html == vertical_align_text_top_a.html vertical_align_text_top_ref.html == vertical_align_text_bottom_a.html vertical_align_text_bottom_ref.html +== inline_hypothetical_box_a.html inline_hypothetical_box_ref.html diff --git a/tests/ref/inline_hypothetical_box_a.html b/tests/ref/inline_hypothetical_box_a.html new file mode 100644 index 00000000000..952cbaced14 --- /dev/null +++ b/tests/ref/inline_hypothetical_box_a.html @@ -0,0 +1,27 @@ + + + + + + +
Helloworld!
+
Helloworld!
+ + + diff --git a/tests/ref/inline_hypothetical_box_ref.html b/tests/ref/inline_hypothetical_box_ref.html new file mode 100644 index 00000000000..ed690090308 --- /dev/null +++ b/tests/ref/inline_hypothetical_box_ref.html @@ -0,0 +1,29 @@ + + + + + + +
world!Hello
+
world!Hello
+ + + +