Bug 1290276 - Implement XBL <stylesheet> (WIP) draft
authorTing-Yu Lin <tlin@mozilla.com>
Thu, 04 May 2017 18:58:26 +0800
changeset 579890 9f82e4410a00fefd1f2f823728b7d05c9db66484
parent 579887 99dbc89ec5c1b10c239491f4b21f939456f9a9fc
child 629146 0b12dac1b040197882f62187b9916db69dbd2775
push id59400
push userbmo:tlin@mozilla.com
push dateWed, 17 May 2017 23:55:25 +0000
bugs1290276
milestone55.0a1
Bug 1290276 - Implement XBL <stylesheet> (WIP) MozReview-Commit-ID: 5VRPsCHYTG3
dom/xbl/nsXBLBinding.cpp
dom/xbl/nsXBLBinding.h
dom/xbl/nsXBLPrototypeBinding.cpp
dom/xbl/nsXBLPrototypeBinding.h
dom/xbl/nsXBLPrototypeResources.cpp
dom/xbl/nsXBLPrototypeResources.h
dom/xbl/nsXBLResourceLoader.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/style/ServoBindingTypes.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
servo/components/style/build_gecko.rs
servo/components/style/dom.rs
servo/components/style/gecko/wrapper.rs
servo/components/style/rule_tree/mod.rs
servo/components/style/stylist.rs
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -866,16 +866,22 @@ nsXBLBinding::WalkRules(nsIStyleRuleProc
   if (mNextBinding)
     mNextBinding->WalkRules(aFunc, aData);
 
   nsIStyleRuleProcessor *rules = mPrototypeBinding->GetRuleProcessor();
   if (rules)
     (*aFunc)(rules, aData);
 }
 
+RawServoStyleSet*
+nsXBLBinding::GetRawServoStyleSet()
+{
+  return mPrototypeBinding->GetRawServoStyleSet();
+}
+
 // Internal helper methods ////////////////////////////////////////////////////////////////
 
 // Get or create a WeakMap object on a given XBL-hosting global.
 //
 // The scheme is as follows. XBL-hosting globals (either privileged content
 // Windows or XBL scopes) get two lazily-defined WeakMap properties. Each
 // WeakMap is keyed by the grand-proto - i.e. the original prototype of the
 // content before it was bound, and the prototype of the class object that we
--- a/dom/xbl/nsXBLBinding.h
+++ b/dom/xbl/nsXBLBinding.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsXBLBinding_h_
 #define nsXBLBinding_h_
 
+#include "mozilla/ServoBindings.h"
 #include "nsXBLService.h"
 #include "nsCOMPtr.h"
 #include "nsINodeList.h"
 #include "nsIStyleRuleProcessor.h"
 #include "nsClassHashtable.h"
 #include "nsTArray.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupportsImpl.h"
@@ -124,16 +125,18 @@ public:
 
   void AttributeChanged(nsIAtom* aAttribute, int32_t aNameSpaceID,
                         bool aRemoveFlag, bool aNotify);
 
   void ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument);
 
   void WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData);
 
+  RawServoStyleSet* GetRawServoStyleSet();
+
   static nsresult DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> obj,
                                 const nsAFlatString& aClassName,
                                 nsXBLPrototypeBinding* aProtoBinding,
                                 JS::MutableHandle<JSObject*> aClassObject,
                                 bool* aNew);
 
   bool AllowScripts();
 
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -564,16 +564,22 @@ nsXBLPrototypeBinding::GetRuleProcessor(
 {
   if (mResources) {
     return mResources->GetRuleProcessor();
   }
 
   return nullptr;
 }
 
+RawServoStyleSet*
+nsXBLPrototypeBinding::GetRawServoStyleSet() const
+{
+  return mResources ? mResources->GetRawServoStyleSet() : nullptr;
+}
+
 void
 nsXBLPrototypeBinding::EnsureAttributeTable()
 {
   if (!mAttributeTable) {
     mAttributeTable =
         new nsClassHashtable<nsUint32HashKey, InnerAttributeTable>(2);
   }
 }
--- a/dom/xbl/nsXBLPrototypeBinding.h
+++ b/dom/xbl/nsXBLPrototypeBinding.h
@@ -124,16 +124,17 @@ public:
   void RemoveStyleSheet(mozilla::StyleSheet* aSheet);
   void InsertStyleSheetAt(size_t aIndex, mozilla::StyleSheet* aSheet);
   mozilla::StyleSheet* StyleSheetAt(size_t aIndex) const;
   size_t SheetCount() const;
   bool HasStyleSheets() const;
   void AppendStyleSheetsTo(nsTArray<mozilla::StyleSheet*>& aResult) const;
 
   nsIStyleRuleProcessor* GetRuleProcessor();
+  RawServoStyleSet* GetRawServoStyleSet() const;
 
   nsresult FlushSkinSheets();
 
   nsIAtom* GetBaseTag(int32_t* aNamespaceID);
   void SetBaseTag(int32_t aNamespaceID, nsIAtom* aTag);
 
   bool ImplementsInterface(REFNSIID aIID) const;
 
--- a/dom/xbl/nsXBLPrototypeResources.cpp
+++ b/dom/xbl/nsXBLPrototypeResources.cpp
@@ -14,16 +14,18 @@
 #include "nsIDocumentObserver.h"
 #include "mozilla/css/Loader.h"
 #include "nsIURI.h"
 #include "nsLayoutCID.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsStyleSet.h"
 #include "mozilla/dom/URL.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/ServoBindings.h"
+#include "mozilla/ServoBindingTypes.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 
 using namespace mozilla;
 using mozilla::dom::IsChromeURI;
 
 nsXBLPrototypeResources::nsXBLPrototypeResources(nsXBLPrototypeBinding* aBinding)
 {
@@ -157,16 +159,28 @@ nsXBLPrototypeResources::GatherRuleProce
   }
   mRuleProcessor = new nsCSSRuleProcessor(Move(sheets),
                                           SheetType::Doc,
                                           nullptr,
                                           mRuleProcessor);
 }
 
 void
+nsXBLPrototypeResources::ComputeRawServoStyleSet(nsPresContext* aPresContext)
+{
+  static uint32_t uniqueIDCounter = 0;
+  mRawSet.reset(Servo_StyleSet_Init(aPresContext));
+  for (StyleSheet* sheet : mStyleSheetList) {
+    const RawServoStyleSheet* rawSheet = sheet->AsServo()->RawSheet();
+    Servo_StyleSet_AppendStyleSheet(mRawSet.get(), rawSheet, uniqueIDCounter++);
+  }
+  Servo_StyleSet_FlushStyleSheets(mRawSet.get());
+}
+
+void
 nsXBLPrototypeResources::AppendStyleSheet(StyleSheet* aSheet)
 {
   mStyleSheetList.AppendElement(aSheet);
 }
 
 void
 nsXBLPrototypeResources::RemoveStyleSheet(StyleSheet* aSheet)
 {
--- a/dom/xbl/nsXBLPrototypeResources.h
+++ b/dom/xbl/nsXBLPrototypeResources.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsXBLPrototypeResources_h__
 #define nsXBLPrototypeResources_h__
 
+#include "mozilla/ServoBindings.h"
 #include "mozilla/StyleSheet.h"
 #include "nsICSSLoaderObserver.h"
 
 class nsCSSRuleProcessor;
 class nsIAtom;
 class nsIContent;
 class nsXBLPrototypeBinding;
 class nsXBLResourceLoader;
@@ -53,20 +54,26 @@ public:
    * Recreates mRuleProcessor to represent the current list of style sheets
    * stored in mStyleSheetList.  (Named GatherRuleProcessor to parallel
    * nsStyleSet::GatherRuleProcessors.)
    */
   void GatherRuleProcessor();
 
   nsCSSRuleProcessor* GetRuleProcessor() const { return mRuleProcessor; }
 
+  RawServoStyleSet* GetRawServoStyleSet() const { return mRawSet.get(); }
+
+  void ComputeRawServoStyleSet(nsPresContext* aPresContext);
+
 private:
   // A loader object. Exists only long enough to load resources, and then it dies.
   RefPtr<nsXBLResourceLoader> mLoader;
 
   // A list of loaded stylesheets for this binding.
   nsTArray<RefPtr<mozilla::StyleSheet>> mStyleSheetList;
 
   // The list of stylesheets converted to a rule processor.
   RefPtr<nsCSSRuleProcessor> mRuleProcessor;
+
+  mozilla::UniquePtr<RawServoStyleSet> mRawSet;
 };
 
 #endif
--- a/dom/xbl/nsXBLResourceLoader.cpp
+++ b/dom/xbl/nsXBLResourceLoader.cpp
@@ -109,17 +109,17 @@ nsXBLResourceLoader::LoadResources(nsICo
 
     if (NS_FAILED(NS_NewURI(getter_AddRefs(url), curr->mSrc,
                             doc->GetDocumentCharacterSet().get(), docURL)))
       continue;
 
     if (curr->mType == nsGkAtoms::image) {
       // Now kick off the image load...
       // Passing nullptr for pretty much everything -- cause we don't care!
-      // XXX: initialDocumentURI is nullptr! 
+      // XXX: initialDocumentURI is nullptr!
       RefPtr<imgRequestProxy> req;
       nsContentUtils::LoadImage(url, doc, doc, docPrincipal, docURL,
                                 doc->GetReferrerPolicy(), nullptr,
                                 nsIRequest::LOAD_BACKGROUND, EmptyString(),
                                 getter_AddRefs(req));
     }
     else if (curr->mType == nsGkAtoms::stylesheet) {
       // Kick off the load of the stylesheet.
@@ -135,16 +135,23 @@ nsXBLResourceLoader::LoadResources(nsICo
                                     nsIScriptSecurityManager::ALLOW_CHROME);
         if (NS_SUCCEEDED(rv)) {
           RefPtr<StyleSheet> sheet;
           rv = cssLoader->LoadSheetSync(url, &sheet);
           NS_ASSERTION(NS_SUCCEEDED(rv), "Load failed!!!");
           if (NS_SUCCEEDED(rv))
           {
             rv = StyleSheetLoaded(sheet, false, NS_OK);
+
+            // XXX: How about those style sheets loaded asynchronously?
+            if (boundDoc->IsStyledByServo()) {
+               mResources->ComputeRawServoStyleSet(
+                 boundDoc->GetShell()->GetPresContext());
+            }
+
             NS_ASSERTION(NS_SUCCEEDED(rv), "Processing the style sheet failed!!!");
           }
         }
       }
       else
       {
         rv = cssLoader->LoadSheet(url, false, docPrincipal, EmptyCString(), this);
         if (NS_SUCCEEDED(rv))
@@ -167,47 +174,49 @@ NS_IMETHODIMP
 nsXBLResourceLoader::StyleSheetLoaded(StyleSheet* aSheet,
                                       bool aWasAlternate,
                                       nsresult aStatus)
 {
   if (!mResources) {
     // Our resources got destroyed -- just bail out
     return NS_OK;
   }
-   
+
   mResources->AppendStyleSheet(aSheet);
 
   if (!mInLoadResourcesFunc)
     mPendingSheets--;
-  
+
   if (mPendingSheets == 0) {
-    // All stylesheets are loaded.  
-    mResources->GatherRuleProcessor();
+    // All stylesheets are loaded.
+    if (aSheet->IsGecko()) {
+      mResources->GatherRuleProcessor();
+    }
 
     // XXX Check for mPendingScripts when scripts also come online.
     if (!mInLoadResourcesFunc)
       NotifyBoundElements();
   }
   return NS_OK;
 }
 
-void 
+void
 nsXBLResourceLoader::AddResource(nsIAtom* aResourceType, const nsAString& aSrc)
 {
   nsXBLResource* res = new nsXBLResource(aResourceType, aSrc);
   if (!mResourceList)
     mResourceList = res;
   else
     mLastResource->mNext = res;
 
   mLastResource = res;
 }
 
 void
-nsXBLResourceLoader::AddResourceListener(nsIContent* aBoundElement) 
+nsXBLResourceLoader::AddResourceListener(nsIContent* aBoundElement)
 {
   if (aBoundElement) {
     mBoundElements.AppendObject(aBoundElement);
   }
 }
 
 void
 nsXBLResourceLoader::NotifyBoundElements()
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -5781,26 +5781,34 @@ nsCSSFrameConstructor::AddFrameConstruct
         return;
 
       if (newPendingBinding->mBinding) {
         pendingBinding = newPendingBinding;
         // aState takes over owning newPendingBinding
         aState.AddPendingBinding(newPendingBinding.forget());
       }
 
-      if (aContent->IsStyledByServo()) {
-        NS_WARNING("stylo: Skipping Unsupported binding re-resolve. This needs fixing.");
-        resolveStyle = false;
-      }
-
       if (resolveStyle) {
-        styleContext =
-          ResolveStyleContext(styleContext->GetParent(), aContent, &aState);
-        display = styleContext->StyleDisplay();
-        aStyleContext = styleContext;
+        if (aContent->IsStyledByServo()) {
+          // XXX: This is a hack. We should have a better way to restyle aContent.
+          ServoRestyleManager::ClearServoDataFromSubtree(aContent->AsElement());
+          mPresShell->StyleSet()->AsServo()->StyleNewSubtree(aContent->AsElement());
+          // should_traverse_children() in traversal.rs skips the parent with
+          // moz-binding the first time. Thus call StyleNewChildren() again.
+          mPresShell->StyleSet()->AsServo()->StyleNewChildren(aContent->AsElement());
+          styleContext =
+            ResolveStyleContext(aParentFrame, aContent, &aState);
+          display = styleContext->StyleDisplay();
+          aStyleContext = styleContext;
+        } else {
+          styleContext =
+            ResolveStyleContext(styleContext->GetParent(), aContent, &aState);
+          display = styleContext->StyleDisplay();
+          aStyleContext = styleContext;
+        }
       }
 
       aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID);
     } else if (display->mBinding.ForceGet()) {
       if (aContent->IsStyledByServo()) {
         // Servo's should_traverse_children skips styling descendants of
         // elements with a -moz-binding value.  For -moz-binding URLs that can
         // be resolved, we will load the binding above, which will style the
--- a/layout/style/ServoBindingTypes.h
+++ b/layout/style/ServoBindingTypes.h
@@ -36,16 +36,17 @@ using ComputedKeyframeValues = nsTArray<
 } // namespace mozilla
 
 class nsCSSValue;
 struct nsFontFaceRuleContainer;
 class nsIDocument;
 class nsINode;
 class nsPresContext;
 struct nsTimingFunction;
+class nsXBLBinding;
 
 using mozilla::dom::StyleChildrenIterator;
 using mozilla::ServoElementSnapshot;
 
 typedef nsINode RawGeckoNode;
 typedef mozilla::dom::Element RawGeckoElement;
 typedef nsIDocument RawGeckoDocument;
 typedef nsPresContext RawGeckoPresContext;
@@ -134,16 +135,17 @@ DECL_BORROWED_REF_TYPE_FOR(RawGeckoKeyfr
 DECL_BORROWED_MUT_REF_TYPE_FOR(RawGeckoComputedKeyframeValuesList)
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoStyleAnimationList)
 DECL_BORROWED_MUT_REF_TYPE_FOR(nsTimingFunction)
 DECL_BORROWED_REF_TYPE_FOR(nsTimingFunction)
 DECL_BORROWED_MUT_REF_TYPE_FOR(RawGeckoFontFaceRuleList)
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoAnimationPropertySegment)
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoComputedTiming)
 DECL_BORROWED_MUT_REF_TYPE_FOR(RawGeckoServoStyleRuleList)
+DECL_BORROWED_REF_TYPE_FOR(nsXBLBinding)
 
 #undef DECL_ARC_REF_TYPE_FOR
 #undef DECL_OWNED_REF_TYPE_FOR
 #undef DECL_NULLABLE_OWNED_REF_TYPE_FOR
 #undef DECL_BORROWED_REF_TYPE_FOR
 #undef DECL_NULLABLE_BORROWED_REF_TYPE_FOR
 #undef DECL_BORROWED_MUT_REF_TYPE_FOR
 #undef DECL_NULLABLE_BORROWED_MUT_REF_TYPE_FOR
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1990,16 +1990,34 @@ Gecko_GetBaseSize(nsIAtom* aLanguage)
 
   prefs.Initialize(langGroupAtom);
   FontSizePrefs sizes;
   sizes.CopyFrom(prefs);
 
   return sizes;
 }
 
+nsXBLBinding*
+Gecko_ElementGetXBLBinding(RawGeckoElementBorrowed aElement)
+{
+  return aElement->GetXBLBinding();
+}
+
+nsXBLBinding*
+Gecko_XBLBinding_GetBaseBinding(nsXBLBinding* aXBLBinding)
+{
+  return aXBLBinding->GetBaseBinding();
+}
+
+RawServoStyleSet*
+Gecko_XBLBinding_GetRawServoStyleSet(nsXBLBinding* aXBLBinding)
+{
+  return aXBLBinding ? aXBLBinding->GetRawServoStyleSet() : nullptr;
+}
+
 void
 InitializeServo()
 {
   URLExtraData::InitDummy();
   Servo_Initialize(URLExtraData::Dummy());
 
   sServoFontMetricsLock = new Mutex("Gecko_GetFontMetrics");
 }
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -55,16 +55,17 @@ class nsCSSCounterStyleRule;
 class nsCSSFontFaceRule;
 struct nsMediaFeature;
 struct nsStyleList;
 struct nsStyleImage;
 struct nsStyleGradientStop;
 class nsStyleGradient;
 class nsStyleCoord;
 struct nsStyleDisplay;
+class nsXBLBinding;
 
 #define NS_DECL_THREADSAFE_FFI_REFCOUNTING(class_, name_)                     \
   void Gecko_AddRef##name_##ArbitraryThread(class_* aPtr);                    \
   void Gecko_Release##name_##ArbitraryThread(class_* aPtr);
 #define NS_IMPL_THREADSAFE_FFI_REFCOUNTING(class_, name_)                     \
   static_assert(class_::HasThreadSafeRefCnt::value,                           \
                 "NS_DECL_THREADSAFE_FFI_REFCOUNTING can only be used with "   \
                 "classes that have thread-safe refcounting");                 \
@@ -494,16 +495,21 @@ NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSS
 bool Gecko_PropertyId_IsPrefEnabled(nsCSSPropertyID id);
 
 void Gecko_nsStyleFont_SetLang(nsStyleFont* font, nsIAtom* atom);
 void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont, const nsStyleFont* aSource);
 void Gecko_nsStyleFont_FixupNoneGeneric(nsStyleFont* font,
                                         RawGeckoPresContextBorrowed pres_context);
 FontSizePrefs Gecko_GetBaseSize(nsIAtom* lang);
 
+nsXBLBinding* Gecko_ElementGetXBLBinding(RawGeckoElementBorrowed aElement);
+
+nsXBLBinding* Gecko_XBLBinding_GetBaseBinding(nsXBLBinding* aXBLBinding);
+RawServoStyleSet* Gecko_XBLBinding_GetRawServoStyleSet(nsXBLBinding* aXBLBinding);
+
 struct GeckoFontMetrics
 {
   nscoord mChSize;
   nscoord mXSize;
 };
 
 GeckoFontMetrics Gecko_GetFontMetrics(RawGeckoPresContextBorrowed pres_context,
                                       bool is_vertical,
--- a/servo/components/style/build_gecko.rs
+++ b/servo/components/style/build_gecko.rs
@@ -497,16 +497,17 @@ mod bindings {
             "StyleShapeSource",
             "StyleTransition",
             "mozilla::UniquePtr",
             "mozilla::DefaultDelete",
             "mozilla::Side",
             "mozilla::binding_danger::AssertAndSuppressCleanupPolicy",
             "mozilla::ParsingMode",
             "mozilla::InheritTarget",
+            "nsXBLBinding",
         ];
         let opaque_types = [
             "std::pair__PCCP",
             "std::namespace::atomic___base", "std::atomic__My_base",
             "std::atomic",
             "std::atomic___base",
             "mozilla::gfx::.*",
             "FallibleTArray",
@@ -764,16 +765,17 @@ mod bindings {
             "nsresult",
             "Loader",
             "ServoStyleSheet",
             "EffectCompositor_CascadeLevel",
             "UpdateAnimationsTasks",
             "ParsingMode",
             "InheritTarget",
             "URLMatchingFunction",
+            "nsXBLBinding",
         ];
         struct ArrayType {
             cpp_type: &'static str,
             rust_type: &'static str
         }
         let array_types = [
             ArrayType { cpp_type: "uintptr_t", rust_type: "usize" },
         ];
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -16,16 +16,17 @@ use font_metrics::FontMetricsProvider;
 use properties::{ComputedValues, PropertyDeclarationBlock};
 #[cfg(feature = "gecko")] use properties::animated_properties::AnimationValue;
 #[cfg(feature = "gecko")] use properties::animated_properties::TransitionProperty;
 use rule_tree::CascadeLevel;
 use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement};
 use selectors::matching::ElementSelectorFlags;
 use shared_lock::Locked;
 use sink::Push;
+use smallvec::VecLike;
 use std::fmt;
 #[cfg(feature = "gecko")] use std::collections::HashMap;
 use std::fmt::Debug;
 use std::hash::Hash;
 use std::ops::Deref;
 use stylearc::Arc;
 use stylist::ApplicableDeclarationBlock;
 use thread_state;
@@ -545,16 +546,26 @@ pub trait TElement : Eq + PartialEq + De
         let data = match self.borrow_data() {
             Some(d) => d,
             None => return false,
         };
         return data.get_restyle()
                    .map_or(false, |r| r.hint.has_animation_hint());
     }
 
+    /// Returns true if the element has the xbl binding. Only gecko elements could have this.
+    fn has_xbl_binding(&self) -> bool {
+        false
+    }
+
+    /// Gets declarations_from_xbl binding from the element. Only gecko element could have this.
+    fn get_declarations_from_xbl_binding<V>(&self,
+                                            _: &mut V)
+        where V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> {}
+
     /// Gets the current existing CSS transitions, by |property, end value| pairs in a HashMap.
     #[cfg(feature = "gecko")]
     fn get_css_transitions_info(&self)
                                 -> HashMap<TransitionProperty, Arc<AnimationValue>>;
 
     /// Does a rough (and cheap) check for whether or not transitions might need to be updated that
     /// will quickly return false for the common case of no transitions specified or running. If
     /// this returns false, we definitely don't need to update transitions but if it returns true
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -18,16 +18,17 @@ use app_units::Au;
 use atomic_refcell::AtomicRefCell;
 use context::{QuirksMode, SharedStyleContext, UpdateAnimationsTasks};
 use data::ElementData;
 use dom::{self, AnimationRules, DescendantsBit, LayoutIterator, NodeInfo, TElement, TNode, UnsafeNode};
 use dom::{OpaqueNode, PresentationalHintsSynthesizer};
 use element_state::ElementState;
 use error_reporting::RustLogReporter;
 use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
+use gecko::data::PerDocumentStyleData;
 use gecko::global_style_data::GLOBAL_STYLE_DATA;
 use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement};
 use gecko::snapshot_helpers;
 use gecko_bindings::bindings;
 use gecko_bindings::bindings::{Gecko_DropStyleChildrenIterator, Gecko_MaybeCreateStyleChildrenIterator};
 use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetLastChild, Gecko_GetNextStyleChild};
 use gecko_bindings::bindings::{Gecko_IsRootElement, Gecko_MatchesElement, Gecko_Namespace};
 use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
@@ -49,30 +50,31 @@ use gecko_bindings::structs::{RawGeckoEl
 use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext};
 use gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
 use gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
 use gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
 use gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;
 use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
 use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
 use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS;
-use gecko_bindings::sugar::ownership::HasArcFFI;
+use gecko_bindings::sugar::ownership::{HasArcFFI, HasSimpleFFI};
 use logical_geometry::WritingMode;
 use media_queries::Device;
 use properties::{ComputedValues, parse_style_attribute};
 use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
 use properties::animated_properties::{AnimationValue, AnimationValueMap, TransitionProperty};
 use properties::style_structs::Font;
 use rule_tree::CascadeLevel as ServoCascadeLevel;
 use selector_parser::ElementExt;
 use selectors::Element;
 use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
 use selectors::parser::{AttrSelector, NamespaceConstraint};
 use shared_lock::Locked;
 use sink::Push;
+use smallvec::VecLike;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt;
 use std::hash::{Hash, Hasher};
 use std::ptr;
 use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
 use stylearc::Arc;
 use stylesheets::UrlExtraData;
@@ -772,16 +774,63 @@ impl<'le> TElement for GeckoElement<'le>
     fn has_css_animations(&self) -> bool {
         self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) }
     }
 
     fn has_css_transitions(&self) -> bool {
         self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) }
     }
 
+    fn has_xbl_binding(&self) -> bool {
+        use gecko_bindings::bindings::Gecko_ElementGetXBLBinding;
+        let binding = unsafe { Gecko_ElementGetXBLBinding(self.0) };
+        !binding.is_null()
+    }
+
+    fn get_declarations_from_xbl_binding<V>(&self,
+                                            applicable_declarations: &mut V)
+        where V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> {
+        use gecko_bindings::bindings::Gecko_ElementGetXBLBinding;
+        use gecko_bindings::bindings::Gecko_XBLBinding_GetBaseBinding;
+        use gecko_bindings::bindings::Gecko_XBLBinding_GetRawServoStyleSet;
+
+        // XXX: Those child contents generated by XBL bindings do not have
+        // bindings themselves. Thus they'll fail to get declarations in
+        // <stylesheets>. Need to fix this.
+        if self.has_xbl_binding() {
+            let mut set_selector_flags = |_: &Self, _: ElementSelectorFlags| {};
+
+            let mut bindings = Vec::new();
+            let mut binding = unsafe { Gecko_ElementGetXBLBinding(self.0) };
+
+            // Implement nsXBLBinding::WalkRules().
+            while !binding.is_null() {
+                bindings.push(binding);
+                binding = unsafe { Gecko_XBLBinding_GetBaseBinding(binding) };
+            }
+
+            bindings.reverse();
+
+            for b in bindings {
+                unsafe {
+                    let raw_data = Gecko_XBLBinding_GetRawServoStyleSet(b);
+                    if !raw_data.is_null() {
+                        let data = PerDocumentStyleData::from_ffi(&*raw_data).borrow();
+                        data.stylist.push_applicable_declarations_xbl(self,
+                                                                      applicable_declarations,
+                                                                      &mut set_selector_flags);
+                    }
+                }
+            }
+
+            // XXX: Need to implement nsBindingManager::WalkRules(), i.e.
+            // walk binding parent and cut off inheritance, etc.
+        }
+    }
+
     fn get_css_transitions_info(&self)
                                 -> HashMap<TransitionProperty, Arc<AnimationValue>> {
         use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
         use gecko_bindings::bindings::Gecko_ElementTransitions_Length;
         use gecko_bindings::bindings::Gecko_ElementTransitions_PropertyAt;
 
         let collection_length =
             unsafe { Gecko_ElementTransitions_Length(self.0) };
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -382,16 +382,18 @@ const RULE_TREE_GC_INTERVAL: usize = 300
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum CascadeLevel {
     /// Normal User-Agent rules.
     UANormal = 0,
     /// Presentational hints.
     PresHints,
     /// User normal rules.
     UserNormal,
+    /// XBL <stylesheet> rules.
+    XBL,
     /// Author normal rules.
     AuthorNormal,
     /// Style attribute normal rules.
     StyleAttributeNormal,
     /// SVG SMIL animations.
     SMILOverride,
     /// CSS animations and script-generated animations.
     Animations,
@@ -1021,17 +1023,18 @@ impl StrongRuleNode {
                         }
                     });
 
                 match node.cascade_level() {
                     // Non-author rules:
                     CascadeLevel::UANormal |
                     CascadeLevel::UAImportant |
                     CascadeLevel::UserNormal |
-                    CascadeLevel::UserImportant => {
+                    CascadeLevel::UserImportant |
+                    CascadeLevel::XBL => {
                         for (id, declaration) in longhands {
                             if properties.contains(id) {
                                 // This property was set by a non-author rule. Stop looking for it in
                                 // this element's rule nodes.
                                 properties.remove(id);
 
                                 // However, if it is inherited, then it might be inherited from an
                                 // author rule from an ancestor element's rule nodes.
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -423,16 +423,17 @@ impl Stylist {
         }
 
         // Cheap `Arc` clone so that the closure below can borrow `&mut Stylist`.
         let device = self.device.clone();
 
         stylesheet.effective_rules(&device, guard, |rule| {
             match *rule {
                 CssRule::Style(ref locked) => {
+                    debug!("Found valid style rule: {:?}", *locked);
                     let style_rule = locked.read_with(&guard);
                     self.num_declarations += style_rule.block.read_with(&guard).len();
                     for selector in &style_rule.selectors.0 {
                         self.num_selectors += 1;
                         self.add_rule_to_map(selector, locked, stylesheet);
                         self.dependencies.note_selector(selector);
                         self.note_for_revalidation(selector);
                         self.note_attribute_and_state_dependencies(selector);
@@ -820,16 +821,36 @@ impl Stylist {
         // during multiple layout passes, but this is totally bogus, in the
         // sense that it's updated asynchronously.
         //
         // This should probably be an argument to `update`, and use the quirks
         // mode info in the `SharedLayoutContext`.
         self.quirks_mode = quirks_mode;
     }
 
+    /// XBL
+    pub fn push_applicable_declarations_xbl<E, V, F>(&self,
+                                                     element: &E,
+                                                     applicable_declarations: &mut V,
+                                                     flags_setter: &mut F)
+        where E: TElement,
+              V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
+              F: FnMut(&E, ElementSelectorFlags),
+    {
+        let mut matching_context =
+            MatchingContext::new(MatchingMode::Normal, None);
+
+        self.element_map.author.get_all_matching_rules(element,
+                                                       element,
+                                                       applicable_declarations,
+                                                       &mut matching_context,
+                                                       flags_setter,
+                                                       CascadeLevel::XBL);
+    }
+
     /// Returns the applicable CSS declarations for the given element.
     ///
     /// This corresponds to `ElementRuleCollector` in WebKit.
     ///
     /// The `StyleRelations` recorded in `MatchingContext` indicate hints about
     /// which kind of rules have matched.
     pub fn push_applicable_declarations<E, V, F>(
                                         &self,
@@ -893,16 +914,18 @@ impl Stylist {
                     }
                 }
                 // Never share style for elements with preshints
                 context.relations |= AFFECTED_BY_PRESENTATIONAL_HINTS;
             }
             debug!("preshints: {:?}", context.relations);
         }
 
+        element.get_declarations_from_xbl_binding(applicable_declarations);
+
         // NB: the following condition, although it may look somewhat
         // inaccurate, would be equivalent to something like:
         //
         //     element.matches_user_and_author_rules() ||
         //     (is_implemented_pseudo &&
         //      rule_hash_target.matches_user_and_author_rules())
         //
         // Which may be more what you would probably expect.