Don't promote all scrollable regions to stacking contexts

Instead annotate all flows with their owning ScrollRoots. When
processing the display list items into a flattened display list, we add
PushScrollRoot and PopScrollRoot to signal when scrolling regions start
and end. It is possible for content from different scrolling regions to
intersect and when they do, the stack of scrolling regions is
duplicated.  When these duplicated scrolling regions stacks reach
WebRender, it will scroll them in tandem.

The PushScrollRoot and PopScrollRoot items are currently represented as
StackingContexts in WebRender, but eventually these will be replaced
with special WebRender display items.

Fixes #13529.
Fixed #13298.
This commit is contained in:
Martin Robinson 2016-10-31 20:24:27 +01:00
parent f7875dad1a
commit ef82d772c1
15 changed files with 436 additions and 163 deletions

View file

@ -81,7 +81,15 @@ impl DisplayList {
scroll_offsets, scroll_offsets,
result); 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) { if let Some(meta) = item.hit_test(*translated_point) {
result.push(meta); 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<Au>,
client_point: &Point2D<Au>,
scroll_offsets: &ScrollOffsetMap,
result: &mut Vec<DisplayItemMetadata>) {
// 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, fn hit_test_stacking_context<'a>(&self,
traversal: &mut DisplayListTraversal<'a>, traversal: &mut DisplayListTraversal<'a>,
stacking_context: &StackingContext, 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. // 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); debug_assert!(stacking_context.context_type == StackingContextType::Real);
let is_fixed = stacking_context.scroll_policy == ScrollPolicy::FixedPosition; let is_fixed = stacking_context.scroll_policy == ScrollPolicy::FixedPosition;
let mut translated_point = if is_fixed { let translated_point = if is_fixed {
*client_point *client_point
} else { } else {
let point = *translated_point - stacking_context.bounds.origin; 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)) 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); 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) { pub fn print_with_tree(&self, print_tree: &mut PrintTree) {
print_tree.new_level("Items".to_owned()); print_tree.new_level("Items".to_owned());
for item in &self.list { for item in &self.list {
print_tree.add_item(format!("{:?} StackingContext: {:?}", print_tree.add_item(format!("{:?} StackingContext: {:?} ScrollRoot: {:?}",
item, item,
item.base().stacking_context_id)); item.base().stacking_context_id,
item.scroll_root_id()));
} }
print_tree.end_level(); print_tree.end_level();
} }
@ -250,6 +264,7 @@ pub enum StackingContextType {
Real, Real,
PseudoPositioned, PseudoPositioned,
PseudoFloat, PseudoFloat,
PseudoScrollingArea,
} }
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)] #[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
@ -291,8 +306,8 @@ pub struct StackingContext {
/// Children of this StackingContext. /// Children of this StackingContext.
pub children: Vec<StackingContext>, pub children: Vec<StackingContext>,
/// If this StackingContext scrolls its overflow area, this will contain the id. /// The id of the parent scrolling area that contains this StackingContext.
pub overflow_scroll_id: Option<ScrollRootId>, pub parent_scroll_id: ScrollRootId,
} }
impl StackingContext { impl StackingContext {
@ -309,7 +324,7 @@ impl StackingContext {
perspective: Matrix4D<f32>, perspective: Matrix4D<f32>,
establishes_3d_context: bool, establishes_3d_context: bool,
scroll_policy: ScrollPolicy, scroll_policy: ScrollPolicy,
scroll_root_id: Option<ScrollRootId>) parent_scroll_id: ScrollRootId)
-> StackingContext { -> StackingContext {
StackingContext { StackingContext {
id: id, id: id,
@ -324,7 +339,7 @@ impl StackingContext {
establishes_3d_context: establishes_3d_context, establishes_3d_context: establishes_3d_context,
scroll_policy: scroll_policy, scroll_policy: scroll_policy,
children: Vec::new(), children: Vec::new(),
overflow_scroll_id: scroll_root_id, parent_scroll_id: parent_scroll_id,
} }
} }
@ -341,7 +356,7 @@ impl StackingContext {
Matrix4D::identity(), Matrix4D::identity(),
true, true,
ScrollPolicy::Scrollable, ScrollPolicy::Scrollable,
None) ScrollRootId::root())
} }
pub fn add_child(&mut self, mut child: StackingContext) { pub fn add_child(&mut self, mut child: StackingContext) {
@ -453,21 +468,40 @@ impl fmt::Debug for StackingContext {
"Pseudo-StackingContext" "Pseudo-StackingContext"
}; };
let scrollable_string = if self.overflow_scroll_id.is_some() { write!(f, "{} at {:?} with overflow {:?}: {:?}",
" (scrolls overflow area)"
} else {
""
};
write!(f, "{}{} at {:?} with overflow {:?}: {:?}",
type_string, type_string,
scrollable_string,
self.bounds, self.bounds,
self.overflow, self.overflow,
self.id) 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<Au>,
/// The size of the contents that can be scrolled inside of the scroll root.
pub size: Size2D<Au>,
}
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. /// One drawing command in the list.
#[derive(Clone, Deserialize, HeapSizeOf, Serialize)] #[derive(Clone, Deserialize, HeapSizeOf, Serialize)]
pub enum DisplayItem { pub enum DisplayItem {
@ -482,6 +516,8 @@ pub enum DisplayItem {
Iframe(Box<IframeDisplayItem>), Iframe(Box<IframeDisplayItem>),
PushStackingContext(Box<PushStackingContextItem>), PushStackingContext(Box<PushStackingContextItem>),
PopStackingContext(Box<PopStackingContextItem>), PopStackingContext(Box<PopStackingContextItem>),
PushScrollRoot(Box<PushScrollRootItem>),
PopScrollRoot(Box<BaseDisplayItem>),
} }
/// Information common to all display items. /// Information common to all display items.
@ -501,6 +537,9 @@ pub struct BaseDisplayItem {
/// The id of the stacking context this item belongs to. /// The id of the stacking context this item belongs to.
pub stacking_context_id: StackingContextId, pub stacking_context_id: StackingContextId,
/// The id of the scroll root this item belongs to.
pub scroll_root_id: ScrollRootId,
} }
impl BaseDisplayItem { impl BaseDisplayItem {
@ -509,7 +548,8 @@ impl BaseDisplayItem {
metadata: DisplayItemMetadata, metadata: DisplayItemMetadata,
clip: &ClippingRegion, clip: &ClippingRegion,
section: DisplayListSection, section: DisplayListSection,
stacking_context_id: StackingContextId) stacking_context_id: StackingContextId,
scroll_root_id: ScrollRootId)
-> BaseDisplayItem { -> BaseDisplayItem {
// Detect useless clipping regions here and optimize them to `ClippingRegion::max()`. // 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 // The painting backend may want to optimize out clipping regions and this makes it easier
@ -524,6 +564,7 @@ impl BaseDisplayItem {
}, },
section: section, section: section,
stacking_context_id: stacking_context_id, stacking_context_id: stacking_context_id,
scroll_root_id: scroll_root_id,
} }
} }
@ -538,6 +579,7 @@ impl BaseDisplayItem {
clip: ClippingRegion::max(), clip: ClippingRegion::max(),
section: DisplayListSection::Content, section: DisplayListSection::Content,
stacking_context_id: StackingContextId::root(), stacking_context_id: StackingContextId::root(),
scroll_root_id: ScrollRootId::root(),
} }
} }
} }
@ -981,6 +1023,15 @@ pub struct PopStackingContextItem {
pub stacking_context_id: StackingContextId, 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. /// How a box shadow should be clipped.
#[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf, Deserialize, Serialize)]
@ -1009,9 +1060,15 @@ impl DisplayItem {
DisplayItem::Iframe(ref iframe) => &iframe.base, DisplayItem::Iframe(ref iframe) => &iframe.base,
DisplayItem::PushStackingContext(ref stacking_context) => &stacking_context.base, DisplayItem::PushStackingContext(ref stacking_context) => &stacking_context.base,
DisplayItem::PopStackingContext(ref item) => &item.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 { pub fn stacking_context_id(&self) -> StackingContextId {
self.base().stacking_context_id self.base().stacking_context_id
} }
@ -1090,6 +1147,14 @@ impl fmt::Debug for DisplayItem {
return write!(f, "PopStackingContext({:?}", item.stacking_context_id); 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, "{} @ {:?} {:?}", write!(f, "{} @ {:?} {:?}",
match *self { match *self {
DisplayItem::SolidColor(ref solid_color) => DisplayItem::SolidColor(ref solid_color) =>
@ -1106,8 +1171,10 @@ impl fmt::Debug for DisplayItem {
DisplayItem::Line(_) => "Line".to_owned(), DisplayItem::Line(_) => "Line".to_owned(),
DisplayItem::BoxShadow(_) => "BoxShadow".to_owned(), DisplayItem::BoxShadow(_) => "BoxShadow".to_owned(),
DisplayItem::Iframe(_) => "Iframe".to_owned(), DisplayItem::Iframe(_) => "Iframe".to_owned(),
DisplayItem::PushStackingContext(_) => "".to_owned(), DisplayItem::PushStackingContext(_) |
DisplayItem::PopStackingContext(_) => "".to_owned(), DisplayItem::PopStackingContext(_) |
DisplayItem::PushScrollRoot(_) |
DisplayItem::PopScrollRoot(_) => "".to_owned(),
}, },
self.bounds(), self.bounds(),
self.base().clip self.base().clip

View file

@ -211,6 +211,11 @@ impl ScrollRootId {
pub fn fragment_type(&self) -> FragmentType { pub fn fragment_type(&self) -> FragmentType {
FragmentType::from_usize(self.0 & 3) 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. /// The type of fragment that a stacking context represents.

View file

@ -1942,17 +1942,6 @@ impl Flow for BlockFlow {
} }
if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { 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, let position_start = self.base.position.start.to_physical(self.base.writing_mode,
container_size); container_size);

View file

@ -15,6 +15,7 @@ use block::{BlockFlow, BlockStackingContextType};
use canvas_traits::{CanvasData, CanvasMsg, FromLayoutMsg}; use canvas_traits::{CanvasData, CanvasMsg, FromLayoutMsg};
use context::SharedLayoutContext; use context::SharedLayoutContext;
use euclid::{Matrix4D, Point2D, Radians, Rect, SideOffsets2D, Size2D}; use euclid::{Matrix4D, Point2D, Radians, Rect, SideOffsets2D, Size2D};
use euclid::point::TypedPoint2D;
use flex::FlexFlow; use flex::FlexFlow;
use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED}; use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED};
use flow_ref::FlowRef; 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::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection};
use gfx::display_list::{GradientDisplayItem, IframeDisplayItem, ImageDisplayItem}; use gfx::display_list::{GradientDisplayItem, IframeDisplayItem, ImageDisplayItem};
use gfx::display_list::{LineDisplayItem, OpaqueNode}; 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::display_list::{TextDisplayItem, TextOrientation, WebGLDisplayItem, WebRenderImageInfo};
use gfx_traits::{ScrollPolicy, ScrollRootId, StackingContextId}; use gfx_traits::{ScrollPolicy, ScrollRootId, StackingContextId};
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT};
@ -43,8 +44,8 @@ use std::mem;
use std::sync::Arc; use std::sync::Arc;
use style::computed_values::{background_attachment, background_clip, background_origin}; use style::computed_values::{background_attachment, background_clip, background_origin};
use style::computed_values::{background_repeat, background_size, border_style}; 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::{cursor, image_rendering, overflow_x};
use style::computed_values::{transform, transform_style, visibility}; 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::_servo_overflow_clip_box as overflow_clip_box;
use style::computed_values::filter::Filter; use style::computed_values::filter::Filter;
use style::computed_values::text_shadow::TextShadow; use style::computed_values::text_shadow::TextShadow;
@ -95,6 +96,7 @@ pub struct DisplayListBuildState<'a> {
pub shared_layout_context: &'a SharedLayoutContext, pub shared_layout_context: &'a SharedLayoutContext,
pub root_stacking_context: StackingContext, pub root_stacking_context: StackingContext,
pub items: HashMap<StackingContextId, Vec<DisplayItem>>, pub items: HashMap<StackingContextId, Vec<DisplayItem>>,
pub scroll_roots: HashMap<ScrollRootId, ScrollRoot>,
pub stacking_context_id_stack: Vec<StackingContextId>, pub stacking_context_id_stack: Vec<StackingContextId>,
pub scroll_root_id_stack: Vec<ScrollRootId>, pub scroll_root_id_stack: Vec<ScrollRootId>,
} }
@ -107,6 +109,7 @@ impl<'a> DisplayListBuildState<'a> {
shared_layout_context: shared_layout_context, shared_layout_context: shared_layout_context,
root_stacking_context: StackingContext::root(), root_stacking_context: StackingContext::root(),
items: HashMap::new(), items: HashMap::new(),
scroll_roots: HashMap::new(),
stacking_context_id_stack: vec!(stacking_context_id), stacking_context_id_stack: vec!(stacking_context_id),
scroll_root_id_stack: vec!(ScrollRootId::root()), scroll_root_id_stack: vec!(ScrollRootId::root()),
} }
@ -117,6 +120,11 @@ impl<'a> DisplayListBuildState<'a> {
items.push(display_item); 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 { pub fn stacking_context_id(&self) -> StackingContextId {
self.stacking_context_id_stack.last().unwrap().clone() self.stacking_context_id_stack.last().unwrap().clone()
} }
@ -126,14 +134,19 @@ impl<'a> DisplayListBuildState<'a> {
} }
pub fn pop_stacking_context_id(&mut self) { pub fn pop_stacking_context_id(&mut self) {
self.stacking_context_id_stack.pop();
assert!(!self.stacking_context_id_stack.is_empty()); 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() 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) { pub fn push_scroll_root_id(&mut self, id: ScrollRootId) {
self.scroll_root_id_stack.push(id); self.scroll_root_id_stack.push(id);
} }
@ -157,14 +170,18 @@ impl<'a> DisplayListBuildState<'a> {
}, },
&clip, &clip,
section, section,
self.stacking_context_id()) self.stacking_context_id(),
self.scroll_root_id())
} }
pub fn to_display_list(mut self) -> DisplayList { 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 mut list = Vec::new();
let root_context = mem::replace(&mut self.root_stacking_context, StackingContext::root()); 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 { DisplayList {
list: list, list: list,
@ -173,7 +190,8 @@ impl<'a> DisplayListBuildState<'a> {
fn to_display_list_for_stacking_context(&mut self, fn to_display_list_for_stacking_context(&mut self,
list: &mut Vec<DisplayItem>, 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()); 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.sort_by(|a, b| a.base().section.cmp(&b.base().section));
child_items.reverse(); child_items.reverse();
@ -186,63 +204,114 @@ impl<'a> DisplayListBuildState<'a> {
if !real_stacking_context { if !real_stacking_context {
self.to_display_list_for_items(list, self.to_display_list_for_items(list,
child_items, child_items,
child_stacking_contexts); child_stacking_contexts,
scroll_root_stack);
return; 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(); let (push_item, pop_item) = stacking_context.to_display_list_items();
list.push(push_item); list.push(push_item);
self.to_display_list_for_items(list, self.to_display_list_for_items(list,
child_items, child_items,
child_stacking_contexts); child_stacking_contexts,
&mut scroll_root_stack);
list.push(pop_item); list.push(pop_item);
} }
fn to_display_list_for_items(&mut self, fn to_display_list_for_items(&mut self,
list: &mut Vec<DisplayItem>, list: &mut Vec<DisplayItem>,
mut child_items: 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 // Properly order display items that make up a stacking context. "Steps" here
// refer to the steps in CSS 2.1 Appendix E. // refer to the steps in CSS 2.1 Appendix E.
// Steps 1 and 2: Borders and background for the root. // Steps 1 and 2: Borders and background for the root.
while child_items.last().map_or(false, while child_items.last().map_or(false,
|child| child.section() == DisplayListSection::BackgroundAndBorders) { |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. // Step 3: Positioned descendants with negative z-indices.
let mut child_stacking_contexts = child_stacking_contexts.into_iter().peekable(); let mut child_stacking_contexts = child_stacking_contexts.into_iter().peekable();
while child_stacking_contexts.peek().map_or(false, |child| child.z_index < 0) { while child_stacking_contexts.peek().map_or(false, |child| child.z_index < 0) {
let context = child_stacking_contexts.next().unwrap(); 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. // Step 4: Block backgrounds and borders.
while child_items.last().map_or(false, while child_items.last().map_or(false,
|child| child.section() == DisplayListSection::BlockBackgroundsAndBorders) { |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. // Step 5: Floats.
while child_stacking_contexts.peek().map_or(false, while child_stacking_contexts.peek().map_or(false,
|child| child.context_type == StackingContextType::PseudoFloat) { |child| child.context_type == StackingContextType::PseudoFloat) {
let context = child_stacking_contexts.next().unwrap(); 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. // Step 6 & 7: Content and inlines that generate stacking contexts.
while child_items.last().map_or(false, while child_items.last().map_or(false,
|child| child.section() == DisplayListSection::Content) { |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. // Step 8 & 9: Positioned descendants with nonnegative, numeric z-indices.
for child in child_stacking_contexts { 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. // 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, base_flow: &BaseFlow,
scroll_policy: ScrollPolicy, scroll_policy: ScrollPolicy,
mode: StackingContextCreationMode, mode: StackingContextCreationMode,
scroll_root_id: Option<ScrollRootId>) parent_scroll_id: ScrollRootId)
-> StackingContext; -> StackingContext;
/// Returns the 4D matrix representing this fragment's transform. /// Returns the 4D matrix representing this fragment's transform.
@ -1476,9 +1545,8 @@ impl FragmentDisplayListBuilding for Fragment {
base_flow: &BaseFlow, base_flow: &BaseFlow,
scroll_policy: ScrollPolicy, scroll_policy: ScrollPolicy,
mode: StackingContextCreationMode, mode: StackingContextCreationMode,
scroll_root_id: Option<ScrollRootId>) parent_scroll_id: ScrollRootId)
-> StackingContext { -> StackingContext {
let scrolls_overflow_area = mode == StackingContextCreationMode::ScrollWrapper;
let border_box = let border_box =
self.stacking_relative_border_box(&base_flow.stacking_relative_position, self.stacking_relative_border_box(&base_flow.stacking_relative_position,
&base_flow.early_absolute_position_info &base_flow.early_absolute_position_info
@ -1486,16 +1554,12 @@ impl FragmentDisplayListBuilding for Fragment {
base_flow.early_absolute_position_info base_flow.early_absolute_position_info
.relative_containing_block_mode, .relative_containing_block_mode,
CoordinateSystem::Parent); CoordinateSystem::Parent);
let overflow = if scrolls_overflow_area { // First, compute the offset of our border box (including relative positioning)
Rect::new(Point2D::zero(), base_flow.overflow.scroll.size) // from our flow origin, since that is what `BaseFlow::overflow` is relative to.
} else { let border_box_offset =
// First, compute the offset of our border box (including relative positioning) border_box.translate(&-base_flow.stacking_relative_position).origin;
// from our flow origin, since that is what `BaseFlow::overflow` is relative to. // Then, using that, compute our overflow region relative to our border box.
let border_box_offset = let overflow = base_flow.overflow.paint.translate(&-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)
};
let transform = self.transform_matrix(&border_box); let transform = self.transform_matrix(&border_box);
let perspective = match self.style().get_effects().perspective { 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 transform_style = self.style().get_used_transform_style();
let establishes_3d_context = scrolls_overflow_area || let establishes_3d_context = transform_style == transform_style::T::flat;
transform_style == transform_style::T::flat;
let context_type = match mode { let context_type = match mode {
StackingContextCreationMode::PseudoFloat => StackingContextType::PseudoFloat, StackingContextCreationMode::PseudoFloat => StackingContextType::PseudoFloat,
@ -1551,7 +1614,7 @@ impl FragmentDisplayListBuilding for Fragment {
perspective, perspective,
establishes_3d_context, establishes_3d_context,
scroll_policy, scroll_policy,
scroll_root_id) parent_scroll_id)
} }
fn adjust_clipping_region_for_children(&self, fn adjust_clipping_region_for_children(&self,
@ -1829,18 +1892,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
fn collect_stacking_contexts_for_block(&mut self, fn collect_stacking_contexts_for_block(&mut self,
parent: &mut StackingContext, parent: &mut StackingContext,
parent_scroll_root_id: ScrollRootId) { parent_scroll_root_id: ScrollRootId) {
let block_stacking_context_type = self.block_stacking_context_type(); let scroll_root_id = if self.has_scrolling_overflow() {
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 {
ScrollRootId::new_of_type(self.fragment.node.id() as usize, ScrollRootId::new_of_type(self.fragment.node.id() as usize,
self.fragment.fragment_type()) self.fragment.fragment_type())
} else { } else {
@ -1848,7 +1900,15 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}; };
self.base.scroll_root_id = scroll_root_id; 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; self.base.stacking_context_id = stacking_context_id;
if block_stacking_context_type == BlockStackingContextType::PseudoStackingContext { if block_stacking_context_type == BlockStackingContextType::PseudoStackingContext {
@ -1864,7 +1924,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
&self.base, &self.base,
ScrollPolicy::Scrollable, ScrollPolicy::Scrollable,
creation_mode, creation_mode,
None); parent_scroll_root_id);
self.base.collect_stacking_contexts_for_children(&mut new_context, 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(); let new_children: Vec<StackingContext> = new_context.children.drain(..).collect();
@ -1888,20 +1948,13 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
ScrollPolicy::Scrollable 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( let mut stacking_context = self.fragment.create_stacking_context(
stacking_context_id, stacking_context_id,
&self.base, &self.base,
scroll_policy, scroll_policy,
creation_mode, StackingContextCreationMode::Normal,
internal_id); parent_scroll_root_id);
self.base.collect_stacking_contexts_for_children(&mut stacking_context, scroll_root_id); self.base.collect_stacking_contexts_for_children(&mut stacking_context, scroll_root_id);
parent.add_child(stacking_context); parent.add_child(stacking_context);
} }
@ -1921,6 +1974,28 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
DisplayListSection::BlockBackgroundsAndBorders 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. // Add the box that starts the block context.
self.fragment self.fragment
.build_display_list(state, .build_display_list(state,
@ -2025,7 +2100,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
&self.base, &self.base,
ScrollPolicy::Scrollable, ScrollPolicy::Scrollable,
StackingContextCreationMode::Normal, StackingContextCreationMode::Normal,
None)); parent_scroll_root_id));
} }
_ => fragment.stacking_context_id = parent.id, _ => fragment.stacking_context_id = parent.id,
} }
@ -2229,7 +2304,6 @@ pub enum BorderPaintingMode<'a> {
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum StackingContextCreationMode { pub enum StackingContextCreationMode {
Normal, Normal,
ScrollWrapper,
PseudoPositioned, PseudoPositioned,
PseudoFloat, PseudoFloat,
} }

View file

@ -2594,8 +2594,6 @@ impl Fragment {
transform_style::T::auto => {} 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, match (self.style().get_box().position,
self.style().get_position().z_index, self.style().get_position().z_index,
self.style().get_box().overflow_x, self.style().get_box().overflow_x,
@ -2614,11 +2612,7 @@ impl Fragment {
overflow_x::T::visible) => false, overflow_x::T::visible) => false,
(position::T::absolute, _, _, _) | (position::T::absolute, _, _, _) |
(position::T::fixed, _, _, _) | (position::T::fixed, _, _, _) |
(position::T::relative, _, _, _) | (position::T::relative, _, _, _) => true,
(_, _, overflow_x::T::auto, _) |
(_, _, overflow_x::T::scroll, _) |
(_, _, _, overflow_x::T::auto) |
(_, _, _, overflow_x::T::scroll) => true,
(position::T::static_, _, _, _) => false (position::T::static_, _, _, _) => false
} }
} }

View file

@ -270,34 +270,34 @@ pub struct BuildDisplayList<'a> {
impl<'a> BuildDisplayList<'a> { impl<'a> BuildDisplayList<'a> {
#[inline] #[inline]
pub fn traverse(&mut self, flow: &mut Flow) { 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() { 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.build_display_list(&mut self.state);
flow::mut_base(flow).restyle_damage.remove(REPAINT); 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) { for kid in flow::child_iter_mut(flow) {
self.traverse(kid); self.traverse(kid);
} }
if new_stacking_context {
self.state.pop_stacking_context_id();
}
if new_scroll_root {
self.state.pop_scroll_root_id();
}
} }
#[inline] #[inline]

View file

@ -8,7 +8,7 @@
// completely converting layout to directly generate WebRender display lists, for example. // completely converting layout to directly generate WebRender display lists, for example.
use app_units::Au; 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::{BorderRadii, BoxShadowClipMode, ClippingRegion};
use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal}; use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal};
use gfx::display_list::{StackingContext, StackingContextType}; use gfx::display_list::{StackingContext, StackingContextType};
@ -24,7 +24,8 @@ trait WebRenderStackingContextConverter {
api: &mut webrender_traits::RenderApi, api: &mut webrender_traits::RenderApi,
pipeline_id: webrender_traits::PipelineId, pipeline_id: webrender_traits::PipelineId,
epoch: webrender_traits::Epoch, epoch: webrender_traits::Epoch,
frame_builder: &mut WebRenderFrameBuilder) frame_builder: &mut WebRenderFrameBuilder,
scroll_layer_id: Option<webrender_traits::ScrollLayerId>)
-> webrender_traits::StackingContextId; -> webrender_traits::StackingContextId;
fn convert_children_to_webrender<'a>(&self, fn convert_children_to_webrender<'a>(&self,
@ -235,17 +236,39 @@ impl WebRenderStackingContextConverter for StackingContext {
&DisplayItem::PushStackingContext(ref stacking_context_item) => { &DisplayItem::PushStackingContext(ref stacking_context_item) => {
let stacking_context = &stacking_context_item.stacking_context; let stacking_context = &stacking_context_item.stacking_context;
debug_assert!(stacking_context.context_type == StackingContextType::Real); debug_assert!(stacking_context.context_type == StackingContextType::Real);
builder.push_stacking_context(
let stacking_context_id =
stacking_context.convert_to_webrender(traversal, stacking_context.convert_to_webrender(traversal,
api, api,
pipeline_id, pipeline_id,
epoch, epoch,
frame_builder); frame_builder,
builder.push_stacking_context(stacking_context_id); 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), _ => item.convert_to_webrender(builder, frame_builder),
} }
} }
@ -256,21 +279,14 @@ impl WebRenderStackingContextConverter for StackingContext {
api: &mut webrender_traits::RenderApi, api: &mut webrender_traits::RenderApi,
pipeline_id: webrender_traits::PipelineId, pipeline_id: webrender_traits::PipelineId,
epoch: webrender_traits::Epoch, epoch: webrender_traits::Epoch,
frame_builder: &mut WebRenderFrameBuilder) frame_builder: &mut WebRenderFrameBuilder,
scroll_layer_id: Option<webrender_traits::ScrollLayerId>)
-> webrender_traits::StackingContextId { -> webrender_traits::StackingContextId {
let webrender_scroll_policy = match self.scroll_policy { let webrender_scroll_policy = match self.scroll_policy {
ScrollPolicy::Scrollable => webrender_traits::ScrollPolicy::Scrollable, ScrollPolicy::Scrollable => webrender_traits::ScrollPolicy::Scrollable,
ScrollPolicy::FixedPosition => webrender_traits::ScrollPolicy::Fixed, 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 = let mut sc =
webrender_traits::StackingContext::new(scroll_layer_id, webrender_traits::StackingContext::new(scroll_layer_id,
webrender_scroll_policy, webrender_scroll_policy,
@ -310,11 +326,14 @@ impl WebRenderDisplayListConverter for DisplayList {
match item { match item {
Some(&DisplayItem::PushStackingContext(ref stacking_context_item)) => { Some(&DisplayItem::PushStackingContext(ref stacking_context_item)) => {
let stacking_context = &stacking_context_item.stacking_context; 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, stacking_context.convert_to_webrender(&mut traversal,
api, api,
pipeline_id, pipeline_id,
epoch, epoch,
frame_builder) frame_builder,
scroll_layer_id)
} }
_ => unreachable!("DisplayList did not start with StackingContext."), _ => unreachable!("DisplayList did not start with StackingContext."),
@ -453,7 +472,11 @@ impl WebRenderDisplayItemConverter for DisplayItem {
item.base.clip.to_clip_region(frame_builder), item.base.clip.to_clip_region(frame_builder),
pipeline_id); 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."),
} }
} }
} }

View file

@ -50,7 +50,8 @@ use euclid::rect::Rect;
use euclid::scale_factor::ScaleFactor; use euclid::scale_factor::ScaleFactor;
use euclid::size::Size2D; use euclid::size::Size2D;
use fnv::FnvHasher; 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;
use gfx::font_cache_thread::FontCacheThread; use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context; use gfx::font_context;
@ -906,8 +907,8 @@ impl LayoutThread {
match (data.goal, display_list_needed) { match (data.goal, display_list_needed) {
(ReflowGoal::ForDisplay, _) | (ReflowGoal::ForScriptQuery, true) => { (ReflowGoal::ForDisplay, _) | (ReflowGoal::ForScriptQuery, true) => {
let mut build_state = let mut build_state =
sequential::build_display_list_for_subtree(layout_root, sequential::build_display_list_for_subtree(layout_root,
shared_layout_context); shared_layout_context);
debug!("Done building display list."); debug!("Done building display list.");

View file

@ -1,4 +0,0 @@
[abspos-overflow-010.htm]
type: reftest
expected:
if os == "linux": FAIL

View file

@ -1,3 +0,0 @@
[abspos-overflow-011.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[max-width-106.htm]
type: reftest
expected: FAIL

View file

@ -5016,6 +5016,30 @@
"url": "/_mozilla/css/stacking_context_rtl.html" "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": [ "css/style_is_in_doc.html": [
{ {
"path": "css/style_is_in_doc.html", "path": "css/style_is_in_doc.html",
@ -19854,6 +19878,30 @@
"url": "/_mozilla/css/stacking_context_rtl.html" "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": [ "css/style_is_in_doc.html": [
{ {
"path": "css/style_is_in_doc.html", "path": "css/style_is_in_doc.html",

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The position:absolute element should be on top of the overflow:auto element</title>
<link rel='match' href='stacking_order_ref.html'>
<style>
body {
margin: 0;
}
.scrollbox {
margin: 50px;
width: 50px;
height: 50px;
overflow: auto;
background: red;
}
.overlay {
position: absolute;
left: 50px;
top: 50px;
width: 50px;
height: 50px;
background: green;
}
</style>
<div class="overlay"></div>
<div class="scrollbox"></div>

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The position:absolute element should be on top of the overflow:scroll element</title>
<link rel='match' href='stacking_order_ref.html'>
<style>
body {
margin: 0;
}
.scrollbox {
margin: 50px;
width: 50px;
height: 50px;
overflow: scroll;
background: red;
}
.overlay {
position: absolute;
left: 50px;
top: 50px;
width: 50px;
height: 50px;
background: green;
}
</style>
<div class="overlay"></div>
<div class="scrollbox"></div>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
body {
margin: 0;
}
.box {
position: absolute;
left: 50px;
top: 50px;
width: 50px;
height: 50px;
background: green;
}
</style>
<div class="box"></div>