servo: Merge #16336 - Used shared clips for overflow:hidden and CSS clip (from mrobinson:overflow-hidden); r=glennw

Instead of passing down a complex clipping region to each item, used
shared clipping to handle overflow:hidden and CSS clips. In addition to
being more efficient, this should also fix quite a few issues related
to absolutely positioned elements.

One existing reftest is slightly modified to avoid tickling a quirk
with the way that WebRender rasterizes masks. We are working out how to
best express these combined masks with the API or need to. The change
does not affect the original subject of the reftest.

Fixes #13109.
Fixes #10151.
Fixes #7575.
Fixes #8074.

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #13109, #10151, #7575, #8074.

<!-- Either: -->
- [x] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

Source-Repo: https://github.com/servo/servo
Source-Revision: ab7e1e2d36829e20df130f7174c3c7d1dbbd5cc5

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : f0f2687589ff10964ff809060f17d3c7e3e358c8
This commit is contained in:
Martin Robinson 2017-04-17 16:08:00 -05:00
parent b29b823ee9
commit 4608399eb9
5 changed files with 385 additions and 316 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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,

View File

@ -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(),
}

View File

@ -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 {