diff --git a/components/style/gecko_bindings/bindings.rs b/components/style/gecko_bindings/bindings.rs index 7c2ccb6450c..4a4b3143f6a 100644 --- a/components/style/gecko_bindings/bindings.rs +++ b/components/style/gecko_bindings/bindings.rs @@ -781,6 +781,17 @@ extern "C" { *mut ::std::os::raw::c_void, len: usize); } +extern "C" { + pub fn Gecko_ClearWillChange(display: *mut nsStyleDisplay, length: usize); +} +extern "C" { + pub fn Gecko_AppendWillChange(display: *mut nsStyleDisplay, + atom: *mut nsIAtom); +} +extern "C" { + pub fn Gecko_CopyWillChangeFrom(dest: *mut nsStyleDisplay, + src: *mut nsStyleDisplay); +} extern "C" { pub fn Gecko_AnimationAppendKeyframe(keyframes: RawGeckoKeyframeListBorrowedMut, diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 57cf0eaee21..f134a9765a8 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -96,7 +96,8 @@ class Longhand(object): predefined_type=None, custom_cascade=False, experimental=False, internal=False, need_clone=False, need_index=False, gecko_ffi_name=None, depend_on_viewport_size=False, allowed_in_keyframe_block=True, complex_color=False, cast_type='u8', - has_uncacheable_values=False, logical=False, alias=None, extra_prefixes=None, boxed=False): + has_uncacheable_values=False, logical=False, alias=None, extra_prefixes=None, boxed=False, + creates_stacking_context=False, fixpos_cb=False, abspos_cb=False): self.name = name if not spec: raise TypeError("Spec should be specified for %s" % name) @@ -120,6 +121,9 @@ class Longhand(object): self.alias = alias.split() if alias else [] self.extra_prefixes = extra_prefixes.split() if extra_prefixes else [] self.boxed = arg_to_bool(boxed) + self.creates_stacking_context = arg_to_bool(creates_stacking_context) + self.fixpos_cb = arg_to_bool(fixpos_cb) + self.abspos_cb = arg_to_bool(abspos_cb) # https://drafts.csswg.org/css-animations/#keyframes # > The inside of accepts any CSS property diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 35ef4a1f004..3c84ea6e402 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -1423,7 +1423,7 @@ fn static_assert() { page-break-before page-break-after scroll-snap-points-x scroll-snap-points-y transform scroll-snap-type-y scroll-snap-coordinate - perspective-origin transform-origin -moz-binding""" %> + perspective-origin transform-origin -moz-binding will-change""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> // We manually-implement the |display| property until we get general @@ -1918,6 +1918,92 @@ fn static_assert() { .expect("clone for Length failed"), } } + + pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) { + use gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange}; + use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_OPACITY; + use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_SCROLL; + use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_TRANSFORM; + use properties::PropertyId; + use properties::longhands::will_change::computed_value::T; + + fn will_change_bitfield_from_prop_flags(prop: &LonghandId) -> u8 { + use properties::{ABSPOS_CB, CREATES_STACKING_CONTEXT, FIXPOS_CB}; + use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_ABSPOS_CB; + use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_FIXPOS_CB; + use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; + let servo_flags = prop.flags(); + let mut bitfield = 0; + + if servo_flags.contains(CREATES_STACKING_CONTEXT) { + bitfield |= NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; + } + if servo_flags.contains(FIXPOS_CB) { + bitfield |= NS_STYLE_WILL_CHANGE_FIXPOS_CB; + } + if servo_flags.contains(ABSPOS_CB) { + bitfield |= NS_STYLE_WILL_CHANGE_ABSPOS_CB; + } + + bitfield as u8 + } + + self.gecko.mWillChangeBitField = 0; + + match v { + T::AnimateableFeatures(features) => { + unsafe { + Gecko_ClearWillChange(&mut self.gecko, features.len()); + } + + for feature in features.iter() { + if feature == &atom!("scroll-position") { + self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_SCROLL as u8; + } else if feature == &atom!("opacity") { + self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_OPACITY as u8; + } else if feature == &atom!("transform") { + self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_TRANSFORM as u8; + } + + unsafe { + Gecko_AppendWillChange(&mut self.gecko, feature.as_ptr()); + } + + if let Ok(prop_id) = PropertyId::parse(feature.to_string().into()) { + match prop_id.as_shorthand() { + Ok(shorthand) => { + for longhand in shorthand.longhands() { + self.gecko.mWillChangeBitField |= + will_change_bitfield_from_prop_flags(longhand); + } + }, + Err(longhand_or_custom) => { + if let PropertyDeclarationId::Longhand(longhand) + = longhand_or_custom { + self.gecko.mWillChangeBitField |= + will_change_bitfield_from_prop_flags(&longhand); + } + }, + } + } + } + }, + T::Auto => { + unsafe { + Gecko_ClearWillChange(&mut self.gecko, 0); + } + }, + }; + } + + pub fn copy_will_change_from(&mut self, other: &Self) { + use gecko_bindings::bindings::Gecko_CopyWillChangeFrom; + + self.gecko.mWillChangeBitField = other.gecko.mWillChangeBitField; + unsafe { + Gecko_CopyWillChangeFrom(&mut self.gecko, &other.gecko as *const _ as *mut _); + } + } <%def name="simple_image_array_property(name, shorthand, field_name)"> diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index 26aab0498b8..17e95115a5c 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -108,6 +108,8 @@ ${helpers.single_keyword("-moz-top-layer", "none top", need_clone="True" extra_gecko_values="sticky" animatable="False" + creates_stacking_context="True" + abspos_cb="True" spec="https://drafts.csswg.org/css-position/#position-property"> impl SpecifiedValue { pub fn is_absolutely_positioned_style(&self) -> bool { @@ -1116,6 +1118,8 @@ ${helpers.predefined_type("scroll-snap-coordinate", <%helpers:longhand name="transform" products="gecko servo" extra_prefixes="webkit" animatable="True" + creates_stacking_context="True" + fixpos_cb="True" spec="https://drafts.csswg.org/css-transforms/#propdef-transform"> use app_units::Au; use style_traits::ToCss; @@ -1672,6 +1676,7 @@ ${helpers.single_keyword("isolation", "auto isolate", products="gecko", spec="https://drafts.fxtf.org/compositing/#isolation", + creates_stacking_context=True, animatable=False)} // TODO add support for logical values recto and verso @@ -1710,6 +1715,8 @@ ${helpers.predefined_type("perspective", spec="https://drafts.csswg.org/css-transforms/#perspective", extra_prefixes="moz webkit", boxed=True, + creates_stacking_context=True, + fixpos_cb=True, animatable=True)} // FIXME: This prop should be animatable @@ -1819,6 +1826,8 @@ ${helpers.single_keyword("transform-style", "flat preserve-3d", spec="https://drafts.csswg.org/css-transforms/#transform-style-property", extra_prefixes="moz webkit", + creates_stacking_context=True, + fixpos_cb=True, animatable=False)} <%helpers:longhand name="transform-origin" animatable="True" extra_prefixes="moz webkit" boxed="True" @@ -1973,3 +1982,67 @@ ${helpers.single_keyword("-moz-orient", gecko_enum_prefix="StyleOrient", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-orient)", animatable=False)} + +<%helpers:longhand name="will-change" products="gecko" animatable="False" + spec="https://drafts.csswg.org/css-will-change/#will-change"> + use cssparser::serialize_identifier; + use std::fmt; + use style_traits::ToCss; + use values::HasViewportPercentage; + use values::computed::ComputedValueAsSpecified; + + impl ComputedValueAsSpecified for SpecifiedValue {} + no_viewport_percentage!(SpecifiedValue); + + pub mod computed_value { + pub use super::SpecifiedValue as T; + } + + #[derive(Debug, Clone, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub enum SpecifiedValue { + Auto, + AnimateableFeatures(Vec), + } + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + SpecifiedValue::Auto => dest.write_str("auto"), + SpecifiedValue::AnimateableFeatures(ref features) => { + let (first, rest) = features.split_first().unwrap(); + // handle head element + serialize_identifier(&*first.to_string(), dest)?; + // handle tail, precede each with a delimiter + for feature in rest { + dest.write_str(", ")?; + serialize_identifier(&*feature.to_string(), dest)?; + } + Ok(()) + } + } + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T::Auto + } + + /// auto | # + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result { + if input.try(|input| input.expect_ident_matching("auto")).is_ok() { + Ok(computed_value::T::Auto) + } else { + input.parse_comma_separated(|i| { + let ident = i.expect_ident()?; + match_ignore_ascii_case! { &ident, + "will-change" | "none" | "all" | "auto" | + "initial" | "inherit" | "unset" | "default" => return Err(()), + _ => {}, + } + Ok((Atom::from(ident))) + }).map(SpecifiedValue::AnimateableFeatures) + } + } + diff --git a/components/style/properties/longhand/effects.mako.rs b/components/style/properties/longhand/effects.mako.rs index dd147481b1d..a25b92267a9 100644 --- a/components/style/properties/longhand/effects.mako.rs +++ b/components/style/properties/longhand/effects.mako.rs @@ -11,6 +11,7 @@ ${helpers.predefined_type("opacity", "Opacity", "1.0", animatable=True, + creates_stacking_context=True, spec="https://drafts.csswg.org/css-color/#opacity")} <%helpers:vector_longhand name="box-shadow" allow_empty="True" @@ -86,6 +87,8 @@ ${helpers.predefined_type("clip", // FIXME: This prop should be animatable <%helpers:longhand name="filter" animatable="False" extra_prefixes="webkit" + creates_stacking_context="True" + fixpos_cb="True" spec="https://drafts.fxtf.org/filters/#propdef-filter"> //pub use self::computed_value::T as SpecifiedValue; use cssparser; @@ -516,4 +519,5 @@ ${helpers.single_keyword("mix-blend-mode", color-burn hard-light soft-light difference exclusion hue saturation color luminosity""", gecko_constant_prefix="NS_STYLE_BLEND", animatable=False, + creates_stacking_context=True, spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode")} diff --git a/components/style/properties/longhand/position.mako.rs b/components/style/properties/longhand/position.mako.rs index 9c61a1dec90..602be097ec2 100644 --- a/components/style/properties/longhand/position.mako.rs +++ b/components/style/properties/longhand/position.mako.rs @@ -26,6 +26,7 @@ ${helpers.predefined_type("z-index", "IntegerOrAuto", "Either::Second(Auto)", spec="https://www.w3.org/TR/CSS2/visuren.html#z-index", + creates_stacking_context=True, animatable="True")} // CSS Flexible Box Layout Module Level 1 diff --git a/components/style/properties/longhand/svg.mako.rs b/components/style/properties/longhand/svg.mako.rs index fa6caff45e3..45da055559f 100644 --- a/components/style/properties/longhand/svg.mako.rs +++ b/components/style/properties/longhand/svg.mako.rs @@ -59,6 +59,7 @@ ${helpers.single_keyword("mask-type", "luminance alpha", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type")} <%helpers:longhand name="clip-path" animatable="False" products="gecko" boxed="True" + creates_stacking_context="True" spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path"> use std::fmt; use style_traits::ToCss; @@ -189,7 +190,8 @@ ${helpers.single_keyword("mask-composite", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite")} <%helpers:vector_longhand name="mask-image" products="gecko" animatable="False" extra_prefixes="webkit" - has_uncacheable_values="${product == 'gecko'}", + has_uncacheable_values="${product == 'gecko'}" + creates_stacking_context="True" spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image"> use std::fmt; use style_traits::ToCss; diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index b2a84529f1a..8fd7dc6d4ee 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -420,6 +420,20 @@ impl Parse for CSSWideKeyword { } } +bitflags! { + /// A set of flags for properties. + pub flags PropertyFlags: u8 { + /// This property requires a stacking context. + const CREATES_STACKING_CONTEXT = 0x01, + /// This property has values that can establish a containing block for + /// fixed positioned and absolutely positioned elements. + const FIXPOS_CB = 0x02, + /// This property has values that can establish a containing block for + /// absolutely positioned elements. + const ABSPOS_CB = 0x04, + } +} + /// An identifier for a given longhand property. #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -458,6 +472,25 @@ impl LonghandId { _ => *self } } + + /// Returns PropertyFlags for given property. + pub fn flags(&self) -> PropertyFlags { + match *self { + % for property in data.longhands: + LonghandId::${property.camel_case} => + %if property.creates_stacking_context: + CREATES_STACKING_CONTEXT | + %endif + %if property.fixpos_cb: + FIXPOS_CB | + %endif + %if property.abspos_cb: + ABSPOS_CB | + %endif + PropertyFlags::empty(), + % endfor + } + } } /// An identifier for a given shorthand property. diff --git a/tests/unit/style/parsing/box_.rs b/tests/unit/style/parsing/box_.rs new file mode 100644 index 00000000000..189e55d75a1 --- /dev/null +++ b/tests/unit/style/parsing/box_.rs @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use cssparser::Parser; +use media_queries::CSSErrorReporterTest; +use parsing::parse; +use style::parser::ParserContext; +use style::stylesheets::Origin; +use style_traits::ToCss; + +#[test] +fn test_will_change() { + use style::properties::longhands::will_change; + + assert_roundtrip_with_context!(will_change::parse, "auto"); + assert_roundtrip_with_context!(will_change::parse, "scroll-position"); + assert_roundtrip_with_context!(will_change::parse, "contents"); + assert_roundtrip_with_context!(will_change::parse, "transition"); + assert_roundtrip_with_context!(will_change::parse, "opacity, transform"); + + assert!(parse(will_change::parse, "will-change").is_err()); + assert!(parse(will_change::parse, "all").is_err()); + assert!(parse(will_change::parse, "none").is_err()); + assert!(parse(will_change::parse, "contents, auto").is_err()); + assert!(parse(will_change::parse, "contents, inherit, initial").is_err()); + assert!(parse(will_change::parse, "transform scroll-position").is_err()); +} diff --git a/tests/unit/style/parsing/mod.rs b/tests/unit/style/parsing/mod.rs index e4a9ce985a3..cfa7582eeda 100644 --- a/tests/unit/style/parsing/mod.rs +++ b/tests/unit/style/parsing/mod.rs @@ -85,6 +85,7 @@ mod animation; mod background; mod basic_shape; mod border; +mod box_; mod column; mod effects; mod font;