From 6b28965b94b56169f8caf4adc1bdd2ee773485aa Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 16 Dec 2014 11:19:37 -0800 Subject: [PATCH] =?UTF-8?q?layout:=20Implement=20`clip`=20per=20CSS=202.1?= =?UTF-8?q?=20=C2=A7=2011.1.2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only the recommended, comma-separated syntax is supported. --- components/layout/block.rs | 4 +- components/layout/display_list_builder.rs | 75 +++++++++++++++------- components/layout/inline.rs | 4 +- components/servo/Cargo.lock | 2 +- components/style/properties/mod.rs.mako | 78 +++++++++++++++++++++++ components/util/geometry.rs | 1 + ports/cef/Cargo.lock | 2 +- ports/gonk/Cargo.lock | 2 +- tests/ref/basic.list | 1 + tests/ref/clip_a.html | 52 +++++++++++++++ tests/ref/clip_ref.html | 70 ++++++++++++++++++++ 11 files changed, 261 insertions(+), 30 deletions(-) create mode 100644 tests/ref/clip_a.html create mode 100644 tests/ref/clip_ref.html diff --git a/components/layout/block.rs b/components/layout/block.rs index 6ff01eaf684..744ba085361 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -1774,8 +1774,8 @@ impl Flow for BlockFlow { } else { self.base.stacking_relative_position }; - let clip_rect = self.fragment.clip_rect_for_children(self.base.clip_rect, - origin_for_children); + let clip_rect = self.fragment.clip_rect_for_children(&self.base.clip_rect, + &origin_for_children); // Process children. let writing_mode = self.base.writing_mode; diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index b88b3809027..25e567d4e3a 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -43,9 +43,9 @@ use std::num::FloatMath; use style::computed::{AngleOrCorner, LengthOrPercentage, HorizontalDirection, VerticalDirection}; use style::computed::{Image, LinearGradient}; use style::computed_values::{background_attachment, background_repeat, border_style, overflow}; -use style::computed_values::{visibility}; -use style::{ComputedValues, RGBA}; +use style::computed_values::{position, visibility}; use style::style_structs::Border; +use style::{ComputedValues, RGBA}; use sync::Arc; use url::Url; @@ -167,8 +167,13 @@ pub trait FragmentDisplayListBuilding { offset: Point2D, layout_context: &LayoutContext); - fn clip_rect_for_children(&self, current_clip_rect: Rect, flow_origin: Point2D) + fn clip_rect_for_children(&self, current_clip_rect: &Rect, flow_origin: &Point2D) -> Rect; + + /// Calculates the clipping rectangle for a fragment, taking the `clip` property into account + /// per CSS 2.1 § 11.1.2. + fn calculate_style_specified_clip(&self, parent_clip_rect: &Rect, origin: &Point2D) + -> Rect; } fn build_border_radius(abs_bounds: &Rect, border_style: &Border) -> BorderRadii { @@ -605,6 +610,24 @@ impl FragmentDisplayListBuilding for Fragment { })); } + fn calculate_style_specified_clip(&self, parent_clip_rect: &Rect, origin: &Point2D) + -> Rect { + // 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::absolute, Some(style_clip_rect)) => style_clip_rect, + _ => return *parent_clip_rect, + }; + + // FIXME(pcwalton, #2795): Get the real container size. + let border_box = self.border_box.to_physical(self.style.writing_mode, Size2D::zero()); + let clip_origin = Point2D(border_box.origin.x + style_clip_rect.left, + border_box.origin.y + style_clip_rect.top); + Rect(clip_origin + *origin, + Size2D(style_clip_rect.right.unwrap_or(border_box.size.width) - clip_origin.x, + style_clip_rect.bottom.unwrap_or(border_box.size.height) - clip_origin.y)) + } + fn build_display_list(&mut self, display_list: &mut DisplayList, layout_context: &LayoutContext, @@ -646,7 +669,11 @@ impl FragmentDisplayListBuilding for Fragment { return } - if !absolute_fragment_bounds.intersects(clip_rect) { + // Calculate the clip rect. If there's nothing to render at all, don't even construct + // display list items. + let clip_rect = self.calculate_style_specified_clip(clip_rect, + &absolute_fragment_bounds.origin); + if !absolute_fragment_bounds.intersects(&clip_rect) { return; } @@ -666,7 +693,7 @@ impl FragmentDisplayListBuilding for Fragment { layout_context, level, &absolute_fragment_bounds, - clip_rect); + &clip_rect); } } None => {} @@ -680,7 +707,7 @@ impl FragmentDisplayListBuilding for Fragment { layout_context, level, &absolute_fragment_bounds, - clip_rect); + &clip_rect); } } @@ -694,7 +721,7 @@ impl FragmentDisplayListBuilding for Fragment { layout_context, level, &absolute_fragment_bounds, - clip_rect); + &clip_rect); } } None => {} @@ -708,7 +735,7 @@ impl FragmentDisplayListBuilding for Fragment { layout_context, level, &absolute_fragment_bounds, - clip_rect); + &clip_rect); } } @@ -721,12 +748,12 @@ impl FragmentDisplayListBuilding for Fragment { display_list, &absolute_fragment_bounds, level, - clip_rect); + &clip_rect); self.build_display_list_for_outline_if_applicable( &**style, display_list, &absolute_fragment_bounds, - clip_rect); + &clip_rect); } } None => {} @@ -739,12 +766,12 @@ impl FragmentDisplayListBuilding for Fragment { display_list, &absolute_fragment_bounds, level, - clip_rect); + &clip_rect); self.build_display_list_for_outline_if_applicable( &*self.style, display_list, &absolute_fragment_bounds, - clip_rect); + &clip_rect); } } } @@ -781,7 +808,7 @@ impl FragmentDisplayListBuilding for Fragment { DisplayItemMetadata::new(self.node, self.style(), cursor), - *clip_rect), + clip_rect), text_run: text_fragment.run.clone(), range: text_fragment.range, text_color: self.style().get_color().color.to_gfx_color(), @@ -805,7 +832,7 @@ impl FragmentDisplayListBuilding for Fragment { DisplayItemMetadata::new(self.node, style, DefaultCursor), - *clip_rect), + clip_rect), color: color.to_gfx_color(), })) } @@ -840,7 +867,7 @@ impl FragmentDisplayListBuilding for Fragment { display_list, flow_origin, &**text_fragment, - clip_rect); + &clip_rect); } } SpecificFragmentInfo::Generic | SpecificFragmentInfo::Iframe(..) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | @@ -849,7 +876,7 @@ impl FragmentDisplayListBuilding for Fragment { if opts::get().show_debug_fragment_borders { self.build_debug_borders_around_fragment(display_list, flow_origin, - clip_rect); + &clip_rect); } } SpecificFragmentInfo::Image(ref mut image_fragment) => { @@ -864,7 +891,7 @@ impl FragmentDisplayListBuilding for Fragment { DisplayItemMetadata::new(self.node, &*self.style, DefaultCursor), - *clip_rect), + clip_rect), image: image.clone(), stretch_size: absolute_content_box.size, })); @@ -880,9 +907,7 @@ impl FragmentDisplayListBuilding for Fragment { } if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(display_list, - flow_origin, - clip_rect) + self.build_debug_borders_around_fragment(display_list, flow_origin, &clip_rect) } // If this is an iframe, then send its position and size up to the constellation. @@ -926,14 +951,17 @@ impl FragmentDisplayListBuilding for Fragment { iframe_rect)); } - fn clip_rect_for_children(&self, current_clip_rect: Rect, flow_origin: Point2D) + fn clip_rect_for_children(&self, current_clip_rect: &Rect, origin: &Point2D) -> Rect { // Don't clip if we're text. match self.specific { - SpecificFragmentInfo::ScannedText(_) => return current_clip_rect, + SpecificFragmentInfo::ScannedText(_) => return *current_clip_rect, _ => {} } + // Account for style-specified `clip`. + let current_clip_rect = self.calculate_style_specified_clip(current_clip_rect, origin); + // Only clip if `overflow` tells us to. match self.style.get_box().overflow { overflow::hidden | overflow::auto | overflow::scroll => {} @@ -944,7 +972,8 @@ impl FragmentDisplayListBuilding for Fragment { // // FIXME(#2795): Get the real container size. let physical_rect = self.border_box.to_physical(self.style.writing_mode, Size2D::zero()); - current_clip_rect.intersection(&Rect(physical_rect.origin + flow_origin, + current_clip_rect.intersection(&Rect(Point2D(physical_rect.origin.x + origin.x, + physical_rect.origin.y + origin.y), physical_rect.size)).unwrap_or(ZERO_RECT) } } diff --git a/components/layout/inline.rs b/components/layout/inline.rs index c3c531fcfdf..d8c113771f9 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -1215,8 +1215,8 @@ impl Flow for InlineFlow { _ => continue, }; - let clip_rect = fragment.clip_rect_for_children(self.base.clip_rect, - stacking_relative_position); + let clip_rect = fragment.clip_rect_for_children(&self.base.clip_rect, + &stacking_relative_position); match fragment.specific { SpecificFragmentInfo::InlineBlock(ref mut info) => { diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 48bfdb26775..27781ede26b 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -247,7 +247,7 @@ source = "git+https://github.com/alexcrichton/gcc-rs#903e8f8a2e3766ad3d514404d45 [[package]] name = "geom" version = "0.1.0" -source = "git+https://github.com/servo/rust-geom#5e52790076fc238a395d1777c4280fa46a1555fa" +source = "git+https://github.com/servo/rust-geom#da6b4a36a5549cf78bf702f0b9387b5c8cf61498" [[package]] name = "gfx" diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako index 8b7abd23ecf..c12181fc86c 100644 --- a/components/style/properties/mod.rs.mako +++ b/components/style/properties/mod.rs.mako @@ -1861,6 +1861,84 @@ pub mod longhands { }) } + + <%self:single_component_value name="clip"> + // NB: `top` and `left` are 0 if `auto` per CSS 2.1 11.1.2. + + pub mod computed_value { + use super::super::Au; + + #[deriving(Clone, PartialEq, Show)] + pub struct ClipRect { + pub top: Au, + pub right: Option, + pub bottom: Option, + pub left: Au, + } + + pub type T = Option; + } + + #[deriving(Clone, Show)] + pub struct SpecifiedClipRect { + pub top: specified::Length, + pub right: Option, + pub bottom: Option, + pub left: specified::Length, + } + + pub type SpecifiedValue = Option; + + #[inline] + pub fn get_initial_value() -> computed_value::T { + None + } + + pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) + -> computed_value::T { + value.map(|value| computed_value::ClipRect { + top: computed::compute_Au(value.top, context), + right: value.right.map(|right| computed::compute_Au(right, context)), + bottom: value.bottom.map(|bottom| computed::compute_Au(bottom, context)), + left: computed::compute_Au(value.left, context), + }) + } + + pub fn from_component_value(input: &ComponentValue, _: &Url) -> Result { + match *input { + Function(ref name, ref args) if name.as_slice().eq_ignore_ascii_case("rect") => { + let sides = try!(parse_slice_comma_separated(args.as_slice(), |parser| { + match parser.next() { + Some(&Ident(ref ident)) if ident.eq_ignore_ascii_case("auto") => { + Ok(None) + } + Some(arg) => { + match specified::Length::parse(arg) { + Err(_) => { + parser.push_back(arg); + Err(()) + } + Ok(value) => Ok(Some(value)), + } + } + None => Err(()), + } + })); + if sides.len() != 4 { + return Err(()) + } + Ok(Some(SpecifiedClipRect { + top: sides[0].unwrap_or(specified::Length::Au(Au(0))), + right: sides[1], + bottom: sides[2], + left: sides[3].unwrap_or(specified::Length::Au(Au(0))), + })) + } + Ident(ref ident) if ident.as_slice().eq_ignore_ascii_case("auto") => Ok(None), + _ => Err(()) + } + } + } diff --git a/components/util/geometry.rs b/components/util/geometry.rs index 2dc9fe61b4c..1e28a3a56f4 100644 --- a/components/util/geometry.rs +++ b/components/util/geometry.rs @@ -333,3 +333,4 @@ pub fn f32_rect_to_au_rect(rect: Rect) -> Rect { Rect(Point2D(Au::from_frac32_px(rect.origin.x), Au::from_frac32_px(rect.origin.y)), Size2D(Au::from_frac32_px(rect.size.width), Au::from_frac32_px(rect.size.height))) } + diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index cf65e21778c..e569fba7e8d 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -243,7 +243,7 @@ source = "git+https://github.com/alexcrichton/gcc-rs#903e8f8a2e3766ad3d514404d45 [[package]] name = "geom" version = "0.1.0" -source = "git+https://github.com/servo/rust-geom#5e52790076fc238a395d1777c4280fa46a1555fa" +source = "git+https://github.com/servo/rust-geom#da6b4a36a5549cf78bf702f0b9387b5c8cf61498" [[package]] name = "gfx" diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 637efe32449..fa5559a143b 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -207,7 +207,7 @@ source = "git+https://github.com/alexcrichton/gcc-rs#d35c34c871dd75f97fadf04cb0e [[package]] name = "geom" version = "0.1.0" -source = "git+https://github.com/servo/rust-geom#5e52790076fc238a395d1777c4280fa46a1555fa" +source = "git+https://github.com/servo/rust-geom#da6b4a36a5549cf78bf702f0b9387b5c8cf61498" [[package]] name = "gfx" diff --git a/tests/ref/basic.list b/tests/ref/basic.list index b316ddaa3fe..3e8abb9e30f 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -218,3 +218,4 @@ fragment=top != ../html/acid2.html acid2_ref.html == empty_cells_a.html empty_cells_ref.html == table_caption_top_a.html table_caption_top_ref.html == table_caption_bottom_a.html table_caption_bottom_ref.html +== clip_a.html clip_ref.html diff --git a/tests/ref/clip_a.html b/tests/ref/clip_a.html new file mode 100644 index 00000000000..249e586105d --- /dev/null +++ b/tests/ref/clip_a.html @@ -0,0 +1,52 @@ + + + + + + + +
+
+
+
+ + + + diff --git a/tests/ref/clip_ref.html b/tests/ref/clip_ref.html new file mode 100644 index 00000000000..61db5ae8130 --- /dev/null +++ b/tests/ref/clip_ref.html @@ -0,0 +1,70 @@ + + + + + + + +
+
+
+
+
+
+
+ + +