From a82fc00806edf63672a660827117c743ef03c63f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 7 Jan 2015 16:05:10 -0800 Subject: [PATCH] =?UTF-8?q?layout:=20Implement=20`overflow-x`=20and=20`ove?= =?UTF-8?q?rflow-y`=20per=20CSS-OVERFLOW=20=C2=A7=203.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fragmentation is not yet supported. --- components/layout/block.rs | 10 +- components/layout/display_list_builder.rs | 39 ++++++-- components/layout/inline.rs | 6 +- .../dom/webidls/CSSStyleDeclaration.webidl | 2 + components/style/properties.mako.rs | 91 ++++++++++++++++++- components/style/values.rs | 2 + tests/ref/basic.list | 1 + tests/ref/overflow_xy_a.html | 36 ++++++++ tests/ref/overflow_xy_ref.html | 29 ++++++ 9 files changed, 199 insertions(+), 17 deletions(-) create mode 100644 tests/ref/overflow_xy_a.html create mode 100644 tests/ref/overflow_xy_ref.html diff --git a/components/layout/block.rs b/components/layout/block.rs index e26fb5f8b8e..822ec6bfb82 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -57,9 +57,10 @@ use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; use std::cmp::{max, min}; use std::fmt; +use style::computed_values::{overflow_x, overflow_y, position, box_sizing, display, float}; use style::properties::ComputedValues; -use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; -use style::computed_values::{overflow, position, box_sizing, display, float}; +use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; +use style::values::computed::{LengthOrPercentageOrNone}; use std::sync::Arc; /// Information specific to floated blocks. @@ -1431,7 +1432,10 @@ impl BlockFlow { display::T::inline_block => { FormattingContextType::Other } - _ if style.get_box().overflow != overflow::T::visible => FormattingContextType::Block, + _ if style.get_box().overflow_x != overflow_x::T::visible || + style.get_box().overflow_y != overflow_y::T(overflow_x::T::visible) => { + FormattingContextType::Block + } _ => FormattingContextType::None, } } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 039dbc9c376..e20ade0ce19 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -41,6 +41,7 @@ use servo_util::cursor::Cursor; use servo_util::geometry::{self, Au, ZERO_POINT, to_px, to_frac_px}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; +use std::cmp; use std::default::Default; use std::iter::repeat; use std::num::Float; @@ -48,7 +49,7 @@ use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirec use style::values::computed::{Image, LinearGradient, LengthOrPercentage}; use style::values::RGBA; use style::computed_values::filter::Filter; -use style::computed_values::{background_attachment, background_repeat, border_style, overflow}; +use style::computed_values::{background_attachment, background_repeat, border_style, overflow_x}; use style::computed_values::{position, visibility}; use style::properties::style_structs::Border; use style::properties::ComputedValues; @@ -990,17 +991,37 @@ impl FragmentDisplayListBuilding for Fragment { } // Account for style-specified `clip`. - let current_clip = self.calculate_style_specified_clip(current_clip, - stacking_relative_border_box); + let mut current_clip = self.calculate_style_specified_clip(current_clip, + stacking_relative_border_box); - // Only clip if `overflow` tells us to. - match self.style.get_box().overflow { - overflow::T::hidden | overflow::T::auto | overflow::T::scroll => { - // Create a new clip rect. - current_clip.intersect_rect(stacking_relative_border_box) + // Clip according to the values of `overflow-x` and `overflow-y`. + // + // TODO(pcwalton): Support scrolling. + // 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: ` or vice versa! + match self.style.get_box().overflow_x { + overflow_x::T::hidden | overflow_x::T::auto | overflow_x::T::scroll => { + let mut bounds = current_clip.bounding_rect(); + let max_x = cmp::min(bounds.max_x(), stacking_relative_border_box.max_x()); + bounds.origin.x = cmp::max(bounds.origin.x, stacking_relative_border_box.origin.x); + bounds.size.width = max_x - bounds.origin.x; + current_clip = current_clip.intersect_rect(&bounds) } - _ => current_clip, + _ => {} } + match self.style.get_box().overflow_y.0 { + overflow_x::T::hidden | overflow_x::T::auto | overflow_x::T::scroll => { + let mut bounds = current_clip.bounding_rect(); + let max_y = cmp::min(bounds.max_y(), stacking_relative_border_box.max_y()); + bounds.origin.y = cmp::max(bounds.origin.y, stacking_relative_border_box.origin.y); + bounds.size.height = max_y - bounds.origin.y; + current_clip = current_clip.intersect_rect(&bounds) + } + _ => {} + } + + current_clip } fn build_display_list_for_text_fragment(&self, diff --git a/components/layout/inline.rs b/components/layout/inline.rs index a5ca5038c30..0a4b575c3c4 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -34,7 +34,7 @@ use std::mem; use std::num::ToPrimitive; use std::ops::{Add, Sub, Mul, Div, Rem, Neg, Shl, Shr, Not, BitOr, BitAnd, BitXor}; use std::u16; -use style::computed_values::{overflow, text_align, text_justify, text_overflow, vertical_align}; +use style::computed_values::{overflow_x, text_align, text_justify, text_overflow, vertical_align}; use style::computed_values::{white_space}; use style::properties::ComputedValues; use std::sync::Arc; @@ -653,8 +653,8 @@ impl LineBreaker { let available_inline_size = self.pending_line.green_zone.inline - self.pending_line.bounds.size.inline - indentation; match (fragment.style().get_inheritedtext().text_overflow, - fragment.style().get_box().overflow) { - (text_overflow::T::clip, _) | (_, overflow::T::visible) => {} + fragment.style().get_box().overflow_x) { + (text_overflow::T::clip, _) | (_, overflow_x::T::visible) => {} (text_overflow::T::ellipsis, _) => { need_ellipsis = fragment.border_box.size.inline > available_inline_size; } diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 1d642acf17c..d6a4420b2a9 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -103,6 +103,8 @@ partial interface CSSStyleDeclaration { [TreatNullAs=EmptyString] attribute DOMString listStyleImage; [TreatNullAs=EmptyString] attribute DOMString overflow; + [TreatNullAs=EmptyString] attribute DOMString overflowX; + [TreatNullAs=EmptyString] attribute DOMString overflowY; [TreatNullAs=EmptyString] attribute DOMString overflowWrap; [TreatNullAs=EmptyString] attribute DOMString tableLayout; diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 2ff84b2107c..83d6570a800 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -676,8 +676,77 @@ pub mod longhands { // CSS 2.1, Section 11 - Visual effects - // FIXME: Implement scrolling for `scroll` and `auto` (#2742). - ${single_keyword("overflow", "visible hidden scroll auto")} + + // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. + <%self:single_keyword_computed name="overflow-x" values="visible hidden scroll auto"> + use values::computed::{Context, ToComputedValue}; + + pub fn compute_with_other_overflow_direction(value: SpecifiedValue, + other_direction: SpecifiedValue) + -> computed_value::T { + // CSS-OVERFLOW 3 states "Otherwise, if one cascaded values is one of the scrolling + // values and the other is `visible`, then computed values are the cascaded values with + // `visible` changed to `auto`." + match (value, other_direction) { + (SpecifiedValue::visible, SpecifiedValue::hidden) | + (SpecifiedValue::visible, SpecifiedValue::scroll) | + (SpecifiedValue::visible, SpecifiedValue::auto) => computed_value::T::auto, + _ => value, + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + compute_with_other_overflow_direction(*self, context.overflow_y.0) + } + } + + + // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. + <%self:longhand name="overflow-y"> + use super::overflow_x; + use values::computed::{Context, ToComputedValue}; + + use cssparser::ToCss; + use text_writer::{self, TextWriter}; + + pub use self::computed_value::T as SpecifiedValue; + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + self.0.to_css(dest) + } + } + + pub mod computed_value { + #[derive(Clone, Copy, PartialEq)] + pub struct T(pub super::super::overflow_x::computed_value::T); + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + let computed_value::T(this) = *self; + computed_value::T(overflow_x::compute_with_other_overflow_direction( + this, + context.overflow_x)) + } + } + + pub fn get_initial_value() -> computed_value::T { + computed_value::T(overflow_x::get_initial_value()) + } + + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { + overflow_x::parse(context, input).map(|value| SpecifiedValue(value)) + } + + ${switch_to_style_struct("InheritedBox")} @@ -2830,6 +2899,16 @@ pub mod shorthands { _ => Err(()), } + + <%self:shorthand name="overflow" sub_properties="overflow-x overflow-y"> + use properties::longhands::{overflow_x, overflow_y}; + + let overflow = try!(overflow_x::parse(context, input)); + Ok(Longhands { + overflow_x: Some(overflow), + overflow_y: Some(overflow_y::SpecifiedValue(overflow)), + }) + } @@ -3474,6 +3553,8 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock false, } } + PropertyDeclaration::OverflowX(ref value) => { + context.overflow_x = get_specified!(get_box, overflow_x, value); + } + PropertyDeclaration::OverflowY(ref value) => { + context.overflow_y = get_specified!(get_box, overflow_y, value); + } PropertyDeclaration::Float(ref value) => { context.floated = get_specified!(get_box, float, value) != longhands::float::SpecifiedValue::none; diff --git a/components/style/values.rs b/components/style/values.rs index 450e82cfa61..ae1e4bc2600 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -690,6 +690,8 @@ pub mod computed { pub font_size: longhands::font_size::computed_value::T, pub root_font_size: longhands::font_size::computed_value::T, pub display: longhands::display::computed_value::T, + pub overflow_x: longhands::overflow_x::computed_value::T, + pub overflow_y: longhands::overflow_y::computed_value::T, pub positioned: bool, pub floated: bool, pub border_top_present: bool, diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 0d83cc16d1f..2361eb2e7df 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -255,6 +255,7 @@ fragment=top != ../html/acid2.html acid2_ref.html == canvas_lineto_a.html canvas_lineto_ref.html != text_decoration_smoke_a.html text_decoration_smoke_ref.html == hide_after_create.html hide_after_create_ref.html +== overflow_xy_a.html overflow_xy_ref.html == text_shadow_simple_a.html text_shadow_simple_ref.html == text_shadow_decorations_a.html text_shadow_decorations_ref.html == text_shadow_blur_a.html text_shadow_blur_ref.html diff --git a/tests/ref/overflow_xy_a.html b/tests/ref/overflow_xy_a.html new file mode 100644 index 00000000000..eee0c79eec8 --- /dev/null +++ b/tests/ref/overflow_xy_a.html @@ -0,0 +1,36 @@ + + + + + + + +
+
+ + + diff --git a/tests/ref/overflow_xy_ref.html b/tests/ref/overflow_xy_ref.html new file mode 100644 index 00000000000..777f18bbebe --- /dev/null +++ b/tests/ref/overflow_xy_ref.html @@ -0,0 +1,29 @@ + + + + + + + +
+
+ + + +