merge mozilla-central to mozilla-inbound. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 06 Sep 2017 11:29:25 +0200
changeset 428790 21229f14023fd62a4acaf63ab16b3f12f58e722d
parent 428789 35cf15dcadbf914aa253a44d661b9603a0118635 (current diff)
parent 428628 c959327c6b75cd4930a6ea087583c38b805e7524 (diff)
child 428791 b876f2c2f66305a5d673025eb84eab7cf648a73a
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-central to mozilla-inbound. r=merge a=merge
browser/base/content/usercontext-briefcase.svg
browser/base/content/usercontext-cart.svg
browser/base/content/usercontext-chill.svg
browser/base/content/usercontext-circle.svg
browser/base/content/usercontext-dollar.svg
browser/base/content/usercontext-fingerprint.svg
browser/base/content/usercontext-food.svg
browser/base/content/usercontext-fruit.svg
browser/base/content/usercontext-gift.svg
browser/base/content/usercontext-pet.svg
browser/base/content/usercontext-tree.svg
browser/base/content/usercontext-vacation.svg
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -1210,17 +1210,17 @@ nsRoleMapEntry aria::gEmptyRoleMap = {
  * The following state rules are applied to any accessible element,
  * whether there is an ARIA role or not:
  */
 static const EStateRule sWAIUnivStateMap[] = {
   eARIABusy,
   eARIACurrent,
   eARIADisabled,
   eARIAExpanded,  // Currently under spec review but precedent exists
-  eARIAHasPopup,  // Note this is technically a "property"
+  eARIAHasPopup,  // Note this is a tokenised attribute starting in ARIA 1.1
   eARIAInvalid,
   eARIAModal,
   eARIARequired,  // XXX not global, Bug 553117
   eARIANone
 };
 
 
 /**
@@ -1243,17 +1243,17 @@ static const AttrCharacteristics gWAIUni
   {&nsGkAtoms::aria_describedby,       ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_details,           ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_disabled,          ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_dropeffect,                         ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_errormessage,      ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_expanded,          ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
   {&nsGkAtoms::aria_flowto,            ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_grabbed,                            ATTR_VALTOKEN | ATTR_GLOBAL },
-  {&nsGkAtoms::aria_haspopup,          ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
+  {&nsGkAtoms::aria_haspopup,          ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_hidden,            ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, /* handled special way */
   {&nsGkAtoms::aria_invalid,           ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_label,             ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_labelledby,        ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_level,             ATTR_BYPASSOBJ                               }, /* handled via groupPosition */
   {&nsGkAtoms::aria_live,                               ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_modal,             ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_multiline,         ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -951,20 +951,16 @@ Accessible::Attributes()
   while(attribIter.Next(name, value))
     attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
 
   if (IsARIAHidden()) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::hidden,
                            NS_LITERAL_STRING("true"));
   }
 
-  // XXX: In ARIA 1.1, the value of aria-haspopup became a token (bug 1355449).
-  if (aria::UniversalStatesFor(mContent->AsElement()) & states::HASPOPUP)
-    nsAccUtils::SetAccAttr(attributes, nsGkAtoms::haspopup, NS_LITERAL_STRING("true"));
-
   // If there is no aria-live attribute then expose default value of 'live'
   // object attribute used for ARIA role of this accessible.
   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
   if (roleMapEntry) {
     if (roleMapEntry->Is(nsGkAtoms::searchbox)) {
       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType,
                              NS_LITERAL_STRING("search"));
     }
--- a/accessible/tests/mochitest/attributes/test_obj.html
+++ b/accessible/tests/mochitest/attributes/test_obj.html
@@ -35,16 +35,20 @@ https://bugzilla.mozilla.org/show_bug.cg
       testAttrs("checkedOption", {"checkable": "true"}, true);
       testAttrs("checkedRadio", {"checkable": "true"}, true);
       testAttrs("checkedTreeitem", {"checkable": "true"}, true);
       testAttrs("dropeffect", {"dropeffect": "copy"}, true);
       testAttrs("grabbed", {"grabbed": "true"}, true);
       testAttrs("haspopupTrue", { "haspopup": "true" }, true);
       testAbsentAttrs("haspopupFalse", { "haspopup": "false" });
       testAbsentAttrs("haspopupEmpty", { "haspopup": "" });
+      testAttrs("haspopupDialog", { "haspopup": "dialog" }, true);
+      testAttrs("haspopupListbox", { "haspopup": "listbox" }, true);
+      testAttrs("haspopupMenu", { "haspopup": "menu" }, true);
+      testAttrs("haspopupTree", { "haspopup": "tree" }, true);
       testAttrs("hidden", {"hidden": "true"}, true);
       testAbsentAttrs("hidden_false", { "hidden": "false" });
       testAbsentAttrs("modal", {"modal": "true"});
       testAttrs("sortAscending", {"sort": "ascending"}, true);
       testAttrs("sortDescending", {"sort": "descending"}, true);
       testAttrs("sortNone", {"sort": "none"}, true);
       testAttrs("sortOther", {"sort": "other"}, true);
       testAttrs("roledescr", {"roledescription": "spreadshit"}, true);
@@ -213,16 +217,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <div id="checkedOption" role="option" aria-checked="true"></div>
   <div id="checkedRadio" role="radio" aria-checked="true"></div>
   <div id="checkedTreeitem" role="treeitem" aria-checked="true"></div>
   <div id="dropeffect" aria-dropeffect="copy"></div>
   <div id="grabbed" aria-grabbed="true"></div>
   <div id="haspopupTrue" aria-haspopup="true"></div>
   <div id="haspopupFalse" aria-haspopup="false"></div>
   <div id="haspopupEmpty" aria-haspopup=""></div>
+  <div id="haspopupDialog" aria-haspopup="dialog"></div>
+  <div id="haspopupListbox" aria-haspopup="listbox"></div>
+  <div id="haspopupMenu" aria-haspopup="menu"></div>
+  <div id="haspopupTree" aria-haspopup="tree"></div>
   <div id="hidden" aria-hidden="true"></div>
   <div id="hidden_false" aria-hidden="false"></div>
   <div id="modal" aria-modal="true"></div>
   <div id="sortAscending" role="columnheader" aria-sort="ascending"></div>
   <div id="sortDescending" role="columnheader" aria-sort="descending"></div>
   <div id="sortNone" role="columnheader" aria-sort="none"></div>
   <div id="sortOther" role="columnheader" aria-sort="other"></div>
   <div id="roledescr" aria-roledescription="spreadshit"></div>
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -432,19 +432,19 @@
            role="group"
            type="arrow"
            hidden="true"
            flip="slide"
            photon="true"
            position="bottomcenter topright"
            tabspecific="true"
            noautofocus="true"
-           copyURL-title="&copyLinkCmd.label;"
+           copyURL-title="&copyURLCmd.label;"
            emailLink-title="&emailPageCmd.label;"
-           sendToDevice-title="&sendTabToDevice.label;"
+           sendToDevice-title="&sendToDevice.label3;"
            sendToDevice-notReadyTitle="&sendToDevice.syncNotReady.label;">
       <photonpanelmultiview id="pageActionPanelMultiView"
                             mainViewId="pageActionPanelMainView"
                             viewCacheId="appMenu-viewCache">
         <panelview id="pageActionPanelMainView"
                    context="pageActionPanelContextMenu"
                    oncontextmenu="BrowserPageActions.onContextMenu(event);"
                    class="PanelUI-subView">
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -132,28 +132,16 @@ browser.jar:
         content/browser/sanitizeDialog.js             (content/sanitizeDialog.js)
         content/browser/sanitizeDialog.css            (content/sanitizeDialog.css)
         content/browser/contentSearchUI.js            (content/contentSearchUI.js)
         content/browser/contentSearchUI.css           (content/contentSearchUI.css)
         content/browser/tabbrowser.css                (content/tabbrowser.css)
         content/browser/tabbrowser.xml                (content/tabbrowser.xml)
 *       content/browser/urlbarBindings.xml            (content/urlbarBindings.xml)
         content/browser/utilityOverlay.js             (content/utilityOverlay.js)
-        content/browser/usercontext-briefcase.svg     (content/usercontext-briefcase.svg)
-        content/browser/usercontext-cart.svg          (content/usercontext-cart.svg)
-        content/browser/usercontext-circle.svg        (content/usercontext-circle.svg)
-        content/browser/usercontext-dollar.svg        (content/usercontext-dollar.svg)
-        content/browser/usercontext-fingerprint.svg   (content/usercontext-fingerprint.svg)
-        content/browser/usercontext-gift.svg          (content/usercontext-gift.svg)
-        content/browser/usercontext-vacation.svg      (content/usercontext-vacation.svg)
-        content/browser/usercontext-food.svg          (content/usercontext-food.svg)
-        content/browser/usercontext-fruit.svg         (content/usercontext-fruit.svg)
-        content/browser/usercontext-pet.svg           (content/usercontext-pet.svg)
-        content/browser/usercontext-tree.svg          (content/usercontext-tree.svg)
-        content/browser/usercontext-chill.svg         (content/usercontext-chill.svg)
         content/browser/web-panels.js                 (content/web-panels.js)
 *       content/browser/web-panels.xul                (content/web-panels.xul)
         content/browser/webext-panels.js              (content/webext-panels.js)
 *       content/browser/webext-panels.xul             (content/webext-panels.xul)
 *       content/browser/baseMenuOverlay.xul           (content/baseMenuOverlay.xul)
         content/browser/nsContextMenu.js              (content/nsContextMenu.js)
 # XXX: We should exclude this one as well (bug 71895)
 *       content/browser/hiddenWindow.xul              (content/hiddenWindow.xul)
rename from browser/base/content/usercontext-briefcase.svg
rename to browser/components/contextualidentity/content/briefcase.svg
rename from browser/base/content/usercontext-cart.svg
rename to browser/components/contextualidentity/content/cart.svg
rename from browser/base/content/usercontext-chill.svg
rename to browser/components/contextualidentity/content/chill.svg
rename from browser/base/content/usercontext-circle.svg
rename to browser/components/contextualidentity/content/circle.svg
rename from browser/base/content/usercontext-dollar.svg
rename to browser/components/contextualidentity/content/dollar.svg
rename from browser/base/content/usercontext-fingerprint.svg
rename to browser/components/contextualidentity/content/fingerprint.svg
rename from browser/base/content/usercontext-food.svg
rename to browser/components/contextualidentity/content/food.svg
rename from browser/base/content/usercontext-fruit.svg
rename to browser/components/contextualidentity/content/fruit.svg
rename from browser/base/content/usercontext-gift.svg
rename to browser/components/contextualidentity/content/gift.svg
rename from browser/base/content/usercontext-pet.svg
rename to browser/components/contextualidentity/content/pet.svg
rename from browser/base/content/usercontext-tree.svg
rename to browser/components/contextualidentity/content/tree.svg
--- a/browser/components/contextualidentity/content/usercontext.css
+++ b/browser/components/contextualidentity/content/usercontext.css
@@ -34,61 +34,61 @@
 }
 
 [data-identity-color="purple"] {
   --identity-tab-color: #af51f5;
   --identity-icon-color: #af51f5;
 }
 
 [data-identity-icon="fingerprint"] {
-  --identity-icon: url("chrome://browser/content/usercontext-fingerprint.svg");
+  --identity-icon: url("resource://usercontext-content/fingerprint.svg");
 }
 
 [data-identity-icon="briefcase"] {
-  --identity-icon: url("chrome://browser/content/usercontext-briefcase.svg");
+  --identity-icon: url("resource://usercontext-content/briefcase.svg");
 }
 
 [data-identity-icon="dollar"] {
-  --identity-icon: url("chrome://browser/content/usercontext-dollar.svg");
+  --identity-icon: url("resource://usercontext-content/dollar.svg");
 }
 
 [data-identity-icon="cart"] {
-  --identity-icon: url("chrome://browser/content/usercontext-cart.svg");
+  --identity-icon: url("resource://usercontext-content/cart.svg");
 }
 
 [data-identity-icon="circle"] {
-  --identity-icon: url("chrome://browser/content/usercontext-circle.svg");
+  --identity-icon: url("resource://usercontext-content/circle.svg");
 }
 
 [data-identity-icon="vacation"] {
-  --identity-icon: url("chrome://browser/content/usercontext-vacation.svg");
+  --identity-icon: url("resource://usercontext-content/vacation.svg");
 }
 
 [data-identity-icon="gift"] {
-  --identity-icon: url("chrome://browser/content/usercontext-gift.svg");
+  --identity-icon: url("resource://usercontext-content/gift.svg");
 }
 
 [data-identity-icon="food"] {
-  --identity-icon: url("chrome://browser/content/usercontext-food.svg");
+  --identity-icon: url("resource://usercontext-content/food.svg");
 }
 
 [data-identity-icon="fruit"] {
-  --identity-icon: url("chrome://browser/content/usercontext-fruit.svg");
+  --identity-icon: url("resource://usercontext-content/fruit.svg");
 }
 
 [data-identity-icon="pet"] {
-  --identity-icon: url("chrome://browser/content/usercontext-pet.svg");
+  --identity-icon: url("resource://usercontext-content/pet.svg");
 }
 
 [data-identity-icon="tree"] {
-  --identity-icon: url("chrome://browser/content/usercontext-tree.svg");
+  --identity-icon: url("resource://usercontext-content/tree.svg");
 }
 
 [data-identity-icon="chill"] {
-  --identity-icon: url("chrome://browser/content/usercontext-chill.svg");
+  --identity-icon: url("resource://usercontext-content/chill.svg");
 }
 
 #userContext-indicator {
   height: 16px;
   width: 16px;
 }
 
 #userContext-label {
rename from browser/base/content/usercontext-vacation.svg
rename to browser/components/contextualidentity/content/vacation.svg
--- a/browser/components/contextualidentity/jar.mn
+++ b/browser/components/contextualidentity/jar.mn
@@ -1,6 +1,20 @@
 # 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/.
 
 browser.jar:
     content/browser/usercontext/usercontext.css (content/usercontext.css)
+
+% resource usercontext-content %content/ contentaccessible=yes
+    content/briefcase.svg     (content/briefcase.svg)
+    content/cart.svg          (content/cart.svg)
+    content/circle.svg        (content/circle.svg)
+    content/dollar.svg        (content/dollar.svg)
+    content/fingerprint.svg   (content/fingerprint.svg)
+    content/gift.svg          (content/gift.svg)
+    content/vacation.svg      (content/vacation.svg)
+    content/food.svg          (content/food.svg)
+    content/fruit.svg         (content/fruit.svg)
+    content/pet.svg           (content/pet.svg)
+    content/tree.svg          (content/tree.svg)
+    content/chill.svg         (content/chill.svg)
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -251,16 +251,22 @@ const CustomizableWidgets = [
       DECKINDEX_TABS: 0,
       DECKINDEX_TABSDISABLED: 1,
       DECKINDEX_FETCHING: 2,
       DECKINDEX_NOCLIENTS: 3,
     },
     TABS_PER_PAGE: 25,
     NEXT_PAGE_MIN_TABS: 5, // Minimum number of tabs displayed when we click "Show All"
     onCreated(aNode) {
+      this._initialize(aNode);
+    },
+    _initialize(aNode) {
+      if (this._initialized) {
+        return;
+      }
       // Add an observer to the button so we get the animation during sync.
       // (Note the observer sets many attributes, including label and
       // tooltiptext, but we only want the 'syncstatus' attribute for the
       // animation)
       let doc = aNode.ownerDocument;
       let obnode = doc.createElementNS(kNSXUL, "observes");
       obnode.setAttribute("element", "sync-status");
       obnode.setAttribute("attribute", "syncstatus");
@@ -295,18 +301,20 @@ const CustomizableWidgets = [
         let os = e.target.getAttribute("mobile-promo-os");
         if (!os || e.button > 1) {
           return;
         }
         let link = Services.prefs.getCharPref(`identity.mobilepromo.${os}`) + "synced-tabs";
         doc.defaultView.openUILinkIn(link, "tab");
         CustomizableUI.hidePanelForNode(e.target);
       });
+      this._initialized = true;
     },
     onViewShowing(aEvent) {
+      this._initialize(aEvent.target);
       let doc = aEvent.target.ownerDocument;
       this._tabsList = doc.getElementById("PanelUI-remotetabs-tabslist");
       Services.obs.addObserver(this, SyncedTabs.TOPIC_TABS_CHANGED);
 
       if (SyncedTabs.isConfiguredToSyncTabs) {
         if (SyncedTabs.hasSyncedThisSession) {
           this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
         } else {
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -531,37 +531,42 @@ FormAutofillHandler.prototype = {
         // Try to abbreviate the value of select element.
         if (type == "address" &&
             detail.fieldName == "address-level1" &&
             element instanceof Ci.nsIDOMHTMLSelectElement) {
           // Don't save the record when the option value is empty *OR* there
           // are multiple options being selected. The empty option is usually
           // assumed to be default along with a meaningless text to users.
           if (!value || element.selectedOptions.length != 1) {
+            // Keep the property and preserve more information for address updating
+            data[type].record[detail.fieldName] = "";
             return;
           }
 
           let text = element.selectedOptions[0].text.trim();
           value = FormAutofillUtils.getAbbreviatedStateName([value, text]) || text;
         }
 
         if (!value) {
+          // Keep the property and preserve more information for updating
+          data[type].record[detail.fieldName] = "";
           return;
         }
 
         data[type].record[detail.fieldName] = value;
 
         if (detail.state == "AUTO_FILLED") {
           data[type].untouchedFields.push(detail.fieldName);
         }
       });
     });
 
     if (data.address &&
-        Object.keys(data.address.record).length < FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD) {
+        Object.values(data.address.record).filter(v => v).length <
+        FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD) {
       log.debug("No address record saving since there are only",
                      Object.keys(data.address.record).length,
                      "usable fields");
       delete data.address;
     }
 
     if (data.creditCard && (!data.creditCard.record["cc-number"] ||
         !FormAutofillUtils.isCCNumber(data.creditCard.record["cc-number"]))) {
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -365,17 +365,17 @@ FormAutofillParent.prototype = {
           switch (state) {
             case "create":
               if (!changedGUIDs.length) {
                 changedGUIDs.push(this.profileStorage.addresses.add(address.record));
               }
               break;
             case "update":
               if (!changedGUIDs.length) {
-                this.profileStorage.addresses.update(address.guid, address.record);
+                this.profileStorage.addresses.update(address.guid, address.record, true);
                 changedGUIDs.push(address.guid);
               } else {
                 this.profileStorage.addresses.remove(address.guid);
               }
               break;
           }
           changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid));
         });
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -369,36 +369,44 @@ class AutofillRecords {
 
   /**
    * Update the specified record.
    *
    * @param  {string} guid
    *         Indicates which record to update.
    * @param  {Object} record
    *         The new record used to overwrite the old one.
+   * @param  {boolean} [preserveOldProperties = false]
+   *         Preserve old record's properties if they don't exist in new record.
    */
-  update(guid, record) {
+  update(guid, record, preserveOldProperties = false) {
     this.log.debug("update:", guid, record);
 
     let recordFound = this._findByGUID(guid);
     if (!recordFound) {
       throw new Error("No matching record.");
     }
 
-    let recordToUpdate = this._clone(record);
+    // Clone the record by Object assign API to preserve the property with empty string.
+    let recordToUpdate = Object.assign({}, record);
     this._normalizeRecord(recordToUpdate);
 
     for (let field of this.VALID_FIELDS) {
       let oldValue = recordFound[field];
       let newValue = recordToUpdate[field];
 
-      if (newValue != null) {
+      // Resume the old field value in the perserve case
+      if (preserveOldProperties && newValue === undefined) {
+        newValue = oldValue;
+      }
+
+      if (!newValue) {
+        delete recordFound[field];
+      } else {
         recordFound[field] = newValue;
-      } else {
-        delete recordFound[field];
       }
 
       this._maybeStoreLastSyncedField(recordFound, field, oldValue);
     }
 
     recordFound.timeLastModified = Date.now();
     let syncMetadata = this._getSyncMetaData(recordFound);
     if (syncMetadata) {
--- a/browser/extensions/formautofill/test/browser/browser.ini
+++ b/browser/extensions/formautofill/test/browser/browser.ini
@@ -1,13 +1,14 @@
 [DEFAULT]
 head = head.js
 
 support-files =
   ../fixtures/autocomplete_basic.html
+  ../fixtures/autocomplete_simple_basic.html
   ../fixtures/autocomplete_creditcard_basic.html
 
 [browser_autocomplete_footer.js]
 [browser_autocomplete_marked_back_forward.js]
 [browser_autocomplete_marked_detached_tab.js]
 [browser_check_installed.js]
 [browser_creditCard_doorhanger.js]
 [browser_dropdown_layout.js]
--- a/browser/extensions/formautofill/test/browser/browser_update_doorhanger.js
+++ b/browser/extensions/formautofill/test/browser/browser_update_doorhanger.js
@@ -132,8 +132,43 @@ add_task(async function test_submit_unto
     }
   );
 
   addresses = await getAddresses();
   is(addresses.length, 2, "Still 2 addresses in storage");
   is(addresses[0].organization, "Organization", "organization should change");
   is(addresses[0].tel, "+16172535702", "tel should remain unchanged");
 });
+
+add_task(async function test_submit_reduced_fields() {
+  let addresses = await getAddresses();
+  is(addresses.length, 2, "2 addresses in storage");
+
+  let url = BASE_URL + "autocomplete_simple_basic.html";
+  await BrowserTestUtils.withNewTab({gBrowser, url},
+    async function(browser) {
+      let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
+                                                       "popupshown");
+      await openPopupOn(browser, "form#simple input[name=tel]");
+      await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
+      await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
+
+      await ContentTask.spawn(browser, null, async function() {
+        let form = content.document.querySelector("form#simple");
+        let tel = form.querySelector("input[name=tel]");
+        await new Promise(resolve => setTimeout(resolve, 1000));
+        tel.setUserInput("123456789");
+
+        // Wait 1000ms before submission to make sure the input value applied
+        await new Promise(resolve => setTimeout(resolve, 1000));
+        form.querySelector("input[type=submit]").click();
+      });
+
+      await promiseShown;
+      await clickDoorhangerButton(MAIN_BUTTON);
+    }
+  );
+
+  addresses = await getAddresses();
+  is(addresses.length, 2, "Still 2 addresses in storage");
+  is(addresses[0].tel, "123456789", "tel should should be changed");
+  is(addresses[0]["postal-code"], "02139", "postal code should be kept");
+});
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/fixtures/autocomplete_simple_basic.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Form Autofill Demo Page for Simplified Form Case</title>
+</head>
+<body>
+  <h1>Form Autofill Demo Page for Simplified Form Case</h1>
+
+  <form id="simple">
+    <p><label>Organization: <input type="text" /></label></p>
+    <p><label>Telephone: <input id="simple_tel" name="tel" /></label></p>
+    <p><label>Email: <input type="text" id="simple_email" name="email" /></label></p>
+    <p><input type="submit" /></p>
+    <p><button type="reset">Reset</button></p>
+  </form>
+
+</body>
+</html>
--- a/browser/extensions/formautofill/test/unit/test_addressRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_addressRecords.js
@@ -21,27 +21,34 @@ const TEST_ADDRESS_1 = {
 };
 
 const TEST_ADDRESS_2 = {
   "street-address": "Some Address",
   country: "US",
 };
 
 const TEST_ADDRESS_3 = {
+  "given-name": "Timothy",
+  "family-name": "Berners-Lee",
   "street-address": "Other Address",
   "postal-code": "12345",
 };
 
 const TEST_ADDRESS_4 = {
   "given-name": "Timothy",
   "additional-name": "John",
   "family-name": "Berners-Lee",
   organization: "World Wide Web Consortium",
 };
 
+const TEST_ADDRESS_FOR_UPDATE = {
+  "name": "Tim Berners",
+  "street-address": "",
+};
+
 const TEST_ADDRESS_WITH_INVALID_FIELD = {
   "street-address": "Another Address",
   invalidField: "INVALID",
 };
 
 const MERGE_TESTCASES = [
   {
     description: "Merge a superset",
@@ -317,16 +324,32 @@ add_task(async function test_update() {
 
   let address = profileStorage.addresses.get(guid, {rawData: true});
 
   do_check_eq(address.country, undefined);
   do_check_neq(address.timeLastModified, timeLastModified);
   do_check_record_matches(address, TEST_ADDRESS_3);
   do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 1);
 
+  // Test preserveOldProperties parameter and field with empty string.
+  profileStorage.addresses.update(guid, TEST_ADDRESS_FOR_UPDATE, true);
+  await onChanged;
+  await profileStorage._saveImmediately();
+
+  profileStorage.addresses.pullSyncChanges(); // force sync metadata, which we check below.
+
+  address = profileStorage.addresses.get(guid, {rawData: true});
+
+  do_check_eq(address["given-name"], "Tim");
+  do_check_eq(address["family-name"], "Berners");
+  do_check_eq(address["street-address"], undefined);
+  do_check_eq(address["postal-code"], "12345");
+  do_check_neq(address.timeLastModified, timeLastModified);
+  do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 2);
+
   Assert.throws(
     () => profileStorage.addresses.update("INVALID_GUID", TEST_ADDRESS_3),
     /No matching record\./
   );
 
   Assert.throws(
     () => profileStorage.addresses.update(guid, TEST_ADDRESS_WITH_INVALID_FIELD),
     /"invalidField" is not a valid field\./
--- a/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
+++ b/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
@@ -61,17 +61,20 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
+            "address-level1": "",
+            "address-level2": "",
             "country": "USA",
+            "email": "",
             "tel": "1-650-903-0800",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
@@ -111,17 +114,20 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
+            "address-level1": "",
+            "address-level2": "",
             "country": "USA",
+            "email": "",
             "tel": "1-650-903-0800",
           },
           untouchedFields: [],
         },
         creditCard: {
           guid: null,
           record: {
             "cc-name": "John Doe",
@@ -143,17 +149,20 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
+            "address-level1": "",
+            "address-level2": "",
             "country": "USA",
+            "email": "",
             "tel": "1-650-903-0800",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
@@ -166,17 +175,20 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
+            "address-level1": "",
+            "address-level2": "",
             "country": "USA",
+            "email": "",
             "tel": "1-650-903-0800",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
@@ -188,18 +200,21 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "address-level1": "CA",
+            "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
+            "email": "",
+            "tel": "",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Save state with lowercase value",
@@ -210,18 +225,21 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "address-level1": "CA",
+            "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
+            "email": "",
+            "tel": "",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Save state with a country code prefixed to the label",
@@ -232,18 +250,21 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "address-level1": "AR",
+            "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
+            "email": "",
+            "tel": "",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Save state with a country code prefixed to the value",
@@ -254,18 +275,21 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "address-level1": "CA",
+            "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
+            "email": "",
+            "tel": "",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Save state with a country code prefixed to the value and label",
@@ -276,18 +300,21 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "address-level1": "AZ",
+            "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
+            "email": "",
+            "tel": "",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Should save select label instead when failed to abbreviate the value",
@@ -298,18 +325,21 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "address-level1": "Arizonac",
+            "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
+            "email": "",
+            "tel": "",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Shouldn't save select with multiple selections",
@@ -321,18 +351,21 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
+            "address-level1": "",
+            "address-level2": "",
             "country": "USA",
             "tel": "1-650-903-0800",
+            "email": "",
           },
           untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Shouldn't save select with empty value",
@@ -344,18 +377,21 @@ const TESTCASES = [
     },
     expectedResult: {
       formSubmission: true,
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
+            "address-level1": "",
+            "address-level2": "",
             "country": "USA",
             "tel": "1-650-903-0800",
+            "email": "",
           },
           untouchedFields: [],
         },
       },
     },
   },
 
 ];
--- a/browser/extensions/pocket/skin/shared/pocket.css
+++ b/browser/extensions/pocket/skin/shared/pocket.css
@@ -114,27 +114,27 @@
   to {
     transform: translateX(-1056px);
     fill: #ef4056;
   }
 }
 
 @keyframes library-pocket-animation-rtl {
   from {
-    transform: scaleX(-1) translateX(0);
+    transform: translateX(1056px);
     fill: inherit;
   }
   25% {
     fill: inherit;
   }
   50% {
     fill: #ef4056;
   }
   to {
-    transform: scaleX(-1) translateX(-1056px);
+    transform: translateX(0);
     fill: #ef4056;
   }
 }
 
 /* We need to use an animation here instead of a transition
    to guarantee that the animation succeeds. With transitions
    if the starting value is already equal to the end value
    then no transition will occur and thus no transitionend event. */
@@ -152,17 +152,16 @@
   width: 1078px;
   animation-name: library-pocket-animation;
   animation-duration: 800ms;
   animation-timing-function: steps(48);
 }
 
 .toolbarbutton-animatable-box[animate="pocket"]:-moz-locale-dir(rtl) > .toolbarbutton-animatable-image {
   animation-name: library-pocket-animation-rtl;
-  transform: scaleX(-1);
 }
 
 .toolbarbutton-animatable-box[animate="pocket"][fade] > .toolbarbutton-animatable-image {
   animation-name: library-pocket-fade;
   animation-duration: 2s;
   animation-timing-function: ease-out;
 }
 
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -1432,17 +1432,20 @@ Function CheckInstall
     System::Call 'kernel32::CloseHandle(i $HandleDownload)'
     StrCpy $ExitCode "${ERR_INSTALL_TIMEOUT}"
     ; Use a timer so the UI has a chance to update
     ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
     Return
   ${EndIf}
 
   ${If} $ProgressCompleted < ${PROGRESS_BAR_INSTALL_END_STEP}
-    IntOp $ProgressCompleted $ProgressCompleted + 1
+    IntOp $0 ${PROGRESS_BAR_INSTALL_END_STEP} - ${PROGRESS_BAR_DOWNLOAD_END_STEP}
+    IntOp $0 $InstallCounterStep * $0
+    IntOp $0 $0 / $InstallTotalSteps
+    IntOp $ProgressCompleted ${PROGRESS_BAR_DOWNLOAD_END_STEP} + $0
     Call SetProgressBars
   ${EndIf}
 
   ${If} ${FileExists} "$INSTDIR\install.log"
     Delete "$INSTDIR\install.tmp"
     CopyFiles /SILENT "$INSTDIR\install.log" "$INSTDIR\install.tmp"
 
     ; The unfocus and refocus that happens approximately here is caused by the
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -548,17 +548,17 @@ These should match what Safari and other
 <!ENTITY viewBGImageCmd.accesskey     "w">
 <!ENTITY setDesktopBackgroundCmd.label      "Set As Desktop Background…">
 <!ENTITY setDesktopBackgroundCmd.accesskey  "S">
 <!ENTITY bookmarkPageCmd2.label       "Bookmark This Page">
 <!ENTITY bookmarkThisLinkCmd.label      "Bookmark This Link">
 <!ENTITY bookmarkThisLinkCmd.accesskey  "L">
 <!ENTITY bookmarkThisFrameCmd.label      "Bookmark This Frame">
 <!ENTITY bookmarkThisFrameCmd.accesskey  "m">
-<!ENTITY copyLinkCmd.label             "Copy Link">
+<!ENTITY copyURLCmd.label             "Copy URL">
 <!ENTITY copyURLFeedback.label        "Copied!">
 <!ENTITY emailPageCmd.label           "Email Link…">
 <!ENTITY emailPageCmd.accesskey       "E">
 <!ENTITY savePageCmd.label            "Save Page As…">
 <!ENTITY savePageCmd.accesskey        "A">
 <!-- alternate for content area context menu -->
 <!ENTITY savePageCmd.accesskey2       "P">
 <!ENTITY savePageCmd.commandkey       "s">
@@ -969,17 +969,17 @@ you can use these alternative items. Oth
 <!ENTITY updateRestart.cancelButton.label "Not Now">
 <!ENTITY updateRestart.cancelButton.accesskey "N">
 <!ENTITY updateRestart.panelUI.label2 "Restart to update &brandShorterName;">
 
 <!ENTITY pageActionButton.tooltip "Page actions">
 <!ENTITY pageAction.addToUrlbar.label "Add to Address Bar">
 <!ENTITY pageAction.removeFromUrlbar.label "Remove from Address Bar">
 
-<!ENTITY sendTabToDevice.label "Send Tab to Device">
+<!ENTITY sendToDevice.label3 "Send Page to Device">
 <!ENTITY sendToDevice.syncNotReady.label "Syncing Devices…">
 
 <!ENTITY libraryButton.tooltip "View history, saved bookmarks, and more">
 
 <!-- LOCALIZATION NOTE: (accessibilityIndicator.tooltip): This is used to
      display a tooltip for accessibility indicator in toolbar/tabbar. It is also
      used as a textual label for the indicator used by assistive technology
      users. -->
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -800,17 +800,18 @@ toolbaritem[cui-areatype="menu-panel"][s
 #appMenu-fxa-label {
   -moz-box-flex: 1;
 }
 
 @keyframes syncRotate {
   to { transform: rotate(360deg); }
 }
 
-#appMenu-fxa-icon[syncstatus="active"] > .toolbarbutton-icon {
+#appMenu-fxa-icon[syncstatus="active"] > .toolbarbutton-icon,
+#PanelUI-remotetabs-syncnow[syncstatus="active"] > .toolbarbutton-icon {
   animation: syncRotate 0.8s linear infinite;
   fill: #0a84ff;
 }
 
 #appMenu-fxa-status {
   -moz-box-align: center;
 }
 
--- a/browser/themes/shared/toolbarbutton-icons.inc.css
+++ b/browser/themes/shared/toolbarbutton-icons.inc.css
@@ -15,17 +15,16 @@ toolbar[brighttext] {
 .toolbarbutton-animatable-box[brighttext],
 toolbar[brighttext] .toolbarbutton-1 {
   fill: #fff;
 }
 
 #back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
 #forward-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
 #reload-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
-#library-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
 #nav-bar-overflow-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
 #PlacesChevron:-moz-locale-dir(rtl) > .toolbarbutton-icon,
 #panic-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 #back-button {
   list-style-image: url("chrome://browser/skin/back.svg");
@@ -429,27 +428,27 @@ toolbar[brighttext] .toolbarbutton-1 {
   to {
     transform: translateX(-1056px);
     fill: var(--toolbarbutton-icon-fill-attention);
   }
 }
 
 @keyframes library-bookmark-animation-rtl {
   from {
-    transform: scaleX(-1) translateX(0);
+    transform: translateX(1056px);
     fill: inherit;
   }
   25% {
     fill: inherit;
   }
   50% {
     fill: var(--toolbarbutton-icon-fill-attention);
   }
   to {
-    transform: scaleX(-1) translateX(-1056px);
+    transform: translateX(0);
     fill: var(--toolbarbutton-icon-fill-attention);
   }
 }
 
 @keyframes library-bookmark-fade {
   from {
     fill: var(--toolbarbutton-icon-fill-attention);
   }
@@ -494,16 +493,15 @@ toolbar[brighttext] .toolbarbutton-1 {
   animation-duration: 800ms;
   animation-timing-function: steps(48);
   -moz-context-properties: fill, stroke;
   stroke: var(--toolbarbutton-icon-fill-attention);
 }
 
 .toolbarbutton-animatable-box[animate="bookmark"]:-moz-locale-dir(rtl) > .toolbarbutton-animatable-image {
   animation-name: library-bookmark-animation-rtl;
-  transform: scaleX(-1);
 }
 
 .toolbarbutton-animatable-box[animate="bookmark"][fade] > .toolbarbutton-animatable-image {
   animation-name: library-bookmark-fade;
   animation-duration: 2s;
   animation-timing-function: ease-out;
 }
new file mode 100644
--- /dev/null
+++ b/build/sparse-profiles/sphinx-docs
@@ -0,0 +1,17 @@
+%include build/sparse-profiles/mach
+
+[include]
+# Code for generating docs.
+glob:tools/docs/**
+
+# Potential docs sources
+glob:**/*.rst
+
+# Python API docs.
+glob:**/*.py
+
+# moz.build files are read to discover location of docs.
+glob:**/moz.build
+
+# Read to set the version of the docs.
+path:config/milestone.txt
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -1968,16 +1968,17 @@ Toolbox.prototype = {
 
   _toggleNoAutohide: Task.async(function* () {
     let front = yield this.preferenceFront;
     let toggledValue = !(yield this._isDisableAutohideEnabled());
 
     front.setBoolPref(DISABLE_AUTOHIDE_PREF, toggledValue);
 
     this.autohideButton.isChecked = toggledValue;
+    this._autohideHasBeenToggled = true;
   }),
 
   _isDisableAutohideEnabled: Task.async(function* () {
     // Ensure that the tools are open, and the button is visible.
     yield this.isOpen;
     if (!this.autohideButton.isVisible) {
       return false;
     }
@@ -2711,16 +2712,23 @@ Toolbox.prototype = {
 
   /**
    * Destroy the preferences actor when the toolbox is unloaded.
    */
   destroyPreference: Task.async(function* () {
     if (!this._preferenceFront) {
       return;
     }
+
+    // Only reset the autohide pref in the Browser Toolbox if it's been toggled
+    // in the UI (don't reset the pref if it was already set before opening)
+    if (this._autohideHasBeenToggled) {
+      yield this._preferenceFront.clearUserPref(DISABLE_AUTOHIDE_PREF);
+    }
+
     this._preferenceFront.destroy();
     this._preferenceFront = null;
   }),
 
   /**
    * Called when any event comes from the PerformanceFront. If the performance tool is
    * already loaded when the first event comes in, immediately unbind this handler, as
    * this is only used to queue up observed recordings before the performance tool can
--- a/dom/animation/test/chrome/test_restyles.html
+++ b/dom/animation/test/chrome/test_restyles.html
@@ -1003,34 +1003,34 @@ waitForAllPaints(function() {
     is(markers.length, 0,
        'CSS animation on an out-of-view element with pre-transform should be ' +
        'throttled.');
 
     await ensureElementRemoval(scrollDiv);
   });
 
   add_task_if_omta_enabled(
-    function* no_restyling_for_compositor_animation_on_unrelated_style_change() {
+    async function no_restyling_for_compositor_animation_on_unrelated_style_change() {
       var div = addDiv(null);
       var animation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
 
-      yield animation.ready;
+      await animation.ready;
       ok(animation.isRunningOnCompositor,
          'The opacity animation is running on the compositor');
 
       div.style.setProperty('color', 'blue', '');
-      var markers = yield observeStyling(5);
+      var markers = await observeStyling(5);
       if (isServo) {
         is(markers.length, 0,
            'The opacity animation keeps running on the compositor when ' +
            'color style is changed');
       } else {
         todo_is(markers.length, 0,
                 'Bug 1307341 The opacity animation keeps running on the ' +
                 'compositor when color style is changed');
       }
-      yield ensureElementRemoval(div);
+      await ensureElementRemoval(div);
     }
   );
 });
 
 </script>
 </body>
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -370,16 +370,51 @@ CollectWindowReports(nsGlobalWindow *aWi
 
   REPORT_SIZE("/layout/servo-style-sets/stylist/rule-tree",
               windowSizes.mLayoutServoStyleSetsStylistRuleTree,
               "Memory used by rule trees within Servo style sets within a "
               "window.");
   aWindowTotalSizes->mLayoutServoStyleSetsStylistRuleTree +=
     windowSizes.mLayoutServoStyleSetsStylistRuleTree;
 
+  REPORT_SIZE("/layout/servo-style-sets/stylist/precomputed-pseudos",
+              windowSizes.mLayoutServoStyleSetsStylistPrecomputedPseudos,
+              "Memory used by precomputed pseudo-element declarations within "
+              "Servo style sets within a window.");
+  aWindowTotalSizes->mLayoutServoStyleSetsStylistPrecomputedPseudos +=
+    windowSizes.mLayoutServoStyleSetsStylistPrecomputedPseudos;
+
+  REPORT_SIZE("/layout/servo-style-sets/stylist/element-and-pseudos-maps",
+              windowSizes.mLayoutServoStyleSetsStylistElementAndPseudosMaps,
+              "Memory used by element and pseudos maps within Servo style "
+              "sets within a window.");
+  aWindowTotalSizes->mLayoutServoStyleSetsStylistElementAndPseudosMaps +=
+    windowSizes.mLayoutServoStyleSetsStylistElementAndPseudosMaps;
+
+  REPORT_SIZE("/layout/servo-style-sets/stylist/invalidation-map",
+              windowSizes.mLayoutServoStyleSetsStylistInvalidationMap,
+              "Memory used by invalidation maps within Servo style sets "
+              "within a window.");
+  aWindowTotalSizes->mLayoutServoStyleSetsStylistInvalidationMap +=
+    windowSizes.mLayoutServoStyleSetsStylistInvalidationMap;
+
+  REPORT_SIZE("/layout/servo-style-sets/stylist/revalidation-selectors",
+              windowSizes.mLayoutServoStyleSetsStylistRevalidationSelectors,
+              "Memory used by selectors for cache revalidation within Servo "
+              "style sets within a window.");
+  aWindowTotalSizes->mLayoutServoStyleSetsStylistRevalidationSelectors +=
+    windowSizes.mLayoutServoStyleSetsStylistRevalidationSelectors;
+
+  REPORT_SIZE("/layout/servo-style-sets/stylist/other",
+              windowSizes.mLayoutServoStyleSetsStylistOther,
+              "Memory used by other Stylist data within Servo style sets "
+              "within a window.");
+  aWindowTotalSizes->mLayoutServoStyleSetsStylistOther +=
+    windowSizes.mLayoutServoStyleSetsStylistOther;
+
   REPORT_SIZE("/layout/servo-style-sets/other",
               windowSizes.mLayoutServoStyleSetsOther,
               "Memory used by other parts of Servo style sets within a "
               "window.");
   aWindowTotalSizes->mLayoutServoStyleSetsOther +=
     windowSizes.mLayoutServoStyleSetsOther;
 
   REPORT_SIZE("/layout/text-runs", windowSizes.mLayoutTextRunsSize,
@@ -653,16 +688,21 @@ nsWindowMemoryReporter::CollectReports(n
          "This is the sum of all windows' 'layout/arenas' numbers.");
 
   REPORT("window-objects/layout/gecko-style-sets",
          windowTotalSizes.mLayoutGeckoStyleSets,
          "This is the sum of all windows' 'layout/gecko-style-sets' numbers.");
 
   REPORT("window-objects/layout/servo-style-sets",
          windowTotalSizes.mLayoutServoStyleSetsStylistRuleTree +
+         windowTotalSizes.mLayoutServoStyleSetsStylistPrecomputedPseudos +
+         windowTotalSizes.mLayoutServoStyleSetsStylistElementAndPseudosMaps +
+         windowTotalSizes.mLayoutServoStyleSetsStylistInvalidationMap +
+         windowTotalSizes.mLayoutServoStyleSetsStylistRevalidationSelectors +
+         windowTotalSizes.mLayoutServoStyleSetsStylistOther +
          windowTotalSizes.mLayoutServoStyleSetsOther,
          "This is the sum of all windows' 'layout/servo-style-sets/' numbers.");
 
 
   REPORT("window-objects/layout/text-runs", windowTotalSizes.mLayoutTextRunsSize,
          "This is the sum of all windows' 'layout/text-runs' numbers.");
 
   REPORT("window-objects/layout/pres-contexts",
--- a/dom/base/nsWindowSizes.h
+++ b/dom/base/nsWindowSizes.h
@@ -172,16 +172,21 @@ class nsWindowSizes
   macro(DOM,   mDOMEventTargetsSize) \
   macro(DOM,   mDOMPerformanceUserEntries) \
   macro(DOM,   mDOMPerformanceResourceEntries) \
   macro(DOM,   mDOMOtherSize) \
   macro(Style, mStyleSheetsSize) \
   macro(Other, mLayoutPresShellSize) \
   macro(Style, mLayoutGeckoStyleSets) \
   macro(Style, mLayoutServoStyleSetsStylistRuleTree) \
+  macro(Style, mLayoutServoStyleSetsStylistPrecomputedPseudos) \
+  macro(Style, mLayoutServoStyleSetsStylistElementAndPseudosMaps) \
+  macro(Style, mLayoutServoStyleSetsStylistInvalidationMap) \
+  macro(Style, mLayoutServoStyleSetsStylistRevalidationSelectors) \
+  macro(Style, mLayoutServoStyleSetsStylistOther) \
   macro(Style, mLayoutServoStyleSetsOther) \
   macro(Other, mLayoutTextRunsSize) \
   macro(Other, mLayoutPresContextSize) \
   macro(Other, mLayoutFramePropertiesSize) \
   macro(Style, mLayoutComputedValuesDom) \
   macro(Style, mLayoutComputedValuesNonDom) \
   macro(Style, mLayoutComputedValuesVisited) \
   macro(Other, mPropertyTablesSize) \
--- a/dom/media/MediaContainerType.h
+++ b/dom/media/MediaContainerType.h
@@ -32,17 +32,17 @@ public:
   {
   }
 
   const MediaMIMEType& Type() const { return mExtendedMIMEType.Type(); }
   const MediaExtendedMIMEType& ExtendedType() const { return mExtendedMIMEType; }
 
   // Original string. Note that "type/subtype" may not be lowercase,
   // use Type().AsString() instead to get the normalized "type/subtype".
-  const nsACString& OriginalString() const { return mExtendedMIMEType.OriginalString(); }
+  const nsCString& OriginalString() const { return mExtendedMIMEType.OriginalString(); }
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 private:
   MediaExtendedMIMEType mExtendedMIMEType;
 };
 
 Maybe<MediaContainerType> MakeMediaContainerType(const nsAString& aType);
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1555,16 +1555,18 @@ MediaDecoder::RequestDebugInfo()
     [str] () {
       return DebugInfoPromise::CreateAndResolve(str, __func__);
     });
 }
 
 void
 MediaDecoder::GetMozDebugReaderData(nsACString& aString)
 {
+  aString += nsPrintfCString("Container Type: %s\n",
+                             ContainerType().Type().AsString().get());
   if (mReader) {
     mReader->GetMozDebugReaderData(aString);
   }
 }
 
 void
 MediaDecoder::NotifyAudibleStateChanged()
 {
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -3112,17 +3112,17 @@ MediaFormatReader::GetImageContainer()
 {
   return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer()
                               : nullptr;
 }
 
 void
 MediaFormatReader::GetMozDebugReaderData(nsACString& aString)
 {
-  nsAutoCString result;
+  nsCString result;
   nsAutoCString audioDecoderName("unavailable");
   nsAutoCString videoDecoderName = audioDecoderName;
   nsAutoCString audioType("none");
   nsAutoCString videoType("none");
 
   if (HasAudio()) {
     MutexAutoLock lock(mAudio.mMutex);
     audioDecoderName = mAudio.mDescription;
--- a/dom/media/MediaMIMETypes.h
+++ b/dom/media/MediaMIMETypes.h
@@ -51,17 +51,17 @@ public:
   // Construction from a DependentMediaMIMEType, with its inherent checks.
   // Implicit so MEDIAMIMETYPE can be used wherever a MediaMIMEType is expected.
   MOZ_IMPLICIT MediaMIMEType(const DependentMediaMIMEType& aType)
     : mMIMEType(aType.AsDependentString())
   {
   }
 
   // MIME "type/subtype", always lowercase.
-  const nsACString& AsString() const { return mMIMEType; }
+  const nsCString& AsString() const { return mMIMEType; }
 
   // Comparison with DependentMediaMIMEType.
   // Useful to compare to MEDIAMIMETYPE literals.
   bool operator==(const DependentMediaMIMEType& aOther) const
   {
     return mMIMEType.Equals(aOther.AsDependentString());
   }
   bool operator!=(const DependentMediaMIMEType& aOther) const
@@ -113,17 +113,17 @@ public:
   // Construction from a literal comma-separated list of codecs. Unchecked.
   template <size_t N>
   explicit MediaCodecs(const char (&aCodecs)[N])
     : mCodecs(NS_ConvertUTF8toUTF16(aCodecs, N - 1))
   {
   }
 
   bool IsEmpty() const { return mCodecs.IsEmpty(); }
-  const nsAString& AsString() const { return mCodecs; }
+  const nsString& AsString() const { return mCodecs; }
 
   using RangeType =
     const StringListRange<nsString, StringListRangeEmptyItems::ProcessEmptyItems>;
 
   // Produces a range object with begin()&end(), can be used in range-for loops.
   // This will iterate through all codecs, even empty ones (except if the
   // original list was an empty string). Iterators dereference to
   // 'const nsDependentString', valid for as long as this MediaCodecs object.
@@ -175,17 +175,17 @@ public:
   // Sizes and rates.
   Maybe<int32_t> GetWidth() const { return GetMaybeNumber(mWidth); }
   Maybe<int32_t> GetHeight() const { return GetMaybeNumber(mHeight); }
   Maybe<int32_t> GetFramerate() const { return GetMaybeNumber(mFramerate); }
   Maybe<int32_t> GetBitrate() const { return GetMaybeNumber(mBitrate); }
 
   // Original string. Note that "type/subtype" may not be lowercase,
   // use Type().AsString() instead to get the normalized "type/subtype".
-  const nsACString& OriginalString() const { return mOriginalString; }
+  const nsCString& OriginalString() const { return mOriginalString; }
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 private:
   friend Maybe<MediaExtendedMIMEType> MakeMediaExtendedMIMEType(const nsAString& aType);
   MediaExtendedMIMEType(const nsACString& aOriginalString,
                         const nsACString& aMIMEType,
                         bool aHaveCodecs, const nsAString& aCodecs,
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -811,19 +811,20 @@ private:
     // end the session.
     mNeedSessionEndTask = false;
   }
   // application should get blob and onstop event
   void DoSessionEndTask(nsresult rv)
   {
     MOZ_ASSERT(NS_IsMainThread());
     CleanupStreams();
-
-    NS_DispatchToMainThread(
-      new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
+    if (!mIsStartEventFired) {
+      NS_DispatchToMainThread(
+        new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
+    }
 
     if (NS_FAILED(rv)) {
       mRecorder->ForceInactive();
       NS_DispatchToMainThread(
         NewRunnableMethod<nsresult>("dom::MediaRecorder::NotifyError",
                                     mRecorder,
                                     &MediaRecorder::NotifyError,
                                     rv));
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -250,16 +250,17 @@ MediaSourceDecoder::SetMediaSourceDurati
   } else {
     SetExplicitDuration(PositiveInfinity<double>());
   }
 }
 
 void
 MediaSourceDecoder::GetMozDebugReaderData(nsACString& aString)
 {
+  aString += NS_LITERAL_CSTRING("Container Type: MediaSource\n");
   if (mReader && mDemuxer) {
     mReader->GetMozDebugReaderData(aString);
     mDemuxer->GetMozDebugReaderData(aString);
   }
 }
 
 double
 MediaSourceDecoder::GetDuration()
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -257,17 +257,17 @@ MediaSourceDemuxer::GetMozDebugReaderDat
   nsAutoCString result;
   result += nsPrintfCString("Dumping Data for Demuxer: %p\n", this);
   if (mAudioTrack) {
     result += nsPrintfCString(
       "\tDumping Audio Track Buffer(%s): mLastAudioTime=%f\n"
       "\t\tAudio Track Buffer Details: NumSamples=%zu"
       " Size=%u Evictable=%u "
       "NextGetSampleIndex=%u NextInsertionIndex=%d\n",
-      mAudioTrack->mAudioTracks.mInfo->mMimeType.get(),
+      mAudioTrack->mType.Type().AsString().get(),
       mAudioTrack->mAudioTracks.mNextSampleTime.ToSeconds(),
       mAudioTrack->mAudioTracks.mBuffers[0].Length(),
       mAudioTrack->mAudioTracks.mSizeBuffer,
       mAudioTrack->Evictable(TrackInfo::kAudioTrack),
       mAudioTrack->mAudioTracks.mNextGetSampleIndex.valueOr(-1),
       mAudioTrack->mAudioTracks.mNextInsertionIndex.valueOr(-1));
 
     result += nsPrintfCString(
@@ -275,17 +275,17 @@ MediaSourceDemuxer::GetMozDebugReaderDat
       DumpTimeRanges(mAudioTrack->SafeBuffered(TrackInfo::kAudioTrack)).get());
   }
   if (mVideoTrack) {
     result += nsPrintfCString(
       "\tDumping Video Track Buffer(%s): mLastVideoTime=%f\n"
       "\t\tVideo Track Buffer Details: NumSamples=%zu"
       " Size=%u Evictable=%u "
       "NextGetSampleIndex=%u NextInsertionIndex=%d\n",
-      mVideoTrack->mVideoTracks.mInfo->mMimeType.get(),
+      mVideoTrack->mType.Type().AsString().get(),
       mVideoTrack->mVideoTracks.mNextSampleTime.ToSeconds(),
       mVideoTrack->mVideoTracks.mBuffers[0].Length(),
       mVideoTrack->mVideoTracks.mSizeBuffer,
       mVideoTrack->Evictable(TrackInfo::kVideoTrack),
       mVideoTrack->mVideoTracks.mNextGetSampleIndex.valueOr(-1),
       mVideoTrack->mVideoTracks.mNextInsertionIndex.valueOr(-1));
 
     result += nsPrintfCString(
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -206,17 +206,17 @@ private:
   RefPtr<MediaByteBuffer> mInputBuffer;
   // Buffer full flag as per https://w3c.github.io/media-source/#sourcebuffer-buffer-full-flag.
   // Accessed on both the main thread and the task queue.
   Atomic<bool> mBufferFull;
   bool mFirstInitializationSegmentReceived;
   // Set to true once a new segment is started.
   bool mNewMediaSegmentStarted;
   bool mActiveTrack;
-  MediaContainerType mType;
+  const MediaContainerType mType;
 
   // ContainerParser objects and methods.
   // Those are used to parse the incoming input buffer.
 
   // Recreate the ContainerParser and if aReuseInitData is true then
   // feed it with the previous init segment found.
   void RecreateParser(bool aReuseInitData);
   nsAutoPtr<ContainerParser> mParser;
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -225,17 +225,17 @@ PDMFactory::CreateDecoder(const CreateDe
       diagnostics->SetFFmpegFailedToLoad();
     }
     if (mGMPPDMFailedToStartup) {
       diagnostics->SetGMPPDMFailedToStartup();
     }
   }
 
   for (auto& current : mCurrentPDMs) {
-    if (!current->SupportsMimeType(config.mMimeType, diagnostics)) {
+    if (!current->Supports(config, diagnostics)) {
       continue;
     }
     RefPtr<MediaDataDecoder> m = CreateDecoderWithPDM(current, aParams);
     if (m) {
       return m.forget();
     }
   }
   NS_WARNING("Unable to create a decoder, no platform found.");
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -820,16 +820,19 @@ tags=msg
 skip-if = toolkit == 'android' # bug 1297432, android(bug 1232305)
 tags=msg
 [test_mediarecorder_creation.html]
 skip-if = android_version == '17' # android(bug 1232305)
 tags=msg capturestream
 [test_mediarecorder_creation_fail.html]
 skip-if = android_version == '17' # android(bug 1232305)
 tags=msg
+[test_mediarecorder_fires_start_event_once_when_erroring.html]
+skip-if = android_version == '17' # android(bug 1232305)
+tags=msg
 [test_mediarecorder_getencodeddata.html]
 skip-if = android_version == '17' # android(bug 1232305)
 tags=msg
 [test_mediarecorder_pause_resume_video.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
 [test_mediarecorder_playback_can_repeat.html]
 skip-if = android_version == '17' || android_version == '22' # android(bug 1232305, bug 1372457)
 tags=msg
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_mediarecorder_fires_start_event_once_when_erroring.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1395022 - MediaRecorder fires start event when erroring.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1395022">Mozilla Bug 1395022</a>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+function startTest() {
+  let audioContext = new AudioContext();
+  let destination1 = audioContext.createMediaStreamDestination();
+  let oscilator = audioContext.createOscillator();
+  oscilator.connect(destination1);
+  oscilator.start();
+
+  let destination2 = audioContext.createMediaStreamDestination();
+
+  let rec = new MediaRecorder(destination1.stream);
+
+  let numStartEvents = 0;
+
+  rec.onstart = () => {
+    numStartEvents += 1;
+    is(numStartEvents, 1, "One start event should be fired by the recorder");
+    // Trigger an error in the recorder
+    destination1.stream.addTrack(destination2.stream.getTracks()[0]);
+  };
+
+  rec.onerror = () => {
+    is(numStartEvents, 1, "One start event should have been fired by the recorder");
+    SimpleTest.finish();
+  };
+
+  rec.start();
+}
+
+SimpleTest.waitForExplicitFinish();
+startTest();
+
+</script>
+</head>
+</html>
\ No newline at end of file
--- a/dom/media/webaudio/AudioBlock.cpp
+++ b/dom/media/webaudio/AudioBlock.cpp
@@ -38,17 +38,17 @@ public:
     size *= aChannelCount;
     size *= sizeof(float);
     size += sizeof(AudioBlockBuffer);
     size += 15;  //padding for alignment
     if (!size.isValid()) {
       MOZ_CRASH();
     }
 
-    void* m = moz_xmalloc(size.value());
+    void* m = operator new(size.value());
     RefPtr<AudioBlockBuffer> p = new (m) AudioBlockBuffer();
     NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) - reinterpret_cast<char*>(p.get())) % 4 == 0,
                  "AudioBlockBuffers should be at least 4-byte aligned");
     return p.forget();
   }
 
   // Graph thread only.
   void DownstreamRefAdded() { ++mDownstreamRefCount; }
--- a/dom/webauthn/AuthenticatorAssertionResponse.cpp
+++ b/dom/webauthn/AuthenticatorAssertionResponse.cpp
@@ -5,57 +5,87 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/WebAuthenticationBinding.h"
 #include "mozilla/dom/AuthenticatorAssertionResponse.h"
 
 namespace mozilla {
 namespace dom {
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(AuthenticatorAssertionResponse)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AuthenticatorAssertionResponse,
+                                                AuthenticatorResponse)
+  tmp->mAuthenticatorDataCachedObj = nullptr;
+  tmp->mSignatureCachedObj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(AuthenticatorAssertionResponse,
+                                               AuthenticatorResponse)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAuthenticatorDataCachedObj)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mSignatureCachedObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AuthenticatorAssertionResponse,
+                                                  AuthenticatorResponse)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_IMPL_ADDREF_INHERITED(AuthenticatorAssertionResponse, AuthenticatorResponse)
 NS_IMPL_RELEASE_INHERITED(AuthenticatorAssertionResponse, AuthenticatorResponse)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AuthenticatorAssertionResponse)
 NS_INTERFACE_MAP_END_INHERITING(AuthenticatorResponse)
 
 AuthenticatorAssertionResponse::AuthenticatorAssertionResponse(nsPIDOMWindowInner* aParent)
   : AuthenticatorResponse(aParent)
-{}
+  , mAuthenticatorDataCachedObj(nullptr)
+  , mSignatureCachedObj(nullptr)
+{
+  mozilla::HoldJSObjects(this);
+}
 
 AuthenticatorAssertionResponse::~AuthenticatorAssertionResponse()
-{}
+{
+  mozilla::DropJSObjects(this);
+}
 
 JSObject*
 AuthenticatorAssertionResponse::WrapObject(JSContext* aCx,
                                            JS::Handle<JSObject*> aGivenProto)
 {
   return AuthenticatorAssertionResponseBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 AuthenticatorAssertionResponse::GetAuthenticatorData(JSContext* aCx,
-                                                     JS::MutableHandle<JSObject*> aRetVal) const
+                                                     JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mAuthenticatorData.ToUint8Array(aCx));
+  if (!mAuthenticatorDataCachedObj) {
+    mAuthenticatorDataCachedObj = mAuthenticatorData.ToUint8Array(aCx);
+  }
+  aRetVal.set(mAuthenticatorDataCachedObj);
 }
 
 nsresult
 AuthenticatorAssertionResponse::SetAuthenticatorData(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mAuthenticatorData.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
 
 void
 AuthenticatorAssertionResponse::GetSignature(JSContext* aCx,
-                                             JS::MutableHandle<JSObject*> aRetVal) const
+                                             JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mSignature.ToUint8Array(aCx));
+  if (!mSignatureCachedObj) {
+    mSignatureCachedObj = mSignature.ToUint8Array(aCx);
+  }
+  aRetVal.set(mSignatureCachedObj);
 }
 
 nsresult
 AuthenticatorAssertionResponse::SetSignature(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mSignature.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/dom/webauthn/AuthenticatorAssertionResponse.h
+++ b/dom/webauthn/AuthenticatorAssertionResponse.h
@@ -18,39 +18,43 @@
 
 namespace mozilla {
 namespace dom {
 
 class AuthenticatorAssertionResponse final : public AuthenticatorResponse
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(AuthenticatorAssertionResponse,
+                                                         AuthenticatorResponse)
 
   explicit AuthenticatorAssertionResponse(nsPIDOMWindowInner* aParent);
 
 protected:
   ~AuthenticatorAssertionResponse() override;
 
 public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
-  GetAuthenticatorData(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetAuthenticatorData(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal);
 
   nsresult
   SetAuthenticatorData(CryptoBuffer& aBuffer);
 
   void
-  GetSignature(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetSignature(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal);
 
   nsresult
   SetSignature(CryptoBuffer& aBuffer);
 
 private:
   CryptoBuffer mAuthenticatorData;
+  JS::Heap<JSObject*> mAuthenticatorDataCachedObj;
   CryptoBuffer mSignature;
+  JS::Heap<JSObject*> mSignatureCachedObj;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AuthenticatorAssertionResponse_h
--- a/dom/webauthn/AuthenticatorAttestationResponse.cpp
+++ b/dom/webauthn/AuthenticatorAttestationResponse.cpp
@@ -5,41 +5,65 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/WebAuthenticationBinding.h"
 #include "mozilla/dom/AuthenticatorAttestationResponse.h"
 
 namespace mozilla {
 namespace dom {
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(AuthenticatorAttestationResponse)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AuthenticatorAttestationResponse,
+                                                AuthenticatorResponse)
+  tmp->mAttestationObjectCachedObj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(AuthenticatorAttestationResponse,
+                                               AuthenticatorResponse)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAttestationObjectCachedObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AuthenticatorAttestationResponse,
+                                                  AuthenticatorResponse)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_IMPL_ADDREF_INHERITED(AuthenticatorAttestationResponse, AuthenticatorResponse)
 NS_IMPL_RELEASE_INHERITED(AuthenticatorAttestationResponse, AuthenticatorResponse)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AuthenticatorAttestationResponse)
 NS_INTERFACE_MAP_END_INHERITING(AuthenticatorResponse)
 
 AuthenticatorAttestationResponse::AuthenticatorAttestationResponse(nsPIDOMWindowInner* aParent)
   : AuthenticatorResponse(aParent)
-{}
+  , mAttestationObjectCachedObj(nullptr)
+{
+  mozilla::HoldJSObjects(this);
+}
 
 AuthenticatorAttestationResponse::~AuthenticatorAttestationResponse()
-{}
+{
+  mozilla::DropJSObjects(this);
+}
 
 JSObject*
 AuthenticatorAttestationResponse::WrapObject(JSContext* aCx,
                                              JS::Handle<JSObject*> aGivenProto)
 {
   return AuthenticatorAttestationResponseBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 AuthenticatorAttestationResponse::GetAttestationObject(JSContext* aCx,
-                                                       JS::MutableHandle<JSObject*> aRetVal) const
+                                                       JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mAttestationObject.ToUint8Array(aCx));
+  if (!mAttestationObjectCachedObj) {
+    mAttestationObjectCachedObj = mAttestationObject.ToUint8Array(aCx);
+  }
+  aRetVal.set(mAttestationObjectCachedObj);
 }
 
 nsresult
 AuthenticatorAttestationResponse::SetAttestationObject(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mAttestationObject.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/dom/webauthn/AuthenticatorAttestationResponse.h
+++ b/dom/webauthn/AuthenticatorAttestationResponse.h
@@ -18,32 +18,35 @@
 
 namespace mozilla {
 namespace dom {
 
 class AuthenticatorAttestationResponse final : public AuthenticatorResponse
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(AuthenticatorAttestationResponse,
+                                                         AuthenticatorResponse)
 
   explicit AuthenticatorAttestationResponse(nsPIDOMWindowInner* aParent);
 
 protected:
   ~AuthenticatorAttestationResponse() override;
 
 public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
-  GetAttestationObject(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetAttestationObject(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal);
 
   nsresult
   SetAttestationObject(CryptoBuffer& aBuffer);
 
 private:
   CryptoBuffer mAttestationObject;
+  JS::Heap<JSObject*> mAttestationObjectCachedObj;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AuthenticatorAttestationResponse_h
--- a/dom/webauthn/AuthenticatorResponse.cpp
+++ b/dom/webauthn/AuthenticatorResponse.cpp
@@ -5,44 +5,67 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/WebAuthenticationBinding.h"
 #include "mozilla/dom/AuthenticatorResponse.h"
 
 namespace mozilla {
 namespace dom {
 
-// Only needed for refcounted objects.
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AuthenticatorResponse, mParent)
+NS_IMPL_CYCLE_COLLECTION_CLASS(AuthenticatorResponse)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AuthenticatorResponse)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  tmp->mClientDataJSONCachedObj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AuthenticatorResponse)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mClientDataJSONCachedObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AuthenticatorResponse)
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AuthenticatorResponse)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AuthenticatorResponse)
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AuthenticatorResponse)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 AuthenticatorResponse::AuthenticatorResponse(nsPIDOMWindowInner* aParent)
   : mParent(aParent)
-{}
+  , mClientDataJSONCachedObj(nullptr)
+{
+  mozilla::HoldJSObjects(this);
+}
 
 AuthenticatorResponse::~AuthenticatorResponse()
-{}
+{
+  mozilla::DropJSObjects(this);
+}
 
 JSObject*
 AuthenticatorResponse::WrapObject(JSContext* aCx,
                                   JS::Handle<JSObject*> aGivenProto)
 {
   return AuthenticatorResponseBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 AuthenticatorResponse::GetClientDataJSON(JSContext* aCx,
-                                         JS::MutableHandle<JSObject*> aRetVal) const
+                                         JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mClientDataJSON.ToUint8Array(aCx));
+  if (!mClientDataJSONCachedObj) {
+    mClientDataJSONCachedObj = mClientDataJSON.ToUint8Array(aCx);
+  }
+  aRetVal.set(mClientDataJSONCachedObj);
 }
 
 nsresult
 AuthenticatorResponse::SetClientDataJSON(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mClientDataJSON.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/dom/webauthn/AuthenticatorResponse.h
+++ b/dom/webauthn/AuthenticatorResponse.h
@@ -39,22 +39,23 @@ public:
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
   GetFormat(nsString& aRetVal) const;
 
   void
-  GetClientDataJSON(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetClientDataJSON(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal);
 
   nsresult
   SetClientDataJSON(CryptoBuffer& aBuffer);
 
 private:
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   CryptoBuffer mClientDataJSON;
+  JS::Heap<JSObject*> mClientDataJSONCachedObj;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AuthenticatorResponse_h
--- a/dom/webauthn/PublicKeyCredential.cpp
+++ b/dom/webauthn/PublicKeyCredential.cpp
@@ -6,46 +6,63 @@
 
 #include "mozilla/dom/PublicKeyCredential.h"
 #include "mozilla/dom/WebAuthenticationBinding.h"
 #include "nsCycleCollectionParticipant.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(PublicKeyCredential, Credential, mResponse)
+NS_IMPL_CYCLE_COLLECTION_CLASS(PublicKeyCredential)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PublicKeyCredential,
+                                                Credential)
+  tmp->mRawIdCachedObj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PublicKeyCredential, Credential)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRawIdCachedObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PublicKeyCredential, Credential)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(PublicKeyCredential, Credential)
 NS_IMPL_RELEASE_INHERITED(PublicKeyCredential, Credential)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PublicKeyCredential)
 NS_INTERFACE_MAP_END_INHERITING(Credential)
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PublicKeyCredential, Credential)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
 PublicKeyCredential::PublicKeyCredential(nsPIDOMWindowInner* aParent)
   : Credential(aParent)
-{}
+  , mRawIdCachedObj(nullptr)
+{
+  mozilla::HoldJSObjects(this);
+}
 
 PublicKeyCredential::~PublicKeyCredential()
-{}
+{
+  mozilla::DropJSObjects(this);
+}
 
 JSObject*
 PublicKeyCredential::WrapObject(JSContext* aCx,
                                 JS::Handle<JSObject*> aGivenProto)
 {
   return PublicKeyCredentialBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 PublicKeyCredential::GetRawId(JSContext* aCx,
-                              JS::MutableHandle<JSObject*> aRetVal) const
+                              JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mRawId.ToUint8Array(aCx));
+  if (!mRawIdCachedObj) {
+    mRawIdCachedObj = mRawId.ToUint8Array(aCx);
+  }
+  aRetVal.set(mRawIdCachedObj);
 }
 
 already_AddRefed<AuthenticatorResponse>
 PublicKeyCredential::Response() const
 {
   RefPtr<AuthenticatorResponse> temp(mResponse);
   return temp.forget();
 }
--- a/dom/webauthn/PublicKeyCredential.h
+++ b/dom/webauthn/PublicKeyCredential.h
@@ -30,29 +30,30 @@ public:
 protected:
   ~PublicKeyCredential() override;
 
 public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
-  GetRawId(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetRawId(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal);
 
   already_AddRefed<AuthenticatorResponse>
   Response() const;
 
   nsresult
   SetRawId(CryptoBuffer& aBuffer);
 
   void
   SetResponse(RefPtr<AuthenticatorResponse>);
 
 private:
   CryptoBuffer mRawId;
+  JS::Heap<JSObject*> mRawIdCachedObj;
   RefPtr<AuthenticatorResponse> mResponse;
   // Extensions are not supported yet.
   // <some type> mClientExtensionResults;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/webauthn/tests/test_webauthn_loopback.html
+++ b/dom/webauthn/tests/test_webauthn_loopback.html
@@ -51,16 +51,21 @@ function() {
        - clientExtensionResults: (not yet supported)
     */
 
     is(aCredInfo.type, "public-key", "Credential type must be public-key")
 
     ok(aCredInfo.rawId.length > 0, "Key ID exists");
     is(aCredInfo.id, bytesToBase64UrlSafe(aCredInfo.rawId), "Encoded Key ID and Raw Key ID match");
 
+    ok(aCredInfo.rawId === aCredInfo.rawId, "PublicKeyCredential.RawID is SameObject");
+    ok(aCredInfo.response === aCredInfo.response, "PublicKeyCredential.Response is SameObject");
+    ok(aCredInfo.response.clientDataJSON === aCredInfo.response.clientDataJSON, "PublicKeyCredential.Response.ClientDataJSON is SameObject");
+    ok(aCredInfo.response.attestationObject === aCredInfo.response.attestationObject, "PublicKeyCredential.Response.AttestationObject is SameObject");
+
     let clientData = JSON.parse(buffer2string(aCredInfo.response.clientDataJSON));
     is(clientData.challenge, bytesToBase64UrlSafe(gCredentialChallenge), "Challenge is correct");
     // WD-05 vs. WD-06: In WD-06, the second parameter should be "window.location.origin". Fix
     // this in Bug 1384776
     is(clientData.origin, document.domain, "Origin is correct");
     is(clientData.hashAlg, "SHA-256", "Hash algorithm is correct");
 
     return webAuthnDecodeCBORAttestation(aCredInfo.response.attestationObject.buffer)
@@ -85,16 +90,19 @@ function() {
          - signature: U2F Sign() Response
     */
 
     is(aAssertion.type, "public-key", "Credential type must be public-key")
 
     ok(aAssertion.rawId.length > 0, "Key ID exists");
     is(aAssertion.id, bytesToBase64UrlSafe(aAssertion.rawId), "Encoded Key ID and Raw Key ID match");
 
+    ok(aAssertion.response.authenticatorData === aAssertion.response.authenticatorData, "AuthenticatorAssertionResponse.AuthenticatorData is SameObject");
+    ok(aAssertion.response.signature === aAssertion.response.signature, "AuthenticatorAssertionResponse.Signature is SameObject");
+
     ok(aAssertion.response.authenticatorData.length > 0, "Authenticator data exists");
     let clientData = JSON.parse(buffer2string(aAssertion.response.clientDataJSON));
     is(clientData.challenge, bytesToBase64UrlSafe(gAssertionChallenge), "Challenge is correct");
     // WD-05 vs. WD-06: In WD-06, the second parameter should be "window.location.origin". Fix
     // this in Bug 1384776
     is(clientData.origin, document.domain, "Origin is correct");
     is(clientData.hashAlg, "SHA-256", "Hash algorithm is correct");
 
--- a/js/src/make-source-package.sh
+++ b/js/src/make-source-package.sh
@@ -96,17 +96,17 @@ case $cmd in
     # copy build and config directory.
     cp -pPR ${TOPSRCDIR}/build ${TOPSRCDIR}/config ${tgtpath}
 
     # copy cargo config
     ${MKDIR} -p ${tgtpath}/.cargo
     cp -pPR ${TOPSRCDIR}/.cargo/config.in ${tgtpath}/.cargo
 
     # generate configure files to avoid build dependency on autoconf-2.13
-    cp -pPR ${TOPSRCDIR}/js/src/configure.in ${tgtpath}/js/src/configure
+    cp -PR ${TOPSRCDIR}/js/src/configure.in ${tgtpath}/js/src/configure
     chmod a+x ${tgtpath}/js/src/configure
     ${AUTOCONF} --localdir=${TOPSRCDIR}/js/src \
         ${TOPSRCDIR}/js/src/old-configure.in >${tgtpath}/js/src/old-configure
 
     # put in js itself
     cp -pPR ${TOPSRCDIR}/mfbt ${tgtpath}
     cp -p ${SRCDIR}/../moz.configure ${tgtpath}/js
     cp -pPR ${SRCDIR}/../public ${tgtpath}/js
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1591,25 +1591,26 @@ Evaluate(JSContext* cx, unsigned argc, V
         if (!JS_GetProperty(cx, opts, "envChainObject", &v))
             return false;
         if (!v.isUndefined()) {
             if (loadBytecode) {
                 JS_ReportErrorASCII(cx, "Can't use both loadBytecode and envChainObject");
                 return false;
             }
 
-            if (v.isObject()) {
-                if (!envChain.append(&v.toObject())) {
-                    JS_ReportOutOfMemory(cx);
-                    return false;
-                }
-            } else {
+            if (!v.isObject()) {
                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
                                           "\"envChainObject\" passed to evaluate()", "not an object");
                 return false;
+            } else if (v.toObject().is<GlobalObject>()) {
+                JS_ReportErrorASCII(cx, "\"envChainObject\" passed to evaluate() should not be a global");
+                return false;
+            } else if (!envChain.append(&v.toObject())) {
+                JS_ReportOutOfMemory(cx);
+                return false;
             }
         }
 
         // We cannot load or save the bytecode if we have no object where the
         // bytecode cache is stored.
         if (loadBytecode || saveBytecode || saveIncrementalBytecode) {
             if (!cacheEntry) {
                 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
--- a/js/src/tests/js1_8_5/extensions/non_syntactic.js
+++ b/js/src/tests/js1_8_5/extensions/non_syntactic.js
@@ -28,9 +28,19 @@ evaluate("assertEq(someVar, 2);", evalOp
 evaluate("assertEq(this.someVar, 2);", evalOpt);
 evaluate("assertEq(this, alsoSomeObject);", evalOpt);
 
 // With an object on the scope, inside a function.
 evaluate("(function() { assertEq(someVar, 2);})()", evalOpt);
 evaluate("(function() { assertEq(this !== alsoSomeObject, true);})()", evalOpt);
 evaluate("(function() { assertEq(this.someVar, 1);})()", evalOpt);
 
+var globalEvalOpt = {
+    envChainObject: this
+};
+try {
+  evaluate("assertEq(someVar, 1);", globalEvalOpt);
+  throw new Error("Globals aren't allowed as a envChainObject argument to evaluate");
+} catch (e) {
+}
+
+
 reportCompare(true, true);
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -427,22 +427,22 @@ RestyleManager::ChangeHintToString(nsCha
     "UpdateSubtreeOverflow", "UpdatePostTransformOverflow",
     "UpdateParentOverflow",
     "ChildrenOnlyTransform", "RecomputePosition", "UpdateContainingBlock",
     "BorderStyleNoneChange", "UpdateTextPath", "SchedulePaint",
     "NeutralChange", "InvalidateRenderingObservers",
     "ReflowChangesSizeOrPosition", "UpdateComputedBSize",
     "UpdateUsesOpacity", "UpdateBackgroundPosition",
     "AddOrRemoveTransform", "CSSOverflowChange",
-    "UpdateWidgetProperties"
+    "UpdateWidgetProperties", "UpdateTableCellSpans"
   };
-  static_assert(nsChangeHint_AllHints == (1 << ArrayLength(names)) - 1,
+  static_assert(nsChangeHint_AllHints == (1u << ArrayLength(names)) - 1,
                 "Name list doesn't match change hints.");
-  uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
-  uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
+  uint32_t hint = aHint & ((1u << ArrayLength(names)) - 1);
+  uint32_t rest = aHint & ~((1u << ArrayLength(names)) - 1);
   if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
     result.AppendLiteral("NS_STYLE_HINT_REFLOW");
     hint = hint & ~NS_STYLE_HINT_REFLOW;
     any = true;
   } else if ((hint & nsChangeHint_AllReflowHints) == nsChangeHint_AllReflowHints) {
     result.AppendLiteral("nsChangeHint_AllReflowHints");
     hint = hint & ~nsChangeHint_AllReflowHints;
     any = true;
@@ -1692,16 +1692,19 @@ RestyleManager::ProcessRestyledFrames(ns
       }
       if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
         presContext->PresShell()->SynthesizeMouseMove(false);
         didUpdateCursor = true;
       }
       if (hint & nsChangeHint_UpdateWidgetProperties) {
         frame->UpdateWidgetProperties();
       }
+      if (hint & nsChangeHint_UpdateTableCellSpans) {
+        frameConstructor->UpdateTableCellSpans(content);
+      }
     }
   }
 
   frameConstructor->EndUpdate();
 
 #ifdef DEBUG
   // Verify the style tree.  Note that this needs to happen once we've
   // processed the whole list, since until then the tree is not in fact in a
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -9954,16 +9954,31 @@ nsCSSFrameConstructor::MaybeRecreateCont
   }
 #endif
 
   ReframeContainingBlock(parent, aInsertionKind, aFlags);
   return true;
 }
 
 void
+nsCSSFrameConstructor::UpdateTableCellSpans(nsIContent* aContent)
+{
+  nsTableCellFrame* cellFrame = do_QueryFrame(aContent->GetPrimaryFrame());
+
+  // It's possible that this warning could fire if some other style change
+  // simultaneously changes the 'display' of the element and makes it no
+  // longer be a table cell.
+  NS_WARNING_ASSERTION(cellFrame, "Hint should only be posted on table cells!");
+
+  if (cellFrame) {
+    cellFrame->GetTableFrame()->RowOrColSpanChanged(cellFrame);
+  }
+}
+
+void
 nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
                                                 InsertionKind aInsertionKind,
                                                 RemoveFlags aFlags)
 {
   MOZ_ASSERT(aInsertionKind != InsertionKind::Async || aContent->IsElement());
   MOZ_ASSERT(aContent);
 
   // If there is no document, we don't want to recreate frames for it.  (You
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1798,16 +1798,21 @@ private:
    * Recreate frames for aContent.
    * @param aContent the content to recreate frames for
    * @param aFlags normally you want to pass REMOVE_FOR_RECONSTRUCTION here
    */
   void RecreateFramesForContent(nsIContent*   aContent,
                                 InsertionKind aInsertionKind,
                                 RemoveFlags   aFlags);
 
+  /**
+   *  Handles change of rowspan and colspan attributes on table cells.
+   */
+  void UpdateTableCellSpans(nsIContent* aContent);
+
   // If removal of aFrame from the frame tree requires reconstruction of some
   // containing block (either of aFrame or of its parent) due to {ib} splits or
   // table pseudo-frames, recreate the relevant frame subtree.  The return value
   // indicates whether this happened.  aFrame must be the result of a
   // GetPrimaryFrame() call on a content node (which means its parent is also
   // not null).
   bool MaybeRecreateContainerForFrameRemoval(nsIFrame*     aFrame,
                                              InsertionKind aInsertionKind,
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -233,32 +233,38 @@ enum nsChangeHint : uint32_t {
   nsChangeHint_CSSOverflowChange = 1 << 28,
 
   /**
    * Indicates that nsIFrame::UpdateWidgetProperties needs to be called.
    * This is used for -moz-window-* properties.
    */
   nsChangeHint_UpdateWidgetProperties = 1 << 29,
 
+  /**
+   *  Indicates that there has been a colspan or rowspan attribute change
+   *  on the cells of a table.
+   */
+  nsChangeHint_UpdateTableCellSpans = 1 << 30,
+
   // IMPORTANT NOTE: When adding a new hint, you will need to add it to
   // one of:
   //
   //   * nsChangeHint_Hints_NeverHandledForDescendants
   //   * nsChangeHint_Hints_AlwaysHandledForDescendants
   //   * nsChangeHint_Hints_SometimesHandledForDescendants
   //
   // and you also may need to handle it in NS_HintsNotHandledForDescendantsIn.
   //
   // Please also add it to RestyleManager::ChangeHintToString and
   // modify nsChangeHint_AllHints below accordingly.
 
   /**
    * Dummy hint value for all hints. It exists for compile time check.
    */
-  nsChangeHint_AllHints = (1 << 30) - 1,
+  nsChangeHint_AllHints = (1u << 31) - 1,
 };
 
 // Redefine these operators to return nothing. This will catch any use
 // of these operators on hints. We should not be using these operators
 // on nsChangeHints
 inline void operator<(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator>(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator!=(nsChangeHint s1, nsChangeHint s2) {}
@@ -348,16 +354,17 @@ inline nsChangeHint operator^=(nsChangeH
   nsChangeHint_UpdateBackgroundPosition |                  \
   nsChangeHint_UpdateComputedBSize |                       \
   nsChangeHint_UpdateContainingBlock |                     \
   nsChangeHint_UpdateEffects |                             \
   nsChangeHint_UpdateOpacityLayer |                        \
   nsChangeHint_UpdateOverflow |                            \
   nsChangeHint_UpdateParentOverflow |                      \
   nsChangeHint_UpdatePostTransformOverflow |               \
+  nsChangeHint_UpdateTableCellSpans |                          \
   nsChangeHint_UpdateTransformLayer |                      \
   nsChangeHint_UpdateUsesOpacity |                         \
   nsChangeHint_AddOrRemoveTransform |                      \
   nsChangeHint_UpdateWidgetProperties                      \
 )
 
 // The change hints that are sometimes considered to be handled for descendants.
 #define nsChangeHint_Hints_SometimesHandledForDescendants (\
--- a/layout/reftests/css-ruby/reftest.list
+++ b/layout/reftests/css-ruby/reftest.list
@@ -19,17 +19,17 @@
 test-pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == inflated-ruby-1.html inflated-ruby-1-ref.html
 == intra-level-whitespace-1.html intra-level-whitespace-1-ref.html
 == intra-level-whitespace-2.html intra-level-whitespace-2-ref.html
 == intra-level-whitespace-3.html intra-level-whitespace-3-ref.html
 == intrinsic-isize-1.html intrinsic-isize-1-ref.html
 == intrinsic-isize-2.html intrinsic-isize-2-ref.html
 == justification-1.html justification-1-ref.html
 == justification-2.html justification-2-ref.html
-fails-if((stylo||styloVsGecko)&&cocoaWidget) fuzzy-if(winWidget,255,792) == lang-specific-style-1.html lang-specific-style-1-ref.html # bug 1134947
+fuzzy-if(winWidget,255,792) == lang-specific-style-1.html lang-specific-style-1-ref.html # bug 1134947
 == line-breaking-1.html line-breaking-1-ref.html
 == line-breaking-2.html line-breaking-2-ref.html
 fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),3,2) == line-break-suppression-1.html line-break-suppression-1-ref.html
 fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),3,2) == line-break-suppression-2.html line-break-suppression-2-ref.html
 == line-break-suppression-3.html line-break-suppression-3-ref.html
 == line-break-suppression-4.html line-break-suppression-4-ref.html
 == line-break-suppression-5.html line-break-suppression-5-ref.html
 == line-height-1.html line-height-1-ref.html
--- a/layout/reftests/forms/input/number/focus-handling.html
+++ b/layout/reftests/forms/input/number/focus-handling.html
@@ -4,17 +4,19 @@
        get snapshotted before it's been focused. We're not testing                 
        invalidation so we don't need to listen for MozReftestInvalidate.           
   -->                                                                              
   <head>
     <meta charset="utf-8">
     <script>
 
 function begin() {
-  document.getElementsByTagName('input')[1].focus();
+  setTimeout(function() {
+    document.getElementsByTagName('input')[1].focus();
+  }, 0);
 }
 
 function end() {
   setTimeout(function() {
     document.documentElement.className = "";
   }, 0);
 }
 
--- a/layout/reftests/forms/input/number/reftest.list
+++ b/layout/reftests/forms/input/number/reftest.list
@@ -31,17 +31,17 @@ skip-if(Android) == number-min-height-1.
 skip-if(Android) == number-min-height-2.html number-min-height-2-ref.html
 skip-if(Android) == number-max-height-1.html number-max-height-1-ref.html
 skip-if(Android) == number-max-height-2.html number-max-height-2-ref.html
 
 # number of significant fractional digits:
 == number-significant-fractional-digits.html number-significant-fractional-digits-ref.html
 
 # focus
-fuzzy-if(skiaContent,2,5) needs-focus skip-if(styloVsGecko||stylo) == focus-handling.html focus-handling-ref.html
+fuzzy-if(skiaContent,2,5) needs-focus == focus-handling.html focus-handling-ref.html
 
 # select
 fuzzy-if(skiaContent,1,1) == number-selected.html number-selected-ref.html
 
 # pseudo-elements not usable from content:
 == number-pseudo-elements.html number-pseudo-elements-ref.html
 
 == number-placeholder.html number-placeholder-ref.html
--- a/layout/reftests/forms/progress/reftest.list
+++ b/layout/reftests/forms/progress/reftest.list
@@ -1,13 +1,13 @@
 == values.html values-ref.html
 == values-rtl.html values-rtl-ref.html
 == margin-padding.html margin-padding-ref.html
 == margin-padding-rtl.html margin-padding-rtl-ref.html
-random-if(styloVsGecko) == bar-pseudo-element.html bar-pseudo-element-ref.html
+== bar-pseudo-element.html bar-pseudo-element-ref.html
 == bar-pseudo-element-rtl.html bar-pseudo-element-rtl-ref.html
 == indeterminate-style-width.html indeterminate-style-width-ref.html
 
 # vertical tests
 == values-vertical.html values-vertical-ref.html
 == values-vertical-rtl.html values-vertical-rtl-ref.html
 == margin-padding-vertical.html margin-padding-vertical-ref.html
 == margin-padding-vertical-rtl.html margin-padding-vertical-rtl-ref.html
--- a/layout/reftests/svg/as-image/reftest.list
+++ b/layout/reftests/svg/as-image/reftest.list
@@ -53,17 +53,17 @@ fuzzy(1,2) fuzzy-if(azureSkia,1,40000) =
 
 == canvas-drawImage-origin-clean-1.html lime100x100-ref.html
 == canvas-drawImage-transform-restored.html canvas-drawImage-transform-restored-ref.html
 
 # Context paint tests (this feature is currently not part of any spec.)
 # context-fill:
 == context-fill-01.html blue100x100-ref.html
 test-pref(svg.context-properties.content.enabled,true) == context-fill-01.html lime100x100-ref.html
-fails-if(styloVsGecko||stylo) == context-fill-02.html transparent100x100-w-border-ref.html # Bug 1380590
+== context-fill-02.html transparent100x100-w-border-ref.html
 test-pref(svg.context-properties.content.enabled,true) == context-fill-02.html lime100x100-w-border-ref.html
 test-pref(svg.context-properties.content.enabled,true) == context-fill-03.html lime100x100-50pct-ref.html
 # fuzz because on win8 the r & b components are off by one
 fuzzy-if(winWidget,1,10000) test-pref(svg.context-properties.content.enabled,true) == context-fill-04.html lime100x100-50pct-ref.html
 test-pref(svg.context-properties.content.enabled,true) == context-fill-05.html context-fill-or-stroke-05-ref.html
 test-pref(svg.context-properties.content.enabled,true) == context-fill-06.html lime100x100-ref.html
 test-pref(svg.context-properties.content.enabled,true) == context-fill-07.html context-fill-07-ref.html
 test-pref(svg.context-properties.content.enabled,true) == context-fill-08.html blue100x100-ref.html
--- a/layout/reftests/writing-mode/reftest.list
+++ b/layout/reftests/writing-mode/reftest.list
@@ -134,17 +134,17 @@ test-pref(dom.meta-viewport.enabled,true
 == 1151993-1-orthogonal-block-size.html 1151993-1-orthogonal-block-size-ref.html
 == 1152941-1-orthogonal-blocksize-overflow.html 1152941-1-orthogonal-blocksize-overflow-ref.html
 == 1156021-text-indent-percent.html 1156021-text-indent-percent-ref.html
 == 1157752-upright-bidi.html 1157752-upright-bidi-ref.html
 == 1157758-1-vertical-arabic.html 1157758-1-vertical-arabic-ref.html
 == 1158549-1-vertical-block-size-constraints.html 1158549-1-vertical-block-size-constraints-ref.html
 == 1163238-orthogonal-auto-margins.html 1163238-orthogonal-auto-margins-ref.html
 == 1174450-intrinsic-sizing.html 1174450-intrinsic-sizing-ref.html
-fails-if((stylo||styloVsGecko)&&cocoaWidget) == 1175789-underline-overline-1.html 1175789-underline-overline-1-ref.html
+== 1175789-underline-overline-1.html 1175789-underline-overline-1-ref.html
 == 1188061-1-nsChangeHint_ClearAncestorIntrinsics.html 1188061-1-nsChangeHint_ClearAncestorIntrinsics-ref.html
 == 1188061-2-nsChangeHint_UpdateComputedBSize.html 1188061-2-nsChangeHint_UpdateComputedBSize-ref.html
 
 # tests involving sideways-lr mode
 == 1193519-sideways-lr-1.html 1193519-sideways-lr-1-ref.html
 == 1193519-sideways-lr-2.html 1193519-sideways-lr-2-ref.html
 fuzzy-if(winWidget,3,84) == 1193519-sideways-lr-3.html 1193519-sideways-lr-3-ref.html
 fuzzy-if(winWidget,3,112) fails-if(webrender) == 1193519-sideways-lr-4.html 1193519-sideways-lr-4-ref.html # see bug 1366692. Rounding error with WR enabled.
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -108,26 +108,28 @@ SERVO_BINDING_FUNC(Servo_StyleSet_GetKey
                    const nsACString* property,
                    nsTimingFunctionBorrowed timing_function,
                    RawGeckoKeyframeListBorrowedMut keyframe_list)
 SERVO_BINDING_FUNC(Servo_StyleSet_GetFontFaceRules, void,
                    RawServoStyleSetBorrowed set,
                    RawGeckoFontFaceRuleListBorrowedMut list)
 SERVO_BINDING_FUNC(Servo_StyleSet_GetCounterStyleRule, nsCSSCounterStyleRule*,
                    RawServoStyleSetBorrowed set, nsIAtom* name)
-SERVO_BINDING_FUNC(Servo_StyleSet_BuildFontFeatureValueSet, bool,
-                   RawServoStyleSetBorrowed set,
-                   gfxFontFeatureValueSet* list)
+// This function may return nullptr or gfxFontFeatureValueSet with zero reference.
+SERVO_BINDING_FUNC(Servo_StyleSet_BuildFontFeatureValueSet,
+                   gfxFontFeatureValueSet*,
+                   RawServoStyleSetBorrowed set)
 SERVO_BINDING_FUNC(Servo_StyleSet_ResolveForDeclarations,
                    ServoStyleContextStrong,
                    RawServoStyleSetBorrowed set,
                    ServoStyleContextBorrowedOrNull parent_style,
                    RawServoDeclarationBlockBorrowed declarations)
 SERVO_BINDING_FUNC(Servo_StyleSet_AddSizeOfExcludingThis, void,
                    mozilla::MallocSizeOf malloc_size_of,
+                   mozilla::MallocSizeOf malloc_enclosing_size_of,
                    mozilla::ServoStyleSetSizes* sizes,
                    RawServoStyleSetBorrowed set)
 SERVO_BINDING_FUNC(Servo_StyleContext_AddRef, void, ServoStyleContextBorrowed ctx);
 SERVO_BINDING_FUNC(Servo_StyleContext_Release, void, ServoStyleContextBorrowed ctx);
 
 SERVO_BINDING_FUNC(Servo_StyleSet_MightHaveAttributeDependency, bool,
                    RawServoStyleSetBorrowed set,
                    RawGeckoElementBorrowed element,
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1329,16 +1329,22 @@ Gecko_nsFont_InitSystem(nsFont* aDest, i
 }
 
 void
 Gecko_nsFont_Destroy(nsFont* aDest)
 {
   aDest->~nsFont();
 }
 
+gfxFontFeatureValueSet*
+Gecko_ConstructFontFeatureValueSet()
+{
+  return new gfxFontFeatureValueSet();
+}
+
 nsTArray<unsigned int>*
 Gecko_AppendFeatureValueHashEntry(gfxFontFeatureValueSet* aFontFeatureValues,
                                   nsIAtom* aFamily, uint32_t aAlternate, nsIAtom* aName)
 {
   MOZ_ASSERT(NS_IsMainThread());
   static_assert(sizeof(unsigned int) == sizeof(uint32_t),
                 "sizeof unsigned int and uint32_t must be the same");
   return aFontFeatureValues->AppendFeatureValueHashEntry(
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -296,16 +296,18 @@ void Gecko_FontFamilyList_AppendNamed(Fo
 void Gecko_FontFamilyList_AppendGeneric(FontFamilyList* list, FontFamilyType familyType);
 void Gecko_CopyFontFamilyFrom(nsFont* dst, const nsFont* src);
 // will not run destructors on dst, give it uninitialized memory
 // font_id is LookAndFeel::FontID
 void Gecko_nsFont_InitSystem(nsFont* dst, int32_t font_id,
                              const nsStyleFont* font, RawGeckoPresContextBorrowed pres_context);
 void Gecko_nsFont_Destroy(nsFont* dst);
 
+// The gfxFontFeatureValueSet returned from this function has zero reference.
+gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet();
 nsTArray<unsigned int>* Gecko_AppendFeatureValueHashEntry(
   gfxFontFeatureValueSet* value_set, nsIAtom* family, uint32_t alternate, nsIAtom* name);
 void Gecko_nsFont_SetFontFeatureValuesLookup(nsFont* font,
                                              const RawGeckoPresContext* pres_context);
 void Gecko_nsFont_ResetFontFeatureValuesLookup(nsFont* font);
 
 // Font variant alternates
 void Gecko_ClearAlternateValues(nsFont* font, size_t length);
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -244,34 +244,48 @@ ServoStyleSet::MediumFeaturesChangedRule
     MarkOriginsDirty(rulesChanged);
     return true;
   }
 
   return false;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSetMallocSizeOf)
+MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSetMallocEnclosingSizeOf)
 
 void
 ServoStyleSet::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
 {
   MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
 
   aSizes.mLayoutServoStyleSetsOther += mallocSizeOf(this);
 
   if (mRawSet) {
     aSizes.mLayoutServoStyleSetsOther += mallocSizeOf(mRawSet.get());
     ServoStyleSetSizes sizes;
     // Measure mRawSet. We use ServoStyleSetMallocSizeOf rather than
     // aMallocSizeOf to distinguish in DMD's output the memory measured within
     // Servo code.
-    Servo_StyleSet_AddSizeOfExcludingThis(ServoStyleSetMallocSizeOf, &sizes,
-                                          mRawSet.get());
-    aSizes.mLayoutServoStyleSetsStylistRuleTree += sizes.mStylistRuleTree;
-    aSizes.mLayoutServoStyleSetsOther += sizes.mOther;
+    Servo_StyleSet_AddSizeOfExcludingThis(ServoStyleSetMallocSizeOf,
+                                          ServoStyleSetMallocEnclosingSizeOf,
+                                          &sizes, mRawSet.get());
+    aSizes.mLayoutServoStyleSetsStylistRuleTree +=
+      sizes.mStylistRuleTree;
+    aSizes.mLayoutServoStyleSetsStylistPrecomputedPseudos +=
+      sizes.mStylistPrecomputedPseudos;
+    aSizes.mLayoutServoStyleSetsStylistElementAndPseudosMaps +=
+      sizes.mStylistElementAndPseudosMaps;
+    aSizes.mLayoutServoStyleSetsStylistInvalidationMap +=
+      sizes.mStylistInvalidationMap;
+    aSizes.mLayoutServoStyleSetsStylistRevalidationSelectors +=
+      sizes.mStylistRevalidationSelectors;
+    aSizes.mLayoutServoStyleSetsStylistOther +=
+      sizes.mStylistOther;
+    aSizes.mLayoutServoStyleSetsOther +=
+      sizes.mOther;
   }
 
   if (mStyleRuleMap) {
     aSizes.mLayoutServoStyleSetsOther +=
       mStyleRuleMap->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
   }
 
   // Measurement of the following members may be added later if DMD finds it is
@@ -1401,21 +1415,18 @@ ServoStyleSet::CounterStyleRuleForName(n
 {
   return Servo_StyleSet_GetCounterStyleRule(mRawSet.get(), aName);
 }
 
 already_AddRefed<gfxFontFeatureValueSet>
 ServoStyleSet::BuildFontFeatureValueSet()
 {
   UpdateStylistIfNeeded();
-  RefPtr<gfxFontFeatureValueSet> set = new gfxFontFeatureValueSet();
-  bool setHasAnyRules = Servo_StyleSet_BuildFontFeatureValueSet(mRawSet.get(), set.get());
-  if (!setHasAnyRules) {
-    return nullptr;
-  }
+  RefPtr<gfxFontFeatureValueSet> set =
+    Servo_StyleSet_BuildFontFeatureValueSet(mRawSet.get());
   return set.forget();
 }
 
 already_AddRefed<ServoStyleContext>
 ServoStyleSet::ResolveForDeclarations(
   const ServoStyleContext* aParentOrNull,
   RawServoDeclarationBlockBorrowed aDeclarations)
 {
--- a/layout/style/ServoTypes.h
+++ b/layout/style/ServoTypes.h
@@ -197,20 +197,30 @@ struct ServoComputedValueFlags {
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
 
 class ServoStyleSetSizes
 {
 public:
   size_t mStylistRuleTree;
+  size_t mStylistPrecomputedPseudos;
+  size_t mStylistElementAndPseudosMaps;
+  size_t mStylistInvalidationMap;
+  size_t mStylistRevalidationSelectors;
+  size_t mStylistOther;
   size_t mOther;
 
   ServoStyleSetSizes()
     : mStylistRuleTree(0)
+    , mStylistPrecomputedPseudos(0)
+    , mStylistElementAndPseudosMaps(0)
+    , mStylistInvalidationMap(0)
+    , mStylistRevalidationSelectors(0)
+    , mStylistOther(0)
     , mOther(0)
   {}
 };
 
 } // namespace mozilla
 
 class ServoComputedData;
 
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -159,16 +159,18 @@ support-files = file_bug1089417_iframe.h
 [test_bug1203766.html]
 [test_bug1232829.html]
 [test_bug1292447.html]
 [test_bug1371488.html]
 [test_bug1375944.html]
 support-files = file_bug1375944.html Ahem.ttf
 [test_bug1382568.html]
 support-files = bug1382568-iframe.html
+[test_bug1394302.html]
+skip-if = !stylo # This is a stylo test; gecko isn't deterministic here
 [test_cascade.html]
 [test_ch_ex_no_infloops.html]
 [test_change_hint_optimizations.html]
 [test_clip-path_polygon.html]
 [test_color_rounding.html]
 [test_compute_data_with_start_struct.html]
 skip-if = toolkit == 'android'
 [test_computed_style.html]
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_bug1394302.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1394302
+-->
+<head>
+  <title>Test for Bug 1394302</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <style>
+    #inner {
+      animation: setFontSize 0s forwards;
+    }
+    @keyframes setFontSize {
+      to { font-size: calc(110% + 0.1em); }
+    }
+    </style>
+</head>
+<body>
+<div id=outer>
+  <div id=inner></div>
+</div>
+<script>
+var outer = document.getElementById("outer");
+outer.style.fontSize = '10px';
+is(getComputedStyle(inner).fontSize, "12px");
+
+outer.style.fontSize = '20px';
+is(getComputedStyle(inner).fontSize, "24px");
+</script>
+</body>
+</html>
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -3909,37 +3909,16 @@ SVGTextFrame::GetBBoxContribution(const 
       run.GetUserSpaceRect(presContext, flags, &m);
     bbox.UnionEdges(bboxForRun);
   }
 
   return bbox;
 }
 
 //----------------------------------------------------------------------
-// nsSVGContainerFrame methods
-
-gfxMatrix
-SVGTextFrame::GetCanvasTM()
-{
-  if (!mCanvasTM) {
-    NS_ASSERTION(GetParent(), "null parent");
-    NS_ASSERTION(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
-                 "should not call GetCanvasTM() when we are non-display");
-
-    nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
-    dom::SVGTextContentElement *content = static_cast<dom::SVGTextContentElement*>(GetContent());
-
-    gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
-
-    mCanvasTM = new gfxMatrix(tm);
-  }
-  return *mCanvasTM;
-}
-
-//----------------------------------------------------------------------
 // SVGTextFrame SVG DOM methods
 
 /**
  * Returns whether the specified node has any non-empty nsTextNodes
  * beneath it.
  */
 static bool
 HasTextContent(nsIContent* aContent)
--- a/layout/svg/SVGTextFrame.h
+++ b/layout/svg/SVGTextFrame.h
@@ -252,19 +252,16 @@ public:
                         const gfxMatrix& aTransform,
                         imgDrawingParams& aImgParams,
                         const nsIntRect* aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   virtual void ReflowSVG() override;
   virtual SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace,
                                       uint32_t aFlags) override;
 
-  // nsSVGContainerFrame methods:
-  virtual gfxMatrix GetCanvasTM() override;
-
   // SVG DOM text methods:
   uint32_t GetNumberOfChars(nsIContent* aContent);
   float GetComputedTextLength(nsIContent* aContent);
   nsresult SelectSubString(nsIContent* aContent, uint32_t charnum, uint32_t nchars);
   nsresult GetSubStringLength(nsIContent* aContent, uint32_t charnum,
                               uint32_t nchars, float* aResult);
   int32_t GetCharNumAtPosition(nsIContent* aContent, mozilla::nsISVGPoint* point);
 
@@ -529,21 +526,16 @@ private:
   gfxFloat GetStartOffset(nsIFrame* aTextPathFrame);
 
   /**
    * The MutationObserver we have registered for the <text> element subtree.
    */
   RefPtr<MutationObserver> mMutationObserver;
 
   /**
-   * Cached canvasTM value.
-   */
-  nsAutoPtr<gfxMatrix> mCanvasTM;
-
-  /**
    * The number of characters in the DOM after the final nsTextFrame.  For
    * example, with
    *
    *   <text>abcd<tspan display="none">ef</tspan></text>
    *
    * mTrailingUndisplayedCharacters would be 2.
    */
   uint32_t mTrailingUndisplayedCharacters;
--- a/layout/svg/nsSVGAFrame.cpp
+++ b/layout/svg/nsSVGAFrame.cpp
@@ -39,24 +39,16 @@ public:
                                      int32_t         aModType) override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGA"), aResult);
   }
 #endif
-  // nsSVGDisplayableFrame interface:
-  virtual void NotifySVGChanged(uint32_t aFlags) override;
-
-  // nsSVGContainerFrame methods:
-  virtual gfxMatrix GetCanvasTM() override;
-
-private:
-  nsAutoPtr<gfxMatrix> mCanvasTM;
 };
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame*
 NS_NewSVGAFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
@@ -106,46 +98,9 @@ nsSVGAFrame::AttributeChanged(int32_t   
     dom::SVGAElement* content = static_cast<dom::SVGAElement*>(GetContent());
 
     // SMIL may change whether an <a> element is a link, in which case we will
     // need to update the link state.
     content->ResetLinkState(true, content->ElementHasHref());
   }
 
  return NS_OK;
-}
-
-//----------------------------------------------------------------------
-// nsSVGDisplayableFrame methods
-
-void
-nsSVGAFrame::NotifySVGChanged(uint32_t aFlags)
-{
-  MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
-             "Invalidation logic may need adjusting");
-
-  if (aFlags & TRANSFORM_CHANGED) {
-    // make sure our cached transform matrix gets (lazily) updated
-    mCanvasTM = nullptr;
-  }
-
-  nsSVGDisplayContainerFrame::NotifySVGChanged(aFlags);
-}
-
-//----------------------------------------------------------------------
-// nsSVGContainerFrame methods:
-
-gfxMatrix
-nsSVGAFrame::GetCanvasTM()
-{
-  if (!mCanvasTM) {
-    NS_ASSERTION(GetParent(), "null parent");
-
-    nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
-    dom::SVGAElement *content = static_cast<dom::SVGAElement*>(GetContent());
-
-    gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
-
-    mCanvasTM = new gfxMatrix(tm);
-  }
-
-  return *mCanvasTM;
-}
+}
\ No newline at end of file
--- a/layout/svg/nsSVGContainerFrame.cpp
+++ b/layout/svg/nsSVGContainerFrame.cpp
@@ -396,16 +396,21 @@ nsSVGDisplayContainerFrame::ReflowSVG()
 }
 
 void
 nsSVGDisplayContainerFrame::NotifySVGChanged(uint32_t aFlags)
 {
   MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
              "Invalidation logic may need adjusting");
 
+  if (aFlags & TRANSFORM_CHANGED) {
+    // make sure our cached transform matrix gets (lazily) updated
+    mCanvasTM = nullptr;
+  }
+
   nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags);
 }
 
 SVGBBox
 nsSVGDisplayContainerFrame::GetBBoxContribution(
   const Matrix &aToBBoxUserspace,
   uint32_t aFlags)
 {
@@ -428,8 +433,25 @@ nsSVGDisplayContainerFrame::GetBBoxContr
       // to use UnionEdges.
       bboxUnion.UnionEdges(svgKid->GetBBoxContribution(gfx::ToMatrix(transform), aFlags));
     }
     kid = kid->GetNextSibling();
   }
 
   return bboxUnion;
 }
+
+gfxMatrix
+nsSVGDisplayContainerFrame::GetCanvasTM()
+{
+  if (!mCanvasTM) {
+    NS_ASSERTION(GetParent(), "null parent");
+
+    nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
+    nsSVGElement *content = static_cast<nsSVGElement*>(GetContent());
+
+    gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
+
+    mCanvasTM = new gfxMatrix(tm);
+  }
+
+  return *mCanvasTM;
+}
--- a/layout/svg/nsSVGContainerFrame.h
+++ b/layout/svg/nsSVGContainerFrame.h
@@ -142,11 +142,18 @@ public:
                         imgDrawingParams& aImgParams,
                         const nsIntRect* aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
   virtual bool IsDisplayContainer() override { return true; }
+  virtual gfxMatrix GetCanvasTM() override;
+
+protected:
+  /**
+   * Cached canvasTM value.
+   */
+  nsAutoPtr<gfxMatrix> mCanvasTM;
 };
 
 #endif
--- a/layout/svg/nsSVGGFrame.cpp
+++ b/layout/svg/nsSVGGFrame.cpp
@@ -39,46 +39,16 @@ nsSVGGFrame::Init(nsIContent*       aCon
 
   nsSVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
 }
 #endif /* DEBUG */
 
 //----------------------------------------------------------------------
 // nsSVGDisplayableFrame methods
 
-void
-nsSVGGFrame::NotifySVGChanged(uint32_t aFlags)
-{
-  MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
-             "Invalidation logic may need adjusting");
-
-  if (aFlags & TRANSFORM_CHANGED) {
-    // make sure our cached transform matrix gets (lazily) updated
-    mCanvasTM = nullptr;
-  }
-
-  nsSVGDisplayContainerFrame::NotifySVGChanged(aFlags);
-}
-
-gfxMatrix
-nsSVGGFrame::GetCanvasTM()
-{
-  if (!mCanvasTM) {
-    NS_ASSERTION(GetParent(), "null parent");
-
-    nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
-    SVGGraphicsElement *content = static_cast<SVGGraphicsElement*>(GetContent());
-
-    gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
-
-    mCanvasTM = new gfxMatrix(tm);
-  }
-  return *mCanvasTM;
-}
-
 nsresult
 nsSVGGFrame::AttributeChanged(int32_t         aNameSpaceID,
                               nsIAtom*        aAttribute,
                               int32_t         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       aAttribute == nsGkAtoms::transform) {
     // We don't invalidate for transform changes (the layers code does that).
--- a/layout/svg/nsSVGGFrame.h
+++ b/layout/svg/nsSVGGFrame.h
@@ -38,19 +38,11 @@ public:
     return MakeFrameName(NS_LITERAL_STRING("SVGG"), aResult);
   }
 #endif
 
   // nsIFrame interface:
   virtual nsresult AttributeChanged(int32_t         aNameSpaceID,
                                     nsIAtom*        aAttribute,
                                     int32_t         aModType) override;
-
-  // nsSVGDisplayableFrame interface:
-  virtual void NotifySVGChanged(uint32_t aFlags) override;
-
-  // nsSVGContainerFrame methods:
-  virtual gfxMatrix GetCanvasTM() override;
-
-  nsAutoPtr<gfxMatrix> mCanvasTM;
 };
 
 #endif
--- a/layout/svg/nsSVGOuterSVGFrame.h
+++ b/layout/svg/nsSVGOuterSVGFrame.h
@@ -182,18 +182,16 @@ protected:
 
   // This is temporary until display list based invalidation is implemented for
   // SVG.
   // A hash-set containing our nsSVGForeignObjectFrame descendants. Note we use
   // a hash-set to avoid the O(N^2) behavior we'd get tearing down an SVG frame
   // subtree if we were to use a list (see bug 381285 comment 20).
   nsAutoPtr<nsTHashtable<nsPtrHashKey<nsSVGForeignObjectFrame> > > mForeignObjectHash;
 
-  nsAutoPtr<gfxMatrix> mCanvasTM;
-
   nsRegion mInvalidRegion;
 
   float mFullZoom;
 
   bool mViewportInitialized;
   bool mIsRootContent;
 };
 
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -384,18 +384,22 @@ public:
    */
   static gfxMatrix AdjustMatrixForUnits(const gfxMatrix &aMatrix,
                                         nsSVGEnum *aUnits,
                                         nsIFrame *aFrame,
                                         uint32_t aFlags);
 
   enum BBoxFlags {
     eBBoxIncludeFill           = 1 << 0,
+    // Include the geometry of the fill even when the fill does not
+    // actually render (e.g. when fill="none" or fill-opacity="0")
     eBBoxIncludeFillGeometry   = 1 << 1,
     eBBoxIncludeStroke         = 1 << 2,
+    // Include the geometry of the stroke even when the stroke does not
+    // actually render (e.g. when stroke="none" or stroke-opacity="0")
     eBBoxIncludeStrokeGeometry = 1 << 3,
     eBBoxIncludeMarkers        = 1 << 4,
     eBBoxIncludeClipped        = 1 << 5,
     // Normally a getBBox call on outer-<svg> should only return the
     // bounds of the elements children. This flag will cause the
     // element's bounds to be returned instead.
     eUseFrameBoundsForOuterSVG = 1 << 6,
     // https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
--- a/layout/svg/nsSVGViewportFrame.cpp
+++ b/layout/svg/nsSVGViewportFrame.cpp
@@ -120,21 +120,16 @@ nsSVGViewportFrame::NotifySVGChanged(uin
       aFlags &= ~COORD_CONTEXT_CHANGED;
 
       if (!aFlags) {
         return; // No notification flags left
       }
     }
   }
 
-  if (aFlags & TRANSFORM_CHANGED) {
-    // make sure our cached transform matrix gets (lazily) updated
-    mCanvasTM = nullptr;
-  }
-
   nsSVGDisplayContainerFrame::NotifySVGChanged(aFlags);
 }
 
 SVGBBox
 nsSVGViewportFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                         uint32_t aFlags)
 {
   // XXXjwatt It seems like authors would want the result to be clipped by the
@@ -272,32 +267,16 @@ nsSVGViewportFrame::NotifyViewportOrTran
   // attributes. Changes to all of these attributes are handled in
   // AttributeChanged(), so we should never be called.
   NS_ERROR("Not called for nsSVGViewportFrame");
 }
 
 //----------------------------------------------------------------------
 // nsSVGContainerFrame methods:
 
-gfxMatrix
-nsSVGViewportFrame::GetCanvasTM()
-{
-  if (!mCanvasTM) {
-    NS_ASSERTION(GetParent(), "null parent");
-
-    nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
-    SVGViewportElement *content = static_cast<SVGViewportElement*>(GetContent());
-
-    gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
-
-    mCanvasTM = new gfxMatrix(tm);
-  }
-  return *mCanvasTM;
-}
-
 bool
 nsSVGViewportFrame::HasChildrenOnlyTransform(gfx::Matrix *aTransform) const
 {
   SVGViewportElement *content = static_cast<SVGViewportElement*>(GetContent());
 
   if (content->HasViewBoxOrSyntheticViewBox()) {
     // XXX Maybe return false if the transform is the identity transform?
     if (aTransform) {
--- a/layout/svg/nsSVGViewportFrame.h
+++ b/layout/svg/nsSVGViewportFrame.h
@@ -38,22 +38,16 @@ public:
                         const nsIntRect* aDirtyRect = nullptr) override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                               uint32_t aFlags) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
 
   // nsSVGContainerFrame methods:
-  virtual gfxMatrix GetCanvasTM() override;
-
   virtual bool HasChildrenOnlyTransform(Matrix *aTransform) const override;
 
   // nsISVGSVGFrame interface:
   virtual void NotifyViewportOrTransformChanged(uint32_t aFlags) override;
-
-protected:
-
-  nsAutoPtr<gfxMatrix> mCanvasTM;
 };
 
 #endif // __NS_SVGVIEWPORTFRAME_H__
 
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -225,18 +225,22 @@ nsTableCellFrame::AttributeChanged(int32
 {
   // We need to recalculate in this case because of the nowrap quirk in
   // BasicTableLayoutStrategy
   if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap &&
       PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
   }
-  // let the table frame decide what to do
-  GetTableFrame()->AttributeChangedFor(this, mContent, aAttribute);
+
+  if (aAttribute == nsGkAtoms::rowspan || aAttribute == nsGkAtoms::colspan) {
+    nsLayoutUtils::PostRestyleEvent(mContent->AsElement(),
+                                    nsRestyleHint(0),
+                                    nsChangeHint_UpdateTableCellSpans);
+  }
   return NS_OK;
 }
 
 /* virtual */ void
 nsTableCellFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
 
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -350,40 +350,34 @@ nsTableFrame::SetInitialChildList(ChildL
     // calc collapsing borders
     if (IsBorderCollapse()) {
       SetFullBCDamageArea();
     }
   }
 }
 
 void
-nsTableFrame::AttributeChangedFor(nsIFrame*       aFrame,
-                                  nsIContent*     aContent,
-                                  nsIAtom*        aAttribute)
-{
-  nsTableCellFrame *cellFrame = do_QueryFrame(aFrame);
-  if (cellFrame) {
-    if ((nsGkAtoms::rowspan == aAttribute) ||
-        (nsGkAtoms::colspan == aAttribute)) {
-      nsTableCellMap* cellMap = GetCellMap();
-      if (cellMap) {
-        // for now just remove the cell from the map and reinsert it
-        int32_t rowIndex, colIndex;
-        cellFrame->GetRowIndex(rowIndex);
-        cellFrame->GetColIndex(colIndex);
-        RemoveCell(cellFrame, rowIndex);
-        AutoTArray<nsTableCellFrame*, 1> cells;
-        cells.AppendElement(cellFrame);
-        InsertCells(cells, rowIndex, colIndex - 1);
-
-        // XXX Should this use eStyleChange?  It currently doesn't need
-        // to, but it might given more optimization.
-        PresContext()->PresShell()->
-          FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
-      }
+nsTableFrame::RowOrColSpanChanged(nsTableCellFrame* aCellFrame)
+{
+  if (aCellFrame) {
+    nsTableCellMap* cellMap = GetCellMap();
+    if (cellMap) {
+      // for now just remove the cell from the map and reinsert it
+      int32_t rowIndex, colIndex;
+      aCellFrame->GetRowIndex(rowIndex);
+      aCellFrame->GetColIndex(colIndex);
+      RemoveCell(aCellFrame, rowIndex);
+      AutoTArray<nsTableCellFrame*, 1> cells;
+      cells.AppendElement(aCellFrame);
+      InsertCells(cells, rowIndex, colIndex - 1);
+
+      // XXX Should this use eStyleChange?  It currently doesn't need
+      // to, but it might given more optimization.
+      PresContext()->PresShell()->
+        FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
     }
   }
 }
 
 
 /* ****** CellMap methods ******* */
 
 /* return the effective col count */
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -175,22 +175,22 @@ public:
   // function for more explanation.) Should be called during frame construction.
   static void RegisterPositionedTablePart(nsIFrame* aFrame);
 
   // Unregister a positioned table part with its nsTableFrame.
   static void UnregisterPositionedTablePart(nsIFrame* aFrame,
                                             nsIFrame* aDestructRoot);
 
   nsPoint GetFirstSectionOrigin(const ReflowInput& aReflowInput) const;
+
   /*
-   * Notification that aAttribute has changed for content inside a table (cell, row, etc)
+   * Notification that rowspan or colspan has changed for content inside a
+   * table cell
    */
-  void AttributeChangedFor(nsIFrame*       aFrame,
-                           nsIContent*     aContent,
-                           nsIAtom*        aAttribute);
+  void RowOrColSpanChanged(nsTableCellFrame* aCellFrame);
 
   /** @see nsIFrame::DestroyFrom */
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
 
   /** @see nsIFrame::DidSetStyleContext */
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
 
   virtual void SetInitialChildList(ChildListID     aListID,
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -17,16 +17,19 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/TelemetryController.jsm");
 
 if (AppConstants.ACCESSIBILITY) {
   XPCOMUtils.defineLazyModuleGetter(this, "AccessFu",
                                     "resource://gre/modules/accessibility/AccessFu.jsm");
 }
 
+XPCOMUtils.defineLazyModuleGetter(this, "AsyncPrefs",
+                                  "resource://gre/modules/AsyncPrefs.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "Manifests",
                                   "resource://gre/modules/Manifest.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "JNI",
                                   "resource://gre/modules/JNI.jsm");
@@ -524,16 +527,19 @@ var BrowserApp = {
     GlobalEventDispatcher.sendRequest({ type: "Gecko:Ready" });
 
     this.deck.addEventListener("DOMContentLoaded", function() {
       InitLater(() => Cu.import("resource://gre/modules/NotificationDB.jsm"));
 
       InitLater(() => Services.obs.notifyObservers(window, "browser-delayed-startup-finished"));
       InitLater(() => GlobalEventDispatcher.sendRequest({ type: "Gecko:DelayedStartup" }));
 
+      // AsyncPrefs is needed for reader mode.
+      InitLater(() => AsyncPrefs.init());
+
       if (!AppConstants.RELEASE_OR_BETA) {
         InitLater(() => WebcompatReporter.init());
       }
 
       // Collect telemetry data.
       // We do this at startup because we want to move away from "gather-telemetry" (bug 1127907)
       InitLater(() => {
         Telemetry.addData("FENNEC_TRACKING_PROTECTION_STATE", parseInt(BrowserApp.getTrackingProtectionState()));
--- a/mobile/android/chrome/geckoview/GeckoViewContent.js
+++ b/mobile/android/chrome/geckoview/GeckoViewContent.js
@@ -1,18 +1,21 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/. */
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/GeckoViewContentModule.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "ViewContent");
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "ViewContent"));
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 class GeckoViewContent extends GeckoViewContentModule {
   register() {
     debug("register");
--- a/mobile/android/chrome/geckoview/GeckoViewScrollContent.js
+++ b/mobile/android/chrome/geckoview/GeckoViewScrollContent.js
@@ -1,19 +1,21 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/. */
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/GeckoViewContentModule.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {})
-           .AndroidLog.d.bind(null, "ViewScrollContent");
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "ViewScrollContent"));
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 class GeckoViewScrollContent extends GeckoViewContentModule {
   register() {
     debug("register");
--- a/mobile/android/chrome/geckoview/geckoview.js
+++ b/mobile/android/chrome/geckoview/geckoview.js
@@ -9,18 +9,19 @@ const { classes: Cc, interfaces: Ci, uti
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
   "resource://gre/modules/Messaging.jsm");
 XPCOMUtils.defineLazyGetter(this, "WindowEventDispatcher",
   () => EventDispatcher.for(window));
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {})
-           .AndroidLog.d.bind(null, "View");
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "View"));
 
 // Creates and manages GeckoView modules.
 // A module must extend GeckoViewModule.
 // Instantiate a module by calling
 //   add(<resource path>, <type name>)
 // and remove by calling
 //   remove(<type name>)
 var ModuleManager = {
--- a/mobile/android/components/geckoview/GeckoView.manifest
+++ b/mobile/android/components/geckoview/GeckoView.manifest
@@ -1,18 +1,21 @@
 # Stylesheets
 category agent-style-sheets browser-content-stylesheet chrome://geckoview/skin/content.css
 
+# GeckoViewStartup.js
+component {8e993c34-fdd6-432c-967e-f995d888777f} GeckoViewStartup.js
+contract @mozilla.org/geckoview/startup;1 {8e993c34-fdd6-432c-967e-f995d888777f}
+category app-startup GeckoViewStartup service,@mozilla.org/geckoview/startup;1
+category profile-after-change GeckoViewStartup @mozilla.org/geckoview/startup;1 process=main
+
 # GeckoViewPermission.js
 component {42f3c238-e8e8-4015-9ca2-148723a8afcf} GeckoViewPermission.js
 contract @mozilla.org/content-permission/prompt;1 {42f3c238-e8e8-4015-9ca2-148723a8afcf}
-category app-startup GeckoViewPermission service,@mozilla.org/content-permission/prompt;1
 
 # GeckoViewPrompt.js
 component {076ac188-23c1-4390-aa08-7ef1f78ca5d9} GeckoViewPrompt.js
 contract @mozilla.org/embedcomp/prompt-service;1 {076ac188-23c1-4390-aa08-7ef1f78ca5d9}
 contract @mozilla.org/prompter;1 {076ac188-23c1-4390-aa08-7ef1f78ca5d9}
-category app-startup GeckoViewPrompt service,@mozilla.org/prompter;1
-category profile-after-change GeckoViewPrompt @mozilla.org/prompter;1 process=main
 component {aa0dd6fc-73dd-4621-8385-c0b377e02cee} GeckoViewPrompt.js process=main
 contract @mozilla.org/colorpicker;1 {aa0dd6fc-73dd-4621-8385-c0b377e02cee} process=main
 component {e4565e36-f101-4bf5-950b-4be0887785a9} GeckoViewPrompt.js process=main
 contract @mozilla.org/filepicker;1 {e4565e36-f101-4bf5-950b-4be0887785a9} process=main
--- a/mobile/android/components/geckoview/GeckoViewPermission.js
+++ b/mobile/android/components/geckoview/GeckoViewPermission.js
@@ -1,45 +1,41 @@
 /* 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/. */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
-                                  "resource://gre/modules/Messaging.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  EventDispatcher: "resource://gre/modules/Messaging.jsm",
+  Services: "resource://gre/modules/Services.jsm",
+});
 
 // See: http://developer.android.com/reference/android/Manifest.permission.html
 const PERM_ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
 const PERM_CAMERA = "android.permission.CAMERA";
 const PERM_RECORD_AUDIO = "android.permission.RECORD_AUDIO";
 
 function GeckoViewPermission() {
+  this.wrappedJSObject = this;
 }
 
 GeckoViewPermission.prototype = {
   classID: Components.ID("{42f3c238-e8e8-4015-9ca2-148723a8afcf}"),
 
   QueryInterface: XPCOMUtils.generateQI([
       Ci.nsIObserver, Ci.nsIContentPermissionPrompt]),
 
   _appPermissions: {},
 
   /* ----------  nsIObserver  ---------- */
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
-      case "app-startup": {
-        Services.obs.addObserver(this, "getUserMedia:ask-device-permission");
-        Services.obs.addObserver(this, "getUserMedia:request");
-        Services.obs.addObserver(this, "PeerConnection:request");
-        break;
-      }
       case "getUserMedia:ask-device-permission": {
         this.handleMediaAskDevicePermission(aData, aSubject);
         break;
       }
       case "getUserMedia:request": {
         this.handleMediaRequest(aSubject);
         break;
       }
--- a/mobile/android/components/geckoview/GeckoViewPrompt.js
+++ b/mobile/android/components/geckoview/GeckoViewPrompt.js
@@ -1,73 +1,34 @@
 /* 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/. */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "AsyncPrefs",
-                                  "resource://gre/modules/AsyncPrefs.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "ContentPrefServiceParent",
-                                  "resource://gre/modules/ContentPrefServiceParent.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
-                                  "resource://gre/modules/Messaging.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
-                                  "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  EventDispatcher: "resource://gre/modules/Messaging.jsm",
+  FileUtils: "resource://gre/modules/FileUtils.jsm",
+  Services: "resource://gre/modules/Services.jsm",
+});
 
 XPCOMUtils.defineLazyServiceGetter(this, "UUIDGen",
                                    "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
 
 function PromptFactory() {
+  this.wrappedJSObject = this;
 }
 
 PromptFactory.prototype = {
   classID: Components.ID("{076ac188-23c1-4390-aa08-7ef1f78ca5d9}"),
 
   QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIObserver, Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
-
-  /* ----------  nsIObserver  ---------- */
-  observe: function(aSubject, aTopic, aData) {
-    switch (aTopic) {
-      case "app-startup": {
-        Services.obs.addObserver(this, "chrome-document-global-created");
-        Services.obs.addObserver(this, "content-document-global-created");
-        break;
-      }
-      case "profile-after-change": {
-        // ContentPrefServiceParent is needed for e10s file picker.
-        ContentPrefServiceParent.init();
-        // AsyncPrefs is needed for reader mode.
-        AsyncPrefs.init();
-        Services.mm.addMessageListener("GeckoView:Prompt", this);
-        break;
-      }
-      case "chrome-document-global-created":
-      case "content-document-global-created": {
-        let win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDocShell).QueryInterface(Ci.nsIDocShellTreeItem)
-                          .rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindow);
-        if (win !== aSubject) {
-          // Only attach to top-level windows.
-          return;
-        }
-        win.addEventListener("click", this); // non-capture
-        win.addEventListener("contextmenu", this); // non-capture
-        break;
-      }
-    }
-  },
+    Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "click":
         this._handleClick(aEvent);
         break;
       case "contextmenu":
         this._handleContextMenu(aEvent);
@@ -403,17 +364,17 @@ function PromptDelegate(aDomWin) {
   if (!aDomWin) {
     return;
   }
   let gvWin = aDomWin.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDocShell).QueryInterface(Ci.nsIDocShellTreeItem)
                      .rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindow);
   try {
-    this._dispatcher = EventDispatcher.for(gvWin);
+    this._dispatcher = gvWin.WindowEventDispatcher || EventDispatcher.for(gvWin);
   } catch (e) {
     // Use global dispatcher.
   }
 }
 
 PromptDelegate.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/components/geckoview/GeckoViewStartup.js
@@ -0,0 +1,155 @@
+/* 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/. */
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+                                  "resource://gre/modules/Services.jsm");
+
+function GeckoViewStartup() {
+}
+
+GeckoViewStartup.prototype = {
+  classID: Components.ID("{8e993c34-fdd6-432c-967e-f995d888777f}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  addLazyGetter: function(aOptions) {
+    let {name, script, service, module, observers, ppmm, mm} = aOptions;
+    if (script) {
+      XPCOMUtils.defineLazyScriptGetter(this, name, script);
+    } else if (module) {
+      XPCOMUtils.defineLazyGetter(this, name, _ => {
+        let sandbox = {};
+        Cu.import(module, sandbox);
+        if (aOptions.init) {
+          aOptions.init.call(this, sandbox[name]);
+        }
+        return sandbox[name];
+      });
+    } else if (service) {
+      XPCOMUtils.defineLazyGetter(this, name, _ =>
+        Cc[service].getService(Ci.nsISupports).wrappedJSObject);
+    }
+
+    if (observers) {
+      let observer = (subject, topic, data) => {
+        Services.obs.removeObserver(observer, topic);
+        if (!aOptions.once) {
+          Services.obs.addObserver(this[name], topic);
+        }
+        this[name].observe(subject, topic, data); // Explicitly notify new observer
+      };
+      observers.forEach(topic => Services.obs.addObserver(observer, topic));
+    }
+
+    if (ppmm || mm) {
+      let target = ppmm ? Services.ppmm : Services.mm;
+      let listener = msg => {
+        target.removeMessageListener(msg.name, listener);
+        if (!aOptions.once) {
+          target.addMessageListener(msg.name, this[name]);
+        }
+        this[name].receiveMessage(msg);
+      };
+      (ppmm || mm).forEach(msg => target.addMessageListener(msg, listener));
+    }
+  },
+
+  addLazyEventListener: function(aOptions) {
+    let {name, target, events, options} = aOptions;
+    let listener = event => {
+      if (!options || !options.once) {
+        target.removeEventListener(event.type, listener, options);
+        target.addEventListener(event.type, this[name], options);
+      }
+      this[name].handleEvent(event);
+    };
+    events.forEach(event => target.addEventListener(event, listener, options));
+  },
+
+  /* ----------  nsIObserver  ---------- */
+  observe: function(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "app-startup": {
+        // Parent and content process.
+        Services.obs.addObserver(this, "chrome-document-global-created");
+        Services.obs.addObserver(this, "content-document-global-created");
+
+        this.addLazyGetter({
+          name: "GeckoViewPermission",
+          service: "@mozilla.org/content-permission/prompt;1",
+          observers: [
+            "getUserMedia:ask-device-permission",
+            "getUserMedia:request",
+            "PeerConnection:request",
+          ],
+        });
+
+        if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT) {
+          // Content process only.
+          this.addLazyGetter({
+            name: "GeckoViewPrompt",
+            service: "@mozilla.org/prompter;1",
+          });
+        }
+        break;
+      }
+
+      case "profile-after-change": {
+        // Parent process only.
+        // ContentPrefServiceParent is needed for e10s file picker.
+        this.addLazyGetter({
+          name: "ContentPrefServiceParent",
+          module: "resource://gre/modules/ContentPrefServiceParent.jsm",
+          init: cpsp => cpsp.alwaysInit(),
+          ppmm: [
+            "ContentPrefs:FunctionCall",
+            "ContentPrefs:AddObserverForName",
+            "ContentPrefs:RemoveObserverForName",
+          ],
+        });
+
+        this.addLazyGetter({
+          name: "GeckoViewPrompt",
+          service: "@mozilla.org/prompter;1",
+          mm: [
+            "GeckoView:Prompt",
+          ],
+        });
+        break;
+      }
+
+      case "chrome-document-global-created":
+      case "content-document-global-created": {
+        let win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDocShell).QueryInterface(Ci.nsIDocShellTreeItem)
+                          .rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDOMWindow);
+        if (win !== aSubject) {
+          // Only attach to top-level windows.
+          return;
+        }
+
+        this.addLazyEventListener({
+          name: "GeckoViewPrompt",
+          target: win,
+          events: [
+            "click",
+            "contextmenu",
+          ],
+          options: {
+            capture: false,
+            mozSystemGroup: true,
+          },
+        });
+        break;
+      }
+    }
+  },
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([GeckoViewStartup]);
--- a/mobile/android/components/geckoview/moz.build
+++ b/mobile/android/components/geckoview/moz.build
@@ -3,9 +3,10 @@
 # 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/.
 
 EXTRA_COMPONENTS += [
     'GeckoView.manifest',
     'GeckoViewPermission.js',
     'GeckoViewPrompt.js',
+    'GeckoViewStartup.js',
 ]
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -274,18 +274,20 @@
 @BINPATH@/components/FeedProcessor.manifest
 @BINPATH@/components/FeedProcessor.js
 @BINPATH@/components/UAOverridesBootstrapper.js
 @BINPATH@/components/UAOverridesBootstrapper.manifest
 @BINPATH@/components/WellKnownOpportunisticUtils.js
 @BINPATH@/components/WellKnownOpportunisticUtils.manifest
 @BINPATH@/components/mozProtocolHandler.js
 @BINPATH@/components/mozProtocolHandler.manifest
+#ifndef MOZ_GECKOVIEW_JAR
 @BINPATH@/components/nsDNSServiceDiscovery.manifest
 @BINPATH@/components/nsDNSServiceDiscovery.js
+#endif
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsSearchService.js
 @BINPATH@/components/nsSidebar.js
 @BINPATH@/components/passwordmgr.manifest
 @BINPATH@/components/nsLoginInfo.js
 @BINPATH@/components/nsLoginManager.js
 @BINPATH@/components/nsLoginManagerPrompter.js
 @BINPATH@/components/storage-mozStorage.js
@@ -521,16 +523,17 @@
 [mobile]
 @BINPATH@/chrome/geckoview@JAREXT@
 @BINPATH@/chrome/geckoview.manifest
 
 #ifdef MOZ_GECKOVIEW_JAR
 @BINPATH@/components/GeckoView.manifest
 @BINPATH@/components/GeckoViewPrompt.js
 @BINPATH@/components/GeckoViewPermission.js
+@BINPATH@/components/GeckoViewStartup.js
 #else
 @BINPATH@/chrome/chrome@JAREXT@
 @BINPATH@/chrome/chrome.manifest
 @BINPATH@/components/AboutRedirector.js
 @BINPATH@/components/AddonUpdateService.js
 @BINPATH@/components/BlocklistPrompt.js
 @BINPATH@/components/BrowserCLH.js
 @BINPATH@/components/ColorPicker.js
--- a/mobile/android/modules/geckoview/GeckoViewContent.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewContent.jsm
@@ -4,19 +4,21 @@
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["GeckoViewContent"];
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/GeckoViewModule.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {})
-           .AndroidLog.d.bind(null, "ViewContent");
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "ViewContent"));
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 class GeckoViewContent extends GeckoViewModule {
   init() {
     this.frameScriptLoaded = false;
--- a/mobile/android/modules/geckoview/GeckoViewContentModule.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewContentModule.jsm
@@ -8,18 +8,19 @@ this.EXPORTED_SYMBOLS = ["GeckoViewConte
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
   "resource://gre/modules/Messaging.jsm");
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {})
-           .AndroidLog.d.bind(null, "ViewContentModule");
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "ViewContentModule"));
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 class GeckoViewContentModule {
   constructor(aModuleName, aMessageManager) {
     this.moduleName = aModuleName;
--- a/mobile/android/modules/geckoview/GeckoViewModule.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewModule.jsm
@@ -3,18 +3,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["GeckoViewModule"];
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {})
-           .AndroidLog.d.bind(null, "ViewModule");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "ViewModule"));
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 class GeckoViewModule {
   constructor(aModuleName, aWindow, aBrowser, aEventDispatcher) {
     this.window = aWindow;
--- a/mobile/android/modules/geckoview/GeckoViewNavigation.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewNavigation.jsm
@@ -11,18 +11,19 @@ const { classes: Cc, interfaces: Ci, uti
 Cu.import("resource://gre/modules/GeckoViewModule.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
   "resource://gre/modules/Messaging.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {})
-           .AndroidLog.d.bind(null, "ViewNavigation");
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "ViewNavigation"));
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 // Handles navigation requests between Gecko and a GeckoView.
 // Handles GeckoView:GoBack and :GoForward requests dispatched by
 // GeckoView.goBack and .goForward.
--- a/mobile/android/modules/geckoview/GeckoViewProgress.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
@@ -10,18 +10,19 @@ const { classes: Cc, interfaces: Ci, uti
 
 Cu.import("resource://gre/modules/GeckoViewModule.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
   "resource://gre/modules/Messaging.jsm");
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {})
-           .AndroidLog.d.bind(null, "ViewProgress");
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "ViewProgress"));
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 var IdentityHandler = {
   // No trusted identity information. No site identity icon is shown.
   IDENTITY_MODE_UNKNOWN: "unknown",
--- a/mobile/android/modules/geckoview/GeckoViewScroll.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewScroll.jsm
@@ -4,19 +4,21 @@
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["GeckoViewScroll"];
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/GeckoViewModule.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {})
-           .AndroidLog.d.bind(null, "ViewScroll");
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "ViewScroll"));
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 class GeckoViewScroll extends GeckoViewModule {
   init() {
     debug("init");
--- a/mobile/android/modules/geckoview/GeckoViewSettings.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewSettings.jsm
@@ -9,18 +9,19 @@ this.EXPORTED_SYMBOLS = ["GeckoViewSetti
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/GeckoViewModule.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
   "resource://gre/modules/SafeBrowsing.jsm");
 
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {})
-           .AndroidLog.d.bind(null, "ViewSettings");
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    Cu.import("resource://gre/modules/AndroidLog.jsm",
+              {}).AndroidLog.d.bind(null, "ViewSettings"));
 
 function debug(aMsg) {
   // dump(aMsg);
 }
 
 // Handles GeckoView settings including:
 // * tracking protection
 // * multiprocess
--- a/mobile/android/modules/geckoview/Messaging.jsm
+++ b/mobile/android/modules/geckoview/Messaging.jsm
@@ -191,30 +191,30 @@ DispatcherDelegate.prototype = {
 
     this._requestHandler.listeners[event] = undefined;
     this.unregisterListener(this._requestHandler, event);
   },
 
   _requestHandler: {
     listeners: {},
 
-    onEvent: Task.async(function* (event, data, callback) {
-      try {
-        let response = yield this.listeners[event](data.data);
+    onEvent: function(event, data, callback) {
+      let self = this;
+      Task.spawn(function* () {
+        return yield self.listeners[event](data.data);
+      }).then(response => {
         callback.onSuccess(response);
-
-      } catch (e) {
+      }, error => {
         Cu.reportError("Error in Messaging handler for " + event + ": " + e);
-
         callback.onError({
           message: e.message || (e && e.toString()),
           stack: e.stack || Components.stack.formattedStack,
         });
-      }
-    }),
+      });
+    },
   },
 };
 
 var EventDispatcher = {
   instance: new DispatcherDelegate(IS_PARENT_PROCESS ? Services.androidBridge : undefined),
 
   for: function(aWindow) {
     let view = aWindow && aWindow.arguments && aWindow.arguments[0] &&
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4170,19 +4170,16 @@ pref("font.name-list.cursive.zh-TW", "Ka
 
 pref("font.name-list.serif.zh-HK", "Times, LiSong Pro, Heiti TC");
 pref("font.name-list.sans-serif.zh-HK", "Helvetica, PingFang TC, Heiti TC, LiHei Pro");
 pref("font.name-list.monospace.zh-HK", "Courier, PingFang TC, Heiti TC, LiHei Pro");
 pref("font.name-list.cursive.zh-HK", "Kaiti TC");
 
 // XP_MACOSX changes to default font sizes
 pref("font.minimum-size.th", 10);
-pref("font.size.variable.zh-CN", 15);
-pref("font.size.variable.zh-HK", 15);
-pref("font.size.variable.zh-TW", 15);
 
 // Apple's Symbol is Unicode so use it
 pref("font.name-list.serif.x-math", "Latin Modern Math, STIX Two Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Symbol, Times");
 pref("font.name-list.sans-serif.x-math", "Helvetica");
 pref("font.name-list.monospace.x-math", "Courier");
 pref("font.name-list.cursive.x-math", "Apple Chancery");
 pref("font.name-list.fantasy.x-math", "Papyrus");
 
--- a/moz.configure
+++ b/moz.configure
@@ -366,44 +366,40 @@ def tup_progs(build_backends):
 tup = check_prog('TUP', tup_progs)
 
 # watchman detection
 # ==============================================================
 
 option(env='WATCHMAN', nargs=1, help='Path to the watchman program')
 
 @depends('WATCHMAN')
-def watchman_info(prog):
+@checking('for watchman', callback=lambda w: w.path if w else 'not found')
+def watchman(prog):
     if not prog:
         prog = find_program('watchman')
 
     if not prog:
         return
 
     # `watchman version` will talk to the Watchman daemon service.
     # This can hang due to permissions problems. e.g.
     # https://github.com/facebook/watchman/issues/376. So use
     # `watchman --version` to prevent a class of failures.
     out = check_cmd_output(prog, '--version', onerror=lambda: None)
     if out is None:
         return
 
     return namespace(path=prog, version=Version(out.strip()))
 
-@depends_if(watchman_info)
-@checking('for watchman')
-def watchman(w):
-    return w.path
-
-@depends_if(watchman_info)
+@depends_if(watchman)
 @checking('for watchman version')
 def watchman_version(w):
     return w.version
 
-set_config('WATCHMAN', watchman)
+set_config('WATCHMAN', watchman.path)
 
 @depends_all(hg_version, hg_config, watchman)
 @checking('for watchman Mercurial integration')
 @imports('os')
 def watchman_hg(hg_version, hg_config, watchman):
     if hg_version < Version('3.8'):
         return 'no (Mercurial 3.8+ required)'
 
--- a/security/manager/ssl/PKCS11ModuleDB.cpp
+++ b/security/manager/ssl/PKCS11ModuleDB.cpp
@@ -147,58 +147,16 @@ PKCS11ModuleDB::AddModule(const nsAStrin
   if (scalarKey.Length() > 0) {
     Telemetry::ScalarSet(Telemetry::ScalarID::SECURITY_PKCS11_MODULES_LOADED,
                          scalarKey, true);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PKCS11ModuleDB::GetInternal(nsIPKCS11Module** _retval)
-{
-  NS_ENSURE_ARG_POINTER(_retval);
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  UniqueSECMODModule nssMod(
-    SECMOD_CreateModule(nullptr, SECMOD_INT_NAME, nullptr, SECMOD_INT_FLAGS));
-  if (!nssMod) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(nssMod.get());
-  module.forget(_retval);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PKCS11ModuleDB::GetInternalFIPS(nsIPKCS11Module** _retval)
-{
-  NS_ENSURE_ARG_POINTER(_retval);
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  UniqueSECMODModule nssMod(
-    SECMOD_CreateModule(nullptr, SECMOD_FIPS_NAME, nullptr, SECMOD_FIPS_FLAGS));
-  if (!nssMod) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(nssMod.get());
-  module.forget(_retval);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 PKCS11ModuleDB::FindModuleByName(const nsACString& name,
                          /*out*/ nsIPKCS11Module** _retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
 
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -214,50 +172,16 @@ PKCS11ModuleDB::FindModuleByName(const n
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(mod.get());
   module.forget(_retval);
   return NS_OK;
 }
 
-/* This is essentially the same as nsIPK11Token::findTokenByName, except
- * that it returns an nsIPKCS11Slot, which may be desired.
- */
-NS_IMETHODIMP
-PKCS11ModuleDB::FindSlotByName(const nsACString& name,
-                       /*out*/ nsIPKCS11Slot** _retval)
-{
-  NS_ENSURE_ARG_POINTER(_retval);
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  nsresult rv = BlockUntilLoadableRootsLoaded();
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (name.IsEmpty()) {
-    return NS_ERROR_ILLEGAL_VALUE;
-  }
-
-  UniquePK11SlotInfo slotInfo(
-    PK11_FindSlotByName(PromiseFlatCString(name).get()));
-  if (!slotInfo) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(slotInfo.get());
-  slot.forget(_retval);
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 PKCS11ModuleDB::ListModules(nsISimpleEnumerator** _retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
 
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
--- a/security/manager/ssl/nsIPK11Token.idl
+++ b/security/manager/ssl/nsIPK11Token.idl
@@ -4,20 +4,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/. */
 
 #include "nsISupports.idl"
 
 [scriptable, uuid(51191434-1dd2-11b2-a17c-e49c4e99a4e3)]
 interface nsIPK11Token : nsISupports
 {
-  const long ASK_EVERY_TIME  = -1;
-  const long ASK_FIRST_TIME  =  0;
-  const long ASK_EXPIRE_TIME =  1;
-
   /*
    * The name of the token
    */
   [must_use]
   readonly attribute AUTF8String tokenName;
   [must_use]
   readonly attribute AUTF8String tokenLabel;
   /**
@@ -44,59 +40,42 @@ interface nsIPK11Token : nsISupports
   [must_use]
   boolean isLoggedIn();
   [must_use]
   void login(in boolean force);
   [must_use]
   void logoutSimple();
   [must_use]
   void logoutAndDropAuthenticatedResources();
+  [must_use]
+  boolean needsLogin();
+  [must_use]
+  readonly attribute boolean needsUserInit;
 
   /*
    * Reset password
    */
   [must_use]
   void reset();
 
-  /*
-   * Password information
-   */
-  [must_use]
-  readonly attribute long minimumPasswordLength;
-  [must_use]
-  readonly attribute boolean needsUserInit;
   /**
    * Checks whether the given password is correct. Logs the token out if an
    * incorrect password is given.
    *
    * @param password The password to check.
    * @return true if the password was correct, false otherwise.
    */
   [must_use]
   boolean checkPassword(in AUTF8String password);
   [must_use]
   void initPassword(in AUTF8String initialPassword);
   [must_use]
   void changePassword(in AUTF8String oldPassword, in AUTF8String newPassword);
-  [must_use]
-  long getAskPasswordTimes();
-  [must_use]
-  long getAskPasswordTimeout();
-  [must_use]
-  void setAskPasswordDefaults([const] in long askTimes, [const] in long timeout);
 
   /*
    * True if a password has been configured for this token, and false otherwise.
    * (Whether or not the user is currently logged in makes no difference.)
    * In particular, this can be used to determine if a user has set a master
    * password (if this is the internal key token).
    */
   [must_use]
   readonly attribute boolean hasPassword;
-
-  /*
-   * Other attributes
-   */
-  [must_use]
-  boolean isHardwareToken();
-  [must_use]
-  boolean needsLogin();
 };
--- a/security/manager/ssl/nsIPK11TokenDB.idl
+++ b/security/manager/ssl/nsIPK11TokenDB.idl
@@ -2,17 +2,16 @@
  *
  * 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/. */
 
 #include "nsISupports.idl"
 
 interface nsIPK11Token;
-interface nsISimpleEnumerator;
 
 /**
  * The PK11 Token Database provides access to the PK11 modules
  * that are installed, and the tokens that are available.
  * Interfaces: nsIPK11TokenDB
  * Threading: ??
  */
 %{C++
@@ -34,15 +33,9 @@ interface nsIPK11TokenDB : nsISupports
    * Find a token by name. Throws NS_ERROR_FAILURE if no token with the given
    * name can be found.
    * @param tokenName a string identifying the name of the token. Must be
    *                  non-empty.
    * @return a token with the given name
    */
   [must_use]
   nsIPK11Token findTokenByName(in AUTF8String tokenName);
-
-  /*
-   * List all tokens
-   */
-  [must_use]
-  nsISimpleEnumerator listTokens();
 };
--- a/security/manager/ssl/nsIPKCS11ModuleDB.idl
+++ b/security/manager/ssl/nsIPKCS11ModuleDB.idl
@@ -22,28 +22,19 @@ interface nsIPKCS11ModuleDB : nsISupport
 
   [must_use]
   void addModule(in AString moduleName,
                  in AString libraryFullPath,
                  in long cryptoMechanismFlags,
                  in long cipherFlags);
 
   [must_use]
-  nsIPKCS11Module getInternal();
-
-  [must_use]
-  nsIPKCS11Module getInternalFIPS();
-
-  [must_use]
   nsIPKCS11Module findModuleByName(in AUTF8String name);
 
   [must_use]
-  nsIPKCS11Slot findSlotByName(in AUTF8String name);
-
-  [must_use]
   nsISimpleEnumerator listModules();
 
   [must_use]
   readonly attribute boolean canToggleFIPS;
 
   [must_use]
   void toggleFIPSMode();
 
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1763,29 +1763,30 @@ GetNSSProfilePath(nsAutoCString& aProfil
 
 #ifndef ANDROID
 // Given a profile path, attempt to rename the PKCS#11 module DB to
 // "<original name>.fips". In the case of a catastrophic failure (e.g. out of
 // memory), returns a failing nsresult. If execution could conceivably proceed,
 // returns NS_OK even if renaming the file didn't work. This simplifies the
 // logic of the calling code.
 static nsresult
-AttemptToRenamePKCS11ModuleDB(const nsACString& profilePath)
+AttemptToRenamePKCS11ModuleDB(const nsACString& profilePath,
+                              const nsACString& moduleDBFilename)
 {
   // profilePath may come from the environment variable
   // MOZPSM_NSSDBDIR_OVERRIDE. If so, the user's NSS DBs are most likely not in
   // their profile directory and we shouldn't mess with them.
   const char* dbDirOverride = getenv("MOZPSM_NSSDBDIR_OVERRIDE");
   if (dbDirOverride && strlen(dbDirOverride) > 0) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("MOZPSM_NSSDBDIR_OVERRIDE set - not renaming PKCS#11 module DB"));
     return NS_OK;
   }
-  NS_NAMED_LITERAL_CSTRING(moduleDBFilename, "secmod.db");
-  NS_NAMED_LITERAL_CSTRING(destModuleDBFilename, "secmod.db.fips");
+  nsAutoCString destModuleDBFilename(moduleDBFilename);
+  destModuleDBFilename.Append(".fips");
   nsCOMPtr<nsIFile> dbFile = do_CreateInstance("@mozilla.org/file/local;1");
   if (!dbFile) {
     return NS_ERROR_FAILURE;
   }
   nsresult rv = dbFile->InitWithNativePath(profilePath);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -1797,17 +1798,17 @@ AttemptToRenamePKCS11ModuleDB(const nsAC
   bool exists;
   rv = dbFile->Exists(&exists);
   if (NS_FAILED(rv)) {
     return rv;
   }
   // This is strange, but not a catastrophic failure.
   if (!exists) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-            ("%s doesn't exist?", moduleDBFilename.get()));
+            ("%s doesn't exist?", PromiseFlatCString(moduleDBFilename).get()));
     return NS_OK;
   }
   nsCOMPtr<nsIFile> destDBFile = do_CreateInstance("@mozilla.org/file/local;1");
   if (!destDBFile) {
     return NS_ERROR_FAILURE;
   }
   rv = destDBFile->InitWithNativePath(profilePath);
   if (NS_FAILED(rv)) {
@@ -1840,16 +1841,32 @@ AttemptToRenamePKCS11ModuleDB(const nsAC
     return rv;
   }
   // This may fail on, e.g., a read-only file system. This would be unfortunate,
   // but again it isn't catastropic and we would want to fall back to
   // initializing NSS in no-DB mode.
   Unused << dbFile->MoveToNative(profileDir, destModuleDBFilename);
   return NS_OK;
 }
+
+// We may be using the legacy databases, in which case we need to use
+// "secmod.db". We may be using the sqlite-backed databases, in which case we
+// need to use "pkcs11.txt".
+static nsresult
+AttemptToRenameBothPKCS11ModuleDBVersions(const nsACString& profilePath)
+{
+  NS_NAMED_LITERAL_CSTRING(legacyModuleDBFilename, "secmod.db");
+  NS_NAMED_LITERAL_CSTRING(sqlModuleDBFilename, "pkcs11.txt");
+  nsresult rv = AttemptToRenamePKCS11ModuleDB(profilePath,
+                                              legacyModuleDBFilename);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  return AttemptToRenamePKCS11ModuleDB(profilePath, sqlModuleDBFilename);
+}
 #endif // ifndef ANDROID
 
 // Given a profile directory, attempt to initialize NSS. If nocertdb is true,
 // (or if we don't have a profile directory) simply initialize NSS in no DB mode
 // and return. Otherwise, first attempt to initialize in read/write mode, and
 // then read-only mode if that fails. If both attempts fail, we may be failing
 // to initialize an NSS DB collection that has FIPS mode enabled. Attempt to
 // ascertain if this is the case, and if so, rename the offending PKCS#11 module
@@ -1887,24 +1904,30 @@ InitializeNSSWithFallbacks(const nsACStr
   // That failed. Try read-only mode.
   srv = ::mozilla::psm::InitializeNSS(profilePathCStr.get(), true, !safeMode);
   if (srv == SECSuccess) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized NSS in r-o mode"));
     return NS_OK;
   }
 #ifndef ANDROID
   savedPRErrorCode2 = PR_GetError();
+
+  MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+          ("failed to initialize NSS with codes %d %d", savedPRErrorCode1,
+           savedPRErrorCode2));
 #endif // ifndef ANDROID
 
 #ifndef ANDROID
   // That failed as well. Maybe we're trying to load a PKCS#11 module DB that is
   // in FIPS mode, but we don't support FIPS? Test load NSS without PKCS#11
   // modules. If that succeeds, that's probably what's going on.
   if (!safeMode && (savedPRErrorCode1 == SEC_ERROR_LEGACY_DATABASE ||
-                    savedPRErrorCode2 == SEC_ERROR_LEGACY_DATABASE)) {
+                    savedPRErrorCode2 == SEC_ERROR_LEGACY_DATABASE ||
+                    savedPRErrorCode1 == SEC_ERROR_PKCS11_DEVICE_ERROR ||
+                    savedPRErrorCode2 == SEC_ERROR_PKCS11_DEVICE_ERROR)) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("attempting no-module db init"));
     // It would make sense to initialize NSS in read-only mode here since this
     // is just a test to see if the PKCS#11 module DB being in FIPS mode is the
     // problem, but for some reason the combination of read-only and no-moddb
     // flags causes NSS initialization to fail, so unfortunately we have to use
     // read-write mode.
     srv = ::mozilla::psm::InitializeNSS(profilePathCStr.get(), false, false);
     if (srv == SECSuccess) {
@@ -1913,17 +1936,17 @@ InitializeNSSWithFallbacks(const nsACStr
       srv = NSS_Shutdown();
       if (srv != SECSuccess) {
         return NS_ERROR_FAILURE;
       }
       MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trying to rename module db"));
       // If this fails non-catastrophically, we'll attempt to initialize NSS
       // again in r/w then r-o mode (both of which will fail), and then we'll
       // fall back to NSS_NoDB_Init, which is the behavior we want.
-      nsresult rv = AttemptToRenamePKCS11ModuleDB(profilePath);
+      nsresult rv = AttemptToRenameBothPKCS11ModuleDBVersions(profilePath);
       if (NS_FAILED(rv)) {
         return rv;
       }
       srv = ::mozilla::psm::InitializeNSS(profilePathCStr.get(), false, true);
       if (srv == SECSuccess) {
         MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized in r/w mode"));
         return NS_OK;
       }
--- a/security/manager/ssl/nsPK11TokenDB.cpp
+++ b/security/manager/ssl/nsPK11TokenDB.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "nsPK11TokenDB.h"
 
 #include <string.h>
 
 #include "ScopedNSSTypes.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Unused.h"
-#include "nsIMutableArray.h"
 #include "nsISupports.h"
 #include "nsNSSComponent.h"
 #include "nsPromiseFlatString.h"
 #include "nsReadableUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "prerror.h"
 #include "secerr.h"
 
@@ -234,30 +233,16 @@ nsPK11Token::Reset()
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   return MapSECStatus(PK11_ResetToken(mSlot.get(), nullptr));
 }
 
 NS_IMETHODIMP
-nsPK11Token::GetMinimumPasswordLength(int32_t* aMinimumPasswordLength)
-{
-  NS_ENSURE_ARG_POINTER(aMinimumPasswordLength);
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
-    return NS_ERROR_NOT_AVAILABLE;
-
-  *aMinimumPasswordLength = PK11_GetMinimumPwdLength(mSlot.get());
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsPK11Token::GetNeedsUserInit(bool* aNeedsUserInit)
 {
   NS_ENSURE_ARG_POINTER(aNeedsUserInit);
 
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
@@ -309,56 +294,16 @@ nsPK11Token::InitPassword(const nsACStri
   }
   if (!PK11_NeedUserInit(mSlot.get()) && !hasPassword) {
     return MapSECStatus(PK11_ChangePW(mSlot.get(), "", passwordCStr.get()));
   }
   return MapSECStatus(PK11_InitPin(mSlot.get(), "", passwordCStr.get()));
 }
 
 NS_IMETHODIMP
-nsPK11Token::GetAskPasswordTimes(int32_t* askTimes)
-{
-  NS_ENSURE_ARG_POINTER(askTimes);
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
-    return NS_ERROR_NOT_AVAILABLE;
-
-  int askTimeout;
-  PK11_GetSlotPWValues(mSlot.get(), askTimes, &askTimeout);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPK11Token::GetAskPasswordTimeout(int32_t* askTimeout)
-{
-  NS_ENSURE_ARG_POINTER(askTimeout);
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
-    return NS_ERROR_NOT_AVAILABLE;
-
-  int askTimes;
-  PK11_GetSlotPWValues(mSlot.get(), &askTimes, askTimeout);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPK11Token::SetAskPasswordDefaults(const int32_t askTimes,
-                                    const int32_t askTimeout)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
-    return NS_ERROR_NOT_AVAILABLE;
-
-  PK11_SetSlotPWValues(mSlot.get(), askTimes, askTimeout);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsPK11Token::ChangePassword(const nsACString& oldPassword,
                             const nsACString& newPassword)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   // PK11_ChangePW() has different semantics for the empty string and for
@@ -384,30 +329,16 @@ nsPK11Token::GetHasPassword(bool* hasPas
   // PK11_NeedLogin returns true if the token is currently configured to require
   // the user to log in (whether or not the user is actually logged in makes no
   // difference).
   *hasPassword = PK11_NeedLogin(mSlot.get()) && !PK11_NeedUserInit(mSlot.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPK11Token::IsHardwareToken(bool* _retval)
-{
-  NS_ENSURE_ARG_POINTER(_retval);
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
-    return NS_ERROR_NOT_AVAILABLE;
-
-  *_retval = PK11_IsHW(mSlot.get());
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsPK11Token::NeedsLogin(bool* _retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
 
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
@@ -481,48 +412,8 @@ nsPK11TokenDB::FindTokenByName(const nsA
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIPK11Token> token = new nsPK11Token(slot.get());
   token.forget(_retval);
 
   return NS_OK;
 }
-
-NS_IMETHODIMP
-nsPK11TokenDB::ListTokens(nsISimpleEnumerator** _retval)
-{
-  NS_ENSURE_ARG_POINTER(_retval);
-
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  nsresult rv = BlockUntilLoadableRootsLoaded();
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
-  if (!array) {
-    return NS_ERROR_FAILURE;
-  }
-
-  *_retval = nullptr;
-
-  UniquePK11SlotList list(
-    PK11_GetAllTokens(CKM_INVALID_MECHANISM, false, false, 0));
-  if (!list) {
-    return NS_ERROR_FAILURE;
-  }
-
-  for (PK11SlotListElement* le = PK11_GetFirstSafe(list.get()); le;
-       le = PK11_GetNextSafe(list.get(), le, false)) {
-    nsCOMPtr<nsIPK11Token> token = new nsPK11Token(le->slot);
-    nsresult rv = array->AppendElement(token, false);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  }
-
-  return array->Enumerate(_retval);
-}
--- a/security/manager/ssl/tests/unit/test_pkcs11_module.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_module.js
@@ -111,44 +111,18 @@ function run_test() {
 
   // Check that finding the test slot by name is possible, and that trying to
   // find a non-present slot fails.
   notEqual(testModule.findSlotByName("Test PKCS11 Slot"), null,
            "Test slot should be findable by name");
   throws(() => testModule.findSlotByName("Not Present"), /NS_ERROR_FAILURE/,
          "Non-present slot should not be findable by name");
 
-  // Check that the strangely named nsIPKCS11ModuleDB.findSlotByName() works.
-  // In particular, a comment in nsPKCS11Slot.cpp notes that the method
-  // "is essentially the same as nsIPK11Token::findTokenByName, except that it
-  //  returns an nsIPKCS11Slot".
-  let strBundleSvc = Cc["@mozilla.org/intl/stringbundle;1"]
-                       .getService(Ci.nsIStringBundleService);
-  let bundle =
-    strBundleSvc.createBundle("chrome://pipnss/locale/pipnss.properties");
-  let internalTokenName = bundle.GetStringFromName("PrivateTokenDescription");
-  let internalTokenAsSlot = gModuleDB.findSlotByName(internalTokenName);
-  notEqual(internalTokenAsSlot, null,
-           "Internal 'slot' should be findable by name via the module DB");
-  ok(internalTokenAsSlot instanceof Ci.nsIPKCS11Slot,
-     "Module DB findSlotByName() should return a token as an nsIPKCS11Slot");
-  equal(internalTokenAsSlot.name,
-        bundle.GetStringFromName("PrivateSlotDescription"),
-        "Spot check: actual and expected internal 'slot' names should be equal");
-  throws(() => gModuleDB.findSlotByName("Not Present"), /NS_ERROR_FAILURE/,
-         "Non-present 'slot' should not be findable by name via the module DB");
-  throws(() => gModuleDB.findSlotByName(""), /NS_ERROR_ILLEGAL_VALUE/,
-         "nsIPKCS11ModuleDB.findSlotByName should throw given an empty name");
-
   // Check that deleting the test module makes it disappear from the module list.
   let pkcs11ModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]
                          .getService(Ci.nsIPKCS11ModuleDB);
   pkcs11ModuleDB.deleteModule("PKCS11 Test Module");
   checkTestModuleNotPresent();
 
   // Check miscellaneous module DB methods and attributes.
-  notEqual(gModuleDB.getInternal(), null,
-           "The internal module should be present");
-  notEqual(gModuleDB.getInternalFIPS(), null,
-           "The internal FIPS module should be present");
   ok(gModuleDB.canToggleFIPS, "It should be possible to toggle FIPS");
   ok(!gModuleDB.isFIPSEnabled, "FIPS should not be enabled");
 }
--- a/security/manager/ssl/tests/unit/test_pkcs11_token.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_token.js
@@ -49,25 +49,16 @@ function checkBasicAttributes(token) {
  *        The password that the token should have been init with.
  */
 function checkPasswordFeaturesAndResetPassword(token, initialPW) {
   ok(!token.needsUserInit,
      "Token should not need user init after setting a password");
   ok(token.hasPassword,
      "Token should have a password after setting a password");
 
-  equal(token.minimumPasswordLength, 0,
-        "Actual and expected min password length should match");
-
-  token.setAskPasswordDefaults(10, 20);
-  equal(token.getAskPasswordTimes(), 10,
-        "Actual and expected ask password times should match");
-  equal(token.getAskPasswordTimeout(), 20,
-        "Actual and expected ask password timeout should match");
-
   ok(token.checkPassword(initialPW),
      "checkPassword() should succeed if the correct initial password is given");
   token.changePassword(initialPW, "newPW ÿ 一二三");
   ok(token.checkPassword("newPW ÿ 一二三"),
      "checkPassword() should succeed if the correct new password is given");
 
   ok(!token.checkPassword("wrongPW"),
      "checkPassword() should fail if an incorrect password is given");
@@ -109,13 +100,11 @@ function run_test() {
   // We reset the password previously, so we need to initialize again.
   token.initPassword("arbitrary");
   ok(token.isLoggedIn(),
      "Token should be logged into after initializing password again");
   token.logoutSimple();
   ok(!token.isLoggedIn(),
      "Token should be logged out after calling logoutSimple()");
 
-  ok(!token.isHardwareToken(),
-     "The internal token should not be considered a hardware token");
   ok(token.needsLogin(),
      "The internal token should always need authentication");
 }
--- a/security/manager/ssl/tests/unit/test_pkcs11_tokenDB.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_tokenDB.js
@@ -6,43 +6,28 @@
 
 // Ensure that the appropriate initialization has happened.
 do_get_profile();
 
 function run_test() {
   let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
                   .getService(Ci.nsIPK11TokenDB);
 
-  let tokenListPreLoad = tokenDB.listTokens();
-  while (tokenListPreLoad.hasMoreElements()) {
-    let token = tokenListPreLoad.getNext().QueryInterface(Ci.nsIPK11Token);
-    notEqual(token.tokenLabel, "Test PKCS11 Tokeñ Label",
-             "Test PKCS11 Token 1 should not be listed prior to module load");
-    notEqual(token.tokenLabel, "Test PKCS11 Tokeñ 2 Label",
-             "Test PKCS11 Token 2 should not be listed prior to module load");
-  }
+  notEqual(tokenDB.getInternalKeyToken(), null,
+           "The internal token should be non-null");
 
   throws(() => tokenDB.findTokenByName("Test PKCS11 Tokeñ Label"),
          /NS_ERROR_FAILURE/,
          "Non-present test token 1 should not be findable by name");
   throws(() => tokenDB.findTokenByName("Test PKCS11 Tokeñ 2 Label"),
          /NS_ERROR_FAILURE/,
          "Non-present test token 2 should not be findable by name");
 
   loadPKCS11TestModule(false);
 
   // Test Token 1 is simulated to insert and remove itself in a tight loop, so
   // we don't bother testing that it's present.
-  let tokenListPostLoad = tokenDB.listTokens();
-  let foundTokenNames = [];
-  while (tokenListPostLoad.hasMoreElements()) {
-    let token = tokenListPostLoad.getNext().QueryInterface(Ci.nsIPK11Token);
-    foundTokenNames.push(token.tokenName);
-  }
-  ok(foundTokenNames.includes("Test PKCS11 Tokeñ 2 Label"),
-     "Test PKCS11 Token 2 should be listed after module load");
-
   notEqual(tokenDB.findTokenByName("Test PKCS11 Tokeñ 2 Label"), null,
            "Test token 2 should be findable by name after loading test module");
 
   throws(() => tokenDB.findTokenByName(""), /NS_ERROR_ILLEGAL_VALUE/,
          "nsIPK11TokenDB.findTokenByName should throw given an empty name");
 }
--- a/security/nss.symbols
+++ b/security/nss.symbols
@@ -355,28 +355,26 @@ PK11_GetDisabledReason
 PK11_GetFirstSafe
 PK11_GetInternalKeySlot
 PK11_GetInternalSlot
 PK11_GetIVLength
 PK11_GetKeyData
 PK11_GetKeyGen
 PK11_GetLowLevelKeyIDForPrivateKey
 PK11_GetMechanism
-PK11_GetMinimumPwdLength
 PK11_GetModInfo
 PK11_GetModuleURI
 PK11_GetNextSafe
 PK11_GetNextSymKey
 PK11_GetPadMechanism
 PK11_GetPrivateKeyNickname
 PK11_GetPrivateModulusLen
 PK11_GetSlotID
 PK11_GetSlotInfo
 PK11_GetSlotName
-PK11_GetSlotPWValues
 PK11_GetSlotSeries
 PK11_GetSymKeyNickname
 PK11_GetTokenInfo
 PK11_GetTokenName
 PK11_GetTokenURI
 PK11_HasAttributeSet
 PK11_HashBuf
 PK11_HasRootCerts
@@ -425,17 +423,16 @@ PK11_PubUnwrapSymKey
 PK11_PubWrapSymKey
 PK11_RandomUpdate
 PK11_ReadRawAttribute
 PK11_ReferenceSlot
 PK11_ResetToken
 PK11SDR_Decrypt
 PK11SDR_Encrypt
 PK11_SetPasswordFunc
-PK11_SetSlotPWValues
 PK11_SetSymKeyNickname
 PK11_Sign
 PK11_SignatureLen
 PK11_SignWithMechanism
 PK11_TokenKeyGenWithFlags
 PK11_UnwrapPrivKey
 PK11_UnwrapSymKey
 PK11_UpdateSlotAttribute
--- a/services/sync/modules/constants.js
+++ b/services/sync/modules/constants.js
@@ -145,16 +145,17 @@ INFO_COLLECTION_COUNTS:                "
 INFO_QUOTA:                            "quota",
 
 // Ways that a sync can be disabled (messages only to be printed in debug log)
 kSyncMasterPasswordLocked:             "User elected to leave Master Password locked",
 kSyncWeaveDisabled:                    "Weave is disabled",
 kSyncNetworkOffline:                   "Network is offline",
 kSyncBackoffNotMet:                    "Trying to sync before the server said it's okay",
 kFirstSyncChoiceNotMade:               "User has not selected an action for first sync",
+kSyncNotConfigured:                    "Sync is not configured",
 
 DEVICE_TYPE_DESKTOP:                   "desktop",
 DEVICE_TYPE_MOBILE:                    "mobile",
 
 SQLITE_MAX_VARIABLE_NUMBER:            999,
 
 })) {
   this[key] = val;
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -1044,17 +1044,20 @@ Sync11Service.prototype = {
    *
    * @param ignore [optional]
    *        array of reasons to ignore when checking
    *
    * @return Reason for not syncing; not-truthy if sync should run
    */
   _checkSync: function _checkSync(ignore) {
     let reason = "";
-    if (!this.enabled)
+    // Ideally we'd call _checkSetup() here but that has too many side-effects.
+    if (Status.service == CLIENT_NOT_CONFIGURED)
+      reason = kSyncNotConfigured;
+    else if (Status.service == STATUS_DISABLED || !this.enabled)
       reason = kSyncWeaveDisabled;
     else if (Services.io.offline)
       reason = kSyncNetworkOffline;
     else if (this.status.minimumNextSync > Date.now())
       reason = kSyncBackoffNotMet;
     else if ((this.status.login == MASTER_PASSWORD_LOCKED) &&
              Utils.mpLocked())
       reason = kSyncMasterPasswordLocked;
--- a/services/sync/tests/unit/test_service_login.js
+++ b/services/sync/tests/unit/test_service_login.js
@@ -52,16 +52,28 @@ function setup() {
     "/1.1/johndoe/storage/meta/global": johnU("meta", new ServerWBO("global").handler()),
     "/1.1/janedoe/storage/crypto/keys": janeU("crypto", new ServerWBO("keys").handler()),
     "/1.1/janedoe/storage/meta/global": janeU("meta", new ServerWBO("global").handler())
   });
 
   return server;
 }
 
+add_task(async function test_not_logged_in() {
+  let server = setup();
+  try {
+    await Service.login();
+    do_check_false(Service.isLoggedIn, "no user configured, so can't be logged in");
+    do_check_eq(Service._checkSync(), kSyncNotConfigured);
+  } finally {
+    Svc.Prefs.resetBranch("");
+    await promiseStopServer(server);
+  }
+});
+
 add_task(async function test_login_logout() {
   enableValidationPrefs();
 
   let server = setup();
 
   try {
     _("Force the initial state.");
     Service.status.service = STATUS_OK;
--- a/services/sync/tps/extensions/mozmill/resource/driver/controller.js
+++ b/services/sync/tps/extensions/mozmill/resource/driver/controller.js
@@ -23,17 +23,17 @@ var windows = {}; Cu.import('resource://
 var assert = new assertions.Assert();
 var waitFor = assert.waitFor;
 
 var sleep = utils.sleep;
 
 // For Mozmill 1.5 backward compatibility
 var windowMap = windows.map;
 
-waitForEvents = function () {
+var waitForEvents = function () {
 }
 
 waitForEvents.prototype = {
   /**
    * Initialize list of events for given node
    */
   init: function waitForEvents_init(node, events) {
     if (node.getNode != undefined)
--- a/servo/components/gfx/display_list/mod.rs
+++ b/servo/components/gfx/display_list/mod.rs
@@ -29,17 +29,18 @@ use std::collections::HashMap;
 use std::fmt;
 use std::sync::Arc;
 use style::computed_values::{border_style, image_rendering};
 use style::values::computed::Filter;
 use style_traits::cursor::Cursor;
 use text::TextRun;
 use text::glyph::ByteIndex;
 use webrender_api::{self, ClipAndScrollInfo, ClipId, ColorF, GradientStop, LocalClip};
-use webrender_api::{MixBlendMode, ScrollPolicy, ScrollSensitivity, TransformStyle};
+use webrender_api::{MixBlendMode, ScrollPolicy, ScrollSensitivity, StickyFrameInfo};
+use webrender_api::TransformStyle;
 
 pub use style::dom::OpaqueNode;
 
 /// The factor that we multiply the blur radius by in order to inflate the boundaries of display
 /// items that involve a blur. This ensures that the display item boundaries include all the ink.
 pub static BLUR_INFLATION_FACTOR: i32 = 3;
 
 #[derive(Deserialize, HeapSizeOf, Serialize)]
@@ -554,16 +555,17 @@ impl fmt::Debug for StackingContext {
                self.overflow,
                self.id)
     }
 }
 
 #[derive(Clone, Debug, Deserialize, HeapSizeOf, Serialize)]
 pub enum ScrollRootType {
     ScrollFrame(ScrollSensitivity),
+    StickyFrame(StickyFrameInfo),
     Clip,
 }
 
 /// Defines a stacking context.
 #[derive(Clone, Debug, Deserialize, HeapSizeOf, Serialize)]
 pub struct ScrollRoot {
     /// The WebRender clip id of this scroll root based on the source of this clip
     /// and information about the fragment.
--- a/servo/components/layout/block.rs
+++ b/servo/components/layout/block.rs
@@ -24,19 +24,19 @@
 //! available here:
 //!
 //!   http://dev.w3.org/csswg/css-sizing/
 
 #![deny(unsafe_code)]
 
 use app_units::{Au, MAX_AU};
 use context::LayoutContext;
-use display_list_builder::{BorderPaintingMode, DisplayListBuildState};
-use display_list_builder::BlockFlowDisplayListBuilding;
-use euclid::{Point2D, Size2D, Rect};
+use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
+use display_list_builder::{DisplayListBuildState, EstablishContainingBlock};
+use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
 use floats::{ClearType, FloatKind, Floats, PlacementInfo};
 use flow::{self, BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag};
 use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT};
 use flow::{CONTAINS_TEXT_OR_REPLACED_FRAGMENTS, INLINE_POSITION_IS_STATIC};
 use flow::{IS_ABSOLUTELY_POSITIONED, FragmentationContext, MARGINS_CANNOT_COLLAPSE};
 use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, OpaqueFlow};
 use flow_list::FlowList;
 use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow};
@@ -49,17 +49,17 @@ use sequential;
 use serde::{Serialize, Serializer};
 use servo_geometry::max_rect;
 use std::cmp::{max, min};
 use std::fmt;
 use std::sync::Arc;
 use style::computed_values::{box_sizing, display, float, overflow_x};
 use style::computed_values::{position, text_align};
 use style::context::SharedStyleContext;
-use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
+use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect, LogicalSize, WritingMode};
 use style::properties::ComputedValues;
 use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
 use style::values::computed::{LengthOrPercentageOrNone, LengthOrPercentage};
 use style::values::computed::LengthOrPercentageOrAuto;
 use traversal::PreorderFlowTraversal;
 
 /// Information specific to floated blocks.
 #[derive(Clone, Serialize)]
@@ -638,17 +638,17 @@ impl BlockFlow {
         }
     }
 
     /// Return this flow's fragment.
     pub fn fragment(&mut self) -> &mut Fragment {
         &mut self.fragment
     }
 
-    pub fn stacking_relative_position(&self, coor: CoordinateSystem) -> Rect<Au> {
+    pub fn stacking_relative_border_box(&self, coor: CoordinateSystem) -> Rect<Au> {
         return self.fragment.stacking_relative_border_box(
             &self.base.stacking_relative_position,
             &self.base.early_absolute_position_info.relative_containing_block_size,
             self.base.early_absolute_position_info.relative_containing_block_mode,
             coor);
     }
 
     /// Return the size of the containing block for the given immediate absolute descendant of this
@@ -1785,16 +1785,30 @@ impl BlockFlow {
             self.flags.remove(HAS_SCROLLING_OVERFLOW);
         }
     }
 
     pub fn has_scrolling_overflow(&mut self) -> bool {
         self.flags.contains(HAS_SCROLLING_OVERFLOW)
     }
 
+    // Return offset from original position because of `position: sticky`.
+    pub fn sticky_position(&self) -> SideOffsets2D<MaybeAuto> {
+        let containing_block_size = &self.base.early_absolute_position_info
+                                              .relative_containing_block_size;
+        let writing_mode = self.base.early_absolute_position_info.relative_containing_block_mode;
+        let offsets = self.fragment.style().logical_position();
+        let as_margins = LogicalMargin::new(writing_mode,
+            MaybeAuto::from_style(offsets.block_start, containing_block_size.inline),
+            MaybeAuto::from_style(offsets.inline_end, containing_block_size.inline),
+            MaybeAuto::from_style(offsets.block_end, containing_block_size.inline),
+            MaybeAuto::from_style(offsets.inline_start, containing_block_size.inline));
+        as_margins.to_physical(writing_mode)
+    }
+
 }
 
 impl Flow for BlockFlow {
     fn class(&self) -> FlowClass {
         FlowClass::Block
     }
 
     fn as_mut_block(&mut self) -> &mut BlockFlow {
@@ -2132,17 +2146,17 @@ impl Flow for BlockFlow {
                     LengthOrPercentageOrAuto::Auto &&
                 self.fragment.style().logical_position().block_end ==
                 LengthOrPercentageOrAuto::Auto {
             self.base.position.start.b = block_position
         }
     }
 
     fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
-        self.collect_stacking_contexts_for_block(state);
+        self.collect_stacking_contexts_for_block(state, EstablishContainingBlock::Yes);
     }
 
     fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
         self.build_display_list_for_block(state, BorderPaintingMode::Separate);
     }
 
     fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
         self.fragment.repair_style(new_style)
--- a/servo/components/layout/display_list_builder.rs
+++ b/servo/components/layout/display_list_builder.rs
@@ -9,17 +9,18 @@
 //! paint.
 
 #![deny(unsafe_code)]
 
 use app_units::{AU_PER_PX, Au};
 use block::{BlockFlow, BlockStackingContextType};
 use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg};
 use context::LayoutContext;
-use euclid::{Transform3D, Point2D, Vector2D, Rect, SideOffsets2D, Size2D, TypedSize2D};
+use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Transform3D, TypedSize2D};
+use euclid::Vector2D;
 use flex::FlexFlow;
 use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED};
 use flow_ref::FlowRef;
 use fragment::{CanvasFragmentSource, CoordinateSystem, Fragment, ImageFragmentInfo, ScannedTextFragmentInfo};
 use fragment::{SpecificFragmentInfo, TruncatedFragmentInfo};
 use gfx::display_list;
 use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDetails, BorderDisplayItem};
 use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion};
@@ -67,17 +68,18 @@ use style::values::generics::image::{Gra
 use style::values::generics::image::{Image, ShapeExtent};
 use style::values::generics::image::PaintWorklet;
 use style::values::specified::position::{X, Y};
 use style_traits::CSSPixel;
 use style_traits::ToCss;
 use style_traits::cursor::Cursor;
 use table_cell::CollapsedBordersForCell;
 use webrender_api::{ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion, GradientStop, LineStyle};
-use webrender_api::{LocalClip, RepeatMode, ScrollPolicy, ScrollSensitivity};
+use webrender_api::{LocalClip, RepeatMode, ScrollPolicy, ScrollSensitivity, StickyFrameInfo};
+use webrender_api::StickySideConstraint;
 use webrender_helpers::{ToBorderRadius, ToMixBlendMode, ToRectF, ToTransformStyle};
 
 trait ResolvePercentage {
     fn resolve(&self, length: u32) -> u32;
 }
 
 impl ResolvePercentage for NumberOrPercentage {
     fn resolve(&self, length: u32) -> u32 {
@@ -96,21 +98,21 @@ fn convert_repeat_mode(from: RepeatKeywo
     match from {
         RepeatKeyword::Stretch => RepeatMode::Stretch,
         RepeatKeyword::Repeat => RepeatMode::Repeat,
         RepeatKeyword::Round => RepeatMode::Round,
         RepeatKeyword::Space => RepeatMode::Space,
     }
 }
 
-fn establishes_containing_block_for_absolute(positioning: position::T) -> bool {
-    match positioning {
-        position::T::absolute | position::T::relative | position::T::fixed => true,
-        _ => false,
-    }
+fn establishes_containing_block_for_absolute(can_establish_containing_block: EstablishContainingBlock,
+                                             positioning: position::T)
+                                             -> bool {
+    can_establish_containing_block == EstablishContainingBlock::Yes &&
+    position::T::static_ != positioning
 }
 
 trait RgbColor {
     fn rgb(r: u8, g: u8, b: u8) -> Self;
 }
 
 impl RgbColor for ColorF {
     fn rgb(r: u8, g: u8, b: u8) -> Self {
@@ -187,16 +189,19 @@ pub struct DisplayListBuildState<'a> {
 
     /// A stack of clips used to cull display list entries that are outside the
     /// rendered region.
     pub clip_stack: Vec<Rect<Au>>,
 
     /// A stack of clips used to cull display list entries that are outside the
     /// rendered region, but only collected at containing block boundaries.
     pub containing_block_clip_stack: Vec<Rect<Au>>,
+
+    /// The flow parent's content box, used to calculate sticky constraints.
+    parent_stacking_relative_content_box: Rect<Au>,
 }
 
 impl<'a> DisplayListBuildState<'a> {
     pub fn new(layout_context: &'a LayoutContext) -> DisplayListBuildState<'a> {
         let root_clip_info = ClipAndScrollInfo::simple(layout_context.id.root_scroll_node());
         DisplayListBuildState {
             layout_context: layout_context,
             root_stacking_context: StackingContext::root(layout_context.id),
@@ -206,16 +211,17 @@ impl<'a> DisplayListBuildState<'a> {
             processing_scroll_root_element: false,
             current_stacking_context_id: StackingContextId::root(),
             current_real_stacking_context_id: StackingContextId::root(),
             current_clip_and_scroll_info: root_clip_info,
             containing_block_clip_and_scroll_info: root_clip_info,
             iframe_sizes: Vec::new(),
             clip_stack: Vec::new(),
             containing_block_clip_stack: Vec::new(),
+            parent_stacking_relative_content_box: Rect::zero(),
         }
     }
 
     fn add_display_item(&mut self, display_item: DisplayItem) {
         let items = self.items.entry(display_item.stacking_context_id()).or_insert(Vec::new());
         items.push(display_item);
     }
 
@@ -2249,27 +2255,39 @@ impl FragmentDisplayListBuilding for Fra
             PseudoElementType::Before(_) => FragmentType::BeforePseudoContent,
             PseudoElementType::After(_) => FragmentType::AfterPseudoContent,
             PseudoElementType::DetailsSummary(_) => FragmentType::FragmentBody,
             PseudoElementType::DetailsContent(_) => FragmentType::FragmentBody,
         }
     }
 }
 
+#[derive(Clone, Copy, PartialEq)]
+pub enum EstablishContainingBlock {
+    Yes,
+    No,
+}
+
 pub trait BlockFlowDisplayListBuilding {
-    fn collect_stacking_contexts_for_block(&mut self, state: &mut DisplayListBuildState);
+    fn collect_stacking_contexts_for_block(&mut self,
+                                           state: &mut DisplayListBuildState,
+                                           can_establish_containing_block: EstablishContainingBlock);
 
     fn transform_clip_to_coordinate_space(&mut self,
                                           state: &mut DisplayListBuildState,
                                           preserved_state: &mut PreservedDisplayListState);
     fn setup_clipping_for_block(&mut self,
                                 state: &mut DisplayListBuildState,
                                 preserved_state: &mut PreservedDisplayListState,
-                                stacking_context_type: BlockStackingContextType)
+                                stacking_context_type: BlockStackingContextType,
+                                can_establish_containing_block: EstablishContainingBlock)
                                 -> ClipAndScrollInfo;
+    fn setup_scroll_root_for_position(&mut self,
+                                      state: &mut DisplayListBuildState,
+                                      border_box: &Rect<Au>);
     fn setup_scroll_root_for_overflow(&mut self,
                                       state: &mut DisplayListBuildState,
                                       border_box: &Rect<Au>);
     fn setup_scroll_root_for_css_clip(&mut self,
                                       state: &mut DisplayListBuildState,
                                       preserved_state: &mut PreservedDisplayListState,
                                       stacking_relative_border_box: &Rect<Au>);
     fn create_pseudo_stacking_context_for_block(&mut self,
@@ -2292,41 +2310,44 @@ pub trait BlockFlowDisplayListBuilding {
 /// TODO(mrobinson): It would be nice to use RAII here to avoid having to call restore.
 pub struct PreservedDisplayListState {
     stacking_context_id: StackingContextId,
     real_stacking_context_id: StackingContextId,
     clip_and_scroll_info: ClipAndScrollInfo,
     containing_block_clip_and_scroll_info: ClipAndScrollInfo,
     clips_pushed: usize,
     containing_block_clips_pushed: usize,
+    stacking_relative_content_box: Rect<Au>,
 }
 
 impl PreservedDisplayListState {
     fn new(state: &mut DisplayListBuildState) -> PreservedDisplayListState {
         PreservedDisplayListState {
             stacking_context_id: state.current_stacking_context_id,
             real_stacking_context_id: state.current_real_stacking_context_id,
             clip_and_scroll_info: state.current_clip_and_scroll_info,
             containing_block_clip_and_scroll_info: state.containing_block_clip_and_scroll_info,
             clips_pushed: 0,
             containing_block_clips_pushed: 0,
+            stacking_relative_content_box: state.parent_stacking_relative_content_box,
         }
     }
 
     fn switch_to_containing_block_clip(&mut self, state: &mut DisplayListBuildState) {
         let clip = state.containing_block_clip_stack.last().cloned().unwrap_or_else(max_rect);
         state.clip_stack.push(clip);
         self.clips_pushed += 1;
     }
 
     fn restore(self, state: &mut DisplayListBuildState) {
         state.current_stacking_context_id = self.stacking_context_id;
         state.current_real_stacking_context_id = self.real_stacking_context_id;
         state.current_clip_and_scroll_info = self.clip_and_scroll_info;
         state.containing_block_clip_and_scroll_info = self.containing_block_clip_and_scroll_info;
+        state.parent_stacking_relative_content_box = self.stacking_relative_content_box;
 
         let truncate_length = state.clip_stack.len() - self.clips_pushed;
         state.clip_stack.truncate(truncate_length);
 
         let truncate_length = state.containing_block_clip_stack.len() -
                               self.containing_block_clips_pushed;
         state.containing_block_clip_stack.truncate(truncate_length);
     }
@@ -2354,17 +2375,17 @@ impl PreservedDisplayListState {
 
 impl BlockFlowDisplayListBuilding for BlockFlow {
     fn transform_clip_to_coordinate_space(&mut self,
                                           state: &mut DisplayListBuildState,
                                           preserved_state: &mut PreservedDisplayListState) {
         if state.clip_stack.is_empty() {
             return;
         }
-        let border_box = self.stacking_relative_position(CoordinateSystem::Parent);
+        let border_box = self.stacking_relative_border_box(CoordinateSystem::Parent);
         let transform = match self.fragment.transform_matrix(&border_box) {
             Some(transform) => transform,
             None => return,
         };
 
         let perspective = self.fragment.perspective_matrix(&border_box)
                                        .unwrap_or_else(Transform3D::identity);
         let transform = transform.pre_mul(&perspective).inverse();
@@ -2407,17 +2428,19 @@ impl BlockFlowDisplayListBuilding for Bl
         }
 
         if let Some(clip) = state.containing_block_clip_stack.last().cloned() {
             state.containing_block_clip_stack.push(transform_clip(&clip));
             preserved_state.containing_block_clips_pushed += 1;
         }
     }
 
-    fn collect_stacking_contexts_for_block(&mut self, state: &mut DisplayListBuildState) {
+    fn collect_stacking_contexts_for_block(&mut self,
+                                           state: &mut DisplayListBuildState,
+                                           can_establish_containing_block: EstablishContainingBlock) {
         let mut preserved_state = PreservedDisplayListState::new(state);
 
         let block_stacking_context_type = self.block_stacking_context_type();
         self.base.stacking_context_id = match block_stacking_context_type {
             BlockStackingContextType::NonstackingContext => state.current_stacking_context_id,
             BlockStackingContextType::PseudoStackingContext |
             BlockStackingContextType::StackingContext => self.fragment.stacking_context_id(),
         };
@@ -2427,18 +2450,23 @@ impl BlockFlowDisplayListBuilding for Bl
             state.current_real_stacking_context_id = self.base.stacking_context_id;
         }
 
         // We are getting the id of the scroll root that contains us here, not the id of
         // any scroll root that we create. If we create a scroll root, its id will be
         // stored in state.current_clip_and_scroll_info. If we create a stacking context,
         // we don't want it to be contained by its own scroll root.
         let containing_clip_and_scroll_info =
-            self.setup_clipping_for_block(state, &mut preserved_state, block_stacking_context_type);
-        if establishes_containing_block_for_absolute(self.positioning()) {
+            self.setup_clipping_for_block(state,
+                                          &mut preserved_state,
+                                          block_stacking_context_type,
+                                          can_establish_containing_block);
+
+        if establishes_containing_block_for_absolute(can_establish_containing_block,
+                                                     self.positioning()) {
             state.containing_block_clip_and_scroll_info = state.current_clip_and_scroll_info;
         }
 
         match block_stacking_context_type {
             BlockStackingContextType::NonstackingContext => {
                 self.base.collect_stacking_contexts_for_children(state);
             }
             BlockStackingContextType::PseudoStackingContext => {
@@ -2454,17 +2482,18 @@ impl BlockFlowDisplayListBuilding for Bl
         }
 
         preserved_state.restore(state);
     }
 
     fn setup_clipping_for_block(&mut self,
                                 state: &mut DisplayListBuildState,
                                 preserved_state: &mut PreservedDisplayListState,
-                                stacking_context_type: BlockStackingContextType)
+                                stacking_context_type: BlockStackingContextType,
+                                can_establish_containing_block: EstablishContainingBlock)
                                 -> ClipAndScrollInfo {
         // If this block is absolutely positioned, we should be clipped and positioned by
         // the scroll root of our nearest ancestor that establishes a containing block.
         let containing_clip_and_scroll_info = match self.positioning() {
             position::T::absolute => {
                 preserved_state.switch_to_containing_block_clip(state);
                 state.current_clip_and_scroll_info = state.containing_block_clip_and_scroll_info;
                 state.containing_block_clip_and_scroll_info
@@ -2472,45 +2501,118 @@ impl BlockFlowDisplayListBuilding for Bl
             position::T::fixed => {
                 preserved_state.push_clip(state, &max_rect(), position::T::fixed);
                 state.current_clip_and_scroll_info
             }
             _ => state.current_clip_and_scroll_info,
         };
         self.base.clip_and_scroll_info = Some(containing_clip_and_scroll_info);
 
-        let coordinate_system = if self.fragment.establishes_stacking_context() {
-            CoordinateSystem::Own
+        let stacking_relative_border_box = if self.fragment.establishes_stacking_context() {
+            self.stacking_relative_border_box(CoordinateSystem::Own)
         } else {
-            CoordinateSystem::Parent
+            self.stacking_relative_border_box(CoordinateSystem::Parent)
         };
 
-        let stacking_relative_border_box = self.fragment.stacking_relative_border_box(
-            &self.base.stacking_relative_position,
-            &self.base.early_absolute_position_info.relative_containing_block_size,
-            self.base.early_absolute_position_info.relative_containing_block_mode,
-            coordinate_system);
-
         if stacking_context_type == BlockStackingContextType::StackingContext {
             self.transform_clip_to_coordinate_space(state, preserved_state);
         }
 
+        self.setup_scroll_root_for_position(state, &stacking_relative_border_box);
         self.setup_scroll_root_for_overflow(state, &stacking_relative_border_box);
         self.setup_scroll_root_for_css_clip(state, preserved_state, &stacking_relative_border_box);
         self.base.clip = state.clip_stack.last().cloned().unwrap_or_else(max_rect);
 
+        // We keep track of our position so that any stickily positioned elements can
+        // properly determine the extent of their movement relative to scrolling containers.
+        if can_establish_containing_block == EstablishContainingBlock::Yes {
+            let border_box = if self.fragment.establishes_stacking_context() {
+                stacking_relative_border_box
+            } else {
+                self.stacking_relative_border_box(CoordinateSystem::Own)
+            };
+            state.parent_stacking_relative_content_box =
+               self.fragment.stacking_relative_content_box(&border_box)
+        }
+
         match self.positioning() {
             position::T::absolute | position::T::relative | position::T::fixed =>
                 state.containing_block_clip_and_scroll_info = state.current_clip_and_scroll_info,
             _ => {}
         }
 
         containing_clip_and_scroll_info
     }
 
+    fn setup_scroll_root_for_position(&mut self,
+                                      state: &mut DisplayListBuildState,
+                                      border_box: &Rect<Au>) {
+        if self.positioning() != position::T::sticky {
+            return;
+        }
+
+        let sticky_position = self.sticky_position();
+        if sticky_position.left == MaybeAuto::Auto && sticky_position.right == MaybeAuto::Auto &&
+           sticky_position.top == MaybeAuto::Auto && sticky_position.bottom == MaybeAuto::Auto {
+            return;
+        }
+
+        // Since position: sticky elements always establish a stacking context, we will
+        // have previously calculated our border box in our own coordinate system. In
+        // order to properly calculate max offsets we need to compare our size and
+        // position in our parent's coordinate system.
+        let border_box_in_parent = self.stacking_relative_border_box(CoordinateSystem::Parent);
+        let margins = self.fragment.margin.to_physical(
+            self.base.early_absolute_position_info.relative_containing_block_mode);
+
+        // Position:sticky elements are always restricted based on the size and position of
+        // their containing block, which for sticky items is like relative and statically
+        // positioned items: just the parent block.
+        let constraint_rect = state.parent_stacking_relative_content_box;
+
+        let to_max_offset = |constraint_edge: Au, moving_edge: Au| -> f32 {
+            (constraint_edge - moving_edge).to_f32_px()
+        };
+
+        let to_sticky_info = |margin: MaybeAuto, max_offset: f32| -> Option<StickySideConstraint> {
+            match margin {
+                MaybeAuto::Auto => None,
+                MaybeAuto::Specified(value) =>
+                    Some(StickySideConstraint { margin: value.to_f32_px(), max_offset }),
+            }
+        };
+
+        let sticky_frame_info = StickyFrameInfo::new(
+             to_sticky_info(sticky_position.top,
+                            to_max_offset(constraint_rect.max_y(), border_box_in_parent.max_y())),
+             to_sticky_info(sticky_position.right,
+                            to_max_offset(constraint_rect.min_x(), border_box_in_parent.min_x() - margins.left)),
+             to_sticky_info(sticky_position.bottom,
+                            to_max_offset(constraint_rect.min_y(), border_box_in_parent.min_y() - margins.top)),
+             to_sticky_info(sticky_position.left,
+                            to_max_offset(constraint_rect.max_x(), border_box_in_parent.max_x())));
+
+        let new_scroll_root_id = ClipId::new(self.fragment.unique_id(IdType::OverflowClip),
+                                             state.layout_context.id.to_webrender());
+        let parent_id = self.clip_and_scroll_info(state.layout_context.id).scroll_node_id;
+        state.add_scroll_root(
+            ScrollRoot {
+                id: new_scroll_root_id,
+                parent_id: parent_id,
+                clip: ClippingRegion::from_rect(border_box),
+                content_rect: Rect::zero(),
+                root_type: ScrollRootType::StickyFrame(sticky_frame_info),
+            },
+        );
+
+        let new_clip_and_scroll_info = ClipAndScrollInfo::simple(new_scroll_root_id);
+        self.base.clip_and_scroll_info = Some(new_clip_and_scroll_info);
+        state.current_clip_and_scroll_info = new_clip_and_scroll_info;
+    }
+
     fn setup_scroll_root_for_overflow(&mut self,
                                       state: &mut DisplayListBuildState,
                                       border_box: &Rect<Au>) {
         if !self.overflow_style_may_require_scroll_root() {
             return;
         }
 
         let content_box = self.fragment.stacking_relative_content_box(&border_box);
@@ -2732,17 +2834,18 @@ pub trait InlineFlowDisplayListBuilding 
 impl InlineFlowDisplayListBuilding for InlineFlow {
     fn collect_stacking_contexts_for_inline(&mut self, state: &mut DisplayListBuildState) {
         self.base.stacking_context_id = state.current_stacking_context_id;
         self.base.clip_and_scroll_info = Some(state.current_clip_and_scroll_info);
         self.base.clip = state.clip_stack.last().cloned().unwrap_or_else(max_rect);
 
         for fragment in self.fragments.fragments.iter_mut() {
             let previous_cb_clip_scroll_info = state.containing_block_clip_and_scroll_info;
-            if establishes_containing_block_for_absolute(fragment.style.get_box().position) {
+            if establishes_containing_block_for_absolute(EstablishContainingBlock::Yes,
+                                                         fragment.style.get_box().position) {
                 state.containing_block_clip_and_scroll_info = state.current_clip_and_scroll_info;
             }
 
             match fragment.specific {
                 SpecificFragmentInfo::InlineBlock(ref mut block_flow) => {
                     let block_flow = FlowRef::deref_mut(&mut block_flow.flow_ref);
                     block_flow.collect_stacking_contexts(state);
                 }
--- a/servo/components/layout/fragment.rs
+++ b/servo/components/layout/fragment.rs
@@ -2513,18 +2513,19 @@ impl Fragment {
             return true;
         }
 
         if self.style().get_box().transform_style == transform_style::T::preserve_3d ||
            self.style().overrides_transform_style() {
             return true
         }
 
-        // Fixed position blocks always create stacking contexts.
-        if self.style.get_box().position == position::T::fixed {
+        // Fixed position and sticky position always create stacking contexts.
+        if self.style().get_box().position == position::T::fixed ||
+           self.style().get_box().position == position::T::sticky  {
             return true
         }
 
         // Statically positioned fragments don't establish stacking contexts if the previous
         // conditions are not fulfilled. Furthermore, z-index doesn't apply to statically
         // positioned fragments.
         if self.style().get_box().position == position::T::static_ {
             return false;
--- a/servo/components/layout/query.rs
+++ b/servo/components/layout/query.rs
@@ -593,16 +593,17 @@ impl FragmentBorderBoxIterator for Paren
                                          &fragment.specific) {
                 // Spec says it's valid if any of these are true:
                 //  1) Is the body element
                 //  2) Is static position *and* is a table or table cell
                 //  3) Is not static position
                 (true, _, _) |
                 (false, computed_values::position::T::static_, &SpecificFragmentInfo::Table) |
                 (false, computed_values::position::T::static_, &SpecificFragmentInfo::TableCell) |
+                (false, computed_values::position::T::sticky, _) |
                 (false, computed_values::position::T::absolute, _) |
                 (false, computed_values::position::T::relative, _) |
                 (false, computed_values::position::T::fixed, _) => true,
 
                 // Otherwise, it's not a valid parent
                 (false, computed_values::position::T::static_, _) => false,
             };
 
@@ -761,17 +762,17 @@ where
 
         PropertyId::Custom(ref name) => {
             return style.computed_value_to_string(PropertyDeclarationId::Custom(name))
         }
     };
 
     let positioned = match style.get_box().position {
         position::computed_value::T::relative |
-        /*position::computed_value::T::sticky |*/
+        position::computed_value::T::sticky |
         position::computed_value::T::fixed |
         position::computed_value::T::absolute => true,
         _ => false
     };
 
     //TODO: determine whether requested property applies to the element.
     //      eg. width does not apply to non-replaced inline elements.
     // Existing browsers disagree about when left/top/right/bottom apply
--- a/servo/components/layout/sequential.rs
+++ b/servo/components/layout/sequential.rs
@@ -88,17 +88,17 @@ pub fn iterate_through_flow_tree_fragmen
 
         for kid in flow::mut_base(flow).child_iter_mut() {
             let mut stacking_context_position = *stacking_context_position;
             if kid.is_block_flow() && kid.as_block().fragment.establishes_stacking_context() {
                 stacking_context_position = Point2D::new(kid.as_block().fragment.margin.inline_start, Au(0)) +
                                             flow::base(kid).stacking_relative_position +
                                             stacking_context_position.to_vector();
                 let relative_position = kid.as_block()
-                    .stacking_relative_position(CoordinateSystem::Own);
+                    .stacking_relative_border_box(CoordinateSystem::Own);
                 if let Some(matrix) = kid.as_block()
                        .fragment
                        .transform_matrix(&relative_position) {
                     let transform_matrix = matrix.transform_point2d(&Point2D::zero());
                     stacking_context_position = stacking_context_position +
                                                 Vector2D::new(Au::from_f32_px(transform_matrix.x),
                                                               Au::from_f32_px(transform_matrix.y))
                 }
--- a/servo/components/layout/table.rs
+++ b/servo/components/layout/table.rs
@@ -5,17 +5,18 @@
 //! CSS table formatting contexts.
 
 #![deny(unsafe_code)]
 
 use app_units::Au;
 use block::{BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer};
 use block::{ISizeConstraintInput, ISizeConstraintSolution};
 use context::LayoutContext;
-use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
+use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
+use display_list_builder::{DisplayListBuildState, EstablishContainingBlock};
 use euclid::Point2D;
 use flow;
 use flow::{BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
 use flow_list::MutFlowListIterator;
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto};
@@ -498,17 +499,17 @@ impl Flow for TableFlow {
             border_collapse::T::separate => BorderPaintingMode::Separate,
             border_collapse::T::collapse => BorderPaintingMode::Hidden,
         };
 
         self.block_flow.build_display_list_for_block(state, border_painting_mode);
     }
 
     fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
-        self.block_flow.collect_stacking_contexts(state);
+        self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::Yes);
     }
 
     fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
         self.block_flow.repair_style(new_style)
     }
 
     fn compute_overflow(&self) -> Overflow {
         self.block_flow.compute_overflow()
--- a/servo/components/layout/table_caption.rs
+++ b/servo/components/layout/table_caption.rs
@@ -4,17 +4,18 @@
 
 //! CSS table formatting contexts.
 
 #![deny(unsafe_code)]
 
 use app_units::Au;
 use block::BlockFlow;
 use context::LayoutContext;
-use display_list_builder::DisplayListBuildState;
+use display_list_builder::{BlockFlowDisplayListBuilding, DisplayListBuildState};
+use display_list_builder::EstablishContainingBlock;
 use euclid::Point2D;
 use flow::{Flow, FlowClass, OpaqueFlow};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx_traits::print_tree::PrintTree;
 use std::fmt;
 use style::logical_geometry::LogicalSize;
 use style::properties::ComputedValues;
 
@@ -75,17 +76,17 @@ impl Flow for TableCaptionFlow {
     }
 
     fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
         debug!("build_display_list_table_caption: same process as block flow");
         self.block_flow.build_display_list(state);
     }
 
     fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
-        self.block_flow.collect_stacking_contexts(state);
+        self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
     }
 
     fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
         self.block_flow.repair_style(new_style)
     }
 
     fn compute_overflow(&self) -> Overflow {
         self.block_flow.compute_overflow()
--- a/servo/components/layout/table_cell.rs
+++ b/servo/components/layout/table_cell.rs
@@ -4,17 +4,18 @@
 
 //! CSS table formatting contexts.
 
 #![deny(unsafe_code)]
 
 use app_units::Au;
 use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
 use context::LayoutContext;
-use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
+use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
+use display_list_builder::{DisplayListBuildState, EstablishContainingBlock};
 use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
 use flow::{self, Flow, FlowClass, IS_ABSOLUTELY_POSITIONED, OpaqueFlow};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use model::MaybeAuto;
 use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode;
 use std::fmt;
@@ -256,17 +257,17 @@ impl Flow for TableCellFlow {
             border_collapse::T::separate => BorderPaintingMode::Separate,
             border_collapse::T::collapse => BorderPaintingMode::Collapse(&self.collapsed_borders),
         };
 
         self.block_flow.build_display_list_for_block(state, border_painting_mode)
     }
 
     fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
-        self.block_flow.collect_stacking_contexts(state);
+        self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
     }
 
     fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
         self.block_flow.repair_style(new_style)
     }
 
     fn compute_overflow(&self) -> Overflow {
         self.block_flow.compute_overflow()
--- a/servo/components/layout/table_row.rs
+++ b/servo/components/layout/table_row.rs
@@ -4,17 +4,18 @@
 
 //! CSS table formatting contexts.
 
 #![deny(unsafe_code)]
 
 use app_units::Au;
 use block::{BlockFlow, ISizeAndMarginsComputer};
 use context::LayoutContext;
-use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
+use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
+use display_list_builder::{DisplayListBuildState, EstablishContainingBlock};
 use euclid::Point2D;
 use flow::{self, EarlyAbsolutePositionInfo, Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
 use flow_list::MutFlowListIterator;
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use model::MaybeAuto;
 use serde::{Serialize, Serializer};
@@ -473,17 +474,17 @@ impl Flow for TableRowFlow {
             border_collapse::T::separate => BorderPaintingMode::Separate,
             border_collapse::T::collapse => BorderPaintingMode::Hidden,
         };
 
         self.block_flow.build_display_list_for_block(state, border_painting_mode);
     }
 
     fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
-        self.block_flow.collect_stacking_contexts(state);
+        self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
     }
 
     fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
         self.block_flow.repair_style(new_style)
     }
 
     fn compute_overflow(&self) -> Overflow {
         self.block_flow.compute_overflow()
--- a/servo/components/layout/table_rowgroup.rs
+++ b/servo/components/layout/table_rowgroup.rs
@@ -4,17 +4,18 @@
 
 //! CSS table formatting contexts.
 
 #![deny(unsafe_code)]
 
 use app_units::Au;
 use block::{BlockFlow, ISizeAndMarginsComputer};
 use context::LayoutContext;
-use display_list_builder::DisplayListBuildState;
+use display_list_builder::{BlockFlowDisplayListBuilding, DisplayListBuildState};
+use display_list_builder::EstablishContainingBlock;
 use euclid::Point2D;
 use flow::{Flow, FlowClass, OpaqueFlow};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use serde::{Serialize, Serializer};
 use std::fmt;
 use std::iter::{IntoIterator, Iterator, Peekable};
@@ -178,17 +179,17 @@ impl Flow for TableRowGroupFlow {
     }
 
     fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
         debug!("build_display_list_table_rowgroup: same process as block flow");
         self.block_flow.build_display_list(state);
     }
 
     fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
-        self.block_flow.collect_stacking_contexts(state);
+        self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
     }
 
     fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
         self.block_flow.repair_style(new_style)
     }
 
     fn compute_overflow(&self) -> Overflow {
         self.block_flow.compute_overflow()
--- a/servo/components/layout/table_wrapper.rs
+++ b/servo/components/layout/table_wrapper.rs
@@ -12,17 +12,18 @@
 //! Hereafter this document is referred to as INTRINSIC.
 
 #![deny(unsafe_code)]
 
 use app_units::Au;
 use block::{AbsoluteNonReplaced, BlockFlow, FloatNonReplaced, ISizeAndMarginsComputer, ISizeConstraintInput};
 use block::{ISizeConstraintSolution, MarginsMayCollapseFlag};
 use context::LayoutContext;
-use display_list_builder::DisplayListBuildState;
+use display_list_builder::{BlockFlowDisplayListBuilding, DisplayListBuildState};
+use display_list_builder::EstablishContainingBlock;
 use euclid::Point2D;
 use floats::FloatKind;
 use flow::{Flow, FlowClass, ImmutableFlowUtils, INLINE_POSITION_IS_STATIC, OpaqueFlow};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx_traits::print_tree::PrintTree;
 use model::MaybeAuto;
 use std::cmp::{max, min};
 use std::fmt;
@@ -452,17 +453,17 @@ impl Flow for TableWrapperFlow {
         self.block_flow.generated_containing_block_size(flow)
     }
 
     fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
         self.block_flow.build_display_list(state);
     }
 
     fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
-        self.block_flow.collect_stacking_contexts(state);
+        self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
     }
 
     fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
         self.block_flow.repair_style(new_style)
     }
 
     fn compute_overflow(&self) -> Overflow {
         self.block_flow.compute_overflow()
--- a/servo/components/layout/webrender_helpers.rs
+++ b/servo/components/layout/webrender_helpers.rs
@@ -487,31 +487,35 @@ impl WebRenderDisplayItemConverter for D
                                               stacking_context.mix_blend_mode,
                                               stacking_context.filters.to_filter_ops());
             }
             DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(),
             DisplayItem::DefineClip(ref item) => {
                 builder.push_clip_id(item.scroll_root.parent_id);
 
                 let our_id = item.scroll_root.id;
+                let item_rect = item.scroll_root.clip.main.to_rectf();
                 let webrender_id = match item.scroll_root.root_type {
                    ScrollRootType::Clip => {
                         builder.define_clip(Some(our_id),
-                                            item.scroll_root.clip.main.to_rectf(),
+                                            item_rect,
                                             item.scroll_root.clip.get_complex_clips(),
                                             None)
                     }
                     ScrollRootType::ScrollFrame(scroll_sensitivity) => {
                         builder.define_scroll_frame(Some(our_id),
                                                     item.scroll_root.content_rect.to_rectf(),
                                                     item.scroll_root.clip.main.to_rectf(),
                                                     item.scroll_root.clip.get_complex_clips(),
                                                     None,
                                                     scroll_sensitivity)
                     }
+                    ScrollRootType::StickyFrame(sticky_frame_info) => {
+                        builder.define_sticky_frame(Some(our_id), item_rect, sticky_frame_info)
+                    }
                 };
                 debug_assert!(our_id == webrender_id);
 
                 builder.pop_clip_id();
             }
         }
     }
 }
--- a/servo/components/profile/time.rs
+++ b/servo/components/profile/time.rs
@@ -149,16 +149,17 @@ impl Formattable for ProfilerCategory {
             ProfilerCategory::ScriptStylesheetLoad => "Script Stylesheet Load",
             ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event",
             ProfilerCategory::ScriptWorkerEvent => "Script Worker Event",
             ProfilerCategory::ScriptServiceWorkerEvent => "Script Service Worker Event",
             ProfilerCategory::ScriptEnterFullscreen => "Script Enter Fullscreen",
             ProfilerCategory::ScriptExitFullscreen => "Script Exit Fullscreen",
             ProfilerCategory::ScriptWebVREvent => "Script WebVR Event",
             ProfilerCategory::ScriptWorkletEvent => "Script Worklet Event",
+            ProfilerCategory::ScriptPerformanceEvent => "Script Performance Event",
             ProfilerCategory::TimeToFirstPaint => "Time To First Paint",
             ProfilerCategory::TimeToFirstContentfulPaint => "Time To First Contentful Paint",
             ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat",
         };
         format!("{}{}", padding, name)
     }
 }
 
--- a/servo/components/profile_traits/time.rs
+++ b/servo/components/profile_traits/time.rs
@@ -85,16 +85,17 @@ pub enum ProfilerCategory {
     ScriptWebSocketEvent = 0x73,
     ScriptWorkerEvent = 0x74,
     ScriptServiceWorkerEvent = 0x75,
     ScriptParseXML = 0x76,
     ScriptEnterFullscreen = 0x77,
     ScriptExitFullscreen = 0x78,
     ScriptWebVREvent = 0x79,
     ScriptWorkletEvent = 0x7a,
+    ScriptPerformanceEvent = 0x7b,
     TimeToFirstPaint = 0x80,
     TimeToFirstContentfulPaint = 0x81,
     ApplicationHeartbeat = 0x90,
 }
 
 #[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
 pub enum TimerMetadataFrameType {
     RootWindow,
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -1581,17 +1581,17 @@ impl Document {
     pub fn run_the_animation_frame_callbacks(&self) {
         rooted_vec!(let mut animation_frame_list);
         mem::swap(
             &mut *animation_frame_list,
             &mut *self.animation_frame_list.borrow_mut());
 
         self.running_animation_callbacks.set(true);
         let was_faking_animation_frames = self.is_faking_animation_frames();
-        let timing = self.window.Performance().Now();
+        let timing = self.global().performance().Now();
 
         for (_, callback) in animation_frame_list.drain(..) {
             if let Some(callback) = callback {
                 callback.call(self, *timing);
             }
         }
 
         self.running_animation_callbacks.set(false);
--- a/servo/components/script/dom/globalscope.rs
+++ b/servo/components/script/dom/globalscope.rs
@@ -1,27 +1,29 @@
 /* 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 devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
 use dom::bindings::conversions::root_from_object;
 use dom::bindings::error::{ErrorInfo, report_pending_exception};
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{MutNullableJS, Root};
 use dom::bindings::reflector::DomObject;
 use dom::bindings::settings_stack::{AutoEntryScript, entry_global, incumbent_global};
 use dom::bindings::str::DOMString;
 use dom::crypto::Crypto;
 use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
 use dom::errorevent::ErrorEvent;
 use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
 use dom::eventtarget::EventTarget;
+use dom::performance::Performance;
 use dom::window::Window;
 use dom::workerglobalscope::WorkerGlobalScope;
 use dom::workletglobalscope::WorkletGlobalScope;
 use dom_struct::dom_struct;
 use ipc_channel::ipc::IpcSender;
 use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
 use js::glue::{IsWrapper, UnwrapObject};
 use js::jsapi::{CurrentGlobalOrNull, GetGlobalForObjectCrossCompartment};
@@ -41,16 +43,17 @@ use script_traits::{MsDuration, ScriptTo
 use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
 use servo_url::{MutableOrigin, ServoUrl};
 use std::cell::Cell;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::ffi::CString;
 use task_source::file_reading::FileReadingTaskSource;
 use task_source::networking::NetworkingTaskSource;
+use task_source::performance_timeline::PerformanceTimelineTaskSource;
 use time::{Timespec, get_time};
 use timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle};
 use timers::{OneshotTimers, TimerCallback};
 
 #[dom_struct]
 pub struct GlobalScope {
     eventtarget: EventTarget,
     crypto: MutNullableJS<Crypto>,
@@ -564,16 +567,39 @@ impl GlobalScope {
     }
 
     /// Returns the ["incumbent"] global object.
     ///
     /// ["incumbent"]: https://html.spec.whatwg.org/multipage/#incumbent
     pub fn incumbent() -> Option<Root<Self>> {
         incumbent_global()
     }
+
+    pub fn performance(&self) -> Root<Performance> {
+        if let Some(window) = self.downcast::<Window>() {
+            return window.Performance();
+        }
+        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
+            return worker.Performance();
+        }
+        unreachable!();
+    }
+
+    /// Channel to send messages to the performance timeline task source
+    /// of this global scope.
+    pub fn performance_timeline_task_source(&self) -> PerformanceTimelineTaskSource {
+        if let Some(window) = self.downcast::<Window>() {
+            return window.performance_timeline_task_source();
+        }
+        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
+            return worker.performance_timeline_task_source();
+        }
+        unreachable!();
+    }
+
 }
 
 fn timestamp_in_ms(time: Timespec) -> u64 {
     (time.sec * 1000 + (time.nsec / 1000000) as i64) as u64
 }
 
 /// Returns the Rust global scope from a JS global object.
 #[allow(unsafe_code)]
--- a/servo/components/script/dom/performance.rs
+++ b/servo/components/script/dom/performance.rs
@@ -1,27 +1,27 @@
 /* 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 dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::PerformanceBinding;
 use dom::bindings::codegen::Bindings::PerformanceBinding::{DOMHighResTimeStamp, PerformanceMethods};
 use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceEntryList as DOMPerformanceEntryList;
+use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{JS, Root};
 use dom::bindings::num::Finite;
-use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
+use dom::globalscope::GlobalScope;
 use dom::performanceentry::PerformanceEntry;
 use dom::performanceobserver::PerformanceObserver as DOMPerformanceObserver;
 use dom::performancetiming::PerformanceTiming;
 use dom::window::Window;
 use dom_struct::dom_struct;
-use script_thread::{Runnable, ScriptThread};
 use std::cell::Cell;
 use std::cmp::Ordering;
 use time;
 
 /// Implementation of a list of PerformanceEntry items shared by the
 /// Performance and PerformanceObserverEntryList interfaces implementations.
 #[derive(HeapSizeOf, JSTraceable)]
 pub struct PerformanceEntryList {
@@ -59,44 +59,50 @@ impl IntoIterator for PerformanceEntryLi
 struct PerformanceObserver {
     observer: Root<DOMPerformanceObserver>,
     entry_types: Vec<DOMString>,
 }
 
 #[dom_struct]
 pub struct Performance {
     reflector_: Reflector,
-    timing: JS<PerformanceTiming>,
+    timing: Option<JS<PerformanceTiming>>,
     entries: DOMRefCell<PerformanceEntryList>,
     observers: DOMRefCell<Vec<PerformanceObserver>>,
     pending_notification_observers_task: Cell<bool>,
+    navigation_start_precise: f64,
 }
 
 impl Performance {
-    fn new_inherited(window: &Window,
+    fn new_inherited(global: &GlobalScope,
                      navigation_start: u64,
                      navigation_start_precise: f64) -> Performance {
         Performance {
             reflector_: Reflector::new(),
-            timing: JS::from_ref(&*PerformanceTiming::new(window,
-                                                            navigation_start,
-                                                            navigation_start_precise)),
+            timing: if global.is::<Window>() {
+                Some(JS::from_ref(&*PerformanceTiming::new(global.as_window(),
+                                                           navigation_start,
+                                                           navigation_start_precise)))
+            } else {
+                None
+            },
             entries: DOMRefCell::new(PerformanceEntryList::new(Vec::new())),
             observers: DOMRefCell::new(Vec::new()),
             pending_notification_observers_task: Cell::new(false),
+            navigation_start_precise,
         }
     }
 
-    pub fn new(window: &Window,
+    pub fn new(global: &GlobalScope,
                navigation_start: u64,
                navigation_start_precise: f64) -> Root<Performance> {
-        reflect_dom_object(box Performance::new_inherited(window,
+        reflect_dom_object(box Performance::new_inherited(global,
                                                           navigation_start,
                                                           navigation_start_precise),
-                           window,
+                           global,
                            PerformanceBinding::Wrap)
     }
 
     /// Add a PerformanceObserver to the list of observers with a set of
     /// observed entry types.
     pub fn add_observer(&self,
                         observer: &DOMPerformanceObserver,
                         entry_types: Vec<DOMString>,
@@ -163,27 +169,25 @@ impl Performance {
         // If there is already a queued notification task, we just bail out.
         if self.pending_notification_observers_task.get() {
             return;
         }
 
         // Step 6.
         // Queue a new notification task.
         self.pending_notification_observers_task.set(true);
-        let global = self.global();
-        let window = global.as_window();
-        let task_source = window.performance_timeline_task_source();
-        task_source.queue_notification(self, window);
+        let task_source = self.global().performance_timeline_task_source();
+        task_source.queue_notification(&self.global());
     }
 
     /// Observers notifications task.
     ///
     /// Algorithm spec (step 7):
     /// https://w3c.github.io/performance-timeline/#queue-a-performanceentry
-    fn notify_observers(&self) {
+    pub fn notify_observers(&self) {
         // Step 7.1.
         self.pending_notification_observers_task.set(false);
 
         // Step 7.2.
         // We have to operate over a copy of the performance observers to avoid
         // the risk of an observer's callback modifying the list of registered
         // observers.
         let observers: Vec<Root<DOMPerformanceObserver>> =
@@ -195,44 +199,31 @@ impl Performance {
 
         // Step 7.3.
         for o in observers.iter() {
             o.notify();
         }
     }
 }
 
-pub struct NotifyPerformanceObserverRunnable {
-    owner: Trusted<Performance>,
-}
-
-impl NotifyPerformanceObserverRunnable {
-    pub fn new(owner: Trusted<Performance>) -> Self {
-        NotifyPerformanceObserverRunnable {
-            owner,
-        }
-    }
-}
-
-impl Runnable for NotifyPerformanceObserverRunnable {
-    fn main_thread_handler(self: Box<NotifyPerformanceObserverRunnable>,
-                           _: &ScriptThread) {
-        self.owner.root().notify_observers();
-    }
-}
-
 impl PerformanceMethods for Performance {
     // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute
     fn Timing(&self) -> Root<PerformanceTiming> {
-        Root::from_ref(&*self.timing)
+        match self.timing {
+            Some(ref timing) => Root::from_ref(&*timing),
+            None => unreachable!("Are we trying to expose Performance.timing in workers?"),
+        }
     }
 
     // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now
     fn Now(&self) -> DOMHighResTimeStamp {
-        let nav_start = self.timing.navigation_start_precise();
+        let nav_start = match self.timing {
+            Some(ref timing) => timing.navigation_start_precise(),
+            None => self.navigation_start_precise,
+        };
         let now = (time::precise_time_ns() as f64 - nav_start) / 1000000 as f64;
         Finite::wrap(now)
     }
 
     // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentries
     fn GetEntries(&self) -> Vec<Root<PerformanceEntry>> {
         self.entries.borrow().get_entries_by_name_and_type(None, None)
     }
--- a/servo/components/script/dom/performanceobserver.rs
+++ b/servo/components/script/dom/performanceobserver.rs
@@ -4,17 +4,16 @@
 
 use dom::bindings::callback::ExceptionHandling;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceEntryList as DOMPerformanceEntryList;
 use dom::bindings::codegen::Bindings::PerformanceObserverBinding;
 use dom::bindings::codegen::Bindings::PerformanceObserverBinding::PerformanceObserverCallback;
 use dom::bindings::codegen::Bindings::PerformanceObserverBinding::PerformanceObserverInit;
 use dom::bindings::codegen::Bindings::PerformanceObserverBinding::PerformanceObserverMethods;
-use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
 use dom::bindings::error::{Error, Fallible};
 use dom::bindings::js::Root;
 use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::globalscope::GlobalScope;
 use dom::performance::PerformanceEntryList;
 use dom::performanceentry::PerformanceEntry;
 use dom::performanceobserverentrylist::PerformanceObserverEntryList;
@@ -46,18 +45,18 @@ impl PerformanceObserver {
             reflector_: Reflector::new(),
             callback,
             entries,
         }
     }
 
     #[allow(unrooted_must_root)]
     pub fn new(global: &GlobalScope,
-           callback: Rc<PerformanceObserverCallback>,
-           entries: DOMPerformanceEntryList)
+               callback: Rc<PerformanceObserverCallback>,
+               entries: DOMPerformanceEntryList)
         -> Root<PerformanceObserver> {
         let observer = PerformanceObserver::new_inherited(callback, DOMRefCell::new(entries));
         reflect_dom_object(box observer, global, PerformanceObserverBinding::Wrap)
     }
 
     pub fn Constructor(global: &GlobalScope, callback: Rc<PerformanceObserverCallback>)
         -> Fallible<Root<PerformanceObserver>> {
         Ok(PerformanceObserver::new(global, callback, Vec::new()))
@@ -105,20 +104,20 @@ impl PerformanceObserverMethods for Perf
                                             .map(|e| e.clone())
                                             .collect::<Vec<DOMString>>();
         // step 2
         // There must be at least one valid entry type.
         if entry_types.is_empty() {
             return Err((Error::Type("entryTypes cannot be empty".to_string())));
         }
 
-        let performance = self.global().as_window().Performance();
         // step 3-4-5
-        performance.add_observer(self, entry_types, options.buffered);
+        self.global().performance().add_observer(self, entry_types, options.buffered);
+
         Ok(())
     }
 
     // https://w3c.github.io/performance-timeline/#dom-performanceobserver-disconnect()
     fn Disconnect(&self) {
-        self.global().as_window().Performance().remove_observer(self);
+        self.global().performance().remove_observer(self);
         self.entries.borrow_mut().clear();
     }
 }
--- a/servo/components/script/dom/webgl_extensions/ext/oestexturefloat.rs
+++ b/servo/components/script/dom/webgl_extensions/ext/oestexturefloat.rs
@@ -27,17 +27,18 @@ impl WebGLExtension for OESTextureFloat 
     fn new(ctx: &WebGLRenderingContext) -> Root<OESTextureFloat> {
         reflect_dom_object(box OESTextureFloat::new_inherited(),
                            &*ctx.global(),
                            OESTextureFloatBinding::Wrap)
     }
 
     fn is_supported(ext: &WebGLExtensions) -> bool {
         ext.supports_any_gl_extension(&["GL_OES_texture_float",
-                                        "GL_ARB_texture_float"])
+                                        "GL_ARB_texture_float",
+                                        "GL_EXT_color_buffer_float"])
     }
 
     fn enable(ext: &WebGLExtensions) {
         // Enable FLOAT text data type
         ext.enable_tex_type(webgl::FLOAT);
         let needs_replace = !ext.supports_gl_extension("GL_OES_texture_float");
         if needs_replace {
             // Special internal formats must be used to avoid clamped float values
--- a/servo/components/script/dom/webgl_extensions/ext/oestexturehalffloat.rs
+++ b/servo/components/script/dom/webgl_extensions/ext/oestexturehalffloat.rs
@@ -28,17 +28,18 @@ impl WebGLExtension for OESTextureHalfFl
         reflect_dom_object(box OESTextureHalfFloat::new_inherited(),
                            &*ctx.global(),
                            OESTextureHalfFloatBinding::Wrap)
     }
 
     fn is_supported(ext: &WebGLExtensions) -> bool {
         ext.supports_any_gl_extension(&["GL_OES_texture_half_float",
                                         "GL_ARB_half_float_pixel",
-                                        "GL_NV_half_float"])
+                                        "GL_NV_half_float",
+                                        "GL_EXT_color_buffer_half_float"])
     }
 
     fn enable(ext: &WebGLExtensions) {
         // Enable FLOAT text data type
         let hf = OESTextureHalfFloatConstants::HALF_FLOAT_OES;
         ext.enable_tex_type(hf);
         let needs_replace = !ext.supports_gl_extension("GL_OES_texture_float");
         if needs_replace {
--- a/servo/components/script/dom/webidls/Performance.webidl
+++ b/servo/components/script/dom/webidls/Performance.webidl
@@ -4,25 +4,27 @@
 /*
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-window.performance-attribute
  */
 
 typedef double DOMHighResTimeStamp;
 typedef sequence<PerformanceEntry> PerformanceEntryList;
 
-[Exposed=(Window,Worker)]
+[Exposed=(Window, Worker)]
 interface Performance {
+  DOMHighResTimeStamp now();
+};
+
+[Exposed=(Window)]
+partial interface Performance {
   readonly attribute PerformanceTiming timing;
   /*  readonly attribute PerformanceNavigation navigation; */
 };
 
-partial interface Performance {
-  DOMHighResTimeStamp now();
-};
-
 // https://w3c.github.io/performance-timeline/#extensions-to-the-performance-interface
+[Exposed=(Window, Worker)]
 partial interface Performance {
   PerformanceEntryList getEntries();
   PerformanceEntryList getEntriesByType(DOMString type);
   PerformanceEntryList getEntriesByName(DOMString name,
                                         optional DOMString type);
 };
--- a/servo/components/script/dom/webidls/PerformanceTiming.webidl
+++ b/servo/components/script/dom/webidls/PerformanceTiming.webidl
@@ -1,17 +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/. */
 /*
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-navigation-timing-interface
  */
 
-[Exposed=(Window,Worker)]
+[Exposed=(Window)]
 interface PerformanceTiming {
   readonly attribute unsigned long long navigationStart;
   /*  readonly attribute unsigned long long unloadEventStart;
   readonly attribute unsigned long long unloadEventEnd;
   readonly attribute unsigned long long redirectStart;
   readonly attribute unsigned long long redirectEnd;
   readonly attribute unsigned long long fetchStart;
   readonly attribute unsigned long long domainLookupStart;
--- a/servo/components/script/dom/webidls/Window.webidl
+++ b/servo/components/script/dom/webidls/Window.webidl
@@ -93,21 +93,16 @@ Window implements WindowTimers;
 interface WindowBase64 {
   [Throws]
   DOMString btoa(DOMString btoa);
   [Throws]
   DOMString atob(DOMString atob);
 };
 Window implements WindowBase64;
 
-// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-window.performance-attribute
-partial interface Window {
-  [Replaceable] readonly attribute Performance performance;
-};
-
 // https://html.spec.whatwg.org/multipage/#Window-partial
 partial interface Window {
   void captureEvents();
   void releaseEvents();
 };
 
 // https://drafts.csswg.org/cssom/#extensions-to-the-window-interface
 partial interface Window {
--- a/servo/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl
+++ b/servo/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl
@@ -21,10 +21,16 @@ interface WindowOrWorkerGlobalScope {
   // void clearInterval(optional long handle = 0);
 
   // ImageBitmap
   // Promise<ImageBitmap> createImageBitmap(ImageBitmapSource image, optional ImageBitmapOptions options);
   // Promise<ImageBitmap> createImageBitmap(
   //   ImageBitmapSource image, long sx, long sy, long sw, long sh, optional ImageBitmapOptions options);
 };
 
+// https://w3c.github.io/hr-time/#the-performance-attribute
+partial interface WindowOrWorkerGlobalScope {
+    [Replaceable]
+    readonly attribute Performance performance;
+};
+
 Window implements WindowOrWorkerGlobalScope;
 WorkerGlobalScope implements WindowOrWorkerGlobalScope;
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -705,17 +705,18 @@ impl WindowMethods for Window {
         // Steps 4-5.
         Some(Root::from_ref(window_proxy.top()))
     }
 
     // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/
     // NavigationTiming/Overview.html#sec-window.performance-attribute
     fn Performance(&self) -> Root<Performance> {
         self.performance.or_init(|| {
-            Performance::new(self, self.navigation_start.get(),
+            let global_scope = self.upcast::<GlobalScope>();
+            Performance::new(global_scope, self.navigation_start.get(),
                              self.navigation_start_precise.get())
         })
     }
 
     // https://html.spec.whatwg.org/multipage/#globaleventhandlers
     global_event_handlers!();
 
     // https://html.spec.whatwg.org/multipage/#windoweventhandlers
--- a/servo/components/script/dom/workerglobalscope.rs
+++ b/servo/components/script/dom/workerglobalscope.rs
@@ -12,16 +12,17 @@ use dom::bindings::inheritance::Castable
 use dom::bindings::js::{MutNullableJS, Root};
 use dom::bindings::reflector::DomObject;
 use dom::bindings::settings_stack::AutoEntryScript;
 use dom::bindings::str::DOMString;
 use dom::bindings::trace::RootedTraceableBox;
 use dom::crypto::Crypto;
 use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
 use dom::globalscope::GlobalScope;
+use dom::performance::Performance;
 use dom::promise::Promise;
 use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
 use dom::window::{base64_atob, base64_btoa};
 use dom::workerlocation::WorkerLocation;
 use dom::workernavigator::WorkerNavigator;
 use dom_struct::dom_struct;
 use fetch;
 use ipc_channel::ipc::IpcSender;
@@ -39,16 +40,18 @@ use script_traits::WorkerGlobalScopeInit
 use servo_url::{MutableOrigin, ServoUrl};
 use std::default::Default;
 use std::rc::Rc;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::mpsc::Receiver;
 use task_source::file_reading::FileReadingTaskSource;
 use task_source::networking::NetworkingTaskSource;
+use task_source::performance_timeline::PerformanceTimelineTaskSource;
+use time::precise_time_ns;
 use timers::{IsInterval, TimerCallback};
 
 pub fn prepare_workerscope_init(global: &GlobalScope,
                                 devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>) -> WorkerGlobalScopeInit {
     let init = WorkerGlobalScopeInit {
             resource_threads: global.resource_threads().clone(),
             mem_profiler_chan: global.mem_profiler_chan().clone(),
             to_devtools_sender: global.devtools_chan().cloned(),
@@ -84,16 +87,19 @@ pub struct WorkerGlobalScope {
     from_devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>,
 
     #[ignore_heap_size_of = "Defined in std"]
     /// This `Receiver` will be ignored later if the corresponding
     /// `IpcSender` doesn't exist
     from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
 
     microtask_queue: MicrotaskQueue,
+
+    navigation_start_precise: f64,
+    performance: MutNullableJS<Performance>,
 }
 
 impl WorkerGlobalScope {
     pub fn new_inherited(init: WorkerGlobalScopeInit,
                          worker_url: ServoUrl,
                          runtime: Runtime,
                          from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
                          timer_event_chan: IpcSender<TimerEvent>,
@@ -115,16 +121,18 @@ impl WorkerGlobalScope {
             worker_url: worker_url,
             closing: closing,
             runtime: runtime,
             location: Default::default(),
             navigator: Default::default(),
             from_devtools_sender: init.from_devtools_sender,
             from_devtools_receiver: from_devtools_receiver,
             microtask_queue: MicrotaskQueue::default(),
+            navigation_start_precise: precise_time_ns() as f64,
+            performance: Default::default(),
         }
     }
 
     pub fn from_devtools_sender(&self) -> Option<IpcSender<DevtoolScriptControlMsg>> {
         self.from_devtools_sender.clone()
     }
 
     pub fn from_devtools_receiver(&self) -> &Receiver<DevtoolScriptControlMsg> {
@@ -315,16 +323,26 @@ impl WorkerGlobalScopeMethods for Worker
         self.ClearTimeout(handle);
     }
 
     #[allow(unrooted_must_root)]
     // https://fetch.spec.whatwg.org/#fetch-method
     fn Fetch(&self, input: RequestOrUSVString, init: RootedTraceableBox<RequestInit>) -> Rc<Promise> {
         fetch::Fetch(self.upcast(), input, init)
     }
+
+    // https://w3c.github.io/hr-time/#the-performance-attribute
+    fn Performance(&self) -> Root<Performance> {
+        self.performance.or_init(|| {
+            let global_scope = self.upcast::<GlobalScope>();
+            Performance::new(global_scope,
+                             0 /* navigation start is not used in workers */,
+                             self.navigation_start_precise)
+        })
+    }
 }
 
 
 impl WorkerGlobalScope {
     #[allow(unsafe_code)]
     pub fn execute_script(&self, source: DOMString) {
         let _aes = AutoEntryScript::new(self.upcast());
         rooted!(in(self.runtime.cx()) let mut rval = UndefinedValue());
@@ -363,16 +381,20 @@ impl WorkerGlobalScope {
     pub fn file_reading_task_source(&self) -> FileReadingTaskSource {
         FileReadingTaskSource(self.script_chan())
     }
 
     pub fn networking_task_source(&self) -> NetworkingTaskSource {
         NetworkingTaskSource(self.script_chan())
     }
 
+    pub fn performance_timeline_task_source(&self) -> PerformanceTimelineTaskSource {
+        PerformanceTimelineTaskSource(self.script_chan())
+    }
+
     pub fn new_script_pair(&self) -> (Box<ScriptChan + Send>, Box<ScriptPort + Send>) {
         let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
         if let Some(dedicated) = dedicated {
             return dedicated.new_script_pair();
         } else {
             panic!("need to implement a sender for SharedWorker/ServiceWorker")
         }
     }
--- a/servo/components/script/script_runtime.rs
+++ b/servo/components/script/script_runtime.rs
@@ -83,17 +83,18 @@ pub enum ScriptThreadEventCategory {
     TimerEvent,
     UpdateReplacedElement,
     WebSocketEvent,
     WorkerEvent,
     WorkletEvent,
     ServiceWorkerEvent,
     EnterFullscreen,
     ExitFullscreen,
-    WebVREvent
+    WebVREvent,
+    PerformanceTimelineTask,
 }
 
 /// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM
 /// APIs that need to abstract over multiple kinds of event loops (worker/main thread) with
 /// different Receiver interfaces.
 pub trait ScriptPort {
     fn recv(&self) -> Result<CommonScriptMsg, ()>;
 }
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -113,17 +113,17 @@ use std::sync::atomic::{AtomicBool, Orde
 use std::sync::mpsc::{Receiver, Select, Sender, channel};
 use std::thread;
 use style::context::ReflowGoal;
 use style::thread_state;
 use task_source::dom_manipulation::{DOMManipulationTask, DOMManipulationTaskSource};
 use task_source::file_reading::FileReadingTaskSource;
 use task_source::history_traversal::HistoryTraversalTaskSource;
 use task_source::networking::NetworkingTaskSource;
-use task_source::performance_timeline::{PerformanceTimelineTask, PerformanceTimelineTaskSource};
+use task_source::performance_timeline::PerformanceTimelineTaskSource;
 use task_source::user_interaction::{UserInteractionTask, UserInteractionTaskSource};
 use time::{get_time, precise_time_ns, Tm};
 use url::Position;
 use webdriver_handlers;
 use webvr_traits::{WebVREvent, WebVRMsg};
 
 pub type ImageCacheMsg = (PipelineId, PendingImageResponse);
 
@@ -267,18 +267,16 @@ pub enum MainThreadScriptMsg {
     Navigate(PipelineId, LoadData, bool),
     /// Tasks that originate from the DOM manipulation task source
     DOMManipulation(DOMManipulationTask),
     /// Tasks that originate from the user interaction task source
     UserInteraction(UserInteractionTask),
     /// Notifies the script thread that a new worklet has been loaded, and thus the page should be
     /// reflowed.
     WorkletLoaded(PipelineId),
-    /// Tasks that originate from the performance timeline task source.
-    PerformanceTimeline(PerformanceTimelineTask),
 }
 
 impl OpaqueSender<CommonScriptMsg> for Box<ScriptChan + Send> {
     fn send(&self, msg: CommonScriptMsg) {
         ScriptChan::send(&**self, msg).unwrap();
     }
 }
 
@@ -855,19 +853,19 @@ impl ScriptThread {
             bluetooth_thread: state.bluetooth_thread,
 
             port: port,
 
             chan: MainThreadScriptChan(chan.clone()),
             dom_manipulation_task_source: DOMManipulationTaskSource(chan.clone()),
             user_interaction_task_source: UserInteractionTaskSource(chan.clone()),
             networking_task_source: NetworkingTaskSource(boxed_script_sender.clone()),
-            history_traversal_task_source: HistoryTraversalTaskSource(chan.clone()),
-            file_reading_task_source: FileReadingTaskSource(boxed_script_sender),
-            performance_timeline_task_source: PerformanceTimelineTaskSource(chan),
+            history_traversal_task_source: HistoryTraversalTaskSource(chan),
+            file_reading_task_source: FileReadingTaskSource(boxed_script_sender.clone()),
+            performance_timeline_task_source: PerformanceTimelineTaskSource(boxed_script_sender),
 
             control_chan: state.control_chan,
             control_port: control_port,
             script_sender: state.script_to_constellation_chan.sender.clone(),
             time_profiler_chan: state.time_profiler_chan,
             mem_profiler_chan: state.mem_profiler_chan,
 
             devtools_chan: state.devtools_chan,
@@ -1187,16 +1185,17 @@ impl ScriptThread {
                 ScriptThreadEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent,
                 ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
                 ScriptThreadEventCategory::WebVREvent => ProfilerCategory::ScriptWebVREvent,
                 ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
                 ScriptThreadEventCategory::WorkletEvent => ProfilerCategory::ScriptWorkletEvent,
                 ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent,
                 ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
                 ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen,
+                ScriptThreadEventCategory::PerformanceTimelineTask => ProfilerCategory::ScriptPerformanceEvent,
             };
             profile(profiler_cat, None, self.time_profiler_chan.clone(), f)
         } else {
             f()
         }
     }
 
     fn handle_msg_from_constellation(&self, msg: ConstellationControlMsg) {
@@ -1290,18 +1289,16 @@ impl ScriptThread {
                 }
             }
             MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) =>
                 self.collect_reports(reports_chan),
             MainThreadScriptMsg::WorkletLoaded(pipeline_id) =>
                 self.handle_worklet_loaded(pipeline_id),
             MainThreadScriptMsg::DOMManipulation(task) =>
                 task.handle_task(self),
-            MainThreadScriptMsg::PerformanceTimeline(task) =>
-                task.handle_task(self),
             MainThreadScriptMsg::UserInteraction(task) =>
                 task.handle_task(self),
         }
     }
 
     fn handle_timer_event(&self, timer_event: TimerEvent) {
         let TimerEvent(source, id) = timer_event;
 
@@ -2004,17 +2001,16 @@ impl ScriptThread {
                 .unwrap();
         }
         debug!("ScriptThread: loading {} on pipeline {:?}", incomplete.url, incomplete.pipeline_id);
 
         let MainThreadScriptChan(ref sender) = self.chan;
         let DOMManipulationTaskSource(ref dom_sender) = self.dom_manipulation_task_source;
         let UserInteractionTaskSource(ref user_sender) = self.user_interaction_task_source;
         let HistoryTraversalTaskSource(ref history_sender) = self.history_traversal_task_source;
-        let PerformanceTimelineTaskSource(ref performance_sender) = self.performance_timeline_task_source;
 
         let (ipc_timer_event_chan, ipc_timer_event_port) = ipc::channel().unwrap();
         ROUTER.route_ipc_receiver_to_mpsc_sender(ipc_timer_event_port,
                                                  self.timer_event_chan.clone());
 
         let origin = if final_url.as_str() == "about:blank" {
             incomplete.origin.clone()
         } else {
@@ -2029,17 +2025,17 @@ impl ScriptThread {
         // Create the window and document objects.
         let window = Window::new(self.js_runtime.clone(),
                                  MainThreadScriptChan(sender.clone()),
                                  DOMManipulationTaskSource(dom_sender.clone()),
                                  UserInteractionTaskSource(user_sender.clone()),
                                  self.networking_task_source.clone(),
                                  HistoryTraversalTaskSource(history_sender.clone()),
                                  self.file_reading_task_source.clone(),
-                                 PerformanceTimelineTaskSource(performance_sender.clone()),
+                                 self.performance_timeline_task_source.clone(),
                                  self.image_cache_channel.clone(),
                                  self.image_cache.clone(),
                                  self.resource_threads.clone(),
                                  self.bluetooth_thread.clone(),
                                  self.mem_profiler_chan.clone(),
                                  self.time_profiler_chan.clone(),
                                  self.devtools_chan.clone(),
                                  script_to_constellation_chan,
--- a/servo/components/script/task_source/performance_timeline.rs
+++ b/servo/components/script/task_source/performance_timeline.rs
@@ -1,58 +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/. */
 
 // XXX The spec says that the performance timeline task source should be
 //     a low priority task and it should be processed during idle periods.
 //     We are currently treating this task queue as a normal priority queue.
 
-use dom::bindings::inheritance::Castable;
 use dom::bindings::refcounted::Trusted;
-use dom::performance::{NotifyPerformanceObserverRunnable, Performance};
-use dom::window::Window;
-use script_thread::{MainThreadScriptMsg, Runnable, RunnableWrapper, ScriptThread};
+use dom::globalscope::GlobalScope;
+use dom::performance::Performance;
+use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
+use script_thread::{Runnable, RunnableWrapper};
 use std::fmt;
 use std::result::Result;
-use std::sync::mpsc::Sender;
 use task_source::TaskSource;
 
-#[derive(Clone, JSTraceable)]
-pub struct PerformanceTimelineTaskSource(pub Sender<MainThreadScriptMsg>);
+pub struct NotifyPerformanceObserverRunnable {
+    owner: Trusted<Performance>,
+}
+
+impl NotifyPerformanceObserverRunnable {
+    pub fn new(owner: Trusted<Performance>) -> Self {
+        NotifyPerformanceObserverRunnable {
+            owner,
+        }
+    }
+}
+
+impl Runnable for NotifyPerformanceObserverRunnable {
+    fn handler(self: Box<NotifyPerformanceObserverRunnable>) {
+        self.owner.root().notify_observers();
+    }
+}
+
+#[derive(JSTraceable)]
+pub struct PerformanceTimelineTaskSource(pub Box<ScriptChan + Send + 'static>);
+
+impl Clone for PerformanceTimelineTaskSource {
+    fn clone(&self) -> PerformanceTimelineTaskSource {
+        PerformanceTimelineTaskSource(self.0.clone())
+    }
+}
 
 impl fmt::Debug for PerformanceTimelineTaskSource {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "PerformanceTimelineTaskSource(...)")
     }
 }
 
 impl TaskSource for PerformanceTimelineTaskSource {
     fn queue_with_wrapper<T>(&self,
                              msg: Box<T>,
                              wrapper: &RunnableWrapper) -> Result<(), ()>
                              where T: Runnable + Send + 'static {
-        let msg = PerformanceTimelineTask(wrapper.wrap_runnable(msg));
-        self.0.send(MainThreadScriptMsg::PerformanceTimeline(msg)).map_err(|_| ())
+        let msg = CommonScriptMsg::RunnableMsg(
+            ScriptThreadEventCategory::PerformanceTimelineTask,
+            wrapper.wrap_runnable(msg)
+        );
+        self.0.send(msg).map_err(|_| ())
     }
 }
 
 impl PerformanceTimelineTaskSource {
-    pub fn queue_notification(&self, owner: &Performance, window: &Window) {
-        let owner = Trusted::new(owner);
+    pub fn queue_notification(&self, global: &GlobalScope) {
+        let owner = Trusted::new(&*global.performance());
         let runnable = box NotifyPerformanceObserverRunnable::new(owner);
-        let _ = self.queue(runnable, window.upcast());
+        let _ = self.queue(runnable, global);
     }
 }
-
-pub struct PerformanceTimelineTask(pub Box<Runnable + Send>);
-
-impl fmt::Debug for PerformanceTimelineTask {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "PerformanceTimelineTask(...)")
-    }
-}
-
-impl PerformanceTimelineTask {
-    pub fn handle_task(self, script_thread: &ScriptThread) {
-        self.0.main_thread_handler(script_thread);
-    }
-}
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -1,16 +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/. */
 
 //! Per-node data used in style calculation.
 
 use context::{SharedStyleContext, StackLimitChecker};
 use dom::TElement;
+use invalidation::element::invalidator::InvalidationResult;
 use invalidation::element::restyle_hints::RestyleHint;
 use properties::ComputedValues;
 use properties::longhands::display::computed_value as display;
 use rule_tree::StrongRuleNode;
 use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
 use servo_arc::Arc;
 use shared_lock::StylesheetGuards;
 use std::fmt;
@@ -324,43 +325,46 @@ impl ElementData {
     /// Invalidates style for this element, its descendants, and later siblings,
     /// based on the snapshot of the element that we took when attributes or
     /// state changed.
     pub fn invalidate_style_if_needed<'a, E: TElement>(
         &mut self,
         element: E,
         shared_context: &SharedStyleContext,
         stack_limit_checker: Option<&StackLimitChecker>,
-    ) {
+    ) -> InvalidationResult {
         // In animation-only restyle we shouldn't touch snapshot at all.
         if shared_context.traversal_flags.for_animation_only() {
-            return;
+            return InvalidationResult::empty();
         }
 
         use invalidation::element::invalidator::TreeStyleInvalidator;
 
         debug!("invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \
                 handled_snapshot: {}, pseudo: {:?}",
                 element,
                 shared_context.traversal_flags,
                 element.has_snapshot(),
                 element.handled_snapshot(),
                 element.implemented_pseudo_element());
 
-        if element.has_snapshot() && !element.handled_snapshot() {
-            let invalidator = TreeStyleInvalidator::new(
-                element,
-                Some(self),
-                shared_context,
-                stack_limit_checker,
-            );
-            invalidator.invalidate();
-            unsafe { element.set_handled_snapshot() }
-            debug_assert!(element.handled_snapshot());
+        if !element.has_snapshot() || element.handled_snapshot() {
+            return InvalidationResult::empty();
         }
+
+        let invalidator = TreeStyleInvalidator::new(
+            element,
+            Some(self),
+            shared_context,
+            stack_limit_checker,
+        );
+        let result = invalidator.invalidate();
+        unsafe { element.set_handled_snapshot() }
+        debug_assert!(element.handled_snapshot());
+        result
     }
 
     /// Returns true if this element has styles.
     #[inline]
     pub fn has_styles(&self) -> bool {
         self.styles.primary.is_some()
     }
 
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -11,17 +11,18 @@ use gecko_bindings::structs::{RawGeckoPr
 use gecko_bindings::structs::{StyleSheetInfo, ServoStyleSheetInner};
 use gecko_bindings::structs::nsIDocument;
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
 use invalidation::media_queries::{MediaListKey, ToMediaListKey};
 use media_queries::{Device, MediaList};
 use properties::ComputedValues;
 use servo_arc::Arc;
 use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
-use stylesheets::{MallocSizeOfFn, PerOrigin, StylesheetContents, StylesheetInDocument};
+use stylesheets::{MallocEnclosingSizeOfFn, MallocSizeOfFn, PerOrigin, StylesheetContents};
+use stylesheets::StylesheetInDocument;
 use stylist::{ExtraStyleData, Stylist};
 
 /// Little wrapper to a Gecko style sheet.
 #[derive(Debug, Eq, PartialEq)]
 pub struct GeckoStyleSheet(*const ServoStyleSheet);
 
 impl ToMediaListKey for ::gecko::data::GeckoStyleSheet {
     fn to_media_list_key(&self) -> MediaListKey {
@@ -182,20 +183,27 @@ impl PerDocumentStyleDataImpl {
     }
     /// Returns whether visited styles are enabled.
     pub fn visited_styles_enabled(&self) -> bool {
         self.visited_links_enabled() && !self.is_private_browsing_enabled()
     }
 
     /// Measures heap usage.
     pub fn malloc_add_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                       malloc_enclosing_size_of: MallocEnclosingSizeOfFn,
                                        sizes: &mut ServoStyleSetSizes) {
-        self.stylist.malloc_add_size_of_children(malloc_size_of, sizes);
+        self.stylist.malloc_add_size_of_children(malloc_size_of, malloc_enclosing_size_of, sizes);
 
-        // We may measure more fields in the future if DMD says it's worth it.
+        let data = &self.extra_style_data;
+        sizes.mStylistOther +=
+            data.user_agent.malloc_size_of_children(malloc_size_of, malloc_enclosing_size_of);
+        sizes.mStylistOther +=
+            data.user.malloc_size_of_children(malloc_size_of, malloc_enclosing_size_of);
+        sizes.mStylistOther +=
+            data.author.malloc_size_of_children(malloc_size_of, malloc_enclosing_size_of);
     }
 }
 
 unsafe impl HasFFI for PerDocumentStyleData {
     type FFIType = RawServoStyleSet;
 }
 unsafe impl HasSimpleFFI for PerDocumentStyleData {}
 unsafe impl HasBoxFFI for PerDocumentStyleData {}
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -909,16 +909,20 @@ extern "C" {
     pub fn Gecko_nsFont_InitSystem(dst: *mut nsFont, font_id: i32,
                                    font: *const nsStyleFont,
                                    pres_context: RawGeckoPresContextBorrowed);
 }
 extern "C" {
     pub fn Gecko_nsFont_Destroy(dst: *mut nsFont);
 }
 extern "C" {
+    pub fn Gecko_ConstructFontFeatureValueSet()
+     -> *mut gfxFontFeatureValueSet;
+}
+extern "C" {
     pub fn Gecko_AppendFeatureValueHashEntry(value_set:
                                                  *mut gfxFontFeatureValueSet,
                                              family: *mut nsIAtom,
                                              alternate: u32,
                                              name: *mut nsIAtom)
      -> *mut nsTArray<::std::os::raw::c_uint>;
 }
 extern "C" {
@@ -2042,32 +2046,32 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_StyleSet_GetCounterStyleRule(set: RawServoStyleSetBorrowed,
                                               name: *mut nsIAtom)
      -> *mut nsCSSCounterStyleRule;
 }
 extern "C" {
     pub fn Servo_StyleSet_BuildFontFeatureValueSet(set:
-                                                       RawServoStyleSetBorrowed,
-                                                   list:
-                                                       *mut gfxFontFeatureValueSet)
-     -> bool;
+                                                       RawServoStyleSetBorrowed)
+     -> *mut gfxFontFeatureValueSet;
 }
 extern "C" {
     pub fn Servo_StyleSet_ResolveForDeclarations(set:
                                                      RawServoStyleSetBorrowed,
                                                  parent_style:
                                                      ServoStyleContextBorrowedOrNull,
                                                  declarations:
                                                      RawServoDeclarationBlockBorrowed)
      -> ServoStyleContextStrong;
 }
 extern "C" {
     pub fn Servo_StyleSet_AddSizeOfExcludingThis(malloc_size_of: MallocSizeOf,
+                                                 malloc_enclosing_size_of:
+                                                     MallocSizeOf,
                                                  sizes:
                                                      *mut ServoStyleSetSizes,
                                                  set:
                                                      RawServoStyleSetBorrowed);
 }
 extern "C" {
     pub fn Servo_StyleContext_AddRef(ctx: ServoStyleContextBorrowed);
 }
--- a/servo/components/style/gecko/generated/structs_debug.rs
+++ b/servo/components/style/gecko/generated/structs_debug.rs
@@ -5560,37 +5560,77 @@ pub mod root {
                         const _ as usize } , 0usize , concat ! (
                         "Alignment of field: " , stringify ! ( GeckoEffects )
                         , "::" , stringify ! ( gecko ) ));
         }
         #[repr(C)]
         #[derive(Debug, Copy)]
         pub struct ServoStyleSetSizes {
             pub mStylistRuleTree: usize,
+            pub mStylistPrecomputedPseudos: usize,
+            pub mStylistElementAndPseudosMaps: usize,
+            pub mStylistInvalidationMap: usize,
+            pub mStylistRevalidationSelectors: usize,
+            pub mStylistOther: usize,
             pub mOther: usize,
         }
         #[test]
         fn bindgen_test_layout_ServoStyleSetSizes() {
-            assert_eq!(::std::mem::size_of::<ServoStyleSetSizes>() , 16usize ,
+            assert_eq!(::std::mem::size_of::<ServoStyleSetSizes>() , 56usize ,
                        concat ! (
                        "Size of: " , stringify ! ( ServoStyleSetSizes ) ));
             assert_eq! (::std::mem::align_of::<ServoStyleSetSizes>() , 8usize
                         , concat ! (
                         "Alignment of " , stringify ! ( ServoStyleSetSizes )
                         ));
             assert_eq! (unsafe {
                         & ( * ( 0 as * const ServoStyleSetSizes ) ) .
                         mStylistRuleTree as * const _ as usize } , 0usize ,
                         concat ! (
                         "Alignment of field: " , stringify ! (
                         ServoStyleSetSizes ) , "::" , stringify ! (
                         mStylistRuleTree ) ));
             assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistPrecomputedPseudos as * const _ as usize } ,
+                        8usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistPrecomputedPseudos ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistElementAndPseudosMaps as * const _ as usize }
+                        , 16usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistElementAndPseudosMaps ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistInvalidationMap as * const _ as usize } ,
+                        24usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistInvalidationMap ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistRevalidationSelectors as * const _ as usize }
+                        , 32usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistRevalidationSelectors ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistOther as * const _ as usize } , 40usize ,
+                        concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistOther ) ));
+            assert_eq! (unsafe {
                         & ( * ( 0 as * const ServoStyleSetSizes ) ) . mOther
-                        as * const _ as usize } , 8usize , concat ! (
+                        as * const _ as usize } , 48usize , concat ! (
                         "Alignment of field: " , stringify ! (
                         ServoStyleSetSizes ) , "::" , stringify ! ( mOther )
                         ));
         }
         impl Clone for ServoStyleSetSizes {
             fn clone(&self) -> Self { *self }
         }
         #[repr(u8)]
--- a/servo/components/style/gecko/generated/structs_release.rs
+++ b/servo/components/style/gecko/generated/structs_release.rs
@@ -5448,37 +5448,77 @@ pub mod root {
                         const _ as usize } , 0usize , concat ! (
                         "Alignment of field: " , stringify ! ( GeckoEffects )
                         , "::" , stringify ! ( gecko ) ));
         }
         #[repr(C)]
         #[derive(Debug, Copy)]
         pub struct ServoStyleSetSizes {
             pub mStylistRuleTree: usize,
+            pub mStylistPrecomputedPseudos: usize,
+            pub mStylistElementAndPseudosMaps: usize,
+            pub mStylistInvalidationMap: usize,
+            pub mStylistRevalidationSelectors: usize,
+            pub mStylistOther: usize,
             pub mOther: usize,
         }
         #[test]
         fn bindgen_test_layout_ServoStyleSetSizes() {
-            assert_eq!(::std::mem::size_of::<ServoStyleSetSizes>() , 16usize ,
+            assert_eq!(::std::mem::size_of::<ServoStyleSetSizes>() , 56usize ,
                        concat ! (
                        "Size of: " , stringify ! ( ServoStyleSetSizes ) ));
             assert_eq! (::std::mem::align_of::<ServoStyleSetSizes>() , 8usize
                         , concat ! (
                         "Alignment of " , stringify ! ( ServoStyleSetSizes )
                         ));
             assert_eq! (unsafe {
                         & ( * ( 0 as * const ServoStyleSetSizes ) ) .
                         mStylistRuleTree as * const _ as usize } , 0usize ,
                         concat ! (
                         "Alignment of field: " , stringify ! (
                         ServoStyleSetSizes ) , "::" , stringify ! (
                         mStylistRuleTree ) ));
             assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistPrecomputedPseudos as * const _ as usize } ,
+                        8usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistPrecomputedPseudos ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistElementAndPseudosMaps as * const _ as usize }
+                        , 16usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistElementAndPseudosMaps ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistInvalidationMap as * const _ as usize } ,
+                        24usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistInvalidationMap ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistRevalidationSelectors as * const _ as usize }
+                        , 32usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistRevalidationSelectors ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistOther as * const _ as usize } , 40usize ,
+                        concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistOther ) ));
+            assert_eq! (unsafe {
                         & ( * ( 0 as * const ServoStyleSetSizes ) ) . mOther
-                        as * const _ as usize } , 8usize , concat ! (
+                        as * const _ as usize } , 48usize , concat ! (
                         "Alignment of field: " , stringify ! (
                         ServoStyleSetSizes ) , "::" , stringify ! ( mOther )
                         ));
         }
         impl Clone for ServoStyleSetSizes {
             fn clone(&self) -> Self { *self }
         }
         #[repr(u8)]
--- a/servo/components/style/invalidation/element/invalidation_map.rs
+++ b/servo/components/style/invalidation/element/invalidation_map.rs
@@ -9,16 +9,18 @@ use context::QuirksMode;
 use element_state::ElementState;
 use selector_map::{MaybeCaseInsensitiveHashMap, SelectorMap, SelectorMapEntry};
 use selector_parser::SelectorImpl;
 use selectors::attr::NamespaceConstraint;
 use selectors::parser::{Combinator, Component};
 use selectors::parser::{Selector, SelectorIter, SelectorMethods};
 use selectors::visitor::SelectorVisitor;
 use smallvec::SmallVec;
+#[cfg(feature = "gecko")]
+use stylesheets::{MallocEnclosingSizeOfFn, MallocSizeOfHash};
 
 #[cfg(feature = "gecko")]
 /// Gets the element state relevant to the given `:dir` pseudo-class selector.
 pub fn dir_selector_to_state(s: &[u16]) -> ElementState {
     use element_state::{IN_LTR_STATE, IN_RTL_STATE};
 
     // Jump through some hoops to deal with our Box<[u16]> thing.
     const LTR: [u16; 4] = [b'l' as u16, b't' as u16, b'r' as u16, 0];
@@ -132,20 +134,20 @@ impl SelectorMapEntry for StateDependenc
 /// In particular, we want to lookup as few things as possible to get the fewer
 /// selectors the better, so this looks up by id, class, or looks at the list of
 /// state/other attribute affecting selectors.
 #[derive(Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct InvalidationMap {
     /// A map from a given class name to all the selectors with that class
     /// selector.
-    pub class_to_selector: MaybeCaseInsensitiveHashMap<Atom, SelectorMap<Dependency>>,
+    pub class_to_selector: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>,
     /// A map from a given id to all the selectors with that ID in the
     /// stylesheets currently applying to the document.
-    pub id_to_selector: MaybeCaseInsensitiveHashMap<Atom, SelectorMap<Dependency>>,
+    pub id_to_selector: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>,
     /// A map of all the state dependencies.
     pub state_affecting_selectors: SelectorMap<StateDependency>,
     /// A map of other attribute affecting selectors.
     pub other_attribute_affecting_selectors: SelectorMap<Dependency>,
     /// Whether there are attribute rules of the form `[class~="foo"]` that may
     /// match. In that case, we need to look at
     /// `other_attribute_affecting_selectors` too even if only the `class` has
     /// changed.
@@ -238,31 +240,31 @@ impl InvalidationMap {
             }
 
             self.has_id_attribute_selectors |= compound_visitor.has_id_attribute_selectors;
             self.has_class_attribute_selectors |= compound_visitor.has_class_attribute_selectors;
 
             for class in compound_visitor.classes {
                 self.class_to_selector
                     .entry(class, quirks_mode)
-                    .or_insert_with(SelectorMap::new)
-                    .insert(Dependency {
+                    .or_insert_with(SmallVec::new)
+                    .push(Dependency {
                         selector: selector.clone(),
                         selector_offset: sequence_start,
-                    }, quirks_mode);
+                    })
             }
 
             for id in compound_visitor.ids {
                 self.id_to_selector
                     .entry(id, quirks_mode)
-                    .or_insert_with(SelectorMap::new)
-                    .insert(Dependency {
+                    .or_insert_with(SmallVec::new)
+                    .push(Dependency {
                         selector: selector.clone(),
                         selector_offset: sequence_start,
-                    }, quirks_mode);
+                    })
             }
 
             if !compound_visitor.state.is_empty() {
                 self.state_affecting_selectors
                     .insert(StateDependency {
                         dep: Dependency {
                             selector: selector.clone(),
                             selector_offset: sequence_start,
@@ -282,16 +284,34 @@ impl InvalidationMap {
             combinator = iter.next_sequence();
             if combinator.is_none() {
                 break;
             }
 
             index += 1; // Account for the combinator.
         }
     }
+
+    /// Measures heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_size_of_children(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        // Currently we measure the HashMap storage, but not things pointed to
+        // by keys and values.
+        let mut n = 0;
+
+        n += self.class_to_selector.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        n += self.id_to_selector.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+
+        n += self.state_affecting_selectors.malloc_size_of_children(malloc_enclosing_size_of);
+
+        n += self.other_attribute_affecting_selectors.malloc_size_of_children(
+            malloc_enclosing_size_of);
+        n
+    }
 }
 
 /// A struct that collects invalidations for a given compound selector.
 struct CompoundSelectorDependencyCollector {
     /// The state this compound selector is affected by.
     state: ElementState,
 
     /// The classes this compound selector is affected by.
--- a/servo/components/style/invalidation/element/invalidator.rs
+++ b/servo/components/style/invalidation/element/invalidator.rs
@@ -18,16 +18,22 @@ use selector_parser::{SelectorImpl, Snap
 use selectors::attr::CaseSensitivity;
 use selectors::matching::{MatchingContext, MatchingMode, VisitedHandlingMode};
 use selectors::matching::{matches_selector, matches_compound_selector};
 use selectors::matching::CompoundSelectorMatchingResult;
 use selectors::parser::{Combinator, Component, Selector};
 use smallvec::SmallVec;
 use std::fmt;
 
+#[derive(Debug, PartialEq)]
+enum VisitedDependent {
+    Yes,
+    No,
+}
+
 /// The struct that takes care of encapsulating all the logic on where and how
 /// element styles need to be invalidated.
 pub struct TreeStyleInvalidator<'a, 'b: 'a, E>
     where E: TElement,
 {
     element: E,
     // TODO(emilio): It's tempting enough to just avoid running invalidation for
     // elements without data.
@@ -110,24 +116,60 @@ impl fmt::Debug for Invalidation {
             }
             component.to_css(f)?;
         }
         f.write_str(")")
     }
 }
 
 /// The result of processing a single invalidation for a given element.
-struct InvalidationResult {
+struct SingleInvalidationResult {
     /// Whether the element itself was invalidated.
     invalidated_self: bool,
     /// Whether the invalidation matched, either invalidating the element or
     /// generating another invalidation.
     matched: bool,
 }
 
+/// The result of a whole invalidation process for a given element.
+pub struct InvalidationResult {
+    /// Whether the element itself was invalidated.
+    invalidated_self: bool,
+    /// Whether the element's descendants were invalidated.
+    invalidated_descendants: bool,
+    /// Whether the element's siblings were invalidated.
+    invalidated_siblings: bool,
+}
+
+impl InvalidationResult {
+    /// Create an emtpy result.
+    pub fn empty() -> Self {
+        Self {
+            invalidated_self: false,
+            invalidated_descendants: false,
+            invalidated_siblings: false,
+        }
+    }
+
+    /// Whether the invalidation has invalidate the element itself.
+    pub fn has_invalidated_self(&self) -> bool {
+        self.invalidated_self
+    }
+
+    /// Whether the invalidation has invalidate desendants.
+    pub fn has_invalidated_descendants(&self) -> bool {
+        self.invalidated_descendants
+    }
+
+    /// Whether the invalidation has invalidate siblings.
+    pub fn has_invalidated_siblings(&self) -> bool {
+        self.invalidated_siblings
+    }
+}
+
 impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
     where E: TElement,
 {
     /// Trivially constructs a new `TreeStyleInvalidator`.
     pub fn new(
         element: E,
         data: Option<&'a mut ElementData>,
         shared_context: &'a SharedStyleContext<'b>,
@@ -137,30 +179,30 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
             element,
             data,
             shared_context,
             stack_limit_checker,
         }
     }
 
     /// Perform the invalidation pass.
-    pub fn invalidate(mut self) {
+    pub fn invalidate(mut self) -> InvalidationResult {
         debug!("StyleTreeInvalidator::invalidate({:?})", self.element);
         debug_assert!(self.element.has_snapshot(), "Why bothering?");
         debug_assert!(self.data.is_some(), "How exactly?");
 
         let shared_context = self.shared_context;
 
         let wrapper =
             ElementWrapper::new(self.element, shared_context.snapshot_map);
         let state_changes = wrapper.state_changes();
         let snapshot = wrapper.snapshot().expect("has_snapshot lied");
 
         if !snapshot.has_attrs() && state_changes.is_empty() {
-            return;
+            return InvalidationResult::empty();
         }
 
         // If we are sensitive to visitedness and the visited state changed, we
         // force a restyle here. Matching doesn't depend on the actual visited
         // state at all, so we can't look at matching results to decide what to
         // do for this case.
         if state_changes.intersects(IN_VISITED_OR_UNVISITED_STATE) {
             trace!(" > visitedness change, force subtree restyle");
@@ -247,18 +289,20 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
             if let Some(ref mut data) = self.data {
                 data.restyle.hint.insert(RESTYLE_SELF);
             }
         }
 
         debug!("Collected invalidations (self: {}): ", invalidated_self);
         debug!(" > descendants: {:?}", descendant_invalidations);
         debug!(" > siblings: {:?}", sibling_invalidations);
-        self.invalidate_descendants(&descendant_invalidations);
-        self.invalidate_siblings(&mut sibling_invalidations);
+        let invalidated_descendants = self.invalidate_descendants(&descendant_invalidations);
+        let invalidated_siblings = self.invalidate_siblings(&mut sibling_invalidations);
+
+        InvalidationResult { invalidated_self, invalidated_descendants, invalidated_siblings }
     }
 
     /// Go through later DOM siblings, invalidating style as needed using the
     /// `sibling_invalidations` list.
     ///
     /// Returns whether any sibling's style or any sibling descendant's style
     /// was invalidated.
     fn invalidate_siblings(
@@ -580,17 +624,17 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
     /// invalidation should be effective to subsequent siblings or descendants
     /// down in the tree.
     fn process_invalidation(
         &mut self,
         invalidation: &Invalidation,
         descendant_invalidations: &mut InvalidationVector,
         sibling_invalidations: &mut InvalidationVector,
         invalidation_kind: InvalidationKind,
-    ) -> InvalidationResult {
+    ) -> SingleInvalidationResult {
         debug!("TreeStyleInvalidator::process_invalidation({:?}, {:?}, {:?})",
                self.element, invalidation, invalidation_kind);
 
         let mut context =
             MatchingContext::new_for_visited(
                 MatchingMode::Normal,
                 None,
                 VisitedHandlingMode::AllLinksVisitedAndUnvisited,
@@ -754,17 +798,17 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
         }
 
         if invalidated_self {
             if let Some(ref mut data) = self.data {
                 data.restyle.hint.insert(RESTYLE_SELF);
             }
         }
 
-        InvalidationResult { invalidated_self, matched, }
+        SingleInvalidationResult { invalidated_self, matched, }
     }
 }
 
 struct InvalidationCollector<'a, 'b: 'a, E>
     where E: TElement,
 {
     element: E,
     wrapper: ElementWrapper<'b, E>,
@@ -787,30 +831,36 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
     fn collect_dependencies_in_invalidation_map(
         &mut self,
         map: &InvalidationMap,
     ) {
         let quirks_mode = self.shared_context.quirks_mode();
         let removed_id = self.removed_id;
         if let Some(ref id) = removed_id {
             if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
-                self.collect_dependencies_in_map(deps)
+                for dep in deps {
+                    self.scan_dependency(dep, VisitedDependent::No);
+                }
             }
         }
 
         let added_id = self.added_id;
         if let Some(ref id) = added_id {
             if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
-                self.collect_dependencies_in_map(deps)
+                for dep in deps {
+                    self.scan_dependency(dep, VisitedDependent::No);
+                }
             }
         }
 
         for class in self.classes_added.iter().chain(self.classes_removed.iter()) {
             if let Some(deps) = map.class_to_selector.get(class, quirks_mode) {
-                self.collect_dependencies_in_map(deps)
+                for dep in deps {
+                    self.scan_dependency(dep, VisitedDependent::No);
+                }
             }
         }
 
         let should_examine_attribute_selector_map =
             self.snapshot.other_attr_changed() ||
             (self.snapshot.class_changed() && map.has_class_attribute_selectors) ||
             (self.snapshot.id_changed() && map.has_id_attribute_selectors);
 
@@ -834,20 +884,17 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
         map: &SelectorMap<Dependency>,
     ) {
         map.lookup_with_additional(
             self.lookup_element,
             self.shared_context.quirks_mode(),
             self.removed_id,
             self.classes_removed,
             &mut |dependency| {
-                self.scan_dependency(
-                    dependency,
-                    /* is_visited_dependent = */ false
-                );
+                self.scan_dependency(dependency, VisitedDependent::No);
                 true
             },
         );
     }
 
     fn collect_state_dependencies(
         &mut self,
         map: &SelectorMap<StateDependency>,
@@ -857,31 +904,34 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
             self.lookup_element,
             self.shared_context.quirks_mode(),
             self.removed_id,
             self.classes_removed,
             &mut |dependency| {
                 if !dependency.state.intersects(state_changes) {
                     return true;
                 }
-                self.scan_dependency(
-                    &dependency.dep,
-                    dependency.state.intersects(IN_VISITED_OR_UNVISITED_STATE)
-                );
+                let visited_dependent =
+                    if dependency.state.intersects(IN_VISITED_OR_UNVISITED_STATE) {
+                        VisitedDependent::Yes
+                    } else {
+                        VisitedDependent::No
+                    };
+                self.scan_dependency(&dependency.dep, visited_dependent);
                 true
             },
         );
     }
 
     fn scan_dependency(
         &mut self,
         dependency: &Dependency,
-        is_visited_dependent: bool
+        is_visited_dependent: VisitedDependent,
     ) {
-        debug!("TreeStyleInvalidator::scan_dependency({:?}, {:?}, {})",
+        debug!("TreeStyleInvalidator::scan_dependency({:?}, {:?}, {:?})",
                self.element,
                dependency,
                is_visited_dependent);
 
         if !self.dependency_may_be_relevant(dependency) {
             return;
         }
 
@@ -938,17 +988,17 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
         // The matching process only considers the relevant link state and
         // visited handling mode when deciding if visited matches.  Instead, we
         // are rematching here in case there is some :visited selector whose
         // matching result changed for some other state or attribute change of
         // this element (for example, for things like [foo]:visited).
         //
         // NOTE: This thing is actually untested because testing it is flaky,
         // see the tests that were added and then backed out in bug 1328509.
-        if is_visited_dependent && now_context.relevant_link_found {
+        if is_visited_dependent == VisitedDependent::Yes && now_context.relevant_link_found {
             then_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
             let matched_then =
                 matches_selector(&dependency.selector,
                                  dependency.selector_offset,
                                  None,
                                  &self.wrapper,
                                  &mut then_context,
                                  &mut |_, _| {});
--- a/servo/components/style/invalidation/media_queries.rs
+++ b/servo/components/style/invalidation/media_queries.rs
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Code related to the invalidation of media-query-affected rules.
 
 use context::QuirksMode;
 use fnv::FnvHashSet;
 use media_queries::Device;
 use shared_lock::SharedRwLockReadGuard;
-use stylesheets::{DocumentRule, ImportRule, MediaRule,  SupportsRule};
-use stylesheets::{NestedRuleIterationCondition, Stylesheet};
+use stylesheets::{DocumentRule, ImportRule, MallocEnclosingSizeOfFn, MallocSizeOfHash, MediaRule};
+use stylesheets::{NestedRuleIterationCondition, Stylesheet, SupportsRule};
 
 /// A key for a given media query result.
 ///
 /// NOTE: It happens to be the case that all the media lists we care about
 /// happen to have a stable address, so we can just use an opaque pointer to
 /// represent them.
 ///
 /// Also, note that right now when a rule or stylesheet is removed, we do a full
@@ -83,16 +83,22 @@ impl EffectiveMediaQueryResults {
     /// Notices that an effective item has been seen, and caches it as matching.
     pub fn saw_effective<T>(&mut self, item: &T)
         where T: ToMediaListKey,
     {
         // NOTE(emilio): We can't assert that we don't cache the same item twice
         // because of stylesheet reusing... shrug.
         self.set.insert(item.to_media_list_key());
     }
+
+    /// Measure heap usage.
+    pub fn malloc_size_of_children(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        self.set.malloc_shallow_size_of_hash(malloc_enclosing_size_of)
+    }
 }
 
 /// A filter that filters over effective rules, but allowing all potentially
 /// effective `@media` rules.
 pub struct PotentiallyEffectiveMediaRules;
 
 impl NestedRuleIterationCondition for PotentiallyEffectiveMediaRules {
     fn process_import(
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -55,17 +55,17 @@ use properties::animated_properties::Tra
 use properties::computed_value_flags::ComputedValueFlags;
 use properties::{longhands, FontComputationData, Importance, LonghandId};
 use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
 use rule_tree::StrongRuleNode;
 use selector_parser::PseudoElement;
 use servo_arc::{Arc, RawOffsetArc};
 use std::mem::{forget, uninitialized, transmute, zeroed};
 use std::{cmp, ops, ptr};
-use values::{self, Auto, CustomIdent, Either, KeyframesName};
+use values::{self, Auto, CustomIdent, Either, KeyframesName, None_};
 use values::computed::{NonNegativeAu, ToComputedValue, Percentage};
 use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
 use computed_values::border_style;
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
     % endfor
@@ -738,20 +738,26 @@ def set_gecko_property(ffi_name, expr):
             SVGPaintKind::Color(color) => {
                 paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_Color;
                 unsafe {
                     *paint.mPaint.mColor.as_mut() = convert_rgba_to_nscolor(&color);
                 }
             }
         }
 
-        if let Some(fallback) = fallback {
-            paint.mFallbackType = nsStyleSVGFallbackType::eStyleSVGFallbackType_Color;
-            paint.mFallbackColor = convert_rgba_to_nscolor(&fallback);
-        }
+        paint.mFallbackType = match fallback {
+            Some(Either::First(color)) => {
+                paint.mFallbackColor = convert_rgba_to_nscolor(&color);
+                nsStyleSVGFallbackType::eStyleSVGFallbackType_Color
+            },
+            Some(Either::Second(_)) => {
+                nsStyleSVGFallbackType::eStyleSVGFallbackType_None
+            },
+            None => nsStyleSVGFallbackType::eStyleSVGFallbackType_NotSet
+        };
     }
 
     #[allow(non_snake_case)]
     pub fn copy_${ident}_from(&mut self, other: &Self) {
         unsafe {
             bindings::Gecko_nsStyleSVGPaint_CopyFrom(
                 &mut ${get_gecko_property(gecko_ffi_name)},
                 & ${get_gecko_property(gecko_ffi_name, "other")}
@@ -766,21 +772,27 @@ def set_gecko_property(ffi_name, expr):
 
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
         use values::generics::svg::{SVGPaint, SVGPaintKind};
         use values::specified::url::SpecifiedUrl;
         use self::structs::nsStyleSVGPaintType;
         use self::structs::nsStyleSVGFallbackType;
         let ref paint = ${get_gecko_property(gecko_ffi_name)};
-        let fallback = if let nsStyleSVGFallbackType::eStyleSVGFallbackType_Color = paint.mFallbackType {
-            Some(convert_nscolor_to_rgba(paint.mFallbackColor))
-        } else {
-            None
+
+        let fallback = match paint.mFallbackType {
+            nsStyleSVGFallbackType::eStyleSVGFallbackType_Color => {
+                Some(Either::First(convert_nscolor_to_rgba(paint.mFallbackColor)))
+            },
+            nsStyleSVGFallbackType::eStyleSVGFallbackType_None => {
+                Some(Either::Second(None_))
+            },
+            nsStyleSVGFallbackType::eStyleSVGFallbackType_NotSet => None,
         };
+
         let kind = match paint.mType {
             nsStyleSVGPaintType::eStyleSVGPaintType_None => SVGPaintKind::None,
             nsStyleSVGPaintType::eStyleSVGPaintType_ContextFill => SVGPaintKind::ContextFill,
             nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke => SVGPaintKind::ContextStroke,
             nsStyleSVGPaintType::eStyleSVGPaintType_Server => {
                 unsafe {
                     SVGPaintKind::PaintServer(
                         SpecifiedUrl::from_url_value_data(
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -198,18 +198,17 @@
 </%helpers:longhand>
 
 ${helpers.single_keyword("-moz-top-layer", "none top",
                          gecko_constant_prefix="NS_STYLE_TOP_LAYER",
                          gecko_ffi_name="mTopLayer", need_clone=True,
                          products="gecko", animation_value_type="none", internal=True,
                          spec="Internal (not web-exposed)")}
 
-${helpers.single_keyword("position", "static absolute relative fixed",
-                         extra_gecko_values="sticky",
+${helpers.single_keyword("position", "static absolute relative fixed sticky",
                          animation_value_type="discrete",
                          flags="CREATES_STACKING_CONTEXT ABSPOS_CB",
                          spec="https://drafts.csswg.org/css-position/#position-property")}
 
 <%helpers:single_keyword_computed name="float"
                                   values="none left right"
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -791,17 +791,17 @@ impl RuleNode {
                 None
             } else {
                 Some(WeakRuleNode::from_ptr(first_child))
             }
         }
     }
 
     fn malloc_size_of_including_self(&self, malloc_size_of: MallocSizeOfFn) -> usize {
-        let mut n = unsafe { malloc_size_of(self as *const _ as *const _) };
+        let mut n = unsafe { (malloc_size_of.0)(self as *const _ as *const _) };
         for child in self.iter_children() {
             n += unsafe { (*child.ptr()).malloc_size_of_including_self(malloc_size_of) };
         }
         n
     }
 }
 
 #[derive(Clone)]
--- a/servo/components/style/selector_map.rs
+++ b/servo/components/style/selector_map.rs
@@ -14,16 +14,18 @@ use hash::map as hash_map;
 use pdqsort::sort_by;
 use precomputed_hash::PrecomputedHash;
 use rule_tree::CascadeLevel;
 use selector_parser::SelectorImpl;
 use selectors::matching::{matches_selector, MatchingContext, ElementSelectorFlags};
 use selectors::parser::{Component, Combinator, SelectorIter};
 use smallvec::{SmallVec, VecLike};
 use std::hash::{BuildHasherDefault, Hash, Hasher};
+#[cfg(feature = "gecko")]
+use stylesheets::{MallocEnclosingSizeOfFn, MallocSizeOfHash};
 use stylist::Rule;
 
 /// A hasher implementation that doesn't hash anything, because it expects its
 /// input to be a suitable u32 hash.
 pub struct PrecomputedHasher {
     hash: Option<u32>,
 }
 
@@ -139,16 +141,32 @@ impl<T: 'static> SelectorMap<T> {
     pub fn is_empty(&self) -> bool {
         self.count == 0
     }
 
     /// Returns the number of entries.
     pub fn len(&self) -> usize {
         self.count
     }
+
+    /// Measures heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_size_of_children(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        // Currently we measure the HashMap storage, but not things pointed to
+        // by keys and values.
+        let mut n = 0;
+        n += self.id_hash.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        n += self.class_hash.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        n += self.local_name_hash.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+
+        // We may measure other fields in the future if DMD says it's worth it.
+
+        n
+    }
 }
 
 impl SelectorMap<Rule> {
     /// Append to `rule_list` all Rules in `self` that match element.
     ///
     /// Extract matching rules as per element's ID, classes, tag name, etc..
     /// Sort the Rules at the end to maintain cascading order.
     pub fn get_all_matching_rules<E, V, F>(&self,
@@ -497,8 +515,19 @@ impl<V: 'static> MaybeCaseInsensitiveHas
     pub fn get(&self, key: &Atom, quirks_mode: QuirksMode) -> Option<&V> {
         if quirks_mode == QuirksMode::Quirks {
             self.0.get(&key.to_ascii_lowercase())
         } else {
             self.0.get(key)
         }
     }
 }
+
+#[cfg(feature = "gecko")]
+impl<K, V> MallocSizeOfHash for MaybeCaseInsensitiveHashMap<K, V>
+    where K: PrecomputedHash + Eq + Hash
+{
+    fn malloc_shallow_size_of_hash(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        self.0.malloc_shallow_size_of_hash(malloc_enclosing_size_of)
+    }
+}
+
--- a/servo/components/style/selector_parser.rs
+++ b/servo/components/style/selector_parser.rs
@@ -183,9 +183,14 @@ impl<T> PerPseudoElementMap<T> {
             Some(i) => i,
             None => return Err(()),
         };
         if self.entries[index].is_none() {
             self.entries[index] = Some(f());
         }
         Ok(self.entries[index].as_mut().unwrap())
     }
+
+    /// Get an iterator for the entries.
+    pub fn iter(&self) -> ::std::slice::Iter<Option<T>> {
+        self.entries.iter()
+    }
 }
--- a/servo/components/style/shared_lock.rs
+++ b/servo/components/style/shared_lock.rs
@@ -171,16 +171,23 @@ impl<T> Locked<T> {
         //   and we’ve checked that it’s the correct lock.
         // * The returned reference borrows *both* the data and the guard,
         //   so that it can outlive neither.
         unsafe {
             &*ptr
         }
     }
 
+    /// Access the data for reading without verifying the lock. Use with caution.
+    #[cfg(feature = "gecko")]
+    pub unsafe fn read_unchecked<'a>(&'a self) -> &'a T {
+        let ptr = self.data.get();
+        &*ptr
+    }
+
     /// Access the data for writing.
     pub fn write_with<'a>(&'a self, guard: &'a mut SharedRwLockWriteGuard) -> &'a mut T {
         assert!(self.same_lock_as(&guard.0),
                 "Locked::write_with called with a guard from an unrelated SharedRwLock");
         let ptr = self.data.get();
 
         // Unsafe:
         //
--- a/servo/components/style/stylesheets/font_feature_values_rule.rs
+++ b/servo/components/style/stylesheets/font_feature_values_rule.rs
@@ -205,17 +205,17 @@ impl<'a, 'b, 'i, T> DeclarationParser<'i
 {
     type Declaration = ();
     type Error = SelectorParseError<'i, StyleParseError<'i>>;
 
     fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                        -> Result<(), ParseError<'i>> {
         let value = input.parse_entirely(|i| T::parse(self.context, i))?;
         let new = FFVDeclaration {
-            name: Atom::from(&*name).to_ascii_lowercase(),
+            name: Atom::from(&*name),
             value: value,
         };
         update_or_push(&mut self.declarations, new);
         Ok(())
     }
 }
 
 macro_rules! font_feature_values_blocks {
--- a/servo/components/style/stylesheets/keyframes_rule.rs
+++ b/servo/components/style/stylesheets/keyframes_rule.rs
@@ -14,17 +14,17 @@ use properties::LonghandIdSet;
 use properties::animated_properties::AnimatableLonghand;
 use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
 use selectors::parser::SelectorParseError;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
 use std::fmt;
 use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, StyleParseError};
 use style_traits::PropertyDeclarationParseError;
-use stylesheets::{CssRuleType, StylesheetContents};
+use stylesheets::{CssRuleType, MallocSizeOfFn, MallocSizeOfVec, StylesheetContents};
 use stylesheets::rule_parser::{VendorPrefix, get_location_with_offset};
 use values::{KeyframesName, serialize_percentage};
 
 /// A [`@keyframes`][keyframes] rule.
 ///
 /// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes
 #[derive(Debug)]
 pub struct KeyframesRule {
@@ -437,16 +437,24 @@ impl KeyframesAnimation {
         if result.steps.last().unwrap().start_percentage.0 != 1. {
             result.steps.push(KeyframesStep::new(KeyframePercentage::new(1.),
                                                  KeyframesStepValue::ComputedValues,
                                                  guard));
         }
 
         result
     }
+
+    /// Measure heap usage.
+    pub fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+        let mut n = 0;
+        n += self.steps.malloc_shallow_size_of_vec(malloc_size_of);
+        n += self.properties_changed.malloc_shallow_size_of_vec(malloc_size_of);
+        n
+    }
 }
 
 /// Parses a keyframes list, like:
 /// 0%, 50% {
 ///     width: 50%;
 /// }
 ///
 /// 40%, 60%, 100% {
--- a/servo/components/style/stylesheets/memory.rs
+++ b/servo/components/style/stylesheets/memory.rs
@@ -4,51 +4,68 @@
 
 //! Memory reporting for the style system when running inside of Gecko.
 
 #[cfg(feature = "gecko")]
 use gecko_bindings::bindings::Gecko_HaveSeenPtr;
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::SeenPtrs;
 #[cfg(feature = "gecko")]
+use hash::HashMap;
+#[cfg(feature = "gecko")]
 use servo_arc::Arc;
 use shared_lock::SharedRwLockReadGuard;
+use std::collections::HashSet;
+use std::hash::{BuildHasher, Hash};
 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;
+/// Note that functions of this type should be called via do_malloc_size_of(),
+/// rather than directly.
+#[derive(Clone, Copy)]
+pub struct MallocSizeOfFn(pub unsafe extern "C" fn(ptr: *const c_void) -> usize);
+
+/// Like MallocSizeOfFn, but can take an interior pointer.
+#[derive(Clone, Copy)]
+pub struct MallocEnclosingSizeOfFn(pub unsafe extern "C" fn(ptr: *const c_void) -> usize);
 
 /// Servo-side counterpart to mozilla::SizeOfState. The only difference is that
 /// this struct doesn't contain the SeenPtrs table, just a pointer to it.
 #[cfg(feature = "gecko")]
 pub struct SizeOfState {
     /// Function that measures the size of heap blocks.
     pub malloc_size_of: MallocSizeOfFn,
     /// Table recording heap blocks that have already been measured.
     pub seen_ptrs: *mut SeenPtrs,
 }
 
-/// Call malloc_size_of on ptr, first checking that the allocation isn't empty.
+/// Check if an allocation is empty.
 pub unsafe fn is_empty<T>(ptr: *const T) -> bool {
     return ptr as usize <= ::std::mem::align_of::<T>();
 }
 
 /// 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 is_empty(ptr) {
         0
     } else {
-        malloc_size_of(ptr as *const c_void)
+        (malloc_size_of.0)(ptr as *const c_void)
     }
 }
 
+/// Call malloc_enclosing_size_of on ptr, which must not be empty.
+pub unsafe fn do_malloc_enclosing_size_of<T>(
+    malloc_enclosing_size_of: MallocEnclosingSizeOfFn, ptr: *const T) -> usize
+{
+    assert!(!is_empty(ptr));
+    (malloc_enclosing_size_of.0)(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 takes a SizeOfState which allows it to measure
@@ -87,17 +104,17 @@ impl<T: MallocSizeOf> MallocSizeOf for V
 }
 
 #[cfg(feature = "gecko")]
 impl<T: MallocSizeOfWithRepeats> MallocSizeOfWithRepeats for Arc<T> {
     fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize {
         let mut n = 0;
         let heap_ptr = self.heap_ptr();
         if unsafe { !is_empty(heap_ptr) && !Gecko_HaveSeenPtr(state.seen_ptrs, heap_ptr) } {
-            n += unsafe { (state.malloc_size_of)(heap_ptr) };
+            n += unsafe { (state.malloc_size_of.0)(heap_ptr) };
             n += (**self).malloc_size_of_children(state);
         }
         n
     }
 }
 
 impl<T: MallocSizeOfWithGuard> MallocSizeOfWithGuard for Vec<T> {
     fn malloc_size_of_children(
@@ -105,8 +122,83 @@ impl<T: MallocSizeOfWithGuard> MallocSiz
         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))
     }
 }
+
+/// Trait for measuring the heap usage of a Box<T>.
+pub trait MallocSizeOfBox {
+    /// Measure shallowly the size of the memory used by the T -- anything
+    /// pointed to by the T must be measured separately.
+    fn malloc_shallow_size_of_box(&self, malloc_size_of: MallocSizeOfFn) -> usize;
+}
+
+impl<T> MallocSizeOfBox for Box<T> {
+    fn malloc_shallow_size_of_box(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+        unsafe { do_malloc_size_of(malloc_size_of, &**self as *const T) }
+    }
+}
+
+/// Trait for measuring the heap usage of a vector.
+pub trait MallocSizeOfVec {
+    /// Measure shallowly the size of the memory used by the Vec's elements --
+    /// anything pointed to by the elements must be measured separately, using
+    /// iteration.
+    fn malloc_shallow_size_of_vec(&self, malloc_size_of: MallocSizeOfFn) -> usize;
+}
+
+impl<T> MallocSizeOfVec for Vec<T> {
+    fn malloc_shallow_size_of_vec(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+        unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) }
+    }
+}
+
+/// Trait for measuring the heap usage of a hash table.
+pub trait MallocSizeOfHash {
+    /// Measure shallowly the size of the memory used within a hash table --
+    /// anything pointer to by the keys and values must be measured separately,
+    /// using iteration.
+    fn malloc_shallow_size_of_hash(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize;
+}
+
+impl<T, S> MallocSizeOfHash for HashSet<T, S>
+    where T: Eq + Hash,
+          S: BuildHasher
+{
+    fn malloc_shallow_size_of_hash(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        // The first value from the iterator gives us an interior pointer.
+        // malloc_enclosing_size_of() then gives us the storage size. This
+        // assumes that the HashSet's contents (values and hashes) are all
+        // stored in a single contiguous heap allocation.
+        let mut n = 0;
+        for v in self.iter() {
+            n += unsafe { do_malloc_enclosing_size_of(malloc_enclosing_size_of, v as *const T) };
+            break;
+        }
+        n
+    }
+}
+
+#[cfg(feature = "gecko")]
+impl<K, V, S> MallocSizeOfHash for HashMap<K, V, S>
+    where K: Eq + Hash,
+          S: BuildHasher
+{
+    fn malloc_shallow_size_of_hash(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        // The first value from the iterator gives us an interior pointer.
+        // malloc_enclosing_size_of() then gives us the storage size. This
+        // assumes that the HashMap's contents (keys, values, and hashes) are
+        // all stored in a single contiguous heap allocation.
+        let mut n = 0;
+        for v in self.values() {
+            n += unsafe { do_malloc_enclosing_size_of(malloc_enclosing_size_of, v as *const V) };
+            break;
+        }
+        n
+    }
+}
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -35,17 +35,18 @@ use style_traits::PARSING_MODE_DEFAULT;
 pub use self::counter_style_rule::CounterStyleRule;
 pub use self::document_rule::DocumentRule;
 pub use self::font_face_rule::FontFaceRule;
 pub use self::font_feature_values_rule::FontFeatureValuesRule;
 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::memory::{MallocEnclosingSizeOfFn, MallocSizeOf, MallocSizeOfBox, MallocSizeOfFn};
+pub use self::memory::{MallocSizeOfHash, MallocSizeOfVec, MallocSizeOfWithGuard};
 #[cfg(feature = "gecko")]
 pub use self::memory::{MallocSizeOfWithRepeats, SizeOfState};
 pub use self::namespace_rule::NamespaceRule;
 pub use self::origin::{Origin, OriginSet, PerOrigin, PerOriginIter};
 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};
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -38,17 +38,19 @@ use smallvec::VecLike;
 use std::fmt::Debug;
 use std::ops;
 use style_traits::viewport::ViewportConstraints;
 use stylesheet_set::{OriginValidity, SheetRebuildKind, StylesheetSet, StylesheetIterator, StylesheetFlusher};
 #[cfg(feature = "gecko")]
 use stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule};
 use stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
 #[cfg(feature = "gecko")]
-use stylesheets::{MallocSizeOf, MallocSizeOfFn};
+use stylesheets::{MallocEnclosingSizeOfFn, MallocSizeOf, MallocSizeOfBox, MallocSizeOfFn};
+#[cfg(feature = "gecko")]
+use stylesheets::{MallocSizeOfHash, MallocSizeOfVec};
 use stylesheets::StyleRule;
 use stylesheets::StylesheetInDocument;
 use stylesheets::UserAgentStylesheets;
 use stylesheets::keyframes_rule::KeyframesAnimation;
 use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
 use thread_state;
 
 pub use ::fnv::FnvHashMap;
@@ -355,16 +357,35 @@ impl DocumentCascadeData {
                         .borrow_mut_for_origin(&origin)
                         .add_counter_style(guard, rule);
                 }
                 // We don't care about any other rule.
                 _ => {}
             }
         }
     }
+
+    /// Measures heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_add_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                       malloc_enclosing_size_of: MallocEnclosingSizeOfFn,
+                                       sizes: &mut ServoStyleSetSizes) {
+        self.per_origin.user_agent.malloc_add_size_of_children(malloc_size_of,
+                                                               malloc_enclosing_size_of, sizes);
+        self.per_origin.user.malloc_add_size_of_children(malloc_size_of,
+                                                         malloc_enclosing_size_of, sizes);
+        self.per_origin.author.malloc_add_size_of_children(malloc_size_of,
+                                                           malloc_enclosing_size_of, sizes);
+
+        for elem in self.precomputed_pseudo_element_decls.iter() {
+            if let Some(ref elem) = *elem {
+                sizes.mStylistPrecomputedPseudos += elem.malloc_shallow_size_of_vec(malloc_size_of);
+            }
+        }
+    }
 }
 
 /// A wrapper over a StylesheetSet that can be `Sync`, since it's only used and
 /// exposed via mutable methods in the `Stylist`.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 struct StylistStylesheetSet(StylesheetSet<StylistSheet>);
 // Read above to see why this is fine.
 unsafe impl Sync for StylistStylesheetSet {}
@@ -1560,19 +1581,23 @@ impl Stylist {
     /// Accessor for a shared reference to the rule tree.
     pub fn rule_tree(&self) -> &RuleTree {
         &self.rule_tree
     }
 
     /// Measures heap usage.
     #[cfg(feature = "gecko")]
     pub fn malloc_add_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                       malloc_enclosing_size_of: MallocEnclosingSizeOfFn,
                                        sizes: &mut ServoStyleSetSizes) {
-        // XXX: need to measure other fields
+        self.cascade_data.malloc_add_size_of_children(malloc_size_of, malloc_enclosing_size_of,
+                                                      sizes);
         sizes.mStylistRuleTree += self.rule_tree.malloc_size_of_children(malloc_size_of);
+
+        // We may measure other fields in the future if DMD says it's worth it.
     }
 }
 
 /// This struct holds data which users of Stylist may want to extract
 /// from stylesheets which can be done at the same time as updating.
 #[derive(Default)]
 pub struct ExtraStyleData {
     /// A list of effective font-face rules and their origin.
@@ -1615,16 +1640,27 @@ impl ExtraStyleData {
     fn clear(&mut self) {
         #[cfg(feature = "gecko")]
         {
             self.font_faces.clear();
             self.font_feature_values.clear();
             self.counter_styles.clear();
         }
     }
+
+    /// Measure heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                   malloc_enclosing_size_of: MallocEnclosingSizeOfFn) -> usize {
+        let mut n = 0;
+        n += self.font_faces.malloc_shallow_size_of_vec(malloc_size_of);
+        n += self.font_feature_values.malloc_shallow_size_of_vec(malloc_size_of);
+        n += self.counter_styles.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        n
+    }
 }
 
 /// SelectorMapEntry implementation for use in our revalidation selector map.
 #[derive(Clone, Debug)]
 struct RevalidationSelectorAndHashes {
     selector: Selector<SelectorImpl>,
     selector_offset: usize,
     hashes: AncestorHashes,
@@ -1908,16 +1944,48 @@ impl CascadeData {
         self.effective_media_query_results.clear();
         self.invalidation_map.clear();
         self.attribute_dependencies.clear();
         self.style_attribute_dependency = false;
         self.state_dependencies = ElementState::empty();
         self.mapped_ids.clear();
         self.selectors_for_cache_revalidation.clear();
     }
+
+    /// Measures heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_add_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                       malloc_enclosing_size_of: MallocEnclosingSizeOfFn,
+                                       sizes: &mut ServoStyleSetSizes) {
+        sizes.mStylistElementAndPseudosMaps +=
+            self.element_map.malloc_size_of_children(malloc_enclosing_size_of);
+
+        for elem in self.pseudos_map.iter() {
+            if let Some(ref elem) = *elem {
+                sizes.mStylistElementAndPseudosMaps +=
+                    elem.malloc_shallow_size_of_box(malloc_size_of) +
+                    elem.malloc_size_of_children(malloc_enclosing_size_of)
+            }
+        }
+
+        sizes.mStylistOther +=
+            self.animations.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        for val in self.animations.values() {
+            sizes.mStylistOther += val.malloc_size_of_children(malloc_size_of);
+        }
+
+        sizes.mStylistInvalidationMap +=
+            self.invalidation_map.malloc_size_of_children(malloc_enclosing_size_of);
+
+        sizes.mStylistRevalidationSelectors +=
+            self.selectors_for_cache_revalidation.malloc_size_of_children(malloc_enclosing_size_of);
+
+        sizes.mStylistOther +=
+            self.effective_media_query_results.malloc_size_of_children(malloc_enclosing_size_of);
+    }
 }
 
 impl Default for CascadeData {
     fn default() -> Self {
         CascadeData::new()
     }
 }
 
--- a/servo/components/style/values/generics/svg.rs
+++ b/servo/components/style/values/generics/svg.rs
@@ -3,31 +3,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Generic types for CSS values in SVG
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt;
 use style_traits::{ParseError, StyleParseError, ToCss};
+use values::{Either, None_};
 use values::computed::NumberOrPercentage;
 use values::computed::length::LengthOrPercentage;
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 
 /// An SVG paint value
 ///
 /// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Animate, Clone, ComputeSquaredDistance, Debug, PartialEq)]
 #[derive(ToAnimatedValue, ToComputedValue, ToCss)]
 pub struct SVGPaint<ColorType, UrlPaintServer> {
     /// The paint source
     pub kind: SVGPaintKind<ColorType, UrlPaintServer>,
-    /// The fallback color
-    pub fallback: Option<ColorType>,
+    /// The fallback color. It would be empty, the `none` keyword or <color>.
+    pub fallback: Option<Either<ColorType, None_>>,
 }
 
 /// An SVG paint value without the fallback
 ///
 /// Whereas the spec only allows PaintServer
 /// to have a fallback, Gecko lets the context
 /// properties have a fallback as well.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -55,25 +56,29 @@ impl<ColorType, UrlPaintServer> SVGPaint
             "none" => Ok(SVGPaintKind::None),
             "context-fill" => Ok(SVGPaintKind::ContextFill),
             "context-stroke" => Ok(SVGPaintKind::ContextStroke),
         }
     }
 }
 
 /// Parse SVGPaint's fallback.
-/// fallback is keyword(none) or Color.
+/// fallback is keyword(none), Color or empty.
 /// https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint
 fn parse_fallback<'i, 't, ColorType: Parse>(context: &ParserContext,
                                             input: &mut Parser<'i, 't>)
-                                            -> Option<ColorType> {
+                                            -> Option<Either<ColorType, None_>> {
     if input.try(|i| i.expect_ident_matching("none")).is_ok() {
-        None
+        Some(Either::Second(None_))
     } else {
-        input.try(|i| ColorType::parse(context, i)).ok()
+        if let Ok(color) = input.try(|i| ColorType::parse(context, i)) {
+            Some(Either::First(color))
+        } else {
+            None
+        }
     }
 }
 
 impl<ColorType: Parse, UrlPaintServer: Parse> Parse for SVGPaint<ColorType, UrlPaintServer> {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if let Ok(url) = input.try(|i| UrlPaintServer::parse(context, i)) {
             Ok(SVGPaint {
                 kind: SVGPaintKind::PaintServer(url),
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -42,16 +42,17 @@ use style::gecko_bindings::bindings::{Ra
 use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned};
 use style::gecko_bindings::bindings::{RawServoStyleSheetContentsBorrowed, ServoComputedDataBorrowed};
 use style::gecko_bindings::bindings::{RawServoStyleSheetContentsStrong, ServoStyleContextBorrowed};
 use style::gecko_bindings::bindings::{RawServoSupportsRule, RawServoSupportsRuleBorrowed};
 use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong};
 use style::gecko_bindings::bindings::{nsACString, nsAString, nsCSSPropertyIDSetBorrowedMut};
 use style::gecko_bindings::bindings::Gecko_AddPropertyToSet;
 use style::gecko_bindings::bindings::Gecko_AppendPropertyValuePair;
+use style::gecko_bindings::bindings::Gecko_ConstructFontFeatureValueSet;
 use style::gecko_bindings::bindings::Gecko_GetOrCreateFinalKeyframe;
 use style::gecko_bindings::bindings::Gecko_GetOrCreateInitialKeyframe;
 use style::gecko_bindings::bindings::Gecko_GetOrCreateKeyframeAtStart;
 use style::gecko_bindings::bindings::Gecko_NewNoneTransform;
 use style::gecko_bindings::bindings::RawGeckoAnimationPropertySegmentBorrowed;
 use style::gecko_bindings::bindings::RawGeckoCSSPropertyIDListBorrowed;
 use style::gecko_bindings::bindings::RawGeckoComputedKeyframeValuesListBorrowedMut;
 use style::gecko_bindings::bindings::RawGeckoComputedTimingBorrowed;
@@ -113,18 +114,19 @@ use style::properties::animated_properti
 use style::properties::animated_properties::compare_property_priority;
 use style::properties::parse_one_declaration_into;
 use style::rule_tree::StyleSource;
 use style::selector_parser::PseudoElementCascadeType;
 use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::Atom;
 use style::style_adjuster::StyleAdjuster;
 use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
-use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MallocSizeOfWithGuard};
-use style::stylesheets::{MediaRule, NamespaceRule, Origin, OriginSet, PageRule, SizeOfState, StyleRule};
+use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MallocEnclosingSizeOfFn};
+use style::stylesheets::{MallocSizeOfFn, MallocSizeOfWithGuard, MediaRule};
+use style::stylesheets::{NamespaceRule, Origin, OriginSet, PageRule, SizeOfState, StyleRule};
 use style::stylesheets::{StylesheetContents, 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::thread_state;
 use style::timer::Timer;
 use style::traversal::DomTraversal;
@@ -770,17 +772,17 @@ pub extern "C" fn Servo_StyleWorkerThrea
 pub extern "C" fn Servo_Element_ClearData(element: RawGeckoElementBorrowed) {
     unsafe { GeckoElement(element).clear_data() };
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_Element_SizeOfExcludingThisAndCVs(malloc_size_of: GeckoMallocSizeOf,
                                                           seen_ptrs: *mut SeenPtrs,
                                                           element: RawGeckoElementBorrowed) -> usize {
-    let malloc_size_of = malloc_size_of.unwrap();
+    let malloc_size_of = MallocSizeOfFn(malloc_size_of.unwrap());
     let element = GeckoElement(element);
     let borrow = element.borrow_data();
     if let Some(data) = borrow {
         let mut state = SizeOfState {
             malloc_size_of: malloc_size_of,
             seen_ptrs: seen_ptrs,
         };
         (*data).malloc_size_of_children_excluding_cvs(&mut state)
@@ -1078,19 +1080,18 @@ pub extern "C" fn Servo_StyleSheet_Clone
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSheet_SizeOfIncludingThis(
     malloc_size_of: GeckoMallocSizeOf,
     sheet: RawServoStyleSheetContentsBorrowed
 ) -> usize {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
-    let malloc_size_of = malloc_size_of.unwrap();
-    StylesheetContents::as_arc(&sheet)
-        .malloc_size_of_children(&guard, malloc_size_of)
+    let malloc_size_of = MallocSizeOfFn(malloc_size_of.unwrap());
+    StylesheetContents::as_arc(&sheet).malloc_size_of_children(&guard, malloc_size_of)
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSheet_GetOrigin(
     sheet: RawServoStyleSheetContentsBorrowed
 ) -> u8 {
     let origin = match StylesheetContents::as_arc(&sheet).origin {
         Origin::UserAgent => OriginFlags_UserAgent,
@@ -1118,16 +1119,30 @@ pub extern "C" fn Servo_StyleSheet_GetSo
 fn read_locked_arc<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
     where Locked<T>: HasArcFFI, F: FnOnce(&T) -> R
 {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
     func(Locked::<T>::as_arc(&raw).read_with(&guard))
 }
 
+#[cfg(debug_assertions)]
+unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
+    where Locked<T>: HasArcFFI, F: FnOnce(&T) -> R
+{
+    read_locked_arc(raw, func)
+}
+
+#[cfg(not(debug_assertions))]
+unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
+    where Locked<T>: HasArcFFI, F: FnOnce(&T) -> R
+{
+    func(Locked::<T>::as_arc(&raw).read_unchecked())
+}
+
 fn write_locked_arc<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
     where Locked<T>: HasArcFFI, F: FnOnce(&mut T) -> R
 {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let mut guard = global_style_data.shared_lock.write();
     func(Locked::<T>::as_arc(&raw).write_with(&mut guard))
 }
 
@@ -2257,19 +2272,24 @@ macro_rules! get_property_id_from_proper
             Ok(property_id) => property_id,
             Err(_) => { return $ret; }
         }
     }}
 }
 
 fn get_property_value(declarations: RawServoDeclarationBlockBorrowed,
                       property_id: PropertyId, value: *mut nsAString) {
-    read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
-        decls.property_value_to_css(&property_id, unsafe { value.as_mut().unwrap() }).unwrap();
-    })
+    // This callsite is hot enough that the lock acquisition shows up in profiles.
+    // Using an unchecked read here improves our performance by ~10% on the
+    // microbenchmark in bug 1355599.
+    unsafe {
+        read_locked_arc_unchecked(declarations, |decls: &PropertyDeclarationBlock| {
+            decls.property_value_to_css(&property_id, value.as_mut().unwrap()).unwrap();
+        })
+    }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_GetPropertyValue(declarations: RawServoDeclarationBlockBorrowed,
                                                           property: *const nsACString, value: *mut nsAString) {
     get_property_value(declarations, get_property_id_from_property!(property, ()), value)
 }
 
@@ -3555,36 +3575,40 @@ pub extern "C" fn Servo_StyleSet_GetCoun
         let global_style_data = &*GLOBAL_STYLE_DATA;
         let guard = global_style_data.shared_lock.read();
         rule.read_with(&guard).get()
     }).unwrap_or(ptr::null_mut())
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_BuildFontFeatureValueSet(
-  raw_data: RawServoStyleSetBorrowed,
-  set: *mut gfxFontFeatureValueSet
-) -> bool {
+  raw_data: RawServoStyleSetBorrowed) -> *mut gfxFontFeatureValueSet {
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
 
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
 
+    let has_rule = data.extra_style_data
+                  .iter_origins()
+                  .any(|(d, _)| !d.font_feature_values.is_empty());
+
+    if !has_rule {
+      return ptr::null_mut();
+    }
+
     let font_feature_values_iter = data.extra_style_data
         .iter_origins_rev()
         .flat_map(|(d, _)| d.font_feature_values.iter());
 
-    let mut any_rule = false;
+    let set = unsafe { Gecko_ConstructFontFeatureValueSet() };
     for src in font_feature_values_iter {
-        any_rule = true;
         let rule = src.read_with(&guard);
         rule.set_at_rules(set);
     }
-
-    any_rule
+    set
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_ResolveForDeclarations(
     raw_data: RawServoStyleSetBorrowed,
     parent_style_context: ServoStyleContextBorrowedOrNull,
     declarations: RawServoDeclarationBlockBorrowed,
 ) -> ServoStyleContextStrong {
@@ -3605,23 +3629,25 @@ pub extern "C" fn Servo_StyleSet_Resolve
         parent_style,
         declarations.clone_arc(),
     ).into()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_AddSizeOfExcludingThis(
     malloc_size_of: GeckoMallocSizeOf,
+    malloc_enclosing_size_of: GeckoMallocSizeOf,
     sizes: *mut ServoStyleSetSizes,
     raw_data: RawServoStyleSetBorrowed
 ) {
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
-    let malloc_size_of = malloc_size_of.unwrap();
+    let malloc_size_of = MallocSizeOfFn(malloc_size_of.unwrap());
+    let malloc_enclosing_size_of = MallocEnclosingSizeOfFn(malloc_enclosing_size_of.unwrap());
     let sizes = unsafe { sizes.as_mut() }.unwrap();
-    data.malloc_add_size_of_children(malloc_size_of, sizes);
+    data.malloc_add_size_of_children(malloc_size_of, malloc_enclosing_size_of, sizes);
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_MightHaveAttributeDependency(
     raw_data: RawServoStyleSetBorrowed,
     element: RawGeckoElementBorrowed,
     local_name: *mut nsIAtom,
 ) -> bool {
@@ -3738,11 +3764,22 @@ pub extern "C" fn Servo_ProcessInvalidat
     let shared_style_context = create_shared_context(&global_style_data,
                                                      &guard,
                                                      &per_doc_data,
                                                      TraversalFlags::empty(),
                                                      unsafe { &*snapshots });
     let mut data = data.as_mut().map(|d| &mut **d);
 
     if let Some(ref mut data) = data {
-        data.invalidate_style_if_needed(element, &shared_style_context, None);
+        let result = data.invalidate_style_if_needed(element, &shared_style_context, None);
+        if result.has_invalidated_siblings() {
+            let parent = element.traversal_parent().expect("How could we invalidate siblings without a common parent?");
+            unsafe {
+                parent.set_dirty_descendants();
+                bindings::Gecko_NoteDirtySubtreeForInvalidation(parent.0);
+            }
+        } else if result.has_invalidated_descendants() {
+            unsafe { bindings::Gecko_NoteDirtySubtreeForInvalidation(element.0) };
+        } else if result.has_invalidated_self() {
+            unsafe { bindings::Gecko_NoteDirtyElement(element.0) };
+        }
     }
 }
--- a/taskcluster/ci/source-test/doc.yml
+++ b/taskcluster/ci/source-test/doc.yml
@@ -13,16 +13,17 @@ doc-generate:
             - type: file
               name: public/docs.tar.gz
               path: /builds/worker/checkouts/gecko/docs-out/main.tar.gz
     run:
         using: run-task
         command: >
             cd /builds/worker/checkouts/gecko &&
             ./mach doc --outdir docs-out --no-open --archive
+        sparse-profile: sphinx-docs
     when:
         files-changed:
             - '**/*.py'
             - '**/*.rst'
             - 'tools/docs/**'
 
 doc-upload:
     description: Generate and upload the Sphinx documentation
@@ -35,15 +36,16 @@ doc-upload:
     worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
         taskcluster-proxy: true
     run:
         using: run-task
         command: cd /builds/worker/checkouts/gecko && ./mach doc --upload --no-open
+        sparse-profile: sphinx-docs
     scopes:
         - secrets:get:project/releng/gecko/build/level-{level}/gecko-docs-upload
     when:
         files-changed:
             - '**/*.py'
             - '**/*.rst'
             - 'tools/docs/**'
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -17,42 +17,45 @@
 
 linux32/debug:
     build-platform: linux/debug
     test-sets:
         - linux32-tests
 linux32/opt:
     build-platform: linux/opt
     test-sets:
-        -  linux32-tests
-        -  linux32-opt-tests
+        - linux32-tests
+        - linux32-opt-tests
 linux32-nightly/opt:
     build-platform: linux-nightly/opt
     test-sets:
-        -  linux32-tests
-        -  linux32-opt-tests
+        - linux32-tests
+        - linux32-opt-tests
 linux32-devedition/opt:
     build-platform: linux-devedition-nightly/opt
     test-sets:
-        -  linux32-tests
-        -  linux32-opt-tests
+        - linux32-tests
+        - linux32-opt-tests
 
 linux64/debug:
     build-platform: linux64/debug
     test-sets:
         - common-tests
         - web-platform-tests
+        - reftest-stylo
 linux64/opt:
     build-platform: linux64/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - opt-only-tests
+        - reftest-stylo
         - desktop-screenshot-capture
         - talos
+        - linux-talos-stylo-disabled
         - awsy
 linux64-nightly/opt:
     build-platform: linux64-nightly/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - opt-only-tests
         - desktop-screenshot-capture
@@ -67,82 +70,78 @@ linux64-devedition/opt:
 
 # TODO: use 'pgo' and 'asan' labels here, instead of -pgo/opt
 linux64-pgo/opt:
     build-platform: linux64-pgo/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - talos
+        - linux-talos-stylo-disabled
 
 linux64-asan/opt:
     build-platform: linux64-asan/opt
     test-sets:
         - common-tests
 
-# Stylo builds only run a subset of tests for the moment. So give them
-# their own test set.
-linux32-stylo/debug:
+# Stylo disabled mode only runs a subset of tests.
+linux32-stylo-disabled/debug:
     build-platform: linux/debug
     test-sets:
-        - stylo-tests
-linux32-stylo/opt:
+        - stylo-disabled-tests
+linux32-stylo-disabled/opt:
     build-platform: linux/opt
     test-sets:
-        - awsy-stylo
-        - stylo-tests
-linux64-stylo/debug:
+        - awsy-stylo-disabled
+        - stylo-disabled-tests
+linux64-stylo-disabled/debug:
     build-platform: linux64/debug
     test-sets:
-        - stylo-tests
+        - stylo-disabled-tests
         - devtools-tests
-linux64-stylo/opt:
+linux64-stylo-disabled/opt:
     build-platform: linux64/opt
     test-sets:
-        - awsy-stylo
-        - stylo-tests
+        - awsy-stylo-disabled
+        - stylo-disabled-tests
         - devtools-tests
-        - talos
-linux64-stylo-sequential/debug:
-    build-platform: linux64/debug
+macosx64-stylo-disabled/debug:
+    build-platform: macosx64/debug
+    test-sets:
+        - stylo-disabled-tests
+macosx64-stylo-disabled/opt:
+    build-platform: macosx64/opt
+    test-sets:
+        - awsy-stylo-disabled
+        - stylo-disabled-tests
+windows7-32-stylo-disabled/debug:
+    build-platform: win32/debug
     test-sets:
-        - stylo-sequential-tests
+        - stylo-disabled-tests
+windows7-32-stylo-disabled/opt:
+    build-platform: win32/opt
+    test-sets:
+        - awsy-stylo-disabled
+        - stylo-disabled-tests
+windows10-64-stylo-disabled/debug:
+    build-platform: win64/debug
+    test-sets:
+        - stylo-disabled-tests
+windows10-64-stylo-disabled/opt:
+    build-platform: win64/opt
+    test-sets:
+        - awsy-stylo-disabled
+        - stylo-disabled-tests
+
+# Stylo sequential runs check memory and performance when using a single thread.
 linux64-stylo-sequential/opt:
     build-platform: linux64/opt
     test-sets:
         - awsy-stylo-sequential
-        - stylo-sequential-tests
         - talos
-macosx64-stylo/debug:
-    build-platform: macosx64/debug
-    test-sets:
-        - stylo-tests
-macosx64-stylo/opt:
-    build-platform: macosx64/opt
-    test-sets:
-        - awsy-stylo
-        - stylo-tests
-windows7-32-stylo/debug:
-    build-platform: win32/debug
-    test-sets:
-        - stylo-tests
-windows7-32-stylo/opt:
-    build-platform: win32/opt
-    test-sets:
-        - awsy-stylo
-        - stylo-tests
-windows10-64-stylo/debug:
-    build-platform: win64/debug
-    test-sets:
-        - stylo-tests
-windows10-64-stylo/opt:
-    build-platform: win64/opt
-    test-sets:
-        - awsy-stylo
-        - stylo-tests
 
 # QR builds just run a subset right now. Note that the tests in this
 # test set are further restricted in tests.yml to run on certain projects
 # only, to avoid adding too much infra load.
 linux64-qr/opt:
     build-platform: linux64/opt
     test-sets:
         - qr-tests
@@ -173,27 +172,27 @@ windows7-32/debug:
     test-sets:
         - windows-tests
 windows7-32/opt:
     build-platform: win32/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-talos
-        - windows-talos-stylo
+        - windows-talos-stylo-disabled
         - windows-tests
 
 windows7-32-pgo/opt:
     build-platform: win32-pgo/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-tests
         - windows-talos
-        - windows-talos-stylo
+        - windows-talos-stylo-disabled
 
 windows7-32-nightly/opt:
     build-platform: win32-nightly/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-tests
 
@@ -210,26 +209,26 @@ windows10-64/debug:
         - windows-tests
 
 windows10-64/opt:
     build-platform: win64/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-talos
-        - windows-talos-stylo
+        - windows-talos-stylo-disabled
         - windows-tests
 
 windows10-64-pgo/opt:
     build-platform: win64-pgo/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-talos
-        - windows-talos-stylo
+        - windows-talos-stylo-disabled
         - windows-tests
 
 windows10-64-nightly/opt:
     build-platform: win64-nightly/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-tests
@@ -273,18 +272,18 @@ windows8-64-devedition/opt:
 macosx64/debug:
     build-platform: macosx64/debug
     test-sets:
         - macosx64-tests
 
 macosx64/opt:
     build-platform: macosx64/opt
     test-sets:
-        - macosx64-tests-talos
-        - macosx64-tests-talos-stylo
+        - macosx64-talos
+        - macosx64-talos-stylo-disabled
         - macosx64-tests
         - desktop-screenshot-capture
         - awsy
 
 macosx64-nightly/opt:
       build-platform: macosx64-nightly/opt
       test-sets:
         - macosx64-tests
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -66,46 +66,43 @@ talos:
     - talos-svgr
     - talos-tp5o
     - talos-perf-reftest
     - talos-perf-reftest-singletons
 
 awsy:
     - awsy
 
-awsy-stylo:
-    - awsy-stylo
+awsy-stylo-disabled:
+    - awsy-stylo-disabled
 
 awsy-stylo-sequential:
     - awsy-stylo-sequential
 
 ##
 # Limited test sets for specific platforms
 
-stylo-tests:
+stylo-disabled-tests:
     - cppunit
     - crashtest
     - reftest
-    - reftest-stylo
     - web-platform-tests
     - web-platform-tests-reftests
     - mochitest
     - mochitest-a11y
     - mochitest-browser-chrome
     - mochitest-browser-screenshots
     - mochitest-chrome
     - mochitest-clipboard
     - mochitest-gpu
     - mochitest-media
     - mochitest-webgl
 
-stylo-sequential-tests:
-    - crashtest
+reftest-stylo:
     - reftest-stylo
-    - mochitest
 
 qr-talos:
     - talos-chrome
     - talos-dromaeojs
     - talos-g1
     # - talos-g2 # doesn't work with QR yet
     - talos-g3
     - talos-g4
@@ -130,16 +127,30 @@ qr-tests:
     - xpcshell
 
 jsdcov-code-coverage-tests:
     - mochitest
     - mochitest-browser-chrome
     - mochitest-devtools-chrome
     - xpcshell
 
+linux-talos-stylo-disabled:
+    - talos-chrome-stylo-disabled
+    - talos-dromaeojs-stylo-disabled
+    - talos-g1-stylo-disabled
+    - talos-g2-stylo-disabled
+    - talos-g3-stylo-disabled
+    - talos-g4-stylo-disabled
+    - talos-g5-stylo-disabled
+    - talos-other-stylo-disabled
+    - talos-svgr-stylo-disabled
+    - talos-tp5o-stylo-disabled
+    - talos-perf-reftest-stylo-disabled
+    - talos-perf-reftest-singletons-stylo-disabled
+
 windows-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
     - jsreftest
@@ -171,32 +182,32 @@ windows-talos:
     - talos-g5
     - talos-other
     - talos-perf-reftest
     - talos-perf-reftest-singletons
     - talos-svgr
     - talos-tp5o
     - talos-xperf
     - talos-tp6
+    - talos-tp6-stylo-threads
 
-windows-talos-stylo:
-    - talos-chrome-stylo
-    - talos-dromaeojs-stylo
-    - talos-g1-stylo
-    - talos-g2-stylo
-    - talos-g4-stylo
-    - talos-g5-stylo
-    - talos-other-stylo
-    - talos-perf-reftest-stylo
-    - talos-perf-reftest-singletons-stylo
-    - talos-svgr-stylo
-    - talos-tp5o-stylo
-    - talos-xperf-stylo
-    - talos-tp6-stylo
-    - talos-tp6-stylo-threads
+windows-talos-stylo-disabled:
+    - talos-chrome-stylo-disabled
+    - talos-dromaeojs-stylo-disabled
+    - talos-g1-stylo-disabled
+    - talos-g2-stylo-disabled
+    - talos-g4-stylo-disabled
+    - talos-g5-stylo-disabled
+    - talos-other-stylo-disabled
+    - talos-perf-reftest-stylo-disabled
+    - talos-perf-reftest-singletons-stylo-disabled
+    - talos-svgr-stylo-disabled
+    - talos-tp5o-stylo-disabled
+    - talos-xperf-stylo-disabled
+    - talos-tp6-stylo-disabled
 
 macosx64-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
@@ -213,44 +224,44 @@ macosx64-tests:
     - mochitest-jetpack
     - mochitest-media
     - mochitest-webgl
     - reftest
     - web-platform-tests
     - web-platform-tests-reftests
     - xpcshell
 
-macosx64-tests-talos:
+macosx64-talos:
     - talos-chrome
     - talos-dromaeojs
     - talos-g1
     - talos-g2
     - talos-g4
     - talos-g5
     - talos-other
     - talos-svgr
     - talos-tp5o
     - talos-perf-reftest
     - talos-perf-reftest-singletons
     - talos-tp6
+    - talos-tp6-stylo-threads
 
-macosx64-tests-talos-stylo:
-    - talos-chrome-stylo
-    - talos-dromaeojs-stylo
-    - talos-g1-stylo
-    - talos-g2-stylo
-    - talos-g4-stylo
-    - talos-g5-stylo
-    - talos-other-stylo
-    - talos-svgr-stylo
-    - talos-tp5o-stylo
-    - talos-perf-reftest-stylo
-    - talos-perf-reftest-singletons-stylo
-    - talos-tp6-stylo
-    - talos-tp6-stylo-threads
+macosx64-talos-stylo-disabled:
+    - talos-chrome-stylo-disabled
+    - talos-dromaeojs-stylo-disabled
+    - talos-g1-stylo-disabled
+    - talos-g2-stylo-disabled
+    - talos-g4-stylo-disabled
+    - talos-g5-stylo-disabled
+    - talos-other-stylo-disabled
+    - talos-svgr-stylo-disabled
+    - talos-tp5o-stylo-disabled
+    - talos-perf-reftest-stylo-disabled
+    - talos-perf-reftest-singletons-stylo-disabled
+    - talos-tp6-stylo-disabled
 
 linux32-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -25,20 +25,20 @@ awsy:
             by-test-platform:
                 windows.*/opt:
                     - awsy/taskcluster_windows_config.py
                 macosx.*/opt:
                     - awsy/macosx_config.py
                 default:
                     - awsy/linux_config.py
 
-awsy-stylo:
-    description: "Are we slim yet for Stylo parallel"
+awsy-stylo-disabled:
+    description: "Are we slim yet for Stylo disabled"
     suite: awsy
-    treeherder-symbol: tc-SY-stylo(sy)
+    treeherder-symbol: tc-SYsd(sy)
     max-run-time: 7200
     instance-size: xlarge
     allow-software-gl-layers: false
     run-on-projects:
         by-test-platform:
             .*-stylo.*: ['autoland', 'mozilla-central', 'try']
             .*-devedition/.*: [] # don't run on devedition
             default: built-projects
@@ -48,39 +48,38 @@ awsy-stylo:
             by-test-platform:
                 windows.*:
                     - awsy/taskcluster_windows_config.py
                 macosx.*:
                     - awsy/macosx_config.py
                 linux.*:
                     - awsy/linux_config.py
         extra-options:
-            - --enable-stylo
+            - --disable-stylo
 
 awsy-stylo-sequential:
     description: "Are we slim yet for Stylo sequential"
     suite: awsy
-    treeherder-symbol: tc-SY-stylo-seq(sy)
+    treeherder-symbol: tc-SYss(sy)
     max-run-time: 7200
     instance-size: xlarge
     allow-software-gl-layers: false
     run-on-projects:
         by-test-platform:
             .*-stylo.*: ['autoland', 'mozilla-central', 'try']
             .*-devedition/.*: [] # don't run on devedition
             default: built-projects
     mozharness:
         script: awsy_script.py
         config:
             # Only run on Linux64.
             by-test-platform:
                 linux.*:
                     - awsy/linux_config.py
         extra-options:
-            - --enable-stylo
             - --single-stylo-traversal
 
 cppunit:
     description: "CPP Unit Tests"
     suite: cppunittest
     treeherder-symbol: tc(Cpp)
     e10s: false
     run-on-projects:
@@ -455,38 +454,37 @@ mochitest:
             default: legacy # Bug 1281241: migrating to m3.large instances
     chunks:
         by-test-platform:
             android-4.3-arm7-api-16/debug: 48
             android.*: 20
             macosx.*: 5
             windows.*: 5
             linux32/debug: 16
-            linux32-stylo/debug: 16
+            linux32-stylo-disabled/debug: 16
             linux.*: 10
     e10s:
         by-test-platform:
             linux64-jsdcov/opt: false
             windows7-32/debug: both
             default: true
     max-run-time:
         by-test-platform:
             android-4.3-arm7-api-16/debug: 10800
             linux64-jsdcov/opt: 10800
             default: 5400
     allow-software-gl-layers: false
     run-on-projects:
         by-test-platform:
             linux.*-stylo.*: ['autoland', 'mozilla-central', 'try']
-            macosx64-stylo/.*: ['mozilla-central', 'try']
+            macosx64-stylo.*: ['mozilla-central', 'try']
             windows7-32-stylo.*: ['autoland', 'mozilla-central', 'try']
             default: built-projects
     tier:
         by-test-platform:
-            macosx64-stylo/.*: 2
             windows10-64-asan.*: 3
             default: default
     mozharness:
         by-test-platform:
             android.*:
                 mochitest-flavor: plain
                 script: android_emulator_unittest.py
                 no-read-buildbot-config: true
@@ -520,17 +518,17 @@ mochitest-a11y:
     suite: mochitest/a11y
     treeherder-symbol: tc-M(a11y)
     loopback-video: true
     e10s: false
     run-on-projects:
         by-test-platform:
             linux64-qr/.*: ['mozilla-central', 'try']
             linux.*-stylo.*: ['autoland', 'mozilla-central', 'try']
-            macosx64-stylo/.*: ['mozilla-central', 'try']
+            macosx64-stylo.*: ['mozilla-central', 'try']
             windows7-32-stylo.*: ['autoland', 'mozilla-central', 'try']
             default: built-projects
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: false
         mochitest-flavor: a11y
         config:
@@ -556,44 +554,44 @@ mochitest-browser-chrome:
         by-test-platform:
             linux64-jsdcov/opt: mochitest/browser-chrome-coverage
             default: mochitest/browser-chrome-chunked
     treeherder-symbol: tc-M(bc)
     loopback-video: true
     chunks:
         by-test-platform:
             linux64/debug: 16
-            linux64-stylo/debug: 16
+            linux64-stylo-disabled/debug: 16
             linux64-asan/opt: 16
             linux64-jsdcov/opt: 35
             linux32/debug: 16
-            linux32-stylo/debug: 16
+            linux32-stylo-disabled/debug: 16
             default: 7
     e10s:
         by-test-platform:
             linux64-jsdcov/opt: false
             windows7-32/debug: both
             default: true
     max-run-time:
         by-test-platform:
             linux64-jsdcov/opt: 7200
             linux64-ccov/opt: 7200
             linux64/debug: 5400
-            linux64-stylo/debug: 5400
+            linux64-stylo-disabled/debug: 5400
             linux32/debug: 5400
-            linux32-stylo/debug: 5400
+            linux32-stylo-disabled/debug: 5400
             default: 3600
     run-on-projects:
         by-test-platform:
             # Deactivate try on Buildbot, by default. We don't have enough machines
             windows8-64.*: ['mozilla-release', 'mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland']
             windows8-64-devedition/opt: built-projects  # No dev edition outside of supported branches
             windows10.*: []
             linux.*-stylo.*: ['autoland', 'mozilla-central', 'try']
-            macosx64-stylo/.*: ['mozilla-central', 'try']
+            macosx64-stylo.*: ['mozilla-central', 'try']
             windows7-32-stylo.*: ['autoland', 'mozilla-central', 'try']
             default: built-projects
     worker-type:
         by-test-platform:
             windows8-64.*: buildbot-bridge/buildbot-bridge
             default: null
     mozharness:
         mochitest-flavor: browser
@@ -675,17 +673,17 @@ mochitest-chrome:
             android.*: 2
             default: 3
     max-run-time: 3600
     e10s: false
     run-on-projects:
         by-test-platform:
             windows7-32.*: ['try']
             linux.*-stylo.*: ['autoland', 'mozilla-central', 'try']
-            macosx64-stylo/.*: ['mozilla-central', 'try']
+            macosx64-stylo.*: ['mozilla-central', 'try']
             default: built-projects
     worker-type:
         by-test-platform:
             windows7-32.*: buildbot-bridge/buildbot-bridge
             windows8-64.*: buildbot-bridge/buildbot-bridge
             default: null
     mozharness:
         by-test-platform:
@@ -716,30 +714,29 @@ mochitest-chrome:
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --mochitest-suite=chrome
     tier:
         by-test-platform:
             windows7-32.*: 2
-            macosx64-stylo/.*: 2
             default: default
 
 mochitest-clipboard:
     description: "Mochitest clipboard run"
     suite: mochitest/clipboard
     treeherder-symbol: tc-M(cl)
     loopback-video: true
     virtualization: hardware
     instance-size: xlarge
     run-on-projects:
         by-test-platform:
             linux.*-stylo.*: ['autoland', 'mozilla-central', 'try']
-            macosx64-stylo/.*: ['mozilla-central', 'try']
+            macosx64-stylo.*: ['mozilla-central', 'try']
             default: built-projects
     worker-type:
         by-test-platform:
             windows7-32.*: buildbot-bridge/buildbot-bridge
             windows8-64.*: buildbot-bridge/buildbot-bridge
             default: null
     e10s:
         by-test-platform:
@@ -768,20 +765,16 @@ mochitest-clipboard:
                         macosx.*:
                             - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --mochitest-suite=plain-clipboard,chrome-clipboard,browser-chrome-clipboard
-    tier:
-        by-test-platform:
-            macosx64-stylo/.*: 2
-            default: default
 
 mochitest-devtools-chrome:
     description: "Mochitest devtools-chrome run"
     suite:
         by-test-platform:
             linux64-jsdcov/opt: mochitest/mochitest-devtools-chrome-coverage
             default: mochitest/mochitest-devtools-chrome-chunked
     treeherder-symbol: tc-M(dt)
@@ -790,17 +783,17 @@ mochitest-devtools-chrome:
     chunks:
         by-test-platform:
             windows.*: 8
             macosx.*: 8
             default: 10
     run-on-projects:
         by-test-platform:
             linux.*-stylo.*: ['autoland', 'mozilla-central', 'try']
-            macosx64-stylo/.*: ['mozilla-central', 'try']
+            macosx64-stylo.*: ['mozilla-central', 'try']
             windows7-32-stylo.*: ['autoland', 'mozilla-central', 'try']
             default: built-projects
     e10s:
         by-test-platform:
             linux64-jsdcov/opt: false
             windows7-32/debug: both
             default: true
     mozharness:
@@ -843,17 +836,17 @@ mochitest-gpu:
     virtualization: virtual-with-gpu
     run-on-projects:
         by-test-platform:
             # Deactivate try on Buildbot, by default. We don't have enough machines
             windows8-64.*: ['mozilla-release', 'mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland']
             windows8-64-devedition/opt: built-projects  # No dev edition outside of supported branches
             windows10.*: []
             linux.*-stylo.*: ['autoland', 'mozilla-central', 'try']
-            macosx64-stylo/.*: ['mozilla-central', 'try']
+            macosx64-stylo.*: ['mozilla-central', 'try']
             windows7-32-stylo.*: ['autoland', 'mozilla-central', 'try']
             default: built-projects
     worker-type:
         by-test-platform:
             windows8-64.*: buildbot-bridge/buildbot-bridge
             default: null
     e10s:
         by-test-platform:
@@ -884,17 +877,16 @@ mochitest-gpu:
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --mochitest-suite=plain-gpu,chrome-gpu,browser-chrome-gpu
     tier:
         by-test-platform:
             linux64-qr/.*: 1
-            macosx64-stylo/.*: 2
             windows10-64-asan.*: 3
             default: default
 
 mochitest-jetpack:
     description: "Mochitest jetpack run"
     suite: mochitest/jetpack-package
     treeherder-symbol: tc-M(JP)
     loopback-video: true
@@ -939,17 +931,17 @@ mochitest-media:
             default: 3
     run-on-projects:
         by-test-platform:
             # Deactivate try on Buildbot, by default. We don't have enough machines
             windows8-64.*: ['mozilla-release', 'mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland']
             windows8-64-devedition/opt: built-projects  # No dev edition outside of supported branches
             windows10.*: []
             linux.*-stylo.*: ['autoland', 'mozilla-central', 'try']
-            macosx64-stylo/.*: ['mozilla-central', 'try']
+            macosx64-stylo.*: ['mozilla-central', 'try']
             windows7-32-stylo.*: ['autoland', 'mozilla-central', 'try']
             default: built-projects
     worker-type:
         by-test-platform:
             windows8-64.*: buildbot-bridge/buildbot-bridge
             default: null
     mozharness:
         by-test-platform:
@@ -979,17 +971,16 @@ mochitest-media:
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --mochitest-suite=mochitest-media
     tier:
         by-test-platform:
             linux64-qr/.*: 1
-            macosx64-stylo/.*: 2
             windows10-64-asan.*: 3
             default: default
 
 mochitest-valgrind:
     description: "Mochitest plain Valgrind run"
     suite: mochitest/valgrind-plain
     treeherder-symbol: tc-M-V()
     run-on-projects: []
@@ -1025,17 +1016,17 @@ mochitest-webgl:
     virtualization: virtual-with-gpu
     run-on-projects:
         by-test-platform:
             # Deactivate try on Buildbot, by default. We don't have enough machines
             windows8-64.*: ['mozilla-release', 'mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland']
             windows8-64-devedition/opt: built-projects  # No dev edition outside of supported branches
             windows10.*: []
             linux.*-stylo.*: ['autoland', 'mozilla-central', 'try']
-            macosx64-stylo/.*: ['mozilla-central', 'try']
+            macosx64-stylo.*: ['mozilla-central', 'try']
             windows7-32-stylo.*: ['autoland', 'mozilla-central', 'try']
             default: built-projects
     worker-type:
         by-test-platform:
             windows8-64.*: buildbot-bridge/buildbot-bridge
             default: null
     chunks:
         by-test-platform:
@@ -1081,17 +1072,16 @@ mochitest-webgl:
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --mochitest-suite=mochitest-gl
     tier:
         by-test-platform:
             linux64-qr/.*: 1
-            macosx64-stylo/.*: 2
             windows10-64-asan.*: 3
             default: default
 
 reftest:
     description: "Reftest run"
     suite: reftest/reftest
     treeherder-symbol: tc-R(R)
     run-on-projects:
@@ -1245,26 +1235,22 @@ reftest-no-accel:
         extra-options:
             - --reftest-suite=reftest-no-accel
     tier:
         by-test-platform:
             windows10-64-asan.*: 3
             default: default
 
 reftest-stylo:
-    description: "Reftest run for Stylo"
+    description: "Reftest run in Stylo vs. Gecko mode"
     suite: reftest/reftest-stylo
     treeherder-symbol: tc-R(Rs)
     virtualization: virtual-with-gpu
     chunks: 8
-    run-on-projects:
-        by-test-platform:
-            windows10.*: []
-            macosx.*: []
-            default: ['autoland', 'mozilla-central', 'try']
+    run-on-projects: ['autoland', 'mozilla-central', 'try']
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
                 macosx.*:
@@ -1318,41 +1304,37 @@ talos-chrome:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=chromez
             - --add-option
             - --webServer,localhost
 
-talos-chrome-stylo:
-    description: "Talos Stylo chrome"
+talos-chrome-stylo-disabled:
+    description: "Talos Stylo disabled chrome"
     suite: talos
-    try-name: chromez-stylo
-    treeherder-symbol: tc-Ts(c)
+    try-name: chromez-stylo-disabled
+    treeherder-symbol: tc-Tsd(c)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=chromez-stylo
+            - --suite=chromez-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 talos-dromaeojs:
     description: "Talos dromaeojs"
     suite: talos
     try-name: dromaeojs
     treeherder-symbol: tc-T(d)
@@ -1374,41 +1356,37 @@ talos-dromaeojs:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=dromaeojs
             - --add-option
             - --webServer,localhost
 
-talos-dromaeojs-stylo:
-    description: "Talos Stylo dromaeojs"
+talos-dromaeojs-stylo-disabled:
+    description: "Talos Stylo disabled dromaeojs"
     suite: talos
-    try-name: dromaeojs-stylo
-    treeherder-symbol: tc-Ts(d)
+    try-name: dromaeojs-stylo-disabled
+    treeherder-symbol: tc-Tsd(d)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=dromaeojs-stylo
+            - --suite=dromaeojs-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 talos-g1:
     description: "Talos g1"
     suite: talos
     try-name: g1
     treeherder-symbol: tc-T(g1)
@@ -1430,41 +1408,37 @@ talos-g1:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=g1
             - --add-option
             - --webServer,localhost
 
-talos-g1-stylo:
-    description: "Talos Stylo g1"
+talos-g1-stylo-disabled:
+    description: "Talos Stylo disabled g1"
     suite: talos
-    try-name: g1-stylo
-    treeherder-symbol: tc-Ts(g1)
+    try-name: g1-stylo-disabled
+    treeherder-symbol: tc-Tsd(g1)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 7200
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=g1-stylo
+            - --suite=g1-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 talos-g2:
     description: "Talos g2"
     suite: talos
     try-name: g2
     treeherder-symbol: tc-T(g2)
@@ -1486,41 +1460,37 @@ talos-g2:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=g2
             - --add-option
             - --webServer,localhost
 
-talos-g2-stylo:
-    description: "Talos Stylo g2"
+talos-g2-stylo-disabled:
+    description: "Talos Stylo disabled g2"
     suite: talos
-    try-name: g2-stylo
-    treeherder-symbol: tc-Ts(g2)
+    try-name: g2-stylo-disabled
+    treeherder-symbol: tc-Tsd(g2)
     virtualization: hardware
     max-run-time: 7200
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=g2-stylo
+            - --suite=g2-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 talos-g3:
     description: "Talos g3"
     suite: talos
     try-name: g3
     treeherder-symbol: tc-T(g3)
@@ -1542,16 +1512,40 @@ talos-g3:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=g3
             - --add-option
             - --webServer,localhost
 
+talos-g3-stylo-disabled:
+    description: "Talos Stylo disabled g3"
+    suite: talos
+    try-name: g3-stylo-disabled
+    treeherder-symbol: tc-Tsd(g3)
+    virtualization: hardware
+    run-on-projects: ['mozilla-central', 'try']
+    max-run-time: 3600
+    mozharness:
+        script: talos_script.py
+        no-read-buildbot-config: true
+        config:
+            by-test-platform:
+                macosx.*:
+                    - talos/mac_config.py
+                windows.*:
+                    - talos/windows_config.py
+                default:
+                    - talos/linux_config.py
+        extra-options:
+            - --suite=g3-stylo-disabled
+            - --add-option
+            - --webServer,localhost
+
 talos-g4:
     description: "Talos g4"
     suite: talos
     try-name: g4
     treeherder-symbol: tc-T(g4)
     virtualization: hardware
     run-on-projects:
         by-test-platform:
@@ -1570,41 +1564,37 @@ talos-g4:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=g4
             - --add-option
             - --webServer,localhost
 
-talos-g4-stylo:
-    description: "Talos Stylo g4"
+talos-g4-stylo-disabled:
+    description: "Talos Stylo disabled g4"
     suite: talos
-    try-name: g4-stylo
-    treeherder-symbol: tc-Ts(g4)
+    try-name: g4-stylo-disabled
+    treeherder-symbol: tc-Tsd(g4)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=g4-stylo
+            - --suite=g4-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 talos-g5:
     description: "Talos g5"
     suite: talos
     try-name: g5
     treeherder-symbol: tc-T(g5)
@@ -1627,42 +1617,38 @@ talos-g5:
                 default:
                     - talos/linux_config.py
                     - remove_executables.py
         extra-options:
             - --suite=g5
             - --add-option
             - --webServer,localhost
 
-talos-g5-stylo:
-    description: "Talos Stylo g5"
+talos-g5-stylo-disabled:
+    description: "Talos Stylo disabled g5"
     suite: talos
-    try-name: g5-stylo
-    treeherder-symbol: tc-Ts(g5)
+    try-name: g5-stylo-disabled
+    treeherder-symbol: tc-Tsd(g5)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
                     - remove_executables.py
         extra-options:
-            - --suite=g5-stylo
+            - --suite=g5-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 talos-other:
     description: "Talos other"
     suite: talos
     try-name: other
     treeherder-symbol: tc-T(o)
@@ -1684,41 +1670,37 @@ talos-other:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=other
             - --add-option
             - --webServer,localhost
 
-talos-other-stylo:
-    description: "Talos Stylo other"
+talos-other-stylo-disabled:
+    description: "Talos Stylo disabled other"
     suite: talos
-    try-name: other-stylo
-    treeherder-symbol: tc-Ts(o)
+    try-name: other-stylo-disabled
+    treeherder-symbol: tc-Tsd(o)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=other-stylo
+            - --suite=other-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 talos-perf-reftest:
     description: "Talos perf-reftest"
     suite: talos
     try-name: perf-reftest
     treeherder-symbol: tc-T(p)
@@ -1762,65 +1744,57 @@ talos-perf-reftest-singletons:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=perf-reftest-singletons
 
-talos-perf-reftest-singletons-stylo:
-    description: "Talos Stylo perf-reftest singletons"
+talos-perf-reftest-singletons-stylo-disabled:
+    description: "Talos Stylo disabled perf-reftest singletons"
     suite: talos
-    try-name: perf-reftest-singletons-stylo
-    treeherder-symbol: tc-Ts(ps)
+    try-name: perf-reftest-singletons-stylo-disabled
+    treeherder-symbol: tc-Tsd(ps)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=perf-reftest-singletons-stylo
+            - --suite=perf-reftest-singletons-stylo-disabled
 
-talos-perf-reftest-stylo:
-    description: "Talos Stylo perf-reftest"
+talos-perf-reftest-stylo-disabled:
+    description: "Talos Stylo disabled perf-reftest"
     suite: talos
-    try-name: perf-reftest-stylo
-    treeherder-symbol: tc-Ts(p)
+    try-name: perf-reftest-stylo-disabled
+    treeherder-symbol: tc-Tsd(p)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=perf-reftest-stylo
+            - --suite=perf-reftest-stylo-disabled
 
 talos-svgr:
     description: "Talos svgr"
     suite: talos
     try-name: svgr
     treeherder-symbol: tc-T(s)
     virtualization: hardware
     run-on-projects:
@@ -1840,41 +1814,37 @@ talos-svgr:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=svgr
             - --add-option
             - --webServer,localhost
 
-talos-svgr-stylo:
-    description: "Talos Stylo svgr"
+talos-svgr-stylo-disabled:
+    description: "Talos Stylo disabled svgr"
     suite: talos
-    try-name: svgr-stylo
-    treeherder-symbol: tc-Ts(s)
+    try-name: svgr-stylo-disabled
+    treeherder-symbol: tc-Tsd(s)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=svgr-stylo
+            - --suite=svgr-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 talos-tp5o:
     description: "Talos tp5o"
     suite: talos
     try-name: tp5o
     treeherder-symbol: tc-T(tp)
@@ -1896,41 +1866,37 @@ talos-tp5o:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=tp5o
             - --add-option
             - --webServer,localhost
 
-talos-tp5o-stylo:
-    description: "Talos Stylo tp5o"
+talos-tp5o-stylo-disabled:
+    description: "Talos Stylo disabled tp5o"
     suite: talos
-    try-name: tp5o-stylo
-    treeherder-symbol: tc-Ts(tp)
+    try-name: tp5o-stylo-disabled
+    treeherder-symbol: tc-Tsd(tp)
     virtualization: hardware
-    run-on-projects:
-        by-test-platform:
-            windows.*: ['mozilla-central', 'try']
-            macosx.*: ['mozilla-central', 'try']
-            default: []
+    run-on-projects: ['mozilla-central', 'try']
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=tp5o-stylo
+            - --suite=tp5o-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 talos-tp6:
     description: "Talos Tp6"
     suite: talos
     try-name: tp6
     treeherder-symbol: tc-T(tp6)
@@ -1951,21 +1917,21 @@ talos-tp6:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=tp6
             - --add-option
             - --webServer,localhost
 
-talos-tp6-stylo:
-    description: "Talos Stylo Tp6"
+talos-tp6-stylo-disabled:
+    description: "Talos Stylo disabled Tp6"
     suite: talos
-    try-name: tp6-stylo
-    treeherder-symbol: tc-Ts(tp6s)
+    try-name: tp6-stylo-disabled
+    treeherder-symbol: tc-Tsd(tp6)
     virtualization: hardware
     run-on-projects:
         by-test-platform:
             windows.*: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
             macosx.*: ['mozilla-beta', 'mozilla-central', 'try']
             default: []
     max-run-time: 3600
     mozharness:
@@ -1975,23 +1941,23 @@ talos-tp6-stylo:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=tp6-stylo
+            - --suite=tp6-stylo-disabled
 
 talos-tp6-stylo-threads:
-    description: "Talos Stylo Threads Tp6"
+    description: "Talos Stylo sequential Tp6"
     suite: talos
     try-name: tp6-stylo-threads
-    treeherder-symbol: tc-Ts(tp6st)
+    treeherder-symbol: tc-Tss(tp6)
     virtualization: hardware
     run-on-projects:
         by-test-platform:
             windows.*: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
             macosx.*: ['mozilla-beta', 'mozilla-central', 'try']
             default: []
     max-run-time: 3600
     mozharness:
@@ -2030,21 +1996,21 @@ talos-xperf:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=xperf
             - --add-option
             - --webServer,localhost
 
-talos-xperf-stylo:
-    description: "Talos Stylo xperf"
+talos-xperf-stylo-disabled:
+    description: "Talos Stylo disabled xperf"
     suite: talos
-    try-name: xperf-stylo
-    treeherder-symbol: tc-Ts(x)
+    try-name: xperf-stylo-disabled
+    treeherder-symbol: tc-Tsd(x)
     run-on-projects:
         by-test-platform:
             windows7-32.*: ['mozilla-central', 'try']
             default: []
     max-run-time: 3600
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
@@ -2052,17 +2018,17 @@ talos-xperf-stylo:
             by-test-platform:
                 macosx.*:
                     - talos/mac_config.py
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
-            - --suite=xperf-stylo
+            - --suite=xperf-stylo-disabled
             - --add-option
             - --webServer,localhost
 
 telemetry-tests-client:
     description: "Telemetry tests client run"
     suite: telemetry-tests-client
     treeherder-symbol: tc-e10s
     max-run-time: 5400
--- a/taskcluster/taskgraph/action.yml
+++ b/taskcluster/taskgraph/action.yml
@@ -23,46 +23,46 @@ routes:
   - "tc-treeherder-stage.v2.{{project}}.{{head_rev}}.{{pushlog_id}}"
 
 payload:
   env:
     GECKO_BASE_REPOSITORY: 'https://hg.mozilla.org/mozilla-unified'
     GECKO_HEAD_REPOSITORY: '{{{head_repository}}}'
     GECKO_HEAD_REF: '{{head_ref}}'
     GECKO_HEAD_REV: '{{head_rev}}'
-    HG_STORE_PATH: /home/worker/checkouts/hg-store
+    HG_STORE_PATH: /builds/worker/checkouts/hg-store
 
   cache:
-    level-{{level}}-checkouts: /home/worker/checkouts
+    level-{{level}}-checkouts: /builds/worker/checkouts
 
   features:
     taskclusterProxy: true
 
   # Note: This task is built server side without the context or tooling that
   # exist in tree so we must hard code the version
   image: 'taskcluster/decision:2.0.0@sha256:4039fd878e5700b326d4a636e28c595c053fbcb53909c1db84ad1f513cf644ef'
 
   # Virtually no network or other potentially risky operations happen as part
   # of the task timeout aside from the initial clone. We intentionally have
   # set this to a lower value _all_ decision tasks should use a root
   # repository which is cached.
   maxRunTime: 1800
 
   command:
-    - /home/worker/bin/run-task
-    - '--vcs-checkout=/home/worker/checkouts/gecko'
+    - /builds/worker/bin/run-task
+    - '--vcs-checkout=/builds/worker/checkouts/gecko'
     - '--'
     - bash
     - -cx
     - >
-        cd /home/worker/checkouts/gecko &&
-        ln -s /home/worker/artifacts artifacts &&
+        cd /builds/worker/checkouts/gecko &&
+        ln -s /builds/worker/artifacts artifacts &&
         ./mach --log-no-times taskgraph {{action}} {{action_args}}
 
   artifacts:
     'public':
       type: 'directory'
-      path: '/home/worker/artifacts'
+      path: '/builds/worker/artifacts'
       expires: '{{#from_now}}7 days{{/from_now}}'
 
 extra:
   treeherder:
     symbol: A
--- a/taskcluster/taskgraph/transforms/job/mozharness_test.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py
@@ -21,36 +21,36 @@ import os
 BUILDER_NAME_PREFIX = {
     'linux64-pgo': 'Ubuntu VM 12.04 x64',
     'linux64': 'Ubuntu VM 12.04 x64',
     'linux64-nightly': 'Ubuntu VM 12.04 x64',
     'linux64-asan': 'Ubuntu ASAN VM 12.04 x64',
     'linux64-ccov': 'Ubuntu Code Coverage VM 12.04 x64',
     'linux64-jsdcov': 'Ubuntu Code Coverage VM 12.04 x64',
     'linux64-qr': 'Ubuntu VM 12.04 x64',
-    'linux64-stylo': 'Ubuntu VM 12.04 x64',
+    'linux64-stylo-disabled': 'Ubuntu VM 12.04 x64',
     'linux64-stylo-sequential': 'Ubuntu VM 12.04 x64',
     'linux64-devedition': 'Ubuntu VM 12.04 x64',
     'linux64-devedition-nightly': 'Ubuntu VM 12.04 x64',
     'macosx64': 'Rev7 MacOSX Yosemite 10.10.5',
     'macosx64-devedition': 'Rev7 MacOSX Yosemite 10.10.5 DevEdition',
     'android-4.3-arm7-api-16': 'Android 4.3 armv7 api-16+',
     'android-4.2-x86': 'Android 4.2 x86 Emulator',
     'android-4.3-arm7-api-16-gradle': 'Android 4.3 armv7 api-16+',
     'windows10-64': 'Windows 10 64-bit',
     'windows10-64-nightly': 'Windows 10 64-bit',
     'windows10-64-pgo': 'Windows 10 64-bit',
     'windows10-64-asan': 'Windows 10 64-bit',
-    'windows10-64-stylo': 'Windows 10 64-bit',
+    'windows10-64-stylo-disabled': 'Windows 10 64-bit',
     'windows7-32': 'Windows 7 32-bit',
     ('windows7-32', 'virtual-with-gpu'): 'Windows 7 VM-GFX 32-bit',
     'windows7-32-nightly': 'Windows 7 32-bit',
     'windows7-32-devedition': 'Windows 7 32-bit DevEdition',
     'windows7-32-pgo': 'Windows 7 32-bit',
-    'windows7-32-stylo': 'Windows 7 32-bit',
+    'windows7-32-stylo-disabled': 'Windows 7 32-bit',
     'windows8-64': 'Windows 8 64-bit',
     'windows8-64-nightly': 'Windows 8 64-bit',
     'windows8-64-devedition': 'Windows 8 64-bit DevEdition',
     'windows8-64-pgo': 'Windows 8 64-bit',
 }
 
 VARIANTS = [
     'nightly',
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -471,24 +471,26 @@ GROUP_NAMES = {
     'tc-Fxfn-r': 'Firefox functional tests (remote) executed by TaskCluster',
     'tc-Fxfn-r-e10s': 'Firefox functional tests (remote) executed by TaskCluster with e10s',
     'tc-M': 'Mochitests executed by TaskCluster',
     'tc-M-e10s': 'Mochitests executed by TaskCluster with e10s',
     'tc-M-V': 'Mochitests on Valgrind executed by TaskCluster',
     'tc-R': 'Reftests executed by TaskCluster',
     'tc-R-e10s': 'Reftests executed by TaskCluster with e10s',
     'tc-T': 'Talos performance tests executed by TaskCluster',
-    'tc-Ts': 'Talos Stylo performance tests executed by TaskCluster',
+    'tc-Tsd': 'Talos performance tests executed by TaskCluster with Stylo disabled',
+    'tc-Tss': 'Talos performance tests executed by TaskCluster with Stylo sequential',
     'tc-T-e10s': 'Talos performance tests executed by TaskCluster with e10s',
-    'tc-Ts-e10s': 'Talos Stylo performance tests executed by TaskCluster with e10s',
+    'tc-Tsd-e10s': 'Talos performance tests executed by TaskCluster with e10s, Stylo disabled',
+    'tc-Tss-e10s': 'Talos performance tests executed by TaskCluster with e10s, Stylo sequential',
     'tc-tt-c': 'Telemetry client marionette tests',
     'tc-tt-c-e10s': 'Telemetry client marionette tests with e10s',
     'tc-SY-e10s': 'Are we slim yet tests by TaskCluster with e10s',
-    'tc-SY-stylo-e10s': 'Are we slim yet tests by TaskCluster with e10s, stylo',
-    'tc-SY-stylo-seq-e10s': 'Are we slim yet tests by TaskCluster with e10s, stylo sequential',
+    'tc-SYsd-e10s': 'Are we slim yet tests by TaskCluster with e10s, Stylo disabled',
+    'tc-SYss-e10s': 'Are we slim yet tests by TaskCluster with e10s, Stylo sequential',
     'tc-VP': 'VideoPuppeteer tests executed by TaskCluster',
     'tc-W': 'Web platform tests executed by TaskCluster',
     'tc-W-e10s': 'Web platform tests executed by TaskCluster with e10s',
     'tc-X': 'Xpcshell tests executed by TaskCluster',
     'tc-X-e10s': 'Xpcshell tests executed by TaskCluster with e10s',
     'tc-L10n': 'Localised Repacks executed by Taskcluster',
     'tc-L10n-Rpk': 'Localized Repackaged Repacks executed by Taskcluster',
     'tc-BM-L10n': 'Beetmover for locales executed by Taskcluster',
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -61,17 +61,17 @@ WINDOWS_WORKER_TYPES = {
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
       'hardware': 'releng-hardware/gecko-t-win7-32-hw',
     },
     'windows7-32-devedition': {
       'virtual': 'aws-provisioner-v1/gecko-t-win7-32',
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
       'hardware': 'releng-hardware/gecko-t-win7-32-hw',
     },
-    'windows7-32-stylo': {
+    'windows7-32-stylo-disabled': {
       'virtual': 'aws-provisioner-v1/gecko-t-win7-32',
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
       'hardware': 'releng-hardware/gecko-t-win7-32-hw',
     },
     'windows10-64': {
       'virtual': 'aws-provisioner-v1/gecko-t-win10-64',
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
       'hardware': 'releng-hardware/gecko-t-win10-64-hw',
@@ -86,17 +86,17 @@ WINDOWS_WORKER_TYPES = {
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
       'hardware': 'releng-hardware/gecko-t-win10-64-hw',
     },
     'windows10-64-nightly': {
       'virtual': 'aws-provisioner-v1/gecko-t-win10-64',
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
       'hardware': 'releng-hardware/gecko-t-win10-64-hw',
     },
-    'windows10-64-stylo': {
+    'windows10-64-stylo-disabled': {
       'virtual': 'aws-provisioner-v1/gecko-t-win10-64',
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
       'hardware': 'releng-hardware/gecko-t-win10-64-hw',
     },
     'windows10-64-asan': {
       'virtual': 'aws-provisioner-v1/gecko-t-win10-64',
       'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
       'hardware': 'releng-hardware/gecko-t-win10-64-hw',
@@ -501,17 +501,17 @@ def set_treeherder_machine_platform(conf
     for test in tests:
         # For most desktop platforms, the above table is not used for "regular"
         # builds, so we'll always pick the test platform here.
         # On macOS though, the regular builds are in the table.  This causes a
         # conflict in `verify_task_graph_symbol` once you add a new test
         # platform based on regular macOS builds, such as for Stylo.
         # Since it's unclear if the regular macOS builds can be removed from
         # the table, workaround the issue for Stylo.
-        if '-stylo' in test['test-platform']:
+        if '-stylo-disabled' in test['test-platform']:
             test['treeherder-machine-platform'] = test['test-platform']
         else:
             test['treeherder-machine-platform'] = translation.get(
                 test['build-platform'], test['test-platform'])
         yield test
 
 
 @transforms.add
@@ -523,34 +523,44 @@ def set_tier(config, tests):
             resolve_keyed_by(test, 'tier', item_name=test['test-name'])
 
         # only override if not set for the test
         if 'tier' not in test or test['tier'] == 'default':
             if test['test-platform'] in ['linux32/opt',
                                          'linux32/debug',
                                          'linux32-nightly/opt',
                                          'linux32-devedition/opt',
+                                         'linux32-stylo-disabled/debug',
+                                         'linux32-stylo-disabled/opt',
                                          'linux64/opt',
                                          'linux64-nightly/opt',
                                          'linux64/debug',
                                          'linux64-pgo/opt',
                                          'linux64-devedition/opt',
                                          'linux64-asan/opt',
+                                         'linux64-stylo-disabled/debug',
+                                         'linux64-stylo-disabled/opt',
                                          'windows7-32/debug',
                                          'windows7-32/opt',
                                          'windows7-32-pgo/opt',
                                          'windows7-32-devedition/opt',
                                          'windows7-32-nightly/opt',
+                                         'windows7-32-stylo-disabled/debug',
+                                         'windows7-32-stylo-disabled/opt',
                                          'windows10-64/debug',
                                          'windows10-64/opt',
                                          'windows10-64-pgo/opt',
                                          'windows10-64-devedition/opt',
                                          'windows10-64-nightly/opt',
+                                         'windows10-64-stylo-disabled/debug',
+                                         'windows10-64-stylo-disabled/opt',
                                          'macosx64/opt',
                                          'macosx64/debug',
+                                         'macosx64-stylo-disabled/debug',
+                                         'macosx64-stylo-disabled/opt',
                                          'android-4.3-arm7-api-16/opt',
                                          'android-4.3-arm7-api-16/debug',
                                          'android-4.2-x86/opt']:
                 test['tier'] = 1
             else:
                 test['tier'] = 2
         yield test
 
@@ -765,28 +775,26 @@ def set_test_type(config, tests):
     for test in tests:
         for test_type in ['mochitest', 'reftest']:
             if test_type in test['suite'] and 'web-platform' not in test['suite']:
                 test.setdefault('tags', {})['test-type'] = test_type
         yield test
 
 
 @transforms.add
-def enable_stylo(config, tests):
+def disable_stylo(config, tests):
     """
-    Force Stylo on for all its tests, except Stylo vs. Gecko reftests where the
-    test harness will handle this.
+    Disable Stylo for all jobs on `-stylo-disabled` platforms.
     """
     for test in tests:
-        if '-stylo' not in test['test-platform']:
+        if '-stylo-disabled' not in test['test-platform']:
             yield test
             continue
 
-        if 'reftest-stylo' not in test['suite']:
-