aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/display_list_builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout/display_list_builder.rs')
-rw-r--r--components/layout/display_list_builder.rs190
1 files changed, 132 insertions, 58 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index 17b3e07f716..b22a1bda2e1 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -15,6 +15,7 @@ use block::{BlockFlow, BlockStackingContextType};
use canvas_traits::{CanvasData, CanvasMsg, FromLayoutMsg};
use context::SharedLayoutContext;
use euclid::{Matrix4D, Point2D, Radians, Rect, SideOffsets2D, Size2D};
+use euclid::point::TypedPoint2D;
use flex::FlexFlow;
use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED};
use flow_ref::FlowRef;
@@ -25,7 +26,7 @@ use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, Cl
use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection};
use gfx::display_list::{GradientDisplayItem, IframeDisplayItem, ImageDisplayItem};
use gfx::display_list::{LineDisplayItem, OpaqueNode};
-use gfx::display_list::{SolidColorDisplayItem, StackingContext, StackingContextType};
+use gfx::display_list::{SolidColorDisplayItem, ScrollRoot, StackingContext, StackingContextType};
use gfx::display_list::{TextDisplayItem, TextOrientation, WebGLDisplayItem, WebRenderImageInfo};
use gfx_traits::{ScrollPolicy, ScrollRootId, StackingContextId};
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT};
@@ -43,8 +44,8 @@ use std::mem;
use std::sync::Arc;
use style::computed_values::{background_attachment, background_clip, background_origin};
use style::computed_values::{background_repeat, background_size, border_style};
-use style::computed_values::{cursor, image_rendering, overflow_x, pointer_events, position};
-use style::computed_values::{transform, transform_style, visibility};
+use style::computed_values::{cursor, image_rendering, overflow_x};
+use style::computed_values::{pointer_events, position, transform, transform_style, visibility};
use style::computed_values::_servo_overflow_clip_box as overflow_clip_box;
use style::computed_values::filter::Filter;
use style::computed_values::text_shadow::TextShadow;
@@ -95,6 +96,7 @@ pub struct DisplayListBuildState<'a> {
pub shared_layout_context: &'a SharedLayoutContext,
pub root_stacking_context: StackingContext,
pub items: HashMap<StackingContextId, Vec<DisplayItem>>,
+ pub scroll_roots: HashMap<ScrollRootId, ScrollRoot>,
pub stacking_context_id_stack: Vec<StackingContextId>,
pub scroll_root_id_stack: Vec<ScrollRootId>,
}
@@ -107,6 +109,7 @@ impl<'a> DisplayListBuildState<'a> {
shared_layout_context: shared_layout_context,
root_stacking_context: StackingContext::root(),
items: HashMap::new(),
+ scroll_roots: HashMap::new(),
stacking_context_id_stack: vec!(stacking_context_id),
scroll_root_id_stack: vec!(ScrollRootId::root()),
}
@@ -117,6 +120,11 @@ impl<'a> DisplayListBuildState<'a> {
items.push(display_item);
}
+ fn add_scroll_root(&mut self, scroll_root: ScrollRoot) {
+ debug_assert!(!self.scroll_roots.contains_key(&scroll_root.id));
+ self.scroll_roots.insert(scroll_root.id, scroll_root);
+ }
+
pub fn stacking_context_id(&self) -> StackingContextId {
self.stacking_context_id_stack.last().unwrap().clone()
}
@@ -126,14 +134,19 @@ impl<'a> DisplayListBuildState<'a> {
}
pub fn pop_stacking_context_id(&mut self) {
- self.stacking_context_id_stack.pop();
assert!(!self.stacking_context_id_stack.is_empty());
+ self.stacking_context_id_stack.pop();
}
- pub fn scroll_root_id(&mut self) -> ScrollRootId {
+ pub fn scroll_root_id(&self) -> ScrollRootId {
self.scroll_root_id_stack.last().unwrap().clone()
}
+ pub fn parent_scroll_root_id(&self) -> ScrollRootId {
+ debug_assert!(self.scroll_root_id_stack.len() > 1);
+ self.scroll_root_id_stack[self.scroll_root_id_stack.len() - 2]
+ }
+
pub fn push_scroll_root_id(&mut self, id: ScrollRootId) {
self.scroll_root_id_stack.push(id);
}
@@ -157,14 +170,18 @@ impl<'a> DisplayListBuildState<'a> {
},
&clip,
section,
- self.stacking_context_id())
+ self.stacking_context_id(),
+ self.scroll_root_id())
}
pub fn to_display_list(mut self) -> DisplayList {
+ let mut scroll_root_stack = Vec::new();
+ scroll_root_stack.push(ScrollRootId::root());
+
let mut list = Vec::new();
let root_context = mem::replace(&mut self.root_stacking_context, StackingContext::root());
- self.to_display_list_for_stacking_context(&mut list, root_context);
+ self.to_display_list_for_stacking_context(&mut list, root_context, &mut scroll_root_stack);
DisplayList {
list: list,
@@ -173,7 +190,8 @@ impl<'a> DisplayListBuildState<'a> {
fn to_display_list_for_stacking_context(&mut self,
list: &mut Vec<DisplayItem>,
- mut stacking_context: StackingContext) {
+ mut stacking_context: StackingContext,
+ scroll_root_stack: &mut Vec<ScrollRootId>) {
let mut child_items = self.items.remove(&stacking_context.id).unwrap_or(Vec::new());
child_items.sort_by(|a, b| a.base().section.cmp(&b.base().section));
child_items.reverse();
@@ -186,63 +204,114 @@ impl<'a> DisplayListBuildState<'a> {
if !real_stacking_context {
self.to_display_list_for_items(list,
child_items,
- child_stacking_contexts);
+ child_stacking_contexts,
+ scroll_root_stack);
return;
}
+ let mut scroll_root_stack = Vec::new();
+ scroll_root_stack.push(stacking_context.parent_scroll_id);
+
let (push_item, pop_item) = stacking_context.to_display_list_items();
list.push(push_item);
self.to_display_list_for_items(list,
child_items,
- child_stacking_contexts);
+ child_stacking_contexts,
+ &mut scroll_root_stack);
list.push(pop_item);
}
fn to_display_list_for_items(&mut self,
list: &mut Vec<DisplayItem>,
mut child_items: Vec<DisplayItem>,
- child_stacking_contexts: Vec<StackingContext>) {
+ child_stacking_contexts: Vec<StackingContext>,
+ scroll_root_stack: &mut Vec<ScrollRootId>) {
// Properly order display items that make up a stacking context. "Steps" here
// refer to the steps in CSS 2.1 Appendix E.
// Steps 1 and 2: Borders and background for the root.
while child_items.last().map_or(false,
|child| child.section() == DisplayListSection::BackgroundAndBorders) {
- list.push(child_items.pop().unwrap());
+ let item = child_items.pop().unwrap();
+ self.switch_scroll_roots(list, item.scroll_root_id(), scroll_root_stack);
+ list.push(item);
}
// Step 3: Positioned descendants with negative z-indices.
let mut child_stacking_contexts = child_stacking_contexts.into_iter().peekable();
while child_stacking_contexts.peek().map_or(false, |child| child.z_index < 0) {
let context = child_stacking_contexts.next().unwrap();
- self.to_display_list_for_stacking_context(list, context);
+ self.switch_scroll_roots(list, context.parent_scroll_id, scroll_root_stack);
+ self.to_display_list_for_stacking_context(list, context, scroll_root_stack);
}
// Step 4: Block backgrounds and borders.
while child_items.last().map_or(false,
|child| child.section() == DisplayListSection::BlockBackgroundsAndBorders) {
- list.push(child_items.pop().unwrap());
+ let item = child_items.pop().unwrap();
+ self.switch_scroll_roots(list, item.scroll_root_id(), scroll_root_stack);
+ list.push(item);
}
// Step 5: Floats.
while child_stacking_contexts.peek().map_or(false,
|child| child.context_type == StackingContextType::PseudoFloat) {
let context = child_stacking_contexts.next().unwrap();
- self.to_display_list_for_stacking_context(list, context);
+ self.switch_scroll_roots(list, context.parent_scroll_id, scroll_root_stack);
+ self.to_display_list_for_stacking_context(list, context, scroll_root_stack);
}
// Step 6 & 7: Content and inlines that generate stacking contexts.
while child_items.last().map_or(false,
|child| child.section() == DisplayListSection::Content) {
- list.push(child_items.pop().unwrap());
+ let item = child_items.pop().unwrap();
+ self.switch_scroll_roots(list, item.scroll_root_id(), scroll_root_stack);
+ list.push(item);
}
// Step 8 & 9: Positioned descendants with nonnegative, numeric z-indices.
for child in child_stacking_contexts {
- self.to_display_list_for_stacking_context(list, child);
+ self.switch_scroll_roots(list, child.parent_scroll_id, scroll_root_stack);
+ self.to_display_list_for_stacking_context(list, child, scroll_root_stack);
}
// Step 10: Outlines.
- list.extend(child_items);
+ for item in child_items.drain(..) {
+ self.switch_scroll_roots(list, item.scroll_root_id(), scroll_root_stack);
+ list.push(item);
+ }
+
+ for _ in scroll_root_stack.drain(1..) {
+ list.push(DisplayItem::PopScrollRoot(Box::new(BaseDisplayItem::empty())));
+ }
+ }
+
+ fn switch_scroll_roots(&self,
+ list: &mut Vec<DisplayItem>,
+ new_id: ScrollRootId,
+ scroll_root_stack: &mut Vec<ScrollRootId>) {
+ if new_id == *scroll_root_stack.last().unwrap() {
+ return;
+ }
+
+ if new_id == *scroll_root_stack.first().unwrap() {
+ for _ in scroll_root_stack.drain(1..) {
+ list.push(DisplayItem::PopScrollRoot(Box::new(BaseDisplayItem::empty())));
+ }
+ return;
+ }
+
+ // We never want to reach the root of the ScrollRoot tree without encountering the
+ // containing scroll root of this StackingContext. If this does happen we've tried to
+ // switch to a ScrollRoot that does not contain our current stacking context or isn't
+ // itself a direct child of our current stacking context. This should never happen
+ // due to CSS stacking rules.
+ debug_assert!(new_id != ScrollRootId::root());
+
+ let scroll_root = self.scroll_roots.get(&new_id).unwrap();
+ self.switch_scroll_roots(list, scroll_root.parent_id, scroll_root_stack);
+
+ scroll_root_stack.push(new_id);
+ list.push(scroll_root.to_push());
}
}
@@ -419,7 +488,7 @@ pub trait FragmentDisplayListBuilding {
base_flow: &BaseFlow,
scroll_policy: ScrollPolicy,
mode: StackingContextCreationMode,
- scroll_root_id: Option<ScrollRootId>)
+ parent_scroll_id: ScrollRootId)
-> StackingContext;
/// Returns the 4D matrix representing this fragment's transform.
@@ -1476,9 +1545,8 @@ impl FragmentDisplayListBuilding for Fragment {
base_flow: &BaseFlow,
scroll_policy: ScrollPolicy,
mode: StackingContextCreationMode,
- scroll_root_id: Option<ScrollRootId>)
+ parent_scroll_id: ScrollRootId)
-> StackingContext {
- let scrolls_overflow_area = mode == StackingContextCreationMode::ScrollWrapper;
let border_box =
self.stacking_relative_border_box(&base_flow.stacking_relative_position,
&base_flow.early_absolute_position_info
@@ -1486,16 +1554,12 @@ impl FragmentDisplayListBuilding for Fragment {
base_flow.early_absolute_position_info
.relative_containing_block_mode,
CoordinateSystem::Parent);
- let overflow = if scrolls_overflow_area {
- Rect::new(Point2D::zero(), base_flow.overflow.scroll.size)
- } else {
- // First, compute the offset of our border box (including relative positioning)
- // from our flow origin, since that is what `BaseFlow::overflow` is relative to.
- let border_box_offset =
- border_box.translate(&-base_flow.stacking_relative_position).origin;
- // Then, using that, compute our overflow region relative to our border box.
- base_flow.overflow.paint.translate(&-border_box_offset)
- };
+ // First, compute the offset of our border box (including relative positioning)
+ // from our flow origin, since that is what `BaseFlow::overflow` is relative to.
+ let border_box_offset =
+ border_box.translate(&-base_flow.stacking_relative_position).origin;
+ // Then, using that, compute our overflow region relative to our border box.
+ let overflow = base_flow.overflow.paint.translate(&-border_box_offset);
let transform = self.transform_matrix(&border_box);
let perspective = match self.style().get_effects().perspective {
@@ -1531,8 +1595,7 @@ impl FragmentDisplayListBuilding for Fragment {
}
let transform_style = self.style().get_used_transform_style();
- let establishes_3d_context = scrolls_overflow_area ||
- transform_style == transform_style::T::flat;
+ let establishes_3d_context = transform_style == transform_style::T::flat;
let context_type = match mode {
StackingContextCreationMode::PseudoFloat => StackingContextType::PseudoFloat,
@@ -1551,7 +1614,7 @@ impl FragmentDisplayListBuilding for Fragment {
perspective,
establishes_3d_context,
scroll_policy,
- scroll_root_id)
+ parent_scroll_id)
}
fn adjust_clipping_region_for_children(&self,
@@ -1829,18 +1892,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
fn collect_stacking_contexts_for_block(&mut self,
parent: &mut StackingContext,
parent_scroll_root_id: ScrollRootId) {
- let block_stacking_context_type = self.block_stacking_context_type();
- if block_stacking_context_type == BlockStackingContextType::NonstackingContext {
- self.base.stacking_context_id = parent.id;
- self.base.collect_stacking_contexts_for_children(parent, parent_scroll_root_id);
- return;
- }
-
- let stacking_context_id = StackingContextId::new_of_type(self.fragment.node.id() as usize,
- self.fragment.fragment_type());
-
- let has_scrolling_overflow = self.has_scrolling_overflow();
- let scroll_root_id = if has_scrolling_overflow {
+ let scroll_root_id = if self.has_scrolling_overflow() {
ScrollRootId::new_of_type(self.fragment.node.id() as usize,
self.fragment.fragment_type())
} else {
@@ -1848,7 +1900,15 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
};
self.base.scroll_root_id = scroll_root_id;
+ let block_stacking_context_type = self.block_stacking_context_type();
+ if block_stacking_context_type == BlockStackingContextType::NonstackingContext {
+ self.base.stacking_context_id = parent.id;
+ self.base.collect_stacking_contexts_for_children(parent, scroll_root_id);
+ return;
+ }
+ let stacking_context_id = StackingContextId::new_of_type(self.fragment.node.id() as usize,
+ self.fragment.fragment_type());
self.base.stacking_context_id = stacking_context_id;
if block_stacking_context_type == BlockStackingContextType::PseudoStackingContext {
@@ -1864,7 +1924,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
&self.base,
ScrollPolicy::Scrollable,
creation_mode,
- None);
+ parent_scroll_root_id);
self.base.collect_stacking_contexts_for_children(&mut new_context, scroll_root_id);
let new_children: Vec<StackingContext> = new_context.children.drain(..).collect();
@@ -1888,20 +1948,13 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
ScrollPolicy::Scrollable
};
- let (creation_mode, internal_id) = if has_scrolling_overflow {
- (StackingContextCreationMode::ScrollWrapper, Some(self.base.scroll_root_id))
- } else {
- (StackingContextCreationMode::Normal, None)
- };
-
let mut stacking_context = self.fragment.create_stacking_context(
stacking_context_id,
&self.base,
scroll_policy,
- creation_mode,
- internal_id);
+ StackingContextCreationMode::Normal,
+ parent_scroll_root_id);
self.base.collect_stacking_contexts_for_children(&mut stacking_context, scroll_root_id);
-
parent.add_child(stacking_context);
}
@@ -1921,6 +1974,28 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
DisplayListSection::BlockBackgroundsAndBorders
};
+ if self.has_scrolling_overflow() {
+ let size = self.base.overflow.scroll.size;
+ let mut clip = self.fragment.stacking_relative_border_box(
+ &self.base.stacking_relative_position,
+ &self.base.early_absolute_position_info.relative_containing_block_size,
+ self.base.early_absolute_position_info.relative_containing_block_mode,
+ CoordinateSystem::Parent);
+ if establishes_stacking_context {
+ clip = Rect::new(TypedPoint2D::zero(), clip.size);
+ }
+
+ let parent_id = state.parent_scroll_root_id();
+ state.add_scroll_root(
+ ScrollRoot {
+ id: self.base.scroll_root_id,
+ parent_id: parent_id,
+ clip: clip,
+ size: size,
+ }
+ );
+ }
+
// Add the box that starts the block context.
self.fragment
.build_display_list(state,
@@ -2025,7 +2100,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
&self.base,
ScrollPolicy::Scrollable,
StackingContextCreationMode::Normal,
- None));
+ parent_scroll_root_id));
}
_ => fragment.stacking_context_id = parent.id,
}
@@ -2229,7 +2304,6 @@ pub enum BorderPaintingMode<'a> {
#[derive(Copy, Clone, PartialEq)]
pub enum StackingContextCreationMode {
Normal,
- ScrollWrapper,
PseudoPositioned,
PseudoFloat,
}