diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 99b1fc0b21c..ab8f1ba7a8f 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -81,7 +81,15 @@ impl DisplayList { scroll_offsets, result); } - &DisplayItem::PopStackingContext(_) => return, + &DisplayItem::PushScrollRoot(ref item) => { + self.hit_test_scroll_root(traversal, + &item.scroll_root, + *translated_point, + client_point, + scroll_offsets, + result); + } + &DisplayItem::PopStackingContext(_) | &DisplayItem::PopScrollRoot(_) => return, _ => { if let Some(meta) = item.hit_test(*translated_point) { result.push(meta); @@ -91,6 +99,26 @@ impl DisplayList { } } + fn hit_test_scroll_root<'a>(&self, + traversal: &mut DisplayListTraversal<'a>, + scroll_root: &ScrollRoot, + mut translated_point: Point2D, + client_point: &Point2D, + scroll_offsets: &ScrollOffsetMap, + result: &mut Vec) { + // Adjust the translated point to account for the scroll offset if + // necessary. This can only happen when WebRender is in use. + // + // We don't perform this adjustment on the root stacking context because + // the DOM-side code has already translated the point for us (e.g. in + // `Window::hit_test_query()`) by now. + if let Some(scroll_offset) = scroll_offsets.get(&scroll_root.id) { + translated_point.x -= Au::from_f32_px(scroll_offset.x); + translated_point.y -= Au::from_f32_px(scroll_offset.y); + } + self.hit_test_contents(traversal, &translated_point, client_point, scroll_offsets, result); + } + fn hit_test_stacking_context<'a>(&self, traversal: &mut DisplayListTraversal<'a>, stacking_context: &StackingContext, @@ -102,7 +130,7 @@ impl DisplayList { // stacking context isn't fixed. If it's fixed, we need to use the client point anyway. debug_assert!(stacking_context.context_type == StackingContextType::Real); let is_fixed = stacking_context.scroll_policy == ScrollPolicy::FixedPosition; - let mut translated_point = if is_fixed { + let translated_point = if is_fixed { *client_point } else { let point = *translated_point - stacking_context.bounds.origin; @@ -112,21 +140,6 @@ impl DisplayList { Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y)) }; - // Adjust the translated point to account for the scroll offset if - // necessary. This can only happen when WebRender is in use. - // - // We don't perform this adjustment on the root stacking context because - // the DOM-side code has already translated the point for us (e.g. in - // `Window::hit_test_query()`) by now. - if !is_fixed && stacking_context.id != StackingContextId::root() { - if let Some(scroll_root_id) = stacking_context.overflow_scroll_id { - if let Some(scroll_offset) = scroll_offsets.get(&scroll_root_id) { - translated_point.x -= Au::from_f32_px(scroll_offset.x); - translated_point.y -= Au::from_f32_px(scroll_offset.y); - } - } - } - self.hit_test_contents(traversal, &translated_point, client_point, scroll_offsets, result); } @@ -138,9 +151,10 @@ impl DisplayList { pub fn print_with_tree(&self, print_tree: &mut PrintTree) { print_tree.new_level("Items".to_owned()); for item in &self.list { - print_tree.add_item(format!("{:?} StackingContext: {:?}", + print_tree.add_item(format!("{:?} StackingContext: {:?} ScrollRoot: {:?}", item, - item.base().stacking_context_id)); + item.base().stacking_context_id, + item.scroll_root_id())); } print_tree.end_level(); } @@ -250,6 +264,7 @@ pub enum StackingContextType { Real, PseudoPositioned, PseudoFloat, + PseudoScrollingArea, } #[derive(Clone, HeapSizeOf, Deserialize, Serialize)] @@ -291,8 +306,8 @@ pub struct StackingContext { /// Children of this StackingContext. pub children: Vec, - /// If this StackingContext scrolls its overflow area, this will contain the id. - pub overflow_scroll_id: Option, + /// The id of the parent scrolling area that contains this StackingContext. + pub parent_scroll_id: ScrollRootId, } impl StackingContext { @@ -309,7 +324,7 @@ impl StackingContext { perspective: Matrix4D, establishes_3d_context: bool, scroll_policy: ScrollPolicy, - scroll_root_id: Option) + parent_scroll_id: ScrollRootId) -> StackingContext { StackingContext { id: id, @@ -324,7 +339,7 @@ impl StackingContext { establishes_3d_context: establishes_3d_context, scroll_policy: scroll_policy, children: Vec::new(), - overflow_scroll_id: scroll_root_id, + parent_scroll_id: parent_scroll_id, } } @@ -341,7 +356,7 @@ impl StackingContext { Matrix4D::identity(), true, ScrollPolicy::Scrollable, - None) + ScrollRootId::root()) } pub fn add_child(&mut self, mut child: StackingContext) { @@ -453,21 +468,40 @@ impl fmt::Debug for StackingContext { "Pseudo-StackingContext" }; - let scrollable_string = if self.overflow_scroll_id.is_some() { - " (scrolls overflow area)" - } else { - "" - }; - - write!(f, "{}{} at {:?} with overflow {:?}: {:?}", + write!(f, "{} at {:?} with overflow {:?}: {:?}", type_string, - scrollable_string, self.bounds, self.overflow, self.id) } } +/// Defines a stacking context. +#[derive(Clone, Debug, HeapSizeOf, Deserialize, Serialize)] +pub struct ScrollRoot { + /// The unique ID of this ScrollRoot. + pub id: ScrollRootId, + + /// The unique ID of the parent of this ScrollRoot. + pub parent_id: ScrollRootId, + + /// The position of this scroll root's frame in the parent stacking context. + pub clip: Rect, + + /// The size of the contents that can be scrolled inside of the scroll root. + pub size: Size2D, +} + +impl ScrollRoot { + pub fn to_push(&self) -> DisplayItem { + DisplayItem::PushScrollRoot(box PushScrollRootItem { + base: BaseDisplayItem::empty(), + scroll_root: self.clone(), + }) + } +} + + /// One drawing command in the list. #[derive(Clone, Deserialize, HeapSizeOf, Serialize)] pub enum DisplayItem { @@ -482,6 +516,8 @@ pub enum DisplayItem { Iframe(Box), PushStackingContext(Box), PopStackingContext(Box), + PushScrollRoot(Box), + PopScrollRoot(Box), } /// Information common to all display items. @@ -501,6 +537,9 @@ pub struct BaseDisplayItem { /// The id of the stacking context this item belongs to. pub stacking_context_id: StackingContextId, + + /// The id of the scroll root this item belongs to. + pub scroll_root_id: ScrollRootId, } impl BaseDisplayItem { @@ -509,7 +548,8 @@ impl BaseDisplayItem { metadata: DisplayItemMetadata, clip: &ClippingRegion, section: DisplayListSection, - stacking_context_id: StackingContextId) + stacking_context_id: StackingContextId, + scroll_root_id: ScrollRootId) -> BaseDisplayItem { // Detect useless clipping regions here and optimize them to `ClippingRegion::max()`. // The painting backend may want to optimize out clipping regions and this makes it easier @@ -524,6 +564,7 @@ impl BaseDisplayItem { }, section: section, stacking_context_id: stacking_context_id, + scroll_root_id: scroll_root_id, } } @@ -538,6 +579,7 @@ impl BaseDisplayItem { clip: ClippingRegion::max(), section: DisplayListSection::Content, stacking_context_id: StackingContextId::root(), + scroll_root_id: ScrollRootId::root(), } } } @@ -981,6 +1023,15 @@ pub struct PopStackingContextItem { pub stacking_context_id: StackingContextId, } +/// Starts a group of items inside a particular scroll root. +#[derive(Clone, HeapSizeOf, Deserialize, Serialize)] +pub struct PushScrollRootItem { + /// Fields common to all display items. + pub base: BaseDisplayItem, + + /// The scroll root that this item starts. + pub scroll_root: ScrollRoot, +} /// How a box shadow should be clipped. #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf, Deserialize, Serialize)] @@ -1009,9 +1060,15 @@ impl DisplayItem { DisplayItem::Iframe(ref iframe) => &iframe.base, DisplayItem::PushStackingContext(ref stacking_context) => &stacking_context.base, DisplayItem::PopStackingContext(ref item) => &item.base, + DisplayItem::PushScrollRoot(ref item) => &item.base, + DisplayItem::PopScrollRoot(ref base) => &base, } } + pub fn scroll_root_id(&self) -> ScrollRootId { + self.base().scroll_root_id + } + pub fn stacking_context_id(&self) -> StackingContextId { self.base().stacking_context_id } @@ -1090,6 +1147,14 @@ impl fmt::Debug for DisplayItem { return write!(f, "PopStackingContext({:?}", item.stacking_context_id); } + if let DisplayItem::PushScrollRoot(ref item) = *self { + return write!(f, "PushScrollRoot({:?}", item.scroll_root); + } + + if let DisplayItem::PopScrollRoot(_) = *self { + return write!(f, "PopScrollRoot"); + } + write!(f, "{} @ {:?} {:?}", match *self { DisplayItem::SolidColor(ref solid_color) => @@ -1106,8 +1171,10 @@ impl fmt::Debug for DisplayItem { DisplayItem::Line(_) => "Line".to_owned(), DisplayItem::BoxShadow(_) => "BoxShadow".to_owned(), DisplayItem::Iframe(_) => "Iframe".to_owned(), - DisplayItem::PushStackingContext(_) => "".to_owned(), - DisplayItem::PopStackingContext(_) => "".to_owned(), + DisplayItem::PushStackingContext(_) | + DisplayItem::PopStackingContext(_) | + DisplayItem::PushScrollRoot(_) | + DisplayItem::PopScrollRoot(_) => "".to_owned(), }, self.bounds(), self.base().clip diff --git a/components/gfx_traits/lib.rs b/components/gfx_traits/lib.rs index 09593189512..9925326da7e 100644 --- a/components/gfx_traits/lib.rs +++ b/components/gfx_traits/lib.rs @@ -211,6 +211,11 @@ impl ScrollRootId { pub fn fragment_type(&self) -> FragmentType { FragmentType::from_usize(self.0 & 3) } + + #[inline] + pub fn to_stacking_context_id(&self) -> StackingContextId { + StackingContextId(self.0) + } } /// The type of fragment that a stacking context represents. diff --git a/components/layout/block.rs b/components/layout/block.rs index 1d6ad5a08d9..7702783bf4c 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -1941,17 +1941,6 @@ impl Flow for BlockFlow { } if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { - // `overflow: auto` and `overflow: scroll` force creation of layers, since we can only - // scroll layers. - match (self.fragment.style().get_box().overflow_x, - self.fragment.style().get_box().overflow_y.0) { - (overflow_x::T::auto, _) | (overflow_x::T::scroll, _) | - (_, overflow_x::T::auto) | (_, overflow_x::T::scroll) => { - self.base.clip = ClippingRegion::max(); - } - _ => {} - } - let position_start = self.base.position.start.to_physical(self.base.writing_mode, container_size); diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index e6501e64050..3e5328afc27 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}; @@ -42,8 +43,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>, + pub scroll_roots: HashMap, pub stacking_context_id_stack: Vec, pub scroll_root_id_stack: Vec, } @@ -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, - mut stacking_context: StackingContext) { + mut stacking_context: StackingContext, + scroll_root_stack: &mut Vec) { 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, mut child_items: Vec, - child_stacking_contexts: Vec) { + child_stacking_contexts: Vec, + scroll_root_stack: &mut Vec) { // 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, + new_id: ScrollRootId, + scroll_root_stack: &mut Vec) { + 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) + 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) + 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 = 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, } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 93206782983..1fa3ae0612a 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -2595,8 +2595,6 @@ impl Fragment { transform_style::T::auto => {} } - // FIXME(pcwalton): Don't unconditionally form stacking contexts for `overflow_x: scroll` - // and `overflow_y: scroll`. This needs multiple layers per stacking context. match (self.style().get_box().position, self.style().get_position().z_index, self.style().get_box().overflow_x, @@ -2615,11 +2613,7 @@ impl Fragment { overflow_x::T::visible) => false, (position::T::absolute, _, _, _) | (position::T::fixed, _, _, _) | - (position::T::relative, _, _, _) | - (_, _, overflow_x::T::auto, _) | - (_, _, overflow_x::T::scroll, _) | - (_, _, _, overflow_x::T::auto) | - (_, _, _, overflow_x::T::scroll) => true, + (position::T::relative, _, _, _) => true, (position::T::static_, _, _, _) => false } } diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 3124bca1dfc..b054568d5d5 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -271,34 +271,34 @@ pub struct BuildDisplayList<'a> { impl<'a> BuildDisplayList<'a> { #[inline] pub fn traverse(&mut self, flow: &mut Flow) { + let new_stacking_context = + flow::base(flow).stacking_context_id != self.state.stacking_context_id(); + if new_stacking_context { + self.state.push_stacking_context_id(flow::base(flow).stacking_context_id); + } + + let new_scroll_root = + flow::base(flow).scroll_root_id != self.state.scroll_root_id(); + if new_scroll_root { + self.state.push_scroll_root_id(flow::base(flow).scroll_root_id); + } + if self.should_process() { - let new_stacking_context = - flow::base(flow).stacking_context_id != self.state.stacking_context_id(); - if new_stacking_context { - self.state.push_stacking_context_id(flow::base(flow).stacking_context_id); - } - - let new_scroll_root = - flow::base(flow).scroll_root_id != self.state.scroll_root_id(); - if new_scroll_root { - self.state.push_scroll_root_id(flow::base(flow).scroll_root_id); - } - flow.build_display_list(&mut self.state); flow::mut_base(flow).restyle_damage.remove(REPAINT); - - if new_stacking_context { - self.state.pop_stacking_context_id(); - } - - if new_scroll_root { - self.state.pop_scroll_root_id(); - } } for kid in flow::child_iter_mut(flow) { self.traverse(kid); } + + if new_stacking_context { + self.state.pop_stacking_context_id(); + } + + if new_scroll_root { + self.state.pop_scroll_root_id(); + } } #[inline] diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs index cf239f79258..07979940314 100644 --- a/components/layout/webrender_helpers.rs +++ b/components/layout/webrender_helpers.rs @@ -8,7 +8,7 @@ // completely converting layout to directly generate WebRender display lists, for example. use app_units::Au; -use euclid::{Point2D, Rect, Size2D}; +use euclid::{Matrix4D, Point2D, Rect, Size2D, TypedRect}; use gfx::display_list::{BorderRadii, BoxShadowClipMode, ClippingRegion}; use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal}; use gfx::display_list::{StackingContext, StackingContextType}; @@ -24,7 +24,8 @@ trait WebRenderStackingContextConverter { api: &mut webrender_traits::RenderApi, pipeline_id: webrender_traits::PipelineId, epoch: webrender_traits::Epoch, - frame_builder: &mut WebRenderFrameBuilder) + frame_builder: &mut WebRenderFrameBuilder, + scroll_layer_id: Option) -> webrender_traits::StackingContextId; fn convert_children_to_webrender<'a>(&self, @@ -235,17 +236,39 @@ impl WebRenderStackingContextConverter for StackingContext { &DisplayItem::PushStackingContext(ref stacking_context_item) => { let stacking_context = &stacking_context_item.stacking_context; debug_assert!(stacking_context.context_type == StackingContextType::Real); - - let stacking_context_id = + builder.push_stacking_context( stacking_context.convert_to_webrender(traversal, api, pipeline_id, epoch, - frame_builder); - builder.push_stacking_context(stacking_context_id); - + frame_builder, + None)); } - &DisplayItem::PopStackingContext(_) => return, + &DisplayItem::PushScrollRoot(ref item) => { + let stacking_context = StackingContext::new( + StackingContextId::new(0), + StackingContextType::Real, + &item.scroll_root.clip, + &TypedRect::new(Point2D::zero(), item.scroll_root.size), + 0, + filter::T::new(Vec::new()), + mix_blend_mode::T::normal, + Matrix4D::identity(), + Matrix4D::identity(), + true, + ScrollPolicy::Scrollable, + ScrollRootId::root()); + let scroll_layer_id = + Some(frame_builder.next_scroll_layer_id(item.scroll_root.id)); + builder.push_stacking_context( + stacking_context.convert_to_webrender(traversal, + api, + pipeline_id, + epoch, + frame_builder, + scroll_layer_id)); + } + &DisplayItem::PopStackingContext(_) | &DisplayItem::PopScrollRoot(_) => return, _ => item.convert_to_webrender(builder, frame_builder), } } @@ -256,21 +279,14 @@ impl WebRenderStackingContextConverter for StackingContext { api: &mut webrender_traits::RenderApi, pipeline_id: webrender_traits::PipelineId, epoch: webrender_traits::Epoch, - frame_builder: &mut WebRenderFrameBuilder) + frame_builder: &mut WebRenderFrameBuilder, + scroll_layer_id: Option) -> webrender_traits::StackingContextId { let webrender_scroll_policy = match self.scroll_policy { ScrollPolicy::Scrollable => webrender_traits::ScrollPolicy::Scrollable, ScrollPolicy::FixedPosition => webrender_traits::ScrollPolicy::Fixed, }; - let scroll_layer_id = if let Some(scroll_root_id) = self.overflow_scroll_id { - Some(frame_builder.next_scroll_layer_id(scroll_root_id)) - } else if self.id == StackingContextId::root() { - Some(frame_builder.next_scroll_layer_id(ScrollRootId::root())) - } else { - None - }; - let mut sc = webrender_traits::StackingContext::new(scroll_layer_id, webrender_scroll_policy, @@ -310,11 +326,14 @@ impl WebRenderDisplayListConverter for DisplayList { match item { Some(&DisplayItem::PushStackingContext(ref stacking_context_item)) => { let stacking_context = &stacking_context_item.stacking_context; + let scroll_layer_id = + Some(frame_builder.next_scroll_layer_id(ScrollRootId::root())); stacking_context.convert_to_webrender(&mut traversal, api, pipeline_id, epoch, - frame_builder) + frame_builder, + scroll_layer_id) } _ => unreachable!("DisplayList did not start with StackingContext."), @@ -453,7 +472,11 @@ impl WebRenderDisplayItemConverter for DisplayItem { item.base.clip.to_clip_region(frame_builder), pipeline_id); } - DisplayItem::PushStackingContext(_) | DisplayItem::PopStackingContext(_) => {} + DisplayItem::PushStackingContext(_) | + DisplayItem::PopStackingContext(_) | + DisplayItem::PushScrollRoot(_) | + DisplayItem::PopScrollRoot(_) => + unreachable!("Tried to convert a scroll root or stacking context structure item."), } } } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 6d4f45ef99e..859183935b1 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -50,7 +50,8 @@ use euclid::rect::Rect; use euclid::scale_factor::ScaleFactor; use euclid::size::Size2D; use fnv::FnvHasher; -use gfx::display_list::{ClippingRegion, OpaqueNode, WebRenderImageInfo}; +use gfx::display_list::{ClippingRegion, OpaqueNode}; +use gfx::display_list::WebRenderImageInfo; use gfx::font; use gfx::font_cache_thread::FontCacheThread; use gfx::font_context; @@ -905,8 +906,8 @@ impl LayoutThread { match (data.goal, display_list_needed) { (ReflowGoal::ForDisplay, _) | (ReflowGoal::ForScriptQuery, true) => { let mut build_state = - sequential::build_display_list_for_subtree(layout_root, - shared_layout_context); + sequential::build_display_list_for_subtree(layout_root, + shared_layout_context); debug!("Done building display list."); diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 77621ee4190..b67414822dc 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -2832,7 +2832,7 @@ dependencies = [ [[package]] name = "webrender" version = "0.8.0" -source = "git+https://github.com/servo/webrender#8b53081a3de714f8c1296e20658fabe4e75a6244" +source = "git+https://github.com/servo/webrender#c5eb15a9b8030bbad15809aba7dd1b5906d96397" dependencies = [ "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2857,7 +2857,7 @@ dependencies = [ [[package]] name = "webrender_traits" version = "0.8.0" -source = "git+https://github.com/servo/webrender#8b53081a3de714f8c1296e20658fabe4e75a6244" +source = "git+https://github.com/servo/webrender#c5eb15a9b8030bbad15809aba7dd1b5906d96397" dependencies = [ "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 250f35df824..d5ee02102b6 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -2690,7 +2690,7 @@ dependencies = [ [[package]] name = "webrender" version = "0.8.0" -source = "git+https://github.com/servo/webrender#8b53081a3de714f8c1296e20658fabe4e75a6244" +source = "git+https://github.com/servo/webrender#c5eb15a9b8030bbad15809aba7dd1b5906d96397" dependencies = [ "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2715,7 +2715,7 @@ dependencies = [ [[package]] name = "webrender_traits" version = "0.8.0" -source = "git+https://github.com/servo/webrender#8b53081a3de714f8c1296e20658fabe4e75a6244" +source = "git+https://github.com/servo/webrender#c5eb15a9b8030bbad15809aba7dd1b5906d96397" dependencies = [ "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/resources/shaders/clip_shared.glsl b/resources/shaders/clip_shared.glsl index 2c1993b1fa5..62e51c3bbf1 100644 --- a/resources/shaders/clip_shared.glsl +++ b/resources/shaders/clip_shared.glsl @@ -9,7 +9,7 @@ flat varying vec4 vClipMaskUvRect; flat varying vec4 vClipMaskLocalRect; #ifdef WR_VERTEX_SHADER -void write_clip(ClipInfo clip) { +void write_clip(ClipData clip) { vClipRect = vec4(clip.rect.rect.xy, clip.rect.rect.xy + clip.rect.rect.zw); vClipRadius = vec4(clip.top_left.outer_inner_radius.x, clip.top_right.outer_inner_radius.x, @@ -17,8 +17,8 @@ void write_clip(ClipInfo clip) { clip.bottom_left.outer_inner_radius.x); //TODO: interpolate the final mask UV vec2 texture_size = textureSize(sMask, 0); - vClipMaskUvRect = clip.mask_info.uv_rect / texture_size.xyxy; - vClipMaskLocalRect = clip.mask_info.local_rect; //TODO: transform + vClipMaskUvRect = clip.mask_data.uv_rect / texture_size.xyxy; + vClipMaskLocalRect = clip.mask_data.local_rect; //TODO: transform } #endif diff --git a/resources/shaders/prim_shared.glsl b/resources/shaders/prim_shared.glsl index d6248030646..e4803c42a6f 100644 --- a/resources/shaders/prim_shared.glsl +++ b/resources/shaders/prim_shared.glsl @@ -347,13 +347,13 @@ ClipRect fetch_clip_rect(int index) { return rect; } -struct ImageMaskInfo { +struct ImageMaskData { vec4 uv_rect; vec4 local_rect; }; -ImageMaskInfo fetch_mask_info(int index) { - ImageMaskInfo info; +ImageMaskData fetch_mask_data(int index) { + ImageMaskData info; ivec2 uv = get_fetch_uv_2(index); @@ -379,24 +379,24 @@ ClipCorner fetch_clip_corner(int index) { return corner; } -struct ClipInfo { +struct ClipData { ClipRect rect; ClipCorner top_left; ClipCorner top_right; ClipCorner bottom_left; ClipCorner bottom_right; - ImageMaskInfo mask_info; + ImageMaskData mask_data; }; -ClipInfo fetch_clip(int index) { - ClipInfo clip; +ClipData fetch_clip(int index) { + ClipData clip; clip.rect = fetch_clip_rect(index + 0); clip.top_left = fetch_clip_corner(index + 1); clip.top_right = fetch_clip_corner(index + 2); clip.bottom_left = fetch_clip_corner(index + 3); clip.bottom_right = fetch_clip_corner(index + 4); - clip.mask_info = fetch_mask_info(index + 5); + clip.mask_data = fetch_mask_data(index + 5); return clip; } diff --git a/resources/shaders/ps_gradient_clip.vs.glsl b/resources/shaders/ps_gradient_clip.vs.glsl index 62e7caeb7f7..8c20dfdbfa4 100644 --- a/resources/shaders/ps_gradient_clip.vs.glsl +++ b/resources/shaders/ps_gradient_clip.vs.glsl @@ -66,6 +66,6 @@ void main(void) { break; } - ClipInfo clip = fetch_clip(prim.clip_index); + ClipData clip = fetch_clip(prim.clip_index); write_clip(clip); } diff --git a/resources/shaders/ps_image_clip.vs.glsl b/resources/shaders/ps_image_clip.vs.glsl index 5426d997e4d..5e9161008e5 100644 --- a/resources/shaders/ps_image_clip.vs.glsl +++ b/resources/shaders/ps_image_clip.vs.glsl @@ -23,7 +23,7 @@ void main(void) { vLocalPos = vi.local_clamped_pos; #endif - ClipInfo clip = fetch_clip(prim.clip_index); + ClipData clip = fetch_clip(prim.clip_index); write_clip(clip); // vUv will contain how many times this image has wrapped around the image size. diff --git a/resources/shaders/ps_rectangle_clip.vs.glsl b/resources/shaders/ps_rectangle_clip.vs.glsl index 20fef16a3d7..1cc281ac61c 100644 --- a/resources/shaders/ps_rectangle_clip.vs.glsl +++ b/resources/shaders/ps_rectangle_clip.vs.glsl @@ -22,6 +22,6 @@ void main(void) { vPos = vi.local_clamped_pos; #endif - ClipInfo clip = fetch_clip(prim.clip_index); + ClipData clip = fetch_clip(prim.clip_index); write_clip(clip); } diff --git a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-010.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-010.htm.ini deleted file mode 100644 index 5504b7061bf..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-010.htm.ini +++ /dev/null @@ -1,4 +0,0 @@ -[abspos-overflow-010.htm] - type: reftest - expected: - if os == "linux": FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-011.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-011.htm.ini deleted file mode 100644 index 6ef62736b3c..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-011.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abspos-overflow-011.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/max-width-106.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/max-width-106.htm.ini deleted file mode 100644 index 4aea4cf359c..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/max-width-106.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[max-width-106.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 3961a0d42eb..b0b62309839 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -5016,6 +5016,30 @@ "url": "/_mozilla/css/stacking_context_rtl.html" } ], + "css/stacking_order_overflow_auto.html": [ + { + "path": "css/stacking_order_overflow_auto.html", + "references": [ + [ + "/_mozilla/css/stacking_order_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/stacking_order_overflow_auto.html" + } + ], + "css/stacking_order_overflow_scroll.html": [ + { + "path": "css/stacking_order_overflow_scroll.html", + "references": [ + [ + "/_mozilla/css/stacking_order_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/stacking_order_overflow_scroll.html" + } + ], "css/style_is_in_doc.html": [ { "path": "css/style_is_in_doc.html", @@ -19848,6 +19872,30 @@ "url": "/_mozilla/css/stacking_context_rtl.html" } ], + "css/stacking_order_overflow_auto.html": [ + { + "path": "css/stacking_order_overflow_auto.html", + "references": [ + [ + "/_mozilla/css/stacking_order_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/stacking_order_overflow_auto.html" + } + ], + "css/stacking_order_overflow_scroll.html": [ + { + "path": "css/stacking_order_overflow_scroll.html", + "references": [ + [ + "/_mozilla/css/stacking_order_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/stacking_order_overflow_scroll.html" + } + ], "css/style_is_in_doc.html": [ { "path": "css/style_is_in_doc.html", diff --git a/tests/wpt/mozilla/tests/css/stacking_order_overflow_auto.html b/tests/wpt/mozilla/tests/css/stacking_order_overflow_auto.html new file mode 100644 index 00000000000..9da6457ce1f --- /dev/null +++ b/tests/wpt/mozilla/tests/css/stacking_order_overflow_auto.html @@ -0,0 +1,31 @@ + + + + + The position:absolute element should be on top of the overflow:auto element + + + +
+
diff --git a/tests/wpt/mozilla/tests/css/stacking_order_overflow_scroll.html b/tests/wpt/mozilla/tests/css/stacking_order_overflow_scroll.html new file mode 100644 index 00000000000..c8e3729dce3 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/stacking_order_overflow_scroll.html @@ -0,0 +1,31 @@ + + + + + The position:absolute element should be on top of the overflow:scroll element + + + +
+
diff --git a/tests/wpt/mozilla/tests/css/stacking_order_ref.html b/tests/wpt/mozilla/tests/css/stacking_order_ref.html new file mode 100644 index 00000000000..908f43b1295 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/stacking_order_ref.html @@ -0,0 +1,20 @@ + + + + + + +