diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2014-09-30 13:19:39 -0700 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-10-01 18:34:53 -0700 |
commit | 287fe3b3aba157ac7e9dbe5da4433ef256745549 (patch) | |
tree | 086d0d7a93efadf3dcfc123fca0c8471178ca7b2 /components/layout/construct.rs | |
parent | f80096069592b864221abe112eaf2ecb6c444fda (diff) | |
download | servo-287fe3b3aba157ac7e9dbe5da4433ef256745549.tar.gz servo-287fe3b3aba157ac7e9dbe5da4433ef256745549.zip |
layout: Implement the correct hypothetical box behavior for
absolutely-positioned elements declared with `display: inline`.
Although the computed `display` property of elements with `position:
absolute` is `block`, `position: absolute; display: inline` can still
behave differently from `position: absolute; display: block`. This is
because the hypothetical box for `position: absolute` can be at the
position it would have been if it had `display: inline`. CSS 2.1 §
10.3.7 describes this case in a parenthetical:
"The static-position containing block is the containing block of a
hypothetical box that would have been the first box of the element if
its specified 'position' value had been 'static' and its specified
'float' had been 'none'. (Note that due to the rules in section 9.7 this
hypothetical calculation might require also assuming a different
computed value for 'display'.)"
To handle this, I had to change both style computation and layout. For
the former, I added an internal property
`-servo-display-for-hypothetical-box`, which stores the `display` value
supplied by the author, before the computed value is calculated. Flow
construction now uses this value.
As for layout, implementing the proper behavior is tricky because the
position of an inline fragment in the inline direction cannot be
determined until height assignment, which is a parallelism hazard
because in parallel layout widths are computed before heights. However,
in this particular case we can avoid the parallelism hazard because the
inline direction of a hypothetical box only affects the layout if an
absolutely-positioned element is unconstrained in the inline direction.
Therefore, we can just lay out such absolutely-positioned elements with
a bogus inline position and fix it up once the true inline position of
the hypothetical box is computed. The name for this fix-up process is
"late computation of inline position" (and the corresponding fix-up for
the block position is called "late computation of block position").
This improves the header on /r/rust.
Diffstat (limited to 'components/layout/construct.rs')
-rw-r--r-- | components/layout/construct.rs | 97 |
1 files changed, 71 insertions, 26 deletions
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) } |