merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Mon, 05 Jun 2017 11:05:49 +0200
changeset 410389 275588f4d852d7dc183a9dcc70a311413dc7a063
parent 410354 7d5df2dead2c9edaa41688a82d9d9dcc86529ef2 (diff)
parent 410388 da5a14ae4ce3084aa63a502e96af1a4c6ef76853 (current diff)
child 410390 6d4b65abc362bd5f7b6406d3b98fa538a1f492eb
child 410407 81e0a353d2dc82c8ea71a5dc26956f292a867895
child 410435 43383407d7c14ca3f1ee006e2b57ec5fe3ff0d3e
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone55.0a1
first release with
nightly linux32
275588f4d852 / 55.0a1 / 20170605100313 / files
nightly linux64
275588f4d852 / 55.0a1 / 20170605100313 / files
nightly mac
275588f4d852 / 55.0a1 / 20170605030204 / files
nightly win32
275588f4d852 / 55.0a1 / 20170605030204 / files
nightly win64
275588f4d852 / 55.0a1 / 20170605030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: G0dLvIdtcAi
toolkit/components/extensions/ExtensionManagement.jsm
toolkit/components/extensions/ExtensionParent.jsm
toolkit/components/extensions/test/xpcshell/xpcshell.ini
toolkit/mozapps/extensions/internal/XPIInstall.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1623,26 +1623,23 @@ pref("dom.mozBrowserFramesEnabled", true
 pref("extensions.pocket.enabled", true);
 
 pref("signon.schemeUpgrades", true);
 
 // "Simplify Page" feature in Print Preview. This feature is disabled by default
 // in toolkit.
 //
 // This feature is only enabled on Nightly for Linux until bug 1306295 is fixed.
-// For non-Linux, this feature is only enabled up to early Beta.
 #ifdef UNIX_BUT_NOT_MAC
 #if defined(NIGHTLY_BUILD)
 pref("print.use_simplify_page", true);
 #endif
 #else
-#if defined(EARLY_BETA_OR_EARLIER)
 pref("print.use_simplify_page", true);
 #endif
-#endif
 
 // Space separated list of URLS that are allowed to send objects (instead of
 // only strings) through webchannels. This list is duplicated in mobile/android/app/mobile.js
 pref("webchannel.allowObject.urlWhitelist", "https://accounts.firefox.com https://content.cdn.mozilla.net https://input.mozilla.org https://support.mozilla.org https://install.mozilla.org");
 
 // Whether or not the browser should scan for unsubmitted
 // crash reports, and then show a notification for submitting
 // those reports.
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -354,16 +354,22 @@
     <key id="key_sanitize_mac" command="Tools:Sanitize" keycode="VK_BACK" modifiers="accel,shift"/>
 #endif
     <key id="key_quitApplication" key="&quitApplicationCmd.key;"
 #ifdef XP_WIN
          modifiers="accel,shift"
 #else
          modifiers="accel"
 #endif
+# On OS X, dark voodoo magic invokes the quit code for this key.
+# So we're not adding the attribute on OSX because of backwards/add-on compat.
+# See bug 1369909 for background on this.
+#ifndef XP_MACOSX
+         command="cmd_quitApplication"
+#endif
          reserved="true"/>
 
 #ifdef FULL_BROWSER_WINDOW
     <key id="key_undoCloseTab" command="History:UndoCloseTab" key="&tabCmd.commandkey;" modifiers="accel,shift"/>
 #endif
     <key id="key_undoCloseWindow" command="History:UndoCloseWindow" key="&newNavigatorCmd.key;" modifiers="accel,shift"/>
 
 #ifdef XP_GNOME
--- a/browser/components/migration/tests/marionette/test_refresh_firefox.py
+++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py
@@ -372,25 +372,23 @@ class TestFirefoxRefresh(MarionetteTestC
           // Ensure the current (temporary) profile is in profiles.ini:
           let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
           let profileName = arguments[1];
           let myProfile = global.profSvc.createProfile(profD, profileName);
           global.profSvc.flush()
 
           // Now add the reset parameters:
           let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
-          let allMarionettePrefs = Services.prefs.getChildList("marionette.");
+          let prefsToKeep = Array.from(Services.prefs.getChildList("marionette."));
+          prefsToKeep.push("datareporting.policy.dataSubmissionPolicyBypassNotification");
           let prefObj = {};
-          for (let pref of allMarionettePrefs) {
-            let prefSuffix = pref.substr("marionette.".length);
-            let prefVal = global.Preferences.get(pref);
-            prefObj[prefSuffix] = prefVal;
+          for (let pref of prefsToKeep) {
+            prefObj[pref] = global.Preferences.get(pref);
           }
-          let marionetteInfo = JSON.stringify(prefObj);
-          env.set("MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS", marionetteInfo);
+          env.set("MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS", JSON.stringify(prefObj));
           env.set("MOZ_RESET_PROFILE_RESTART", "1");
           env.set("XRE_PROFILE_PATH", arguments[0]);
           env.set("XRE_PROFILE_NAME", profileName);
         """, script_args=[self.marionette.instance.profile.profile, profileName])
 
         profileLeafName = os.path.basename(os.path.normpath(self.marionette.instance.profile.profile))
 
         # Now restart the browser to get it reset:
--- a/browser/components/preferences/in-content-new/findInPage.js
+++ b/browser/components/preferences/in-content-new/findInPage.js
@@ -287,29 +287,19 @@ var gSearchResultsPane = {
       }
 
       // Access key are presented
       let complexTextNodesResult = this.highlightMatches(accessKeyTextNodes, nodeSizes, allNodeText.toLowerCase(), searchPhrase);
 
       // Searching some elements, such as xul:button, have a 'label' attribute that contains the user-visible text.
       let labelResult = this.stringMatchesFilters(nodeObject.getAttribute("label"), searchPhrase);
 
-      // Creating tooltips for buttons
-      if (labelResult && nodeObject.tagName === "button") {
-        this.listSearchTooltips.push(nodeObject);
-      }
-
       // Searching some elements, such as xul:label, store their user-visible text in a "value" attribute.
       let valueResult = this.stringMatchesFilters(nodeObject.getAttribute("value"), searchPhrase);
 
-      // Creating tooltips for buttons
-      if (valueResult && nodeObject.tagName === "button") {
-        this.listSearchTooltips.push(nodeObject);
-      }
-
       // Searching some elements, such as xul:button, buttons to open subdialogs.
       let keywordsResult = this.stringMatchesFilters(nodeObject.getAttribute("searchkeywords"), searchPhrase);
 
       // Creating tooltips for buttons
       if (keywordsResult && nodeObject.tagName === "button") {
         this.listSearchTooltips.push(nodeObject);
       }
 
--- a/layout/reftests/w3c-css/received/reftest.list
+++ b/layout/reftests/w3c-css/received/reftest.list
@@ -235,17 +235,17 @@ fuzzy-if(OSX,40,6) == css-values-3/ch-un
 == css-values-3/initial-background-color.html css-values-3/reference/all-green.html
 == css-values-3/vh-calc-support-pct.html css-values-3/reference/all-green.html
 == css-values-3/vh-calc-support.html css-values-3/reference/all-green.html
 == css-values-3/vh-em-inherit.html css-values-3/reference/all-green.html
 == css-values-3/vh-inherit.html css-values-3/reference/all-green.html
 == css-values-3/vh-interpolate-pct.html css-values-3/reference/all-green.html
 == css-values-3/vh-interpolate-px.html css-values-3/reference/all-green.html
 == css-values-3/vh-interpolate-vh.html css-values-3/reference/all-green.html
-fails-if(styloVsGecko||stylo) == css-values-3/vh-support-atviewport.html css-values-3/reference/all-green.html
+== css-values-3/vh-support-atviewport.html css-values-3/reference/all-green.html
 == css-values-3/vh-support-margin.html css-values-3/reference/all-green.html
 skip == css-values-3/vh-support-transform-origin.html css-values-3/reference/all-green.html
 skip == css-values-3/vh-support-transform-translate.html css-values-3/reference/all-green.html
 == css-values-3/vh-support.html css-values-3/reference/all-green.html
 == css-values-3/vh-zero-support.html css-values-3/reference/all-green.html
 skip == css-values-3/vh_not_refreshing_on_chrome.html css-values-3/reference/vh_not_refreshing_on_chrome-ref.html
 skip == css-values-3/vh_not_refreshing_on_chrome_iframe.html css-values-3/reference/vh_not_refreshing_on_chrome-ref.html
 == css-writing-modes-3/abs-pos-non-replaced-icb-vlr-003.xht css21/reference/ref-filled-green-100px-square.xht
--- a/layout/style/CSS.cpp
+++ b/layout/style/CSS.cpp
@@ -88,17 +88,18 @@ CSS::Supports(const GlobalObject& aGloba
 
   if (info.mStyleBackendType == StyleBackendType::Servo) {
     NS_ConvertUTF16toUTF8 cond(aCondition);
     return Servo_CSSSupports(&cond);
   }
 
   nsCSSParser parser;
   return parser.EvaluateSupportsCondition(aCondition, info.mDocURI,
-                                          info.mBaseURI, info.mPrincipal);
+                                          info.mBaseURI, info.mPrincipal,
+                                          css::SupportsParsingSettings::ImpliedParentheses);
 }
 
 /* static */ void
 CSS::Escape(const GlobalObject& aGlobal,
             const nsAString& aIdent,
             nsAString& aReturn)
 {
   nsStyleUtil::AppendEscapedCSSIdent(aIdent, aReturn);
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -258,17 +258,19 @@ public:
                                    const nsAString& aValue,
                                    nsIURI* aDocURL,
                                    nsIURI* aBaseURL,
                                    nsIPrincipal* aDocPrincipal);
 
   bool EvaluateSupportsCondition(const nsAString& aCondition,
                                  nsIURI* aDocURL,
                                  nsIURI* aBaseURL,
-                                 nsIPrincipal* aDocPrincipal);
+                                 nsIPrincipal* aDocPrincipal,
+                                 SupportsParsingSettings aSettings
+                                  = SupportsParsingSettings::Normal);
 
   already_AddRefed<nsIAtom> ParseCounterStyleName(const nsAString& aBuffer,
                                                   nsIURI* aURL);
 
   bool ParseCounterDescriptor(nsCSSCounterDesc aDescID,
                               const nsAString& aBuffer,
                               nsIURI* aSheetURL,
                               nsIURI* aBaseURL,
@@ -2409,25 +2411,32 @@ CSSParserImpl::EvaluateSupportsDeclarati
 
   return parsedOK;
 }
 
 bool
 CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration,
                                          nsIURI* aDocURL,
                                          nsIURI* aBaseURL,
-                                         nsIPrincipal* aDocPrincipal)
+                                         nsIPrincipal* aDocPrincipal,
+                                         SupportsParsingSettings aSettings)
 {
   nsCSSScanner scanner(aDeclaration, 0);
   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
   InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
   nsAutoSuppressErrors suppressErrors(this);
 
   bool conditionMet;
-  bool parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
+  bool parsedOK;
+
+  if (aSettings == SupportsParsingSettings::ImpliedParentheses) {
+    parsedOK = ParseSupportsConditionInParensInsideParens(conditionMet) && !GetToken(true);
+  } else {
+    parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
+  }
 
   CLEAR_ERROR();
   ReleaseScanner();
 
   return parsedOK && conditionMet;
 }
 
 bool
@@ -18150,20 +18159,22 @@ nsCSSParser::EvaluateSupportsDeclaration
     EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL,
                                 aDocPrincipal);
 }
 
 bool
 nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition,
                                        nsIURI* aDocURL,
                                        nsIURI* aBaseURL,
-                                       nsIPrincipal* aDocPrincipal)
+                                       nsIPrincipal* aDocPrincipal,
+                                       SupportsParsingSettings aSettings)
 {
   return static_cast<CSSParserImpl*>(mImpl)->
-    EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL, aDocPrincipal);
+    EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL,
+                              aDocPrincipal, aSettings);
 }
 
 bool
 nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue,
                                          VariableEnumFunc aFunc,
                                          void* aData)
 {
   return static_cast<CSSParserImpl*>(mImpl)->
--- a/layout/style/nsCSSParser.h
+++ b/layout/style/nsCSSParser.h
@@ -29,16 +29,22 @@ struct nsRuleData;
 
 namespace mozilla {
 class CSSStyleSheet;
 class CSSVariableValues;
 namespace css {
 class Rule;
 class Declaration;
 class StyleRule;
+
+enum class SupportsParsingSettings {
+  Normal,
+  ImpliedParentheses
+};
+
 } // namespace css
 } // namespace mozilla
 
 // Interface to the css parser.
 
 class MOZ_STACK_CLASS nsCSSParser {
 public:
   explicit nsCSSParser(mozilla::css::Loader* aLoader = nullptr,
@@ -247,21 +253,27 @@ public:
                                    const nsAString& aValue,
                                    nsIURI* aDocURL,
                                    nsIURI* aBaseURL,
                                    nsIPrincipal* aDocPrincipal);
 
   /**
    * Parse an @supports condition and returns the result of evaluating the
    * condition.
+   *
+   * The one-argument CSS.supports() allows for providing a parentheses-less
+   * @supports condition, i.e. the parentheses are "implied". In such a case,
+   * aSettings can be set to ImpliedParentheses.
    */
   bool EvaluateSupportsCondition(const nsAString& aCondition,
                                  nsIURI* aDocURL,
                                  nsIURI* aBaseURL,
-                                 nsIPrincipal* aDocPrincipal);
+                                 nsIPrincipal* aDocPrincipal,
+                                 mozilla::css::SupportsParsingSettings aSettings
+                                  = mozilla::css::SupportsParsingSettings::Normal);
 
   typedef void (*VariableEnumFunc)(const nsAString&, void*);
 
   /**
    * Parses aPropertyValue as a property value and calls aFunc for each
    * variable reference that is found.  Returns false if there was
    * a syntax error in the use of variable references.
    */
--- a/layout/style/test/stylo-failures.md
+++ b/layout/style/test/stylo-failures.md
@@ -110,17 +110,17 @@ to mochitest command.
     * test_selectors.html `:dir` [11]
 * Quirks mode support
   * test_hover_quirk.html: hover quirks bug 1355724 [6]
 * Unit should be preserved after parsing servo/servo#15346
   * test_units_time.html [1]
 * getComputedStyle style doesn't contain custom properties bug 1336891
   * test_variable_serialization_computed.html [35]
   * test_variables.html `custom property name` [2]
-* test_css_supports.html: issues around @supports syntax servo/servo#15482 [8]
+* test_css_supports.html: issues around @supports syntax servo/servo#15482 [7]
 * test_author_specified_style.html: support serializing color as author specified bug 1348165 [27]
 * browser_newtab_share_rule_processors.js: agent style sheet sharing [1]
 * :visited support (bug 1328509)
   * test_visited_reftests.html `selector-descendant-2.xhtml` [2]
   * ... `selector-child-2.xhtml` [2]
   * ... `color-on-bullets-1.html` [2]
   * ... `inherit-keyword-1.xhtml` [2]
   * ... `mathml-links.html` [2]
--- a/layout/style/test/test_css_supports.html
+++ b/layout/style/test/test_css_supports.html
@@ -17,16 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 779917 **/
 
 function runTest()
 {
   var passingConditions = [
+    "color: green",
     "(color: green)",
     "((color: green))",
     "(color: green !important)",
     "(color: rainbow) or (color: green)",
     "(color: green) or (color: rainbow)",
     "(color: green) and (color: blue)",
     "(color: rainbow) or (color: iridescent) or (color: green)",
     "(color: red) and (color: green) and (color: blue)",
@@ -64,17 +65,16 @@ function runTest()
     "not (color: rainbow) and not (color: iridescent)",
     "not (color: rainbow) or (color: green)",
     "(not (color: rainbow) or (color: green))",
     "(unknown: green)",
     "not ({ something @with (unbalanced brackets })",
     "(color: green) or an-extension(that is [unbalanced)",
     "not(unknown: unknown)",
     "(color: green) or(color: blue)",
-    "color: green",
     "(color: green;)",
     "(font-family: 'Helvetica\n",
     "(font-family: 'Helvetica\n')",
     "()",
     ""
   ];
 
   var passingDeclarations = [
--- a/layout/tools/reftest/reftest-preferences.js
+++ b/layout/tools/reftest/reftest-preferences.js
@@ -73,16 +73,19 @@ user_pref("browser.startup.page", 0);
 
 // Allow XUL and XBL files to be opened from file:// URIs
 user_pref("dom.allow_XUL_XBL_for_file", true);
 
 // Allow view-source URIs to be opened from URIs that share
 // their protocol with the inner URI of the view-source URI
 user_pref("security.view-source.reachable-from-inner-protocol", true);
 
+// Skip data reporting policy notifications.
+user_pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
+
 // Ensure that telemetry is disabled, so we don't connect to the telemetry
 // server in the middle of the tests.
 user_pref("toolkit.telemetry.enabled", false);
 user_pref("toolkit.telemetry.unified", false);
 // Likewise for safebrowsing.
 user_pref("browser.safebrowsing.phishing.enabled", false);
 user_pref("browser.safebrowsing.malware.enabled", false);
 user_pref("browser.safebrowsing.blockedURIs.enabled", false);
--- a/media/libstagefright/binding/ResourceStream.cpp
+++ b/media/libstagefright/binding/ResourceStream.cpp
@@ -25,43 +25,43 @@ ResourceStream::ReadAt(int64_t aOffset, 
                        size_t* aBytesRead)
 {
   uint32_t sum = 0;
   uint32_t bytesRead = 0;
   do {
     uint64_t offset = aOffset + sum;
     char* buffer = reinterpret_cast<char*>(aBuffer) + sum;
     uint32_t toRead = aCount - sum;
-    nsresult rv = mResource->ReadAt(offset, buffer, toRead, &bytesRead);
+    nsresult rv = mResource.ReadAt(offset, buffer, toRead, &bytesRead);
     if (NS_FAILED(rv)) {
       return false;
     }
     sum += bytesRead;
   } while (sum < aCount && bytesRead > 0);
 
   *aBytesRead = sum;
   return true;
 }
 
 bool
 ResourceStream::CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
                         size_t* aBytesRead)
 {
-  nsresult rv = mResource->ReadFromCache(reinterpret_cast<char*>(aBuffer),
-                                         aOffset, aCount);
+  nsresult rv = mResource.GetResource()->ReadFromCache(
+    reinterpret_cast<char*>(aBuffer), aOffset, aCount);
   if (NS_FAILED(rv)) {
     *aBytesRead = 0;
     return false;
   }
   *aBytesRead = aCount;
   return true;
 }
 
 bool
 ResourceStream::Length(int64_t* aSize)
 {
-  if (mResource->GetLength() < 0)
+  if (mResource.GetLength() < 0)
     return false;
-  *aSize = mResource->GetLength();
+  *aSize = mResource.GetLength();
   return true;
 }
 
 } // namespace mp4_demuxer
--- a/media/libstagefright/binding/include/mp4_demuxer/ResourceStream.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/ResourceStream.h
@@ -20,30 +20,30 @@ public:
   virtual bool ReadAt(int64_t offset, void* aBuffer, size_t aCount,
                       size_t* aBytesRead) override;
   virtual bool CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
                             size_t* aBytesRead) override;
   virtual bool Length(int64_t* size) override;
 
   void Pin()
   {
-    mResource->Pin();
+    mResource.GetResource()->Pin();
     ++mPinCount;
   }
 
   void Unpin()
   {
-    mResource->Unpin();
+    mResource.GetResource()->Unpin();
     MOZ_ASSERT(mPinCount);
     --mPinCount;
   }
 
 protected:
   virtual ~ResourceStream();
 
 private:
-  RefPtr<mozilla::MediaResource> mResource;
+  mozilla::MediaResourceIndex mResource;
   uint32_t mPinCount;
 };
 
 }
 
 #endif // RESOURCESTREAM_H_
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -15,16 +15,17 @@
 #include "MultiLogCTVerifier.h"
 #include "NSSCertDBTrustDomain.h"
 #include "NSSErrorsService.h"
 #include "cert.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "nsNSSComponent.h"
+#include "nsPromiseFlatString.h"
 #include "nsServiceManagerUtils.h"
 #include "pk11pub.h"
 #include "pkix/pkix.h"
 #include "pkix/pkixnss.h"
 #include "secmod.h"
 
 using namespace mozilla::ct;
 using namespace mozilla::pkix;
@@ -937,46 +938,46 @@ CertVerifier::VerifyCert(CERTCertificate
 }
 
 Result
 CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert,
                      /*optional*/ const SECItem* stapledOCSPResponse,
                      /*optional*/ const SECItem* sctsFromTLS,
                                   Time time,
                      /*optional*/ void* pinarg,
-                                  const char* hostname,
+                                  const nsACString& hostname,
                           /*out*/ UniqueCERTCertList& builtChain,
                      /*optional*/ UniqueCERTCertList* peerCertChain,
                      /*optional*/ bool saveIntermediatesInPermanentDatabase,
                      /*optional*/ Flags flags,
                      /*optional*/ const OriginAttributes& originAttributes,
                  /*optional out*/ SECOidTag* evOidPolicy,
                  /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
                  /*optional out*/ KeySizeStatus* keySizeStatus,
                  /*optional out*/ SHA1ModeResult* sha1ModeResult,
                  /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
                  /*optional out*/ CertificateTransparencyInfo* ctInfo)
 {
   MOZ_ASSERT(peerCert);
   // XXX: MOZ_ASSERT(pinarg);
-  MOZ_ASSERT(hostname);
-  MOZ_ASSERT(hostname[0]);
+  MOZ_ASSERT(!hostname.IsEmpty());
 
   if (evOidPolicy) {
     *evOidPolicy = SEC_OID_UNKNOWN;
   }
 
-  if (!hostname || !hostname[0]) {
+  if (hostname.IsEmpty()) {
     return Result::ERROR_BAD_CERT_DOMAIN;
   }
 
   // CreateCertErrorRunnable assumes that CheckCertHostname is only called
   // if VerifyCert succeeded.
   Result rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time,
-                         pinarg, hostname, builtChain, peerCertChain, flags,
+                         pinarg, PromiseFlatCString(hostname).get(), builtChain,
+                         peerCertChain, flags,
                          stapledOCSPResponse, sctsFromTLS, originAttributes,
                          evOidPolicy, ocspStaplingStatus, keySizeStatus,
                          sha1ModeResult, pinningTelemetryInfo, ctInfo);
   if (rv != Success) {
     return rv;
   }
 
   Input peerCertInput;
@@ -1000,18 +1001,19 @@ CertVerifier::VerifySSLServerCert(const 
   if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) {
     rv = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr);
     if (rv != Success) {
       return rv;
     }
   }
 
   Input hostnameInput;
-  rv = hostnameInput.Init(BitwiseCast<const uint8_t*, const char*>(hostname),
-                          strlen(hostname));
+  rv = hostnameInput.Init(
+    BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()),
+    hostname.Length());
   if (rv != Success) {
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
   bool isBuiltInRoot;
   rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
   if (rv != Success) {
     return rv;
   }
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -10,16 +10,17 @@
 #include "BRNameMatchingPolicy.h"
 #include "CTPolicyEnforcer.h"
 #include "CTVerifyResult.h"
 #include "OCSPCache.h"
 #include "ScopedNSSTypes.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
+#include "nsString.h"
 #include "pkix/pkixtypes.h"
 
 #if defined(_MSC_VER)
 #pragma warning(push)
 // Silence "RootingAPI.h(718): warning C4324: 'js::DispatchWrapper<T>':
 // structure was padded due to alignment specifier with [ T=void * ]"
 #pragma warning(disable:4324)
 #endif /* defined(_MSC_VER) */
@@ -143,17 +144,17 @@ public:
    /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr);
 
   mozilla::pkix::Result VerifySSLServerCert(
                     const UniqueCERTCertificate& peerCert,
        /*optional*/ const SECItem* stapledOCSPResponse,
        /*optional*/ const SECItem* sctsFromTLS,
                     mozilla::pkix::Time time,
        /*optional*/ void* pinarg,
-                    const char* hostname,
+                    const nsACString& hostname,
             /*out*/ UniqueCERTCertList& builtChain,
        /*optional*/ UniqueCERTCertList* peerCertChain = nullptr,
        /*optional*/ bool saveIntermediatesInPermanentDatabase = false,
        /*optional*/ Flags flags = 0,
        /*optional*/ const OriginAttributes& originAttributes =
                       OriginAttributes(),
    /*optional out*/ SECOidTag* evOidPolicy = nullptr,
    /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -123,16 +123,17 @@
 #include "nsISocketProvider.h"
 #include "nsIThreadPool.h"
 #include "nsNSSCertificate.h"
 #include "nsNSSComponent.h"
 #include "nsNSSIOLayer.h"
 #include "nsNSSShutDown.h"
 #include "nsSSLStatus.h"
 #include "nsServiceManagerUtils.h"
+#include "nsString.h"
 #include "nsURLHelper.h"
 #include "nsXPCOMCIDInternal.h"
 #include "pkix/pkix.h"
 #include "pkix/pkixnss.h"
 #include "secerr.h"
 #include "secoidt.h"
 #include "secport.h"
 #include "ssl.h"
@@ -223,17 +224,17 @@ class SSLServerCertVerificationResult : 
 public:
   NS_DECL_NSIRUNNABLE
 
   SSLServerCertVerificationResult(nsNSSSocketInfo* infoObject,
                                   PRErrorCode errorCode,
                                   Telemetry::HistogramID telemetryID = Telemetry::HistogramCount,
                                   uint32_t telemetryValue = -1,
                                   SSLErrorMessageType errorMessageType =
-                                      PlainErrorMessage);
+                                    SSLErrorMessageType::Plain);
 
   void Dispatch();
 private:
   const RefPtr<nsNSSSocketInfo> mInfoObject;
 public:
   const PRErrorCode mErrorCode;
   const SSLErrorMessageType mErrorMessageType;
   const Telemetry::HistogramID mTelemetryID;
@@ -333,25 +334,24 @@ MapCertErrorToProbeValue(PRErrorCode err
     probeValue ^= FATAL_ERROR_FLAG;
     probeValue += 90;
   }
   return probeValue;
 }
 
 SECStatus
 DetermineCertOverrideErrors(const UniqueCERTCertificate& cert,
-                            const char* hostName,
+                            const nsACString& hostName,
                             PRTime now, PRErrorCode defaultErrorCodeToReport,
                             /*out*/ uint32_t& collectedErrors,
                             /*out*/ PRErrorCode& errorCodeTrust,
                             /*out*/ PRErrorCode& errorCodeMismatch,
                             /*out*/ PRErrorCode& errorCodeTime)
 {
   MOZ_ASSERT(cert);
-  MOZ_ASSERT(hostName);
   MOZ_ASSERT(collectedErrors == 0);
   MOZ_ASSERT(errorCodeTrust == 0);
   MOZ_ASSERT(errorCodeMismatch == 0);
   MOZ_ASSERT(errorCodeTime == 0);
 
   // Assumes the error prioritization described in mozilla::pkix's
   // BuildForward function. Also assumes that CheckCertHostname was only
   // called if CertVerifier::VerifyCert succeeded.
@@ -414,18 +414,18 @@ DetermineCertOverrideErrors(const Unique
   if (defaultErrorCodeToReport != SSL_ERROR_BAD_CERT_DOMAIN) {
     Input certInput;
     if (certInput.Init(cert->derCert.data, cert->derCert.len) != Success) {
       PR_SetError(SEC_ERROR_BAD_DER, 0);
       return SECFailure;
     }
     Input hostnameInput;
     Result result = hostnameInput.Init(
-      BitwiseCast<const uint8_t*, const char*>(hostName),
-      strlen(hostName));
+      BitwiseCast<const uint8_t*, const char*>(hostName.BeginReading()),
+      hostName.Length());
     if (result != Success) {
       PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
       return SECFailure;
     }
     // Use a lax policy so as to not generate potentially spurious name
     // mismatch "hints".
     BRNameMatchingPolicy nameMatchingPolicy(
       BRNameMatchingPolicy::Mode::DoNotEnforce);
@@ -532,18 +532,17 @@ CertErrorRunnable::CheckCertOverrides()
   Unused << mFdForLogging;
 
   if (!NS_IsMainThread()) {
     NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
     return new SSLServerCertVerificationResult(mInfoObject,
                                                mDefaultErrorCodeToReport);
   }
 
-  int32_t port;
-  mInfoObject->GetPort(&port);
+  int32_t port = mInfoObject->GetPort();
 
   nsAutoCString hostWithPortString(mInfoObject->GetHostName());
   hostWithPortString.Append(':');
   hostWithPortString.AppendInt(port);
 
   uint32_t remaining_display_errors = mCollectedErrors;
 
   bool overrideAllowed;
@@ -637,17 +636,17 @@ CertErrorRunnable::CheckCertOverrides()
                                 : mErrorCodeTime     ? mErrorCodeTime
                                 : mDefaultErrorCodeToReport;
 
   SSLServerCertVerificationResult* result =
     new SSLServerCertVerificationResult(mInfoObject,
                                         errorCodeToReport,
                                         Telemetry::HistogramCount,
                                         -1,
-                                        OverridableCertErrorMessage);
+                                        SSLErrorMessageType::OverridableCert);
 
   LogInvalidCertError(mInfoObject,
                       result->mErrorCode,
                       result->mErrorMessageType);
 
   return result;
 }
 
@@ -677,17 +676,17 @@ CreateCertErrorRunnable(CertVerifier& ce
 
   uint32_t probeValue = MapCertErrorToProbeValue(defaultErrorCodeToReport);
   Telemetry::Accumulate(Telemetry::SSL_CERT_VERIFICATION_ERRORS, probeValue);
 
   uint32_t collected_errors = 0;
   PRErrorCode errorCodeTrust = 0;
   PRErrorCode errorCodeMismatch = 0;
   PRErrorCode errorCodeTime = 0;
-  if (DetermineCertOverrideErrors(cert, infoObject->GetHostNameRaw(), now,
+  if (DetermineCertOverrideErrors(cert, infoObject->GetHostName(), now,
                                   defaultErrorCodeToReport, collected_errors,
                                   errorCodeTrust, errorCodeMismatch,
                                   errorCodeTime) != SECSuccess) {
     // Attempt to enforce that if DetermineCertOverrideErrors failed,
     // PR_SetError was set with a non-overridable error. This is because if we
     // return from CreateCertErrorRunnable without calling
     // infoObject->SetStatusErrorBits, we won't have the required information
     // to actually add a certificate error override. This results in a broken
@@ -1398,17 +1397,17 @@ AuthCertificate(CertVerifier& certVerifi
   if (!infoObject->SharedState().IsOCSPStaplingEnabled() ||
       !infoObject->SharedState().IsOCSPMustStapleEnabled()) {
     flags |= CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
   }
 
   Result rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
                                                sctsFromTLSExtension, time,
                                                infoObject,
-                                               infoObject->GetHostNameRaw(),
+                                               infoObject->GetHostName(),
                                                certList, &peerCertChain,
                                                saveIntermediates, flags,
                                                infoObject->
                                                       GetOriginAttributes(),
                                                &evOidPolicy,
                                                &ocspStaplingStatus,
                                                &keySizeStatus, &sha1ModeResult,
                                                &pinningTelemetryInfo,
@@ -1791,17 +1790,17 @@ AuthCertificateHook(void* arg, PRFileDes
         return SECFailure;
       }
 
       if (runnable->mResult->mErrorCode == 0) {
         return SECSuccess; // cert error override occurred.
       }
 
       // We must call SetCanceled here to set the error message type
-      // in case it isn't PlainErrorMessage, which is what we would
+      // in case it isn't SSLErrorMessageType::Plain, which is what we would
       // default to if we just called
       // PR_SetError(runnable->mResult->mErrorCode, 0) and returned
       // SECFailure without doing this.
       socketInfo->SetCanceled(runnable->mResult->mErrorCode,
                               runnable->mResult->mErrorMessageType);
       error = runnable->mResult->mErrorCode;
     }
   }
--- a/security/manager/ssl/TransportSecurityInfo.cpp
+++ b/security/manager/ssl/TransportSecurityInfo.cpp
@@ -33,23 +33,23 @@
                        //read/write buffer to a log.
                        //Uses PR_LOG except on Mac where
                        //we always write out to our own
                        //file.
 
 namespace mozilla { namespace psm {
 
 TransportSecurityInfo::TransportSecurityInfo()
-  : mMutex("TransportSecurityInfo::mMutex"),
-    mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE),
-    mSubRequestsBrokenSecurity(0),
-    mSubRequestsNoSecurity(0),
-    mErrorCode(0),
-    mErrorMessageType(PlainErrorMessage),
-    mPort(0)
+  : mMutex("TransportSecurityInfo::mMutex")
+  , mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE)
+  , mSubRequestsBrokenSecurity(0)
+  , mSubRequestsNoSecurity(0)
+  , mErrorCode(0)
+  , mErrorMessageType(SSLErrorMessageType::Plain)
+  , mPort(0)
 {
 }
 
 TransportSecurityInfo::~TransportSecurityInfo()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return;
@@ -71,36 +71,27 @@ NS_IMPL_ISUPPORTS(TransportSecurityInfo,
                   nsIClassInfo)
 
 void
 TransportSecurityInfo::SetHostName(const char* host)
 {
   mHostName.Assign(host);
 }
 
-nsresult
+void
 TransportSecurityInfo::SetPort(int32_t aPort)
 {
   mPort = aPort;
-  return NS_OK;
 }
 
-nsresult
-TransportSecurityInfo::GetPort(int32_t *aPort)
-{
-  *aPort = mPort;
-  return NS_OK;
-}
-
-nsresult
+void
 TransportSecurityInfo::SetOriginAttributes(
   const OriginAttributes& aOriginAttributes)
 {
   mOriginAttributes = aOriginAttributes;
-  return NS_OK;
 }
 
 PRErrorCode
 TransportSecurityInfo::GetErrorCode() const
 {
   MutexAutoLock lock(mMutex);
 
   return mErrorCode;
@@ -119,21 +110,20 @@ TransportSecurityInfo::SetCanceled(PRErr
 
 NS_IMETHODIMP
 TransportSecurityInfo::GetSecurityState(uint32_t* state)
 {
   *state = mSecurityState;
   return NS_OK;
 }
 
-nsresult
+void
 TransportSecurityInfo::SetSecurityState(uint32_t aState)
 {
   mSecurityState = aState;
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 TransportSecurityInfo::GetCountSubRequestsBrokenSecurity(
   int32_t *aSubRequestsBrokenSecurity)
 {
   *aSubRequestsBrokenSecurity = mSubRequestsBrokenSecurity;
   return NS_OK;
@@ -236,21 +226,21 @@ TransportSecurityInfo::formatErrorMessag
     return NS_OK;
   }
 
   if (!XRE_IsParentProcess()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsresult rv;
-  MOZ_ASSERT(errorMessageType != OverridableCertErrorMessage ||
+  MOZ_ASSERT(errorMessageType != SSLErrorMessageType::OverridableCert ||
                (mSSLStatus && mSSLStatus->HasServerCert() &&
                 mSSLStatus->mHaveCertErrorBits),
              "formatErrorMessage() called for cert error without cert");
-  if (errorMessageType == OverridableCertErrorMessage &&
+  if (errorMessageType == SSLErrorMessageType::OverridableCert &&
       mSSLStatus && mSSLStatus->HasServerCert()) {
     rv = formatOverridableCertErrorMessage(*mSSLStatus, errorCode,
                                            mHostName, mPort,
                                            suppressPort443,
                                            wantsHtml,
                                            result);
   } else {
     rv = formatPlainErrorMessage(mHostName, mPort, 
@@ -492,22 +482,20 @@ TransportSecurityInfo::GetSSLStatus(nsIS
   NS_ENSURE_ARG_POINTER(_result);
 
   *_result = mSSLStatus;
   NS_IF_ADDREF(*_result);
 
   return NS_OK;
 }
 
-nsresult
+void
 TransportSecurityInfo::SetSSLStatus(nsSSLStatus *aSSLStatus)
 {
   mSSLStatus = aSSLStatus;
-
-  return NS_OK;
 }
 
 /* Formats an error message for non-certificate-related SSL errors
  * and non-overridable certificate errors (both are of type
  * PlainErrormMessage). Use formatOverridableCertErrorMessage
  * for overridable cert errors.
  */
 static nsresult
@@ -943,25 +931,19 @@ RememberCertErrorsTable::RememberCertErr
 static nsresult
 GetHostPortKey(TransportSecurityInfo* infoObject, /*out*/ nsCString& result)
 {
   MOZ_ASSERT(infoObject);
   NS_ENSURE_ARG(infoObject);
 
   result.Truncate();
 
-  int32_t port;
-  nsresult rv = infoObject->GetPort(&port);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
   result.Assign(infoObject->GetHostName());
   result.Append(':');
-  result.AppendInt(port);
+  result.AppendInt(infoObject->GetPort());
 
   return NS_OK;
 }
 
 void
 RememberCertErrorsTable::RememberCertHasError(TransportSecurityInfo* infoObject,
                                               nsSSLStatus* status,
                                               SECStatus certVerificationResult)
--- a/security/manager/ssl/TransportSecurityInfo.h
+++ b/security/manager/ssl/TransportSecurityInfo.h
@@ -20,70 +20,68 @@
 #include "nsITransportSecurityInfo.h"
 #include "nsNSSShutDown.h"
 #include "nsSSLStatus.h"
 #include "nsString.h"
 #include "pkix/pkixtypes.h"
 
 namespace mozilla { namespace psm {
 
-enum SSLErrorMessageType {
-  OverridableCertErrorMessage  = 1, // for *overridable* certificate errors
-  PlainErrorMessage = 2             // all other errors (or "no error")
+enum class SSLErrorMessageType {
+  OverridableCert = 1, // for *overridable* certificate errors
+  Plain = 2,           // all other errors (or "no error")
 };
 
 class TransportSecurityInfo : public nsITransportSecurityInfo,
                               public nsIInterfaceRequestor,
                               public nsISSLStatusProvider,
                               public nsIAssociatedContentSecurity,
                               public nsISerializable,
                               public nsIClassInfo,
                               public nsNSSShutDownObject,
                               public nsOnPK11LogoutCancelObject
 {
 protected:
   virtual ~TransportSecurityInfo();
 public:
   TransportSecurityInfo();
-  
+
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITRANSPORTSECURITYINFO
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSISSLSTATUSPROVIDER
   NS_DECL_NSIASSOCIATEDCONTENTSECURITY
   NS_DECL_NSISERIALIZABLE
   NS_DECL_NSICLASSINFO
 
-  nsresult SetSecurityState(uint32_t aState);
+  void SetSecurityState(uint32_t aState);
 
   const nsACString & GetHostName() const { return mHostName; }
-  const char * GetHostNameRaw() const { return mHostName.get(); }
 
   void SetHostName(const char* host);
 
   int32_t GetPort() const { return mPort; }
-  nsresult GetPort(int32_t *aPort);
-  nsresult SetPort(int32_t aPort);
+  void SetPort(int32_t aPort);
 
   const OriginAttributes& GetOriginAttributes() const {
     return mOriginAttributes;
   }
-  nsresult SetOriginAttributes(const OriginAttributes& aOriginAttributes);
+  void SetOriginAttributes(const OriginAttributes& aOriginAttributes);
 
   PRErrorCode GetErrorCode() const;
-  
+
   void GetErrorLogMessage(PRErrorCode errorCode,
                           ::mozilla::psm::SSLErrorMessageType errorMessageType,
                           nsString &result);
-  
+
   void SetCanceled(PRErrorCode errorCode,
                    ::mozilla::psm::SSLErrorMessageType errorMessageType);
 
   /* Set SSL Status values */
-  nsresult SetSSLStatus(nsSSLStatus *aSSLStatus);
+  void SetSSLStatus(nsSSLStatus* aSSLStatus);
   nsSSLStatus* SSLStatus() { return mSSLStatus; }
   void SetStatusErrorBits(nsNSSCertificate* cert, uint32_t collected_errors);
 
   nsresult SetFailedCertChain(UniqueCERTCertList certList);
 
 private:
   mutable ::mozilla::Mutex mMutex;
 
@@ -133,20 +131,19 @@ private:
 
 public:
   void RememberCertHasError(TransportSecurityInfo * infoobject,
                             nsSSLStatus * status,
                             SECStatus certVerificationResult);
   void LookupCertErrorBits(TransportSecurityInfo * infoObject,
                            nsSSLStatus* status);
 
-  static nsresult Init()
+  static void Init()
   {
     sInstance = new RememberCertErrorsTable();
-    return NS_OK;
   }
 
   static RememberCertErrorsTable & GetInstance()
   {
     MOZ_ASSERT(sInstance);
     return *sInstance;
   }
 
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -1127,17 +1127,17 @@ DetermineEVAndCTStatusAndSetNewCert(RefP
   UniqueCERTCertList unusedBuiltChain;
   const bool saveIntermediates = false;
   mozilla::pkix::Result rv = certVerifier->VerifySSLServerCert(
     cert,
     stapledOCSPResponse,
     sctsFromTLSExtension,
     mozilla::pkix::Now(),
     infoObject,
-    infoObject->GetHostNameRaw(),
+    infoObject->GetHostName(),
     unusedBuiltChain,
     &peerCertChain,
     saveIntermediates,
     flags,
     infoObject->GetOriginAttributes(),
     &evOidPolicy,
     nullptr, // OCSP stapling telemetry
     nullptr, // key size telemetry
--- a/security/manager/ssl/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/nsNSSCertificateDB.cpp
@@ -1395,31 +1395,31 @@ VerifyCertAtTime(nsIX509Cert* aCert,
 
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
 
   UniqueCERTCertList resultChain;
   SECOidTag evOidPolicy;
   mozilla::pkix::Result result;
 
-  const nsCString& flatHostname = PromiseFlatCString(aHostname);
   if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
     result = certVerifier->VerifySSLServerCert(nssCert,
                                                nullptr, // stapledOCSPResponse
                                                nullptr, // sctsFromTLSExtension
                                                aTime,
                                                nullptr, // Assume no context
-                                               flatHostname.get(),
+                                               aHostname,
                                                resultChain,
                                                nullptr, // no peerCertChain
                                                false, // don't save intermediates
                                                aFlags,
                                                OriginAttributes(),
                                                &evOidPolicy);
   } else {
+    const nsCString& flatHostname = PromiseFlatCString(aHostname);
     result = certVerifier->VerifyCert(nssCert.get(), aUsage, aTime,
                                       nullptr, // Assume no context
                                       aHostname.IsVoid() ? nullptr
                                                          : flatHostname.get(),
                                       resultChain,
                                       nullptr, // no peerCertChain
                                       aFlags,
                                       nullptr, // stapledOCSPResponse
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -370,17 +370,17 @@ nsNSSSocketInfo::DriveHandshake()
   SECStatus rv = SSL_ForceHandshake(mFd);
 
   if (rv != SECSuccess) {
     errorCode = PR_GetError();
     if (errorCode == PR_WOULD_BLOCK_ERROR) {
       return NS_BASE_STREAM_WOULD_BLOCK;
     }
 
-    SetCanceled(errorCode, PlainErrorMessage);
+    SetCanceled(errorCode, SSLErrorMessageType::Plain);
     return GetXPCOMFromNSSError(errorCode);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
 {
@@ -445,26 +445,25 @@ nsNSSSocketInfo::IsAcceptableForHost(con
   // actually be the wrong chain. We should consider having JoinConnection
   // return the certificate chain built here, so that the calling Necko code
   // can associate the correct certificate chain with the HTTP transactions it
   // is trying to join onto this connection.
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   if (!certVerifier) {
     return NS_OK;
   }
-  nsAutoCString hostnameFlat(PromiseFlatCString(hostname));
   CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY;
   UniqueCERTCertList unusedBuiltChain;
   mozilla::pkix::Result result =
     certVerifier->VerifySSLServerCert(nssCert,
                                       nullptr, // stapledOCSPResponse
                                       nullptr, // sctsFromTLSExtension
                                       mozilla::pkix::Now(),
                                       nullptr, // pinarg
-                                      hostnameFlat.get(),
+                                      hostname,
                                       unusedBuiltChain,
                                       nullptr, // no peerCertChain
                                       false, // save intermediates
                                       flags);
   if (result != mozilla::pkix::Success) {
     return NS_OK;
   }
 
@@ -620,17 +619,17 @@ nsNSSSocketInfo::SetCertVerificationResu
   MOZ_ASSERT(mCertVerificationState == waiting_for_cert_verification,
              "Invalid state transition to cert_verification_finished");
 
   if (mFd) {
     SECStatus rv = SSL_AuthCertificateComplete(mFd, errorCode);
     // Only replace errorCode if there was originally no error
     if (rv != SECSuccess && errorCode == 0) {
       errorCode = PR_GetError();
-      errorMessageType = PlainErrorMessage;
+      errorMessageType = SSLErrorMessageType::Plain;
       if (errorCode == 0) {
         NS_ERROR("SSL_AuthCertificateComplete didn't set error code");
         errorCode = PR_INVALID_STATE_ERROR;
       }
     }
   }
 
   if (errorCode) {
@@ -675,17 +674,17 @@ nsHandleSSLError(nsNSSSocketInfo* socket
   // SetCanceled()/GetError*().
   if (socketInfo->GetErrorCode()) {
     // If the socket has been flagged as canceled,
     // the code who did was responsible for setting the error code.
     return;
   }
 
   // We must cancel first, which sets the error code.
-  socketInfo->SetCanceled(err, PlainErrorMessage);
+  socketInfo->SetCanceled(err, SSLErrorMessageType::Plain);
   nsXPIDLString errorString;
   socketInfo->GetErrorLogMessage(err, errtype, errorString);
 
   if (!errorString.IsEmpty()) {
     nsContentUtils::LogSimpleConsoleError(errorString, "SSL");
   }
 }
 
@@ -1244,19 +1243,18 @@ checkHandshake(int32_t bytesTransfered, 
     // The socketInfo->GetErrorCode() check is here to ensure we don't try to
     // do the synchronous dispatch to the main thread unnecessarily after we've
     // already handled a certificate error. (SSLErrorRunnable calls
     // nsHandleSSLError, which has logic to avoid replacing the error message,
     // so without the !socketInfo->GetErrorCode(), it would just be an
     // expensive no-op.)
     if (!wantRetry && mozilla::psm::IsNSSErrorCode(err) &&
         !socketInfo->GetErrorCode()) {
-      RefPtr<SyncRunnableBase> runnable(new SSLErrorRunnable(socketInfo,
-                                                             PlainErrorMessage,
-                                                             err));
+      RefPtr<SyncRunnableBase> runnable(
+        new SSLErrorRunnable(socketInfo, SSLErrorMessageType::Plain, err));
       (void) runnable->DispatchToMainThreadAndWait();
     }
   } else if (wasReading && 0 == bytesTransfered) {
     // zero bytes on reading, socket closed
     if (handleHandshakeResultNow) {
       wantRetry = retryDueToTLSIntolerance(PR_END_OF_FILE_ERROR, socketInfo);
     }
   }
@@ -1284,17 +1282,17 @@ checkHandshake(int32_t bytesTransfered, 
 
   if (bytesTransfered < 0) {
     // Remember that we encountered an error so that getSocketInfoIfRunning
     // will correctly cause us to fail if another part of Gecko
     // (erroneously) calls an I/O function (PR_Send/PR_Recv/etc.) again on
     // this socket. Note that we use the original error because if we use
     // PR_CONNECT_RESET_ERROR, we'll repeated try to reconnect.
     if (originalError != PR_WOULD_BLOCK_ERROR && !socketInfo->GetErrorCode()) {
-      socketInfo->SetCanceled(originalError, PlainErrorMessage);
+      socketInfo->SetCanceled(originalError, SSLErrorMessageType::Plain);
     }
     PR_SetError(err, 0);
   }
 
   return bytesTransfered;
 }
 
 } // namespace
@@ -2251,19 +2249,16 @@ ClientAuthDataRunnable::RunOnTargetThrea
         }
       }
 
       if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
         // list is empty - no matching certs
         goto loser;
       }
 
-      int32_t port;
-      mSocketInfo->GetPort(&port);
-
       UniquePORTString corg(CERT_GetOrgName(&mServerCert->subject));
       nsAutoCString org(corg.get());
 
       UniquePORTString cissuer(CERT_GetOrgName(&mServerCert->issuer));
       nsAutoCString issuer(cissuer.get());
 
       nsCOMPtr<nsIMutableArray> certArray = nsArrayBase::Create();
       if (!certArray) {
@@ -2290,17 +2285,18 @@ ClientAuthDataRunnable::RunOnTargetThrea
                          NS_CLIENTAUTHDIALOGS_CONTRACTID);
 
       if (NS_FAILED(rv)) {
         goto loser;
       }
 
       uint32_t selectedIndex = 0;
       bool certChosen = false;
-      rv = dialogs->ChooseCertificate(mSocketInfo, hostname, port, org, issuer,
+      rv = dialogs->ChooseCertificate(mSocketInfo, hostname,
+                                      mSocketInfo->GetPort(), org, issuer,
                                       certArray, &selectedIndex, &certChosen);
       if (NS_FAILED(rv)) {
         goto loser;
       }
 
       // even if the user has canceled, we want to remember that, to avoid repeating prompts
       bool wantRemember = false;
       mSocketInfo->GetRememberClientAuthCertificate(&wantRemember);
--- a/security/manager/ssl/nsSiteSecurityService.cpp
+++ b/security/manager/ssl/nsSiteSecurityService.cpp
@@ -1071,17 +1071,17 @@ nsSiteSecurityService::ProcessPKPHeader(
   // to the site (or it's disabled, in which case we wouldn't want to enforce it
   // anyway).
   CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY |
                               CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
   if (certVerifier->VerifySSLServerCert(nssCert,
                                         nullptr, // stapledOCSPResponse
                                         nullptr, // sctsFromTLSExtension
                                         now, nullptr, // pinarg
-                                        host.get(), // hostname
+                                        host, // hostname
                                         certList,
                                         nullptr, // no peerCertChain
                                         false, // don't store intermediates
                                         flags,
                                         aOriginAttributes)
         != mozilla::pkix::Success) {
     return NS_ERROR_FAILURE;
   }
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -62,16 +62,25 @@ source = "registry+https://github.com/ru
 dependencies = [
  "heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "arraydeque"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nodrop 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "arrayvec"
 version = "0.3.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "nodrop 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -2865,16 +2874,17 @@ name = "strsim"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "style"
 version = "0.0.1"
 dependencies = [
  "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "arraydeque 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bindgen 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3494,16 +3504,17 @@ dependencies = [
 "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
 "checksum alloc-no-stdlib 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b21f6ad9c9957eb5d70c3dee16d31c092b3cab339628f821766b05e6833d72b8"
 "checksum android_glue 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d8289e9637439939cc92b1995b0972117905be88bc28116c86b64d6e589bcd38"
 "checksum android_injected_glue 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ec08bc5e100186b5223a24dcfe5655d1488aed9eafeb44fb9a0f67a4f53d0fc"
 "checksum angle 0.2.0 (git+https://github.com/servo/angle?branch=servo)" = "<none>"
 "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
 "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
 "checksum app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c89beb28482985f88b312de4021d748f45b3eecec6cc8dbaf0c2b3c3d1ce6da5"
+"checksum arraydeque 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "96e774cadb24c2245225280c6799793f9802b918a58a79615e9490607489a717"
 "checksum arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "699e63a93b79d717e8c3b5eb1b28b7780d0d6d9e59a72eb769291c83b0c8dc67"
 "checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
 "checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
 "checksum audio-video-metadata 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3b6ef29ee98ad95a37f34547fd7fb40724772294110ed6ca0445fc2e964c29d1"
 "checksum azure 0.15.0 (git+https://github.com/servo/rust-azure)" = "<none>"
 "checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76"
 "checksum backtrace-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0d842ea781ce92be2bf78a9b38883948542749640b8378b3b2f03d1fd9f1ff"
 "checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -90,26 +90,25 @@ use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::{Arc, Mutex};
 use std::sync::atomic::{AtomicBool, AtomicUsize};
 use std::sync::mpsc::{Receiver, Sender};
 use std::time::{SystemTime, Instant};
 use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto};
 use style::context::QuirksMode;
 use style::element_state::*;
-use style::keyframes::Keyframe;
 use style::media_queries::MediaList;
 use style::properties::PropertyDeclarationBlock;
 use style::selector_parser::{PseudoElement, Snapshot};
 use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocked};
 use style::stylearc::Arc as StyleArc;
 use style::stylesheets::{CssRules, FontFaceRule, KeyframesRule, MediaRule};
-use style::stylesheets::{NamespaceRule, StyleRule, ImportRule, SupportsRule};
+use style::stylesheets::{NamespaceRule, StyleRule, ImportRule, SupportsRule, ViewportRule};
+use style::stylesheets::keyframes_rule::Keyframe;
 use style::values::specified::Length;
-use style::viewport::ViewportRule;
 use time::Duration;
 use uuid::Uuid;
 use webrender_traits::{WebGLBufferId, WebGLError, WebGLFramebufferId, WebGLProgramId};
 use webrender_traits::{WebGLRenderbufferId, WebGLShaderId, WebGLTextureId, WebGLVertexArrayId};
 use webvr_traits::WebVRGamepadHand;
 
 /// A trait to allow tracing (only) DOM objects.
 pub unsafe trait JSTraceable {
--- a/servo/components/script/dom/css.rs
+++ b/servo/components/script/dom/css.rs
@@ -7,17 +7,17 @@ use dom::bindings::codegen::Bindings::Wi
 use dom::bindings::error::Fallible;
 use dom::bindings::reflector::Reflector;
 use dom::bindings::str::DOMString;
 use dom::window::Window;
 use dom_struct::dom_struct;
 use style::context::QuirksMode;
 use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
 use style::stylesheets::CssRuleType;
-use style::supports::{Declaration, parse_condition_or_declaration};
+use style::stylesheets::supports_rule::{Declaration, parse_condition_or_declaration};
 
 #[dom_struct]
 pub struct CSS {
     reflector_: Reflector,
 }
 
 impl CSS {
     /// http://dev.w3.org/csswg/cssom/#serialize-an-identifier
--- a/servo/components/script/dom/csskeyframerule.rs
+++ b/servo/components/script/dom/csskeyframerule.rs
@@ -7,19 +7,19 @@ use dom::bindings::inheritance::Castable
 use dom::bindings::js::{JS, MutNullableJS, Root};
 use dom::bindings::reflector::{DomObject, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::cssrule::{CSSRule, SpecificCSSRule};
 use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
 use dom::cssstylesheet::CSSStyleSheet;
 use dom::window::Window;
 use dom_struct::dom_struct;
-use style::keyframes::Keyframe;
 use style::shared_lock::{Locked, ToCssWithGuard};
 use style::stylearc::Arc;
+use style::stylesheets::keyframes_rule::Keyframe;
 
 #[dom_struct]
 pub struct CSSKeyframeRule {
     cssrule: CSSRule,
     #[ignore_heap_size_of = "Arc"]
     keyframerule: Arc<Locked<Keyframe>>,
     style_decl: MutNullableJS<CSSStyleDeclaration>,
 }
--- a/servo/components/script/dom/csskeyframesrule.rs
+++ b/servo/components/script/dom/csskeyframesrule.rs
@@ -11,20 +11,19 @@ use dom::bindings::js::{MutNullableJS, R
 use dom::bindings::reflector::{DomObject, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::csskeyframerule::CSSKeyframeRule;
 use dom::cssrule::{CSSRule, SpecificCSSRule};
 use dom::cssrulelist::{CSSRuleList, RulesSource};
 use dom::cssstylesheet::CSSStyleSheet;
 use dom::window::Window;
 use dom_struct::dom_struct;
-use style::keyframes::{Keyframe, KeyframeSelector};
 use style::shared_lock::{Locked, ToCssWithGuard};
 use style::stylearc::Arc;
-use style::stylesheets::KeyframesRule;
+use style::stylesheets::keyframes_rule::{KeyframesRule, Keyframe, KeyframeSelector};
 use style::values::KeyframesName;
 
 #[dom_struct]
 pub struct CSSKeyframesRule {
     cssrule: CSSRule,
     #[ignore_heap_size_of = "Arc"]
     keyframesrule: Arc<Locked<KeyframesRule>>,
     rulelist: MutNullableJS<CSSRuleList>,
--- a/servo/components/script/dom/csssupportsrule.rs
+++ b/servo/components/script/dom/csssupportsrule.rs
@@ -12,17 +12,17 @@ use dom::cssconditionrule::CSSConditionR
 use dom::cssrule::SpecificCSSRule;
 use dom::cssstylesheet::CSSStyleSheet;
 use dom::window::Window;
 use dom_struct::dom_struct;
 use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
 use style::shared_lock::{Locked, ToCssWithGuard};
 use style::stylearc::Arc;
 use style::stylesheets::{CssRuleType, SupportsRule};
-use style::supports::SupportsCondition;
+use style::stylesheets::supports_rule::SupportsCondition;
 use style_traits::ToCss;
 
 #[dom_struct]
 pub struct CSSSupportsRule {
     cssconditionrule: CSSConditionRule,
     #[ignore_heap_size_of = "Arc"]
     supportsrule: Arc<Locked<SupportsRule>>,
 }
--- a/servo/components/script/dom/cssviewportrule.rs
+++ b/servo/components/script/dom/cssviewportrule.rs
@@ -7,17 +7,17 @@ use dom::bindings::js::Root;
 use dom::bindings::reflector::reflect_dom_object;
 use dom::bindings::str::DOMString;
 use dom::cssrule::{CSSRule, SpecificCSSRule};
 use dom::cssstylesheet::CSSStyleSheet;
 use dom::window::Window;
 use dom_struct::dom_struct;
 use style::shared_lock::{Locked, ToCssWithGuard};
 use style::stylearc::Arc;
-use style::viewport::ViewportRule;
+use style::stylesheets::ViewportRule;
 
 #[dom_struct]
 pub struct CSSViewportRule {
     cssrule: CSSRule,
     #[ignore_heap_size_of = "Arc"]
     viewportrule: Arc<Locked<ViewportRule>>,
 }
 
--- a/servo/components/script/dom/htmlmetaelement.rs
+++ b/servo/components/script/dom/htmlmetaelement.rs
@@ -21,18 +21,17 @@ use dom_struct::dom_struct;
 use html5ever::{LocalName, Prefix};
 use servo_config::prefs::PREFS;
 use std::ascii::AsciiExt;
 use std::sync::atomic::AtomicBool;
 use style::attr::AttrValue;
 use style::media_queries::MediaList;
 use style::str::HTML_SPACE_CHARACTERS;
 use style::stylearc::Arc;
-use style::stylesheets::{Stylesheet, CssRule, CssRules, Origin};
-use style::viewport::ViewportRule;
+use style::stylesheets::{Stylesheet, CssRule, CssRules, Origin, ViewportRule};
 
 #[dom_struct]
 pub struct HTMLMetaElement {
     htmlelement: HTMLElement,
     #[ignore_heap_size_of = "Arc"]
     stylesheet: DOMRefCell<Option<Arc<Stylesheet>>>,
     cssom_stylesheet: MutNullableJS<CSSStyleSheet>,
 }
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -27,16 +27,17 @@ servo = ["serde", "serde_derive", "heaps
 
          "rayon/unstable", "servo_url"]
 testing = []
 gecko_debug = ["nsstring_vendor/gecko_debug"]
 
 [dependencies]
 app_units = "0.4.1"
 arrayvec = "0.3.20"
+arraydeque = "0.2.3"
 atomic_refcell = "0.1"
 bitflags = "0.7"
 bit-vec = "0.4.3"
 byteorder = "1.0"
 cfg-if = "0.1.0"
 cssparser = "0.13.7"
 encoding = {version = "0.2", optional = true}
 euclid = "0.11"
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -6,27 +6,27 @@
 #![deny(missing_docs)]
 
 use Atom;
 use bezier::Bezier;
 use context::SharedStyleContext;
 use dom::OpaqueNode;
 use euclid::point::Point2D;
 use font_metrics::FontMetricsProvider;
-use keyframes::{KeyframesStep, KeyframesStepValue};
 use properties::{self, CascadeFlags, ComputedValues, Importance};
 use properties::animated_properties::{AnimatedProperty, TransitionProperty};
 use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
 use properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount;
 use properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
 use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd;
 use properties::longhands::transition_timing_function::single_value::computed_value::T as TransitionTimingFunction;
 use rule_tree::CascadeLevel;
 use std::sync::mpsc::Sender;
 use stylearc::Arc;
+use stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue};
 use timer::Timer;
 use values::computed::Time;
 
 /// This structure represents a keyframes animation current iteration state.
 ///
 /// If the iteration count is infinite, there's no other state, otherwise we
 /// have to keep track the current iteration and the max iteration count.
 #[derive(Debug, Clone)]
--- a/servo/components/style/cache.rs
+++ b/servo/components/style/cache.rs
@@ -1,39 +1,38 @@
 /* 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/. */
 
 //! A simple LRU cache.
 
 #![deny(missing_docs)]
 
-use std::collections::VecDeque;
-use std::collections::vec_deque;
+extern crate arraydeque;
+use self::arraydeque::Array;
+use self::arraydeque::ArrayDeque;
 
 /// A LRU cache used to store a set of at most `n` elements at the same time.
 ///
 /// The most-recently-used entry is at index zero.
-pub struct LRUCache<K> {
-    entries: VecDeque<K>,
-    cache_size: usize,
+pub struct LRUCache <K: Array>{
+    entries: ArrayDeque<K>,
 }
 
 /// A iterator over the items of the LRU cache.
-pub type LRUCacheIterator<'a, K> = vec_deque::Iter<'a, K>;
+pub type LRUCacheIterator<'a, K> = arraydeque::Iter<'a, K>;
 
 /// A iterator over the mutable items of the LRU cache.
-pub type LRUCacheMutIterator<'a, K> = vec_deque::IterMut<'a, K>;
+pub type LRUCacheMutIterator<'a, K> = arraydeque::IterMut<'a, K>;
 
-impl<K: PartialEq> LRUCache<K> {
+impl<K: Array> LRUCache<K> {
     /// Create a new LRU cache with `size` elements at most.
-    pub fn new(size: usize) -> Self {
+    pub fn new() -> Self {
         LRUCache {
-          entries: VecDeque::with_capacity(size),
-          cache_size: size,
+          entries: ArrayDeque::new(),
         }
     }
 
     /// Returns the number of elements in the cache.
     pub fn num_entries(&self) -> usize {
         self.entries.len()
     }
 
@@ -44,31 +43,31 @@ impl<K: PartialEq> LRUCache<K> {
         if pos != last_index {
             let entry = self.entries.remove(pos).unwrap();
             self.entries.push_front(entry);
         }
     }
 
     /// Iterate over the contents of this cache, from more to less recently
     /// used.
-    pub fn iter(&self) -> vec_deque::Iter<K> {
+    pub fn iter(&self) -> arraydeque::Iter<K::Item> {
         self.entries.iter()
     }
 
     /// Iterate mutably over the contents of this cache.
-    pub fn iter_mut(&mut self) -> vec_deque::IterMut<K> {
+    pub fn iter_mut(&mut self) -> arraydeque::IterMut<K::Item> {
         self.entries.iter_mut()
     }
 
     /// Insert a given key in the cache.
-    pub fn insert(&mut self, key: K) {
-        if self.entries.len() == self.cache_size {
+    pub fn insert(&mut self, key: K::Item) {
+        if self.entries.len() == self.entries.capacity() {
             self.entries.pop_back();
         }
         self.entries.push_front(key);
-        debug_assert!(self.entries.len() <= self.cache_size);
+        debug_assert!(self.entries.len() <= self.entries.capacity());
     }
 
     /// Evict all elements from the cache.
     pub fn evict_all(&mut self) {
         self.entries.clear();
     }
 }
--- a/servo/components/style/context.rs
+++ b/servo/components/style/context.rs
@@ -344,32 +344,32 @@ impl<E: TElement> SequentialTask<E> {
 
 /// Map from Elements to ElementSelectorFlags. Used to defer applying selector
 /// flags until after the traversal.
 pub struct SelectorFlagsMap<E: TElement> {
     /// The hashmap storing the flags to apply.
     map: FnvHashMap<SendElement<E>, ElementSelectorFlags>,
     /// An LRU cache to avoid hashmap lookups, which can be slow if the map
     /// gets big.
-    cache: LRUCache<(SendElement<E>, ElementSelectorFlags)>,
+    cache: LRUCache<[(SendElement<E>, ElementSelectorFlags); 4 + 1]>,
 }
 
 #[cfg(debug_assertions)]
 impl<E: TElement> Drop for SelectorFlagsMap<E> {
     fn drop(&mut self) {
         debug_assert!(self.map.is_empty());
     }
 }
 
 impl<E: TElement> SelectorFlagsMap<E> {
     /// Creates a new empty SelectorFlagsMap.
     pub fn new() -> Self {
         SelectorFlagsMap {
             map: FnvHashMap::default(),
-            cache: LRUCache::new(4),
+            cache: LRUCache::new(),
         }
     }
 
     /// Inserts some flags into the map for a given element.
     pub fn insert_flags(&mut self, element: E, flags: ElementSelectorFlags) {
         let el = unsafe { SendElement::new(element) };
         // Check the cache. If the flags have already been noted, we're done.
         if self.cache.iter().find(|x| x.0 == el)
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -12,25 +12,25 @@ use gecko_bindings::bindings::{RawServoK
 use gecko_bindings::bindings::{RawServoMediaList, RawServoMediaRule};
 use gecko_bindings::bindings::{RawServoNamespaceRule, RawServoPageRule};
 use gecko_bindings::bindings::{RawServoRuleNode, RawServoRuleNodeStrong, RawServoDocumentRule};
 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 keyframes::Keyframe;
 use media_queries::MediaList;
 use properties::{ComputedValues, PropertyDeclarationBlock};
 use properties::animated_properties::AnimationValue;
 use rule_tree::StrongRuleNode;
 use shared_lock::Locked;
 use std::{mem, ptr};
 use stylesheets::{CssRules, Stylesheet, StyleRule, ImportRule, KeyframesRule, MediaRule};
 use stylesheets::{NamespaceRule, PageRule, SupportsRule, DocumentRule};
+use stylesheets::keyframes_rule::Keyframe;
 
 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 {}
 
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -96,27 +96,25 @@ pub mod animation;
 pub mod bezier;
 pub mod bloom;
 pub mod cache;
 pub mod cascade_info;
 pub mod context;
 pub mod counter_style;
 pub mod custom_properties;
 pub mod data;
-pub mod document_condition;
 pub mod dom;
 pub mod element_state;
 #[cfg(feature = "servo")] mod encoding_support;
 pub mod error_reporting;
 pub mod font_face;
 pub mod font_metrics;
 #[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko;
 #[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko_bindings;
 pub mod invalidation;
-pub mod keyframes;
 #[allow(missing_docs)] // TODO.
 pub mod logical_geometry;
 pub mod matching;
 pub mod media_queries;
 pub mod parallel;
 pub mod parser;
 pub mod restyle_hints;
 pub mod rule_tree;
@@ -129,24 +127,22 @@ pub mod stylist;
 #[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo;
 pub mod sequential;
 pub mod sink;
 pub mod str;
 pub mod style_adjuster;
 pub mod stylearc;
 pub mod stylesheet_set;
 pub mod stylesheets;
-pub mod supports;
 pub mod thread_state;
 pub mod timer;
 pub mod traversal;
 #[macro_use]
 #[allow(non_camel_case_types)]
 pub mod values;
-pub mod viewport;
 
 use std::fmt;
 use style_traits::ToCss;
 
 #[cfg(feature = "gecko")] pub use gecko_string_cache as string_cache;
 #[cfg(feature = "gecko")] pub use gecko_string_cache::Atom;
 #[cfg(feature = "gecko")] pub use gecko_string_cache::Namespace;
 #[cfg(feature = "gecko")] pub use gecko_string_cache::Atom as Prefix;
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -1069,29 +1069,27 @@
 
 // Define property that supports prefixed intrinsic size keyword values for gecko.
 // E.g. -moz-max-content, -moz-min-content, etc.
 <%def name="gecko_size_type(name, length_type, initial_value, logical, **kwargs)">
     <%call expr="longhand(name,
                           predefined_type=length_type,
                           logical=logical,
                           **kwargs)">
-        use std::fmt;
-        use style_traits::ToCss;
         % if not logical:
             use values::specified::AllowQuirks;
         % endif
         use values::specified::${length_type};
 
         pub mod computed_value {
             pub type T = ::values::computed::${length_type};
         }
 
-        #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
         pub struct SpecifiedValue(pub ${length_type});
 
         % if length_type == "MozLength":
         impl SpecifiedValue {
             /// Returns the `auto` value.
             pub fn auto() -> Self {
                 use values::specified::length::LengthOrPercentageOrAuto;
                 SpecifiedValue(MozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::Auto))
@@ -1126,22 +1124,16 @@
             % if "block" in name:
                 if let Ok(${length_type}::ExtremumLength(..)) = ret {
                     return Err(())
                 }
             % endif
             ret.map(SpecifiedValue)
         }
 
-        impl ToCss for SpecifiedValue {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-                self.0.to_css(dest)
-            }
-        }
-
         impl ToComputedValue for SpecifiedValue {
             type ComputedValue = computed_value::T;
             #[inline]
             fn to_computed_value(&self, context: &Context) -> computed_value::T {
                 % if not logical or "block" in name:
                     use values::computed::${length_type};
                 % endif
                 let computed = self.0.to_computed_value(context);
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -289,19 +289,24 @@ impl<'a> From<TransitionProperty> for Pr
 
 /// An animated property interpolation between two computed values for that
 /// property.
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum AnimatedProperty {
     % for prop in data.longhands:
         % if prop.animatable:
+            <%
+                if prop.is_animatable_with_computed_value:
+                    value_type = "longhands::{}::computed_value::T".format(prop.ident)
+                else:
+                    value_type = prop.animation_value_type
+            %>
             /// ${prop.name}
-            ${prop.camel_case}(longhands::${prop.ident}::computed_value::T,
-                               longhands::${prop.ident}::computed_value::T),
+            ${prop.camel_case}(${value_type}, ${value_type}),
         % endif
     % endfor
 }
 
 impl AnimatedProperty {
     /// Get the name of this property.
     pub fn name(&self) -> &'static str {
         match *self {
@@ -351,16 +356,19 @@ impl AnimatedProperty {
                         % if prop.animation_value_type == "discrete":
                             let value = if progress < 0.5 { from.clone() } else { to.clone() };
                         % else:
                             let value = match from.interpolate(to, progress) {
                                 Ok(value) => value,
                                 Err(()) => return,
                             };
                         % endif
+                        % if not prop.is_animatable_with_computed_value:
+                            let value: longhands::${prop.ident}::computed_value::T = value.into();
+                        % endif
                         style.mutate_${prop.style_struct.ident.strip("_")}().set_${prop.ident}(value);
                     }
                 % endif
             % endfor
         }
     }
 
     /// Get an animatable value from a transition-property, an old style, and a
@@ -370,18 +378,18 @@ impl AnimatedProperty {
                                     new_style: &ComputedValues)
                                     -> AnimatedProperty {
         match *transition_property {
             TransitionProperty::All => panic!("Can't use TransitionProperty::All here."),
             % for prop in data.longhands:
                 % if prop.animatable:
                     TransitionProperty::${prop.camel_case} => {
                         AnimatedProperty::${prop.camel_case}(
-                            old_style.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}(),
-                            new_style.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
+                            old_style.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}().into(),
+                            new_style.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}().into())
                     }
                 % endif
             % endfor
             ref other => panic!("Can't use TransitionProperty::{:?} here", other),
         }
     }
 }
 
@@ -435,17 +443,17 @@ impl AnimationValue {
                         PropertyDeclaration::${prop.camel_case}(
                             % if prop.boxed:
                             Box::new(
                             % endif
                                 longhands::${prop.ident}::SpecifiedValue::from_computed_value(
                                 % if prop.is_animatable_with_computed_value:
                                     from
                                 % else:
-                                    &from.into()
+                                    &from.clone().into()
                                 % endif
                                 ))
                             % if prop.boxed:
                             )
                             % endif
                     }
                 % endif
             % endfor
@@ -467,17 +475,17 @@ impl AnimationValue {
                 if let Some(sf) = val.get_system() {
                     longhands::system_font::resolve_system_font(sf, context);
                 }
             % endif
                 Some(AnimationValue::${prop.camel_case}(
                 % if prop.is_animatable_with_computed_value:
                     val.to_computed_value(context)
                 % else:
-                    From::from(&val.to_computed_value(context))
+                    From::from(val.to_computed_value(context))
                 % endif
                 ))
             },
             % endif
             % endfor
             PropertyDeclaration::CSSWideKeyword(id, keyword) => {
                 match id {
                     // We put all the animatable properties first in the hopes
@@ -498,17 +506,17 @@ impl AnimationValue {
                             % endif
                             CSSWideKeyword::Inherit => {
                                 let inherit_struct = context.inherited_style
                                                             .get_${prop.style_struct.name_lower}();
                                 inherit_struct.clone_${prop.ident}()
                             },
                         };
                         % if not prop.is_animatable_with_computed_value:
-                            let computed = From::from(&computed);
+                            let computed = From::from(computed);
                         % endif
                         Some(AnimationValue::${prop.camel_case}(computed))
                     },
                     % endif
                     % endfor
                     % for prop in data.longhands:
                     % if not prop.animatable:
                     LonghandId::${prop.camel_case} => None,
@@ -565,18 +573,18 @@ impl AnimationValue {
             TransitionProperty::All => panic!("Can't use TransitionProperty::All here."),
             % for prop in data.longhands:
                 % if prop.animatable:
                     TransitionProperty::${prop.camel_case} => {
                         AnimationValue::${prop.camel_case}(
                         % if prop.is_animatable_with_computed_value:
                             computed_values.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
                         % else:
-                            From::from(&computed_values.get_${prop.style_struct.ident.strip("_")}()
-                                                                  .clone_${prop.ident}()))
+                            From::from(computed_values.get_${prop.style_struct.ident.strip("_")}()
+                                                                 .clone_${prop.ident}()))
                         % endif
                     }
                 % endif
             % endfor
             ref other => panic!("Can't use TransitionProperty::{:?} here.", other),
         }
     }
 }
@@ -931,106 +939,16 @@ impl Animatable for BackgroundSizeList {
     }
 
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
         self.0.compute_squared_distance(&other.0)
     }
 }
 
-/// https://drafts.csswg.org/css-transitions/#animtype-color
-impl Animatable for RGBA {
-    #[inline]
-    fn add_weighted(&self, other: &RGBA, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        fn clamp(val: f32) -> f32 {
-            val.max(0.).min(1.)
-        }
-
-        let alpha = clamp(try!(self.alpha_f32().add_weighted(&other.alpha_f32(),
-                                                             self_portion, other_portion)));
-        if alpha == 0. {
-            Ok(RGBA::transparent())
-        } else {
-            // NB: We rely on RGBA::from_floats clamping already.
-            let red = try!((self.red_f32() * self.alpha_f32())
-                            .add_weighted(&(other.red_f32() * other.alpha_f32()),
-                                          self_portion, other_portion))
-                            * 1. / alpha;
-            let green = try!((self.green_f32() * self.alpha_f32())
-                             .add_weighted(&(other.green_f32() * other.alpha_f32()),
-                                           self_portion, other_portion))
-                             * 1. / alpha;
-            let blue = try!((self.blue_f32() * self.alpha_f32())
-                             .add_weighted(&(other.blue_f32() * other.alpha_f32()),
-                                           self_portion, other_portion))
-                             * 1. / alpha;
-            Ok(RGBA::from_floats(red, green, blue, alpha))
-        }
-    }
-
-    /// https://www.w3.org/TR/smil-animation/#animateColorElement says we should use Euclidean
-    /// RGB-cube distance.
-    #[inline]
-    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
-        self.compute_squared_distance(other).map(|sd| sd.sqrt())
-    }
-
-    #[inline]
-    fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
-        fn clamp(val: f32) -> f32 {
-            val.max(0.).min(1.)
-        }
-
-        let start_a = clamp(self.alpha_f32());
-        let end_a = clamp(other.alpha_f32());
-        let start = [ start_a,
-                      self.red_f32() * start_a,
-                      self.green_f32() * start_a,
-                      self.blue_f32() * start_a ];
-        let end = [ end_a,
-                    other.red_f32() * end_a,
-                    other.green_f32() * end_a,
-                    other.blue_f32() * end_a ];
-        let diff = start.iter().zip(&end)
-                               .fold(0.0f64, |n, (&a, &b)| {
-                                   let diff = (a - b) as f64;
-                                   n + diff * diff
-                               });
-        Ok(diff)
-    }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-color
-impl Animatable for CSSParserColor {
-    #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        match (*self, *other) {
-            (CSSParserColor::RGBA(ref this), CSSParserColor::RGBA(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion).map(CSSParserColor::RGBA)
-            }
-            _ => Err(()),
-        }
-    }
-
-    #[inline]
-    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
-        self.compute_squared_distance(other).map(|sq| sq.sqrt())
-    }
-
-    #[inline]
-    fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
-        match (*self, *other) {
-            (CSSParserColor::RGBA(ref this), CSSParserColor::RGBA(ref other)) => {
-                this.compute_squared_distance(other)
-            },
-            _ => Ok(0.0),
-        }
-    }
-}
-
 /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
 impl Animatable for CalcLengthOrPercentage {
     #[inline]
     fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
         fn add_weighted_half<T>(this: Option<T>,
                                 other: Option<T>,
                                 self_portion: f64,
                                 other_portion: f64)
@@ -1564,19 +1482,16 @@ impl Animatable for ClipRect {
             result.extend(self.0.iter().cloned());
             result.extend(other.0.iter().cloned());
 
             Ok(${item}List(result))
         }
     }
 </%def>
 
-${impl_animatable_for_shadow('BoxShadow', 'CSSParserColor::RGBA(RGBA::transparent())',)}
-${impl_animatable_for_shadow('TextShadow', 'CSSParserColor::RGBA(RGBA::transparent())',)}
-
 /// Check if it's possible to do a direct numerical interpolation
 /// between these two transform lists.
 /// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
 fn can_interpolate_list(from_list: &[TransformOperation],
                         to_list: &[TransformOperation]) -> bool {
     // Lists must be equal length
     if from_list.len() != to_list.len() {
         return false;
@@ -2621,28 +2536,28 @@ impl<T, U> Animatable for Either<T, U>
             (&Either::Second(ref this), &Either::Second(ref other)) => {
                 this.compute_squared_distance(other)
             },
             _ => Err(())
         }
     }
 }
 
-impl <'a> From<<&'a IntermediateRGBA> for RGBA {
-    fn from(extended_rgba: &IntermediateRGBA) -> RGBA {
+impl From<IntermediateRGBA> for RGBA {
+    fn from(extended_rgba: IntermediateRGBA) -> RGBA {
         // RGBA::from_floats clamps each component values.
         RGBA::from_floats(extended_rgba.red,
                           extended_rgba.green,
                           extended_rgba.blue,
                           extended_rgba.alpha)
     }
 }
 
-impl <'a> From<<&'a RGBA> for IntermediateRGBA {
-    fn from(rgba: &RGBA) -> IntermediateRGBA {
+impl From<RGBA> for IntermediateRGBA {
+    fn from(rgba: RGBA) -> IntermediateRGBA {
         IntermediateRGBA::new(rgba.red_f32(),
                               rgba.green_f32(),
                               rgba.blue_f32(),
                               rgba.alpha_f32())
     }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -2720,41 +2635,41 @@ impl Animatable for IntermediateRGBA {
                                .fold(0.0f64, |n, (&a, &b)| {
                                    let diff = (a - b) as f64;
                                    n + diff * diff
                                });
         Ok(diff)
     }
 }
 
-impl<'a> From<<&'a Either<CSSParserColor, Auto>> for Either<IntermediateColor, Auto> {
-    fn from(from: &Either<CSSParserColor, Auto>) -> Either<IntermediateColor, Auto> {
-        match *from {
-            Either::First(ref from) =>
-                match *from {
-                    CSSParserColor::RGBA(ref color) =>
+impl From<Either<CSSParserColor, Auto>> for Either<IntermediateColor, Auto> {
+    fn from(from: Either<CSSParserColor, Auto>) -> Either<IntermediateColor, Auto> {
+        match from {
+            Either::First(from) =>
+                match from {
+                    CSSParserColor::RGBA(color) =>
                         Either::First(IntermediateColor::IntermediateRGBA(
                             IntermediateRGBA::new(color.red_f32(),
                                                   color.green_f32(),
                                                   color.blue_f32(),
                                                   color.alpha_f32()))),
                     CSSParserColor::CurrentColor =>
                         Either::First(IntermediateColor::CurrentColor),
                 },
             Either::Second(Auto) => Either::Second(Auto),
         }
     }
 }
 
-impl<'a> From<<&'a Either<IntermediateColor, Auto>> for Either<CSSParserColor, Auto> {
-    fn from(from: &Either<IntermediateColor, Auto>) -> Either<CSSParserColor, Auto> {
-        match *from {
-            Either::First(ref from) =>
-                match *from {
-                    IntermediateColor::IntermediateRGBA(ref color) =>
+impl From<Either<IntermediateColor, Auto>> for Either<CSSParserColor, Auto> {
+    fn from(from: Either<IntermediateColor, Auto>) -> Either<CSSParserColor, Auto> {
+        match from {
+            Either::First(from) =>
+                match from {
+                    IntermediateColor::IntermediateRGBA(color) =>
                         Either::First(CSSParserColor::RGBA(RGBA::from_floats(color.red,
                                                                              color.green,
                                                                              color.blue,
                                                                              color.alpha))),
                     IntermediateColor::CurrentColor =>
                         Either::First(CSSParserColor::CurrentColor),
                 },
             Either::Second(Auto) => Either::Second(Auto),
@@ -2795,33 +2710,33 @@ impl Animatable for IntermediateColor {
             (IntermediateColor::IntermediateRGBA(ref this), IntermediateColor::IntermediateRGBA(ref other)) => {
                 this.compute_squared_distance(other)
             },
             _ => Ok(0.0),
         }
     }
 }
 
-impl <'a> From<<&'a CSSParserColor> for IntermediateColor {
-    fn from(color: &CSSParserColor) -> IntermediateColor {
-        match *color {
-            CSSParserColor::RGBA(ref color) =>
+impl From<CSSParserColor> for IntermediateColor {
+    fn from(color: CSSParserColor) -> IntermediateColor {
+        match color {
+            CSSParserColor::RGBA(color) =>
                 IntermediateColor::IntermediateRGBA(IntermediateRGBA::new(color.red_f32(),
                                                                           color.green_f32(),
                                                                           color.blue_f32(),
                                                                           color.alpha_f32())),
             CSSParserColor::CurrentColor => IntermediateColor::CurrentColor,
         }
     }
 }
 
-impl <'a> From<<&'a IntermediateColor> for CSSParserColor {
-    fn from(color: &IntermediateColor) -> CSSParserColor {
-        match *color {
-            IntermediateColor::IntermediateRGBA(ref color) =>
+impl From<IntermediateColor> for CSSParserColor {
+    fn from(color: IntermediateColor) -> CSSParserColor {
+        match color {
+            IntermediateColor::IntermediateRGBA(color) =>
                 CSSParserColor::RGBA(RGBA::from_floats(color.red,
                                                        color.green,
                                                        color.blue,
                                                        color.alpha)),
             IntermediateColor::CurrentColor => CSSParserColor::CurrentColor,
         }
     }
 }
@@ -2845,50 +2760,50 @@ impl <'a> From<<&'a IntermediateColor> f
     }
 
     #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     #[allow(missing_docs)]
     /// Intermediate type for box-shadow list and text-shadow list.
     pub struct Intermediate${type}ShadowList(pub SmallVec<[Intermediate${type}Shadow; 1]>);
 
-    impl <'a> From<<&'a Intermediate${type}ShadowList> for ${type}ShadowList {
-        fn from(shadow_list: &Intermediate${type}ShadowList) -> ${type}ShadowList {
-            ${type}ShadowList(shadow_list.0.iter().map(|s| s.into()).collect())
+    impl From<Intermediate${type}ShadowList> for ${type}ShadowList {
+        fn from(shadow_list: Intermediate${type}ShadowList) -> ${type}ShadowList {
+            ${type}ShadowList(shadow_list.0.into_iter().map(|s| s.into()).collect())
         }
     }
 
-    impl <'a> From<<&'a ${type}ShadowList> for Intermediate${type}ShadowList {
-        fn from(shadow_list: &${type}ShadowList) -> Intermediate${type}ShadowList {
-            Intermediate${type}ShadowList(shadow_list.0.iter().map(|s| s.into()).collect())
+    impl From<${type}ShadowList> for Intermediate${type}ShadowList {
+        fn from(shadow_list: ${type}ShadowList) -> Intermediate${type}ShadowList {
+            Intermediate${type}ShadowList(shadow_list.0.into_iter().map(|s| s.into()).collect())
         }
     }
 
-    impl <'a> From<<&'a Intermediate${type}Shadow> for ${type}Shadow {
-        fn from(shadow: &Intermediate${type}Shadow) -> ${type}Shadow {
+    impl From<Intermediate${type}Shadow> for ${type}Shadow {
+        fn from(shadow: Intermediate${type}Shadow) -> ${type}Shadow {
             ${type}Shadow {
                 offset_x: shadow.offset_x,
                 offset_y: shadow.offset_y,
                 blur_radius: shadow.blur_radius,
-                color: (&shadow.color).into(),
+                color: shadow.color.into(),
                 % if type == "Box":
                 spread_radius: shadow.spread_radius,
                 inset: shadow.inset,
                 % endif
             }
         }
     }
 
-    impl <'a> From<<&'a ${type}Shadow> for Intermediate${type}Shadow {
-        fn from(shadow: &${type}Shadow) -> Intermediate${type}Shadow {
+    impl From<${type}Shadow> for Intermediate${type}Shadow {
+        fn from(shadow: ${type}Shadow) -> Intermediate${type}Shadow {
             Intermediate${type}Shadow {
                 offset_x: shadow.offset_x,
                 offset_y: shadow.offset_y,
                 blur_radius: shadow.blur_radius,
-                color: (&shadow.color).into(),
+                color: shadow.color.into(),
                 % if type == "Box":
                 spread_radius: shadow.spread_radius,
                 inset: shadow.inset,
                 % endif
             }
         }
     }
     ${impl_animatable_for_shadow('Intermediate%sShadow' % type,
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -217,40 +217,32 @@
     use std::fmt;
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::RepeatKeyword;
 
-        #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Debug, Clone, PartialEq, ToCss)]
         pub struct T(pub RepeatKeyword, pub RepeatKeyword);
     }
 
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub RepeatKeyword,
                               pub Option<RepeatKeyword>);
 
     define_css_keyword_enum!(RepeatKeyword:
                              "stretch" => Stretch,
                              "repeat" => Repeat,
                              "round" => Round,
                              "space" => Space);
 
-
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.0.to_css(dest));
-            try!(dest.write_str(" "));
-            self.1.to_css(dest)
-        }
-    }
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.0.to_css(dest));
             if let Some(second) = self.1 {
                 try!(dest.write_str(" "));
                 try!(second.to_css(dest));
             }
             Ok(())
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -8,45 +8,37 @@
 
 <% from data import to_rust_ident %>
 
 <%helpers:longhand name="color" need_clone="True"
                    animation_value_type="IntermediateRGBA"
                    ignored_when_colors_disabled="True"
                    spec="https://drafts.csswg.org/css-color/#color">
     use cssparser::RGBA;
-    use std::fmt;
-    use style_traits::ToCss;
     use values::specified::{AllowQuirks, Color, CSSColor};
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             self.0.parsed.to_computed_value(context)
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(Color::RGBA(*computed).into())
         }
     }
 
-    #[derive(Clone, PartialEq, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+    #[derive(Clone, Debug, PartialEq, ToCss)]
     pub struct SpecifiedValue(pub CSSColor);
     no_viewport_percentage!(SpecifiedValue);
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            self.0.to_css(dest)
-        }
-    }
-
     pub mod computed_value {
         use cssparser;
         pub type T = cssparser::RGBA;
     }
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         RGBA::new(0, 0, 0, 255) // black
     }
@@ -86,16 +78,19 @@
                               IMERawInputForeground IMERawInputUnderline IMESelectedRawTextBackground
                               IMESelectedRawTextForeground IMESelectedRawTextUnderline
                               IMEConvertedTextBackground IMEConvertedTextForeground IMEConvertedTextUnderline
                               IMESelectedConvertedTextBackground IMESelectedConvertedTextForeground
                               IMESelectedConvertedTextUnderline SpellCheckerUnderline""".split()
         %>
         use gecko_bindings::bindings::Gecko_GetLookAndFeelSystemColor;
         use gecko_bindings::structs::root::mozilla::LookAndFeel_ColorID;
+        use std::fmt;
+        use style_traits::ToCss;
+
         pub type SystemColor = LookAndFeel_ColorID;
 
         impl ToCss for SystemColor {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                 let s = match *self {
                     % for color in system_colors + extra_colors:
                         LookAndFeel_ColorID::eColorID_${to_rust_ident(color)} => "${color}",
                     % endfor
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -2144,28 +2144,25 @@ https://drafts.csswg.org/css-fonts-4/#lo
                          needs_conversion=True)}
 
 <%helpers:longhand name="-moz-script-min-size" products="gecko" animation_value_type="none"
                    predefined_type="Length" gecko_ffi_name="mScriptMinSize"
                    spec="Internal (not web-exposed)"
                    internal="True" disable_when_testing="True">
     use app_units::Au;
     use gecko_bindings::structs::NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT;
-    use std::fmt;
-    use style_traits::ToCss;
     use values::specified::length::{AU_PER_PT, FontBaseSize, NoCalcLength};
 
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
     pub struct SpecifiedValue(pub NoCalcLength);
 
     pub mod computed_value {
         pub type T = super::Au;
     }
 
-
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         fn to_computed_value(&self, cx: &Context) -> Au {
             // this value is used in the computation of font-size, so
             // we use the parent size
             let base_size = FontBaseSize::InheritedStyle;
             match self.0 {
@@ -2180,22 +2177,16 @@ https://drafts.csswg.org/css-fonts-4/#lo
                 }
             }
         }
         fn from_computed_value(other: &computed_value::T) -> Self {
             SpecifiedValue(ToComputedValue::from_computed_value(other))
         }
     }
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            self.0.to_css(dest)
-        }
-    }
-
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         Au((NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * AU_PER_PT) as i32)
     }
 
     pub fn parse(_context: &ParserContext, _input: &mut Parser) -> Result<SpecifiedValue, ()> {
         debug_assert!(false, "Should be set directly by presentation attributes only.");
         Err(())
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -26,18 +26,18 @@
     use std::fmt;
     use style_traits::ToCss;
     use values::specified::{AllowQuirks, Length};
 
     pub mod computed_value {
         use app_units::Au;
         use properties::animated_properties::Animatable;
 
-        #[derive(Clone, Copy, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Clone, Copy, Debug, PartialEq, ToCss)]
         pub struct T {
             pub horizontal: Au,
             pub vertical: Au,
         }
 
         /// https://drafts.csswg.org/css-transitions/#animtype-simple-list
         impl Animatable for T {
             #[inline]
@@ -87,24 +87,16 @@
             if let Some(vertical) = self.vertical.as_ref() {
                 try!(dest.write_str(" "));
                 vertical.to_css(dest)?;
             }
             Ok(())
         }
     }
 
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.horizontal.to_css(dest));
-            try!(dest.write_str(" "));
-            self.vertical.to_css(dest)
-        }
-    }
-
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let horizontal = self.horizontal.to_computed_value(context);
             computed_value::T {
                 horizontal: horizontal,
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -438,18 +438,18 @@
         use app_units::Au;
         use cssparser::Color;
         use smallvec::SmallVec;
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub SmallVec<[TextShadow; 1]>);
 
-        #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Clone, PartialEq, Debug, ToCss)]
         pub struct TextShadow {
             pub offset_x: Au,
             pub offset_y: Au,
             pub blur_radius: Au,
             pub color: Color,
         }
     }
 
@@ -463,28 +463,16 @@
             for shadow in iter {
                 dest.write_str(", ")?;
                 shadow.to_css(dest)?;
             }
             Ok(())
         }
     }
 
-    impl ToCss for computed_value::TextShadow {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            self.offset_x.to_css(dest)?;
-            dest.write_str(" ")?;
-            self.offset_y.to_css(dest)?;
-            dest.write_str(" ")?;
-            self.blur_radius.to_css(dest)?;
-            dest.write_str(" ")?;
-            self.color.to_css(dest)
-        }
-    }
-
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             let mut iter = self.0.iter();
             match iter.next() {
                 Some(shadow) => shadow.to_css(dest)?,
                 None => return dest.write_str("none"),
             }
             for shadow in iter {
@@ -762,29 +750,28 @@
             _ => return Err(()),
         };
         Ok(SpecifiedValue::Keyword(keyword_value))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="text-emphasis-position" animation_value_type="none" products="gecko"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
-    use std::fmt;
     use values::computed::ComputedValueAsSpecified;
     use style_traits::ToCss;
 
     define_css_keyword_enum!(HorizontalWritingModeValue:
                              "over" => Over,
                              "under" => Under);
     define_css_keyword_enum!(VerticalWritingModeValue:
                              "right" => Right,
                              "left" => Left);
 
-    #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+    #[derive(Debug, Clone, PartialEq, ToCss)]
     pub struct SpecifiedValue(pub HorizontalWritingModeValue, pub VerticalWritingModeValue);
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
@@ -799,25 +786,16 @@
             Ok(SpecifiedValue(horizontal, vertical))
         } else {
             let vertical = try!(VerticalWritingModeValue::parse(input));
             let horizontal = try!(HorizontalWritingModeValue::parse(input));
             Ok(SpecifiedValue(horizontal, vertical))
         }
     }
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            let SpecifiedValue(horizontal, vertical) = *self;
-            try!(horizontal.to_css(dest));
-            try!(dest.write_char(' '));
-            vertical.to_css(dest)
-        }
-    }
-
     % if product == "gecko":
         impl SpecifiedValue {
             pub fn from_gecko_keyword(kw: u32) -> Self {
                 use gecko_bindings::structs;
 
                 let vert = if kw & structs::NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT != 0 {
                     VerticalWritingModeValue::Right
                 } else {
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -109,40 +109,32 @@
             })
         }
     </%helpers:longhand>
 % endif
 
 <%helpers:longhand name="list-style-image" animation_value_type="none"
                    boxed="${product == 'gecko'}"
                    spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image">
-    use std::fmt;
     use values::computed::ComputedValueAsSpecified;
     use values::specified::UrlOrNone;
     pub use self::computed_value::T as SpecifiedValue;
-    use style_traits::ToCss;
 
     pub mod computed_value {
         use values::specified::UrlOrNone;
 
-        #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Debug, Clone, PartialEq, ToCss)]
         pub struct T(pub UrlOrNone);
     }
 
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            self.0.to_css(dest)
-        }
-    }
-
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(Either::Second(None_))
     }
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue(Either::Second(None_))
     }
--- a/servo/components/style/shared_lock.rs
+++ b/servo/components/style/shared_lock.rs
@@ -221,16 +221,27 @@ pub trait ToCssWithGuard {
     #[inline]
     fn to_css_string(&self, guard: &SharedRwLockReadGuard) -> String {
         let mut s = String::new();
         self.to_css(guard, &mut s).unwrap();
         s
     }
 }
 
+/// A trait to do a deep clone of a given CSS type. Gets a lock and a read
+/// guard, in order to be able to read and clone nested structures.
+pub trait DeepCloneWithLock : Sized {
+    /// Deep clones this object.
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard
+    ) -> Self;
+}
+
 /// Guards for a document
 #[derive(Clone)]
 pub struct StylesheetGuards<'a> {
     /// For author-origin stylesheets
     pub author: &'a SharedRwLockReadGuard<'a>,
 
     /// For user-agent-origin and user-origin stylesheets
     pub ua_or_user: &'a SharedRwLockReadGuard<'a>,
--- a/servo/components/style/sharing/mod.rs
+++ b/servo/components/style/sharing/mod.rs
@@ -304,24 +304,24 @@ pub enum StyleSharingResult {
 }
 
 /// An LRU cache of the last few nodes seen, so that we can aggressively try to
 /// reuse their styles.
 ///
 /// Note that this cache is flushed every time we steal work from the queue, so
 /// storing nodes here temporarily is safe.
 pub struct StyleSharingCandidateCache<E: TElement> {
-    cache: LRUCache<StyleSharingCandidate<E>>,
+    cache: LRUCache<[StyleSharingCandidate<E>; STYLE_SHARING_CANDIDATE_CACHE_SIZE + 1]>,
 }
 
 impl<E: TElement> StyleSharingCandidateCache<E> {
     /// Create a new style sharing candidate cache.
     pub fn new() -> Self {
         StyleSharingCandidateCache {
-            cache: LRUCache::new(STYLE_SHARING_CANDIDATE_CACHE_SIZE),
+            cache: LRUCache::new(),
         }
     }
 
     /// Returns the number of entries in the cache.
     pub fn num_entries(&self) -> usize {
         self.cache.num_entries()
     }
 
deleted file mode 100644
--- a/servo/components/style/stylesheets.rs
+++ /dev/null
@@ -1,1943 +0,0 @@
-/* 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/. */
-
-//! Style sheets and their CSS rules.
-
-#![deny(missing_docs)]
-
-use {Atom, Prefix, Namespace};
-use context::QuirksMode;
-use counter_style::{parse_counter_style_name, parse_counter_style_body};
-#[cfg(feature = "servo")]
-use counter_style::CounterStyleRuleData;
-use cssparser::{AtRuleParser, Parser, QualifiedRuleParser};
-use cssparser::{AtRuleType, RuleListParser, parse_one_rule, SourceLocation};
-use cssparser::ToCss as ParserToCss;
-use document_condition::DocumentCondition;
-use error_reporting::{ParseErrorReporter, NullReporter};
-#[cfg(feature = "servo")]
-use font_face::FontFaceRuleData;
-use font_face::parse_font_face_block;
-#[cfg(feature = "gecko")]
-pub use gecko::rules::{CounterStyleRule, FontFaceRule};
-#[cfg(feature = "gecko")]
-use gecko_bindings::structs::URLExtraData;
-#[cfg(feature = "gecko")]
-use gecko_bindings::sugar::refptr::RefPtr;
-use keyframes::{Keyframe, KeyframeSelector, parse_keyframe_list};
-use media_queries::{Device, MediaList, parse_media_query_list};
-use parking_lot::RwLock;
-use parser::{PARSING_MODE_DEFAULT, Parse, ParserContext, log_css_error};
-use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
-use selector_parser::{SelectorImpl, SelectorParser};
-use selectors::parser::SelectorList;
-#[cfg(feature = "servo")]
-use servo_config::prefs::PREFS;
-#[cfg(not(feature = "gecko"))]
-use servo_url::ServoUrl;
-use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
-use smallvec::SmallVec;
-use std::{fmt, mem};
-use std::borrow::Borrow;
-use std::mem::align_of;
-use std::os::raw::c_void;
-use std::slice;
-use std::sync::atomic::{AtomicBool, Ordering};
-use str::starts_with_ignore_ascii_case;
-use style_traits::ToCss;
-use stylearc::Arc;
-use stylist::FnvHashMap;
-use supports::SupportsCondition;
-use values::{CustomIdent, KeyframesName};
-use values::specified::NamespaceId;
-use values::specified::url::SpecifiedUrl;
-use viewport::ViewportRule;
-
-
-/// Extra data that the backend may need to resolve url values.
-#[cfg(not(feature = "gecko"))]
-pub type UrlExtraData = ServoUrl;
-
-/// Extra data that the backend may need to resolve url values.
-#[cfg(feature = "gecko")]
-pub type UrlExtraData = RefPtr<URLExtraData>;
-
-#[cfg(feature = "gecko")]
-impl UrlExtraData {
-    /// Returns a string for the url.
-    ///
-    /// Unimplemented currently.
-    pub fn as_str(&self) -> &str {
-        // TODO
-        "(stylo: not supported)"
-    }
-}
-
-// XXX We probably need to figure out whether we should mark Eq here.
-// It is currently marked so because properties::UnparsedValue wants Eq.
-#[cfg(feature = "gecko")]
-impl Eq for UrlExtraData {}
-
-/// Each style rule has an origin, which determines where it enters the cascade.
-///
-/// http://dev.w3.org/csswg/css-cascade/#cascading-origins
-#[derive(Clone, PartialEq, Eq, Copy, Debug)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub enum Origin {
-    /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-ua
-    UserAgent,
-
-    /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-author
-    Author,
-
-    /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-user
-    User,
-}
-
-/// A set of namespaces applying to a given stylesheet.
-///
-/// The namespace id is used in gecko
-#[derive(Clone, Default, Debug)]
-#[allow(missing_docs)]
-pub struct Namespaces {
-    pub default: Option<(Namespace, NamespaceId)>,
-    pub prefixes: FnvHashMap<Prefix, (Namespace, NamespaceId)>,
-}
-
-/// Like gecko_bindings::structs::MallocSizeOf, but without the Option<> wrapper. Note that
-/// functions of this type should not be called via do_malloc_size_of(), rather than directly.
-pub type MallocSizeOfFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
-
-/// Call malloc_size_of on ptr, first checking that the allocation isn't empty.
-pub unsafe fn do_malloc_size_of<T>(malloc_size_of: MallocSizeOfFn, ptr: *const T) -> usize {
-    if ptr as usize <= align_of::<T>() {
-        0
-    } else {
-        malloc_size_of(ptr as *const c_void)
-    }
-}
-
-/// Trait for measuring the size of heap data structures.
-pub trait MallocSizeOf {
-    /// Measure the size of any heap-allocated structures that hang off this value, but not the
-    /// space taken up by the value itself.
-    fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize;
-}
-
-/// Like MallocSizeOf, but operates with the global SharedRwLockReadGuard locked.
-pub trait MallocSizeOfWithGuard {
-    /// Like MallocSizeOf::malloc_size_of_children, but with a |guard| argument.
-    fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
-                               malloc_size_of: MallocSizeOfFn) -> usize;
-}
-
-impl<A: MallocSizeOf, B: MallocSizeOf> MallocSizeOf for (A, B) {
-    fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
-        self.0.malloc_size_of_children(malloc_size_of) +
-            self.1.malloc_size_of_children(malloc_size_of)
-    }
-}
-
-impl<T: MallocSizeOf> MallocSizeOf for Vec<T> {
-    fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
-        self.iter().fold(
-            unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) },
-            |n, elem| n + elem.malloc_size_of_children(malloc_size_of))
-    }
-}
-
-impl<T: MallocSizeOfWithGuard> MallocSizeOfWithGuard for Vec<T> {
-    fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
-                               malloc_size_of: MallocSizeOfFn) -> usize {
-        self.iter().fold(
-            unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) },
-            |n, elem| n + elem.malloc_size_of_children(guard, malloc_size_of))
-    }
-}
-
-/// A list of CSS rules.
-#[derive(Debug)]
-pub struct CssRules(pub Vec<CssRule>);
-
-impl CssRules {
-    /// Whether this CSS rules is empty.
-    pub fn is_empty(&self) -> bool {
-        self.0.is_empty()
-    }
-
-    /// Creates a deep clone where each CssRule has also been cloned with
-    /// the provided lock.
-    fn deep_clone_with_lock(&self,
-                            lock: &SharedRwLock) -> CssRules {
-        CssRules(
-            self.0.iter().map(|ref x| x.deep_clone_with_lock(lock)).collect()
-        )
-    }
-}
-
-impl MallocSizeOfWithGuard for CssRules {
-    fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
-                               malloc_size_of: MallocSizeOfFn) -> usize {
-        self.0.malloc_size_of_children(guard, malloc_size_of)
-    }
-}
-
-#[allow(missing_docs)]
-pub enum RulesMutateError {
-    Syntax,
-    IndexSize,
-    HierarchyRequest,
-    InvalidState,
-}
-
-impl From<SingleRuleParseError> for RulesMutateError {
-    fn from(other: SingleRuleParseError) -> Self {
-        match other {
-            SingleRuleParseError::Syntax => RulesMutateError::Syntax,
-            SingleRuleParseError::Hierarchy => RulesMutateError::HierarchyRequest,
-        }
-    }
-}
-
-impl CssRules {
-    #[allow(missing_docs)]
-    pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> {
-        Arc::new(shared_lock.wrap(CssRules(rules)))
-    }
-
-    fn only_ns_or_import(&self) -> bool {
-        self.0.iter().all(|r| {
-            match *r {
-                CssRule::Namespace(..) |
-                CssRule::Import(..) => true,
-                _ => false
-            }
-        })
-    }
-
-    /// https://drafts.csswg.org/cssom/#remove-a-css-rule
-    pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> {
-        // Step 1, 2
-        if index >= self.0.len() {
-            return Err(RulesMutateError::IndexSize);
-        }
-
-        {
-            // Step 3
-            let ref rule = self.0[index];
-
-            // Step 4
-            if let CssRule::Namespace(..) = *rule {
-                if !self.only_ns_or_import() {
-                    return Err(RulesMutateError::InvalidState);
-                }
-            }
-        }
-
-        // Step 5, 6
-        self.0.remove(index);
-        Ok(())
-    }
-}
-
-/// A trait to implement helpers for `Arc<Locked<CssRules>>`.
-pub trait CssRulesHelpers {
-    /// https://drafts.csswg.org/cssom/#insert-a-css-rule
-    ///
-    /// Written in this funky way because parsing an @import rule may cause us
-    /// to clone a stylesheet from the same document due to caching in the CSS
-    /// loader.
-    ///
-    /// TODO(emilio): We could also pass the write guard down into the loader
-    /// instead, but that seems overkill.
-    fn insert_rule(&self,
-                   lock: &SharedRwLock,
-                   rule: &str,
-                   parent_stylesheet: &Stylesheet,
-                   index: usize,
-                   nested: bool,
-                   loader: Option<&StylesheetLoader>)
-                   -> Result<CssRule, RulesMutateError>;
-}
-
-impl CssRulesHelpers for Arc<Locked<CssRules>> {
-    fn insert_rule(&self,
-                   lock: &SharedRwLock,
-                   rule: &str,
-                   parent_stylesheet: &Stylesheet,
-                   index: usize,
-                   nested: bool,
-                   loader: Option<&StylesheetLoader>)
-                   -> Result<CssRule, RulesMutateError> {
-        let state = {
-            let read_guard = lock.read();
-            let rules = self.read_with(&read_guard);
-
-            // Step 1, 2
-            if index > rules.0.len() {
-                return Err(RulesMutateError::IndexSize);
-            }
-
-            // Computes the parser state at the given index
-            if nested {
-                None
-            } else if index == 0 {
-                Some(State::Start)
-            } else {
-                rules.0.get(index - 1).map(CssRule::rule_state)
-            }
-        };
-
-        // Step 3, 4
-        // XXXManishearth should we also store the namespace map?
-        let (new_rule, new_state) =
-            try!(CssRule::parse(&rule, parent_stylesheet, state, loader));
-
-        {
-            let mut write_guard = lock.write();
-            let mut rules = self.write_with(&mut write_guard);
-            // Step 5
-            // Computes the maximum allowed parser state at a given index.
-            let rev_state = rules.0.get(index).map_or(State::Body, CssRule::rule_state);
-            if new_state > rev_state {
-                // We inserted a rule too early, e.g. inserting
-                // a regular style rule before @namespace rules
-                return Err(RulesMutateError::HierarchyRequest);
-            }
-
-            // Step 6
-            if let CssRule::Namespace(..) = new_rule {
-                if !rules.only_ns_or_import() {
-                    return Err(RulesMutateError::InvalidState);
-                }
-            }
-
-            rules.0.insert(index, new_rule.clone());
-        }
-
-        Ok(new_rule)
-    }
-}
-
-
-/// The structure servo uses to represent a stylesheet.
-#[derive(Debug)]
-pub struct Stylesheet {
-    /// List of rules in the order they were found (important for
-    /// cascading order)
-    pub rules: Arc<Locked<CssRules>>,
-    /// List of media associated with the Stylesheet.
-    pub media: Arc<Locked<MediaList>>,
-    /// The origin of this stylesheet.
-    pub origin: Origin,
-    /// The url data this stylesheet should use.
-    pub url_data: UrlExtraData,
-    /// The lock used for objects inside this stylesheet
-    pub shared_lock: SharedRwLock,
-    /// The namespaces that apply to this stylesheet.
-    pub namespaces: RwLock<Namespaces>,
-    /// Whether this stylesheet would be dirty when the viewport size changes.
-    pub dirty_on_viewport_size_change: AtomicBool,
-    /// Whether this stylesheet should be disabled.
-    pub disabled: AtomicBool,
-    /// The quirks mode of this stylesheet.
-    pub quirks_mode: QuirksMode,
-}
-
-impl MallocSizeOfWithGuard for Stylesheet {
-    fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
-                               malloc_size_of: MallocSizeOfFn) -> usize {
-        // Measurement of other fields may be added later.
-        self.rules.read_with(guard).malloc_size_of_children(guard, malloc_size_of)
-    }
-}
-
-/// This structure holds the user-agent and user stylesheets.
-pub struct UserAgentStylesheets {
-    /// The lock used for user-agent stylesheets.
-    pub shared_lock: SharedRwLock,
-    /// The user or user agent stylesheets.
-    pub user_or_user_agent_stylesheets: Vec<Stylesheet>,
-    /// The quirks mode stylesheet.
-    pub quirks_mode_stylesheet: Stylesheet,
-}
-
-
-/// A CSS rule.
-///
-/// TODO(emilio): Lots of spec links should be around.
-#[derive(Debug, Clone)]
-#[allow(missing_docs)]
-pub enum CssRule {
-    // No Charset here, CSSCharsetRule has been removed from CSSOM
-    // https://drafts.csswg.org/cssom/#changes-from-5-december-2013
-
-    Namespace(Arc<Locked<NamespaceRule>>),
-    Import(Arc<Locked<ImportRule>>),
-    Style(Arc<Locked<StyleRule>>),
-    Media(Arc<Locked<MediaRule>>),
-    FontFace(Arc<Locked<FontFaceRule>>),
-    CounterStyle(Arc<Locked<CounterStyleRule>>),
-    Viewport(Arc<Locked<ViewportRule>>),
-    Keyframes(Arc<Locked<KeyframesRule>>),
-    Supports(Arc<Locked<SupportsRule>>),
-    Page(Arc<Locked<PageRule>>),
-    Document(Arc<Locked<DocumentRule>>),
-}
-
-impl MallocSizeOfWithGuard for CssRule {
-    fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
-                               malloc_size_of: MallocSizeOfFn) -> usize {
-        match *self {
-            CssRule::Style(ref lock) => {
-                lock.read_with(guard).malloc_size_of_children(guard, malloc_size_of)
-            },
-            // Measurement of these fields may be added later.
-            CssRule::Import(_) => 0,
-            CssRule::Media(_) => 0,
-            CssRule::FontFace(_) => 0,
-            CssRule::CounterStyle(_) => 0,
-            CssRule::Keyframes(_) => 0,
-            CssRule::Namespace(_) => 0,
-            CssRule::Viewport(_) => 0,
-            CssRule::Supports(_) => 0,
-            CssRule::Page(_) => 0,
-            CssRule::Document(_)  => 0,
-        }
-    }
-}
-
-#[allow(missing_docs)]
-#[derive(PartialEq, Eq, Copy, Clone)]
-pub enum CssRuleType {
-    // https://drafts.csswg.org/cssom/#the-cssrule-interface
-    Style               = 1,
-    Charset             = 2,
-    Import              = 3,
-    Media               = 4,
-    FontFace            = 5,
-    Page                = 6,
-    // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl
-    Keyframes           = 7,
-    Keyframe            = 8,
-    // https://drafts.csswg.org/cssom/#the-cssrule-interface
-    Margin              = 9,
-    Namespace           = 10,
-    // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface
-    CounterStyle        = 11,
-    // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface
-    Supports            = 12,
-    // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface
-    Document            = 13,
-    // https://drafts.csswg.org/css-fonts-3/#om-fontfeaturevalues
-    FontFeatureValues   = 14,
-    // https://drafts.csswg.org/css-device-adapt/#css-rule-interface
-    Viewport            = 15,
-}
-
-#[allow(missing_docs)]
-pub enum SingleRuleParseError {
-    Syntax,
-    Hierarchy,
-}
-
-impl CssRule {
-    #[allow(missing_docs)]
-    pub fn rule_type(&self) -> CssRuleType {
-        match *self {
-            CssRule::Style(_) => CssRuleType::Style,
-            CssRule::Import(_) => CssRuleType::Import,
-            CssRule::Media(_) => CssRuleType::Media,
-            CssRule::FontFace(_) => CssRuleType::FontFace,
-            CssRule::CounterStyle(_) => CssRuleType::CounterStyle,
-            CssRule::Keyframes(_) => CssRuleType::Keyframes,
-            CssRule::Namespace(_) => CssRuleType::Namespace,
-            CssRule::Viewport(_) => CssRuleType::Viewport,
-            CssRule::Supports(_) => CssRuleType::Supports,
-            CssRule::Page(_) => CssRuleType::Page,
-            CssRule::Document(_)  => CssRuleType::Document,
-        }
-    }
-
-    fn rule_state(&self) -> State {
-        match *self {
-            // CssRule::Charset(..) => State::Start,
-            CssRule::Import(..) => State::Imports,
-            CssRule::Namespace(..) => State::Namespaces,
-            _ => State::Body,
-        }
-    }
-
-    // input state is None for a nested rule
-    // Returns a parsed CSS rule and the final state of the parser
-    #[allow(missing_docs)]
-    pub fn parse(css: &str,
-                 parent_stylesheet: &Stylesheet,
-                 state: Option<State>,
-                 loader: Option<&StylesheetLoader>)
-                 -> Result<(Self, State), SingleRuleParseError> {
-        let error_reporter = NullReporter;
-        let context = ParserContext::new(
-            parent_stylesheet.origin,
-            &parent_stylesheet.url_data,
-            &error_reporter,
-            None,
-            PARSING_MODE_DEFAULT,
-            parent_stylesheet.quirks_mode
-        );
-
-        let mut input = Parser::new(css);
-
-        let mut guard = parent_stylesheet.namespaces.write();
-
-        // nested rules are in the body state
-        let state = state.unwrap_or(State::Body);
-        let mut rule_parser = TopLevelRuleParser {
-            stylesheet_origin: parent_stylesheet.origin,
-            context: context,
-            shared_lock: &parent_stylesheet.shared_lock,
-            loader: loader,
-            state: state,
-            namespaces: Some(&mut *guard),
-        };
-        match parse_one_rule(&mut input, &mut rule_parser) {
-            Ok(result) => Ok((result, rule_parser.state)),
-            Err(_) => {
-                if let State::Invalid = rule_parser.state {
-                    Err(SingleRuleParseError::Hierarchy)
-                } else {
-                    Err(SingleRuleParseError::Syntax)
-                }
-            }
-        }
-    }
-
-    /// Deep clones this CssRule.
-    fn deep_clone_with_lock(
-        &self,
-        lock: &SharedRwLock
-    ) -> CssRule {
-        let guard = lock.read();
-        match *self {
-            CssRule::Namespace(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::Namespace(Arc::new(lock.wrap(rule.clone())))
-            },
-            CssRule::Import(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::Import(Arc::new(lock.wrap(rule.clone())))
-            },
-            CssRule::Style(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::Style(Arc::new(
-                    lock.wrap(rule.deep_clone_with_lock(lock))))
-            },
-            CssRule::Media(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::Media(Arc::new(
-                    lock.wrap(rule.deep_clone_with_lock(lock))))
-            },
-            CssRule::FontFace(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
-            },
-            CssRule::CounterStyle(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone())))
-            },
-            CssRule::Viewport(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::Viewport(Arc::new(lock.wrap(rule.clone())))
-            },
-            CssRule::Keyframes(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::Keyframes(Arc::new(
-                    lock.wrap(rule.deep_clone_with_lock(lock))))
-            },
-            CssRule::Supports(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::Supports(Arc::new(
-                    lock.wrap(rule.deep_clone_with_lock(lock))))
-            },
-            CssRule::Page(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::Page(Arc::new(
-                    lock.wrap(rule.deep_clone_with_lock(lock))))
-            },
-            CssRule::Document(ref arc) => {
-                let rule = arc.read_with(&guard);
-                CssRule::Document(Arc::new(
-                    lock.wrap(rule.deep_clone_with_lock(lock))))
-            },
-        }
-    }
-}
-
-impl ToCssWithGuard for CssRule {
-    // https://drafts.csswg.org/cssom/#serialize-a-css-rule
-    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
-    where W: fmt::Write {
-        match *self {
-            CssRule::Namespace(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::Viewport(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::Media(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::Supports(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),
-            CssRule::Document(ref lock) => lock.read_with(guard).to_css(guard, dest),
-        }
-    }
-}
-
-/// Calculates the location of a rule's source given an offset.
-fn get_location_with_offset(location: SourceLocation, offset: u64)
-    -> SourceLocation {
-    SourceLocation {
-        line: location.line + offset as usize - 1,
-        column: location.column,
-    }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-#[allow(missing_docs)]
-pub struct NamespaceRule {
-    /// `None` for the default Namespace
-    pub prefix: Option<Prefix>,
-    pub url: Namespace,
-    pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for NamespaceRule {
-    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule
-    fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
-    where W: fmt::Write {
-        try!(dest.write_str("@namespace "));
-        if let Some(ref prefix) = self.prefix {
-            try!(dest.write_str(&*prefix.to_string()));
-            try!(dest.write_str(" "));
-        }
-        try!(dest.write_str("url(\""));
-        try!(dest.write_str(&*self.url.to_string()));
-        dest.write_str("\");")
-    }
-}
-
-
-/// The [`@import`][import] at-rule.
-///
-/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import
-#[derive(Debug)]
-pub struct ImportRule {
-    /// The `<url>` this `@import` rule is loading.
-    pub url: SpecifiedUrl,
-
-    /// The stylesheet is always present.
-    ///
-    /// It contains an empty list of rules and namespace set that is updated
-    /// when it loads.
-    pub stylesheet: Arc<Stylesheet>,
-
-    /// The line and column of the rule's source code.
-    pub source_location: SourceLocation,
-}
-
-impl Clone for ImportRule {
-    fn clone(&self) -> ImportRule {
-        let stylesheet: &Stylesheet = self.stylesheet.borrow();
-        ImportRule {
-            url: self.url.clone(),
-            stylesheet: Arc::new(stylesheet.clone()),
-            source_location: self.source_location.clone(),
-        }
-    }
-}
-
-impl ToCssWithGuard for ImportRule {
-    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
-    where W: fmt::Write {
-        try!(dest.write_str("@import "));
-        try!(self.url.to_css(dest));
-        let media = self.stylesheet.media.read_with(guard);
-        if !media.is_empty() {
-            try!(dest.write_str(" "));
-            try!(media.to_css(dest));
-        }
-        dest.write_str(";")
-    }
-}
-
-/// A [`@keyframes`][keyframes] rule.
-///
-/// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes
-#[derive(Debug)]
-pub struct KeyframesRule {
-    /// The name of the current animation.
-    pub name: KeyframesName,
-    /// The keyframes specified for this CSS rule.
-    pub keyframes: Vec<Arc<Locked<Keyframe>>>,
-    /// Vendor prefix type the @keyframes has.
-    pub vendor_prefix: Option<VendorPrefix>,
-    /// The line and column of the rule's source code.
-    pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for KeyframesRule {
-    // Serialization of KeyframesRule is not specced.
-    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
-    where W: fmt::Write {
-        try!(dest.write_str("@keyframes "));
-        try!(self.name.to_css(dest));
-        try!(dest.write_str(" {"));
-        let iter = self.keyframes.iter();
-        for lock in iter {
-            try!(dest.write_str("\n"));
-            let keyframe = lock.read_with(&guard);
-            try!(keyframe.to_css(guard, dest));
-        }
-        dest.write_str("\n}")
-    }
-}
-
-impl KeyframesRule {
-    /// Returns the index of the last keyframe that matches the given selector.
-    /// If the selector is not valid, or no keyframe is found, returns None.
-    ///
-    /// Related spec:
-    /// https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule
-    pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
-        if let Ok(selector) = Parser::new(selector).parse_entirely(KeyframeSelector::parse) {
-            for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
-                if keyframe.read_with(guard).selector == selector {
-                    return Some(i);
-                }
-            }
-        }
-        None
-    }
-}
-
-impl KeyframesRule {
-    /// Deep clones this KeyframesRule.
-    fn deep_clone_with_lock(&self,
-                            lock: &SharedRwLock) -> KeyframesRule {
-        let guard = lock.read();
-        KeyframesRule {
-            name: self.name.clone(),
-            keyframes: self.keyframes.iter()
-                .map(|ref x| Arc::new(lock.wrap(
-                    x.read_with(&guard).deep_clone_with_lock(lock))))
-                .collect(),
-            vendor_prefix: self.vendor_prefix.clone(),
-            source_location: self.source_location.clone(),
-        }
-    }
-}
-
-#[allow(missing_docs)]
-#[derive(Debug)]
-pub struct MediaRule {
-    pub media_queries: Arc<Locked<MediaList>>,
-    pub rules: Arc<Locked<CssRules>>,
-    pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for MediaRule {
-    // Serialization of MediaRule is not specced.
-    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule
-    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
-    where W: fmt::Write {
-        try!(dest.write_str("@media "));
-        try!(self.media_queries.read_with(guard).to_css(dest));
-        try!(dest.write_str(" {"));
-        for rule in self.rules.read_with(guard).0.iter() {
-            try!(dest.write_str(" "));
-            try!(rule.to_css(guard, dest));
-        }
-        dest.write_str(" }")
-    }
-}
-
-impl MediaRule {
-    /// Deep clones this MediaRule.
-    fn deep_clone_with_lock(&self,
-                            lock: &SharedRwLock) -> MediaRule {
-        let guard = lock.read();
-        let media_queries = self.media_queries.read_with(&guard);
-        let rules = self.rules.read_with(&guard);
-        MediaRule {
-            media_queries: Arc::new(lock.wrap(media_queries.clone())),
-            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
-            source_location: self.source_location.clone(),
-        }
-    }
-}
-
-
-#[derive(Debug)]
-/// An @supports rule
-pub struct SupportsRule {
-    /// The parsed condition
-    pub condition: SupportsCondition,
-    /// Child rules
-    pub rules: Arc<Locked<CssRules>>,
-    /// The result of evaluating the condition
-    pub enabled: bool,
-    /// The line and column of the rule's source code.
-    pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for SupportsRule {
-    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
-    where W: fmt::Write {
-        try!(dest.write_str("@supports "));
-        try!(self.condition.to_css(dest));
-        try!(dest.write_str(" {"));
-        for rule in self.rules.read_with(guard).0.iter() {
-            try!(dest.write_str(" "));
-            try!(rule.to_css(guard, dest));
-        }
-        dest.write_str(" }")
-    }
-}
-
-impl SupportsRule {
-    fn deep_clone_with_lock(&self,
-                            lock: &SharedRwLock) -> SupportsRule {
-        let guard = lock.read();
-        let rules = self.rules.read_with(&guard);
-        SupportsRule {
-            condition: self.condition.clone(),
-            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
-            enabled: self.enabled,
-            source_location: self.source_location.clone(),
-        }
-    }
-}
-
-/// A [`@page`][page] rule.  This implements only a limited subset of the CSS 2.2 syntax.  In this
-/// subset, [page selectors][page-selectors] are not implemented.
-///
-/// [page]: https://drafts.csswg.org/css2/page.html#page-box
-/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
-#[allow(missing_docs)]
-#[derive(Debug)]
-pub struct PageRule {
-    pub block: Arc<Locked<PropertyDeclarationBlock>>,
-    pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for PageRule {
-    // Serialization of PageRule is not specced, adapted from steps for StyleRule.
-    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
-    where W: fmt::Write {
-        dest.write_str("@page { ")?;
-        let declaration_block = self.block.read_with(guard);
-        declaration_block.to_css(dest)?;
-        if declaration_block.declarations().len() > 0 {
-            write!(dest, " ")?;
-        }
-        dest.write_str("}")
-    }
-}
-
-impl PageRule {
-    fn deep_clone_with_lock(&self,
-                            lock: &SharedRwLock) -> PageRule {
-        let guard = lock.read();
-        PageRule {
-            block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
-            source_location: self.source_location.clone(),
-        }
-    }
-}
-
-#[allow(missing_docs)]
-#[derive(Debug)]
-pub struct StyleRule {
-    pub selectors: SelectorList<SelectorImpl>,
-    pub block: Arc<Locked<PropertyDeclarationBlock>>,
-    pub source_location: SourceLocation,
-}
-
-impl MallocSizeOfWithGuard for StyleRule {
-    fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
-                               malloc_size_of: MallocSizeOfFn) -> usize {
-        // Measurement of other fields may be added later.
-        self.block.read_with(guard).malloc_size_of_children(malloc_size_of)
-    }
-}
-
-impl ToCssWithGuard for StyleRule {
-    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule
-    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
-    where W: fmt::Write {
-        // Step 1
-        try!(self.selectors.to_css(dest));
-        // Step 2
-        try!(dest.write_str(" { "));
-        // Step 3
-        let declaration_block = self.block.read_with(guard);
-        try!(declaration_block.to_css(dest));
-        // Step 4
-        if declaration_block.declarations().len() > 0 {
-            try!(write!(dest, " "));
-        }
-        // Step 5
-        try!(dest.write_str("}"));
-        Ok(())
-    }
-}
-
-impl StyleRule {
-    /// Deep clones this StyleRule.
-    fn deep_clone_with_lock(&self,
-                            lock: &SharedRwLock) -> StyleRule {
-        let guard = lock.read();
-        StyleRule {
-            selectors: self.selectors.clone(),
-            block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
-            source_location: self.source_location.clone(),
-        }
-    }
-}
-
-/// A @font-face rule
-#[cfg(feature = "servo")]
-pub type FontFaceRule = FontFaceRuleData;
-
-/// A @counter-style rule
-#[cfg(feature = "servo")]
-pub type CounterStyleRule = CounterStyleRuleData;
-
-#[derive(Debug)]
-/// A @-moz-document rule
-pub struct DocumentRule {
-    /// The parsed condition
-    pub condition: DocumentCondition,
-    /// Child rules
-    pub rules: Arc<Locked<CssRules>>,
-    /// The line and column of the rule's source code.
-    pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for DocumentRule {
-    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
-    where W: fmt::Write {
-        try!(dest.write_str("@-moz-document "));
-        try!(self.condition.to_css(dest));
-        try!(dest.write_str(" {"));
-        for rule in self.rules.read_with(guard).0.iter() {
-            try!(dest.write_str(" "));
-            try!(rule.to_css(guard, dest));
-        }
-        dest.write_str(" }")
-    }
-}
-
-impl DocumentRule {
-    /// Deep clones this DocumentRule.
-    fn deep_clone_with_lock(&self,
-                            lock: &SharedRwLock) -> DocumentRule {
-        let guard = lock.read();
-        let rules = self.rules.read_with(&guard);
-        DocumentRule {
-            condition: self.condition.clone(),
-            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
-            source_location: self.source_location.clone(),
-        }
-    }
-}
-
-/// A trait that describes statically which rules are iterated for a given
-/// RulesIterator.
-pub trait NestedRuleIterationCondition {
-    /// Whether we should process the nested rules in a given `@import` rule.
-    fn process_import(
-        guard: &SharedRwLockReadGuard,
-        device: &Device,
-        quirks_mode: QuirksMode,
-        rule: &ImportRule)
-        -> bool;
-
-    /// Whether we should process the nested rules in a given `@media` rule.
-    fn process_media(
-        guard: &SharedRwLockReadGuard,
-        device: &Device,
-        quirks_mode: QuirksMode,
-        rule: &MediaRule)
-        -> bool;
-
-    /// Whether we should process the nested rules in a given `@-moz-document` rule.
-    fn process_document(
-        guard: &SharedRwLockReadGuard,
-        device: &Device,
-        quirks_mode: QuirksMode,
-        rule: &DocumentRule)
-        -> bool;
-
-    /// Whether we should process the nested rules in a given `@supports` rule.
-    fn process_supports(
-        guard: &SharedRwLockReadGuard,
-        device: &Device,
-        quirks_mode: QuirksMode,
-        rule: &SupportsRule)
-        -> bool;
-}
-
-/// A struct that represents the condition that a rule applies to the document.
-pub struct EffectiveRules;
-
-impl NestedRuleIterationCondition for EffectiveRules {
-    fn process_import(
-        guard: &SharedRwLockReadGuard,
-        device: &Device,
-        quirks_mode: QuirksMode,
-        rule: &ImportRule)
-        -> bool
-    {
-        rule.stylesheet.media.read_with(guard).evaluate(device, quirks_mode)
-    }
-
-    fn process_media(
-        guard: &SharedRwLockReadGuard,
-        device: &Device,
-        quirks_mode: QuirksMode,
-        rule: &MediaRule)
-        -> bool
-    {
-        rule.media_queries.read_with(guard).evaluate(device, quirks_mode)
-    }
-
-    fn process_document(
-        _: &SharedRwLockReadGuard,
-        device: &Device,
-        _: QuirksMode,
-        rule: &DocumentRule)
-        -> bool
-    {
-        rule.condition.evaluate(device)
-    }
-
-    fn process_supports(
-        _: &SharedRwLockReadGuard,
-        _: &Device,
-        _: QuirksMode,
-        rule: &SupportsRule)
-        -> bool
-    {
-        rule.enabled
-    }
-}
-
-/// A filter that processes all the rules in a rule list.
-pub struct AllRules;
-
-impl NestedRuleIterationCondition for AllRules {
-    fn process_import(
-        _: &SharedRwLockReadGuard,
-        _: &Device,
-        _: QuirksMode,
-        _: &ImportRule)
-        -> bool
-    {
-        true
-    }
-
-    fn process_media(
-        _: &SharedRwLockReadGuard,
-        _: &Device,
-        _: QuirksMode,
-        _: &MediaRule)
-        -> bool
-    {
-        true
-    }
-
-    fn process_document(
-        _: &SharedRwLockReadGuard,
-        _: &Device,
-        _: QuirksMode,
-        _: &DocumentRule)
-        -> bool
-    {
-        true
-    }
-
-    fn process_supports(
-        _: &SharedRwLockReadGuard,
-        _: &Device,
-        _: QuirksMode,
-        _: &SupportsRule)
-        -> bool
-    {
-        true
-    }
-}
-
-/// An iterator over all the effective rules of a stylesheet.
-///
-/// NOTE: This iterator recurses into `@import` rules.
-pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>;
-
-/// An iterator over a list of rules.
-pub struct RulesIterator<'a, 'b, C>
-    where 'b: 'a,
-          C: NestedRuleIterationCondition + 'static,
-{
-    device: &'a Device,
-    quirks_mode: QuirksMode,
-    guard: &'a SharedRwLockReadGuard<'b>,
-    stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,
-    _phantom: ::std::marker::PhantomData<C>,
-}
-
-impl<'a, 'b, C> RulesIterator<'a, 'b, C>
-    where 'b: 'a,
-          C: NestedRuleIterationCondition + 'static,
-{
-    /// Creates a new `RulesIterator` to iterate over `rules`.
-    pub fn new(
-        device: &'a Device,
-        quirks_mode: QuirksMode,
-        guard: &'a SharedRwLockReadGuard<'b>,
-        rules: &'a CssRules)
-        -> Self
-    {
-        let mut stack = SmallVec::new();
-        stack.push(rules.0.iter());
-        Self {
-            device: device,
-            quirks_mode: quirks_mode,
-            guard: guard,
-            stack: stack,
-            _phantom: ::std::marker::PhantomData,
-        }
-    }
-
-    /// Skips all the remaining children of the last nested rule processed.
-    pub fn skip_children(&mut self) {
-        self.stack.pop();
-    }
-}
-
-impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
-    where 'b: 'a,
-          C: NestedRuleIterationCondition + 'static,
-{
-    type Item = &'a CssRule;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let mut nested_iter_finished = false;
-        while !self.stack.is_empty() {
-            if nested_iter_finished {
-                self.stack.pop();
-                nested_iter_finished = false;
-                continue;
-            }
-
-            let rule;
-            let sub_iter;
-            {
-                let mut nested_iter = self.stack.last_mut().unwrap();
-                rule = match nested_iter.next() {
-                    Some(r) => r,
-                    None => {
-                        nested_iter_finished = true;
-                        continue
-                    }
-                };
-
-                sub_iter = match *rule {
-                    CssRule::Import(ref import_rule) => {
-                        let import_rule = import_rule.read_with(self.guard);
-                        if !C::process_import(self.guard, self.device, self.quirks_mode, import_rule) {
-                            continue;
-                        }
-                        Some(import_rule.stylesheet.rules.read_with(self.guard).0.iter())
-                    }
-                    CssRule::Document(ref doc_rule) => {
-                        let doc_rule = doc_rule.read_with(self.guard);
-                        if !C::process_document(self.guard, self.device, self.quirks_mode, doc_rule) {
-                            continue;
-                        }
-                        Some(doc_rule.rules.read_with(self.guard).0.iter())
-                    }
-                    CssRule::Media(ref lock) => {
-                        let media_rule = lock.read_with(self.guard);
-                        if !C::process_media(self.guard, self.device, self.quirks_mode, media_rule) {
-                            continue;
-                        }
-                        Some(media_rule.rules.read_with(self.guard).0.iter())
-                    }
-                    CssRule::Supports(ref lock) => {
-                        let supports_rule = lock.read_with(self.guard);
-                        if !C::process_supports(self.guard, self.device, self.quirks_mode, supports_rule) {
-                            continue;
-                        }
-                        Some(supports_rule.rules.read_with(self.guard).0.iter())
-                    }
-                    CssRule::Namespace(_) |
-                    CssRule::Style(_) |
-                    CssRule::FontFace(_) |
-                    CssRule::CounterStyle(_) |
-                    CssRule::Viewport(_) |
-                    CssRule::Keyframes(_) |
-                    CssRule::Page(_) => None,
-                };
-            }
-
-            if let Some(sub_iter) = sub_iter {
-                self.stack.push(sub_iter);
-            }
-
-            return Some(rule);
-        }
-
-        None
-    }
-}
-
-impl Stylesheet {
-    /// Updates an empty stylesheet from a given string of text.
-    pub fn update_from_str(existing: &Stylesheet,
-                           css: &str,
-                           url_data: &UrlExtraData,
-                           stylesheet_loader: Option<&StylesheetLoader>,
-                           error_reporter: &ParseErrorReporter,
-                           line_number_offset: u64) {
-        let namespaces = RwLock::new(Namespaces::default());
-        // FIXME: we really should update existing.url_data with the given url_data,
-        // otherwise newly inserted rule may not have the right base url.
-        let (rules, dirty_on_viewport_size_change) =
-            Stylesheet::parse_rules(
-                css,
-                url_data,
-                existing.origin,
-                &mut *namespaces.write(),
-                &existing.shared_lock,
-                stylesheet_loader,
-                error_reporter,
-                existing.quirks_mode,
-                line_number_offset
-            );
-
-        mem::swap(&mut *existing.namespaces.write(), &mut *namespaces.write());
-        existing.dirty_on_viewport_size_change
-            .store(dirty_on_viewport_size_change, Ordering::Release);
-
-        // Acquire the lock *after* parsing, to minimize the exclusive section.
-        let mut guard = existing.shared_lock.write();
-        *existing.rules.write_with(&mut guard) = CssRules(rules);
-    }
-
-    fn parse_rules(
-        css: &str,
-        url_data: &UrlExtraData,
-        origin: Origin,
-        namespaces: &mut Namespaces,
-        shared_lock: &SharedRwLock,
-        stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: &ParseErrorReporter,
-        quirks_mode: QuirksMode,
-        line_number_offset: u64
-    ) -> (Vec<CssRule>, bool) {
-        let mut rules = Vec::new();
-        let mut input = Parser::new(css);
-
-        let context =
-            ParserContext::new_with_line_number_offset(
-                origin,
-                url_data,
-                error_reporter,
-                line_number_offset,
-                PARSING_MODE_DEFAULT,
-                quirks_mode
-            );
-
-        let rule_parser = TopLevelRuleParser {
-            stylesheet_origin: origin,
-            shared_lock: shared_lock,
-            loader: stylesheet_loader,
-            context: context,
-            state: State::Start,
-            namespaces: Some(namespaces),
-        };
-
-        input.look_for_viewport_percentages();
-
-        {
-            let mut iter =
-                RuleListParser::new_for_stylesheet(&mut input, rule_parser);
-
-            while let Some(result) = iter.next() {
-                match result {
-                    Ok(rule) => rules.push(rule),
-                    Err(range) => {
-                        let pos = range.start;
-                        let message = format!("Invalid rule: '{}'", iter.input.slice(range));
-                        log_css_error(iter.input, pos, &*message, &iter.parser.context);
-                    }
-                }
-            }
-        }
-
-        (rules, input.seen_viewport_percentages())
-    }
-
-    /// Creates an empty stylesheet and parses it with a given base url, origin
-    /// and media.
-    ///
-    /// Effectively creates a new stylesheet and forwards the hard work to
-    /// `Stylesheet::update_from_str`.
-    pub fn from_str(css: &str,
-                    url_data: UrlExtraData,
-                    origin: Origin,
-                    media: Arc<Locked<MediaList>>,
-                    shared_lock: SharedRwLock,
-                    stylesheet_loader: Option<&StylesheetLoader>,
-                    error_reporter: &ParseErrorReporter,
-                    quirks_mode: QuirksMode,
-                    line_number_offset: u64)
-                    -> Stylesheet {
-        let namespaces = RwLock::new(Namespaces::default());
-        let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
-            css,
-            &url_data,
-            origin,
-            &mut *namespaces.write(),
-            &shared_lock,
-            stylesheet_loader,
-            error_reporter,
-            quirks_mode,
-            line_number_offset,
-        );
-
-        Stylesheet {
-            origin: origin,
-            url_data: url_data,
-            namespaces: namespaces,
-            rules: CssRules::new(rules, &shared_lock),
-            media: media,
-            shared_lock: shared_lock,
-            dirty_on_viewport_size_change: AtomicBool::new(dirty_on_viewport_size_change),
-            disabled: AtomicBool::new(false),
-            quirks_mode: quirks_mode,
-        }
-    }
-
-    /// Whether this stylesheet can be dirty on viewport size change.
-    pub fn dirty_on_viewport_size_change(&self) -> bool {
-        self.dirty_on_viewport_size_change.load(Ordering::SeqCst)
-    }
-
-    /// When CSSOM inserts a rule or declaration into this stylesheet, it needs to call this method
-    /// with the return value of `cssparser::Parser::seen_viewport_percentages`.
-    ///
-    /// FIXME: actually make these calls
-    ///
-    /// Note: when *removing* a rule or declaration that contains a viewport percentage,
-    /// to keep the flag accurate we’d need to iterator through the rest of the stylesheet to
-    /// check for *other* such values.
-    ///
-    /// Instead, we conservatively assume there might be some.
-    /// Restyling will some some more work than necessary, but give correct results.
-    pub fn inserted_has_viewport_percentages(&self, has_viewport_percentages: bool) {
-        self.dirty_on_viewport_size_change.fetch_or(has_viewport_percentages, Ordering::SeqCst);
-    }
-
-    /// Returns whether the style-sheet applies for the current device depending
-    /// on the associated MediaList.
-    ///
-    /// Always true if no associated MediaList exists.
-    pub fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
-        self.media.read_with(guard).evaluate(device, self.quirks_mode)
-    }
-
-    /// Return an iterator over the effective rules within the style-sheet, as
-    /// according to the supplied `Device`.
-    #[inline]
-    pub fn effective_rules<'a, 'b>(
-        &'a self,
-        device: &'a Device,
-        guard: &'a SharedRwLockReadGuard<'b>)
-        -> EffectiveRulesIterator<'a, 'b>
-    {
-        self.iter_rules::<'a, 'b, EffectiveRules>(device, guard)
-    }
-
-    /// Return an iterator using the condition `C`.
-    #[inline]
-    pub fn iter_rules<'a, 'b, C>(
-        &'a self,
-        device: &'a Device,
-        guard: &'a SharedRwLockReadGuard<'b>)
-        -> RulesIterator<'a, 'b, C>
-        where C: NestedRuleIterationCondition,
-    {
-        RulesIterator::new(
-            device,
-            self.quirks_mode,
-            guard,
-            &self.rules.read_with(guard))
-    }
-
-    /// Returns whether the stylesheet has been explicitly disabled through the
-    /// CSSOM.
-    pub fn disabled(&self) -> bool {
-        self.disabled.load(Ordering::SeqCst)
-    }
-
-    /// Records that the stylesheet has been explicitly disabled through the
-    /// CSSOM.
-    ///
-    /// Returns whether the the call resulted in a change in disabled state.
-    ///
-    /// Disabled stylesheets remain in the document, but their rules are not
-    /// added to the Stylist.
-    pub fn set_disabled(&self, disabled: bool) -> bool {
-        self.disabled.swap(disabled, Ordering::SeqCst) != disabled
-    }
-}
-
-impl Clone for Stylesheet {
-    fn clone(&self) -> Stylesheet {
-        // Create a new lock for our clone.
-        let lock = self.shared_lock.clone();
-        let guard = self.shared_lock.read();
-
-        // Make a deep clone of the rules, using the new lock.
-        let rules = self.rules.read_with(&guard);
-        let cloned_rules = rules.deep_clone_with_lock(&lock);
-
-        // Make a deep clone of the media, using the new lock.
-        let media = self.media.read_with(&guard);
-        let cloned_media = media.clone();
-
-        Stylesheet {
-            rules: Arc::new(lock.wrap(cloned_rules)),
-            media: Arc::new(lock.wrap(cloned_media)),
-            origin: self.origin,
-            url_data: self.url_data.clone(),
-            shared_lock: lock,
-            namespaces: RwLock::new((*self.namespaces.read()).clone()),
-            dirty_on_viewport_size_change: AtomicBool::new(
-                self.dirty_on_viewport_size_change.load(Ordering::SeqCst)),
-            disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
-            quirks_mode: self.quirks_mode,
-        }
-    }
-}
-
-macro_rules! rule_filter {
-    ($( $method: ident($variant:ident => $rule_type: ident), )+) => {
-        impl Stylesheet {
-            $(
-                #[allow(missing_docs)]
-                pub fn $method<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
-                    where F: FnMut(&$rule_type),
-                {
-                    for rule in self.effective_rules(device, guard) {
-                        if let CssRule::$variant(ref lock) = *rule {
-                            let rule = lock.read_with(guard);
-                            f(&rule)
-                        }
-                    }
-                }
-            )+
-        }
-    }
-}
-
-rule_filter! {
-    effective_style_rules(Style => StyleRule),
-    effective_media_rules(Media => MediaRule),
-    effective_font_face_rules(FontFace => FontFaceRule),
-    effective_counter_style_rules(CounterStyle => CounterStyleRule),
-    effective_viewport_rules(Viewport => ViewportRule),
-    effective_keyframes_rules(Keyframes => KeyframesRule),
-    effective_supports_rules(Supports => SupportsRule),
-    effective_page_rules(Page => PageRule),
-    effective_document_rules(Document => DocumentRule),
-}
-
-/// The stylesheet loader is the abstraction used to trigger network requests
-/// for `@import` rules.
-pub trait StylesheetLoader {
-    /// Request a stylesheet after parsing a given `@import` rule.
-    ///
-    /// The called code is responsible to update the `stylesheet` rules field
-    /// when the sheet is done loading.
-    ///
-    /// The convoluted signature allows impls to look at MediaList and ImportRule
-    /// before they’re locked, while keeping the trait object-safe.
-    fn request_stylesheet(
-        &self,
-        media: Arc<Locked<MediaList>>,
-        make_import: &mut FnMut(Arc<Locked<MediaList>>) -> ImportRule,
-        make_arc: &mut FnMut(ImportRule) -> Arc<Locked<ImportRule>>,
-    ) -> Arc<Locked<ImportRule>>;
-}
-
-struct NoOpLoader;
-
-impl StylesheetLoader for NoOpLoader {
-    fn request_stylesheet(
-        &self,
-        media: Arc<Locked<MediaList>>,
-        make_import: &mut FnMut(Arc<Locked<MediaList>>) -> ImportRule,
-        make_arc: &mut FnMut(ImportRule) -> Arc<Locked<ImportRule>>,
-    ) -> Arc<Locked<ImportRule>> {
-        make_arc(make_import(media))
-    }
-}
-
-
-struct TopLevelRuleParser<'a> {
-    stylesheet_origin: Origin,
-    shared_lock: &'a SharedRwLock,
-    loader: Option<&'a StylesheetLoader>,
-    context: ParserContext<'a>,
-    state: State,
-    namespaces: Option<&'a mut Namespaces>,
-}
-
-impl<'b> TopLevelRuleParser<'b> {
-    fn nested<'a: 'b>(&'a self) -> NestedRuleParser<'a, 'b> {
-        NestedRuleParser {
-            stylesheet_origin: self.stylesheet_origin,
-            shared_lock: self.shared_lock,
-            context: &self.context,
-        }
-    }
-}
-
-#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
-#[allow(missing_docs)]
-pub enum State {
-    Start = 1,
-    Imports = 2,
-    Namespaces = 3,
-    Body = 4,
-    Invalid = 5,
-}
-
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-/// Vendor prefix.
-pub enum VendorPrefix {
-    /// -moz prefix.
-    Moz,
-    /// -webkit prefix.
-    WebKit,
-}
-
-enum AtRulePrelude {
-    /// A @font-face rule prelude.
-    FontFace(SourceLocation),
-    /// A @counter-style rule prelude, with its counter style name.
-    CounterStyle(CustomIdent),
-    /// A @media rule prelude, with its media queries.
-    Media(Arc<Locked<MediaList>>, SourceLocation),
-    /// An @supports rule, with its conditional
-    Supports(SupportsCondition, SourceLocation),
-    /// A @viewport rule prelude.
-    Viewport,
-    /// A @keyframes rule, with its animation name and vendor prefix if exists.
-    Keyframes(KeyframesName, Option<VendorPrefix>, SourceLocation),
-    /// A @page rule prelude.
-    Page(SourceLocation),
-    /// A @document rule, with its conditional.
-    Document(DocumentCondition, SourceLocation),
-}
-
-
-#[cfg(feature = "gecko")]
-fn register_namespace(ns: &Namespace) -> Result<i32, ()> {
-    let id = unsafe { ::gecko_bindings::bindings::Gecko_RegisterNamespace(ns.0.as_ptr()) };
-    if id == -1 {
-        Err(())
-    } else {
-        Ok(id)
-    }
-}
-
-#[cfg(feature = "servo")]
-fn register_namespace(_: &Namespace) -> Result<(), ()> {
-    Ok(()) // servo doesn't use namespace ids
-}
-
-impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
-    type Prelude = AtRulePrelude;
-    type AtRule = CssRule;
-
-    fn parse_prelude(
-        &mut self,
-        name: &str,
-        input: &mut Parser
-    ) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
-        let location = get_location_with_offset(input.current_source_location(),
-                                                self.context.line_number_offset);
-        match_ignore_ascii_case! { name,
-            "import" => {
-                if self.state > State::Imports {
-                    self.state = State::Invalid;
-                    return Err(())  // "@import must be before any rule but @charset"
-                }
-
-                self.state = State::Imports;
-                let url_string = input.expect_url_or_string()?;
-                let specified_url = SpecifiedUrl::parse_from_string(url_string, &self.context)?;
-
-                let media = parse_media_query_list(&self.context, input);
-                let media = Arc::new(self.shared_lock.wrap(media));
-
-                let noop_loader = NoOpLoader;
-                let loader = if !specified_url.is_invalid() {
-                    self.loader.expect("Expected a stylesheet loader for @import")
-                } else {
-                    &noop_loader
-                };
-
-                let mut specified_url = Some(specified_url);
-                let arc = loader.request_stylesheet(media, &mut |media| {
-                    ImportRule {
-                        url: specified_url.take().unwrap(),
-                        stylesheet: Arc::new(Stylesheet {
-                            rules: CssRules::new(Vec::new(), self.shared_lock),
-                            media: media,
-                            shared_lock: self.shared_lock.clone(),
-                            origin: self.context.stylesheet_origin,
-                            url_data: self.context.url_data.clone(),
-                            namespaces: RwLock::new(Namespaces::default()),
-                            dirty_on_viewport_size_change: AtomicBool::new(false),
-                            disabled: AtomicBool::new(false),
-                            quirks_mode: self.context.quirks_mode,
-                        }),
-                        source_location: location,
-                    }
-                }, &mut |import_rule| {
-                    Arc::new(self.shared_lock.wrap(import_rule))
-                });
-
-                return Ok(AtRuleType::WithoutBlock(CssRule::Import(arc)))
-            },
-            "namespace" => {
-                if self.state > State::Namespaces {
-                    self.state = State::Invalid;
-                    return Err(())  // "@namespace must be before any rule but @charset and @import"
-                }
-                self.state = State::Namespaces;
-
-                let prefix_result = input.try(|input| input.expect_ident());
-                let url = Namespace::from(try!(input.expect_url_or_string()));
-
-                let id = register_namespace(&url)?;
-
-                let mut namespaces = self.namespaces.as_mut().unwrap();
-
-                let opt_prefix = if let Ok(prefix) = prefix_result {
-                    let prefix = Prefix::from(prefix);
-                    namespaces
-                        .prefixes
-                        .insert(prefix.clone(), (url.clone(), id));
-                    Some(prefix)
-                } else {
-                    namespaces.default = Some((url.clone(), id));
-                    None
-                };
-
-                return Ok(AtRuleType::WithoutBlock(CssRule::Namespace(Arc::new(
-                    self.shared_lock.wrap(NamespaceRule {
-                        prefix: opt_prefix,
-                        url: url,
-                        source_location: location,
-                    })
-                ))))
-            },
-            // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
-            // anything left is invalid.
-            "charset" => return Err(()), // (insert appropriate error message)
-            _ => {}
-        }
-        // Don't allow starting with an invalid state
-        if self.state > State::Body {
-            self.state = State::Invalid;
-            return Err(());
-        }
-        self.state = State::Body;
-
-        // "Freeze" the namespace map (no more namespace rules can be parsed
-        // after this point), and stick it in the context.
-        if self.namespaces.is_some() {
-            let namespaces = &*self.namespaces.take().unwrap();
-            self.context.namespaces = Some(namespaces);
-        }
-        AtRuleParser::parse_prelude(&mut self.nested(), name, input)
-    }
-
-    #[inline]
-    fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CssRule, ()> {
-        AtRuleParser::parse_block(&mut self.nested(), prelude, input)
-    }
-}
-
-
-impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
-    type Prelude = SelectorList<SelectorImpl>;
-    type QualifiedRule = CssRule;
-
-    #[inline]
-    fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
-        self.state = State::Body;
-
-        // "Freeze" the namespace map (no more namespace rules can be parsed
-        // after this point), and stick it in the context.
-        if self.namespaces.is_some() {
-            let namespaces = &*self.namespaces.take().unwrap();
-            self.context.namespaces = Some(namespaces);
-        }
-
-        QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
-    }
-
-    #[inline]
-    fn parse_block(
-        &mut self,
-        prelude: SelectorList<SelectorImpl>,
-        input: &mut Parser
-    ) -> Result<CssRule, ()> {
-        QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input)
-    }
-}
-
-#[derive(Clone)]  // shallow, relatively cheap .clone
-struct NestedRuleParser<'a, 'b: 'a> {
-    stylesheet_origin: Origin,
-    shared_lock: &'a SharedRwLock,
-    context: &'a ParserContext<'b>,
-}
-
-impl<'a, 'b> NestedRuleParser<'a, 'b> {
-    fn parse_nested_rules(
-        &mut self,
-        input: &mut Parser,
-        rule_type: CssRuleType
-    ) -> Arc<Locked<CssRules>> {
-        let context = ParserContext::new_with_rule_type(self.context, Some(rule_type));
-
-        let nested_parser = NestedRuleParser {
-            stylesheet_origin: self.stylesheet_origin,
-            shared_lock: self.shared_lock,
-            context: &context,
-        };
-
-        let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
-        let mut rules = Vec::new();
-        while let Some(result) = iter.next() {
-            match result {
-                Ok(rule) => rules.push(rule),
-                Err(range) => {
-                    let pos = range.start;
-                    let message = format!("Unsupported rule: '{}'", iter.input.slice(range));
-                    log_css_error(iter.input, pos, &*message, self.context);
-                }
-            }
-        }
-        CssRules::new(rules, self.shared_lock)
-    }
-}
-
-#[cfg(feature = "servo")]
-fn is_viewport_enabled() -> bool {
-    PREFS.get("layout.viewport.enabled").as_boolean().unwrap_or(false)
-}
-
-#[cfg(not(feature = "servo"))]
-fn is_viewport_enabled() -> bool {
-    true
-}
-
-impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
-    type Prelude = AtRulePrelude;
-    type AtRule = CssRule;
-
-    fn parse_prelude(
-        &mut self,
-        name: &str,
-        input: &mut Parser
-    ) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
-        let location =
-            get_location_with_offset(
-                input.current_source_location(),
-                self.context.line_number_offset
-            );
-
-        match_ignore_ascii_case! { name,
-            "media" => {
-                let media_queries = parse_media_query_list(self.context, input);
-                let arc = Arc::new(self.shared_lock.wrap(media_queries));
-                Ok(AtRuleType::WithBlock(AtRulePrelude::Media(arc, location)))
-            },
-            "supports" => {
-                let cond = SupportsCondition::parse(input)?;
-                Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond, location)))
-            },
-            "font-face" => {
-                Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace(location)))
-            },
-            "counter-style" => {
-                if !cfg!(feature = "gecko") {
-                    // Support for this rule is not fully implemented in Servo yet.
-                    return Err(())
-                }
-                let name = parse_counter_style_name(input)?;
-                // ASCII-case-insensitive matches for "decimal" are already lower-cased
-                // by `parse_counter_style_name`, so we can use == here.
-                // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1359323 use atom!("decimal")
-                if name.0 == Atom::from("decimal") {
-                    return Err(())
-                }
-                Ok(AtRuleType::WithBlock(AtRulePrelude::CounterStyle(name)))
-            },
-            "viewport" => {
-                if is_viewport_enabled() {
-                    Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
-                } else {
-                    Err(())
-                }
-            },
-            "keyframes" | "-webkit-keyframes" | "-moz-keyframes" => {
-                let prefix = if starts_with_ignore_ascii_case(name, "-webkit-") {
-                    Some(VendorPrefix::WebKit)
-                } else if starts_with_ignore_ascii_case(name, "-moz-") {
-                    Some(VendorPrefix::Moz)
-                } else {
-                    None
-                };
-                if cfg!(feature = "servo") &&
-                   prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {
-                    // Servo should not support @-moz-keyframes.
-                    return Err(())
-                }
-                let name = KeyframesName::parse(self.context, input)?;
-
-                Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix, location)))
-            },
-            "page" => {
-                if cfg!(feature = "gecko") {
-                    Ok(AtRuleType::WithBlock(AtRulePrelude::Page(location)))
-                } else {
-                    Err(())
-                }
-            },
-            "-moz-document" => {
-                if cfg!(feature = "gecko") {
-                    let cond = DocumentCondition::parse(self.context, input)?;
-                    Ok(AtRuleType::WithBlock(AtRulePrelude::Document(cond, location)))
-                } else {
-                    Err(())
-                }
-            },
-            _ => Err(())
-        }
-    }
-
-    fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CssRule, ()> {
-        match prelude {
-            AtRulePrelude::FontFace(location) => {
-                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::FontFace));
-                Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
-                   parse_font_face_block(&context, input, location).into()))))
-            }
-            AtRulePrelude::CounterStyle(name) => {
-                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::CounterStyle));
-                Ok(CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(
-                   parse_counter_style_body(name, &context, input)?.into()))))
-            }
-            AtRulePrelude::Media(media_queries, location) => {
-                Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
-                    media_queries: media_queries,
-                    rules: self.parse_nested_rules(input, CssRuleType::Media),
-                    source_location: location,
-                }))))
-            }
-            AtRulePrelude::Supports(cond, location) => {
-                let enabled = cond.eval(self.context);
-                Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(SupportsRule {
-                    condition: cond,
-                    rules: self.parse_nested_rules(input, CssRuleType::Supports),
-                    enabled: enabled,
-                    source_location: location,
-                }))))
-            }
-            AtRulePrelude::Viewport => {
-                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Viewport));
-                Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
-                   try!(ViewportRule::parse(&context, input))))))
-            }
-            AtRulePrelude::Keyframes(name, prefix, location) => {
-                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes));
-                Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule {
-                    name: name,
-                    keyframes: parse_keyframe_list(&context, input, self.shared_lock),
-                    vendor_prefix: prefix,
-                    source_location: location,
-                }))))
-            }
-            AtRulePrelude::Page(location) => {
-                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Page));
-                let declarations = parse_property_declaration_list(&context, input);
-                Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
-                    block: Arc::new(self.shared_lock.wrap(declarations)),
-                    source_location: location,
-                }))))
-            }
-            AtRulePrelude::Document(cond, location) => {
-                if cfg!(feature = "gecko") {
-                    Ok(CssRule::Document(Arc::new(self.shared_lock.wrap(DocumentRule {
-                        condition: cond,
-                        rules: self.parse_nested_rules(input, CssRuleType::Document),
-                        source_location: location,
-                    }))))
-                } else {
-                    unreachable!()
-                }
-            }
-        }
-    }
-}
-
-impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
-    type Prelude = SelectorList<SelectorImpl>;
-    type QualifiedRule = CssRule;
-
-    fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
-        let selector_parser = SelectorParser {
-            stylesheet_origin: self.stylesheet_origin,
-            namespaces: self.context.namespaces.unwrap(),
-        };
-
-        SelectorList::parse(&selector_parser, input)
-    }
-
-    fn parse_block(
-        &mut self,
-        prelude: SelectorList<SelectorImpl>,
-        input: &mut Parser
-    ) -> Result<CssRule, ()> {
-        let location = get_location_with_offset(input.current_source_location(),
-                                                self.context.line_number_offset);
-        let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Style));
-        let declarations = parse_property_declaration_list(&context, input);
-        Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
-            selectors: prelude,
-            block: Arc::new(self.shared_lock.wrap(declarations)),
-            source_location: location,
-        }))))
-    }
-}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/counter_style_rule.rs
@@ -0,0 +1,12 @@
+/* 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/. */
+
+// TODO(emilio): unify this, components/style/counter_style.rs, and
+// components/style/gecko/rules.rs
+#![allow(missing_docs)]
+
+#[cfg(feature = "servo")]
+pub use counter_style::CounterStyleRuleData as CounterStyleRule;
+#[cfg(feature = "gecko")]
+pub use gecko::rules::CounterStyleRule;
rename from servo/components/style/document_condition.rs
rename to servo/components/style/stylesheets/document_rule.rs
--- a/servo/components/style/document_condition.rs
+++ b/servo/components/style/stylesheets/document_rule.rs
@@ -1,29 +1,67 @@
 /* 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/. */
 
 //! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document)
 //! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.
 //! We implement the prefixed `@-moz-document`.
 
-use cssparser::{Parser, Token, serialize_string};
-#[cfg(feature = "gecko")]
-use gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
-#[cfg(feature = "gecko")]
-use gecko_bindings::structs::URLMatchingFunction as GeckoUrlMatchingFunction;
+use cssparser::{Parser, Token, SourceLocation, serialize_string};
 use media_queries::Device;
-#[cfg(feature = "gecko")]
-use nsstring::nsCString;
 use parser::{Parse, ParserContext};
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt;
 use style_traits::ToCss;
+use stylearc::Arc;
+use stylesheets::CssRules;
 use values::specified::url::SpecifiedUrl;
 
+#[derive(Debug)]
+/// A @-moz-document rule
+pub struct DocumentRule {
+    /// The parsed condition
+    pub condition: DocumentCondition,
+    /// Child rules
+    pub rules: Arc<Locked<CssRules>>,
+    /// The line and column of the rule's source code.
+    pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for DocumentRule {
+    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+    where W: fmt::Write {
+        try!(dest.write_str("@-moz-document "));
+        try!(self.condition.to_css(dest));
+        try!(dest.write_str(" {"));
+        for rule in self.rules.read_with(guard).0.iter() {
+            try!(dest.write_str(" "));
+            try!(rule.to_css(guard, dest));
+        }
+        dest.write_str(" }")
+    }
+}
+
+impl DeepCloneWithLock for DocumentRule {
+    /// Deep clones this DocumentRule.
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard,
+    ) -> Self {
+        let rules = self.rules.read_with(guard);
+        DocumentRule {
+            condition: self.condition.clone(),
+            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
+            source_location: self.source_location.clone(),
+        }
+    }
+}
+
 /// A URL matching function for a `@document` rule's condition.
 #[derive(Clone, Debug)]
 pub enum UrlMatchingFunction {
     /// Exact URL matching function. It evaluates to true whenever the
     /// URL of the document being styled is exactly the URL given.
     Url(SpecifiedUrl),
     /// URL prefix matching function. It evaluates to true whenever the
     /// URL of the document being styled has the argument to the
@@ -79,22 +117,27 @@ impl UrlMatchingFunction {
         } else {
             Err(())
         }
     }
 
     #[cfg(feature = "gecko")]
     /// Evaluate a URL matching function.
     pub fn evaluate(&self, device: &Device) -> bool {
+        use gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
+        use gecko_bindings::structs::URLMatchingFunction as GeckoUrlMatchingFunction;
+        use nsstring::nsCString;
+
         let func = match *self {
             UrlMatchingFunction::Url(_) => GeckoUrlMatchingFunction::eURL,
             UrlMatchingFunction::UrlPrefix(_) => GeckoUrlMatchingFunction::eURLPrefix,
             UrlMatchingFunction::Domain(_) => GeckoUrlMatchingFunction::eDomain,
             UrlMatchingFunction::RegExp(_) => GeckoUrlMatchingFunction::eRegExp,
         };
+
         let pattern = nsCString::from(match *self {
             UrlMatchingFunction::Url(ref url) => url.as_str(),
             UrlMatchingFunction::UrlPrefix(ref pat) |
             UrlMatchingFunction::Domain(ref pat) |
             UrlMatchingFunction::RegExp(ref pat) => pat,
         });
         unsafe {
             Gecko_DocumentRule_UseForPresentation(&*device.pres_context, &*pattern, func)
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/font_face_rule.rs
@@ -0,0 +1,12 @@
+/* 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/. */
+
+// TODO(emilio): unify this, components/style/font_face.rs, and
+// components/style/gecko/rules.rs
+#![allow(missing_docs)]
+
+#[cfg(feature = "servo")]
+pub use font_face::FontFaceRuleData as FontFaceRule;
+#[cfg(feature = "gecko")]
+pub use gecko::rules::FontFaceRule;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/import_rule.rs
@@ -0,0 +1,59 @@
+/* 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/. */
+
+//! The [`@import`][import] at-rule.
+//!
+//! [import]: https://drafts.csswg.org/css-cascade-3/#at-import
+
+use cssparser::SourceLocation;
+use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use style_traits::ToCss;
+use stylearc::Arc;
+use stylesheets::stylesheet::Stylesheet;
+use values::specified::url::SpecifiedUrl;
+
+/// The [`@import`][import] at-rule.
+///
+/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import
+#[derive(Debug)]
+pub struct ImportRule {
+    /// The `<url>` this `@import` rule is loading.
+    pub url: SpecifiedUrl,
+
+    /// The stylesheet is always present.
+    ///
+    /// It contains an empty list of rules and namespace set that is updated
+    /// when it loads.
+    pub stylesheet: Arc<Stylesheet>,
+
+    /// The line and column of the rule's source code.
+    pub source_location: SourceLocation,
+}
+
+impl Clone for ImportRule {
+    fn clone(&self) -> ImportRule {
+        let stylesheet: &Stylesheet = &*self.stylesheet;
+        ImportRule {
+            url: self.url.clone(),
+            stylesheet: Arc::new(stylesheet.clone()),
+            source_location: self.source_location.clone(),
+        }
+    }
+}
+
+impl ToCssWithGuard for ImportRule {
+    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
+        dest.write_str("@import ")?;
+        self.url.to_css(dest)?;
+        let media = self.stylesheet.media.read_with(guard);
+        if !media.is_empty() {
+            dest.write_str(" ")?;
+            media.to_css(dest)?;
+        }
+        dest.write_str(";")
+    }
+}
rename from servo/components/style/keyframes.rs
rename to servo/components/style/stylesheets/keyframes_rule.rs
--- a/servo/components/style/keyframes.rs
+++ b/servo/components/style/stylesheets/keyframes_rule.rs
@@ -1,30 +1,99 @@
 /* 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/. */
 
 //! Keyframes: https://drafts.csswg.org/css-animations/#keyframes
 
-#![deny(missing_docs)]
-
 use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
-use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule};
+use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule, SourceLocation};
 use error_reporting::NullReporter;
 use parser::{PARSING_MODE_DEFAULT, ParserContext, log_css_error};
 use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
 use properties::{PropertyDeclarationId, LonghandId, SourcePropertyDeclaration};
 use properties::LonghandIdSet;
 use properties::animated_properties::TransitionProperty;
 use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
-use shared_lock::{SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
+use shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
 use std::fmt;
 use style_traits::ToCss;
 use stylearc::Arc;
-use stylesheets::{CssRuleType, Stylesheet, VendorPrefix};
+use stylesheets::{CssRuleType, Stylesheet};
+use stylesheets::rule_parser::VendorPrefix;
+use values::KeyframesName;
+
+/// A [`@keyframes`][keyframes] rule.
+///
+/// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes
+#[derive(Debug)]
+pub struct KeyframesRule {
+    /// The name of the current animation.
+    pub name: KeyframesName,
+    /// The keyframes specified for this CSS rule.
+    pub keyframes: Vec<Arc<Locked<Keyframe>>>,
+    /// Vendor prefix type the @keyframes has.
+    pub vendor_prefix: Option<VendorPrefix>,
+    /// The line and column of the rule's source code.
+    pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for KeyframesRule {
+    // Serialization of KeyframesRule is not specced.
+    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
+        dest.write_str("@keyframes ")?;
+        self.name.to_css(dest)?;
+        dest.write_str(" {")?;
+        let iter = self.keyframes.iter();
+        for lock in iter {
+            dest.write_str("\n")?;
+            let keyframe = lock.read_with(&guard);
+            keyframe.to_css(guard, dest)?;
+        }
+        dest.write_str("\n}")
+    }
+}
+
+impl KeyframesRule {
+    /// Returns the index of the last keyframe that matches the given selector.
+    /// If the selector is not valid, or no keyframe is found, returns None.
+    ///
+    /// Related spec:
+    /// https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule
+    pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
+        if let Ok(selector) = Parser::new(selector).parse_entirely(KeyframeSelector::parse) {
+            for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
+                if keyframe.read_with(guard).selector == selector {
+                    return Some(i);
+                }
+            }
+        }
+        None
+    }
+}
+
+impl DeepCloneWithLock for KeyframesRule {
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard
+    ) -> Self {
+        KeyframesRule {
+            name: self.name.clone(),
+            keyframes: self.keyframes.iter()
+                .map(|ref x| Arc::new(lock.wrap(
+                    x.read_with(guard).deep_clone_with_lock(lock, guard))))
+                .collect(),
+            vendor_prefix: self.vendor_prefix.clone(),
+            source_location: self.source_location.clone(),
+        }
+    }
+}
 
 /// A number from 0 to 1, indicating the percentage of the animation when this
 /// keyframe should run.
 #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct KeyframePercentage(pub f32);
 
 impl ::std::cmp::Ord for KeyframePercentage {
@@ -145,24 +214,28 @@ impl Keyframe {
         let mut declarations = SourcePropertyDeclaration::new();
         let mut rule_parser = KeyframeListParser {
             context: &context,
             shared_lock: &parent_stylesheet.shared_lock,
             declarations: &mut declarations,
         };
         parse_one_rule(&mut input, &mut rule_parser)
     }
+}
 
+impl DeepCloneWithLock for Keyframe {
     /// Deep clones this Keyframe.
-    pub fn deep_clone_with_lock(&self,
-                                lock: &SharedRwLock) -> Keyframe {
-        let guard = lock.read();
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard
+    ) -> Keyframe {
         Keyframe {
             selector: self.selector.clone(),
-            block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
+            block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
         }
     }
 }
 
 /// A keyframes step value. This can be a synthetised keyframes animation, that
 /// is, one autogenerated from the current computed values, or a list of
 /// declarations to apply.
 ///
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/loader.rs
@@ -0,0 +1,43 @@
+/* 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/. */
+
+//! The stylesheet loader is the abstraction used to trigger network requests
+//! for `@import` rules.
+
+use media_queries::MediaList;
+use shared_lock::Locked;
+use stylearc::Arc;
+use stylesheets::ImportRule;
+
+/// The stylesheet loader is the abstraction used to trigger network requests
+/// for `@import` rules.
+pub trait StylesheetLoader {
+    /// Request a stylesheet after parsing a given `@import` rule.
+    ///
+    /// The called code is responsible to update the `stylesheet` rules field
+    /// when the sheet is done loading.
+    ///
+    /// The convoluted signature allows impls to look at MediaList and
+    /// ImportRule before they’re locked, while keeping the trait object-safe.
+    fn request_stylesheet(
+        &self,
+        media: Arc<Locked<MediaList>>,
+        make_import: &mut FnMut(Arc<Locked<MediaList>>) -> ImportRule,
+        make_arc: &mut FnMut(ImportRule) -> Arc<Locked<ImportRule>>,
+    ) -> Arc<Locked<ImportRule>>;
+}
+
+/// A dummy loader that just creates the import rule with the empty stylesheet.
+pub struct NoOpLoader;
+
+impl StylesheetLoader for NoOpLoader {
+    fn request_stylesheet(
+        &self,
+        media: Arc<Locked<MediaList>>,
+        make_import: &mut FnMut(Arc<Locked<MediaList>>) -> ImportRule,
+        make_arc: &mut FnMut(ImportRule) -> Arc<Locked<ImportRule>>,
+    ) -> Arc<Locked<ImportRule>> {
+        make_arc(make_import(media))
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/media_rule.rs
@@ -0,0 +1,60 @@
+/* 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/. */
+
+//! An [`@media`][media] urle.
+//!
+//! [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media
+
+use cssparser::SourceLocation;
+use media_queries::MediaList;
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use style_traits::ToCss;
+use stylearc::Arc;
+use stylesheets::CssRules;
+
+/// An [`@media`][media] urle.
+///
+/// [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media
+#[derive(Debug)]
+pub struct MediaRule {
+    /// The list of media queries used by this media rule.
+    pub media_queries: Arc<Locked<MediaList>>,
+    /// The nested rules to this media rule.
+    pub rules: Arc<Locked<CssRules>>,
+    /// The source position where this media rule was found.
+    pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for MediaRule {
+    // Serialization of MediaRule is not specced.
+    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule
+    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+    where W: fmt::Write {
+        dest.write_str("@media ")?;
+        self.media_queries.read_with(guard).to_css(dest)?;
+        dest.write_str(" {")?;
+        for rule in self.rules.read_with(guard).0.iter() {
+            dest.write_str(" ")?;
+            rule.to_css(guard, dest)?;
+        }
+        dest.write_str(" }")
+    }
+}
+
+impl DeepCloneWithLock for MediaRule {
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard
+    ) -> Self {
+        let media_queries = self.media_queries.read_with(guard);
+        let rules = self.rules.read_with(guard);
+        MediaRule {
+            media_queries: Arc::new(lock.wrap(media_queries.clone())),
+            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
+            source_location: self.source_location.clone(),
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/memory.rs
@@ -0,0 +1,71 @@
+/* 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/. */
+
+//! Memory reporting for the style system when running inside of Gecko.
+
+use shared_lock::SharedRwLockReadGuard;
+use std::os::raw::c_void;
+
+/// Like gecko_bindings::structs::MallocSizeOf, but without the Option<>
+/// wrapper.
+///
+/// Note that functions of this type should not be called via
+/// do_malloc_size_of(), rather than directly.
+pub type MallocSizeOfFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
+
+/// Call malloc_size_of on ptr, first checking that the allocation isn't empty.
+pub unsafe fn do_malloc_size_of<T>(malloc_size_of: MallocSizeOfFn, ptr: *const T) -> usize {
+    use std::mem::align_of;
+
+    if ptr as usize <= align_of::<T>() {
+        0
+    } else {
+        malloc_size_of(ptr as *const c_void)
+    }
+}
+
+/// Trait for measuring the size of heap data structures.
+pub trait MallocSizeOf {
+    /// Measure the size of any heap-allocated structures that hang off this
+    /// value, but not the space taken up by the value itself.
+    fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize;
+}
+
+/// Like MallocSizeOf, but operates with the global SharedRwLockReadGuard
+/// locked.
+pub trait MallocSizeOfWithGuard {
+    /// Like MallocSizeOf::malloc_size_of_children, but with a |guard| argument.
+    fn malloc_size_of_children(
+        &self,
+        guard: &SharedRwLockReadGuard,
+        malloc_size_of: MallocSizeOfFn
+    ) -> usize;
+}
+
+impl<A: MallocSizeOf, B: MallocSizeOf> MallocSizeOf for (A, B) {
+    fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+        self.0.malloc_size_of_children(malloc_size_of) +
+            self.1.malloc_size_of_children(malloc_size_of)
+    }
+}
+
+impl<T: MallocSizeOf> MallocSizeOf for Vec<T> {
+    fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+        self.iter().fold(
+            unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) },
+            |n, elem| n + elem.malloc_size_of_children(malloc_size_of))
+    }
+}
+
+impl<T: MallocSizeOfWithGuard> MallocSizeOfWithGuard for Vec<T> {
+    fn malloc_size_of_children(
+        &self,
+        guard: &SharedRwLockReadGuard,
+        malloc_size_of: MallocSizeOfFn,
+    ) -> usize {
+        self.iter().fold(
+            unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) },
+            |n, elem| n + elem.malloc_size_of_children(guard, malloc_size_of))
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/mod.rs
@@ -0,0 +1,344 @@
+/* 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/. */
+
+//! Style sheets and their CSS rules.
+
+mod counter_style_rule;
+mod document_rule;
+mod font_face_rule;
+mod import_rule;
+pub mod keyframes_rule;
+mod loader;
+mod media_rule;
+mod memory;
+mod namespace_rule;
+mod page_rule;
+mod rule_list;
+mod rule_parser;
+mod rules_iterator;
+mod style_rule;
+mod stylesheet;
+pub mod supports_rule;
+pub mod viewport_rule;
+
+use cssparser::{parse_one_rule, Parser};
+use error_reporting::NullReporter;
+use parser::{ParserContext, PARSING_MODE_DEFAULT};
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use stylearc::Arc;
+
+pub use self::counter_style_rule::CounterStyleRule;
+pub use self::document_rule::DocumentRule;
+pub use self::font_face_rule::FontFaceRule;
+pub use self::import_rule::ImportRule;
+pub use self::keyframes_rule::KeyframesRule;
+pub use self::loader::StylesheetLoader;
+pub use self::media_rule::MediaRule;
+pub use self::memory::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard};
+pub use self::namespace_rule::NamespaceRule;
+pub use self::page_rule::PageRule;
+pub use self::rule_parser::{State, TopLevelRuleParser};
+pub use self::rule_list::{CssRules, CssRulesHelpers};
+pub use self::rules_iterator::{AllRules, EffectiveRules, NestedRuleIterationCondition, RulesIterator};
+pub use self::stylesheet::{Namespaces, Stylesheet, UserAgentStylesheets};
+pub use self::style_rule::StyleRule;
+pub use self::supports_rule::SupportsRule;
+pub use self::viewport_rule::ViewportRule;
+
+/// Extra data that the backend may need to resolve url values.
+#[cfg(not(feature = "gecko"))]
+pub type UrlExtraData = ::servo_url::ServoUrl;
+
+/// Extra data that the backend may need to resolve url values.
+#[cfg(feature = "gecko")]
+pub type UrlExtraData =
+    ::gecko_bindings::sugar::refptr::RefPtr<::gecko_bindings::structs::URLExtraData>;
+
+#[cfg(feature = "gecko")]
+impl UrlExtraData {
+    /// Returns a string for the url.
+    ///
+    /// Unimplemented currently.
+    pub fn as_str(&self) -> &str {
+        // TODO
+        "(stylo: not supported)"
+    }
+}
+
+// XXX We probably need to figure out whether we should mark Eq here.
+// It is currently marked so because properties::UnparsedValue wants Eq.
+#[cfg(feature = "gecko")]
+impl Eq for UrlExtraData {}
+
+/// Each style rule has an origin, which determines where it enters the cascade.
+///
+/// http://dev.w3.org/csswg/css-cascade/#cascading-origins
+#[derive(Clone, PartialEq, Eq, Copy, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum Origin {
+    /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-ua
+    UserAgent,
+
+    /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-author
+    Author,
+
+    /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-user
+    User,
+}
+
+/// A CSS rule.
+///
+/// TODO(emilio): Lots of spec links should be around.
+#[derive(Debug, Clone)]
+#[allow(missing_docs)]
+pub enum CssRule {
+    // No Charset here, CSSCharsetRule has been removed from CSSOM
+    // https://drafts.csswg.org/cssom/#changes-from-5-december-2013
+
+    Namespace(Arc<Locked<NamespaceRule>>),
+    Import(Arc<Locked<ImportRule>>),
+    Style(Arc<Locked<StyleRule>>),
+    Media(Arc<Locked<MediaRule>>),
+    FontFace(Arc<Locked<FontFaceRule>>),
+    CounterStyle(Arc<Locked<CounterStyleRule>>),
+    Viewport(Arc<Locked<ViewportRule>>),
+    Keyframes(Arc<Locked<KeyframesRule>>),
+    Supports(Arc<Locked<SupportsRule>>),
+    Page(Arc<Locked<PageRule>>),
+    Document(Arc<Locked<DocumentRule>>),
+}
+
+impl MallocSizeOfWithGuard for CssRule {
+    fn malloc_size_of_children(
+        &self,
+        guard: &SharedRwLockReadGuard,
+        malloc_size_of: MallocSizeOfFn
+    ) -> usize {
+        match *self {
+            CssRule::Style(ref lock) => {
+                lock.read_with(guard).malloc_size_of_children(guard, malloc_size_of)
+            },
+            // Measurement of these fields may be added later.
+            CssRule::Import(_) => 0,
+            CssRule::Media(_) => 0,
+            CssRule::FontFace(_) => 0,
+            CssRule::CounterStyle(_) => 0,
+            CssRule::Keyframes(_) => 0,
+            CssRule::Namespace(_) => 0,
+            CssRule::Viewport(_) => 0,
+            CssRule::Supports(_) => 0,
+            CssRule::Page(_) => 0,
+            CssRule::Document(_)  => 0,
+        }
+    }
+}
+
+#[allow(missing_docs)]
+#[derive(PartialEq, Eq, Copy, Clone)]
+pub enum CssRuleType {
+    // https://drafts.csswg.org/cssom/#the-cssrule-interface
+    Style               = 1,
+    Charset             = 2,
+    Import              = 3,
+    Media               = 4,
+    FontFace            = 5,
+    Page                = 6,
+    // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl
+    Keyframes           = 7,
+    Keyframe            = 8,
+    // https://drafts.csswg.org/cssom/#the-cssrule-interface
+    Margin              = 9,
+    Namespace           = 10,
+    // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface
+    CounterStyle        = 11,
+    // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface
+    Supports            = 12,
+    // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface
+    Document            = 13,
+    // https://drafts.csswg.org/css-fonts-3/#om-fontfeaturevalues
+    FontFeatureValues   = 14,
+    // https://drafts.csswg.org/css-device-adapt/#css-rule-interface
+    Viewport            = 15,
+}
+
+#[allow(missing_docs)]
+pub enum SingleRuleParseError {
+    Syntax,
+    Hierarchy,
+}
+
+#[allow(missing_docs)]
+pub enum RulesMutateError {
+    Syntax,
+    IndexSize,
+    HierarchyRequest,
+    InvalidState,
+}
+
+impl From<SingleRuleParseError> for RulesMutateError {
+    fn from(other: SingleRuleParseError) -> Self {
+        match other {
+            SingleRuleParseError::Syntax => RulesMutateError::Syntax,
+            SingleRuleParseError::Hierarchy => RulesMutateError::HierarchyRequest,
+        }
+    }
+}
+
+impl CssRule {
+    /// Returns the CSSOM rule type of this rule.
+    pub fn rule_type(&self) -> CssRuleType {
+        match *self {
+            CssRule::Style(_) => CssRuleType::Style,
+            CssRule::Import(_) => CssRuleType::Import,
+            CssRule::Media(_) => CssRuleType::Media,
+            CssRule::FontFace(_) => CssRuleType::FontFace,
+            CssRule::CounterStyle(_) => CssRuleType::CounterStyle,
+            CssRule::Keyframes(_) => CssRuleType::Keyframes,
+            CssRule::Namespace(_) => CssRuleType::Namespace,
+            CssRule::Viewport(_) => CssRuleType::Viewport,
+            CssRule::Supports(_) => CssRuleType::Supports,
+            CssRule::Page(_) => CssRuleType::Page,
+            CssRule::Document(_)  => CssRuleType::Document,
+        }
+    }
+
+    fn rule_state(&self) -> State {
+        match *self {
+            // CssRule::Charset(..) => State::Start,
+            CssRule::Import(..) => State::Imports,
+            CssRule::Namespace(..) => State::Namespaces,
+            _ => State::Body,
+        }
+    }
+
+    /// Parse a CSS rule.
+    ///
+    /// Returns a parsed CSS rule and the final state of the parser.
+    ///
+    /// Input state is None for a nested rule
+    pub fn parse(
+        css: &str,
+        parent_stylesheet: &Stylesheet,
+        state: Option<State>,
+        loader: Option<&StylesheetLoader>
+    ) -> Result<(Self, State), SingleRuleParseError> {
+        let error_reporter = NullReporter;
+        let context = ParserContext::new(
+            parent_stylesheet.origin,
+            &parent_stylesheet.url_data,
+            &error_reporter,
+            None,
+            PARSING_MODE_DEFAULT,
+            parent_stylesheet.quirks_mode
+        );
+
+        let mut input = Parser::new(css);
+
+        let mut guard = parent_stylesheet.namespaces.write();
+
+        // nested rules are in the body state
+        let state = state.unwrap_or(State::Body);
+        let mut rule_parser = TopLevelRuleParser {
+            stylesheet_origin: parent_stylesheet.origin,
+            context: context,
+            shared_lock: &parent_stylesheet.shared_lock,
+            loader: loader,
+            state: state,
+            namespaces: Some(&mut *guard),
+        };
+
+        match parse_one_rule(&mut input, &mut rule_parser) {
+            Ok(result) => Ok((result, rule_parser.state)),
+            Err(_) => {
+                Err(match rule_parser.state {
+                    State::Invalid => SingleRuleParseError::Hierarchy,
+                    _ => SingleRuleParseError::Syntax,
+                })
+            }
+        }
+    }
+}
+
+impl DeepCloneWithLock for CssRule {
+    /// Deep clones this CssRule.
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard,
+    ) -> CssRule {
+        match *self {
+            CssRule::Namespace(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::Namespace(Arc::new(lock.wrap(rule.clone())))
+            },
+            CssRule::Import(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::Import(Arc::new(lock.wrap(rule.clone())))
+            },
+            CssRule::Style(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::Style(Arc::new(
+                    lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+            },
+            CssRule::Media(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::Media(Arc::new(
+                    lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+            },
+            CssRule::FontFace(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
+            },
+            CssRule::CounterStyle(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone())))
+            },
+            CssRule::Viewport(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::Viewport(Arc::new(lock.wrap(rule.clone())))
+            },
+            CssRule::Keyframes(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::Keyframes(Arc::new(
+                    lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+            },
+            CssRule::Supports(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::Supports(Arc::new(
+                    lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+            },
+            CssRule::Page(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::Page(Arc::new(
+                    lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+            },
+            CssRule::Document(ref arc) => {
+                let rule = arc.read_with(guard);
+                CssRule::Document(Arc::new(
+                    lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+            },
+        }
+    }
+}
+
+impl ToCssWithGuard for CssRule {
+    // https://drafts.csswg.org/cssom/#serialize-a-css-rule
+    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+    where W: fmt::Write {
+        match *self {
+            CssRule::Namespace(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::Viewport(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::Media(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::Supports(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),
+            CssRule::Document(ref lock) => lock.read_with(guard).to_css(guard, dest),
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/namespace_rule.rs
@@ -0,0 +1,39 @@
+/* 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/. */
+
+//! The `@namespace` at-rule.
+
+use {Namespace, Prefix};
+use cssparser::SourceLocation;
+use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+
+/// A `@namespace` rule.
+#[derive(Clone, Debug, PartialEq)]
+#[allow(missing_docs)]
+pub struct NamespaceRule {
+    /// The namespace prefix, and `None` if it's the default Namespace
+    pub prefix: Option<Prefix>,
+    /// The actual namespace url.
+    pub url: Namespace,
+    /// The source location this rule was found at.
+    pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for NamespaceRule {
+    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule
+    fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+    where W: fmt::Write {
+        dest.write_str("@namespace ")?;
+        if let Some(ref prefix) = self.prefix {
+            dest.write_str(&*prefix.to_string())?;
+            dest.write_str(" ")?;
+        }
+
+        // FIXME(emilio): Pretty sure this needs some escaping, or something?
+        dest.write_str("url(\"")?;
+        dest.write_str(&*self.url.to_string())?;
+        dest.write_str("\");")
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/page_rule.rs
@@ -0,0 +1,60 @@
+/* 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/. */
+
+//! A [`@page`][page] rule.
+//!
+//! [page]: https://drafts.csswg.org/css2/page.html#page-box
+
+use cssparser::SourceLocation;
+use properties::PropertyDeclarationBlock;
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use style_traits::ToCss;
+use stylearc::Arc;
+
+/// A [`@page`][page] rule.
+///
+/// This implements only a limited subset of the CSS
+/// 2.2 syntax.
+///
+/// In this subset, [page selectors][page-selectors] are not implemented.
+///
+/// [page]: https://drafts.csswg.org/css2/page.html#page-box
+/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
+#[derive(Debug)]
+pub struct PageRule {
+    /// The declaration block this page rule contains.
+    pub block: Arc<Locked<PropertyDeclarationBlock>>,
+    /// The source position this rule was found at.
+    pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for PageRule {
+    /// Serialization of PageRule is not specced, adapted from steps for
+    /// StyleRule.
+    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
+        dest.write_str("@page { ")?;
+        let declaration_block = self.block.read_with(guard);
+        declaration_block.to_css(dest)?;
+        if !declaration_block.declarations().is_empty() {
+            dest.write_str(" ")?;
+        }
+        dest.write_str("}")
+    }
+}
+
+impl DeepCloneWithLock for PageRule {
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard,
+    ) -> Self {
+        PageRule {
+            block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
+            source_location: self.source_location.clone(),
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/rule_list.rs
@@ -0,0 +1,168 @@
+/* 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/. */
+
+//! A list of CSS rules.
+
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard};
+use stylearc::Arc;
+use stylesheets::{CssRule, RulesMutateError};
+use stylesheets::loader::StylesheetLoader;
+use stylesheets::memory::{MallocSizeOfFn, MallocSizeOfWithGuard};
+use stylesheets::rule_parser::State;
+use stylesheets::stylesheet::Stylesheet;
+
+/// A list of CSS rules.
+#[derive(Debug)]
+pub struct CssRules(pub Vec<CssRule>);
+
+impl CssRules {
+    /// Whether this CSS rules is empty.
+    pub fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+}
+
+impl DeepCloneWithLock for CssRules {
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard
+    ) -> Self {
+        CssRules(
+            self.0.iter().map(|ref x| x.deep_clone_with_lock(lock, guard)).collect()
+        )
+    }
+}
+
+impl MallocSizeOfWithGuard for CssRules {
+    fn malloc_size_of_children(
+        &self,
+        guard: &SharedRwLockReadGuard,
+        malloc_size_of: MallocSizeOfFn
+    ) -> usize {
+        self.0.malloc_size_of_children(guard, malloc_size_of)
+    }
+}
+
+impl CssRules {
+    /// Trivially construct a new set of CSS rules.
+    pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> {
+        Arc::new(shared_lock.wrap(CssRules(rules)))
+    }
+
+    /// Returns whether all the rules in this list are namespace or import
+    /// rules.
+    fn only_ns_or_import(&self) -> bool {
+        self.0.iter().all(|r| {
+            match *r {
+                CssRule::Namespace(..) |
+                CssRule::Import(..) => true,
+                _ => false
+            }
+        })
+    }
+
+    /// https://drafts.csswg.org/cssom/#remove-a-css-rule
+    pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> {
+        // Step 1, 2
+        if index >= self.0.len() {
+            return Err(RulesMutateError::IndexSize);
+        }
+
+        {
+            // Step 3
+            let ref rule = self.0[index];
+
+            // Step 4
+            if let CssRule::Namespace(..) = *rule {
+                if !self.only_ns_or_import() {
+                    return Err(RulesMutateError::InvalidState);
+                }
+            }
+        }
+
+        // Step 5, 6
+        self.0.remove(index);
+        Ok(())
+    }
+}
+
+/// A trait to implement helpers for `Arc<Locked<CssRules>>`.
+pub trait CssRulesHelpers {
+    /// https://drafts.csswg.org/cssom/#insert-a-css-rule
+    ///
+    /// Written in this funky way because parsing an @import rule may cause us
+    /// to clone a stylesheet from the same document due to caching in the CSS
+    /// loader.
+    ///
+    /// TODO(emilio): We could also pass the write guard down into the loader
+    /// instead, but that seems overkill.
+    fn insert_rule(&self,
+                   lock: &SharedRwLock,
+                   rule: &str,
+                   parent_stylesheet: &Stylesheet,
+                   index: usize,
+                   nested: bool,
+                   loader: Option<&StylesheetLoader>)
+                   -> Result<CssRule, RulesMutateError>;
+}
+
+impl CssRulesHelpers for Arc<Locked<CssRules>> {
+    fn insert_rule(&self,
+                   lock: &SharedRwLock,
+                   rule: &str,
+                   parent_stylesheet: &Stylesheet,
+                   index: usize,
+                   nested: bool,
+                   loader: Option<&StylesheetLoader>)
+                   -> Result<CssRule, RulesMutateError> {
+        let state = {
+            let read_guard = lock.read();
+            let rules = self.read_with(&read_guard);
+
+            // Step 1, 2
+            if index > rules.0.len() {
+                return Err(RulesMutateError::IndexSize);
+            }
+
+            // Computes the parser state at the given index
+            if nested {
+                None
+            } else if index == 0 {
+                Some(State::Start)
+            } else {
+                rules.0.get(index - 1).map(CssRule::rule_state)
+            }
+        };
+
+        // Step 3, 4
+        // XXXManishearth should we also store the namespace map?
+        let (new_rule, new_state) =
+            CssRule::parse(&rule, parent_stylesheet, state, loader)?;
+
+        {
+            let mut write_guard = lock.write();
+            let mut rules = self.write_with(&mut write_guard);
+            // Step 5
+            // Computes the maximum allowed parser state at a given index.
+            let rev_state = rules.0.get(index).map_or(State::Body, CssRule::rule_state);
+            if new_state > rev_state {
+                // We inserted a rule too early, e.g. inserting
+                // a regular style rule before @namespace rules
+                return Err(RulesMutateError::HierarchyRequest);
+            }
+
+            // Step 6
+            if let CssRule::Namespace(..) = new_rule {
+                if !rules.only_ns_or_import() {
+                    return Err(RulesMutateError::InvalidState);
+                }
+            }
+
+            rules.0.insert(index, new_rule.clone());
+        }
+
+        Ok(new_rule)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/rule_parser.rs
@@ -0,0 +1,520 @@
+/* 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/. */
+
+//! Parsing of the stylesheet contents.
+
+use {Namespace, Prefix};
+use counter_style::{parse_counter_style_body, parse_counter_style_name};
+use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListParser, SourceLocation};
+use font_face::parse_font_face_block;
+use media_queries::{parse_media_query_list, MediaList};
+use parking_lot::RwLock;
+use parser::{Parse, ParserContext, log_css_error};
+use properties::parse_property_declaration_list;
+use selector_parser::{SelectorImpl, SelectorParser};
+use selectors::SelectorList;
+use shared_lock::{Locked, SharedRwLock};
+use std::sync::atomic::AtomicBool;
+use str::starts_with_ignore_ascii_case;
+use stylearc::Arc;
+use stylesheets::{CssRule, CssRules, CssRuleType, Origin, StylesheetLoader};
+use stylesheets::{DocumentRule, ImportRule, KeyframesRule, MediaRule, NamespaceRule, PageRule};
+use stylesheets::{StyleRule, SupportsRule, ViewportRule};
+use stylesheets::document_rule::DocumentCondition;
+use stylesheets::keyframes_rule::parse_keyframe_list;
+use stylesheets::loader::NoOpLoader;
+use stylesheets::stylesheet::{Namespaces, Stylesheet};
+use stylesheets::supports_rule::SupportsCondition;
+use values::CustomIdent;
+use values::KeyframesName;
+use values::specified::url::SpecifiedUrl;
+
+/// The parser for the top-level rules in a stylesheet.
+pub struct TopLevelRuleParser<'a> {
+    /// The origin of the stylesheet we're parsing.
+    pub stylesheet_origin: Origin,
+    /// A reference to the lock we need to use to create rules.
+    pub shared_lock: &'a SharedRwLock,
+    /// A reference to a stylesheet loader if applicable, for `@import` rules.
+    pub loader: Option<&'a StylesheetLoader>,
+    /// The parser context. This initially won't contain any namespaces, but
+    /// will be populated after parsing namespace rules, if any.
+    pub context: ParserContext<'a>,
+    /// The current state of the parser.
+    pub state: State,
+    /// The namespace map we use for parsing. Needs to start as `Some()`, and
+    /// will be taken out after parsing namespace rules, and that reference will
+    /// be moved to `ParserContext`.
+    pub namespaces: Option<&'a mut Namespaces>,
+}
+
+impl<'b> TopLevelRuleParser<'b> {
+    fn nested<'a: 'b>(&'a self) -> NestedRuleParser<'a, 'b> {
+        NestedRuleParser {
+            stylesheet_origin: self.stylesheet_origin,
+            shared_lock: self.shared_lock,
+            context: &self.context,
+        }
+    }
+
+    /// Returns the associated parser context with this rule parser.
+    pub fn context(&self) -> &ParserContext {
+        &self.context
+    }
+
+    /// Returns the current state of the parser.
+    pub fn state(&self) -> State {
+        self.state
+    }
+}
+
+/// The current state of the parser.
+#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
+pub enum State {
+    /// We haven't started parsing rules.
+    Start = 1,
+    /// We're parsing `@import` rules.
+    Imports = 2,
+    /// We're parsing `@namespace` rules.
+    Namespaces = 3,
+    /// We're parsing the main body of the stylesheet.
+    Body = 4,
+    /// We've found an invalid state (as, a namespace rule after style rules),
+    /// and the rest of the stylesheet should be ignored.
+    Invalid = 5,
+}
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+/// Vendor prefix.
+pub enum VendorPrefix {
+    /// -moz prefix.
+    Moz,
+    /// -webkit prefix.
+    WebKit,
+}
+
+/// A rule prelude for a given at-rule.
+pub enum AtRulePrelude {
+    /// A @font-face rule prelude.
+    FontFace(SourceLocation),
+    /// A @counter-style rule prelude, with its counter style name.
+    CounterStyle(CustomIdent),
+    /// A @media rule prelude, with its media queries.
+    Media(Arc<Locked<MediaList>>, SourceLocation),
+    /// An @supports rule, with its conditional
+    Supports(SupportsCondition, SourceLocation),
+    /// A @viewport rule prelude.
+    Viewport,
+    /// A @keyframes rule, with its animation name and vendor prefix if exists.
+    Keyframes(KeyframesName, Option<VendorPrefix>, SourceLocation),
+    /// A @page rule prelude.
+    Page(SourceLocation),
+    /// A @document rule, with its conditional.
+    Document(DocumentCondition, SourceLocation),
+}
+
+
+#[cfg(feature = "gecko")]
+fn register_namespace(ns: &Namespace) -> Result<i32, ()> {
+    use gecko_bindings::bindings;
+    let id = unsafe { bindings::Gecko_RegisterNamespace(ns.0.as_ptr()) };
+    if id == -1 {
+        Err(())
+    } else {
+        Ok(id)
+    }
+}
+
+#[cfg(feature = "servo")]
+fn register_namespace(_: &Namespace) -> Result<(), ()> {
+    Ok(()) // servo doesn't use namespace ids
+}
+
+impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
+    type Prelude = AtRulePrelude;
+    type AtRule = CssRule;
+
+    fn parse_prelude(
+        &mut self,
+        name: &str,
+        input: &mut Parser
+    ) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
+        let location = get_location_with_offset(input.current_source_location(),
+                                                self.context.line_number_offset);
+        match_ignore_ascii_case! { name,
+            "import" => {
+                if self.state > State::Imports {
+                    self.state = State::Invalid;
+                    return Err(())  // "@import must be before any rule but @charset"
+                }
+
+                self.state = State::Imports;
+                let url_string = input.expect_url_or_string()?;
+                let specified_url = SpecifiedUrl::parse_from_string(url_string, &self.context)?;
+
+                let media = parse_media_query_list(&self.context, input);
+                let media = Arc::new(self.shared_lock.wrap(media));
+
+                let noop_loader = NoOpLoader;
+                let loader = if !specified_url.is_invalid() {
+                    self.loader.expect("Expected a stylesheet loader for @import")
+                } else {
+                    &noop_loader
+                };
+
+                let mut specified_url = Some(specified_url);
+                let arc = loader.request_stylesheet(media, &mut |media| {
+                    ImportRule {
+                        url: specified_url.take().unwrap(),
+                        stylesheet: Arc::new(Stylesheet {
+                            rules: CssRules::new(Vec::new(), self.shared_lock),
+                            media: media,
+                            shared_lock: self.shared_lock.clone(),
+                            origin: self.context.stylesheet_origin,
+                            url_data: self.context.url_data.clone(),
+                            namespaces: RwLock::new(Namespaces::default()),
+                            dirty_on_viewport_size_change: AtomicBool::new(false),
+                            disabled: AtomicBool::new(false),
+                            quirks_mode: self.context.quirks_mode,
+                        }),
+                        source_location: location,
+                    }
+                }, &mut |import_rule| {
+                    Arc::new(self.shared_lock.wrap(import_rule))
+                });
+
+                return Ok(AtRuleType::WithoutBlock(CssRule::Import(arc)))
+            },
+            "namespace" => {
+                if self.state > State::Namespaces {
+                    self.state = State::Invalid;
+                    return Err(())  // "@namespace must be before any rule but @charset and @import"
+                }
+                self.state = State::Namespaces;
+
+                let prefix_result = input.try(|input| input.expect_ident());
+                let url = Namespace::from(try!(input.expect_url_or_string()));
+
+                let id = register_namespace(&url)?;
+
+                let mut namespaces = self.namespaces.as_mut().unwrap();
+
+                let opt_prefix = if let Ok(prefix) = prefix_result {
+                    let prefix = Prefix::from(prefix);
+                    namespaces
+                        .prefixes
+                        .insert(prefix.clone(), (url.clone(), id));
+                    Some(prefix)
+                } else {
+                    namespaces.default = Some((url.clone(), id));
+                    None
+                };
+
+                return Ok(AtRuleType::WithoutBlock(CssRule::Namespace(Arc::new(
+                    self.shared_lock.wrap(NamespaceRule {
+                        prefix: opt_prefix,
+                        url: url,
+                        source_location: location,
+                    })
+                ))))
+            },
+            // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
+            // anything left is invalid.
+            "charset" => return Err(()), // (insert appropriate error message)
+            _ => {}
+        }
+        // Don't allow starting with an invalid state
+        if self.state > State::Body {
+            self.state = State::Invalid;
+            return Err(());
+        }
+        self.state = State::Body;
+
+        // "Freeze" the namespace map (no more namespace rules can be parsed
+        // after this point), and stick it in the context.
+        if self.namespaces.is_some() {
+            let namespaces = &*self.namespaces.take().unwrap();
+            self.context.namespaces = Some(namespaces);
+        }
+        AtRuleParser::parse_prelude(&mut self.nested(), name, input)
+    }
+
+    #[inline]
+    fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CssRule, ()> {
+        AtRuleParser::parse_block(&mut self.nested(), prelude, input)
+    }
+}
+
+
+impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
+    type Prelude = SelectorList<SelectorImpl>;
+    type QualifiedRule = CssRule;
+
+    #[inline]
+    fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
+        self.state = State::Body;
+
+        // "Freeze" the namespace map (no more namespace rules can be parsed
+        // after this point), and stick it in the context.
+        if self.namespaces.is_some() {
+            let namespaces = &*self.namespaces.take().unwrap();
+            self.context.namespaces = Some(namespaces);
+        }
+
+        QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
+    }
+
+    #[inline]
+    fn parse_block(
+        &mut self,
+        prelude: SelectorList<SelectorImpl>,
+        input: &mut Parser
+    ) -> Result<CssRule, ()> {
+        QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input)
+    }
+}
+
+#[derive(Clone)]  // shallow, relatively cheap .clone
+struct NestedRuleParser<'a, 'b: 'a> {
+    stylesheet_origin: Origin,
+    shared_lock: &'a SharedRwLock,
+    context: &'a ParserContext<'b>,
+}
+
+impl<'a, 'b> NestedRuleParser<'a, 'b> {
+    fn parse_nested_rules(
+        &mut self,
+        input: &mut Parser,
+        rule_type: CssRuleType
+    ) -> Arc<Locked<CssRules>> {
+        let context = ParserContext::new_with_rule_type(self.context, Some(rule_type));
+
+        let nested_parser = NestedRuleParser {
+            stylesheet_origin: self.stylesheet_origin,
+            shared_lock: self.shared_lock,
+            context: &context,
+        };
+
+        let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
+        let mut rules = Vec::new();
+        while let Some(result) = iter.next() {
+            match result {
+                Ok(rule) => rules.push(rule),
+                Err(range) => {
+                    let pos = range.start;
+                    let message = format!("Unsupported rule: '{}'", iter.input.slice(range));
+                    log_css_error(iter.input, pos, &*message, self.context);
+                }
+            }
+        }
+        CssRules::new(rules, self.shared_lock)
+    }
+}
+
+#[cfg(feature = "servo")]
+fn is_viewport_enabled() -> bool {
+    use servo_config::prefs::PREFS;
+    PREFS.get("layout.viewport.enabled").as_boolean().unwrap_or(false)
+}
+
+#[cfg(not(feature = "servo"))]
+fn is_viewport_enabled() -> bool {
+    false // Gecko doesn't support @viewport.
+}
+
+impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
+    type Prelude = AtRulePrelude;
+    type AtRule = CssRule;
+
+    fn parse_prelude(
+        &mut self,
+        name: &str,
+        input: &mut Parser
+    ) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
+        let location =
+            get_location_with_offset(
+                input.current_source_location(),
+                self.context.line_number_offset
+            );
+
+        match_ignore_ascii_case! { name,
+            "media" => {
+                let media_queries = parse_media_query_list(self.context, input);
+                let arc = Arc::new(self.shared_lock.wrap(media_queries));
+                Ok(AtRuleType::WithBlock(AtRulePrelude::Media(arc, location)))
+            },
+            "supports" => {
+                let cond = SupportsCondition::parse(input)?;
+                Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond, location)))
+            },
+            "font-face" => {
+                Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace(location)))
+            },
+            "counter-style" => {
+                if !cfg!(feature = "gecko") {
+                    // Support for this rule is not fully implemented in Servo yet.
+                    return Err(())
+                }
+                let name = parse_counter_style_name(input)?;
+                // ASCII-case-insensitive matches for "decimal" are already
+                // lower-cased by `parse_counter_style_name`, so we can use ==
+                // here.
+                if name.0 == atom!("decimal") {
+                    return Err(())
+                }
+                Ok(AtRuleType::WithBlock(AtRulePrelude::CounterStyle(name)))
+            },
+            "viewport" => {
+                if is_viewport_enabled() {
+                    Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
+                } else {
+                    Err(())
+                }
+            },
+            "keyframes" | "-webkit-keyframes" | "-moz-keyframes" => {
+                let prefix = if starts_with_ignore_ascii_case(name, "-webkit-") {
+                    Some(VendorPrefix::WebKit)
+                } else if starts_with_ignore_ascii_case(name, "-moz-") {
+                    Some(VendorPrefix::Moz)
+                } else {
+                    None
+                };
+                if cfg!(feature = "servo") &&
+                   prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {
+                    // Servo should not support @-moz-keyframes.
+                    return Err(())
+                }
+                let name = KeyframesName::parse(self.context, input)?;
+
+                Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix, location)))
+            },
+            "page" => {
+                if cfg!(feature = "gecko") {
+                    Ok(AtRuleType::WithBlock(AtRulePrelude::Page(location)))
+                } else {
+                    Err(())
+                }
+            },
+            "-moz-document" => {
+                if cfg!(feature = "gecko") {
+                    let cond = DocumentCondition::parse(self.context, input)?;
+                    Ok(AtRuleType::WithBlock(AtRulePrelude::Document(cond, location)))
+                } else {
+                    Err(())
+                }
+            },
+            _ => Err(())
+        }
+    }
+
+    fn parse_block(
+        &mut self,
+        prelude: AtRulePrelude,
+        input: &mut Parser
+    ) -> Result<CssRule, ()> {
+        match prelude {
+            AtRulePrelude::FontFace(location) => {
+                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::FontFace));
+                Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
+                   parse_font_face_block(&context, input, location).into()))))
+            }
+            AtRulePrelude::CounterStyle(name) => {
+                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::CounterStyle));
+                Ok(CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(
+                   parse_counter_style_body(name, &context, input)?.into()))))
+            }
+            AtRulePrelude::Media(media_queries, location) => {
+                Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
+                    media_queries: media_queries,
+                    rules: self.parse_nested_rules(input, CssRuleType::Media),
+                    source_location: location,
+                }))))
+            }
+            AtRulePrelude::Supports(cond, location) => {
+                let enabled = cond.eval(self.context);
+                Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(SupportsRule {
+                    condition: cond,
+                    rules: self.parse_nested_rules(input, CssRuleType::Supports),
+                    enabled: enabled,
+                    source_location: location,
+                }))))
+            }
+            AtRulePrelude::Viewport => {
+                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Viewport));
+                Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
+                   try!(ViewportRule::parse(&context, input))))))
+            }
+            AtRulePrelude::Keyframes(name, prefix, location) => {
+                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes));
+                Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule {
+                    name: name,
+                    keyframes: parse_keyframe_list(&context, input, self.shared_lock),
+                    vendor_prefix: prefix,
+                    source_location: location,
+                }))))
+            }
+            AtRulePrelude::Page(location) => {
+                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Page));
+                let declarations = parse_property_declaration_list(&context, input);
+                Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
+                    block: Arc::new(self.shared_lock.wrap(declarations)),
+                    source_location: location,
+                }))))
+            }
+            AtRulePrelude::Document(cond, location) => {
+                if cfg!(feature = "gecko") {
+                    Ok(CssRule::Document(Arc::new(self.shared_lock.wrap(DocumentRule {
+                        condition: cond,
+                        rules: self.parse_nested_rules(input, CssRuleType::Document),
+                        source_location: location,
+                    }))))
+                } else {
+                    unreachable!()
+                }
+            }
+        }
+    }
+}
+
+impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
+    type Prelude = SelectorList<SelectorImpl>;
+    type QualifiedRule = CssRule;
+
+    fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
+        let selector_parser = SelectorParser {
+            stylesheet_origin: self.stylesheet_origin,
+            namespaces: self.context.namespaces.unwrap(),
+        };
+
+        SelectorList::parse(&selector_parser, input)
+    }
+
+    fn parse_block(
+        &mut self,
+        prelude: SelectorList<SelectorImpl>,
+        input: &mut Parser
+    ) -> Result<CssRule, ()> {
+        let location = get_location_with_offset(input.current_source_location(),
+                                                self.context.line_number_offset);
+        let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Style));
+        let declarations = parse_property_declaration_list(&context, input);
+        Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
+            selectors: prelude,
+            block: Arc::new(self.shared_lock.wrap(declarations)),
+            source_location: location,
+        }))))
+    }
+}
+
+/// Calculates the location of a rule's source given an offset.
+fn get_location_with_offset(
+    location: SourceLocation,
+    offset: u64
+) -> SourceLocation {
+    SourceLocation {
+        line: location.line + offset as usize - 1,
+        column: location.column,
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/rules_iterator.rs
@@ -0,0 +1,273 @@
+/* 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/. */
+
+//! An iterator over a list of rules.
+
+use context::QuirksMode;
+use media_queries::Device;
+use shared_lock::SharedRwLockReadGuard;
+use smallvec::SmallVec;
+use std::slice;
+use stylesheets::{CssRule, CssRules, DocumentRule, ImportRule, MediaRule, SupportsRule};
+
+/// An iterator over a list of rules.
+pub struct RulesIterator<'a, 'b, C>
+    where 'b: 'a,
+          C: NestedRuleIterationCondition + 'static,
+{
+    device: &'a Device,
+    quirks_mode: QuirksMode,
+    guard: &'a SharedRwLockReadGuard<'b>,
+    stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,
+    _phantom: ::std::marker::PhantomData<C>,
+}
+
+impl<'a, 'b, C> RulesIterator<'a, 'b, C>
+    where 'b: 'a,
+          C: NestedRuleIterationCondition + 'static,
+{
+    /// Creates a new `RulesIterator` to iterate over `rules`.
+    pub fn new(
+        device: &'a Device,
+        quirks_mode: QuirksMode,
+        guard: &'a SharedRwLockReadGuard<'b>,
+        rules: &'a CssRules)
+        -> Self
+    {
+        let mut stack = SmallVec::new();
+        stack.push(rules.0.iter());
+        Self {
+            device: device,
+            quirks_mode: quirks_mode,
+            guard: guard,
+            stack: stack,
+            _phantom: ::std::marker::PhantomData,
+        }
+    }
+
+    /// Skips all the remaining children of the last nested rule processed.
+    pub fn skip_children(&mut self) {
+        self.stack.pop();
+    }
+}
+
+impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
+    where 'b: 'a,
+          C: NestedRuleIterationCondition + 'static,
+{
+    type Item = &'a CssRule;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let mut nested_iter_finished = false;
+        while !self.stack.is_empty() {
+            if nested_iter_finished {
+                self.stack.pop();
+                nested_iter_finished = false;
+                continue;
+            }
+
+            let rule;
+            let sub_iter;
+            {
+                let mut nested_iter = self.stack.last_mut().unwrap();
+                rule = match nested_iter.next() {
+                    Some(r) => r,
+                    None => {
+                        nested_iter_finished = true;
+                        continue
+                    }
+                };
+
+                sub_iter = match *rule {
+                    CssRule::Import(ref import_rule) => {
+                        let import_rule = import_rule.read_with(self.guard);
+                        if !C::process_import(self.guard,
+                                              self.device,
+                                              self.quirks_mode,
+                                              import_rule) {
+                            continue;
+                        }
+                        Some(import_rule.stylesheet.rules.read_with(self.guard).0.iter())
+                    }
+                    CssRule::Document(ref doc_rule) => {
+                        let doc_rule = doc_rule.read_with(self.guard);
+                        if !C::process_document(self.guard,
+                                                self.device,
+                                                self.quirks_mode,
+                                                doc_rule) {
+                            continue;
+                        }
+                        Some(doc_rule.rules.read_with(self.guard).0.iter())
+                    }
+                    CssRule::Media(ref lock) => {
+                        let media_rule = lock.read_with(self.guard);
+                        if !C::process_media(self.guard,
+                                             self.device,
+                                             self.quirks_mode,
+                                             media_rule) {
+                            continue;
+                        }
+                        Some(media_rule.rules.read_with(self.guard).0.iter())
+                    }
+                    CssRule::Supports(ref lock) => {
+                        let supports_rule = lock.read_with(self.guard);
+                        if !C::process_supports(self.guard,
+                                                self.device,
+                                                self.quirks_mode,
+                                                supports_rule) {
+                            continue;
+                        }
+                        Some(supports_rule.rules.read_with(self.guard).0.iter())
+                    }
+                    CssRule::Namespace(_) |
+                    CssRule::Style(_) |
+                    CssRule::FontFace(_) |
+                    CssRule::CounterStyle(_) |
+                    CssRule::Viewport(_) |
+                    CssRule::Keyframes(_) |
+                    CssRule::Page(_) => None,
+                };
+            }
+
+            if let Some(sub_iter) = sub_iter {
+                self.stack.push(sub_iter);
+            }
+
+            return Some(rule);
+        }
+
+        None
+    }
+}
+
+/// RulesIterator.
+pub trait NestedRuleIterationCondition {
+    /// Whether we should process the nested rules in a given `@import` rule.
+    fn process_import(
+        guard: &SharedRwLockReadGuard,
+        device: &Device,
+        quirks_mode: QuirksMode,
+        rule: &ImportRule)
+        -> bool;
+
+    /// Whether we should process the nested rules in a given `@media` rule.
+    fn process_media(
+        guard: &SharedRwLockReadGuard,
+        device: &Device,
+        quirks_mode: QuirksMode,
+        rule: &MediaRule)
+        -> bool;
+
+    /// Whether we should process the nested rules in a given `@-moz-document`
+    /// rule.
+    fn process_document(
+        guard: &SharedRwLockReadGuard,
+        device: &Device,
+        quirks_mode: QuirksMode,
+        rule: &DocumentRule)
+        -> bool;
+
+    /// Whether we should process the nested rules in a given `@supports` rule.
+    fn process_supports(
+        guard: &SharedRwLockReadGuard,
+        device: &Device,
+        quirks_mode: QuirksMode,
+        rule: &SupportsRule)
+        -> bool;
+}
+
+/// A struct that represents the condition that a rule applies to the document.
+pub struct EffectiveRules;
+
+impl NestedRuleIterationCondition for EffectiveRules {
+    fn process_import(
+        guard: &SharedRwLockReadGuard,
+        device: &Device,
+        quirks_mode: QuirksMode,
+        rule: &ImportRule)
+        -> bool
+    {
+        rule.stylesheet.media.read_with(guard).evaluate(device, quirks_mode)
+    }
+
+    fn process_media(
+        guard: &SharedRwLockReadGuard,
+        device: &Device,
+        quirks_mode: QuirksMode,
+        rule: &MediaRule)
+        -> bool
+    {
+        rule.media_queries.read_with(guard).evaluate(device, quirks_mode)
+    }
+
+    fn process_document(
+        _: &SharedRwLockReadGuard,
+        device: &Device,
+        _: QuirksMode,
+        rule: &DocumentRule)
+        -> bool
+    {
+        rule.condition.evaluate(device)
+    }
+
+    fn process_supports(
+        _: &SharedRwLockReadGuard,
+        _: &Device,
+        _: QuirksMode,
+        rule: &SupportsRule)
+        -> bool
+    {
+        rule.enabled
+    }
+}
+
+/// A filter that processes all the rules in a rule list.
+pub struct AllRules;
+
+impl NestedRuleIterationCondition for AllRules {
+    fn process_import(
+        _: &SharedRwLockReadGuard,
+        _: &Device,
+        _: QuirksMode,
+        _: &ImportRule)
+        -> bool
+    {
+        true
+    }
+
+    fn process_media(
+        _: &SharedRwLockReadGuard,
+        _: &Device,
+        _: QuirksMode,
+        _: &MediaRule)
+        -> bool
+    {
+        true
+    }
+
+    fn process_document(
+        _: &SharedRwLockReadGuard,
+        _: &Device,
+        _: QuirksMode,
+        _: &DocumentRule)
+        -> bool
+    {
+        true
+    }
+
+    fn process_supports(
+        _: &SharedRwLockReadGuard,
+        _: &Device,
+        _: QuirksMode,
+        _: &SupportsRule)
+        -> bool
+    {
+        true
+    }
+}
+
+/// An iterator over all the effective rules of a stylesheet.
+///
+/// NOTE: This iterator recurses into `@import` rules.
+pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/style_rule.rs
@@ -0,0 +1,76 @@
+/* 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/. */
+
+//! A style rule.
+
+use cssparser::SourceLocation;
+use properties::PropertyDeclarationBlock;
+use selector_parser::SelectorImpl;
+use selectors::SelectorList;
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use style_traits::ToCss;
+use stylearc::Arc;
+use stylesheets::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard};
+
+/// A style rule, with selectors and declarations.
+#[derive(Debug)]
+pub struct StyleRule {
+    /// The list of selectors in this rule.
+    pub selectors: SelectorList<SelectorImpl>,
+    /// The declaration block with the properties it contains.
+    pub block: Arc<Locked<PropertyDeclarationBlock>>,
+    /// The location in the sheet where it was found.
+    pub source_location: SourceLocation,
+}
+
+impl DeepCloneWithLock for StyleRule {
+    /// Deep clones this StyleRule.
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard,
+    ) -> StyleRule {
+        StyleRule {
+            selectors: self.selectors.clone(),
+            block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
+            source_location: self.source_location.clone(),
+        }
+    }
+}
+
+impl MallocSizeOfWithGuard for StyleRule {
+    fn malloc_size_of_children(
+        &self,
+        guard: &SharedRwLockReadGuard,
+        malloc_size_of: MallocSizeOfFn
+    ) -> usize {
+        // Measurement of other fields may be added later.
+        self.block.read_with(guard).malloc_size_of_children(malloc_size_of)
+    }
+}
+
+impl ToCssWithGuard for StyleRule {
+    /// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule
+    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
+        use cssparser::ToCss;
+
+        // Step 1
+        self.selectors.to_css(dest)?;
+        // Step 2
+        dest.write_str(" { ")?;
+        // Step 3
+        let declaration_block = self.block.read_with(guard);
+        declaration_block.to_css(dest)?;
+        // Step 4
+        if !declaration_block.declarations().is_empty() {
+            dest.write_str(" ")?;
+        }
+        // Step 5
+        dest.write_str("}")
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -0,0 +1,342 @@
+/* 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 {Prefix, Namespace};
+use context::QuirksMode;
+use cssparser::{Parser, RuleListParser};
+use error_reporting::ParseErrorReporter;
+use fnv::FnvHashMap;
+use media_queries::{MediaList, Device};
+use parking_lot::RwLock;
+use parser::{PARSING_MODE_DEFAULT, ParserContext, log_css_error};
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard};
+use std::mem;
+use std::sync::atomic::{AtomicBool, Ordering};
+use stylearc::Arc;
+use stylesheets::{CssRule, CssRules, Origin, UrlExtraData};
+use stylesheets::loader::StylesheetLoader;
+use stylesheets::memory::{MallocSizeOfFn, MallocSizeOfWithGuard};
+use stylesheets::rule_parser::{State, TopLevelRuleParser};
+use stylesheets::rules_iterator::{EffectiveRules, EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator};
+use values::specified::NamespaceId;
+
+/// This structure holds the user-agent and user stylesheets.
+pub struct UserAgentStylesheets {
+    /// The lock used for user-agent stylesheets.
+    pub shared_lock: SharedRwLock,
+    /// The user or user agent stylesheets.
+    pub user_or_user_agent_stylesheets: Vec<Stylesheet>,
+    /// The quirks mode stylesheet.
+    pub quirks_mode_stylesheet: Stylesheet,
+}
+
+/// A set of namespaces applying to a given stylesheet.
+///
+/// The namespace id is used in gecko
+#[derive(Clone, Default, Debug)]
+#[allow(missing_docs)]
+pub struct Namespaces {
+    pub default: Option<(Namespace, NamespaceId)>,
+    pub prefixes: FnvHashMap<Prefix, (Namespace, NamespaceId)>,
+}
+
+/// The structure servo uses to represent a stylesheet.
+#[derive(Debug)]
+pub struct Stylesheet {
+    /// List of rules in the order they were found (important for
+    /// cascading order)
+    pub rules: Arc<Locked<CssRules>>,
+    /// List of media associated with the Stylesheet.
+    pub media: Arc<Locked<MediaList>>,
+    /// The origin of this stylesheet.
+    pub origin: Origin,
+    /// The url data this stylesheet should use.
+    pub url_data: UrlExtraData,
+    /// The lock used for objects inside this stylesheet
+    pub shared_lock: SharedRwLock,
+    /// The namespaces that apply to this stylesheet.
+    pub namespaces: RwLock<Namespaces>,
+    /// Whether this stylesheet would be dirty when the viewport size changes.
+    pub dirty_on_viewport_size_change: AtomicBool,
+    /// Whether this stylesheet should be disabled.
+    pub disabled: AtomicBool,
+    /// The quirks mode of this stylesheet.
+    pub quirks_mode: QuirksMode,
+}
+
+impl Stylesheet {
+    /// Updates an empty stylesheet from a given string of text.
+    pub fn update_from_str(existing: &Stylesheet,
+                           css: &str,
+                           url_data: &UrlExtraData,
+                           stylesheet_loader: Option<&StylesheetLoader>,
+                           error_reporter: &ParseErrorReporter,
+                           line_number_offset: u64) {
+        let namespaces = RwLock::new(Namespaces::default());
+        // FIXME: we really should update existing.url_data with the given url_data,
+        // otherwise newly inserted rule may not have the right base url.
+        let (rules, dirty_on_viewport_size_change) =
+            Stylesheet::parse_rules(
+                css,
+                url_data,
+                existing.origin,
+                &mut *namespaces.write(),
+                &existing.shared_lock,
+                stylesheet_loader,
+                error_reporter,
+                existing.quirks_mode,
+                line_number_offset
+            );
+
+        mem::swap(&mut *existing.namespaces.write(), &mut *namespaces.write());
+        existing.dirty_on_viewport_size_change
+            .store(dirty_on_viewport_size_change, Ordering::Release);
+
+        // Acquire the lock *after* parsing, to minimize the exclusive section.
+        let mut guard = existing.shared_lock.write();
+        *existing.rules.write_with(&mut guard) = CssRules(rules);
+    }
+
+    fn parse_rules(
+        css: &str,
+        url_data: &UrlExtraData,
+        origin: Origin,
+        namespaces: &mut Namespaces,
+        shared_lock: &SharedRwLock,
+        stylesheet_loader: Option<&StylesheetLoader>,
+        error_reporter: &ParseErrorReporter,
+        quirks_mode: QuirksMode,
+        line_number_offset: u64
+    ) -> (Vec<CssRule>, bool) {
+        let mut rules = Vec::new();
+        let mut input = Parser::new(css);
+
+        let context =
+            ParserContext::new_with_line_number_offset(
+                origin,
+                url_data,
+                error_reporter,
+                line_number_offset,
+                PARSING_MODE_DEFAULT,
+                quirks_mode
+            );
+
+        let rule_parser = TopLevelRuleParser {
+            stylesheet_origin: origin,
+            shared_lock: shared_lock,
+            loader: stylesheet_loader,
+            context: context,
+            state: State::Start,
+            namespaces: Some(namespaces),
+        };
+
+        input.look_for_viewport_percentages();
+
+        {
+            let mut iter =
+                RuleListParser::new_for_stylesheet(&mut input, rule_parser);
+
+            while let Some(result) = iter.next() {
+                match result {
+                    Ok(rule) => rules.push(rule),
+                    Err(range) => {
+                        let pos = range.start;
+                        let message = format!("Invalid rule: '{}'", iter.input.slice(range));
+                        log_css_error(iter.input, pos, &*message, iter.parser.context());
+                    }
+                }
+            }
+        }
+
+        (rules, input.seen_viewport_percentages())
+    }
+
+    /// Creates an empty stylesheet and parses it with a given base url, origin
+    /// and media.
+    ///
+    /// Effectively creates a new stylesheet and forwards the hard work to
+    /// `Stylesheet::update_from_str`.
+    pub fn from_str(css: &str,
+                    url_data: UrlExtraData,
+                    origin: Origin,
+                    media: Arc<Locked<MediaList>>,
+                    shared_lock: SharedRwLock,
+                    stylesheet_loader: Option<&StylesheetLoader>,
+                    error_reporter: &ParseErrorReporter,
+                    quirks_mode: QuirksMode,
+                    line_number_offset: u64)
+                    -> Stylesheet {
+        let namespaces = RwLock::new(Namespaces::default());
+        let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
+            css,
+            &url_data,
+            origin,
+            &mut *namespaces.write(),
+            &shared_lock,
+            stylesheet_loader,
+            error_reporter,
+            quirks_mode,
+            line_number_offset,
+        );
+
+        Stylesheet {
+            origin: origin,
+            url_data: url_data,
+            namespaces: namespaces,
+            rules: CssRules::new(rules, &shared_lock),
+            media: media,
+            shared_lock: shared_lock,
+            dirty_on_viewport_size_change: AtomicBool::new(dirty_on_viewport_size_change),
+            disabled: AtomicBool::new(false),
+            quirks_mode: quirks_mode,
+        }
+    }
+
+    /// Whether this stylesheet can be dirty on viewport size change.
+    pub fn dirty_on_viewport_size_change(&self) -> bool {
+        self.dirty_on_viewport_size_change.load(Ordering::SeqCst)
+    }
+
+    /// When CSSOM inserts a rule or declaration into this stylesheet, it needs to call this method
+    /// with the return value of `cssparser::Parser::seen_viewport_percentages`.
+    ///
+    /// FIXME: actually make these calls
+    ///
+    /// Note: when *removing* a rule or declaration that contains a viewport percentage,
+    /// to keep the flag accurate we’d need to iterator through the rest of the stylesheet to
+    /// check for *other* such values.
+    ///
+    /// Instead, we conservatively assume there might be some.
+    /// Restyling will some some more work than necessary, but give correct results.
+    pub fn inserted_has_viewport_percentages(&self, has_viewport_percentages: bool) {
+        self.dirty_on_viewport_size_change.fetch_or(has_viewport_percentages, Ordering::SeqCst);
+    }
+
+    /// Returns whether the style-sheet applies for the current device depending
+    /// on the associated MediaList.
+    ///
+    /// Always true if no associated MediaList exists.
+    pub fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
+        self.media.read_with(guard).evaluate(device, self.quirks_mode)
+    }
+
+    /// Return an iterator over the effective rules within the style-sheet, as
+    /// according to the supplied `Device`.
+    #[inline]
+    pub fn effective_rules<'a, 'b>(
+        &'a self,
+        device: &'a Device,
+        guard: &'a SharedRwLockReadGuard<'b>)
+        -> EffectiveRulesIterator<'a, 'b>
+    {
+        self.iter_rules::<'a, 'b, EffectiveRules>(device, guard)
+    }
+
+    /// Return an iterator using the condition `C`.
+    #[inline]
+    pub fn iter_rules<'a, 'b, C>(
+        &'a self,
+        device: &'a Device,
+        guard: &'a SharedRwLockReadGuard<'b>)
+        -> RulesIterator<'a, 'b, C>
+        where C: NestedRuleIterationCondition,
+    {
+        RulesIterator::new(
+            device,
+            self.quirks_mode,
+            guard,
+            &self.rules.read_with(guard))
+    }
+
+    /// Returns whether the stylesheet has been explicitly disabled through the
+    /// CSSOM.
+    pub fn disabled(&self) -> bool {
+        self.disabled.load(Ordering::SeqCst)
+    }
+
+    /// Records that the stylesheet has been explicitly disabled through the
+    /// CSSOM.
+    ///
+    /// Returns whether the the call resulted in a change in disabled state.
+    ///
+    /// Disabled stylesheets remain in the document, but their rules are not
+    /// added to the Stylist.
+    pub fn set_disabled(&self, disabled: bool) -> bool {
+        self.disabled.swap(disabled, Ordering::SeqCst) != disabled
+    }
+}
+
+impl Clone for Stylesheet {
+    fn clone(&self) -> Stylesheet {
+        // Create a new lock for our clone.
+        let lock = self.shared_lock.clone();
+        let guard = self.shared_lock.read();
+
+        // Make a deep clone of the rules, using the new lock.
+        let rules = self.rules.read_with(&guard);
+        let cloned_rules = rules.deep_clone_with_lock(&lock, &guard);
+
+        // Make a deep clone of the media, using the new lock.
+        let media = self.media.read_with(&guard);
+        let cloned_media = media.clone();
+
+        Stylesheet {
+            rules: Arc::new(lock.wrap(cloned_rules)),
+            media: Arc::new(lock.wrap(cloned_media)),
+            origin: self.origin,
+            url_data: self.url_data.clone(),
+            shared_lock: lock,
+            namespaces: RwLock::new((*self.namespaces.read()).clone()),
+            dirty_on_viewport_size_change: AtomicBool::new(
+                self.dirty_on_viewport_size_change.load(Ordering::SeqCst)),
+            disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
+            quirks_mode: self.quirks_mode,
+        }
+    }
+}
+
+impl MallocSizeOfWithGuard for Stylesheet {
+    fn malloc_size_of_children(
+        &self,
+        guard: &SharedRwLockReadGuard,
+        malloc_size_of: MallocSizeOfFn
+    ) -> usize {
+        // Measurement of other fields may be added later.
+        self.rules.read_with(guard).malloc_size_of_children(guard, malloc_size_of)
+    }
+}
+
+macro_rules! rule_filter {
+    ($( $method: ident($variant:ident => $rule_type: ident), )+) => {
+        impl Stylesheet {
+            $(
+                #[allow(missing_docs)]
+                pub fn $method<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
+                    where F: FnMut(&::stylesheets::$rule_type),
+                {
+                    use stylesheets::CssRule;
+
+                    for rule in self.effective_rules(device, guard) {
+                        if let CssRule::$variant(ref lock) = *rule {
+                            let rule = lock.read_with(guard);
+                            f(&rule)
+                        }
+                    }
+                }
+            )+
+        }
+    }
+}
+
+rule_filter! {
+    effective_style_rules(Style => StyleRule),
+    effective_media_rules(Media => MediaRule),
+    effective_font_face_rules(FontFace => FontFaceRule),
+    effective_counter_style_rules(CounterStyle => CounterStyleRule),
+    effective_viewport_rules(Viewport => ViewportRule),
+    effective_keyframes_rules(Keyframes => KeyframesRule),
+    effective_supports_rules(Supports => SupportsRule),
+    effective_page_rules(Page => PageRule),
+    effective_document_rules(Document => DocumentRule),
+}
rename from servo/components/style/supports.rs
rename to servo/components/style/stylesheets/supports_rule.rs
--- a/servo/components/style/supports.rs
+++ b/servo/components/style/stylesheets/supports_rule.rs
@@ -1,25 +1,72 @@
 /* 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/. */
 
 //! [@supports rules](https://drafts.csswg.org/css-conditional-3/#at-supports)
 
-use cssparser::{parse_important, Parser, Token};
+use cssparser::{parse_important, Parser, SourceLocation, Token};
 use parser::ParserContext;
 use properties::{PropertyId, PropertyDeclaration, SourcePropertyDeclaration};
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt;
 use style_traits::ToCss;
-use stylesheets::CssRuleType;
+use stylearc::Arc;
+use stylesheets::{CssRuleType, CssRules};
+
+/// An [`@supports`][supports] rule.
+///
+/// [supports]: https://drafts.csswg.org/css-conditional-3/#at-supports
+#[derive(Debug)]
+pub struct SupportsRule {
+    /// The parsed condition
+    pub condition: SupportsCondition,
+    /// Child rules
+    pub rules: Arc<Locked<CssRules>>,
+    /// The result of evaluating the condition
+    pub enabled: bool,
+    /// The line and column of the rule's source code.
+    pub source_location: SourceLocation,
+}
 
-#[derive(Clone, Debug)]
+impl ToCssWithGuard for SupportsRule {
+    fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+    where W: fmt::Write {
+        dest.write_str("@supports ")?;
+        self.condition.to_css(dest)?;
+        dest.write_str(" {")?;
+        for rule in self.rules.read_with(guard).0.iter() {
+            dest.write_str(" ")?;
+            rule.to_css(guard, dest)?;
+        }
+        dest.write_str(" }")
+    }
+}
+
+impl DeepCloneWithLock for SupportsRule {
+    fn deep_clone_with_lock(
+        &self,
+        lock: &SharedRwLock,
+        guard: &SharedRwLockReadGuard
+    ) -> Self {
+        let rules = self.rules.read_with(guard);
+        SupportsRule {
+            condition: self.condition.clone(),
+            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
+            enabled: self.enabled,
+            source_location: self.source_location.clone(),
+        }
+    }
+}
+
 /// An @supports condition
 ///
 /// https://drafts.csswg.org/css-conditional-3/#at-supports
+#[derive(Clone, Debug)]
 pub enum SupportsCondition {
     /// `not (condition)`
     Not(Box<SupportsCondition>),
     /// `(condition)`
     Parenthesized(Box<SupportsCondition>),
     /// `(condition) and (condition) and (condition) ..`
     And(Vec<SupportsCondition>),
     /// `(condition) or (condition) or (condition) ..`
@@ -114,17 +161,18 @@ pub fn parse_condition_or_declaration(in
         Ok(SupportsCondition::Parenthesized(Box::new(condition)))
     } else {
         Declaration::parse(input).map(SupportsCondition::Declaration)
     }
 }
 
 impl ToCss for SupportsCondition {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
-        where W: fmt::Write {
+        where W: fmt::Write,
+    {
         match *self {
             SupportsCondition::Not(ref cond) => {
                 dest.write_str("not ")?;
                 cond.to_css(dest)
             }
             SupportsCondition::Parenthesized(ref cond) => {
                 dest.write_str("(")?;
                 cond.to_css(dest)?;
@@ -168,32 +216,33 @@ pub struct Declaration {
     /// The property name
     pub prop: String,
     /// The property value
     pub val: String,
 }
 
 impl ToCss for Declaration {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
-        where W: fmt::Write {
+        where W: fmt::Write
+    {
         dest.write_str(&self.prop)?;
         dest.write_str(":")?;
         // no space, the `val` already contains any possible spaces
         dest.write_str(&self.val)
     }
 }
 
 /// Slurps up input till exhausted, return string from source position
 fn parse_anything(input: &mut Parser) -> String {
     let pos = input.position();
     consume_all(input);
     input.slice_from(pos).to_owned()
 }
 
-/// consume input till done
+/// Consume input till done
 fn consume_all(input: &mut Parser) {
     while let Ok(_) = input.next() {}
 }
 
 impl Declaration {
     /// Parse a declaration
     pub fn parse(input: &mut Parser) -> Result<Declaration, ()> {
         let prop = input.expect_ident()?.into_owned();
rename from servo/components/style/viewport.rs
rename to servo/components/style/stylesheets/viewport_rule.rs
--- a/servo/components/style/viewport.rs
+++ b/servo/components/style/stylesheets/viewport_rule.rs
@@ -2,18 +2,16 @@
  * 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/. */
 
 //! The [`@viewport`][at] at-rule and [`meta`][meta] element.
 //!
 //! [at]: https://drafts.csswg.org/css-device-adapt/#atviewport-rule
 //! [meta]: https://drafts.csswg.org/css-device-adapt/#viewport-meta
 
-#![deny(missing_docs)]
-
 use app_units::Au;
 use context::QuirksMode;
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser, parse_important};
 use cssparser::ToCss as ParserToCss;
 use euclid::size::TypedSize2D;
 use font_metrics::get_metrics_provider_for_product;
 use media_queries::Device;
 use parser::{Parse, ParserContext, log_css_error};
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -10,17 +10,16 @@ use context::{QuirksMode, SharedStyleCon
 use data::ComputedStyle;
 use dom::TElement;
 use element_state::ElementState;
 use error_reporting::RustLogReporter;
 use font_metrics::FontMetricsProvider;
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
 use invalidation::media_queries::EffectiveMediaQueryResults;
-use keyframes::KeyframesAnimation;
 use media_queries::Device;
 use properties::{self, CascadeFlags, ComputedValues};
 use properties::{AnimationRules, PropertyDeclarationBlock};
 #[cfg(feature = "servo")]
 use properties::INHERIT_ALL;
 use restyle_hints::{HintComputationContext, DependencySet, RestyleHint};
 use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
 use selector_map::{SelectorMap, SelectorMapEntry};
@@ -37,18 +36,19 @@ use smallvec::{SmallVec, VecLike};
 #[cfg(feature = "servo")]
 use std::marker::PhantomData;
 use style_traits::viewport::ViewportConstraints;
 use stylearc::Arc;
 #[cfg(feature = "gecko")]
 use stylesheets::{CounterStyleRule, FontFaceRule};
 use stylesheets::{CssRule, StyleRule};
 use stylesheets::{Stylesheet, Origin, UserAgentStylesheets};
+use stylesheets::keyframes_rule::KeyframesAnimation;
+use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
 use thread_state;
-use viewport::{self, MaybeNew, ViewportRule};
 
 pub use ::fnv::FnvHashMap;
 
 /// List of applicable declaration. This is a transient structure that shuttles
 /// declarations between selector matching and inserting into the rule tree, and
 /// therefore we want to avoid heap-allocation where possible.
 ///
 /// In measurements on wikipedia, we pretty much never have more than 8 applicable
@@ -351,17 +351,17 @@ impl Stylist {
 
         if !(self.is_device_dirty || stylesheets_changed) {
             return false;
         }
 
         self.num_rebuilds += 1;
 
         let cascaded_rule = ViewportRule {
-            declarations: viewport::Cascade::from_stylesheets(
+            declarations: viewport_rule::Cascade::from_stylesheets(
                 doc_stylesheets.clone(), guards.author, &self.device
             ).finish(),
         };
 
         self.viewport_constraints =
             ViewportConstraints::maybe_new(&self.device, &cascaded_rule, self.quirks_mode);
 
         if let Some(ref constraints) = self.viewport_constraints {
@@ -764,17 +764,17 @@ impl Stylist {
     /// FIXME(emilio): The semantics of the device for Servo and Gecko are
     /// different enough we may want to unify them.
     #[cfg(feature = "servo")]
     pub fn set_device(&mut self,
                       mut device: Device,
                       guard: &SharedRwLockReadGuard,
                       stylesheets: &[Arc<Stylesheet>]) {
         let cascaded_rule = ViewportRule {
-            declarations: viewport::Cascade::from_stylesheets(stylesheets.iter(), guard, &device).finish(),
+            declarations: viewport_rule::Cascade::from_stylesheets(stylesheets.iter(), guard, &device).finish(),
         };
 
         self.viewport_constraints =
             ViewportConstraints::maybe_new(&device, &cascaded_rule, self.quirks_mode);
 
         if let Some(ref constraints) = self.viewport_constraints {
             device.account_for_viewport_rule(constraints);
         }
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -618,19 +618,19 @@ impl LengthOrNumber {
     }
 }
 
 /// Either a computed `<length>` or the `normal` keyword.
 pub type LengthOrNormal = Either<Length, Normal>;
 
 /// A value suitable for a `min-width`, `min-height`, `width` or `height` property.
 /// See specified/values/length.rs for more details.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum MozLength {
     LengthOrPercentageOrAuto(LengthOrPercentageOrAuto),
     ExtremumLength(ExtremumLength),
 }
 
 impl MozLength {
     /// Returns the `auto` value.
     pub fn auto() -> Self {
@@ -660,32 +660,21 @@ impl ToComputedValue for specified::MozL
                 specified::MozLength::LengthOrPercentageOrAuto(
                     specified::LengthOrPercentageOrAuto::from_computed_value(&lopoa)),
             MozLength::ExtremumLength(ref ext) =>
                 specified::MozLength::ExtremumLength(ext.clone()),
         }
     }
 }
 
-impl ToCss for MozLength {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            MozLength::LengthOrPercentageOrAuto(lopoa) =>
-                lopoa.to_css(dest),
-            MozLength::ExtremumLength(ext) =>
-                ext.to_css(dest),
-        }
-    }
-}
-
 /// A value suitable for a `max-width` or `max-height` property.
 /// See specified/values/length.rs for more details.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum MaxLength {
     LengthOrPercentageOrNone(LengthOrPercentageOrNone),
     ExtremumLength(ExtremumLength),
 }
 
 impl MaxLength {
     /// Returns the `none` value.
     pub fn none() -> Self {
@@ -713,19 +702,8 @@ impl ToComputedValue for specified::MaxL
             MaxLength::LengthOrPercentageOrNone(ref lopon) =>
                 specified::MaxLength::LengthOrPercentageOrNone(
                     specified::LengthOrPercentageOrNone::from_computed_value(&lopon)),
             MaxLength::ExtremumLength(ref ext) =>
                 specified::MaxLength::ExtremumLength(ext.clone()),
         }
     }
 }
-
-impl ToCss for MaxLength {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            MaxLength::LengthOrPercentageOrNone(lopon) =>
-                lopon.to_css(dest),
-            MaxLength::ExtremumLength(ext) =>
-                ext.to_css(dest),
-        }
-    }
-}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -476,19 +476,19 @@ pub struct Shadow {
     pub spread_radius: Au,
     pub color: CSSColor,
     pub inset: bool,
 }
 
 /// A `<number>` value.
 pub type Number = CSSFloat;
 
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum NumberOrPercentage {
     Percentage(Percentage),
     Number(Number),
 }
 
 impl ToComputedValue for specified::NumberOrPercentage {
     type ComputedValue = NumberOrPercentage;
 
@@ -507,25 +507,16 @@ impl ToComputedValue for specified::Numb
             NumberOrPercentage::Percentage(percentage) =>
                 specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value(&percentage)),
             NumberOrPercentage::Number(number) =>
                 specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number)),
         }
     }
 }
 
-impl ToCss for NumberOrPercentage {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            NumberOrPercentage::Percentage(percentage) => percentage.to_css(dest),
-            NumberOrPercentage::Number(number) => number.to_css(dest),
-        }
-    }
-}
-
 /// A type used for opacity.
 pub type Opacity = CSSFloat;
 
 /// A `<integer>` value.
 pub type Integer = CSSInteger;
 
 /// <integer> | auto
 pub type IntegerOrAuto = Either<CSSInteger, Auto>;
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -48,17 +48,17 @@ pub enum ShapeSource<BasicShape, Referen
     Url(SpecifiedUrl),
     Shape(BasicShape, Option<ReferenceBox>),
     Box(ReferenceBox),
     None,
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Debug, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
 pub enum BasicShape<H, V, LengthOrPercentage> {
     Inset(InsetRect<LengthOrPercentage>),
     Circle(Circle<H, V, LengthOrPercentage>),
     Ellipse(Ellipse<H, V, LengthOrPercentage>),
     Polygon(Polygon<LengthOrPercentage>),
 }
 
 /// https://drafts.csswg.org/css-shapes/#funcdef-inset
@@ -148,33 +148,16 @@ impl ToCss for GeometryBox {
             GeometryBox::FillBox => dest.write_str("fill-box"),
             GeometryBox::StrokeBox => dest.write_str("stroke-box"),
             GeometryBox::ViewBox => dest.write_str("view-box"),
             GeometryBox::ShapeBox(s) => s.to_css(dest),
         }
     }
 }
 
-impl<H, V, L> ToCss for BasicShape<H, V, L>
-    where H: ToCss,
-          V: ToCss,
-          L: PartialEq + ToCss,
-          Circle<H, V, L>: ToCss,
-          Ellipse<H, V, L>: ToCss,
-{
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            BasicShape::Inset(ref rect) => rect.to_css(dest),
-            BasicShape::Circle(ref circle) => circle.to_css(dest),
-            BasicShape::Ellipse(ref ellipse) => ellipse.to_css(dest),
-            BasicShape::Polygon(ref polygon) => polygon.to_css(dest),
-        }
-    }
-}
-
 impl<L> ToCss for InsetRect<L>
     where L: ToCss + PartialEq
 {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_str("inset(")?;
         self.rect.to_css(dest)?;
         if let Some(ref radius) = self.round {
             dest.write_str(" round ")?;
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -103,18 +103,18 @@ define_css_keyword_enum!(ShapeExtent:
     "contain" => Contain,
     "cover" => Cover
 );
 no_viewport_percentage!(ShapeExtent);
 impl ComputedValueAsSpecified for ShapeExtent {}
 
 /// A gradient item.
 /// https://drafts.csswg.org/css-images-4/#color-stop-syntax
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
 pub enum GradientItem<Color, LengthOrPercentage> {
     /// A color stop.
     ColorStop(ColorStop<Color, LengthOrPercentage>),
     /// An interpolation hint.
     InterpolationHint(LengthOrPercentage),
 }
 
 /// A color stop.
@@ -283,27 +283,16 @@ impl<L, LoP> ToCss for EndingShape<L, Lo
                 x.to_css(dest)?;
                 dest.write_str(" ")?;
                 y.to_css(dest)
             },
         }
     }
 }
 
-impl<C, L> ToCss for GradientItem<C, L>
-    where C: ToCss, L: ToCss,
-{
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            GradientItem::ColorStop(ref stop) => stop.to_css(dest),
-            GradientItem::InterpolationHint(ref hint) => hint.to_css(dest),
-        }
-    }
-}
-
 impl<C, L> fmt::Debug for ColorStop<C, L>
     where C: fmt::Debug, L: fmt::Debug,
 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "{:?}", self.color)?;
         if let Some(ref pos) = self.position {
             write!(f, " {:?}", pos)?;
         }
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -1,20 +1,17 @@
 /* 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/. */
 
 //! Generic types for CSS values that are related to transformations.
 
-use std::fmt;
-use style_traits::ToCss;
-
 /// A generic transform origin.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
 pub struct TransformOrigin<H, V, Depth> {
     /// The horizontal origin.
     pub horizontal: H,
     /// The vertical origin.
     pub vertical: V,
     /// The depth.
     pub depth: Depth,
 }
@@ -24,22 +21,8 @@ impl<H, V, D> TransformOrigin<H, V, D> {
     pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
         Self {
             horizontal: horizontal,
             vertical: vertical,
             depth: depth,
         }
     }
 }
-
-impl<H, V, D> ToCss for TransformOrigin<H, V, D>
-    where H: ToCss, V: ToCss, D: ToCss,
-{
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
-        where W: fmt::Write,
-    {
-        self.horizontal.to_css(dest)?;
-        dest.write_str(" ")?;
-        self.vertical.to_css(dest)?;
-        dest.write_str(" ")?;
-        self.depth.to_css(dest)
-    }
-}
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -29,44 +29,35 @@ pub type CSSInteger = i32;
 
 /// The default font size.
 pub const FONT_MEDIUM_PX: i32 = 16;
 
 define_keyword_type!(None_, "none");
 define_keyword_type!(Auto, "auto");
 define_keyword_type!(Normal, "normal");
 
-#[derive(Clone, Copy, HasViewportPercentage, PartialEq)]
+/// A struct representing one of two kinds of values.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-/// A struct representing one of two kinds of values.
+#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToCss)]
 pub enum Either<A, B> {
     /// The first value.
     First(A),
     /// The second kind of value.
     Second(B),
 }
 
 impl<A: Debug, B: Debug> Debug for Either<A, B> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             Either::First(ref v) => v.fmt(f),
             Either::Second(ref v) => v.fmt(f),
         }
     }
 }
 
-impl<A: ToCss, B: ToCss> ToCss for Either<A, B> {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            Either::First(ref v) => v.to_css(dest),
-            Either::Second(ref v) => v.to_css(dest),
-        }
-    }
-}
-
 impl<A: Parse, B: Parse> Parse for Either<A, B> {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Either<A, B>, ()> {
         if let Ok(v) = input.try(|i| A::parse(context, i)) {
             Ok(Either::First(v))
         } else {
             B::parse(context, input).map(Either::Second)
         }
     }
--- a/servo/components/style/values/specified/align.rs
+++ b/servo/components/style/values/specified/align.rs
@@ -204,17 +204,17 @@ impl Parse for AlignJustifyContent {
         }
         Err(())
     }
 }
 
 /// Value of the `align-self` or `justify-self` property.
 ///
 /// https://drafts.csswg.org/css-align/#self-alignment
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, ToCss)]
 pub struct AlignJustifySelf(pub AlignFlags);
 
 impl AlignJustifySelf {
     /// The initial value 'auto'
     #[inline]
     pub fn auto() -> Self {
         AlignJustifySelf(ALIGN_AUTO)
     }
@@ -223,22 +223,16 @@ impl AlignJustifySelf {
     #[inline]
     pub fn has_extra_flags(self) -> bool {
         self.0.intersects(ALIGN_FLAG_BITS)
     }
 }
 
 no_viewport_percentage!(AlignJustifySelf);
 
-impl ToCss for AlignJustifySelf {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.0.to_css(dest)
-    }
-}
-
 impl Parse for AlignJustifySelf {
     // auto | normal | stretch | <baseline-position> |
     // [ <overflow-position>? && <self-position> ]
     fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         // auto | normal | stretch | <baseline-position>
         if let Ok(value) = input.try(parse_auto_normal_stretch_baseline) {
             return Ok(AlignJustifySelf(value))
         }
@@ -248,17 +242,17 @@ impl Parse for AlignJustifySelf {
         }
         Err(())
     }
 }
 
 /// Value of the `align-items` property
 ///
 /// https://drafts.csswg.org/css-align/#self-alignment
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, ToCss)]
 pub struct AlignItems(pub AlignFlags);
 
 impl AlignItems {
     /// The initial value 'normal'
     #[inline]
     pub fn normal() -> Self {
         AlignItems(ALIGN_NORMAL)
     }
@@ -267,22 +261,16 @@ impl AlignItems {
     #[inline]
     pub fn has_extra_flags(self) -> bool {
         self.0.intersects(ALIGN_FLAG_BITS)
     }
 }
 
 no_viewport_percentage!(AlignItems);
 
-impl ToCss for AlignItems {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.0.to_css(dest)
-    }
-}
-
 impl Parse for AlignItems {
     // normal | stretch | <baseline-position> |
     // [ <overflow-position>? && <self-position> ]
     fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         // normal | stretch | <baseline-position>
         if let Ok(value) = input.try(parse_normal_stretch_baseline) {
             return Ok(AlignItems(value))
         }
@@ -292,17 +280,17 @@ impl Parse for AlignItems {
         }
         Err(())
     }
 }
 
 /// Value of the `justify-items` property
 ///
 /// https://drafts.csswg.org/css-align/#justify-items-property
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, ToCss)]
 pub struct JustifyItems(pub AlignFlags);
 
 impl JustifyItems {
     /// The initial value 'auto'
     #[inline]
     pub fn auto() -> Self {
         JustifyItems(ALIGN_AUTO)
     }
@@ -311,22 +299,16 @@ impl JustifyItems {
     #[inline]
     pub fn has_extra_flags(self) -> bool {
         self.0.intersects(ALIGN_FLAG_BITS)
     }
 }
 
 no_viewport_percentage!(JustifyItems);
 
-impl ToCss for JustifyItems {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.0.to_css(dest)
-    }
-}
-
 impl Parse for JustifyItems {
     // auto | normal | stretch | <baseline-position> |
     // [ <overflow-position>? && <self-position> ]
     // [ legacy && [ left | right | center ] ]
     fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         // auto | normal | stretch | <baseline-position>
         if let Ok(value) = input.try(parse_auto_normal_stretch_baseline) {
             return Ok(JustifyItems(value))
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -526,43 +526,34 @@ impl NoCalcLength {
         NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
     }
 }
 
 /// An extension to `NoCalcLength` to parse `calc` expressions.
 /// This is commonly used for the `<length>` values.
 ///
 /// https://drafts.csswg.org/css-values/#lengths
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum Length {
     /// The internal length type that cannot parse `calc`
     NoCalc(NoCalcLength),
     /// A calc expression.
     ///
     /// https://drafts.csswg.org/css-values/#calc-notation
     Calc(Box<CalcLengthOrPercentage>),
 }
 
 impl From<NoCalcLength> for Length {
     #[inline]
     fn from(len: NoCalcLength) -> Self {
         Length::NoCalc(len)
     }
 }
 
-impl ToCss for Length {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            Length::NoCalc(ref inner) => inner.to_css(dest),
-            Length::Calc(ref calc) => calc.to_css(dest),
-        }
-    }
-}
-
 impl Mul<CSSFloat> for Length {
     type Output = Length;
 
     #[inline]
     fn mul(self, scalar: CSSFloat) -> Length {
         match self {
             Length::NoCalc(inner) => Length::NoCalc(inner * scalar),
             Length::Calc(..) => panic!("Can't multiply Calc!"),
@@ -753,19 +744,19 @@ impl Parse for Percentage {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         Self::parse_with_clamping_mode(input, AllowedNumericType::All)
     }
 }
 
 impl ComputedValueAsSpecified for Percentage {}
 
 /// A length or a percentage value.
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum LengthOrPercentage {
     Length(NoCalcLength),
     Percentage(Percentage),
     Calc(Box<CalcLengthOrPercentage>),
 }
 
 impl From<Length> for LengthOrPercentage {
     fn from(len: Length) -> LengthOrPercentage {
@@ -785,26 +776,16 @@ impl From<NoCalcLength> for LengthOrPerc
 
 impl From<Percentage> for LengthOrPercentage {
     #[inline]
     fn from(pc: Percentage) -> Self {
         LengthOrPercentage::Percentage(pc)
     }
 }
 
-impl ToCss for LengthOrPercentage {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            LengthOrPercentage::Length(ref length) => length.to_css(dest),
-            LengthOrPercentage::Percentage(percentage) => percentage.to_css(dest),
-            LengthOrPercentage::Calc(ref calc) => calc.to_css(dest),
-        }
-    }
-}
-
 impl LengthOrPercentage {
     #[inline]
     /// Returns a `zero` length.
     pub fn zero() -> LengthOrPercentage {
         LengthOrPercentage::Length(NoCalcLength::zero())
     }
 
     fn parse_internal(context: &ParserContext,
@@ -1208,35 +1189,24 @@ impl LengthOrNumber {
     pub fn zero() -> Self {
         Either::Second(Number::new(0.))
     }
 }
 
 /// A value suitable for a `min-width` or `min-height` property.
 /// Unlike `max-width` or `max-height` properties, a MozLength can be
 /// `auto`, and cannot be `none`.
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum MozLength {
     LengthOrPercentageOrAuto(LengthOrPercentageOrAuto),
     ExtremumLength(ExtremumLength),
 }
 
-impl ToCss for MozLength {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            MozLength::LengthOrPercentageOrAuto(ref lopoa) =>
-                lopoa.to_css(dest),
-            MozLength::ExtremumLength(ref ext) =>
-                ext.to_css(dest),
-        }
-    }
-}
-
 impl Parse for MozLength {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         MozLength::parse_quirky(context, input, AllowQuirks::No)
     }
 }
 
 impl MozLength {
     /// Parses, with quirks.
@@ -1245,35 +1215,24 @@ impl MozLength {
                         allow_quirks: AllowQuirks) -> Result<Self, ()> {
         input.try(ExtremumLength::parse).map(MozLength::ExtremumLength)
             .or_else(|()| input.try(|i| LengthOrPercentageOrAuto::parse_non_negative_quirky(context, i, allow_quirks))
                                .map(MozLength::LengthOrPercentageOrAuto))
     }
 }
 
 /// A value suitable for a `max-width` or `max-height` property.
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum MaxLength {
     LengthOrPercentageOrNone(LengthOrPercentageOrNone),
     ExtremumLength(ExtremumLength),
 }
 
-impl ToCss for MaxLength {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            MaxLength::LengthOrPercentageOrNone(ref lopon) =>
-                lopon.to_css(dest),
-            MaxLength::ExtremumLength(ref ext) =>
-                ext.to_css(dest),
-        }
-    }
-}
-
 impl Parse for MaxLength {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         MaxLength::parse_quirky(context, input, AllowQuirks::No)
     }
 }
 
 impl MaxLength {
     /// Parses, with quirks.
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -647,19 +647,19 @@ impl ToCss for Number {
             dest.write_str(")")?;
         }
         Ok(())
     }
 }
 
 /// <number-percentage>
 /// Accepts only non-negative numbers.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum NumberOrPercentage {
     Percentage(Percentage),
     Number(Number),
 }
 
 no_viewport_percentage!(NumberOrPercentage);
 
 impl NumberOrPercentage {
@@ -681,28 +681,19 @@ impl NumberOrPercentage {
 }
 
 impl Parse for NumberOrPercentage {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
     }
 }
 
-impl ToCss for NumberOrPercentage {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            NumberOrPercentage::Percentage(percentage) => percentage.to_css(dest),
-            NumberOrPercentage::Number(number) => number.to_css(dest),
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, ToCss)]
 pub struct Opacity(Number);
 
 no_viewport_percentage!(Opacity);
 
 impl Parse for Opacity {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         parse_number(context, input).map(Opacity)
     }
@@ -717,22 +708,16 @@ impl ToComputedValue for Opacity {
     }
 
     #[inline]
     fn from_computed_value(computed: &CSSFloat) -> Self {
         Opacity(Number::from_computed_value(computed))
     }
 }
 
-impl ToCss for Opacity {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.0.to_css(dest)
-    }
-}
-
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct Integer {
     value: CSSInteger,
     was_calc: bool,
 }
 
--- a/servo/components/style_derive/lib.rs
+++ b/servo/components/style_derive/lib.rs
@@ -6,20 +6,27 @@ extern crate proc_macro;
 #[macro_use] extern crate quote;
 extern crate syn;
 extern crate synstructure;
 
 use proc_macro::TokenStream;
 
 mod has_viewport_percentage;
 mod to_computed_value;
+mod to_css;
 
 #[proc_macro_derive(HasViewportPercentage)]
 pub fn derive_has_viewport_percentage(stream: TokenStream) -> TokenStream {
     let input = syn::parse_derive_input(&stream.to_string()).unwrap();
     has_viewport_percentage::derive(input).to_string().parse().unwrap()
 }
 
 #[proc_macro_derive(ToComputedValue)]
 pub fn derive_to_computed_value(stream: TokenStream) -> TokenStream {
     let input = syn::parse_derive_input(&stream.to_string()).unwrap();
     to_computed_value::derive(input).to_string().parse().unwrap()
 }
+
+#[proc_macro_derive(ToCss)]
+pub fn derive_to_css(stream: TokenStream) -> TokenStream {
+    let input = syn::parse_derive_input(&stream.to_string()).unwrap();
+    to_css::derive(input).to_string().parse().unwrap()
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style_derive/to_css.rs
@@ -0,0 +1,70 @@
+/* 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 quote;
+use syn;
+use synstructure;
+
+pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
+    let name = &input.ident;
+    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+    let mut where_clause = where_clause.clone();
+    for param in &input.generics.ty_params {
+        where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into())))
+    }
+
+    let style = synstructure::BindStyle::Ref.into();
+    let match_body = synstructure::each_variant(&input, &style, |bindings, _| {
+        if bindings.is_empty() {
+            panic!("unit variants are not yet supported");
+        }
+        let (first, rest) = bindings.split_first().expect("unit variants are not yet supported");
+        where_clause.predicates.push(where_predicate(first.field.ty.clone()));
+        let mut expr = quote! {
+            ::style_traits::ToCss::to_css(#first, dest)
+        };
+        for binding in rest {
+            where_clause.predicates.push(where_predicate(binding.field.ty.clone()));
+            expr = quote! {
+                #expr?;
+                dest.write_str(" ")?;
+                ::style_traits::ToCss::to_css(#binding, dest)
+            };
+        }
+        Some(expr)
+    });
+
+    quote! {
+        impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause {
+            #[allow(unused_variables, unused_imports)]
+            #[inline]
+            fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
+            where
+                W: ::std::fmt::Write
+            {
+                match *self {
+                    #match_body
+                }
+            }
+        }
+    }
+}
+
+/// `#ty: ::style_traits::ToCss`
+fn where_predicate(ty: syn::Ty) -> syn::WherePredicate {
+    syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
+        bound_lifetimes: vec![],
+        bounded_ty: ty,
+        bounds: vec![syn::TyParamBound::Trait(
+            syn::PolyTraitRef {
+                bound_lifetimes: vec![],
+                trait_ref: syn::Path {
+                    global: true,
+                    segments: vec!["style_traits".into(), "ToCss".into()],
+                },
+            },
+            syn::TraitBoundModifier::None
+        )],
+    })
+}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -78,17 +78,16 @@ use style::gecko_bindings::structs::Styl
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::structs::nsCSSValueSharedList;
 use style::gecko_bindings::structs::nsCompatibility;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties::{self, style_structs};
-use style::keyframes::{Keyframe, KeyframeSelector, KeyframesStepValue};
 use style::media_queries::{MediaList, parse_media_query_list};
 use style::parallel;
 use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
 use style::properties::{CascadeFlags, ComputedValues, Importance, SourcePropertyDeclaration};
 use style::properties::{LonghandIdSet, PropertyDeclarationBlock, PropertyId, StyleBuilder};
 use style::properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP;
 use style::properties::animated_properties::{Animatable, AnimationValue, TransitionProperty};
 use style::properties::parse_one_declaration_into;
@@ -99,18 +98,19 @@ 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, DocumentRule};
 use style::stylesheets::{ImportRule, KeyframesRule, MallocSizeOfWithGuard, MediaRule};
 use style::stylesheets::{NamespaceRule, Origin, PageRule, Stylesheet, StyleRule, SupportsRule};
 use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
+use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
+use style::stylesheets::supports_rule::parse_condition_or_declaration;
 use style::stylist::RuleInclusion;
-use style::supports::parse_condition_or_declaration;
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::{ANIMATION_ONLY, DomTraversal, FOR_CSS_RULE_CHANGES, FOR_RECONSTRUCT};
 use style::traversal::{FOR_DEFAULT_STYLES, TraversalDriver, TraversalFlags, UNSTYLED_CHILDREN_ONLY};
 use style::traversal::{resolve_style, resolve_default_style};
 use style::values::{CustomIdent, KeyframesName};
 use style::values::computed::Context;
 use style_traits::ToCss;
--- a/servo/tests/unit/style/animated_properties.rs
+++ b/servo/tests/unit/style/animated_properties.rs
@@ -1,50 +1,56 @@
 /* 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 cssparser::{Color, RGBA};
-use style::properties::animated_properties::Animatable;
+use cssparser::RGBA;
+use style::properties::animated_properties::{Animatable, IntermediateRGBA};
+
+fn interpolate_rgba(from: RGBA, to: RGBA, progress: f64) -> RGBA {
+    let from: IntermediateRGBA = from.into();
+    let to: IntermediateRGBA = to.into();
+    from.interpolate(&to, progress).unwrap().into()
+}
 
 #[test]
 fn test_rgba_color_interepolation_preserves_transparent() {
-    assert_eq!(Color::RGBA(RGBA::transparent())
-                .interpolate(&Color::RGBA(RGBA::transparent()), 0.5).unwrap(),
-               Color::RGBA(RGBA::transparent()));
+    assert_eq!(interpolate_rgba(RGBA::transparent(),
+                                RGBA::transparent(), 0.5),
+               RGBA::transparent());
 }
 
 #[test]
 fn test_rgba_color_interepolation_alpha() {
-    assert_eq!(Color::RGBA(RGBA::new(200, 0, 0, 100))
-                .interpolate(&Color::RGBA(RGBA::new(0, 200, 0, 200)), 0.5).unwrap(),
-               Color::RGBA(RGBA::new(67, 133, 0, 150)));
+    assert_eq!(interpolate_rgba(RGBA::new(200, 0, 0, 100),
+                                RGBA::new(0, 200, 0, 200), 0.5),
+               RGBA::new(67, 133, 0, 150));
 }
 
 #[test]
 fn test_rgba_color_interepolation_out_of_range_1() {
     // Some cubic-bezier functions produce values that are out of range [0, 1].
     // Unclamped cases.
-    assert_eq!(Color::RGBA(RGBA::from_floats(0.3, 0.0, 0.0, 0.4)).interpolate(
-              &Color::RGBA(RGBA::from_floats(0.0, 1.0, 0.0, 0.6)), -0.5).unwrap(),
-               Color::RGBA(RGBA::new(154, 0, 0, 77)));
+    assert_eq!(interpolate_rgba(RGBA::from_floats(0.3, 0.0, 0.0, 0.4),
+                                RGBA::from_floats(0.0, 1.0, 0.0, 0.6), -0.5),
+               RGBA::new(154, 0, 0, 77));
 }
 
 #[test]
 fn test_rgba_color_interepolation_out_of_range_2() {
-    assert_eq!(Color::RGBA(RGBA::from_floats(1.0, 0.0, 0.0, 0.6)).interpolate(
-              &Color::RGBA(RGBA::from_floats(0.0, 0.3, 0.0, 0.4)), 1.5).unwrap(),
-               Color::RGBA(RGBA::new(0, 154, 0, 77)));
+    assert_eq!(interpolate_rgba(RGBA::from_floats(1.0, 0.0, 0.0, 0.6),
+                                RGBA::from_floats(0.0, 0.3, 0.0, 0.4), 1.5),
+               RGBA::new(0, 154, 0, 77));
 }
 
 #[test]
 fn test_rgba_color_interepolation_out_of_range_clamped_1() {
-    assert_eq!(Color::RGBA(RGBA::from_floats(1.0, 0.0, 0.0, 0.8)).interpolate(
-              &Color::RGBA(RGBA::from_floats(0.0, 1.0, 0.0, 0.2)), -0.5).unwrap(),
-               Color::RGBA(RGBA::from_floats(1.0, 0.0, 0.0, 1.0)));
+    assert_eq!(interpolate_rgba(RGBA::from_floats(1.0, 0.0, 0.0, 0.8),
+                                RGBA::from_floats(0.0, 1.0, 0.0, 0.2), -0.5),
+               RGBA::from_floats(1.0, 0.0, 0.0, 1.0));
 }
 
 #[test]
 fn test_rgba_color_interepolation_out_of_range_clamped_2() {
-    assert_eq!(Color::RGBA(RGBA::from_floats(1.0, 0.0, 0.0, 0.8)).interpolate(
-              &Color::RGBA(RGBA::from_floats(0.0, 1.0, 0.0, 0.2)), 1.5).unwrap(),
-               Color::RGBA(RGBA::from_floats(0.0, 0.0, 0.0, 0.0)));
+    assert_eq!(interpolate_rgba(RGBA::from_floats(1.0, 0.0, 0.0, 0.8),
+                                RGBA::from_floats(0.0, 1.0, 0.0, 0.2), 1.5),
+               RGBA::from_floats(0.0, 0.0, 0.0, 0.0));
 }
--- a/servo/tests/unit/style/keyframes.rs
+++ b/servo/tests/unit/style/keyframes.rs
@@ -1,18 +1,18 @@
 /* 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 style::keyframes::{Keyframe, KeyframesAnimation, KeyframePercentage,  KeyframeSelector};
-use style::keyframes::{KeyframesStep, KeyframesStepValue};
 use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, Importance};
 use style::properties::animated_properties::TransitionProperty;
 use style::shared_lock::SharedRwLock;
 use style::stylearc::Arc;
+use style::stylesheets::keyframes_rule::{Keyframe, KeyframesAnimation, KeyframePercentage,  KeyframeSelector};
+use style::stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue};
 use style::values::specified::{LengthOrPercentageOrAuto, NoCalcLength};
 
 #[test]
 fn test_empty_keyframe() {
     let shared_lock = SharedRwLock::new();
     let keyframes = vec![];
     let animation = KeyframesAnimation::from_keyframes(&keyframes,
                                                        /* vendor_prefix = */ None,
--- a/servo/tests/unit/style/parsing/supports.rs
+++ b/servo/tests/unit/style/parsing/supports.rs
@@ -1,14 +1,14 @@
 /* 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 cssparser::Parser;
-use style::supports::SupportsCondition;
+use style::stylesheets::supports_rule::SupportsCondition;
 use style_traits::ToCss;
 
 #[test]
 fn test_supports_condition() {
     assert_roundtrip!(SupportsCondition::parse, "(margin: 1px)");
     assert_roundtrip!(SupportsCondition::parse, "not (--be: to be)");
     assert_roundtrip!(SupportsCondition::parse, "(color: blue) and future-extension(4)");
 }
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -10,26 +10,26 @@ use selectors::attr::*;
 use selectors::parser::*;
 use servo_atoms::Atom;
 use servo_url::ServoUrl;
 use std::borrow::ToOwned;
 use std::sync::Mutex;
 use std::sync::atomic::AtomicBool;
 use style::context::QuirksMode;
 use style::error_reporting::ParseErrorReporter;
-use style::keyframes::{Keyframe, KeyframeSelector, KeyframePercentage};
 use style::media_queries::MediaList;
 use style::properties::Importance;
 use style::properties::{CSSWideKeyword, DeclaredValueOwned, PropertyDeclaration, PropertyDeclarationBlock};
 use style::properties::longhands;
 use style::properties::longhands::animation_play_state;
 use style::shared_lock::SharedRwLock;
 use style::stylearc::Arc;
 use style::stylesheets::{Origin, Namespaces};
 use style::stylesheets::{Stylesheet, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule};
+use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframePercentage};
 use style::values::{KeyframesName, CustomIdent};
 use style::values::specified::{LengthOrPercentageOrAuto, Percentage, PositionComponent};
 
 pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
 where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
     let mut block = PropertyDeclarationBlock::new();
     for (d, i) in iterable {
         block.push(d, i)
--- a/servo/tests/unit/style/viewport.rs
+++ b/servo/tests/unit/style/viewport.rs
@@ -8,20 +8,20 @@ use media_queries::CSSErrorReporterTest;
 use servo_config::prefs::{PREFS, PrefValue};
 use servo_url::ServoUrl;
 use style::context::QuirksMode;
 use style::media_queries::{Device, MediaList, MediaType};
 use style::parser::{PARSING_MODE_DEFAULT, Parse, ParserContext};
 use style::shared_lock::SharedRwLock;
 use style::stylearc::Arc;
 use style::stylesheets::{CssRuleType, Stylesheet, Origin};
+use style::stylesheets::viewport_rule::*;
 use style::values::specified::LengthOrPercentageOrAuto::{self, Auto};
 use style::values::specified::NoCalcLength::{self, ViewportPercentage};
 use style::values::specified::ViewportPercentageLength::Vw;
-use style::viewport::*;
 use style_traits::PinchZoomFactor;
 use style_traits::viewport::*;
 
 macro_rules! stylesheet {
     ($css:expr, $origin:ident, $error_reporter:expr) => {
         stylesheet!($css, $origin, $error_reporter, SharedRwLock::new())
     };
     ($css:expr, $origin:ident, $error_reporter:expr, $shared_lock:expr) => {
--- a/taskcluster/taskgraph/test/test_try_option_syntax.py
+++ b/taskcluster/taskgraph/test/test_try_option_syntax.py
@@ -95,16 +95,21 @@ class TestTryOptionSyntax(unittest.TestC
 
     def test_unknown_args(self):
         "unknown arguments are ignored"
         tos = TryOptionSyntax('try: --doubledash -z extra', graph_with_jobs)
         # equilvant to "try:"..
         self.assertEqual(tos.build_types, [])
         self.assertEqual(tos.jobs, None)
 
+    def test_apostrophe_in_message(self):
+        "apostrophe does not break parsing"
+        tos = TryOptionSyntax('Increase spammy log\'s log level. try: -b do', graph_with_jobs)
+        self.assertEqual(sorted(tos.build_types), ['debug', 'opt'])
+
     def test_b_do(self):
         "-b do should produce both build_types"
         tos = TryOptionSyntax('try: -b do', graph_with_jobs)
         self.assertEqual(sorted(tos.build_types), ['debug', 'opt'])
 
     def test_b_d(self):
         "-b d should produce build_types=['debug']"
         tos = TryOptionSyntax('try: -b d', graph_with_jobs)
--- a/taskcluster/taskgraph/try_option_syntax.py
+++ b/taskcluster/taskgraph/try_option_syntax.py
@@ -189,30 +189,28 @@ def escape_whitespace_in_brackets(input_
             result += '\ '
             continue
 
         result += char
 
     return result
 
 
-def find_try_idx(message):
+def split_try_msg(message):
+    try:
+        try_idx = message.index('try:')
+    except ValueError:
+        return []
+    message = message[try_idx:].split('\n')[0]
     # shlex used to ensure we split correctly when giving values to argparse.
-    parts = shlex.split(escape_whitespace_in_brackets(message))
-    try_idx = None
-    for idx, part in enumerate(parts):
-        if part == TRY_DELIMITER:
-            try_idx = idx
-            break
-
-    return try_idx, parts
+    return shlex.split(escape_whitespace_in_brackets(message))
 
 
 def parse_message(message):
-    try_idx, parts = find_try_idx(message)
+    parts = split_try_msg(message)
 
     # Argument parser based on try flag flags
     parser = argparse.ArgumentParser()
     parser.add_argument('-b', '--build', dest='build_types')
     parser.add_argument('-p', '--platform', nargs='?',
                         dest='platforms', const='all', default='all')
     parser.add_argument('-u', '--unittests', nargs='?',
                         dest='unittests', const='all', default='all')
@@ -235,17 +233,16 @@ def parse_message(message):
     # While we are transitioning from BB to TC, we want to push jobs to tc-worker
     # machines but not overload machines with every try push. Therefore, we add
     # this temporary option to be able to push jobs to tc-worker.
     parser.add_argument('-w', '--taskcluster-worker',
                         dest='taskcluster_worker', action='store_true', default=False)
 
     # In order to run test jobs multiple times
     parser.add_argument('--rebuild', dest='trigger_tests', type=int, default=1)
-    parts = parts[try_idx:] if try_idx is not None else []
     args, _ = parser.parse_known_args(parts)
     return args
 
 
 class TryOptionSyntax(object):
 
     def __init__(self, message, full_task_graph):
         """
@@ -285,18 +282,18 @@ class TryOptionSyntax(object):
         self.interactive = False
         self.notifications = None
         self.talos_trigger_tests = 0
         self.env = []
         self.profile = False
         self.tag = None
         self.no_retry = False
 
-        try_idx, _ = find_try_idx(message)
-        if try_idx is None:
+        parts = split_try_msg(message)
+        if not parts:
             return None
 
         args = parse_message(message)
         assert args is not None
 
         self.jobs = self.parse_jobs(args.jobs)
         self.build_types = self.parse_build_types(args.build_types, full_task_graph)
         self.platforms = self.parse_platforms(args.platforms, full_task_graph)
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -103,17 +103,17 @@ const prefs = {
         Cu.reportError(
             "Invalid Marionette preferences in environment; " +
             "preferences will not have been applied");
         Cu.reportError(e);
       }
 
       if (prefs) {
         for (let prefName of Object.keys(prefs)) {
-          Preferences.set("marionette." + prefName, prefs[prefName]);
+          Preferences.set(prefName, prefs[prefName]);
         }
       }
     }
   },
 };
 
 function MarionetteComponent() {
   this.enabled = env.exists(ENV_ENABLED);
--- a/testing/mozbase/mozprofile/mozprofile/profile.py
+++ b/testing/mozbase/mozprofile/mozprofile/profile.py
@@ -348,16 +348,18 @@ class FirefoxProfile(Profile):
         # Don't check for the default web browser during startup
         'browser.shell.checkDefaultBrowser': False,
         # Don't warn on exit when multiple tabs are open
         'browser.tabs.warnOnClose': False,
         # Don't warn when exiting the browser
         'browser.warnOnQuit': False,
         # Don't send Firefox health reports to the production server
         'datareporting.healthreport.documentServerURI': 'http://%(server)s/healthreport/',
+        # Skip data reporting policy notifications
+        'datareporting.policy.dataSubmissionPolicyBypassNotification': True,
         # Only install add-ons from the profile and the application scope
         # Also ensure that those are not getting disabled.
         # see: https://developer.mozilla.org/en/Installing_extensions
         'extensions.enabledScopes': 5,
         'extensions.autoDisableScopes': 10,
         # Don't send the list of installed addons to AMO
         'extensions.getAddons.cache.enabled': False,
         # Don't install distribution add-ons from the app folder
deleted file mode 100644
--- a/testing/web-platform/meta/cssom/CSS.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[CSS.html]
-  type: testharness
-  [CSS.supports, one argument form]
-    expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/third_party/rust/arraydeque/.cargo-checksum.json
@@ -0,0 +1,1 @@
+{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805",".travis.yml":"6ef3b45cd0e38b989c77b44ec569f3028d75e9fd804a567deb5e471413436547","Cargo.toml":"6ee2958e4b76d84a3007e5e2add2339488f5e70e96f8726772f9c32112e022c9","Cargo.toml.orig":"7412fdfe51c59c7d87e0ae1b4360ca4a5beae9b413df899866c8eff25b157993","LICENSE":"6c5c8b27da74e0dd71048d7f9cc4a4ad230767caa7afa46bc97b7b7443570930","README.md":"f7bfcc3ff3adb1b77112512bb5b3d18a40152549205d7b28783f443f504826e2","src/array.rs":"dca08b8c759bc24a2e53c0d14e86313d6f7500ec3b80b1e8534a400857b87e76","src/lib.rs":"18ca673af87ab41212dd785a0c8c7f2c362ac16e649d2636ba72d7c149911243"},"package":"96e774cadb24c2245225280c6799793f9802b918a58a79615e9490607489a717"}
\ No newline at end of file
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/third_party/rust/arraydeque/.gitignore
@@ -0,0 +1,2 @@
+target
+Cargo.lock
new file mode 100644
--- /dev/null
+++ b/third_party/rust/arraydeque/.travis.yml
@@ -0,0 +1,55 @@
+language: rust
+
+sudo: required
+
+matrix:
+  include:
+    - rust: 1.15.0
+    - rust: stable
+    - rust: beta
+    - rust: nightly
+      env:
+        - NIGHTLY=1
+
+branches:
+  only:
+    - master
+
+script:
+  - |
+      cargo test --verbose &&
+      cargo test --verbose --no-default-features &&
+      cargo test --verbose --features "use_generic_array" &&
+      cargo test --verbose --no-default-features --features "use_generic_array" &&
+      ([ "$NIGHTLY" != 1 ] || cargo test --verbose --features "use_union") &&
+      ([ "$NIGHTLY" != 1 ] || cargo test --verbose --features "use_union use_generic_array") &&
+      ([ "$NIGHTLY" != 1 ] || cargo test --verbose --no-default-features --features "use_union") &&
+      ([ "$NIGHTLY" != 1 ] || cargo test --verbose --no-default-features --features "use_union use_generic_array")
+
+before_install:
+  - sudo apt-get update
+
+addons:
+  apt:
+    packages:
+      - libcurl4-openssl-dev
+      - libelf-dev
+      - libdw-dev
+      - cmake
+      - gcc
+      - binutils-dev
+
+after_success: |
+  wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
+  tar xzf master.tar.gz &&
+  cd kcov-master &&
+  mkdir build &&
+  cd build &&
+  cmake .. &&
+  make &&
+  sudo make install &&
+  cd ../.. &&
+  rm -rf kcov-master &&
+  for file in target/debug/arraydeque-*; do mkdir -p "target/cov/$(basename $file)"; kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; done &&
+  bash <(curl -s https://codecov.io/bash) &&
+  echo "Uploaded code coverage"
new file mode 100644
--- /dev/null
+++ b/third_party/rust/arraydeque/Cargo.toml
@@ -0,0 +1,39 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g. crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "arraydeque"
+version = "0.2.3"
+authors = ["goandylok"]
+description = "A ring buffer with a fixed capacity, can be stored on the stack."
+homepage = "https://github.com/goandylok/arraydeque"
+documentation = "https://docs.rs/arraydeque"
+keywords = ["ring", "circular", "stack", "array", "no_std"]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/goandylok/arraydeque"
+[dependencies.nodrop]
+version = "0.1.8"
+default-features = false
+
+[dependencies.odds]
+version = "0.2.12"
+default-features = false
+
+[dependencies.generic-array]
+version = "0.5.1"
+optional = true
+
+[features]
+use_generic_array = ["generic-array"]
+use_union = ["nodrop/use_union"]
+default = ["std"]
+std = ["odds/std", "nodrop/std"]
new file mode 100644
--- /dev/null
+++ b/third_party/rust/arraydeque/Cargo.toml.orig
@@ -0,0 +1,30 @@
+[package]
+name = "arraydeque"
+version = "0.2.3"
+authors = ["goandylok"]
+license = "MIT/Apache-2.0"
+
+description = "A ring buffer with a fixed capacity, can be stored on the stack."
+documentation = "https://docs.rs/arraydeque"
+repository = "https://github.com/goandylok/arraydeque"
+homepage = "https://github.com/goandylok/arraydeque"
+
+keywords = ["ring", "circular", "stack", "array", "no_std"]
+
+[dependencies.odds]
+version = "0.2.12"
+default-features = false
+
+[dependencies.nodrop]
+version = "0.1.8"
+default-features = false
+
+[dependencies.generic-array]
+version = "0.5.1"
+optional = true
+
+[features]
+default = ["std"]
+std = ["odds/std", "nodrop/std"]
+use_union = ["nodrop/use_union"]
+use_generic_array = ["generic-array"]
new file mode 100644
--- /dev/null
+++ b/third_party/rust/arraydeque/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 goandylok
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/arraydeque/README.md
@@ -0,0 +1,197 @@
+# `arraydeque`
+
+[![build status](https://travis-ci.org/goandylok/arraydeque.svg?branch=master)](https://travis-ci.org/goandylok/arraydeque)
+[![codecov](https://codecov.io/gh/goandylok/arraydeque/branch/master/graph/badge.svg)](https://codecov.io/gh/goandylok/arraydeque)
+[![crates.io](https://img.shields.io/crates/v/arraydeque.svg)](https://crates.io/crates/arraydeque)
+[![docs.rs](https://docs.rs/arraydeque/badge.svg)](https://docs.rs/arraydeque)
+
+A circular buffer with fixed capacity.  Requires Rust 1.15+.
+
+This crate is inspired by [**bluss/arrayvec**](https://github.com/bluss/arrayvec)
+
+### [**Documentation**](https://docs.rs/arraydeque)
+
+## Feature Flags
+
+The **arraydeque** crate has the following cargo feature flags:
+
+- `std`
+  - Optional, enabled by default
+  - Use libstd
+
+
+- `use_union`
+  - Optional
+  - Requires Rust nightly channel
+  - Use the unstable feature untagged unions for the internal implementation,
+    which has reduced space overhead
+
+
+- `use_generic_array`
+  - Optional
+  - Requires Rust stable channel
+  - Depend on generic-array and allow using it just like a fixed
+    size array for ArrayDeque storage.    
+
+
+## Usage
+
+First, add the following to your `Cargo.toml`:
+
+```toml
+[dependencies]
+arraydeque = "0.2"
+```
+
+Next, add this to your crate root:
+
+```rust
+extern crate arraydeque;
+```
+
+Currently arraydeque by default links to the standard library, but if you would
+instead like to use arraydeque in a `#![no_std]` situation or crate you can
+request this via:
+
+```toml
+[dependencies]
+arraydeque = { version = "0.2", default-features = false }
+```
+## Capacity
+
+Note that the `capacity()` is always `backed_array.len() - 1`.
+[Read more]
+
+[Read more]: https://en.wikipedia.org/wiki/Circular_buffer
+
+## Example
+
+### Push & Pop
+
+```rust
+extern crate arraydeque;
+
+use arraydeque::ArrayDeque;
+
+fn main() {
+    let mut vector: ArrayDeque<[_; 8]> = ArrayDeque::new();
+    assert_eq!(vector.capacity(), 7);
+    assert_eq!(vector.len(), 0);
+
+    vector.push_back(1);
+    vector.push_back(2);
+    assert_eq!(vector.len(), 2);
+
+    assert_eq!(vector.pop_front(), Some(1));
+    assert_eq!(vector.pop_front(), Some(2));
+    assert_eq!(vector.pop_front(), None);
+}
+```
+
+### Insert & Remove
+
+```rust
+use arraydeque::ArrayDeque;
+
+let mut vector: ArrayDeque<[_; 8]> = ArrayDeque::new();
+
+vector.push_back(11);
+vector.push_back(13);
+vector.insert(1, 12);
+vector.remove(0);
+
+assert_eq!(vector[0], 12);
+assert_eq!(vector[1], 13);
+```
+
+### Append & Extend
+
+```rust
+use arraydeque::ArrayDeque;
+
+let mut vector: ArrayDeque<[_; 8]> = ArrayDeque::new();
+let mut vector2: ArrayDeque<[_; 8]> = ArrayDeque::new();
+
+vector.extend(0..5);
+vector2.extend(5..7);
+
+assert_eq!(format!("{:?}", vector), "[0, 1, 2, 3, 4]");
+assert_eq!(format!("{:?}", vector2), "[5, 6]");
+
+vector.append(&mut vector2);
+
+assert_eq!(format!("{:?}", vector), "[0, 1, 2, 3, 4, 5, 6]");
+assert_eq!(format!("{:?}", vector2), "[]");
+```
+
+### Iterator
+
+```rust
+use arraydeque::ArrayDeque;
+
+let mut vector: ArrayDeque<[_; 8]> = ArrayDeque::new();
+
+vector.extend(0..5);
+
+let iters: Vec<_> = vector.into_iter().collect();
+assert_eq!(iters, vec![0, 1, 2, 3, 4]);
+```
+
+### From Iterator
+
+```rust
+use arraydeque::ArrayDeque;
+
+let vector: ArrayDeque<[_; 8]>;
+let vector2: ArrayDeque<[_; 8]>;
+
+vector = vec![0, 1, 2, 3, 4].into_iter().collect();
+
+vector2 = (0..5).into_iter().collect();
+
+assert_eq!(vector, vector2);
+```
+
+### Generic Array
+
+```toml
+[dependencies]
+generic-array = "0.5.1"
+
+[dependencies.arraydeque]
+version = "0.2"
+features = ["use_generic_array"]
+```
+```rust
+#[macro_use]
+extern crate generic_array;
+extern crate arraydeque;
+
+use generic_array::GenericArray;
+use generic_array::typenum::U41;
+
+use arraydeque::ArrayDeque;
+
+fn main() {
+    let mut vec: ArrayDeque<GenericArray<i32, U41>> = ArrayDeque::new();
+
+    assert_eq!(vec.len(), 0);
+    assert_eq!(vec.capacity(), 40);
+
+    vec.extend(0..20);
+
+    assert_eq!(vec.len(), 20);
+    assert_eq!(vec.into_iter().take(5).collect::<Vec<_>>(), vec![0, 1, 2, 3, 4]);
+}
+```
+
+## Contribution
+
+All kinds of contribution are welcomed.
+
+- **Issus.** Feel free to open an issue when you find typos, bugs, or have any question.
+- **Pull requests**. New collection, better implementation, more tests, more documents and typo fixes are all welcomed.
+
+## License
+
+Licensed under MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
new file mode 100644
--- /dev/null
+++ b/third_party/rust/arraydeque/src/array.rs
@@ -0,0 +1,86 @@
+/// Trait for fixed size arrays.
+pub unsafe trait Array {
+    /// The array's element type
+    type Item;
+    #[doc(hidden)]
+    /// The smallest index type that indexes the array.
+    type Index: Index;
+    #[doc(hidden)]
+    fn as_ptr(&self) -> *const Self::Item;
+    #[doc(hidden)]
+    fn as_mut_ptr(&mut self) -> *mut Self::Item;
+    #[doc(hidden)]
+    fn capacity() -> usize;
+}
+
+pub trait Index : PartialEq + Copy {
+    fn to_usize(self) -> usize;
+    fn from(usize) -> Self;
+}
+
+#[cfg(feature = "use_generic_array")]
+unsafe impl<T, U> Array for ::generic_array::GenericArray<T, U>
+    where U: ::generic_array::ArrayLength<T>
+{
+    type Item = T;
+    type Index = usize;
+    fn as_ptr(&self) -> *const Self::Item {
+        (**self).as_ptr()
+    }
+    fn as_mut_ptr(&mut self) -> *mut Self::Item {
+        (**self).as_mut_ptr()
+    }
+    fn capacity() -> usize {
+        U::to_usize()
+    }
+
+}
+
+impl Index for u8 {
+    #[inline(always)]
+    fn to_usize(self) -> usize { self as usize }
+    #[inline(always)]
+    fn from(ix: usize) ->  Self { ix as u8 }
+}
+
+impl Index for u16 {
+    #[inline(always)]
+    fn to_usize(self) -> usize { self as usize }
+    #[inline(always)]
+    fn from(ix: usize) ->  Self { ix as u16 }
+}
+
+impl Index for usize {
+    #[inline(always)]
+    fn to_usize(self) -> usize { self }
+    #[inline(always)]
+    fn from(ix: usize) ->  Self { ix }
+}
+
+macro_rules! fix_array_impl {
+    ($index_type:ty, $len:expr ) => (
+        unsafe impl<T> Array for [T; $len] {
+            type Item = T;
+            type Index = $index_type;
+            #[inline(always)]
+            fn as_ptr(&self) -> *const T { self as *const _ as *const _ }
+            #[inline(always)]
+            fn as_mut_ptr(&mut self) -> *mut T { self as *mut _ as *mut _}
+            #[inline(always)]
+            fn capacity() -> usize { $len }
+        }
+    )
+}
+
+macro_rules! fix_array_impl_recursive {
+    ($index_type:ty, ) => ();
+    ($index_type:ty, $len:expr, $($more:expr,)*) => (
+        fix_array_impl!($index_type, $len);
+        fix_array_impl_recursive!($index_type, $($more,)*);
+    );
+}
+
+fix_array_impl_recursive!(u8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+                          16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+                          32, 40, 48, 56, 64, 72, 96, 128, 160, 192, 224,);
+fix_array_impl_recursive!(u16, 256, 384, 512, 768, 1024, 2048, 4096, 8192, 16384, 32768,);
new file mode 100644
--- /dev/null
+++ b/third_party/rust/arraydeque/src/lib.rs
@@ -0,0 +1,2633 @@
+//! A circular buffer with fixed capacity.
+//! Requires Rust 1.15+
+//!
+//! It can be stored directly on the stack if needed.
+//!
+//! This queue has `O(1)` amortized inserts and removals from both ends of the
+//! container. It also has `O(1)` indexing like a vector. The contained elements
+//! are not required to be copyable
+//!
+//! This crate is inspired by [**bluss/arrayvec**]
+//! [**bluss/arrayvec**]: https://github.com/bluss/arrayvec
+//!
+//! # Feature Flags
+//! The **arraydeque** crate has the following cargo feature flags:
+//!
+//! - `std`
+//!   - Optional, enabled by default
+//!   - Use libstd
+//!
+//!
+//! - `use_union`
+//!   - Optional
+//!   - Requires Rust nightly channel
+//!   - Use the unstable feature untagged unions for the internal implementation,
+//!     which has reduced space overhead
+//!
+//!
+//! - `use_generic_array`
+//!   - Optional
+//!   - Requires Rust stable channel
+//!   - Depend on generic-array and allow using it just like a fixed
+//!     size array for ArrayDeque storage.
+//!
+//!
+//! # Usage
+//!
+//! First, add the following to your `Cargo.toml`:
+//!
+//! ```toml
+//! [dependencies]
+//! arraydeque = "0.2"
+//! ```
+//!
+//! Next, add this to your crate root:
+//!
+//! ```
+//! extern crate arraydeque;
+//! ```
+//!
+//! Currently arraydeque by default links to the standard library, but if you would
+//! instead like to use arraydeque in a `#![no_std]` situation or crate you can
+//! request this via:
+//!
+//! ```toml
+//! [dependencies]
+//! arraydeque = { version = "0.2", default-features = false }
+//! ```
+//!
+//! # Capacity
+//!
+//! Note that the `capacity()` is always `backed_array.len() - 1`.
+//! [Read more]
+//!
+//! [Read more]: https://en.wikipedia.org/wiki/Circular_buffer
+//!
+//! # Examples
+//! ```
+//! extern crate arraydeque;
+//!
+//! use arraydeque::ArrayDeque;
+//!
+//! fn main() {
+//!     let mut vector: ArrayDeque<[_; 8]> = ArrayDeque::new();
+//!     assert_eq!(vector.capacity(), 7);
+//!     assert_eq!(vector.len(), 0);
+//!
+//!     vector.push_back(1);
+//!     vector.push_back(2);
+//!     assert_eq!(vector.len(), 2);
+//!
+//!     assert_eq!(vector.pop_front(), Some(1));
+//!     assert_eq!(vector.pop_front(), Some(2));
+//!     assert_eq!(vector.pop_front(), None);
+//! }
+//! ```
+//!
+//! # Insert & Remove
+//! ```
+//! use arraydeque::ArrayDeque;
+//!
+//! let mut vector: ArrayDeque<[_; 8]> = ArrayDeque::new();
+//!
+//! vector.push_back(11);
+//! vector.push_back(13);
+//! vector.insert(1, 12);
+//! vector.remove(0);
+//!
+//! assert_eq!(vector[0], 12);
+//! assert_eq!(vector[1], 13);
+//! ```
+//!
+//! # Append & Extend
+//! ```
+//! use arraydeque::ArrayDeque;
+//!
+//! let mut vector: ArrayDeque<[_; 8]> = ArrayDeque::new();
+//! let mut vector2: ArrayDeque<[_; 8]> = ArrayDeque::new();
+//!
+//! vector.extend(0..5);
+//! vector2.extend(5..7);
+//!
+//! assert_eq!(format!("{:?}", vector), "[0, 1, 2, 3, 4]");
+//! assert_eq!(format!("{:?}", vector2), "[5, 6]");
+//!
+//! vector.append(&mut vector2);
+//!
+//! assert_eq!(format!("{:?}", vector), "[0, 1, 2, 3, 4, 5, 6]");
+//! assert_eq!(format!("{:?}", vector2), "[]");
+//! ```
+//!
+//! # Iterator
+//! ```
+//! use arraydeque::ArrayDeque;
+//!
+//! let mut vector: ArrayDeque<[_; 8]> = ArrayDeque::new();
+//!
+//! vector.extend(0..5);
+//!
+//! let iters: Vec<_> = vector.into_iter().collect();
+//! assert_eq!(iters, vec![0, 1, 2, 3, 4]);
+//! ```
+//!
+//! # From Iterator
+//! ```
+//! use arraydeque::ArrayDeque;
+//!
+//! let vector: ArrayDeque<[_; 8]>;
+//! let vector2: ArrayDeque<[_; 8]>;
+//!
+//! vector = vec![0, 1, 2, 3, 4].into_iter().collect();
+//!
+//! vector2 = (0..5).into_iter().collect();
+//!
+//! assert_eq!(vector, vector2);
+//! ```
+//!
+//! # Generic Array
+//! ```toml
+//! [dependencies]
+//! generic-array = "0.5.1"
+//!
+//! [dependencies.arraydeque]
+//! version = "0.2"
+//! features = ["use_generic_array"]
+//! ```
+//! ```
+//! # #[cfg(feature = "use_generic_array")]
+//! #[macro_use]
+//! extern crate generic_array;
+//! extern crate arraydeque;
+//!
+//! # #[cfg(feature = "use_generic_array")]
+//! use generic_array::GenericArray;
+//! # #[cfg(feature = "use_generic_array")]
+//! use generic_array::typenum::U41;
+//!
+//! use arraydeque::ArrayDeque;
+//!
+//! # #[cfg(feature = "use_generic_array")]
+//! fn main() {
+//!     let mut vec: ArrayDeque<GenericArray<i32, U41>> = ArrayDeque::new();
+//!
+//!     assert_eq!(vec.len(), 0);
+//!     assert_eq!(vec.capacity(), 40);
+//!
+//!     vec.extend(0..20);
+//!
+//!     assert_eq!(vec.len(), 20);
+//!     assert_eq!(vec.into_iter().take(5).collect::<Vec<_>>(), vec![0, 1, 2, 3, 4]);
+//! }
+//!
+//! # #[cfg(not(feature = "use_generic_array"))]
+//! # fn main() {}
+//! ```
+
+#![cfg_attr(not(any(feature="std", test)), no_std)]
+
+extern crate odds;
+extern crate nodrop;
+
+#[cfg(feature = "use_generic_array")]
+extern crate generic_array;
+
+// #![cfg_attr(not(or(feature="std", test), no_std))]
+#[cfg(not(any(feature="std", test)))]
+extern crate core as std;
+
+use std::mem;
+use std::cmp;
+use std::cmp::Ordering;
+use std::hash::{Hash, Hasher};
+use std::fmt;
+use std::ptr;
+use std::slice;
+use std::iter;
+use std::ops::Index;
+use std::ops::IndexMut;
+
+pub use odds::IndexRange as RangeArgument;
+use nodrop::NoDrop;
+
+pub mod array;
+
+pub use array::Array;
+use array::Index as ArrayIndex;
+
+unsafe fn new_array<A: Array>() -> A {
+    // Note: Returning an uninitialized value here only works
+    // if we can be sure the data is never used. The nullable pointer
+    // inside enum optimization conflicts with this this for example,
+    // so we need to be extra careful. See `NoDrop` enum.
+    mem::uninitialized()
+}
+
+/// A fixed capacity ring buffer.
+///
+/// It can be stored directly on the stack if needed.
+///
+/// The "default" usage of this type as a queue is to use `push_back` to add to
+/// the queue, and `pop_front` to remove from the queue. `extend` and `append`
+/// push onto the back in this manner, and iterating over `ArrayDeque` goes front
+/// to back.
+///
+/// # Capacity
+///
+/// Note that the `capacity()` is always `backed_array.len() - 1`.
+/// [Read more]
+///
+/// [Read more]: https://en.wikipedia.org/wiki/Circular_buffer
+pub struct ArrayDeque<A: Array> {
+    xs: NoDrop<A>,
+    head: A::Index,
+    tail: A::Index,
+}
+
+impl<A: Array> Clone for ArrayDeque<A>
+    where A::Item: Clone
+{
+    fn clone(&self) -> ArrayDeque<A> {
+        self.iter().cloned().collect()
+    }
+}
+
+impl<A: Array> Drop for ArrayDeque<A> {
+    fn drop(&mut self) {
+        self.clear();
+
+        // NoDrop inhibits array's drop
+        // panic safety: NoDrop::drop will trigger on panic, so the inner
+        // array will not drop even after panic.
+    }
+}
+
+impl<A: Array> Default for ArrayDeque<A> {
+    #[inline]
+    fn default() -> ArrayDeque<A> {
+        ArrayDeque::new()
+    }
+}
+
+impl<A: Array> ArrayDeque<A> {
+    #[inline]
+    fn wrap_add(index: usize, addend: usize) -> usize {
+        wrap_add(index, addend, A::capacity())
+    }
+
+    #[inline]
+    fn wrap_sub(index: usize, subtrahend: usize) -> usize {
+        wrap_sub(index, subtrahend, A::capacity())
+    }
+
+    #[inline]
+    fn ptr(&self) -> *const A::Item {
+        self.xs.as_ptr()
+    }
+
+    #[inline]
+    fn ptr_mut(&mut self) -> *mut A::Item {
+        self.xs.as_mut_ptr()
+    }
+
+    #[inline]
+    fn is_contiguous(&self) -> bool {
+        self.tail() <= self.head()
+    }
+
+    #[inline]
+    fn is_full(&self) -> bool {
+        A::capacity() - self.len() == 1
+    }
+
+    #[inline]
+    fn head(&self) -> usize {
+        self.head.to_usize()
+    }
+
+    #[inline]
+    fn tail(&self) -> usize {
+        self.tail.to_usize()
+    }
+
+    #[inline]
+    unsafe fn set_head(&mut self, head: usize) {
+        debug_assert!(head <= self.capacity());
+        self.head = ArrayIndex::from(head);
+    }
+
+    #[inline]
+    unsafe fn set_tail(&mut self, tail: usize) {
+        debug_assert!(tail <= self.capacity());
+        self.tail = ArrayIndex::from(tail);
+    }
+
+    /// Copies a contiguous block of memory len long from src to dst
+    #[inline]
+    unsafe fn copy(&mut self, dst: usize, src: usize, len: usize) {
+        debug_assert!(dst + len <= A::capacity(),
+                      "cpy dst={} src={} len={} cap={}",
+                      dst,
+                      src,
+                      len,
+                      A::capacity());
+        debug_assert!(src + len <= A::capacity(),
+                      "cpy dst={} src={} len={} cap={}",
+                      dst,
+                      src,
+                      len,
+                      A::capacity());
+        ptr::copy(self.ptr_mut().offset(src as isize),
+                  self.ptr_mut().offset(dst as isize),
+                  len);
+    }
+
+    /// Copies a potentially wrapping block of memory len long from src to dest.
+    /// (abs(dst - src) + len) must be no larger than cap() (There must be at
+    /// most one continuous overlapping region between src and dest).
+    unsafe fn wrap_copy(&mut self, dst: usize, src: usize, len: usize) {
+        #[allow(dead_code)]
+        fn diff(a: usize, b: usize) -> usize {
+            if a <= b { b - a } else { a - b }
+        }
+        debug_assert!(cmp::min(diff(dst, src), A::capacity() - diff(dst, src)) + len <=
+                      A::capacity(),
+                      "wrc dst={} src={} len={} cap={}",
+                      dst,
+                      src,
+                      len,
+                      A::capacity());
+
+        if src == dst || len == 0 {
+            return;
+        }
+
+        let dst_after_src = Self::wrap_sub(dst, src) < len;
+
+        let src_pre_wrap_len = A::capacity() - src;
+        let dst_pre_wrap_len = A::capacity() - dst;
+        let src_wraps = src_pre_wrap_len < len;
+        let dst_wraps = dst_pre_wrap_len < len;
+
+        match (dst_after_src, src_wraps, dst_wraps) {
+            (_, false, false) => {
+                // src doesn't wrap, dst doesn't wrap
+                //
+                //        S . . .
+                // 1 [_ _ A A B B C C _]
+                // 2 [_ _ A A A A B B _]
+                //            D . . .
+                //
+                self.copy(dst, src, len);
+            }
+            (false, false, true) => {
+                // dst before src, src doesn't wrap, dst wraps
+                //
+                //    S . . .
+                // 1 [A A B B _ _ _ C C]
+                // 2 [A A B B _ _ _ A A]
+                // 3 [B B B B _ _ _ A A]
+                //    . .           D .
+                //
+                self.copy(dst, src, dst_pre_wrap_len);
+                self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len);
+            }
+            (true, false, true) => {
+                // src before dst, src doesn't wrap, dst wraps
+                //
+                //              S . . .
+                // 1 [C C _ _ _ A A B B]
+                // 2 [B B _ _ _ A A B B]
+                // 3 [B B _ _ _ A A A A]
+                //    . .           D .
+                //
+                self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len);
+                self.copy(dst, src, dst_pre_wrap_len);
+            }
+            (false, true, false) => {
+                // dst before src, src wraps, dst doesn't wrap
+                //
+                //    . .           S .
+                // 1 [C C _ _ _ A A B B]
+                // 2 [C C _ _ _ B B B B]
+                // 3 [C C _ _ _ B B C C]
+                //              D . . .
+                //
+                self.copy(dst, src, src_pre_wrap_len);
+                self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len);
+            }
+            (true, true, false) => {
+                // src before dst, src wraps, dst doesn't wrap
+                //
+                //    . .           S .
+                // 1 [A A B B _ _ _ C C]
+                // 2 [A A A A _ _ _ C C]
+                // 3 [C C A A _ _ _ C C]
+                //    D . . .
+                //
+                self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len);
+                self.copy(dst, src, src_pre_wrap_len);
+            }
+            (false, true, true) => {
+                // dst before src, src wraps, dst wraps
+                //
+                //    . . .         S .
+                // 1 [A B C D _ E F G H]
+                // 2 [A B C D _ E G H H]
+                // 3 [A B C D _ E G H A]
+                // 4 [B C C D _ E G H A]
+                //    . .         D . .
+                //
+                debug_assert!(dst_pre_wrap_len > src_pre_wrap_len);
+                let delta = dst_pre_wrap_len - src_pre_wrap_len;
+                self.copy(dst, src, src_pre_wrap_len);
+                self.copy(dst + src_pre_wrap_len, 0, delta);
+                self.copy(0, delta, len - dst_pre_wrap_len);
+            }
+            (true, true, true) => {
+                // src before dst, src wraps, dst wraps
+                //
+                //    . .         S . .
+                // 1 [A B C D _ E F G H]
+                // 2 [A A B D _ E F G H]
+                // 3 [H A B D _ E F G H]
+                // 4 [H A B D _ E F F G]
+                //    . . .         D .
+                //
+                debug_assert!(src_pre_wrap_len > dst_pre_wrap_len);
+                let delta = src_pre_wrap_len - dst_pre_wrap_len;
+                self.copy(delta, 0, len - src_pre_wrap_len);
+                self.copy(0, A::capacity() - delta, delta);
+                self.copy(dst, src, dst_pre_wrap_len);
+            }
+        }
+    }
+
+    #[inline]
+    unsafe fn buffer_as_slice(&self) -> &[A::Item] {
+        slice::from_raw_parts(self.ptr(), A::capacity())
+    }
+
+    #[inline]
+    unsafe fn buffer_as_mut_slice(&mut self) -> &mut [A::Item] {
+        slice::from_raw_parts_mut(self.ptr_mut(), A::capacity())
+    }
+
+    #[inline]
+    unsafe fn buffer_read(&mut self, offset: usize) -> A::Item {
+        ptr::read(self.ptr().offset(offset as isize))
+    }
+
+    #[inline]
+    unsafe fn buffer_write(&mut self, offset: usize, element: A::Item) {
+        ptr::write(self.ptr_mut().offset(offset as isize), element);
+    }
+}
+
+impl<A: Array> ArrayDeque<A> {
+    /// Creates an empty `ArrayDeque`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let vector: ArrayDeque<[usize; 3]> = ArrayDeque::new();
+    /// ```
+    #[inline]
+    pub fn new() -> ArrayDeque<A> {
+        unsafe {
+            ArrayDeque {
+                xs: NoDrop::new(::new_array()),
+                head: ArrayIndex::from(0),
+                tail: ArrayIndex::from(0),
+            }
+        }
+    }
+
+    /// Retrieves an element in the `ArrayDeque` by index.
+    ///
+    /// Element at index 0 is the front of the queue.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// buf.push_back(3);
+    /// buf.push_back(4);
+    /// buf.push_back(5);
+    /// assert_eq!(buf.get(1), Some(&4));
+    /// ```
+    #[inline]
+    pub fn get(&self, index: usize) -> Option<&A::Item> {
+        if index < self.len() {
+            let idx = Self::wrap_add(self.tail(), index);
+            unsafe { Some(&*self.ptr().offset(idx as isize)) }
+        } else {
+            None
+        }
+    }
+
+    /// Retrieves an element in the `ArrayDeque` mutably by index.
+    ///
+    /// Element at index 0 is the front of the queue.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// buf.push_back(3);
+    /// buf.push_back(4);
+    /// buf.push_back(5);
+    /// if let Some(elem) = buf.get_mut(1) {
+    ///     *elem = 7;
+    /// }
+    ///
+    /// assert_eq!(buf[1], 7);
+    /// ```
+    #[inline]
+    pub fn get_mut(&mut self, index: usize) -> Option<&mut A::Item> {
+        if index < self.len() {
+            let idx = Self::wrap_add(self.tail(), index);
+            unsafe { Some(&mut *self.ptr_mut().offset(idx as isize)) }
+        } else {
+            None
+        }
+    }
+
+    /// Swaps elements at indices `i` and `j`.
+    ///
+    /// `i` and `j` may be equal.
+    ///
+    /// Fails if there is no element with either index.
+    ///
+    /// Element at index 0 is the front of the queue.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// buf.push_back(3);
+    /// buf.push_back(4);
+    /// buf.push_back(5);
+    /// buf.swap(0, 2);
+    /// assert_eq!(buf[0], 5);
+    /// assert_eq!(buf[2], 3);
+    /// ```
+    #[inline]
+    pub fn swap(&mut self, i: usize, j: usize) {
+        assert!(i < self.len());
+        assert!(j < self.len());
+        let ri = Self::wrap_add(self.tail(), i);
+        let rj = Self::wrap_add(self.tail(), j);
+        unsafe {
+            ptr::swap(self.ptr_mut().offset(ri as isize),
+                      self.ptr_mut().offset(rj as isize))
+        }
+    }
+
+    /// Return the capacity of the `ArrayDeque`.
+    ///
+    /// # Capacity
+    ///
+    /// Note that the `capacity()` is always `backed_array.len() - 1`.
+    /// [Read more]
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[usize; 4]> = ArrayDeque::new();
+    /// assert_eq!(buf.capacity(), 3);
+    /// ```
+    ///
+    /// [Read more]: https://en.wikipedia.org/wiki/Circular_buffer
+    #[inline]
+    pub fn capacity(&self) -> usize {
+        A::capacity() - 1
+    }
+
+    /// Returns a front-to-back iterator.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// buf.push_back(5);
+    /// buf.push_back(3);
+    /// buf.push_back(4);
+    /// let b: &[_] = &[&5, &3, &4];
+    /// let c: Vec<&i32> = buf.iter().collect();
+    /// assert_eq!(&c[..], b);
+    /// ```
+    #[inline]
+    pub fn iter(&self) -> Iter<A::Item> {
+        Iter {
+            head: self.head(),
+            tail: self.tail(),
+            ring: unsafe { self.buffer_as_slice() },
+        }
+    }
+
+    /// Returns a front-to-back iterator that returns mutable references.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// buf.push_back(5);
+    /// buf.push_back(3);
+    /// buf.push_back(4);
+    /// for num in buf.iter_mut() {
+    ///     *num = *num - 2;
+    /// }
+    /// let b: &[_] = &[&mut 3, &mut 1, &mut 2];
+    /// assert_eq!(&buf.iter_mut().collect::<Vec<&mut i32>>()[..], b);
+    /// ```
+    #[inline]
+    pub fn iter_mut(&mut self) -> IterMut<A::Item> {
+        IterMut {
+            head: self.head(),
+            tail: self.tail(),
+            ring: unsafe { self.buffer_as_mut_slice() },
+        }
+    }
+
+    /// Returns a pair of slices which contain, in order, the contents of the
+    /// `ArrayDeque`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut vector: ArrayDeque<[_; 6]> = ArrayDeque::new();
+    ///
+    /// vector.push_back(0);
+    /// vector.push_back(1);
+    /// vector.push_back(2);
+    ///
+    /// assert_eq!(vector.as_slices(), (&[0, 1, 2][..], &[][..]));
+    ///
+    /// vector.push_front(10);
+    /// vector.push_front(9);
+    ///
+    /// assert_eq!(vector.as_slices(), (&[9, 10][..], &[0, 1, 2][..]));
+    /// ```
+    #[inline]
+    pub fn as_slices(&self) -> (&[A::Item], &[A::Item]) {
+        unsafe {
+            let (first, second) = (*(self as *const Self as *mut Self)).as_mut_slices();
+            (first, second)
+        }
+    }
+
+    /// Returns a pair of slices which contain, in order, the contents of the
+    /// `ArrayDeque`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut vector: ArrayDeque<[_; 5]> = ArrayDeque::new();
+    ///
+    /// vector.push_back(0);
+    /// vector.push_back(1);
+    ///
+    /// vector.push_front(10);
+    /// vector.push_front(9);
+    ///
+    /// vector.as_mut_slices().0[0] = 42;
+    /// vector.as_mut_slices().1[0] = 24;
+    /// assert_eq!(vector.as_slices(), (&[42, 10][..], &[24, 1][..]));
+    /// ```
+    #[inline]
+    pub fn as_mut_slices(&mut self) -> (&mut [A::Item], &mut [A::Item]) {
+        unsafe {
+            let contiguous = self.is_contiguous();
+            let head = self.head();
+            let tail = self.tail();
+            let buf = self.buffer_as_mut_slice();
+
+            if contiguous {
+                let (empty, buf) = buf.split_at_mut(0);
+                (&mut buf[tail..head], empty)
+            } else {
+                let (mid, right) = buf.split_at_mut(tail);
+                let (left, _) = mid.split_at_mut(head);
+
+                (right, left)
+            }
+        }
+    }
+
+    /// Returns the number of elements in the `ArrayDeque`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut v: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// assert_eq!(v.len(), 0);
+    /// v.push_back(1);
+    /// assert_eq!(v.len(), 1);
+    /// ```
+    #[inline]
+    pub fn len(&self) -> usize {
+        count(self.tail(), self.head(), A::capacity())
+    }
+
+    /// Returns true if the buffer contains no elements
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut v: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// assert!(v.is_empty());
+    /// v.push_front(1);
+    /// assert!(!v.is_empty());
+    /// ```
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.head() == self.tail()
+    }
+
+    /// Create a draining iterator that removes the specified range in the
+    /// `ArrayDeque` and yields the removed items.
+    ///
+    /// Note 1: The element range is removed even if the iterator is not
+    /// consumed until the end.
+    ///
+    /// Note 2: It is unspecified how many elements are removed from the deque,
+    /// if the `Drain` value is not dropped, but the borrow it holds expires
+    /// (eg. due to mem::forget).
+    ///
+    /// # Panics
+    ///
+    /// Panics if the starting point is greater than the end point or if
+    /// the end point is greater than the length of the vector.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut v: ArrayDeque<[_; 4]> = vec![1, 2, 3].into_iter().collect();
+    /// assert_eq!(vec![3].into_iter().collect::<ArrayDeque<[_; 4]>>(), v.drain(2..).collect());
+    /// assert_eq!(vec![1, 2].into_iter().collect::<ArrayDeque<[_; 4]>>(), v);
+    ///
+    /// // A full range clears all contents
+    /// v.drain(..);
+    /// assert!(v.is_empty());
+    /// ```
+    pub fn drain<R>(&mut self, range: R) -> Drain<A>
+        where R: RangeArgument<usize>
+    {
+        let len = self.len();
+        let start = range.start().unwrap_or(0);
+        let end = range.end().unwrap_or(len);
+        assert!(start <= end, "drain lower bound was too large");
+        assert!(end <= len, "drain upper bound was too large");
+
+        let drain_tail = Self::wrap_add(self.tail(), start);
+        let drain_head = Self::wrap_add(self.tail(), end);
+        let head = self.head();
+
+        unsafe { self.set_head(drain_tail) }
+
+        Drain {
+            deque: self as *mut _,
+            after_tail: drain_head,
+            after_head: head,
+            iter: Iter {
+                tail: drain_tail,
+                head: drain_head,
+                ring: unsafe { self.buffer_as_mut_slice() },
+            },
+        }
+    }
+
+    /// Clears the buffer, removing all values.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut v: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// v.push_back(1);
+    /// v.clear();
+    /// assert!(v.is_empty());
+    /// ```
+    #[inline]
+    pub fn clear(&mut self) {
+        self.drain(..);
+    }
+
+    /// Returns `true` if the `ArrayDeque` contains an element equal to the
+    /// given value.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut vector: ArrayDeque<[_; 3]> = ArrayDeque::new();
+    ///
+    /// vector.push_back(0);
+    /// vector.push_back(1);
+    ///
+    /// assert_eq!(vector.contains(&1), true);
+    /// assert_eq!(vector.contains(&10), false);
+    /// ```
+    pub fn contains(&self, x: &A::Item) -> bool
+        where A::Item: PartialEq<A::Item>
+    {
+        let (a, b) = self.as_slices();
+        a.contains(x) || b.contains(x)
+    }
+
+    /// Provides a reference to the front element, or `None` if the sequence is
+    /// empty.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut d: ArrayDeque<[_; 3]> = ArrayDeque::new();
+    /// assert_eq!(d.front(), None);
+    ///
+    /// d.push_back(1);
+    /// d.push_back(2);
+    /// assert_eq!(d.front(), Some(&1));
+    /// ```
+    pub fn front(&self) -> Option<&A::Item> {
+        if !self.is_empty() {
+            Some(&self[0])
+        } else {
+            None
+        }
+    }
+
+    /// Provides a mutable reference to the front element, or `None` if the
+    /// sequence is empty.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut d: ArrayDeque<[_; 3]> = ArrayDeque::new();
+    /// assert_eq!(d.front_mut(), None);
+    ///
+    /// d.push_back(1);
+    /// d.push_back(2);
+    /// match d.front_mut() {
+    ///     Some(x) => *x = 9,
+    ///     None => (),
+    /// }
+    /// assert_eq!(d.front(), Some(&9));
+    /// ```
+    pub fn front_mut(&mut self) -> Option<&mut A::Item> {
+        if !self.is_empty() {
+            Some(&mut self[0])
+        } else {
+            None
+        }
+    }
+
+    /// Provides a reference to the back element, or `None` if the sequence is
+    /// empty.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut d: ArrayDeque<[_; 3]> = ArrayDeque::new();
+    /// assert_eq!(d.back(), None);
+    ///
+    /// d.push_back(1);
+    /// d.push_back(2);
+    /// assert_eq!(d.back(), Some(&2));
+    /// ```
+    pub fn back(&self) -> Option<&A::Item> {
+        if !self.is_empty() {
+            Some(&self[self.len() - 1])
+        } else {
+            None
+        }
+    }
+
+    /// Provides a mutable reference to the back element, or `None` if the
+    /// sequence is empty.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut d: ArrayDeque<[_; 3]> = ArrayDeque::new();
+    /// assert_eq!(d.back(), None);
+    ///
+    /// d.push_back(1);
+    /// d.push_back(2);
+    /// match d.back_mut() {
+    ///     Some(x) => *x = 9,
+    ///     None => (),
+    /// }
+    /// assert_eq!(d.back(), Some(&9));
+    /// ```
+    pub fn back_mut(&mut self) -> Option<&mut A::Item> {
+        let len = self.len();
+        if !self.is_empty() {
+            Some(&mut self[len - 1])
+        } else {
+            None
+        }
+    }
+
+    /// Removes the first element and returns it, or `None` if the sequence is
+    /// empty.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut d: ArrayDeque<[_; 3]> = ArrayDeque::new();
+    /// d.push_back(1);
+    /// d.push_back(2);
+    ///
+    /// assert_eq!(d.pop_front(), Some(1));
+    /// assert_eq!(d.pop_front(), Some(2));
+    /// assert_eq!(d.pop_front(), None);
+    /// ```
+    pub fn pop_front(&mut self) -> Option<A::Item> {
+        if self.is_empty() {
+            return None;
+        }
+        unsafe {
+            let tail = self.tail();
+            self.set_tail(Self::wrap_add(tail, 1));
+            Some(self.buffer_read(tail))
+        }
+    }
+
+    /// Inserts an element first in the sequence.
+    ///
+    /// Return `None` if the push succeeds, or and return `Some(` *element* `)`
+    /// if the vector is full.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut d: ArrayDeque<[_; 3]> = ArrayDeque::new();
+    /// d.push_front(1);
+    /// d.push_front(2);
+    /// let overflow = d.push_front(3);
+    ///
+    /// assert_eq!(d.front(), Some(&2));
+    /// assert_eq!(overflow, Some(3));
+    /// ```
+    pub fn push_front(&mut self, element: A::Item) -> Option<A::Item> {
+        if !self.is_full() {
+            unsafe {
+                let new_tail = Self::wrap_sub(self.tail(), 1);
+                self.set_tail(new_tail);
+                self.buffer_write(new_tail, element);
+            }
+            None
+        } else {
+            Some(element)
+        }
+    }
+
+    /// Appends an element to the back of a buffer
+    ///
+    /// Return `None` if the push succeeds, or and return `Some(` *element* `)`
+    /// if the vector is full.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[_; 3]> = ArrayDeque::new();
+    /// buf.push_back(1);
+    /// buf.push_back(3);
+    /// let overflow = buf.push_back(5);
+    ///
+    /// assert_eq!(3, *buf.back().unwrap());
+    /// assert_eq!(overflow, Some(5));
+    /// ```
+    pub fn push_back(&mut self, element: A::Item) -> Option<A::Item> {
+        if !self.is_full() {
+            unsafe {
+                let head = self.head();
+                self.set_head(Self::wrap_add(head, 1));
+                self.buffer_write(head, element);
+            }
+            None
+        } else {
+            Some(element)
+        }
+    }
+
+    /// Removes the last element from a buffer and returns it, or `None` if
+    /// it is empty.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[_; 3]> = ArrayDeque::new();
+    /// assert_eq!(buf.pop_back(), None);
+    /// buf.push_back(1);
+    /// buf.push_back(3);
+    /// assert_eq!(buf.pop_back(), Some(3));
+    /// ```
+    pub fn pop_back(&mut self) -> Option<A::Item> {
+        if self.is_empty() {
+            return None;
+        }
+        unsafe {
+            let new_head = Self::wrap_sub(self.head(), 1);
+            self.set_head(new_head);
+            Some(self.buffer_read(new_head))
+        }
+    }
+
+    /// Removes an element from anywhere in the `ArrayDeque` and returns it, replacing it with the
+    /// last element.
+    ///
+    /// This does not preserve ordering, but is O(1).
+    ///
+    /// Returns `None` if `index` is out of bounds.
+    ///
+    /// Element at index 0 is the front of the queue.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// assert_eq!(buf.swap_remove_back(0), None);
+    /// buf.push_back(1);
+    /// buf.push_back(2);
+    /// buf.push_back(3);
+    ///
+    /// assert_eq!(buf.swap_remove_back(0), Some(1));
+    /// assert_eq!(buf.len(), 2);
+    /// assert_eq!(buf[0], 3);
+    /// assert_eq!(buf[1], 2);
+    /// ```
+    pub fn swap_remove_back(&mut self, index: usize) -> Option<A::Item> {
+        let length = self.len();
+        if length > 0 && index < length - 1 {
+            self.swap(index, length - 1);
+        } else if index >= length {
+            return None;
+        }
+        self.pop_back()
+    }
+
+    /// Removes an element from anywhere in the `ArrayDeque` and returns it,
+    /// replacing it with the first element.
+    ///
+    /// This does not preserve ordering, but is O(1).
+    ///
+    /// Returns `None` if `index` is out of bounds.
+    ///
+    /// Element at index 0 is the front of the queue.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use arraydeque::ArrayDeque;
+    ///
+    /// let mut buf: ArrayDeque<[_; 4]> = ArrayDeque::new();
+    /// assert_eq!(buf.swap_remove_front(0), None);
+    /// buf.push_back(1);
+    /// buf.push_back(2);
+    /// buf.push_back(3);
+    ///
+    /// assert_eq!(buf.swap_remove_front(2), Some(3));
+    /// assert_eq!(buf.len(), 2);
+    /// assert_eq!(buf[0], 2);
+    /// assert_eq!(buf[1], 1);
+    /// ```
+    pub fn swap_remove_front(&mut self, index: usize) -> Option<A::Item> {
+        let length = self.len();
+        if length > 0 && index < length && index != 0 {
+            self.swap(index, 0);
+        } else if index >= length {
+            return None;
+        }
+        self.pop_front()
+    }
+
+    /// Inserts an element at `index` within the `ArrayDeque`. Whichever
+    /// end is closer to the insertion point will be moved to make room,
+    /// and all the affected elements will be moved to new positions.
+    ///
+    /// Return `None` if the push succeeds, or and return `Some(` *element* `)`
+    /// if the vector is full.
+    ///
+    /// Element at index 0 is the front of the queue.