servo: Merge #15813 - Implement parsing/serialization and glue for will-change property (from canaltinova:will-change); r=upsuper,emilio
authorNazım Can Altınova <canaltinova@gmail.com>
Tue, 21 Mar 2017 21:56:23 -0700
changeset 348751 e9c5e7c44f857855f13a259cfe7db2322d7e89db
parent 348750 5bd473699d777438dae3c43892732f83ca3a7163
child 348752 68e06a8f32fbe464f7d255017c35b5dd7dfa77d6
push id31535
push usercbook@mozilla.com
push dateWed, 22 Mar 2017 13:25:07 +0000
treeherdermozilla-central@ee30286771eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersupsuper, emilio
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #15813 - Implement parsing/serialization and glue for will-change property (from canaltinova:will-change); r=upsuper,emilio Implement parsing/serialization and glue for will-change property --- <!-- 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 #15706 (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes <!-- 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: cb1438e44f0550378867a308494e4f84b1e00478
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/properties/data.py
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/properties/longhand/svg.mako.rs
servo/components/style/properties/properties.mako.rs
servo/tests/unit/style/parsing/box_.rs
servo/tests/unit/style/parsing/mod.rs
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -777,16 +777,27 @@ extern "C" {
                                                  len: usize);
 }
 extern "C" {
     pub fn Gecko_EnsureStyleTransitionArrayLength(array:
                                                       *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,
                                          offset: f32,
                                          timingFunction:
                                              *const nsTimingFunction)
      -> *mut Keyframe;
 }
 extern "C" {
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -91,17 +91,18 @@ def arg_to_bool(arg):
     return arg == "True"
 
 
 class Longhand(object):
     def __init__(self, style_struct, name, spec=None, animatable=None, derived_from=None, keyword=None,
                  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)
         self.spec = spec
         self.keyword = keyword
         self.predefined_type = predefined_type
         self.ident = to_rust_ident(name)
         self.camel_case = to_camel_case(self.ident)
@@ -115,16 +116,19 @@ class Longhand(object):
         self.depend_on_viewport_size = depend_on_viewport_size
         self.derived_from = (derived_from or "").split()
         self.complex_color = complex_color
         self.cast_type = cast_type
         self.logical = arg_to_bool(logical)
         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 <declaration-list> inside of <keyframe-block> accepts any CSS property
         # > except those defined in this specification,
         # > but does accept the `animation-play-state` property and interprets it specially.
         self.allowed_in_keyframe_block = allowed_in_keyframe_block \
             and allowed_in_keyframe_block != "False"
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1418,17 +1418,17 @@ fn static_assert() {
                           animation-name animation-delay animation-duration
                           animation-direction animation-fill-mode animation-play-state
                           animation-iteration-count animation-timing-function
                           transition-duration transition-delay
                           transition-timing-function transition-property
                           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
     // infrastructure for preffing certain values.
     <% display_keyword = Keyword("display", "inline block inline-block table inline-table table-row-group " +
                                             "table-header-group table-footer-group table-row table-column-group " +
                                             "table-column table-cell table-caption list-item flex none " +
                                             "inline-flex grid inline-grid ruby ruby-base ruby-base-container " +
@@ -1913,16 +1913,102 @@ fn static_assert() {
             horizontal: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mTransformOrigin[0])
                 .expect("clone for LengthOrPercentage failed"),
             vertical: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mTransformOrigin[1])
                 .expect("clone for LengthOrPercentage failed"),
             depth: Au::from_gecko_style_coord(&self.gecko.mTransformOrigin[2])
                 .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 _);
+        }
+    }
 </%self:impl_trait>
 
 <%def name="simple_image_array_property(name, shorthand, field_name)">
     <%
         image_layers_field = "mImage" if shorthand == "background" else "mMask"
     %>
     pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) {
         use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -103,16 +103,18 @@
                          products="gecko", animatable=False, internal=True,
                          spec="Internal (not web-exposed)")}
 
 <%helpers:single_keyword_computed name="position"
                                   values="static absolute relative fixed"
                                   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 {
             matches!(*self, SpecifiedValue::absolute | SpecifiedValue::fixed)
         }
     }
 
     use values::HasViewportPercentage;
@@ -1111,16 +1113,18 @@
                           animatable=True,
                           allow_empty=True,
                           delegate_animate=True)}
 
 
 
 <%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;
     use values::CSSFloat;
     use values::HasViewportPercentage;
 
     use std::fmt;
 
@@ -1667,16 +1671,17 @@
 </%helpers:longhand>
 
 // Compositing and Blending Level 1
 // http://www.w3.org/TR/compositing-1/
 ${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
 ${helpers.single_keyword("page-break-after",
                          "auto always avoid left right",
                          products="gecko",
                          spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after",
                          animatable=False)}
@@ -1705,16 +1710,18 @@
 
 ${helpers.predefined_type("perspective",
                           "LengthOrNone",
                           "Either::Second(None_)",
                           gecko_ffi_name="mChildPerspective",
                           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
 <%helpers:longhand name="perspective-origin" boxed="True" animatable="False" extra_prefixes="moz webkit"
                    spec="https://drafts.csswg.org/css-transforms/#perspective-origin-property">
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
@@ -1814,16 +1821,18 @@
                          animatable=False)}
 
 // `auto` keyword is not supported in gecko yet.
 ${helpers.single_keyword("transform-style",
                          "auto flat preserve-3d" if product == "servo" else
                          "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"
                    spec="https://drafts.csswg.org/css-transforms/#transform-origin-property">
     use app_units::Au;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
@@ -1968,8 +1977,72 @@
 
 ${helpers.single_keyword("-moz-orient",
                           "inline block horizontal vertical",
                           products="gecko",
                           gecko_ffi_name="mOrient",
                           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<Atom>),
+    }
+
+    impl ToCss for SpecifiedValue {
+        fn to_css<W>(&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 | <animateable-feature>#
+    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        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)
+        }
+    }
+</%helpers:longhand>
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -6,16 +6,17 @@
 
 // Box-shadow, etc.
 <% data.new_style_struct("Effects", inherited=False) %>
 
 ${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"
                           animatable="True" extra_prefixes="webkit"
                           spec="https://drafts.csswg.org/css-backgrounds/#box-shadow">
     use cssparser;
     use std::fmt;
     use style_traits::ToCss;
@@ -81,16 +82,18 @@
                           "ClipRectOrAuto",
                           "computed::ClipRectOrAuto::auto()",
                           animatable=False,
                           boxed="True",
                           spec="https://drafts.fxtf.org/css-masking/#clip-property")}
 
 // 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;
     use std::fmt;
     use style_traits::{self, ToCss};
     use values::{CSSFloat, HasViewportPercentage};
     use values::specified::{Angle, CSSColor, Length, Shadow};
     use values::specified::url::SpecifiedUrl;
@@ -511,9 +514,10 @@ pub fn parse_origin(context: &ParserCont
     }
 }
 
 ${helpers.single_keyword("mix-blend-mode",
                          """normal multiply screen overlay darken lighten color-dodge
                             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")}
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -21,16 +21,17 @@
                               "computed::LengthOrPercentageOrAuto::Auto",
                               spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side,
                               animatable=True, logical=True)}
 % endfor
 
 ${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
 // http://www.w3.org/TR/css3-flexbox/
 
 // Flex container properties
 ${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
--- a/servo/components/style/properties/longhand/svg.mako.rs
+++ b/servo/components/style/properties/longhand/svg.mako.rs
@@ -54,16 +54,17 @@
 
 // CSS Masking Module Level 1
 // https://drafts.fxtf.org/css-masking
 ${helpers.single_keyword("mask-type", "luminance alpha",
                          products="gecko", animatable=False,
                          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;
     use values::HasViewportPercentage;
     use values::specified::basic_shape::{ShapeSource, GeometryBox};
 
     pub mod computed_value {
         use app_units::Au;
@@ -184,17 +185,18 @@
                          "add subtract intersect exclude",
                          vector=True,
                          products="gecko",
                          extra_prefixes="webkit",
                          animatable=False,
                          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;
     use std::sync::Arc;
     use values::specified::Image;
     use values::specified::url::SpecifiedUrl;
     use values::HasViewportPercentage;
 
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -415,16 +415,30 @@ impl Parse for CSSWideKeyword {
             "initial" => Ok(CSSWideKeyword::Initial),
             "inherit" => Ok(CSSWideKeyword::Inherit),
             "unset" => Ok(CSSWideKeyword::Unset),
             _ => Err(())
         }
     }
 }
 
+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))]
 pub enum LonghandId {
     % for i, property in enumerate(data.longhands):
         /// ${property.name}
         ${property.camel_case} = ${i},
     % endfor
@@ -453,16 +467,35 @@ impl LonghandId {
                             </%def>
                         </%helpers:logical_setter_helper>
                     }
                 % endif
             % endfor
             _ => *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.
 #[derive(Clone, Copy, Eq, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum ShorthandId {
     % for property in data.shorthands:
         /// ${property.name}
new file mode 100644
--- /dev/null
+++ b/servo/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());
+}
--- a/servo/tests/unit/style/parsing/mod.rs
+++ b/servo/tests/unit/style/parsing/mod.rs
@@ -80,16 +80,17 @@ macro_rules! parse_longhand {
         $name::parse(&context, &mut Parser::new($s)).unwrap()
     }};
 }
 
 mod animation;
 mod background;
 mod basic_shape;
 mod border;
+mod box_;
 mod column;
 mod effects;
 mod font;
 mod image;
 mod inherited_box;
 mod inherited_text;
 mod length;
 mod mask;