aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout/block.rs106
-rw-r--r--components/layout/display_list_builder.rs517
-rw-r--r--components/layout/flow.rs65
-rw-r--r--components/layout/webrender_helpers.rs7
-rw-r--r--components/layout_thread/lib.rs6
-rw-r--r--tests/wpt/metadata-css/css-backgrounds-3_dev/html4/attachment-local-clipping-image-6.htm.ini3
-rw-r--r--tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-004.htm.ini3
-rw-r--r--tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-005.htm.ini3
-rw-r--r--tests/wpt/metadata-css/css21_dev/html4/abspos-overflow-006.htm.ini3
-rw-r--r--tests/wpt/metadata-css/css21_dev/html4/overflow-applies-to-014.htm.ini3
-rw-r--r--tests/wpt/metadata-css/css21_dev/html4/overflow-applies-to-015.htm.ini3
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json6
-rw-r--r--tests/wpt/mozilla/meta/css/overflow_position_abs_inside_normal_a.html.ini4
-rw-r--r--tests/wpt/mozilla/tests/css/border_radius_in_border_radius_a.html1
-rw-r--r--tests/wpt/mozilla/tests/css/overflow_clipping.html48
-rw-r--r--tests/wpt/mozilla/tests/css/overflow_clipping_ref.html48
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>