servo: Merge #17788 - style: Cleanup the cascade a good bit (from emilio:clean-cascade); r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 20 Jul 2017 02:36:15 -0700
changeset 418553 bb2f333bca75af2a0966259e944bb3aa30a7ef97
parent 418552 058760087ee4ca8518872ce89bcd1d4f52c379ad
child 418554 26a9c5a88cc3f204fcc94e71bb4a1fca02e3207a
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
milestone56.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 #17788 - style: Cleanup the cascade a good bit (from emilio:clean-cascade); r=heycam Was about the time. Source-Repo: https://github.com/servo/servo Source-Revision: f594ae58a68479af958989aae369a2bfee2b2246
servo/components/style/gecko/conversions.rs
servo/components/style/gecko/media_queries.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/color.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_box.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/text.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/servo/media_queries.rs
servo/components/style/style_adjuster.rs
servo/components/style/stylesheets/viewport_rule.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/color.rs
servo/components/style/values/specified/length.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/parsing/mod.rs
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -133,48 +133,31 @@ impl Angle {
             nsCSSUnit::eCSSUnit_Turn => Angle::Turn(value),
             _ => panic!("Unexpected unit {:?} for angle", unit),
         }
     }
 }
 
 impl nsStyleImage {
     /// Set a given Servo `Image` value into this `nsStyleImage`.
-    pub fn set(&mut self, image: Image, cacheable: &mut bool) {
+    pub fn set(&mut self, image: Image) {
         match image {
             GenericImage::Gradient(gradient) => {
                 self.set_gradient(gradient)
             },
             GenericImage::Url(ref url) => {
                 unsafe {
                     Gecko_SetLayerImageImageValue(self, url.image_value.clone().unwrap().get());
-                    // We unfortunately must make any url() value uncacheable, since
-                    // the applicable declarations cache is not per document, but
-                    // global, and the imgRequestProxy objects we store in the style
-                    // structs don't like to be tracked by more than one document.
-                    //
-                    // FIXME(emilio): With the scoped TLS thing this is no longer
-                    // true, remove this line in a follow-up!
-                    *cacheable = false;
                 }
             },
             GenericImage::Rect(ref image_rect) => {
                 unsafe {
                     Gecko_SetLayerImageImageValue(self, image_rect.url.image_value.clone().unwrap().get());
                     Gecko_InitializeImageCropRect(self);
 
-                    // We unfortunately must make any url() value uncacheable, since
-                    // the applicable declarations cache is not per document, but
-                    // global, and the imgRequestProxy objects we store in the style
-                    // structs don't like to be tracked by more than one document.
-                    //
-                    // FIXME(emilio): With the scoped TLS thing this is no longer
-                    // true, remove this line in a follow-up!
-                    *cacheable = false;
-
                     // Set CropRect
                     let ref mut rect = *self.mCropRect.mPtr;
                     image_rect.top.to_gecko_style_coord(&mut rect.data_at_mut(0));
                     image_rect.right.to_gecko_style_coord(&mut rect.data_at_mut(1));
                     image_rect.bottom.to_gecko_style_coord(&mut rect.data_at_mut(2));
                     image_rect.left.to_gecko_style_coord(&mut rect.data_at_mut(3));
                 }
             }
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -619,19 +619,17 @@ impl Expression {
 
 
         let provider = get_metrics_provider_for_product();
 
         // http://dev.w3.org/csswg/mediaqueries3/#units
         // em units are relative to the initial font-size.
         let context = computed::Context {
             is_root_element: false,
-            device: device,
-            inherited_style: default_values,
-            style: StyleBuilder::for_derived_style(device, default_values, None),
+            builder: StyleBuilder::for_derived_style(device, default_values, None, None),
             font_metrics_provider: &provider,
             cached_system_font: None,
             in_media_query: true,
             // TODO: pass the correct value here.
             quirks_mode: quirks_mode,
         };
 
         let required_value = match self.value {
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1184,17 +1184,17 @@ fn static_assert() {
 
     pub fn set_border_image_source(&mut self, image: longhands::border_image_source::computed_value::T) {
         unsafe {
             // Prevent leaking of the last elements we did set
             Gecko_SetNullImageValue(&mut self.gecko.mBorderImageSource);
         }
 
         if let Either::Second(image) = image {
-            self.gecko.mBorderImageSource.set(image, &mut false)
+            self.gecko.mBorderImageSource.set(image);
         }
     }
 
     pub fn copy_border_image_source_from(&mut self, other: &Self) {
         unsafe {
             Gecko_CopyImageValueFrom(&mut self.gecko.mBorderImageSource,
                                      &other.gecko.mBorderImageSource);
         }
@@ -3418,17 +3418,17 @@ fn static_assert() {
                                       .take(count as usize) {
                 Gecko_CopyImageValueFrom(&mut layer.mImage, &other.mImage);
             }
             self.gecko.${image_layers_field}.mImageCount = count;
         }
     }
 
     #[allow(unused_variables)]
-    pub fn set_${shorthand}_image<I>(&mut self, images: I, cacheable: &mut bool)
+    pub fn set_${shorthand}_image<I>(&mut self, images: I)
         where I: IntoIterator<Item = longhands::${shorthand}_image::computed_value::single_value::T>,
               I::IntoIter: ExactSizeIterator
     {
         use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
 
         let images = images.into_iter();
 
         unsafe {
@@ -3441,17 +3441,17 @@ fn static_assert() {
                                           LayerType::${shorthand.title()});
         }
 
         self.gecko.${image_layers_field}.mImageCount = images.len() as u32;
 
         for (image, geckoimage) in images.zip(self.gecko.${image_layers_field}
                                                   .mLayers.iter_mut()) {
             if let Either::Second(image) = image {
-                geckoimage.mImage.set(image, cacheable)
+                geckoimage.mImage.set(image)
             }
         }
     }
 
     pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T {
         use values::None_;
 
         longhands::${shorthand}_image::computed_value::T(
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -258,16 +258,17 @@
 
         impl ToComputedValue for SpecifiedValue {
             type ComputedValue = computed_value::T;
 
             #[inline]
             fn to_computed_value(&self, context: &Context) -> computed_value::T {
                 computed_value::T(self.compute_iter(context).collect())
             }
+
             #[inline]
             fn from_computed_value(computed: &computed_value::T) -> Self {
                 SpecifiedValue(computed.0.iter()
                                     .map(ToComputedValue::from_computed_value)
                                     .collect())
             }
         }
     </%call>
@@ -311,129 +312,104 @@
         #[allow(unused_imports)]
         use values::computed::{Context, ToComputedValue};
         #[allow(unused_imports)]
         use values::{computed, generics, specified};
         #[allow(unused_imports)]
         use Atom;
         ${caller.body()}
         #[allow(unused_variables)]
-        pub fn cascade_property(declaration: &PropertyDeclaration,
-                                inherited_style: &ComputedValues,
-                                default_style: &ComputedValues,
-                                context: &mut computed::Context,
-                                cacheable: &mut bool,
-                                cascade_info: &mut Option<<&mut CascadeInfo>) {
+        pub fn cascade_property(
+            declaration: &PropertyDeclaration,
+            context: &mut computed::Context,
+            cascade_info: &mut Option<<&mut CascadeInfo>,
+        ) {
             let value = match *declaration {
                 PropertyDeclaration::${property.camel_case}(ref value) => {
                     DeclaredValue::Value(value)
                 },
                 PropertyDeclaration::CSSWideKeyword(id, value) => {
                     debug_assert!(id == LonghandId::${property.camel_case});
                     DeclaredValue::CSSWideKeyword(value)
                 },
                 PropertyDeclaration::WithVariables(..) => {
                     panic!("variables should already have been substituted")
                 }
                 _ => panic!("entered the wrong cascade_property() implementation"),
             };
 
             % if not property.derived_from:
                 if let Some(ref mut cascade_info) = *cascade_info {
-                    cascade_info.on_cascade_property(&declaration,
-                                                     &value);
+                    cascade_info.on_cascade_property(&declaration, &value);
                 }
-                % if property.logical:
-                    let wm = context.style.writing_mode;
-                % endif
-                <%
-                    maybe_wm = ", wm" if property.logical else ""
-                    maybe_cacheable = ", cacheable" if property.has_uncacheable_values == "True" else ""
-                    props_need_device = "content list_style_type".split() if product == "gecko" else []
-                    maybe_device = ", context.device" if property.ident in props_need_device else ""
-                %>
                 match value {
                     DeclaredValue::Value(ref specified_value) => {
                         % if property.ident in SYSTEM_FONT_LONGHANDS and product == "gecko":
                             if let Some(sf) = specified_value.get_system() {
                                 longhands::system_font::resolve_system_font(sf, context);
                             }
                         % endif
                         % if property.is_vector:
-                            // In the case of a vector property we want to pass down
-                            // an iterator so that this can be computed without allocation
+                            // In the case of a vector property we want to pass
+                            // down an iterator so that this can be computed
+                            // without allocation
                             //
-                            // However, computing requires a context, but the style struct
-                            // being mutated is on the context. We temporarily remove it,
-                            // mutate it, and then put it back. Vector longhands cannot
-                            // touch their own style struct whilst computing, else this will panic.
-                            let mut s = context.mutate_style().take_${data.current_style_struct.name_lower}();
+                            // However, computing requires a context, but the
+                            // style struct being mutated is on the context. We
+                            // temporarily remove it, mutate it, and then put it
+                            // back. Vector longhands cannot touch their own
+                            // style struct whilst computing, else this will
+                            // panic.
+                            let mut s =
+                                context.builder.take_${data.current_style_struct.name_lower}();
                             {
                                 let iter = specified_value.compute_iter(context);
-                                s.set_${property.ident}(iter ${maybe_cacheable});
+                                s.set_${property.ident}(iter);
                             }
-                            context.mutate_style().put_${data.current_style_struct.name_lower}(s);
+                            context.builder.put_${data.current_style_struct.name_lower}(s);
                         % else:
                             let computed = specified_value.to_computed_value(context);
-                             % if property.ident == "font_size":
-                                 longhands::font_size::cascade_specified_font_size(context,
-                                                                                   specified_value,
-                                                                                   computed,
-                                                                                   inherited_style.get_font());
+                            % if property.ident == "font_size":
+                                 longhands::font_size::cascade_specified_font_size(
+                                     context,
+                                     &specified_value,
+                                     computed,
+                                 );
                             % else:
-                                context.mutate_style().mutate_${data.current_style_struct.name_lower}()
-                                       .set_${property.ident}(computed ${maybe_device}
-                                                              ${maybe_cacheable} ${maybe_wm});
+                                context.builder.set_${property.ident}(computed)
                             % endif
                         % endif
                     }
                     DeclaredValue::WithVariables(_) => unreachable!(),
                     DeclaredValue::CSSWideKeyword(keyword) => match keyword {
                         % if not data.current_style_struct.inherited:
                         CSSWideKeyword::Unset |
                         % endif
                         CSSWideKeyword::Initial => {
                             % if property.ident == "font_size":
                                 longhands::font_size::cascade_initial_font_size(context);
                             % else:
-                                // We assume that it's faster to use copy_*_from rather than
-                                // set_*(get_initial_value());
-                                let initial_struct = default_style
-                                                    .get_${data.current_style_struct.name_lower}();
-                                context.mutate_style().mutate_${data.current_style_struct.name_lower}()
-                                                    .copy_${property.ident}_from(initial_struct ${maybe_wm});
+                                context.builder.reset_${property.ident}();
                             % endif
                         },
                         % if data.current_style_struct.inherited:
                         CSSWideKeyword::Unset |
                         % endif
                         CSSWideKeyword::Inherit => {
-                            // This is a bit slow, but this is rare so it shouldn't
-                            // matter.
-                            //
-                            // FIXME: is it still?
-                            *cacheable = false;
-                            let inherited_struct =
-                                inherited_style.get_${data.current_style_struct.name_lower}();
-
                             % if property.ident == "font_size":
-                                longhands::font_size::cascade_inherit_font_size(context, inherited_struct);
+                                longhands::font_size::cascade_inherit_font_size(context);
                             % else:
-                                context.mutate_style().mutate_${data.current_style_struct.name_lower}()
-                                    .copy_${property.ident}_from(inherited_struct ${maybe_wm});
+                                context.builder.inherit_${property.ident}();
                             % endif
                         }
                     }
                 }
 
                 % if property.custom_cascade:
-                    cascade_property_custom(declaration,
-                                            inherited_style,
-                                            context,
-                                            cacheable);
+                    cascade_property_custom(declaration, context);
                 % endif
             % else:
                 // Do not allow stylesheets to set derived properties.
             % endif
         }
         % if not property.derived_from:
             pub fn parse_specified<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                 % if property.boxed:
@@ -1103,17 +1079,17 @@
                     % if "block" in name:
                         if let ${length_type}::ExtremumLength(..) = computed {
                             return get_initial_value()
                         }
                     % endif
                 % else:
                     if let ${length_type}::ExtremumLength(..) = computed {
                         <% is_height = "true" if "height" in name else "false" %>
-                        if ${is_height} != context.style().writing_mode.is_vertical() {
+                        if ${is_height} != context.builder.writing_mode.is_vertical() {
                             return get_initial_value()
                         }
                     }
                 % endif
                 computed
             }
 
             #[inline]
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -450,22 +450,17 @@ impl AnimatedProperty {
                                 Ok(value) => value,
                                 Err(()) => return,
                             };
                         % endif
                         % if not prop.is_animatable_with_computed_value:
                             let value: longhands::${prop.ident}::computed_value::T =
                                 ToAnimatedValue::from_animated_value(value);
                         % endif
-                        <% method = "style.mutate_" + prop.style_struct.ident.strip("_") + "().set_" + prop.ident %>
-                        % if prop.has_uncacheable_values is "True":
-                            ${method}(value, &mut false);
-                        % else:
-                            ${method}(value);
-                        % endif
+                        style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value);
                     }
                 % endif
             % endfor
         }
     }
 
     /// Get an animatable value from a transition-property, an old style, and a
     /// new style.
@@ -557,19 +552,22 @@ impl AnimationValue {
                             )
                             % endif
                     }
                 % endif
             % endfor
         }
     }
 
-    /// Construct an AnimationValue from a property declaration
-    pub fn from_declaration(decl: &PropertyDeclaration, context: &mut Context,
-                            initial: &ComputedValues) -> Option<Self> {
+    /// Construct an AnimationValue from a property declaration.
+    pub fn from_declaration(
+        decl: &PropertyDeclaration,
+        context: &mut Context,
+        initial: &ComputedValues
+    ) -> Option<Self> {
         use properties::LonghandId;
 
         match *decl {
             % for prop in data.longhands:
             % if prop.animatable:
             PropertyDeclaration::${prop.camel_case}(ref val) => {
             % if prop.ident in SYSTEM_FONT_LONGHANDS and product == "gecko":
                 if let Some(sf) = val.get_system() {
@@ -601,17 +599,17 @@ impl AnimationValue {
                             CSSWideKeyword::Initial => {
                                 let initial_struct = initial.get_${prop.style_struct.name_lower}();
                                 initial_struct.clone_${prop.ident}()
                             },
                             % if prop.style_struct.inherited:
                                 CSSWideKeyword::Unset |
                             % endif
                             CSSWideKeyword::Inherit => {
-                                let inherit_struct = context.inherited_style
+                                let inherit_struct = context.inherited_style()
                                                             .get_${prop.style_struct.name_lower}();
                                 inherit_struct.clone_${prop.ident}()
                             },
                         };
                         % if not prop.is_animatable_with_computed_value:
                         let computed = computed.to_animated_value();
                         % endif
                         Some(AnimationValue::${prop.camel_case}(computed))
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -176,19 +176,17 @@
             % endfor
         }
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
 
     % if product == "servo":
         fn cascade_property_custom(_declaration: &PropertyDeclaration,
-                                   _inherited_style: &ComputedValues,
-                                   context: &mut computed::Context,
-                                   _cacheable: &mut bool) {
+                                   context: &mut computed::Context) {
             longhands::_servo_display_for_hypothetical_box::derive_from_display(context);
             longhands::_servo_text_decorations_in_effect::derive_from_display(context);
             longhands::_servo_under_display_none::derive_from_display(context);
         }
     % endif
 
     ${helpers.gecko_keyword_conversion(Keyword('display', ' '.join(values),
                                                gecko_enum_prefix='StyleDisplay',
@@ -297,17 +295,17 @@
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
     #[inline]
     pub fn derive_from_display(context: &mut Context) {
         let d = context.style().get_box().clone_display();
-        context.mutate_style().mutate_box().set__servo_display_for_hypothetical_box(d);
+        context.builder.set__servo_display_for_hypothetical_box(d);
     }
 
 </%helpers:longhand>
 
 <%helpers:longhand name="vertical-align" animation_value_type="ComputedValue"
                    spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align">
     use std::fmt;
     use style_traits::ToCss;
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -16,17 +16,17 @@
     use values::specified::{AllowQuirks, Color};
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             self.0.to_computed_value(context)
-                .to_rgba(context.inherited_style.get_color().clone_color())
+                .to_rgba(context.inherited_style().get_color().clone_color())
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(Color::rgba(*computed).into())
         }
     }
 
@@ -104,17 +104,17 @@
         }
 
         impl ToComputedValue for SystemColor {
             type ComputedValue = u32; // nscolor
             #[inline]
             fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
                 unsafe {
                     Gecko_GetLookAndFeelSystemColor(*self as i32,
-                                                    cx.device.pres_context())
+                                                    cx.device().pres_context())
                 }
             }
 
             #[inline]
             fn from_computed_value(_: &Self::ComputedValue) -> Self {
                 unreachable!()
             }
         }
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -557,17 +557,16 @@ macro_rules! impl_gecko_keyword_conversi
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="font-size" need_clone="True" animation_value_type="ComputedValue"
                    allow_quirks="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size">
     use app_units::Au;
     use properties::longhands::system_font::SystemFont;
-    use properties::style_structs::Font;
     use std::fmt;
     use style_traits::{HasViewportPercentage, ToCss};
     use values::FONT_MEDIUM_PX;
     use values::specified::{AllowQuirks, FontRelativeLength, LengthOrPercentage, NoCalcLength};
     use values::specified::length::FontBaseSize;
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -901,98 +900,112 @@ macro_rules! impl_gecko_keyword_conversi
             } else {
                 None
             }
         }
     }
 
     #[allow(unused_mut)]
     pub fn cascade_specified_font_size(context: &mut Context,
-                                      specified_value: &SpecifiedValue,
-                                      mut computed: Au,
-                                      parent: &Font) {
+                                       specified_value: &SpecifiedValue,
+                                       mut computed: Au) {
         if let SpecifiedValue::Keyword(kw, fraction) = *specified_value {
-            context.mutate_style().font_size_keyword = Some((kw, fraction));
+            context.builder.font_size_keyword = Some((kw, fraction));
         } else if let Some(ratio) = specified_value.as_font_ratio() {
             // In case a font-size-relative value was applied to a keyword
             // value, we must preserve this fact in case the generic font family
             // changes. relative values (em and %) applied to keywords must be
             // recomputed from the base size for the keyword and the relative size.
             //
             // See bug 1355707
-            if let Some((kw, fraction)) = context.inherited_style().font_computation_data.font_size_keyword {
-                context.mutate_style().font_size_keyword = Some((kw, fraction * ratio));
+            if let Some((kw, fraction)) = context.builder.inherited_style().font_computation_data.font_size_keyword {
+                context.builder.font_size_keyword = Some((kw, fraction * ratio));
             } else {
-                context.mutate_style().font_size_keyword = None;
+                context.builder.font_size_keyword = None;
             }
         } else {
-            context.mutate_style().font_size_keyword = None;
+            context.builder.font_size_keyword = None;
         }
 
         // we could use clone_language and clone_font_family() here but that's
         // expensive. Do it only in gecko mode for now.
         % if product == "gecko":
             use gecko_bindings::structs::nsIAtom;
             // if the language or generic changed, we need to recalculate
             // the font size from the stored font-size origin information.
-            if context.style().get_font().gecko().mLanguage.raw::<nsIAtom>() !=
-               context.inherited_style().get_font().gecko().mLanguage.raw::<nsIAtom>() ||
-               context.style().get_font().gecko().mGenericID !=
-               context.inherited_style().get_font().gecko().mGenericID {
-                if let Some((kw, ratio)) = context.style().font_size_keyword {
+            if context.builder.get_font().gecko().mLanguage.raw::<nsIAtom>() !=
+               context.builder.inherited_style().get_font().gecko().mLanguage.raw::<nsIAtom>() ||
+               context.builder.get_font().gecko().mGenericID !=
+               context.builder.inherited_style().get_font().gecko().mGenericID {
+                if let Some((kw, ratio)) = context.builder.font_size_keyword {
                     computed = kw.to_computed_value(context).scale_by(ratio);
                 }
             }
         % endif
 
+        let device = context.builder.device;
+        let mut font = context.builder.take_font();
         let parent_unconstrained = {
-            let (style, device) = context.mutate_style_with_device();
-
-            style.mutate_font().apply_font_size(computed, parent, device)
+            let parent_style = context.builder.inherited_style();
+            let parent_font = parent_style.get_font();
+            font.apply_font_size(computed, parent_font, device)
         };
-
+        context.builder.put_font(font);
 
         if let Some(parent) = parent_unconstrained {
-            let new_unconstrained = specified_value
-                        .to_computed_value_against(context, FontBaseSize::Custom(parent));
-            context.mutate_style()
+            let new_unconstrained =
+                specified_value
+                    .to_computed_value_against(context, FontBaseSize::Custom(parent));
+            context.builder
                    .mutate_font()
                    .apply_unconstrained_font_size(new_unconstrained);
         }
     }
 
-    pub fn cascade_inherit_font_size(context: &mut Context, parent: &Font) {
-        // If inheriting, we must recompute font-size in case of language changes
-        // using the font_size_keyword. We also need to do this to handle
-        // mathml scriptlevel changes
-        let kw_inherited_size = context.style().font_size_keyword.map(|(kw, ratio)| {
+    /// FIXME(emilio): This is very complex. Also, it should move to
+    /// StyleBuilder.
+    pub fn cascade_inherit_font_size(context: &mut Context) {
+        // If inheriting, we must recompute font-size in case of language
+        // changes using the font_size_keyword. We also need to do this to
+        // handle mathml scriptlevel changes
+        let kw_inherited_size = context.builder.font_size_keyword.map(|(kw, ratio)| {
             SpecifiedValue::Keyword(kw, ratio).to_computed_value(context)
         });
-        let parent_kw = context.inherited_style.font_computation_data.font_size_keyword;
-        let (style, device) = context.mutate_style_with_device();
-        let used_kw = style.mutate_font()
-               .inherit_font_size_from(parent, kw_inherited_size, device);
-        if used_kw {
-            style.font_size_keyword = parent_kw;
-        } else {
-            style.font_size_keyword = None;
-        }
+        let parent_kw;
+        let device = context.builder.device;
+        let mut font = context.builder.take_font();
+        let used_kw = {
+            let parent_style = context.builder.inherited_style();
+            let parent_font = parent_style.get_font();
+            parent_kw = parent_style.font_computation_data.font_size_keyword;
+
+            font.inherit_font_size_from(parent_font, kw_inherited_size, device)
+        };
+        context.builder.put_font(font);
+        context.builder.font_size_keyword =
+            if used_kw { parent_kw } else { None };
     }
 
+    /// Cascade the initial value for the `font-size` property.
+    ///
+    /// FIXME(emilio): This is the only function that is outside of the
+    /// `StyleBuilder`, and should really move inside!
+    ///
+    /// Can we move the font stuff there?
     pub fn cascade_initial_font_size(context: &mut Context) {
         // font-size's default ("medium") does not always
         // compute to the same value and depends on the font
         let computed = longhands::font_size::get_initial_specified_value()
                             .to_computed_value(context);
-        let (style, _device) = context.mutate_style_with_device();
-        style.mutate_font().set_font_size(computed);
+        context.builder.mutate_font().set_font_size(computed);
         % if product == "gecko":
-            style.mutate_font().fixup_font_min_size(_device);
+            let device = context.builder.device;
+            context.builder.mutate_font().fixup_font_min_size(device);
         % endif
-        style.font_size_keyword = Some((Default::default(), 1.));
+        context.builder.font_size_keyword = Some((Default::default(), 1.));
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-size-adjust" animation_value_type="ComputedValue"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
     use properties::longhands::system_font::SystemFont;
 
     no_viewport_percentage!(SpecifiedValue);
@@ -2409,18 +2422,18 @@ https://drafts.csswg.org/css-fonts-4/#lo
                     % endfor
                 };
 
                 let mut system: nsFont = unsafe { mem::uninitialized() };
                 unsafe {
                     bindings::Gecko_nsFont_InitSystem(
                         &mut system,
                         id as i32,
-                        cx.style.get_font().gecko(),
-                        cx.device.pres_context()
+                        cx.style().get_font().gecko(),
+                        cx.device().pres_context()
                     )
                 }
                 let family = system.fontlist.mFontlist.iter().map(|font| {
                     use properties::longhands::font_family::computed_value::*;
                     FontFamily::FamilyName(FamilyName {
                         name: (&*font.mName).into(),
                         quoted: true
                     })
--- a/servo/components/style/properties/longhand/inherited_box.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_box.mako.rs
@@ -280,13 +280,13 @@
         }
     }
 
     #[inline]
     pub fn derive_from_display(context: &mut Context) {
         use super::display::computed_value::T as Display;
 
         if context.style().get_box().clone_display() == Display::none {
-            context.mutate_style().mutate_inheritedbox()
-                                  .set__servo_under_display_none(SpecifiedValue(true));
+            context.builder
+                .set__servo_under_display_none(SpecifiedValue(true));
         }
     }
 </%helpers:longhand>
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -345,23 +345,23 @@
                                     || result.line_through.is_some(), context);
 
         result
     }
 
     #[inline]
     pub fn derive_from_text_decoration(context: &mut Context) {
         let derived = derive(context);
-        context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived);
+        context.builder.set__servo_text_decorations_in_effect(derived);
     }
 
     #[inline]
     pub fn derive_from_display(context: &mut Context) {
         let derived = derive(context);
-        context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived);
+        context.builder.set__servo_text_decorations_in_effect(derived);
     }
 </%helpers:longhand>
 
 <%helpers:single_keyword_computed name="white-space"
                                   values="normal pre nowrap pre-wrap pre-line"
                                   extra_gecko_values="-moz-pre-space"
                                   gecko_enum_prefix="StyleWhiteSpace"
                                   needs_conversion="True"
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -244,19 +244,17 @@
             }
         }
 
         if !empty { Ok(result) } else { Err(StyleParseError::UnspecifiedError.into()) }
     }
 
     % if product == "servo":
         fn cascade_property_custom(_declaration: &PropertyDeclaration,
-                                   _inherited_style: &ComputedValues,
-                                   context: &mut computed::Context,
-                                   _cacheable: &mut bool) {
+                                   context: &mut computed::Context) {
             longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context);
         }
     % endif
 
     #[cfg(feature = "gecko")]
     impl_bitflags_conversions!(SpecifiedValue);
 </%helpers:longhand>
 
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2448,22 +2448,41 @@ impl<'a, T: 'a> ops::Deref for StyleStru
 }
 
 /// A type used to compute a struct with minimal overhead.
 ///
 /// This allows holding references to the parent/default computed values without
 /// actually cloning them, until we either build the style, or mutate the
 /// inherited value.
 pub struct StyleBuilder<'a> {
-    device: &'a Device,
+    /// The device we're using to compute style.
+    ///
+    /// This provides access to viewport unit ratios, etc.
+    pub device: &'a Device,
+
+    /// The style we're inheriting from.
+    ///
+    /// This is effectively
+    /// `parent_style.unwrap_or(device.default_computed_values())`.
+    inherited_style: &'a ComputedValues,
+
+    /// The style we're getting reset structs from.
+    reset_style: &'a ComputedValues,
+
+    /// The style we're inheriting from explicitly, or none if we're the root of
+    /// a subtree.
     parent_style: Option<<&'a ComputedValues>,
+
+    /// The pseudo-element this style will represent.
     pseudo: Option<<&'a PseudoElement>,
+
     /// The rule node representing the ordered list of rules matched for this
     /// node.
     rules: Option<StrongRuleNode>,
+
     custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
     /// The writing mode flags.
     ///
     /// TODO(emilio): Make private.
     pub writing_mode: WritingMode,
     /// The keyword behind the current font-size property, if any.
     pub font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
     /// Flags for the computed value.
@@ -2477,36 +2496,38 @@ pub struct StyleBuilder<'a> {
     % endfor
 }
 
 impl<'a> StyleBuilder<'a> {
     /// Trivially construct a `StyleBuilder`.
     fn new(
         device: &'a Device,
         parent_style: Option<<&'a ComputedValues>,
-        reset_style: &'a ComputedValues,
         pseudo: Option<<&'a PseudoElement>,
         cascade_flags: CascadeFlags,
         rules: Option<StrongRuleNode>,
         custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
         writing_mode: WritingMode,
         font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
         flags: ComputedValueFlags,
         visited_style: Option<Arc<ComputedValues>>,
     ) -> Self {
+        let reset_style = device.default_computed_values();
         let inherited_style = parent_style.unwrap_or(reset_style);
         let reset_style = if cascade_flags.contains(INHERIT_ALL) {
             inherited_style
         } else {
             reset_style
         };
 
         StyleBuilder {
             device,
             parent_style,
+            inherited_style,
+            reset_style,
             pseudo,
             rules,
             custom_properties,
             writing_mode,
             font_size_keyword,
             flags,
             visited_style,
             % for style_struct in data.active_style_structs():
@@ -2518,48 +2539,125 @@ impl<'a> StyleBuilder<'a> {
             % endfor
         }
     }
 
     /// Creates a StyleBuilder holding only references to the structs of `s`, in
     /// order to create a derived style.
     pub fn for_derived_style(
         device: &'a Device,
-        s: &'a ComputedValues,
+        style_to_derive_from: &'a ComputedValues,
+        parent_style: Option<<&'a ComputedValues>,
         pseudo: Option<<&'a PseudoElement>,
     ) -> Self {
-        Self::for_inheritance(device, s, s, pseudo)
+        let reset_style = device.default_computed_values();
+        let inherited_style = parent_style.unwrap_or(reset_style);
+        StyleBuilder {
+            device,
+            parent_style,
+            inherited_style,
+            reset_style,
+            pseudo,
+            rules: None, // FIXME(emilio): Dubious...
+            custom_properties: style_to_derive_from.custom_properties(),
+            writing_mode: style_to_derive_from.writing_mode,
+            font_size_keyword: style_to_derive_from.font_computation_data.font_size_keyword,
+            flags: style_to_derive_from.flags,
+            visited_style: style_to_derive_from.clone_visited_style(),
+            % for style_struct in data.active_style_structs():
+            ${style_struct.ident}: StyleStructRef::Borrowed(
+                style_to_derive_from.${style_struct.name_lower}_arc()
+            ),
+            % endfor
+        }
     }
 
+    % for property in data.longhands:
+    % if property.ident != "font_size":
+    /// Inherit `${property.ident}` from our parent style.
+    #[allow(non_snake_case)]
+    pub fn inherit_${property.ident}(&mut self) {
+        let inherited_struct =
+            self.inherited_style.get_${property.style_struct.name_lower}();
+        self.${property.style_struct.ident}.mutate()
+            .copy_${property.ident}_from(
+                inherited_struct,
+                % if property.logical:
+                self.writing_mode,
+                % endif
+            );
+    }
+
+    /// Reset `${property.ident}` to the initial value.
+    #[allow(non_snake_case)]
+    pub fn reset_${property.ident}(&mut self) {
+        let reset_struct = self.reset_style.get_${property.style_struct.name_lower}();
+        self.${property.style_struct.ident}.mutate()
+            .copy_${property.ident}_from(
+                reset_struct,
+                % if property.logical:
+                self.writing_mode,
+                % endif
+            );
+    }
+
+    % if not property.is_vector:
+    /// Set the `${property.ident}` to the computed value `value`.
+    #[allow(non_snake_case)]
+    pub fn set_${property.ident}(
+        &mut self,
+        value: longhands::${property.ident}::computed_value::T
+    ) {
+        self.${property.style_struct.ident}.mutate()
+            .set_${property.ident}(
+                value,
+                % if property.logical:
+                self.writing_mode,
+                % elif product == "gecko" and property.ident in ["content", "list_style_type"]:
+                self.device,
+                % endif
+            );
+    }
+    % endif
+    % endif
+    % endfor
+
     /// Inherits style from the parent element, accounting for the default
     /// computed values that need to be provided as well.
     pub fn for_inheritance(
         device: &'a Device,
         parent: &'a ComputedValues,
-        reset: &'a ComputedValues,
         pseudo: Option<<&'a PseudoElement>,
     ) -> Self {
         // FIXME(emilio): This Some(parent) here is inconsistent with what we
         // usually do if `parent` is the default computed values, but that's
         // fine, and we want to eventually get rid of it.
         Self::new(
             device,
             Some(parent),
-            reset,
             pseudo,
             CascadeFlags::empty(),
             /* rules = */ None,
             parent.custom_properties(),
             parent.writing_mode,
             parent.font_computation_data.font_size_keyword,
             parent.flags,
             parent.clone_visited_style()
         )
     }
 
+    /// Returns the style we're inheriting from.
+    pub fn inherited_style(&self) -> &'a ComputedValues {
+        self.inherited_style
+    }
+
+    /// Returns the style we're getting reset properties from.
+    pub fn default_style(&self) -> &'a ComputedValues {
+        self.reset_style
+    }
 
     % for style_struct in data.active_style_structs():
         /// Gets an immutable view of the current `${style_struct.name}` style.
         pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
             &self.${style_struct.ident}
         }
 
         /// Gets a mutable view of the current `${style_struct.name}` style.
@@ -2580,19 +2678,19 @@ impl<'a> StyleBuilder<'a> {
         /// Gets a mutable view of the current `${style_struct.name}` style,
         /// only if it's been mutated before.
         pub fn get_${style_struct.name_lower}_if_mutated(&mut self)
                                                          -> Option<<&mut style_structs::${style_struct.name}> {
             self.${style_struct.ident}.get_if_mutated()
         }
 
         /// Reset the current `${style_struct.name}` style to its default value.
-        pub fn reset_${style_struct.name_lower}(&mut self, default: &'a ComputedValuesInner) {
+        pub fn reset_${style_struct.name_lower}_struct(&mut self) {
             self.${style_struct.ident} =
-                StyleStructRef::Borrowed(default.${style_struct.name_lower}_arc());
+                StyleStructRef::Borrowed(self.reset_style.${style_struct.name_lower}_arc());
         }
     % endfor
 
     /// Returns whether this computed style represents a floated object.
     pub fn floated(&self) -> bool {
         self.get_box().clone_float() != longhands::float::computed_value::T::none
     }
 
@@ -2680,20 +2778,17 @@ mod lazy_static_module {
             }
         };
     }
 }
 
 /// A per-longhand function that performs the CSS cascade for that longhand.
 pub type CascadePropertyFn =
     extern "Rust" fn(declaration: &PropertyDeclaration,
-                     inherited_style: &ComputedValues,
-                     default_style: &ComputedValues,
                      context: &mut computed::Context,
-                     cacheable: &mut bool,
                      cascade_info: &mut Option<<&mut CascadeInfo>);
 
 /// A per-longhand array of functions to perform the CSS cascade on each of
 /// them, effectively doing virtual dispatch.
 static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
     % for property in data.longhands:
         longhands::${property.ident}::cascade_property,
     % endfor
@@ -2826,17 +2921,16 @@ where
              layout_parent_style.unwrap_or(parent_style))
         },
         None => {
             (device.default_computed_values(),
              device.default_computed_values())
         }
     };
 
-    let default_style = device.default_computed_values();
     let inherited_custom_properties = inherited_style.custom_properties();
     let mut custom_properties = None;
     let mut seen_custom = HashSet::new();
     for (declaration, _cascade_level) in iter_declarations() {
         if let PropertyDeclaration::Custom(ref name, ref value) = *declaration {
             ::custom_properties::cascade(
                 &mut custom_properties, &inherited_custom_properties,
                 &mut seen_custom, name, value.borrow());
@@ -2844,25 +2938,22 @@ where
     }
 
     let custom_properties =
         ::custom_properties::finish_cascade(
             custom_properties, &inherited_custom_properties);
 
     let mut context = computed::Context {
         is_root_element: flags.contains(IS_ROOT_ELEMENT),
-        device: device,
-        inherited_style: inherited_style,
         // We'd really like to own the rules here to avoid refcount traffic, but
         // animation's usage of `apply_declarations` make this tricky. See bug
         // 1375525.
-        style: StyleBuilder::new(
+        builder: StyleBuilder::new(
             device,
             parent_style,
-            device.default_computed_values(),
             pseudo,
             flags,
             Some(rules.clone()),
             custom_properties,
             WritingMode::empty(),
             inherited_style.font_computation_data.font_size_keyword,
             ComputedValueFlags::empty(),
             visited_style,
@@ -2878,20 +2969,16 @@ where
         let color = device.default_background_color();
         Some(PropertyDeclaration::BackgroundColor(color.into()))
     } else {
         None
     };
 
     // Set computed values, overwriting earlier declarations for the same
     // property.
-    //
-    // NB: The cacheable boolean is not used right now, but will be once we
-    // start caching computed values in the rule nodes.
-    let mut cacheable = true;
     let mut seen = LonghandIdSet::new();
 
     // Declaration blocks are stored in increasing precedence order, we want
     // them in decreasing order here.
     //
     // We could (and used to) use a pattern match here, but that bloats this
     // function to over 100K of compiled code!
     //
@@ -2904,17 +2991,17 @@ where
             let mut font_size = None;
             let mut font_family = None;
         % endif
         for (declaration, cascade_level) in iter_declarations() {
             let mut declaration = match *declaration {
                 PropertyDeclaration::WithVariables(id, ref unparsed) => {
                     Cow::Owned(unparsed.substitute_variables(
                         id,
-                        &context.style.custom_properties,
+                        &context.builder.custom_properties,
                         context.quirks_mode
                     ))
                 }
                 ref d => Cow::Borrowed(d)
             };
 
             let longhand_id = match declaration.id() {
                 PropertyDeclarationId::Longhand(id) => id,
@@ -2978,25 +3065,22 @@ where
                 if LonghandId::FontFamily == longhand_id {
                     font_family = Some(declaration.clone());
                     continue;
                 }
             % endif
 
             let discriminant = longhand_id as usize;
             (CASCADE_PROPERTY[discriminant])(&*declaration,
-                                             inherited_style,
-                                             default_style,
                                              &mut context,
-                                             &mut cacheable,
                                              &mut cascade_info);
         }
         % if category_to_cascade_now == "early":
-            let writing_mode = get_writing_mode(context.style.get_inheritedbox());
-            context.style.writing_mode = writing_mode;
+            let writing_mode = get_writing_mode(context.builder.get_inheritedbox());
+            context.builder.writing_mode = writing_mode;
 
             let mut _skip_font_family = false;
 
             % if product == "gecko":
                 // Whenever a single generic value is specified, gecko will do a bunch of
                 // recalculation walking up the rule tree, including handling the font-size stuff.
                 // It basically repopulates the font struct with the default font for a given
                 // generic and language. We handle the font-size stuff separately, so this boils
@@ -3022,23 +3106,24 @@ where
                             }
                         }
                     }
 
                     // In case of just the language changing, the parent could have had no generic,
                     // which Gecko just does regular cascading with. Do the same.
                     // This can only happen in the case where the language changed but the family did not
                     if generic != structs::kGenericFont_NONE {
-                        let gecko_font = context.style.mutate_font().gecko_mut();
+                        let pres_context = context.builder.device.pres_context();
+                        let gecko_font = context.builder.mutate_font().gecko_mut();
                         gecko_font.mGenericID = generic;
                         unsafe {
                             bindings::Gecko_nsStyleFont_PrefillDefaultForGeneric(
                                 gecko_font,
-                                context.device.pres_context(),
-                                generic
+                                pres_context,
+                                generic,
                             );
                         }
                     }
                 }
             % endif
 
             // It is important that font_size is computed before
             // the late properties (for em units), but after font-family
@@ -3051,97 +3136,85 @@ where
             // To avoid an extra iteration, we just pull out the property
             // during the early iteration and cascade them in order
             // after it.
             if !_skip_font_family {
                 if let Some(ref declaration) = font_family {
 
                     let discriminant = LonghandId::FontFamily as usize;
                     (CASCADE_PROPERTY[discriminant])(declaration,
-                                                     inherited_style,
-                                                     default_style,
                                                      &mut context,
-                                                     &mut cacheable,
                                                      &mut cascade_info);
                     % if product == "gecko":
-                        context.style.mutate_font().fixup_none_generic(context.device);
+                        let device = context.builder.device;
+                        context.builder.mutate_font().fixup_none_generic(device);
                     % endif
                 }
             }
 
             if let Some(ref declaration) = font_size {
                 let discriminant = LonghandId::FontSize as usize;
                 (CASCADE_PROPERTY[discriminant])(declaration,
-                                                 inherited_style,
-                                                 default_style,
                                                  &mut context,
-                                                 &mut cacheable,
                                                  &mut cascade_info);
             % if product == "gecko":
             // Font size must be explicitly inherited to handle lang changes and
             // scriptlevel changes.
             } else if seen.contains(LonghandId::XLang) ||
                       seen.contains(LonghandId::MozScriptLevel) ||
                       seen.contains(LonghandId::MozMinFontSizeRatio) ||
                       font_family.is_some() {
                 let discriminant = LonghandId::FontSize as usize;
                 let size = PropertyDeclaration::CSSWideKeyword(
                     LonghandId::FontSize, CSSWideKeyword::Inherit);
 
                 (CASCADE_PROPERTY[discriminant])(&size,
-                                                 inherited_style,
-                                                 default_style,
                                                  &mut context,
-                                                 &mut cacheable,
                                                  &mut cascade_info);
             % endif
             }
         % endif
     % endfor
 
-    let mut style = context.style;
+    let mut builder = context.builder;
 
     {
-        StyleAdjuster::new(&mut style)
-            .adjust(
-                layout_parent_style,
-                context.device.default_computed_values(),
-                flags
-            );
+        StyleAdjuster::new(&mut builder)
+            .adjust(layout_parent_style, flags);
     }
 
     % if product == "gecko":
-        if let Some(ref mut bg) = style.get_background_if_mutated() {
+        if let Some(ref mut bg) = builder.get_background_if_mutated() {
             bg.fill_arrays();
         }
 
-        if let Some(ref mut svg) = style.get_svg_if_mutated() {
+        if let Some(ref mut svg) = builder.get_svg_if_mutated() {
             svg.fill_arrays();
         }
     % endif
 
     % if product == "servo":
         if seen.contains(LonghandId::FontStyle) ||
            seen.contains(LonghandId::FontWeight) ||
            seen.contains(LonghandId::FontStretch) ||
            seen.contains(LonghandId::FontFamily) {
-            style.mutate_font().compute_font_hash();
+            builder.mutate_font().compute_font_hash();
         }
     % endif
 
-    style.build()
+    builder.build()
 }
 
 /// See StyleAdjuster::adjust_for_border_width.
 pub fn adjust_border_width(style: &mut StyleBuilder) {
     % for side in ["top", "right", "bottom", "left"]:
         // Like calling to_computed_value, which wouldn't type check.
         if style.get_border().clone_border_${side}_style().none_or_hidden() &&
            style.get_border().border_${side}_has_nonzero_width() {
-            style.mutate_border().set_border_${side}_width(Au(0));
+            style.set_border_${side}_width(Au(0));
         }
     % endfor
 }
 
 /// Adjusts borders as appropriate to account for a fragment's status as the
 /// first or last fragment within the range of an element.
 ///
 /// Specifically, this function sets border widths to zero on the sides for
--- a/servo/components/style/servo/media_queries.rs
+++ b/servo/components/style/servo/media_queries.rs
@@ -224,19 +224,17 @@ pub enum Range<T> {
 
 impl Range<specified::Length> {
     fn to_computed_range(&self, device: &Device, quirks_mode: QuirksMode) -> Range<Au> {
         let default_values = device.default_computed_values();
         // http://dev.w3.org/csswg/mediaqueries3/#units
         // em units are relative to the initial font-size.
         let context = computed::Context {
             is_root_element: false,
-            device: device,
-            inherited_style: default_values,
-            style: StyleBuilder::for_derived_style(device, default_values, None),
+            builder: StyleBuilder::for_derived_style(device, default_values, None, None),
             // Servo doesn't support font metrics
             // A real provider will be needed here once we do; since
             // ch units can exist in media queries.
             font_metrics_provider: &ServoMetricsProvider,
             in_media_query: true,
             cached_system_font: None,
             quirks_mode: quirks_mode,
         };
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -382,17 +382,16 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
     /// Do ruby-related style adjustments, which include:
     /// * propagate the line break suppression flag,
     /// * inlinify block descendants,
     /// * suppress border and padding for ruby level containers,
     /// * correct unicode-bidi.
     #[cfg(feature = "gecko")]
     fn adjust_for_ruby(&mut self,
                        layout_parent_style: &ComputedValues,
-                       default_computed_values: &'b ComputedValues,
                        flags: CascadeFlags) {
         use properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP;
         use properties::computed_value_flags::SHOULD_SUPPRESS_LINEBREAK;
         let self_display = self.style.get_box().clone_display();
         // Check whether line break should be suppressed for this element.
         if self.should_suppress_linebreak(layout_parent_style) {
             self.style.flags.insert(SHOULD_SUPPRESS_LINEBREAK);
             // Inlinify the display type if allowed.
@@ -403,18 +402,18 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
                 }
             }
         }
         // Suppress border and padding for ruby level containers.
         // This is actually not part of the spec. It is currently unspecified
         // how border and padding should be handled for ruby level container,
         // and suppressing them here make it easier for layout to handle.
         if self_display.is_ruby_level_container() {
-            self.style.reset_border(default_computed_values);
-            self.style.reset_padding(default_computed_values);
+            self.style.reset_border_struct();
+            self.style.reset_padding_struct();
         }
         // Force bidi isolation on all internal ruby boxes and ruby container
         // per spec https://drafts.csswg.org/css-ruby-1/#bidi
         if self_display.is_ruby_type() {
             let new_value = match self.style.get_text().clone_unicode_bidi() {
                 unicode_bidi::normal | unicode_bidi::embed => Some(unicode_bidi::isolate),
                 unicode_bidi::bidi_override => Some(unicode_bidi::isolate_override),
                 _ => None,
@@ -427,17 +426,16 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
 
     /// Adjusts the style to account for various fixups that don't fit naturally
     /// into the cascade.
     ///
     /// When comparing to Gecko, this is similar to the work done by
     /// `nsStyleContext::ApplyStyleFixups`.
     pub fn adjust(&mut self,
                   layout_parent_style: &ComputedValues,
-                  _default_computed_values: &'b ComputedValues,
                   flags: CascadeFlags) {
         #[cfg(feature = "gecko")]
         {
             self.adjust_for_prohibited_display_contents(flags);
             self.adjust_for_fieldset_content(layout_parent_style, flags);
         }
         self.adjust_for_top_layer();
         self.blockify_if_necessary(layout_parent_style, flags);
@@ -454,13 +452,12 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
             self.adjust_for_alignment(layout_parent_style);
         }
         self.adjust_for_border_width();
         self.adjust_for_outline();
         self.adjust_for_writing_mode(layout_parent_style);
         self.adjust_for_text_decoration_lines(layout_parent_style);
         #[cfg(feature = "gecko")]
         {
-            self.adjust_for_ruby(layout_parent_style,
-                                 _default_computed_values, flags);
+            self.adjust_for_ruby(layout_parent_style, flags);
         }
     }
 }
--- a/servo/components/style/stylesheets/viewport_rule.rs
+++ b/servo/components/style/stylesheets/viewport_rule.rs
@@ -699,19 +699,17 @@ impl MaybeNew for ViewportConstraints {
         let initial_viewport = device.au_viewport_size();
 
         let provider = get_metrics_provider_for_product();
 
         let default_values = device.default_computed_values();
 
         let context = Context {
             is_root_element: false,
-            device: device,
-            inherited_style: default_values,
-            style: StyleBuilder::for_derived_style(device, default_values, None),
+            builder: StyleBuilder::for_derived_style(device, default_values, None, None),
             font_metrics_provider: &provider,
             cached_system_font: None,
             in_media_query: false,
             quirks_mode: quirks_mode,
         };
 
         // DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom'
         let extend_width;
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -63,26 +63,20 @@ pub mod text;
 pub mod transform;
 
 /// A `Context` is all the data a specified value could ever need to compute
 /// itself and be transformed to a computed value.
 pub struct Context<'a> {
     /// Whether the current element is the root element.
     pub is_root_element: bool,
 
-    /// The Device holds the viewport and other external state.
-    pub device: &'a Device,
-
-    /// The style we're inheriting from.
-    pub inherited_style: &'a ComputedValues,
-
     /// Values accessed through this need to be in the properties "computed
     /// early": color, text-decoration, font-size, display, position, float,
     /// border-*-style, outline-style, font-family, writing-mode...
-    pub style: StyleBuilder<'a>,
+    pub builder: StyleBuilder<'a>,
 
     /// A cached computed system font value, for use by gecko.
     ///
     /// See properties/longhands/font.mako.rs
     #[cfg(feature = "gecko")]
     pub cached_system_font: Option<properties::longhands::system_font::ComputedSystemFont>,
 
     /// A dummy option for servo so initializing a computed::Context isn't
@@ -100,28 +94,44 @@ pub struct Context<'a> {
     pub in_media_query: bool,
 
     /// The quirks mode of this context.
     pub quirks_mode: QuirksMode,
 }
 
 impl<'a> Context<'a> {
     /// Whether the current element is the root element.
-    pub fn is_root_element(&self) -> bool { self.is_root_element }
+    pub fn is_root_element(&self) -> bool {
+        self.is_root_element
+    }
+
+    /// The current device.
+    pub fn device(&self) -> &Device {
+        self.builder.device
+    }
+
     /// The current viewport size.
-    pub fn viewport_size(&self) -> Size2D<Au> { self.device.au_viewport_size() }
+    pub fn viewport_size(&self) -> Size2D<Au> {
+        self.builder.device.au_viewport_size()
+    }
+
     /// The style we're inheriting from.
-    pub fn inherited_style(&self) -> &ComputedValues { &self.inherited_style }
-    /// The current style. Note that only "eager" properties should be accessed
-    /// from here, see the comment in the member.
-    pub fn style(&self) -> &StyleBuilder { &self.style }
-    /// A mutable reference to the current style.
-    pub fn mutate_style(&mut self) -> &mut StyleBuilder<'a> { &mut self.style }
-    /// Get a mutable reference to the current style as well as the device
-    pub fn mutate_style_with_device(&mut self) -> (&mut StyleBuilder<'a>, &Device) { (&mut self.style, &self.device) }
+    pub fn inherited_style(&self) -> &ComputedValues {
+        self.builder.inherited_style()
+    }
+
+    /// The default computed style we're getting our reset style from.
+    pub fn default_style(&self) -> &ComputedValues {
+        self.builder.default_style()
+    }
+
+    /// The current style.
+    pub fn style(&self) -> &StyleBuilder {
+        &self.builder
+    }
 }
 
 /// An iterator over a slice of computed values
 #[derive(Clone)]
 pub struct ComputedVecIter<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> {
     cx: &'cx Context<'cx_a>,
     values: &'a [S],
 }
@@ -390,17 +400,17 @@ impl ToComputedValue for specified::Just
     type ComputedValue = JustifyItems;
 
     // https://drafts.csswg.org/css-align/#valdef-justify-items-auto
     fn to_computed_value(&self, context: &Context) -> JustifyItems {
         use values::specified::align;
         // If the inherited value of `justify-items` includes the `legacy` keyword, `auto` computes
         // to the inherited value.
         if self.0 == align::ALIGN_AUTO {
-            let inherited = context.inherited_style.get_position().clone_justify_items();
+            let inherited = context.inherited_style().get_position().clone_justify_items();
             if inherited.0.contains(align::ALIGN_LEGACY) {
                 return inherited
             }
         }
         return *self
     }
 
     #[inline]
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -249,31 +249,31 @@ impl ToComputedValue for Color {
             Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
             Color::Complex(ref complex) => *complex,
             #[cfg(feature = "gecko")]
             Color::System(system) =>
                 convert_nscolor_to_computedcolor(system.to_computed_value(_context)),
             #[cfg(feature = "gecko")]
             Color::Special(special) => {
                 use self::gecko::SpecialColorKeyword as Keyword;
-                let pres_context = _context.device.pres_context();
+                let pres_context = _context.device().pres_context();
                 convert_nscolor_to_computedcolor(match special {
                     Keyword::MozDefaultColor => pres_context.mDefaultColor,
                     Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
                     Keyword::MozHyperlinktext => pres_context.mLinkColor,
                     Keyword::MozActiveHyperlinktext => pres_context.mActiveLinkColor,
                     Keyword::MozVisitedHyperlinktext => pres_context.mVisitedLinkColor,
                 })
             }
             #[cfg(feature = "gecko")]
             Color::InheritFromBodyQuirk => {
                 use dom::TElement;
                 use gecko::wrapper::GeckoElement;
                 use gecko_bindings::bindings::Gecko_GetBody;
-                let pres_context = _context.device.pres_context();
+                let pres_context = _context.device().pres_context();
                 let body = unsafe {
                     Gecko_GetBody(pres_context)
                 };
                 if let Some(body) = body {
                     let wrap = GeckoElement(body);
                     let borrow = wrap.borrow_data();
                     ComputedColor::rgba(borrow.as_ref().unwrap()
                                               .styles.primary()
@@ -311,17 +311,17 @@ impl Parse for RGBAColor {
     }
 }
 
 impl ToComputedValue for RGBAColor {
     type ComputedValue = RGBA;
 
     fn to_computed_value(&self, context: &Context) -> RGBA {
         self.0.to_computed_value(context)
-            .to_rgba(context.style.get_color().clone_color())
+            .to_rgba(context.style().get_color().clone_color())
     }
 
     fn from_computed_value(computed: &RGBA) -> Self {
         RGBAColor(Color::rgba(*computed))
     }
 }
 
 impl From<Color> for RGBAColor {
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -102,17 +102,17 @@ impl FontRelativeLength {
     /// Computes the font-relative length. We use the base_size
     /// flag to pass a different size for computing font-size and unconstrained font-size
     pub fn to_computed_value(&self, context: &Context, base_size: FontBaseSize) -> Au {
         fn query_font_metrics(context: &Context, reference_font_size: Au) -> FontMetricsQueryResult {
             context.font_metrics_provider.query(context.style().get_font(),
                                                 reference_font_size,
                                                 context.style().writing_mode,
                                                 context.in_media_query,
-                                                context.device)
+                                                context.device())
         }
 
         let reference_font_size = base_size.resolve(context);
 
         match *self {
             FontRelativeLength::Em(length) => reference_font_size.scale_by(length),
             FontRelativeLength::Ex(length) => {
                 match query_font_metrics(context, reference_font_size) {
@@ -153,17 +153,17 @@ impl FontRelativeLength {
                 //
                 //     When specified on the font-size property of the root
                 //     element, the rem units refer to the property’s initial
                 //     value.
                 //
                 if context.is_root_element {
                     reference_font_size.scale_by(length)
                 } else {
-                    context.device.root_font_size().scale_by(length)
+                    context.device().root_font_size().scale_by(length)
                 }
             }
         }
     }
 }
 
 #[derive(Clone, PartialEq, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -357,17 +357,17 @@ impl PhysicalLength {
 
     /// Computes the given character width.
     pub fn to_computed_value(&self, context: &Context) -> Au {
         use gecko_bindings::bindings;
         // Same as Gecko
         const MM_PER_INCH: f32 = 25.4;
 
         let physical_inch = unsafe {
-            bindings::Gecko_GetAppUnitsPerPhysicalInch(context.device.pres_context())
+            bindings::Gecko_GetAppUnitsPerPhysicalInch(context.device().pres_context())
         };
 
         let inch = self.0 / MM_PER_INCH;
 
         to_au_round(inch, physical_inch as f32)
     }
 }
 
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1684,17 +1684,16 @@ fn get_pseudo_style(
     if is_probe {
         return style;
     }
 
     Some(style.unwrap_or_else(|| {
         StyleBuilder::for_inheritance(
             doc_data.stylist.device(),
             styles.primary(),
-            doc_data.default_computed_values(),
             Some(pseudo),
         ).build()
     }))
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_ComputedValues_Inherit(
     raw_data: RawServoStyleSetBorrowed,
@@ -1707,31 +1706,31 @@ pub extern "C" fn Servo_ComputedValues_I
     let for_text = target == structs::InheritTarget::Text;
     let atom = Atom::from(pseudo_tag);
     let pseudo = PseudoElement::from_anon_box_atom(&atom)
         .expect("Not an anon-box? Gah!");
     let style = if let Some(reference) = parent_style_context {
         let mut style = StyleBuilder::for_inheritance(
             data.stylist.device(),
             reference,
-            data.default_computed_values(),
             Some(&pseudo)
         );
 
         if for_text {
             StyleAdjuster::new(&mut style)
                 .adjust_for_text();
         }
 
         style.build()
     } else {
         debug_assert!(!for_text);
         StyleBuilder::for_derived_style(
             data.stylist.device(),
             data.default_computed_values(),
+            /* parent_style = */ None,
             Some(&pseudo),
         ).build()
     };
 
     style.into_strong()
 }
 
 #[no_mangle]
@@ -2881,30 +2880,31 @@ fn simulate_compute_values_failure(prope
     id.as_shorthand().is_ok() && property.mSimulateComputeValuesFailure
 }
 
 #[cfg(not(feature = "gecko_debug"))]
 fn simulate_compute_values_failure(_: &PropertyValuePair) -> bool {
     false
 }
 
-fn create_context<'a>(per_doc_data: &'a PerDocumentStyleDataImpl,
-                      font_metrics_provider: &'a FontMetricsProvider,
-                      style: &'a ComputedValues,
-                      parent_style: Option<&'a ComputedValues>,
-                      pseudo: Option<&'a PseudoElement>)
-                      -> Context<'a> {
-    let default_values = per_doc_data.default_computed_values();
-    let inherited_style = parent_style.unwrap_or(default_values);
-
+fn create_context<'a>(
+    per_doc_data: &'a PerDocumentStyleDataImpl,
+    font_metrics_provider: &'a FontMetricsProvider,
+    style: &'a ComputedValues,
+    parent_style: Option<&'a ComputedValues>,
+    pseudo: Option<&'a PseudoElement>,
+) -> Context<'a> {
     Context {
         is_root_element: false,
-        device: per_doc_data.stylist.device(),
-        inherited_style: inherited_style,
-        style: StyleBuilder::for_derived_style(per_doc_data.stylist.device(), style, pseudo),
+        builder: StyleBuilder::for_derived_style(
+            per_doc_data.stylist.device(),
+            style,
+            parent_style,
+            pseudo,
+        ),
         font_metrics_provider: font_metrics_provider,
         cached_system_font: None,
         in_media_query: false,
         quirks_mode: per_doc_data.stylist.quirks_mode(),
     }
 }
 
 #[no_mangle]
--- a/servo/tests/unit/style/parsing/mod.rs
+++ b/servo/tests/unit/style/parsing/mod.rs
@@ -49,19 +49,17 @@ fn assert_computed_serialization<C, F, T
           T: ToComputedValue<ComputedValue=C>, C: ToCss
 {
     let viewport_size = TypedSize2D::new(0., 0.);
     let initial_style = ComputedValues::initial_values();
     let device = Device::new(MediaType::Screen, viewport_size);
 
     let context = Context {
         is_root_element: true,
-        device: &device,
-        inherited_style: initial_style,
-        style: StyleBuilder::for_derived_style(&device, initial_style, None),
+        builder: StyleBuilder::for_derived_style(&device, initial_style, None, None),
         cached_system_font: None,
         font_metrics_provider: &ServoMetricsProvider,
         in_media_query: false,
         quirks_mode: QuirksMode::NoQuirks,
     };
 
     let parsed = parse(f, input).unwrap();
     let computed = parsed.to_computed_value(&context);