Merge mozilla-inbound to mozilla-central. a=merge
authorDorel Luca <dluca@mozilla.com>
Tue, 21 Aug 2018 12:54:24 +0300
changeset 432559 a955df76e2b6
parent 432558 6cc53c0f6efe (current diff)
parent 432525 6c3db80981da (diff)
child 432560 88803cf0dec1
child 432603 c3b06d8fd001
push id106777
push userdluca@mozilla.com
push dateTue, 21 Aug 2018 10:00:27 +0000
treeherdermozilla-inbound@88803cf0dec1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
a955df76e2b6 / 63.0a1 / 20180821100053 / files
nightly linux64
a955df76e2b6 / 63.0a1 / 20180821100053 / files
nightly mac
a955df76e2b6 / 63.0a1 / 20180821100053 / files
nightly win32
a955df76e2b6 / 63.0a1 / 20180821100053 / files
nightly win64
a955df76e2b6 / 63.0a1 / 20180821100053 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
layout/reftests/bugs/reftest.list
testing/xpcshell/example/unit/import_sub_module.jsm
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -269,16 +269,36 @@ DocAccessibleChild::SendRoleChangedEvent
     return PDocAccessibleChild::SendRoleChangedEvent(aRole);
   }
 
   PushDeferredEvent(MakeUnique<SerializedRoleChanged>(this, aRole));
   return true;
 }
 
 bool
+DocAccessibleChild::SendScrollingEvent(const uint64_t& aID,
+                                       const uint64_t& aType,
+                                       const uint32_t& aScrollX,
+                                       const uint32_t& aScrollY,
+                                       const uint32_t& aMaxScrollX,
+                                       const uint32_t& aMaxScrollY)
+{
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendScrollingEvent(aID, aType,
+                                                   aScrollX, aScrollY,
+                                                   aMaxScrollX, aMaxScrollY);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedScrolling>(this, aID, aType,
+                                                    aScrollX, aScrollY,
+                                                    aMaxScrollX, aMaxScrollY));
+  return true;
+}
+
+bool
 DocAccessibleChild::ConstructChildDocInParentProcess(
                                         DocAccessibleChild* aNewChildDoc,
                                         uint64_t aUniqueID, uint32_t aMsaaID)
 {
   if (IsConstructedInParentProcess()) {
     // We may send the constructor immediately
     auto tabChild = static_cast<dom::TabChild*>(Manager());
     MOZ_ASSERT(tabChild);
--- a/accessible/ipc/win/DocAccessibleChild.h
+++ b/accessible/ipc/win/DocAccessibleChild.h
@@ -53,16 +53,20 @@ public:
                       const LayoutDeviceIntRect& aCaretRect);
   bool SendTextChangeEvent(const uint64_t& aID, const nsString& aStr,
                            const int32_t& aStart, const uint32_t& aLen,
                            const bool& aIsInsert, const bool& aFromUser,
                            const bool aDoSyncCheck = true);
   bool SendSelectionEvent(const uint64_t& aID, const uint64_t& aWidgetID,
                           const uint32_t& aType);
   bool SendRoleChangedEvent(const a11y::role& aRole);
+  bool SendScrollingEvent(const uint64_t& aID, const uint64_t& aType,
+                          const uint32_t& aScrollX, const uint32_t& aScrollY,
+                          const uint32_t& aMaxScrollX,
+                          const uint32_t& aMaxScrollY);
 
   bool ConstructChildDocInParentProcess(DocAccessibleChild* aNewChildDoc,
                                         uint64_t aUniqueID, uint32_t aMsaaID);
 
   bool SendBindChildDoc(DocAccessibleChild* aChildDoc,
                         const uint64_t& aNewParentID);
 
 protected:
@@ -264,16 +268,45 @@ private:
     void Dispatch(DocAccessibleChild* aIPCDoc) override
     {
       Unused << aIPCDoc->SendRoleChangedEvent(mRole);
     }
 
     a11y::role mRole;
   };
 
+  struct SerializedScrolling final : public DeferredEvent
+  {
+    explicit SerializedScrolling(DocAccessibleChild* aTarget,
+                                 uint64_t aID, uint64_t aType,
+                                 uint32_t aScrollX, uint32_t aScrollY,
+                                 uint32_t aMaxScrollX, uint32_t aMaxScrollY)
+      : DeferredEvent(aTarget)
+      , mID(aID)
+      , mType(aType)
+      , mScrollX(aScrollX)
+      , mScrollY(aScrollY)
+      , mMaxScrollX(aMaxScrollX)
+      , mMaxScrollY(aMaxScrollY)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendScrollingEvent(mID, mType, mScrollX, mScrollY,
+                                            mMaxScrollX, mMaxScrollY);
+    }
+
+    uint64_t mID;
+    uint64_t mType;
+    uint32_t mScrollX;
+    uint32_t mScrollY;
+    uint32_t mMaxScrollX;
+    uint32_t mMaxScrollY;
+  };
+
   struct SerializedEvent final : public DeferredEvent
   {
     SerializedEvent(DocAccessibleChild* aTarget, uint64_t aID, uint32_t aType)
       : DeferredEvent(aTarget)
       , mID(aID)
       , mType(aType)
     {}
 
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -770,42 +770,48 @@ window._gBrowser = {
       ).URI;
       return resolvedURI.schemeIs("jar") || resolvedURI.schemeIs("file");
     } catch (ex) {
       // aURI might be invalid.
       return false;
     }
   },
 
-  setIcon(aTab, aIconURL = "", aOriginalURL = aIconURL) {
+  setIcon(aTab, aIconURL = "", aOriginalURL = aIconURL, aLoadingPrincipal = null) {
     let makeString = (url) => url instanceof Ci.nsIURI ? url.spec : url;
 
     aIconURL = makeString(aIconURL);
     aOriginalURL = makeString(aOriginalURL);
 
     let LOCAL_PROTOCOLS = [
       "chrome:",
       "about:",
       "resource:",
       "data:",
     ];
 
-    if (aIconURL && !LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol))) {
-      console.error(`Attempt to set a remote URL ${aIconURL} as a tab icon.`);
+    if (aIconURL && !aLoadingPrincipal && !LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol))) {
+      console.error(`Attempt to set a remote URL ${aIconURL} as a tab icon without a loading principal.`);
       return;
     }
 
     let browser = this.getBrowserForTab(aTab);
     browser.mIconURL = aIconURL;
 
     if (aIconURL != aTab.getAttribute("image")) {
       if (aIconURL) {
+        if (aLoadingPrincipal) {
+          aTab.setAttribute("iconloadingprincipal", aLoadingPrincipal);
+        } else {
+          aTab.removeAttribute("iconloadingprincipal");
+        }
         aTab.setAttribute("image", aIconURL);
       } else {
         aTab.removeAttribute("image");
+        aTab.removeAttribute("iconloadingprincipal");
       }
       this._tabAttrModified(aTab, ["image"]);
     }
 
     // The aOriginalURL argument is currently only used by tests.
     this._callProgressListeners(browser, "onLinkIconAvailable", [aIconURL, aOriginalURL]);
   },
 
--- a/browser/base/content/test/chrome/test_aboutRestartRequired.xul
+++ b/browser/base/content/test/chrome/test_aboutRestartRequired.xul
@@ -8,17 +8,16 @@
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
   <iframe type="content" id="frame1"/>
   <iframe type="content" id="frame2" onload="doTest()"/>
   <script type="application/javascript"><![CDATA[
     ChromeUtils.import("resource://gre/modules/Services.jsm");
-    ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
     SimpleTest.waitForExplicitFinish();
 
     // Load error pages do not fire "load" events, so let's use a progressListener.
     function waitForErrorPage(frame) {
       return new Promise(resolve => {
         let progressListener = {
           onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
@@ -27,18 +26,18 @@
                             .getInterface(Ci.nsIWebProgress)
                             .removeProgressListener(progressListener,
                                                     Ci.nsIWebProgress.NOTIFY_LOCATION);
 
               resolve();
             }
           },
 
-          QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                                 Ci.nsISupportsWeakReference])
+          QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                                  Ci.nsISupportsWeakReference])
         };
 
         frame.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIWebProgress)
                       .addProgressListener(progressListener,
                                            Ci.nsIWebProgress.NOTIFY_LOCATION);
       });
     }
--- a/browser/components/payments/content/paymentDialogFrameScript.js
+++ b/browser/components/payments/content/paymentDialogFrameScript.js
@@ -71,18 +71,18 @@ let PaymentFrameScript = {
    * Expose privileged utility functions to the unprivileged page.
    */
   exposeUtilityFunctions() {
     let waivedContent = Cu.waiveXrays(content);
     let PaymentDialogUtils = {
       DEFAULT_REGION: FormAutofill.DEFAULT_REGION,
       supportedCountries: FormAutofill.supportedCountries,
 
-      getAddressLabel(address) {
-        return FormAutofillUtils.getAddressLabel(address);
+      getAddressLabel(address, addressFields = null) {
+        return FormAutofillUtils.getAddressLabel(address, addressFields);
       },
 
       isCCNumber(value) {
         return FormAutofillUtils.isCCNumber(value);
       },
 
       getFormFormat(country) {
         let format = FormAutofillUtils.getFormFormat(country);
--- a/browser/components/payments/res/components/address-option.js
+++ b/browser/components/payments/res/components/address-option.js
@@ -52,18 +52,18 @@ export default class AddressOption exten
 
   connectedCallback() {
     for (let name of ["name", "street-address", "email", "tel"]) {
       this.appendChild(this[`_${name}`]);
     }
     super.connectedCallback();
   }
 
-  static formatSingleLineLabel(address) {
-    return PaymentDialogUtils.getAddressLabel(address);
+  static formatSingleLineLabel(address, addressFields) {
+    return PaymentDialogUtils.getAddressLabel(address, addressFields);
   }
 
   render() {
     // Fall back to empty strings to prevent 'null' from appearing.
     this._name.textContent = this.name || "";
     this["_street-address"].textContent =
       `${this.streetAddress || ""} ${this.addressLevel2 || ""} ` +
       `${this.addressLevel1 || ""} ${this.postalCode || ""} ${this.country || ""}`;
--- a/browser/components/payments/res/containers/address-picker.js
+++ b/browser/components/payments/res/containers/address-picker.js
@@ -95,17 +95,20 @@ export default class AddressPicker exten
         let val = address[key];
         if (val) {
           optionEl.setAttribute(key, val);
         } else {
           optionEl.removeAttribute(key);
         }
       }
 
-      optionEl.textContent = AddressOption.formatSingleLineLabel(address);
+      // fieldNames getter is not used here because it returns a default array with
+      // attributes even when "address-fields" observed attribute is null.
+      let addressFields = this.getAttribute("address-fields");
+      optionEl.textContent = AddressOption.formatSingleLineLabel(address, addressFields);
       desiredOptions.push(optionEl);
     }
 
     this.dropdown.popupBox.textContent = "";
     for (let option of desiredOptions) {
       this.dropdown.popupBox.appendChild(option);
     }
 
--- a/browser/components/payments/res/unprivileged-fallbacks.js
+++ b/browser/components/payments/res/unprivileged-fallbacks.js
@@ -17,17 +17,22 @@
 var log = {
   error: console.error.bind(console, "paymentRequest.xhtml:"),
   warn: console.warn.bind(console, "paymentRequest.xhtml:"),
   info: console.info.bind(console, "paymentRequest.xhtml:"),
   debug: console.debug.bind(console, "paymentRequest.xhtml:"),
 };
 
 var PaymentDialogUtils = {
-  getAddressLabel(address) {
+  getAddressLabel(address, addressFields = null) {
+    if (addressFields) {
+      let requestedFields = addressFields.trim().split(/\s+/);
+      return requestedFields.filter(f => f && address[f]).map(f => address[f]).join(", ") +
+        ` (${address.guid})`;
+    }
     return `${address.name} (${address.guid})`;
   },
   isCCNumber(str) {
     return !!str.replace(/[-\s]/g, "").match(/^\d{9,}$/);
   },
   DEFAULT_REGION: "US",
   supportedCountries: ["US", "CA"],
   getFormFormat(country) {
--- a/browser/components/payments/test/browser/browser_address_edit.js
+++ b/browser/components/payments/test/browser/browser_address_edit.js
@@ -370,16 +370,99 @@ add_task(async function test_edit_payer_
 
     info("clicking cancel");
     spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
 
     await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
   });
 });
 
+add_task(async function test_shipping_address_picker() {
+  await setup();
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: BLANK_PAGE_URL,
+  }, async browser => {
+    let {win, frame} =
+      await setupPaymentDialog(browser, {
+        methodData: [PTU.MethodData.basicCard],
+        details: PTU.Details.total60USD,
+        options: PTU.Options.requestShippingOption,
+        merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
+      }
+    );
+
+    await spawnPaymentDialogTask(frame, async function test_picker_option_label(address) {
+      let {
+        PaymentTestUtils: PTU,
+      } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+      ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+
+      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+        return Object.keys(state.savedAddresses).length == 1;
+      }, "One saved addresses when starting test");
+      let savedAddress = Object.values(state.savedAddresses)[0];
+
+      let selector = "address-picker[selected-state-key='selectedShippingAddress']";
+      let picker = content.document.querySelector(selector);
+      let option = Cu.waiveXrays(picker).dropdown.popupBox.children[0];
+      ok(option.textContent,
+         FormAutofillUtils.getAddressLabel(savedAddress, null),
+         "Shows correct shipping option label");
+    });
+
+    info("clicking cancel");
+    spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
+
+    await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
+  });
+});
+
+add_task(async function test_payer_address_picker() {
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: BLANK_PAGE_URL,
+  }, async browser => {
+    let {win, frame} =
+      await setupPaymentDialog(browser, {
+        methodData: [PTU.MethodData.basicCard],
+        details: PTU.Details.total60USD,
+        options: PTU.Options.requestPayerNameEmailAndPhone,
+        merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
+      }
+    );
+
+    await spawnPaymentDialogTask(frame, async function test_picker_option_label(address) {
+      let {
+        PaymentTestUtils: PTU,
+      } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+      ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+
+      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+        return Object.keys(state.savedAddresses).length == 1;
+      }, "One saved addresses when starting test");
+      let savedAddress = Object.values(state.savedAddresses)[0];
+
+      let selector = "address-picker[selected-state-key='selectedPayerAddress']";
+      let picker = content.document.querySelector(selector);
+      let option = Cu.waiveXrays(picker).dropdown.popupBox.children[0];
+      is(option.textContent.includes("32 Vassar Street"), false,
+         "Payer option label does not contain street address");
+      ok(option.textContent,
+         FormAutofillUtils.getAddressLabel(savedAddress, "name tel email"),
+         "Shows correct payer option label");
+    });
+
+    info("clicking cancel");
+    spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
+
+    await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
+  });
+});
+
 /*
  * Test that we can correctly add an address from a private window
  */
 add_task(async function test_private_persist_addresses() {
   let prefilledGuids = await setup();
 
   is((await formAutofillStorage.addresses.getAll()).length, 1,
      "Setup results in 1 stored address at start of test");
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -1977,17 +1977,16 @@ var SessionStoreInternal = {
     let tabTitle = aTab.label;
     let {permanentKey} = aTab.linkedBrowser;
 
     let tabData = {
       permanentKey,
       state: tabState,
       title: tabTitle,
       image: tabbrowser.getIcon(aTab),
-      iconLoadingPrincipal: Utils.serializePrincipal(aTab.linkedBrowser.contentPrincipal),
       pos: aTab._tPos,
       closedAt: Date.now()
     };
 
     let closedTabs = this._windows[aWindow.__SSi]._closedTabs;
 
     // Determine whether the tab contains any information worth saving. Note
     // that there might be pending state changes queued in the child that
@@ -2774,19 +2773,17 @@ var SessionStoreInternal = {
         win.gBrowser.setInitialTabTitle(tab, activePageData.title, { isContentTitle: true });
       } else if (activePageData.url != "about:blank") {
         win.gBrowser.setInitialTabTitle(tab, activePageData.url);
       }
     }
 
     // Restore the tab icon.
     if ("image" in tabData) {
-      // Use the serialized contentPrincipal with the new icon load.
-      let loadingPrincipal = Utils.deserializePrincipal(tabData.iconLoadingPrincipal);
-      win.gBrowser.setIcon(tab, tabData.image, loadingPrincipal);
+      win.gBrowser.setIcon(tab, tabData.image, undefined, tabData.iconLoadingPrincipal);
       TabStateCache.update(browser, { image: null, iconLoadingPrincipal: null });
     }
   },
 
   // This method deletes all the closedTabs matching userContextId.
   _forgetTabsWithUserContextId(userContextId) {
     let windowsEnum = Services.wm.getEnumerator("navigator:browser");
     while (windowsEnum.hasMoreElements()) {
--- a/browser/components/sessionstore/TabAttributes.jsm
+++ b/browser/components/sessionstore/TabAttributes.jsm
@@ -7,20 +7,17 @@
 var EXPORTED_SYMBOLS = ["TabAttributes"];
 
 // We never want to directly read or write these attributes.
 // 'image' should not be accessed directly but handled by using the
 //         gBrowser.getIcon()/setIcon() methods.
 // 'muted' should not be accessed directly but handled by using the
 //         tab.linkedBrowser.audioMuted/toggleMuteAudio methods.
 // 'pending' is used internal by sessionstore and managed accordingly.
-// 'iconloadingprincipal' is same as 'image' that it should be handled by
-//                        using the gBrowser.getIcon()/setIcon() methods.
-const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending", "iconloadingprincipal",
-                                    "skipbackgroundnotify"]);
+const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending", "skipbackgroundnotify"]);
 
 // A set of tab attributes to persist. We will read a given list of tab
 // attributes when collecting tab data and will re-set those attributes when
 // the given tab data is restored to a new tab.
 var TabAttributes = Object.freeze({
   persist(name) {
     return TabAttributesInternal.persist(name);
   },
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -7,18 +7,16 @@
 var EXPORTED_SYMBOLS = ["TabState"];
 
 ChromeUtils.defineModuleGetter(this, "PrivacyFilter",
   "resource://gre/modules/sessionstore/PrivacyFilter.jsm");
 ChromeUtils.defineModuleGetter(this, "TabStateCache",
   "resource:///modules/sessionstore/TabStateCache.jsm");
 ChromeUtils.defineModuleGetter(this, "TabAttributes",
   "resource:///modules/sessionstore/TabAttributes.jsm");
-ChromeUtils.defineModuleGetter(this, "Utils",
-  "resource://gre/modules/sessionstore/Utils.jsm");
 
 /**
  * Module that contains tab state collection methods.
  */
 var TabState = Object.freeze({
   update(browser, data) {
     TabStateInternal.update(browser, data);
   },
@@ -120,21 +118,16 @@ var TabStateInternal = {
     // be read from the tab/browser every time we collect data.
 
     // Store the tab icon.
     if (!("image" in tabData)) {
       let tabbrowser = tab.ownerGlobal.gBrowser;
       tabData.image = tabbrowser.getIcon(tab);
     }
 
-    // Store the serialized contentPrincipal of this tab to use for the icon.
-    if (!("iconLoadingPrincipal" in tabData)) {
-      tabData.iconLoadingPrincipal = Utils.serializePrincipal(browser.mIconLoadingPrincipal);
-    }
-
     // If there is a userTypedValue set, then either the user has typed something
     // in the URL bar, or a new tab was opened with a URI to load.
     // If so, we also track whether we were still in the process of loading something.
     if (!("userTypedValue" in tabData) && browser.userTypedValue) {
       tabData.userTypedValue = browser.userTypedValue;
       // We always used to keep track of the loading state as an integer, where
       // '0' indicated the user had typed since the last load (or no load was
       // ongoing), and any positive value indicated we had started a load since
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -52,16 +52,17 @@ support-files =
   browser_911547_sample.html^headers^
   restore_redirect_http.html
   restore_redirect_http.html^headers^
   restore_redirect_js.html
   restore_redirect_target.html
   browser_1234021_page.html
   browser_1284886_suspend_tab.html
   browser_1284886_suspend_tab_2.html
+  empty.html
 
 #NB: the following are disabled
 #  browser_464620_a.html
 #  browser_464620_b.html
 #  browser_464620_xd.html
 
 
 #disabled-for-intermittent-failures--bug-766044, browser_459906_empty.html
@@ -102,16 +103,17 @@ skip-if = (verify && debug)
 [browser_formdata_xpath.js]
 [browser_frametree.js]
 [browser_frame_history.js]
 skip-if = (verify && (os == 'win' || os == 'mac'))
 [browser_global_store.js]
 [browser_history_persist.js]
 [browser_label_and_icon.js]
 [browser_merge_closed_tabs.js]
+[browser_old_favicon.js]
 [browser_page_title.js]
 [browser_pending_tabs.js]
 [browser_privatetabs.js]
 [browser_purge_shistory.js]
 skip-if = e10s # Bug 1271024
 [browser_replace_load.js]
 [browser_restore_redirect.js]
 [browser_restore_cookies_noOriginAttributes.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_old_favicon.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Ensure that we can restore old style favicon and principals.
+ */
+add_task(async function test_label_and_icon() {
+  let helper = Cc["@mozilla.org/network/serialization-helper;1"].getService(Ci.nsISerializationHelper);
+
+  // Make sure that tabs are restored on demand as otherwise the tab will start
+  // loading immediately and override the icon.
+  await SpecialPowers.pushPrefEnv({
+    set: [["browser.sessionstore.restore_on_demand", true]],
+  });
+
+  // Create a new tab.
+  let tab = BrowserTestUtils.addTab(gBrowser, "http://www.example.com/browser/browser/components/sessionstore/test/empty.html");
+  let browser = tab.linkedBrowser;
+  await promiseBrowserLoaded(browser);
+
+  let contentPrincipal = browser.contentPrincipal;
+  let serializedPrincipal = helper.serializeToString(contentPrincipal);
+
+  // Retrieve the tab state.
+  await TabStateFlusher.flush(browser);
+  let state = JSON.parse(ss.getTabState(tab));
+  state.image = "http://www.example.com/favicon.ico";
+  state.iconLoadingPrincipal = serializedPrincipal;
+
+  BrowserTestUtils.removeTab(tab);
+
+  // Open a new tab to restore into.
+  tab = BrowserTestUtils.addTab(gBrowser, "about:blank");
+  ss.setTabState(tab, state);
+  await promiseTabRestoring(tab);
+
+  // Check that label and icon are set for the restoring tab.
+  is(gBrowser.getIcon(tab), "http://www.example.com/favicon.ico", "icon is set");
+  is(tab.getAttribute("image"), "http://www.example.com/favicon.ico", "tab image is set");
+  is(tab.getAttribute("iconloadingprincipal"), serializedPrincipal, "tab image loading principal is set");
+
+  // Cleanup.
+  BrowserTestUtils.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/empty.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+
+<html>
+  <body>
+  </body>
+</html>
--- a/browser/config/mozconfigs/linux64/plain-opt
+++ b/browser/config/mozconfigs/linux64/plain-opt
@@ -4,13 +4,13 @@ export LLVM_CONFIG="${TOOLTOOL_DIR}/clan
 CARGO="${TOOLTOOL_DIR}/rustc/bin/cargo"
 RUSTC="${TOOLTOOL_DIR}/rustc/bin/rustc"
 RUSTDOC="${TOOLTOOL_DIR}/rustc/bin/rustdoc"
 RUSTFMT="${TOOLTOOL_DIR}/rustc/bin/rustfmt"
 CBINDGEN="${TOOLTOOL_DIR}/cbindgen/cbindgen"
 
 export NODEJS="${TOOLTOOL_DIR}/node/bin/node"
 
-CC="${TOOLTOOL_DIR}/gcc/bin/gcc"
-CXX="${TOOLTOOL_DIR}/gcc/bin/g++"
+CC="${TOOLTOOL_DIR}/clang/bin/clang"
+CXX="${TOOLTOOL_DIR}/clang/bin/clang++"
 
 mk_add_options "export PATH=${TOOLTOOL_DIR}/gcc/bin:${PATH}"
 mk_add_options "export LD_LIBRARY_PATH=${TOOLTOOL_DIR}/gcc/lib64:${TOOLTOOL_DIR}/gcc/lib32:${TOOLTOOL_DIR}/gcc/lib"
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -241,51 +241,56 @@ this.FormAutofillUtils = {
 
   getAddressSeparator() {
     // The separator should be based on the L10N address format, and using a
     // white space is a temporary solution.
     return " ";
   },
 
   /**
-   * Get address display label. It should display up to two pieces of
-   * information, separated by a comma.
+   * Get address display label. It should display information separated
+   * by a comma.
    *
    * @param  {object} address
+   * @param  {string?} addressFields Override the fields which can be displayed, but not the order.
    * @returns {string}
    */
-  getAddressLabel(address) {
+  getAddressLabel(address, addressFields = null) {
     // TODO: Implement a smarter way for deciding what to display
     //       as option text. Possibly improve the algorithm in
     //       ProfileAutoCompleteResult.jsm and reuse it here.
-    const fieldOrder = [
+    let fieldOrder = [
       "name",
       "-moz-street-address-one-line",  // Street address
       "address-level2",  // City/Town
       "organization",    // Company or organization name
       "address-level1",  // Province/State (Standardized code if possible)
       "country-name",    // Country name
       "postal-code",     // Postal code
       "tel",             // Phone number
       "email",           // Email address
     ];
 
     address = {...address};
     let parts = [];
+    if (addressFields) {
+      let requiredFields = addressFields.trim().split(/\s+/);
+      fieldOrder = fieldOrder.filter(name => requiredFields.includes(name));
+    }
     if (address["street-address"]) {
       address["-moz-street-address-one-line"] = this.toOneLineAddress(
         address["street-address"]
       );
     }
     for (const fieldName of fieldOrder) {
       let string = address[fieldName];
       if (string) {
         parts.push(string);
       }
-      if (parts.length == 2) {
+      if (parts.length == 2 && !addressFields) {
         break;
       }
     }
     return parts.join(", ");
   },
 
   toOneLineAddress(address, delimiter = "\n") {
     let array = typeof address == "string" ? address.split(delimiter) : address;
--- a/build/autoconf/nspr-build.m4
+++ b/build/autoconf/nspr-build.m4
@@ -151,18 +151,18 @@ if test -n "$MOZ_SYSTEM_NSPR" -o -n "$NS
                 AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT or including prtypes.h does not provide it]))
     AC_TRY_COMPILE([#include "prtypes.h"],
                 [#ifndef PR_UINT64
                  #error PR_UINT64 not defined or requires including prtypes.h
                  #endif],
                 ,
                 AC_MSG_ERROR([system NSPR does not support PR_UINT64 or including prtypes.h does not provide it]))
     CFLAGS=$_SAVE_CFLAGS
-    NSPR_INCLUDE_DIR=`echo ${NSPR_CFLAGS} | sed -e 's/.*-I\([^ ]*\).*/\1/'`
-    NSPR_LIB_DIR=`echo ${NSPR_LIBS} | sed -e 's/.*-L\([^ ]*\).*/\1/'`
+    NSPR_INCLUDE_DIR=`echo ${NSPR_CFLAGS} | sed -e 's/.*-I\([[^ ]]*\).*/\1/'`
+    NSPR_LIB_DIR=`echo ${NSPR_LIBS} | sed -e 's/.*-L\([[^ ]]*\).*/\1/'`
 elif test -z "$JS_POSIX_NSPR"; then
     NSPR_INCLUDE_DIR="${DIST}/include/nspr"
     NSPR_CFLAGS="-I${NSPR_INCLUDE_DIR}"
     if test -n "$GNU_CC"; then
         if test -n "$MOZ_FOLD_LIBS"; then
            NSPR_LIB_DIR=${DIST}/lib
         else
            NSPR_LIB_DIR=${DIST}/bin
--- a/build/autoconf/sanitize.m4
+++ b/build/autoconf/sanitize.m4
@@ -1,34 +1,18 @@
 dnl This Source Code Form is subject to the terms of the Mozilla Public
 dnl License, v. 2.0. If a copy of the MPL was not distributed with this
 dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 AC_DEFUN([MOZ_CONFIG_SANITIZE], [
 
 dnl ========================================================
-dnl = Link Time Optimization (LTO)
-dnl ========================================================
-if test -n "$MOZ_LTO"; then
-    MOZ_LLVM_HACKS=1
-    AC_DEFINE(MOZ_LTO)
-    MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
-
-    CFLAGS="$CFLAGS $MOZ_LTO_CFLAGS"
-    CPPFLAGS="$CPPFLAGS $MOZ_LTO_CFLAGS"
-    CXXFLAGS="$CXXFLAGS $MOZ_LTO_CFLAGS"
-    LDFLAGS="$LDFLAGS $MOZ_LTO_LDFLAGS"
-fi
-AC_SUBST(MOZ_LTO)
-
-dnl ========================================================
 dnl = Use Address Sanitizer
 dnl ========================================================
 if test -n "$MOZ_ASAN"; then
-    MOZ_LLVM_HACKS=1
     if test -n "$CLANG_CL"; then
         # Look for the ASan runtime binary
         if test "$CPU_ARCH" = "x86_64"; then
           MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-x86_64.dll
         else
           MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll
         fi
         # We use MOZ_PATH_PROG in order to get a Windows style path.
@@ -56,17 +40,16 @@ AC_SUBST(MOZ_ASAN)
 dnl ========================================================
 dnl = Use Memory Sanitizer
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(memory-sanitizer,
 [  --enable-memory-sanitizer       Enable Memory Sanitizer (default=no)],
     MOZ_MSAN=1,
     MOZ_MSAN= )
 if test -n "$MOZ_MSAN"; then
-    MOZ_LLVM_HACKS=1
     CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CFLAGS"
     CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CXXFLAGS"
     if test -z "$CLANG_CL"; then
         LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $LDFLAGS"
     fi
     AC_DEFINE(MOZ_MSAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
@@ -75,17 +58,16 @@ AC_SUBST(MOZ_MSAN)
 dnl ========================================================
 dnl = Use Thread Sanitizer
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(thread-sanitizer,
 [  --enable-thread-sanitizer       Enable Thread Sanitizer (default=no)],
    MOZ_TSAN=1,
    MOZ_TSAN= )
 if test -n "$MOZ_TSAN"; then
-    MOZ_LLVM_HACKS=1
     CFLAGS="-fsanitize=thread $CFLAGS"
     CXXFLAGS="-fsanitize=thread $CXXFLAGS"
     if test -z "$CLANG_CL"; then
         LDFLAGS="-fsanitize=thread $LDFLAGS"
     fi
     AC_DEFINE(MOZ_TSAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
@@ -100,17 +82,16 @@ MOZ_ARG_ENABLE_BOOL(signed-overflow-sani
    MOZ_SIGNED_OVERFLOW_SANITIZE=1,
    MOZ_SIGNED_OVERFLOW_SANITIZE= )
 MOZ_ARG_ENABLE_BOOL(unsigned-overflow-sanitizer,
 [  --enable-unsigned-overflow-sanitizer       Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts, default=no)],
    MOZ_UNSIGNED_OVERFLOW_SANITIZE=1,
    MOZ_UNSIGNED_OVERFLOW_SANITIZE= )
 
 if test -n "$MOZ_SIGNED_OVERFLOW_SANITIZE$MOZ_UNSIGNED_OVERFLOW_SANITIZE"; then
-    MOZ_LLVM_HACKS=1
     MOZ_UBSAN=1
     SANITIZER_BLACKLISTS=""
     if test -n "$MOZ_SIGNED_OVERFLOW_SANITIZE"; then
         SANITIZER_BLACKLISTS="-fsanitize-blacklist=$_topsrcdir/build/sanitizers/ubsan_signed_overflow_blacklist.txt $SANITIZER_BLACKLISTS"
         CFLAGS="-fsanitize=signed-integer-overflow $CFLAGS"
         CXXFLAGS="-fsanitize=signed-integer-overflow $CXXFLAGS"
         if test -z "$CLANG_CL"; then
             LDFLAGS="-fsanitize=signed-integer-overflow $LDFLAGS"
@@ -134,28 +115,14 @@ fi
 AC_SUBST(MOZ_SIGNED_OVERFLOW_SANITIZE)
 AC_SUBST(MOZ_UNSIGNED_OVERFLOW_SANITIZE)
 AC_SUBST(MOZ_UBSAN)
 
 # The LLVM symbolizer is used by all sanitizers
 AC_SUBST(LLVM_SYMBOLIZER)
 
 dnl ========================================================
-dnl = Enable hacks required for LLVM instrumentations
-dnl ========================================================
-MOZ_ARG_ENABLE_BOOL(llvm-hacks,
-[  --enable-llvm-hacks       Enable workarounds required for several LLVM instrumentations (default=no)],
-    MOZ_LLVM_HACKS=1,
-    MOZ_LLVM_HACKS= )
-if test -n "$MOZ_LLVM_HACKS"; then
-    MOZ_NO_WLZDEFS=1
-    MOZ_CFLAGS_NSS=1
-fi
-AC_SUBST(MOZ_NO_WLZDEFS)
-AC_SUBST(MOZ_CFLAGS_NSS)
-
-dnl ========================================================
 dnl = Test for whether the compiler is compatible with the
 dnl = given sanitize options.
 dnl ========================================================
 AC_TRY_LINK(,,,AC_MSG_ERROR([compiler is incompatible with sanitize options]))
 
 ])
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -928,29 +928,29 @@ def compiler(language, host_or_target, c
             raise FatalCheckError(
                 'Only clang/llvm 3.6 or newer is supported.')
 
         if info.type == 'msvc':
             if info.version < '19.13.26128':
                 raise FatalCheckError(
                     'This version (%s) of the MSVC compiler is not '
                     'supported.\n'
-                    'You must install Visual C++ 2017 Update 6 in '
-                    'order to build.\n'
+                    'You must install Visual C++ 2017 Update 6 or '
+                    'Update 8 or later in order to build.\n'
                     'See https://developer.mozilla.org/en/'
                     'Windows_Build_Prerequisites' % info.version)
 
             # MSVC version 15.7 and the previews for 15.8, at least,
             # can't build Firefox.
-            if info.version >= '19.14.0':
+            if info.version >= '19.14.0' and info.version < '19.15.0':
                 raise FatalCheckError(
                     'This version (%s) of the MSVC compiler is not '
                     'supported due to compiler bugs.\n'
-                    'You must install Visual C++ 2017 Update 6 in '
-                    'order to build.\n'
+                    'You must install Visual C++ 2017 Update 6 or '
+                    'Update 8 or later in order to build.\n'
                     'See https://developer.mozilla.org/en/'
                     'Windows_Build_Prerequisites' % info.version)
 
         if info.flags:
             raise FatalCheckError(
                 'Unknown compiler or compiler not supported.')
 
         return namespace(
@@ -1398,18 +1398,20 @@ def lto(value, pgo, c_compiler):
     return namespace(
         enabled=enabled,
         cflags=cflags,
         ldflags=ldflags
     )
 
 
 add_old_configure_assignment('MOZ_LTO', lto.enabled)
-add_old_configure_assignment('MOZ_LTO_CFLAGS', lto.cflags)
-add_old_configure_assignment('MOZ_LTO_LDFLAGS', lto.ldflags)
+set_config('MOZ_LTO', lto.enabled)
+set_define('MOZ_LTO', lto.enabled)
+set_config('MOZ_LTO_CFLAGS', lto.cflags)
+set_config('MOZ_LTO_LDFLAGS', lto.ldflags)
 
 # ASAN
 # ==============================================================
 
 js_option('--enable-address-sanitizer', help='Enable Address Sanitizer')
 
 
 @depends_if('--enable-address-sanitizer')
--- a/build/unix/elfhack/inject/moz.build
+++ b/build/unix/elfhack/inject/moz.build
@@ -33,12 +33,12 @@ for v in ('OS_CPPFLAGS', 'OS_CFLAGS', 'D
     for flag in COMPILE_FLAGS[v]:
         if flag == '-isystem':
             flags.append(''.join(COMPILE_FLAGS[v][idx:idx + 2]))
         elif flag.startswith(('-m', '-I', '-isystem')):
             flags.append(flag)
         idx += 1
     COMPILE_FLAGS[v] = flags
 
-COMPILE_FLAGS['OS_CFLAGS'] += ['-O2', '-fno-stack-protector']
+COMPILE_FLAGS['OS_CFLAGS'] += ['-O2', '-fno-stack-protector', '-fno-lto']
 
 AllowCompilerWarnings()
 NoVisibilityFlags()
--- a/build/unix/elfhack/moz.build
+++ b/build/unix/elfhack/moz.build
@@ -9,21 +9,19 @@ DIRS += ['inject']
 
 if not CONFIG['CROSS_COMPILE']:
     SOURCES += [
         'dummy.c',
         'test-array.c',
         'test-ctors.c',
     ]
 
-    for f in CONFIG['OS_CFLAGS']:
-        if f.startswith('-flto'):
-            SOURCES['dummy.c'].flags += ['-fno-lto']
-            SOURCES['test-array.c'].flags += ['-fno-lto']
-            SOURCES['test-ctors.c'].flags += ['-fno-lto']
+    SOURCES['dummy.c'].flags += ['-fno-lto']
+    SOURCES['test-array.c'].flags += ['-fno-lto']
+    SOURCES['test-ctors.c'].flags += ['-fno-lto']
 
 HOST_SOURCES += [
     'elf.cpp',
     'elfhack.cpp',
 ]
 
 HostProgram('elfhack')
 
--- a/build/unix/mozconfig.linux
+++ b/build/unix/mozconfig.linux
@@ -2,17 +2,17 @@ if [ "x$IS_NIGHTLY" = "xyes" ]; then
   # Some nightlies (eg: Mulet) don't want these set.
   MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
 fi
 
 . "$topsrcdir/build/mozconfig.common"
 
 TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
 
-if [ -n "$FORCE_GCC" ]; then
+if [ -n "$FORCE_GCC" -o -n "$MOZ_PGO" ]; then
     CC="$TOOLTOOL_DIR/gcc/bin/gcc"
     CXX="$TOOLTOOL_DIR/gcc/bin/g++"
 else
     CC="$TOOLTOOL_DIR/clang/bin/clang"
     CXX="$TOOLTOOL_DIR/clang/bin/clang++"
 fi
 
 # We want to make sure we use binutils and other binaries in the tooltool
--- a/config/config.mk
+++ b/config/config.mk
@@ -194,22 +194,22 @@ INCLUDES = \
   -I$(srcdir) \
   -I$(CURDIR) \
   $(LOCAL_INCLUDES) \
   -I$(ABS_DIST)/include \
   $(NULL)
 
 include $(MOZILLA_DIR)/config/static-checking-config.mk
 
-LDFLAGS		= $(COMPUTED_LDFLAGS) $(PGO_LDFLAGS) $(MK_LDFLAGS)
+LDFLAGS		= $(MOZ_LTO_LDFLAGS) $(COMPUTED_LDFLAGS) $(PGO_LDFLAGS) $(MK_LDFLAGS)
 
-COMPILE_CFLAGS	= $(COMPUTED_CFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES)
-COMPILE_CXXFLAGS = $(COMPUTED_CXXFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES)
-COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS)
-COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS)
+COMPILE_CFLAGS	= $(MOZ_LTO_CFLAGS) $(COMPUTED_CFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES)
+COMPILE_CXXFLAGS = $(MOZ_LTO_CFLAGS) $(COMPUTED_CXXFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES)
+COMPILE_CMFLAGS = $(MOZ_LTO_CFLAGS) $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS)
+COMPILE_CMMFLAGS = $(MOZ_LTO_CFLAGS) $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS)
 ASFLAGS = $(COMPUTED_ASFLAGS)
 SFLAGS = $(COMPUTED_SFLAGS)
 
 HOST_CFLAGS = $(COMPUTED_HOST_CFLAGS) $(_DEPEND_CFLAGS)
 HOST_CXXFLAGS = $(COMPUTED_HOST_CXXFLAGS) $(_DEPEND_CFLAGS)
 HOST_C_LDFLAGS = $(COMPUTED_HOST_C_LDFLAGS)
 HOST_CXX_LDFLAGS = $(COMPUTED_HOST_CXX_LDFLAGS)
 
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -383,18 +383,27 @@ ifneq (,$(filter ml%,$(AS)))
 ASOUTOPTION = -Fo# eol
 else
 ASOUTOPTION = -o # eol
 endif
 
 ifeq (,$(CROSS_COMPILE))
 HOST_OUTOPTION = $(OUTOPTION)
 else
+# Windows-to-Windows cross compiles should always use MSVC-style options for
+# host compiles.
+ifeq (WINNT_WINNT,$(HOST_OS_ARCH)_$(OS_ARCH))
+ifneq (,$(filter-out msvc clang-cl,$(HOST_CC_TYPE)))
+$(error MSVC-style compilers should be used for host compilations!)
+endif
+HOST_OUTOPTION = -Fo# eol
+else
 HOST_OUTOPTION = -o # eol
 endif
+endif
 ################################################################################
 
 # Ensure the build config is up to date. This is done automatically when builds
 # are performed through |mach build|. The check here is to catch people not
 # using mach. If we ever enforce builds through mach, this code can be removed.
 ifndef MOZBUILD_BACKEND_CHECKED
 ifndef MACH
 ifndef TOPLEVEL_BUILD
--- a/config/tests/unit-nsinstall.py
+++ b/config/tests/unit-nsinstall.py
@@ -163,17 +163,17 @@ class TestNsinstall(unittest.TestCase):
             testdir = self.mkdirs(u"\u4241\u1D04\u1414")
             # We don't use subprocess because it can't handle Unicode on
             # Windows <http://bugs.python.org/issue1759845>. mozprocess calls
             # CreateProcessW directly so it's perfect.
             p = processhandler.ProcessHandlerMixin([sys.executable,
                                                     NSINSTALL_PATH,
                                                     testfile, testdir])
             p.run()
-            rv = p.waitForFinish()
+            rv = p.wait()
 
             self.assertEqual(rv, 0)
             destfile = os.path.join(testdir, filename)
             self.assert_(os.path.isfile(destfile))
 
     # TODO: implement -R, -l, -L and test them!
 
 
--- a/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
@@ -46,18 +46,18 @@ async function runTests(inspector) {
   await onInspectorUpdated;
 
   ok(tooltip.isVisible(), "EventTooltip visible.");
 
   onInspectorUpdated = inspector.once("inspector-updated");
   const onTooltipHidden = tooltip.once("hidden");
 
   info("Click on another tag to hide the event tooltip");
-  const h1 = await getContainerForSelector("h1", inspector);
-  const tag = h1.elt.querySelector(".tag");
+  const script = await getContainerForSelector("script", inspector);
+  const tag = script.elt.querySelector(".tag");
   EventUtils.synthesizeMouseAtCenter(tag, {}, inspector.markup.doc.defaultView);
 
   await onTooltipHidden;
   // New node is selected, wait for inspector-updated.
   await onInspectorUpdated;
 
   ok(!tooltip.isVisible(), "EventTooltip hidden.");
 }
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -389,29 +389,35 @@ ul.children + .tag-line::before {
 /* Applicable to the DOCTYPE */
 .doctype {
   font-style: italic;
 }
 
 /* Markup Badges */
 .markup-badge {
   display: inline-block;
-  font-size: 9px;
+  font-size: 10px;
   font-weight: normal;
   line-height: 11px;
   vertical-align: 1px;
   border: 1px solid var(--markup-badge-border-color);
   border-radius: 3px;
   padding: 0px 2px;
   margin-inline-start: 5px;
   -moz-user-select: none;
   background-color: var(--markup-badge-background-color);
   color: var(--markup-badge-color);
 }
 
+@media (min-resolution: 1.1dppx) {
+  .markup-badge {
+    font-size: 9px;
+  }
+}
+
 .markup-badge.active {
   background-color: var(--markup-badge-active-background-color);
   border-color: var(--theme-selection-color);
   color: var(--theme-selection-color);
 }
 
 .markup-badge[data-custom],
 .markup-badge[data-display="flex"].interactive,
--- a/devtools/shared/heapsnapshot/tests/gtest/DeserializedStackFrameUbiStackFrames.cpp
+++ b/devtools/shared/heapsnapshot/tests/gtest/DeserializedStackFrameUbiStackFrames.cpp
@@ -3,16 +3,17 @@
  * 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/. */
 
 // Test that the `JS::ubi::StackFrame`s we create from
 // `mozilla::devtools::DeserializedStackFrame` instances look and behave as we would
 // like.
 
 #include "DevTools.h"
+#include "js/SavedFrameAPI.h"
 #include "js/TypeDecls.h"
 #include "mozilla/devtools/DeserializedNode.h"
 
 using testing::Field;
 using testing::ReturnRef;
 
 // A mock DeserializedStackFrame for testing.
 struct MockDeserializedStackFrame : public DeserializedStackFrame
--- a/dom/base/BodyUtil.cpp
+++ b/dom/base/BodyUtil.cpp
@@ -13,16 +13,17 @@
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsDOMString.h"
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
+#include "js/JSON.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FormData.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/URLSearchParams.h"
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ChromeUtils.h"
 
+#include "js/AutoByteString.h"
+#include "js/SavedFrameAPI.h"
 #include "jsfriendapi.h"
 #include "WrapperFactory.h"
 
 #include "mozilla/Base64.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/PerformanceMetricsCollector.h"
 #include "mozilla/Preferences.h"
new file mode 100644
--- /dev/null
+++ b/dom/base/VisualViewport.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VisualViewport.h"
+#include "nsIScrollableFrame.h"
+#include "nsIDocShell.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+VisualViewport::VisualViewport(nsPIDOMWindowInner* aWindow)
+  : DOMEventTargetHelper(aWindow)
+{
+}
+
+VisualViewport::~VisualViewport()
+{
+}
+
+/* virtual */
+JSObject*
+VisualViewport::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return VisualViewport_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+CSSSize
+VisualViewport::VisualViewportSize() const
+{
+  CSSSize size = CSSSize(0,0);
+
+  nsIPresShell* presShell = GetPresShell();
+  if (presShell) {
+    if (presShell->IsVisualViewportSizeSet()) {
+      size = CSSRect::FromAppUnits(
+        presShell->GetVisualViewportSize());
+    } else {
+      nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
+      if (sf) {
+        size = CSSRect::FromAppUnits(sf->GetScrollPortRect().Size());
+      }
+    }
+  }
+  return size;
+}
+
+double
+VisualViewport::Width() const
+{
+  CSSSize size = VisualViewportSize();
+  return size.width;
+}
+
+double
+VisualViewport::Height() const
+{
+  CSSSize size = VisualViewportSize();
+  return size.height;
+}
+
+double
+VisualViewport::Scale() const
+{
+  double scale = 1;
+  nsIPresShell* presShell = GetPresShell();
+  if (presShell) {
+    scale = presShell->GetResolution();
+  }
+  return scale;
+}
+
+CSSPoint
+VisualViewport::VisualViewportOffset() const
+{
+  CSSPoint offset = CSSPoint(0,0);
+
+  nsIPresShell* presShell = GetPresShell();
+  if (presShell) {
+      offset = CSSPoint::FromAppUnits(presShell->GetVisualViewportOffset());
+  }
+  return offset;
+}
+
+CSSPoint
+VisualViewport::LayoutViewportOffset() const
+{
+  CSSPoint offset = CSSPoint(0,0);
+
+  nsIPresShell* presShell = GetPresShell();
+  if (presShell) {
+    nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
+    if (sf) {
+      offset = CSSPoint::FromAppUnits(sf->GetScrollPosition());
+    }
+  }
+  return offset;
+}
+
+double
+VisualViewport::PageLeft() const
+{
+  return VisualViewportOffset().X();
+}
+
+double
+VisualViewport::PageTop() const
+{
+  return VisualViewportOffset().Y();
+}
+
+double
+VisualViewport::OffsetLeft() const
+{
+  return PageLeft() - LayoutViewportOffset().X();
+}
+
+double
+VisualViewport::OffsetTop() const
+{
+  return PageTop() - LayoutViewportOffset().Y();
+}
+
+nsIPresShell*
+VisualViewport::GetPresShell() const
+{
+  nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
+  if (!window) {
+    return nullptr;
+  }
+
+  nsIDocShell* docShell = window->GetDocShell();
+  if (!docShell) {
+    return nullptr;
+  }
+
+  return docShell->GetPresShell();
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/VisualViewport.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef mozilla_dom_VisualViewport_h
+#define mozilla_dom_VisualViewport_h
+
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/VisualViewportBinding.h"
+#include "Units.h"
+#include "nsIPresShell.h"
+
+namespace mozilla {
+namespace dom {
+
+/* Visual Viewport API spec:  https://wicg.github.io/visual-viewport/#the-visualviewport-interface */
+class VisualViewport final: public mozilla::DOMEventTargetHelper
+{
+
+public:
+  explicit VisualViewport(nsPIDOMWindowInner* aWindow);
+
+  double OffsetLeft() const;
+  double OffsetTop() const;
+  double PageLeft() const;
+  double PageTop() const;
+  double Width() const;
+  double Height() const;
+  double Scale() const;
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+private:
+  virtual ~VisualViewport();
+
+  CSSSize VisualViewportSize() const;
+  CSSPoint VisualViewportOffset() const;
+  CSSPoint LayoutViewportOffset() const;
+  nsIPresShell* GetPresShell() const;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_VisualViewport_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -222,16 +222,17 @@ EXPORTS.mozilla.dom += [
     'SyncMessageSender.h',
     'TabGroup.h',
     'Text.h',
     'Timeout.h',
     'TimeoutHandler.h',
     'TimeoutManager.h',
     'TreeIterator.h',
     'TreeWalker.h',
+    'VisualViewport.h',
     'WebKitCSSMatrix.h',
     'WindowOrientationObserver.h',
 ]
 
 if CONFIG['FUZZING']:
     EXPORTS.mozilla.dom += [
         'FuzzingFunctions.h',
     ]
@@ -378,16 +379,17 @@ UNIFIED_SOURCES += [
     'TextInputProcessor.cpp',
     'ThirdPartyUtil.cpp',
     'Timeout.cpp',
     'TimeoutBudgetManager.cpp',
     'TimeoutExecutor.cpp',
     'TimeoutHandler.cpp',
     'TimeoutManager.cpp',
     'TreeWalker.cpp',
+    'VisualViewport.cpp',
     'WebKitCSSMatrix.cpp',
     'WindowDestroyedEvent.cpp',
     'WindowNamedPropertiesHandler.cpp',
     'WindowOrientationObserver.cpp',
     'XPathGenerator.cpp',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -15,16 +15,17 @@
 #include "harfbuzz/hb.h"
 #include "imgICache.h"
 #include "imgIContainer.h"
 #include "imgINotificationObserver.h"
 #include "imgLoader.h"
 #include "imgRequestProxy.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/JSON.h"
 #include "js/Value.h"
 #include "Layers.h"
 #include "nsAppRunner.h"
 // nsNPAPIPluginInstance must be included before nsIDocument.h, which is included in mozAutoDocUpdate.h.
 #include "nsNPAPIPluginInstance.h"
 #include "gfxDrawable.h"
 #include "gfxPrefs.h"
 #include "ImageOps.h"
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -24,16 +24,17 @@
 #include "nsIInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIMemoryReporter.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
 #include "xpcpublic.h"
+#include "js/JSON.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/ChildProcessMessageManager.h"
 #include "mozilla/dom/ChromeMessageBroadcaster.h"
 #include "mozilla/dom/File.h"
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -28,16 +28,17 @@
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/StorageNotifierService.h"
 #include "mozilla/dom/StorageUtils.h"
 #include "mozilla/dom/Timeout.h"
 #include "mozilla/dom/TimeoutHandler.h"
 #include "mozilla/dom/TimeoutManager.h"
+#include "mozilla/dom/VisualViewport.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #if defined(MOZ_WIDGET_ANDROID)
 #include "mozilla/dom/WindowOrientationObserver.h"
 #endif
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsIIdleService.h"
 #include "nsISizeOfEventTarget.h"
@@ -2212,16 +2213,25 @@ nsPIDOMWindowInner::Navigator()
 {
   if (!mNavigator) {
     mNavigator = new mozilla::dom::Navigator(this);
   }
 
   return mNavigator;
 }
 
+VisualViewport* nsGlobalWindowInner::VisualViewport()
+{
+  if (!mVisualViewport) {
+    mVisualViewport = new mozilla::dom::VisualViewport(this);
+  }
+
+  return mVisualViewport;
+}
+
 nsScreen*
 nsGlobalWindowInner::GetScreen(ErrorResult& aError)
 {
   if (!mScreen) {
     mScreen = nsScreen::Create(this);
     if (!mScreen) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -121,16 +121,17 @@ class Promise;
 class PostMessageEvent;
 struct RequestInit;
 class RequestOrUSVString;
 class Selection;
 class SpeechSynthesis;
 class TabGroup;
 class Timeout;
 class U2F;
+class VisualViewport;
 class VRDisplay;
 enum class VRDisplayEventReason : uint8_t;
 class VREventObserver;
 class WakeLock;
 #if defined(MOZ_WIDGET_ANDROID)
 class WindowOrientationObserver;
 #endif
 class Worklet;
@@ -759,16 +760,17 @@ public:
   mozilla::dom::Storage* GetSessionStorage(mozilla::ErrorResult& aError);
   mozilla::dom::Storage*
   GetLocalStorage(mozilla::ErrorResult& aError);
   mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError);
   mozilla::dom::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError);
   already_AddRefed<nsICSSDeclaration>
     GetComputedStyle(mozilla::dom::Element& aElt, const nsAString& aPseudoElt,
                      mozilla::ErrorResult& aError) override;
+  mozilla::dom::VisualViewport* VisualViewport();
   already_AddRefed<mozilla::dom::MediaQueryList> MatchMedia(
     const nsAString& aQuery,
     mozilla::dom::CallerType aCallerType,
     mozilla::ErrorResult& aError);
   nsScreen* GetScreen(mozilla::ErrorResult& aError);
   void MoveTo(int32_t aXPos, int32_t aYPos,
               mozilla::dom::CallerType aCallerType,
               mozilla::ErrorResult& aError);
@@ -1388,16 +1390,18 @@ protected:
   RefPtr<mozilla::dom::Storage> mLocalStorage;
   RefPtr<mozilla::dom::Storage> mSessionStorage;
 
   RefPtr<mozilla::EventListenerManager> mListenerManager;
   RefPtr<mozilla::dom::Location> mLocation;
   RefPtr<nsHistory>           mHistory;
   RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements;
 
+  RefPtr<mozilla::dom::VisualViewport> mVisualViewport;
+
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   // mTabChild is only ever populated in the content process.
   nsCOMPtr<nsITabChild>  mTabChild;
 
   uint32_t mSuspendDepth;
   uint32_t mFreezeDepth;
 
   // the method that was used to focus mFocusedNode
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -7,16 +7,17 @@
 /*
  * Base class for all DOM nodes.
  */
 
 #include "nsINode.h"
 
 #include "AccessCheck.h"
 #include "jsapi.h"
+#include "js/JSON.h"
 #include "mozAutoDocUpdate.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/Likely.h"
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -14,17 +14,19 @@
  * the generated code itself.
  */
 
 #include "mozilla/Assertions.h"
 
 #include "GeckoProfiler.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/AutoByteString.h"
 #include "js/Conversions.h"
+#include "js/StableStringChars.h"
 #include "nsString.h"
 
 class nsIScriptContext;
 class nsIScriptElement;
 class nsIScriptGlobalObject;
 class nsXBLPrototypeBinding;
 
 namespace mozilla {
@@ -220,17 +222,17 @@ public:
 
   static void ResetTimeZone();
 };
 
 template<typename T>
 inline bool
 AssignJSString(JSContext *cx, T &dest, JSString *s)
 {
-  size_t len = js::GetStringLength(s);
+  size_t len = JS::GetStringLength(s);
   static_assert(js::MaxStringLength < (1 << 28),
                 "Shouldn't overflow here or in SetCapacity");
   if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible))) {
     JS_ReportOutOfMemory(cx);
     return false;
   }
   return js::CopyStringChars(cx, dest.BeginWriting(), s, len);
 }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -12,16 +12,18 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "mozilla/UseCounter.h"
 
 #include "AccessCheck.h"
+#include "js/JSON.h"
+#include "js/StableStringChars.h"
 #include "jsfriendapi.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsHTMLTags.h"
 #include "nsIDocShell.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "nsINode.h"
@@ -2853,17 +2855,17 @@ ConvertJSValueToByteString(JSContext* cx
       // terminator.
       char badCharArray[6];
       static_assert(sizeof(char16_t) <= 2, "badCharArray too small");
       SprintfLiteral(badCharArray, "%d", badChar);
       ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray);
       return false;
     }
   } else {
-    length = js::GetStringLength(s);
+    length = JS::GetStringLength(s);
   }
 
   static_assert(js::MaxStringLength < UINT32_MAX,
                 "length+1 shouldn't overflow");
 
   if (!result.SetLength(length, fallible)) {
     return false;
   }
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3,16 +3,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/. */
 
 #ifndef mozilla_dom_BindingUtils_h__
 #define mozilla_dom_BindingUtils_h__
 
 #include "jsfriendapi.h"
+#include "js/AutoByteString.h"
 #include "js/Wrapper.h"
 #include "js/Conversions.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Alignment.h"
 #include "mozilla/Array.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DeferredFinalize.h"
 #include "mozilla/dom/BindingDeclarations.h"
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1059,16 +1059,20 @@ DOMInterfaces = {
 'TreeContentView': {
     'nativeType': 'nsTreeContentView',
 },
 
 'TreeWalker': {
     'wrapperCache': False,
 },
 
+'VisualViewport': {
+    'nativeType': 'mozilla::dom::VisualViewport',
+},
+
 'VTTCue': {
     'nativeType': 'mozilla::dom::TextTrackCue'
 },
 
 'VTTRegion': {
   'nativeType': 'mozilla::dom::TextTrackRegion',
 },
 
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_DOMJSClass_h
 #define mozilla_dom_DOMJSClass_h
 
+#include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Wrapper.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 
 #include "mozilla/dom/PrototypeList.h" // auto-generated
 
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -4,16 +4,17 @@
  * 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 "mozilla/dom/Exceptions.h"
 
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "jsapi.h"
+#include "js/SavedFrameAPI.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "XPCWrapper.h"
--- a/dom/console/ConsoleAPIStorage.js
+++ b/dom/console/ConsoleAPIStorage.js
@@ -41,22 +41,16 @@ const CONSOLEAPISTORAGE_CID = Components
 function ConsoleAPIStorageService() {
   this.init();
 }
 
 ConsoleAPIStorageService.prototype = {
   classID : CONSOLEAPISTORAGE_CID,
   QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleAPIStorage,
                                           Ci.nsIObserver]),
-  classInfo: XPCOMUtils.generateCI({
-    classID: CONSOLEAPISTORAGE_CID,
-    contractID: '@mozilla.org/consoleAPI-storage;1',
-    interfaces: [Ci.nsIConsoleAPIStorage, Ci.nsIObserver],
-    flags: Ci.nsIClassInfo.SINGLETON
-  }),
 
   observe: function CS_observe(aSubject, aTopic, aData)
   {
     if (aTopic == "xpcom-shutdown") {
       Services.obs.removeObserver(this, "xpcom-shutdown");
       Services.obs.removeObserver(this, "inner-window-destroyed");
       Services.obs.removeObserver(this, "memory-pressure");
     }
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -10,16 +10,17 @@
 
 #include "gfxPrefs.h"
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/DocAccessibleChild.h"
 #endif
 #include "Layers.h"
 #include "ContentChild.h"
 #include "TabParent.h"
+#include "js/JSON.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
 #include "mozilla/dom/MessageManagerBinding.h"
--- a/dom/media/webvtt/WebVTTParserWrapper.js
+++ b/dom/media/webvtt/WebVTTParserWrapper.js
@@ -1,17 +1,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/. */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/vtt.jsm");
 
 var WEBVTTPARSERWRAPPER_CID = "{acf6e493-0092-4b26-b172-241e375c57ab}";
-var WEBVTTPARSERWRAPPER_CONTRACTID = "@mozilla.org/webvttParserWrapper;1";
 
 function WebVTTParserWrapper()
 {
   // Nothing
 }
 
 WebVTTParserWrapper.prototype =
 {
@@ -55,16 +54,11 @@ WebVTTParserWrapper.prototype =
   processCues: function(window, cues, overlay, controls)
   {
     WebVTT.processCues(window, cues, overlay, controls);
   },
 
   classDescription: "Wrapper for the JS WebVTT implementation (vtt.js)",
   classID: Components.ID(WEBVTTPARSERWRAPPER_CID),
   QueryInterface: ChromeUtils.generateQI([Ci.nsIWebVTTParserWrapper]),
-  classInfo: XPCOMUtils.generateCI({
-    classID:    WEBVTTPARSERWRAPPER_CID,
-    contractID: WEBVTTPARSERWRAPPER_CONTRACTID,
-    interfaces: [Ci.nsIWebVTTParserWrapper]
-  })
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebVTTParserWrapper]);
--- a/dom/payments/PaymentRequestUtils.cpp
+++ b/dom/payments/PaymentRequestUtils.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "js/JSON.h"
 #include "nsArrayUtils.h"
 #include "PaymentRequestUtils.h"
 #include "nsIMutableArray.h"
 #include "nsISupportsPrimitives.h"
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1130,17 +1130,17 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JS
 
   if (!sJSObjWrappers) {
     // No hash yet (or any more), initialize it.
     if (!CreateJSObjWrapperTable())
       return nullptr;
   }
   MOZ_ASSERT(sJSObjWrappersAccessible);
 
-  JSObjWrapperTable::Ptr p = sJSObjWrappers->lookupForAdd(nsJSObjWrapperKey(obj, npp));
+  JSObjWrapperTable::Ptr p = sJSObjWrappers->lookup(nsJSObjWrapperKey(obj, npp));
   if (p) {
     MOZ_ASSERT(p->value());
     // Found a live nsJSObjWrapper, return it.
 
     return _retainobject(p->value());
   }
 
   // No existing nsJSObjWrapper, create one.
--- a/dom/script/ScriptSettings.cpp
+++ b/dom/script/ScriptSettings.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/WorkerPrivate.h"
 
 #include "jsapi.h"
+#include "js/StableStringChars.h"
 #include "xpcpublic.h"
 #include "nsIGlobalObject.h"
 #include "nsIDocShell.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsPIDOMWindow.h"
@@ -705,17 +706,17 @@ AutoEntryScript::DocshellEntryMonitor::E
       !window->GetDocShell()->GetRecordProfileTimelineMarkers()) {
     return;
   }
 
   nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
   nsString filename;
   uint32_t lineNumber = 0;
 
-  js::AutoStableStringChars functionName(aCx);
+  JS::AutoStableStringChars functionName(aCx);
   if (rootedFunction) {
     JS::Rooted<JSString*> displayId(aCx, JS_GetFunctionDisplayId(rootedFunction));
     if (displayId) {
       if (!functionName.initTwoByte(aCx, displayId)) {
         JS_ClearPendingException(aCx);
         return;
       }
     }
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -47,17 +47,17 @@ function spawnWithObserver(browser, obse
     // before exiting the test
     "  let resolve = () => {",
     "    Services.obs.removeObserver(ConsoleObserver, 'console-api-log-event');",
     "    _resolve();",
     "  };",
     // This is the observer itself, it calls the passed-in function whenever
     // it encounters an event
     "  let ConsoleObserver = {",
-    "    QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),",
+    "    QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),",
     "    observe: function(aSubject, aTopic, aData) {",
     "      try {",
     "        (" + observerFunc.toString() + ")(aSubject.wrappedJSObject);",
     "      } catch (ex) {",
     "        ok(false, 'Exception thrown in observe: ' + ex);",
     "      }",
     "    }",
     "  };",
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -1157,16 +1157,18 @@ var interfaceNamesInGlobalScope =
     {name: "UserProximityEvent", insecureContext: true, disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "ValidityState", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VideoPlaybackQuality", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VideoStreamTrack", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "VisualViewport", insecureContext: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VRDisplay", insecureContext: true, releaseNonWindows: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VRDisplayCapabilities", insecureContext: true, releaseNonWindows: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VRDisplayEvent", insecureContext: true, releaseNonWindows: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VREyeParameters", insecureContext: true, releaseNonWindows: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/VisualViewport.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/.
+ *
+ * The origin of this IDL file is:
+ * https://wicg.github.io/visual-viewport/#the-visualviewport-interface
+ */
+
+interface VisualViewport : EventTarget {
+  readonly attribute double offsetLeft;
+  readonly attribute double offsetTop;
+
+  readonly attribute double pageLeft;
+  readonly attribute double pageTop;
+
+  readonly attribute double width;
+  readonly attribute double height;
+
+  readonly attribute double scale;
+};
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -11,16 +11,17 @@
  * http://dev.w3.org/csswg/cssom-view/
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/Overview.html
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
  * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
  * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
  * https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object
  * https://w3c.github.io/requestidlecallback/
  * https://drafts.css-houdini.org/css-paint-api-1/#dom-window-paintworklet
+ * https://wicg.github.io/visual-viewport/#the-visualviewport-interface
  */
 
 interface IID;
 interface nsIBrowserDOMWindow;
 interface XULControllers;
 interface nsIDOMWindowUtils;
 
 typedef OfflineResourceList ApplicationCache;
@@ -560,8 +561,14 @@ partial interface Window {
    * Getter funcion for IntlUtils, which provides helper functions for
    * localization.
    */
   [Throws, Func="IsChromeOrXBL"]
   readonly attribute IntlUtils intlUtils;
 };
 
 Window implements WebGPUProvider;
+
+partial interface Window {
+  [SameObject, Pref="dom.visualviewport.enabled", Replaceable]
+  readonly attribute VisualViewport visualViewport;
+
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -913,16 +913,17 @@ WEBIDL_FILES = [
     'UIEvent.webidl',
     'URL.webidl',
     'URLSearchParams.webidl',
     'ValidityState.webidl',
     'VideoPlaybackQuality.webidl',
     'VideoStreamTrack.webidl',
     'VideoTrack.webidl',
     'VideoTrackList.webidl',
+    'VisualViewport.webidl',
     'VRDisplay.webidl',
     'VRDisplayEvent.webidl',
     'VRServiceTest.webidl',
     'VTTCue.webidl',
     'VTTRegion.webidl',
     'WaveShaperNode.webidl',
     'WebAuthentication.webidl',
     'WebComponents.webidl',
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -69,16 +69,17 @@
 #include "nsIPromptFactory.h"
 #include "nsIWindowWatcher.h"
 #include "nsIConsoleService.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "nsStringBuffer.h"
 #include "nsIFileChannel.h"
 #include "mozilla/Telemetry.h"
+#include "js/JSON.h"
 #include "jsfriendapi.h"
 #include "GeckoProfiler.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/Attributes.h"
 #include "MultipartBlobImpl.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsIHttpChannelInternal.h"
--- a/gfx/ipc/GPUChild.cpp
+++ b/gfx/ipc/GPUChild.cpp
@@ -3,16 +3,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/. */
 #include "GPUChild.h"
 #include "gfxConfig.h"
 #include "gfxPrefs.h"
 #include "GPUProcessHost.h"
 #include "GPUProcessManager.h"
+#include "VRProcessManager.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TelemetryIPC.h"
 #include "mozilla/dom/CheckerboardReportService.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/gfx/gfxVars.h"
 #if defined(XP_WIN)
 # include "mozilla/gfx/DeviceManagerDx.h"
 #endif
@@ -105,16 +106,22 @@ GPUChild::EnsureGPUReady()
   }
 
   gfxPlatform::GetPlatform()->ImportGPUDeviceData(data);
   Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS_2, mHost->GetLaunchTime());
   mGPUReady = true;
   return true;
 }
 
+base::ProcessHandle
+GPUChild::GetChildProcessHandle()
+{
+  return mHost->GetChildProcessHandle();
+}
+
 PAPZInputBridgeChild*
 GPUChild::AllocPAPZInputBridgeChild(const LayersId& aLayersId)
 {
   APZInputBridgeChild* child = new APZInputBridgeChild();
   child->AddRef();
   return child;
 }
 
@@ -166,16 +173,34 @@ GPUChild::RecvInitCrashReporter(Shmem&& 
     GeckoProcessType_GPU,
     aShmem,
     aThreadId);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+GPUChild::RecvCreateVRProcess()
+{
+  // Make sure create VR process at the main process
+  MOZ_ASSERT(XRE_IsParentProcess());
+  if (gfxPrefs::VRProcessEnabled()) {
+    VRProcessManager::Initialize();
+    VRProcessManager* vr = VRProcessManager::Get();
+    MOZ_ASSERT(vr, "VRProcessManager must be initialized first.");
+
+    if (vr) {
+      vr->LaunchVRProcess();
+    }
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 GPUChild::RecvNotifyUiObservers(const nsCString& aTopic)
 {
   nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
   MOZ_ASSERT(obsSvc);
   if (obsSvc) {
     obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr);
   }
   return IPC_OK();
--- a/gfx/ipc/GPUChild.h
+++ b/gfx/ipc/GPUChild.h
@@ -31,27 +31,29 @@ class GPUChild final
 
 public:
   explicit GPUChild(GPUProcessHost* aHost);
   ~GPUChild();
 
   void Init();
 
   bool EnsureGPUReady();
+  base::ProcessHandle GetChildProcessHandle();
 
   PAPZInputBridgeChild* AllocPAPZInputBridgeChild(const LayersId& aLayersId) override;
   bool DeallocPAPZInputBridgeChild(PAPZInputBridgeChild* aActor) override;
 
   // gfxVarReceiver overrides.
   void OnVarChanged(const GfxVarUpdate& aVar) override;
 
   // PGPUChild overrides.
   mozilla::ipc::IPCResult RecvInitComplete(const GPUDeviceData& aData) override;
   mozilla::ipc::IPCResult RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog) override;
   mozilla::ipc::IPCResult RecvInitCrashReporter(Shmem&& shmem, const NativeThreadId& aThreadId) override;
+  mozilla::ipc::IPCResult RecvCreateVRProcess() override;
 
   mozilla::ipc::IPCResult RecvAccumulateChildHistograms(InfallibleTArray<HistogramAccumulation>&& aAccumulations) override;
   mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms(InfallibleTArray<KeyedHistogramAccumulation>&& aAccumulations) override;
   mozilla::ipc::IPCResult RecvUpdateChildScalars(InfallibleTArray<ScalarAction>&& aScalarActions) override;
   mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(InfallibleTArray<KeyedScalarAction>&& aScalarActions) override;
   mozilla::ipc::IPCResult RecvRecordChildEvents(nsTArray<ChildEventData>&& events) override;
   mozilla::ipc::IPCResult RecvRecordDiscardedData(const DiscardedData& aDiscardedData) override;
 
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -36,16 +36,17 @@
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/HangDetails.h"
 #include "nsDebugImpl.h"
 #include "nsIGfxInfo.h"
 #include "nsThreadManager.h"
 #include "prenv.h"
 #include "ProcessUtils.h"
+#include "VRGPUChild.h"
 #include "VRManager.h"
 #include "VRManagerParent.h"
 #include "VRThread.h"
 #include "VsyncBridgeParent.h"
 #if defined(XP_WIN)
 # include "mozilla/gfx/DeviceManagerDx.h"
 # include <process.h>
 # include <dwrite.h>
@@ -302,16 +303,23 @@ GPUParent::RecvInitImageBridge(Endpoint<
 mozilla::ipc::IPCResult
 GPUParent::RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint)
 {
   VRManagerParent::CreateForGPUProcess(std::move(aEndpoint));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+GPUParent::RecvInitVR(Endpoint<PVRGPUChild>&& aEndpoint)
+{
+  gfx::VRGPUChild::InitForGPUProcess(std::move(aEndpoint));
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 GPUParent::RecvInitUiCompositorController(const LayersId& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint)
 {
   UiCompositorControllerParent::Start(aRootLayerTreeId, std::move(aEndpoint));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 GPUParent::RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint)
@@ -460,16 +468,25 @@ GPUParent::RecvRequestMemoryReport(const
 {
   nsPrintfCString processName("GPU (pid %u)", (unsigned)getpid());
 
   mozilla::dom::MemoryReportRequestClient::Start(
     aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+GPUParent::RecvShutdownVR()
+{
+  if (gfxPrefs::VRProcessEnabled()) {
+    VRGPUChild::ShutDown();
+  }
+  return IPC_OK();
+}
+
 void
 GPUParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (AbnormalShutdown == aWhy) {
     NS_WARNING("Shutting down GPU process early due to a crash!");
     ProcessChild::QuickExit();
   }
 
--- a/gfx/ipc/GPUParent.h
+++ b/gfx/ipc/GPUParent.h
@@ -38,16 +38,17 @@ public:
   mozilla::ipc::IPCResult RecvInit(nsTArray<GfxPrefSetting>&& prefs,
                                    nsTArray<GfxVarUpdate>&& vars,
                                    const DevicePrefs& devicePrefs,
                                    nsTArray<LayerTreeIdMapping>&& mappings) override;
   mozilla::ipc::IPCResult RecvInitCompositorManager(Endpoint<PCompositorManagerParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override;
   mozilla::ipc::IPCResult RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
+  mozilla::ipc::IPCResult RecvInitVR(Endpoint<PVRGPUChild>&& aVRGPUChild) override;
   mozilla::ipc::IPCResult RecvInitUiCompositorController(const LayersId& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvUpdatePref(const GfxPrefSetting& pref) override;
   mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref) override;
   mozilla::ipc::IPCResult RecvNewContentCompositorManager(Endpoint<PCompositorManagerParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvNewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent>&& aEndpoint) override;
@@ -56,16 +57,17 @@ public:
   mozilla::ipc::IPCResult RecvAddLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override;
   mozilla::ipc::IPCResult RecvRemoveLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override;
   mozilla::ipc::IPCResult RecvNotifyGpuObservers(const nsCString& aTopic) override;
   mozilla::ipc::IPCResult RecvRequestMemoryReport(
     const uint32_t& generation,
     const bool& anonymize,
     const bool& minimizeMemoryUsage,
     const MaybeFileDesc& DMDFile) override;
+  mozilla::ipc::IPCResult RecvShutdownVR() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
 private:
   const TimeStamp mLaunchTime;
   RefPtr<VsyncBridgeParent> mVsyncBridge;
 #ifdef MOZ_GECKO_PROFILER
   RefPtr<ChildProfilerController> mProfilerController;
--- a/gfx/ipc/GPUProcessHost.cpp
+++ b/gfx/ipc/GPUProcessHost.cpp
@@ -155,16 +155,18 @@ GPUProcessHost::InitAfterConnect(bool aS
 void
 GPUProcessHost::Shutdown()
 {
   MOZ_ASSERT(!mShutdownRequested);
 
   mListener = nullptr;
 
   if (mGPUChild) {
+    mGPUChild->SendShutdownVR();
+
     // OnChannelClosed uses this to check if the shutdown was expected or
     // unexpected.
     mShutdownRequested = true;
 
     // The channel might already be closed if we got here unexpectedly.
     if (!mChannelClosed) {
       mGPUChild->Close();
     }
--- a/gfx/ipc/GraphicsMessages.ipdlh
+++ b/gfx/ipc/GraphicsMessages.ipdlh
@@ -10,16 +10,29 @@ using struct mozilla::null_t from "ipc/I
 using mozilla::gfx::FeatureStatus from "gfxTelemetry.h";
 using mozilla::gfx::BackendType from "mozilla/gfx/Types.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
 using gfxImageFormat from "mozilla/gfx/Types.h";
 
 namespace mozilla {
 namespace gfx {
 
+union GfxPrefValue {
+  bool;
+  int32_t;
+  uint32_t;
+  float;
+  nsCString;
+};
+
+struct GfxPrefSetting {
+  int32_t index;
+  GfxPrefValue value;
+};
+
 struct D3D11DeviceStatus
 {
   bool isWARP;
   bool textureSharingWorks;
   uint32_t featureLevel;
   DxgiAdapterDesc adapter;
   int32_t sequenceNumber;
   bool useNV12;
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -5,16 +5,17 @@
 
 include GraphicsMessages;
 include MemoryReportTypes;
 include HangTypes;
 include protocol PAPZInputBridge;
 include protocol PCompositorManager;
 include protocol PImageBridge;
 include protocol PProfiler;
+include protocol PVRGPU;
 include protocol PVRManager;
 include protocol PVsyncBridge;
 include protocol PUiCompositorController;
 include protocol PVideoDecoderManager;
 
 using base::ProcessId from "base/process.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using mozilla::Telemetry::HistogramAccumulation from "mozilla/TelemetryComms.h";
@@ -25,29 +26,16 @@ using mozilla::Telemetry::ChildEventData
 using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
 using mozilla::gfx::Feature from "gfxFeature.h";
 using mozilla::gfx::Fallback from "gfxFallback.h";
 using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
 
 namespace mozilla {
 namespace gfx {
 
-union GfxPrefValue {
-  bool;
-  int32_t;
-  uint32_t;
-  float;
-  nsCString;
-};
-
-struct GfxPrefSetting {
-  int32_t index;
-  GfxPrefValue value;
-};
-
 struct LayerTreeIdMapping {
   LayersId layersId;
   ProcessId ownerId;
 };
 
 // This protocol allows the UI process to talk to the GPU process. There is one
 // instance of this protocol, with the GPUParent living on the main thread of
 // the GPU process and the GPUChild living on the main thread of the UI process.
@@ -67,17 +55,18 @@ parent:
              LayerTreeIdMapping[] mapping);
 
   async InitCompositorManager(Endpoint<PCompositorManagerParent> endpoint);
   async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint);
   async InitImageBridge(Endpoint<PImageBridgeParent> endpoint);
   async InitVRManager(Endpoint<PVRManagerParent> endpoint);
   async InitUiCompositorController(LayersId rootLayerTreeId, Endpoint<PUiCompositorControllerParent> endpoint);
   async InitProfiler(Endpoint<PProfilerChild> endpoint);
-
+  // Forward GPU process its endpoints to the VR process.
+  async InitVR(Endpoint<PVRGPUChild> endpoint);
   // Called to update a gfx preference or variable.
   async UpdatePref(GfxPrefSetting pref);
   async UpdateVar(GfxVarUpdate var);
 
   // Create a new content-process compositor bridge.
   async NewContentCompositorManager(Endpoint<PCompositorManagerParent> endpoint);
   async NewContentImageBridge(Endpoint<PImageBridgeParent> endpoint);
   async NewContentVRManager(Endpoint<PVRManagerParent> endpoint);
@@ -98,30 +87,33 @@ parent:
   // Have a message be broadcasted to the GPU process by the GPU process
   // observer service.
   async NotifyGpuObservers(nsCString aTopic);
 
   async RequestMemoryReport(uint32_t generation,
                             bool anonymize,
                             bool minimizeMemoryUsage,
                             MaybeFileDesc DMDFile);
+  async ShutdownVR();
 
 child:
   // Sent when the GPU process has initialized devices. This occurs once, after
   // Init().
   async InitComplete(GPUDeviceData data);
 
   // Sent when APZ detects checkerboarding and apz checkerboard reporting is enabled.
   async ReportCheckerboard(uint32_t severity, nsCString log);
 
   // Graphics errors, analogous to PContent::GraphicsError
   async GraphicsError(nsCString aError);
 
   async InitCrashReporter(Shmem shmem, NativeThreadId threadId);
 
+  async CreateVRProcess();
+
   // Have a message be broadcasted to the UI process by the UI process
   // observer service.
   async NotifyUiObservers(nsCString aTopic);
 
   // Messages for reporting telemetry to the UI process.
   async AccumulateChildHistograms(HistogramAccumulation[] accumulations);
   async AccumulateChildKeyedHistograms(KeyedHistogramAccumulation[] accumulations);
   async UpdateChildScalars(ScalarAction[] actions);
--- a/gfx/thebes/DeviceManagerDx.cpp
+++ b/gfx/thebes/DeviceManagerDx.cpp
@@ -149,16 +149,17 @@ DeviceManagerDx::ReleaseD3D11()
   mD3D11Module.reset();
   sD3D11CreateDeviceFn = nullptr;
 }
 
 static inline bool
 ProcessOwnsCompositor()
 {
   return XRE_GetProcessType() == GeckoProcessType_GPU ||
+         XRE_GetProcessType() == GeckoProcessType_VR ||
          (XRE_IsParentProcess() && !gfxConfig::IsEnabled(Feature::GPU_PROCESS));
 }
 
 bool
 DeviceManagerDx::CreateCompositorDevices()
 {
   MOZ_ASSERT(ProcessOwnsCompositor());
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -26,16 +26,17 @@
 
 #include "gfxCrashReporterUtils.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "gfxEnv.h"
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 #include "gfxConfig.h"
+#include "VRProcessManager.h"
 #include "VRThread.h"
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #else
 #include <unistd.h>
 #endif
@@ -998,16 +999,17 @@ gfxPlatform::Shutdown()
     // We should only support the default GL provider on Windows; then, this
     // could go away. Unfortunately, we currently support WGL (the default) for
     // WebGL on Optimus.
     GLContextProviderEGL::Shutdown();
 #endif
 
     if (XRE_IsParentProcess()) {
       GPUProcessManager::Shutdown();
+      VRProcessManager::Shutdown();
     }
 
     gfx::Factory::ShutDown();
 
     delete gGfxPlatformPrefsLock;
 
     gfxVars::Shutdown();
     gfxPrefs::DestroySingleton();
--- a/gfx/thebes/gfxPrefs.cpp
+++ b/gfx/thebes/gfxPrefs.cpp
@@ -8,16 +8,18 @@
 #include "MainThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/GPUChild.h"
 #include "mozilla/gfx/GPUProcessManager.h"
+#include "VRProcessManager.h"
+#include "VRChild.h"
 
 using namespace mozilla;
 
 nsTArray<gfxPrefs::Pref*>* gfxPrefs::sGfxPrefList = nullptr;
 gfxPrefs* gfxPrefs::sInstance = nullptr;
 bool gfxPrefs::sInstanceHasBeenDestroyed = false;
 
 gfxPrefs&
@@ -87,16 +89,23 @@ gfxPrefs::Pref::OnChange()
 {
   if (auto gpm = gfx::GPUProcessManager::Get()) {
     if (gfx::GPUChild* gpu = gpm->GetGPUChild()) {
       GfxPrefValue value;
       GetLiveValue(&value);
       Unused << gpu->SendUpdatePref(gfx::GfxPrefSetting(mIndex, value));
     }
   }
+  if (auto vpm = gfx::VRProcessManager::Get()) {
+    if (gfx::VRChild* vr = vpm->GetVRChild()) {
+      GfxPrefValue value;
+      GetLiveValue(&value);
+      Unused << vr->SendUpdatePref(gfx::GfxPrefSetting(mIndex, value));
+    }
+  }
   FireChangeCallback();
 }
 
 void
 gfxPrefs::Pref::FireChangeCallback()
 {
   if (mChangeCallback) {
     GfxPrefValue value;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -367,16 +367,17 @@ private:
   DECL_GFX_PREF(Live, "apz.scale_repaint_delay_ms",            APZScaleRepaintDelay, int32_t, 500);
 
   DECL_GFX_PREF(Live, "browser.ui.scroll-toolbar-threshold",   ToolbarScrollThreshold, int32_t, 10);
   DECL_GFX_PREF(Live, "browser.ui.zoom.force-user-scalable",   ForceUserScalable, bool, false);
   DECL_GFX_PREF(Live, "browser.viewport.desktopWidth",         DesktopViewportWidth, int32_t, 980);
 
   DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled",  PluginAsyncDrawingEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.meta-viewport.enabled",             MetaViewportEnabled, bool, false);
+  DECL_GFX_PREF(Live, "dom.visualviewport.enabled",            VisualViewportEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.enabled",                        VREnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.autoactivate.enabled",           VRAutoActivateEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.controller_trigger_threshold",   VRControllerTriggerThreshold, float, 0.1f);
   DECL_GFX_PREF(Once, "dom.vr.external.enabled",               VRExternalEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.navigation.timeout",             VRNavigationTimeout, int32_t, 1000);
   DECL_GFX_PREF(Once, "dom.vr.oculus.enabled",                 VROculusEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.oculus.invisible.enabled",       VROculusInvisibleEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.oculus.present.timeout",         VROculusPresentTimeout, int32_t, 500);
@@ -386,16 +387,17 @@ private:
   DECL_GFX_PREF(Live, "dom.vr.controller.enumerate.interval",  VRControllerEnumerateInterval, int32_t, 1000);
   DECL_GFX_PREF(Live, "dom.vr.display.enumerate.interval",     VRDisplayEnumerateInterval, int32_t, 5000);
   DECL_GFX_PREF(Live, "dom.vr.inactive.timeout",               VRInactiveTimeout, int32_t, 5000);
   DECL_GFX_PREF(Live, "dom.vr.poseprediction.enabled",         VRPosePredictionEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.require-gesture",                VRRequireGesture, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.puppet.enabled",                 VRPuppetEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.puppet.submitframe",             VRPuppetSubmitFrame, uint32_t, 0);
   DECL_GFX_PREF(Live, "dom.vr.display.rafMaxDuration",         VRDisplayRafMaxDuration, uint32_t, 50);
+  DECL_GFX_PREF(Once, "dom.vr.process.enabled",                VRProcessEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.service.enabled",                VRServiceEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled",        PointerEventsEnabled, bool, false);
 
   DECL_GFX_PREF(Live, "general.smoothScroll",                  SmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.currentVelocityWeighting",
                 SmoothScrollCurrentVelocityWeighting, float, 0.25);
   DECL_GFX_PREF(Live, "general.smoothScroll.durationToIntervalRatio",
                 SmoothScrollDurationToIntervalRatio, int32_t, 200);
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -2,24 +2,26 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "VRManager.h"
 #include "VRManagerParent.h"
+#include "VRGPUChild.h"
 #include "VRThread.h"
 #include "gfxVR.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/VRDisplay.h"
 #include "mozilla/dom/GamepadEventTypes.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/Unused.h"
+#include "mozilla/gfx/GPUParent.h"
 
 #include "gfxPrefs.h"
 #include "gfxVR.h"
 #include "gfxVRExternal.h"
 #if defined(XP_WIN)
 #include "gfxVROculus.h"
 #endif
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
@@ -43,26 +45,29 @@ namespace gfx {
 
 static StaticRefPtr<VRManager> sVRManagerSingleton;
 
 /*static*/ void
 VRManager::ManagerInit()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  // TODO: We should make VRManager::ManagerInit
+  // be called when entering VR content pages.
   if (sVRManagerSingleton == nullptr) {
     sVRManagerSingleton = new VRManager();
     ClearOnShutdown(&sVRManagerSingleton);
   }
 }
 
 VRManager::VRManager()
   : mInitialized(false)
   , mVRDisplaysRequested(false)
   , mVRControllersRequested(false)
+  , mVRServiceStarted(false)
 {
   MOZ_COUNT_CTOR(VRManager);
   MOZ_ASSERT(sVRManagerSingleton == nullptr);
 
   RefPtr<VRSystemManager> mgr;
 
   /**
    * We must add the VRDisplayManager's to mManagers in a careful order to
@@ -76,17 +81,23 @@ VRManager::VRManager()
    *
    * OSVR will be used if Oculus SDK and OpenVR don't detect any HMDS,
    * to support everyone else.
    */
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
   // The VR Service accesses all hardware from a separate process
   // and replaces the other VRSystemManager when enabled.
-  mVRService = VRService::Create();
+  if (!gfxPrefs::VRProcessEnabled()) {
+    mVRService = VRService::Create();
+  } else if (gfxPrefs::VRProcessEnabled() && XRE_IsGPUProcess()) {
+    gfx::GPUParent* gpu = GPUParent::GetSingleton();
+    MOZ_ASSERT(gpu);
+    Unused << gpu->SendCreateVRProcess();
+  }
   if (mVRService) {
     mExternalManager = VRSystemManagerExternal::Create(mVRService->GetAPIShmem());
   }
   if (mExternalManager) {
     mManagers.AppendElement(mExternalManager);
   }
 #endif
 
@@ -140,33 +151,49 @@ VRManager::~VRManager()
 void
 VRManager::Destroy()
 {
   mVRDisplays.Clear();
   mVRControllers.Clear();
   for (uint32_t i = 0; i < mManagers.Length(); ++i) {
     mManagers[i]->Destroy();
   }
-
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+  if (mVRService) {
+    mVRService->Stop();
+    mVRService = nullptr;
+  }
+#endif
   mInitialized = false;
 }
 
 void
 VRManager::Shutdown()
 {
   mVRDisplays.Clear();
   mVRControllers.Clear();
   for (uint32_t i = 0; i < mManagers.Length(); ++i) {
     mManagers[i]->Shutdown();
   }
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
   if (mVRService) {
     mVRService->Stop();
   }
+  if (gfxPrefs::VRProcessEnabled()) {
+    RefPtr<Runnable> task = NS_NewRunnableFunction(
+      "VRGPUChild::SendStopVRService",
+      [] () -> void {
+        VRGPUChild* vrGPUChild = VRGPUChild::Get();
+        vrGPUChild->SendStopVRService();
+    });
+
+    NS_DispatchToMainThread(task.forget());
+  }
 #endif
+  mVRServiceStarted = false;
 }
 
 void
 VRManager::Init()
 {
   mInitialized = true;
 }
 
@@ -344,18 +371,32 @@ VRManager::RefreshVRDisplays(bool aMustD
 {
   /**
   * If we aren't viewing WebVR content, don't enumerate
   * new hardware, as it will cause some devices to power on
   * or interrupt other VR activities.
   */
   if (mVRDisplaysRequested || aMustDispatch) {
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
-    if (mVRService) {
-      mVRService->Start();
+    // Tell VR process to start VR service.
+    if (gfxPrefs::VRProcessEnabled() && !mVRServiceStarted) {
+      RefPtr<Runnable> task = NS_NewRunnableFunction(
+        "VRGPUChild::SendStartVRService",
+        [] () -> void {
+          VRGPUChild* vrGPUChild = VRGPUChild::Get();
+          vrGPUChild->SendStartVRService();
+      });
+
+      NS_DispatchToMainThread(task.forget());
+      mVRServiceStarted = true;
+    } else if (!gfxPrefs::VRProcessEnabled()){
+      if (mVRService) {
+        mVRService->Start();
+        mVRServiceStarted = true;
+      }
     }
 #endif
     EnumerateVRDisplays();
   }
 
   /**
    * VRSystemManager::GetHMDs will not activate new hardware
    * or result in interruption of other VR activities.
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -97,14 +97,15 @@ private:
   TimeStamp mLastActiveTime;
   RefPtr<VRSystemManagerPuppet> mPuppetManager;
   RefPtr<VRSystemManagerExternal> mExternalManager;
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
   RefPtr<VRService> mVRService;
 #endif
   bool mVRDisplaysRequested;
   bool mVRControllersRequested;
+  bool mVRServiceStarted;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // GFX_VR_MANAGER_H
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -349,16 +349,20 @@ VRSystemManagerExternal::VRSystemManager
   mShmemFD = 0;
 #elif defined(XP_WIN)
   mShmemFile = NULL;
 #elif defined(MOZ_WIDGET_ANDROID)
   mDoShutdown = false;
   mExternalStructFailed = false;
   mEnumerationCompleted = false;
 #endif
+
+  if (!aAPIShmem) {
+    OpenShmem();
+  }
 }
 
 VRSystemManagerExternal::~VRSystemManagerExternal()
 {
   CloseShmem();
 }
 
 void
@@ -395,17 +399,24 @@ VRSystemManagerExternal::OpenShmem()
     // TODO - Implement logging
     mExternalShmem = NULL;
     CloseShmem();
     return;
   }
 
 #elif defined(XP_WIN)
   if (mShmemFile == NULL) {
-    mShmemFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, kShmemName);
+    if (gfxPrefs::VRProcessEnabled()) {
+      mShmemFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
+                                      sizeof(VRExternalShmem), kShmemName);
+	    MOZ_ASSERT(GetLastError() == 0);
+    } else {
+      mShmemFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, kShmemName);
+    }
+
     if (mShmemFile == NULL) {
       // TODO - Implement logging
       CloseShmem();
       return;
     }
   }
   LARGE_INTEGER length;
   length.QuadPart = sizeof(VRExternalShmem);
@@ -499,17 +510,18 @@ VRSystemManagerExternal::CloseShmem()
 VRSystemManagerExternal::Create(VRExternalShmem* aAPIShmem /* = nullptr*/)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!gfxPrefs::VREnabled()) {
     return nullptr;
   }
 
-  if (!gfxPrefs::VRExternalEnabled() && aAPIShmem == nullptr) {
+  if ((!gfxPrefs::VRExternalEnabled() && aAPIShmem == nullptr) ||
+      !XRE_IsGPUProcess()) {
     return nullptr;
   }
 
   RefPtr<VRSystemManagerExternal> manager = new VRSystemManagerExternal(aAPIShmem);
   return manager.forget();
 }
 
 void
--- a/gfx/vr/gfxVRExternal.h
+++ b/gfx/vr/gfxVRExternal.h
@@ -114,17 +114,17 @@ protected:
   virtual ~VRSystemManagerExternal();
 
 private:
   // there can only be one
   RefPtr<impl::VRDisplayExternal> mDisplay;
 #if defined(XP_MACOSX)
   int mShmemFD;
 #elif defined(XP_WIN)
-  HANDLE mShmemFile;
+  base::ProcessHandle mShmemFile;
 #elif defined(MOZ_WIDGET_ANDROID)
   bool mDoShutdown;
   bool mExternalStructFailed;
   bool mEnumerationCompleted;
 #endif
 
   volatile VRExternalShmem* mExternalShmem;
 #if !defined(MOZ_WIDGET_ANDROID)
--- a/gfx/vr/gfxVRPuppet.h
+++ b/gfx/vr/gfxVRPuppet.h
@@ -9,20 +9,28 @@
 
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
 #include "nsRefPtrHashtable.h"
 
 #include "gfxVR.h"
 #include "VRDisplayLocal.h"
 
+#if defined(XP_WIN)
+#include "CompositorD3D11.h"
+#endif
+
 #if defined(XP_MACOSX)
 class MacIOSurface;
 #endif
 namespace mozilla {
+namespace layers {
+struct VertexShaderConstants;
+struct PixelShaderConstants;
+}
 namespace gfx {
 namespace impl {
 
 class VRDisplayPuppet : public VRDisplayLocal
 {
 public:
   void SetDisplayInfo(const VRDisplayInfo& aDisplayInfo);
   void SetSensorState(const VRHMDSensorState& aSensorState);
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/PVR.ipdl
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
+
+using mozilla::TimeStamp from "mozilla/TimeStamp.h";
+
+include GraphicsMessages;
+include protocol PVRGPU;
+
+namespace mozilla {
+namespace gfx {
+
+async protocol PVR
+{
+parent:
+  async NewGPUVRManager(Endpoint<PVRGPUParent> endpoint);
+  async Init(GfxPrefSetting[] prefs, GfxVarUpdate[] vars, DevicePrefs devicePrefs);
+  async NotifyVsync(TimeStamp vsyncTimestamp);
+
+  async UpdatePref(GfxPrefSetting pref);
+  async UpdateVar(GfxVarUpdate var);
+};
+
+} // namespace gfx
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/PVRGPU.ipdl
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
+
+namespace mozilla {
+namespace gfx {
+
+// IPC for VR-Content process
+async protocol PVRGPU
+{
+parent:
+  async StartVRService();
+  async StopVRService();
+};
+
+} // gfx
+} // mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRChild.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VRChild.h"
+#include "VRProcessParent.h"
+#include "gfxConfig.h"
+
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/SystemGroup.h"
+#include "mozilla/VsyncDispatcher.h"
+
+namespace mozilla {
+namespace gfx {
+
+VRChild::VRChild(VRProcessParent* aHost)
+ : mHost(aHost)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+}
+
+void
+VRChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  gfxVars::RemoveReceiver(this);
+  mHost->OnChannelClosed();
+  XRE_ShutdownChildProcess();
+}
+
+void
+VRChild::Init()
+{
+  // Build a list of prefs the VR process will need. Note that because we
+  // limit the VR process to prefs contained in gfxPrefs, we can simplify
+  // the message in two ways: one, we only need to send its index in gfxPrefs
+  // rather than its name, and two, we only need to send prefs that don't
+  // have their default value.
+  // Todo: Consider to make our own vrPrefs that we are interested in VR process.
+  nsTArray<GfxPrefSetting> prefs;
+  for (auto pref : gfxPrefs::all()) {
+    if (pref->HasDefaultValue()) {
+      continue;
+    }
+
+    GfxPrefValue value;
+    pref->GetCachedValue(&value);
+    prefs.AppendElement(GfxPrefSetting(pref->Index(), value));
+  }
+  nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars();
+
+  DevicePrefs devicePrefs;
+  devicePrefs.hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING);
+  devicePrefs.d3d11Compositing() = gfxConfig::GetValue(Feature::D3D11_COMPOSITING);
+  devicePrefs.oglCompositing() = gfxConfig::GetValue(Feature::OPENGL_COMPOSITING);
+  devicePrefs.advancedLayers() = gfxConfig::GetValue(Feature::ADVANCED_LAYERS);
+  devicePrefs.useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D);
+
+  SendInit(prefs, updates, devicePrefs);
+  gfxVars::AddReceiver(this);
+}
+
+void
+VRChild::OnVarChanged(const GfxVarUpdate& aVar)
+{
+  SendUpdateVar(aVar);
+}
+
+class DeferredDeleteVRChild : public Runnable
+{
+public:
+  explicit DeferredDeleteVRChild(UniquePtr<VRChild>&& aChild)
+    : Runnable("gfx::DeferredDeleteVRChild")
+    , mChild(std::move(aChild))
+  {
+  }
+
+  NS_IMETHODIMP Run() override {
+    return NS_OK;
+  }
+
+private:
+  UniquePtr<VRChild> mChild;
+};
+
+/* static */ void
+VRChild::Destroy(UniquePtr<VRChild>&& aChild)
+{
+  NS_DispatchToMainThread(new DeferredDeleteVRChild(std::move(aChild)));
+}
+
+} // namespace gfx
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRChild.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_CHILD_H
+#define GFX_VR_CHILD_H
+
+#include "mozilla/gfx/PVRChild.h"
+#include "mozilla/gfx/gfxVarReceiver.h"
+#include "mozilla/VsyncDispatcher.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRProcessParent;
+class VRChild;
+
+class VRChild final
+ : public PVRChild,
+   public gfxVarReceiver {
+
+public:
+  explicit VRChild(VRProcessParent* aHost);
+  ~VRChild() = default;
+
+  static void Destroy(UniquePtr<VRChild>&& aChild);
+  void Init();
+  virtual void OnVarChanged(const GfxVarUpdate& aVar) override;
+
+protected:
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+  VRProcessParent* mHost;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif  // GFX_VR_CHILD_H
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUChild.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VRGPUChild.h"
+
+
+namespace mozilla {
+namespace gfx {
+
+static StaticRefPtr<VRGPUChild> sVRGPUChildSingleton;
+
+/* static */ bool
+VRGPUChild::InitForGPUProcess(Endpoint<PVRGPUChild>&& aEndpoint)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!sVRGPUChildSingleton);
+
+  RefPtr<VRGPUChild> child(new VRGPUChild());
+  if (!aEndpoint.Bind(child)) {
+    return false;
+  }
+  sVRGPUChildSingleton = child;
+  return true;
+}
+
+/* static */ bool
+VRGPUChild::IsCreated()
+{
+  return !!sVRGPUChildSingleton;
+}
+
+/* static */ VRGPUChild*
+VRGPUChild::Get()
+{
+  MOZ_ASSERT(IsCreated(), "VRGPUChild haven't initialized yet.");
+  return sVRGPUChildSingleton;
+}
+
+/*static*/ void
+VRGPUChild::ShutDown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (sVRGPUChildSingleton) {
+    sVRGPUChildSingleton->Destroy();
+    sVRGPUChildSingleton = nullptr;
+  }
+}
+
+class DeferredDeleteVRGPUChild : public Runnable
+{
+public:
+  explicit DeferredDeleteVRGPUChild(RefPtr<VRGPUChild> aChild)
+    : Runnable("gfx::DeferredDeleteVRGPUChild")
+    , mChild(std::move(aChild))
+  {
+  }
+
+  NS_IMETHODIMP Run() override {
+    mChild->Close();
+    return NS_OK;
+  }
+
+private:
+  RefPtr<VRGPUChild> mChild;
+};
+
+void
+VRGPUChild::Destroy()
+{
+  // Keep ourselves alive until everything has been shut down
+  RefPtr<VRGPUChild> selfRef = this;
+  NS_DispatchToMainThread(new DeferredDeleteVRGPUChild(this));
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUChild.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_GPU_CHILD_H
+#define GFX_VR_GPU_CHILD_H
+
+#include "mozilla/gfx/PVRGPUChild.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRGPUChild final : public PVRGPUChild
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRGPUChild);
+
+  static VRGPUChild* Get();
+  static bool InitForGPUProcess(Endpoint<PVRGPUChild>&& aEndpoint);
+  static bool IsCreated();
+  static void ShutDown();
+
+protected:
+  explicit VRGPUChild() {}
+  ~VRGPUChild() {}
+
+  void Destroy();
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(VRGPUChild);
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_GPU_CHILD_H
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUParent.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VRGPUParent.h"
+
+#include "mozilla/ipc/ProcessChild.h"
+
+
+namespace mozilla {
+namespace gfx {
+
+using namespace ipc;
+
+
+VRGPUParent::VRGPUParent(ProcessId aChildProcessId)
+{
+  MOZ_COUNT_CTOR(VRGPUParent);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  SetOtherProcessId(aChildProcessId);
+}
+
+void
+VRGPUParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+  if (mVRService) {
+    mVRService->Stop();
+    mVRService = nullptr;
+  }
+#endif
+
+  MessageLoop::current()->PostTask(
+  NewRunnableMethod("gfx::VRGPUParent::DeferredDestroy",
+                    this,
+                    &VRGPUParent::DeferredDestroy));
+}
+
+void
+VRGPUParent::DeferredDestroy()
+{
+  mSelfRef = nullptr;
+}
+
+/* static */ RefPtr<VRGPUParent>
+VRGPUParent::CreateForGPU(Endpoint<PVRGPUParent>&& aEndpoint)
+{
+  RefPtr<VRGPUParent> vcp = new VRGPUParent(aEndpoint.OtherPid());
+  MessageLoop::current()->PostTask(
+    NewRunnableMethod<Endpoint<PVRGPUParent>&&>(
+    "gfx::VRGPUParent::Bind",
+    vcp,
+    &VRGPUParent::Bind,
+    std::move(aEndpoint)));
+
+  return vcp;
+}
+
+void
+VRGPUParent::Bind(Endpoint<PVRGPUParent>&& aEndpoint)
+{
+  if (!aEndpoint.Bind(this)) {
+    return;
+  }
+
+  mSelfRef = this;
+}
+
+mozilla::ipc::IPCResult
+VRGPUParent::RecvStartVRService()
+{
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+  mVRService = VRService::Create();
+  MOZ_ASSERT(mVRService);
+
+  mVRService->Start();
+#endif
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+VRGPUParent::RecvStopVRService()
+{
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+  if (mVRService) {
+    mVRService->Stop();
+    mVRService = nullptr;
+  }
+#endif
+
+  return IPC_OK();
+}
+
+} // namespace gfx
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRGPUParent.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_GPU_PARENT_H
+#define GFX_VR_GPU_PARENT_H
+
+#include "mozilla/gfx/PVRGPUParent.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRGPUParent final : public PVRGPUParent {
+   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRGPUParent)
+
+public:
+  explicit VRGPUParent(ProcessId aChildProcessId);
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+  static RefPtr<VRGPUParent> CreateForGPU(Endpoint<PVRGPUParent>&& aEndpoint);
+
+protected:
+  ~VRGPUParent() {}
+
+  void Bind(Endpoint<PVRGPUParent>&& aEndpoint);
+  virtual mozilla::ipc::IPCResult RecvStartVRService() override;
+  virtual mozilla::ipc::IPCResult RecvStopVRService() override;
+
+private:
+  void DeferredDestroy();
+
+  RefPtr<VRGPUParent> mSelfRef;
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+  RefPtr<VRService> mVRService;
+#endif
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_CONTENT_PARENT_H
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRParent.cpp
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VRParent.h"
+#include "VRGPUParent.h"
+#include "VRManager.h"
+#include "gfxConfig.h"
+
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/ProcessChild.h"
+
+#if defined(XP_WIN)
+#include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+using mozilla::ipc::IPCResult;
+
+VRParent::VRParent()
+ : mVRGPUParent(nullptr)
+{
+}
+
+IPCResult
+VRParent::RecvNewGPUVRManager(Endpoint<PVRGPUParent>&& aEndpoint)
+{
+  RefPtr<VRGPUParent> vrGPUParent = VRGPUParent::CreateForGPU(std::move(aEndpoint));
+  if (!vrGPUParent) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  mVRGPUParent = std::move(vrGPUParent);
+  return IPC_OK();
+}
+
+IPCResult
+VRParent::RecvInit(nsTArray<GfxPrefSetting>&& prefs,
+                   nsTArray<GfxVarUpdate>&& vars,
+                   const DevicePrefs& devicePrefs)
+{
+  const nsTArray<gfxPrefs::Pref*>& globalPrefs = gfxPrefs::all();
+  for (auto& setting : prefs) {
+    gfxPrefs::Pref* pref = globalPrefs[setting.index()];
+    pref->SetCachedValue(setting.value());
+  }
+  for (const auto& var : vars) {
+    gfxVars::ApplyUpdate(var);
+  }
+
+  // Inherit device preferences.
+  gfxConfig::Inherit(Feature::HW_COMPOSITING, devicePrefs.hwCompositing());
+  gfxConfig::Inherit(Feature::D3D11_COMPOSITING, devicePrefs.d3d11Compositing());
+  gfxConfig::Inherit(Feature::OPENGL_COMPOSITING, devicePrefs.oglCompositing());
+  gfxConfig::Inherit(Feature::ADVANCED_LAYERS, devicePrefs.advancedLayers());
+  gfxConfig::Inherit(Feature::DIRECT2D, devicePrefs.useD2D1());
+
+#if defined(XP_WIN)
+  if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+    DeviceManagerDx::Get()->CreateCompositorDevices();
+  }
+#endif
+  return IPC_OK();
+}
+
+IPCResult
+VRParent::RecvNotifyVsync(const TimeStamp& vsyncTimestamp)
+{
+  VRManager* vm = VRManager::Get();
+  vm->NotifyVsync(vsyncTimestamp);
+  return IPC_OK();
+}
+
+IPCResult
+VRParent::RecvUpdatePref(const GfxPrefSetting& setting)
+{
+  gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
+  pref->SetCachedValue(setting.value());
+  return IPC_OK();
+}
+
+IPCResult
+VRParent::RecvUpdateVar(const GfxVarUpdate& aUpdate)
+{
+  gfxVars::ApplyUpdate(aUpdate);
+  return IPC_OK();
+}
+
+void
+VRParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (AbnormalShutdown == aWhy) {
+    NS_WARNING("Shutting down VR process early due to a crash!");
+    ProcessChild::QuickExit();
+  }
+
+  mVRGPUParent->Close();
+#if defined(XP_WIN)
+  DeviceManagerDx::Shutdown();
+#endif
+  gfxVars::Shutdown();
+  gfxConfig::Shutdown();
+  gfxPrefs::DestroySingleton();
+  XRE_ShutdownChildProcess();
+}
+
+bool
+VRParent::Init(base::ProcessId aParentPid,
+               const char* aParentBuildID,
+               MessageLoop* aIOLoop,
+               IPC::Channel* aChannel)
+{
+  // Now it's safe to start IPC.
+  if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) {
+    return false;
+  }
+
+  // This must be checked before any IPDL message, which may hit sentinel
+  // errors due to parent and content processes having different
+  // versions.
+  MessageChannel* channel = GetIPCChannel();
+  if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
+    // We need to quit this process if the buildID doesn't match the parent's.
+    // This can occur when an update occurred in the background.
+    ProcessChild::QuickExit();
+  }
+
+  // Ensure gfxPrefs are initialized.
+  gfxPrefs::GetSingleton();
+  gfxConfig::Init();
+  gfxVars::Initialize();
+#if defined(XP_WIN)
+  DeviceManagerDx::Init();
+#endif
+  return true;
+}
+
+} // namespace gfx
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRParent.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_PARENT_H
+#define GFX_VR_PARENT_H
+
+#include "mozilla/gfx/PVRParent.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRGPUParent;
+class VRService;
+class VRSystemManagerExternal;
+
+class VRParent final : public PVRParent {
+
+public:
+  VRParent();
+  bool Init(base::ProcessId aParentPid,
+            const char* aParentBuildID,
+            MessageLoop* aIOLoop,
+            IPC::Channel* aChannel);
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+protected:
+  virtual mozilla::ipc::IPCResult RecvNewGPUVRManager(Endpoint<PVRGPUParent>&& aEndpoint) override;
+  virtual mozilla::ipc::IPCResult RecvInit(nsTArray<GfxPrefSetting>&& prefs,
+                                           nsTArray<GfxVarUpdate>&& vars,
+                                           const DevicePrefs& devicePrefs) override;
+  virtual mozilla::ipc::IPCResult RecvNotifyVsync(const TimeStamp& vsyncTimestamp) override;
+  virtual mozilla::ipc::IPCResult RecvUpdatePref(const GfxPrefSetting& setting) override;
+  virtual mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref) override;
+
+private:
+  RefPtr<VRGPUParent> mVRGPUParent;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_PARENT_H
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessChild.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VRProcessChild.h"
+
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/ipc/IOThreadChild.h"
+
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using mozilla::ipc::IOThreadChild;
+
+
+VRProcessChild::VRProcessChild(ProcessId aParentPid)
+  : ProcessChild(aParentPid)
+#if defined(aParentPid)
+  , mVR(nullptr)
+#endif
+{
+}
+
+VRProcessChild::~VRProcessChild()
+{
+}
+
+bool
+VRProcessChild::Init(int aArgc, char* aArgv[])
+{
+  BackgroundHangMonitor::Startup();
+
+  char* parentBuildID = nullptr;
+  for (int i = 1; i < aArgc; i++) {
+    if (strcmp(aArgv[i], "-parentBuildID") == 0) {
+      parentBuildID = aArgv[i + 1];
+    }
+  }
+
+  mVR.Init(ParentPid(), parentBuildID,
+           IOThreadChild::message_loop(),
+           IOThreadChild::channel());
+
+  return true;
+}
+
+void
+VRProcessChild::CleanUp()
+{
+  NS_ShutdownXPCOM(nullptr);
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessChild.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_PROCESS_CHILD_H
+#define GFX_VR_PROCESS_CHILD_H
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "VRParent.h"
+
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Contains the VRChild object that facilitates IPC communication to/from
+ * the instance of the VR library that is run in this process.
+ */
+class VRProcessChild final : public mozilla::ipc::ProcessChild
+{
+protected:
+  typedef mozilla::ipc::ProcessChild ProcessChild;
+
+public:
+  explicit VRProcessChild(ProcessId aParentPid);
+  ~VRProcessChild();
+
+  // ProcessChild functions.
+  virtual bool Init(int aArgc, char* aArgv[]) override;
+  virtual void CleanUp() override;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(VRProcessChild);
+
+  VRParent mVR;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_PROCESS_CHILD_H */
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessManager.cpp
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VRProcessManager.h"
+
+#include "VRProcessParent.h"
+#include "VRChild.h"
+#include "VRGPUChild.h"
+#include "VRGPUParent.h"
+
+
+namespace mozilla {
+namespace gfx {
+
+static StaticAutoPtr<VRProcessManager> sSingleton;
+
+/* static */ VRProcessManager*
+VRProcessManager::Get()
+{
+  return sSingleton;
+}
+
+/* static */ void
+VRProcessManager::Initialize()
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  sSingleton = new VRProcessManager();
+}
+
+/* static */ void
+VRProcessManager::Shutdown()
+{
+  sSingleton = nullptr;
+}
+
+VRProcessManager::VRProcessManager()
+ : mProcess(nullptr)
+{
+  MOZ_COUNT_CTOR(VRProcessManager);
+
+  mObserver = new Observer(this);
+  nsContentUtils::RegisterShutdownObserver(mObserver);
+}
+
+VRProcessManager::~VRProcessManager()
+{
+  MOZ_COUNT_DTOR(VRProcessManager);
+
+  DestroyProcess();
+  // The VR process should have already been shut down.
+  MOZ_ASSERT(!mProcess);
+}
+
+void
+VRProcessManager::LaunchVRProcess()
+{
+  if (mProcess) {
+    return;
+  }
+
+  // The subprocess is launched asynchronously, so we wait for a callback to
+  // acquire the IPDL actor.
+  mProcess = new VRProcessParent();
+  if (!mProcess->Launch()) {
+    DisableVRProcess("Failed to launch VR process");
+  }
+}
+
+void
+VRProcessManager::DisableVRProcess(const char* aMessage)
+{
+  if (!gfxPrefs::VRProcessEnabled()) {
+    return;
+  }
+
+  DestroyProcess();
+}
+
+void
+VRProcessManager::DestroyProcess()
+{
+  if (!mProcess) {
+    return;
+  }
+
+  mProcess->Shutdown();
+  mProcess = nullptr;
+}
+
+bool
+VRProcessManager::CreateGPUBridges(base::ProcessId aOtherProcess,
+                                   mozilla::ipc::Endpoint<PVRGPUChild>* aOutVRBridge)
+{
+  if (!CreateGPUVRManager(aOtherProcess, aOutVRBridge)) {
+    return false;
+  }
+  return true;
+}
+
+bool
+VRProcessManager::CreateGPUVRManager(base::ProcessId aOtherProcess,
+                                     mozilla::ipc::Endpoint<PVRGPUChild>* aOutEndpoint)
+{
+  base::ProcessId vrparentPid = mProcess
+                                ? mProcess->OtherPid()  // VR process id.
+                                : base::GetCurrentProcId();
+
+  ipc::Endpoint<PVRGPUParent> vrparentPipe;
+  ipc::Endpoint<PVRGPUChild> vrchildPipe;
+  nsresult rv = PVRGPU::CreateEndpoints(vrparentPid,   // vr process id
+                                        aOtherProcess, // gpu process id
+                                        &vrparentPipe,
+                                        &vrchildPipe);
+
+  if (NS_FAILED(rv)) {
+	  gfxCriticalNote << "Could not create gpu-vr bridge: " << hexa(int(rv));
+	  return false;
+  }
+
+  // Bind vr-gpu pipe to VRParent and make a PVRGPU connection.
+  VRChild* vrChild = mProcess->GetActor();
+  vrChild->SendNewGPUVRManager(std::move(vrparentPipe));
+
+  *aOutEndpoint = std::move(vrchildPipe);
+  return true;
+}
+
+NS_IMPL_ISUPPORTS(VRProcessManager::Observer, nsIObserver);
+
+VRProcessManager::Observer::Observer(VRProcessManager* aManager)
+ : mManager(aManager)
+{
+}
+
+NS_IMETHODIMP
+VRProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+{
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    mManager->OnXPCOMShutdown();
+  }
+  return NS_OK;
+}
+
+void
+VRProcessManager::CleanShutdown()
+{
+  DestroyProcess();
+}
+
+void
+VRProcessManager::OnXPCOMShutdown()
+{
+  if (mObserver) {
+    nsContentUtils::UnregisterShutdownObserver(mObserver);
+    mObserver = nullptr;
+  }
+
+  CleanShutdown();
+}
+
+VRChild*
+VRProcessManager::GetVRChild()
+{
+  return mProcess->GetActor();
+}
+
+} // namespace gfx
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessManager.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef GFX_VR_PROCESS_MANAGER_H
+#define GFX_VR_PROCESS_MANAGER_H
+
+
+namespace mozilla {
+namespace gfx {
+
+class VRProcessParent;
+class VRManagerChild;
+class PVRGPUChild;
+class VRChild;
+
+// The VRProcessManager is a singleton responsible for creating VR-bound
+// objects that may live in another process.
+class VRProcessManager final
+{
+public:
+  static VRProcessManager* Get();
+  static void Initialize();
+  static void Shutdown();
+
+  ~VRProcessManager();
+
+  // If not using a VR process, launch a new VR process asynchronously.
+  void LaunchVRProcess();
+  void DestroyProcess();
+
+  bool CreateGPUBridges(base::ProcessId aOtherProcess,
+                        mozilla::ipc::Endpoint<PVRGPUChild>* aOutVRBridge);
+
+  VRChild* GetVRChild();
+
+private:
+  VRProcessManager();
+
+  DISALLOW_COPY_AND_ASSIGN(VRProcessManager);
+
+  bool CreateGPUVRManager(base::ProcessId aOtherProcess,
+                              mozilla::ipc::Endpoint<PVRGPUChild>* aOutEndpoint);
+  void OnXPCOMShutdown();
+  void CleanShutdown();
+
+  // Permanently disable the VR process and record a message why.
+  void DisableVRProcess(const char* aMessage);
+
+  class Observer final : public nsIObserver {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOBSERVER
+    explicit Observer(VRProcessManager* aManager);
+
+  protected:
+    ~Observer() {}
+
+    VRProcessManager* mManager;
+  };
+  friend class Observer;
+
+  RefPtr<Observer> mObserver;
+  VRProcessParent* mProcess;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_PROCESS_MANAGER_H
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessParent.cpp
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VRProcessParent.h"
+#include "VRGPUChild.h"
+#include "VRProcessManager.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/GPUChild.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/ipc/ProtocolUtils.h"       // for IToplevelProtocol
+#include "mozilla/TimeStamp.h"               // for TimeStamp
+#include "mozilla/Unused.h"
+#include "VRChild.h"
+#include "VRManager.h"
+#include "VRThread.h"
+#include "gfxVRPuppet.h"
+
+#include "nsAppRunner.h"                  // for IToplevelProtocol
+#include "mozilla/ipc/ProtocolUtils.h"
+
+using std::vector;
+using std::string;
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace gfx {
+
+VRProcessParent::VRProcessParent()
+  : GeckoChildProcessHost(GeckoProcessType_VR),
+    mTaskFactory(this),
+    mChannelClosed(false)
+{
+  MOZ_COUNT_CTOR(VRProcessParent);
+}
+
+VRProcessParent::~VRProcessParent()
+{
+  // Cancel all tasks. We don't want anything triggering after our caller
+  // expects this to go away.
+  {
+    MonitorAutoLock lock(mMonitor);
+    mTaskFactory.RevokeAll();
+  }
+  MOZ_COUNT_DTOR(VRProcessParent);
+}
+
+bool
+VRProcessParent::Launch()
+{
+  mLaunchThread = NS_GetCurrentThread();
+
+  std::vector<std::string> extraArgs;
+  nsCString parentBuildID(mozilla::PlatformBuildID());
+  extraArgs.push_back("-parentBuildID");
+  extraArgs.push_back(parentBuildID.get());
+
+  if (!GeckoChildProcessHost::AsyncLaunch(extraArgs)) {
+    return false;
+  }
+  return true;
+}
+
+void
+VRProcessParent::Shutdown()
+{
+  if (mVRChild) {
+    // The channel might already be closed if we got here unexpectedly.
+    if (!mChannelClosed) {
+      mVRChild->Close();
+    }
+
+#ifndef NS_FREE_PERMANENT_DATA
+    // No need to communicate shutdown, the VR process doesn't need to
+    // communicate anything back.
+    KillHard("NormalShutdown");
+#endif
+
+    // If we're shutting down unexpectedly, we're in the middle of handling an
+    // ActorDestroy for PGPUChild, which is still on the stack. We'll return
+    // back to OnChannelClosed.
+    //
+    // Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild
+    // acknowledges shutdown.
+    return;
+  }
+
+  DestroyProcess();
+}
+
+static void
+DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
+{
+  XRE_GetIOMessageLoop()->
+    PostTask(mozilla::MakeAndAddRef<DeleteTask<GeckoChildProcessHost>>(aSubprocess));
+}
+
+
+void
+VRProcessParent::DestroyProcess()
+{
+  mLaunchThread->Dispatch(NewRunnableFunction("DestroyProcessRunnable", DelayedDeleteSubprocess, this));
+}
+
+void
+VRProcessParent::InitAfterConnect(bool aSucceeded)
+{
+  if (aSucceeded) {
+    mVRChild = MakeUnique<VRChild>(this);
+
+    DebugOnly<bool> rv =
+      mVRChild->Open(GetChannel(), base::GetProcId(GetChildProcessHandle()));
+    MOZ_ASSERT(rv);
+
+    mVRChild->Init();
+
+    // Make vr-gpu process connection
+    GPUChild* gpuChild = GPUProcessManager::Get()->GetGPUChild();
+    MOZ_ASSERT(gpuChild);
+
+    Endpoint<PVRGPUChild> vrGPUBridge;
+    VRProcessManager* vpm = VRProcessManager::Get();
+    DebugOnly<bool> opened = vpm->CreateGPUBridges(gpuChild->OtherPid(), &vrGPUBridge);
+    MOZ_ASSERT(opened);
+
+    Unused << gpuChild->SendInitVR(std::move(vrGPUBridge));
+  }
+}
+
+void
+VRProcessParent::KillHard(const char* aReason)
+{
+  ProcessHandle handle = GetChildProcessHandle();
+  if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) {
+    NS_WARNING("failed to kill subprocess!");
+  }
+
+  SetAlreadyDead();
+}
+
+void
+VRProcessParent::OnChannelError()
+{
+  MOZ_ASSERT(false, "VR process channel error.");
+}
+
+void
+VRProcessParent::OnChannelConnected(int32_t peer_pid)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  GeckoChildProcessHost::OnChannelConnected(peer_pid);
+
+  // Post a task to the main thread. Take the lock because mTaskFactory is not
+  // thread-safe.
+  RefPtr<Runnable> runnable;
+  {
+    MonitorAutoLock lock(mMonitor);
+    runnable = mTaskFactory.NewRunnableMethod(&VRProcessParent::OnChannelConnectedTask);
+  }
+  NS_DispatchToMainThread(runnable);
+}
+
+void
+VRProcessParent::OnChannelConnectedTask()
+{
+  InitAfterConnect(true);
+}
+
+void
+VRProcessParent::OnChannelErrorTask()
+{
+  MOZ_ASSERT(false, "VR process channel error.");
+}
+
+void
+VRProcessParent::OnChannelClosed()
+{
+  mChannelClosed = true;
+  DestroyProcess();
+
+  // Release the actor.
+  VRChild::Destroy(std::move(mVRChild));
+  MOZ_ASSERT(!mVRChild);
+}
+
+base::ProcessId
+VRProcessParent::OtherPid()
+{
+  return mVRChild->OtherPid();
+}
+
+} // namespace gfx
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessParent.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_PROCESS_PARENT_H
+#define GFX_VR_PROCESS_PARENT_H
+
+#include "mozilla/UniquePtr.h"
+
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/TaskFactory.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRChild;
+
+class VRProcessParent final : public mozilla::ipc::GeckoChildProcessHost
+{
+public:
+  explicit VRProcessParent();
+  ~VRProcessParent();
+
+  bool Launch();
+  void Shutdown();
+  void DestroyProcess();
+  bool CanShutdown() override { return true; }
+
+  void OnChannelError() override;
+  void OnChannelConnected(int32_t peer_pid) override;
+  void OnChannelConnectedTask();
+  void OnChannelErrorTask();
+  void OnChannelClosed();
+
+  base::ProcessId OtherPid();
+  VRChild* GetActor() const {
+    return mVRChild.get();
+  }
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(VRProcessParent);
+
+  void InitAfterConnect(bool aSucceeded);
+  void KillHard(const char* aReason);
+
+  UniquePtr<VRChild> mVRChild;
+  mozilla::ipc::TaskFactory<VRProcessParent> mTaskFactory;
+  nsCOMPtr<nsIThread> mLaunchThread;
+  bool mChannelClosed;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // ifndef GFX_VR_PROCESS_PARENT_H
\ No newline at end of file
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -2,38 +2,54 @@
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS += [
     'external_api/moz_external_vr.h',
     'gfxVR.h',
+    'gfxVRExternal.h',
+    'ipc/VRChild.h',
+    'ipc/VRGPUChild.h',
+    'ipc/VRGPUParent.h',
     'ipc/VRLayerChild.h',
     'ipc/VRManagerChild.h',
     'ipc/VRManagerParent.h',
     'ipc/VRMessageUtils.h',
+    'ipc/VRParent.h',
+    'ipc/VRProcessChild.h',
+    'ipc/VRProcessManager.h',
+    'ipc/VRProcessParent.h',
     'VRDisplayClient.h',
+    'VRDisplayHost.h',
     'VRDisplayPresentation.h',
     'VRManager.h',
     'VRThread.h',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/gfx/layers/d3d11',
     '/gfx/thebes',
 ]
 
 UNIFIED_SOURCES += [
     'gfxVR.cpp',
+    'ipc/VRChild.cpp',
+    'ipc/VRGPUChild.cpp',
+    'ipc/VRGPUParent.cpp',
     'ipc/VRLayerChild.cpp',
     'ipc/VRLayerParent.cpp',
     'ipc/VRManagerChild.cpp',
     'ipc/VRManagerParent.cpp',
+    'ipc/VRParent.cpp',
+    'ipc/VRProcessChild.cpp',
+    'ipc/VRProcessManager.cpp',
+    'ipc/VRProcessParent.cpp',
     'VRDisplayClient.cpp',
     'VRDisplayPresentation.cpp',
     'VRManager.cpp',
     'VRThread.cpp',
 ]
 
 if CONFIG['OS_TARGET'] != 'Android':
     UNIFIED_SOURCES += [
@@ -64,16 +80,18 @@ if CONFIG['OS_TARGET'] == 'WINNT':
     SOURCES += [
         'gfxVROculus.cpp',
     ]
 
 if CONFIG['OS_TARGET'] == 'Android':
     LOCAL_INCLUDES += ['/widget/android']
 
 IPDL_SOURCES = [
+    'ipc/PVR.ipdl',
+    'ipc/PVRGPU.ipdl',
     'ipc/PVRLayer.ipdl',
     'ipc/PVRManager.ipdl',
 ]
 
 # For building with the real SDK instead of our local hack
 #SOURCES += [
 #    'OVR_CAPI_Util.cpp',
 #    'OVR_CAPIShim.c',
--- a/gfx/vr/service/VRService.cpp
+++ b/gfx/vr/service/VRService.cpp
@@ -54,23 +54,37 @@ VRService::Create()
   return service.forget();
 }
 
 VRService::VRService()
  : mSystemState{}
  , mBrowserState{}
  , mServiceThread(nullptr)
  , mShutdownRequested(false)
+ , mAPIShmem(nullptr)
+ , mTargetShmemFile(0)
 {
-  memset(&mAPIShmem, 0, sizeof(mAPIShmem));
+  // When we have the VR process, we map the memory
+  // of mAPIShmem from GPU process.
+  // If we don't have the VR process, we will instantiate
+  // mAPIShmem in VRService.
+  if (!gfxPrefs::VRProcessEnabled()) {
+    mAPIShmem = new VRExternalShmem();
+    memset(mAPIShmem, 0, sizeof(VRExternalShmem));
+  }
 }
 
 VRService::~VRService()
 {
   Stop();
+
+  if (!gfxPrefs::VRProcessEnabled() && mAPIShmem) {
+    delete mAPIShmem;
+    mAPIShmem = nullptr;
+  }
 }
 
 void
 VRService::Start()
 {
   if (!mServiceThread) {
     /**
      * We must ensure that any time the service is re-started, that
@@ -105,51 +119,98 @@ VRService::Start()
     ));
   }
 }
 
 void
 VRService::Stop()
 {
   if (mServiceThread) {
-    mServiceThread->message_loop()->PostTask(NewRunnableMethod(
-      "gfx::VRService::RequestShutdown",
-      this, &VRService::RequestShutdown
-    ));
+    mShutdownRequested = true;
     delete mServiceThread;
     mServiceThread = nullptr;
   }
+  if (mTargetShmemFile) {
+#if defined(XP_WIN)
+    CloseHandle(mTargetShmemFile);
+#endif
+    mTargetShmemFile = 0;
+  }
+  if (gfxPrefs::VRProcessEnabled() && mAPIShmem) {
+#if defined(XP_WIN)
+    UnmapViewOfFile((void *)mAPIShmem);
+#endif
+    mAPIShmem = nullptr;
+  }
+  mSession = nullptr;
+}
+
+bool
+VRService::InitShmem()
+{
+  if (!gfxPrefs::VRProcessEnabled()) {
+    return true;
+  }
+
+#if defined(XP_WIN)
+  const char* kShmemName = "moz.gecko.vr_ext.0.0.1";
+  base::ProcessHandle targetHandle = 0;
+
+  // Opening a file-mapping object by name
+  targetHandle = OpenFileMappingA(
+                  FILE_MAP_ALL_ACCESS,   // read/write access
+                  FALSE,                 // do not inherit the name
+                  kShmemName);           // name of mapping object
+
+  MOZ_ASSERT(GetLastError() == 0);
+
+  LARGE_INTEGER length;
+  length.QuadPart = sizeof(VRExternalShmem);
+  mAPIShmem = (VRExternalShmem *)MapViewOfFile(reinterpret_cast<base::ProcessHandle>(targetHandle), // handle to map object
+                                               FILE_MAP_ALL_ACCESS,  // read/write permission
+                                               0,
+                                               0,
+                                               length.QuadPart);
+  MOZ_ASSERT(GetLastError() == 0);
+  // TODO - Implement logging
+  mTargetShmemFile = targetHandle;
+  if (!mAPIShmem) {
+    MOZ_ASSERT(mAPIShmem);
+    return false;
+  }
+#else
+  // TODO: Implement shmem for other platforms.
+#endif
+
+ return true;
 }
 
 bool
 VRService::IsInServiceThread()
 {
   return mServiceThread && mServiceThread->thread_id() == PlatformThread::CurrentId();
 }
 
 void
-VRService::RequestShutdown()
-{
-  MOZ_ASSERT(IsInServiceThread());
-  mShutdownRequested = true;
-}
-
-void
 VRService::ServiceInitialize()
 {
   MOZ_ASSERT(IsInServiceThread());
 
+  if (!InitShmem()) {
+    return;
+  }
+
   mShutdownRequested = false;
   memset(&mBrowserState, 0, sizeof(mBrowserState));
 
   // Try to start a VRSession
-  unique_ptr<VRSession> session;
+  UniquePtr<VRSession> session;
 
   // Try OpenVR
-  session = make_unique<OpenVRSession>();
+  session = MakeUnique<OpenVRSession>();
   if (!session->Initialize(mSystemState)) {
     session = nullptr;
   }
   if (session) {
     mSession = std::move(session);
     // Setting enumerationCompleted to true indicates to the browser
     // that it should resolve any promises in the WebVR/WebXR API
     // waiting for hardware detection.
@@ -277,56 +338,62 @@ VRService::ServiceImmersiveMode()
     "gfx::VRService::ServiceImmersiveMode",
     this, &VRService::ServiceImmersiveMode
   ));
 }
 
 void
 VRService::PushState(const mozilla::gfx::VRSystemState& aState)
 {
+  if (!mAPIShmem) {
+    return;
+  }
   // Copying the VR service state to the shmem is atomic, infallable,
   // and non-blocking on x86/x64 architectures.  Arm requires a mutex
   // that is locked for the duration of the memcpy to and from shmem on
   // both sides.
 
 #if defined(MOZ_WIDGET_ANDROID)
     if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
-      memcpy((void *)&mAPIShmem.state, &aState, sizeof(VRSystemState));
+      memcpy((void *)&mAPIShmem->state, &aState, sizeof(VRSystemState));
       pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
     }
 #else
-  mAPIShmem.generationA++;
-  memcpy((void *)&mAPIShmem.state, &aState, sizeof(VRSystemState));
-  mAPIShmem.generationB++;
+  mAPIShmem->generationA++;
+  memcpy((void *)&mAPIShmem->state, &aState, sizeof(VRSystemState));
+  mAPIShmem->generationB++;
 #endif
 }
 
 void
 VRService::PullState(mozilla::gfx::VRBrowserState& aState)
 {
+  if (!mAPIShmem) {
+    return;
+  }
   // Copying the browser state from the shmem is non-blocking
   // on x86/x64 architectures.  Arm requires a mutex that is
   // locked for the duration of the memcpy to and from shmem on
   // both sides.
   // On x86/x64 It is fallable -- If a dirty copy is detected by
   // a mismatch of browserGenerationA and browserGenerationB,
   // the copy is discarded and will not replace the last known
   // browser state.
 
 #if defined(MOZ_WIDGET_ANDROID)
     if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) {
       memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
       pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex));
     }
 #else
   VRExternalShmem tmp;
-  memcpy(&tmp, &mAPIShmem, sizeof(VRExternalShmem));
+  memcpy(&tmp, mAPIShmem, sizeof(VRExternalShmem));
   if (tmp.browserGenerationA == tmp.browserGenerationB && tmp.browserGenerationA != 0 && tmp.browserGenerationA != -1) {
     memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
   }
 #endif
 }
 
 VRExternalShmem*
 VRService::GetAPIShmem()
 {
-  return &mAPIShmem;
+  return mAPIShmem;
 }
--- a/gfx/vr/service/VRService.h
+++ b/gfx/vr/service/VRService.h
@@ -6,17 +6,16 @@
 
 #ifndef GFX_VR_SERVICE_VRSERVICE_H
 #define GFX_VR_SERVICE_VRSERVICE_H
 
 #include "mozilla/Atomics.h"
 
 #include "moz_external_vr.h"
 
-#include <thread>
 namespace base {
 class Thread;
 } // namespace base
 namespace mozilla {
 namespace gfx {
 
 class VRSession;
 
@@ -28,16 +27,18 @@ public:
 
   void Start();
   void Stop();
   VRExternalShmem* GetAPIShmem();
 
 private:
   VRService();
   ~VRService();
+  
+  bool InitShmem();
   void PushState(const mozilla::gfx::VRSystemState& aState);
   void PullState(mozilla::gfx::VRBrowserState& aState);
 
   /**
    * VRSystemState contains the most recent state of the VR
    * system, to be shared with the browser by Shmem.
    * mSystemState is the VR Service copy of this data, which
    * is memcpy'ed atomically to the Shmem.
@@ -46,24 +47,24 @@ private:
    */
   VRSystemState mSystemState;
   /**
    * VRBrowserState contains the most recent state of the browser.
    * mBrowserState is memcpy'ed from the Shmem atomically
    */
   VRBrowserState mBrowserState;
 
-  std::unique_ptr<VRSession> mSession;
+  UniquePtr<VRSession> mSession;
   base::Thread* mServiceThread;
   bool mShutdownRequested;
 
-  VRExternalShmem mAPIShmem;
+  VRExternalShmem* MOZ_OWNING_REF mAPIShmem;
+  base::ProcessHandle mTargetShmemFile;
 
   bool IsInServiceThread();
-  void RequestShutdown();
 
   /**
    * The VR Service thread is a state machine that always has one
    * task queued depending on the state.
    *
    * VR Service thread state task functions:
    */
   void ServiceInitialize();
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,14 +1,15 @@
 [package]
 name = "webrender"
 version = "0.57.2"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
+description = "A GPU accelerated 2D renderer for web content"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
 profiler = ["thread_profiler/thread_profiler", "debug_renderer"]
 debugger = ["ws", "serde_json", "serde", "image", "base64", "debug_renderer"]
 capture = ["webrender_api/serialize", "ron", "serde", "debug_renderer"]
@@ -35,17 +36,17 @@ plane-split = "0.12.1"
 png = { optional = true, version = "0.12" }
 rayon = "1"
 ron = { optional = true, version = "0.1.7" }
 serde = { optional = true, version = "1.0", features = ["serde_derive"] }
 serde_json = { optional = true, version = "1.0" }
 smallvec = "0.6"
 thread_profiler = "0.1.1"
 time = "0.1"
-webrender_api = {path = "../webrender_api"}
+webrender_api = { version = "0.57.2", path = "../webrender_api" }
 ws = { optional = true, version = "0.7.3" }
 
 [dependencies.pathfinder_font_renderer]
 git = "https://github.com/pcwalton/pathfinder"
 optional = true
 # Uncomment to test FreeType on macOS:
 # features = ["freetype"]
 
--- a/gfx/webrender/res/brush_image.glsl
+++ b/gfx/webrender/res/brush_image.glsl
@@ -170,16 +170,17 @@ void brush_vs(
     switch (color_mode) {
         case COLOR_MODE_ALPHA:
         case COLOR_MODE_BITMAP:
             vMaskSwizzle = vec2(0.0, 1.0);
             vColor = image_data.color;
             break;
         case COLOR_MODE_SUBPX_BG_PASS2:
         case COLOR_MODE_SUBPX_DUAL_SOURCE:
+        case COLOR_MODE_IMAGE:
             vMaskSwizzle = vec2(1.0, 0.0);
             vColor = image_data.color;
             break;
         case COLOR_MODE_SUBPX_CONST_COLOR:
         case COLOR_MODE_SUBPX_BG_PASS0:
         case COLOR_MODE_COLOR_BITMAP:
             vMaskSwizzle = vec2(1.0, 0.0);
             vColor = vec4(image_data.color.a);
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -37,16 +37,17 @@ varying vec4 vClipMaskUv;
 #define COLOR_MODE_ALPHA              1
 #define COLOR_MODE_SUBPX_CONST_COLOR  2
 #define COLOR_MODE_SUBPX_BG_PASS0     3
 #define COLOR_MODE_SUBPX_BG_PASS1     4
 #define COLOR_MODE_SUBPX_BG_PASS2     5
 #define COLOR_MODE_SUBPX_DUAL_SOURCE  6
 #define COLOR_MODE_BITMAP             7
 #define COLOR_MODE_COLOR_BITMAP       8
+#define COLOR_MODE_IMAGE              9
 
 uniform HIGHP_SAMPLER_FLOAT sampler2D sPrimitiveHeadersF;
 uniform HIGHP_SAMPLER_FLOAT isampler2D sPrimitiveHeadersI;
 
 // Instanced attributes
 in ivec4 aData;
 
 #define VECS_PER_PRIM_HEADER_F 2
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -258,46 +258,35 @@ impl OpaqueBatchList {
             batch.instances.reverse();
         }
     }
 }
 
 pub struct BatchList {
     pub alpha_batch_list: AlphaBatchList,
     pub opaque_batch_list: OpaqueBatchList,
-    pub combined_bounding_rect: DeviceIntRect,
 }
 
 impl BatchList {
     pub fn new(screen_size: DeviceIntSize) -> Self {
         // The threshold for creating a new batch is
         // one quarter the screen size.
         let batch_area_threshold = (screen_size.width * screen_size.height) as f32 / 4.0;
 
         BatchList {
             alpha_batch_list: AlphaBatchList::new(),
             opaque_batch_list: OpaqueBatchList::new(batch_area_threshold),
-            combined_bounding_rect: DeviceIntRect::zero(),
         }
     }
 
-    fn add_bounding_rect(
-        &mut self,
-        task_relative_bounding_rect: &DeviceIntRect,
-    ) {
-        self.combined_bounding_rect = self.combined_bounding_rect.union(task_relative_bounding_rect);
-    }
-
     pub fn get_suitable_batch(
         &mut self,
         key: BatchKey,
         task_relative_bounding_rect: &DeviceIntRect,
     ) -> &mut Vec<PrimitiveInstance> {
-        self.add_bounding_rect(task_relative_bounding_rect);
-
         match key.blend_mode {
             BlendMode::None => {
                 self.opaque_batch_list
                     .get_suitable_batch(key, task_relative_bounding_rect)
             }
             BlendMode::Alpha |
             BlendMode::PremultipliedAlpha |
             BlendMode::PremultipliedDestOut |
@@ -404,41 +393,37 @@ impl AlphaBatchContainer {
     }
 }
 
 /// Encapsulates the logic of building batches for items that are blended.
 pub struct AlphaBatchBuilder {
     pub batch_list: BatchList,
     glyph_fetch_buffer: Vec<GlyphFetchResult>,
     target_rect: DeviceIntRect,
+    can_merge: bool,
 }
 
 impl AlphaBatchBuilder {
     pub fn new(
         screen_size: DeviceIntSize,
         target_rect: DeviceIntRect,
+        can_merge: bool,
     ) -> Self {
         AlphaBatchBuilder {
             batch_list: BatchList::new(screen_size),
             glyph_fetch_buffer: Vec::new(),
             target_rect,
+            can_merge,
         }
     }
 
     pub fn build(mut self, merged_batches: &mut AlphaBatchContainer) -> Option<AlphaBatchContainer> {
         self.batch_list.finalize();
 
-        let task_relative_target_rect = DeviceIntRect::new(
-            DeviceIntPoint::zero(),
-            self.target_rect.size,
-        );
-
-        let can_merge = task_relative_target_rect.contains_rect(&self.batch_list.combined_bounding_rect);
-
-        if can_merge {
+        if self.can_merge {
             merged_batches.merge(self);
             None
         } else {
             Some(AlphaBatchContainer {
                 alpha_batches: self.batch_list.alpha_batch_list.batches,
                 opaque_batches: self.batch_list.opaque_batch_list.batches,
                 target_rect: Some(self.target_rect),
             })
@@ -468,22 +453,18 @@ impl AlphaBatchBuilder {
         };
 
         // Even though most of the time a splitter isn't used or needed,
         // they are cheap to construct so we will always pass one down.
         let mut splitter = BspSplitter::new();
 
         // Add each run in this picture to the batch.
         for run in &pic.runs {
-            let transform_id = ctx
-                .transforms
-                .get_id(run.spatial_node_index);
             self.add_run_to_batch(
                 run,
-                transform_id,
                 ctx,
                 gpu_cache,
                 render_tasks,
                 task_id,
                 task_address,
                 deferred_resolves,
                 &mut splitter,
                 content_origin,
@@ -535,32 +516,35 @@ impl AlphaBatchBuilder {
     }
 
     // Helper to add an entire primitive run to a batch list.
     // TODO(gw): Restructure this so the param list isn't quite
     //           so daunting!
     fn add_run_to_batch(
         &mut self,
         run: &PrimitiveRun,
-        transform_id: TransformPaletteId,
         ctx: &RenderTargetContext,
         gpu_cache: &mut GpuCache,
         render_tasks: &RenderTaskTree,
         task_id: RenderTaskId,
         task_address: RenderTaskAddress,
         deferred_resolves: &mut Vec<DeferredResolve>,
         splitter: &mut BspSplitter<f64, WorldPixel>,
         content_origin: DeviceIntPoint,
         prim_headers: &mut PrimitiveHeaders,
     ) {
         for i in 0 .. run.count {
             let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
             let metadata = &ctx.prim_store.primitives[prim_index.0].metadata;
 
             if metadata.screen_rect.is_some() {
+                let transform_id = ctx
+                    .transforms
+                    .get_id(metadata.spatial_node_index);
+
                 self.add_prim_to_batch(
                     transform_id,
                     prim_index,
                     ctx,
                     gpu_cache,
                     render_tasks,
                     task_id,
                     task_address,
@@ -665,33 +649,33 @@ impl AlphaBatchBuilder {
                     BrushKind::Picture(ref picture) => {
                         // If this picture is participating in a 3D rendering context,
                         // then don't add it to any batches here. Instead, create a polygon
                         // for it and add it to the current plane splitter.
                         if picture.is_in_3d_context {
                             // Push into parent plane splitter.
                             debug_assert!(picture.surface.is_some());
                             let transform = &ctx.transforms
-                                .get_transform(picture.original_spatial_node_index);
+                                .get_transform_by_id(transform_id);
 
                             match transform.transform_kind {
                                 TransformedRectKind::AxisAligned => {
                                     let polygon = Polygon::from_transformed_rect(
-                                        picture.real_local_rect.cast(),
+                                        prim_metadata.local_rect.cast(),
                                         transform.m.cast(),
                                         prim_index.0,
                                     ).unwrap();
                                     splitter.add(polygon);
                                 }
                                 TransformedRectKind::Complex => {
                                     let mut clipper = Clipper::new();
                                     let bounds = (screen_rect.clipped.to_f32() / ctx.device_pixel_scale).to_f64();
                                     let matrix = transform.m.cast();
                                     let results = clipper.clip_transformed(
-                                        Polygon::from_rect(picture.real_local_rect.cast(), prim_index.0),
+                                        Polygon::from_rect(prim_metadata.local_rect.cast(), prim_index.0),
                                         &matrix,
                                         Some(bounds),
                                     );
                                     for poly in results {
                                         splitter.add(poly);
                                     }
                                 }
                             }
@@ -718,17 +702,17 @@ impl AlphaBatchBuilder {
                                                 let key = BatchKey::new(
                                                     kind,
                                                     non_segmented_blend_mode,
                                                     textures,
                                                 );
                                                 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
                                                 let prim_header_index = prim_headers.push(&prim_header, [
                                                     uv_rect_address.as_int(),
-                                                    (ShaderColorMode::ColorBitmap as i32) << 16 |
+                                                    (ShaderColorMode::Image as i32) << 16 |
                                                     RasterizationSpace::Screen as i32,
                                                     0,
                                                 ]);
 
                                                 let instance = BrushInstance {
                                                     prim_header_index,
                                                     segment_index: 0,
                                                     edge_flags: EdgeAaSegmentMask::empty(),
@@ -781,17 +765,17 @@ impl AlphaBatchBuilder {
                                                 .get_texture_address(gpu_cache)
                                                 .as_int();
 
                                             // Get the GPU cache address of the extra data handle.
                                             let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handle);
 
                                             let content_prim_header_index = prim_headers.push(&prim_header, [
                                                 content_uv_rect_address,
-                                                (ShaderColorMode::ColorBitmap as i32) << 16 |
+                                                (ShaderColorMode::Image as i32) << 16 |
                                                 RasterizationSpace::Screen as i32,
                                                 0,
                                             ]);
 
                                             let shadow_rect = prim_metadata.local_rect.translate(&offset);
                                             let shadow_clip_rect = prim_metadata.local_clip_rect.translate(&offset);
 
                                             let shadow_prim_header = PrimitiveHeader {
@@ -966,17 +950,17 @@ impl AlphaBatchBuilder {
                                     &task_relative_bounding_rect
                                 );
 
                                 let uv_rect_address = render_tasks[cache_task_id]
                                     .get_texture_address(gpu_cache)
                                     .as_int();
                                 let prim_header_index = prim_headers.push(&prim_header, [
                                     uv_rect_address,
-                                    (ShaderColorMode::ColorBitmap as i32) << 16 |
+                                    (ShaderColorMode::Image as i32) << 16 |
                                     RasterizationSpace::Screen as i32,
                                     0,
                                 ]);
 
                                 let instance = BrushInstance {
                                     prim_header_index,
                                     clip_task_address,
                                     segment_index: 0,
@@ -1198,18 +1182,16 @@ impl AlphaBatchBuilder {
         let base_instance = BrushInstance {
             prim_header_index,
             clip_task_address,
             segment_index: 0,
             edge_flags,
             brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
         };
 
-        self.batch_list.add_bounding_rect(task_relative_bounding_rect);
-
         let batch_key = BatchKey {
             blend_mode,
             kind: BatchKind::Brush(batch_kind),
             textures,
         };
         let batch = self.batch_list.get_suitable_batch(batch_key, task_relative_bounding_rect);
         batch.push(PrimitiveInstance::from(base_instance));
     }
@@ -1231,18 +1213,16 @@ impl AlphaBatchBuilder {
         let base_instance = BrushInstance {
             prim_header_index,
             clip_task_address,
             segment_index: 0,
             edge_flags: EdgeAaSegmentMask::all(),
             brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
         };
 
-        self.batch_list.add_bounding_rect(task_relative_bounding_rect);
-
         match brush.segment_desc {
             Some(ref segment_desc) => {
                 let alpha_batch_key = BatchKey {
                     blend_mode: alpha_blend_mode,
                     kind: BatchKind::Brush(batch_kind),
                     textures,
                 };
 
@@ -1312,17 +1292,16 @@ fn add_gradient_tiles(
     blend_mode: BlendMode,
     task_relative_bounding_rect: &DeviceIntRect,
     clip_task_address: RenderTaskAddress,
     gpu_cache: &GpuCache,
     batch_list: &mut BatchList,
     base_prim_header: &PrimitiveHeader,
     prim_headers: &mut PrimitiveHeaders,
 ) {
-    batch_list.add_bounding_rect(task_relative_bounding_rect);
     let batch = batch_list.get_suitable_batch(
         BatchKey {
             blend_mode: blend_mode,
             kind: BatchKind::Brush(kind),
             textures: BatchTextures::no_texture(),
         },
         task_relative_bounding_rect
     );
@@ -1368,17 +1347,17 @@ fn get_image_tile_params(
         None
     } else {
         let textures = BatchTextures::color(cache_item.texture_id);
         Some((
             BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
             textures,
             [
                 cache_item.uv_rect_handle.as_int(gpu_cache),
-                (ShaderColorMode::ColorBitmap as i32) << 16 |
+                (ShaderColorMode::Image as i32) << 16 |
                      RasterizationSpace::Local as i32,
                 0,
             ],
         ))
     }
 }
 
 impl BrushPrimitive {
@@ -1418,17 +1397,17 @@ impl BrushPrimitive {
                 } else {
                     let textures = BatchTextures::color(cache_item.texture_id);
 
                     Some((
                         BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
                         textures,
                         [
                             cache_item.uv_rect_handle.as_int(gpu_cache),
-                            (ShaderColorMode::ColorBitmap as i32) << 16|
+                            (ShaderColorMode::Image as i32) << 16|
                              RasterizationSpace::Local as i32,
                             0,
                         ],
                     ))
                 }
             }
             BrushKind::Border { ref source, .. } => {
                 let cache_item = match *source {
@@ -1456,17 +1435,17 @@ impl BrushPrimitive {
                 } else {
                     let textures = BatchTextures::color(cache_item.texture_id);
 
                     Some((
                         BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
                         textures,
                         [
                             cache_item.uv_rect_handle.as_int(gpu_cache),
-                            (ShaderColorMode::ColorBitmap as i32) << 16|
+                            (ShaderColorMode::Image as i32) << 16|
                              RasterizationSpace::Local as i32,
                             0,
                         ],
                     ))
                 }
             }
             BrushKind::Picture { .. } => {
                 panic!("bug: get_batch_key is handled at higher level for pictures");
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -6,17 +6,17 @@ use api::{BorderRadius, ClipMode, Comple
 use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, LocalClip};
 use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle, LayoutTransform};
 use border::{ensure_no_corner_overlap};
 use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
 use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex};
 use ellipse::Ellipse;
 use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
 use gpu_types::BoxShadowStretchMode;
-use prim_store::{ClipData, ImageMaskData};
+use prim_store::{BrushClipMaskKind, ClipData, ImageMaskData};
 use render_task::to_cache_size;
 use resource_cache::{ImageRequest, ResourceCache};
 use spatial_node::SpatialNode;
 use std::u32;
 use util::{extract_inner_rect_safe, pack_as_float, recycle_vec, MatrixHelpers};
 
 /*
 
@@ -347,22 +347,23 @@ pub struct ClipStore {
     pub clip_nodes: Vec<ClipNode>,
     pub clip_chain_nodes: Vec<ClipChainNode>,
     clip_node_indices: Vec<ClipNodeInstance>,
     clip_node_info: Vec<ClipNodeInfo>,
 }
 
 // A clip chain instance is what gets built for a given clip
 // chain id + local primitive region + positioning node.
+#[derive(Debug)]
 pub struct ClipChainInstance {
     pub clips_range: ClipNodeRange,
     pub local_clip_rect: LayoutRect,
-    pub has_clips_from_other_coordinate_systems: bool,
     pub has_non_root_coord_system: bool,
     pub local_bounding_rect: LayoutRect,
+    pub clip_mask_kind: BrushClipMaskKind,
 }
 
 impl ClipStore {
     pub fn new() -> Self {
         ClipStore {
             clip_nodes: Vec::new(),
             clip_chain_nodes: Vec::new(),
             clip_node_indices: Vec::new(),
@@ -544,22 +545,29 @@ impl ClipStore {
 
         // Now, we've collected all the clip nodes that *potentially* affect this
         // primitive region, and reduced the size of the prim region as much as possible.
 
         // Run through the clip nodes, and see which ones affect this prim region.
 
         let first_clip_node_index = self.clip_node_indices.len() as u32;
         let mut has_non_root_coord_system = false;
-        let mut has_clips_from_other_coordinate_systems = false;
+        let mut clip_mask_kind = BrushClipMaskKind::Individual;
 
         // For each potential clip node
         for node_info in self.clip_node_info.drain(..) {
             let node = &mut self.clip_nodes[node_info.node_index.0 as usize];
 
+            // TODO(gw): We can easily extend the segment builder to support these clip sources in
+            // the future, but they are rarely used.
+            // We must do this check here in case we continue early below.
+            if node.item.is_image_or_line_decoration_clip() {
+                clip_mask_kind = BrushClipMaskKind::Global;
+            }
+
             // Convert the prim rect into the clip nodes local space
             let prim_rect = node_info
                 .conversion
                 .transform_from_prim_space(&current_local_clip_rect);
 
             // See how this clip affects the prim region.
             let clip_result = match prim_rect {
                 Some(prim_rect) => {
@@ -595,20 +603,25 @@ impl ClipStore {
 
                     // Calculate some flags that are required for the segment
                     // building logic.
                     let flags = match node_info.conversion {
                         ClipSpaceConversion::Local => {
                             ClipNodeFlags::SAME_SPATIAL_NODE | ClipNodeFlags::SAME_COORD_SYSTEM
                         }
                         ClipSpaceConversion::Offset(..) => {
+                            if !node.item.is_rect() {
+                                clip_mask_kind = BrushClipMaskKind::Global;
+                            }
                             ClipNodeFlags::SAME_COORD_SYSTEM
                         }
                         ClipSpaceConversion::Transform(..) => {
-                            has_clips_from_other_coordinate_systems = true;
+                            // If this primitive is clipped by clips from a different coordinate system, then we
+                            // need to apply a clip mask for the entire primitive.
+                            clip_mask_kind = BrushClipMaskKind::Global;
                             ClipNodeFlags::empty()
                         }
                     };
 
                     // Store this in the index buffer for this clip chain instance.
                     self.clip_node_indices
                         .push(ClipNodeInstance::new(node_info.node_index, flags));
 
@@ -621,20 +634,20 @@ impl ClipStore {
         let clips_range = ClipNodeRange {
             first: first_clip_node_index,
             count: self.clip_node_indices.len() as u32 - first_clip_node_index,
         };
 
         // Return a valid clip chain instance
         Some(ClipChainInstance {
             clips_range,
-            has_clips_from_other_coordinate_systems,
             has_non_root_coord_system,
             local_clip_rect: current_local_clip_rect,
             local_bounding_rect,
+            clip_mask_kind,
         })
     }
 }
 
 #[derive(Debug)]
 pub struct LineDecorationClipSource {
     rect: LayoutRect,
     style: LineStyle,
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -1,13 +1,13 @@
 /* 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 api::{ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D};
+use api::{ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D, LayoutVector3D};
 use api::{PipelineId, ScrollClamping, ScrollNodeState, ScrollLocation};
 use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint};
 use clip::{ClipStore};
 use gpu_types::TransformPalette;
 use internal_types::{FastHashMap, FastHashSet};
 use print_tree::{PrintTree, PrintTreePrinter};
 use scene::SceneProperties;
 use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo};
@@ -19,44 +19,59 @@ pub type ScrollStates = FastHashMap<Exte
 /// coordinate system has an id and those ids will be shared when the coordinates
 /// system are the same or are in the same axis-aligned space. This allows
 /// for optimizing mask generation.
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CoordinateSystemId(pub u32);
 
+/// A node in the hierarchy of coordinate system
+/// transforms.
+#[derive(Debug)]
+pub struct CoordinateSystem {
+    pub offset: LayoutVector3D,
+    pub transform: LayoutTransform,
+    pub parent: Option<CoordinateSystemId>,
+}
+
+impl CoordinateSystem {
+    fn root() -> Self {
+        CoordinateSystem {
+            offset: LayoutVector3D::zero(),
+            transform: LayoutTransform::identity(),
+            parent: None,
+        }
+    }
+}
+
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct SpatialNodeIndex(pub usize);
 
 const ROOT_REFERENCE_FRAME_INDEX: SpatialNodeIndex = SpatialNodeIndex(0);
 const TOPMOST_SCROLL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(1);
 
 impl CoordinateSystemId {
     pub fn root() -> Self {
         CoordinateSystemId(0)
     }
-
-    pub fn next(&self) -> Self {
-        let CoordinateSystemId(id) = *self;
-        CoordinateSystemId(id + 1)
-    }
-
-    pub fn advance(&mut self) {
-        self.0 += 1;
-    }
 }
 
 pub struct ClipScrollTree {
     /// Nodes which determine the positions (offsets and transforms) for primitives
     /// and clips.
     pub spatial_nodes: Vec<SpatialNode>,
 
+    /// A list of transforms that establish new coordinate systems.
+    /// Spatial nodes only establish a new coordinate system when
+    /// they have a transform that is not a simple 2d translation.
+    pub coord_systems: Vec<CoordinateSystem>,
+
     pub pending_scroll_offsets: FastHashMap<ExternalScrollId, (LayoutPoint, ScrollClamping)>,
 
     /// A set of pipelines which should be discarded the next time this
     /// tree is drained.
     pub pipelines_to_discard: FastHashSet<PipelineId>,
 }
 
 #[derive(Clone)]
@@ -80,21 +95,59 @@ pub struct TransformUpdateState {
     /// node will not be clipped by clips that are transformed by this node.
     pub invertible: bool,
 }
 
 impl ClipScrollTree {
     pub fn new() -> Self {
         ClipScrollTree {
             spatial_nodes: Vec::new(),
+            coord_systems: Vec::new(),
             pending_scroll_offsets: FastHashMap::default(),
             pipelines_to_discard: FastHashSet::default(),
         }
     }
 
+    /// Calculate the relative transform from `ref_node_index`
+    /// to `target_node_index`. It's assumed that `ref_node_index`
+    /// is a parent of `target_node_index`. This method will
+    /// panic if that invariant isn't true!
+    pub fn get_relative_transform(
+        &self,
+        ref_node_index: SpatialNodeIndex,
+        target_node_index: SpatialNodeIndex,
+    ) -> LayoutTransform {
+        let ref_node = &self.spatial_nodes[ref_node_index.0];
+        let target_node = &self.spatial_nodes[target_node_index.0];
+
+        let mut offset = LayoutVector3D::new(
+            target_node.coordinate_system_relative_offset.x,
+            target_node.coordinate_system_relative_offset.y,
+            0.0,
+        );
+        let mut transform = LayoutTransform::identity();
+
+        // Walk up the tree of coordinate systems, accumulating each
+        // relative transform.
+        let mut current_coordinate_system_id = target_node.coordinate_system_id;
+        while current_coordinate_system_id != ref_node.coordinate_system_id {
+            let coord_system = &self.coord_systems[current_coordinate_system_id.0 as usize];
+
+            let relative_transform = coord_system
+                .transform
+                .post_translate(offset);
+            transform = transform.pre_mul(&relative_transform);
+
+            offset = coord_system.offset;
+            current_coordinate_system_id = coord_system.parent.expect("invalid parent!");
+        }
+
+        transform
+    }
+
     /// The root reference frame, which is the true root of the ClipScrollTree. Initially
     /// this ID is not valid, which is indicated by ```spatial_nodes``` being empty.
     pub fn root_reference_frame_index(&self) -> SpatialNodeIndex {
         // TODO(mrobinson): We should eventually make this impossible to misuse.
         debug_assert!(!self.spatial_nodes.is_empty());
         ROOT_REFERENCE_FRAME_INDEX
     }
 
@@ -128,16 +181,17 @@ impl ClipScrollTree {
             match old_node.node_type {
                 SpatialNodeType::ScrollFrame(info) if info.external_id.is_some() => {
                     scroll_states.insert(info.external_id.unwrap(), info);
                 }
                 _ => {}
             }
         }
 
+        self.coord_systems.clear();
         self.pipelines_to_discard.clear();
         scroll_states
     }
 
     pub fn scroll_node(
         &mut self,
         origin: LayoutPoint,
         id: ExternalScrollId,
@@ -186,72 +240,71 @@ impl ClipScrollTree {
         pan: WorldPoint,
         scene_properties: &SceneProperties,
     ) -> TransformPalette {
         let mut transform_palette = TransformPalette::new(self.spatial_nodes.len());
         if self.spatial_nodes.is_empty() {
             return transform_palette;
         }
 
+        self.coord_systems.clear();
+        self.coord_systems.push(CoordinateSystem::root());
+
         let root_reference_frame_index = self.root_reference_frame_index();
         let mut state = TransformUpdateState {
             parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(),
             parent_accumulated_scroll_offset: LayoutVector2D::zero(),
             nearest_scrolling_ancestor_offset: LayoutVector2D::zero(),
             nearest_scrolling_ancestor_viewport: LayoutRect::zero(),
             current_coordinate_system_id: CoordinateSystemId::root(),
             coordinate_system_relative_offset: LayoutVector2D::zero(),
             invertible: true,
         };
 
-        let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
         self.update_node(
             root_reference_frame_index,
             &mut state,
-            &mut next_coordinate_system_id,
             &mut transform_palette,
             scene_properties,
         );
 
         transform_palette
     }
 
     fn update_node(
         &mut self,
         node_index: SpatialNodeIndex,
         state: &mut TransformUpdateState,
-        next_coordinate_system_id: &mut CoordinateSystemId,
         transform_palette: &mut TransformPalette,
         scene_properties: &SceneProperties,
     ) {
         // TODO(gw): This is an ugly borrow check workaround to clone these.
         //           Restructure this to avoid the clones!
         let mut state = state.clone();
         let node_children = {
             let node = match self.spatial_nodes.get_mut(node_index.0) {
                 Some(node) => node,
                 None => return,
             };
 
-            node.update(&mut state, next_coordinate_system_id, scene_properties);
+            node.update(&mut state, &mut self.coord_systems, scene_properties);
             node.push_gpu_data(transform_palette, node_index);
 
             if node.children.is_empty() {
                 return;
             }
 
             node.prepare_state_for_children(&mut state);
             node.children.clone()
         };
 
         for child_node_index in node_children {
             self.update_node(
                 child_node_index,
                 &mut state,
-                next_coordinate_system_id,
                 transform_palette,
                 scene_properties,
             );
         }
     }
 
     pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
         for node in &mut self.spatial_nodes {
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -508,16 +508,17 @@ impl<'a> DisplayListFlattener<'a> {
                     clip_and_scroll,
                     &prim_info,
                     info.stretch_size,
                     info.tile_spacing,
                     None,
                     info.image_key,
                     info.image_rendering,
                     info.alpha_type,
+                    info.color,
                 );
             }
             SpecificDisplayItem::YuvImage(ref info) => {
                 self.add_yuv_image(
                     clip_and_scroll,
                     &prim_info,
                     info.yuv_data,
                     info.color_space,
@@ -771,25 +772,27 @@ impl<'a> DisplayListFlattener<'a> {
 
     /// Create a primitive and add it to the prim store. This method doesn't
     /// add the primitive to the draw list, so can be used for creating
     /// sub-primitives.
     pub fn create_primitive(
         &mut self,
         info: &LayoutPrimitiveInfo,
         clip_chain_id: ClipChainId,
+        spatial_node_index: SpatialNodeIndex,
         container: PrimitiveContainer,
     ) -> PrimitiveIndex {
         let stacking_context = self.sc_stack.last().expect("bug: no stacking context!");
 
         self.prim_store.add_primitive(
             &info.rect,
             &info.clip_rect,
             info.is_backface_visible && stacking_context.is_backface_visible,
             clip_chain_id,
+            spatial_node_index,
             info.tag,
             container,
         )
     }
 
     pub fn add_primitive_to_hit_testing_list(
         &mut self,
         info: &LayoutPrimitiveInfo,
@@ -812,22 +815,21 @@ impl<'a> DisplayListFlattener<'a> {
 
         self.hit_testing_runs.push(HitTestingRun(vec![new_item], clip_and_scroll));
     }
 
     /// Add an already created primitive to the draw lists.
     pub fn add_primitive_to_draw_list(
         &mut self,
         prim_index: PrimitiveIndex,
-        spatial_node_index: SpatialNodeIndex,
     ) {
         // Add primitive to the top-most Picture on the stack.
         let pic_prim_index = *self.picture_stack.last().unwrap();
         let pic = self.prim_store.get_pic_mut(pic_prim_index);
-        pic.add_primitive(prim_index, spatial_node_index);
+        pic.add_primitive(prim_index);
     }
 
     /// Convenience interface that creates a primitive entry and adds it
     /// to the draw list.
     pub fn add_primitive(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
@@ -854,44 +856,46 @@ impl<'a> DisplayListFlattener<'a> {
                     clip_and_scroll.spatial_node_index,
                     clip_and_scroll.clip_chain_id,
                 );
 
                 // Construct and add a primitive for the given shadow.
                 let shadow_prim_index = self.create_primitive(
                     &info,
                     clip_chain_id,
+                    clip_and_scroll.spatial_node_index,
                     container.create_shadow(shadow),
                 );
 
                 // Add the new primitive to the shadow picture.
                 let shadow_pic = self.prim_store.get_pic_mut(shadow_pic_prim_index);
-                shadow_pic.add_primitive(
-                    shadow_prim_index,
-                    clip_and_scroll.spatial_node_index,
-                );
+                shadow_pic.add_primitive(shadow_prim_index);
             }
             self.shadow_stack = shadow_stack;
         }
 
         if container.is_visible() {
             let clip_chain_id = self.build_clip_chain(
                 clip_items,
                 clip_and_scroll.spatial_node_index,
                 clip_and_scroll.clip_chain_id,
             );
-            let prim_index = self.create_primitive(info, clip_chain_id, container);
+            let prim_index = self.create_primitive(
+                info,
+                clip_chain_id,
+                clip_and_scroll.spatial_node_index,
+                container,
+            );
             if cfg!(debug_assertions) && ChasePrimitive::LocalRect(info.rect) == self.config.chase_primitive {
                 println!("Chasing {:?}", prim_index);
                 self.prim_store.chase_id = Some(prim_index);
             }
             self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
             self.add_primitive_to_draw_list(
                 prim_index,
-                clip_and_scroll.spatial_node_index,
             );
         }
     }
 
     fn get_next_picture_id(&mut self) -> PictureId {
         let id = PictureId(self.next_picture_id);
         self.next_picture_id += 1;
         id
@@ -930,26 +934,26 @@ impl<'a> DisplayListFlattener<'a> {
 
             // This picture stores primitive runs for items on the
             // main framebuffer.
             let picture = PicturePrimitive::new_image(
                 self.get_next_picture_id(),
                 None,
                 false,
                 pipeline_id,
-                spatial_node_index,
                 None,
                 true,
             );
 
             let prim_index = self.prim_store.add_primitive(
                 &LayoutRect::zero(),
                 &max_clip,
                 true,
                 ClipChainId::NONE,
+                spatial_node_index,
                 None,
                 PrimitiveContainer::Brush(BrushPrimitive::new_picture(picture)),
             );
 
             self.picture_stack.push(prim_index);
         } else if composite_ops.mix_blend_mode.is_some() && self.sc_stack.len() > 2 {
             // If we have a mix-blend-mode, and we aren't the primary framebuffer,
             // the stacking context needs to be isolated to blend correctly as per
@@ -996,36 +1000,36 @@ impl<'a> DisplayListFlattener<'a> {
             // If establishing a 3d context, we need to add a picture
             // that will be the container for all the planes and any
             // un-transformed content.
             let picture = PicturePrimitive::new_image(
                 self.get_next_picture_id(),
                 None,
                 false,
                 pipeline_id,
-                spatial_node_index,
                 None,
                 true,
             );
 
             let prim = BrushPrimitive::new_picture(picture);
 
             let prim_index = self.prim_store.add_primitive(
                 &LayoutRect::zero(),
                 &max_clip,
                 is_backface_visible,
                 clip_chain_id,
+                spatial_node_index,
                 None,
                 PrimitiveContainer::Brush(prim),
             );
 
             let parent_prim_index = *self.picture_stack.last().unwrap();
 
             let pic = self.prim_store.get_pic_mut(parent_prim_index);
-            pic.add_primitive(prim_index, spatial_node_index);
+            pic.add_primitive(prim_index);
 
             self.picture_stack.push(prim_index);
 
             Some(prim_index)
         } else {
             None
         };
 
@@ -1047,65 +1051,65 @@ impl<'a> DisplayListFlattener<'a> {
 
         // For each filter, create a new image with that composite mode.
         for filter in composite_ops.filters.iter().rev() {
             let picture = PicturePrimitive::new_image(
                 self.get_next_picture_id(),
                 Some(PictureCompositeMode::Filter(*filter)),
                 false,
                 pipeline_id,
-                spatial_node_index,
                 None,
                 true,
             );
 
             let src_prim = BrushPrimitive::new_picture(picture);
             let src_prim_index = self.prim_store.add_primitive(
                 &LayoutRect::zero(),
                 &max_clip,
                 is_backface_visible,
                 clip_chain_id,
+                spatial_node_index,
                 None,
                 PrimitiveContainer::Brush(src_prim),
             );
 
             let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
             parent_prim_index = src_prim_index;
 
-            parent_pic.add_primitive(src_prim_index, spatial_node_index);
+            parent_pic.add_primitive(src_prim_index);
 
             self.picture_stack.push(src_prim_index);
         }
 
         // Same for mix-blend-mode.
         if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
             let picture = PicturePrimitive::new_image(
                 self.get_next_picture_id(),
                 Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
                 false,
                 pipeline_id,
-                spatial_node_index,
                 None,
                 true,
             );
 
             let src_prim = BrushPrimitive::new_picture(picture);
 
             let src_prim_index = self.prim_store.add_primitive(
                 &LayoutRect::zero(),
                 &max_clip,
                 is_backface_visible,
                 clip_chain_id,
+                spatial_node_index,
                 None,
                 PrimitiveContainer::Brush(src_prim),
             );
 
             let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
             parent_prim_index = src_prim_index;
-            parent_pic.add_primitive(src_prim_index, spatial_node_index);
+            parent_pic.add_primitive(src_prim_index);
 
             self.picture_stack.push(src_prim_index);
         }
 
         // By default, this picture will be collapsed into
         // the owning target.
         let mut composite_mode = None;
         let mut frame_output_pipeline_id = None;
@@ -1133,36 +1137,36 @@ impl<'a> DisplayListFlattener<'a> {
         }
 
         // Add picture for this actual stacking context contents to render into.
         let picture = PicturePrimitive::new_image(
             self.get_next_picture_id(),
             composite_mode,
             participating_in_3d_context,
             pipeline_id,
-            spatial_node_index,
             frame_output_pipeline_id,
             true,
         );
 
         // Create a brush primitive that draws this picture.
         let sc_prim = BrushPrimitive::new_picture(picture);
 
         // Add the brush to the parent picture.
         let sc_prim_index = self.prim_store.add_primitive(
             &LayoutRect::zero(),
             &max_clip,
             is_backface_visible,
             clip_chain_id,
+            spatial_node_index,
             None,
             PrimitiveContainer::Brush(sc_prim),
         );
 
         let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
-        parent_pic.add_primitive(sc_prim_index, spatial_node_index);
+        parent_pic.add_primitive(sc_prim_index);
 
         // Add this as the top-most picture for primitives to be added to.
         self.picture_stack.push(sc_prim_index);
 
         // Push the SC onto the stack, so we know how to handle things in
         // pop_stacking_context.
         let sc = FlattenedStackingContext {
             composite_ops,
@@ -1379,37 +1383,36 @@ impl<'a> DisplayListFlattener<'a> {
         // blur radius is 0, the code in Picture::prepare_for_render will
         // detect this and mark the picture to be drawn directly into the
         // parent picture, which avoids an intermediate surface and blur.
         let shadow_pic = PicturePrimitive::new_image(
             self.get_next_picture_id(),
             Some(PictureCompositeMode::Filter(FilterOp::Blur(std_deviation))),
             false,
             pipeline_id,
-            clip_and_scroll.spatial_node_index,
             None,
             apply_local_clip_rect,
         );
 
         // Create the primitive to draw the shadow picture into the scene.
         let shadow_prim = BrushPrimitive::new_picture(shadow_pic);
         let shadow_prim_index = self.prim_store.add_primitive(
             &LayoutRect::zero(),
             &max_clip,
             info.is_backface_visible,
             clip_and_scroll.clip_chain_id,
+            clip_and_scroll.spatial_node_index,
             None,
             PrimitiveContainer::Brush(shadow_prim),
         );
 
         // Add the shadow primitive. This must be done before pushing this
         // picture on to the shadow stack, to avoid infinite recursion!
         self.add_primitive_to_draw_list(
             shadow_prim_index,
-            clip_and_scroll.spatial_node_index,
         );
         self.shadow_stack.push((shadow, shadow_prim_index));
     }
 
     pub fn pop_all_shadows(&mut self) {
         assert!(self.shadow_stack.len() > 0, "popped shadows, but none were present");
         self.shadow_stack.clear();
     }
@@ -1474,22 +1477,22 @@ impl<'a> DisplayListFlattener<'a> {
         let prim = BrushPrimitive::new(
             BrushKind::new_solid(color),
             None,
         );
 
         let prim_index = self.create_primitive(
             info,
             ClipChainId::NONE,
+            spatial_node_index,
             PrimitiveContainer::Brush(prim),
         );
 
         self.add_primitive_to_draw_list(
             prim_index,
-            spatial_node_index,
         );
 
         self.scrollbar_prims.push(ScrollbarPrimitive {
             prim_index,
             scroll_frame_index: scrollbar_info.0,
             frame_rect: scrollbar_info.1,
         });
     }
@@ -1903,16 +1906,17 @@ impl<'a> DisplayListFlattener<'a> {
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         stretch_size: LayoutSize,
         mut tile_spacing: LayoutSize,
         sub_rect: Option<TexelRect>,
         image_key: ImageKey,
         image_rendering: ImageRendering,
         alpha_type: AlphaType,
+        color: ColorF,
     ) {
         let mut prim_rect = info.rect;
         simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
         let info = LayoutPrimitiveInfo {
             rect: prim_rect,
             .. *info
         };
 
@@ -1934,16 +1938,17 @@ impl<'a> DisplayListFlattener<'a> {
                 request: ImageRequest {
                     key: image_key,
                     rendering: image_rendering,
                     tile: None,
                 },
                 alpha_type,
                 stretch_size,
                 tile_spacing,
+                color,
                 source: ImageSource::Default,
                 sub_rect,
                 visible_tiles: Vec::new(),
                 opacity_binding: OpacityBinding::new(),
             },
             None,
         );
 
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -8,24 +8,24 @@ use api::{LayoutPoint, LayoutRect, Layou
 use clip::{ClipStore};
 use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
 use display_list_flattener::{DisplayListFlattener};
 use gpu_cache::GpuCache;
 use gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind};
 use hit_test::{HitTester, HitTestingRun};
 use internal_types::{FastHashMap};
 use picture::PictureSurface;
-use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveStore, Transform};
+use prim_store::{PrimitiveIndex, PrimitiveRun, LocalRectBuilder, PrimitiveStore, Transform};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_backend::FrameId;
 use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
 use resource_cache::{ResourceCache};
 use scene::{ScenePipeline, SceneProperties};
 use spatial_node::SpatialNode;
-use std::{mem, f32};
+use std::f32;
 use std::sync::Arc;
 use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext};
 use tiling::{ScrollbarPrimitive, SpecialRenderPasses};
 use util;
 
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
@@ -84,18 +84,16 @@ pub struct FrameBuildingState<'a> {
     pub resource_cache: &'a mut ResourceCache,
     pub gpu_cache: &'a mut GpuCache,
     pub special_render_passes: &'a mut SpecialRenderPasses,
 }
 
 pub struct PictureContext {
     pub pipeline_id: PipelineId,
     pub prim_runs: Vec<PrimitiveRun>,
-    pub spatial_node_index: SpatialNodeIndex,
-    pub original_spatial_node_index: SpatialNodeIndex,
     pub apply_local_clip_rect: bool,
     pub inflation_factor: f32,
     pub allow_subpixel_aa: bool,
 }
 
 pub struct PictureState {
     pub tasks: Vec<RenderTaskId>,
     pub has_non_root_coord_system: bool,
@@ -107,30 +105,30 @@ impl PictureState {
         PictureState {
             tasks: Vec::new(),
             has_non_root_coord_system: false,
             local_rect_changed: false,
         }
     }
 }
 
-pub struct PrimitiveRunContext<'a> {
-    pub scroll_node: &'a SpatialNode,
+pub struct PrimitiveContext<'a> {
+    pub spatial_node: &'a SpatialNode,
     pub spatial_node_index: SpatialNodeIndex,
     pub transform: Transform<'a>,
 }
 
-impl<'a> PrimitiveRunContext<'a> {
+impl<'a> PrimitiveContext<'a> {
     pub fn new(
-        scroll_node: &'a SpatialNode,
+        spatial_node: &'a SpatialNode,
         spatial_node_index: SpatialNodeIndex,
         transform: Transform<'a>,
     ) -> Self {
-        PrimitiveRunContext {
-            scroll_node,
+        PrimitiveContext {
+            spatial_node,
             spatial_node_index,
             transform,
         }
     }
 }
 
 impl FrameBuilder {
     pub fn empty() -> Self {
@@ -190,21 +188,21 @@ impl FrameBuilder {
         scene_properties: &SceneProperties,
         transform_palette: &TransformPalette,
     ) -> Option<RenderTaskId> {
         profile_scope!("cull");
 
         if self.prim_store.primitives.is_empty() {
             return None
         }
+        self.prim_store.reset_prim_visibility();
 
         // The root picture is always the first one added.
         let root_prim_index = PrimitiveIndex(0);
         let root_spatial_node_index = clip_scroll_tree.root_reference_frame_index();
-        let root_spatial_node = &clip_scroll_tree.spatial_nodes[root_spatial_node_index.0];
 
         const MAX_CLIP_COORD: f32 = 1.0e9;
 
         let frame_context = FrameBuildingContext {
             scene_id: self.scene_id,
             device_pixel_scale,
             scene_properties,
             pipelines,
@@ -221,44 +219,43 @@ impl FrameBuilder {
             render_tasks,
             profile_counters,
             clip_store: &mut self.clip_store,
             resource_cache,
             gpu_cache,
             special_render_passes,
         };
 
-        let pic_context = PictureContext {
-            pipeline_id: root_spatial_node.pipeline_id,
-            prim_runs: mem::replace(
-                &mut self.prim_store.get_pic_mut(root_prim_index).runs,
-                Vec::new(),
-            ),
-            spatial_node_index: root_spatial_node_index,
-            original_spatial_node_index: root_spatial_node_index,
-            apply_local_clip_rect: true,
-            inflation_factor: 0.0,
-            allow_subpixel_aa: true,
-        };
-
         let mut pic_state = PictureState::new();
 
-        self.prim_store.reset_prim_visibility();
+        let pic_context = self
+            .prim_store
+            .get_pic_mut(root_prim_index)
+            .take_context(true);
+
+        let mut local_rect_builder = LocalRectBuilder::new(
+            root_spatial_node_index,
+        );
+
         self.prim_store.prepare_prim_runs(
             &pic_context,
             &mut pic_state,
             &frame_context,
             &mut frame_state,
+            &mut local_rect_builder,
         );
 
-        let pic = self.prim_store.get_pic_mut(root_prim_index);
-        pic.runs = pic_context.prim_runs;
+        let pic = self
+            .prim_store
+            .get_pic_mut(root_prim_index);
+        pic.restore_context(pic_context, local_rect_builder);
 
         let root_render_task = RenderTask::new_picture(
             RenderTaskLocation::Fixed(frame_context.screen_rect),
+            frame_context.screen_rect.size,
             root_prim_index,
             DeviceIntPoint::zero(),
             pic_state.tasks,
             UvRectKind::Rect,
         );
 
         let render_task_id = frame_state.render_tasks.add(root_render_task);
         pic.surface = Some(PictureSurface::RenderTask(render_task_id));
--- a/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
+++ b/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
@@ -296,17 +296,17 @@ fn request_render_task_from_pathfinder(g
 
     // FIXME(pcwalton): Support vertical subpixel offsets.
     // FIXME(pcwalton): Embolden amount should be 0 on macOS if "Use LCD font
     // smoothing" is unchecked in System Preferences.
 
     let subpixel_offset = TypedPoint2D::new(glyph_subpixel_offset as f32, 0.0);
     let embolden_amount = compute_embolden_amount(size.to_f32_px());
 
-    let location = RenderTaskLocation::Dynamic(None, Some(*glyph_size));
+    let location = RenderTaskLocation::Dynamic(None, *glyph_size);
     let glyph_render_task = RenderTask::new_glyph(location,
                                                   mesh,
                                                   &glyph_origin,
                                                   &subpixel_offset,
                                                   font.render_mode,
                                                   &embolden_amount);
 
     let root_task_id = render_tasks.add(glyph_render_task);
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -349,17 +349,17 @@ impl From<BrushInstance> for PrimitiveIn
 #[repr(C)]
 pub struct TransformPaletteId(pub u32);
 
 impl TransformPaletteId {
     /// Identity transform ID.
     pub const IDENTITY: Self = TransformPaletteId(0);
 
     /// Extract the spatial node index from the id.
-    pub fn _spatial_node_index(&self) -> SpatialNodeIndex {
+    pub fn spatial_node_index(&self) -> SpatialNodeIndex {
         SpatialNodeIndex(self.0 as usize & 0xFFFFFF)
     }
 
     /// Extract the transform kind from the id.
     pub fn transform_kind(&self) -> TransformedRectKind {
         if (self.0 >> 24) == 0 {
             TransformedRectKind::AxisAligned
         } else {
@@ -475,16 +475,23 @@ impl TransformPalette {
 
         Transform {
             m: &data.transform,
             transform_kind: metadata.transform_kind,
             backface_is_visible: data.transform.is_backface_visible(),
         }
     }
 
+    pub fn get_transform_by_id(
+        &self,
+        id: TransformPaletteId,
+    ) -> Transform {
+        self.get_transform(id.spatial_node_index())
+    }
+
     // Get a transform palette id for the given spatial node.
     // TODO(gw): In the future, it will be possible to specify
     //           a coordinate system id here, to allow retrieving
     //           transforms in the local space of a given spatial node.
     pub fn get_id(
         &self,
         index: SpatialNodeIndex,
     ) -> TransformPaletteId {
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,21 +1,21 @@
 /* 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 api::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF};
 use api::{DeviceIntRect, DeviceIntSize, DevicePoint, LayoutPoint, LayoutRect};
 use api::{DevicePixelScale, PictureIntPoint, PictureIntRect, PictureIntSize};
 use box_shadow::{BLUR_SAMPLE_SCALE};
-use clip_scroll_tree::SpatialNodeIndex;
-use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PrimitiveRunContext};
+use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState};
+use frame_builder::{PictureContext, PrimitiveContext};
 use gpu_cache::{GpuCacheHandle};
 use gpu_types::UvRectKind;
-use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
+use prim_store::{PrimitiveIndex, PrimitiveRun, LocalRectBuilder};
 use prim_store::{PrimitiveMetadata, Transform};
 use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle};
 use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
 use scene::{FilterOpHelpers, SceneProperties};
 use std::mem;
 use tiling::RenderTargetKind;
 use util::TransformedRectKind;
 
@@ -141,21 +141,16 @@ pub struct PicturePrimitive {
     /// If None, don't composite - just draw directly on parent surface.
     pub composite_mode: Option<PictureCompositeMode>,
     // If true, this picture is part of a 3D context.
     pub is_in_3d_context: bool,
     // If requested as a frame output (for rendering
     // pages to a texture), this is the pipeline this
     // picture is the root of.
     pub frame_output_pipeline_id: Option<PipelineId>,
-    // The original reference spatial node for this picture.
-    // It is only different if this is part of a 3D
-    // rendering context.
-    pub original_spatial_node_index: SpatialNodeIndex,
-    pub real_local_rect: LayoutRect,
     // An optional cache handle for storing extra data
     // in the GPU cache, depending on the type of
     // picture.
     pub extra_gpu_data_handle: GpuCacheHandle,
 
     // Unique identifier for this picture.
     pub id: PictureId,
 }
@@ -177,69 +172,85 @@ impl PicturePrimitive {
         }
     }
 
     pub fn new_image(
         id: PictureId,
         composite_mode: Option<PictureCompositeMode>,
         is_in_3d_context: bool,
         pipeline_id: PipelineId,
-        original_spatial_node_index: SpatialNodeIndex,
         frame_output_pipeline_id: Option<PipelineId>,
         apply_local_clip_rect: bool,
     ) -> Self {
         PicturePrimitive {
             runs: Vec::new(),
             surface: None,
             secondary_render_task_id: None,
             composite_mode,
             is_in_3d_context,
             frame_output_pipeline_id,
-            original_spatial_node_index,
-            real_local_rect: LayoutRect::zero(),
             extra_gpu_data_handle: GpuCacheHandle::new(),
             apply_local_clip_rect,
             pipeline_id,
             id,
         }
     }
 
+    pub fn take_context(
+        &mut self,
+        allow_subpixel_aa: bool,
+    ) -> PictureContext {
+        // TODO(lsalzman): allow overriding parent if intermediate surface is opaque
+        let allow_subpixel_aa = allow_subpixel_aa && self.allow_subpixel_aa();
+
+        let inflation_factor = match self.composite_mode {
+            Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
+                // The amount of extra space needed for primitives inside
+                // this picture to ensure the visibility check is correct.
+                BLUR_SAMPLE_SCALE * blur_radius
+            }
+            _ => {
+                0.0
+            }
+        };
+
+        PictureContext {
+            pipeline_id: self.pipeline_id,
+            prim_runs: mem::replace(&mut self.runs, Vec::new()),
+            apply_local_clip_rect: self.apply_local_clip_rect,
+            inflation_factor,
+            allow_subpixel_aa,
+        }
+    }
+
     pub fn add_primitive(
         &mut self,
         prim_index: PrimitiveIndex,
-        spatial_node_index: SpatialNodeIndex,
     ) {
         if let Some(ref mut run) = self.runs.last_mut() {
-            if run.spatial_node_index == spatial_node_index &&
-               run.base_prim_index.0 + run.count == prim_index.0 {
+            if run.base_prim_index.0 + run.count == prim_index.0 {
                 run.count += 1;
                 return;
             }
         }
 
         self.runs.push(PrimitiveRun {
             base_prim_index: prim_index,
             count: 1,
-            spatial_node_index,
         });
     }
 
-    pub fn update_local_rect_and_set_runs(
+    pub fn restore_context(
         &mut self,
-        prim_run_rect: PrimitiveRunLocalRect,
-        prim_runs: Vec<PrimitiveRun>,
+        context: PictureContext,
+        local_rect_builder: LocalRectBuilder,
     ) -> LayoutRect {
-        self.runs = prim_runs;
+        self.runs = context.prim_runs;
 
-        let local_content_rect = prim_run_rect.mapping.local_rect;
-
-        self.real_local_rect = match prim_run_rect.original_mapping {
-            Some(mapping) => mapping.local_rect,
-            None => local_content_rect,
-        };
+        let local_content_rect = local_rect_builder.local_rect;
 
         match self.composite_mode {
             Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
                 let inflate_size = (blur_radius * BLUR_SAMPLE_SCALE).ceil();
                 local_content_rect.inflate(inflate_size, inflate_size)
             }
             Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _))) => {
                 let inflate_size = (blur_radius * BLUR_SAMPLE_SCALE).ceil();
@@ -272,25 +283,25 @@ impl PicturePrimitive {
             }
             None => {
                 true
             }
         }
     }
 
     // Disallow subpixel AA if an intermediate surface is needed.
-    pub fn allow_subpixel_aa(&self) -> bool {
+    fn allow_subpixel_aa(&self) -> bool {
         self.can_draw_directly_to_parent_surface()
     }
 
     pub fn prepare_for_render(
         &mut self,
         prim_index: PrimitiveIndex,
         prim_metadata: &mut PrimitiveMetadata,
-        prim_run_context: &PrimitiveRunContext,
+        prim_context: &PrimitiveContext,
         mut pic_state_for_children: PictureState,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
     ) {
         let prim_screen_rect = prim_metadata
                                 .screen_rect
                                 .as_ref()
@@ -323,30 +334,31 @@ impl PicturePrimitive {
                 let device_rect = prim_screen_rect
                     .clipped
                     .inflate(blur_range, blur_range)
                     .intersection(&prim_screen_rect.unclipped)
                     .unwrap();
 
                 let uv_rect_kind = calculate_uv_rect_kind(
                     &prim_metadata.local_rect,
-                    &prim_run_context.transform,
+                    &prim_context.transform,
                     &device_rect,
                     frame_context.device_pixel_scale,
                 );
 
                 // If we are drawing a blur that has primitives or clips that contain
                 // a complex coordinate system, don't bother caching them (for now).
                 // It's likely that they are animating and caching may not help here
                 // anyway. In the future we should relax this a bit, so that we can
                 // cache tasks with complex coordinate systems if we detect the
                 // relevant transforms haven't changed from frame to frame.
                 let surface = if pic_state_for_children.has_non_root_coord_system {
                     let picture_task = RenderTask::new_picture(
-                        RenderTaskLocation::Dynamic(None, Some(device_rect.size)),
+                        RenderTaskLocation::Dynamic(None, device_rect.size),
+                        prim_screen_rect.unclipped.size,
                         prim_index,
                         device_rect.origin,
                         pic_state_for_children.tasks,
                         uv_rect_kind,
                     );
 
                     let picture_task_id = frame_state.render_tasks.add(picture_task);
 
@@ -392,17 +404,18 @@ impl PicturePrimitive {
                         frame_state.gpu_cache,
                         frame_state.render_tasks,
                         None,
                         false,
                         |render_tasks| {
                             let child_tasks = mem::replace(&mut pic_state_for_children.tasks, Vec::new());
 
                             let picture_task = RenderTask::new_picture(
-                                RenderTaskLocation::Dynamic(None, Some(device_rect.size)),
+                                RenderTaskLocation::Dynamic(None, device_rect.size),
+                                prim_screen_rect.unclipped.size,
                                 prim_index,
                                 device_rect.origin,
                                 child_tasks,
                                 uv_rect_kind,
                             );
 
                             let picture_task_id = render_tasks.add(picture_task);
 
@@ -442,23 +455,24 @@ impl PicturePrimitive {
                 let device_rect = prim_screen_rect
                     .clipped
                     .inflate(blur_range, blur_range)
                     .intersection(&prim_screen_rect.unclipped)
                     .unwrap();
 
                 let uv_rect_kind = calculate_uv_rect_kind(
                     &prim_metadata.local_rect,
-                    &prim_run_context.transform,
+                    &prim_context.transform,
                     &device_rect,
                     frame_context.device_pixel_scale,
                 );
 
                 let mut picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, Some(device_rect.size)),
+                    RenderTaskLocation::Dynamic(None, device_rect.size),
+                    prim_screen_rect.unclipped.size,
                     prim_index,
                     device_rect.origin,
                     pic_state_for_children.tasks,
                     uv_rect_kind,
                 );
                 picture_task.mark_for_saving();
 
                 let picture_task_id = frame_state.render_tasks.add(picture_task);
@@ -511,23 +525,24 @@ impl PicturePrimitive {
                     // segment rect / extra data
                     request.push(shadow_rect);
                     request.push([0.0, 0.0, 0.0, 0.0]);
                 }
             }
             Some(PictureCompositeMode::MixBlend(..)) => {
                 let uv_rect_kind = calculate_uv_rect_kind(
                     &prim_metadata.local_rect,
-                    &prim_run_context.transform,
+                    &prim_context.transform,
                     &prim_screen_rect.clipped,
                     frame_context.device_pixel_scale,
                 );
 
                 let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, Some(prim_screen_rect.clipped.size)),
+                    RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
+                    prim_screen_rect.unclipped.size,
                     prim_index,
                     prim_screen_rect.clipped.origin,
                     pic_state_for_children.tasks,
                     uv_rect_kind,
                 );
 
                 let readback_task_id = frame_state.render_tasks.add(
                     RenderTask::new_readback(prim_screen_rect.clipped)
@@ -546,43 +561,45 @@ impl PicturePrimitive {
                         for i in 0..5 {
                             request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
                         }
                     }
                 }
 
                 let uv_rect_kind = calculate_uv_rect_kind(
                     &prim_metadata.local_rect,
-                    &prim_run_context.transform,
+                    &prim_context.transform,
                     &prim_screen_rect.clipped,
                     frame_context.device_pixel_scale,
                 );
 
                 let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, Some(prim_screen_rect.clipped.size)),
+                    RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
+                    prim_screen_rect.unclipped.size,
                     prim_index,
                     prim_screen_rect.clipped.origin,
                     pic_state_for_children.tasks,
                     uv_rect_kind,
                 );
 
                 let render_task_id = frame_state.render_tasks.add(picture_task);
                 pic_state.tasks.push(render_task_id);
                 self.surface = Some(PictureSurface::RenderTask(render_task_id));
             }
             Some(PictureCompositeMode::Blit) | None => {
                 let uv_rect_kind = calculate_uv_rect_kind(
                     &prim_metadata.local_rect,
-                    &prim_run_context.transform,
+                    &prim_context.transform,
                     &prim_screen_rect.clipped,
                     frame_context.device_pixel_scale,
                 );
 
                 let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, Some(prim_screen_rect.clipped.size)),
+                    RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
+                    prim_screen_rect.unclipped.size,
                     prim_index,
                     prim_screen_rect.clipped.origin,
                     pic_state_for_children.tasks,
                     uv_rect_kind,
                 );
 
                 let render_task_id = frame_state.render_tasks.add(picture_task);
                 pic_state.tasks.push(render_task_id);
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -5,36 +5,34 @@
 use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipMode, ColorF};
 use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, LayoutTransform};
 use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset};
 use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D};
 use api::{PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets};
 use api::{BorderWidths, BoxShadowClipMode, LayoutToWorldScale, NormalBorder};
 use app_units::Au;
 use border::{BorderCacheKey, BorderRenderTaskInfo};
-use box_shadow::BLUR_SAMPLE_SCALE;
-use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex};
+use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex};
 use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
-use frame_builder::PrimitiveRunContext;
+use frame_builder::PrimitiveContext;
 use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
 use gpu_types::BrushFlags;
 use image::{for_each_tile, for_each_repetition};
 use picture::{PictureCompositeMode, PicturePrimitive};
 #[cfg(debug_assertions)]
 use render_backend::FrameId;
 use render_task::{BlitSource, RenderTask, RenderTaskCacheKey};
 use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
 use scene::SceneProperties;
 use segment::SegmentBuilder;
-use spatial_node::SpatialNode;
 use std::{mem, usize};
 use util::{MatrixHelpers, calculate_screen_bounding_rect};
 use util::{pack_as_float, recycle_vec, TransformedRectKind};
 
 
 const MIN_BRUSH_SPLIT_AREA: f32 = 256.0 * 256.0;
 pub const VECS_PER_SEGMENT: usize = 2;
 
@@ -66,17 +64,16 @@ pub struct Transform<'a> {
     pub backface_is_visible: bool,
     pub transform_kind: TransformedRectKind,
 }
 
 #[derive(Debug)]
 pub struct PrimitiveRun {
     pub base_prim_index: PrimitiveIndex,
     pub count: usize,
-    pub spatial_node_index: SpatialNodeIndex,
 }
 
 impl PrimitiveRun {
     pub fn is_chasing(&self, index: Option<PrimitiveIndex>) -> bool {
         match index {
             Some(id) if cfg!(debug_assertions) => {
                 self.base_prim_index <= id && id.0 < self.base_prim_index.0 + self.count
             }
@@ -102,91 +99,20 @@ impl PrimitiveOpacity {
     pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
         PrimitiveOpacity {
             is_opaque: alpha == 1.0,
         }
     }
 }
 
 #[derive(Debug)]
-pub enum CoordinateSpaceMappingKind {
+pub enum CoordinateSpaceMapping {
     Local,
     Offset(LayoutVector2D),
-    Transform(Option<LayoutTransform>),
-}
-
-#[derive(Debug)]
-pub struct CoordinateSpaceMapping {
-    kind: CoordinateSpaceMappingKind,
-    pub local_rect: LayoutRect,
-    ref_spatial_node_index: SpatialNodeIndex,
-}
-
-impl CoordinateSpaceMapping {
-    fn new(
-        ref_spatial_node_index: SpatialNodeIndex,
-    ) -> Self {
-        CoordinateSpaceMapping {
-            kind: CoordinateSpaceMappingKind::Local,
-            local_rect: LayoutRect::zero(),
-            ref_spatial_node_index,
-        }
-    }
-
-    pub fn set_target_spatial_node(
-        &mut self,
-        target_node_index: SpatialNodeIndex,
-        spatial_nodes: &[SpatialNode],
-    ) {
-        let ref_spatial_node = &spatial_nodes[self.ref_spatial_node_index.0];
-        let target_spatial_node = &spatial_nodes[target_node_index.0];
-
-        self.kind = if self.ref_spatial_node_index == target_node_index {
-            CoordinateSpaceMappingKind::Local
-        } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id {
-            let offset = target_spatial_node.coordinate_system_relative_offset -
-                         ref_spatial_node.coordinate_system_relative_offset;
-            CoordinateSpaceMappingKind::Offset(offset)
-        } else {
-            let relative_transform = ref_spatial_node
-                .world_content_transform
-                .inverse()
-                .map(|inv_parent| {
-                    inv_parent.pre_mul(&target_spatial_node.world_content_transform)
-                })
-                .map(|transform| {
-                    *transform.to_transform()
-                });
-            CoordinateSpaceMappingKind::Transform(relative_transform)
-        }
-    }
-
-    pub fn accumulate(&mut self, rect: &LayoutRect) {
-        match self.kind {
-            CoordinateSpaceMappingKind::Local => {
-                self.local_rect = self.local_rect.union(rect);
-            }
-            CoordinateSpaceMappingKind::Offset(ref offset) => {
-                let rect = rect.translate(offset);
-                self.local_rect = self.local_rect.union(&rect);
-            }
-            CoordinateSpaceMappingKind::Transform(ref transform) => {
-                if let Some(ref matrix) = transform {
-                    match matrix.transform_rect(rect) {
-                        Some(bounds) => {
-                            self.local_rect = self.local_rect.union(&bounds);
-                        }
-                        None => {
-                            warn!("parent relative transform can't transform the primitive rect for {:?}", rect);
-                        }
-                    }
-                }
-            }
-        }
-    }
+    Transform(LayoutTransform),
 }
 
 // Represents the local space rect of a list of
 // primitive runs. For most primitive runs, the
 // primitive runs are attached to the parent they
 // are declared in. However, when a primitive run
 // is part of a 3d rendering context, it may get
 // hoisted to a higher level in the picture tree.
@@ -195,58 +121,79 @@ impl CoordinateSpaceMapping {
 // allows constructing the true world space polygons
 // for the primitive, to enable the plane splitting
 // logic to work correctly.
 // TODO(gw) In the future, we can probably simplify
 //          this - perhaps calculate the world space
 //          polygons directly and store internally
 //          in the picture structure.
 #[derive(Debug)]
-pub struct PrimitiveRunLocalRect {
-    pub mapping: CoordinateSpaceMapping,
-    pub original_mapping: Option<CoordinateSpaceMapping>,
+pub struct LocalRectBuilder {
+    kind: CoordinateSpaceMapping,
+    ref_spatial_node_index: SpatialNodeIndex,
+    current_target_spatial_node_index: SpatialNodeIndex,
+    pub local_rect: LayoutRect,
 }
 
-impl PrimitiveRunLocalRect {
-    pub fn new(
-        spatial_node_index: SpatialNodeIndex,
-        original_spatial_node_index: SpatialNodeIndex,
-    ) -> Self {
-        let mapping = CoordinateSpaceMapping::new(spatial_node_index);
-
-        let original_mapping = if spatial_node_index == original_spatial_node_index {
-            None
-        } else {
-            Some(CoordinateSpaceMapping::new(original_spatial_node_index))
-        };
-
-        PrimitiveRunLocalRect {
-            mapping,
-            original_mapping,
+impl LocalRectBuilder {
+    pub fn new(ref_spatial_node_index: SpatialNodeIndex) -> Self {
+        LocalRectBuilder {
+            kind: CoordinateSpaceMapping::Local,
+            local_rect: LayoutRect::zero(),
+            ref_spatial_node_index,
+            current_target_spatial_node_index: ref_spatial_node_index,
         }
     }
 
     pub fn set_target_spatial_node(
         &mut self,
         target_node_index: SpatialNodeIndex,
-        spatial_nodes: &[SpatialNode],
+        clip_scroll_tree: &ClipScrollTree,
     ) {
-        self.mapping
-            .set_target_spatial_node(target_node_index, spatial_nodes);
+        if target_node_index != self.current_target_spatial_node_index {
+            let spatial_nodes = &clip_scroll_tree.spatial_nodes;
+            let ref_spatial_node = &spatial_nodes[self.ref_spatial_node_index.0];
+            let target_spatial_node = &spatial_nodes[target_node_index.0];
+            self.current_target_spatial_node_index = target_node_index;
 
-        if let Some(ref mut mapping) = self.original_mapping {
-            mapping.set_target_spatial_node(target_node_index, spatial_nodes);
+            self.kind = if self.ref_spatial_node_index == target_node_index {
+                CoordinateSpaceMapping::Local
+            } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id {
+                let offset = target_spatial_node.coordinate_system_relative_offset -
+                             ref_spatial_node.coordinate_system_relative_offset;
+                CoordinateSpaceMapping::Offset(offset)
+            } else {
+                let transform = clip_scroll_tree.get_relative_transform(
+                    self.ref_spatial_node_index,
+                    target_node_index,
+                );
+                CoordinateSpaceMapping::Transform(transform)
+            };
         }
     }
 
     pub fn accumulate(&mut self, rect: &LayoutRect) {
-        self.mapping.accumulate(rect);
-
-        if let Some(ref mut mapping) = self.original_mapping {
-            mapping.accumulate(rect);
+        match self.kind {
+            CoordinateSpaceMapping::Local => {
+                self.local_rect = self.local_rect.union(rect);
+            }
+            CoordinateSpaceMapping::Offset(ref offset) => {
+                let rect = rect.translate(offset);
+                self.local_rect = self.local_rect.union(&rect);
+            }
+            CoordinateSpaceMapping::Transform(ref transform) => {
+                match transform.transform_rect(rect) {
+                    Some(bounds) => {
+                        self.local_rect = self.local_rect.union(&bounds);
+                    }
+                    None => {
+                        warn!("parent relative transform can't transform the primitive rect for {:?}", rect);
+                    }
+                }
+            }
         }
     }
 }
 
 /// For external images, it's not possible to know the
 /// UV coords of the image (or the image data itself)
 /// until the render thread receives the frame and issues
 /// callbacks to the client application. For external
@@ -287,16 +234,17 @@ pub struct ScreenRect {
     pub unclipped: DeviceIntRect,
 }
 
 // TODO(gw): Pack the fields here better!
 #[derive(Debug)]
 pub struct PrimitiveMetadata {
     pub opacity: PrimitiveOpacity,
     pub clip_chain_id: ClipChainId,
+    pub spatial_node_index: SpatialNodeIndex,
     pub gpu_location: GpuCacheHandle,
     pub clip_task_id: Option<RenderTaskId>,
 
     // TODO(gw): In the future, we should just pull these
     //           directly from the DL item, instead of
     //           storing them here.
     pub local_rect: LayoutRect,
     pub local_clip_rect: LayoutRect,
@@ -395,16 +343,17 @@ pub enum BrushKind {
     },
     Clear,
     Picture(PicturePrimitive),
     Image {
         request: ImageRequest,
         alpha_type: AlphaType,
         stretch_size: LayoutSize,
         tile_spacing: LayoutSize,
+        color: ColorF,
         source: ImageSource,
         sub_rect: Option<DeviceIntRect>,
         opacity_binding: OpacityBinding,
         visible_tiles: Vec<VisibleImageTile>,
     },
     YuvImage {
         yuv_key: [ImageKey; 3],
         format: YuvFormat,
@@ -598,18 +547,18 @@ impl BrushPrimitive {
                     local_rect.size.width,
                     local_rect.size.height,
                     0.0,
                     0.0,
                 ]);
             }
             // Images are drawn as a white color, modulated by the total
             // opacity coming from any collapsed property bindings.
-            BrushKind::Image { stretch_size, tile_spacing, ref opacity_binding, .. } => {
-                request.push(ColorF::new(1.0, 1.0, 1.0, opacity_binding.current).premultiplied());
+            BrushKind::Image { stretch_size, tile_spacing, color, ref opacity_binding, .. } => {
+                request.push(color.scale_alpha(opacity_binding.current).premultiplied());
                 request.push(PremultipliedColorF::WHITE);
                 request.push([
                     stretch_size.width + tile_spacing.width,
                     stretch_size.height + tile_spacing.height,
                     0.0,
                     0.0,
                 ]);
             }
@@ -1369,25 +1318,27 @@ impl PrimitiveStore {
     }
 
     pub fn add_primitive(
         &mut self,
         local_rect: &LayoutRect,
         local_clip_rect: &LayoutRect,
         is_backface_visible: bool,
         clip_chain_id: ClipChainId,
+        spatial_node_index: SpatialNodeIndex,
         tag: Option<ItemTag>,
         container: PrimitiveContainer,
     ) -> PrimitiveIndex {
         let prim_index = self.primitives.len();
 
         let base_metadata = PrimitiveMetadata {
             clip_chain_id,
             gpu_location: GpuCacheHandle::new(),
             clip_task_id: None,
+            spatial_node_index,
             local_rect: *local_rect,
             local_clip_rect: *local_clip_rect,
             combined_local_clip_rect: *local_clip_rect,
             is_backface_visible,
             screen_rect: None,
             tag,
             opacity: PrimitiveOpacity::translucent(),
             #[cfg(debug_assertions)]
@@ -1551,120 +1502,93 @@ impl PrimitiveStore {
 
     pub fn prim_count(&self) -> usize {
         self.primitives.len()
     }
 
     pub fn prepare_prim_for_render(
         &mut self,
         prim_index: PrimitiveIndex,
-        prim_run_context: &PrimitiveRunContext,
+        prim_context: &PrimitiveContext,
         pic_context: &PictureContext,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         display_list: &BuiltDisplayList,
+        is_chased: bool,
     ) -> Option<LayoutRect> {
         let mut may_need_clip_mask = true;
         let mut pic_state_for_children = PictureState::new();
-        let is_chased = Some(prim_index) == self.chase_id;
 
         // If we have dependencies, we need to prepare them first, in order
         // to know the actual rect of this primitive.
         // For example, scrolling may affect the location of an item in
         // local space, which may force us to render this item on a larger
         // picture target, if being composited.
         let pic_context_for_children = {
-            let prim = &mut self.primitives[prim_index.0];
-
-            // Do some basic checks first, that can early out
-            // without even knowing the local rect.
-            if !prim.metadata.is_backface_visible && prim_run_context.transform.backface_is_visible {
-                if cfg!(debug_assertions) && is_chased {
-                    println!("\tculled for not having visible back faces");
-                }
-                return None;
-            }
-
-            match prim.details {
+            match self.primitives[prim_index.0].details {
                 PrimitiveDetails::Brush(ref mut brush) => {
                     match brush.kind {
                         BrushKind::Picture(ref mut pic) => {
                             if !pic.resolve_scene_properties(frame_context.scene_properties) {
                                 if cfg!(debug_assertions) && is_chased {
                                     println!("\tculled for carrying an invisible composite filter");
                                 }
                                 return None;
                             }
 
                             may_need_clip_mask = pic.composite_mode.is_some();
 
-                            let inflation_factor = match pic.composite_mode {
-                                Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
-                                    // The amount of extra space needed for primitives inside
-                                    // this picture to ensure the visibility check is correct.
-                                    BLUR_SAMPLE_SCALE * blur_radius
-                                }
-                                _ => {
-                                    0.0
-                                }
-                            };
-
                             // Mark whether this picture has a complex coordinate system.
                             pic_state_for_children.has_non_root_coord_system |=
-                                prim_run_context.scroll_node.coordinate_system_id != CoordinateSystemId::root();
+                                prim_context.spatial_node.coordinate_system_id != CoordinateSystemId::root();
 
-                            Some(PictureContext {
-                                pipeline_id: pic.pipeline_id,
-                                prim_runs: mem::replace(&mut pic.runs, Vec::new()),
-                                spatial_node_index: prim_run_context.spatial_node_index,
-                                original_spatial_node_index: pic.original_spatial_node_index,
-                                apply_local_clip_rect: pic.apply_local_clip_rect,
-                                inflation_factor,
-                                // TODO(lsalzman): allow overriding parent if intermediate surface is opaque
-                                allow_subpixel_aa: pic_context.allow_subpixel_aa && pic.allow_subpixel_aa(),
-                            })
+                            Some(pic.take_context(pic_context.allow_subpixel_aa))
                         }
                         _ => {
                             None
                         }
                     }
                 }
                 PrimitiveDetails::TextRun(..) => {
                     None
                 }
             }
         };
 
         if let Some(pic_context_for_children) = pic_context_for_children {
-            let result = self.prepare_prim_runs(
+            let mut local_rect_builder = LocalRectBuilder::new(
+                prim_context.spatial_node_index,
+            );
+
+            self.prepare_prim_runs(
                 &pic_context_for_children,
                 &mut pic_state_for_children,
                 frame_context,
                 frame_state,
+                &mut local_rect_builder,
             );
 
             // Restore the dependencies (borrow check dance)
             let prim = &mut self.primitives[prim_index.0];
             let new_local_rect = prim
                 .as_pic_mut()
-                .update_local_rect_and_set_runs(
-                    result,
-                    pic_context_for_children.prim_runs
+                .restore_context(
+                    pic_context_for_children,
+                    local_rect_builder,
                 );
 
             if new_local_rect != prim.metadata.local_rect {
                 prim.metadata.local_rect = new_local_rect;
                 frame_state.gpu_cache.invalidate(&mut prim.metadata.gpu_location);
                 pic_state.local_rect_changed = true;
             }
         }
 
         let prim = &mut self.primitives[prim_index.0];
-        prim.metadata.screen_rect = None;
 
         if prim.metadata.local_rect.size.width <= 0.0 ||
            prim.metadata.local_rect.size.height <= 0.0 {
             if cfg!(debug_assertions) && is_chased {
                 println!("\tculled for zero local rectangle");
             }
             return None;
         }
@@ -1690,17 +1614,17 @@ impl PrimitiveStore {
         };
 
         let clip_chain = frame_state
             .clip_store
             .build_clip_chain_instance(
                 prim.metadata.clip_chain_id,
                 local_rect,
                 prim.metadata.local_clip_rect,
-                prim_run_context.spatial_node_index,
+                prim_context.spatial_node_index,
                 &frame_context.clip_scroll_tree.spatial_nodes,
                 frame_state.gpu_cache,
                 frame_state.resource_cache,
                 frame_context.device_pixel_scale,
             );
 
         let clip_chain = match clip_chain {
             Some(clip_chain) => clip_chain,
@@ -1715,42 +1639,42 @@ impl PrimitiveStore {
                 clip_chain.clips_range,
                 if pic_context.apply_local_clip_rect { "(applied)" } else { "" },
             );
         }
 
         pic_state.has_non_root_coord_system |= clip_chain.has_non_root_coord_system;
 
         let unclipped_device_rect = match calculate_screen_bounding_rect(
-            &prim_run_context.scroll_node.world_content_transform,
+            &prim_context.spatial_node.world_content_transform,
             &local_rect,
             frame_context.device_pixel_scale,
             None, //TODO: inflate `frame_context.screen_rect` appropriately
         ) {
             Some(rect) => rect,
             None => {
                 if cfg!(debug_assertions) && is_chased {
                     println!("\tculled for being behind the near plane of transform: {:?}",
-                        prim_run_context.scroll_node.world_content_transform);
+                        prim_context.spatial_node.world_content_transform);
                 }
                 return None
             }
         };
 
         let clipped_device_rect = match calculate_screen_bounding_rect(
-            &prim_run_context.scroll_node.world_content_transform,
+            &prim_context.spatial_node.world_content_transform,
             &clip_chain.local_bounding_rect,
             frame_context.device_pixel_scale,
             None,
         ) {
             Some(rect) => rect,
             None => {
                 if cfg!(debug_assertions) && is_chased {
                     println!("\tculled for being behind the near plane of transform: {:?}",
-                        prim_run_context.scroll_node.world_content_transform);
+                        prim_context.spatial_node.world_content_transform);
                 }
                 return None
             }
         };
 
         let clipped_device_rect = match clipped_device_rect.intersection(&frame_context.screen_rect) {
             Some(clipped_device_rect) => clipped_device_rect,
             None => return None,
@@ -1774,34 +1698,34 @@ impl PrimitiveStore {
 
         prim.build_prim_segments_if_needed(
             pic_state,
             frame_state,
             frame_context,
         );
 
         if may_need_clip_mask && !prim.update_clip_task(
-            prim_run_context,
+            prim_context,
             &clipped_device_rect,
             &clip_chain,
             pic_state,
             frame_context,
             frame_state,
             is_chased,
         ) {
             return None;
         }
 
         if cfg!(debug_assertions) && is_chased {
             println!("\tconsidered visible and ready with local rect {:?}", local_rect);
         }
 
         prim.prepare_prim_for_render_inner(
             prim_index,
-            prim_run_context,
+            prim_context,
             pic_state_for_children,
             pic_context,
             pic_state,
             frame_context,
             frame_state,
             display_list,
             is_chased,
         );
@@ -1818,89 +1742,100 @@ impl PrimitiveStore {
     }
 
     pub fn prepare_prim_runs(
         &mut self,
         pic_context: &PictureContext,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
-    ) -> PrimitiveRunLocalRect {
-        let mut result = PrimitiveRunLocalRect::new(
-            pic_context.spatial_node_index,
-            pic_context.original_spatial_node_index,
-        );
-
+        local_rect_builder: &mut LocalRectBuilder,
+    ) {
         let display_list = &frame_context
             .pipelines
             .get(&pic_context.pipeline_id)
             .expect("No display list?")
             .display_list;
 
         for run in &pic_context.prim_runs {
-            // TODO(gw): Perhaps we can restructure this to not need to create
-            //           a new primitive context for every run (if the hash
-            //           lookups ever show up in a profile).
-            let scroll_node = &frame_context
-                .clip_scroll_tree
-                .spatial_nodes[run.spatial_node_index.0];
-
             if run.is_chasing(self.chase_id) {
                 println!("\tpreparing a run of length {} in pipeline {:?}",
                     run.count, pic_context.pipeline_id);
-                println!("\trun {:?}", run.spatial_node_index);
-                println!("\ttransform {:?}", scroll_node.world_content_transform.to_transform());
             }
 
-            // Mark whether this picture contains any complex coordinate
-            // systems, due to either the scroll node or the clip-chain.
-            pic_state.has_non_root_coord_system |=
-                scroll_node.coordinate_system_id != CoordinateSystemId::root();
-
-            if !scroll_node.invertible {
-                if run.is_chasing(self.chase_id) {
-                    println!("\tculled for the scroll node transform being invertible");
-                }
-                continue;
-            }
-
-            result.set_target_spatial_node(
-                run.spatial_node_index,
-                &frame_context.clip_scroll_tree.spatial_nodes,
-            );
-
-            let transform = frame_context
-                .transforms
-                .get_transform(run.spatial_node_index);
-
-            let child_prim_run_context = PrimitiveRunContext::new(
-                scroll_node,
-                run.spatial_node_index,
-                transform,
-            );
-
             for i in 0 .. run.count {
                 let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
+                let is_chased = Some(prim_index) == self.chase_id;
+
+                // TODO(gw): These workarounds for borrowck are unfortunate. We
+                //           should see if we can re-structure these to avoid so
+                //           many special borrow blocks.
+                let (spatial_node_index, is_backface_visible) = {
+                    let prim = &self.primitives[prim_index.0];
+                    (prim.metadata.spatial_node_index, prim.metadata.is_backface_visible)
+                };
+
+                let spatial_node = &frame_context
+                    .clip_scroll_tree
+                    .spatial_nodes[spatial_node_index.0];
+
+                let transform = frame_context
+                    .transforms
+                    .get_transform(spatial_node_index);
+
+                // TODO(gw): Although constructing these is cheap, they are often
+                //           the same for many consecutive primitives, so it may
+                //           be worth caching the most recent context.
+                let prim_context = PrimitiveContext::new(
+                    spatial_node,
+                    spatial_node_index,
+                    transform,
+                );
+
+                // Do some basic checks first, that can early out
+                // without even knowing the local rect.
+                if !is_backface_visible && prim_context.transform.backface_is_visible {
+                    if cfg!(debug_assertions) && is_chased {
+                        println!("\tculled for not having visible back faces");
+                    }
+                    continue;
+                }
+
+                if !spatial_node.invertible {
+                    if cfg!(debug_assertions) && is_chased {
+                        println!("\tculled for the scroll node transform being invertible");
+                    }
+                    continue;
+                }
+
+                // Mark whether this picture contains any complex coordinate
+                // systems, due to either the scroll node or the clip-chain.
+                pic_state.has_non_root_coord_system |=
+                    spatial_node.coordinate_system_id != CoordinateSystemId::root();
+
+                local_rect_builder.set_target_spatial_node(
+                    spatial_node_index,
+                    &frame_context.clip_scroll_tree,
+                );
 
                 if let Some(prim_local_rect) = self.prepare_prim_for_render(
                     prim_index,
-                    &child_prim_run_context,
+                    &prim_context,
                     pic_context,
                     pic_state,
                     frame_context,
                     frame_state,
                     display_list,
+                    is_chased,
                 ) {
                     frame_state.profile_counters.visible_primitives.inc();
-                    result.accumulate(&prim_local_rect);
+                    local_rect_builder.accumulate(&prim_local_rect);
                 }
             }
         }
-
-        result
     }
 }
 
 fn build_gradient_stops_request(
     stops_handle: &mut GpuCacheHandle,
     stops_range: ItemRange<GradientStop>,
     reverse_stops: bool,
     frame_state: &mut FrameBuildingState,
@@ -1918,17 +1853,17 @@ fn build_gradient_stops_request(
     }
 }
 
 fn decompose_repeated_primitive(
     visible_tiles: &mut Vec<VisibleGradientTile>,
     metadata: &mut PrimitiveMetadata,
     stretch_size: &LayoutSize,
     tile_spacing: &LayoutSize,
-    prim_run_context: &PrimitiveRunContext,
+    prim_context: &PrimitiveContext,
     frame_context: &FrameBuildingContext,
     frame_state: &mut FrameBuildingState,
     callback: &mut FnMut(&LayoutRect, GpuDataRequest),
 ) {
     visible_tiles.clear();
 
     // Tighten the clip rect because decomposing the repeated image can
     // produce primitives that are partially covering the original image
@@ -1938,17 +1873,17 @@ fn decompose_repeated_primitive(
         .intersection(&metadata.local_rect).unwrap();
 
     let unclipped_device_rect = &metadata
         .screen_rect
         .unwrap()
         .unclipped;
 
     let visible_rect = compute_conservative_visible_rect(
-        prim_run_context,
+        prim_context,
         frame_context,
         unclipped_device_rect,
         &tight_clip_rect
     );
     let stride = *stretch_size + *tile_spacing;
 
     for_each_repetition(
         &metadata.local_rect,
@@ -1979,26 +1914,26 @@ fn decompose_repeated_primitive(
         // Clearing the screen rect has the effect of "culling out" the primitive
         // from the point of view of the batch builder, and ensures we don't hit
         // assertions later on because we didn't request any image.
         metadata.screen_rect = None;
     }
 }
 
 fn compute_conservative_visible_rect(
-    prim_run_context: &PrimitiveRunContext,
+    prim_context: &PrimitiveContext,
     frame_context: &FrameBuildingContext,
     clipped_device_rect: &DeviceIntRect,
     local_clip_rect: &LayoutRect,
 ) -> LayoutRect {
     let world_screen_rect = clipped_device_rect
         .to_f32() / frame_context.device_pixel_scale;
 
-    if let Some(layer_screen_rect) = prim_run_context
-        .scroll_node
+    if let Some(layer_screen_rect) = prim_context
+        .spatial_node
         .world_content_transform
         .unapply(&world_screen_rect) {
 
         return local_clip_rect.intersection(&layer_screen_rect).unwrap_or(LayoutRect::zero());
     }
 
     *local_clip_rect
 }
@@ -2034,17 +1969,17 @@ fn write_brush_segment_description(
     metadata: &PrimitiveMetadata,
     clip_chain: &ClipChainInstance,
     frame_state: &mut FrameBuildingState,
 ) {
     match brush.segment_desc {
         Some(ref segment_desc) => {
             // If we already have a segment descriptor, only run through the
             // clips list if we haven't already determined the mask kind.
-            if segment_desc.clip_mask_kind != BrushClipMaskKind::Unknown {
+            if segment_desc.clip_mask_kind == clip_chain.clip_mask_kind {
                 return;
             }
         }
         None => {
             // If no segment descriptor built yet, see if it is a brush
             // type that wants to be segmented.
             if !brush.kind.supports_segments(frame_state.resource_cache) {
                 return;
@@ -2065,51 +2000,26 @@ fn write_brush_segment_description(
     let mut rect_clips_only = true;
 
     let mut segment_builder = SegmentBuilder::new(
         metadata.local_rect,
         None,
         metadata.local_clip_rect
     );
 
-    // If this primitive is clipped by clips from a different coordinate system, then we
-    // need to apply a clip mask for the entire primitive.
-    let mut clip_mask_kind = if clip_chain.has_clips_from_other_coordinate_systems {
-        BrushClipMaskKind::Global
-    } else {
-        BrushClipMaskKind::Individual
-    };
-
     // Segment the primitive on all the local-space clip sources that we can.
     for i in 0 .. clip_chain.clips_range.count {
         let (clip_node, flags) = frame_state.clip_store.get_node_from_range(&clip_chain.clips_range, i);
 
-        if !flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
-            continue;
-        }
-
-        // TODO(gw): We can easily extend the segment builder to support these clip sources in
-        // the future, but they are rarely used.
-        // We must do this check here in case we continue early below.
-        if clip_node.item.is_image_or_line_decoration_clip() {
-            clip_mask_kind = BrushClipMaskKind::Global;
-        }
-
         // If this clip item is positioned by another positioning node, its relative position
         // could change during scrolling. This means that we would need to resegment. Instead
         // of doing that, only segment with clips that have the same positioning node.
         // TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only
         // when necessary while scrolling.
         if !flags.contains(ClipNodeFlags::SAME_SPATIAL_NODE) {
-            // We don't need to generate a global clip mask for rectangle clips because we are
-            // in the same coordinate system and rectangular clips are handled by the local
-            // clip chain rectangle.
-            if !clip_node.item.is_rect() {
-                clip_mask_kind = BrushClipMaskKind::Global;
-            }
             continue;
         }
 
         let (local_clip_rect, radius, mode) = match clip_node.item {
             ClipItem::RoundedRectangle(rect, radii, clip_mode) => {
                 rect_clips_only = false;
                 (rect, Some(radii), clip_mode)
             }
@@ -2150,17 +2060,17 @@ fn write_brush_segment_description(
         };
 
         segment_builder.push_clip_rect(local_clip_rect, radius, mode);
     }
 
     if is_large || rect_clips_only {
         match brush.segment_desc {
             Some(ref mut segment_desc) => {
-                segment_desc.clip_mask_kind = clip_mask_kind;
+                segment_desc.clip_mask_kind = clip_chain.clip_mask_kind;
             }
             None => {
                 // TODO(gw): We can probably make the allocation
                 //           patterns of this and the segment
                 //           builder significantly better, by
                 //           retaining it across primitives.
                 let mut segments = Vec::new();
 
@@ -2173,27 +2083,27 @@ fn write_brush_segment_description(
                             [0.0; 4],
                             BrushFlags::empty(),
                         ),
                     );
                 });
 
                 brush.segment_desc = Some(BrushSegmentDescriptor {
                     segments,
-                    clip_mask_kind,
+                    clip_mask_kind: clip_chain.clip_mask_kind,
                 });
             }
         }
     }
 }
 
 impl Primitive {
     fn update_clip_task_for_brush(
         &mut self,
-        prim_run_context: &PrimitiveRunContext,
+        prim_context: &PrimitiveContext,
         clip_chain: &ClipChainInstance,
         combined_outer_rect: &DeviceIntRect,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
     ) -> bool {
         debug_assert!(frame_context.screen_rect.contains_rect(combined_outer_rect));
 
@@ -2217,17 +2127,17 @@ impl Primitive {
 
         for segment in &mut segment_desc.segments {
             if !segment.may_need_clip_mask && clip_mask_kind != BrushClipMaskKind::Global {
                 segment.clip_task_id = BrushSegmentTaskId::Opaque;
                 continue;
             }
 
             let intersected_rect = calculate_screen_bounding_rect(
-                &prim_run_context.scroll_node.world_content_transform,
+                &prim_context.spatial_node.world_content_transform,
                 &segment.local_rect,
                 frame_context.device_pixel_scale,
                 Some(&combined_outer_rect),
             );
 
             let bounds = match intersected_rect {
                 Some(bounds) => bounds,
                 None => {
@@ -2264,17 +2174,17 @@ impl Primitive {
                 }
             }
         }
     }
 
     fn prepare_prim_for_render_inner(
         &mut self,
         prim_index: PrimitiveIndex,
-        prim_run_context: &PrimitiveRunContext,
+        prim_context: &PrimitiveContext,
         pic_state_for_children: PictureState,
         pic_context: &PictureContext,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         display_list: &BuiltDisplayList,
         is_chased: bool,
     ) {
@@ -2283,31 +2193,32 @@ impl Primitive {
         #[cfg(debug_assertions)]
         {
             metadata.prepared_frame_id = frame_state.render_tasks.frame_id();
         }
 
         match self.details {
             PrimitiveDetails::TextRun(ref mut text) => {
                 // The transform only makes sense for screen space rasterization
-                let transform = prim_run_context.scroll_node.world_content_transform.to_transform();
+                let transform = prim_context.spatial_node.world_content_transform.to_transform();
                 text.prepare_for_render(
                     frame_context.device_pixel_scale,
                     &transform,
                     pic_context.allow_subpixel_aa,
                     display_list,
                     frame_state,
                 );
             }
             PrimitiveDetails::Brush(ref mut brush) => {
                 match brush.kind {
                     BrushKind::Image {
                         request,
                         sub_rect,
                         stretch_size,
+                        color,
                         ref mut tile_spacing,
                         ref mut source,
                         ref mut opacity_binding,
                         ref mut visible_tiles,
                         ..
                     } => {
                         let image_properties = frame_state
                             .resource_cache
@@ -2323,17 +2234,18 @@ impl Primitive {
                             if opacity_binding.update(frame_context.scene_properties) {
                                 frame_state.gpu_cache.invalidate(&mut metadata.gpu_location);
                             }
 
                             // Update opacity for this primitive to ensure the correct
                             // batching parameters are used.
                             metadata.opacity.is_opaque =
                                 image_properties.descriptor.is_opaque &&
-                                opacity_binding.current == 1.0;
+                                opacity_binding.current == 1.0 &&
+                                color.a == 1.0;
 
                             if *tile_spacing != LayoutSize::zero() && !is_tiled {
                                 *source = ImageSource::Cache {
                                     // Size in device-pixels we need to allocate in render task cache.
                                     size: image_properties.descriptor.size.to_i32(),
                                     handle: None,
                                 };
                             }
@@ -2436,17 +2348,17 @@ impl Primitive {
                                 // Tighten the clip rect because decomposing the repeated image can
                                 // produce primitives that are partially covering the original image
                                 // rect and we want to clip these extra parts out.
                                 let tight_clip_rect = metadata
                                     .combined_local_clip_rect
                                     .intersection(&metadata.local_rect).unwrap();
 
                                 let visible_rect = compute_conservative_visible_rect(
-                                    prim_run_context,
+                                    prim_context,
                                     frame_context,
                                     &metadata.screen_rect.unwrap().clipped,
                                     &tight_clip_rect
                                 );
 
                                 let base_edge_flags = edge_flags_for_tile_spacing(tile_spacing);
 
                                 let stride = stretch_size + *tile_spacing;
@@ -2576,17 +2488,17 @@ impl Primitive {
                         if tile_spacing != LayoutSize::zero() {
                             is_tiled = true;
 
                             decompose_repeated_primitive(
                                 visible_tiles,
                                 metadata,
                                 &stretch_size,
                                 &tile_spacing,
-                                prim_run_context,
+                                prim_context,
                                 frame_context,
                                 frame_state,
                                 &mut |rect, mut request| {
                                     request.push([
                                         center.x,
                                         center.y,
                                         start_radius,
                                         end_radius,
@@ -2626,17 +2538,17 @@ impl Primitive {
                         if tile_spacing != LayoutSize::zero() {
                             is_tiled = true;
 
                             decompose_repeated_primitive(
                                 visible_tiles,
                                 metadata,
                                 &stretch_size,
                                 &tile_spacing,
-                                prim_run_context,
+                                prim_context,
                                 frame_context,
                                 frame_state,
                                 &mut |rect, mut request| {
                                     request.push([
                                         start_point.x,
                                         start_point.y,
                                         end_point.x,
                                         end_point.y,
@@ -2651,17 +2563,17 @@ impl Primitive {
                                 }
                             );
                         }
                     }
                     BrushKind::Picture(ref mut pic) => {
                         pic.prepare_for_render(
                             prim_index,
                             metadata,
-                            prim_run_context,
+                            prim_context,
                             pic_state_for_children,
                             pic_state,
                             frame_context,
                             frame_state,
                         );
                     }
                     BrushKind::Solid { ref color, ref mut opacity_binding, .. } => {
                         // If the opacity changed, invalidate the GPU cache so that
@@ -2714,33 +2626,33 @@ impl Primitive {
                     }
                 }
             }
         }
     }
 
     fn update_clip_task(
         &mut self,
-        prim_run_context: &PrimitiveRunContext,
+        prim_context: &PrimitiveContext,
         prim_screen_rect: &DeviceIntRect,
         clip_chain: &ClipChainInstance,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         is_chased: bool,
     ) -> bool {
         if cfg!(debug_assertions) && is_chased {
             println!("\tupdating clip task with screen rect {:?}", prim_screen_rect);
         }
         // Reset clips from previous frames since we may clip differently each frame.
         self.reset_clip_task();
 
         // First try to  render this primitive's mask using optimized brush rendering.
         if self.update_clip_task_for_brush(
-            prim_run_context,
+            prim_context,
             &clip_chain,
             prim_screen_rect,
             pic_state,
             frame_context,
             frame_state,
         ) {
             if cfg!(debug_assertions) && is_chased {
                 println!("\tsegment tasks have been created for clipping");
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -173,17 +173,17 @@ impl ops::IndexMut<RenderTaskId> for Ren
     }
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum RenderTaskLocation {
     Fixed(DeviceIntRect),
-    Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, Option<DeviceIntSize>),
+    Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
     TextureCache(SourceTexture, i32, DeviceIntRect),
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CacheMaskTask {
     actual_rect: DeviceIntRect,
@@ -197,16 +197,17 @@ pub struct ClipRegionTask {
     pub clip_data_address: GpuCacheAddress,
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct PictureTask {
     pub prim_index: PrimitiveIndex,
+    pub can_merge: bool,
     pub content_origin: DeviceIntPoint,
     pub uv_rect_handle: GpuCacheHandle,
     uv_rect_kind: UvRectKind,
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -325,48 +326,50 @@ impl RenderTask {
         size: DeviceIntSize,
         children: Vec<RenderTaskId>,
         kind: RenderTaskKind,
         clear_mode: ClearMode,
     ) -> Self {
         render_task_sanity_check(&size);
 
         RenderTask {
-            location: RenderTaskLocation::Dynamic(None, Some(size)),
+            location: RenderTaskLocation::Dynamic(None, size),
             children,
             kind,
             clear_mode,
             saved_index: None,
         }
     }
 
     pub fn new_picture(
         location: RenderTaskLocation,
+        unclipped_size: DeviceIntSize,
         prim_index: PrimitiveIndex,
         content_origin: DeviceIntPoint,
         children: Vec<RenderTaskId>,
         uv_rect_kind: UvRectKind,
     ) -> Self {
         let size = match location {
-            RenderTaskLocation::Dynamic(_, Some(size)) => Some(size),
-            RenderTaskLocation::Fixed(rect) => Some(rect.size),
-            RenderTaskLocation::TextureCache(_, _, rect) => Some(rect.size),
-            _ => None,
+            RenderTaskLocation::Dynamic(_, size) => size,
+            RenderTaskLocation::Fixed(rect) => rect.size,
+            RenderTaskLocation::TextureCache(_, _, rect) => rect.size,
         };
 
-        if let Some(size) = size {
-            render_task_sanity_check(&size);
-        }
+        render_task_sanity_check(&size);
+
+        let can_merge = size.width >= unclipped_size.width &&
+                        size.height >= unclipped_size.height;
 
         RenderTask {
             location,
             children,
             kind: RenderTaskKind::Picture(PictureTask {
                 prim_index,
                 content_origin,
+                can_merge,
                 uv_rect_handle: GpuCacheHandle::new(),
                 uv_rect_kind,
             }),
             clear_mode: ClearMode::Transparent,
             saved_index: None,
         }
     }
 
@@ -765,20 +768,17 @@ impl RenderTask {
                 panic!("texture handle not supported for this task kind");
             }
         }
     }
 
     pub fn get_dynamic_size(&self) -> DeviceIntSize {
         match self.location {
             RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(),
-            RenderTaskLocation::Dynamic(_, Some(size)) => size,
-            RenderTaskLocation::Dynamic(_, None) => {
-                panic!("bug: render task must have assigned size by now");
-            }
+            RenderTaskLocation::Dynamic(_, size) => size,
             RenderTaskLocation::TextureCache(_, _, rect) => rect.size,
         }
     }
 
     pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
         match self.location {
             RenderTaskLocation::Fixed(rect) => {
                 (rect, RenderTargetIndex(0))
@@ -793,17 +793,16 @@ impl RenderTask {
             // Render tasks that are created but not assigned to
             // passes consume a row in the render task texture, but
             // don't allocate any space in render targets nor
             // draw any pixels.
             // TODO(gw): Consider some kind of tag or other method
             //           to mark a task as unused explicitly. This
             //           would allow us to restore this debug check.
             RenderTaskLocation::Dynamic(Some((origin, target_index)), size) => {
-                let size = size.expect("bug: must be assigned a size by now");
                 (DeviceIntRect::new(origin, size), target_index)
             }
             RenderTaskLocation::Dynamic(None, _) => {
                 (DeviceIntRect::zero(), RenderTargetIndex(0))
             }
             RenderTaskLocation::TextureCache(_, layer, rect) => {
                 (rect, RenderTargetIndex(layer as usize))
             }
@@ -1091,20 +1090,17 @@ impl RenderTaskCache {
                 let target_kind = render_task.target_kind();
 
                 // Find out what size to alloc in the texture cache.
                 let size = match render_task.location {
                     RenderTaskLocation::Fixed(..) |
                     RenderTaskLocation::TextureCache(..) => {
                         panic!("BUG: dynamic task was expected");
                     }
-                    RenderTaskLocation::Dynamic(_, None) => {
-                        panic!("BUG: must have assigned size by now");
-                    }
-                    RenderTaskLocation::Dynamic(_, Some(size)) => size,
+                    RenderTaskLocation::Dynamic(_, size) => size,
                 };
 
                 // Select the right texture page to allocate from.
                 let image_format = match target_kind {
                     RenderTargetKind::Color => ImageFormat::BGRA8,
                     RenderTargetKind::Alpha => ImageFormat::R8,
                 };
 
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -243,16 +243,17 @@ pub enum ShaderColorMode {
     Alpha = 1,
     SubpixelConstantTextColor = 2,
     SubpixelWithBgColorPass0 = 3,
     SubpixelWithBgColorPass1 = 4,
     SubpixelWithBgColorPass2 = 5,
     SubpixelDualSource = 6,
     Bitmap = 7,
     ColorBitmap = 8,
+    Image = 9,
 }
 
 impl From<GlyphFormat> for ShaderColorMode {
     fn from(format: GlyphFormat) -> ShaderColorMode {
         match format {
             GlyphFormat::Alpha | GlyphFormat::TransformedAlpha => ShaderColorMode::Alpha,
             GlyphFormat::Subpixel | GlyphFormat::TransformedSubpixel => {
                 panic!("Subpixel glyph formats must be handled separately.");
--- a/gfx/webrender/src/spatial_node.rs
+++ b/gfx/webrender/src/spatial_node.rs
@@ -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/. */
 
 use api::{ExternalScrollId, LayoutPixel, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
 use api::{LayoutVector2D, PipelineId, PropertyBinding, ScrollClamping, ScrollLocation};
-use api::{ScrollSensitivity, StickyOffsetBounds};
-use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex, TransformUpdateState};
+use api::{ScrollSensitivity, StickyOffsetBounds, LayoutVector3D};
+use clip_scroll_tree::{CoordinateSystem, CoordinateSystemId, SpatialNodeIndex, TransformUpdateState};
 use euclid::SideOffsets2D;
 use gpu_types::TransformPalette;
 use scene::SceneProperties;
 use util::{LayoutFastTransform, LayoutToWorldFastTransform, MatrixHelpers, TransformedRectKind};
 
 #[derive(Clone, Debug)]
 pub enum SpatialNodeType {
     /// A special kind of node that adjusts its position based on the position
@@ -209,27 +209,27 @@ impl SpatialNode {
         }
 
         transform_palette.set(node_index, &self.world_content_transform);
     }
 
     pub fn update(
         &mut self,
         state: &mut TransformUpdateState,
-        next_coordinate_system_id: &mut CoordinateSystemId,
+        coord_systems: &mut Vec<CoordinateSystem>,
         scene_properties: &SceneProperties,
     ) {
         // If any of our parents was not rendered, we are not rendered either and can just
         // quit here.
         if !state.invertible {
             self.mark_uninvertible();
             return;
         }
 
-        self.update_transform(state, next_coordinate_system_id, scene_properties);
+        self.update_transform(state, coord_systems, scene_properties);
         self.transform_kind = self.world_content_transform.kind();
 
         // If this node is a reference frame, we check if it has a non-invertible matrix.
         // For non-reference-frames we assume that they will produce only additional
         // translations which should be invertible.
         match self.node_type {
             SpatialNodeType::ReferenceFrame(info) if !info.invertible => {
                 self.mark_uninvertible();
@@ -237,17 +237,17 @@ impl SpatialNode {
             }
             _ => self.invertible = true,
         }
     }
 
     pub fn update_transform(
         &mut self,
         state: &mut TransformUpdateState,
-        next_coordinate_system_id: &mut CoordinateSystemId,
+        coord_systems: &mut Vec<CoordinateSystem>,
         scene_properties: &SceneProperties,
     ) {
         match self.node_type {
             SpatialNodeType::ReferenceFrame(ref mut info) => {
                 // Resolve the transform against any property bindings.
                 let source_transform = scene_properties.resolve_layout_transform(&info.source_transform);
                 info.resolved_transform =
                     LayoutFastTransform::with_vector(info.origin_in_parent_reference_frame)
@@ -276,18 +276,29 @@ impl SpatialNode {
                 if relative_transform.is_simple_2d_translation() {
                     self.coordinate_system_relative_offset =
                         state.coordinate_system_relative_offset +
                         LayoutVector2D::new(relative_transform.m41, relative_transform.m42);
                 } else {
                     // If we break 2D axis alignment or have a perspective component, we need to start a
                     // new incompatible coordinate system with which we cannot share clips without masking.
                     self.coordinate_system_relative_offset = LayoutVector2D::zero();
-                    state.current_coordinate_system_id = *next_coordinate_system_id;
-                    next_coordinate_system_id.advance();
+
+                    // Push that new coordinate system and record the new id.
+                    let coord_system = CoordinateSystem {
+                        offset: LayoutVector3D::new(
+                            state.coordinate_system_relative_offset.x,
+                            state.coordinate_system_relative_offset.y,
+                            0.0,
+                        ),
+                        transform: *info.resolved_transform.to_transform(),
+                        parent: Some(state.current_coordinate_system_id),
+                    };
+                    state.current_coordinate_system_id = CoordinateSystemId(coord_systems.len() as u32);
+                    coord_systems.push(coord_system);
                 }
 
                 self.coordinate_system_id = state.current_coordinate_system_id;
             }
             _ => {
                 // We calculate this here to avoid a double-borrow later.
                 let sticky_offset = self.calculate_sticky_offset(
                     &state.nearest_scrolling_ancestor_offset,
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -346,17 +346,21 @@ impl RenderTarget for ColorRenderTarget 
             let task = &render_tasks[*task_id];
 
             match task.kind {
                 RenderTaskKind::Picture(ref pic_task) => {
                     let pic = ctx.prim_store.get_pic(pic_task.prim_index);
 
                     let (target_rect, _) = task.get_target_rect();
 
-                    let mut batch_builder = AlphaBatchBuilder::new(self.screen_size, target_rect);
+                    let mut batch_builder = AlphaBatchBuilder::new(
+                        self.screen_size,
+                        target_rect,
+                        pic_task.can_merge,
+                    );
 
                     batch_builder.add_pic_to_batch(
                         pic,
                         *task_id,
                         ctx,
                         gpu_cache,
                         render_tasks,
                         deferred_resolves,
@@ -840,17 +844,16 @@ impl RenderPass {
                         let texture_target = match task.location {
                             RenderTaskLocation::TextureCache(texture_id, layer, _) => {
                                 Some((texture_id, layer))
                             }
                             RenderTaskLocation::Fixed(..) => {
                                 None
                             }
                             RenderTaskLocation::Dynamic(ref mut origin, size) => {
-                                let size = size.expect("bug: size must be assigned by now");
                                 let alloc_size = DeviceUintSize::new(size.width as u32, size.height as u32);
                                 let (alloc_origin, target_index) =  match target_kind {
                                     RenderTargetKind::Color => color.allocate(alloc_size),
                                     RenderTargetKind::Alpha => alpha.allocate(alloc_size),
                                 };
                                 *origin = Some((alloc_origin.to_i32(), target_index));
                                 None
                             }
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -1,14 +1,15 @@
 [package]
 name = "webrender_api"
 version = "0.57.2"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
+description = "Public API for WebRender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
 serialize = []
 deserialize = []
 
 [dependencies]
--- a/gfx/webrender_api/src/color.rs
+++ b/gfx/webrender_api/src/color.rs
@@ -43,16 +43,23 @@ impl PremultipliedColorF {
 pub struct ColorF {
     pub r: f32,
     pub g: f32,
     pub b: f32,
     pub a: f32,
 }
 
 impl ColorF {
+    ///
+    pub const BLACK: Self = ColorF { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
+    ///
+    pub const TRANSPARENT: Self = ColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
+    ///
+    pub const WHITE: Self = ColorF { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
+
     /// Constructs a new `ColorF` from its components.
     pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
         ColorF { r, g, b, a }
     }
 
     /// Multiply the RGB channels (but not alpha) with a given factor.
     pub fn scale_rgb(&self, scale: f32) -> Self {
         ColorF {
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -553,16 +553,17 @@ pub struct IframeDisplayItem {
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ImageDisplayItem {
     pub image_key: ImageKey,
     pub stretch_size: LayoutSize,
     pub tile_spacing: LayoutSize,
     pub image_rendering: ImageRendering,
     pub alpha_type: AlphaType,
+    pub color: ColorF,
 }
 
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum ImageRendering {
     Auto = 0,
     CrispEdges = 1,
     Pixelated = 2,
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -1057,23 +1057,25 @@ impl DisplayListBuilder {
     pub fn push_image(
         &mut self,
         info: &LayoutPrimitiveInfo,
         stretch_size: LayoutSize,
         tile_spacing: LayoutSize,
         image_rendering: ImageRendering,
         alpha_type: AlphaType,
         key: ImageKey,
+        color: ColorF,
     ) {
         let item = SpecificDisplayItem::Image(ImageDisplayItem {
             image_key: key,
             stretch_size,
             tile_spacing,
             image_rendering,
             alpha_type,
+            color,
         });
 
         self.push_item(item, info);
     }
 
     /// Push a yuv image. All planar data in yuv image should use the same buffer type.
     pub fn push_yuv_image(
         &mut self,
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-890f3746b798538d201e54b66d2711a70447b781
+e70bae07664def86aefd11c86dac818ab7ea64ea
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1973,17 +1973,18 @@ pub extern "C" fn wr_dp_push_image(state
     };
     state.frame_builder
          .dl_builder
          .push_image(&prim_info,
                      stretch_size,
                      tile_spacing,
                      image_rendering,
                      alpha_type,
-                     key);
+                     key,
+                     ColorF::WHITE);
 }
 
 /// Push a 3 planar yuv image.
 #[no_mangle]
 pub extern "C" fn wr_dp_push_yuv_planar_image(state: &mut WrState,
                                               bounds: LayoutRect,
                                               clip: LayoutRect,
                                               is_backface_visible: bool,
--- a/gfx/wrench/src/main.rs
+++ b/gfx/wrench/src/main.rs
@@ -81,18 +81,16 @@ use webrender::DebugFlags;
 use webrender::api::*;
 use winit::dpi::{LogicalPosition, LogicalSize};
 use winit::VirtualKeyCode;
 use wrench::{Wrench, WrenchThing};
 use yaml_frame_reader::YamlFrameReader;
 
 lazy_static! {
     static ref PLATFORM_DEFAULT_FACE_NAME: String = String::from("Arial");
-    static ref WHITE_COLOR: ColorF = ColorF::new(1.0, 1.0, 1.0, 1.0);
-    static ref BLACK_COLOR: ColorF = ColorF::new(0.0, 0.0, 0.0, 1.0);
 }
 
 pub static mut CURRENT_FRAME_NUMBER: u32 = 0;
 
 #[cfg(feature = "headless")]
 pub struct HeadlessContext {
     width: u32,
     height: u32,
--- a/gfx/wrench/src/rawtest.rs
+++ b/gfx/wrench/src/rawtest.rs
@@ -109,16 +109,17 @@ impl<'a> RawtestHarness<'a> {
         // setup some malicious image size parameters
         builder.push_image(
             &info,
             size(151., 56.0),
             size(151.0, 56.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
 
         let mut epoch = Epoch(0);
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
 
         self.rx.recv().unwrap();
         self.wrench.render();
@@ -175,16 +176,17 @@ impl<'a> RawtestHarness<'a> {
         // setup some malicious image size parameters
         builder.push_image(
             &info,
             image_size * 2.,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
         txn.set_image_visible_area(
             blob_img,
             NormalizedRect {
                 origin: point2(0.0, 0.03),
                 size: size2(1.0, 0.03),
             }
         );
@@ -279,16 +281,17 @@ impl<'a> RawtestHarness<'a> {
 
         builder.push_image(
             &info,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img1,
+            ColorF::WHITE,
         );
 
         self.submit_dl(&mut Epoch(0), layout_size, builder, &txn.resource_updates);
         let pixels1 = self.render_and_get_pixels(window_rect);
 
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
         let mut txn = Transaction::new();
 
@@ -314,16 +317,17 @@ impl<'a> RawtestHarness<'a> {
 
         builder.push_image(
             &info,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img2,
+            ColorF::WHITE,
         );
 
         self.submit_dl(&mut Epoch(1), layout_size, builder, &txn.resource_updates);
         let pixels2 = self.render_and_get_pixels(window_rect);
 
         assert!(pixels1 == pixels2);
 
         txn = Transaction::new();
@@ -367,16 +371,17 @@ impl<'a> RawtestHarness<'a> {
         // setup some malicious image size parameters
         builder.push_image(
             &info,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
 
         let mut epoch = Epoch(0);
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
 
         let original_pixels = self.render_and_get_pixels(window_rect);
 
@@ -391,16 +396,17 @@ impl<'a> RawtestHarness<'a> {
         // setup some malicious image size parameters
         builder.push_image(
             &info,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
 
         self.submit_dl(&mut epoch, layout_size, builder, &[]);
 
         let _offscreen_pixels = self.render_and_get_pixels(window_rect);
 
         let mut txn = Transaction::new();
 
@@ -420,16 +426,17 @@ impl<'a> RawtestHarness<'a> {
         // setup some malicious image size parameters
         builder.push_image(
             &info,
             image_size,
             image_size,
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
 
         let mut epoch = Epoch(2);
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
 
         let pixels = self.render_and_get_pixels(window_rect);
 
@@ -480,16 +487,17 @@ impl<'a> RawtestHarness<'a> {
 
         builder.push_image(
             &info,
             size(200.0, 200.0),
             size(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
 
         let mut epoch = Epoch(0);
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
 
         let pixels_first = self.render_and_get_pixels(window_rect);
 
@@ -502,16 +510,17 @@ impl<'a> RawtestHarness<'a> {
         let info = LayoutPrimitiveInfo::new(rect(1.0, 60.0, 200.0, 200.0));
         builder.push_image(
             &info,
             size(200.0, 200.0),
             size(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
 
         txn.resource_updates.clear();
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
 
         let pixels_second = self.render_and_get_pixels(window_rect);
 
@@ -586,24 +595,26 @@ impl<'a> RawtestHarness<'a> {
         let push_images = |builder: &mut DisplayListBuilder| {
             builder.push_image(
                 &info,
                 size(200.0, 200.0),
                 size(0.0, 0.0),
                 ImageRendering::Auto,
                 AlphaType::PremultipliedAlpha,
                 blob_img,
+                ColorF::WHITE,
             );
             builder.push_image(
                 &info2,
                 size(200.0, 200.0),
                 size(0.0, 0.0),
                 ImageRendering::Auto,
                 AlphaType::PremultipliedAlpha,
                 blob_img2,
+                ColorF::WHITE,
             );
         };
 
         push_images(&mut builder);
 
         let mut epoch = Epoch(0);
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
@@ -684,16 +695,17 @@ impl<'a> RawtestHarness<'a> {
 
         builder.push_image(
             &info,
             size(200.0, 200.0),
             size(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
 
         let mut epoch = Epoch(0);
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
         let pixels_first = self.render_and_get_pixels(window_rect);
 
         // draw the blob image a second time after updating it with the same color
@@ -710,16 +722,17 @@ impl<'a> RawtestHarness<'a> {
         let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
         builder.push_image(
             &info,
             size(200.0, 200.0),
             size(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
         let pixels_second = self.render_and_get_pixels(window_rect);
 
         // draw the blob image a third time after updating it with a different color
         let mut txn = Transaction::new();
         txn.update_image(
@@ -734,16 +747,17 @@ impl<'a> RawtestHarness<'a> {
         let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
         builder.push_image(
             &info,
             size(200.0, 200.0),
             size(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             blob_img,
+            ColorF::WHITE,
         );
 
         self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
         let pixels_third = self.render_and_get_pixels(window_rect);
 
         assert!(pixels_first == pixels_second);
         assert!(pixels_first != pixels_third);
     }
@@ -895,16 +909,17 @@ impl<'a> RawtestHarness<'a> {
 
         builder.push_image(
             &LayoutPrimitiveInfo::new(rect(300.0, 70.0, 150.0, 50.0)),
             size(150.0, 50.0),
             size(0.0, 0.0),
             ImageRendering::Auto,
             AlphaType::PremultipliedAlpha,
             image,
+            ColorF::WHITE,
         );
 
         let mut txn = Transaction::new();
 
         txn.set_display_list(
             Epoch(0),
             Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
             layout_size,
--- a/gfx/wrench/src/wrench.rs
+++ b/gfx/wrench/src/wrench.rs
@@ -17,17 +17,17 @@ use std::collections::HashMap;
 use std::path::PathBuf;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Receiver;
 use time;
 use webrender;
 use webrender::api::*;
 use webrender::{DebugFlags, RendererStats};
 use yaml_frame_writer::YamlFrameWriterReceiver;
-use {WindowWrapper, NotifierEvent, BLACK_COLOR, WHITE_COLOR};
+use {WindowWrapper, NotifierEvent};
 
 // TODO(gw): This descriptor matches what we currently support for fonts
 //           but is quite a mess. We should at least document and
 //           use better types for things like the style and stretch.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub enum FontDescriptor {
     Path { path: PathBuf, font_index: u32 },
     Family { name: String },
@@ -556,17 +556,17 @@ impl Wrench {
             "S - Toggle compact profiler",
             "Q - Toggle GPU queries for time and samples",
             "M - Trigger memory pressure event",
             "T - Save CPU profile to a file",
             "C - Save a capture to captures/wrench/",
             "X - Do a hit test at the current cursor position",
         ];
 
-        let color_and_offset = [(*BLACK_COLOR, 2.0), (*WHITE_COLOR, 0.0)];
+        let color_and_offset = [(ColorF::BLACK, 2.0), (ColorF::WHITE, 0.0)];
         let dr = self.renderer.debug_renderer().unwrap();
 
         for ref co in &color_and_offset {
             let x = self.device_pixel_ratio * (15.0 + co.1);
             let mut y = self.device_pixel_ratio * (15.0 + co.1 + dr.line_height());
             for ref line in &help_lines {
                 dr.add_text(x, y, line, co.0.into());
                 y += self.device_pixel_ratio * dr.line_height();
--- a/gfx/wrench/src/yaml_frame_reader.rs
+++ b/gfx/wrench/src/yaml_frame_reader.rs
@@ -12,17 +12,17 @@ use premultiply::premultiply;
 use std::collections::HashMap;
 use std::fs::File;
 use std::io::Read;
 use std::path::{Path, PathBuf};
 use webrender::api::*;
 use wrench::{FontDescriptor, Wrench, WrenchThing};
 use yaml_helper::{StringEnum, YamlHelper, make_perspective};
 use yaml_rust::{Yaml, YamlLoader};
-use {BLACK_COLOR, PLATFORM_DEFAULT_FACE_NAME, WHITE_COLOR};
+use PLATFORM_DEFAULT_FACE_NAME;
 
 fn rsrc_path(item: &Yaml, aux_dir: &PathBuf) -> PathBuf {
     let filename = item.as_str().unwrap();
     let mut file = aux_dir.clone();
     file.push(filename);
     file
 }
 
@@ -675,17 +675,17 @@ impl YamlFrameReader {
         let bounds_key = if item["type"].is_badvalue() {
             "rect"
         } else {
             "bounds"
         };
         info.rect = item[bounds_key]
             .as_rect()
             .expect("rect type must have bounds");
-        let color = item["color"].as_colorf().unwrap_or(*WHITE_COLOR);
+        let color = item["color"].as_colorf().unwrap_or(ColorF::WHITE);
         dl.push_rect(&info, color);
     }
 
     fn handle_clear_rect(
         &mut self,
         dl: &mut DisplayListBuilder,
         item: &Yaml,
         info: &mut LayoutPrimitiveInfo,
@@ -697,17 +697,17 @@ impl YamlFrameReader {
     }
 
     fn handle_line(
         &mut self,
         dl: &mut DisplayListBuilder,
         item: &Yaml,
         info: &mut LayoutPrimitiveInfo,
     ) {
-        let color = item["color"].as_colorf().unwrap_or(*BLACK_COLOR);
+        let color = item["color"].as_colorf().unwrap_or(ColorF::BLACK);
         let orientation = item["orientation"]
             .as_str()
             .and_then(LineOrientation::from_str)
             .expect("line must have orientation");
         let style = item["style"]
             .as_str()
             .and_then(LineStyle::from_str)
             .expect("line must have style");
@@ -1118,28 +1118,28 @@ impl YamlFrameReader {
         let alpha_type = match item["alpha-type"].as_str() {
             Some("premultiplied-alpha") | None => AlphaType::PremultipliedAlpha,
             Some("alpha") => AlphaType::Alpha,
             Some(_) => panic!(
                 "AlphaType can be premultiplied-alpha or alpha -- got {:?}",
                 item
             ),
         };
-        dl.push_image(&info, stretch_size, tile_spacing, rendering, alpha_type, image_key);
+        dl.push_image(&info, stretch_size, tile_spacing, rendering, alpha_type, image_key, ColorF::WHITE);
     }
 
     fn handle_text(
         &mut self,
         dl: &mut DisplayListBuilder,
         wrench: &mut Wrench,
         item: &Yaml,
         info: &mut LayoutPrimitiveInfo,
     ) {
         let size = item["size"].as_pt_to_au().unwrap_or(Au::from_f32_px(16.0));
-        let color = item["color"].as_colorf().unwrap_or(*BLACK_COLOR);
+        let color = item["color"].as_colorf().unwrap_or(ColorF::BLACK);
         let bg_color = item["bg-color"].as_colorf().map(|c| c.into());
         let synthetic_italics = if let Some(angle) = item["synthetic-italics"].as_f32() {
             SyntheticItalics::from_degrees(angle)
         } else if item["synthetic-italics"].as_bool().unwrap_or(false) {
             SyntheticItalics::enabled()
         } else {
             SyntheticItalics::disabled()
         };
@@ -1447,17 +1447,17 @@ impl YamlFrameReader {
     pub fn handle_push_shadow(
         &mut self,
         dl: &mut DisplayListBuilder,
         yaml: &Yaml,
         info: &mut LayoutPrimitiveInfo,
     ) {
         let blur_radius = yaml["blur-radius"].as_f32().unwrap_or(0.0);
         let offset = yaml["offset"].as_vector().unwrap_or(LayoutVector2D::zero());
-        let color = yaml["color"].as_colorf().unwrap_or(*BLACK_COLOR);
+        let color = yaml["color"].as_colorf().unwrap_or(ColorF::BLACK);
 
         dl.push_shadow(
             &info,
             Shadow {
                 blur_radius,
                 offset,
                 color,
             },
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -138,17 +138,18 @@ GeckoChildProcessHost::~GeckoChildProces
   }
 }
 
 //static
 auto
 GeckoChildProcessHost::GetPathToBinary(FilePath& exePath, GeckoProcessType processType) -> BinaryPathType
 {
   if (sRunSelfAsContentProc &&
-      (processType == GeckoProcessType_Content || processType == GeckoProcessType_GPU)) {
+      (processType == GeckoProcessType_Content || processType == GeckoProcessType_GPU ||
+       processType == GeckoProcessType_VR)) {
 #if defined(OS_WIN)
     wchar_t exePathBuf[MAXPATHLEN];
     if (!::GetModuleFileNameW(nullptr, exePathBuf, MAXPATHLEN)) {
       MOZ_CRASH("GetModuleFileNameW failed (FIXME)");
     }
 #if defined(MOZ_SANDBOX)
     // We need to start the child process using the real path, so that the
     // sandbox policy rules will match for DLLs loaded from the bin dir after
@@ -758,17 +759,17 @@ GeckoChildProcessHost::PerformAsyncLaunc
     }
   }
 
   // Add the application directory path (-appdir path)
   AddAppDirToCommandLine(childArgv);
 
   // Tmp dir that the GPU process should use for crash reports. This arg is
   // always populated (but possibly with an empty value) for a GPU child process.
-  if (mProcessType == GeckoProcessType_GPU) {
+  if (mProcessType == GeckoProcessType_GPU || mProcessType == GeckoProcessType_VR) {
     nsCOMPtr<nsIFile> file;
     CrashReporter::GetChildProcessTmpDir(getter_AddRefs(file));
     nsAutoCString path;
     if (file) {
       file->GetNativePath(path);
     }
     childArgv.push_back(path.get());
   }
@@ -995,16 +996,21 @@ GeckoChildProcessHost::PerformAsyncLaunc
       if (mSandboxLevel > 0 && !PR_GetEnv("MOZ_DISABLE_GPU_SANDBOX")) {
         // For now we treat every failure as fatal in SetSecurityLevelForGPUProcess
         // and just crash there right away. Should this change in the future then we
         // should also handle the error here.
         mSandboxBroker.SetSecurityLevelForGPUProcess(mSandboxLevel);
         shouldSandboxCurrentProcess = true;
       }
       break;
+    case GeckoProcessType_VR:
+      if (mSandboxLevel > 0 && !PR_GetEnv("MOZ_DISABLE_VR_SANDBOX")) {
+        // TODO: Implement sandbox for VR process, Bug 1430043.
+      }
+      break;
     case GeckoProcessType_Default:
     default:
       MOZ_CRASH("Bad process type in GeckoChildProcessHost");
       break;
   };
 
   if (shouldSandboxCurrentProcess) {
     for (auto it = mAllowedFilesRead.begin();
@@ -1077,16 +1083,17 @@ GeckoChildProcessHost::PerformAsyncLaunc
   {
     base::LaunchApp(cmdLine, *mLaunchOptions, &process);
 
 # ifdef MOZ_SANDBOX
     // We need to be able to duplicate handles to some types of non-sandboxed
     // child processes.
     if (mProcessType == GeckoProcessType_Content ||
         mProcessType == GeckoProcessType_GPU ||
+        mProcessType == GeckoProcessType_VR ||
         mProcessType == GeckoProcessType_GMPlugin) {
       if (!mSandboxBroker.AddTargetPeer(process)) {
         NS_WARNING("Failed to add content process as target peer.");
       }
     }
 # endif // MOZ_SANDBOX
   }
 
--- a/ipc/mscom/MainThreadRuntime.cpp
+++ b/ipc/mscom/MainThreadRuntime.cpp
@@ -8,16 +8,17 @@
 
 #if defined(ACCESSIBILITY)
 #include "mozilla/a11y/Compatibility.h"
 #endif
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/WindowsVersion.h"
 #if defined(ACCESSIBILITY)
 #include "nsExceptionHandler.h"
 #endif // defined(ACCESSIBILITY)
 #include "nsWindowsHelpers.h"
 #include "nsXULAppAPI.h"
 
 #include <accctrl.h>
 #include <aclapi.h>
@@ -191,32 +192,51 @@ MainThreadRuntime::InitializeSecurity()
 
   BYTE adminSid[SECURITY_MAX_SID_SIZE];
   DWORD adminSidSize = sizeof(adminSid);
   if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid,
                             &adminSidSize)) {
     return HRESULT_FROM_WIN32(::GetLastError());
   }
 
-  // Grant access to SYSTEM, Administrators, and the user.
+  BYTE appContainersSid[SECURITY_MAX_SID_SIZE];
+  DWORD appContainersSidSize = sizeof(appContainersSid);
+  if (XRE_IsParentProcess() && IsWin8OrLater()) {
+    if (!::CreateWellKnownSid(WinBuiltinAnyPackageSid, nullptr,
+                              appContainersSid, &appContainersSidSize)) {
+      return HRESULT_FROM_WIN32(::GetLastError());
+    }
+  }
+
+  // Grant access to SYSTEM, Administrators, the user, and when running as the
+  // browser process on Windows 8+, all app containers.
   EXPLICIT_ACCESS entries[] = {
     {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
       {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
        reinterpret_cast<LPWSTR>(systemSid)}},
     {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
       {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP,
        reinterpret_cast<LPWSTR>(adminSid)}},
     {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
       {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
-       reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}}
+       reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}},
+    // appContainersSid must be the last entry in this array!
+    {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP,
+       reinterpret_cast<LPWSTR>(appContainersSid)}}
   };
 
+  ULONG numEntries = ArrayLength(entries);
+  if (!XRE_IsParentProcess() || !IsWin8OrLater()) {
+    // Exclude appContainersSid on Windows 7 and non-parent processes.
+    --numEntries;
+  }
+
   PACL rawDacl = nullptr;
-  win32Error = ::SetEntriesInAcl(ArrayLength(entries), entries, nullptr,
-                                 &rawDacl);
+  win32Error = ::SetEntriesInAcl(numEntries, entries, nullptr, &rawDacl);
   if (win32Error != ERROR_SUCCESS) {
     return HRESULT_FROM_WIN32(win32Error);
   }
 
   UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
 
   if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
     return HRESULT_FROM_WIN32(::GetLastError());
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -11,16 +11,17 @@
 #endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>     /* for isatty() */
 #endif
 
 #include "base/basictypes.h"
 
 #include "jsapi.h"
+#include "js/AutoByteString.h"
 
 #include "xpcpublic.h"
 
 #include "XPCShellEnvironment.h"
 
 #include "mozilla/XPCOM.h"
 
 #include "nsIChannel.h"
new file mode 100644
--- /dev/null
+++ b/js/public/AutoByteString.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * DEPRECATED functions and classes for heap-allocating copies of a JSString's
+ * data.
+ */
+
+#ifndef js_AutoByteString_h
+#define js_AutoByteString_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_RAII, MOZ_GUARD*
+
+#include <string.h> // strlen
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/MemoryFunctions.h" // JS_free
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/TypeDecls.h" // JSContext, JSString
+#include "js/Utility.h" // js_free, JS::UniqueChars
+
+/**
+ * DEPRECATED
+ *
+ * Allocate memory sufficient to contain the characters of |str| truncated to
+ * Latin-1 and a trailing null terminator, fill the memory with the characters
+ * interpreted in that manner plus the null terminator, and return a pointer to
+ * the memory.  The memory must be freed using JS_free to avoid leaking.
+ *
+ * This function *loses information* when it copies the characters of |str| if
+ * |str| contains code units greater than 0xFF.  Additionally, users that
+ * depend on null-termination will misinterpret the copied characters if |str|
+ * contains any nulls.  Avoid using this function if possible, because it will
+ * eventually be removed.
+ */
+extern JS_PUBLIC_API(char*)
+JS_EncodeString(JSContext* cx, JSString* str);
+
+/**
+ * DEPRECATED
+ *
+ * Same behavior as JS_EncodeString(), but encode into a UTF-8 string.
+ *
+ * This function *loses information* when it copies the characters of |str| if
+ * |str| contains invalid UTF-16: U+FFFD REPLACEMENT CHARACTER will be copied
+ * instead.
+ *
+ * The returned string is also subject to misinterpretation if |str| contains
+ * any nulls (which are faithfully transcribed into the returned string, but
+ * which will implicitly truncate the string if it's passed to functions that
+ * expect null-terminated strings).
+ *
+ * Avoid using this function if possible, because we'll remove it once we can
+ * devise a better API for the task.
+ */
+extern JS_PUBLIC_API(char*)
+JS_EncodeStringToUTF8(JSContext* cx, JS::Handle<JSString*> str);
+
+/**
+ * DEPRECATED
+ *
+ * A lightweight RAII helper class around the various JS_Encode* functions
+ * above, subject to the same pitfalls noted above.  Avoid using this class if
+ * possible, because as with the functions above, it too needs to be replaced
+ * with a better, safer API.
+ */
+class MOZ_RAII JSAutoByteString final
+{
+  private:
+    char* mBytes;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+
+  private:
+    JSAutoByteString(const JSAutoByteString& another) = delete;
+    void operator=(const JSAutoByteString& another) = delete;
+
+  public:
+    JSAutoByteString(JSContext* cx, JSString* str
+                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mBytes(JS_EncodeString(cx, str))
+    {
+        MOZ_ASSERT(cx);
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    explicit JSAutoByteString(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
+      : mBytes(nullptr)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    ~JSAutoByteString() {
+        JS_free(nullptr, mBytes);
+    }
+
+    /* Take ownership of the given byte array. */
+    void initBytes(JS::UniqueChars&& bytes) {
+        MOZ_ASSERT(!mBytes);
+        mBytes = bytes.release();
+    }
+
+    char* encodeLatin1(JSContext* cx, JSString* str) {
+        MOZ_ASSERT(!mBytes);
+        MOZ_ASSERT(cx);
+        mBytes = JS_EncodeString(cx, str);
+        return mBytes;
+    }
+
+    char* encodeUtf8(JSContext* cx, JS::Handle<JSString*> str) {
+        MOZ_ASSERT(!mBytes);
+        MOZ_ASSERT(cx);
+        mBytes = JS_EncodeStringToUTF8(cx, str);
+        return mBytes;
+    }
+
+    void clear() {
+        js_free(mBytes);
+        mBytes = nullptr;
+    }
+
+    char* ptr() const {
+        return mBytes;
+    }
+
+    bool operator!() const {
+        return !mBytes;
+    }
+
+    size_t length() const {
+        if (!mBytes)
+            return 0;
+        return strlen(mBytes);
+    }
+};
+
+#endif /* js_AutoByteString_h */
new file mode 100644
--- /dev/null
+++ b/js/public/ErrorReport.h
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Error-reporting types and structures.
+ *
+ * Despite these types and structures existing in js/public, significant parts
+ * of their heritage date back to distant SpiderMonkey past, and they are not
+ * all universally well-thought-out as ideal, intended-to-be-permanent API.
+ * We may eventually replace this with something more consistent with
+ * ECMAScript the language and less consistent with '90s-era JSAPI inventions,
+ * but it's doubtful this will happen any time soon.
+ */
+
+#ifndef js_ErrorReport_h
+#define js_ErrorReport_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include <iterator> // std::input_iterator_tag, std::iterator
+#include <stddef.h> // size_t
+#include <stdint.h> // int16_t, uint16_t
+#include <string.h> // strlen
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/AllocPolicy.h" // js::SystemAllocPolicy
+#include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
+#include "js/UniquePtr.h" // js::UniquePtr
+#include "js/Vector.h" // js::Vector
+
+struct JSContext;
+class JSString;
+
+/**
+ * Possible exception types. These types are part of a JSErrorFormatString
+ * structure. They define which error to throw in case of a runtime error.
+ *
+ * JSEXN_WARN is used for warnings in js.msg files (for instance because we
+ * don't want to prepend 'Error:' to warning messages). This value can go away
+ * if we ever decide to use an entirely separate mechanism for warnings.
+ */
+enum JSExnType
+{
+    JSEXN_ERR,
+    JSEXN_FIRST = JSEXN_ERR,
+        JSEXN_INTERNALERR,
+        JSEXN_EVALERR,
+        JSEXN_RANGEERR,
+        JSEXN_REFERENCEERR,
+        JSEXN_SYNTAXERR,
+        JSEXN_TYPEERR,
+        JSEXN_URIERR,
+        JSEXN_DEBUGGEEWOULDRUN,
+        JSEXN_WASMCOMPILEERROR,
+        JSEXN_WASMLINKERROR,
+        JSEXN_WASMRUNTIMEERROR,
+    JSEXN_ERROR_LIMIT,
+    JSEXN_WARN = JSEXN_ERROR_LIMIT,
+    JSEXN_NOTE,
+    JSEXN_LIMIT
+};
+
+struct JSErrorFormatString
+{
+     /** The error message name in ASCII. */
+    const char* name;
+
+    /** The error format string in ASCII. */
+    const char* format;
+
+    /** The number of arguments to expand in the formatted error message. */
+    uint16_t argCount;
+
+    /** One of the JSExnType constants above. */
+    int16_t exnType;
+};
+
+using JSErrorCallback =
+    const JSErrorFormatString* (*)(void* userRef, const unsigned errorNumber);
+
+/**
+ * Base class that implements parts shared by JSErrorReport and
+ * JSErrorNotes::Note.
+ */
+class JSErrorBase
+{
+  private:
+    // The (default) error message.
+    // If ownsMessage_ is true, the it is freed in destructor.
+    JS::ConstUTF8CharsZ message_;
+
+  public:
+    // Source file name, URL, etc., or null.
+    const char* filename;
+
+    // Source line number.
+    unsigned lineno;
+
+    // Zero-based column index in line.
+    unsigned column;
+
+    // the error number, e.g. see js.msg.
+    unsigned errorNumber;
+
+  private:
+    bool ownsMessage_ : 1;
+
+  public:
+    JSErrorBase()
+      : filename(nullptr), lineno(0), column(0),
+        errorNumber(0),
+        ownsMessage_(false)
+    {}
+
+    ~JSErrorBase() {
+        freeMessage();
+    }
+
+  public:
+    const JS::ConstUTF8CharsZ message() const {
+        return message_;
+    }
+
+    void initOwnedMessage(const char* messageArg) {
+        initBorrowedMessage(messageArg);
+        ownsMessage_ = true;
+    }
+    void initBorrowedMessage(const char* messageArg) {
+        MOZ_ASSERT(!message_);
+        message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg));
+    }
+
+    JSString* newMessageString(JSContext* cx);
+
+  private:
+    void freeMessage();
+};
+
+/**
+ * Notes associated with JSErrorReport.
+ */
+class JSErrorNotes
+{
+  public:
+    class Note final : public JSErrorBase
+    {};
+
+  private:
+    // Stores pointers to each note.
+    js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_;
+
+  public:
+    JSErrorNotes();
+    ~JSErrorNotes();
+
+    // Add an note to the given position.
+    bool addNoteASCII(JSContext* cx,
+                      const char* filename, unsigned lineno, unsigned column,
+                      JSErrorCallback errorCallback, void* userRef,
+                      const unsigned errorNumber, ...);
+    bool addNoteLatin1(JSContext* cx,
+                       const char* filename, unsigned lineno, unsigned column,
+                       JSErrorCallback errorCallback, void* userRef,
+                       const unsigned errorNumber, ...);
+    bool addNoteUTF8(JSContext* cx,
+                     const char* filename, unsigned lineno, unsigned column,
+                     JSErrorCallback errorCallback, void* userRef,
+                     const unsigned errorNumber, ...);
+
+    JS_PUBLIC_API(size_t) length();
+
+    // Create a deep copy of notes.
+    js::UniquePtr<JSErrorNotes> copy(JSContext* cx);
+
+    class iterator final
+      : public std::iterator<std::input_iterator_tag, js::UniquePtr<Note>>
+    {
+      private:
+        js::UniquePtr<Note>* note_;
+
+      public:
+        explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note)
+        {}
+
+        bool operator==(iterator other) const {
+            return note_ == other.note_;
+        }
+        bool operator!=(iterator other) const {
+            return !(*this == other);
+        }
+        iterator& operator++() {
+            note_++;
+            return *this;
+        }
+        reference operator*() {
+            return *note_;
+        }
+    };
+
+    JS_PUBLIC_API(iterator) begin();
+    JS_PUBLIC_API(iterator) end();
+};
+
+/**
+ * Describes a single error or warning that occurs in the execution of script.
+ */
+class JSErrorReport : public JSErrorBase
+{
+  private:
+    // Offending source line without final '\n'.
+    // If ownsLinebuf_ is true, the buffer is freed in destructor.
+    const char16_t* linebuf_;
+
+    // Number of chars in linebuf_. Does not include trailing '\0'.
+    size_t linebufLength_;
+
+    // The 0-based offset of error token in linebuf_.
+    size_t tokenOffset_;
+
+  public:
+    // Associated notes, or nullptr if there's no note.
+    js::UniquePtr<JSErrorNotes> notes;
+
+    // error/warning, etc.
+    unsigned flags;
+
+    // One of the JSExnType constants.
+    int16_t exnType;
+
+    // See the comment in TransitiveCompileOptions.
+    bool isMuted : 1;
+
+  private:
+    bool ownsLinebuf_ : 1;
+
+  public:
+    JSErrorReport()
+      : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0),
+        notes(nullptr),
+        flags(0), exnType(0), isMuted(false),
+        ownsLinebuf_(false)
+    {}
+
+    ~JSErrorReport() {
+        freeLinebuf();
+    }
+
+  public:
+    const char16_t* linebuf() const {
+        return linebuf_;
+    }
+    size_t linebufLength() const {
+        return linebufLength_;
+    }
+    size_t tokenOffset() const {
+        return tokenOffset_;
+    }
+    void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+                          size_t tokenOffsetArg) {
+        initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg);
+        ownsLinebuf_ = true;
+    }
+    void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+                             size_t tokenOffsetArg);
+
+  private:
+    void freeLinebuf();
+};
+
+/*
+ * JSErrorReport flag values.  These may be freely composed.
+ */
+#define JSREPORT_ERROR      0x0     /* pseudo-flag for default case */
+#define JSREPORT_WARNING    0x1     /* reported via JS_ReportWarning */
+#define JSREPORT_EXCEPTION  0x2     /* exception was thrown */
+#define JSREPORT_STRICT     0x4     /* error or warning due to strict option */
+
+#define JSREPORT_USER_1     0x8     /* user-defined flag */
+
+/*
+ * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception
+ * has been thrown for this runtime error, and the host should ignore it.
+ * Exception-aware hosts should also check for JS_IsExceptionPending if
+ * JS_ExecuteScript returns failure, and signal or propagate the exception, as
+ * appropriate.
+ */
+#define JSREPORT_IS_WARNING(flags)      (((flags) & JSREPORT_WARNING) != 0)
+#define JSREPORT_IS_EXCEPTION(flags)    (((flags) & JSREPORT_EXCEPTION) != 0)
+#define JSREPORT_IS_STRICT(flags)       (((flags) & JSREPORT_STRICT) != 0)
+
+#endif /* js_ErrorReport_h */
new file mode 100644
--- /dev/null
+++ b/js/public/JSON.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * JSON serialization and deserialization operations.
+ */
+
+#ifndef js_JSON_h
+#define js_JSON_h
+
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
+
+struct JSContext;
+class JSObject;
+class JSString;
+
+namespace JS { union Value; }
+
+using JSONWriteCallback = bool (*)(const char16_t* buf, uint32_t len, void* data);
+
+/**
+ * Performs the JSON.stringify operation, as specified by ECMAScript, except
+ * writing stringified data by repeated calls of |callback|, with each such
+ * call passed |data| as argument.
+ */
+extern JS_PUBLIC_API(bool)
+JS_Stringify(JSContext* cx, JS::MutableHandle<JS::Value> value, JS::Handle<JSObject*> replacer,
+             JS::Handle<JS::Value> space, JSONWriteCallback callback, void* data);
+
+namespace JS {
+
+/**
+ * An API akin to JS_Stringify but with the goal of not having observable
+ * side-effects when the stringification is performed.  This means it does not
+ * allow a replacer or a custom space and has the following constraints on its
+ * input:
+ *
+ * 1) The input must be a plain object or array, not an abitrary value.
+ * 2) Every value in the graph reached by the algorithm starting with this
+ *    object must be one of the following: null, undefined, a string (NOT a
+ *    string object!), a boolean, a finite number (i.e. no NaN or Infinity or
+ *    -Infinity), a plain object with no accessor properties, or an Array with
+ *    no holes.
+ *
+ * The actual behavior differs from JS_Stringify only in asserting the above and
+ * NOT attempting to get the "toJSON" property from things, since that could
+ * clearly have side-effects.
+ */
+extern JS_PUBLIC_API(bool)
+ToJSONMaybeSafely(JSContext* cx, JS::Handle<JSObject*> input,
+                  JSONWriteCallback callback, void* data);
+
+} /* namespace JS */
+
+/**
+ * Performs the JSON.parse operation as specified by ECMAScript.
+ */
+extern JS_PUBLIC_API(bool)
+JS_ParseJSON(JSContext* cx, const char16_t* chars, uint32_t len, JS::MutableHandle<JS::Value> vp);
+
+/**
+ * Performs the JSON.parse operation as specified by ECMAScript.
+ */
+extern JS_PUBLIC_API(bool)
+JS_ParseJSON(JSContext* cx, JS::Handle<JSString*> str, JS::MutableHandle<JS::Value> vp);
+
+/**
+ * Performs the JSON.parse operation as specified by ECMAScript, using the
+ * given |reviver| argument as the corresponding optional argument to that
+ * function.
+ */
+extern JS_PUBLIC_API(bool)
+JS_ParseJSONWithReviver(JSContext* cx, const char16_t* chars, uint32_t len,
+                        JS::Handle<JS::Value> reviver, JS::MutableHandle<JS::Value> vp);
+
+/**
+ * Performs the JSON.parse operation as specified by ECMAScript, using the
+ * given |reviver| argument as the corresponding optional argument to that
+ * function.
+ */
+extern JS_PUBLIC_API(bool)
+JS_ParseJSONWithReviver(JSContext* cx, JS::Handle<JSString*> str, JS::Handle<JS::Value> reviver,
+                        JS::MutableHandle<JS::Value> vp);
+
+#endif /* js_JSON_h */
new file mode 100644
--- /dev/null
+++ b/js/public/MemoryFunctions.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Low-level memory-allocation functions. */
+
+#ifndef js_MemoryFunctions_h
+#define js_MemoryFunctions_h
+
+#include "mozilla/Attributes.h" // MOZ_MUST_USE
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+struct JSContext;
+
+extern JS_PUBLIC_API(void*)
+JS_malloc(JSContext* cx, size_t nbytes);
+
+extern JS_PUBLIC_API(void*)
+JS_realloc(JSContext* cx, void* p, size_t oldBytes, size_t newBytes);
+
+/**
+ * A wrapper for |js_free(p)| that may delay |js_free(p)| invocation as a
+ * performance optimization.  |cx| may be nullptr.
+ */
+extern JS_PUBLIC_API(void)
+JS_free(JSContext* cx, void* p);
+
+/**
+ * A wrapper for |js_free(p)| that may delay |js_free(p)| invocation as a
+ * performance optimization as specified by the given JSFreeOp instance.
+ */
+extern JS_PUBLIC_API(void)
+JS_freeop(JSFreeOp* fop, void* p);
+
+extern JS_PUBLIC_API(void)
+JS_updateMallocCounter(JSContext* cx, size_t nbytes);
+
+#endif /* js_MemoryFunctions_h */
new file mode 100644
--- /dev/null
+++ b/js/public/SavedFrameAPI.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Functions and types related to SavedFrame objects created by the Debugger
+ * API.
+ */
+
+#ifndef js_SavedFrameAPI_h
+#define js_SavedFrameAPI_h
+
+#include "jstypes.h" // JS_FRIEND_API, JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
+
+struct JSContext;
+class JSObject;
+struct JSPrincipals;
+
+namespace JS {
+
+/*
+ * Accessors for working with SavedFrame JSObjects
+ *
+ * Each of these functions assert that if their `HandleObject savedFrame`
+ * argument is non-null, its JSClass is the SavedFrame class (or it is a
+ * cross-compartment or Xray wrapper around an object with the SavedFrame class)
+ * and the object is not the SavedFrame.prototype object.
+ *
+ * Each of these functions will find the first SavedFrame object in the chain
+ * whose underlying stack frame principals are subsumed by the given
+ * |principals|, and operate on that SavedFrame object. This prevents leaking
+ * information about privileged frames to un-privileged callers
+ *
+ * The SavedFrame in parameters do _NOT_ need to be in the same compartment as
+ * the cx, and the various out parameters are _NOT_ guaranteed to be in the same
+ * compartment as cx.
+ *
+ * You may consider or skip over self-hosted frames by passing
+ * `SavedFrameSelfHosted::Include` or `SavedFrameSelfHosted::Exclude`
+ * respectively.
+ *
+ * Additionally, it may be the case that there is no such SavedFrame object
+ * whose captured frame's principals are subsumed by |principals|! If the
+ * `HandleObject savedFrame` argument is null, or the |principals| do not
+ * subsume any of the chained SavedFrame object's principals,
+ * `SavedFrameResult::AccessDenied` is returned and a (hopefully) sane default
+ * value is chosen for the out param.
+ *
+ * See also `js/src/doc/SavedFrame/SavedFrame.md`.
+ */
+
+enum class SavedFrameResult {
+    Ok,
+    AccessDenied
+};
+
+enum class SavedFrameSelfHosted {
+    Include,
+    Exclude
+};
+
+/**
+ * Given a SavedFrame JSObject, get its source property. Defaults to the empty
+ * string.
+ */
+extern JS_PUBLIC_API(SavedFrameResult)
+GetSavedFrameSource(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+                    MutableHandle<JSString*> sourcep,
+                    SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its line property. Defaults to 0.
+ */
+extern JS_PUBLIC_API(SavedFrameResult)
+GetSavedFrameLine(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+                  uint32_t* linep,
+                  SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its column property. Defaults to 0.
+ */
+extern JS_PUBLIC_API(SavedFrameResult)
+GetSavedFrameColumn(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+                    uint32_t* columnp,
+                    SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its functionDisplayName string, or nullptr
+ * if SpiderMonkey was unable to infer a name for the captured frame's
+ * function. Defaults to nullptr.
+ */
+extern JS_PUBLIC_API(SavedFrameResult)
+GetSavedFrameFunctionDisplayName(JSContext* cx, JSPrincipals* principals,
+                                 Handle<JSObject*> savedFrame,
+                                 MutableHandle<JSString*> namep,
+                                 SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its asyncCause string. Defaults to nullptr.
+ */
+extern JS_PUBLIC_API(SavedFrameResult)
+GetSavedFrameAsyncCause(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+                        MutableHandle<JSString*> asyncCausep,
+                        SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its asyncParent SavedFrame object or nullptr
+ * if there is no asyncParent. The `asyncParentp` out parameter is _NOT_
+ * guaranteed to be in the cx's compartment. Defaults to nullptr.
+ */
+extern JS_PUBLIC_API(SavedFrameResult)
+GetSavedFrameAsyncParent(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+                         MutableHandle<JSObject*> asyncParentp,
+                         SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if
+ * it is the oldest frame in the stack. The `parentp` out parameter is _NOT_
+ * guaranteed to be in the cx's compartment. Defaults to nullptr.
+ */
+extern JS_PUBLIC_API(SavedFrameResult)
+GetSavedFrameParent(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+                    MutableHandle<JSObject*> parentp,
+                    SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+} // namespace JS
+
+namespace js {
+
+/**
+ * Get the first SavedFrame object in this SavedFrame stack whose principals are
+ * subsumed by the given |principals|. If there is no such frame, return nullptr.
+ *
+ * Do NOT pass a non-SavedFrame object here.
+ */
+extern JS_FRIEND_API(JSObject*)
+GetFirstSubsumedSavedFrame(JSContext* cx, JSPrincipals* principals,
+                           JS::Handle<JSObject*> savedFrame, JS::SavedFrameSelfHosted selfHosted);
+
+} // namespace js
+
+#endif /* js_SavedFrameAPI_h */
new file mode 100644
--- /dev/null
+++ b/js/public/StableStringChars.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Safely access the contents of a string even as GC can cause the string's
+ * contents to move around in memory.
+ */
+
+#ifndef js_StableStringChars_h
+#define js_StableStringChars_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_INIT_OUTSIDE_CTOR, MOZ_STACK_CLASS, MOZ_MUST_USE
+#include "mozilla/Maybe.h" // mozilla::Maybe
+#include "mozilla/Range.h" // mozilla::Range
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint8_t
+
+#include "jstypes.h" // JS_FRIEND_API
+
+#include "js/HeapAPI.h" // JS::shadow::String
+#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
+#include "js/TypeDecls.h" // JSContext, JS::Latin1Char, JSString
+#include "js/Vector.h" // js::Vector
+
+class JSLinearString;
+
+namespace JS {
+
+MOZ_ALWAYS_INLINE size_t
+GetStringLength(JSString* s)
+{
+    return reinterpret_cast<shadow::String*>(s)->length();
+}
+
+/**
+ * This class provides safe access to a string's chars across a GC. Once
+ * we allocate strings and chars in the nursery (bug 903519), this class
+ * will have to make a copy of the string's chars if they are allocated
+ * in the nursery, so it's best to avoid using this class unless you really
+ * need it. It's usually more efficient to use the latin1Chars/twoByteChars
+ * JSString methods and often the code can be rewritten so that only indexes
+ * instead of char pointers are used in parts of the code that can GC.
+ */
+class MOZ_STACK_CLASS JS_FRIEND_API(AutoStableStringChars) final
+{
+    /*
+     * When copying string char, use this many bytes of inline storage.  This is
+     * chosen to allow the inline string types to be copied without allocating.
+     * This is asserted in AutoStableStringChars::allocOwnChars.
+     */
+    static const size_t InlineCapacity = 24;
+
+    /* Ensure the string is kept alive while we're using its chars. */
+    Rooted<JSString*> s_;
+    MOZ_INIT_OUTSIDE_CTOR union {
+        const char16_t* twoByteChars_;
+        const Latin1Char* latin1Chars_;
+    };
+    mozilla::Maybe<js::Vector<uint8_t, InlineCapacity>> ownChars_;
+    enum State { Uninitialized, Latin1, TwoByte };
+    State state_;
+
+  public:
+    explicit AutoStableStringChars(JSContext* cx)
+      : s_(cx), state_(Uninitialized)
+    {}
+
+    MOZ_MUST_USE bool init(JSContext* cx, JSString* s);
+
+    /* Like init(), but Latin1 chars are inflated to TwoByte. */
+    MOZ_MUST_USE bool initTwoByte(JSContext* cx, JSString* s);
+
+    bool isLatin1() const { return state_ == Latin1; }
+    bool isTwoByte() const { return state_ == TwoByte; }
+
+    const Latin1Char* latin1Chars() const {
+        MOZ_ASSERT(state_ == Latin1);
+        return latin1Chars_;
+    }
+    const char16_t* twoByteChars() const {
+        MOZ_ASSERT(state_ == TwoByte);
+        return twoByteChars_;
+    }
+
+    mozilla::Range<const Latin1Char> latin1Range() const {
+        MOZ_ASSERT(state_ == Latin1);
+        return mozilla::Range<const Latin1Char>(latin1Chars_, GetStringLength(s_));
+    }
+
+    mozilla::Range<const char16_t> twoByteRange() const {
+        MOZ_ASSERT(state_ == TwoByte);
+        return mozilla::Range<const char16_t>(twoByteChars_,
+                                              GetStringLength(s_));
+    }
+
+    /* If we own the chars, transfer ownership to the caller. */
+    bool maybeGiveOwnershipToCaller() {
+        MOZ_ASSERT(state_ != Uninitialized);
+        if (!ownChars_.isSome() || !ownChars_->extractRawBuffer())
+            return false;
+        state_ = Uninitialized;
+        ownChars_.reset();
+        return true;
+    }
+
+  private:
+    AutoStableStringChars(const AutoStableStringChars& other) = delete;
+    void operator=(const AutoStableStringChars& other) = delete;
+
+    bool baseIsInline(Handle<JSLinearString*> linearString);
+    template <typename T> T* allocOwnChars(JSContext* cx, size_t count);
+    bool copyLatin1Chars(JSContext* cx, Handle<JSLinearString*> linearString);
+    bool copyTwoByteChars(JSContext* cx, Handle<JSLinearString*> linearString);
+    bool copyAndInflateLatin1Chars(JSContext*, Handle<JSLinearString*> linearString);
+};
+
+} // namespace JS
+
+#endif /* js_StableStringChars_h */
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -6,30 +6,32 @@
 
 #include "builtin/Eval.h"
 
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Range.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "gc/HashUtil.h"
+#include "js/StableStringChars.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSONParser.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 
 using mozilla::AddToHash;
 using mozilla::HashString;
 using mozilla::RangedPtr;
 
 using JS::AutoCheckCannotGC;
+using JS::AutoStableStringChars;
 
 // We should be able to assert this for *any* fp->environmentChain().
 static void
 AssertInnerizedEnvironmentChain(JSContext* cx, JSObject& env)
 {
 #ifdef DEBUG
     RootedObject obj(cx);
     for (obj = &env; obj; obj = obj->enclosingEnvironment())
--- a/js/src/builtin/JSON.cpp
+++ b/js/src/builtin/JSON.cpp
@@ -14,16 +14,17 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/Array.h"
 #ifdef ENABLE_BIGINT
 #include "builtin/BigInt.h"
 #endif
 #include "builtin/String.h"
+#include "js/StableStringChars.h"
 #include "util/StringBuffer.h"
 #include "vm/Interpreter.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSONParser.h"
 
 #include "builtin/Array-inl.h"
@@ -33,16 +34,18 @@
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::IsFinite;
 using mozilla::Maybe;
 using mozilla::RangedPtr;
 
+using JS::AutoStableStringChars;
+
 const Class js::JSONClass = {
     js_JSON_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_JSON)
 };
 
 /* ES5 15.12.3 Quote.
  * Requires that the destination has enough space allocated for src after escaping
  * (that is, `2 + 6 * (srcEnd - srcBegin)` characters).
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1960,16 +1960,17 @@ static const JSFunctionSpec object_stati
     JS_FN("getOwnPropertyNames",       obj_getOwnPropertyNames,     1, 0),
     JS_FN("getOwnPropertySymbols",     obj_getOwnPropertySymbols,   1, 0),
     JS_SELF_HOSTED_FN("isExtensible",  "ObjectIsExtensible",        1, 0),
     JS_FN("preventExtensions",         obj_preventExtensions,       1, 0),
     JS_FN("freeze",                    obj_freeze,                  1, 0),
     JS_FN("isFrozen",                  obj_isFrozen,                1, 0),
     JS_FN("seal",                      obj_seal,                    1, 0),
     JS_FN("isSealed",                  obj_isSealed,                1, 0),
+    JS_SELF_HOSTED_FN("fromEntries",   "ObjectFromEntries",         1, 0),
     JS_FS_END
 };
 
 static JSObject*
 CreateObjectConstructor(JSContext* cx, JSProtoKey key)
 {
     Rooted<GlobalObject*> self(cx, cx->global());
     if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
--- a/js/src/builtin/Object.js
+++ b/js/src/builtin/Object.js
@@ -281,8 +281,28 @@ function ObjectOrReflectDefineProperty(o
 // 19.1.2.4 Object.defineProperty ( O, P, Attributes )
 function ObjectDefineProperty(obj, propertyKey, attributes) {
     // Steps 1-4.
     ObjectOrReflectDefineProperty(obj, propertyKey, attributes, true);
 
     // Step 5.
     return obj;
 }
+
+// Proposal https://tc39.github.io/proposal-object-from-entries/
+// 1. Object.fromEntries ( iterable )
+function ObjectFromEntries(iter) {
+    // We omit the usual step number comments here because they don't help.
+    // This implementation inlines AddEntriesFromIterator and
+    // CreateDataPropertyOnObject, so it looks more like the polyfill
+    // <https://github.com/tc39/proposal-object-from-entries/blob/master/polyfill.js>
+    // than the spec algorithm.
+    const obj = {};
+
+    for (const pair of allowContentIter(iter)) {
+        if (!IsObject(pair))
+            ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "Object.fromEntries");
+        _DefineDataProperty(obj, pair[0], pair[1]);
+    }
+
+    return obj;
+}
+
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -14,26 +14,28 @@
 
 #include "jspubtd.h"
 
 #include "builtin/Array.h"
 #include "builtin/Reflect.h"
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 #include "js/CharacterEncoding.h"
+#include "js/StableStringChars.h"
 #include "vm/JSAtom.h"
 #include "vm/JSObject.h"
 #include "vm/RegExpObject.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 
+using JS::AutoStableStringChars;
 using JS::AutoValueArray;
 using mozilla::DebugOnly;
 
 enum ASTType {
     AST_ERROR = -1,
 #define ASTDEF(ast, str, method) ast,
 #include "jsast.tbl"
 #undef ASTDEF
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -26,16 +26,17 @@
 
 #include "builtin/Array.h"
 #include "builtin/Boolean.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/RegExp.h"
 #include "jit/InlinableNatives.h"
 #include "js/Conversions.h"
+#include "js/StableStringChars.h"
 #include "js/UniquePtr.h"
 #if ENABLE_INTL_API
 # include "unicode/uchar.h"
 # include "unicode/unorm2.h"
 #endif
 #include "util/StringBuffer.h"
 #include "util/Unicode.h"
 #include "vm/BytecodeUtil.h"
@@ -63,16 +64,17 @@ using JS::SymbolCode;
 
 using mozilla::CheckedInt;
 using mozilla::IsNaN;
 using mozilla::IsSame;
 using mozilla::PodCopy;
 using mozilla::RangedPtr;
 
 using JS::AutoCheckCannotGC;
+using JS::AutoStableStringChars;
 
 static JSLinearString*
 ArgToLinearString(JSContext* cx, const CallArgs& args, unsigned argno)
 {
     if (argno >= args.length())
         return cx->names().undefined;
 
     JSString* str = ToString<CanGC>(cx, args[argno]);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -36,18 +36,20 @@
 #include "irregexp/RegExpAST.h"
 #include "irregexp/RegExpEngine.h"
 #include "irregexp/RegExpParser.h"
 #endif
 #include "gc/Heap.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitRealm.h"
+#include "js/AutoByteString.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
+#include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
@@ -80,16 +82,18 @@
 #include "vm/NativeObject-inl.h"
 #include "vm/StringType-inl.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 
+using JS::AutoStableStringChars;
+
 // If fuzzingSafe is set, remove functionality that could cause problems with
 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
 mozilla::Atomic<bool> fuzzingSafe(false);
 
 // If disableOOMFunctions is set, disable functionality that causes artificial
 // OOM conditions.
 static mozilla::Atomic<bool> disableOOMFunctions(false);
 
--- a/js/src/builtin/intl/Collator.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -12,26 +12,30 @@
 
 #include "jsapi.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "gc/FreeOp.h"
+#include "js/AutoByteString.h"
+#include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
+using JS::AutoStableStringChars;
+
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 using js::intl::ReportInternalError;
 using js::intl::SharedIntlData;
 using js::intl::StringsAreEqual;
 
 const ClassOps CollatorObject::classOps_ = {
     nullptr, /* addProperty */
--- a/js/src/builtin/intl/DateTimeFormat.cpp
+++ b/js/src/builtin/intl/DateTimeFormat.cpp
@@ -14,26 +14,29 @@
 #include "jsfriendapi.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
 #include "gc/FreeOp.h"
+#include "js/AutoByteString.h"
+#include "js/StableStringChars.h"
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
+using JS::AutoStableStringChars;
 using JS::ClippedTime;
 using JS::TimeClip;
 
 using js::intl::CallICU;
 using js::intl::DateTimeFormatOptions;
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 using js::intl::INITIAL_CHAR_BUFFER_SIZE;
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -16,29 +16,33 @@
 
 #include "builtin/intl/Collator.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/ScopedICUObject.h"
+#include "js/AutoByteString.h"
 #include "js/Class.h"
+#include "js/StableStringChars.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::Range;
 using mozilla::RangedPtr;
 
+using JS::AutoStableStringChars;
+
 using js::intl::CallICU;
 using js::intl::DateTimeFormatOptions;
 using js::intl::IcuLocale;
 
 /******************** Intl ********************/
 
 bool
 js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/builtin/intl/NumberFormat.cpp
+++ b/js/src/builtin/intl/NumberFormat.cpp
@@ -16,35 +16,38 @@
 #include <stdint.h>
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "js/RootingAPI.h"
+#include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 #include "vm/Stack.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::AssertedCast;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
-using mozilla::IsNegativeZero;
+using mozilla::IsNegative;
 
 using js::intl::CallICU;
 using js::intl::DateTimeFormatOptions;
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 
+using JS::AutoStableStringChars;
+
 const ClassOps NumberFormatObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     NumberFormatObject::finalize
@@ -368,34 +371,30 @@ NewUNumberFormat(JSContext* cx, Handle<N
     }
     unum_setAttribute(nf, UNUM_GROUPING_USED, uUseGrouping);
     unum_setAttribute(nf, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
 
     return toClose.forget();
 }
 
 static JSString*
-PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double* x,
+PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double x,
                        UFieldPositionIterator* fpositer)
 {
-    // PartitionNumberPattern doesn't consider -0.0 to be negative.
-    if (IsNegativeZero(*x))
-        *x = 0.0;
-
     return CallICU(cx, [nf, x, fpositer](UChar* chars, int32_t size, UErrorCode* status) {
-        return unum_formatDoubleForFields(nf, *x, chars, size, fpositer, status);
+        return unum_formatDoubleForFields(nf, x, chars, size, fpositer, status);
     });
 }
 
 static bool
 intl_FormatNumber(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result)
 {
     // Passing null for |fpositer| will just not compute partition information,
     // letting us common up all ICU number-formatting code.
-    JSString* str = PartitionNumberPattern(cx, nf, &x, nullptr);
+    JSString* str = PartitionNumberPattern(cx, nf, x, nullptr);
     if (!str)
         return false;
 
     result.setString(str);
     return true;
 }
 
 using FieldType = ImmutablePropertyNamePtr JSAtomState::*;
@@ -421,23 +420,20 @@ GetFieldTypeForNumberField(UNumberFormat
 
       case UNUM_DECIMAL_SEPARATOR_FIELD:
         return &JSAtomState::decimal;
 
       case UNUM_FRACTION_FIELD:
         return &JSAtomState::fraction;
 
       case UNUM_SIGN_FIELD: {
-        MOZ_ASSERT(!IsNegativeZero(d),
-                   "-0 should have been excluded by PartitionNumberPattern");
-
         // Manual trawling through the ICU call graph appears to indicate that
         // the basic formatting we request will never include a positive sign.
         // But this analysis may be mistaken, so don't absolutely trust it.
-        return d < 0 ? &JSAtomState::minusSign : &JSAtomState::plusSign;
+        return IsNegative(d) ? &JSAtomState::minusSign : &JSAtomState::plusSign;
       }
 
       case UNUM_PERCENT_FIELD:
         return &JSAtomState::percentSign;
 
       case UNUM_CURRENCY_FIELD:
         return &JSAtomState::currency;
 
@@ -477,17 +473,17 @@ intl_FormatNumberToParts(JSContext* cx, 
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
 
     MOZ_ASSERT(fpositer);
     ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer);
 
-    RootedString overallResult(cx, PartitionNumberPattern(cx, nf, &x, fpositer));
+    RootedString overallResult(cx, PartitionNumberPattern(cx, nf, x, fpositer));
     if (!overallResult)
         return false;
 
     RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
     if (!partsArray)
         return false;
 
     // First, vacuum up fields in the overall formatted string.
--- a/js/src/builtin/intl/PluralRules.cpp
+++ b/js/src/builtin/intl/PluralRules.cpp
@@ -10,16 +10,17 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
+#include "js/AutoByteString.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp
+++ b/js/src/builtin/intl/RelativeTimeFormat.cpp
@@ -10,16 +10,17 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/FloatingPoint.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
+#include "js/AutoByteString.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::IsNegativeZero;
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -35,30 +35,33 @@
 #include "jsnum.h"
 
 #include "builtin/TypedObject.h"
 #include "ctypes/Library.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "gc/Zone.h"
 #include "jit/AtomicOperations.h"
+#include "js/AutoByteString.h"
+#include "js/StableStringChars.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "util/Windows.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace std;
 
 using mozilla::IsAsciiAlpha;
 using mozilla::IsAsciiDigit;
 
 using JS::AutoCheckCannotGC;
+using JS::AutoStableStringChars;
 
 namespace js {
 namespace ctypes {
 
 template <typename CharT>
 size_t
 GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars,
                             size_t nchars)
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -5,16 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ctypes/Library.h"
 
 #include "prerror.h"
 #include "prlink.h"
 
 #include "ctypes/CTypes.h"
+#include "js/AutoByteString.h"
+#include "js/StableStringChars.h"
+
+using JS::AutoStableStringChars;
 
 namespace js {
 namespace ctypes {
 
 /*******************************************************************************
 ** JSAPI function prototypes
 *******************************************************************************/
 
--- a/js/src/frontend/EmitterScope.cpp
+++ b/js/src/frontend/EmitterScope.cpp
@@ -3,16 +3,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/. */
 
 #include "frontend/EmitterScope.h"
 
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/TDZCheckCache.h"
+#include "js/AutoByteString.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::Some;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -32,16 +32,17 @@
 #include "jstypes.h"
 
 #include "builtin/ModuleObject.h"
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
+#include "js/AutoByteString.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/RegExpObject.h"
 #include "vm/StringType.h"
 #include "wasm/AsmJS.h"
@@ -420,17 +421,17 @@ ParseContext::init()
 
     return true;
 }
 
 bool
 UsedNameTracker::noteUse(JSContext* cx, JSAtom* name, uint32_t scriptId, uint32_t scopeId)
 {
     if (UsedNameMap::AddPtr p = map_.lookupForAdd(name)) {
-        if (!p->value().noteUsedInScope(scriptId, scopeId))
+        if (!p || !p->value().noteUsedInScope(scriptId, scopeId))
             return false;
     } else {
         UsedNameInfo info(cx);
         if (!info.noteUsedInScope(scriptId, scopeId))
             return false;
         if (!map_.add(p, name, std::move(info)))
             return false;
     }
--- a/js/src/gc/HashUtil.h
+++ b/js/src/gc/HashUtil.h
@@ -42,17 +42,18 @@ struct DependentAddPtr
             return false;
         }
         return true;
     }
 
     template <class KeyInput>
     void remove(JSContext* cx, T& table, const KeyInput& key) {
         refreshAddPtr(cx, table, key);
-        table.remove(addPtr);
+        if (addPtr)
+            table.remove(addPtr);
     }
 
     bool found() const                 { return addPtr.found(); }
     explicit operator bool() const     { return found(); }
     const Entry& operator*() const     { return *addPtr; }
     const Entry* operator->() const    { return &*addPtr; }
 
   private:
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/structured-clone/roundtrip.js
@@ -0,0 +1,30 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+load(libdir + "asserts.js");
+
+const objects = [
+    {},
+    {a: 1, b: 2},
+    {0: 1, 1: 2},
+    {0: 1, 1: 2, a: 1},
+    {0: 1, 1: 2, a: 1, b: 2},
+    {1000000: 0, 1000001: 1},
+    {0: 0, 1: 0, 1000000: 0, 1000001: 1},
+
+    [],
+    [0, 1, 2],
+    [0, 15, 16],
+    [{a: 0, b: 0}, {b: 0, a: 0}],
+    [0, , , 1, 2],
+    [, 1],
+    [0,,],
+    [,,],
+]
+
+for (const obj of objects) {
+    assertDeepEq(deserialize(serialize(obj)), obj);
+    assertDeepEq(deserialize(serialize(wrapWithProto(obj, null))), obj);
+}
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -5,16 +5,17 @@
  * 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 <limits>
 #include <string.h>
 
 #include "builtin/String.h"
 
+#include "js/JSON.h"
 #include "js/Printf.h"
 #include "jsapi-tests/tests.h"
 
 using namespace js;
 
 class AutoInflatedString {
     JSContext * const cx;
     char16_t* chars_;
--- a/js/src/jsapi-tests/testSavedStacks.cpp
+++ b/js/src/jsapi-tests/testSavedStacks.cpp
@@ -3,16 +3,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/. */
 
 #include "jsfriendapi.h"
 #include "builtin/String.h"
 
 #include "builtin/TestingFunctions.h"
+#include "js/SavedFrameAPI.h"
 #include "jsapi-tests/tests.h"
 #include "vm/ArrayObject.h"
 #include "vm/Realm.h"
 #include "vm/SavedStacks.h"
 
 BEGIN_TEST(testSavedStacks_withNoStack)
 {
     JS::Realm* realm = cx->realm();
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -12,16 +12,17 @@
 
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "gc/GC.h"
 #include "js/AllocPolicy.h"
+#include "js/AutoByteString.h"
 #include "js/Vector.h"
 #include "vm/JSContext.h"
 
 /* Note: Aborts on OOM. */
 class JSAPITestString {
     js::Vector<char, 0, js::SystemAllocPolicy> chars;
 
   public:
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -50,22 +50,25 @@
 #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "gc/WeakMap.h"
 #include "jit/JitCommon.h"
 #include "jit/JitSpewer.h"
+#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Initialization.h"
+#include "js/JSON.h"
 #include "js/Proxy.h"
 #include "js/SliceBudget.h"
+#include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
 #include "js/Utility.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/DateObject.h"
@@ -103,16 +106,18 @@
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::Maybe;
 using mozilla::PodCopy;
 using mozilla::Some;
 
+using JS::AutoStableStringChars;
+
 #ifdef HAVE_VA_LIST_AS_ARRAY
 #define JS_ADDRESSOF_VA_LIST(ap) ((va_list*)(ap))
 #else
 #define JS_ADDRESSOF_VA_LIST(ap) (&(ap))
 #endif
 
 JS_PUBLIC_API(bool)
 JS::CallArgs::requireAtLeast(JSContext* cx, const char* fnname, unsigned required) const
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -12,31 +12,32 @@
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Range.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Variant.h"
 
-#include <iterator>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 
 #include "jspubtd.h"
 
 #include "js/AllocPolicy.h"
 #include "js/CallArgs.h"
 #include "js/CharacterEncoding.h"
 #include "js/Class.h"
+#include "js/ErrorReport.h"
 #include "js/GCVector.h"
 #include "js/HashTable.h"
 #include "js/Id.h"
+#include "js/MemoryFunctions.h"
 #include "js/Principals.h"
 #include "js/Realm.h"
 #include "js/RefCounted.h"
 #include "js/RootingAPI.h"
 #include "js/Stream.h"
 #include "js/TracingAPI.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
@@ -217,61 +218,16 @@ enum class PromiseRejectionHandlingState
 
 } /* namespace JS */
 
 typedef void
 (* JSPromiseRejectionTrackerCallback)(JSContext* cx, JS::HandleObject promise,
                                       JS::PromiseRejectionHandlingState state,
                                       void* data);
 
-/**
- * Possible exception types. These types are part of a JSErrorFormatString
- * structure. They define which error to throw in case of a runtime error.
- *
- * JSEXN_WARN is used for warnings in js.msg files (for instance because we
- * don't want to prepend 'Error:' to warning messages). This value can go away
- * if we ever decide to use an entirely separate mechanism for warnings.
- */
-typedef enum JSExnType {
-    JSEXN_ERR,
-    JSEXN_FIRST = JSEXN_ERR,
-        JSEXN_INTERNALERR,
-        JSEXN_EVALERR,
-        JSEXN_RANGEERR,
-        JSEXN_REFERENCEERR,
-        JSEXN_SYNTAXERR,
-        JSEXN_TYPEERR,
-        JSEXN_URIERR,
-        JSEXN_DEBUGGEEWOULDRUN,
-        JSEXN_WASMCOMPILEERROR,
-        JSEXN_WASMLINKERROR,
-        JSEXN_WASMRUNTIMEERROR,
-    JSEXN_ERROR_LIMIT,
-    JSEXN_WARN = JSEXN_ERROR_LIMIT,
-    JSEXN_NOTE,
-    JSEXN_LIMIT
-} JSExnType;
-
-struct JSErrorFormatString {
-     /** The error message name in ASCII. */
-    const char* name;
-
-    /** The error format string in ASCII. */
-    const char* format;
-
-    /** The number of arguments to expand in the formatted error message. */
-    uint16_t argCount;
-
-    /** One of the JSExnType constants above. */
-    int16_t exnType;
-};
-
-typedef const JSErrorFormatString*
-(* JSErrorCallback)(void* userRef, const unsigned errorNumber);
-
 typedef bool
 (* JSLocaleToUpperCase)(JSContext* cx, JS::HandleString src, JS::MutableHandleValue rval);
 
 typedef bool
 (* JSLocaleToLowerCase)(JSContext* cx, JS::HandleString src, JS::MutableHandleValue rval);
 
 typedef bool
 (* JSLocaleCompare)(JSContext* cx, JS::HandleString src1, JS::HandleString src2,
@@ -1306,40 +1262,16 @@ struct JSCTypesCallbacks {
  * pointer to static data that exists for the lifetime of 'ctypesObj', but it
  * may safely be altered after calling this function and without having
  * to call this function again.
  */
 extern JS_PUBLIC_API(void)
 JS_SetCTypesCallbacks(JSObject* ctypesObj, const JSCTypesCallbacks* callbacks);
 #endif
 
-extern JS_PUBLIC_API(void*)
-JS_malloc(JSContext* cx, size_t nbytes);
-
-extern JS_PUBLIC_API(void*)
-JS_realloc(JSContext* cx, void* p, size_t oldBytes, size_t newBytes);
-
-/**
- * A wrapper for js_free(p) that may delay js_free(p) invocation as a
- * performance optimization.
- * cx may be nullptr.
- */
-extern JS_PUBLIC_API(void)
-JS_free(JSContext* cx, void* p);
-
-/**
- * A wrapper for js_free(p) that may delay js_free(p) invocation as a
- * performance optimization as specified by the given JSFreeOp instance.
- */
-extern JS_PUBLIC_API(void)
-JS_freeop(JSFreeOp* fop, void* p);
-
-extern JS_PUBLIC_API(void)
-JS_updateMallocCounter(JSContext* cx, size_t nbytes);
-
 /*
  * A replacement for MallocAllocPolicy that allocates in the JS heap and adds no
  * extra behaviours.
  *
  * This is currently used for allocating source buffers for parsing. Since these
  * are temporary and will not be freed by GC, the memory is not tracked by the
  * usual accounting.
  */
@@ -4659,29 +4591,16 @@ JS_ConcatStrings(JSContext* cx, JS::Hand
  * NB: This function does not store an additional zero byte or char16_t after the
  * transcoded string.
  */
 JS_PUBLIC_API(bool)
 JS_DecodeBytes(JSContext* cx, const char* src, size_t srclen, char16_t* dst,
                size_t* dstlenp);
 
 /**
- * A variation on JS_EncodeCharacters where a null terminated string is
- * returned that you are expected to call JS_free on when done.
- */
-JS_PUBLIC_API(char*)
-JS_EncodeString(JSContext* cx, JSString* str);
-
-/**
- * Same behavior as JS_EncodeString(), but encode into UTF-8 string
- */
-JS_PUBLIC_API(char*)
-JS_EncodeStringToUTF8(JSContext* cx, JS::HandleString str);
-
-/**
  * Get number of bytes in the string encoding (without accounting for a
  * terminating zero bytes. The function returns (size_t) -1 if the string
  * can not be encoded into bytes and reports an error using cx accordingly.
  */
 JS_PUBLIC_API(size_t)
 JS_GetStringEncodingLength(JSContext* cx, JSString* str);
 
 /**
@@ -4690,85 +4609,16 @@ JS_GetStringEncodingLength(JSContext* cx
  * encoded into bytes with no error reported. Otherwise it returns the number
  * of bytes that are necessary to encode the string. If that exceeds the
  * length parameter, the string will be cut and only length bytes will be
  * written into the buffer.
  */
 MOZ_MUST_USE JS_PUBLIC_API(bool)
 JS_EncodeStringToBuffer(JSContext* cx, JSString* str, char* buffer, size_t length);
 
-class MOZ_RAII JSAutoByteString
-{
-  public:
-    JSAutoByteString(JSContext* cx, JSString* str
-                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : mBytes(JS_EncodeString(cx, str))
-    {
-        MOZ_ASSERT(cx);
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    explicit JSAutoByteString(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
-      : mBytes(nullptr)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    ~JSAutoByteString() {
-        JS_free(nullptr, mBytes);
-    }
-
-    /* Take ownership of the given byte array. */
-    void initBytes(JS::UniqueChars&& bytes) {
-        MOZ_ASSERT(!mBytes);
-        mBytes = bytes.release();
-    }
-
-    char* encodeLatin1(JSContext* cx, JSString* str) {
-        MOZ_ASSERT(!mBytes);
-        MOZ_ASSERT(cx);
-        mBytes = JS_EncodeString(cx, str);
-        return mBytes;
-    }
-
-    char* encodeUtf8(JSContext* cx, JS::HandleString str) {
-        MOZ_ASSERT(!mBytes);
-        MOZ_ASSERT(cx);
-        mBytes = JS_EncodeStringToUTF8(cx, str);
-        return mBytes;
-    }
-
-    void clear() {
-        js_free(mBytes);
-        mBytes = nullptr;
-    }
-
-    char* ptr() const {
-        return mBytes;
-    }
-
-    bool operator!() const {
-        return !mBytes;
-    }
-
-    size_t length() const {
-        if (!mBytes)
-            return 0;
-        return strlen(mBytes);
-    }
-
-  private:
-    char* mBytes;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-
-    /* Copy and assignment are not supported. */
-    JSAutoByteString(const JSAutoByteString& another);
-    JSAutoByteString& operator=(const JSAutoByteString& another);
-};
-
 /************************************************************************/
 /*
  * Symbols
  */
 
 namespace JS {
 
 /**
@@ -4869,71 +4719,16 @@ PropertySpecNameEqualsId(const char* nam
  * during GC marking.
  */
 JS_PUBLIC_API(bool)
 PropertySpecNameToPermanentId(JSContext* cx, const char* name, jsid* idp);
 
 } /* namespace JS */
 
 /************************************************************************/
-/*
- * JSON functions
- */
-typedef bool (* JSONWriteCallback)(const char16_t* buf, uint32_t len, void* data);
-
-/**
- * JSON.stringify as specified by ES5.
- */
-JS_PUBLIC_API(bool)
-JS_Stringify(JSContext* cx, JS::MutableHandleValue value, JS::HandleObject replacer,
-             JS::HandleValue space, JSONWriteCallback callback, void* data);
-
-namespace JS {
-
-/**
- * An API akin to JS_Stringify but with the goal of not having observable
- * side-effects when the stringification is performed.  This means it does not
- * allow a replacer or a custom space, and has the following constraints on its
- * input:
- *
- * 1) The input must be a plain object or array, not an abitrary value.
- * 2) Every value in the graph reached by the algorithm starting with this
- *    object must be one of the following: null, undefined, a string (NOT a
- *    string object!), a boolean, a finite number (i.e. no NaN or Infinity or
- *    -Infinity), a plain object with no accessor properties, or an Array with
- *    no holes.
- *
- * The actual behavior differs from JS_Stringify only in asserting the above and
- * NOT attempting to get the "toJSON" property from things, since that could
- * clearly have side-effects.
- */
-JS_PUBLIC_API(bool)
-ToJSONMaybeSafely(JSContext* cx, JS::HandleObject input,
-                  JSONWriteCallback callback, void* data);
-
-} /* namespace JS */
-
-/**
- * JSON.parse as specified by ES5.
- */
-JS_PUBLIC_API(bool)
-JS_ParseJSON(JSContext* cx, const char16_t* chars, uint32_t len, JS::MutableHandleValue vp);
-
-JS_PUBLIC_API(bool)
-JS_ParseJSON(JSContext* cx, JS::HandleString str, JS::MutableHandleValue vp);
-
-JS_PUBLIC_API(bool)
-JS_ParseJSONWithReviver(JSContext* cx, const char16_t* chars, uint32_t len, JS::HandleValue reviver,
-                        JS::MutableHandleValue vp);
-
-JS_PUBLIC_API(bool)
-JS_ParseJSONWithReviver(JSContext* cx, JS::HandleString str, JS::HandleValue reviver,
-                        JS::MutableHandleValue vp);
-
-/************************************************************************/
 
 /**
  * The default locale for the ECMAScript Internationalization API
  * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
  * Note that the Internationalization API encourages clients to
  * specify their own locales.
  * The locale string remains owned by the caller.
  */
@@ -5113,218 +4908,16 @@ extern MOZ_COLD JS_PUBLIC_API(void)
 JS_ReportOutOfMemory(JSContext* cx);
 
 /**
  * Complain when an allocation size overflows the maximum supported limit.
  */
 extern JS_PUBLIC_API(void)
 JS_ReportAllocationOverflow(JSContext* cx);
 
-/**
- * Base class that implements parts shared by JSErrorReport and
- * JSErrorNotes::Note.
- */
-class JSErrorBase
-{
-    // The (default) error message.
-    // If ownsMessage_ is true, the it is freed in destructor.
-    JS::ConstUTF8CharsZ message_;
-
-  public:
-    JSErrorBase()
-      : filename(nullptr), lineno(0), column(0),
-        errorNumber(0),
-        ownsMessage_(false)
-    {}
-
-    ~JSErrorBase() {
-        freeMessage();
-    }
-
-    // Source file name, URL, etc., or null.
-    const char* filename;
-
-    // Source line number.
-    unsigned lineno;
-
-    // Zero-based column index in line.
-    unsigned column;
-
-    // the error number, e.g. see js.msg.
-    unsigned errorNumber;
-
-  private:
-    bool ownsMessage_ : 1;
-
-  public:
-    const JS::ConstUTF8CharsZ message() const {
-        return message_;
-    }
-
-    void initOwnedMessage(const char* messageArg) {
-        initBorrowedMessage(messageArg);
-        ownsMessage_ = true;
-    }
-    void initBorrowedMessage(const char* messageArg) {
-        MOZ_ASSERT(!message_);
-        message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg));
-    }
-
-    JSString* newMessageString(JSContext* cx);
-
-  private:
-    void freeMessage();
-};
-
-/**
- * Notes associated with JSErrorReport.
- */
-class JSErrorNotes
-{
-  public:
-    class Note : public JSErrorBase
-    {};
-
-  private:
-    // Stores pointers to each note.
-    js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_;
-
-  public:
-    JSErrorNotes();
-    ~JSErrorNotes();
-
-    // Add an note to the given position.
-    bool addNoteASCII(JSContext* cx,
-                      const char* filename, unsigned lineno, unsigned column,
-                      JSErrorCallback errorCallback, void* userRef,
-                      const unsigned errorNumber, ...);
-    bool addNoteLatin1(JSContext* cx,
-                       const char* filename, unsigned lineno, unsigned column,
-                       JSErrorCallback errorCallback, void* userRef,
-                       const unsigned errorNumber, ...);
-    bool addNoteUTF8(JSContext* cx,
-                     const char* filename, unsigned lineno, unsigned column,
-                     JSErrorCallback errorCallback, void* userRef,
-                     const unsigned errorNumber, ...);
-
-    JS_PUBLIC_API(size_t) length();
-
-    // Create a deep copy of notes.
-    js::UniquePtr<JSErrorNotes> copy(JSContext* cx);
-
-    class iterator : public std::iterator<std::input_iterator_tag, js::UniquePtr<Note>>
-    {
-        js::UniquePtr<Note>* note_;
-      public:
-        explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note)
-        {}
-
-        bool operator==(iterator other) const {
-            return note_ == other.note_;
-        }
-        bool operator!=(iterator other) const {
-            return !(*this == other);
-        }
-        iterator& operator++() {
-            note_++;
-            return *this;
-        }
-        reference operator*() {
-            return *note_;
-        }
-    };
-    JS_PUBLIC_API(iterator) begin();
-    JS_PUBLIC_API(iterator) end();
-};
-
-/**
- * Describes a single error or warning that occurs in the execution of script.
- */
-class JSErrorReport : public JSErrorBase
-{
-    // Offending source line without final '\n'.
-    // If ownsLinebuf_ is true, the buffer is freed in destructor.
-    const char16_t* linebuf_;
-
-    // Number of chars in linebuf_. Does not include trailing '\0'.
-    size_t linebufLength_;
-
-    // The 0-based offset of error token in linebuf_.
-    size_t tokenOffset_;
-
-  public:
-    JSErrorReport()
-      : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0),
-        notes(nullptr),
-        flags(0), exnType(0), isMuted(false),
-        ownsLinebuf_(false)
-    {}
-
-    ~JSErrorReport() {
-        freeLinebuf();
-    }
-
-    // Associated notes, or nullptr if there's no note.
-    js::UniquePtr<JSErrorNotes> notes;
-
-    // error/warning, etc.
-    unsigned flags;
-
-    // One of the JSExnType constants.
-    int16_t exnType;
-
-    // See the comment in TransitiveCompileOptions.
-    bool isMuted : 1;
-
-  private:
-    bool ownsLinebuf_ : 1;
-
-  public:
-    const char16_t* linebuf() const {
-        return linebuf_;
-    }
-    size_t linebufLength() const {
-        return linebufLength_;
-    }
-    size_t tokenOffset() const {
-        return tokenOffset_;
-    }
-    void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
-                          size_t tokenOffsetArg) {
-        initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg);
-        ownsLinebuf_ = true;
-    }
-    void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
-                             size_t tokenOffsetArg);
-
-  private:
-    void freeLinebuf();
-};
-
-/*
- * JSErrorReport flag values.  These may be freely composed.
- */
-#define JSREPORT_ERROR      0x0     /* pseudo-flag for default case */
-#define JSREPORT_WARNING    0x1     /* reported via JS_ReportWarning */
-#define JSREPORT_EXCEPTION  0x2     /* exception was thrown */
-#define JSREPORT_STRICT     0x4     /* error or warning due to strict option */
-
-#define JSREPORT_USER_1     0x8     /* user-defined flag */
-
-/*
- * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception
- * has been thrown for this runtime error, and the host should ignore it.
- * Exception-aware hosts should also check for JS_IsExceptionPending if
- * JS_ExecuteScript returns failure, and signal or propagate the exception, as
- * appropriate.
- */
-#define JSREPORT_IS_WARNING(flags)      (((flags) & JSREPORT_WARNING) != 0)
-#define JSREPORT_IS_EXCEPTION(flags)    (((flags) & JSREPORT_EXCEPTION) != 0)
-#define JSREPORT_IS_STRICT(flags)       (((flags) & JSREPORT_STRICT) != 0)
-
 namespace JS {
 
 using WarningReporter = void (*)(JSContext* cx, JSErrorReport* report);
 
 extern JS_PUBLIC_API(WarningReporter)
 SetWarningReporter(JSContext* cx, WarningReporter reporter);
 
 extern JS_PUBLIC_API(WarningReporter)
@@ -6245,120 +5838,16 @@ CaptureCurrentStack(JSContext* cx, Mutab
  * new stack object is written to |stackp|.  Returns true on success,
  * or sets an exception and returns |false| on error.
  */
 extern JS_PUBLIC_API(bool)
 CopyAsyncStack(JSContext* cx, HandleObject asyncStack,
                HandleString asyncCause, MutableHandleObject stackp,
                const mozilla::Maybe<size_t>& maxFrameCount);
 
-/*
- * Accessors for working with SavedFrame JSObjects
- *
- * Each of these functions assert that if their `HandleObject savedFrame`
- * argument is non-null, its JSClass is the SavedFrame class (or it is a
- * cross-compartment or Xray wrapper around an object with the SavedFrame class)
- * and the object is not the SavedFrame.prototype object.
- *
- * Each of these functions will find the first SavedFrame object in the chain
- * whose underlying stack frame principals are subsumed by the given
- * |principals|, and operate on that SavedFrame object. This prevents leaking
- * information about privileged frames to un-privileged callers
- *
- * The SavedFrame in parameters do _NOT_ need to be in the same compartment as
- * the cx, and the various out parameters are _NOT_ guaranteed to be in the same
- * compartment as cx.
- *
- * You may consider or skip over self-hosted frames by passing
- * `SavedFrameSelfHosted::Include` or `SavedFrameSelfHosted::Exclude`
- * respectively.
- *
- * Additionally, it may be the case that there is no such SavedFrame object
- * whose captured frame's principals are subsumed by |principals|! If the
- * `HandleObject savedFrame` argument is null, or the |principals| do not
- * subsume any of the chained SavedFrame object's principals,
- * `SavedFrameResult::AccessDenied` is returned and a (hopefully) sane default
- * value is chosen for the out param.
- *
- * See also `js/src/doc/SavedFrame/SavedFrame.md`.
- */
-
-enum class SavedFrameResult {
-    Ok,
-    AccessDenied
-};
-
-enum class SavedFrameSelfHosted {
-    Include,
-    Exclude
-};
-
-/**
- * Given a SavedFrame JSObject, get its source property. Defaults to the empty
- * string.
- */
-extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameSource(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
-                    MutableHandleString sourcep,
-                    SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
-
-/**
- * Given a SavedFrame JSObject, get its line property. Defaults to 0.
- */
-extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameLine(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
-                  uint32_t* linep,
-                  SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
-
-/**
- * Given a SavedFrame JSObject, get its column property. Defaults to 0.
- */
-extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameColumn(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
-                    uint32_t* columnp,
-                    SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
-
-/**
- * Given a SavedFrame JSObject, get its functionDisplayName string, or nullptr
- * if SpiderMonkey was unable to infer a name for the captured frame's
- * function. Defaults to nullptr.
- */
-extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameFunctionDisplayName(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
-                                 MutableHandleString namep,
-                                 SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
-
-/**
- * Given a SavedFrame JSObject, get its asyncCause string. Defaults to nullptr.
- */
-extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameAsyncCause(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
-                        MutableHandleString asyncCausep,
-                        SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
-
-/**
- * Given a SavedFrame JSObject, get its asyncParent SavedFrame object or nullptr
- * if there is no asyncParent. The `asyncParentp` out parameter is _NOT_
- * guaranteed to be in the cx's compartment. Defaults to nullptr.
- */
-extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameAsyncParent(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
-                         MutableHandleObject asyncParentp,
-                         SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
-
-/**
- * Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if
- * it is the oldest frame in the stack. The `parentp` out parameter is _NOT_
- * guaranteed to be in the cx's compartment. Defaults to nullptr.
- */
-extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameParent(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame,
-                    MutableHandleObject parentp,
-                    SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
-
 /**
  * Given a SavedFrame JSObject stack, stringify it in the same format as
  * Error.prototype.stack. The stringified stack out parameter is placed in the
  * cx's compartment. Defaults to the empty string.
  *
  * The same notes above about SavedFrame accessors applies here as well: cx
  * doesn't need to be in stack's compartment, and stack can be null, a
  * SavedFrame object, or a wrapper (CCW or Xray) around a SavedFrame object.
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -18,16 +18,17 @@
 
 #include "jsapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
+#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/ErrorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -12,16 +12,18 @@
 #define jsexn_h
 
 #include "jsapi.h"
 #include "NamespaceImports.h"
 
 #include "js/UniquePtr.h"
 #include "vm/JSContext.h"
 
+class JSAutoByteString;
+
 namespace js {
 class ErrorObject;
 
 UniquePtr<JSErrorNotes::Note>
 CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note);
 
 UniquePtr<JSErrorReport>
 CopyErrorReport(JSContext* cx, JSErrorReport* report);
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -15,16 +15,17 @@
 #ifdef ENABLE_BIGINT
 #include "builtin/BigInt.h"
 #endif
 #include "builtin/Promise.h"
 #include "builtin/TestingFunctions.h"
 #include "gc/GCInternals.h"
 #include "gc/PublicIterators.h"
 #include "gc/WeakMap.h"
+#include "js/AutoByteString.h"
 #include "js/Printf.h"
 #include "js/Proxy.h"
 #include "js/Wrapper.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/Realm.h"
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -8,23 +8,26 @@
 #define jsfriendapi_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/UniquePtr.h"
 
-#include "jsapi.h" // For JSAutoByteString.  See bug 1033916.
 #include "jspubtd.h"
 
+#include "js/AutoByteString.h"
 #include "js/CallArgs.h"
 #include "js/CallNonGenericMethod.h"
+#include "js/CharacterEncoding.h"
 #include "js/Class.h"
+#include "js/ErrorReport.h"
 #include "js/HeapAPI.h"
+#include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 
 #ifndef JS_STACK_GROWTH_DIRECTION
 # ifdef __hppa
 #  define JS_STACK_GROWTH_DIRECTION (1)
 # else
 #  define JS_STACK_GROWTH_DIRECTION (-1)
@@ -33,19 +36,17 @@
 
 #if JS_STACK_GROWTH_DIRECTION > 0
 # define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) < (limit)))
 #else
 # define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) > (limit)))
 #endif
 
 struct JSErrorFormatString;
-class JSLinearString;
 struct JSJitInfo;
-class JSErrorReport;
 
 namespace JS {
 template <class T>
 class Heap;
 } /* namespace JS */
 
 namespace js {
 class JS_FRIEND_API(BaseProxyHandler);
@@ -796,22 +797,16 @@ MOZ_ALWAYS_INLINE size_t
 GetAtomLength(JSAtom* atom)
 {
     return reinterpret_cast<JS::shadow::String*>(atom)->length();
 }