servo: Merge #17875 - Add support for having two separate parent styles. Fixes gecko bug 1382806 (from bzbarsky:first-line-dual-inheritance); r=emilio
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 26 Jul 2017 15:21:37 -0500
changeset 419855 d9e5fd58d089cbf9504cdd510d8a7fb31a8e46ea
parent 419854 702f9dbca19f3a8f31b628909fa4f75001a2f184
child 419856 11724cc8e83431c71ee5c2fe40829799b6b4abe5
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)
reviewersemilio
bugs1382806
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 #17875 - Add support for having two separate parent styles. Fixes gecko bug 1382806 (from bzbarsky:first-line-dual-inheritance); r=emilio <!-- Please describe your changes on the following line: --> This is needed for ::first-line support. See https://drafts.csswg.org/css-pseudo-4/#first-line-inheritance This PR doesn't quite implement what the CSS spec draft says right now. It implements what Gecko does, which is what an earlier draft said. --- <!-- 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 https://bugzilla.mozilla.org/show_bug.cgi?id=1382806 <!-- Either: --> - [ ] There are tests for these changes OR - [X] These changes do not require tests because servo doesn't support ::first-line yet <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- 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: 020188fdd77f0f0f2848e21eb9bcc28362d98506
servo/components/style/animation.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/color.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/style_adjuster.rs
servo/components/style/style_resolver.rs
servo/components/style/stylist.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/length.rs
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -494,16 +494,17 @@ fn compute_style_for_animation_step(cont
             // as existing browsers don't appear to animate visited styles.
             let computed =
                 properties::apply_declarations(context.stylist.device(),
                                                /* pseudo = */ None,
                                                previous_style.rules(),
                                                iter,
                                                Some(previous_style),
                                                Some(previous_style),
+                                               Some(previous_style),
                                                /* cascade_info = */ None,
                                                /* visited_style = */ None,
                                                font_metrics_provider,
                                                CascadeFlags::empty(),
                                                context.quirks_mode);
             computed
         }
     }
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -600,18 +600,18 @@ 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()
-                                                            .get_${prop.style_struct.name_lower}();
+                                let inherit_struct = context.builder
+                                                            .get_parent_${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/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -17,17 +17,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.builder.get_parent_color().clone_color())
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(Color::rgba(*computed).into())
         }
     }
 
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -543,19 +543,19 @@ macro_rules! impl_gecko_keyword_conversi
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             match *self {
                 SpecifiedValue::Weight(weight) => weight,
                 SpecifiedValue::Normal => computed_value::T::normal(),
                 SpecifiedValue::Bold => computed_value::T::bold(),
                 SpecifiedValue::Bolder =>
-                    context.inherited_style().get_font().clone_font_weight().bolder(),
+                    context.builder.get_parent_font().clone_font_weight().bolder(),
                 SpecifiedValue::Lighter =>
-                    context.inherited_style().get_font().clone_font_weight().lighter(),
+                    context.builder.get_parent_font().clone_font_weight().lighter(),
                 SpecifiedValue::System(_) => {
                     <%self:nongecko_unreachable>
                         context.cached_system_font.as_ref().unwrap().font_weight.clone()
                     </%self:nongecko_unreachable>
                 }
             }
         }
 
@@ -935,46 +935,45 @@ macro_rules! impl_gecko_keyword_conversi
             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.builder.inherited_style().font_computation_data.font_size_keyword {
+            if let Some((kw, fraction)) = context.builder.inherited_font_computation_data().font_size_keyword {
                 context.builder.font_size_keyword = Some((kw, fraction * ratio));
             } else {
                 context.builder.font_size_keyword = None;
             }
         } else {
             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.builder.get_font().gecko().mLanguage.raw::<nsIAtom>() !=
-               context.builder.inherited_style().get_font().gecko().mLanguage.raw::<nsIAtom>() ||
+               context.builder.get_parent_font().gecko().mLanguage.raw::<nsIAtom>() ||
                context.builder.get_font().gecko().mGenericID !=
-               context.builder.inherited_style().get_font().gecko().mGenericID {
+               context.builder.get_parent_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 parent_style = context.builder.inherited_style();
-            let parent_font = parent_style.get_font();
+            let parent_font = context.builder.get_parent_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));
@@ -992,19 +991,18 @@ macro_rules! impl_gecko_keyword_conversi
         // 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;
         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;
+            let parent_font = context.builder.get_parent_font();
+            parent_kw = context.builder.inherited_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 };
     }
 
@@ -2274,26 +2272,26 @@ https://drafts.csswg.org/css-fonts-4/#lo
         type ComputedValue = computed_value::T;
 
         fn to_computed_value(&self, cx: &Context) -> i8 {
             use properties::longhands::_moz_math_display::SpecifiedValue as DisplayValue;
             use std::{cmp, i8};
 
             let int = match *self {
                 SpecifiedValue::Auto => {
-                    let parent = cx.inherited_style().get_font().clone__moz_script_level() as i32;
-                    let display = cx.inherited_style().get_font().clone__moz_math_display();
+                    let parent = cx.builder.get_parent_font().clone__moz_script_level() as i32;
+                    let display = cx.builder.get_parent_font().clone__moz_math_display();
                     if display == DisplayValue::inline {
                         parent + 1
                     } else {
                         parent
                     }
                 }
                 SpecifiedValue::Relative(rel) => {
-                    let parent = cx.inherited_style().get_font().clone__moz_script_level();
+                    let parent = cx.builder.get_parent_font().clone__moz_script_level();
                     parent as i32 + rel
                 }
                 SpecifiedValue::Absolute(abs) => abs,
             };
             cmp::min(int, i8::MAX as i32) as i8
         }
         fn from_computed_value(other: &computed_value::T) -> Self {
             SpecifiedValue::Absolute(*other as i32)
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -225,28 +225,28 @@
                         // but the parent dir of that element is LTR even if it's <html dir=rtl>
                         // and will only be RTL if certain prefs have been set.
                         // In that case, the default behavior here will set it to left,
                         // but we want to set it to right -- instead set it to the default (`start`),
                         // which will do the right thing in this case (but not the general case)
                         if context.is_root_element {
                             return get_initial_value();
                         }
-                        let parent = context.inherited_style().get_inheritedtext().clone_text_align();
-                        let ltr = context.inherited_style().writing_mode.is_bidi_ltr();
+                        let parent = context.builder.get_parent_inheritedtext().clone_text_align();
+                        let ltr = context.builder.inherited_writing_mode().is_bidi_ltr();
                         match (parent, ltr) {
                             (computed_value::T::start, true) => computed_value::T::left,
                             (computed_value::T::start, false) => computed_value::T::right,
                             (computed_value::T::end, true) => computed_value::T::right,
                             (computed_value::T::end, false) => computed_value::T::left,
                             _ => parent
                         }
                     }
                     SpecifiedValue::MozCenterOrInherit => {
-                        let parent = context.inherited_style().get_inheritedtext().clone_text_align();
+                        let parent = context.builder.get_parent_inheritedtext().clone_text_align();
                         if parent == computed_value::T::start {
                             computed_value::T::center
                         } else {
                             parent
                         }
                     }
                 }
             }
@@ -335,17 +335,17 @@
         // declarations in effect and add in the text decorations that this block specifies.
         let mut result = match context.style().get_box().clone_display() {
             super::display::computed_value::T::inline_block |
             super::display::computed_value::T::inline_table => SpecifiedValue {
                 underline: None,
                 overline: None,
                 line_through: None,
             },
-            _ => context.inherited_style().get_inheritedtext().clone__servo_text_decorations_in_effect()
+            _ => context.builder.get_parent_inheritedtext().clone__servo_text_decorations_in_effect()
         };
 
         result.underline = maybe(context.style().get_text().has_underline()
                                  || result.underline.is_some(), context);
         result.overline = maybe(context.style().get_text().has_overline()
                                 || result.overline.is_some(), context);
         result.line_through = maybe(context.style().get_text().has_line_through()
                                     || result.line_through.is_some(), context);
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -9,16 +9,17 @@
 // can be escaped. In the above example, Vec<<&Foo> or Vec< &Foo> achieves the desired result of Vec<&Foo>.
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 use servo_arc::{Arc, UniqueArc};
 use std::borrow::Cow;
 use std::collections::HashSet;
 use std::{fmt, mem, ops};
+#[cfg(feature = "gecko")] use std::ptr;
 
 use app_units::Au;
 #[cfg(feature = "servo")] use cssparser::RGBA;
 use cssparser::{Parser, TokenSerializationType, serialize_identifier};
 use cssparser::ParserInput;
 #[cfg(feature = "servo")] use euclid::SideOffsets2D;
 use computed_values;
 use context::QuirksMode;
@@ -2465,16 +2466,21 @@ pub struct StyleBuilder<'a> {
     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 inheriting from for properties that don't inherit from
+    /// ::first-line.  This is the same as inherited_style, unless
+    /// inherited_style is a ::first-line style.
+    inherited_style_ignoring_first_line: &'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.
@@ -2502,37 +2508,50 @@ pub struct StyleBuilder<'a> {
     % endfor
 }
 
 impl<'a> StyleBuilder<'a> {
     /// Trivially construct a `StyleBuilder`.
     fn new(
         device: &'a Device,
         parent_style: Option<<&'a ComputedValues>,
+        parent_style_ignoring_first_line: Option<<&'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 {
+        debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
+        #[cfg(feature = "gecko")]
+        debug_assert!(parent_style.is_none() ||
+                      ptr::eq(parent_style.unwrap(),
+                              parent_style_ignoring_first_line.unwrap()) ||
+                      parent_style.unwrap().pseudo() == Some(PseudoElement::FirstLine));
         let reset_style = device.default_computed_values();
         let inherited_style = parent_style.unwrap_or(reset_style);
+        let inherited_style_ignoring_first_line = parent_style_ignoring_first_line.unwrap_or(reset_style);
+        // FIXME(bz): INHERIT_ALL seems like a fundamentally broken idea.  I'm
+        // 99% sure it should give incorrect behavior for table anonymous box
+        // backgrounds, for example.  This code doesn't attempt to make it play
+        // nice with inherited_style_ignoring_first_line.
         let reset_style = if cascade_flags.contains(INHERIT_ALL) {
             inherited_style
         } else {
             reset_style
         };
 
         StyleBuilder {
             device,
             parent_style,
             inherited_style,
+            inherited_style_ignoring_first_line,
             reset_style,
             pseudo,
             rules,
             custom_properties,
             writing_mode,
             font_size_keyword,
             flags,
             visited_style,
@@ -2551,20 +2570,25 @@ impl<'a> StyleBuilder<'a> {
     pub fn for_derived_style(
         device: &'a Device,
         style_to_derive_from: &'a ComputedValues,
         parent_style: Option<<&'a ComputedValues>,
         pseudo: Option<<&'a PseudoElement>,
     ) -> Self {
         let reset_style = device.default_computed_values();
         let inherited_style = parent_style.unwrap_or(reset_style);
+        #[cfg(feature = "gecko")]
+        debug_assert!(parent_style.is_none() ||
+                      parent_style.unwrap().pseudo() != Some(PseudoElement::FirstLine));
         StyleBuilder {
             device,
             parent_style,
             inherited_style,
+            // None of our callers pass in ::first-line parent styles.
+            inherited_style_ignoring_first_line: 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(),
@@ -2576,18 +2600,23 @@ impl<'a> StyleBuilder<'a> {
         }
     }
 
     % 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) {
+        % if property.style_struct.inherited:
         let inherited_struct =
             self.inherited_style.get_${property.style_struct.name_lower}();
+        % else:
+        let inherited_struct =
+            self.inherited_style_ignoring_first_line.get_${property.style_struct.name_lower}();
+        % endif
         self.${property.style_struct.ident}.mutate()
             .copy_${property.ident}_from(
                 inherited_struct,
                 % if property.logical:
                 self.writing_mode,
                 % endif
             );
     }
@@ -2634,37 +2663,33 @@ impl<'a> StyleBuilder<'a> {
         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),
+            Some(parent),
             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 whether we have a visited style.
     pub fn has_visited_style(&self) -> bool {
         self.visited_style.is_some()
     }
 
-    /// 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} {
@@ -2747,16 +2772,52 @@ impl<'a> StyleBuilder<'a> {
 
     /// Get the custom properties map if necessary.
     ///
     /// Cloning the Arc here is fine because it only happens in the case where
     /// we have custom properties, and those are both rare and expensive.
     fn custom_properties(&self) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
         self.custom_properties.clone()
     }
+
+    /// Access to various information about our inherited styles.  We don't
+    /// expose an inherited ComputedValues directly, because in the
+    /// ::first-line case some of the inherited information needs to come from
+    /// one ComputedValues instance and some from a different one.
+
+    /// Inherited font bits.
+    pub fn inherited_font_computation_data(&self) -> &FontComputationData {
+        &self.inherited_style.font_computation_data
+    }
+
+    /// Inherited writing-mode.
+    pub fn inherited_writing_mode(&self) -> &WritingMode {
+        &self.inherited_style.writing_mode
+    }
+
+    /// Inherited style flags.
+    pub fn inherited_flags(&self) -> &ComputedValueFlags {
+        &self.inherited_style.flags
+    }
+
+    /// And access to inherited style structs.
+    % for style_struct in data.active_style_structs():
+        /// Gets our inherited `${style_struct.name}`.  We don't name these
+        /// accessors `inherited_${style_struct.name_lower}` because we already
+        /// have things like "box" vs "inherited_box" as struct names.  Do the
+        /// next-best thing and call them `parent_${style_struct.name_lower}`
+        /// instead.
+        pub fn get_parent_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
+            % if style_struct.inherited:
+            self.inherited_style.get_${style_struct.name_lower}()
+            % else:
+            self.inherited_style_ignoring_first_line.get_${style_struct.name_lower}()
+            % endif
+        }
+    % endfor
 }
 
 #[cfg(feature = "servo")]
 pub use self::lazy_static_module::INITIAL_SERVO_VALUES;
 
 // Use a module to work around #[cfg] on lazy_static! not being applied to every generated item.
 #[cfg(feature = "servo")]
 #[allow(missing_docs)]
@@ -2862,23 +2923,30 @@ bitflags! {
 ///   * `flags`: Various flags.
 ///
 pub fn cascade(
     device: &Device,
     pseudo: Option<<&PseudoElement>,
     rule_node: &StrongRuleNode,
     guards: &StylesheetGuards,
     parent_style: Option<<&ComputedValues>,
+    parent_style_ignoring_first_line: Option<<&ComputedValues>,
     layout_parent_style: Option<<&ComputedValues>,
     visited_style: Option<Arc<ComputedValues>>,
     cascade_info: Option<<&mut CascadeInfo>,
     font_metrics_provider: &FontMetricsProvider,
     flags: CascadeFlags,
     quirks_mode: QuirksMode
 ) -> Arc<ComputedValues> {
+    debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
+    #[cfg(feature = "gecko")]
+    debug_assert!(parent_style.is_none() ||
+                  ptr::eq(parent_style.unwrap(),
+                          parent_style_ignoring_first_line.unwrap()) ||
+                  parent_style.unwrap().pseudo() == Some(PseudoElement::FirstLine));
     let iter_declarations = || {
         rule_node.self_and_ancestors().flat_map(|node| {
             let cascade_level = node.cascade_level();
             let source = node.style_source();
             let declarations = if source.is_some() {
                 source.read(cascade_level.guard(guards)).declarations()
             } else {
                 // The root node has no style source.
@@ -2914,16 +2982,17 @@ pub fn cascade(
         })
     };
     apply_declarations(
         device,
         pseudo,
         rule_node,
         iter_declarations,
         parent_style,
+        parent_style_ignoring_first_line,
         layout_parent_style,
         visited_style,
         cascade_info,
         font_metrics_provider,
         flags,
         quirks_mode,
     )
 }
@@ -2932,28 +3001,35 @@ pub fn cascade(
 /// first.
 #[allow(unused_mut)] // conditionally compiled code for "position"
 pub fn apply_declarations<'a, F, I>(
     device: &Device,
     pseudo: Option<<&PseudoElement>,
     rules: &StrongRuleNode,
     iter_declarations: F,
     parent_style: Option<<&ComputedValues>,
+    parent_style_ignoring_first_line: Option<<&ComputedValues>,
     layout_parent_style: Option<<&ComputedValues>,
     visited_style: Option<Arc<ComputedValues>>,
     mut cascade_info: Option<<&mut CascadeInfo>,
     font_metrics_provider: &FontMetricsProvider,
     flags: CascadeFlags,
     quirks_mode: QuirksMode,
 ) -> Arc<ComputedValues>
 where
     F: Fn() -> I,
     I: Iterator<Item = (&'a PropertyDeclaration, CascadeLevel)>,
 {
     debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
+    debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
+    #[cfg(feature = "gecko")]
+    debug_assert!(parent_style.is_none() ||
+                  ptr::eq(parent_style.unwrap(),
+                          parent_style_ignoring_first_line.unwrap()) ||
+                  parent_style.unwrap().pseudo() == Some(PseudoElement::FirstLine));
     let (inherited_style, layout_parent_style) = match parent_style {
         Some(parent_style) => {
             (parent_style,
              layout_parent_style.unwrap_or(parent_style))
         },
         None => {
             (device.default_computed_values(),
              device.default_computed_values())
@@ -2978,16 +3054,17 @@ where
     let mut context = computed::Context {
         is_root_element: flags.contains(IS_ROOT_ELEMENT),
         // 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.
         builder: StyleBuilder::new(
             device,
             parent_style,
+            parent_style_ignoring_first_line,
             pseudo,
             flags,
             Some(rules.clone()),
             custom_properties,
             WritingMode::empty(),
             inherited_style.font_computation_data.font_size_keyword,
             ComputedValueFlags::empty(),
             visited_style,
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -441,17 +441,17 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
 
         if !self.style.has_visited_style() {
             return;
         }
 
         let relevant_link_visited = if flags.contains(IS_LINK) {
             flags.contains(IS_VISITED_LINK)
         } else {
-            self.style.inherited_style().flags.contains(IS_RELEVANT_LINK_VISITED)
+            self.style.inherited_flags().contains(IS_RELEVANT_LINK_VISITED)
         };
 
         if relevant_link_visited {
             self.style.flags.insert(IS_RELEVANT_LINK_VISITED);
         }
     }
 
     /// Adjusts the style to account for various fixups that don't fit naturally
--- a/servo/components/style/style_resolver.rs
+++ b/servo/components/style/style_resolver.rs
@@ -474,16 +474,17 @@ where
         let implemented_pseudo = self.element.implemented_pseudo_element();
         let values =
             cascade(
                 self.context.shared.stylist.device(),
                 pseudo.or(implemented_pseudo.as_ref()),
                 rules.unwrap_or(self.context.shared.stylist.rule_tree().root()),
                 &self.context.shared.guards,
                 parent_style,
+                parent_style,
                 layout_parent_style,
                 style_if_visited,
                 Some(&mut cascade_info),
                 &self.context.thread_local.font_metrics_provider,
                 cascade_flags,
                 self.context.shared.quirks_mode
             );
 
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -632,16 +632,17 @@ impl Stylist {
         // the actual used value, and the computed value of it would need
         // blockification.
         properties::cascade(&self.device,
                             Some(pseudo),
                             &rule_node,
                             guards,
                             parent,
                             parent,
+                            parent,
                             None,
                             None,
                             font_metrics,
                             cascade_flags,
                             self.quirks_mode)
     }
 
     /// Returns the style for an anonymous box of the given type.
@@ -748,16 +749,17 @@ impl Stylist {
             // (Though the flags don't indicate so!)
             let computed =
                 properties::cascade(&self.device,
                                     Some(pseudo),
                                     rule_node,
                                     guards,
                                     Some(inherited_style),
                                     Some(inherited_style),
+                                    Some(inherited_style),
                                     None,
                                     None,
                                     font_metrics,
                                     CascadeFlags::empty(),
                                     self.quirks_mode);
 
             Some(computed)
         } else {
@@ -773,16 +775,17 @@ impl Stylist {
         // (tl;dr: It doesn't apply for replaced elements and such, but the
         // computed value is still "contents").
         Some(properties::cascade(&self.device,
                                  Some(pseudo),
                                  rules,
                                  guards,
                                  Some(parent_style),
                                  Some(parent_style),
+                                 Some(parent_style),
                                  visited_values,
                                  None,
                                  font_metrics,
                                  CascadeFlags::empty(),
                                  self.quirks_mode))
     }
 
     /// Computes the cascade inputs for a lazily-cascaded pseudo-element.
@@ -1337,16 +1340,17 @@ impl Stylist {
         let metrics = get_metrics_provider_for_product();
         // FIXME(emilio): the pseudo bit looks quite dubious!
         properties::cascade(&self.device,
                             /* pseudo = */ None,
                             &rule_node,
                             guards,
                             Some(parent_style),
                             Some(parent_style),
+                            Some(parent_style),
                             None,
                             None,
                             &metrics,
                             CascadeFlags::empty(),
                             self.quirks_mode)
     }
 
     /// Accessor for a shared reference to the device.
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -114,21 +114,16 @@ impl<'a> Context<'a> {
         self.builder.device
     }
 
     /// The current 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.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
@@ -406,17 +401,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.builder.get_parent_position().clone_justify_items();
             if inherited.0.contains(align::ALIGN_LEGACY) {
                 return inherited
             }
         }
         return *self
     }
 
     #[inline]
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -88,17 +88,17 @@ pub enum FontBaseSize {
 }
 
 impl FontBaseSize {
     /// Calculate the actual size for a given context
     pub fn resolve(&self, context: &Context) -> Au {
         match *self {
             FontBaseSize::Custom(size) => size,
             FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size(),
-            FontBaseSize::InheritedStyle => context.inherited_style().get_font().clone_font_size(),
+            FontBaseSize::InheritedStyle => context.style().get_parent_font().clone_font_size(),
         }
     }
 }
 
 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 {