diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2018-06-05 04:16:56 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-05 04:16:56 -0400 |
commit | a07c71889524f947fb39bcb65baf135bd2a04d0d (patch) | |
tree | 44acf0acd09c25b484c36ab4bf27b3047aea3b8e | |
parent | eb1dfd07756f690dfbebae2e53447bc747c5763a (diff) | |
parent | 32f00ef821c895ada5eb5cf7c291d5a0a220eb58 (diff) | |
download | servo-a07c71889524f947fb39bcb65baf135bd2a04d0d.tar.gz servo-a07c71889524f947fb39bcb65baf135bd2a04d0d.zip |
Auto merge of #20767 - mrobinson:use-reference-frame-for-fixed-position, r=gw3583
Use reference frames explicitly for fixed positioning
<!-- Please describe your changes on the following line: -->
---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).
<!-- Either: -->
- [x] There are tests for these changes because they should not change behavior.
- [ ] These changes do not require tests because _____
<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20767)
<!-- Reviewable:end -->
-rw-r--r-- | components/layout/display_list/builder.rs | 123 | ||||
-rw-r--r-- | components/layout/display_list/items.rs | 56 | ||||
-rw-r--r-- | components/layout/display_list/webrender_helpers.rs | 25 | ||||
-rw-r--r-- | components/layout/fragment.rs | 6 |
4 files changed, 160 insertions, 50 deletions
diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index a3a40a98cc0..b5c252cc64e 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -192,12 +192,16 @@ pub struct StackingContextCollectionState { /// recursively building and processing the display list. pub current_stacking_context_id: StackingContextId, - /// The current stacking real context id, which doesn't include pseudo-stacking contexts. + /// The current reference frame ClipScrollNodeIndex. 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 reference frame id. This is used to assign items to the parent + /// reference frame when we encounter a fixed position stacking context. + pub current_parent_reference_frame_id: ClipScrollNodeIndex, + /// The current clip and scroll info, used to keep track of state when /// recursively building and processing the display list. pub current_clipping_and_scrolling: ClippingAndScrolling, @@ -221,19 +225,8 @@ pub struct StackingContextCollectionState { impl StackingContextCollectionState { pub fn new(pipeline_id: PipelineId) -> StackingContextCollectionState { - let root_clip_indices = ClippingAndScrolling::simple(ClipScrollNodeIndex(0)); - - // This is just a dummy node to take up a slot in the array. WebRender - // takes care of adding this root node and it can be ignored during DL conversion. - let root_node = ClipScrollNode { - parent_index: ClipScrollNodeIndex(0), - clip: ClippingRegion::from_rect(LayoutRect::zero()), - content_rect: LayoutRect::zero(), - node_type: ClipScrollNodeType::ScrollFrame( - ScrollSensitivity::ScriptAndInputEvents, - pipeline_id.root_scroll_id(), - ), - }; + let root_clip_indices = + ClippingAndScrolling::simple(ClipScrollNodeIndex::root_scroll_node()); let mut stacking_context_info = FnvHashMap::default(); stacking_context_info.insert( @@ -241,14 +234,22 @@ impl StackingContextCollectionState { StackingContextInfo::new(StackingContextId::root()), ); + // We add two empty nodes to represent the WebRender root reference frame and + // root scroll nodes. WebRender adds these automatically and we add them here + // so that the ids in the array match up with the ones we assign during display + // list building. We ignore these two nodes during conversion to WebRender + // display lists. + let clip_scroll_nodes = vec![ClipScrollNode::placeholder(), ClipScrollNode::placeholder()]; + StackingContextCollectionState { pipeline_id: pipeline_id, root_stacking_context: StackingContext::root(), stacking_context_info, - clip_scroll_nodes: vec![root_node], + clip_scroll_nodes, current_stacking_context_id: StackingContextId::root(), current_real_stacking_context_id: StackingContextId::root(), next_stacking_context_id: StackingContextId::root().next(), + current_parent_reference_frame_id: ClipScrollNodeIndex::root_reference_frame(), current_clipping_and_scrolling: root_clip_indices, containing_block_clipping_and_scrolling: root_clip_indices, clip_stack: Vec::new(), @@ -291,17 +292,27 @@ impl StackingContextCollectionState { } fn add_clip_scroll_node(&mut self, clip_scroll_node: ClipScrollNode) -> ClipScrollNodeIndex { - // We want the scroll root to be defined before any possible item that could use it, - // so we make sure that it is added to the beginning of the parent "real" (non-pseudo) - // stacking context. This ensures that item reordering will not result in an item using - // the scroll root before it is defined. + let is_placeholder = clip_scroll_node.is_placeholder(); + self.clip_scroll_nodes.push(clip_scroll_node); - let index = ClipScrollNodeIndex(self.clip_scroll_nodes.len() - 1); - self.stacking_context_info - .get_mut(&self.current_real_stacking_context_id) - .unwrap() - .clip_scroll_nodes - .push(index); + let index = ClipScrollNodeIndex::new(self.clip_scroll_nodes.len() - 1); + + // If this node is a placeholder node (currently just reference frames), then don't add + // it to the stacking context list. Placeholder nodes are created automatically by + // WebRender and we don't want to explicitly create them in the display list. The node + // is just there to take up a spot in the global list of ClipScrollNodes. + if !is_placeholder { + // We want the scroll root to be defined before any possible item that could use it, + // so we make sure that it is added to the beginning of the parent "real" (non-pseudo) + // stacking context. This ensures that item reordering will not result in an item using + // the scroll root before it is defined. + self.stacking_context_info + .get_mut(&self.current_real_stacking_context_id) + .unwrap() + .clip_scroll_nodes + .push(index); + } + index } } @@ -347,7 +358,6 @@ impl<'a> DisplayListBuildState<'a> { layout_context: &'a LayoutContext, state: StackingContextCollectionState, ) -> DisplayListBuildState<'a> { - let root_clip_indices = ClippingAndScrolling::simple(ClipScrollNodeIndex(0)); DisplayListBuildState { layout_context: layout_context, root_stacking_context: state.root_stacking_context, @@ -356,7 +366,8 @@ impl<'a> DisplayListBuildState<'a> { clip_scroll_nodes: state.clip_scroll_nodes, processing_scrolling_overflow_element: false, current_stacking_context_id: StackingContextId::root(), - current_clipping_and_scrolling: root_clip_indices, + current_clipping_and_scrolling: + ClippingAndScrolling::simple(ClipScrollNodeIndex::root_scroll_node()), iframe_sizes: Vec::new(), indexable_text: IndexableText::default(), } @@ -374,7 +385,7 @@ impl<'a> DisplayListBuildState<'a> { return index; } - self.clip_scroll_nodes[index.0].parent_index + self.clip_scroll_nodes[index.to_index()].parent_index } fn is_background_or_border_of_clip_scroll_node(&self, section: DisplayListSection) -> bool { @@ -429,7 +440,7 @@ impl<'a> DisplayListBuildState<'a> { // stacking context. This ensures that item reordering will not result in an item using // the scroll root before it is defined. self.clip_scroll_nodes.push(node); - let index = ClipScrollNodeIndex(self.clip_scroll_nodes.len() - 1); + let index = ClipScrollNodeIndex::new(self.clip_scroll_nodes.len() - 1); let real_stacking_context_id = self.stacking_context_info[&self.current_stacking_context_id].real_stacking_context_id; self.stacking_context_info @@ -751,6 +762,7 @@ pub trait FragmentDisplayListBuilding { base_flow: &BaseFlow, scroll_policy: ScrollPolicy, context_type: StackingContextType, + established_reference_frame: Option<ClipScrollNodeIndex>, parent_clipping_and_scrolling: ClippingAndScrolling, ) -> StackingContext; @@ -1873,6 +1885,7 @@ impl FragmentDisplayListBuilding for Fragment { base_flow: &BaseFlow, scroll_policy: ScrollPolicy, context_type: StackingContextType, + established_reference_frame: Option<ClipScrollNodeIndex>, parent_clipping_and_scrolling: ClippingAndScrolling, ) -> StackingContext { let border_box = self.stacking_relative_border_box( @@ -1916,6 +1929,7 @@ impl FragmentDisplayListBuilding for Fragment { self.perspective_matrix(&border_box), scroll_policy, parent_clipping_and_scrolling, + established_reference_frame, ) } @@ -2135,6 +2149,7 @@ pub trait BlockFlowDisplayListBuilding { state: &mut StackingContextCollectionState, preserved_state: &mut SavedStackingContextCollectionState, stacking_context_type: Option<StackingContextType>, + established_reference_frame: Option<ClipScrollNodeIndex>, flags: StackingContextCollectionFlags, ) -> ClippingAndScrolling; fn setup_clip_scroll_node_for_position( @@ -2164,6 +2179,7 @@ pub trait BlockFlowDisplayListBuilding { &mut self, parent_stacking_context_id: StackingContextId, parent_clipping_and_scrolling: ClippingAndScrolling, + established_reference_frame: Option<ClipScrollNodeIndex>, state: &mut StackingContextCollectionState, ); fn build_display_list_for_block( @@ -2187,6 +2203,8 @@ pub trait BlockFlowDisplayListBuilding { &self, flags: StackingContextCollectionFlags, ) -> Option<StackingContextType>; + + fn is_reference_frame(&self, context_type: Option<StackingContextType>) -> bool; } /// This structure manages ensuring that modification to StackingContextCollectionState is @@ -2197,6 +2215,7 @@ pub trait BlockFlowDisplayListBuilding { pub struct SavedStackingContextCollectionState { stacking_context_id: StackingContextId, real_stacking_context_id: StackingContextId, + parent_reference_frame_id: ClipScrollNodeIndex, clipping_and_scrolling: ClippingAndScrolling, containing_block_clipping_and_scrolling: ClippingAndScrolling, clips_pushed: usize, @@ -2209,6 +2228,7 @@ impl SavedStackingContextCollectionState { SavedStackingContextCollectionState { stacking_context_id: state.current_stacking_context_id, real_stacking_context_id: state.current_real_stacking_context_id, + parent_reference_frame_id: state.current_parent_reference_frame_id, clipping_and_scrolling: state.current_clipping_and_scrolling, containing_block_clipping_and_scrolling: state.containing_block_clipping_and_scrolling, clips_pushed: 0, @@ -2230,6 +2250,7 @@ impl SavedStackingContextCollectionState { fn restore(self, state: &mut StackingContextCollectionState) { state.current_stacking_context_id = self.stacking_context_id; state.current_real_stacking_context_id = self.real_stacking_context_id; + state.current_parent_reference_frame_id = self.parent_reference_frame_id; state.current_clipping_and_scrolling = self.clipping_and_scrolling; state.containing_block_clipping_and_scrolling = self.containing_block_clipping_and_scrolling; @@ -2332,6 +2353,16 @@ impl BlockFlowDisplayListBuilding for BlockFlow { } } + /// Returns true if this fragment may establish a reference frame and this block + /// creates a stacking context. Both are necessary in order to establish a reference + /// frame. + fn is_reference_frame(&self, context_type: Option<StackingContextType>) -> bool { + match context_type { + Some(StackingContextType::Real) => self.fragment.can_establish_reference_frame(), + _ => false, + } + } + fn collect_stacking_contexts_for_block( &mut self, state: &mut StackingContextCollectionState, @@ -2348,8 +2379,17 @@ impl BlockFlowDisplayListBuilding for BlockFlow { if stacking_context_type == Some(StackingContextType::Real) { state.current_real_stacking_context_id = self.base.stacking_context_id; + } + let established_reference_frame = if self.is_reference_frame(stacking_context_type) { + // WebRender currently creates reference frames automatically, so just add + // a placeholder node to allocate a ClipScrollNodeIndex for this reference frame. + Some(state.add_clip_scroll_node(ClipScrollNode::placeholder())) + } else { + None + }; + // We are getting the id of the scroll root that contains us here, not the id of // any scroll root that we create. If we create a scroll root, its index will be // stored in state.current_clipping_and_scrolling. If we create a stacking context, @@ -2358,6 +2398,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { state, &mut preserved_state, stacking_context_type, + established_reference_frame, flags, ); @@ -2371,6 +2412,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { self.create_real_stacking_context_for_block( preserved_state.stacking_context_id, containing_clipping_and_scrolling, + established_reference_frame, state, ); }, @@ -2392,6 +2434,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { state: &mut StackingContextCollectionState, preserved_state: &mut SavedStackingContextCollectionState, stacking_context_type: Option<StackingContextType>, + established_reference_frame: Option<ClipScrollNodeIndex>, flags: StackingContextCollectionFlags, ) -> ClippingAndScrolling { // If this block is absolutely positioned, we should be clipped and positioned by @@ -2404,13 +2447,23 @@ impl BlockFlowDisplayListBuilding for BlockFlow { state.containing_block_clipping_and_scrolling }, StylePosition::Fixed => { + // If we are a fixed positioned stacking context, we want to be scrolled by + // our reference frame instead of the clip scroll node that we are inside. preserved_state.push_clip(state, Rect::max_rect(), StylePosition::Fixed); + state.current_clipping_and_scrolling.scrolling = + state.current_parent_reference_frame_id; state.current_clipping_and_scrolling }, _ => state.current_clipping_and_scrolling, }; self.base.clipping_and_scrolling = Some(containing_clipping_and_scrolling); + if let Some(reference_frame_index) = established_reference_frame { + let clipping_and_scrolling = ClippingAndScrolling::simple(reference_frame_index); + state.current_clipping_and_scrolling = clipping_and_scrolling; + self.base.clipping_and_scrolling = Some(clipping_and_scrolling); + } + let stacking_relative_border_box = if self.fragment.establishes_stacking_context() { self.stacking_relative_border_box(CoordinateSystem::Own) } else { @@ -2658,6 +2711,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { &self.base, ScrollPolicy::Scrollable, stacking_context_type, + None, parent_clipping_and_scrolling, ); state.add_stacking_context(parent_stacking_context_id, new_context); @@ -2683,19 +2737,15 @@ impl BlockFlowDisplayListBuilding for BlockFlow { &mut self, parent_stacking_context_id: StackingContextId, parent_clipping_and_scrolling: ClippingAndScrolling, + established_reference_frame: Option<ClipScrollNodeIndex>, state: &mut StackingContextCollectionState, ) { - let scroll_policy = if self.is_fixed() { - ScrollPolicy::Fixed - } else { - ScrollPolicy::Scrollable - }; - let stacking_context = self.fragment.create_stacking_context( self.base.stacking_context_id, &self.base, - scroll_policy, + ScrollPolicy::Scrollable, StackingContextType::Real, + established_reference_frame, parent_clipping_and_scrolling, ); @@ -2833,6 +2883,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow { &self.base, ScrollPolicy::Scrollable, StackingContextType::Real, + None, state.current_clipping_and_scrolling, ); diff --git a/components/layout/display_list/items.rs b/components/layout/display_list/items.rs index 09539050789..7d293996c58 100644 --- a/components/layout/display_list/items.rs +++ b/components/layout/display_list/items.rs @@ -38,14 +38,25 @@ pub static BLUR_INFLATION_FACTOR: i32 = 3; /// An index into the vector of ClipScrollNodes. During WebRender conversion these nodes /// are given ClipIds. #[derive(Clone, Copy, Debug, PartialEq, Serialize)] -pub struct ClipScrollNodeIndex(pub usize); +pub struct ClipScrollNodeIndex(usize); impl ClipScrollNodeIndex { + pub fn root_scroll_node() -> ClipScrollNodeIndex { + ClipScrollNodeIndex(1) + } + + pub fn root_reference_frame() -> ClipScrollNodeIndex { + ClipScrollNodeIndex(0) + } + + pub fn new(index: usize) -> ClipScrollNodeIndex { + assert_ne!(index, 0, "Use the root_reference_frame constructor"); + assert_ne!(index, 1, "Use the root_scroll_node constructor"); + ClipScrollNodeIndex(index) + } + pub fn is_root_scroll_node(&self) -> bool { - match *self { - ClipScrollNodeIndex(0) => true, - _ => false, - } + *self == Self::root_scroll_node() } pub fn to_define_item(&self) -> DisplayItem { @@ -54,6 +65,10 @@ impl ClipScrollNodeIndex { node_index: *self, })) } + + pub fn to_index(self) -> usize { + self.0 + } } /// A set of indices into the clip scroll node vector for a given item. @@ -193,6 +208,9 @@ pub struct StackingContext { /// The clip and scroll info for this StackingContext. pub parent_clipping_and_scrolling: ClippingAndScrolling, + + /// The index of the reference frame that this stacking context estalishes. + pub established_reference_frame: Option<ClipScrollNodeIndex>, } impl StackingContext { @@ -211,6 +229,7 @@ impl StackingContext { perspective: Option<LayoutTransform>, scroll_policy: ScrollPolicy, parent_clipping_and_scrolling: ClippingAndScrolling, + established_reference_frame: Option<ClipScrollNodeIndex>, ) -> StackingContext { StackingContext { id, @@ -225,6 +244,7 @@ impl StackingContext { perspective, scroll_policy, parent_clipping_and_scrolling, + established_reference_frame, } } @@ -242,7 +262,8 @@ impl StackingContext { TransformStyle::Flat, None, ScrollPolicy::Scrollable, - ClippingAndScrolling::simple(ClipScrollNodeIndex(0)), + ClippingAndScrolling::simple(ClipScrollNodeIndex::root_scroll_node()), + None, ) } @@ -309,15 +330,16 @@ impl fmt::Debug for StackingContext { } } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, PartialEq, Serialize)] pub struct StickyFrameData { pub margins: SideOffsets2D<Option<f32>>, pub vertical_offset_bounds: StickyOffsetBounds, pub horizontal_offset_bounds: StickyOffsetBounds, } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, PartialEq, Serialize)] pub enum ClipScrollNodeType { + Placeholder, ScrollFrame(ScrollSensitivity, ExternalScrollId), StickyFrame(StickyFrameData), Clip, @@ -339,6 +361,21 @@ pub struct ClipScrollNode { pub node_type: ClipScrollNodeType, } +impl ClipScrollNode { + pub fn placeholder() -> ClipScrollNode { + ClipScrollNode { + parent_index: ClipScrollNodeIndex(0), + clip: ClippingRegion::from_rect(LayoutRect::zero()), + content_rect: LayoutRect::zero(), + node_type: ClipScrollNodeType::Placeholder, + } + } + + pub fn is_placeholder(&self) -> bool { + self.node_type == ClipScrollNodeType::Placeholder + } +} + /// One drawing command in the list. #[derive(Clone, Serialize)] pub enum DisplayItem { @@ -412,7 +449,8 @@ impl BaseDisplayItem { clip_rect: LayoutRect::max_rect(), section: DisplayListSection::Content, stacking_context_id: StackingContextId::root(), - clipping_and_scrolling: ClippingAndScrolling::simple(ClipScrollNodeIndex(0)), + clipping_and_scrolling: + ClippingAndScrolling::simple(ClipScrollNodeIndex::root_scroll_node()), } } } diff --git a/components/layout/display_list/webrender_helpers.rs b/components/layout/display_list/webrender_helpers.rs index 30f61386b92..da66b9c2b80 100644 --- a/components/layout/display_list/webrender_helpers.rs +++ b/components/layout/display_list/webrender_helpers.rs @@ -41,7 +41,12 @@ impl WebRenderDisplayListConverter for DisplayList { let mut clip_ids = Vec::with_capacity(self.clip_scroll_nodes.len()); clip_ids.resize(self.clip_scroll_nodes.len(), None); - clip_ids[0] = Some(ClipId::root_scroll_node(pipeline_id.to_webrender())); + + // We need to add the WebRender root reference frame and root scroll node ids + // here manually, because WebRender creates these automatically. + let webrender_pipeline = pipeline_id.to_webrender(); + clip_ids[0] = Some(ClipId::root_reference_frame(webrender_pipeline)); + clip_ids[1] = Some(ClipId::root_scroll_node(webrender_pipeline)); for item in &self.list { item.convert_to_webrender( @@ -78,7 +83,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { current_clip_and_scroll_info: &mut ClipAndScrollInfo, ) { let get_id = |clip_ids: &[Option<ClipId>], index: ClipScrollNodeIndex| -> ClipId { - match clip_ids[index.0] { + match clip_ids[index.to_index()] { Some(id) => id, None => unreachable!("Tried to use WebRender ClipId before it was defined."), } @@ -214,7 +219,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { let stacking_context = &item.stacking_context; debug_assert_eq!(stacking_context.context_type, StackingContextType::Real); - builder.push_stacking_context( + let reference_frame_clip_id = builder.push_stacking_context( &webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds), None, stacking_context.scroll_policy, @@ -225,10 +230,17 @@ impl WebRenderDisplayItemConverter for DisplayItem { stacking_context.filters.clone(), GlyphRasterSpace::Screen, ); + + match (reference_frame_clip_id, stacking_context.established_reference_frame) { + (Some(webrender_id), Some(frame_index)) => + clip_ids[frame_index.to_index()] = Some(webrender_id), + (None, None) => {}, + _ => warn!("Mismatch between reference frame establishment!"), + } }, DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(), DisplayItem::DefineClipScrollNode(ref item) => { - let node = &clip_scroll_nodes[item.node_index.0]; + let node = &clip_scroll_nodes[item.node_index.to_index()]; let parent_id = get_id(clip_ids, node.parent_index); let item_rect = node.clip.main; @@ -262,9 +274,12 @@ impl WebRenderDisplayItemConverter for DisplayItem { builder.pop_clip_id(); id }, + ClipScrollNodeType::Placeholder => { + unreachable!("Found DefineClipScrollNode for Placeholder type node."); + } }; - clip_ids[item.node_index.0] = Some(webrender_id); + clip_ids[item.node_index.to_index()] = Some(webrender_id); }, } } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index a1c84da3569..38ea9d03b93 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -2474,6 +2474,12 @@ impl Fragment { stacking_relative_border_box.size.height - border_padding.vertical())) } + /// Returns true if this fragment may establish a reference frame. + pub fn can_establish_reference_frame(&self) -> bool { + !self.style().get_box().transform.0.is_empty() || + self.style().get_box().perspective != Perspective::None + } + /// Returns true if this fragment has a filter, transform, or perspective property set. pub fn has_filter_transform_or_perspective(&self) -> bool { !self.style().get_box().transform.0.is_empty() || |