aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/display_list_builder.rs
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2017-07-27 11:25:59 +0200
committerMartin Robinson <mrobinson@igalia.com>2017-08-03 17:52:02 +0200
commitdaf638bc3f1deebb23cc4375f872a1b1b4bc543c (patch)
tree87ea0fd8ab332146fcd829c0410d7b9a99d033b8 /components/layout/display_list_builder.rs
parent46f6e68bad7aafc380976bfddd9e76bfaa86229e (diff)
downloadservo-daf638bc3f1deebb23cc4375f872a1b1b4bc543c.tar.gz
servo-daf638bc3f1deebb23cc4375f872a1b1b4bc543c.zip
Fix fixed position items with parents with CSS clips
In order to properly handle CSS clipping, we need to keep track of what the different kinds of clips that we have. On one hand, clipping due to overflow rules should respect the containing block hierarchy, while CSS clipping should respect the flow tree hierarchy. In order to represent the complexity of items that are scrolled via one clip/scroll frame and clipped by another we keep track of that status with a ClipAndScrollInfo.
Diffstat (limited to 'components/layout/display_list_builder.rs')
-rw-r--r--components/layout/display_list_builder.rs130
1 files changed, 71 insertions, 59 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index f8b26899b57..5e238a683fe 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -71,8 +71,8 @@ use style_traits::CSSPixel;
use style_traits::ToCss;
use style_traits::cursor::Cursor;
use table_cell::CollapsedBordersForCell;
-use webrender_api::{ClipId, ColorF, ComplexClipRegion, GradientStop, LocalClip, RepeatMode};
-use webrender_api::{LineStyle, ScrollPolicy, ScrollSensitivity, TransformStyle};
+use webrender_api::{ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion, GradientStop, LineStyle};
+use webrender_api::{LocalClip, RepeatMode, ScrollPolicy, ScrollSensitivity, TransformStyle};
use webrender_helpers::{ToBorderRadius, ToMixBlendMode, ToRectF, ToTransformStyle};
trait ResolvePercentage {
@@ -169,14 +169,14 @@ pub struct DisplayListBuildState<'a> {
/// recursively building and processing the display list.
pub current_stacking_context_id: StackingContextId,
- /// The current scroll root id, used to keep track of state when
+ /// The current clip and scroll info, used to keep track of state when
/// recursively building and processing the display list.
- pub current_scroll_root_id: ClipId,
+ pub current_clip_and_scroll_info: ClipAndScrollInfo,
- /// The scroll root id of the first ancestor which defines a containing block.
+ /// The clip and scroll info of the first ancestor which defines a containing block.
/// This is necessary because absolutely positioned items should be clipped
/// by their containing block's scroll root.
- pub containing_block_scroll_root_id: ClipId,
+ pub containing_block_clip_and_scroll_info: ClipAndScrollInfo,
/// Vector containing iframe sizes, used to inform the constellation about
/// new iframe sizes
@@ -196,6 +196,7 @@ pub struct DisplayListBuildState<'a> {
impl<'a> DisplayListBuildState<'a> {
pub fn new(layout_context: &'a LayoutContext) -> DisplayListBuildState<'a> {
+ let root_clip_info = ClipAndScrollInfo::simple(layout_context.id.root_scroll_node());
DisplayListBuildState {
layout_context: layout_context,
root_stacking_context: StackingContext::root(layout_context.id),
@@ -204,8 +205,8 @@ impl<'a> DisplayListBuildState<'a> {
scroll_root_parents: HashMap::new(),
processing_scroll_root_element: false,
current_stacking_context_id: StackingContextId::root(),
- current_scroll_root_id: layout_context.id.root_scroll_node(),
- containing_block_scroll_root_id: layout_context.id.root_scroll_node(),
+ current_clip_and_scroll_info: root_clip_info,
+ containing_block_clip_and_scroll_info: root_clip_info,
iframe_sizes: Vec::new(),
clip_stack: Vec::new(),
containing_block_clip_stack: Vec::new(),
@@ -249,6 +250,12 @@ impl<'a> DisplayListBuildState<'a> {
*self.scroll_root_parents.get(&scroll_root_id).unwrap()
}
+ fn is_background_or_border_of_scroll_root(&self, section: DisplayListSection) -> bool {
+ (section == DisplayListSection::BackgroundAndBorders ||
+ section == DisplayListSection::BlockBackgroundsAndBorders) &&
+ self.processing_scroll_root_element
+ }
+
fn create_base_display_item(&self,
bounds: &Rect<Au>,
clip: LocalClip,
@@ -256,12 +263,10 @@ impl<'a> DisplayListBuildState<'a> {
cursor: Option<Cursor>,
section: DisplayListSection)
-> BaseDisplayItem {
- let scroll_root_id = if (section == DisplayListSection::BackgroundAndBorders ||
- section == DisplayListSection::BlockBackgroundsAndBorders) &&
- self.processing_scroll_root_element {
- self.parent_scroll_root_id(self.current_scroll_root_id)
+ let clip_and_scroll_info = if self.is_background_or_border_of_scroll_root(section) {
+ ClipAndScrollInfo::simple(self.parent_scroll_root_id(self.current_clip_and_scroll_info.scroll_node_id))
} else {
- self.current_scroll_root_id
+ self.current_clip_and_scroll_info
};
BaseDisplayItem::new(&bounds,
@@ -272,7 +277,7 @@ impl<'a> DisplayListBuildState<'a> {
clip,
section,
self.current_stacking_context_id,
- scroll_root_id)
+ clip_and_scroll_info)
}
pub fn to_display_list(mut self) -> DisplayList {
@@ -548,7 +553,7 @@ pub trait FragmentDisplayListBuilding {
base_flow: &BaseFlow,
scroll_policy: ScrollPolicy,
mode: StackingContextCreationMode,
- parent_scroll_id: ClipId)
+ parent_clip_and_scroll_info: ClipAndScrollInfo)
-> StackingContext;
@@ -2029,7 +2034,7 @@ impl FragmentDisplayListBuilding for Fragment {
base_flow: &BaseFlow,
scroll_policy: ScrollPolicy,
mode: StackingContextCreationMode,
- parent_scroll_id: ClipId)
+ parent_clip_and_scroll_info: ClipAndScrollInfo)
-> StackingContext {
let border_box =
self.stacking_relative_border_box(&base_flow.stacking_relative_position,
@@ -2069,7 +2074,7 @@ impl FragmentDisplayListBuilding for Fragment {
self.style().get_used_transform_style().to_transform_style(),
self.perspective_matrix(&border_box),
scroll_policy,
- parent_scroll_id)
+ parent_clip_and_scroll_info)
}
fn build_display_list_for_text_fragment(&self,
@@ -2255,7 +2260,7 @@ pub trait BlockFlowDisplayListBuilding {
state: &mut DisplayListBuildState,
preserved_state: &mut PreservedDisplayListState,
stacking_context_type: BlockStackingContextType)
- -> ClipId;
+ -> ClipAndScrollInfo;
fn setup_scroll_root_for_overflow(&mut self,
state: &mut DisplayListBuildState,
border_box: &Rect<Au>);
@@ -2265,11 +2270,11 @@ pub trait BlockFlowDisplayListBuilding {
stacking_relative_border_box: &Rect<Au>);
fn create_pseudo_stacking_context_for_block(&mut self,
parent_stacking_context_id: StackingContextId,
- parent_scroll_root_id: ClipId,
+ parent_clip_and_scroll_info: ClipAndScrollInfo,
state: &mut DisplayListBuildState);
fn create_real_stacking_context_for_block(&mut self,
parent_stacking_context_id: StackingContextId,
- parent_scroll_root_id: ClipId,
+ parent_clip_and_scroll_info: ClipAndScrollInfo,
state: &mut DisplayListBuildState);
fn build_display_list_for_block(&mut self,
state: &mut DisplayListBuildState,
@@ -2283,8 +2288,8 @@ pub trait BlockFlowDisplayListBuilding {
/// TODO(mrobinson): It would be nice to use RAII here to avoid having to call restore.
pub struct PreservedDisplayListState {
stacking_context_id: StackingContextId,
- scroll_root_id: ClipId,
- containing_block_scroll_root_id: ClipId,
+ clip_and_scroll_info: ClipAndScrollInfo,
+ containing_block_clip_and_scroll_info: ClipAndScrollInfo,
clips_pushed: usize,
containing_block_clips_pushed: usize,
transform_style: TransformStyle,
@@ -2294,8 +2299,8 @@ impl PreservedDisplayListState {
fn new(state: &mut DisplayListBuildState) -> PreservedDisplayListState {
PreservedDisplayListState {
stacking_context_id: state.current_stacking_context_id,
- scroll_root_id: state.current_scroll_root_id,
- containing_block_scroll_root_id: state.containing_block_scroll_root_id,
+ clip_and_scroll_info: state.current_clip_and_scroll_info,
+ containing_block_clip_and_scroll_info: state.containing_block_clip_and_scroll_info,
clips_pushed: 0,
containing_block_clips_pushed: 0,
transform_style: state.current_transform_style,
@@ -2310,8 +2315,8 @@ impl PreservedDisplayListState {
fn restore(self, state: &mut DisplayListBuildState) {
state.current_stacking_context_id = self.stacking_context_id;
- state.current_scroll_root_id = self.scroll_root_id;
- state.containing_block_scroll_root_id = self.containing_block_scroll_root_id;
+ state.current_clip_and_scroll_info = self.clip_and_scroll_info;
+ state.containing_block_clip_and_scroll_info = self.containing_block_clip_and_scroll_info;
let truncate_length = state.clip_stack.len() - self.clips_pushed;
state.clip_stack.truncate(truncate_length);
@@ -2417,14 +2422,12 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
// 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 id will be
- // stored in state.current_scroll_root_id. If we should create a stacking context,
- // we don't want it to be clipped by its own scroll root.
- let containing_scroll_root_id = self.setup_clipping_for_block(state,
- &mut preserved_state,
- block_stacking_context_type);
-
+ // stored in state.current_clip_and_scroll_info. If we create a stacking context,
+ // we don't want it to be contained by its own scroll root.
+ let containing_clip_and_scroll_info =
+ self.setup_clipping_for_block(state, &mut preserved_state, block_stacking_context_type);
if establishes_containing_block_for_absolute(self.positioning()) {
- state.containing_block_scroll_root_id = state.current_scroll_root_id;
+ state.containing_block_clip_and_scroll_info = state.current_clip_and_scroll_info;
}
match block_stacking_context_type {
@@ -2433,12 +2436,12 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}
BlockStackingContextType::PseudoStackingContext => {
self.create_pseudo_stacking_context_for_block(preserved_state.stacking_context_id,
- containing_scroll_root_id,
+ containing_clip_and_scroll_info,
state);
}
BlockStackingContextType::StackingContext => {
self.create_real_stacking_context_for_block(preserved_state.stacking_context_id,
- containing_scroll_root_id,
+ containing_clip_and_scroll_info,
state);
}
}
@@ -2450,22 +2453,22 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
state: &mut DisplayListBuildState,
preserved_state: &mut PreservedDisplayListState,
stacking_context_type: BlockStackingContextType)
- -> ClipId {
+ -> ClipAndScrollInfo {
// If this block is absolutely positioned, we should be clipped and positioned by
// the scroll root of our nearest ancestor that establishes a containing block.
- let containing_scroll_root_id = match self.positioning() {
+ let containing_clip_and_scroll_info = match self.positioning() {
position::T::absolute => {
preserved_state.switch_to_containing_block_clip(state);
- state.current_scroll_root_id = state.containing_block_scroll_root_id;
- state.containing_block_scroll_root_id
+ state.current_clip_and_scroll_info = state.containing_block_clip_and_scroll_info;
+ state.containing_block_clip_and_scroll_info
}
position::T::fixed => {
preserved_state.push_clip(state, &max_rect(), position::T::fixed);
- state.current_scroll_root_id
+ state.current_clip_and_scroll_info
}
- _ => state.current_scroll_root_id,
+ _ => state.current_clip_and_scroll_info,
};
- self.base.scroll_root_id = Some(containing_scroll_root_id);
+ self.base.clip_and_scroll_info = Some(containing_clip_and_scroll_info);
let coordinate_system = if self.fragment.establishes_stacking_context() {
CoordinateSystem::Own
@@ -2489,11 +2492,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
match self.positioning() {
position::T::absolute | position::T::relative | position::T::fixed =>
- state.containing_block_scroll_root_id = state.current_scroll_root_id,
+ state.containing_block_clip_and_scroll_info = state.current_clip_and_scroll_info,
_ => {}
}
- containing_scroll_root_id
+ containing_clip_and_scroll_info
}
fn setup_scroll_root_for_overflow(&mut self,
@@ -2542,7 +2545,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size;
let content_size = Size2D::new(content_size.x, content_size.y);
- let parent_id = self.scroll_root_id(state.layout_context.id);
+ let parent_id = self.clip_and_scroll_info(state.layout_context.id).scroll_node_id;
state.add_scroll_root(
ScrollRoot {
id: new_scroll_root_id,
@@ -2554,8 +2557,9 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
self.base.stacking_context_id
);
- self.base.scroll_root_id = Some(new_scroll_root_id);
- state.current_scroll_root_id = new_scroll_root_id;
+ let new_clip_and_scroll_info = ClipAndScrollInfo::simple(new_scroll_root_id);
+ self.base.clip_and_scroll_info = Some(new_clip_and_scroll_info);
+ state.current_clip_and_scroll_info = new_clip_and_scroll_info;
}
/// Adds a scroll root for a block to take the `clip` property into account
@@ -2570,6 +2574,13 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
_ => return,
};
+ // CSS `clip` should only apply to position:absolute or positione:fixed elements.
+ // CSS Masking Appendix A: "Applies to: Absolutely positioned elements."
+ match self.positioning() {
+ position::T::absolute | position::T::fixed => {}
+ _ => return,
+ }
+
let clip_origin = Point2D::new(stacking_relative_border_box.origin.x +
style_clip_rect.left.unwrap_or(Au(0)),
stacking_relative_border_box.origin.y +
@@ -2595,7 +2606,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let clip_rect = Rect::new(clip_origin, clip_size);
preserved_state.push_clip(state, &clip_rect, self.positioning());
- let parent_id = self.scroll_root_id(state.layout_context.id);
+ let parent_id = self.clip_and_scroll_info(state.layout_context.id).scroll_node_id;
state.add_scroll_root(
ScrollRoot {
id: new_scroll_root_id,
@@ -2607,13 +2618,14 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
self.base.stacking_context_id
);
- self.base.scroll_root_id = Some(new_scroll_root_id);
- state.current_scroll_root_id = new_scroll_root_id;
+ let new_clip_and_scroll_info = ClipAndScrollInfo::new(new_scroll_root_id, new_scroll_root_id);
+ self.base.clip_and_scroll_info = Some(new_clip_and_scroll_info);
+ state.current_clip_and_scroll_info = new_clip_and_scroll_info;
}
fn create_pseudo_stacking_context_for_block(&mut self,
parent_stacking_context_id: StackingContextId,
- parent_scroll_root_id: ClipId,
+ parent_clip_and_scroll_info: ClipAndScrollInfo,
state: &mut DisplayListBuildState) {
let creation_mode = if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) ||
self.fragment.style.get_box().position != position::T::static_ {
@@ -2627,7 +2639,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
&self.base,
ScrollPolicy::Scrollable,
creation_mode,
- parent_scroll_root_id);
+ parent_clip_and_scroll_info);
state.add_stacking_context(parent_stacking_context_id, new_context);
self.base.collect_stacking_contexts_for_children(state);
@@ -2647,7 +2659,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
fn create_real_stacking_context_for_block(&mut self,
parent_stacking_context_id: StackingContextId,
- parent_scroll_root_id: ClipId,
+ parent_clip_and_scroll_info: ClipAndScrollInfo,
state: &mut DisplayListBuildState) {
let scroll_policy = if self.is_fixed() {
ScrollPolicy::Fixed
@@ -2660,7 +2672,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
&self.base,
scroll_policy,
StackingContextCreationMode::Normal,
- parent_scroll_root_id);
+ parent_clip_and_scroll_info);
state.add_stacking_context(parent_stacking_context_id, stacking_context);
self.base.collect_stacking_contexts_for_children(state);
@@ -2715,13 +2727,13 @@ pub trait InlineFlowDisplayListBuilding {
impl InlineFlowDisplayListBuilding for InlineFlow {
fn collect_stacking_contexts_for_inline(&mut self, state: &mut DisplayListBuildState) {
self.base.stacking_context_id = state.current_stacking_context_id;
- self.base.scroll_root_id = Some(state.current_scroll_root_id);
+ self.base.clip_and_scroll_info = Some(state.current_clip_and_scroll_info);
self.base.clip = state.clip_stack.last().cloned().unwrap_or_else(max_rect);
for mut fragment in self.fragments.fragments.iter_mut() {
- let previous_containing_block_scroll_root_id = state.containing_block_scroll_root_id;
+ let previous_cb_clip_scroll_info = state.containing_block_clip_and_scroll_info;
if establishes_containing_block_for_absolute(fragment.style.get_box().position) {
- state.containing_block_scroll_root_id = state.current_scroll_root_id;
+ state.containing_block_clip_and_scroll_info = state.current_clip_and_scroll_info;
}
match fragment.specific {
@@ -2745,14 +2757,14 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
&self.base,
ScrollPolicy::Scrollable,
StackingContextCreationMode::Normal,
- state.current_scroll_root_id);
+ state.current_clip_and_scroll_info);
state.add_stacking_context(current_stacking_context_id,
stacking_context);
}
_ => fragment.stacking_context_id = state.current_stacking_context_id,
}
- state.containing_block_scroll_root_id = previous_containing_block_scroll_root_id;
+ state.containing_block_clip_and_scroll_info = previous_cb_clip_scroll_info;
}
}