Use reference frames explicitly for fixed positioning

Now that WebRender gives us reference frame ClipIds, we can use those to
implement fixed positioning in Servo. This will allow us to remove the
feature from WebRender entirely.
This commit is contained in:
Martin Robinson 2018-05-08 17:05:06 +02:00
parent 11a89bcc47
commit 32f00ef821
4 changed files with 160 additions and 50 deletions

View file

@ -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,
);