Bug 1304792: stylo: Implement @import. r?heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sat, 17 Dec 2016 10:58:56 +0100
changeset 454554 c472a313eb5d62fe2b8d7d5814f53a399a0a9a97
parent 454553 3b6992f9c1d3c12eb80ed71c79b5b59e838f314e
child 454555 645417a36e724abab6e48389bbe7b7cb76386533
push id39971
push userbmo:emilio+bugs@crisal.io
push dateThu, 29 Dec 2016 17:39:02 +0000
reviewersheycam
bugs1304792
milestone53.0a1
Bug 1304792: stylo: Implement @import. r?heycam MozReview-Commit-ID: Hw1V66JxIBD
layout/style/Loader.cpp
layout/style/Loader.h
layout/style/ServoArcTypeList.h
layout/style/ServoBindingList.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/ServoStyleSheet.cpp
layout/style/ServoStyleSheet.h
layout/style/nsCSSParser.cpp
layout/style/nsLayoutStylesheetCache.cpp
servo/components/style/build_gecko.rs
servo/components/style/gecko/conversions.rs
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/gecko_bindings/structs_debug.rs
servo/components/style/gecko_bindings/structs_release.rs
servo/ports/geckolib/glue.rs
servo/ports/geckolib/stylesheet_loader.rs
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -48,16 +48,17 @@
 #include "mozilla/css/ImportRule.h"
 #include "nsThreadUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIThreadInternal.h"
 #include "nsINetworkPredictor.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/URL.h"
 #include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/ServoBindings.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/ConsoleReportCollector.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPrototypeCache.h"
 #endif
 
@@ -1400,35 +1401,35 @@ Loader::InsertSheetInDoc(StyleSheet* aSh
  * to put it anyway.  So just append for now.  (In the future if we
  * want to insert the sheet at the correct position, we'll need to
  * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
  * bug 1220506.)
  */
 nsresult
 Loader::InsertChildSheet(StyleSheet* aSheet,
                          StyleSheet* aParentSheet,
-                         ImportRule* aParentRule)
+                         ImportRule* aGeckoParentRule,
+                         RawServoImportRule* aServoParentRule)
 {
   LOG(("css::Loader::InsertChildSheet"));
-  NS_PRECONDITION(aSheet, "Nothing to insert");
-  NS_PRECONDITION(aParentSheet, "Need a parent to insert into");
-  NS_PRECONDITION(aParentSheet, "How did we get imported?");
-
-  // XXXheycam The InsertChildSheet API doesn't work with ServoStyleSheets,
-  // since they won't have Gecko ImportRules in them.
-  if (aSheet->IsServo()) {
-    return NS_ERROR_FAILURE;
+  MOZ_ASSERT(aSheet, "Nothing to insert");
+  MOZ_ASSERT(aParentSheet, "Need a parent to insert into");
+  MOZ_ASSERT_IF(aSheet->IsGecko(), aGeckoParentRule && !aServoParentRule);
+  MOZ_ASSERT_IF(aSheet->IsServo(), aServoParentRule && !aGeckoParentRule);
+  if (aSheet->IsGecko()) {
+    // child sheets should always start out enabled, even if they got
+    // cloned off of top-level sheets which were disabled
+    aSheet->AsGecko()->SetEnabled(true);
+    aGeckoParentRule->SetSheet(aSheet->AsGecko()); // This sets the ownerRule on the sheet
+  } else {
+    RefPtr<RawServoStyleSheet> sheet =
+      Servo_ImportRule_GetSheet(aServoParentRule).Consume();
+    aSheet->AsServo()->SetSheetForImport(sheet);
   }
-
-  // child sheets should always start out enabled, even if they got
-  // cloned off of top-level sheets which were disabled
-  aSheet->AsGecko()->SetEnabled(true);
-
   aParentSheet->AppendStyleSheet(aSheet);
-  aParentRule->SetSheet(aSheet->AsGecko()); // This sets the ownerRule on the sheet
 
   LOG(("  Inserting into parent sheet"));
   return NS_OK;
 }
 
 /**
  * LoadSheet handles the actual load of a sheet.  If the load is
  * supposed to be synchronous it just opens a channel synchronously
@@ -1768,17 +1769,18 @@ Loader::ParseSheet(const nsAString& aInp
 
   if (aLoadData->mSheet->IsGecko()) {
     nsCSSParser parser(this, aLoadData->mSheet->AsGecko());
     rv = parser.ParseSheet(aInput, sheetURI, baseURI,
                            aLoadData->mSheet->Principal(),
                            aLoadData->mLineNumber);
   } else {
     rv =
-      aLoadData->mSheet->AsServo()->ParseSheet(aInput, sheetURI, baseURI,
+      aLoadData->mSheet->AsServo()->ParseSheet(this,
+                                               aInput, sheetURI, baseURI,
                                                aLoadData->mSheet->Principal(),
                                                aLoadData->mLineNumber);
   }
 
   mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
 
   if (NS_FAILED(rv)) {
     LOG_ERROR(("  Low-level error in parser!"));
@@ -2190,35 +2192,45 @@ HaveAncestorDataWithURI(SheetLoadData *a
 
   return false;
 }
 
 nsresult
 Loader::LoadChildSheet(StyleSheet* aParentSheet,
                        nsIURI* aURL,
                        nsMediaList* aMedia,
-                       ImportRule* aParentRule,
+                       ImportRule* aGeckoParentRule,
+                       RawServoImportRule* aServoParentRule,
                        LoaderReusableStyleSheets* aReusableSheets)
 {
   LOG(("css::Loader::LoadChildSheet"));
   NS_PRECONDITION(aURL, "Must have a URI to load");
   NS_PRECONDITION(aParentSheet, "Must have a parent sheet");
 
+  // Servo doesn't support reusable sheets.
+  MOZ_ASSERT_IF(aReusableSheets, aParentSheet->IsGecko());
+  MOZ_ASSERT_IF(aParentSheet->IsGecko(), aGeckoParentRule && !aServoParentRule);
+  MOZ_ASSERT_IF(aParentSheet->IsServo(), aServoParentRule && !aGeckoParentRule);
+
   if (!mEnabled) {
     LOG_WARN(("  Not enabled"));
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   LOG_URI("  Child uri: '%s'", aURL);
 
   nsCOMPtr<nsINode> owningNode;
 
   // check for an owning document: if none, don't bother walking up the parent
   // sheets
-  if (aParentSheet->GetOwningDocument()) {
+  //
+  // FIXME(emilio): Figure out whether this walk up is necessary (try seems
+  // green without it), and fix the parenting of stylesheets in the servo case
+  // if that's the case.
+  if (aParentSheet->GetOwningDocument() && aParentSheet->IsGecko()) {
     StyleSheet* topSheet = aParentSheet;
     while (StyleSheet* parent = topSheet->GetParentSheet()) {
       topSheet = parent;
     }
     owningNode = topSheet->GetOwnerNode();
   }
 
   nsISupports* context = owningNode;
@@ -2259,34 +2271,35 @@ Loader::LoadChildSheet(StyleSheet* aPare
 
   // Now that we know it's safe to load this (passes security check and not a
   // loop) do so.
   RefPtr<StyleSheet> sheet;
   RefPtr<CSSStyleSheet> reusableSheet;
   StyleSheetState state;
   if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, reusableSheet)) {
     sheet = reusableSheet;
-    aParentRule->SetSheet(reusableSheet);
+    aGeckoParentRule->SetSheet(reusableSheet);
     state = eSheetComplete;
   } else {
     bool isAlternate;
     const nsSubstring& empty = EmptyString();
     // For now, use CORS_NONE for child sheets
     rv = CreateSheet(aURL, nullptr, principal,
                      aParentSheet->ParsingMode(),
                      CORS_NONE, aParentSheet->GetReferrerPolicy(),
                      EmptyString(), // integrity is only checked on main sheet
                      parentData ? parentData->mSyncLoad : false,
                      false, empty, state, &isAlternate, &sheet);
     NS_ENSURE_SUCCESS(rv, rv);
 
     PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate);
   }
 
-  rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
+  rv = InsertChildSheet(sheet, aParentSheet, aGeckoParentRule,
+                        aServoParentRule);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (state == eSheetComplete) {
     LOG(("  Sheet already complete"));
     // We're completely done.  No need to notify, even, since the
     // @import rule addition/modification will trigger the right style
     // changes automatically.
     return NS_OK;
--- a/layout/style/Loader.h
+++ b/layout/style/Loader.h
@@ -280,25 +280,29 @@ public:
    * complete aParentSheet will be QIed to nsICSSLoaderObserver and
    * asynchronously notified, just like for LoadStyleLink.  Note that if the
    * child sheet is already complete when this method returns, no
    * nsICSSLoaderObserver notification will be sent.
    *
    * @param aParentSheet the parent of this child sheet
    * @param aURL the URL of the child sheet
    * @param aMedia the already-parsed media list for the child sheet
-   * @param aRule the @import rule importing this child.  This is used to
-   *              properly order the child sheet list of aParentSheet.
+   * @param aGeckoParentRule the @import rule importing this child, when using
+   *                         Gecko's style system. This is used to properly
+   *                         order the child sheet list of aParentSheet.
+   * @param aServoParentRule the @import rule importing this child, when using
+   *                         Servo's style system.
    * @param aSavedSheets any saved style sheets which could be reused
    *              for this load
    */
   nsresult LoadChildSheet(StyleSheet* aParentSheet,
                           nsIURI* aURL,
                           nsMediaList* aMedia,
-                          ImportRule* aRule,
+                          ImportRule* aGeckoParentRule,
+                          RawServoImportRule* aServoParentRule,
                           LoaderReusableStyleSheets* aSavedSheets);
 
   /**
    * Synchronously load and return the stylesheet at aURL.  Any child sheets
    * will also be loaded synchronously.  Note that synchronous loads over some
    * protocols may involve spinning up a new event loop, so use of this method
    * does NOT guarantee not receiving any events before the sheet loads.  This
    * method can be used to load sheets not associated with a document.
@@ -479,17 +483,18 @@ private:
                     bool isAlternate);
 
   nsresult InsertSheetInDoc(StyleSheet* aSheet,
                             nsIContent* aLinkingContent,
                             nsIDocument* aDocument);
 
   nsresult InsertChildSheet(StyleSheet* aSheet,
                             StyleSheet* aParentSheet,
-                            ImportRule* aParentRule);
+                            ImportRule* aGeckoParentRule,
+                            RawServoImportRule* aServoParentRule);
 
   nsresult InternalLoadNonDocumentSheet(nsIURI* aURL,
                                         bool aIsPreload,
                                         SheetParsingMode aParsingMode,
                                         bool aUseSystemPrincipal,
                                         nsIPrincipal* aOriginPrincipal,
                                         const nsCString& aCharset,
                                         RefPtr<StyleSheet>* aSheet,
--- a/layout/style/ServoArcTypeList.h
+++ b/layout/style/ServoArcTypeList.h
@@ -6,8 +6,9 @@
 
 /* a list of all Servo Arc types used in stylo bindings for preprocessing */
 
 SERVO_ARC_TYPE(CssRules, ServoCssRules)
 SERVO_ARC_TYPE(StyleSheet, RawServoStyleSheet)
 SERVO_ARC_TYPE(ComputedValues, ServoComputedValues)
 SERVO_ARC_TYPE(DeclarationBlock, RawServoDeclarationBlock)
 SERVO_ARC_TYPE(StyleRule, RawServoStyleRule)
+SERVO_ARC_TYPE(ImportRule, RawServoImportRule)
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -21,22 +21,36 @@
 // Element data
 SERVO_BINDING_FUNC(Servo_Element_ClearData, void, RawGeckoElementBorrowed node)
 SERVO_BINDING_FUNC(Servo_Element_ShouldTraverse, bool, RawGeckoElementBorrowed node)
 
 // Styleset and Stylesheet management
 SERVO_BINDING_FUNC(Servo_StyleSheet_Empty, RawServoStyleSheetStrong,
                    mozilla::css::SheetParsingMode parsing_mode)
 SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetStrong,
+                   mozilla::css::Loader* loader,
+                   mozilla::ServoStyleSheet* gecko_stylesheet,
                    const nsACString* data,
                    mozilla::css::SheetParsingMode parsing_mode,
                    const nsACString* base_url,
                    ThreadSafeURIHolder* base,
                    ThreadSafeURIHolder* referrer,
                    ThreadSafePrincipalHolder* principal)
+SERVO_BINDING_FUNC(Servo_ImportRule_GetSheet,
+                   RawServoStyleSheetStrong,
+                   const RawServoImportRuleBorrowed import_rule)
+SERVO_BINDING_FUNC(Servo_StyleSheet_ClearAndUpdate,
+                   void,
+                   RawServoStyleSheetBorrowed stylesheet,
+                   mozilla::css::Loader* loader,
+                   mozilla::ServoStyleSheet* gecko_stylesheet,
+                   const nsACString* data,
+                   ThreadSafeURIHolder* base,
+                   ThreadSafeURIHolder* referrer,
+                   ThreadSafePrincipalHolder* principal)
 SERVO_BINDING_FUNC(Servo_StyleSheet_HasRules, bool,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_GetRules, ServoCssRulesStrong,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_Init, RawServoStyleSetOwned)
 SERVO_BINDING_FUNC(Servo_StyleSet_Drop, void, RawServoStyleSetOwned set)
 SERVO_BINDING_FUNC(Servo_StyleSet_AppendStyleSheet, void,
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet, bool flush)
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -5,26 +5,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ServoBindings.h"
 
 #include "ChildIterator.h"
 #include "StyleStructContext.h"
 #include "gfxFontFamilyList.h"
 #include "nsAttrValueInlines.h"
+#include "nsCSSParser.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsContentUtils.h"
 #include "nsDOMTokenList.h"
 #include "nsIContentInlines.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsINode.h"
 #include "nsIPrincipal.h"
 #include "nsNameSpaceManager.h"
+#include "nsNetUtil.h"
 #include "nsRuleNode.h"
 #include "nsString.h"
 #include "nsStyleStruct.h"
 #include "nsStyleUtil.h"
 #include "nsTArray.h"
 
 #include "mozilla/EventStates.h"
 #include "mozilla/ServoElementSnapshot.h"
@@ -1016,16 +1018,51 @@ Gecko_CSSValue_SetFunction(nsCSSValueBor
 }
 
 nsCSSValueBorrowedMut
 Gecko_CSSValue_GetArrayItem(nsCSSValueBorrowedMut aCSSValue, int32_t aIndex)
 {
   return &aCSSValue->GetArrayValue()->Item(aIndex);
 }
 
+void
+Gecko_LoadStyleSheet(css::Loader* aLoader,
+                     ServoStyleSheet* aParent,
+                     RawServoImportRuleStrong aImportRule,
+                     const uint8_t* aURLString,
+                     uint32_t aURLStringLength,
+                     const uint8_t* aMediaString,
+                     uint32_t aMediaStringLength)
+{
+  MOZ_ASSERT(aLoader, "Should've catched this before");
+  MOZ_ASSERT(aParent, "Only used for @import, so parent should exist!");
+  MOZ_ASSERT(aURLString, "Invalid URLs shouldn't be loaded!");
+  RefPtr<nsMediaList> media = new nsMediaList();
+  if (aMediaStringLength) {
+    MOZ_ASSERT(aMediaString);
+    // TODO(emilio, bug 1325878): This is not great, though this is going away
+    // soon anyway, when we can have a Servo-backed nsMediaList.
+    nsDependentCSubstring medium(reinterpret_cast<const char*>(aMediaString),
+                                 aMediaStringLength);
+    nsCSSParser mediumParser(aLoader);
+    mediumParser.ParseMediaList(
+        NS_ConvertUTF8toUTF16(medium), nullptr, 0, media);
+  }
+
+  nsDependentCSubstring urlSpec(reinterpret_cast<const char*>(aURLString),
+                                aURLStringLength);
+
+  // Servo's loader guarantees that the URL is valid.
+  nsCOMPtr<nsIURI> uri;
+  MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), urlSpec));
+
+  RefPtr<RawServoImportRule> import = aImportRule.Consume();
+  aLoader->LoadChildSheet(aParent, uri, media, nullptr, import, nullptr);
+}
+
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList);
 
 #define STYLE_STRUCT(name, checkdata_cb)                                      \
                                                                               \
 void                                                                          \
 Gecko_Construct_nsStyle##name(nsStyle##name* ptr)                             \
 {                                                                             \
   new (ptr) nsStyle##name(StyleStructContext::ServoContext());                \
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -24,16 +24,17 @@
  * Functions beginning with Servo_ are implemented in Servo and invoked from Gecko.
  */
 
 class nsIAtom;
 class nsIPrincipal;
 class nsIURI;
 struct nsFont;
 namespace mozilla {
+  class ServoStyleSheet;
   class FontFamilyList;
   enum FontFamilyType : uint32_t;
 }
 using mozilla::FontFamilyList;
 using mozilla::FontFamilyType;
 using mozilla::ServoElementSnapshot;
 struct nsStyleList;
 struct nsStyleImage;
@@ -89,16 +90,23 @@ RawGeckoNodeBorrowedOrNull Gecko_GetLast
 RawGeckoNodeBorrowedOrNull Gecko_GetPrevSibling(RawGeckoNodeBorrowed node);
 RawGeckoNodeBorrowedOrNull Gecko_GetNextSibling(RawGeckoNodeBorrowed node);
 RawGeckoElementBorrowedOrNull Gecko_GetParentElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetFirstChildElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetLastChildElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetPrevSiblingElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetNextSiblingElement(RawGeckoElementBorrowed element);
 RawGeckoElementBorrowedOrNull Gecko_GetDocumentElement(RawGeckoDocumentBorrowed document);
+void Gecko_LoadStyleSheet(mozilla::css::Loader* loader,
+                          mozilla::ServoStyleSheet* parent,
+                          RawServoImportRuleStrong import_rule,
+                          const uint8_t* url_bytes,
+                          uint32_t url_length,
+                          const uint8_t* media_bytes,
+                          uint32_t media_length);
 
 // By default, Servo walks the DOM by traversing the siblings of the DOM-view
 // first child. This generally works, but misses anonymous children, which we
 // want to traverse during styling. To support these cases, we create an
 // optional heap-allocated iterator for nodes that need it. If the creation
 // method returns null, Servo falls back to the aforementioned simpler (and
 // faster) sibling traversal.
 StyleChildrenIteratorOwnedOrNull Gecko_MaybeCreateStyleChildrenIterator(RawGeckoNodeBorrowed node);
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -57,43 +57,46 @@ ServoStyleSheet::GetParentSheet() const
   // to fix SetOwningDocument to propagate the owning document down
   // to the children.
   MOZ_CRASH("stylo: not implemented");
 }
 
 void
 ServoStyleSheet::AppendStyleSheet(ServoStyleSheet* aSheet)
 {
-  // XXXheycam: When we implement support for child sheets, we'll have
-  // to fix SetOwningDocument to propagate the owning document down
-  // to the children.
-  MOZ_CRASH("stylo: not implemented");
+  aSheet->mDocument = mDocument;
 }
 
 nsresult
-ServoStyleSheet::ParseSheet(const nsAString& aInput,
+ServoStyleSheet::ParseSheet(css::Loader* aLoader,
+                            const nsAString& aInput,
                             nsIURI* aSheetURI,
                             nsIURI* aBaseURI,
                             nsIPrincipal* aSheetPrincipal,
                             uint32_t aLineNumber)
 {
-  DropSheet();
-
   RefPtr<ThreadSafeURIHolder> base = new ThreadSafeURIHolder(aBaseURI);
   RefPtr<ThreadSafeURIHolder> referrer = new ThreadSafeURIHolder(aSheetURI);
   RefPtr<ThreadSafePrincipalHolder> principal =
     new ThreadSafePrincipalHolder(aSheetPrincipal);
 
   nsCString baseString;
   nsresult rv = aBaseURI->GetSpec(baseString);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ConvertUTF16toUTF8 input(aInput);
-  mSheet = Servo_StyleSheet_FromUTF8Bytes(&input, mParsingMode, &baseString,
-                                          base, referrer, principal).Consume();
+  if (!mSheet) {
+    mSheet =
+      Servo_StyleSheet_FromUTF8Bytes(aLoader, this, &input, mParsingMode,
+                                     &baseString, base, referrer,
+                                     principal).Consume();
+  } else {
+    Servo_StyleSheet_ClearAndUpdate(mSheet, aLoader, this, &input, base,
+                                    referrer, principal);
+  }
 
   return NS_OK;
 }
 
 void
 ServoStyleSheet::LoadFailed()
 {
   mSheet = Servo_StyleSheet_Empty(mParsingMode).Consume();
--- a/layout/style/ServoStyleSheet.h
+++ b/layout/style/ServoStyleSheet.h
@@ -13,16 +13,20 @@
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInfo.h"
 #include "nsStringFwd.h"
 
 namespace mozilla {
 
 class ServoCSSRuleList;
 
+namespace css {
+class Loader;
+}
+
 /**
  * CSS style sheet object that is a wrapper for a Servo Stylesheet.
  */
 class ServoStyleSheet : public StyleSheet
 {
 public:
   ServoStyleSheet(css::SheetParsingMode aParsingMode,
                   CORSMode aCORSMode,
@@ -31,17 +35,18 @@ public:
 
   bool HasRules() const;
 
   void SetOwningDocument(nsIDocument* aDocument);
 
   ServoStyleSheet* GetParentSheet() const;
   void AppendStyleSheet(ServoStyleSheet* aSheet);
 
-  MOZ_MUST_USE nsresult ParseSheet(const nsAString& aInput,
+  MOZ_MUST_USE nsresult ParseSheet(css::Loader* aLoader,
+                                   const nsAString& aInput,
                                    nsIURI* aSheetURI,
                                    nsIURI* aBaseURI,
                                    nsIPrincipal* aSheetPrincipal,
                                    uint32_t aLineNumber);
 
   /**
    * Called instead of ParseSheet to initialize the Servo stylesheet object
    * for a failed load. Either ParseSheet or LoadFailed must be called before
@@ -51,16 +56,20 @@ public:
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
 #ifdef DEBUG
   void List(FILE* aOut = stdout, int32_t aIndex = 0) const;
 #endif
 
   RawServoStyleSheet* RawSheet() const { return mSheet; }
+  void SetSheetForImport(RawServoStyleSheet* aSheet) {
+    MOZ_ASSERT(!mSheet);
+    mSheet = aSheet;
+  }
 
   // WebIDL StyleSheet API
   nsMediaList* Media() final;
 
   // WebIDL CSSStyleSheet API
   // Can't be inline because we can't include ImportRule here.  And can't be
   // called GetOwnerRule because that would be ambiguous with the ImportRule
   // version.
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -3810,17 +3810,19 @@ CSSParserImpl::ProcessImport(const nsStr
       // import url is bad
       REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
       OUTPUT_ERROR();
     }
     return;
   }
 
   if (mChildLoader) {
-    mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, mReusableSheets);
+    mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule,
+                                 /* aServoParentRule = */ nullptr,
+                                 mReusableSheets);
   }
 }
 
 // Parse the {} part of an @media or @-moz-document rule.
 bool
 CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
                               RuleAppendFunc aAppendFunc,
                               void* aData)
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -966,17 +966,20 @@ nsLayoutStylesheetCache::BuildPreference
 
   NS_ASSERTION(sheetText.Length() <= kPreallocSize,
                "kPreallocSize should be big enough to build preference style "
                "sheet without reallocation");
 
   if (sheet->IsGecko()) {
     sheet->AsGecko()->ReparseSheet(sheetText);
   } else {
-    nsresult rv = sheet->AsServo()->ParseSheet(sheetText, uri, uri, nullptr, 0);
+    ServoStyleSheet* servoSheet = sheet->AsServo();
+    // NB: The pref sheet never has @import rules.
+    nsresult rv =
+      servoSheet->ParseSheet(nullptr, sheetText, uri, uri, nullptr, 0);
     // Parsing the about:PreferenceStyleSheet URI can only fail on OOM. If we
     // are OOM before we parsed any documents we might as well abort.
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   }
 
 #undef NS_GET_R_G_B
 }
 
--- a/servo/components/style/build_gecko.rs
+++ b/servo/components/style/build_gecko.rs
@@ -217,16 +217,17 @@ mod bindings {
             "NS_STYLE_.*",
             "NS_CORNER_.*",
             "NS_RADIUS_.*",
             "BORDER_COLOR_.*",
             "BORDER_STYLE_.*"
         ];
         let whitelist = [
             "RawGecko.*",
+            "mozilla::ServoStyleSheet",
             "mozilla::ServoElementSnapshot.*",
             "mozilla::ConsumeStyleBehavior",
             "mozilla::LazyComputeBehavior",
             "mozilla::css::SheetParsingMode",
             "mozilla::TraversalRootBehavior",
             "mozilla::DisplayItemClip",  // Needed because bindgen generates
                                          // specialization tests for this even
                                          // though it shouldn't.
@@ -505,30 +506,33 @@ mod bindings {
             "nsStyleUnion",
             "nsStyleUnit",
             "nsStyleUserInterface",
             "nsStyleVariables",
             "nsStyleVisibility",
             "nsStyleXUL",
             "nscoord",
             "nsresult",
+            "Loader",
+            "ServoStyleSheet",
         ];
         struct ArrayType {
             cpp_type: &'static str,
             rust_type: &'static str
         }
         let array_types = [
             ArrayType { cpp_type: "uintptr_t", rust_type: "usize" },
         ];
         let servo_nullable_arc_types = [
             "ServoComputedValues",
             "ServoCssRules",
             "RawServoStyleSheet",
             "RawServoDeclarationBlock",
             "RawServoStyleRule",
+            "RawServoImportRule",
         ];
         struct ServoOwnedType {
             name: &'static str,
             opaque: bool,
         }
         let servo_owned_types = [
             ServoOwnedType { name: "RawServoStyleSet", opaque: true },
             ServoOwnedType { name: "StyleChildrenIterator", opaque: true },
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -6,25 +6,25 @@
 //! Ideally, it would be in geckolib itself, but coherence
 //! forces us to keep the traits and implementations here
 
 #![allow(unsafe_code)]
 
 use app_units::Au;
 use gecko::values::{convert_rgba_to_nscolor, StyleCoordHelpers};
 use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetUrlImageValue};
-use gecko_bindings::bindings::{RawServoStyleSheet, RawServoDeclarationBlock, RawServoStyleRule};
+use gecko_bindings::bindings::{RawServoStyleSheet, RawServoDeclarationBlock, RawServoStyleRule, RawServoImportRule};
 use gecko_bindings::bindings::{ServoComputedValues, ServoCssRules};
 use gecko_bindings::structs::{nsStyleCoord_CalcValue, nsStyleImage};
 use gecko_bindings::structs::nsresult;
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut};
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI};
 use parking_lot::RwLock;
 use properties::{ComputedValues, PropertyDeclarationBlock};
-use stylesheets::{CssRules, RulesMutateError, Stylesheet, StyleRule};
+use stylesheets::{CssRules, RulesMutateError, Stylesheet, StyleRule, ImportRule};
 use values::computed::{CalcLengthOrPercentage, Gradient, Image, LengthOrPercentage, LengthOrPercentageOrAuto};
 
 unsafe impl HasFFI for Stylesheet {
     type FFIType = RawServoStyleSheet;
 }
 unsafe impl HasArcFFI for Stylesheet {}
 unsafe impl HasFFI for ComputedValues {
     type FFIType = ServoComputedValues;
@@ -41,16 +41,21 @@ unsafe impl HasFFI for RwLock<CssRules> 
 }
 unsafe impl HasArcFFI for RwLock<CssRules> {}
 
 unsafe impl HasFFI for RwLock<StyleRule> {
     type FFIType = RawServoStyleRule;
 }
 unsafe impl HasArcFFI for RwLock<StyleRule> {}
 
+unsafe impl HasFFI for RwLock<ImportRule> {
+    type FFIType = RawServoImportRule;
+}
+unsafe impl HasArcFFI for RwLock<ImportRule> {}
+
 impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
     fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
         let has_percentage = other.percentage.is_some();
         nsStyleCoord_CalcValue {
             mLength: other.length.0,
             mPercent: other.percentage.unwrap_or(0.0),
             mHasPercent: has_percentage,
         }
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -139,16 +139,18 @@ unsafe impl Sync for nsStyleVariables {}
 use gecko_bindings::structs::nsStyleVisibility;
 unsafe impl Send for nsStyleVisibility {}
 unsafe impl Sync for nsStyleVisibility {}
 use gecko_bindings::structs::nsStyleXUL;
 unsafe impl Send for nsStyleXUL {}
 unsafe impl Sync for nsStyleXUL {}
 use gecko_bindings::structs::nscoord;
 use gecko_bindings::structs::nsresult;
+use gecko_bindings::structs::Loader;
+use gecko_bindings::structs::ServoStyleSheet;
 pub type nsTArrayBorrowed_uintptr_t<'a> = &'a mut ::gecko_bindings::structs::nsTArray<usize>;
 pub type ServoComputedValuesStrong = ::gecko_bindings::sugar::ownership::Strong<ServoComputedValues>;
 pub type ServoComputedValuesBorrowed<'a> = &'a ServoComputedValues;
 pub type ServoComputedValuesBorrowedOrNull<'a> = Option<&'a ServoComputedValues>;
 enum ServoComputedValuesVoid { }
 pub struct ServoComputedValues(ServoComputedValuesVoid);
 pub type ServoCssRulesStrong = ::gecko_bindings::sugar::ownership::Strong<ServoCssRules>;
 pub type ServoCssRulesBorrowed<'a> = &'a ServoCssRules;
@@ -165,16 +167,21 @@ pub type RawServoDeclarationBlockBorrowe
 pub type RawServoDeclarationBlockBorrowedOrNull<'a> = Option<&'a RawServoDeclarationBlock>;
 enum RawServoDeclarationBlockVoid { }
 pub struct RawServoDeclarationBlock(RawServoDeclarationBlockVoid);
 pub type RawServoStyleRuleStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoStyleRule>;
 pub type RawServoStyleRuleBorrowed<'a> = &'a RawServoStyleRule;
 pub type RawServoStyleRuleBorrowedOrNull<'a> = Option<&'a RawServoStyleRule>;
 enum RawServoStyleRuleVoid { }
 pub struct RawServoStyleRule(RawServoStyleRuleVoid);
+pub type RawServoImportRuleStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoImportRule>;
+pub type RawServoImportRuleBorrowed<'a> = &'a RawServoImportRule;
+pub type RawServoImportRuleBorrowedOrNull<'a> = Option<&'a RawServoImportRule>;
+enum RawServoImportRuleVoid { }
+pub struct RawServoImportRule(RawServoImportRuleVoid);
 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);
@@ -233,16 +240,22 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_StyleRule_AddRef(ptr: RawServoStyleRuleBorrowed);
 }
 extern "C" {
     pub fn Servo_StyleRule_Release(ptr: RawServoStyleRuleBorrowed);
 }
 extern "C" {
+    pub fn Servo_ImportRule_AddRef(ptr: RawServoImportRuleBorrowed);
+}
+extern "C" {
+    pub fn Servo_ImportRule_Release(ptr: RawServoImportRuleBorrowed);
+}
+extern "C" {
     pub fn Servo_StyleSet_Drop(ptr: RawServoStyleSetOwned);
 }
 extern "C" {
     pub fn Gecko_EnsureTArrayCapacity(aArray: *mut ::std::os::raw::c_void,
                                       aCapacity: usize, aElementSize: usize);
 }
 extern "C" {
     pub fn Gecko_ClearPODTArray(aArray: *mut ::std::os::raw::c_void,
@@ -308,16 +321,23 @@ extern "C" {
     pub fn Gecko_GetNextSiblingElement(element: RawGeckoElementBorrowed)
      -> RawGeckoElementBorrowedOrNull;
 }
 extern "C" {
     pub fn Gecko_GetDocumentElement(document: RawGeckoDocumentBorrowed)
      -> RawGeckoElementBorrowedOrNull;
 }
 extern "C" {
+    pub fn Gecko_LoadStyleSheet(loader: *mut Loader,
+                                parent: *mut ServoStyleSheet,
+                                import_rule: RawServoImportRuleStrong,
+                                url_bytes: *const u8, url_length: u32,
+                                media_bytes: *const u8, media_length: u32);
+}
+extern "C" {
     pub fn Gecko_MaybeCreateStyleChildrenIterator(node: RawGeckoNodeBorrowed)
      -> StyleChildrenIteratorOwnedOrNull;
 }
 extern "C" {
     pub fn Gecko_DropStyleChildrenIterator(it: StyleChildrenIteratorOwned);
 }
 extern "C" {
     pub fn Gecko_GetNextStyleChild(it: StyleChildrenIteratorBorrowedMut)
@@ -963,27 +983,46 @@ extern "C" {
     pub fn Servo_Element_ShouldTraverse(node: RawGeckoElementBorrowed)
      -> bool;
 }
 extern "C" {
     pub fn Servo_StyleSheet_Empty(parsing_mode: SheetParsingMode)
      -> RawServoStyleSheetStrong;
 }
 extern "C" {
-    pub fn Servo_StyleSheet_FromUTF8Bytes(data: *const nsACString_internal,
+    pub fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader,
+                                          gecko_stylesheet:
+                                              *mut ServoStyleSheet,
+                                          data: *const nsACString_internal,
                                           parsing_mode: SheetParsingMode,
                                           base_url:
                                               *const nsACString_internal,
                                           base: *mut ThreadSafeURIHolder,
                                           referrer: *mut ThreadSafeURIHolder,
                                           principal:
                                               *mut ThreadSafePrincipalHolder)
      -> RawServoStyleSheetStrong;
 }
 extern "C" {
+    pub fn Servo_ImportRule_GetSheet(import_rule: RawServoImportRuleBorrowed)
+     -> RawServoStyleSheetStrong;
+}
+extern "C" {
+    pub fn Servo_StyleSheet_ClearAndUpdate(stylesheet:
+                                               RawServoStyleSheetBorrowed,
+                                           loader: *mut Loader,
+                                           gecko_stylesheet:
+                                               *mut ServoStyleSheet,
+                                           data: *const nsACString_internal,
+                                           base: *mut ThreadSafeURIHolder,
+                                           referrer: *mut ThreadSafeURIHolder,
+                                           principal:
+                                               *mut ThreadSafePrincipalHolder);
+}
+extern "C" {
     pub fn Servo_StyleSheet_HasRules(sheet: RawServoStyleSheetBorrowed)
      -> bool;
 }
 extern "C" {
     pub fn Servo_StyleSheet_GetRules(sheet: RawServoStyleSheetBorrowed)
      -> ServoCssRulesStrong;
 }
 extern "C" {
--- a/servo/components/style/gecko_bindings/structs_debug.rs
+++ b/servo/components/style/gecko_bindings/structs_debug.rs
@@ -1000,16 +1000,17 @@ pub mod root {
     pub const NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY: ::std::os::raw::c_uint = 2;
     pub const NS_STYLE_ORIENTATION_PORTRAIT: ::std::os::raw::c_uint = 0;
     pub const NS_STYLE_ORIENTATION_LANDSCAPE: ::std::os::raw::c_uint = 1;
     pub const NS_STYLE_SCAN_PROGRESSIVE: ::std::os::raw::c_uint = 0;
     pub const NS_STYLE_SCAN_INTERLACE: ::std::os::raw::c_uint = 1;
     pub const NS_STYLE_DISPLAY_MODE_BROWSER: ::std::os::raw::c_uint = 0;
     pub const NS_STYLE_DISPLAY_MODE_MINIMAL_UI: ::std::os::raw::c_uint = 1;
     pub const NS_STYLE_DISPLAY_MODE_STANDALONE: ::std::os::raw::c_uint = 2;
+    pub const NS_STYLE_DISPLAY_MODE_FULLSCREEN: ::std::os::raw::c_uint = 3;
     pub const NS_STYLE_INHERIT_MASK: ::std::os::raw::c_uint = 16777215;
     pub const NS_STYLE_HAS_TEXT_DECORATION_LINES: ::std::os::raw::c_uint =
         16777216;
     pub const NS_STYLE_HAS_PSEUDO_ELEMENT_DATA: ::std::os::raw::c_uint =
         33554432;
     pub const NS_STYLE_RELEVANT_LINK_VISITED: ::std::os::raw::c_uint =
         67108864;
     pub const NS_STYLE_IS_STYLE_IF_VISITED: ::std::os::raw::c_uint =
@@ -2357,16 +2358,24 @@ pub mod root {
             pub static mut StyleSheet__cycleCollectorGlobal:
                        root::mozilla::StyleSheet_cycleCollection;
         }
         #[test]
         fn bindgen_test_layout_StyleSheet() {
             assert_eq!(::std::mem::size_of::<StyleSheet>() , 88usize);
             assert_eq!(::std::mem::align_of::<StyleSheet>() , 8usize);
         }
+        #[repr(C)]
+        #[derive(Debug, Copy)]
+        pub struct ServoStyleSheet {
+            pub _address: u8,
+        }
+        impl Clone for ServoStyleSheet {
+            fn clone(&self) -> Self { *self }
+        }
         #[repr(u32)]
         #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
         pub enum Side {
             eSideTop = 0,
             eSideRight = 1,
             eSideBottom = 2,
             eSideLeft = 3,
         }
@@ -2588,17 +2597,17 @@ pub mod root {
         }
         impl Clone for FramePropertyDescriptorUntyped {
             fn clone(&self) -> Self { *self }
         }
         /**
  * The FramePropertyTable is optimized for storing 0 or 1 properties on
  * a given frame. Storing very large numbers of properties on a single
  * frame will not be efficient.
- * 
+ *
  * Property values are passed as void* but do not actually have to be
  * valid pointers. You can use NS_INT32_TO_PTR/NS_PTR_TO_INT32 to
  * store int32_t values. Null/zero values can be stored and retrieved.
  * Of course, the destructor function (if any) must handle such values
  * correctly.
  */
         #[repr(C)]
         #[derive(Debug)]
--- a/servo/components/style/gecko_bindings/structs_release.rs
+++ b/servo/components/style/gecko_bindings/structs_release.rs
@@ -1000,16 +1000,17 @@ pub mod root {
     pub const NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY: ::std::os::raw::c_uint = 2;
     pub const NS_STYLE_ORIENTATION_PORTRAIT: ::std::os::raw::c_uint = 0;
     pub const NS_STYLE_ORIENTATION_LANDSCAPE: ::std::os::raw::c_uint = 1;
     pub const NS_STYLE_SCAN_PROGRESSIVE: ::std::os::raw::c_uint = 0;
     pub const NS_STYLE_SCAN_INTERLACE: ::std::os::raw::c_uint = 1;
     pub const NS_STYLE_DISPLAY_MODE_BROWSER: ::std::os::raw::c_uint = 0;
     pub const NS_STYLE_DISPLAY_MODE_MINIMAL_UI: ::std::os::raw::c_uint = 1;
     pub const NS_STYLE_DISPLAY_MODE_STANDALONE: ::std::os::raw::c_uint = 2;
+    pub const NS_STYLE_DISPLAY_MODE_FULLSCREEN: ::std::os::raw::c_uint = 3;
     pub const NS_STYLE_INHERIT_MASK: ::std::os::raw::c_uint = 16777215;
     pub const NS_STYLE_HAS_TEXT_DECORATION_LINES: ::std::os::raw::c_uint =
         16777216;
     pub const NS_STYLE_HAS_PSEUDO_ELEMENT_DATA: ::std::os::raw::c_uint =
         33554432;
     pub const NS_STYLE_RELEVANT_LINK_VISITED: ::std::os::raw::c_uint =
         67108864;
     pub const NS_STYLE_IS_STYLE_IF_VISITED: ::std::os::raw::c_uint =
@@ -2340,16 +2341,24 @@ pub mod root {
             pub static mut StyleSheet__cycleCollectorGlobal:
                        root::mozilla::StyleSheet_cycleCollection;
         }
         #[test]
         fn bindgen_test_layout_StyleSheet() {
             assert_eq!(::std::mem::size_of::<StyleSheet>() , 80usize);
             assert_eq!(::std::mem::align_of::<StyleSheet>() , 8usize);
         }
+        #[repr(C)]
+        #[derive(Debug, Copy)]
+        pub struct ServoStyleSheet {
+            pub _address: u8,
+        }
+        impl Clone for ServoStyleSheet {
+            fn clone(&self) -> Self { *self }
+        }
         #[repr(u32)]
         #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
         pub enum Side {
             eSideTop = 0,
             eSideRight = 1,
             eSideBottom = 2,
             eSideLeft = 3,
         }
@@ -2571,17 +2580,17 @@ pub mod root {
         }
         impl Clone for FramePropertyDescriptorUntyped {
             fn clone(&self) -> Self { *self }
         }
         /**
  * The FramePropertyTable is optimized for storing 0 or 1 properties on
  * a given frame. Storing very large numbers of properties on a single
  * frame will not be efficient.
- * 
+ *
  * Property values are passed as void* but do not actually have to be
  * valid pointers. You can use NS_INT32_TO_PTR/NS_PTR_TO_INT32 to
  * store int32_t values. Null/zero values can be stored and retrieved.
  * Of course, the destructor function (if any) must handle such values
  * correctly.
  */
         #[repr(C)]
         #[derive(Debug)]
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -31,36 +31,40 @@ use style::gecko::wrapper::GeckoElement;
 use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
 use style::gecko_bindings::bindings::{RawServoStyleRuleBorrowed, RawServoStyleRuleStrong};
 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::{ServoCssRulesBorrowed, ServoCssRulesStrong};
 use style::gecko_bindings::bindings::{nsACString, nsAString};
 use style::gecko_bindings::bindings::RawGeckoElementBorrowed;
+use style::gecko_bindings::bindings::RawServoImportRuleBorrowed;
 use style::gecko_bindings::bindings::ServoComputedValuesBorrowedOrNull;
 use style::gecko_bindings::bindings::nsTArrayBorrowed_uintptr_t;
 use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID};
 use style::gecko_bindings::structs::{ThreadSafePrincipalHolder, ThreadSafeURIHolder};
 use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint};
+use style::gecko_bindings::structs::Loader;
+use style::gecko_bindings::structs::ServoStyleSheet;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasBoxFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI};
 use style::parallel;
 use style::parser::{ParserContext, ParserContextExtraData};
 use style::properties::{CascadeFlags, ComputedValues, Importance, PropertyDeclaration};
 use style::properties::{PropertyDeclarationParseResult, PropertyDeclarationBlock, PropertyId};
 use style::properties::{apply_declarations, parse_one_declaration};
 use style::restyle_hints::RestyleHint;
 use style::selector_parser::PseudoElementCascadeType;
 use style::sequential;
 use style::string_cache::Atom;
-use style::stylesheets::{CssRule, CssRules, Origin, Stylesheet, StyleRule};
+use style::stylesheets::{CssRule, CssRules, Origin, Stylesheet, StyleRule, ImportRule};
+use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::{recalc_style_at, DomTraversal, PerLevelTraversalData};
 use style_traits::ToCss;
 use stylesheet_loader::StylesheetLoader;
 
 /*
  * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
@@ -220,27 +224,28 @@ pub extern "C" fn Servo_Element_ShouldTr
 pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyleSheetStrong {
     let url = ServoUrl::parse("about:blank").unwrap();
     let extra_data = ParserContextExtraData::default();
     let origin = match mode {
         SheetParsingMode::eAuthorSheetFeatures => Origin::Author,
         SheetParsingMode::eUserSheetFeatures => Origin::User,
         SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent,
     };
-    let loader = StylesheetLoader::new();
     let sheet = Arc::new(Stylesheet::from_str(
-        "", url, origin, Default::default(), Some(&loader),
+        "", url, origin, Default::default(), None,
         Box::new(StdoutErrorReporter), extra_data));
     unsafe {
         transmute(sheet)
     }
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(data: *const nsACString,
+pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader,
+                                                 stylesheet: *mut ServoStyleSheet,
+                                                 data: *const nsACString,
                                                  mode: SheetParsingMode,
                                                  base_url: *const nsACString,
                                                  base: *mut ThreadSafeURIHolder,
                                                  referrer: *mut ThreadSafeURIHolder,
                                                  principal: *mut ThreadSafePrincipalHolder)
                                                  -> RawServoStyleSheetStrong {
     let input = unsafe { data.as_ref().unwrap().as_str_unchecked() };
 
@@ -252,26 +257,72 @@ pub extern "C" fn Servo_StyleSheet_FromU
 
     let base_str = unsafe { base_url.as_ref().unwrap().as_str_unchecked() };
     let url = ServoUrl::parse(base_str).unwrap();
     let extra_data = unsafe { ParserContextExtraData {
         base: Some(GeckoArcURI::new(base)),
         referrer: Some(GeckoArcURI::new(referrer)),
         principal: Some(GeckoArcPrincipal::new(principal)),
     }};
-    let loader = StylesheetLoader::new();
+    let loader = if loader.is_null() {
+        None
+    } else {
+        Some(StylesheetLoader::new(loader, stylesheet))
+    };
+
+    // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
+    let loader: Option<&StyleStylesheetLoader> = match loader {
+        None => None,
+        Some(ref s) => Some(s),
+    };
+
     let sheet = Arc::new(Stylesheet::from_str(
-        input, url, origin, Default::default(), Some(&loader),
+        input, url, origin, Default::default(), loader,
         Box::new(StdoutErrorReporter), extra_data));
     unsafe {
         transmute(sheet)
     }
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_StyleSheet_ClearAndUpdate(stylesheet: RawServoStyleSheetBorrowed,
+                                                  loader: *mut Loader,
+                                                  gecko_stylesheet: *mut ServoStyleSheet,
+                                                  data: *const nsACString,
+                                                  base: *mut ThreadSafeURIHolder,
+                                                  referrer: *mut ThreadSafeURIHolder,
+                                                  principal: *mut ThreadSafePrincipalHolder)
+{
+    let input = unsafe { data.as_ref().unwrap().as_str_unchecked() };
+    let extra_data = unsafe { ParserContextExtraData {
+        base: Some(GeckoArcURI::new(base)),
+        referrer: Some(GeckoArcURI::new(referrer)),
+        principal: Some(GeckoArcPrincipal::new(principal)),
+    }};
+
+    let loader = if loader.is_null() {
+        None
+    } else {
+        Some(StylesheetLoader::new(loader, gecko_stylesheet))
+    };
+
+    // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
+    let loader: Option<&StyleStylesheetLoader> = match loader {
+        None => None,
+        Some(ref s) => Some(s),
+    };
+
+    let sheet = Stylesheet::as_arc(&stylesheet);
+    sheet.rules.write().0.clear();
+
+    Stylesheet::update_from_str(&sheet, input, loader,
+                                Box::new(StdoutErrorReporter), extra_data);
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_StyleSet_AppendStyleSheet(raw_data: RawServoStyleSetBorrowed,
                                                   raw_sheet: RawServoStyleSheetBorrowed,
                                                   flush: bool) {
     let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
     let sheet = HasArcFFI::as_arc(&raw_sheet);
     data.stylesheets.retain(|x| !arc_ptr_eq(x, sheet));
     data.stylesheets.push(sheet.clone());
     data.stylesheets_changed = true;
@@ -453,16 +504,26 @@ pub extern "C" fn Servo_StyleRule_GetCss
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: RawServoStyleRuleBorrowed, result: *mut nsAString) -> () {
     let rule = RwLock::<StyleRule>::as_arc(&rule);
     rule.read().selectors.to_css(unsafe { result.as_mut().unwrap() }).unwrap();
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_ImportRule_AddRef(rule: RawServoImportRuleBorrowed) -> () {
+    unsafe { RwLock::<ImportRule>::addref(rule) };
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ImportRule_Release(rule: RawServoImportRuleBorrowed) -> () {
+    unsafe { RwLock::<ImportRule>::release(rule) };
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null: ServoComputedValuesBorrowedOrNull,
                                                          pseudo_tag: *mut nsIAtom,
                                                          raw_data: RawServoStyleSetBorrowed)
      -> ServoComputedValuesStrong {
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
     let atom = Atom::from(pseudo_tag);
     let pseudo = PseudoElement::from_atom_unchecked(atom, /* anon_box = */ true);
 
@@ -828,16 +889,24 @@ pub extern "C" fn Servo_NoteExplicitHint
         restyle_data.hint.insert(&restyle_hint.into());
         restyle_data.damage |= damage;
     } else {
         debug!("(Element not styled, discarding hints)");
     }
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_ImportRule_GetSheet(import_rule:
+                                            RawServoImportRuleBorrowed)
+                                            -> RawServoStyleSheetStrong {
+    let import_rule = RwLock::<ImportRule>::as_arc(&import_rule);
+    unsafe { transmute(import_rule.read().stylesheet.clone()) }
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_CheckChangeHint(element: RawGeckoElementBorrowed) -> nsChangeHint
 {
     let element = GeckoElement(element);
     if element.get_data().is_none() {
         error!("Trying to get change hint from unstyled element");
         return nsChangeHint(0);
     }
 
--- a/servo/ports/geckolib/stylesheet_loader.rs
+++ b/servo/ports/geckolib/stylesheet_loader.rs
@@ -1,21 +1,47 @@
 /* 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/. */
 
 use parking_lot::RwLock;
+use std::mem;
 use std::sync::Arc;
+use style::gecko_bindings::bindings::Gecko_LoadStyleSheet;
+use style::gecko_bindings::structs::{Loader, ServoStyleSheet};
 use style::stylesheets::{ImportRule, StylesheetLoader as StyleStylesheetLoader};
+use style_traits::ToCss;
 
-pub struct StylesheetLoader;
+pub struct StylesheetLoader(*mut Loader, *mut ServoStyleSheet);
 
 impl StylesheetLoader {
-    pub fn new() -> Self {
-        StylesheetLoader
+    pub fn new(loader: *mut Loader, parent: *mut ServoStyleSheet) -> Self {
+        StylesheetLoader(loader, parent)
     }
 }
 
 impl StyleStylesheetLoader for StylesheetLoader {
-    fn request_stylesheet(&self, _import: &Arc<RwLock<ImportRule>>) {
-        // FIXME(emilio): Implement `@import` in stylo.
+    fn request_stylesheet(&self, import_rule: &Arc<RwLock<ImportRule>>) {
+        let import = import_rule.read();
+        let (spec_bytes, spec_len) = import.url.as_slice_components()
+            .expect("Import only loads valid URLs");
+
+        // TODO(emilio): We probably want to share media representation with
+        // Gecko in Stylo.
+        //
+        // This also allows us to get rid of a bunch of extra work to evaluate
+        // and ensure parity, and shouldn't be much Gecko work given we always
+        // evaluate them on the main thread.
+        //
+        // Meanwhile, this works.
+        let media = import.stylesheet.media.read().to_css_string();
+
+        unsafe {
+            Gecko_LoadStyleSheet(self.0,
+                                 self.1,
+                                 mem::transmute(import_rule.clone()),
+                                 spec_bytes,
+                                 spec_len as u32,
+                                 media.as_bytes().as_ptr(),
+                                 media.len() as u32);
+        }
     }
 }