servo: Merge #17912 - Implement ::first-line support in stylo (from bzbarsky:stylo-first-line); r=emilio
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 28 Jul 2017 22:51:20 -0500
changeset 422908 91a488108e10bfd4df90ccf8b738ae5c4a0f0dc1
parent 422907 8c470a91f0ff7617990e08e09e696612dd674d9e
child 422909 6253d87d0734034979535fa1e041270a54837e58
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1324619
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 #17912 - Implement ::first-line support in stylo (from bzbarsky:stylo-first-line); r=emilio <!-- Please describe your changes on the following line: --> Fixes Gecko bug 1324619. --- <!-- 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=1324619 <!-- Either: --> - [ ] There are tests for these changes OR - [X] These changes do not require tests because there are Gecko tests <!-- 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: ed75bcae75bda9e4ad04f24ebc07b53f7b51b8b6
servo/components/style/context.rs
servo/components/style/gecko/data.rs
servo/components/style/gecko/generated/bindings.rs
servo/components/style/gecko/pseudo_element.rs
servo/components/style/matching.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/servo/selector_parser.rs
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
--- a/servo/components/style/context.rs
+++ b/servo/components/style/context.rs
@@ -186,17 +186,17 @@ pub struct CascadeInputs {
     /// node if visited, only computed if there's a relevant link for this
     /// element. A element's "relevant link" is the element being matched if it
     /// is a link or the nearest ancestor link.
     pub visited_rules: Option<StrongRuleNode>,
 }
 
 impl CascadeInputs {
     /// Construct inputs from previous cascade results, if any.
-    pub fn new_from_style(style: &Arc<ComputedValues>) -> Self {
+    pub fn new_from_style(style: &ComputedValues) -> Self {
         CascadeInputs {
             rules: style.rules.clone(),
             visited_rules: style.get_visited_style().and_then(|v| v.rules.clone()),
         }
     }
 }
 
 // We manually implement Debug for CascadeInputs so that we can avoid the
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -199,15 +199,24 @@ impl PerDocumentStyleDataImpl {
         self.stylist.device().default_computed_values_arc()
     }
 
     /// Clear the stylist.  This will be a no-op if the stylist is
     /// already cleared; the stylist handles that.
     pub fn clear_stylist(&mut self) {
         self.stylist.clear();
     }
+
+    /// Returns whether visited links are enabled.
+    fn visited_links_enabled(&self) -> bool {
+        unsafe { bindings::Gecko_AreVisitedLinksEnabled() }
+    }
+    /// Returns whether visited styles are enabled.
+    pub fn visited_styles_enabled(&self) -> bool {
+        self.visited_links_enabled() && !self.is_private_browsing_enabled()
+    }
 }
 
 unsafe impl HasFFI for PerDocumentStyleData {
     type FFIType = RawServoStyleSet;
 }
 unsafe impl HasSimpleFFI for PerDocumentStyleData {}
 unsafe impl HasBoxFFI for PerDocumentStyleData {}
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -2813,16 +2813,26 @@ extern "C" {
                                     pseudo_type: CSSPseudoElementType,
                                     rule_inclusion: StyleRuleInclusion,
                                     snapshots:
                                         *const ServoElementSnapshotTable,
                                     set: RawServoStyleSetBorrowed)
      -> ServoStyleContextStrong;
 }
 extern "C" {
+    pub fn Servo_ReparentStyle(style_to_reparent: ServoStyleContextBorrowed,
+                               parent_style: ServoStyleContextBorrowed,
+                               parent_style_ignoring_first_line:
+                                   ServoStyleContextBorrowed,
+                               layout_parent_style: ServoStyleContextBorrowed,
+                               element: RawGeckoElementBorrowedOrNull,
+                               set: RawServoStyleSetBorrowed)
+     -> ServoStyleContextStrong;
+}
+extern "C" {
     pub fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
                                  set: RawServoStyleSetBorrowed,
                                  snapshots: *const ServoElementSnapshotTable,
                                  flags: ServoTraversalFlags) -> bool;
 }
 extern "C" {
     pub fn Servo_AssertTreeIsClean(root: RawGeckoElementBorrowed);
 }
--- a/servo/components/style/gecko/pseudo_element.rs
+++ b/servo/components/style/gecko/pseudo_element.rs
@@ -82,16 +82,22 @@ impl PseudoElement {
     }
 
     /// Whether this pseudo-element is ::first-letter.
     #[inline]
     pub fn is_first_letter(&self) -> bool {
         *self == PseudoElement::FirstLetter
     }
 
+    /// Whether this pseudo-element is ::first-line.
+    #[inline]
+    pub fn is_first_line(&self) -> bool {
+        *self == PseudoElement::FirstLine
+    }
+
     /// Whether this pseudo-element is ::-moz-fieldset-content.
     #[inline]
     pub fn is_fieldset_content(&self) -> bool {
         *self == PseudoElement::FieldsetContent
     }
 
     /// Whether this pseudo-element is lazily-cascaded.
     #[inline]
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -822,17 +822,17 @@ pub trait MatchMethods : TElement {
             //
             // However, it may be the case that the style of this element would
             // make us think we need a pseudo, but we don't, like for pseudos in
             // replaced elements, that's why we need the old != new instead of
             // just check whether the new style would generate a pseudo.
             return StyleDifference::new(RestyleDamage::empty(), StyleChange::Unchanged)
         }
 
-        if pseudo.map_or(false, |p| p.is_first_letter()) {
+        if pseudo.map_or(false, |p| p.is_first_letter() || p.is_first_line()) {
             // No one cares about this pseudo, and we've checked above that
             // we're not switching from a "cares" to a "doesn't care" state
             // or vice versa.
             return StyleDifference::new(RestyleDamage::empty(),
                                         StyleChange::Unchanged)
         }
 
         // If we are changing display property we need to accumulate
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -36,16 +36,19 @@ use gecko_bindings::bindings::Gecko_nsSt
 use gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
 use gecko_bindings::bindings::Gecko_SetListStyleImageNone;
 use gecko_bindings::bindings::Gecko_SetListStyleImageImageValue;
 use gecko_bindings::bindings::Gecko_SetNullImageValue;
 use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
 use gecko_bindings::bindings::RawGeckoPresContextBorrowed;
 use gecko_bindings::structs;
 use gecko_bindings::structs::nsCSSPropertyID;
+use gecko_bindings::structs::mozilla::CSSPseudoElementType;
+use gecko_bindings::structs::mozilla::CSSPseudoElementType_InheritingAnonBox;
+use gecko_bindings::structs::root::NS_STYLE_CONTEXT_TYPE_SHIFT;
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
 use gecko::values::convert_nscolor_to_rgba;
 use gecko::values::convert_rgba_to_nscolor;
 use gecko::values::GeckoStyleCoordConvertible;
 use gecko::values::round_border_to_device_pixels;
 use logical_geometry::WritingMode;
 use media_queries::Device;
 use properties::animated_properties::TransitionProperty;
@@ -128,16 +131,28 @@ impl ComputedValues {
         let atom = (self.0)._base.mPseudoTag.raw::<structs::nsIAtom>();
         if atom.is_null() {
             return None;
         }
 
         let atom = Atom::from(atom);
         PseudoElement::from_atom(&atom)
     }
+
+    fn get_pseudo_type(&self) -> CSSPseudoElementType {
+        let bits = (self.0)._base.mBits;
+        let our_type = bits >> NS_STYLE_CONTEXT_TYPE_SHIFT;
+        unsafe { transmute(our_type as u8) }
+    }
+
+    pub fn is_anon_box(&self) -> bool {
+        let our_type = self.get_pseudo_type();
+        return our_type == CSSPseudoElementType_InheritingAnonBox ||
+               our_type == CSSPseudoElementType::NonInheritingAnonBox;
+    }
 }
 
 impl Drop for ComputedValues {
     fn drop(&mut self) {
         unsafe {
             bindings::Gecko_ServoStyleContext_Destroy(&mut self.0);
         }
     }
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -37,16 +37,18 @@ use style_traits::{ParseError, StylePars
 #[repr(usize)]
 pub enum PseudoElement {
     // Eager pseudos. Keep these first so that eager_index() works.
     After = 0,
     Before,
     Selection,
     // If/when :first-letter is added, update is_first_letter accordingly.
 
+    // If/when :first-line is added, update is_first_line accordingly.
+
     // If/when ::first-letter, ::first-line, or ::placeholder are added, adjust
     // our property_restriction implementation to do property filtering for
     // them.  Also, make sure the UA sheet has the !important rules some of the
     // APPLIES_TO_PLACEHOLDER properties expect!
 
     // Non-eager pseudos.
     DetailsSummary,
     DetailsContent,
@@ -120,16 +122,22 @@ impl PseudoElement {
     }
 
     /// Whether the current pseudo element is :first-letter
     #[inline]
     pub fn is_first_letter(&self) -> bool {
         false
     }
 
+    /// Whether the current pseudo element is :first-line
+    #[inline]
+    pub fn is_first_line(&self) -> bool {
+        false
+    }
+
     /// Whether this pseudo-element is eagerly-cascaded.
     #[inline]
     pub fn is_eager(&self) -> bool {
         self.cascade_type() == PseudoElementCascadeType::Eager
     }
 
     /// Whether this pseudo-element is lazily-cascaded.
     #[inline]
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -15,16 +15,17 @@ use font_metrics::FontMetricsProvider;
 use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
 use invalidation::element::invalidation_map::InvalidationMap;
 use invalidation::media_queries::{EffectiveMediaQueryResults, ToMediaListKey};
 use media_queries::Device;
 use properties::{self, CascadeFlags, ComputedValues};
 use properties::{AnimationRules, PropertyDeclarationBlock};
 #[cfg(feature = "servo")]
 use properties::INHERIT_ALL;
+use properties::IS_LINK;
 use rule_tree::{CascadeLevel, RuleTree, StyleSource};
 use selector_map::{SelectorMap, SelectorMapEntry};
 use selector_parser::{SelectorImpl, PseudoElement};
 use selectors::attr::NamespaceConstraint;
 use selectors::bloom::BloomFilter;
 use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
 use selectors::matching::VisitedHandlingMode;
 use selectors::parser::{AncestorHashes, Combinator, Component, Selector};
@@ -722,75 +723,125 @@ impl Stylist {
                                                     -> Option<Arc<ComputedValues>>
     {
         // We may have only visited rules in cases when we are actually
         // resolving, not probing, pseudo-element style.
         if inputs.rules.is_none() && inputs.visited_rules.is_none() {
             return None
         }
 
+        // FIXME(emilio): The lack of layout_parent_style here could be
+        // worrying, but we're probably dropping the display fixup for
+        // pseudos other than before and after, so it's probably ok.
+        //
+        // (Though the flags don't indicate so!)
+        Some(self.compute_style_with_inputs(inputs,
+                                            Some(pseudo),
+                                            guards,
+                                            parent_style,
+                                            parent_style,
+                                            parent_style,
+                                            font_metrics,
+                                            CascadeFlags::empty()))
+    }
+
+    /// Computes a style using the given CascadeInputs.  This can be used to
+    /// compute a style any time we know what rules apply and just need to use
+    /// the given parent styles.
+    ///
+    /// parent_style is the style to inherit from for properties affected by
+    /// first-line ancestors.
+    ///
+    /// parent_style_ignoring_first_line is the style to inherit from for
+    /// properties not affected by first-line ancestors.
+    ///
+    /// layout_parent_style is the style used for some property fixups.  It's
+    /// the style of the nearest ancestor with a layout box.
+    ///
+    /// is_link should be true if we're computing style for a link; that affects
+    /// how :visited handling is done.
+    pub fn compute_style_with_inputs(&self,
+                                     inputs: &CascadeInputs,
+                                     pseudo: Option<&PseudoElement>,
+                                     guards: &StylesheetGuards,
+                                     parent_style: &ComputedValues,
+                                     parent_style_ignoring_first_line: &ComputedValues,
+                                     layout_parent_style: &ComputedValues,
+                                     font_metrics: &FontMetricsProvider,
+                                     cascade_flags: CascadeFlags)
+                                     -> Arc<ComputedValues>
+    {
         // We need to compute visited values if we have visited rules or if our
         // parent has visited values.
         let visited_values = if inputs.visited_rules.is_some() || parent_style.get_visited_style().is_some() {
             // Slightly annoying: we know that inputs has either rules or
             // visited rules, but we can't do inputs.rules() up front because
             // maybe it just has visited rules, so can't unwrap_or.
             let rule_node = match inputs.visited_rules.as_ref() {
                 Some(rules) => rules,
                 None => inputs.rules.as_ref().unwrap(),
             };
-            // We want to use the visited bits (if any) from our parent style as
-            // our parent.
-            let inherited_style =
-                parent_style.get_visited_style().unwrap_or(parent_style);
+            let inherited_style;
+            let inherited_style_ignoring_first_line;
+            let layout_parent_style_for_visited;
+            if cascade_flags.contains(IS_LINK) {
+                // We just want to use our parent style as our parent.
+                inherited_style = parent_style;
+                inherited_style_ignoring_first_line = parent_style_ignoring_first_line;
+                layout_parent_style_for_visited = layout_parent_style;
+            } else {
+                // We want to use the visited bits (if any) from our parent
+                // style as our parent.
+                inherited_style =
+                    parent_style.get_visited_style().unwrap_or(parent_style);
+                inherited_style_ignoring_first_line =
+                    parent_style_ignoring_first_line.get_visited_style().unwrap_or(parent_style_ignoring_first_line);
+                layout_parent_style_for_visited =
+                    layout_parent_style.get_visited_style().unwrap_or(layout_parent_style);
+            }
 
-            // FIXME(emilio): The lack of layout_parent_style here could be
-            // worrying, but we're probably dropping the display fixup for
-            // pseudos other than before and after, so it's probably ok.
-            //
-            // (Though the flags don't indicate so!)
             let computed =
                 properties::cascade(&self.device,
-                                    Some(pseudo),
+                                    pseudo,
                                     rule_node,
                                     guards,
                                     Some(inherited_style),
-                                    Some(inherited_style),
-                                    Some(inherited_style),
+                                    Some(inherited_style_ignoring_first_line),
+                                    Some(layout_parent_style_for_visited),
                                     None,
                                     None,
                                     font_metrics,
-                                    CascadeFlags::empty(),
+                                    cascade_flags,
                                     self.quirks_mode);
 
             Some(computed)
         } else {
             None
         };
 
         // We may not have non-visited rules, if we only had visited ones.  In
         // that case we want to use the root rulenode for our non-visited rules.
         let rules = inputs.rules.as_ref().unwrap_or(self.rule_tree.root());
 
         // Read the comment on `precomputed_values_for_pseudo` to see why it's
         // difficult to assert that display: contents nodes never arrive here
         // (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))
+        properties::cascade(&self.device,
+                            pseudo,
+                            rules,
+                            guards,
+                            Some(parent_style),
+                            Some(parent_style_ignoring_first_line),
+                            Some(layout_parent_style),
+                            visited_values,
+                            None,
+                            font_metrics,
+                            cascade_flags,
+                            self.quirks_mode)
     }
 
     /// Computes the cascade inputs for a lazily-cascaded pseudo-element.
     ///
     /// See the documentation on lazy pseudo-elements in
     /// docs/components/style.md
     pub fn lazy_pseudo_rules<E>(&self,
                                 guards: &StylesheetGuards,
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -90,20 +90,21 @@ use style::gecko_bindings::structs::nsre
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties::style_structs;
 use style::invalidation::element::restyle_hints::{self, RestyleHint};
 use style::media_queries::{MediaList, parse_media_query_list};
 use style::parallel;
 use style::parser::{ParserContext, self};
-use style::properties::{ComputedValues, Importance};
-use style::properties::{IS_FIELDSET_CONTENT, LonghandIdSet};
+use style::properties::{CascadeFlags, ComputedValues, Importance};
+use style::properties::{IS_FIELDSET_CONTENT, IS_LINK, IS_VISITED_LINK, LonghandIdSet};
 use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId, ShorthandId};
 use style::properties::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, SourcePropertyDeclaration, StyleBuilder};
+use style::properties::PROHIBIT_DISPLAY_CONTENTS;
 use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue};
 use style::properties::animated_properties::compare_property_priority;
 use style::properties::parse_one_declaration_into;
 use style::rule_tree::StyleSource;
 use style::selector_parser::PseudoElementCascadeType;
 use style::sequential;
 use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::Atom;
@@ -174,23 +175,19 @@ unsafe fn dummy_url_data() -> &'static R
 }
 
 fn create_shared_context<'a>(global_style_data: &GlobalStyleData,
                              guard: &'a SharedRwLockReadGuard,
                              per_doc_data: &'a PerDocumentStyleDataImpl,
                              traversal_flags: TraversalFlags,
                              snapshot_map: &'a ServoElementSnapshotTable)
                              -> SharedStyleContext<'a> {
-    let visited_styles_enabled =
-        unsafe { bindings::Gecko_AreVisitedLinksEnabled() } &&
-        !per_doc_data.is_private_browsing_enabled();
-
     SharedStyleContext {
         stylist: &per_doc_data.stylist,
-        visited_styles_enabled: visited_styles_enabled,
+        visited_styles_enabled: per_doc_data.visited_styles_enabled(),
         options: global_style_data.options.clone(),
         guards: StylesheetGuards::same(guard),
         timer: Timer::new(),
         quirks_mode: per_doc_data.stylist.quirks_mode(),
         traversal_flags: traversal_flags,
         snapshot_map: snapshot_map,
     }
 }
@@ -1656,19 +1653,37 @@ fn get_pseudo_style(
                                 pseudo,
                                 &guards,
                                 inherited_styles,
                                 &metrics
                             )
                     })
                 },
                 _ => {
-                    debug_assert!(inherited_styles.is_none() ||
-                                  ptr::eq(inherited_styles.unwrap(),
-                                          &**styles.primary()));
+                    // Unfortunately, we can't assert that inherited_styles, if
+                    // present, is pointer-equal to styles.primary(), or even
+                    // equal in any meaningful way.  The way it can fail is as
+                    // follows.  Say we append an element with a ::before,
+                    // ::after, or ::first-line to a parent with a ::first-line,
+                    // such that the element ends up on the first line of the
+                    // parent (e.g. it's an inline-block in the case it has a
+                    // ::first-line, or any container in the ::before/::after
+                    // cases).  Then gecko will update its frame's style to
+                    // inherit from the parent's ::first-line.  The next time we
+                    // try to get the ::before/::after/::first-line style for
+                    // the kid, we'll likely pass in the frame's style as
+                    // inherited_styles, but that's not pointer-identical to
+                    // styles.primary(), because it got reparented.
+                    //
+                    // Now in practice this turns out to be OK, because all the
+                    // cases in which there's a mismatch go ahead and reparent
+                    // styles again as needed to make sure the ::first-line
+                    // affects all the things it should affect.  But it makes it
+                    // impossible to assert anything about the two styles
+                    // matching here, unfortunately.
                     styles.pseudos.get(&pseudo).cloned()
                 },
             }
         }
         PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
         PseudoElementCascadeType::Lazy => {
             debug_assert!(inherited_styles.is_none() ||
                           ptr::eq(inherited_styles.unwrap(),
@@ -2898,16 +2913,69 @@ pub extern "C" fn Servo_ResolveStyleLazi
         shared: &shared,
         thread_local: &mut tlc,
     };
 
     let styles = resolve_style(&mut context, element, rule_inclusion);
     finish(&styles).into()
 }
 
+#[no_mangle]
+pub extern "C" fn Servo_ReparentStyle(style_to_reparent: ServoStyleContextBorrowed,
+                                      parent_style: ServoStyleContextBorrowed,
+                                      parent_style_ignoring_first_line: ServoStyleContextBorrowed,
+                                      layout_parent_style: ServoStyleContextBorrowed,
+                                      element: RawGeckoElementBorrowedOrNull,
+                                      raw_data: RawServoStyleSetBorrowed)
+     -> ServoStyleContextStrong
+{
+    let global_style_data = &*GLOBAL_STYLE_DATA;
+    let guard = global_style_data.shared_lock.read();
+    let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
+    let inputs = CascadeInputs::new_from_style(style_to_reparent);
+    let metrics = get_metrics_provider_for_product();
+    let pseudo = style_to_reparent.pseudo();
+    let element = element.map(GeckoElement);
+
+    let mut cascade_flags = CascadeFlags::empty();
+    if style_to_reparent.is_anon_box() {
+        cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP);
+    }
+    if let Some(element) = element {
+        if element.is_link() {
+            cascade_flags.insert(IS_LINK);
+            if element.is_visited_link() &&
+                doc_data.visited_styles_enabled() {
+                cascade_flags.insert(IS_VISITED_LINK);
+            }
+        };
+
+        if element.is_native_anonymous() {
+            cascade_flags.insert(PROHIBIT_DISPLAY_CONTENTS);
+        }
+    }
+    if let Some(pseudo) = pseudo.as_ref() {
+        cascade_flags.insert(PROHIBIT_DISPLAY_CONTENTS);
+        if pseudo.is_fieldset_content() {
+            cascade_flags.insert(IS_FIELDSET_CONTENT);
+        }
+    }
+
+    doc_data.stylist
+        .compute_style_with_inputs(&inputs,
+                                   pseudo.as_ref(),
+                                   &StylesheetGuards::same(&guard),
+                                   parent_style,
+                                   parent_style_ignoring_first_line,
+                                   layout_parent_style,
+                                   &metrics,
+                                   cascade_flags)
+        .into()
+}
+
 #[cfg(feature = "gecko_debug")]
 fn simulate_compute_values_failure(property: &PropertyValuePair) -> bool {
     let p = property.mProperty;
     let id = get_property_id_from_nscsspropertyid!(p, false);
     id.as_shorthand().is_ok() && property.mSimulateComputeValuesFailure
 }
 
 #[cfg(not(feature = "gecko_debug"))]