From 36fa7e4c448cbc110ada5d5e45482aa228e505e3 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Mon, 16 Oct 2017 13:41:01 +0200 Subject: [PATCH] Fix duplicate stacking context creation for anonymous Flows Anonymous nodes were previously creating duplicate stacking contexts, one for each node in the anonymous node chain. This change eliminates that for tables. Additionally the use of stacking context ids based on node addresses is no longer necessary since stacking contexts no longer control scrolling. This is the first step in eliminating the dependency between node addresses and ClipScrollNodes which causes issues like #16425. --- components/gfx_traits/lib.rs | 7 +-- components/layout/block.rs | 14 ----- components/layout/display_list_builder.rs | 61 +++++++++++++++---- components/layout/table.rs | 6 +- components/style/stylist.rs | 12 +++- .../html/transform-table-002.htm.ini | 4 ++ .../html/transform-table-005.htm.ini | 4 ++ ...spos-containing-block-initial-005b.htm.ini | 3 - ...spos-containing-block-initial-005d.htm.ini | 3 - 9 files changed, 72 insertions(+), 42 deletions(-) create mode 100644 tests/wpt/metadata-css/css-transforms-1_dev/html/transform-table-002.htm.ini create mode 100644 tests/wpt/metadata-css/css-transforms-1_dev/html/transform-table-005.htm.ini delete mode 100644 tests/wpt/metadata-css/css21_dev/html4/abspos-containing-block-initial-005b.htm.ini delete mode 100644 tests/wpt/metadata-css/css21_dev/html4/abspos-containing-block-initial-005d.htm.ini diff --git a/components/gfx_traits/lib.rs b/components/gfx_traits/lib.rs index e6f6c124ec2..994d0c947ad 100644 --- a/components/gfx_traits/lib.rs +++ b/components/gfx_traits/lib.rs @@ -42,10 +42,9 @@ impl StackingContextId { StackingContextId(0) } - /// Returns a new sacking context id with the given numeric id. - #[inline] - pub fn new(id: u64) -> StackingContextId { - StackingContextId(id) + pub fn next(&self) -> StackingContextId { + let StackingContextId(id) = *self; + StackingContextId(id + 1) } } diff --git a/components/layout/block.rs b/components/layout/block.rs index bffcbef0e67..aaad1d51937 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -1670,20 +1670,6 @@ impl BlockFlow { self.base.flags = flags } - pub fn block_stacking_context_type(&self) -> BlockStackingContextType { - if self.fragment.establishes_stacking_context() { - return BlockStackingContextType::StackingContext - } - - if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) || - self.fragment.style.get_box().position != position::T::static_ || - self.base.flags.is_float() { - BlockStackingContextType::PseudoStackingContext - } else { - BlockStackingContextType::NonstackingContext - } - } - pub fn overflow_style_may_require_clip_scroll_node(&self) -> bool { match (self.fragment.style().get_box().overflow_x, self.fragment.style().get_box().overflow_y) { diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index f29a1f377ae..af08a690ca0 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -181,6 +181,9 @@ pub struct StackingContextCollectionState { /// The current stacking real context id, which doesn't include pseudo-stacking contexts. pub current_real_stacking_context_id: StackingContextId, + /// The next stacking context id that we will assign to a stacking context. + pub next_stacking_context_id: StackingContextId, + /// The current clip and scroll info, used to keep track of state when /// recursively building and processing the display list. pub current_clip_and_scroll_info: ClipAndScrollInfo, @@ -212,6 +215,7 @@ impl StackingContextCollectionState { clip_scroll_node_parents: FnvHashMap::default(), current_stacking_context_id: StackingContextId::root(), current_real_stacking_context_id: StackingContextId::root(), + next_stacking_context_id: StackingContextId::root().next(), current_clip_and_scroll_info: root_clip_info, containing_block_clip_and_scroll_info: root_clip_info, clip_stack: Vec::new(), @@ -220,6 +224,11 @@ impl StackingContextCollectionState { } } + fn generate_stacking_context_id(&mut self) -> StackingContextId { + let next_stacking_context_id = self.next_stacking_context_id.next(); + mem::replace(&mut self.next_stacking_context_id, next_stacking_context_id) + } + fn add_stacking_context(&mut self, parent_id: StackingContextId, stacking_context: StackingContext) { @@ -611,10 +620,6 @@ pub trait FragmentDisplayListBuilding { parent_clip_and_scroll_info: ClipAndScrollInfo) -> StackingContext; - - /// The id of stacking context this fragment would create. - fn stacking_context_id(&self) -> StackingContextId; - fn unique_id(&self, id_type: IdType) -> u64; fn fragment_type(&self) -> FragmentType; @@ -2169,10 +2174,6 @@ impl FragmentDisplayListBuilding for Fragment { } } - fn stacking_context_id(&self) -> StackingContextId { - StackingContextId::new(self.unique_id(IdType::StackingContext)) - } - fn create_stacking_context(&self, id: StackingContextId, base_flow: &BaseFlow, @@ -2394,9 +2395,11 @@ impl FragmentDisplayListBuilding for Fragment { bitflags! { pub flags StackingContextCollectionFlags: u8 { /// This flow never establishes a containing block. - const NEVER_CREATES_CONTAINING_BLOCK = 0x01, + const NEVER_CREATES_CONTAINING_BLOCK = 0b001, /// This flow never creates a ClipScrollNode. - const NEVER_CREATES_CLIP_SCROLL_NODE = 0x02, + const NEVER_CREATES_CLIP_SCROLL_NODE = 0b010, + /// This flow never creates a stacking context. + const NEVER_CREATES_STACKING_CONTEXT = 0b100, } } @@ -2435,6 +2438,11 @@ pub trait BlockFlowDisplayListBuilding { fn build_display_list_for_block(&mut self, state: &mut DisplayListBuildState, border_painting_mode: BorderPaintingMode); + + fn block_stacking_context_type( + &self, + flags: StackingContextCollectionFlags, + ) -> BlockStackingContextType; } /// This structure manages ensuring that modification to StackingContextCollectionState is @@ -2572,11 +2580,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow { flags: StackingContextCollectionFlags) { let mut preserved_state = SavedStackingContextCollectionState::new(state); - let block_stacking_context_type = self.block_stacking_context_type(); + let block_stacking_context_type = self.block_stacking_context_type(flags); self.base.stacking_context_id = match block_stacking_context_type { BlockStackingContextType::NonstackingContext => state.current_stacking_context_id, BlockStackingContextType::PseudoStackingContext | - BlockStackingContextType::StackingContext => self.fragment.stacking_context_id(), + BlockStackingContextType::StackingContext => state.generate_stacking_context_id(), }; state.current_stacking_context_id = self.base.stacking_context_id; @@ -2963,6 +2971,33 @@ impl BlockFlowDisplayListBuilding for BlockFlow { state.processing_scrolling_overflow_element = false; } + #[inline] + fn block_stacking_context_type( + &self, + flags: StackingContextCollectionFlags, + ) -> BlockStackingContextType { + if flags.contains(NEVER_CREATES_STACKING_CONTEXT) { + return BlockStackingContextType::NonstackingContext; + } + + if self.fragment.establishes_stacking_context() { + return BlockStackingContextType::StackingContext + } + + if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { + return BlockStackingContextType::PseudoStackingContext + } + + if self.fragment.style.get_box().position != position::T::static_ { + return BlockStackingContextType::PseudoStackingContext + } + + if self.base.flags.is_float() { + return BlockStackingContextType::PseudoStackingContext + } + + BlockStackingContextType::NonstackingContext + } } pub trait InlineFlowDisplayListBuilding { @@ -2988,7 +3023,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow { if !fragment.collect_stacking_contexts_for_blocklike_fragment(state) { if fragment.establishes_stacking_context() { - fragment.stacking_context_id = fragment.stacking_context_id(); + fragment.stacking_context_id = state.generate_stacking_context_id(); let current_stacking_context_id = state.current_stacking_context_id; let stacking_context = diff --git a/components/layout/table.rs b/components/layout/table.rs index 7009e4ec60b..37fb246935f 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -11,7 +11,7 @@ use block::{BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer}; use block::{ISizeConstraintInput, ISizeConstraintSolution}; use context::LayoutContext; use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode}; -use display_list_builder::{DisplayListBuildState, StackingContextCollectionFlags}; +use display_list_builder::{DisplayListBuildState, NEVER_CREATES_STACKING_CONTEXT}; use display_list_builder::StackingContextCollectionState; use euclid::Point2D; use flow; @@ -504,8 +504,8 @@ impl Flow for TableFlow { } fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) { - self.block_flow.collect_stacking_contexts_for_block(state, - StackingContextCollectionFlags::empty()); + // Stacking contexts are collected by the table wrapper. + self.block_flow.collect_stacking_contexts_for_block(state, NEVER_CREATES_STACKING_CONTEXT); } fn repair_style(&mut self, new_style: &::ServoArc) { diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 324bf2301af..d2a74908978 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -763,13 +763,21 @@ impl Stylist { // For most (but not all) pseudo-elements, we inherit all values from the parent. let inherit_all = match *pseudo { + // Anonymous table flows shouldn't inherit their parents properties in order + // to avoid doubling up styles such as transformations. + PseudoElement::ServoAnonymousTableCell | + PseudoElement::ServoAnonymousTableRow | PseudoElement::ServoText | PseudoElement::ServoInputText => false, PseudoElement::ServoAnonymousBlock | + + // For tables, we do want style to inherit, because TableWrapper is responsible + // for handling clipping and scrolling, while Table is responsible for creating + // stacking contexts. StackingContextCollectionFlags makes sure this is processed + // properly. PseudoElement::ServoAnonymousTable | - PseudoElement::ServoAnonymousTableCell | - PseudoElement::ServoAnonymousTableRow | PseudoElement::ServoAnonymousTableWrapper | + PseudoElement::ServoTableWrapper | PseudoElement::ServoInlineBlockWrapper | PseudoElement::ServoInlineAbsolute => true, diff --git a/tests/wpt/metadata-css/css-transforms-1_dev/html/transform-table-002.htm.ini b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform-table-002.htm.ini new file mode 100644 index 00000000000..7a0e1e2fac8 --- /dev/null +++ b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform-table-002.htm.ini @@ -0,0 +1,4 @@ +[transform-table-002.htm] + type: reftest + expected: FAIL + bug: https://github.com/servo/servo/issues/8003 diff --git a/tests/wpt/metadata-css/css-transforms-1_dev/html/transform-table-005.htm.ini b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform-table-005.htm.ini new file mode 100644 index 00000000000..1e3a14cf667 --- /dev/null +++ b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform-table-005.htm.ini @@ -0,0 +1,4 @@ +[transform-table-005.htm] + type: reftest + expected: FAIL + bug: https://github.com/servo/servo/issues/8003 diff --git a/tests/wpt/metadata-css/css21_dev/html4/abspos-containing-block-initial-005b.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abspos-containing-block-initial-005b.htm.ini deleted file mode 100644 index 02c3f7ec9dd..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abspos-containing-block-initial-005b.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abspos-containing-block-initial-005b.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/abspos-containing-block-initial-005d.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abspos-containing-block-initial-005d.htm.ini deleted file mode 100644 index 087e871ddf4..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abspos-containing-block-initial-005d.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abspos-containing-block-initial-005d.htm] - type: reftest - expected: FAIL