diff options
16 files changed, 483 insertions, 343 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs index 5504f0fc412..9e28cf2095d 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -29,9 +29,9 @@ use app_units::{Au, MAX_AU}; use context::LayoutContext; -use display_list_builder::{BorderPaintingMode, DisplayListBuildState, FragmentDisplayListBuilding}; +use display_list_builder::{BorderPaintingMode, DisplayListBuildState}; use display_list_builder::BlockFlowDisplayListBuilding; -use euclid::{Matrix4D, Point2D, Rect, Size2D}; +use euclid::{Point2D, Size2D}; use floats::{ClearType, FloatKind, Floats, PlacementInfo}; use flow::{self, BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag}; use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT}; @@ -42,13 +42,13 @@ use flow::IS_ABSOLUTELY_POSITIONED; use flow_list::FlowList; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow}; use fragment::{IS_INLINE_FLEX_ITEM, IS_BLOCK_FLEX_ITEM}; -use gfx::display_list::ClippingRegion; use gfx_traits::print_tree::PrintTree; use layout_debug; use model::{AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto}; use model::{specified, specified_or_none}; use sequential; use serde::{Serialize, Serializer}; +use servo_geometry::max_rect; use std::cmp::{max, min}; use std::fmt; use std::sync::Arc; @@ -1678,11 +1678,12 @@ impl BlockFlow { } } - pub fn style_permits_scrolling_overflow(&self) -> bool { + pub fn overflow_style_may_require_scroll_root(&self) -> bool { 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) => true, + (overflow_x::T::auto, _) | (overflow_x::T::scroll, _) | (overflow_x::T::hidden, _) | + (_, overflow_x::T::auto) | (_, overflow_x::T::scroll) | (_, overflow_x::T::hidden) => + true, (_, _) => false, } } @@ -1776,65 +1777,6 @@ impl BlockFlow { self.fragment.flags.contains(IS_BLOCK_FLEX_ITEM) } - /// Changes this block's clipping region from its parent's coordinate system to its own - /// coordinate system if necessary (i.e. if this block is a stacking context). - /// - /// The clipping region is initially in each block's parent's coordinate system because the - /// parent of each block does not have enough information to determine what the child's - /// coordinate system is on its own. Specifically, if the child is absolutely positioned, the - /// parent does not know where the child's absolute position is at the time it assigns clipping - /// regions, because flows compute their own absolute positions. - fn switch_coordinate_system_if_necessary(&mut self) { - // Avoid overflows! - if self.base.clip.is_max() { - return - } - - if !self.fragment.establishes_stacking_context() { - return - } - - let stacking_relative_border_box = - 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); - self.base.clip = self.base.clip.translate(&-stacking_relative_border_box.origin); - - // Account for `transform`, if applicable. - if self.fragment.style.get_box().transform.0.is_none() { - return - } - let transform = match self.fragment - .transform_matrix(&stacking_relative_border_box) - .unwrap_or(Matrix4D::identity()) - .inverse() { - Some(transform) => transform, - None => { - // Singular matrix. Ignore it. - return - } - }; - - // FIXME(pcwalton): This is inaccurate: not all transforms are 2D, and not all clips are - // axis-aligned. - let bounding_rect = self.base.clip.bounding_rect(); - let bounding_rect = Rect::new(Point2D::new(bounding_rect.origin.x.to_f32_px(), - bounding_rect.origin.y.to_f32_px()), - Size2D::new(bounding_rect.size.width.to_f32_px(), - bounding_rect.size.height.to_f32_px())); - let clip_rect = transform.to_2d().transform_rect(&bounding_rect); - let clip_rect = Rect::new(Point2D::new(Au::from_f32_px(clip_rect.origin.x), - Au::from_f32_px(clip_rect.origin.y)), - Size2D::new(Au::from_f32_px(clip_rect.size.width), - Au::from_f32_px(clip_rect.size.height))); - self.base.clip = ClippingRegion::from_rect(&clip_rect) - } - pub fn mark_scrolling_overflow(&mut self, has_scrolling_overflow: bool) { if has_scrolling_overflow { self.flags.insert(HAS_SCROLLING_OVERFLOW); @@ -2006,7 +1948,7 @@ impl Flow for BlockFlow { let container_size = Size2D::new(self.base.block_container_inline_size, Au(0)); if self.is_root() { - self.base.clip = ClippingRegion::max(); + self.base.clip = max_rect(); } if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { @@ -2107,28 +2049,6 @@ impl Flow for BlockFlow { self.base.stacking_relative_position + relative_offset }; - let stacking_relative_border_box = - 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::Own); - - // Our parent set our `clip` field to the clipping region in its coordinate system. Change - // it to our coordinate system. - self.switch_coordinate_system_if_necessary(); - self.fragment.adjust_clip_for_style(&mut self.base.clip, &stacking_relative_border_box); - - // Compute the clipping region for children, taking our `overflow` properties and so forth - // into account. - let mut clip_for_children = self.base.clip.clone(); - self.fragment.adjust_clipping_region_for_children(&mut clip_for_children, - &stacking_relative_border_box); - // Process children. for kid in self.base.child_iter_mut() { if flow::base(kid).flags.contains(INLINE_POSITION_IS_STATIC) || @@ -2161,16 +2081,6 @@ impl Flow for BlockFlow { flow::mut_base(kid).late_absolute_position_info = late_absolute_position_info_for_children; - - // This clipping region is in our coordinate system. The child will fix it up to be in - // its own coordinate system by itself if necessary. - // - // Rationale: If the child is absolutely positioned, it hasn't been positioned at this - // point (as absolutely-positioned flows position themselves in - // `compute_absolute_position()`). Therefore, we don't always know what the child's - // coordinate system is here. So we store the clipping region in our coordinate system - // for now; the child will move it later if needed. - flow::mut_base(kid).clip = clip_for_children.clone() } self.base.restyle_damage.remove(REPOSITION) diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 9a4fb5bd9ca..8eae8909a31 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -14,7 +14,7 @@ use app_units::{AU_PER_PX, Au}; use block::{BlockFlow, BlockStackingContextType}; use canvas_traits::{CanvasData, CanvasMsg, FromLayoutMsg}; use context::LayoutContext; -use euclid::{Point2D, Rect, SideOffsets2D, Size2D, TypedSize2D}; +use euclid::{Matrix4D, Point2D, Rect, SideOffsets2D, Size2D, TypedSize2D}; use flex::FlexFlow; use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED}; use flow_ref::FlowRef; @@ -39,6 +39,7 @@ use net_traits::image::base::PixelFormat; use net_traits::image_cache::UsePlaceholder; use range::Range; use servo_config::opts; +use servo_geometry::max_rect; use servo_url::ServoUrl; use std::{cmp, f32}; use std::collections::HashMap; @@ -49,7 +50,6 @@ use style::computed_values::{background_attachment, background_clip, background_ use style::computed_values::{background_repeat, background_size, border_style}; use style::computed_values::{cursor, image_rendering, overflow_x}; use style::computed_values::{pointer_events, position, 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; use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode}; @@ -57,7 +57,7 @@ use style::properties::{self, ServoComputedValues}; use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword; use style::properties::style_structs; use style::servo::restyle_damage::REPAINT; -use style::values::{RGBA, computed}; +use style::values::{Either, RGBA, computed}; use style::values::computed::{AngleOrCorner, Gradient, GradientKind, LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::computed::NumberOrPercentage; use style::values::specified::{HorizontalDirection, VerticalDirection}; @@ -172,6 +172,14 @@ pub struct DisplayListBuildState<'a> { /// Vector containing iframe sizes, used to inform the constellation about /// new iframe sizes pub iframe_sizes: Vec<(PipelineId, TypedSize2D<f32, CSSPixel>)>, + + /// A stack of clips used to cull display list entries that are outside the + /// rendered region. + pub clip_stack: Vec<Rect<Au>>, + + /// A stack of clips used to cull display list entries that are outside the + /// rendered region, but only collected at containing block boundaries. + pub containing_block_clip_stack: Vec<Rect<Au>>, } impl<'a> DisplayListBuildState<'a> { @@ -187,6 +195,8 @@ impl<'a> DisplayListBuildState<'a> { current_scroll_root_id: ScrollRootId::root(), containing_block_scroll_root_id: ScrollRootId::root(), iframe_sizes: Vec::new(), + clip_stack: Vec::new(), + containing_block_clip_stack: Vec::new(), } } @@ -204,6 +214,10 @@ impl<'a> DisplayListBuildState<'a> { info.children.push(stacking_context); } + fn has_scroll_root(&mut self, id: ScrollRootId) -> bool { + self.scroll_root_parents.contains_key(&id) + } + fn add_scroll_root(&mut self, scroll_root: ScrollRoot, stacking_context_id: StackingContextId) { self.scroll_root_parents.insert(scroll_root.id, scroll_root.parent_id); let info = self.stacking_context_info @@ -241,7 +255,7 @@ impl<'a> DisplayListBuildState<'a> { node: node, pointing: cursor, }, - &clip, + clip, section, self.current_stacking_context_id, scroll_root_id) @@ -342,8 +356,7 @@ pub trait FragmentDisplayListBuilding { state: &mut DisplayListBuildState, style: &ServoComputedValues, display_list_section: DisplayListSection, - absolute_bounds: &Rect<Au>, - clip: &ClippingRegion); + absolute_bounds: &Rect<Au>); /// Computes the background size for an image with the given background area according to the /// rules in CSS-BACKGROUNDS § 3.9. @@ -389,7 +402,7 @@ pub trait FragmentDisplayListBuilding { border_painting_mode: BorderPaintingMode, bounds: &Rect<Au>, display_list_section: DisplayListSection, - clip: &ClippingRegion); + clip: &Rect<Au>); /// Adds the display items necessary to paint the outline of this fragment to the display list /// if necessary. @@ -397,7 +410,7 @@ pub trait FragmentDisplayListBuilding { state: &mut DisplayListBuildState, style: &ServoComputedValues, bounds: &Rect<Au>, - clip: &ClippingRegion); + clip: &Rect<Au>); /// Adds the display items necessary to paint the box shadow of this fragment to the display /// list if necessary. @@ -406,7 +419,7 @@ pub trait FragmentDisplayListBuilding { style: &ServoComputedValues, display_list_section: DisplayListSection, absolute_bounds: &Rect<Au>, - clip: &ClippingRegion); + clip: &Rect<Au>); /// Adds display items necessary to draw debug boxes around a scanned text fragment. fn build_debug_borders_around_text_fragments(&self, @@ -415,13 +428,13 @@ pub trait FragmentDisplayListBuilding { stacking_relative_border_box: &Rect<Au>, stacking_relative_content_box: &Rect<Au>, text_fragment: &ScannedTextFragmentInfo, - clip: &ClippingRegion); + clip: &Rect<Au>); /// Adds display items necessary to draw debug boxes around this fragment. fn build_debug_borders_around_fragment(&self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, - clip: &ClippingRegion); + clip: &Rect<Au>); /// Adds the display items for this fragment to the given display list. /// @@ -442,18 +455,7 @@ pub trait FragmentDisplayListBuilding { relative_containing_block_mode: WritingMode, border_painting_mode: BorderPaintingMode, display_list_section: DisplayListSection, - clip: &ClippingRegion); - - /// Adjusts the clipping region for all descendants of this fragment as appropriate. - fn adjust_clipping_region_for_children(&self, - current_clip: &mut ClippingRegion, - stacking_relative_border_box: &Rect<Au>); - - /// Adjusts the clipping rectangle for a fragment to take the `clip` property into account - /// per CSS 2.1 § 11.1.2. - fn adjust_clip_for_style(&self, - parent_clip: &mut ClippingRegion, - stacking_relative_border_box: &Rect<Au>); + clip: &Rect<Au>); /// Builds the display items necessary to paint the selection and/or caret for this fragment, /// if any. @@ -461,7 +463,7 @@ pub trait FragmentDisplayListBuilding { state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, display_list_section: DisplayListSection, - clip: &ClippingRegion); + clip: &Rect<Au>); /// Creates the text display item for one text fragment. This can be called multiple times for /// one fragment if there are text shadows. @@ -472,21 +474,21 @@ pub trait FragmentDisplayListBuilding { text_fragment: &ScannedTextFragmentInfo, stacking_relative_content_box: &Rect<Au>, text_shadow: Option<&TextShadow>, - clip: &ClippingRegion); + clip: &Rect<Au>); /// Creates the display item for a text decoration: underline, overline, or line-through. fn build_display_list_for_text_decoration(&self, state: &mut DisplayListBuildState, color: &RGBA, stacking_relative_box: &LogicalRect<Au>, - clip: &ClippingRegion, + clip: &Rect<Au>, blur_radius: Au); /// A helper method that `build_display_list` calls to create per-fragment-type display items. fn build_fragment_type_specific_display_items(&mut self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, - clip: &ClippingRegion); + clip: &Rect<Au>); /// Creates a stacking context for associated fragment. fn create_stacking_context(&self, @@ -579,14 +581,13 @@ impl FragmentDisplayListBuilding for Fragment { state: &mut DisplayListBuildState, style: &ServoComputedValues, display_list_section: DisplayListSection, - absolute_bounds: &Rect<Au>, - clip: &ClippingRegion) { + absolute_bounds: &Rect<Au>) { // Adjust the clipping region as necessary to account for `border-radius`. let border_radii = build_border_radius(absolute_bounds, style.get_border()); - let mut clip = (*clip).clone(); + let mut clip = ClippingRegion::max(); if !border_radii.is_square() { - clip.intersect_with_rounded_rect(absolute_bounds, &border_radii) - } + clip.intersect_with_rounded_rect(absolute_bounds, &border_radii); + }; let background = style.get_background(); // FIXME: This causes a lot of background colors to be displayed when they are clearly not @@ -864,10 +865,10 @@ impl FragmentDisplayListBuilding for Fragment { // Create the image display item. let base = state.create_base_display_item(&bounds, - &clip, - self.node, - style.get_cursor(Cursor::Default), - display_list_section); + &clip, + self.node, + style.get_cursor(Cursor::Default), + display_list_section); state.add_display_item(DisplayItem::Image(box ImageDisplayItem { base: base, webrender_image: webrender_image, @@ -1035,7 +1036,7 @@ impl FragmentDisplayListBuilding for Fragment { style: &ServoComputedValues, display_list_section: DisplayListSection, absolute_bounds: &Rect<Au>, - clip: &ClippingRegion) { + clip: &Rect<Au>) { // NB: According to CSS-BACKGROUNDS, box shadows render in *reverse* order (front to back). for box_shadow in style.get_effects().box_shadow.0.iter().rev() { let bounds = @@ -1046,7 +1047,7 @@ impl FragmentDisplayListBuilding for Fragment { // TODO(pcwalton): Multiple border radii; elliptical border radii. let base = state.create_base_display_item(&bounds, - &clip, + &ClippingRegion::from_rect(&clip), self.node, style.get_cursor(Cursor::Default), display_list_section); @@ -1076,7 +1077,7 @@ impl FragmentDisplayListBuilding for Fragment { border_painting_mode: BorderPaintingMode, bounds: &Rect<Au>, display_list_section: DisplayListSection, - clip: &ClippingRegion) { + clip: &Rect<Au>) { let mut border = style.logical_border_width(); match border_painting_mode { @@ -1118,7 +1119,7 @@ impl FragmentDisplayListBuilding for Fragment { // Append the border to the display list. let base = state.create_base_display_item(&bounds, - &clip, + &ClippingRegion::from_rect(&clip), self.node, style.get_cursor(Cursor::Default), display_list_section); @@ -1203,7 +1204,7 @@ impl FragmentDisplayListBuilding for Fragment { state: &mut DisplayListBuildState, style: &ServoComputedValues, bounds: &Rect<Au>, - clip: &ClippingRegion) { + clip: &Rect<Au>) { use style::values::Either; let width = style.get_outline().outline_width; @@ -1229,7 +1230,7 @@ impl FragmentDisplayListBuilding for Fragment { // Append the outline to the display list. let color = style.resolve_color(style.get_outline().outline_color).to_gfx_color(); let base = state.create_base_display_item(&bounds, - &clip, + &ClippingRegion::from_rect(&clip), self.node, style.get_cursor(Cursor::Default), DisplayListSection::Outlines); @@ -1250,13 +1251,13 @@ impl FragmentDisplayListBuilding for Fragment { stacking_relative_border_box: &Rect<Au>, stacking_relative_content_box: &Rect<Au>, text_fragment: &ScannedTextFragmentInfo, - clip: &ClippingRegion) { + clip: &Rect<Au>) { // FIXME(pcwalton, #2795): Get the real container size. let container_size = Size2D::zero(); // Compute the text fragment bounds and draw a border surrounding them. let base = state.create_base_display_item(stacking_relative_border_box, - clip, + &ClippingRegion::from_rect(&clip), self.node, style.get_cursor(Cursor::Default), DisplayListSection::Content); @@ -1279,7 +1280,7 @@ impl FragmentDisplayListBuilding for Fragment { let baseline = baseline.to_physical(self.style.writing_mode, container_size); let base = state.create_base_display_item(&baseline, - clip, + &ClippingRegion::from_rect(&clip), self.node, style.get_cursor(Cursor::Default), DisplayListSection::Content); @@ -1293,10 +1294,10 @@ impl FragmentDisplayListBuilding for Fragment { fn build_debug_borders_around_fragment(&self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, - clip: &ClippingRegion) { + clip: &Rect<Au>) { // This prints a debug border around the border of this fragment. let base = state.create_base_display_item(stacking_relative_border_box, - clip, + &ClippingRegion::from_rect(&clip), self.node, self.style.get_cursor(Cursor::Default), DisplayListSection::Content); @@ -1311,33 +1312,11 @@ impl FragmentDisplayListBuilding for Fragment { })); } - fn adjust_clip_for_style(&self, - parent_clip: &mut ClippingRegion, - stacking_relative_border_box: &Rect<Au>) { - use style::values::Either; - // Account for `clip` per CSS 2.1 § 11.1.2. - let style_clip_rect = match (self.style().get_box().position, - self.style().get_effects().clip) { - (position::T::absolute, Either::First(style_clip_rect)) => style_clip_rect, - _ => return, - }; - - // FIXME(pcwalton, #2795): Get the real container size. - let clip_origin = Point2D::new(stacking_relative_border_box.origin.x + - style_clip_rect.left.unwrap_or(Au(0)), - stacking_relative_border_box.origin.y + - style_clip_rect.top.unwrap_or(Au(0))); - let right = style_clip_rect.right.unwrap_or(stacking_relative_border_box.size.width); - let bottom = style_clip_rect.bottom.unwrap_or(stacking_relative_border_box.size.height); - let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y); - parent_clip.intersect_rect(&Rect::new(clip_origin, clip_size)) - } - fn build_display_items_for_selection_if_necessary(&self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, display_list_section: DisplayListSection, - clip: &ClippingRegion) { + clip: &Rect<Au>) { let scanned_text_fragment_info = match self.specific { SpecificFragmentInfo::ScannedText(ref scanned_text_fragment_info) => { scanned_text_fragment_info @@ -1352,7 +1331,7 @@ impl FragmentDisplayListBuilding for Fragment { let style = self.selected_style(); let background_color = style.resolve_color(style.get_background().background_color); let base = state.create_base_display_item(stacking_relative_border_box, - &clip, + &ClippingRegion::from_rect(&clip), self.node, self.style.get_cursor(Cursor::Default), display_list_section); @@ -1391,7 +1370,7 @@ impl FragmentDisplayListBuilding for Fragment { }; let base = state.create_base_display_item(&insertion_point_bounds, - &clip, + &ClippingRegion::from_rect(&clip), self.node, self.style.get_cursor(cursor), display_list_section); @@ -1408,7 +1387,7 @@ impl FragmentDisplayListBuilding for Fragment { relative_containing_block_mode: WritingMode, border_painting_mode: BorderPaintingMode, display_list_section: DisplayListSection, - clip: &ClippingRegion) { + clip: &Rect<Au>) { self.restyle_damage.remove(REPAINT); if self.style().get_inheritedbox().visibility != visibility::T::visible { return @@ -1431,7 +1410,7 @@ impl FragmentDisplayListBuilding for Fragment { // Check the clip rect. If there's nothing to render at all, don't even construct display // list items. - let empty_rect = !clip.might_intersect_rect(&stacking_relative_border_box); + let empty_rect = !clip.intersects(&stacking_relative_border_box); if self.is_primary_fragment() && !empty_rect { // Add shadows, background, borders, and outlines, if applicable. if let Some(ref inline_context) = self.inline_context { @@ -1440,8 +1419,7 @@ impl FragmentDisplayListBuilding for Fragment { state, &*node.style, display_list_section, - &stacking_relative_border_box, - clip); + &stacking_relative_border_box); self.build_display_list_for_box_shadow_if_applicable( state, &*node.style, @@ -1474,8 +1452,7 @@ impl FragmentDisplayListBuilding for Fragment { self.build_display_list_for_background_if_applicable(state, &*self.style, display_list_section, - &stacking_relative_border_box, - clip); + &stacking_relative_border_box); self.build_display_list_for_box_shadow_if_applicable(state, &*self.style, display_list_section, @@ -1522,7 +1499,7 @@ impl FragmentDisplayListBuilding for Fragment { fn build_fragment_type_specific_display_items(&mut self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, - clip: &ClippingRegion) { + clip: &Rect<Au>) { // Compute the context box position relative to the parent stacking context. let stacking_relative_content_box = self.stacking_relative_content_box(stacking_relative_border_box); @@ -1585,7 +1562,7 @@ impl FragmentDisplayListBuilding for Fragment { if !stacking_relative_content_box.is_empty() { let base = state.create_base_display_item( &stacking_relative_content_box, - clip, + &ClippingRegion::from_rect(clip), self.node, self.style.get_cursor(Cursor::Default), DisplayListSection::Content); @@ -1606,7 +1583,7 @@ impl FragmentDisplayListBuilding for Fragment { if let Some(ref image) = image_fragment.image { let base = state.create_base_display_item( &stacking_relative_content_box, - clip, + &ClippingRegion::from_rect(clip), self.node, self.style.get_cursor(Cursor::Default), DisplayListSection::Content); @@ -1637,7 +1614,7 @@ impl FragmentDisplayListBuilding for Fragment { let base = state.create_base_display_item( &stacking_relative_content_box, - clip, + &ClippingRegion::from_rect(clip), self.node, self.style.get_cursor(Cursor::Default), DisplayListSection::Content); @@ -1731,67 +1708,12 @@ impl FragmentDisplayListBuilding for Fragment { parent_scroll_id) } - fn adjust_clipping_region_for_children(&self, - current_clip: &mut ClippingRegion, - stacking_relative_border_box: &Rect<Au>) { - // Don't clip if we're text. - if self.is_scanned_text_fragment() { - return - } - - let overflow_x = self.style.get_box().overflow_x; - let overflow_y = self.style.get_box().overflow_y.0; - if overflow_x == overflow_x::T::visible && overflow_y == overflow_x::T::visible { - return - } - - let overflow_clip_rect_owner; - let overflow_clip_rect = match self.style.get_box()._servo_overflow_clip_box { - overflow_clip_box::T::padding_box => { - // FIXME(SimonSapin): should be the padding box, not border box. - stacking_relative_border_box - } - overflow_clip_box::T::content_box => { - overflow_clip_rect_owner = - self.stacking_relative_content_box(stacking_relative_border_box); - &overflow_clip_rect_owner - } - }; - - // Clip according to the values of `overflow-x` and `overflow-y`. - // - // FIXME(pcwalton): This may be more complex than it needs to be, since it seems to be - // impossible with the computed value rules as they are to have `overflow-x: visible` - // with `overflow-y: <scrolling>` or vice versa! - if let overflow_x::T::hidden = self.style.get_box().overflow_x { - let mut bounds = current_clip.bounding_rect(); - let max_x = cmp::min(bounds.max_x(), overflow_clip_rect.max_x()); - bounds.origin.x = cmp::max(bounds.origin.x, overflow_clip_rect.origin.x); - bounds.size.width = max_x - bounds.origin.x; - current_clip.intersect_rect(&bounds) - } - if let overflow_x::T::hidden = self.style.get_box().overflow_y.0 { - let mut bounds = current_clip.bounding_rect(); - let max_y = cmp::min(bounds.max_y(), overflow_clip_rect.max_y()); - bounds.origin.y = cmp::max(bounds.origin.y, overflow_clip_rect.origin.y); - bounds.size.height = max_y - bounds.origin.y; - current_clip.intersect_rect(&bounds) - } - - let border_radii = build_border_radius(stacking_relative_border_box, - self.style.get_border()); - if !border_radii.is_square() { - current_clip.intersect_with_rounded_rect(stacking_relative_border_box, - &border_radii) - } - } - fn build_display_list_for_text_fragment(&self, state: &mut DisplayListBuildState, text_fragment: &ScannedTextFragmentInfo, stacking_relative_content_box: &Rect<Au>, text_shadow: Option<&TextShadow>, - clip: &ClippingRegion) { + clip: &Rect<Au>) { // TODO(emilio): Allow changing more properties by ::selection let text_color = if let Some(shadow) = text_shadow { // If we're painting a shadow, paint the text the same color as the shadow. @@ -1828,7 +1750,7 @@ impl FragmentDisplayListBuilding for Fragment { // Create the text display item. let base = state.create_base_display_item(&stacking_relative_content_box, - clip, + &ClippingRegion::from_rect(&clip), self.node, self.style().get_cursor(cursor), DisplayListSection::Content); @@ -1894,7 +1816,7 @@ impl FragmentDisplayListBuilding for Fragment { state: &mut DisplayListBuildState, color: &RGBA, stacking_relative_box: &LogicalRect<Au>, - clip: &ClippingRegion, + clip: &Rect<Au>, blur_radius: Au) { // Perhaps surprisingly, text decorations are box shadows. This is because they may need // to have blur in the case of `text-shadow`, and this doesn't hurt performance because box @@ -1906,7 +1828,7 @@ impl FragmentDisplayListBuilding for Fragment { container_size); let base = state.create_base_display_item( &shadow_bounds(&stacking_relative_box, blur_radius, Au(0)), - clip, + &ClippingRegion::from_rect(&clip), self.node, self.style.get_cursor(Cursor::Default), DisplayListSection::Content); @@ -1926,7 +1848,23 @@ impl FragmentDisplayListBuilding for Fragment { pub trait BlockFlowDisplayListBuilding { fn collect_stacking_contexts_for_block(&mut self, state: &mut DisplayListBuildState); - fn setup_scroll_root_for_block(&mut self, state: &mut DisplayListBuildState) -> ScrollRootId; + + fn transform_clip_to_coordinate_space(&mut self, + state: &mut DisplayListBuildState, + preserved_state: &mut PreservedDisplayListState); + fn setup_clipping_for_block(&mut self, + state: &mut DisplayListBuildState, + preserved_state: &mut PreservedDisplayListState, + stacking_context_type: BlockStackingContextType) + -> ScrollRootId; + fn setup_scroll_root_for_overflow(&mut self, + state: &mut DisplayListBuildState, + preserved_state: &mut PreservedDisplayListState, + border_box: &Rect<Au>); + fn setup_scroll_root_for_css_clip(&mut self, + state: &mut DisplayListBuildState, + preserved_state: &mut PreservedDisplayListState, + stacking_relative_border_box: &Rect<Au>); fn create_pseudo_stacking_context_for_block(&mut self, parent_stacking_context_id: StackingContextId, parent_scroll_root_id: ScrollRootId, @@ -1940,9 +1878,131 @@ pub trait BlockFlowDisplayListBuilding { border_painting_mode: BorderPaintingMode); } +/// This structure manages ensuring that modification to DisplayListBuildState +/// is only temporary. It's useful for moving recursively down the flow tree +/// and ensuring that the state is restored for siblings. To use this structure, +/// we must call PreservedDisplayListState::restore in order to restore the state. +/// TODO(mrobinson): It would be nice to use RAII here to avoid having to call restore. +pub struct PreservedDisplayListState { + stacking_context_id: StackingContextId, + scroll_root_id: ScrollRootId, + containing_block_scroll_root_id: ScrollRootId, + clips_pushed: usize, + containing_block_clips_pushed: usize, +} + +impl PreservedDisplayListState { + fn new(state: &mut DisplayListBuildState) -> PreservedDisplayListState { + PreservedDisplayListState { + stacking_context_id: state.current_stacking_context_id, + scroll_root_id: state.current_scroll_root_id, + containing_block_scroll_root_id: state.containing_block_scroll_root_id, + clips_pushed: 0, + containing_block_clips_pushed: 0, + } + } + + fn switch_to_containing_block_clip(&mut self, state: &mut DisplayListBuildState) { + let clip = state.containing_block_clip_stack.last().cloned().unwrap_or_else(max_rect); + state.clip_stack.push(clip); + self.clips_pushed += 1; + } + + fn restore(self, state: &mut DisplayListBuildState) { + state.current_stacking_context_id = self.stacking_context_id; + state.current_scroll_root_id = self.scroll_root_id; + state.containing_block_scroll_root_id = self.containing_block_scroll_root_id; + + let truncate_length = state.clip_stack.len() - self.clips_pushed; + state.clip_stack.truncate(truncate_length); + + let truncate_length = state.containing_block_clip_stack.len() - + self.containing_block_clips_pushed; + state.containing_block_clip_stack.truncate(truncate_length); + } + + fn push_clip(&mut self, + state: &mut DisplayListBuildState, + clip: &Rect<Au>, + positioning: position::T) { + let mut clip = *clip; + if positioning != position::T::fixed { + if let Some(old_clip) = state.clip_stack.last() { + clip = old_clip.intersection(&clip).unwrap_or_else(Rect::zero); + } + } + + state.clip_stack.push(clip); + self.clips_pushed += 1; + + if position::T::absolute == positioning { + state.containing_block_clip_stack.push(clip); + self.containing_block_clips_pushed += 1; + } + } +} + impl BlockFlowDisplayListBuilding for BlockFlow { + fn transform_clip_to_coordinate_space(&mut self, + state: &mut DisplayListBuildState, + preserved_state: &mut PreservedDisplayListState) { + if state.clip_stack.is_empty() { + return; + } + + let border_box = 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); + + let transform = match self.fragment.transform_matrix(&border_box) { + Some(transform) => transform, + None => return, + }; + + let perspective = self.fragment.perspective_matrix(&border_box) + .unwrap_or_else(Matrix4D::identity); + let transform = transform.pre_mul(&perspective).inverse(); + + let origin = &border_box.origin; + let transform_clip = |clip: &Rect<Au>| { + if *clip == max_rect() { + return *clip; + } + + match transform { + Some(transform) => { + let clip = Rect::new(Point2D::new((clip.origin.x - origin.x).to_f32_px(), + (clip.origin.y - origin.y).to_f32_px()), + Size2D::new(clip.size.width.to_f32_px(), + clip.size.height.to_f32_px())); + + let clip = transform.transform_rect(&clip); + + Rect::new(Point2D::new(Au::from_f32_px(clip.origin.x), + Au::from_f32_px(clip.origin.y)), + Size2D::new(Au::from_f32_px(clip.size.width), + Au::from_f32_px(clip.size.height))) + } + None => Rect::zero(), + } + }; + + if let Some(clip) = state.clip_stack.last().cloned() { + state.clip_stack.push(transform_clip(&clip)); + preserved_state.clips_pushed += 1; + } + + if let Some(clip) = state.containing_block_clip_stack.last().cloned() { + state.containing_block_clip_stack.push(transform_clip(&clip)); + preserved_state.containing_block_clips_pushed += 1; + } + } + fn collect_stacking_contexts_for_block(&mut self, state: &mut DisplayListBuildState) { - let parent_stacking_context_id = state.current_stacking_context_id; + let mut preserved_state = PreservedDisplayListState::new(state); + let block_stacking_context_type = self.block_stacking_context_type(); self.base.stacking_context_id = match block_stacking_context_type { BlockStackingContextType::NonstackingContext => state.current_stacking_context_id, @@ -1951,14 +2011,13 @@ impl BlockFlowDisplayListBuilding for BlockFlow { }; state.current_stacking_context_id = self.base.stacking_context_id; - let original_scroll_root_id = state.current_scroll_root_id; - let original_containing_block_scroll_root = state.containing_block_scroll_root_id; - // We are getting the id of the scroll root that contains us here, not the id of // any scroll root that we create. If we create a scroll root, its id will be // stored in state.current_scroll_root_id. If we should create a stacking context, // we don't want it to be clipped by its own scroll root. - let containing_scroll_root_id = self.setup_scroll_root_for_block(state); + let containing_scroll_root_id = self.setup_clipping_for_block(state, + &mut preserved_state, + block_stacking_context_type); if establishes_containing_block_for_absolute(self.positioning()) { state.containing_block_scroll_root_id = state.current_scroll_root_id; @@ -1969,35 +2028,40 @@ impl BlockFlowDisplayListBuilding for BlockFlow { self.base.collect_stacking_contexts_for_children(state); } BlockStackingContextType::PseudoStackingContext => { - self.create_pseudo_stacking_context_for_block(parent_stacking_context_id, + self.create_pseudo_stacking_context_for_block(preserved_state.stacking_context_id, containing_scroll_root_id, state); } BlockStackingContextType::StackingContext => { - self.create_real_stacking_context_for_block(parent_stacking_context_id, + self.create_real_stacking_context_for_block(preserved_state.stacking_context_id, containing_scroll_root_id, state); } } - state.current_scroll_root_id = original_scroll_root_id; - state.containing_block_scroll_root_id = original_containing_block_scroll_root; - state.current_stacking_context_id = parent_stacking_context_id; + preserved_state.restore(state); } - fn setup_scroll_root_for_block(&mut self, state: &mut DisplayListBuildState) -> ScrollRootId { + fn setup_clipping_for_block(&mut self, + state: &mut DisplayListBuildState, + preserved_state: &mut PreservedDisplayListState, + stacking_context_type: BlockStackingContextType) + -> ScrollRootId { // If this block is absolutely positioned, we should be clipped and positioned by // the scroll root of our nearest ancestor that establishes a containing block. let containing_scroll_root_id = match self.positioning() { - position::T::absolute => state.containing_block_scroll_root_id, + position::T::absolute => { + preserved_state.switch_to_containing_block_clip(state); + state.current_scroll_root_id = state.containing_block_scroll_root_id; + state.containing_block_scroll_root_id + } + position::T::fixed => { + preserved_state.push_clip(state, &max_rect(), position::T::fixed); + state.current_scroll_root_id + } _ => state.current_scroll_root_id, }; self.base.scroll_root_id = containing_scroll_root_id; - state.current_scroll_root_id = containing_scroll_root_id; - - if !self.style_permits_scrolling_overflow() { - return containing_scroll_root_id; - } let coordinate_system = if self.fragment.establishes_stacking_context() { CoordinateSystem::Own @@ -2005,25 +2069,58 @@ impl BlockFlowDisplayListBuilding for BlockFlow { CoordinateSystem::Parent }; - let border_box = self.fragment.stacking_relative_border_box( + let stacking_relative_border_box = 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, coordinate_system); - let content_box = self.fragment.stacking_relative_content_box(&border_box); - // If we don't overflow our box at all, we can avoid creating a scroll root. - if self.base.overflow.scroll.origin == Point2D::zero() && - self.base.overflow.scroll.size.width <= content_box.size.width && - self.base.overflow.scroll.size.height <= content_box.size.height { - self.mark_scrolling_overflow(false); - return containing_scroll_root_id; + if stacking_context_type == BlockStackingContextType::StackingContext { + self.transform_clip_to_coordinate_space(state, preserved_state); } - self.mark_scrolling_overflow(true); + self.setup_scroll_root_for_overflow(state, preserved_state, &stacking_relative_border_box); + self.setup_scroll_root_for_css_clip(state, preserved_state, &stacking_relative_border_box); + self.base.clip = state.clip_stack.last().cloned().unwrap_or_else(max_rect); + + match self.positioning() { + position::T::absolute | position::T::relative | position::T::fixed => + state.containing_block_scroll_root_id = state.current_scroll_root_id, + _ => {} + } + + containing_scroll_root_id + } + + fn setup_scroll_root_for_overflow(&mut self, + state: &mut DisplayListBuildState, + preserved_state: &mut PreservedDisplayListState, + border_box: &Rect<Au>) { + if !self.overflow_style_may_require_scroll_root() { + return; + } + + let content_box = self.fragment.stacking_relative_content_box(&border_box); + let has_scrolling_overflow = + self.base.overflow.scroll.origin != Point2D::zero() || + self.base.overflow.scroll.size.width > content_box.size.width || + self.base.overflow.scroll.size.height > content_box.size.height || + overflow_x::T::hidden == self.fragment.style.get_box().overflow_x || + overflow_x::T::hidden == self.fragment.style.get_box().overflow_y.0; + + self.mark_scrolling_overflow(has_scrolling_overflow); + if !has_scrolling_overflow { + return; + } let new_scroll_root_id = ScrollRootId::new_of_type(self.fragment.node.id() as usize, self.fragment.fragment_type()); + // If we already have a scroll root for this flow, just return. This can happen + // when fragments map to more than one flow, such as in the case of table + // wrappers. We just accept the first scroll root in that case. + if state.has_scroll_root(new_scroll_root_id) { + return; + } let clip_rect = Rect::new(Point2D::zero(), content_box.size); let mut clip = ClippingRegion::from_rect(&clip_rect); @@ -2035,21 +2132,92 @@ impl BlockFlowDisplayListBuilding for BlockFlow { } let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size; + let mut content_size = Size2D::new(content_size.x, content_size.y); + if overflow_x::T::hidden == self.fragment.style.get_box().overflow_x { + content_size.width = content_box.size.width; + } + + if overflow_x::T::hidden == self.fragment.style.get_box().overflow_y.0 { + content_size.height = content_box.size.height; + } + + if overflow_x::T::hidden == self.fragment.style.get_box().overflow_y.0 || + overflow_x::T::hidden == self.fragment.style.get_box().overflow_x { + preserved_state.push_clip(state, &border_box, self.positioning()); + } + + let clip_rect = Rect::new(Point2D::zero(), content_box.size); + let mut clip = ClippingRegion::from_rect(&clip_rect); + let radii = build_border_radius_for_inner_rect(&border_box, self.fragment.style.clone()); + if !radii.is_square() { + clip.intersect_with_rounded_rect(&clip_rect, &radii) + } + state.add_scroll_root( ScrollRoot { id: new_scroll_root_id, - parent_id: containing_scroll_root_id, + parent_id: self.base.scroll_root_id, clip: clip, - content_rect: Rect::new(content_box.origin, - Size2D::new(content_size.x, content_size.y)), + content_rect: Rect::new(content_box.origin, content_size), }, self.base.stacking_context_id ); self.base.scroll_root_id = new_scroll_root_id; state.current_scroll_root_id = new_scroll_root_id; + } - containing_scroll_root_id + /// Adds a scroll root for a block to take the `clip` property into account + /// per CSS 2.1 § 11.1.2. + fn setup_scroll_root_for_css_clip(&mut self, + state: &mut DisplayListBuildState, + preserved_state: &mut PreservedDisplayListState, + stacking_relative_border_box: &Rect<Au>) { + // Account for `clip` per CSS 2.1 § 11.1.2. + let style_clip_rect = match self.fragment.style().get_effects().clip { + Either::First(style_clip_rect) => style_clip_rect, + _ => return, + }; + + let clip_origin = Point2D::new(stacking_relative_border_box.origin.x + + style_clip_rect.left.unwrap_or(Au(0)), + stacking_relative_border_box.origin.y + + style_clip_rect.top.unwrap_or(Au(0))); + let right = style_clip_rect.right.unwrap_or(stacking_relative_border_box.size.width); + let bottom = style_clip_rect.bottom.unwrap_or(stacking_relative_border_box.size.height); + let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y); + + // We use the node id to create scroll roots for overflow properties, so we + // use the fragment address to do the same for CSS clipping. + // TODO(mrobinson): This should be more resilient while maintaining the space + // efficiency of ScrollRootId. + let fragment_id = &mut self.fragment as *mut _; + let new_scroll_root_id = ScrollRootId::new_of_type(fragment_id as usize, + self.fragment.fragment_type()); + + // If we already have a scroll root for this flow, just return. This can happen + // when fragments map to more than one flow, such as in the case of table + // wrappers. We just accept the first scroll root in that case. + if state.has_scroll_root(new_scroll_root_id) { + return; + } + + let content_rect = Rect::new(clip_origin, clip_size); + preserved_state.push_clip(state, &content_rect, self.positioning()); + + + state.add_scroll_root( + ScrollRoot { + id: new_scroll_root_id, + parent_id: self.base.scroll_root_id, + clip: ClippingRegion::from_rect(&Rect::new(Point2D::zero(), clip_size)), + content_rect: content_rect, + }, + self.base.stacking_context_id + ); + + self.base.scroll_root_id = new_scroll_root_id; + state.current_scroll_root_id = new_scroll_root_id; } fn create_pseudo_stacking_context_for_block(&mut self, @@ -2102,6 +2270,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { scroll_policy, StackingContextCreationMode::Normal, parent_scroll_root_id); + state.add_stacking_context(parent_stacking_context_id, stacking_context); self.base.collect_stacking_contexts_for_children(state); } @@ -2156,6 +2325,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow { fn collect_stacking_contexts_for_inline(&mut self, state: &mut DisplayListBuildState) { self.base.stacking_context_id = state.current_stacking_context_id; self.base.scroll_root_id = state.current_scroll_root_id; + self.base.clip = state.clip_stack.last().cloned().unwrap_or_else(max_rect); for mut fragment in self.fragments.fragments.iter_mut() { let previous_containing_block_scroll_root_id = state.containing_block_scroll_root_id; @@ -2194,6 +2364,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow { } state.containing_block_scroll_root_id = previous_containing_block_scroll_root_id; } + } fn build_display_list_for_inline_fragment_at_index(&mut self, @@ -2309,7 +2480,7 @@ impl BaseFlowDisplayListBuilding for BaseFlow { color.a = 1.0; let base = state.create_base_display_item( &stacking_context_relative_bounds.inflate(Au::from_px(2), Au::from_px(2)), - &self.clip, + &ClippingRegion::from_rect(&self.clip), node, None, DisplayListSection::Content); diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 945943a27c5..372d76a404c 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -29,13 +29,12 @@ use app_units::Au; use block::{BlockFlow, FormattingContextType}; use context::LayoutContext; use display_list_builder::DisplayListBuildState; -use euclid::{Matrix4D, Point2D, Size2D}; +use euclid::{Matrix4D, Point2D, Rect, Size2D}; use flex::FlexFlow; use floats::{Floats, SpeculatedFloatPlacement}; use flow_list::{FlowList, MutFlowListIterator}; use flow_ref::{FlowRef, WeakFlowRef}; -use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; -use gfx::display_list::ClippingRegion; +use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow}; use gfx_traits::{ScrollRootId, StackingContextId}; use gfx_traits::print_tree::PrintTree; use inline::InlineFlow; @@ -43,7 +42,7 @@ use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; use multicol::MulticolFlow; use parallel::FlowParallelInfo; use serde::ser::{Serialize, SerializeStruct, Serializer}; -use servo_geometry::{au_rect_to_f32_rect, f32_rect_to_au_rect}; +use servo_geometry::{au_rect_to_f32_rect, f32_rect_to_au_rect, max_rect}; use std::{fmt, mem, raw}; use std::iter::Zip; use std::slice::IterMut; @@ -264,6 +263,24 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { } } + let border_box = self.as_block().fragment.stacking_relative_border_box( + &base(self).stacking_relative_position, + &base(self).early_absolute_position_info.relative_containing_block_size, + base(self).early_absolute_position_info.relative_containing_block_mode, + CoordinateSystem::Own); + if overflow_x::T::visible != self.as_block().fragment.style.get_box().overflow_x { + overflow.paint.origin.x = Au(0); + overflow.paint.size.width = border_box.size.width; + overflow.scroll.origin.x = Au(0); + overflow.scroll.size.width = border_box.size.width; + } + if overflow_x::T::visible != self.as_block().fragment.style.get_box().overflow_y.0 { + overflow.paint.origin.y = Au(0); + overflow.paint.size.height = border_box.size.height; + overflow.scroll.origin.y = Au(0); + overflow.scroll.size.height = border_box.size.height; + } + if !self.as_block().fragment.establishes_stacking_context() || self.as_block().fragment.style.get_box().transform.0.is_none() { overflow.translate(&position.origin); @@ -311,40 +328,8 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => { - let overflow_x = self.as_block().fragment.style.get_box().overflow_x; - let overflow_y = self.as_block().fragment.style.get_box().overflow_y; - for kid in mut_base(self).children.iter_mut() { - let mut kid_overflow = kid.get_overflow_in_parent_coordinates(); - - // If the overflow for this flow is hidden on a given axis, just - // put the existing overflow in the kid rect, so that the union - // has no effect on this axis. - match overflow_x { - overflow_x::T::hidden => { - kid_overflow.paint.origin.x = overflow.paint.origin.x; - kid_overflow.paint.size.width = overflow.paint.size.width; - kid_overflow.scroll.origin.x = overflow.scroll.origin.x; - kid_overflow.scroll.size.width = overflow.scroll.size.width; - } - overflow_x::T::scroll | - overflow_x::T::auto | - overflow_x::T::visible => {} - } - - match overflow_y.0 { - overflow_x::T::hidden => { - kid_overflow.paint.origin.y = overflow.paint.origin.y; - kid_overflow.paint.size.height = overflow.paint.size.height; - kid_overflow.scroll.origin.y = overflow.scroll.origin.y; - kid_overflow.scroll.size.height = overflow.scroll.size.height; - } - overflow_x::T::scroll | - overflow_x::T::auto | - overflow_x::T::visible => {} - } - - overflow.union(&kid_overflow) + overflow.union(&kid.get_overflow_in_parent_coordinates()); } } _ => {} @@ -957,10 +942,10 @@ pub struct BaseFlow { /// assignment. pub late_absolute_position_info: LateAbsolutePositionInfo, - /// The clipping region for this flow and its descendants, in the coordinate system of the + /// The clipping rectangle for this flow and its descendants, in the coordinate system of the /// nearest ancestor stacking context. If this flow itself represents a stacking context, then /// this is in the flow's own coordinate system. - pub clip: ClippingRegion, + pub clip: Rect<Au>, /// The writing mode for this flow. pub writing_mode: WritingMode, @@ -1115,7 +1100,7 @@ impl BaseFlow { absolute_cb: ContainingBlockLink::new(), early_absolute_position_info: EarlyAbsolutePositionInfo::new(writing_mode), late_absolute_position_info: LateAbsolutePositionInfo::new(), - clip: ClippingRegion::max(), + clip: max_rect(), flags: flags, writing_mode: writing_mode, thread_id: 0, diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs index 19ea757bc0e..0f21523ceb9 100644 --- a/components/layout/webrender_helpers.rs +++ b/components/layout/webrender_helpers.rs @@ -425,11 +425,16 @@ impl WebRenderDisplayItemConverter for DisplayItem { } DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(), DisplayItem::PushScrollRoot(ref item) => { - let our_id = ClipId::new(item.scroll_root.id.0 as u64, builder.pipeline_id); + let pipeline_id = builder.pipeline_id; + builder.push_clip_id(item.scroll_root.parent_id.convert_to_webrender(pipeline_id)); + + let our_id = item.scroll_root.id.convert_to_webrender(pipeline_id); let clip = item.scroll_root.clip.to_clip_region(builder); let content_rect = item.scroll_root.content_rect.to_rectf(); let webrender_id = builder.define_clip(content_rect, clip, Some(our_id)); debug_assert!(our_id == webrender_id); + + builder.pop_clip_id(); } DisplayItem::PopScrollRoot(_) => {} //builder.pop_scroll_layer(), } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 4428c0c4771..9683afcbd29 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -45,8 +45,7 @@ use euclid::rect::Rect; use euclid::scale_factor::ScaleFactor; use euclid::size::Size2D; use fnv::FnvHasher; -use gfx::display_list::{ClippingRegion, OpaqueNode}; -use gfx::display_list::WebRenderImageInfo; +use gfx::display_list::{OpaqueNode, WebRenderImageInfo}; use gfx::font; use gfx::font_cache_thread::FontCacheThread; use gfx::font_context; @@ -854,8 +853,7 @@ impl LayoutThread { LogicalPoint::zero(writing_mode).to_physical(writing_mode, self.viewport_size); - flow::mut_base(layout_root).clip = - ClippingRegion::from_rect(&data.page_clip_rect); + flow::mut_base(layout_root).clip = data.page_clip_rect; if flow::base(layout_root).restyle_damage.contains(REPOSITION) { layout_root.traverse_preorder(&ComputeAbsolutePositions { diff --git a/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/attachment-local-clipping-image-6.htm.ini b/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/attachment-local-clipping-image-6.htm.ini deleted file mode 100644 index c58b15efad7..00000000000 --- a/tests/wpt/metadata-css/css-backgrounds-3_dev/html4/attachment-local-clipping-image-6.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[attachment-local-clipping-image-6.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-004.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-004.htm.ini deleted file mode 100644 index a88b258cb5d..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-004.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abspos-overflow-004.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-005.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-005.htm.ini deleted file mode 100644 index fdf80129dd8..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-005.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abspos-overflow-005.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-006.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-006.htm.ini deleted file mode 100644 index a8f722cf09d..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-006.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abspos-overflow-006.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/overflow-applies-to-014.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/overflow-applies-to-014.htm.ini deleted file mode 100644 index fe7f4280a49..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/overflow-applies-to-014.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[overflow-applies-to-014.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/overflow-applies-to-015.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/overflow-applies-to-015.htm.ini deleted file mode 100644 index 2be519d22b6..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/overflow-applies-to-015.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[overflow-applies-to-015.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 6a9a17e11cf..480e7fed1a5 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -20601,7 +20601,7 @@ "support" ], "css/border_radius_in_border_radius_a.html": [ - "ee5099078e3403236fafbe82879c203f4c13cf50", + "64ad2121d35db22286fbea2d0835f2a36aa90570", "reftest" ], "css/border_radius_in_border_radius_ref.html": [ @@ -22813,11 +22813,11 @@ "support" ], "css/overflow_clipping.html": [ - "f87d3c74c15393fb490e3855c88f6331fce9e077", + "3e8eb236afa8013c19adbfa807b674a393fc1eed", "reftest" ], "css/overflow_clipping_ref.html": [ - "d9a6a3b4fa21742d0f4ddd3f348534ec35ab8579", + "f645ece68f6c7afe273daee4d1ec172c7d245632", "support" ], "css/overflow_position_abs_inline_block.html": [ diff --git a/tests/wpt/mozilla/meta/css/overflow_position_abs_inside_normal_a.html.ini b/tests/wpt/mozilla/meta/css/overflow_position_abs_inside_normal_a.html.ini deleted file mode 100644 index 7a3a9f959fa..00000000000 --- a/tests/wpt/mozilla/meta/css/overflow_position_abs_inside_normal_a.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[overflow_position_abs_inside_normal_a.html] - type: reftest - expected: FAIL - bug: https://github.com/servo/servo/issues/8780 diff --git a/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_a.html b/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_a.html index f19170d0144..95195a276d7 100644 --- a/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_a.html +++ b/tests/wpt/mozilla/tests/css/border_radius_in_border_radius_a.html @@ -8,7 +8,6 @@ html, body { } .green { - overflow: hidden; border-radius: 50px; background: green; padding: 50px; diff --git a/tests/wpt/mozilla/tests/css/overflow_clipping.html b/tests/wpt/mozilla/tests/css/overflow_clipping.html index 045dc08ecd2..844686325a5 100644 --- a/tests/wpt/mozilla/tests/css/overflow_clipping.html +++ b/tests/wpt/mozilla/tests/css/overflow_clipping.html @@ -18,7 +18,6 @@ </style> </head> <body> - <div class="test grayest box" style="overflow: scroll"> <div style="position: relative;"> <div class="overflowing-absolute-child grayer"></div> @@ -35,5 +34,52 @@ </div> --> + <div style="height: 30px; clear: both;"> </div> + + <div class="test grayest box" style="overflow: auto;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: fixed;"></div> + </div> + <div class="test grayest box" style="overflow: hidden;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: fixed;"></div> + </div> + <div class="test grayest box" style="overflow: scroll;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: fixed;"></div> + </div> + + <div style="height: 30px; clear: both;"> </div> + + <div class="test grayest box" style="overflow: auto;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: absolute;"></div> + </div> + <div class="test grayest box" style="overflow: hidden;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: absolute;"></div> + </div> + <div class="test grayest box" style="overflow: scroll;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: absolute;"></div> + </div> + + <div style="height: 30px; clear: both;"> </div> + + <div class="test grayest box" style="overflow: auto;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: relative;"></div> + </div> + <div class="test grayest box" style="overflow: hidden;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: relative;"></div> + </div> + <div class="test grayest box" style="overflow: scroll;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: relative;"></div> + </div> + + <div style="height: 30px; clear: both;"> </div> + + <div class="test grayest box" style="overflow: auto;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box" style="overflow: hidden;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box" style="overflow: scroll;"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div> + </div> </body> </html> diff --git a/tests/wpt/mozilla/tests/css/overflow_clipping_ref.html b/tests/wpt/mozilla/tests/css/overflow_clipping_ref.html index 0a1a3e09497..53ec1e6a15a 100644 --- a/tests/wpt/mozilla/tests/css/overflow_clipping_ref.html +++ b/tests/wpt/mozilla/tests/css/overflow_clipping_ref.html @@ -22,5 +22,53 @@ <div class="grayer smallbox" style="margin-left: 10px; margin-top: 25px; height: 25px;"></div> </div> --> + + <div style="height: 30px; clear: both;"> </div> + + <div class="test grayest box"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + + <div style="height: 30px; clear: both;"> </div> + + <div class="test grayest box"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box"> + <div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + + <div style="height: 30px; clear: both;"> </div> + + <div class="test grayest box"> + <div class="grayer smallbox" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box"> + <div class="grayer smallbox" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box"> + <div class="grayer smallbox" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + + <div style="height: 30px; clear: both;"> </div> + + <div class="test grayest box"> + <div class="grayer smallbox" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box"> + <div class="grayer smallbox" style="margin-left: 10px; margin-top: 10px;"></div> + </div> + <div class="test grayest box"> + <div class="grayer smallbox" style="margin-left: 10px; margin-top: 10px;"></div> + </div> </body> </html> |