Bug 1349651 - Add Servo_ResolveRuleNode and Servo_HasAuthorSpecifiedRules glue. r=bholley draft
authorMatt Brubeck <mbrubeck@mozilla.com>
Mon, 08 May 2017 16:58:04 -0700
changeset 574512 dbdfdf705f4834794480ba7a1eaa37318accb10a
parent 574435 e0955584782e6eb13bb1e7b197d3064660bb756f
child 574513 939f8ce062723b73e8d01212e30845c9a56f42f2
push id57739
push userbmo:mbrubeck@mozilla.com
push dateTue, 09 May 2017 00:00:11 +0000
reviewersbholley
bugs1349651
milestone55.0a1
Bug 1349651 - Add Servo_ResolveRuleNode and Servo_HasAuthorSpecifiedRules glue. r=bholley MozReview-Commit-ID: IXRcwtnMIb7
layout/base/nsPresContext.cpp
layout/style/ServoArcTypeList.h
layout/style/ServoBindingList.h
layout/style/ServoStyleSet.cpp
layout/style/ServoStyleSet.h
servo/components/style/gecko/arc_types.rs
servo/components/style/gecko/generated/bindings.rs
servo/components/style/rule_tree/mod.rs
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -70,16 +70,17 @@
 #include "nsFrameLoader.h"
 #include "mozilla/dom/FontFaceSet.h"
 #include "nsContentUtils.h"
 #include "nsPIWindowRoot.h"
 #include "mozilla/Preferences.h"
 #include "gfxTextRun.h"
 #include "nsFontFaceUtils.h"
 #include "nsLayoutStylesheetCache.h"
+#include "mozilla/ServoBindings.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/PerformanceTiming.h"
 #include "mozilla/layers/APZThreadUtils.h"
 
 // Needed for Start/Stop of Image Animation
@@ -2207,20 +2208,40 @@ nsPresContext::UpdateIsChrome()
   mIsChrome = mContainer &&
               nsIDocShellTreeItem::typeChrome == mContainer->ItemType();
 }
 
 bool
 nsPresContext::HasAuthorSpecifiedRules(const nsIFrame *aFrame,
                                        uint32_t ruleTypeMask) const
 {
-  return
-    nsRuleNode::HasAuthorSpecifiedRules(aFrame->StyleContext(),
-                                        ruleTypeMask,
-                                        UseDocumentColors());
+  if (mShell->StyleSet()->IsGecko()) {
+    return
+      nsRuleNode::HasAuthorSpecifiedRules(aFrame->StyleContext(),
+                                          ruleTypeMask,
+                                          UseDocumentColors());
+  } else {
+    Element *elem = aFrame->GetContent()->AsElement();
+    if (elem->IsNativeAnonymous()) {
+      elem = nsContentUtils::GetClosestNonNativeAnonymousAncestor(elem);
+    }
+    if (!elem->HasServoData()) {
+      return false;
+    }
+
+    nsIAtom *pseudoTag = aFrame->StyleContext()->GetPseudo();
+    RefPtr<RawServoRuleNode> ruleNode;
+    ruleNode = mShell->StyleSet()->AsServo()->ResolveRuleNode(elem, pseudoTag);
+    if (!ruleNode) {
+      return false;
+    }
+    return Servo_HasAuthorSpecifiedRules(ruleNode,
+                                         ruleTypeMask,
+                                         UseDocumentColors());
+  }
 }
 
 gfxUserFontSet*
 nsPresContext::GetUserFontSet(bool aFlushUserFontSet)
 {
   return mDocument->GetUserFontSet(aFlushUserFontSet);
 }
 
--- a/layout/style/ServoArcTypeList.h
+++ b/layout/style/ServoArcTypeList.h
@@ -13,8 +13,9 @@ SERVO_ARC_TYPE(DeclarationBlock, RawServ
 SERVO_ARC_TYPE(StyleRule, RawServoStyleRule)
 SERVO_ARC_TYPE(ImportRule, RawServoImportRule)
 SERVO_ARC_TYPE(AnimationValue, RawServoAnimationValue)
 SERVO_ARC_TYPE(MediaList, RawServoMediaList)
 SERVO_ARC_TYPE(MediaRule, RawServoMediaRule)
 SERVO_ARC_TYPE(NamespaceRule, RawServoNamespaceRule)
 SERVO_ARC_TYPE(PageRule, RawServoPageRule)
 SERVO_ARC_TYPE(SupportsRule, RawServoSupportsRule)
+SERVO_ARC_TYPE(RuleNode, RawServoRuleNode)
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -381,16 +381,23 @@ SERVO_BINDING_FUNC(Servo_NoteExplicitHin
 SERVO_BINDING_FUNC(Servo_TakeChangeHint, nsChangeHint, RawGeckoElementBorrowed element)
 SERVO_BINDING_FUNC(Servo_ResolveStyle, ServoComputedValuesStrong,
                    RawGeckoElementBorrowed element,
                    RawServoStyleSetBorrowed set,
                    bool allow_stale)
 SERVO_BINDING_FUNC(Servo_ResolvePseudoStyle, ServoComputedValuesStrong,
                    RawGeckoElementBorrowed element, nsIAtom* pseudo_tag,
                    bool is_probe, RawServoStyleSetBorrowed set)
+SERVO_BINDING_FUNC(Servo_ResolveRuleNode, RawServoRuleNodeStrong,
+                   RawGeckoElementBorrowed element, nsIAtom* pseudo_tag,
+                   RawServoStyleSetBorrowed set)
+SERVO_BINDING_FUNC(Servo_HasAuthorSpecifiedRules, bool,
+                   RawServoRuleNodeBorrowed rules,
+                   uint32_t rule_type_mask,
+                   bool author_colors_allowed)
 
 // Resolves style for an element or pseudo-element without processing pending
 // restyles first. The Element and its ancestors may be unstyled, have pending
 // restyles, or be in a display:none subtree. Styles are cached when possible,
 // though caching is not possible within display:none subtrees, and the styles
 // may be invalidated by already-scheduled restyles.
 //
 // The tree must be in a consistent state such that a normal traversal could be
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -540,16 +540,23 @@ ServoStyleSet::ResolveNonInheritingAnony
 
   RefPtr<nsStyleContext> retval =
     GetContext(computedValues.forget(), nullptr, aPseudoTag,
                CSSPseudoElementType::NonInheritingAnonBox, nullptr);
   cache = retval;
   return retval.forget();
 }
 
+already_AddRefed<RawServoRuleNode>
+ServoStyleSet::ResolveRuleNode(dom::Element *aElement, nsIAtom *aPseudoTag)
+{
+  MOZ_ASSERT(aElement);
+  return Servo_ResolveRuleNode(aElement, aPseudoTag, mRawSet.get()).Consume();
+}
+
 // manage the set of style sheets in the style set
 nsresult
 ServoStyleSet::AppendStyleSheet(SheetType aType,
                                 ServoStyleSheet* aSheet)
 {
   MOZ_ASSERT(aSheet);
   MOZ_ASSERT(aSheet->IsApplicable());
   MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -33,16 +33,17 @@ class ServoStyleSheet;
 struct Keyframe;
 struct ServoComputedValuesWithParent;
 } // namespace mozilla
 class nsIContent;
 class nsIDocument;
 class nsStyleContext;
 class nsPresContext;
 struct nsTimingFunction;
+struct RawServoRuleNode;
 struct TreeMatchContext;
 
 namespace mozilla {
 
 /**
  * The set of style sheets that apply to a document, backed by a Servo
  * Stylist.  A ServoStyleSet contains ServoStyleSheets.
  */
@@ -187,16 +188,20 @@ public:
                                      nsStyleContext* aParentContext);
 
   // Get a style context for an anonymous box that does not inherit style from
   // anything.  aPseudoTag is the pseudo-tag to use and must be non-null.  It
   // must be an anon box, and must be a non-inheriting one.
   already_AddRefed<nsStyleContext>
   ResolveNonInheritingAnonymousBoxStyle(nsIAtom* aPseudoTag);
 
+  // Get the rule node for a (pseudo-)element, resolving it lazily if needed.
+  already_AddRefed<RawServoRuleNode>
+  ResolveRuleNode(dom::Element *aElement, nsIAtom *aPseudoTag);
+
   // manage the set of style sheets in the style set
   nsresult AppendStyleSheet(SheetType aType, ServoStyleSheet* aSheet);
   nsresult PrependStyleSheet(SheetType aType, ServoStyleSheet* aSheet);
   nsresult RemoveStyleSheet(SheetType aType, ServoStyleSheet* aSheet);
   nsresult ReplaceSheets(SheetType aType,
                          const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets);
   nsresult InsertStyleSheetBefore(SheetType aType,
                                   ServoStyleSheet* aNewSheet,
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -4,27 +4,30 @@
 
 //! This file lists all arc FFI types and defines corresponding addref
 //! and release functions. This list corresponds to ServoArcTypeList.h
 //! file in Gecko.
 
 #![allow(non_snake_case, missing_docs)]
 
 use gecko_bindings::bindings::{RawServoMediaList, RawServoMediaRule, RawServoNamespaceRule, RawServoPageRule};
+use gecko_bindings::bindings::{RawServoRuleNode, RawServoRuleNodeStrong};
 use gecko_bindings::bindings::{RawServoStyleSheet, RawServoImportRule, RawServoSupportsRule};
 use gecko_bindings::bindings::{ServoComputedValues, ServoCssRules};
 use gecko_bindings::structs::{RawServoDeclarationBlock, RawServoStyleRule};
 use gecko_bindings::structs::RawServoAnimationValue;
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI};
 use media_queries::MediaList;
 use properties::{ComputedValues, PropertyDeclarationBlock};
 use properties::animated_properties::AnimationValue;
 use shared_lock::Locked;
+use std::{mem, ptr};
 use stylesheets::{CssRules, Stylesheet, StyleRule, ImportRule, MediaRule};
 use stylesheets::{NamespaceRule, PageRule, SupportsRule};
+use rule_tree::StrongRuleNode;
 
 macro_rules! impl_arc_ffi {
     ($servo_type:ty => $gecko_type:ty [$addref:ident, $release:ident]) => {
         unsafe impl HasFFI for $servo_type {
             type FFIType = $gecko_type;
         }
         unsafe impl HasArcFFI for $servo_type {}
 
@@ -70,8 +73,33 @@ impl_arc_ffi!(Locked<MediaRule> => RawSe
 impl_arc_ffi!(Locked<NamespaceRule> => RawServoNamespaceRule
               [Servo_NamespaceRule_AddRef, Servo_NamespaceRule_Release]);
 
 impl_arc_ffi!(Locked<PageRule> => RawServoPageRule
               [Servo_PageRule_AddRef, Servo_PageRule_Release]);
 
 impl_arc_ffi!(Locked<SupportsRule> => RawServoSupportsRule
               [Servo_SupportsRule_AddRef, Servo_SupportsRule_Release]);
+
+// RuleNode is a Arc-like type but it does not use Arc.
+
+impl StrongRuleNode {
+    pub fn into_strong(self) -> RawServoRuleNodeStrong {
+        let ptr = self.ptr();
+        mem::forget(self);
+        unsafe { mem::transmute(ptr) }
+    }
+
+    pub fn from_ffi<'a>(ffi: &'a &RawServoRuleNode) -> &'a Self {
+        unsafe { &*(ffi as *const &RawServoRuleNode as *const StrongRuleNode) }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_RuleNode_AddRef(obj: &RawServoRuleNode) {
+    mem::forget(StrongRuleNode::from_ffi(&obj).clone());
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_RuleNode_Release(obj: &RawServoRuleNode) {
+    let ptr = StrongRuleNode::from_ffi(&obj);
+    ptr::read(ptr as *const StrongRuleNode);
+}
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -242,16 +242,21 @@ pub type RawServoPageRuleBorrowed<'a> = 
 pub type RawServoPageRuleBorrowedOrNull<'a> = Option<&'a RawServoPageRule>;
 enum RawServoPageRuleVoid { }
 pub struct RawServoPageRule(RawServoPageRuleVoid);
 pub type RawServoSupportsRuleStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoSupportsRule>;
 pub type RawServoSupportsRuleBorrowed<'a> = &'a RawServoSupportsRule;
 pub type RawServoSupportsRuleBorrowedOrNull<'a> = Option<&'a RawServoSupportsRule>;
 enum RawServoSupportsRuleVoid { }
 pub struct RawServoSupportsRule(RawServoSupportsRuleVoid);
+pub type RawServoRuleNodeStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoRuleNode>;
+pub type RawServoRuleNodeBorrowed<'a> = &'a RawServoRuleNode;
+pub type RawServoRuleNodeBorrowedOrNull<'a> = Option<&'a RawServoRuleNode>;
+enum RawServoRuleNodeVoid { }
+pub struct RawServoRuleNode(RawServoRuleNodeVoid);
 pub type RawServoStyleSetOwned = ::gecko_bindings::sugar::ownership::Owned<RawServoStyleSet>;
 pub type RawServoStyleSetOwnedOrNull = ::gecko_bindings::sugar::ownership::OwnedOrNull<RawServoStyleSet>;
 pub type RawServoStyleSetBorrowed<'a> = &'a RawServoStyleSet;
 pub type RawServoStyleSetBorrowedOrNull<'a> = Option<&'a RawServoStyleSet>;
 pub type RawServoStyleSetBorrowedMut<'a> = &'a mut RawServoStyleSet;
 pub type RawServoStyleSetBorrowedMutOrNull<'a> = Option<&'a mut RawServoStyleSet>;
 enum RawServoStyleSetVoid { }
 pub struct RawServoStyleSet(RawServoStyleSetVoid);
@@ -404,16 +409,22 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_SupportsRule_AddRef(ptr: RawServoSupportsRuleBorrowed);
 }
 extern "C" {
     pub fn Servo_SupportsRule_Release(ptr: RawServoSupportsRuleBorrowed);
 }
 extern "C" {
+    pub fn Servo_RuleNode_AddRef(ptr: RawServoRuleNodeBorrowed);
+}
+extern "C" {
+    pub fn Servo_RuleNode_Release(ptr: RawServoRuleNodeBorrowed);
+}
+extern "C" {
     pub fn Servo_StyleSet_Drop(ptr: RawServoStyleSetOwned);
 }
 extern "C" {
     pub fn Gecko_ChildrenCount(node: RawGeckoNodeBorrowed) -> u32;
 }
 extern "C" {
     pub fn Gecko_NodeIsElement(node: RawGeckoNodeBorrowed) -> bool;
 }
@@ -2168,16 +2179,26 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
                                     pseudo_tag: *mut nsIAtom, is_probe: bool,
                                     set: RawServoStyleSetBorrowed)
      -> ServoComputedValuesStrong;
 }
 extern "C" {
+    pub fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed,
+                                 pseudo_tag: *mut nsIAtom,
+                                 set: RawServoStyleSetBorrowed)
+     -> RawServoRuleNodeStrong;
+}
+extern "C" {
+    pub fn Servo_HasAuthorSpecifiedRules(rules: RawServoRuleNodeStrong,
+                                         rule_type_mask: u32) -> bool;
+}
+extern "C" {
     pub fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
                                     pseudo_tag: *mut nsIAtom,
                                     set: RawServoStyleSetBorrowed)
      -> ServoComputedValuesStrong;
 }
 extern "C" {
     pub fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
                                  set: RawServoStyleSetBorrowed,
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -372,17 +372,18 @@ impl CascadeLevel {
             CascadeLevel::SMILOverride |
             CascadeLevel::Animations |
             CascadeLevel::Transitions => true,
             _ => false,
         }
     }
 }
 
-struct RuleNode {
+/// A node in the rule tree.
+pub struct RuleNode {
     /// The root node. Only the root has no root pointer, for obvious reasons.
     root: Option<WeakRuleNode>,
 
     /// The parent rule node. Only the root has no parent.
     parent: Option<StrongRuleNode>,
 
     /// The actual style source, either coming from a selector in a StyleRule,
     /// or a raw property declaration block (like the style attribute).
@@ -643,17 +644,18 @@ impl StrongRuleNode {
                 }
             }
 
             // Try again inserting after the new last child.
             last = Some(strong);
         }
     }
 
-    fn ptr(&self) -> *mut RuleNode {
+    /// Raw pointer to the RuleNode
+    pub fn ptr(&self) -> *mut RuleNode {
         self.ptr
     }
 
     fn get(&self) -> &RuleNode {
         if cfg!(debug_assertions) {
             let node = unsafe { &*self.ptr };
             assert!(node.refcount.load(Ordering::Relaxed) > 0);
         }
@@ -785,16 +787,23 @@ impl StrongRuleNode {
 
     unsafe fn maybe_gc(&self) {
         debug_assert!(self.get().is_root(), "Can't call GC on a non-root node!");
         if self.get().free_count.load(Ordering::Relaxed) > RULE_TREE_GC_INTERVAL {
             self.gc();
         }
     }
 
+    /// TODO: docs
+    pub fn has_author_specified_rules(&self,
+                                      _rule_type_mask: u32,
+                                      _author_colors_allowed: bool) -> bool {
+        true // TODO
+    }
+
     /// Returns true if there is either animation or transition level rule.
     pub fn has_animation_or_transition_rules(&self) -> bool {
         self.self_and_ancestors()
             .take_while(|node| node.cascade_level() >= CascadeLevel::SMILOverride)
             .any(|node| node.cascade_level().is_animation())
     }
 }
 
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -485,23 +485,56 @@ impl Stylist {
                                                   pseudo: &PseudoElement,
                                                   parent: &Arc<ComputedValues>,
                                                   font_metrics: &FontMetricsProvider)
                                                   -> Option<ComputedStyle>
         where E: TElement +
                  fmt::Debug +
                  PresentationalHintsSynthetizer
     {
+        let rule_node = match self.lazy_pseudo_rules(guards, element, pseudo) {
+            Some(rule_node) => rule_node,
+            None => return None
+        };
+
+        // 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").
+        let computed =
+            properties::cascade(&self.device,
+                                &rule_node,
+                                guards,
+                                Some(&**parent),
+                                Some(&**parent),
+                                None,
+                                &RustLogReporter,
+                                font_metrics,
+                                CascadeFlags::empty(),
+                                self.quirks_mode);
+
+        Some(ComputedStyle::new(rule_node, Arc::new(computed)))
+    }
+
+    /// Computes the rule node 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,
+                                element: &E,
+                                pseudo: &PseudoElement)
+                                -> Option<StrongRuleNode>
+        where E: TElement + fmt::Debug + PresentationalHintsSynthetizer
+    {
         debug_assert!(pseudo.is_lazy());
         if self.pseudos_map.get(pseudo).is_none() {
-            return None;
+            return None
         }
 
-        let mut declarations = vec![];
-
         // Apply the selector flags. We should be in sequential mode
         // already, so we can directly apply the parent flags.
         let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
             if cfg!(feature = "servo") {
                 // Servo calls this function from the worker, but only for internal
                 // pseudos, so we should never generate selector flags here.
                 unreachable!("internal pseudo generated slow selector flags?");
             }
@@ -517,51 +550,34 @@ impl Stylist {
             if !parent_flags.is_empty() {
                 if let Some(p) = element.parent_element() {
                     unsafe { p.set_selector_flags(parent_flags); }
                 }
             }
         };
 
 
+        let mut declarations = vec![];
         self.push_applicable_declarations(element,
                                           None,
                                           None,
                                           None,
                                           AnimationRules(None, None),
                                           Some(pseudo),
                                           guards,
                                           &mut declarations,
                                           &mut set_selector_flags);
-
         if declarations.is_empty() {
             return None
         }
 
-        let rule_node =
-            self.rule_tree.insert_ordered_rules(
-                declarations.into_iter().map(|a| (a.source, a.level)));
-
-        // 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").
-        let computed =
-            properties::cascade(&self.device,
-                                &rule_node,
-                                guards,
-                                Some(&**parent),
-                                Some(&**parent),
-                                None,
-                                &RustLogReporter,
-                                font_metrics,
-                                CascadeFlags::empty(),
-                                self.quirks_mode);
-
-        Some(ComputedStyle::new(rule_node, Arc::new(computed)))
+        let rule_node = self.rule_tree.insert_ordered_rules(declarations.into_iter().map(|a| {
+            (a.source, a.level)
+        }));
+        Some(rule_node)
     }
 
     /// Set a given device, which may change the styles that apply to the
     /// document.
     ///
     /// This means that we may need to rebuild style data even if the
     /// stylesheets haven't changed.
     ///
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -27,16 +27,17 @@ use style::gecko::traversal::RecalcStyle
 use style::gecko::wrapper::GeckoElement;
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::{RawGeckoKeyframeListBorrowed, RawGeckoKeyframeListBorrowedMut};
 use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
 use style::gecko_bindings::bindings::{RawServoMediaList, RawServoMediaListBorrowed, RawServoMediaListStrong};
 use style::gecko_bindings::bindings::{RawServoMediaRule, RawServoMediaRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoPageRule, RawServoPageRuleBorrowed};
+use style::gecko_bindings::bindings::{RawServoRuleNodeBorrowed, RawServoRuleNodeStrong};
 use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned};
 use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed};
 use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong};
 use style::gecko_bindings::bindings::{RawServoSupportsRule, RawServoSupportsRuleBorrowed};
 use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong};
 use style::gecko_bindings::bindings::{nsACString, nsAString};
 use style::gecko_bindings::bindings::Gecko_AnimationAppendKeyframe;
 use style::gecko_bindings::bindings::RawGeckoAnimationPropertySegmentBorrowed;
@@ -72,17 +73,17 @@ use style::media_queries::{MediaList, pa
 use style::parallel;
 use style::parser::{LengthParsingMode, ParserContext};
 use style::properties::{CascadeFlags, ComputedValues, Importance, ParsedDeclaration, StyleBuilder};
 use style::properties::{PropertyDeclarationBlock, PropertyId};
 use style::properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP;
 use style::properties::animated_properties::{AnimationValue, ComputeDistance, Interpolate, TransitionProperty};
 use style::properties::parse_one_declaration;
 use style::restyle_hints::{self, RestyleHint};
-use style::rule_tree::StyleSource;
+use style::rule_tree::{StrongRuleNode, StyleSource};
 use style::selector_parser::PseudoElementCascadeType;
 use style::sequential;
 use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::Atom;
 use style::style_adjuster::StyleAdjuster;
 use style::stylearc::Arc;
 use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers};
 use style::stylesheets::{ImportRule, MediaRule, NamespaceRule, Origin};
@@ -934,16 +935,47 @@ pub extern "C" fn Servo_ComputedValues_G
     let metrics = get_metrics_provider_for_product();
     data.stylist.precomputed_values_for_pseudo(&guards, &pseudo, maybe_parent,
                                                cascade_flags, &metrics)
         .values.unwrap()
         .into_strong()
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed,
+                                        pseudo_tag: *mut nsIAtom,
+                                        raw_data: RawServoStyleSetBorrowed)
+     -> RawServoRuleNodeStrong
+{
+    let element = GeckoElement(element);
+    let doc_data = PerDocumentStyleData::from_ffi(raw_data);
+    let guard = (*GLOBAL_STYLE_DATA).shared_lock.read();
+
+    let data = element.mutate_data().unwrap();
+    let styles = match data.get_styles() {
+        Some(styles) => styles,
+        None => {
+            warn!("Calling Servo_ResolveRuleNode on unstyled element");
+            return Strong::null()
+        }
+    };
+
+    let maybe_rules = if pseudo_tag.is_null() {
+        Some(styles.primary.rules.clone())
+    } else {
+        get_pseudo_rule_node(&guard, element, pseudo_tag, styles, doc_data)
+    };
+
+    match maybe_rules {
+        Some(rule_node) => rule_node.into_strong(),
+        None => Strong::null(),
+    }
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
                                            pseudo_tag: *mut nsIAtom, is_probe: bool,
                                            raw_data: RawServoStyleSetBorrowed)
      -> ServoComputedValuesStrong
 {
     let element = GeckoElement(element);
     let data = unsafe { element.ensure_data() }.borrow_mut();
     let doc_data = PerDocumentStyleData::from_ffi(raw_data);
@@ -962,16 +994,45 @@ pub extern "C" fn Servo_ResolvePseudoSty
     let guard = global_style_data.shared_lock.read();
     match get_pseudo_style(&guard, element, pseudo_tag, data.styles(), doc_data) {
         Some(values) => values.into_strong(),
         None if !is_probe => data.styles().primary.values().clone().into_strong(),
         None => Strong::null(),
     }
 }
 
+#[no_mangle]
+pub extern "C" fn Servo_HasAuthorSpecifiedRules(rule_node: RawServoRuleNodeBorrowed,
+                                                rule_type_mask: u32,
+                                                author_colors_allowed: bool)
+    -> bool
+{
+    StrongRuleNode::from_ffi(&rule_node)
+        .has_author_specified_rules(rule_type_mask, author_colors_allowed)
+}
+
+fn get_pseudo_rule_node(guard: &SharedRwLockReadGuard,
+                        element: GeckoElement,
+                        pseudo_tag: *mut nsIAtom,
+                        styles: &ElementStyles,
+                        doc_data: &PerDocumentStyleData)
+                        -> Option<StrongRuleNode>
+{
+    let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false);
+    match pseudo.cascade_type() {
+        PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.rules.clone()),
+        PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
+        PseudoElementCascadeType::Lazy => {
+            let d = doc_data.borrow_mut();
+            let guards = StylesheetGuards::same(guard);
+            d.stylist.lazy_pseudo_rules(&guards, &element, &pseudo)
+        },
+    }
+}
+
 fn get_pseudo_style(guard: &SharedRwLockReadGuard,
                     element: GeckoElement,
                     pseudo_tag: *mut nsIAtom,
                     styles: &ElementStyles,
                     doc_data: &PerDocumentStyleData)
                     -> Option<Arc<ComputedValues>>
 {
     let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false);