Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Mon, 04 Mar 2019 23:54:12 +0200
changeset 520197 901d4a94863c6782f27412ccfb61ed581295ced8
parent 520196 9a345448aaa1b2424f9f6a2e2768780ac4474bd1 (current diff)
parent 520136 8602628e7edaecadea855a64179b329da6ff1f20 (diff)
child 520198 9635abe5bf24340252ff06d02dae09d3b7d27fd9
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
devtools/client/shared/widgets/BreadcrumbsWidget.jsm
devtools/client/shared/widgets/FastListWidget.js
dom/base/nsContentUtils.cpp
gfx/wr/webrender/src/batch.rs
gfx/wr/webrender/src/intern_types.rs
js/src/builtin/TestingFunctions.cpp
js/src/vm/StructuredClone.cpp
netwerk/test/unit/CA.cert.der
testing/xpcshell/moz-http2/http2-key.pem
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1097,17 +1097,19 @@ nsContextMenu.prototype = {
              windowID, linkDownload, isContentWindowPrivate) {
     // canonical def in nsURILoader.h
     const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020;
 
     // an object to proxy the data through to
     // nsIExternalHelperAppService.doContent, which will wait for the
     // appropriate MIME-type headers and then prompt the user with a
     // file picker
-    function saveAsListener() {}
+    function saveAsListener(principal) {
+      this._triggeringPrincipal = principal;
+    }
     saveAsListener.prototype = {
       extListener: null,
 
       onStartRequest: function saveLinkAs_onStartRequest(aRequest) {
         // if the timer fired, the error status will have been caused by that,
         // and we'll be restarting in onStopRequest, so no reason to notify
         // the user
         if (aRequest.status == NS_ERROR_SAVE_LINK_AS_TIMEOUT)
@@ -1141,17 +1143,17 @@ nsContextMenu.prototype = {
       },
 
       onStopRequest: function saveLinkAs_onStopRequest(aRequest,
                                                        aStatusCode) {
         if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
           // do it the old fashioned way, which will pick the best filename
           // it can without waiting.
           saveURL(linkURL, linkText, dialogTitle, bypassCache, false, docURI,
-                  doc, isContentWindowPrivate);
+                  doc, isContentWindowPrivate, this._triggeringPrincipal);
         }
         if (this.extListener)
           this.extListener.onStopRequest(aRequest, aStatusCode);
       },
 
       onDataAvailable: function saveLinkAs_onDataAvailable(aRequest,
                                                            aInputStream,
                                                            aOffset, aCount) {
@@ -1221,17 +1223,17 @@ nsContextMenu.prototype = {
     // fallback to the old way if we don't see the headers quickly
     var timeToWait =
       Services.prefs.getIntPref("browser.download.saveLinkAsFilenameTimeout");
     var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     timer.initWithCallback(new timerCallback(), timeToWait,
                            timer.TYPE_ONE_SHOT);
 
     // kick off the channel with our proxy object as the listener
-    channel.asyncOpen(new saveAsListener());
+    channel.asyncOpen(new saveAsListener(this.principal));
   },
 
   // Save URL of clicked-on link.
   saveLink() {
     let isContentWindowPrivate = this.isRemote ? this.ownerDoc.isPrivate : undefined;
     this.saveHelper(this.linkURL, this.linkTextStr, null, true, this.ownerDoc,
                     gContextMenuContentData.documentURIObject,
                     this.frameOuterWindowID,
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -17,16 +17,21 @@
 .tab-throbber:not([busy]),
 .tab-icon-sound:not([soundplaying]):not([muted]):not([activemedia-blocked]),
 .tab-icon-sound[pinned],
 .tab-sharing-icon-overlay,
 .tab-icon-overlay {
   display: none;
 }
 
+.tab-icon-pending[preopened],
+.tab-icon-image[preopened] {
+  visibility: hidden;
+}
+
 .tab-sharing-icon-overlay[sharing]:not([selected]),
 .tab-icon-overlay[soundplaying][pinned],
 .tab-icon-overlay[muted][pinned],
 .tab-icon-overlay[activemedia-blocked][pinned],
 .tab-icon-overlay[crashed] {
   display: -moz-box;
 }
 
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -27,16 +27,17 @@ window._gBrowser = {
     Services.els.addSystemEventListener(document, "keydown", this, false);
     if (AppConstants.platform == "macosx") {
       Services.els.addSystemEventListener(document, "keypress", this, false);
     }
     window.addEventListener("sizemodechange", this);
     window.addEventListener("occlusionstatechange", this);
 
     this._setupInitialBrowserAndTab();
+    this._preopenPinnedTabs();
 
     if (Services.prefs.getBoolPref("browser.display.use_system_colors")) {
       this.tabpanels.style.backgroundColor = "-moz-default-background-color";
     } else if (Services.prefs.getIntPref("browser.display.document_color_use") == 2) {
       this.tabpanels.style.backgroundColor =
         Services.prefs.getCharPref("browser.display.background_color");
     }
 
@@ -378,16 +379,39 @@ window._gBrowser = {
     let filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
                   .createInstance(Ci.nsIWebProgress);
     filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
     this._tabListeners.set(tab, tabListener);
     this._tabFilters.set(tab, filter);
     browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
   },
 
+  _preopenPinnedTabs() {
+    let numPinnedTabs = 0;
+    let windows = browserWindows();
+    windows.getNext();
+    let isOnlyWindow = !windows.hasMoreElements();
+    if (isOnlyWindow) {
+      numPinnedTabs = Services.prefs.getIntPref("browser.tabs.firstWindowRestore.numPinnedTabs", 0);
+    }
+
+    for (let i = 0; i < numPinnedTabs; i++) {
+      let tab = this.addTrustedTab("about:blank", {
+        skipAnimation: true,
+        noInitialLabel: true,
+        skipBackgroundNotify: true,
+        createLazyBrowser: true,
+        pinned: true,
+        isForFirstWindowRestore: true,
+      });
+
+      tab.setAttribute("preopened", "true");
+    }
+  },
+
   /**
    * BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
    * MAKE SURE TO ADD IT HERE AS WELL.
    */
   get canGoBack() {
     return this.selectedBrowser.canGoBack;
   },
 
@@ -588,46 +612,66 @@ window._gBrowser = {
   },
 
   _updateTabBarForPinnedTabs() {
     this.tabContainer._unlockTabSizing();
     this.tabContainer._positionPinnedTabs();
     this.tabContainer._updateCloseButtons();
   },
 
-  _notifyPinnedStatus(aTab) {
+  _sendPinnedTabContentMessage(aTab) {
     this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: aTab.pinned });
+  },
+
+  _notifyPinnedStatus(aTab, aDeferContentMessage = false) {
+    if (!aDeferContentMessage) {
+      this._sendPinnedTabContentMessage(aTab);
+    }
 
     let event = document.createEvent("Events");
     event.initEvent(aTab.pinned ? "TabPinned" : "TabUnpinned", true, false);
     aTab.dispatchEvent(event);
   },
 
+  _maybeUpdateNumPinnedTabsPref() {
+    if (BrowserWindowTracker.getTopWindow() == window) {
+      Services.prefs.setIntPref("browser.tabs.firstWindowRestore.numPinnedTabs",
+                                this._numPinnedTabs);
+    }
+  },
+
+  activatePreopenedPinnedTab(aTab) {
+    this._insertBrowser(aTab);
+    this._sendPinnedTabContentMessage(aTab);
+  },
+
   pinTab(aTab) {
     if (aTab.pinned)
       return;
 
     if (aTab.hidden)
       this.showTab(aTab);
 
     this.moveTabTo(aTab, this._numPinnedTabs);
     aTab.setAttribute("pinned", "true");
     this._updateTabBarForPinnedTabs();
     this._notifyPinnedStatus(aTab);
+    this._maybeUpdateNumPinnedTabsPref();
   },
 
   unpinTab(aTab) {
     if (!aTab.pinned)
       return;
 
     this.moveTabTo(aTab, this._numPinnedTabs - 1);
     aTab.removeAttribute("pinned");
     aTab.style.marginInlineStart = "";
     this._updateTabBarForPinnedTabs();
     this._notifyPinnedStatus(aTab);
+    this._maybeUpdateNumPinnedTabsPref();
   },
 
   previewTab(aTab, aCallback) {
     let currentTab = this.selectedTab;
     try {
       // Suppress focus, ownership and selected tab changes
       this._previewMode = true;
       this.selectedTab = aTab;
@@ -2301,16 +2345,17 @@ window._gBrowser = {
     bulkOrderedOpen,
     charset,
     createLazyBrowser,
     eventDetail,
     focusUrlBar,
     forceNotRemote,
     fromExternal,
     index,
+    isForFirstWindowRestore,
     lazyTabTitle,
     name,
     nextTabParentId,
     noInitialLabel,
     noReferrer,
     opener,
     openerBrowser,
     originPrincipal,
@@ -2484,16 +2529,19 @@ window._gBrowser = {
       if (tabAfter) {
         this._updateTabsAfterInsert();
       } else {
         t._tPos = index;
       }
 
       if (pinned) {
         this._updateTabBarForPinnedTabs();
+        if (!isForFirstWindowRestore) {
+          this._maybeUpdateNumPinnedTabsPref();
+        }
       }
       this.tabContainer._setPositionalAttributes();
 
       TabBarVisibility.update();
 
       // If we don't have a preferred remote type, and we have a remote
       // opener, use the opener's remote type.
       if (!preferredRemoteType && openerBrowser) {
@@ -2563,23 +2611,25 @@ window._gBrowser = {
 
         if (lazyBrowserURI) {
           // Lazy browser must be explicitly registered so tab will appear as
           // a switch-to-tab candidate in autocomplete.
           this.UrlbarProviderOpenTabs.registerOpenTab(lazyBrowserURI.spec,
                                                       userContextId);
           b.registeredOpenURI = lazyBrowserURI;
         }
-        SessionStore.setTabState(t, {
-          entries: [{
-            url: lazyBrowserURI ? lazyBrowserURI.spec : "about:blank",
-            title: lazyTabTitle,
-            triggeringPrincipal_base64: E10SUtils.serializePrincipal(triggeringPrincipal),
-          }],
-        });
+        if (!isForFirstWindowRestore) {
+          SessionStore.setTabState(t, {
+            entries: [{
+              url: lazyBrowserURI ? lazyBrowserURI.spec : "about:blank",
+              title: lazyTabTitle,
+              triggeringPrincipal_base64: E10SUtils.serializePrincipal(triggeringPrincipal),
+            }],
+          });
+        }
       } else {
         this._insertBrowser(t, true);
       }
     } catch (e) {
       Cu.reportError("Failed to create tab");
       Cu.reportError(e);
       t.remove();
       if (t.linkedBrowser) {
@@ -2607,17 +2657,19 @@ window._gBrowser = {
       if (!aURIObject ||
           (doGetProtocolFlags(aURIObject) & URI_INHERITS_SECURITY_CONTEXT)) {
         b.createAboutBlankContentViewer(originPrincipal);
       }
     }
 
     // If we didn't swap docShells with a preloaded browser
     // then let's just continue loading the page normally.
-    if (!usingPreloadedContent && (!uriIsAboutBlank || !allowInheritPrincipal)) {
+    if (!usingPreloadedContent &&
+        !createLazyBrowser &&
+        (!uriIsAboutBlank || !allowInheritPrincipal)) {
       // pretend the user typed this so it'll be available till
       // the document successfully loads
       if (aURI && !gInitialPages.includes(aURI)) {
         b.userTypedValue = aURI;
       }
 
       let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
       if (allowThirdPartyFixup) {
@@ -2660,17 +2712,17 @@ window._gBrowser = {
       requestAnimationFrame(function() {
         // kick the animation off
         t.setAttribute("fadein", "true");
       });
     }
 
     // Additionally send pinned tab events
     if (pinned) {
-      this._notifyPinnedStatus(t);
+      this._notifyPinnedStatus(t, isForFirstWindowRestore);
     }
 
     return t;
   },
 
   moveTabsToStart(contextTab) {
     let tabs = contextTab.multiselected ?
       this.selectedTabs :
@@ -3158,18 +3210,20 @@ window._gBrowser = {
     if (aTab.hidden)
       this.tabContainer._updateHiddenTabsStatus();
 
     // ... and fix up the _tPos properties immediately.
     for (let i = aTab._tPos; i < this.tabs.length; i++)
       this.tabs[i]._tPos = i;
 
     if (!this._windowIsClosing) {
-      if (wasPinned)
+      if (wasPinned) {
         this.tabContainer._positionPinnedTabs();
+        this._maybeUpdateNumPinnedTabsPref();
+      }
 
       // update tab close buttons state
       this.tabContainer._updateCloseButtons();
 
       setTimeout(function(tabs) {
         tabs._lastTabClosedByMouse = false;
       }, 0, this.tabContainer);
     }
@@ -3832,17 +3886,16 @@ window._gBrowser = {
     let createLazyBrowser = !aTab.linkedPanel;
     let params = {
       eventDetail: { adoptedTab: aTab },
       preferredRemoteType: linkedBrowser.remoteType,
       sameProcessAsFrameLoader: linkedBrowser.frameLoader,
       skipAnimation: true,
       index: aIndex,
       createLazyBrowser,
-      allowInheritPrincipal: createLazyBrowser,
     };
 
     let numPinned = this._numPinnedTabs;
     if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) {
       params.pinned = true;
     }
 
     if (aTab.hasAttribute("usercontextid")) {
@@ -5009,16 +5062,17 @@ class TabProgressListener {
           // remoteness or is a new tab/window).
           this.mBrowser.urlbarChangeTracker.startedLoad();
         }
         delete this.mBrowser.initialPageLoadedFromUserAction;
         // If the browser is loading it must not be crashed anymore
         this.mTab.removeAttribute("crashed");
       }
 
+      this.mTab.removeAttribute("preopened");
       if (this._shouldShowProgress(aRequest)) {
         if (!(aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING) &&
             aWebProgress && aWebProgress.isTopLevel) {
           this.mTab.setAttribute("busy", "true");
           gBrowser._tabAttrModified(this.mTab, ["busy"]);
           this.mTab._notselectedsinceload = !this.mTab.selected;
           gBrowser.syncThrobberAnimations(this.mTab);
         }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1932,20 +1932,20 @@
                   anonid="tab-loading-burst"
                   class="tab-loading-burst"/>
         <xul:hbox xbl:inherits="pinned,selected=visuallyselected,titlechanged,attention"
                   class="tab-content" align="center">
           <xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected"
                     anonid="tab-throbber"
                     class="tab-throbber"
                     layer="true"/>
-          <xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected,pendingicon"
+          <xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected,pendingicon,preopened"
                     anonid="tab-icon-pending"
                     class="tab-icon-pending"/>
-          <xul:image xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
+          <xul:image xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing,preopened"
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="sharing,selected=visuallyselected,pinned"
                      anonid="sharing-icon"
                      class="tab-sharing-icon-overlay"
                      role="presentation"/>
--- a/browser/base/content/test/general/browser_double_close_tab.js
+++ b/browser/base/content/test/general/browser_double_close_tab.js
@@ -61,17 +61,17 @@ add_task(async function() {
         doCompletion();
       });
       // Click again:
       document.getAnonymousElementByAttribute(testTab, "anonid", "close-button").click();
     });
     // Click once:
     document.getAnonymousElementByAttribute(testTab, "anonid", "close-button").click();
   });
-  await promiseWaitForCondition(() => !testTab.parentNode);
+  await TestUtils.waitForCondition(() => !testTab.parentNode);
   ok(!testTab.parentNode, "Tab should be closed completely");
 });
 
 registerCleanupFunction(function() {
   if (testTab.parentNode) {
     // Remove the handler, or closing this tab will prove tricky:
     try {
       testTab.linkedBrowser.contentWindow.onbeforeunload = null;
--- a/browser/base/content/test/performance/browser.ini
+++ b/browser/base/content/test/performance/browser.ini
@@ -22,17 +22,17 @@ skip-if = asan || debug || (os == 'win' 
 skip-if = !debug
 [browser_startup.js]
 [browser_startup_content.js]
 skip-if = !e10s
 [browser_startup_flicker.js]
 run-if = debug || devedition || nightly_build # Requires startupRecorder.js, which isn't shipped everywhere by default
 [browser_tabclose_grow.js]
 [browser_tabclose.js]
-skip-if = (os == 'win' && bits == 32) || (os == 'mac') # Bug 1488537, Bug 1531417
+skip-if = (os == 'win') || (os == 'mac') # Bug 1488537, Bug 1531417, Bug 1497713
 [browser_tabdetach.js]
 [browser_tabopen.js]
 skip-if = (verify && (os == 'mac'))
 [browser_tabopen_squeeze.js]
 [browser_tabstrip_overflow_underflow.js]
 skip-if = (verify && !debug && (os == 'win')) || (!debug && (os == 'win') && (bits == 32)) # Bug 1502255
 [browser_tabswitch.js]
 [browser_toolbariconcolor_restyles.js]
--- a/browser/base/content/test/permissions/browser_autoplay_blocked.js
+++ b/browser/base/content/test/permissions/browser_autoplay_blocked.js
@@ -124,16 +124,40 @@ add_task(async function testGloballyBloc
 });
 
 add_task(async function testBFCache() {
   Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.BLOCKED);
 
   await BrowserTestUtils.withNewTab("about:home", async function(browser) {
     await BrowserTestUtils.loadURI(browser, AUTOPLAY_PAGE);
     await blockedIconShown(browser);
+
+    gBrowser.goBack();
+    await TestUtils.waitForCondition(() => {
+      return BrowserTestUtils.is_hidden(autoplayBlockedIcon());
+    });
+
+    // Not sure why using `gBrowser.goForward()` doesn't trigger document's
+    // visibility changes in some debug build on try server, which makes us not
+    // to receive the blocked event.
+    await ContentTask.spawn(browser, null, () => {
+      content.history.forward();
+    });
+    await blockedIconShown(browser);
+  });
+
+  Services.perms.removeAll();
+});
+
+add_task(async function testChangingBlockingSettingDuringNavigation() {
+  Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.BLOCKED);
+
+  await BrowserTestUtils.withNewTab("about:home", async function(browser) {
+    await BrowserTestUtils.loadURI(browser, AUTOPLAY_PAGE);
+    await blockedIconShown(browser);
     Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.ALLOWED);
 
     gBrowser.goBack();
     await TestUtils.waitForCondition(() => {
       return BrowserTestUtils.is_hidden(autoplayBlockedIcon());
     });
 
     gBrowser.goForward();
--- a/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js
+++ b/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js
@@ -21,27 +21,27 @@ add_task(async function() {
   // Hide it again.
   document.getElementById("widget-overflow").hidePopup();
 
   let fullscreenButton = document.getElementById("fullscreen-button");
   ok(!fullscreenButton.checked, "Fullscreen button should not be checked when not in fullscreen.");
   ok(!isFullscreenSizeMode(), "Should not be in fullscreen sizemode before we enter fullscreen.");
 
   BrowserFullScreen();
-  await waitForCondition(() => isFullscreenSizeMode());
+  await TestUtils.waitForCondition(() => isFullscreenSizeMode());
   ok(fullscreenButton.checked, "Fullscreen button should be checked when in fullscreen.");
 
   await startCustomizing();
 
   let fullscreenButtonWrapper = document.getElementById("wrapper-fullscreen-button");
   ok(fullscreenButtonWrapper.hasAttribute("itemobserves"), "Observer should be moved to wrapper");
   fullscreenButton = document.getElementById("fullscreen-button");
   ok(!fullscreenButton.hasAttribute("observes"), "Observer should be removed from button");
   ok(!fullscreenButton.checked, "Fullscreen button should no longer be checked during customization mode");
 
   await endCustomizing();
 
   BrowserFullScreen();
   fullscreenButton = document.getElementById("fullscreen-button");
-  await waitForCondition(() => !isFullscreenSizeMode());
+  await TestUtils.waitForCondition(() => !isFullscreenSizeMode());
   ok(!fullscreenButton.checked, "Fullscreen button should not be checked when not in fullscreen.");
   CustomizableUI.reset();
 });
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -244,18 +244,18 @@ var SessionStore = {
   setBrowserState: function ss_setBrowserState(aState) {
     SessionStoreInternal.setBrowserState(aState);
   },
 
   getWindowState: function ss_getWindowState(aWindow) {
     return SessionStoreInternal.getWindowState(aWindow);
   },
 
-  setWindowState: function ss_setWindowState(aWindow, aState, aOverwrite) {
-    SessionStoreInternal.setWindowState(aWindow, aState, aOverwrite);
+  setWindowState: function ss_setWindowState(aWindow, aState, aOverwrite, aFirstWindow) {
+    SessionStoreInternal.setWindowState(aWindow, aState, aOverwrite, aFirstWindow);
   },
 
   getTabState: function ss_getTabState(aTab) {
     return SessionStoreInternal.getTabState(aTab);
   },
 
   setTabState: function ss_setTabState(aTab, aState) {
     SessionStoreInternal.setTabState(aTab, aState);
@@ -726,16 +726,17 @@ var SessionStoreInternal = {
 
     this._max_tabs_undo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
     this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
 
     this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
     this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);
 
     this._restore_on_demand = this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
+    this._restore_pinned_tabs_on_demand = this._prefBranch.getBoolPref("sessionstore.restore_pinned_tabs_on_demand");
     this._prefBranch.addObserver("sessionstore.restore_on_demand", this, true);
 
     gResistFingerprintingEnabled = Services.prefs.getBoolPref("privacy.resistFingerprinting");
     Services.prefs.addObserver("privacy.resistFingerprinting", this);
   },
 
   /**
    * Called on application shutdown, after notifications:
@@ -2494,22 +2495,25 @@ var SessionStoreInternal = {
     if (DyingWindowCache.has(aWindow)) {
       let data = DyingWindowCache.get(aWindow);
       return JSON.stringify({ windows: [data] });
     }
 
     throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
   },
 
-  setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite) {
+  setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite, aFirstWindow) {
     if (!aWindow.__SSi) {
       throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
     }
 
-    this.restoreWindows(aWindow, aState, {overwriteTabs: aOverwrite});
+    this.restoreWindows(aWindow, aState, {
+      overwriteTabs: aOverwrite,
+      firstWindow: aFirstWindow,
+    });
 
     // Notify of changes to closed objects.
     this._notifyOfClosedObjectsChange();
   },
 
   getTabState: function ssi_getTabState(aTab) {
     if (!aTab || !aTab.ownerGlobal) {
       throw Components.Exception("Need a valid tab", Cr.NS_ERROR_INVALID_ARG);
@@ -3198,16 +3202,17 @@ var SessionStoreInternal = {
     } catch (e) {}
 
     // Start the throbber to pretend we're doing something while actually
     // waiting for data from the frame script. This throbber is disabled
     // if the URI is a local about: URI.
     if (!uriObj || (uriObj && !window.gBrowser.isLocalAboutURI(uriObj))) {
       tab.setAttribute("busy", "true");
     }
+    tab.removeAttribute("preopened");
 
     // Hack to ensure that the about:home, about:newtab, and about:welcome
     // favicon is loaded instantaneously, to avoid flickering and improve
     // perceived performance.
     window.gBrowser.setDefaultIcon(tab, uriObj);
 
     TAB_STATE_FOR_BROWSER.set(tab.linkedBrowser, TAB_STATE_WILL_RESTORE);
 
@@ -3591,16 +3596,46 @@ var SessionStoreInternal = {
       if (!winData || !winData.tabs || !winData.tabs[0])
         continue;
       this._openWindowWithState({ windows: [winData] });
     }
     return Promise.all([...WINDOW_SHOWING_PROMISES.values()].map(deferred => deferred.promise));
   },
 
   /**
+   * Handles the pinning / unpinning of a selected tab restored with
+   * restoreWindow.
+   *
+   * @param aWindow
+   *        Window reference to the window used for restoration
+   * @param aWinData
+   *        The window data we're restoring
+   * @param aRestoreIndex
+   *        The index of the tab data we're currently restoring
+   * @returns the selected tab
+   */
+  _updateRestoredSelectedTabPinnedState(aWindow, aWinData, aRestoreIndex) {
+    let tabbrowser = aWindow.gBrowser;
+    let tabData = aWinData.tabs[aRestoreIndex];
+    let tab = tabbrowser.selectedTab;
+    let needsUnpin = tab.pinned && !tabData.pinned;
+    let needsPin = !tab.pinned && tabData.pinned;
+    if (needsUnpin) {
+      tabbrowser.unpinTab(tab);
+    } else if (needsPin && tab == tabbrowser.tabs[aRestoreIndex]) {
+      tabbrowser.pinTab(tab);
+    } else if (needsPin) {
+      tabbrowser.removeTab(tabbrowser.tabs[aRestoreIndex]);
+      tabbrowser.pinTab(tab);
+      tabbrowser.moveTabTo(tab, aRestoreIndex);
+    }
+    return tab;
+  },
+
+  /**
    * restore features to a single window
    * @param aWindow
    *        Window reference to the window to use for restoration
    * @param winData
    *        JS object
    * @param aOptions
    *        {overwriteTabs: true} to overwrite existing tabs w/ new ones
    *        {firstWindow: true} if this is the first non-private window we're
@@ -3645,26 +3680,29 @@ var SessionStoreInternal = {
 
     // disable smooth scrolling while adding, moving, removing and selecting tabs
     let arrowScrollbox = tabbrowser.tabContainer.arrowScrollbox;
     let smoothScroll = arrowScrollbox.smoothScroll;
     arrowScrollbox.smoothScroll = false;
 
     // We need to keep track of the initially open tabs so that they
     // can be moved to the end of the restored tabs.
-    let initialTabs;
+    let initialTabs = [];
     if (!overwriteTabs && firstWindow) {
       initialTabs = Array.slice(tabbrowser.tabs);
     }
 
     // Get rid of tabs that aren't needed anymore.
     if (overwriteTabs) {
       for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) {
-        if (!tabbrowser.tabs[i].selected) {
+        if (!tabbrowser.tabs[i].selected &&
+            !tabbrowser.tabs[i].hasAttribute("preopened")) {
           tabbrowser.removeTab(tabbrowser.tabs[i]);
+        } else if (tabbrowser.tabs[i].hasAttribute("preopened")) {
+          initialTabs.push(tabbrowser.tabs[i]);
         }
       }
     }
 
     let restoreTabsLazily = this._prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") && this._restore_on_demand;
 
     for (var t = 0; t < newTabCount; t++) {
       let tabData = winData.tabs[t];
@@ -3672,24 +3710,28 @@ var SessionStoreInternal = {
       let userContextId = tabData.userContextId;
       let select = t == selectTab - 1;
       let tab;
 
       // Re-use existing selected tab if possible to avoid the overhead of
       // selecting a new tab.
       if (select &&
           tabbrowser.selectedTab.userContextId == userContextId) {
-        tab = tabbrowser.selectedTab;
-        if (!tabData.pinned) {
-          tabbrowser.unpinTab(tab);
-        }
+        tab = this._updateRestoredSelectedTabPinnedState(aWindow, winData, t);
+
         tabbrowser.moveTabToEnd();
         if (aWindow.gMultiProcessBrowser && !tab.linkedBrowser.isRemoteBrowser) {
           tabbrowser.updateBrowserRemoteness(tab.linkedBrowser, true);
         }
+      } else if (tabData.pinned &&
+          tabbrowser.tabs[t] &&
+          tabbrowser.tabs[t].pinned &&
+          !tabbrowser.tabs[t].linkedPanel) {
+        tab = tabbrowser.tabs[t];
+        tabbrowser.activatePreopenedPinnedTab(tab);
       }
 
       // Add a new tab if needed.
       if (!tab) {
         let createLazyBrowser = restoreTabsLazily && !select && !tabData.pinned;
 
         let url = "about:blank";
         if (createLazyBrowser && tabData.entries && tabData.entries.length) {
@@ -3728,21 +3770,24 @@ var SessionStoreInternal = {
       }
 
       if (tabData.pinned) {
         tabbrowser.pinTab(tab);
       }
     }
 
     // Move the originally open tabs to the end.
-    if (initialTabs) {
-      let endPosition = tabbrowser.tabs.length - 1;
-      for (let i = 0; i < initialTabs.length; i++) {
-        tabbrowser.unpinTab(initialTabs[i]);
-        tabbrowser.moveTabTo(initialTabs[i], endPosition);
+    let endPosition = tabbrowser.tabs.length - 1;
+    for (let tab of initialTabs) {
+      if (tab.hasAttribute("preopened") &&
+          !tab.linkedPanel) {
+        tabbrowser.removeTab(tab);
+      } else if (!tab.hasAttribute("preopened")) {
+        tabbrowser.unpinTab(tab);
+        tabbrowser.moveTabTo(tab, endPosition);
       }
     }
 
     // We want to correlate the window with data from the last session, so
     // assign another id if we have one. Otherwise clear so we don't do
     // anything with it.
     delete aWindow.__SS_lastSessionWindowID;
     if (winData.__lastSessionWindowID)
@@ -3993,16 +4038,19 @@ var SessionStoreInternal = {
     if (selectedIndex > -1) {
       this.restoreTab(tabbrowser.selectedTab, aTabData[selectedIndex]);
     }
 
     // Restore all tabs.
     for (let t = 0; t < aTabs.length; t++) {
       if (t != selectedIndex) {
         this.restoreTab(aTabs[t], aTabData[t]);
+        if (this._restore_pinned_tabs_on_demand) {
+          aTabs[t].removeAttribute("preopened");
+        }
       }
     }
   },
 
   // Restores the given tab state for a given tab.
   restoreTab(tab, tabData, options = {}) {
     let browser = tab.linkedBrowser;
 
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -108,16 +108,17 @@ support-files = file_formdata_password.h
 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_preopened_apptabs.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]
 [browser_scrollPositions.js]
 [browser_scrollPositionsReaderMode.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_preopened_apptabs.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
+const PREF_NUM_PINNED_TABS = "browser.tabs.firstWindowRestore.numPinnedTabs";
+
+function muffleMainWindowType() {
+  let oldWinType = document.documentElement.getAttribute("windowtype");
+  // Check if we've already done this to allow calling multiple times:
+  if (oldWinType != "navigator:testrunner") {
+    // Make the main test window not count as a browser window any longer
+    document.documentElement.setAttribute("windowtype", "navigator:testrunner");
+
+    registerCleanupFunction(() => {
+      document.documentElement.setAttribute("windowtype", oldWinType);
+    });
+  }
+}
+
+registerCleanupFunction(function() {
+  Services.prefs.clearUserPref(PREF_NUM_PINNED_TABS);
+});
+
+let testCases = [
+  {numPinnedPref: 5, selected: 3, overrideTabs: false},
+  {numPinnedPref: 5, selected: 3, overrideTabs: true},
+  {numPinnedPref: 5, selected: 1, overrideTabs: false},
+  {numPinnedPref: 5, selected: 1, overrideTabs: true},
+  {numPinnedPref: 3, selected: 3, overrideTabs: false},
+  {numPinnedPref: 3, selected: 3, overrideTabs: true},
+  {numPinnedPref: 3, selected: 1, overrideTabs: false},
+  {numPinnedPref: 3, selected: 1, overrideTabs: true},
+  {numPinnedPref: 1, selected: 3, overrideTabs: false},
+  {numPinnedPref: 1, selected: 3, overrideTabs: true},
+  {numPinnedPref: 1, selected: 1, overrideTabs: false},
+  {numPinnedPref: 1, selected: 1, overrideTabs: true},
+  {numPinnedPref: 0, selected: 3, overrideTabs: false},
+  {numPinnedPref: 0, selected: 3, overrideTabs: true},
+  {numPinnedPref: 0, selected: 1, overrideTabs: false},
+  {numPinnedPref: 0, selected: 1, overrideTabs: true},
+];
+
+for (let {numPinnedPref, selected, overrideTabs} of testCases) {
+  add_task(async function testPrefSynced() {
+    Services.prefs.setIntPref(PREF_NUM_PINNED_TABS, numPinnedPref);
+
+    let state = { windows: [{ tabs: [
+      { entries: [{ url: "http://example.org/#1", triggeringPrincipal_base64 }], pinned: true, userContextId: 0 },
+      { entries: [{ url: "http://example.org/#2", triggeringPrincipal_base64 }], pinned: true, userContextId: 0 },
+      { entries: [{ url: "http://example.org/#3", triggeringPrincipal_base64 }], pinned: true, userContextId: 0 },
+    ], selected }] };
+
+    muffleMainWindowType();
+    let win = await BrowserTestUtils.openNewBrowserWindow();
+    Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, numPinnedPref);
+    Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 1);
+    await setWindowState(win, state, overrideTabs, true);
+    Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
+    Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length,
+                 overrideTabs ? 3 : 4);
+    await BrowserTestUtils.closeWindow(win);
+  });
+}
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -185,18 +185,19 @@ function promiseWindowRestored(win) {
   return new Promise(resolve => win.addEventListener("SSWindowRestored", resolve, {once: true}));
 }
 
 async function setBrowserState(state, win = window) {
   ss.setBrowserState(typeof state != "string" ? JSON.stringify(state) : state);
   await promiseWindowRestored(win);
 }
 
-async function setWindowState(win, state, overwrite = false) {
-  ss.setWindowState(win, typeof state != "string" ? JSON.stringify(state) : state, overwrite);
+async function setWindowState(win, state, overwrite = false, firstWindow = false) {
+  ss.setWindowState(win, typeof state != "string" ? JSON.stringify(state) : state,
+                    overwrite, firstWindow);
   await promiseWindowRestored(win);
 }
 
 /**
  * Wait for a content -> chrome message.
  */
 function promiseContentMessage(browser, name) {
   let mm = browser.messageManager;
--- a/browser/modules/BrowserWindowTracker.jsm
+++ b/browser/modules/BrowserWindowTracker.jsm
@@ -91,16 +91,21 @@ function _trackWindowOrder(window) {
 }
 
 function _untrackWindowOrder(window) {
   let idx = _trackedWindows.indexOf(window);
   if (idx >= 0)
     _trackedWindows.splice(idx, 1);
 }
 
+function _trackPinnedTabs(window) {
+  Services.prefs.setIntPref("browser.tabs.firstWindowRestore.numPinnedTabs",
+                            window.gBrowser._numPinnedTabs);
+}
+
 // Methods that impact a window. Put into single object for organization.
 var WindowHelper = {
   addWindow(window) {
     // Add event listeners
     TAB_EVENTS.forEach(function(event) {
       window.gBrowser.tabContainer.addEventListener(event, _handleEvent);
     });
     WINDOW_EVENTS.forEach(function(event) {
@@ -133,16 +138,17 @@ var WindowHelper = {
 
   onActivate(window) {
     // If this window was the last focused window, we don't need to do anything
     if (window == _trackedWindows[0])
       return;
 
     _untrackWindowOrder(window);
     _trackWindowOrder(window);
+    _trackPinnedTabs(window);
 
     _updateCurrentContentOuterWindowID(window.gBrowser.selectedBrowser);
   },
 };
 
 this.BrowserWindowTracker = {
   /**
    * Get the most recent browser window.
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -180,16 +180,18 @@
   list-style-image: url("chrome://browser/skin/mail.svg");
 }
 
 #pageAction-panel-sendToDevice,
 #pageAction-urlbar-sendToDevice {
   list-style-image: url("chrome://browser/skin/send-to-device.svg");
 }
 
+#pageAction-panel-pinTab:-moz-locale-dir(rtl) > .toolbarbutton-icon,
+#pageAction-urlbar-pinTab:-moz-locale-dir(rtl),
 #pageAction-panel-sendToDevice:-moz-locale-dir(rtl) > .toolbarbutton-icon,
 #pageAction-urlbar-sendToDevice:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
 
 .pageAction-sendToDevice-device[clientType=phone] {
   list-style-image: url("chrome://browser/skin/device-phone.svg");
 }
--- a/devtools/client/debugger/new/src/main.development.js
+++ b/devtools/client/debugger/new/src/main.development.js
@@ -8,17 +8,17 @@ import React from "react";
 import ReactDOM from "react-dom";
 
 import { onConnect } from "./client";
 
 const { bootstrap, L10N } = require("devtools-launchpad");
 
 window.L10N = L10N;
 // $FlowIgnore:
-window.L10N.setBundle(require("../assets/panel/debugger.properties"));
+window.L10N.setBundle(require("../../../locales/en-US/debugger.properties"));
 
 bootstrap(React, ReactDOM).then(connection => {
   onConnect(connection, require("devtools-source-map"), {
     emit: eventName => console.log(`emitted: ${eventName}`),
     openLink: url => {
       const win = window.open(url, "_blank");
       win.focus();
     },
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -3188,18 +3188,18 @@ Toolbox.prototype = {
   get gViewSourceUtils() {
     return this.win.gViewSourceUtils;
   },
 
   /**
    * Opens source in style editor. Falls back to plain "view-source:".
    * @see devtools/client/shared/source-utils.js
    */
-  viewSourceInStyleEditor: function(sourceURL, sourceLine) {
-    return viewSource.viewSourceInStyleEditor(this, sourceURL, sourceLine);
+  viewSourceInStyleEditor: function(sourceURL, sourceLine, sourceColumn) {
+    return viewSource.viewSourceInStyleEditor(this, sourceURL, sourceLine, sourceColumn);
   },
 
   /**
    * Opens source in debugger. Falls back to plain "view-source:".
    * @see devtools/client/shared/source-utils.js
    */
   viewSourceInDebugger: function(sourceURL, sourceLine, sourceId, reason) {
     return viewSource.viewSourceInDebugger(this, sourceURL, sourceLine, sourceId, reason);
--- a/devtools/client/memory/components/DominatorTree.js
+++ b/devtools/client/memory/components/DominatorTree.js
@@ -90,16 +90,17 @@ class DominatorTreeSiblingLinkClass exte
       dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
       dom.span(
         {
           className: "heap-tree-item-field heap-tree-item-name",
           style: { marginInlineStart: depth * TREE_ROW_HEIGHT },
         },
         dom.a(
           {
+            className: "load-more-link",
             onClick: () => onLoadMoreSiblings(item),
           },
           L10N.getStr("tree-item.load-more")
         )
       )
     );
   }
 }
--- a/devtools/client/shared/view-source.js
+++ b/devtools/client/shared/view-source.js
@@ -16,21 +16,21 @@ var { gDevTools } = require("devtools/cl
  *
  * @param {Toolbox} toolbox
  * @param {string} sourceURL
  * @param {number} sourceLine
  *
  * @return {Promise<boolean>}
  */
 exports.viewSourceInStyleEditor = async function(toolbox, sourceURL,
-                                                        sourceLine) {
+                                                        sourceLine, sourceColumn) {
   const panel = await toolbox.loadTool("styleeditor");
 
   try {
-    await panel.selectStyleSheet(sourceURL, sourceLine);
+    await panel.selectStyleSheet(sourceURL, sourceLine, sourceColumn);
     await toolbox.selectTool("styleeditor");
     return true;
   } catch (e) {
     exports.viewSource(toolbox, sourceURL, sourceLine);
     return false;
   }
 };
 
deleted file mode 100644
--- a/devtools/client/shared/widgets/BreadcrumbsWidget.jsm
+++ /dev/null
@@ -1,252 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=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/. */
-"use strict";
-
-const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
-
-const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
-const { ViewHelpers, setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
-const EventEmitter = require("devtools/shared/event-emitter");
-
-this.EXPORTED_SYMBOLS = ["BreadcrumbsWidget"];
-
-/**
- * A breadcrumb-like list of items.
- *
- * Note: this widget should be used in tandem with the WidgetMethods in
- * view-helpers.js.
- *
- * @param Node aNode
- *        The element associated with the widget.
- * @param Object aOptions
- *        - smoothScroll: specifies if smooth scrolling on selection is enabled.
- */
-this.BreadcrumbsWidget = function BreadcrumbsWidget(aNode, aOptions = {}) {
-  this.document = aNode.ownerDocument;
-  this.window = this.document.defaultView;
-  this._parent = aNode;
-
-  // Create an internal arrowscrollbox container.
-  this._list = this.document.createElement("arrowscrollbox");
-  this._list.className = "breadcrumbs-widget-container";
-  this._list.setAttribute("flex", "1");
-  this._list.setAttribute("orient", "horizontal");
-  this._list.setAttribute("clicktoscroll", "true");
-  this._list.setAttribute("smoothscroll", !!aOptions.smoothScroll);
-  this._list.addEventListener("keydown", e => this.emit("keyDown", e));
-  this._list.addEventListener("mousedown", e => this.emit("mousePress", e));
-  this._parent.appendChild(this._list);
-
-  // By default, hide the arrows. We let the arrowscrollbox show them
-  // in case of overflow.
-  this._list._scrollButtonUp.collapsed = true;
-  this._list._scrollButtonDown.collapsed = true;
-  this._list.addEventListener("underflow", this._onUnderflow.bind(this));
-  this._list.addEventListener("overflow", this._onOverflow.bind(this));
-
-  // This widget emits events that can be handled in a MenuContainer.
-  EventEmitter.decorate(this);
-
-  // Delegate some of the associated node's methods to satisfy the interface
-  // required by MenuContainer instances.
-  ViewHelpers.delegateWidgetAttributeMethods(this, aNode);
-  ViewHelpers.delegateWidgetEventMethods(this, aNode);
-};
-
-BreadcrumbsWidget.prototype = {
-  /**
-   * Inserts an item in this container at the specified index.
-   *
-   * @param number aIndex
-   *        The position in the container intended for this item.
-   * @param Node aContents
-   *        The node displayed in the container.
-   * @return Node
-   *         The element associated with the displayed item.
-   */
-  insertItemAt: function(aIndex, aContents) {
-    const list = this._list;
-    const breadcrumb = new Breadcrumb(this, aContents);
-    return list.insertBefore(breadcrumb._target, list.childNodes[aIndex]);
-  },
-
-  /**
-   * Returns the child node in this container situated at the specified index.
-   *
-   * @param number aIndex
-   *        The position in the container intended for this item.
-   * @return Node
-   *         The element associated with the displayed item.
-   */
-  getItemAtIndex: function(aIndex) {
-    return this._list.childNodes[aIndex];
-  },
-
-  /**
-   * Removes the specified child node from this container.
-   *
-   * @param Node aChild
-   *        The element associated with the displayed item.
-   */
-  removeChild: function(aChild) {
-    this._list.removeChild(aChild);
-
-    if (this._selectedItem == aChild) {
-      this._selectedItem = null;
-    }
-  },
-
-  /**
-   * Removes all of the child nodes from this container.
-   */
-  removeAllItems: function() {
-    const list = this._list;
-
-    while (list.hasChildNodes()) {
-      list.firstChild.remove();
-    }
-
-    this._selectedItem = null;
-  },
-
-  /**
-   * Gets the currently selected child node in this container.
-   * @return Node
-   */
-  get selectedItem() {
-    return this._selectedItem;
-  },
-
-  /**
-   * Sets the currently selected child node in this container.
-   * @param Node aChild
-   */
-  set selectedItem(aChild) {
-    const childNodes = this._list.childNodes;
-
-    if (!aChild) {
-      this._selectedItem = null;
-    }
-    for (const node of childNodes) {
-      if (node == aChild) {
-        node.setAttribute("checked", "");
-        this._selectedItem = node;
-      } else {
-        node.removeAttribute("checked");
-      }
-    }
-  },
-
-  /**
-   * Returns the value of the named attribute on this container.
-   *
-   * @param string aName
-   *        The name of the attribute.
-   * @return string
-   *         The current attribute value.
-   */
-  getAttribute: function(aName) {
-    if (aName == "scrollPosition") {
-      return this._list.scrollPosition;
-    }
-    if (aName == "scrollWidth") {
-      return this._list.scrollWidth;
-    }
-    return this._parent.getAttribute(aName);
-  },
-
-  /**
-   * Ensures the specified element is visible.
-   *
-   * @param Node aElement
-   *        The element to make visible.
-   */
-  ensureElementIsVisible: function(aElement) {
-    if (!aElement) {
-      return;
-    }
-
-    // Repeated calls to ensureElementIsVisible would interfere with each other
-    // and may sometimes result in incorrect scroll positions.
-    setNamedTimeout("breadcrumb-select", ENSURE_SELECTION_VISIBLE_DELAY, () => {
-      if (this._list.ensureElementIsVisible) {
-        this._list.ensureElementIsVisible(aElement);
-      }
-    });
-  },
-
-  /**
-   * The underflow and overflow listener for the arrowscrollbox container.
-   */
-  _onUnderflow: function({ target }) {
-    if (target != this._list) {
-      return;
-    }
-    target._scrollButtonUp.collapsed = true;
-    target._scrollButtonDown.collapsed = true;
-    target.removeAttribute("overflows");
-  },
-
-  /**
-   * The underflow and overflow listener for the arrowscrollbox container.
-   */
-  _onOverflow: function({ target }) {
-    if (target != this._list) {
-      return;
-    }
-    target._scrollButtonUp.collapsed = false;
-    target._scrollButtonDown.collapsed = false;
-    target.setAttribute("overflows", "");
-  },
-
-  window: null,
-  document: null,
-  _parent: null,
-  _list: null,
-  _selectedItem: null,
-};
-
-/**
- * A Breadcrumb constructor for the BreadcrumbsWidget.
- *
- * @param BreadcrumbsWidget aWidget
- *        The widget to contain this breadcrumb.
- * @param Node aContents
- *        The node displayed in the container.
- */
-function Breadcrumb(aWidget, aContents) {
-  this.document = aWidget.document;
-  this.window = aWidget.window;
-  this.ownerView = aWidget;
-
-  this._target = this.document.createElement("hbox");
-  this._target.className = "breadcrumbs-widget-item";
-  this._target.setAttribute("align", "center");
-  this.contents = aContents;
-}
-
-Breadcrumb.prototype = {
-  /**
-   * Sets the contents displayed in this item's view.
-   *
-   * @param string | Node aContents
-   *        The string or node displayed in the container.
-   */
-  set contents(aContents) {
-    // If there are already some contents displayed, replace them.
-    if (this._target.hasChildNodes()) {
-      this._target.replaceChild(aContents, this._target.firstChild);
-      return;
-    }
-    // These are the first contents ever displayed.
-    this._target.appendChild(aContents);
-  },
-
-  window: null,
-  document: null,
-  ownerView: null,
-  _target: null,
-};
deleted file mode 100644
--- a/devtools/client/shared/widgets/FastListWidget.js
+++ /dev/null
@@ -1,247 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=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/. */
-"use strict";
-
-const EventEmitter = require("devtools/shared/event-emitter");
-const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
-
-/**
- * A list menu widget that attempts to be very fast.
- *
- * Note: this widget should be used in tandem with the WidgetMethods in
- * view-helpers.js.
- *
- * @param Node aNode
- *        The element associated with the widget.
- */
-const FastListWidget = module.exports = function FastListWidget(node) {
-  this.document = node.ownerDocument;
-  this.window = this.document.defaultView;
-  this._parent = node;
-  this._fragment = this.document.createDocumentFragment();
-
-  // This is a prototype element that each item added to the list clones.
-  this._templateElement = this.document.createElement("hbox");
-
-  // Create an internal scrollbox container.
-  this._list = this.document.createElement("scrollbox");
-  this._list.className = "fast-list-widget-container theme-body";
-  this._list.setAttribute("flex", "1");
-  this._list.setAttribute("orient", "vertical");
-  this._list.setAttribute("tabindex", "0");
-  this._list.addEventListener("keydown", e => this.emit("keyDown", e));
-  this._list.addEventListener("mousedown", e => this.emit("mousePress", e));
-  this._parent.appendChild(this._list);
-
-  this._orderedMenuElementsArray = [];
-  this._itemsByElement = new Map();
-
-  // This widget emits events that can be handled in a MenuContainer.
-  EventEmitter.decorate(this);
-
-  // Delegate some of the associated node's methods to satisfy the interface
-  // required by MenuContainer instances.
-  ViewHelpers.delegateWidgetAttributeMethods(this, node);
-  ViewHelpers.delegateWidgetEventMethods(this, node);
-};
-
-FastListWidget.prototype = {
-  /**
-   * Inserts an item in this container at the specified index, optionally
-   * grouping by name.
-   *
-   * @param number aIndex
-   *        The position in the container intended for this item.
-   * @param Node aContents
-   *        The node to be displayed in the container.
-   * @param Object aAttachment [optional]
-   *        Extra data for the user.
-   * @return Node
-   *         The element associated with the displayed item.
-   */
-  insertItemAt: function(index, contents, attachment = {}) {
-    const element = this._templateElement.cloneNode();
-    element.appendChild(contents);
-
-    if (index >= 0) {
-      throw new Error("FastListWidget only supports appending items.");
-    }
-
-    this._fragment.appendChild(element);
-    this._orderedMenuElementsArray.push(element);
-    this._itemsByElement.set(element, this);
-
-    return element;
-  },
-
-  /**
-   * This is a non-standard widget implementation method. When appending items,
-   * they are queued in a document fragment. This method appends the document
-   * fragment to the dom.
-   */
-  flush: function() {
-    this._list.appendChild(this._fragment);
-  },
-
-  /**
-   * Removes all of the child nodes from this container.
-   */
-  removeAllItems: function() {
-    const list = this._list;
-
-    while (list.hasChildNodes()) {
-      list.firstChild.remove();
-    }
-
-    this._selectedItem = null;
-
-    this._orderedMenuElementsArray.length = 0;
-    this._itemsByElement.clear();
-  },
-
-  /**
-   * Remove the given item.
-   */
-  removeChild: function(child) {
-    throw new Error("Not yet implemented");
-  },
-
-  /**
-   * Gets the currently selected child node in this container.
-   * @return Node
-   */
-  get selectedItem() {
-    return this._selectedItem;
-  },
-
-  /**
-   * Sets the currently selected child node in this container.
-   * @param Node child
-   */
-  set selectedItem(child) {
-    const menuArray = this._orderedMenuElementsArray;
-
-    if (!child) {
-      this._selectedItem = null;
-    }
-    for (const node of menuArray) {
-      if (node == child) {
-        node.classList.add("selected");
-        this._selectedItem = node;
-      } else {
-        node.classList.remove("selected");
-      }
-    }
-
-    this.ensureElementIsVisible(this.selectedItem);
-  },
-
-  /**
-   * Returns the child node in this container situated at the specified index.
-   *
-   * @param number index
-   *        The position in the container intended for this item.
-   * @return Node
-   *         The element associated with the displayed item.
-   */
-  getItemAtIndex: function(index) {
-    return this._orderedMenuElementsArray[index];
-  },
-
-  /**
-   * Adds a new attribute or changes an existing attribute on this container.
-   *
-   * @param string name
-   *        The name of the attribute.
-   * @param string value
-   *        The desired attribute value.
-   */
-  setAttribute: function(name, value) {
-    this._parent.setAttribute(name, value);
-
-    if (name == "emptyText") {
-      this._textWhenEmpty = value;
-    }
-  },
-
-  /**
-   * Removes an attribute on this container.
-   *
-   * @param string name
-   *        The name of the attribute.
-   */
-  removeAttribute: function(name) {
-    this._parent.removeAttribute(name);
-
-    if (name == "emptyText") {
-      this._removeEmptyText();
-    }
-  },
-
-  /**
-   * Ensures the specified element is visible.
-   *
-   * @param Node element
-   *        The element to make visible.
-   */
-  ensureElementIsVisible: function(element) {
-    if (!element) {
-      return;
-    }
-
-    // Ensure the element is visible but not scrolled horizontally.
-    element.scrollIntoView({ block: "nearest" });
-    this._list.scrollBy(-this._list.clientWidth, 0);
-  },
-
-  /**
-   * Sets the text displayed in this container when empty.
-   * @param string aValue
-   */
-  set _textWhenEmpty(value) {
-    if (this._emptyTextNode) {
-      this._emptyTextNode.setAttribute("value", value);
-    }
-    this._emptyTextValue = value;
-    this._showEmptyText();
-  },
-
-  /**
-   * Creates and appends a label signaling that this container is empty.
-   */
-  _showEmptyText: function() {
-    if (this._emptyTextNode || !this._emptyTextValue) {
-      return;
-    }
-    const label = this.document.createElement("label");
-    label.className = "plain fast-list-widget-empty-text";
-    label.setAttribute("value", this._emptyTextValue);
-
-    this._parent.insertBefore(label, this._list);
-    this._emptyTextNode = label;
-  },
-
-  /**
-   * Removes the label signaling that this container is empty.
-   */
-  _removeEmptyText: function() {
-    if (!this._emptyTextNode) {
-      return;
-    }
-    this._parent.removeChild(this._emptyTextNode);
-    this._emptyTextNode = null;
-  },
-
-  window: null,
-  document: null,
-  _parent: null,
-  _list: null,
-  _selectedItem: null,
-  _orderedMenuElementsArray: null,
-  _itemsByElement: null,
-  _emptyTextNode: null,
-  _emptyTextValue: "",
-};
--- a/devtools/client/shared/widgets/moz.build
+++ b/devtools/client/shared/widgets/moz.build
@@ -6,21 +6,19 @@
 
 DIRS += [
     'tooltip',
 ]
 
 DevToolsModules(
     'AbstractTreeItem.jsm',
     'BarGraphWidget.js',
-    'BreadcrumbsWidget.jsm',
     'Chart.js',
     'CubicBezierPresets.js',
     'CubicBezierWidget.js',
-    'FastListWidget.js',
     'FilterWidget.js',
     'FlameGraph.js',
     'Graphs.js',
     'GraphsWorker.js',
     'LineGraphWidget.js',
     'MountainGraphWidget.js',
     'ShapesInContextEditor.js',
     'SideMenuWidget.jsm',
--- a/devtools/client/shared/widgets/widgets.css
+++ b/devtools/client/shared/widgets/widgets.css
@@ -8,22 +8,16 @@
 .breadcrumbs-widget-item {
   direction: ltr;
 }
 
 .breadcrumbs-widget-item {
   -moz-user-focus: normal;
 }
 
-/* FastListWidget */
-
-.fast-list-widget-container {
-  overflow: auto;
-}
-
 /* SideMenuWidget */
 
 .side-menu-widget-container {
   overflow-x: hidden;
   overflow-y: auto;
 }
 
 .side-menu-widget-item-contents {
--- a/devtools/client/themes/memory.css
+++ b/devtools/client/themes/memory.css
@@ -4,24 +4,28 @@
 
 /* CSS Variables specific to this panel that aren't defined by the themes */
 .theme-dark {
   --cell-border-color: rgba(255,255,255,0.15);
   --cell-border-color-light: rgba(255,255,255,0.1);
   --focus-cell-border-color: rgba(255,255,255,0.5);
   --row-alt-background-color: rgba(86, 117, 185, 0.15);
   --row-hover-background-color: rgba(86, 117, 185, 0.25);
+  --link-color: var(--blue-40);
+  --link-color-active: var(--blue-30);
 }
 
 .theme-light {
   --cell-border-color: rgba(0,0,0,0.15);
   --cell-border-color-light: rgba(0,0,0,0.1);
   --focus-cell-border-color: rgba(0,0,0,0.3);
   --row-alt-background-color: rgba(76,158,217,0.1);
   --row-hover-background-color: rgba(76,158,217,0.2);
+  --link-color: var(--blue-60);
+  --link-color-active: var(--blue-70);
 }
 
 html, body, #app, #memory-tool {
   height: 100%;
 }
 
 #memory-tool {
   /**
@@ -273,17 +277,17 @@ html, body, #app, #memory-tool {
    */
   min-width: 0;
 }
 
 #heap-view {
   /**
    * Flex: contains a .heap-view-panel which needs to fill out all the
    * available space, horizontally and vertically.
-   */;
+   */
   display: flex;
   /**
    * Flexing to fill out remaining horizontal space. The preceeding sibling
    * is the sidebar. @see #memory-tool-container.
    */
   flex: 1;
   background-color: var(--theme-body-background);
 
@@ -545,16 +549,29 @@ html, body, #app, #memory-tool {
 
 .tree-map-container {
   width: 100%;
   height: 100%;
   position: relative;
   overflow: hidden;
 }
 
+.load-more-link {
+  cursor: pointer;
+  color: var(--link-color);
+}
+
+.load-more-link:hover {
+  text-decoration: underline;
+}
+
+.load-more-link:active {
+  color: var(--link-color-active);
+}
+
 /**
  * Heap tree errors.
  */
 
 .error::before {
   content: "";
   display: inline-block;
   vertical-align: -2px;
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -958,22 +958,28 @@ body {
 
 /* Apply a style similar to collapse-button for the object tree arrows */
 .webconsole-app .tree .arrow,
 .webconsole-app .object-inspector .tree-node .arrow {
   width: 10px;
   height: 10px;
   background: url("chrome://devtools/skin/images/arrow.svg") no-repeat center;
   background-size: 10px;
+  transform: rotate(-90deg);
   /* Needed for alignment */
   margin-top: -1px;
   -moz-context-properties: fill;
   fill: var(--theme-icon-dimmed-color);
 }
 
+.webconsole-app .tree .arrow.expanded,
+.webconsole-app .object-inspector .tree-node .arrow.expanded {
+  transform: rotate(0deg);
+}
+
 /* Sidebar */
 .sidebar {
   display: flex;
   grid-row: 1 / -1;
   grid-column: -1 / -2;
   background-color: var(--theme-sidebar-background);
   border-inline-start: 1px solid var(--theme-splitter-color);
 }
--- a/devtools/client/themes/widgets.css
+++ b/devtools/client/themes/widgets.css
@@ -96,31 +96,16 @@
     /* To hide generic-toggled-pane, negative margins are applied dynamically.
      * If a vertical layout, the pane is on the bottom and should be hidden
      * using negative bottom margin only.
      */
     margin-inline-end: 0 !important;
   }
 }
 
-/* FastListWidget */
-
-.fast-list-widget-container {
-  /* Hack: force hardware acceleration */
-  transform: translateZ(1px);
-}
-
-.fast-list-widget-empty-text {
-  padding: 4px 8px;
-}
-
-.fast-list-widget-empty-text {
-  color: var(--theme-body-color-alt);
-}
-
 /* SideMenuWidget */
 
 .side-menu-widget-container {
   /* Hack: force hardware acceleration */
   transform: translateZ(1px);
 }
 
 /* SideMenuWidget container */
--- a/devtools/client/webconsole/test/mochitest/browser_console_context_menu_entries.js
+++ b/devtools/client/webconsole/test/mochitest/browser_console_context_menu_entries.js
@@ -77,16 +77,31 @@ async function performTests() {
     "#editmenu-copy (editmenu-copy)",
     "#editmenu-paste (editmenu-paste)",
     "#editmenu-delete (editmenu-delete) [disabled]",
     "#editmenu-selectAll (editmenu-select-all) [disabled]",
   ];
   is(getL10NContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
     "The context menu has the correct edit menu items");
 
+  const node = hud.jsterm.inputNode || hud.jsterm.node;
+  const inputContainer = node.closest(".jsterm-input-container");
+  await openContextMenu(hud, inputContainer);
+
+  expectedContextMenu = [
+    "#editmenu-undo (editmenu-undo) [disabled]",
+    "#editmenu-cut (editmenu-cut)",
+    "#editmenu-copy (editmenu-copy)",
+    "#editmenu-paste (editmenu-paste)",
+    "#editmenu-delete (editmenu-delete) [disabled]",
+    "#editmenu-selectAll (editmenu-select-all) [disabled]",
+  ];
+  is(getL10NContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
+    "The context menu has the required elements");
+
   await hideContextMenu(hud);
   // Close the browser console.
   await HUDService.toggleBrowserConsole();
 }
 
 function addPrefBasedEntries(expectedEntries) {
   if (Services.prefs.getBoolPref("devtools.webconsole.sidebarToggle", false)) {
     expectedEntries.push("#console-menu-open-sidebar (V) [disabled]");
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_context_menu_copy_link_location.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_context_menu_copy_link_location.js
@@ -18,28 +18,39 @@ add_task(async function() {
   await pushPref("devtools.webconsole.filter.net", true);
 
   const hud = await openNewTabAndConsole(TEST_URI);
   hud.ui.clearOutput();
 
   info("Test Copy URL menu item for text log");
 
   info("Logging a text message in the content window");
-  const onLogMessage = waitForMessage(hud, "simple text message");
+  const onLogMessage = waitForMessage(hud, "stringLog");
   await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
-    content.wrappedJSObject.console.log("simple text message");
+    content.wrappedJSObject.stringLog();
   });
   let message = await onLogMessage;
   ok(message, "Text log found in the console");
 
   info("Open and check the context menu for the logged text message");
   let menuPopup = await openContextMenu(hud, message.node);
+
   let copyURLItem = menuPopup.querySelector(CONTEXT_MENU_ID);
   ok(!copyURLItem, "Copy URL menu item is hidden for a simple text message");
 
+  info("Open and check the context menu for the logged text message");
+  const locationElement = message.node.querySelector(".frame-link-source-inner");
+  menuPopup = await openContextMenu(hud, locationElement);
+  copyURLItem = menuPopup.querySelector(CONTEXT_MENU_ID);
+  ok(copyURLItem, "The Copy Link Location entry is displayed");
+
+  info("Click on Copy URL menu item and wait for clipboard to be updated");
+  await waitForClipboardPromise(() => copyURLItem.click(), TEST_URI);
+  ok(true, "Expected text was copied to the clipboard.");
+
   await hideContextMenu(hud);
   hud.ui.clearOutput();
 
   info("Test Copy URL menu item for network log");
 
   info("Reload the content window to produce a network log");
   const onNetworkMessage = waitForMessage(hud, "test-console.html");
   await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_location_styleeditor_link.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_location_styleeditor_link.js
@@ -36,21 +36,22 @@ async function testViewSource(hud, toolb
   const panel = await onStyleEditorSelected;
   ok(true, "The style editor is selected when clicking on the location element");
 
   await onStyleEditorReady(panel);
 
   info("style editor window focused");
   const href = frameLinkNode.getAttribute("data-url");
   const line = frameLinkNode.getAttribute("data-line");
+  const column = frameLinkNode.getAttribute("data-column");
   ok(line, "found source line");
 
   const editor = getEditorForHref(panel.UI, href);
   ok(editor, "found style editor for " + href);
-  await performLineCheck(panel.UI, editor, line - 1);
+  await checkCursorPosition(panel.UI, editor, line - 1, column - 1);
 }
 
 async function onStyleEditorReady(panel) {
   const win = panel.panelWindow;
   ok(win, "Style Editor Window is defined");
   ok(panel.UI, "Style Editor UI is defined");
 
   info("Waiting the style editor to be focused");
@@ -67,18 +68,20 @@ function getEditorForHref(styleEditorUI,
     if (editor.styleSheet.href == href) {
       foundEditor = editor;
       break;
     }
   }
   return foundEditor;
 }
 
-async function performLineCheck(styleEditorUI, editor, line) {
+async function checkCursorPosition(styleEditorUI, editor, line, column) {
   info("wait for source editor to load");
   // Get out of the styleeditor-selected event loop.
   await waitForTick();
 
   is(editor.sourceEditor.getCursor().line, line,
      "correct line is selected");
+  is(editor.sourceEditor.getCursor().ch, column,
+     "correct column is selected");
   is(styleEditorUI.selectedStyleSheetIndex, editor.styleSheet.styleSheetIndex,
      "correct stylesheet is selected in the editor");
 }
--- a/devtools/client/webconsole/utils/context-menu.js
+++ b/devtools/client/webconsole/utils/context-menu.js
@@ -43,16 +43,17 @@ function createContextMenu(webConsoleUI,
   clipboardText,
   variableText,
   message,
   serviceContainer,
   openSidebar,
   rootActorId,
   executionPoint,
   toolbox,
+  url,
 }) {
   const win = parentNode.ownerDocument.defaultView;
   const selection = win.getSelection();
 
   const { source, request } = message || {};
 
   const menu = new Menu({
     id: "webconsole-menu",
@@ -202,12 +203,21 @@ function createContextMenu(webConsoleUI,
       disabled: false,
       click: () => {
         const threadClient = toolbox.threadClient;
         threadClient.timeWarp(executionPoint);
       },
     }));
   }
 
+  if (url) {
+    menu.append(new MenuItem({
+      id: "console-menu-copy-url",
+      label: l10n.getStr("webconsole.menu.copyURL.label"),
+      accesskey: l10n.getStr("webconsole.menu.copyURL.accesskey"),
+      click: () => clipboardHelper.copyString(url),
+    }));
+  }
+
   return menu;
 }
 
 exports.createContextMenu = createContextMenu;
--- a/devtools/client/webconsole/webconsole-wrapper.js
+++ b/devtools/client/webconsole/webconsole-wrapper.js
@@ -193,16 +193,19 @@ class WebConsoleWrapper {
       // is available in the current scope and we can pass it into
       // `createContextMenu` method.
       serviceContainer.openContextMenu = (e, message) => {
         const { screenX, screenY, target } = e;
 
         const messageEl = target.closest(".message");
         const clipboardText = getElementText(messageEl);
 
+        const linkEl = target.closest("a[href]");
+        const url = linkEl && linkEl.href;
+
         const messageVariable = target.closest(".objectBox");
         // Ensure that console.group and console.groupCollapsed commands are not captured
         const variableText = (messageVariable
           && !(messageEl.classList.contains("startGroup"))
           && !(messageEl.classList.contains("startGroupCollapsed")))
             ? messageVariable.textContent : null;
 
         // Retrieve closes actor id from the DOM.
@@ -228,16 +231,17 @@ class WebConsoleWrapper {
           clipboardText,
           variableText,
           message,
           serviceContainer,
           openSidebar,
           rootActorId,
           executionPoint,
           toolbox: this.toolbox,
+          url,
         });
 
         // Emit the "menu-open" event for testing.
         menu.once("open", () => this.emit("menu-open"));
         menu.popup(screenX, screenY, { doc: this.hud.chromeWindow.document });
 
         return menu;
       };
@@ -274,17 +278,18 @@ class WebConsoleWrapper {
             frame.line
           ).then(() => {
             this.telemetry.recordEvent("jump_to_source", "webconsole",
                                        null, { "session_id": this.toolbox.sessionId }
             );
           }),
           onViewSourceInStyleEditor: frame => this.toolbox.viewSourceInStyleEditor(
             frame.url,
-            frame.line
+            frame.line,
+            frame.column
           ).then(() => {
             this.telemetry.recordEvent("jump_to_source", "webconsole",
                                        null, { "session_id": this.toolbox.sessionId }
             );
           }),
           openNetworkPanel: (requestId) => {
             return this.toolbox.selectTool("netmonitor").then((panel) => {
               return panel.panelWin.Netmonitor.inspectRequest(requestId);
--- a/devtools/server/actors/animation.js
+++ b/devtools/server/actors/animation.js
@@ -687,37 +687,16 @@ exports.AnimationsActor = protocol.Actor
    * client doesn't want this to happen anymore, it should call this method.
    */
   stopAnimationPlayerUpdates: function() {
     if (this.observer && !Cu.isDeadWrapper(this.observer)) {
       this.observer.disconnect();
     }
   },
 
-  /**
-   * Iterates through all nodes below a given rootNode (optionally also in
-   * nested frames) and finds all existing animation players.
-   * @param {DOMNode} rootNode The root node to start iterating at. Animation
-   * players will *not* be reported for this node.
-   * @param {Boolean} traverseFrames Whether we should iterate through nested
-   * frames too.
-   * @return {Array} An array of AnimationPlayer objects.
-   */
-  getAllAnimations: function(rootNode, traverseFrames) {
-    if (!traverseFrames) {
-      return rootNode.getAnimations({subtree: true});
-    }
-
-    let animations = [];
-    for (const {document} of this.targetActor.windows) {
-      animations = [...animations, ...document.getAnimations({subtree: true})];
-    }
-    return animations;
-  },
-
   onWillNavigate: function({isTopLevel}) {
     if (isTopLevel) {
       this.stopAnimationPlayerUpdates();
     }
   },
 
   onNavigate: function({isTopLevel}) {
     if (isTopLevel) {
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -3866,17 +3866,17 @@ Element* Document::FindContentForSubDocu
 bool Document::IsNodeOfType(uint32_t aFlags) const { return false; }
 
 Element* Document::GetRootElement() const {
   return (mCachedRootElement && mCachedRootElement->GetParentNode() == this)
              ? mCachedRootElement
              : GetRootElementInternal();
 }
 
-nsIContent* Document::GetUnfocusedKeyEventTarget() { return GetRootElement(); }
+Element* Document::GetUnfocusedKeyEventTarget() { return GetRootElement(); }
 
 Element* Document::GetRootElementInternal() const {
   // We invoke GetRootElement() immediately before the servo traversal, so we
   // should always have a cache hit from Servo.
   MOZ_ASSERT(NS_IsMainThread());
 
   // Loop backwards because any non-elements, such as doctypes and PIs
   // are likely to appear before the root element.
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -1337,17 +1337,17 @@ class Document : public nsINode,
       mozilla::ErrorResult& aRv);
   already_AddRefed<mozilla::dom::Promise> RequestStorageAccess(
       mozilla::ErrorResult& aRv);
 
   /**
    * Gets the event target to dispatch key events to if there is no focused
    * content in the document.
    */
-  virtual nsIContent* GetUnfocusedKeyEventTarget();
+  virtual Element* GetUnfocusedKeyEventTarget();
 
   /**
    * Retrieve information about the viewport as a data structure.
    * This will return information in the viewport META data section
    * of the document. This can be used in lieu of ProcessViewportInfo(),
    * which places the viewport information in the document header instead
    * of returning it directly.
    *
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7504,30 +7504,30 @@ void nsContentUtils::TransferableToIPCTr
 
         Shmem dataAsShmem = ConvertToShmem(aChild, aParent, dataAsString);
         if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
           continue;
         }
 
         IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
         item->flavor() = flavorStr;
-        item->data() = dataAsShmem;
+        item->data() = std::move(dataAsShmem);
       } else if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
         // Images to be pasted on the clipboard are nsIInputStreams
         nsCString imageData;
         NS_ConsumeStream(stream, UINT32_MAX, imageData);
 
         Shmem imageDataShmem = ConvertToShmem(aChild, aParent, imageData);
         if (!imageDataShmem.IsReadable() || !imageDataShmem.Size<char>()) {
           continue;
         }
 
         IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
         item->flavor() = flavorStr;
-        item->data() = imageDataShmem;
+        item->data() = std::move(imageDataShmem);
       } else if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
         // Images to be placed on the clipboard are imgIContainers.
         RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
             imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
         if (!surface) {
           continue;
         }
         RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
@@ -7545,17 +7545,17 @@ void nsContentUtils::TransferableToIPCTr
 
         if (surfaceData.isNothing()) {
           continue;
         }
 
         IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
         item->flavor() = flavorStr;
         // Turn item->data() into an nsCString prior to accessing it.
-        item->data() = surfaceData.ref();
+        item->data() = std::move(surfaceData.ref());
 
         IPCDataTransferImage& imageDetails = item->imageDetails();
         mozilla::gfx::IntSize size = dataSurface->GetSize();
         imageDetails.width() = size.width;
         imageDetails.height() = size.height;
         imageDetails.stride() = stride;
         imageDetails.format() = dataSurface->GetFormat();
       } else {
@@ -7576,17 +7576,17 @@ void nsContentUtils::TransferableToIPCTr
               Shmem dataAsShmem = ConvertToShmem(aChild, aParent, data);
               if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
                 continue;
               }
 
               IPCDataTransferItem* item =
                   aIPCDataTransfer->items().AppendElement();
               item->flavor() = type;
-              item->data() = dataAsShmem;
+              item->data() = std::move(dataAsShmem);
             }
 
             continue;
           }
 
           if (aParent) {
             bool isDir = false;
             if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -391,21 +391,31 @@ nsresult nsFrameLoader::ReallyStartLoadi
   // is very important; needed to prevent XSS attacks on documents loaded in
   // subframes!
   if (mTriggeringPrincipal) {
     loadState->SetTriggeringPrincipal(mTriggeringPrincipal);
   } else {
     loadState->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
   }
 
-  // Currently we query the CSP from the principal, but after
-  // Bug 1529877 we should query the CSP from within GetURL and
-  // store it as a member, similar to mTriggeringPrincipal.
+  // Expanded Principals override the CSP of the document, hence we first check
+  // if the triggeringPrincipal overrides the document's principal. If so, let's
+  // query the CSP from that Principal, otherwise we use the document's CSP.
+  // Note that even after Bug 965637, Expanded Principals will hold their own
+  // CSP.
   nsCOMPtr<nsIContentSecurityPolicy> csp;
-  loadState->TriggeringPrincipal()->GetCsp(getter_AddRefs(csp));
+  if (BasePrincipal::Cast(loadState->TriggeringPrincipal())
+          ->OverridesCSP(mOwnerContent->NodePrincipal())) {
+    loadState->TriggeringPrincipal()->GetCsp(getter_AddRefs(csp));
+  } else {
+    // Currently the NodePrincipal holds the CSP for a document. After
+    // Bug 965637 we can query the CSP from mOwnerContent->OwnerDoc()
+    // instead of mOwnerContent->NodePrincipal().
+    mOwnerContent->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+  }
   loadState->SetCsp(csp);
 
   nsCOMPtr<nsIURI> referrer;
 
   nsAutoString srcdoc;
   bool isSrcdoc =
       mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
       mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc, srcdoc);
new file mode 100644
--- /dev/null
+++ b/dom/base/test/iframe_shared_compartment2a.html
@@ -0,0 +1,2 @@
+<iframe src="http://mochi.test:8888/tests/dom/base/test/iframe_shared_compartment2b.html">
+</iframe>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/iframe_shared_compartment2b.html
@@ -0,0 +1,3 @@
+<script>
+window.onload = () => window.parent.parent.go(this);
+</script>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -225,16 +225,18 @@ support-files =
   iframe2_bug431701.html
   iframe3_bug431701.html
   iframe4_bug431701.xml
   iframe5_bug431701.xml
   iframe6_bug431701.xml
   iframe7_bug431701.xml
   iframe1_bug426646.html
   iframe2_bug426646.html
+  iframe_shared_compartment2a.html
+  iframe_shared_compartment2b.html
   file1_setting_opener.html
   file2_setting_opener.html
   file3_setting_opener.html
   file4_setting_opener.html
   PASS.html
   FAIL.html
   !/dom/animation/test/testcommon.js
   !/dom/events/test/event_leak_utils.js
@@ -786,16 +788,18 @@ support-files =
   file_js_cache_syntax_error.js
 [test_setInterval_from_start.html]
 [test_setInterval_uncatchable_exception.html]
 skip-if = debug == false
 [test_settimeout_extra_arguments.html]
 [test_settimeout_inner.html]
 [test_setTimeoutWith0.html]
 [test_setting_opener.html]
+[test_shared_compartment1.html]
+[test_shared_compartment2.html]
 [test_style_cssText.html]
 [test_text_wholeText.html]
 [test_textnode_normalize_in_selection.html]
 [test_textnode_split_in_selection.html]
 [test_timeout_clamp.html]
 skip-if = debug == true && toolkit == 'android' # Timing dependent, skip slow debug android builds
 [test_timer_flood.html]
 [test_title.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_shared_compartment1.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1530608
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1530608</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1530608 **/
+  SimpleTest.waitForExplicitFinish();
+
+  var Cu = SpecialPowers.Cu;
+  var isSameCompartment = Cu.getJSTestingFunctions().isSameCompartment;
+
+  var testsDone = 0;
+  function finishIfDone() {
+    testsDone++;
+    if (testsDone === 4) {
+      SimpleTest.finish();
+    }
+  }
+
+  // Test 1: same-origin iframe.
+  function testFrame1() {
+    var frameWin = document.getElementById("frame1").contentWindow;
+    ok(isSameCompartment(window, frameWin),
+       "Same-origin iframe must be same-compartment");
+    finishIfDone();
+  }
+
+  // Test 2: cross-origin iframe.
+  function testFrame2() {
+    var frameWin = document.getElementById("frame2").contentWindow;
+    ok(!isSameCompartment(window, frameWin),
+       "Cross-origin iframe must be cross-compartment");
+    finishIfDone();
+  }
+
+  // Test 3: same-site, cross-origin iframe.
+  function testFrame3() {
+    var frame = document.getElementById("frame3");
+    ok(!isSameCompartment(window, frame.contentWindow),
+       "Same-site cross-origin iframe must be cross-compartment");
+
+    // Now load a same-origin page in this iframe.
+    frame.onload = function() {
+      ok(isSameCompartment(window, frame.contentWindow),
+         "Frame must be same-compartment now");
+      finishIfDone();
+    };
+    frame.src = "file_empty.html";
+  }
+
+  // Test 4: dynamically created iframe.
+  addLoadEvent(function() {
+    var frame = document.createElement("iframe");
+    document.body.appendChild(frame);
+    ok(isSameCompartment(window, frame.contentWindow),
+       "Newly created iframe must be same-compartment");
+    finishIfDone();
+  });
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1530608">Mozilla Bug 1530608</a>
+
+<iframe id="frame1" onload="testFrame1()" src="file_empty.html"></iframe>
+<iframe id="frame2" onload="testFrame2()" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+<iframe id="frame3" onload="testFrame3()" src="http://test1.mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_shared_compartment2.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1530608
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1530608</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1530608 **/
+  SimpleTest.waitForExplicitFinish();
+
+  // We have the following origins:
+  //
+  // 1: this page:    mochi.test:8888
+  // 2: iframe:       example.org
+  // 3: inner iframe: mochi.test:8888
+  //
+  // Test that 1 and 2 are cross-compartment (because cross-origin), but 1 and 3
+  // are same-compartment.
+
+  function go(innerWin) {
+    var Cu = SpecialPowers.Cu;
+    var isSameCompartment = Cu.getJSTestingFunctions().isSameCompartment;
+
+    var frame = document.getElementById("frame");
+    ok(!isSameCompartment(window, frame.contentWindow),
+       "Cross-origin iframe must be cross-compartment");
+
+    ok(isSameCompartment(window, innerWin),
+       "Same-origin inner iframe must be same-compartment");
+
+    SimpleTest.finish();
+  }
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1530608">Mozilla Bug 1530608</a>
+
+<iframe id="frame" src="http://example.org/tests/dom/base/test/iframe_shared_compartment2a.html"></iframe>
+
+</body>
+</html>
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3732,16 +3732,17 @@ void HTMLMediaElement::DispatchEventsWhe
   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
       this, NS_LITERAL_STRING("MozAutoplayMediaBlocked"), CanBubble::eYes,
       ChromeOnlyDispatch::eYes);
   asyncDispatcher->PostDOMEvent();
 #endif
   OwnerDoc()->MaybeNotifyAutoplayBlocked();
   ReportToConsole(nsIScriptError::warningFlag, "BlockAutoplayError");
   mHasPlayEverBeenBlocked = true;
+  mHasEverBeenBlockedForAutoplay = true;
 }
 
 void HTMLMediaElement::PlayInternal(bool aHandlingUserInput) {
   if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE) {
     // The media load algorithm will be initiated by a user interaction.
     // We want to boost the channel priority for better responsiveness.
     // Note this must be done before UpdatePreloadAction() which will
     // update |mPreloadAction|.
@@ -5980,16 +5981,24 @@ void HTMLMediaElement::SuspendOrResumeEl
         if (!mPaused && !mDecoder->IsEnded()) {
           mDecoder->Play();
         }
       }
       if (mEventDeliveryPaused) {
         mEventDeliveryPaused = false;
         DispatchPendingMediaEvents();
       }
+      // If the media element has been blocked and isn't still allowed to play
+      // when it comes back from the bfcache, we would notify front end to show
+      // the blocking icon in order to inform user that the site is still being
+      // blocked.
+      if (mHasEverBeenBlockedForAutoplay &&
+          !AutoplayPolicy::IsAllowedToPlay(*this)) {
+        OwnerDoc()->MaybeNotifyAutoplayBlocked();
+      }
     }
   }
 }
 
 bool HTMLMediaElement::IsBeingDestroyed() {
   Document* ownerDoc = OwnerDoc();
   nsIDocShell* docShell = ownerDoc ? ownerDoc->GetDocShell() : nullptr;
   bool isBeingDestroyed = false;
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1664,16 +1664,21 @@ class HTMLMediaElement : public nsGeneri
   // yet.
   bool mBlockedAsWithoutMetadata = false;
 
   // This promise is used to notify MediaElementAudioSourceNode that media
   // element is allowed to play when MediaElement is used as a source for web
   // audio.
   MozPromiseHolder<GenericNonExclusivePromise> mAllowedToPlayPromise;
 
+  // True if media has ever been blocked for autoplay, it's used to notify front
+  // end to show the correct blocking icon when the document goes back from
+  // bfcache.
+  bool mHasEverBeenBlockedForAutoplay = false;
+
  public:
   // Helper class to measure times for playback telemetry stats
   class TimeDurationAccumulator {
    public:
     TimeDurationAccumulator() : mCount(0) {}
     void Start() {
       if (IsStarted()) {
         return;
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -750,17 +750,17 @@ void nsHTMLDocument::SetCompatibilityMod
   }
   mCompatMode = aMode;
   CSSLoader()->SetCompatibilityMode(mCompatMode);
   if (nsPresContext* pc = GetPresContext()) {
     pc->CompatibilityModeChanged();
   }
 }
 
-nsIContent* nsHTMLDocument::GetUnfocusedKeyEventTarget() {
+Element* nsHTMLDocument::GetUnfocusedKeyEventTarget() {
   if (nsGenericHTMLElement* body = GetBody()) {
     return body;
   }
   return Document::GetUnfocusedKeyEventTarget();
 }
 
 already_AddRefed<nsIURI> nsHTMLDocument::GetDomainURI() {
   nsIPrincipal* principal = NodePrincipal();
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -68,17 +68,17 @@ class nsHTMLDocument : public mozilla::d
   virtual void BeginLoad() override;
   virtual void EndLoad() override;
 
   // nsIHTMLDocument
   virtual void SetCompatibilityMode(nsCompatibility aMode) override;
 
   virtual bool IsWriting() override { return mWriteLevel != uint32_t(0); }
 
-  virtual nsIContent* GetUnfocusedKeyEventTarget() override;
+  virtual Element* GetUnfocusedKeyEventTarget() override;
 
   nsContentList* GetExistingForms() const { return mForms; }
 
   mozilla::dom::HTMLAllCollection* All();
 
   // Returns whether an object was found for aName.
   bool ResolveName(JSContext* aCx, const nsAString& aName,
                    JS::MutableHandle<JS::Value> aRetval,
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2013,17 +2013,17 @@ bool ContentChild::DeallocPPSMContentDow
     PPSMContentDownloaderChild* aListener) {
   auto* listener = static_cast<PSMContentDownloaderChild*>(aListener);
   RefPtr<PSMContentDownloaderChild> child = dont_AddRef(listener);
   return true;
 }
 
 PExternalHelperAppChild* ContentChild::AllocPExternalHelperAppChild(
     const OptionalURIParams& uri,
-    const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
+    const Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
     const nsCString& aMimeContentType, const nsCString& aContentDisposition,
     const uint32_t& aContentDispositionHint,
     const nsString& aContentDispositionFilename, const bool& aForceSave,
     const int64_t& aContentLength, const bool& aWasFileChannel,
     const OptionalURIParams& aReferrer, PBrowserChild* aBrowser) {
   auto* child = new ExternalHelperAppChild();
   child->AddRef();
   return child;
@@ -3550,17 +3550,17 @@ mozilla::ipc::IPCResult ContentChild::Re
 mozilla::ipc::IPCResult ContentChild::RecvSaveRecording(
     const FileDescriptor& aFile) {
   recordreplay::parent::SaveRecording(aFile);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvCrossProcessRedirect(
     const uint32_t& aRegistrarId, nsIURI* aURI, const uint32_t& aNewLoadFlags,
-    const OptionalLoadInfoArgs& aLoadInfo, const uint64_t& aChannelId,
+    const Maybe<LoadInfoArgs>& aLoadInfo, const uint64_t& aChannelId,
     nsIURI* aOriginalURI, const uint64_t& aIdentifier) {
   nsCOMPtr<nsILoadInfo> loadInfo;
   nsresult rv =
       mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfo, getter_AddRefs(loadInfo));
   if (NS_FAILED(rv)) {
     MOZ_DIAGNOSTIC_ASSERT(false, "LoadInfoArgsToLoadInfo failed");
     return IPC_OK();
   }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -275,17 +275,17 @@ class ContentChild final : public PConte
   PPSMContentDownloaderChild* AllocPPSMContentDownloaderChild(
       const uint32_t& aCertType);
 
   bool DeallocPPSMContentDownloaderChild(
       PPSMContentDownloaderChild* aDownloader);
 
   PExternalHelperAppChild* AllocPExternalHelperAppChild(
       const OptionalURIParams& uri,
-      const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
+      const Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
       const nsCString& aMimeContentType, const nsCString& aContentDisposition,
       const uint32_t& aContentDispositionHint,
       const nsString& aContentDispositionFilename, const bool& aForceSave,
       const int64_t& aContentLength, const bool& aWasFileChannel,
       const OptionalURIParams& aReferrer, PBrowserChild* aBrowser);
 
   bool DeallocPExternalHelperAppChild(PExternalHelperAppChild* aService);
 
@@ -675,19 +675,18 @@ class ContentChild final : public PConte
       const ClientOpenWindowArgs& aArgs) override;
 
   bool DeallocPClientOpenWindowOpChild(PClientOpenWindowOpChild* aActor);
 
   mozilla::ipc::IPCResult RecvSaveRecording(const FileDescriptor& aFile);
 
   mozilla::ipc::IPCResult RecvCrossProcessRedirect(
       const uint32_t& aRegistrarId, nsIURI* aURI, const uint32_t& aNewLoadFlags,
-      const OptionalLoadInfoArgs& aLoadInfoForwarder,
-      const uint64_t& aChannelId, nsIURI* aOriginalURI,
-      const uint64_t& aIdentifier);
+      const Maybe<LoadInfoArgs>& aLoadInfoForwarder, const uint64_t& aChannelId,
+      nsIURI* aOriginalURI, const uint64_t& aIdentifier);
 
 #ifdef NIGHTLY_BUILD
   // Fetch the current number of pending input events.
   //
   // NOTE: This method performs an atomic read, and is safe to call from all
   // threads.
   uint32_t GetPendingInputEvents() { return mPendingInputEvents; }
 #endif
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3627,17 +3627,17 @@ bool ContentParent::DeallocPPSMContentDo
     PPSMContentDownloaderParent* aListener) {
   auto* listener = static_cast<PSMContentDownloaderParent*>(aListener);
   RefPtr<PSMContentDownloaderParent> downloader = dont_AddRef(listener);
   return true;
 }
 
 PExternalHelperAppParent* ContentParent::AllocPExternalHelperAppParent(
     const OptionalURIParams& uri,
-    const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
+    const Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
     const nsCString& aMimeContentType, const nsCString& aContentDisposition,
     const uint32_t& aContentDispositionHint,
     const nsString& aContentDispositionFilename, const bool& aForceSave,
     const int64_t& aContentLength, const bool& aWasFileChannel,
     const OptionalURIParams& aReferrer, PBrowserParent* aBrowser) {
   ExternalHelperAppParent* parent = new ExternalHelperAppParent(
       uri, aContentLength, aWasFileChannel, aContentDisposition,
       aContentDispositionHint, aContentDispositionFilename);
@@ -3650,18 +3650,19 @@ bool ContentParent::DeallocPExternalHelp
   ExternalHelperAppParent* parent =
       static_cast<ExternalHelperAppParent*>(aService);
   parent->Release();
   return true;
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvPExternalHelperAppConstructor(
     PExternalHelperAppParent* actor, const OptionalURIParams& uri,
-    const OptionalLoadInfoArgs& loadInfoArgs, const nsCString& aMimeContentType,
-    const nsCString& aContentDisposition, const uint32_t& aContentDispositionHint,
+    const Maybe<LoadInfoArgs>& loadInfoArgs, const nsCString& aMimeContentType,
+    const nsCString& aContentDisposition,
+    const uint32_t& aContentDispositionHint,
     const nsString& aContentDispositionFilename, const bool& aForceSave,
     const int64_t& aContentLength, const bool& aWasFileChannel,
     const OptionalURIParams& aReferrer, PBrowserParent* aBrowser) {
   static_cast<ExternalHelperAppParent*>(actor)->Init(
       loadInfoArgs, aMimeContentType, aForceSave, aReferrer, aBrowser);
   return IPC_OK();
 }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -871,29 +871,30 @@ class ContentParent final : public PCont
   PPSMContentDownloaderParent* AllocPPSMContentDownloaderParent(
       const uint32_t& aCertType);
 
   bool DeallocPPSMContentDownloaderParent(
       PPSMContentDownloaderParent* aDownloader);
 
   PExternalHelperAppParent* AllocPExternalHelperAppParent(
       const OptionalURIParams& aUri,
-      const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
+      const Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
       const nsCString& aMimeContentType, const nsCString& aContentDisposition,
       const uint32_t& aContentDispositionHint,
       const nsString& aContentDispositionFilename, const bool& aForceSave,
       const int64_t& aContentLength, const bool& aWasFileChannel,
       const OptionalURIParams& aReferrer, PBrowserParent* aBrowser);
 
   bool DeallocPExternalHelperAppParent(PExternalHelperAppParent* aService);
 
   mozilla::ipc::IPCResult RecvPExternalHelperAppConstructor(
       PExternalHelperAppParent* actor, const OptionalURIParams& uri,
-      const OptionalLoadInfoArgs& loadInfoArgs, const nsCString& aMimeContentType,
-      const nsCString& aContentDisposition, const uint32_t& aContentDispositionHint,
+      const Maybe<LoadInfoArgs>& loadInfoArgs,
+      const nsCString& aMimeContentType, const nsCString& aContentDisposition,
+      const uint32_t& aContentDispositionHint,
       const nsString& aContentDispositionFilename, const bool& aForceSave,
       const int64_t& aContentLength, const bool& aWasFileChannel,
       const OptionalURIParams& aReferrer, PBrowserParent* aBrowser) override;
 
   PHandlerServiceParent* AllocPHandlerServiceParent();
 
   bool DeallocPHandlerServiceParent(PHandlerServiceParent*);
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -768,17 +768,17 @@ child:
 
     // This message is sent to content processes, and triggers the creation of a
     // new HttpChannelChild that will be connected to the parent channel
     // represented by registrarId.
     // This is on PContent not PNecko, as PNecko may not be initialized yet.
     async CrossProcessRedirect(uint32_t aRegistrarId,
                                nsIURI aURI,
                                uint32_t aNewLoadFlags,
-                               OptionalLoadInfoArgs aLoadInfo,
+                               LoadInfoArgs? aLoadInfo,
                                uint64_t aChannelId,
                                nsIURI aOriginalURI,
                                uint64_t aIdentifier);
 
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     sync OpenRecordReplayChannel(uint32_t channelId)
@@ -896,17 +896,17 @@ parent:
     // complex. In the long run, the notification implementation will be
     // overhauled to directly process the notification click/close and directly
     // translate that to a ServiceWorker event.
     async NotificationEvent(nsString type, NotificationEventData data);
 
     async PPSMContentDownloader(uint32_t aCertType);
 
     async PExternalHelperApp(OptionalURIParams uri,
-                             OptionalLoadInfoArgs loadInfoArgs,
+                             LoadInfoArgs? loadInfoArgs,
                              nsCString aMimeContentType,
                              nsCString aContentDisposition,
                              uint32_t aContentDispositionHint,
                              nsString aContentDispositionFilename,
                              bool aForceSave,
                              int64_t aContentLength,
                              bool aWasFileChannel,
                              OptionalURIParams aReferrer,
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -666,17 +666,17 @@ mozilla::ipc::IPCResult ChromiumCDMChild
   if (status != cdm::kSuccess || !buffer) {
     Unused << SendDecryptFailed(aId, status);
     return IPC_OK();
   }
 
   // Success! Return the decrypted sample to parent.
   MOZ_ASSERT(!HasShmemOfSize(outputShmemSize));
   ipc::Shmem shmem = buffer->ExtractShmem();
-  if (SendDecrypted(aId, cdm::kSuccess, shmem)) {
+  if (SendDecrypted(aId, cdm::kSuccess, std::move(shmem))) {
     // No need to deallocate the output shmem; it should have been returned
     // to the content process.
     autoDeallocateOutputShmem.release();
   }
 
   return IPC_OK();
 }
 
@@ -815,17 +815,17 @@ void ChromiumCDMChild::ReturnOutput(Wide
   uint64_t duration = 0;
   if (mFrameDurations.Find(aFrame.Timestamp(), duration)) {
     output.mDuration() = duration;
   }
 
   CDMBuffer* base = reinterpret_cast<CDMBuffer*>(aFrame.FrameBuffer());
   if (base->AsShmemBuffer()) {
     ipc::Shmem shmem = base->AsShmemBuffer()->ExtractShmem();
-    Unused << SendDecodedShmem(output, shmem);
+    Unused << SendDecodedShmem(output, std::move(shmem));
   } else {
     MOZ_ASSERT(base->AsArrayBuffer());
     Unused << SendDecodedData(output, base->AsArrayBuffer()->ExtractBuffer());
   }
 }
 
 mozilla::ipc::IPCResult ChromiumCDMChild::RecvDrain() {
   MOZ_ASSERT(IsOnMessageLoopThread());
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -265,17 +265,17 @@ bool ChromiumCDMParent::InitCDMInputBuff
       break;
   }
 
   const nsTArray<uint8_t>& iv =
       encryptionScheme != GMPEncryptionScheme::kGMPEncryptionCbcs
           ? crypto.mIV
           : crypto.mConstantIV;
   aBuffer = gmp::CDMInputBuffer(
-      shmem, crypto.mKeyId, iv, aSample->mTime.ToMicroseconds(),
+      std::move(shmem), crypto.mKeyId, iv, aSample->mTime.ToMicroseconds(),
       aSample->mDuration.ToMicroseconds(), crypto.mPlainSizes,
       crypto.mEncryptedSizes, crypto.mCryptByteBlock, crypto.mSkipByteBlock,
       encryptionScheme);
   MOZ_ASSERT(
       aBuffer.mEncryptionScheme() == GMPEncryptionScheme::kGMPEncryptionNone ||
           aBuffer.mEncryptionScheme() ==
               GMPEncryptionScheme::kGMPEncryptionCenc ||
           aBuffer.mEncryptionScheme() ==
@@ -286,17 +286,17 @@ bool ChromiumCDMParent::InitCDMInputBuff
 }
 
 bool ChromiumCDMParent::SendBufferToCDM(uint32_t aSizeInBytes) {
   GMP_LOG("ChromiumCDMParent::SendBufferToCDM() size=%" PRIu32, aSizeInBytes);
   Shmem shmem;
   if (!AllocShmem(aSizeInBytes, Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
     return false;
   }
-  if (!SendGiveBuffer(shmem)) {
+  if (!SendGiveBuffer(std::move(shmem))) {
     DeallocShmem(shmem);
     return false;
   }
   return true;
 }
 
 RefPtr<DecryptPromise> ChromiumCDMParent::Decrypt(MediaRawData* aSample) {
   if (mIsShutdown) {
@@ -708,17 +708,17 @@ ipc::IPCResult ChromiumCDMParent::RecvDe
         MediaResult(NS_ERROR_OUT_OF_MEMORY,
                     RESULT_DETAIL("Can't create VideoData")),
         __func__);
     return IPC_OK();
   }
 
   // Return the shmem to the CDM so the shmem can be reused to send us
   // another frame.
-  if (!SendGiveBuffer(aShmem)) {
+  if (!SendGiveBuffer(std::move(aShmem))) {
     mDecodePromise.RejectIfExists(
         MediaResult(NS_ERROR_OUT_OF_MEMORY,
                     RESULT_DETAIL("Can't return shmem to CDM process")),
         __func__);
     return IPC_OK();
   }
 
   // Don't need to deallocate the shmem since the CDM process is responsible
--- a/dom/media/gmp/GMPSharedMemManager.cpp
+++ b/dom/media/gmp/GMPSharedMemManager.cpp
@@ -63,17 +63,17 @@ bool GMPSharedMemManager::MgrDeallocShme
       MOZ_CRASH("Deallocating Shmem we already have in our cache!");
       // return true;
     }
   }
 
   // XXX This works; there are better pool algorithms.  We need to avoid
   // "falling off a cliff" with too low a number
   if (GetGmpFreelist(aClass).Length() > 10) {
-    Dealloc(GetGmpFreelist(aClass)[0]);
+    Dealloc(std::move(GetGmpFreelist(aClass)[0]));
     GetGmpFreelist(aClass).RemoveElementAt(0);
     // The allocation numbers will be fubar on the Child!
     mData->mGmpAllocated[aClass]--;
   }
   for (uint32_t i = 0; i < GetGmpFreelist(aClass).Length(); i++) {
     MOZ_ASSERT(GetGmpFreelist(aClass)[i].IsWritable());
     total += GetGmpFreelist(aClass)[i].Size<uint8_t>();
     if (size < GetGmpFreelist(aClass)[i].Size<uint8_t>()) {
--- a/dom/media/gmp/GMPSharedMemManager.h
+++ b/dom/media/gmp/GMPSharedMemManager.h
@@ -63,17 +63,17 @@ class GMPSharedMemManager {
   // crashed
   virtual uint32_t NumInUse(GMPSharedMem::GMPMemoryClasses aClass);
 
   // These have to be implemented using the AllocShmem/etc provided by the
   // IPDL-generated interfaces, so have the Parent/Child implement them.
   virtual bool Alloc(size_t aSize,
                      ipc::Shmem::SharedMemory::SharedMemoryType aType,
                      ipc::Shmem* aMem) = 0;
-  virtual void Dealloc(ipc::Shmem& aMem) = 0;
+  virtual void Dealloc(ipc::Shmem&& aMem) = 0;
 
  private:
   nsTArray<ipc::Shmem>& GetGmpFreelist(GMPSharedMem::GMPMemoryClasses aTypes) {
     return mData->mGmpFreelist[aTypes];
   }
 
   GMPSharedMem* mData;
 };
--- a/dom/media/gmp/GMPVideoDecoderChild.cpp
+++ b/dom/media/gmp/GMPVideoDecoderChild.cpp
@@ -202,18 +202,18 @@ bool GMPVideoDecoderChild::Alloc(size_t 
   rv = AllocShmem(aSize, aType, aMem);
 #  else
   rv = AllocUnsafeShmem(aSize, aType, aMem);
 #  endif
 #endif
   return rv;
 }
 
-void GMPVideoDecoderChild::Dealloc(Shmem& aMem) {
+void GMPVideoDecoderChild::Dealloc(Shmem&& aMem) {
 #ifndef SHMEM_ALLOC_IN_CHILD
-  SendParentShmemForPool(aMem);
+  SendParentShmemForPool(std::move(aMem));
 #else
   DeallocShmem(aMem);
 #endif
 }
 
 }  // namespace gmp
 }  // namespace mozilla
--- a/dom/media/gmp/GMPVideoDecoderChild.h
+++ b/dom/media/gmp/GMPVideoDecoderChild.h
@@ -38,17 +38,17 @@ class GMPVideoDecoderChild : public PGMP
   void InputDataExhausted() override;
   void DrainComplete() override;
   void ResetComplete() override;
   void Error(GMPErr aError) override;
 
   // GMPSharedMemManager
   bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType,
              Shmem* aMem) override;
-  void Dealloc(Shmem& aMem) override;
+  void Dealloc(Shmem&& aMem) override;
 
  private:
   virtual ~GMPVideoDecoderChild();
 
   // PGMPVideoDecoderChild
   mozilla::ipc::IPCResult RecvInitDecode(
       const GMPVideoCodec& aCodecSettings,
       InfallibleTArray<uint8_t>&& aCodecSpecific, const int32_t& aCoreCount);
--- a/dom/media/gmp/GMPVideoDecoderParent.h
+++ b/dom/media/gmp/GMPVideoDecoderParent.h
@@ -55,17 +55,17 @@ class GMPVideoDecoderParent final : publ
   bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType,
              Shmem* aMem) override {
 #ifdef GMP_SAFE_SHMEM
     return AllocShmem(aSize, aType, aMem);
 #else
     return AllocUnsafeShmem(aSize, aType, aMem);
 #endif
   }
-  void Dealloc(Shmem& aMem) override { DeallocShmem(aMem); }
+  void Dealloc(Shmem&& aMem) override { DeallocShmem(aMem); }
 
  private:
   ~GMPVideoDecoderParent();
 
   // PGMPVideoDecoderParent
   void ActorDestroy(ActorDestroyReason aWhy) override;
   mozilla::ipc::IPCResult RecvDecoded(
       const GMPVideoi420FrameData& aDecodedFrame) override;
--- a/dom/media/gmp/GMPVideoEncoderChild.cpp
+++ b/dom/media/gmp/GMPVideoEncoderChild.cpp
@@ -194,18 +194,18 @@ bool GMPVideoEncoderChild::Alloc(size_t 
   rv = AllocShmem(aSize, aType, aMem);
 #  else
   rv = AllocUnsafeShmem(aSize, aType, aMem);
 #  endif
 #endif
   return rv;
 }
 
-void GMPVideoEncoderChild::Dealloc(Shmem& aMem) {
+void GMPVideoEncoderChild::Dealloc(Shmem&& aMem) {
 #ifndef SHMEM_ALLOC_IN_CHILD
-  SendParentShmemForPool(aMem);
+  SendParentShmemForPool(std::move(aMem));
 #else
   DeallocShmem(aMem);
 #endif
 }
 
 }  // namespace gmp
 }  // namespace mozilla
--- a/dom/media/gmp/GMPVideoEncoderChild.h
+++ b/dom/media/gmp/GMPVideoEncoderChild.h
@@ -34,17 +34,17 @@ class GMPVideoEncoderChild : public PGMP
   void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
                const uint8_t* aCodecSpecificInfo,
                uint32_t aCodecSpecificInfoLength) override;
   void Error(GMPErr aError) override;
 
   // GMPSharedMemManager
   bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType,
              Shmem* aMem) override;
-  void Dealloc(Shmem& aMem) override;
+  void Dealloc(Shmem&& aMem) override;
 
  private:
   virtual ~GMPVideoEncoderChild();
 
   // PGMPVideoEncoderChild
   mozilla::ipc::IPCResult RecvInitEncode(
       const GMPVideoCodec& aCodecSettings,
       InfallibleTArray<uint8_t>&& aCodecSpecific, const int32_t& aNumberOfCores,
--- a/dom/media/gmp/GMPVideoEncoderParent.h
+++ b/dom/media/gmp/GMPVideoEncoderParent.h
@@ -53,17 +53,17 @@ class GMPVideoEncoderParent : public GMP
   bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType,
              Shmem* aMem) override {
 #ifdef GMP_SAFE_SHMEM
     return AllocShmem(aSize, aType, aMem);
 #else
     return AllocUnsafeShmem(aSize, aType, aMem);
 #endif
   }
-  void Dealloc(Shmem& aMem) override { DeallocShmem(aMem); }
+  void Dealloc(Shmem&& aMem) override { DeallocShmem(aMem); }
 
  private:
   virtual ~GMPVideoEncoderParent(){};
 
   // PGMPVideoEncoderParent
   void ActorDestroy(ActorDestroyReason aWhy) override;
   mozilla::ipc::IPCResult RecvEncoded(
       const GMPVideoEncodedFrameData& aEncodedFrame,
--- a/dom/media/ipc/RemoteAudioDecoder.cpp
+++ b/dom/media/ipc/RemoteAudioDecoder.cpp
@@ -109,15 +109,15 @@ void RemoteAudioDecoderParent::ProcessDe
         audio->Data().Length() == buffer.Size<AudioDataValue>()) {
       PodCopy(buffer.get<AudioDataValue>(), audio->Data().Elements(),
               audio->Data().Length());
     }
 
     RemoteAudioDataIPDL output(
         MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode,
                       data->mDuration, data->mKeyframe),
-        audio->mChannels, audio->mRate, audio->mChannelMap, buffer);
+        audio->mChannels, audio->mRate, audio->mChannelMap, std::move(buffer));
 
     Unused << SendOutput(output);
   }
 }
 
 }  // namespace mozilla
--- a/dom/media/ipc/RemoteDecoderChild.cpp
+++ b/dom/media/ipc/RemoteDecoderChild.cpp
@@ -109,17 +109,17 @@ RefPtr<MediaDataDecoder::DecodePromise> 
         NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
   }
 
   memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size());
 
   MediaRawDataIPDL sample(
       MediaDataIPDL(aSample->mOffset, aSample->mTime, aSample->mTimecode,
                     aSample->mDuration, aSample->mKeyframe),
-      buffer);
+      std::move(buffer));
   SendInput(sample);
 
   return mDecodePromise.Ensure(__func__);
 }
 
 RefPtr<MediaDataDecoder::FlushPromise> RemoteDecoderChild::Flush() {
   AssertOnManagerThread();
   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
--- a/dom/media/ipc/RemoteVideoDecoder.cpp
+++ b/dom/media/ipc/RemoteVideoDecoder.cpp
@@ -179,17 +179,17 @@ void RemoteVideoDecoderParent::ProcessDe
     PlanarYCbCrImage* image =
         static_cast<PlanarYCbCrImage*>(video->mImage.get());
 
     SurfaceDescriptorBuffer sdBuffer;
     Shmem buffer;
     if (AllocShmem(image->GetDataSize(), Shmem::SharedMemory::TYPE_BASIC,
                    &buffer) &&
         image->GetDataSize() == buffer.Size<uint8_t>()) {
-      sdBuffer.data() = buffer;
+      sdBuffer.data() = std::move(buffer);
       image->BuildSurfaceDescriptorBuffer(sdBuffer);
     }
 
     RemoteVideoDataIPDL output(
         MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode,
                       data->mDuration, data->mKeyframe),
         video->mDisplay, image->GetSize(), sdBuffer, video->mFrameID);
     Unused << SendOutput(output);
--- a/dom/media/ipc/VideoDecoderChild.cpp
+++ b/dom/media/ipc/VideoDecoderChild.cpp
@@ -238,17 +238,17 @@ RefPtr<MediaDataDecoder::DecodePromise> 
         NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
   }
 
   memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size());
 
   MediaRawDataIPDL sample(
       MediaDataIPDL(aSample->mOffset, aSample->mTime, aSample->mTimecode,
                     aSample->mDuration, aSample->mKeyframe),
-      buffer);
+      std::move(buffer));
   SendInput(sample);
   return mDecodePromise.Ensure(__func__);
 }
 
 RefPtr<MediaDataDecoder::FlushPromise> VideoDecoderChild::Flush() {
   AssertOnManagerThread();
   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
--- a/dom/media/ipc/VideoDecoderManagerParent.cpp
+++ b/dom/media/ipc/VideoDecoderManagerParent.cpp
@@ -260,17 +260,17 @@ mozilla::ipc::IPCResult VideoDecoderMana
     *aResult = null_t();
     return IPC_OK();
   }
 
   dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
   dt->Flush();
 
   *aResult = SurfaceDescriptorBuffer(RGBDescriptor(size, format, true),
-                                     MemoryOrShmem(buffer));
+                                     MemoryOrShmem(std::move(buffer)));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VideoDecoderManagerParent::RecvDeallocateSurfaceDescriptorGPUVideo(
     const SurfaceDescriptorGPUVideo& aSD) {
   mImageMap.erase(aSD.handle());
   mTextureMap.erase(aSD.handle());
--- a/dom/media/systemservices/CamerasChild.cpp
+++ b/dom/media/systemservices/CamerasChild.cpp
@@ -570,17 +570,17 @@ mozilla::ipc::IPCResult CamerasChild::Re
     mozilla::ipc::Shmem&& shmem, const VideoFrameProperties& prop) {
   MutexAutoLock lock(mCallbackMutex);
   if (Callback(capEngine, capId)) {
     unsigned char* image = shmem.get<unsigned char>();
     Callback(capEngine, capId)->DeliverFrame(image, prop);
   } else {
     LOG(("DeliverFrame called with dead callback"));
   }
-  SendReleaseFrame(shmem);
+  SendReleaseFrame(std::move(shmem));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult CamerasChild::RecvDeviceChange() {
   this->OnDeviceChange();
   return IPC_OK();
 }
 
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -273,24 +273,25 @@ int CamerasParent::DeliverFrameOverIPC(C
       LOG(("No usable Video shmem in DeliverFrame (out of buffers?)"));
       // We can skip this frame if we run out of buffers, it's not a real error.
       return 0;
     }
 
     // get() and Size() check for proper alignment of the segment
     memcpy(shMemBuff.GetBytes(), altbuffer, aProps.bufferSize());
 
-    if (!SendDeliverFrame(capEng, aStreamId, shMemBuff.Get(), aProps)) {
+    if (!SendDeliverFrame(capEng, aStreamId, std::move(shMemBuff.Get()),
+                          aProps)) {
       return -1;
     }
   } else {
     MOZ_ASSERT(buffer.Valid());
     // ShmemBuffer was available, we're all good. A single copy happened
     // in the original webrtc callback.
-    if (!SendDeliverFrame(capEng, aStreamId, buffer.Get(), aProps)) {
+    if (!SendDeliverFrame(capEng, aStreamId, std::move(buffer.Get()), aProps)) {
       return -1;
     }
   }
 
   return 0;
 }
 
 ShmemBuffer CamerasParent::GetBuffer(size_t aSize) {
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -2717,18 +2717,18 @@ void PluginInstanceChild::NPN_SetCurrent
         return;
       }
 
       IntRect dirty = changed ? NPRectToIntRect(*changed)
                               : IntRect(IntPoint(0, 0), bitmap->mSize);
 
       // Need a holder since IPDL zaps the object for mysterious reasons.
       Shmem shmemHolder = bitmap->mShmem;
-      SendShowDirectBitmap(shmemHolder, bitmap->mFormat, bitmap->mStride,
-                           bitmap->mSize, dirty);
+      SendShowDirectBitmap(std::move(shmemHolder), bitmap->mFormat,
+                           bitmap->mStride, bitmap->mSize, dirty);
       break;
     }
 #if defined(XP_WIN)
     case NPDrawingModelAsyncWindowsDXGISurface: {
       WindowsHandle handle;
       if (!mDxgiSurfaces.Get(surface, &handle)) {
         return;
       }
@@ -3564,18 +3564,18 @@ bool PluginInstanceChild::ShowPluginFram
       mCurrentSurfaceActor = SendPPluginSurfaceConstructor(
           handle, mCurrentSurface->GetSize(), haveTransparentPixels);
     }
     currSurf = mCurrentSurfaceActor;
     s->Flush();
   } else
 #endif
       if (gfxSharedImageSurface::IsSharedImage(mCurrentSurface)) {
-    currSurf =
-        static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem();
+    currSurf = std::move(
+        static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem());
   } else {
     MOZ_CRASH("Surface type is not remotable");
     return false;
   }
 
   // Unused, except to possibly return a shmem to us
   SurfaceDescriptor returnSurf;
 
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -844,18 +844,18 @@ mozilla::ipc::IPCResult PluginInstancePa
     } else
 #endif
     {
       mFrontSurface->Flush();
     }
   }
 
   if (mFrontSurface && gfxSharedImageSurface::IsSharedImage(mFrontSurface))
-    *prevSurface =
-        static_cast<gfxSharedImageSurface*>(mFrontSurface.get())->GetShmem();
+    *prevSurface = std::move(
+        static_cast<gfxSharedImageSurface*>(mFrontSurface.get())->GetShmem());
   else
     *prevSurface = null_t();
 
   if (surface) {
     // Notify the cairo backend that this surface has changed behind
     // its back.
     gfxRect ur(updatedRect.left, updatedRect.top,
                updatedRect.right - updatedRect.left,
@@ -1156,17 +1156,17 @@ PluginInstanceParent::BackgroundDescript
   return SurfaceDescriptorX11(xsurf);
 #endif
 
 #ifdef XP_WIN
   MOZ_ASSERT(gfxSharedImageSurface::IsSharedImage(mBackground),
              "Expected shared image surface");
   gfxSharedImageSurface* shmem =
       static_cast<gfxSharedImageSurface*>(mBackground.get());
-  return shmem->GetShmem();
+  return mozilla::plugins::SurfaceDescriptor(std::move(shmem->GetShmem()));
 #endif
 
   // If this is ever used, which it shouldn't be, it will trigger a
   // hard assertion in IPDL-generated code.
   return mozilla::plugins::SurfaceDescriptor();
 }
 
 ImageContainer* PluginInstanceParent::GetImageContainer() {
@@ -1547,18 +1547,18 @@ int16_t PluginInstanceParent::NPP_Handle
         PLUGIN_LOG_DEBUG(("NPCocoaEventDrawRect on window of size 0."));
         return false;
       }
       if (!mShSurface.IsReadable()) {
         PLUGIN_LOG_DEBUG(("Shmem is not readable."));
         return false;
       }
 
-      if (!CallNPP_HandleEvent_Shmem(npremoteevent, mShSurface, &handled,
-                                     &mShSurface))
+      if (!CallNPP_HandleEvent_Shmem(npremoteevent, std::move(mShSurface),
+                                     &handled, &mShSurface))
         return false;  // no good way to handle errors here...
 
       if (!mShSurface.IsReadable()) {
         PLUGIN_LOG_DEBUG(
             ("Shmem not returned. Either the plugin crashed "
              "or we have a bug."));
         return false;
       }
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -525,17 +525,17 @@ void PluginModuleChromeParent::OnProcess
 
 bool PluginModuleChromeParent::InitCrashReporter() {
   ipc::Shmem shmem;
   if (!ipc::CrashReporterClient::AllocShmem(this, &shmem)) {
     return false;
   }
 
   NativeThreadId threadId;
-  if (!CallInitCrashReporter(shmem, &threadId)) {
+  if (!CallInitCrashReporter(std::move(shmem), &threadId)) {
     return false;
   }
 
   {
     mozilla::MutexAutoLock lock(mCrashReporterMutex);
     mCrashReporter = MakeUnique<ipc::CrashReporterHost>(GeckoProcessType_Plugin,
                                                         shmem, threadId);
   }
--- a/dom/push/test/xpcshell/head-http2.js
+++ b/dom/push/test/xpcshell/head-http2.js
@@ -1,61 +1,37 @@
+const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+
 // Returns the test H/2 server port, throwing if it's missing or invalid.
 function getTestServerPort() {
   let portEnv = Cc["@mozilla.org/process/environment;1"]
                   .getService(Ci.nsIEnvironment).get("MOZHTTP2_PORT");
   let port = parseInt(portEnv, 10);
   if (!Number.isFinite(port) || port < 1 || port > 65535) {
     throw new Error(`Invalid port in MOZHTTP2_PORT env var: ${portEnv}`);
   }
   info(`Using HTTP/2 server on port ${port}`);
   return port;
 }
 
-// Support for making sure we can talk to the invalid cert the server presents
-var CertOverrideListener = function(host, port, bits) {
-  this.host = host;
-  this.port = port || 443;
-  this.bits = bits;
-};
-
-CertOverrideListener.prototype = {
-  host: null,
-  bits: null,
-
-  getInterface: function(aIID) {
-    return this.QueryInterface(aIID);
-  },
-
-  QueryInterface: function(aIID) {
-    if (aIID.equals(Ci.nsIBadCertListener2) ||
-        aIID.equals(Ci.nsIInterfaceRequestor) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+function readFile(file) {
+  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
+                  .createInstance(Ci.nsIFileInputStream);
+  fstream.init(file, -1, 0, 0);
+  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
+  fstream.close();
+  return data;
+}
 
-  notifyCertProblem: function(socketInfo, secInfo, targetHost) {
-    var cert = secInfo.serverCert;
-    var cos = Cc["@mozilla.org/security/certoverride;1"].
-              getService(Ci.nsICertOverrideService);
-    cos.rememberValidityOverride(this.host, this.port, cert, this.bits, false);
-    dump("Certificate Override in place\n");
-    return true;
-  },
-};
+function addCertFromFile(certdb, filename, trustString) {
+  let certFile = do_get_file(filename, false);
+  let pem = readFile(certFile)
+              .replace(/-----BEGIN CERTIFICATE-----/, "")
+              .replace(/-----END CERTIFICATE-----/, "")
+              .replace(/[\r\n]/g, "");
+  certdb.addCertFromBase64(pem, trustString);
+}
 
-function addCertOverride(host, port, bits) {
-  var req = new XMLHttpRequest();
-  try {
-    var url;
-    if (port && (port > 0) && (port !== 443)) {
-      url = "https://" + host + ":" + port + "/";
-    } else {
-      url = "https://" + host + "/";
-    }
-    req.open("GET", url, false);
-    req.channel.notificationCallbacks = new CertOverrideListener(host, port, bits);
-    req.send(null);
-  } catch (e) {
-    // This will fail since the server is not trusted yet
-  }
+function trustHttp2CA() {
+  let certdb = Cc["@mozilla.org/security/x509certdb;1"]
+                  .getService(Ci.nsIX509CertDB);
+  addCertFromFile(certdb, "../../../../netwerk/test/unit/http2-ca.pem", "CTu,u,u");
 }
--- a/dom/push/test/xpcshell/test_notification_http2.js
+++ b/dom/push/test/xpcshell/test_notification_http2.js
@@ -19,20 +19,17 @@ function run_test() {
   prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
 
   // Set to allow the cert presented by our H2 server
   var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
   prefs.setBoolPref("dom.push.enabled", true);
   prefs.setBoolPref("dom.push.connection.enabled", true);
 
-  addCertOverride("localhost", serverPort,
-                  Ci.nsICertOverrideService.ERROR_UNTRUSTED |
-                  Ci.nsICertOverrideService.ERROR_MISMATCH |
-                  Ci.nsICertOverrideService.ERROR_TIME);
+  trustHttp2CA();
 
   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
 
   run_next_test();
 }
 
 add_task(async function test_pushNotifications() {
 
--- a/dom/push/test/xpcshell/test_register_error_http2.js
+++ b/dom/push/test/xpcshell/test_register_error_http2.js
@@ -49,20 +49,17 @@ add_task(async function test_pushSubscri
   PushService.uninit();
 });
 
 add_task(async function test_TLS() {
     // Set to allow the cert presented by our H2 server
   var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
 
-  addCertOverride("localhost", serverPort,
-                  Ci.nsICertOverrideService.ERROR_UNTRUSTED |
-                  Ci.nsICertOverrideService.ERROR_MISMATCH |
-                  Ci.nsICertOverrideService.ERROR_TIME);
+  trustHttp2CA();
 
   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
 });
 
 add_task(async function test_pushSubscriptionMissingLocation() {
 
   let db = PushServiceHttp2.newPushDB();
   registerCleanupFunction(() => {
--- a/dom/push/test/xpcshell/test_register_success_http2.js
+++ b/dom/push/test/xpcshell/test_register_success_http2.js
@@ -22,20 +22,17 @@ function run_test() {
   pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled");
 
   // Set to allow the cert presented by our H2 server
   var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
   prefs.setBoolPref("dom.push.enabled", true);
   prefs.setBoolPref("dom.push.connection.enabled", true);
 
-  addCertOverride("localhost", serverPort,
-                  Ci.nsICertOverrideService.ERROR_UNTRUSTED |
-                  Ci.nsICertOverrideService.ERROR_MISMATCH |
-                  Ci.nsICertOverrideService.ERROR_TIME);
+  trustHttp2CA();
 
   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
 
   serverURL = "https://localhost:" + serverPort;
 
   run_next_test();
 }
 
--- a/dom/push/test/xpcshell/test_unregister_success_http2.js
+++ b/dom/push/test/xpcshell/test_unregister_success_http2.js
@@ -21,20 +21,17 @@ function run_test() {
   pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled");
 
   // Set to allow the cert presented by our H2 server
   var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
   prefs.setBoolPref("dom.push.enabled", true);
   prefs.setBoolPref("dom.push.connection.enabled", true);
 
-  addCertOverride("localhost", serverPort,
-                  Ci.nsICertOverrideService.ERROR_UNTRUSTED |
-                  Ci.nsICertOverrideService.ERROR_MISMATCH |
-                  Ci.nsICertOverrideService.ERROR_TIME);
+  trustHttp2CA();
 
   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
 
   run_next_test();
 }
 
 add_task(async function test_pushUnsubscriptionSuccess() {
   let db = PushServiceHttp2.newPushDB();
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -83,17 +83,19 @@ UniquePtr<SurfaceFactory> GLScreenBuffer
     factory = SurfaceFactory_IOSurface::Create(gl, caps, ipcChannel, flags);
 #elif defined(MOZ_X11)
     if (sGLXLibrary.UseTextureFromPixmap())
       factory = SurfaceFactory_GLXDrawable::Create(gl, caps, ipcChannel, flags);
 #elif defined(MOZ_WIDGET_UIKIT)
     factory = MakeUnique<SurfaceFactory_GLTexture>(mGLContext, caps, ipcChannel,
                                                    mFlags);
 #elif defined(MOZ_WIDGET_ANDROID)
-    if (XRE_IsParentProcess() && !gfxPrefs::WebGLSurfaceTextureEnabled()) {
+    // XXX WebRender does not support SurfaceFactory_EGLImage usage.
+    if (XRE_IsParentProcess() && !gfxPrefs::WebGLSurfaceTextureEnabled() &&
+        backend != layers::LayersBackend::LAYERS_WR) {
       factory = SurfaceFactory_EGLImage::Create(gl, caps, ipcChannel, flags);
     } else {
       factory =
           SurfaceFactory_SurfaceTexture::Create(gl, caps, ipcChannel, flags);
     }
 #else
     if (gl->GetContextType() == GLContextType::EGL) {
       if (XRE_IsParentProcess()) {
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -1163,19 +1163,19 @@ struct ParamTraits<mozilla::Array<T, Len
     }
     return true;
   }
 };
 
 template <>
 struct ParamTraits<mozilla::gfx::PaintFragment> {
   typedef mozilla::gfx::PaintFragment paramType;
-  static void Write(Message* aMsg, paramType& aParam) {
+  static void Write(Message* aMsg, paramType&& aParam) {
     WriteParam(aMsg, aParam.mSize);
-    WriteParam(aMsg, aParam.mRecording);
+    WriteParam(aMsg, std::move(aParam.mRecording));
     WriteParam(aMsg, aParam.mDependencies);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
     return ReadParam(aMsg, aIter, &aResult->mSize) &&
            ReadParam(aMsg, aIter, &aResult->mRecording) &&
            ReadParam(aMsg, aIter, &aResult->mDependencies);
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -479,17 +479,18 @@ TextureData* MemoryTextureData::CreateSi
 }
 
 bool ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
   MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
   if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
     return false;
   }
 
-  aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(mShmem));
+  aOutDescriptor =
+      SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(std::move(mShmem)));
 
   return true;
 }
 
 ShmemTextureData* ShmemTextureData::Create(gfx::IntSize aSize,
                                            gfx::SurfaceFormat aFormat,
                                            gfx::BackendType aMoz2DBackend,
                                            LayersBackend aLayersBackend,
--- a/gfx/layers/D3D11YCbCrImage.cpp
+++ b/gfx/layers/D3D11YCbCrImage.cpp
@@ -138,16 +138,21 @@ already_AddRefed<SourceSurface> D3D11YCb
   RefPtr<ID3D11Texture2D> texCb = dxgiData->GetD3D11Texture(1);
   RefPtr<ID3D11Texture2D> texCr = dxgiData->GetD3D11Texture(2);
   RefPtr<ID3D11Texture2D> softTexY, softTexCb, softTexCr;
   D3D11_TEXTURE2D_DESC desc;
 
   RefPtr<ID3D11Device> dev;
   texY->GetDevice(getter_AddRefs(dev));
 
+  if (!dev || dev != gfx::DeviceManagerDx::Get()->GetImageDevice()) {
+    gfxCriticalError() << "D3D11Device is obsoleted";
+    return nullptr;
+  }
+
   RefPtr<ID3D10Multithread> mt;
   hr = dev->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt));
 
   if (FAILED(hr) || !mt) {
     gfxCriticalError() << "Multithread safety interface not supported.";
     return nullptr;
   }
 
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -1199,17 +1199,17 @@ void LayerManagerComposite::HandlePixels
     return;
   }
   CompositorOGL* compositor = mCompositor->AsCompositorOGL();
   GLContext* gl = compositor->gl();
   MOZ_ASSERT(gl);
   gl->fReadPixels(0, 0, bufferWidth, bufferHeight, LOCAL_GL_RGBA,
                   LOCAL_GL_UNSIGNED_BYTE, mem.get<uint8_t>());
   Unused << mScreenPixelsTarget->SendScreenPixels(
-      mem, ScreenIntSize(bufferWidth, bufferHeight));
+      std::move(mem), ScreenIntSize(bufferWidth, bufferHeight));
   mScreenPixelsTarget = nullptr;
 }
 #endif
 
 already_AddRefed<PaintedLayer> LayerManagerComposite::CreatePaintedLayer() {
   if (mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return nullptr;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -959,17 +959,17 @@ bool ShadowLayerForwarder::AllocSurfaceD
     bufferDesc = reinterpret_cast<uintptr_t>(data);
   } else {
     mozilla::ipc::Shmem shmem;
     if (!GetTextureForwarder()->AllocUnsafeShmem(size, OptimalShmemType(),
                                                  &shmem)) {
       return false;
     }
 
-    bufferDesc = shmem;
+    bufferDesc = std::move(shmem);
   }
 
   // Use an intermediate buffer by default. Skipping the intermediate buffer is
   // only possible in certain configurations so let's keep it simple here for
   // now.
   const bool hasIntermediateBuffer = true;
   *aBuffer = SurfaceDescriptorBuffer(
       RGBDescriptor(aSize, format, hasIntermediateBuffer), bufferDesc);
--- a/gfx/layers/ipc/UiCompositorControllerChild.cpp
+++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp
@@ -161,17 +161,17 @@ bool UiCompositorControllerChild::Enable
 }
 
 bool UiCompositorControllerChild::ToolbarPixelsToCompositor(
     Shmem& aMem, const ScreenIntSize& aSize) {
   if (!mIsOpen) {
     return false;
   }
 
-  return SendToolbarPixelsToCompositor(aMem, aSize);
+  return SendToolbarPixelsToCompositor(std::move(aMem), aSize);
 }
 
 void UiCompositorControllerChild::Destroy() {
   if (!IsOnUiThread()) {
     GetUiThread()->Dispatch(
         NewRunnableMethod("layers::UiCompositorControllerChild::Destroy", this,
                           &UiCompositorControllerChild::Destroy),
         nsIThread::DISPATCH_SYNC);
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -93,17 +93,18 @@ void WebRenderBridgeChild::UpdateResourc
     return;
   }
 
   nsTArray<OpUpdateResource> resourceUpdates;
   nsTArray<RefCountedShmem> smallShmems;
   nsTArray<ipc::Shmem> largeShmems;
   aResources.Flush(resourceUpdates, smallShmems, largeShmems);
 
-  this->SendUpdateResources(resourceUpdates, smallShmems, largeShmems);
+  this->SendUpdateResources(resourceUpdates, smallShmems,
+                            std::move(largeShmems));
 }
 
 void WebRenderBridgeChild::EndTransaction(
     const wr::LayoutSize& aContentSize, wr::BuiltDisplayList& aDL,
     wr::IpcResourceUpdateQueue& aResources, const gfx::IntSize& aSize,
     TransactionId aTransactionId, const WebRenderScrollData& aScrollData,
     bool aContainsSVGGroup, const mozilla::VsyncId& aVsyncId,
     const mozilla::TimeStamp& aVsyncStartTime,
@@ -118,22 +119,22 @@ void WebRenderBridgeChild::EndTransactio
 
   TimeStamp fwdTime = TimeStamp::Now();
 
   nsTArray<OpUpdateResource> resourceUpdates;
   nsTArray<RefCountedShmem> smallShmems;
   nsTArray<ipc::Shmem> largeShmems;
   aResources.Flush(resourceUpdates, smallShmems, largeShmems);
 
-  this->SendSetDisplayList(aSize, mParentCommands, mDestroyedActors,
-                           GetFwdTransactionId(), aTransactionId, aContentSize,
-                           dlData, aDL.dl_desc, aScrollData, resourceUpdates,
-                           smallShmems, largeShmems, mIdNamespace,
-                           aContainsSVGGroup, aVsyncId, aVsyncStartTime,
-                           aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime);
+  this->SendSetDisplayList(
+      aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(),
+      aTransactionId, aContentSize, std::move(dlData), aDL.dl_desc, aScrollData,
+      resourceUpdates, smallShmems, std::move(largeShmems), mIdNamespace,
+      aContainsSVGGroup, aVsyncId, aVsyncStartTime, aRefreshStartTime,
+      aTxnStartTime, aTxnURL, fwdTime);
 
   mParentCommands.Clear();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
 void WebRenderBridgeChild::EndEmptyTransaction(
     const FocusTarget& aFocusTarget, const ScrollUpdatesMap& aUpdates,
@@ -153,18 +154,18 @@ void WebRenderBridgeChild::EndEmptyTrans
   if (aResources) {
     aResources->Flush(resourceUpdates, smallShmems, largeShmems);
     aResources.reset();
   }
 
   this->SendEmptyTransaction(
       aFocusTarget, aUpdates, aPaintSequenceNumber, mParentCommands,
       mDestroyedActors, GetFwdTransactionId(), aTransactionId, resourceUpdates,
-      smallShmems, largeShmems, mIdNamespace, aVsyncId, aVsyncStartTime,
-      aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime);
+      smallShmems, std::move(largeShmems), mIdNamespace, aVsyncId,
+      aVsyncStartTime, aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime);
   mParentCommands.Clear();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
 void WebRenderBridgeChild::ProcessWebRenderParentCommands() {
   MOZ_ASSERT(!mDestroyed);
 
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -1466,17 +1466,17 @@ impl AlphaBatchBuilder {
                                 // except we store a little more data in the filter mode and
                                 // a gpu cache handle in the user data.
                                 let surface = ctx.surfaces[raster_config.surface_index.0]
                                     .surface
                                     .as_ref()
                                     .expect("bug: surface must be allocated by now");
 
 
-                                let filter_data = &ctx.data_stores.filterdata[handle];
+                                let filter_data = &ctx.data_stores.filter_data[handle];
                                 let filter_mode : i32 = 13 |
                                     ((filter_data.data.r_func.to_int() << 28 |
                                       filter_data.data.g_func.to_int() << 24 |
                                       filter_data.data.b_func.to_int() << 20 |
                                       filter_data.data.a_func.to_int() << 16) as i32);
 
                                 let user_data = filter_data.gpu_cache_handle.as_int(gpu_cache);
 
--- a/gfx/wr/webrender/src/box_shadow.rs
+++ b/gfx/wr/webrender/src/box_shadow.rs
@@ -1,19 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, DeviceIntSize, LayoutPrimitiveInfo};
-use api::{LayoutRect, LayoutSize, LayoutVector2D, MAX_BLUR_RADIUS};
+use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, LayoutPrimitiveInfo, PrimitiveKeyKind};
+use api::MAX_BLUR_RADIUS;
+use api::units::*;
 use clip::ClipItemKey;
 use display_list_flattener::DisplayListFlattener;
 use gpu_cache::GpuCacheHandle;
 use gpu_types::BoxShadowStretchMode;
-use prim_store::{ScrollNodeAndClipChain, PrimitiveKeyKind};
+use prim_store::ScrollNodeAndClipChain;
 use render_task::RenderTaskCacheEntryHandle;
 use util::RectHelpers;
 
 #[derive(Debug, Clone, MallocSizeOf)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct BoxShadowClipSource {
     // Parameters that define the shadow and are constant.
--- a/gfx/wr/webrender/src/clip.rs
+++ b/gfx/wr/webrender/src/clip.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::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
 use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D};
 use api::{BoxShadowClipMode, LayoutToWorldScale, PicturePixel, WorldPixel};
 use api::{PictureRect, LayoutPixel, WorldPoint, WorldSize, WorldRect, LayoutToWorldTransform};
-use api::{ImageKey};
+use api::{ClipIntern, ImageKey};
 use app_units::Au;
 use border::{ensure_no_corner_overlap, BorderRadiusAu};
 use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
 use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
 use ellipse::Ellipse;
 use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
 use gpu_types::{BoxShadowStretchMode};
 use image::{self, Repetition};
@@ -99,18 +99,18 @@ use util::{extract_inner_rect_safe, proj
     | flags            | flags            | flags            | flags            | flags            |
     | ...              | ...              | ...              | ...              | ...              |
     +------------------+------------------+------------------+------------------+------------------+
 
  */
 
 // Type definitions for interning clip nodes.
 
-pub use intern_types::clip::Store as ClipDataStore;
-use intern_types::clip::Handle as ClipDataHandle;
+pub type ClipDataStore = intern::DataStore<ClipIntern>;
+type ClipDataHandle = intern::Handle<ClipIntern>;
 
 // Result of comparing a clip node instance against a local rect.
 #[derive(Debug)]
 enum ClipResult {
     // The clip does not affect the region at all.
     Accept,
     // The clip prevents the region from being drawn.
     Reject,
@@ -842,16 +842,22 @@ impl ClipItemKey {
             Au::from_f32_px(blur_radius),
             clip_mode,
         )
     }
 }
 
 impl intern::InternDebug for ClipItemKey {}
 
+impl intern::Internable for ClipIntern {
+    type Key = ClipItemKey;
+    type StoreData = ClipNode;
+    type InternData = ();
+}
+
 #[derive(Debug, MallocSizeOf)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum ClipItem {
     Rectangle(LayoutSize, ClipMode),
     RoundedRectangle(LayoutSize, BorderRadius, ClipMode),
     Image {
         image: ImageKey,
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -1,47 +1,48 @@
 /* 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::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter};
-use api::{ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use api::{DisplayItemRef, ExtendMode, ExternalScrollId, AuHelpers};
-use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, RasterSpace, GradientStop};
-use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint, ColorDepth};
-use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
-use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId};
+use api::{ClipId, ColorF, ComplexClipRegion, RasterSpace};
+use api::{DisplayItemRef, ExtendMode, ExternalScrollId};
+use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GradientStop};
+use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, ColorDepth};
+use api::{LayoutPrimitiveInfo, LineOrientation, LineStyle, NinePatchBorderSource, PipelineId};
 use api::{PropertyBinding, ReferenceFrame, ReferenceFrameKind, ScrollFrameDisplayItem, ScrollSensitivity};
-use api::{Shadow, SpaceAndClipInfo, SpatialId, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
-use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData, TempFilterData};
+use api::{Shadow, SpaceAndClipInfo, SpatialId, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem};
+use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, YuvData, TempFilterData};
+use api::units::*;
 use app_units::Au;
 use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore};
 use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
 use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
 use glyph_rasterizer::FontInstance;
 use hit_test::{HitTestingItem, HitTestingRun};
 use image::simplify_repeated_primitive;
-use intern::{Handle, Internable, InternDebug};
+use intern::Interner;
 use internal_types::{FastHashMap, FastHashSet};
 use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureOptions};
 use picture::{BlitReason, OrderedPictureChild, PrimitiveList, TileCache};
-use prim_store::{PrimitiveInstance, PrimitiveKeyKind, PrimitiveSceneData};
+use prim_store::{PrimitiveInstance, PrimitiveSceneData};
 use prim_store::{PrimitiveInstanceKind, NinePatchDescriptor, PrimitiveStore};
 use prim_store::{PrimitiveStoreStats, ScrollNodeAndClipChain, PictureIndex};
+use prim_store::InternablePrimitive;
 use prim_store::{register_prim_chase_id, get_line_decoration_sizes};
 use prim_store::borders::{ImageBorder, NormalBorderPrim};
 use prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams};
 use prim_store::image::{Image, YuvImage};
 use prim_store::line_dec::{LineDecoration, LineDecorationCacheKey};
 use prim_store::picture::{Picture, PictureCompositeKey, PictureKey};
 use prim_store::text_run::TextRun;
 use render_backend::{DocumentView};
 use resource_cache::{FontInstanceMap, ImageRequest};
 use scene::{Scene, StackingContextHelpers};
-use scene_builder::{InternerMut, Interners};
+use scene_builder::Interners;
 use spatial_node::{StickyFrameInfo, ScrollFrameKind, SpatialNodeType};
 use std::{f32, mem, usize};
 use std::collections::vec_deque::VecDeque;
 use std::sync::Arc;
 use tiling::{CompositeOps};
 use util::{MaxRect, VecHelper};
 use ::filterdata::{SFilterDataComponent, SFilterData, SFilterDataKey};
 
@@ -1143,36 +1144,35 @@ impl<'a> DisplayListFlattener<'a> {
     fn create_primitive<P>(
         &mut self,
         info: &LayoutPrimitiveInfo,
         clip_chain_id: ClipChainId,
         spatial_node_index: SpatialNodeIndex,
         prim: P,
     ) -> PrimitiveInstance
     where
-        P: Internable<InternData=PrimitiveSceneData>,
-        P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
-        Interners: InternerMut<P>,
+        P: InternablePrimitive,
+        Interners: AsMut<Interner<P>>,
     {
         // Build a primitive key.
-        let prim_key = prim.build_key(info);
+        let prim_key = prim.into_key(info);
 
-        let interner = self.interners.interner_mut();
-        let prim_data_handle =
-            interner
+        let interner = self.interners.as_mut();
+        let prim_data_handle = interner
             .intern(&prim_key, || {
                 PrimitiveSceneData {
                     prim_size: info.rect.size,
                     is_backface_visible: info.is_backface_visible,
                 }
             });
 
         let current_offset = self.rf_mapper.current_offset();
 
-        let instance_kind = prim_key.as_instance_kind(
+        let instance_kind = P::make_instance_kind(
+            prim_key,
             prim_data_handle,
             &mut self.prim_store,
             current_offset,
         );
 
         PrimitiveInstance::new(
             info.rect.origin,
             info.clip_rect,
@@ -1223,19 +1223,18 @@ impl<'a> DisplayListFlattener<'a> {
     fn add_nonshadowable_primitive<P>(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         clip_items: Vec<(LayoutPoint, ClipItemKey)>,
         prim: P,
     )
     where
-        P: Internable<InternData = PrimitiveSceneData> + IsVisible,
-        P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
-        Interners: InternerMut<P>,
+        P: InternablePrimitive + IsVisible,
+        Interners: AsMut<Interner<P>>,
     {
         if prim.is_visible() {
             let clip_chain_id = self.build_clip_chain(
                 clip_items,
                 clip_and_scroll.spatial_node_index,
                 clip_and_scroll.clip_chain_id,
             );
             self.add_prim_to_draw_list(
@@ -1250,19 +1249,18 @@ impl<'a> DisplayListFlattener<'a> {
     pub fn add_primitive<P>(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         clip_items: Vec<(LayoutPoint, ClipItemKey)>,
         prim: P,
     )
     where
-        P: Internable<InternData = PrimitiveSceneData> + IsVisible,
-        P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
-        Interners: InternerMut<P>,
+        P: InternablePrimitive + IsVisible,
+        Interners: AsMut<Interner<P>>,
         ShadowItem: From<PendingPrimitive<P>>
     {
         // If a shadow context is not active, then add the primitive
         // directly to the parent picture.
         if self.pending_shadow_items.is_empty() {
             self.add_nonshadowable_primitive(
                 clip_and_scroll,
                 info,
@@ -1285,19 +1283,18 @@ impl<'a> DisplayListFlattener<'a> {
     fn add_prim_to_draw_list<P>(
         &mut self,
         info: &LayoutPrimitiveInfo,
         clip_chain_id: ClipChainId,
         clip_and_scroll: ScrollNodeAndClipChain,
         prim: P,
     )
     where
-        P: Internable<InternData = PrimitiveSceneData>,
-        P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
-        Interners: InternerMut<P>,
+        P: InternablePrimitive,
+        Interners: AsMut<Interner<P>>,
     {
         let prim_instance = self.create_primitive(
             info,
             clip_chain_id,
             clip_and_scroll.spatial_node_index,
             prim,
         );
         self.register_chase_primitive_by_rect(
@@ -1601,17 +1598,17 @@ impl<'a> DisplayListFlattener<'a> {
                                     b_func: SFilterDataComponent::from_functype_values(
                                         filter_data.func_b_type, &filter_data.b_values),
                                     a_func: SFilterDataComponent::from_functype_values(
                                         filter_data.func_a_type, &filter_data.a_values),
                                 },
                         };
 
                         let handle = self.interners
-                            .filterdata
+                            .filter_data
                             .intern(&filter_data_key, || ());
                         PictureCompositeMode::ComponentTransferFilter(handle)
                     }
                 }
                 _ => PictureCompositeMode::Filter(filter),
             });
 
             let filter_pic_index = PictureIndex(self.prim_store.pictures
@@ -2124,19 +2121,18 @@ impl<'a> DisplayListFlattener<'a> {
 
     fn add_shadow_prim<P>(
         &mut self,
         pending_shadow: &PendingShadow,
         pending_primitive: &PendingPrimitive<P>,
         prims: &mut Vec<PrimitiveInstance>,
     )
     where
-        P: Internable<InternData=PrimitiveSceneData> + CreateShadow,
-        P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
-        Interners: InternerMut<P>,
+        P: InternablePrimitive + CreateShadow,
+        Interners: AsMut<Interner<P>>,
     {
         // Offset the local rect and clip rect by the shadow offset.
         let mut info = pending_primitive.info.clone();
         info.rect = info.rect.translate(&pending_shadow.shadow.offset);
         info.clip_rect = info.clip_rect.translate(&pending_shadow.shadow.offset);
 
         // Construct and add a primitive for the given shadow.
         let shadow_prim_instance = self.create_primitive(
@@ -2151,19 +2147,18 @@ impl<'a> DisplayListFlattener<'a> {
         // Add the new primitive to the shadow picture.
         prims.push(shadow_prim_instance);
     }
 
     fn add_shadow_prim_to_draw_list<P>(
         &mut self,
         pending_primitive: PendingPrimitive<P>,
     ) where
-        P: Internable<InternData = PrimitiveSceneData> + IsVisible,
-        P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
-        Interners: InternerMut<P>,
+        P: InternablePrimitive + IsVisible,
+        Interners: AsMut<Interner<P>>,
     {
         // For a normal primitive, if it has alpha > 0, then we add this
         // as a normal primitive to the parent picture.
         if pending_primitive.prim.is_visible() {
             self.add_prim_to_draw_list(
                 &pending_primitive.info,
                 pending_primitive.clip_and_scroll.clip_chain_id,
                 pending_primitive.clip_and_scroll,
@@ -2676,24 +2671,16 @@ impl<'a> DisplayListFlattener<'a> {
                 }
                 Picture3DContext::In { .. } => {}
                 Picture3DContext::Out => panic!("Unable to find 3D root"),
             }
         }
     }
 }
 
-pub trait AsInstanceKind<H> {
-    fn as_instance_kind(
-        &self,
-        data_handle: H,
-        prim_store: &mut PrimitiveStore,
-        reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind;
-}
 
 pub trait CreateShadow {
     fn create_shadow(&self, shadow: &Shadow) -> Self;
 }
 
 pub trait IsVisible {
     fn is_visible(&self) -> bool;
 }
--- a/gfx/wr/webrender/src/filterdata.rs
+++ b/gfx/wr/webrender/src/filterdata.rs
@@ -2,20 +2,20 @@
  * 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 std::{hash};
 use gpu_cache::{GpuCacheHandle};
 use frame_builder::FrameBuildingState;
 use gpu_cache::GpuDataRequest;
 use intern;
-use api::{ComponentTransferFuncType};
+use api::{FilterDataIntern, ComponentTransferFuncType};
 
 
-pub use intern_types::filterdata::Handle as FilterDataHandle;
+pub type FilterDataHandle = intern::Handle<FilterDataIntern>;
 
 #[derive(Debug, Clone, MallocSizeOf, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum SFilterDataComponent {
     Identity,
     Table(Vec<f32>),
     Discrete(Vec<f32>),
@@ -139,16 +139,22 @@ impl SFilterDataTemplate {
             assert!(self.data.r_func != SFilterDataComponent::Identity
                  || self.data.g_func != SFilterDataComponent::Identity
                  || self.data.b_func != SFilterDataComponent::Identity
                  || self.data.a_func != SFilterDataComponent::Identity);
         }
     }
 }
 
+impl intern::Internable for FilterDataIntern {
+    type Key = SFilterDataKey;
+    type StoreData = SFilterDataTemplate;
+    type InternData = ();
+}
+
 fn push_component_transfer_data(
     func_comp: &SFilterDataComponent,
     request: &mut GpuDataRequest,
 ) {
     match func_comp {
         SFilterDataComponent::Identity => { return; }
         SFilterDataComponent::Table(values) |
         SFilterDataComponent::Discrete(values) => {
--- a/gfx/wr/webrender/src/intern.rs
+++ b/gfx/wr/webrender/src/intern.rs
@@ -28,17 +28,16 @@
 //! free-list structure, for content access and memory
 //! usage efficiency.
 //!
 //! The epoch is incremented each time a scene is
 //! built. The most recently used scene epoch is
 //! stored inside each handle. This is then used for
 //! cache invalidation.
 
-use api::{LayoutPrimitiveInfo};
 use internal_types::FastHashMap;
 use malloc_size_of::MallocSizeOf;
 use profiler::ResourceProfileCounter;
 use std::fmt::Debug;
 use std::hash::Hash;
 use std::marker::PhantomData;
 use std::{mem, ops, u64};
 use std::sync::atomic::{AtomicUsize, Ordering};
@@ -74,25 +73,38 @@ impl ItemUid {
     pub fn next_uid() -> ItemUid {
         let uid = NEXT_UID.fetch_add(1, Ordering::Relaxed);
         ItemUid { uid }
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
-#[derive(Debug, Copy, Clone, MallocSizeOf)]
-pub struct Handle<M: Copy> {
+#[derive(Debug, MallocSizeOf)]
+pub struct Handle<I> {
     index: u32,
     epoch: Epoch,
     uid: ItemUid,
-    _marker: PhantomData<M>,
+    _marker: PhantomData<I>,
 }
 
-impl <M> Handle<M> where M: Copy {
+impl<I> Clone for Handle<I> {
+    fn clone(&self) -> Self {
+        Handle {
+            index: self.index,
+            epoch: self.epoch,
+            uid: self.uid,
+            _marker: self._marker,
+        }
+    }
+}
+
+impl<I> Copy for Handle<I> {}
+
+impl<I> Handle<I> {
     pub fn uid(&self) -> ItemUid {
         self.uid
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
@@ -113,161 +125,124 @@ pub trait InternDebug {
     fn on_interned(&self, _uid: ItemUid) {}
 }
 
 /// The data store lives in the frame builder thread. It
 /// contains a free-list of items for fast access.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
-pub struct DataStore<S, T: MallocSizeOf, M> {
-    items: Vec<Option<T>>,
-    _source: PhantomData<S>,
-    _marker: PhantomData<M>,
+pub struct DataStore<I: Internable> {
+    items: Vec<Option<I::StoreData>>,
 }
 
-impl<S, T, M> ::std::default::Default for DataStore<S, T, M>
-where
-    S: Debug + MallocSizeOf,
-    T: From<S> + MallocSizeOf,
-    M: Debug
-{
+impl<I: Internable> Default for DataStore<I> {
     fn default() -> Self {
         DataStore {
             items: Vec::new(),
-            _source: PhantomData,
-            _marker: PhantomData,
         }
     }
 }
 
-impl<S, T, M> DataStore<S, T, M>
-where
-    S: Debug + MallocSizeOf,
-    T: From<S> + MallocSizeOf,
-    M: Debug
-{
+impl<I: Internable> DataStore<I> {
     /// Apply any updates from the scene builder thread to
     /// this data store.
     pub fn apply_updates(
         &mut self,
-        update_list: UpdateList<S>,
+        update_list: UpdateList<I::Key>,
         profile_counter: &mut ResourceProfileCounter,
     ) {
         let mut data_iter = update_list.data.into_iter();
         for update in update_list.updates {
             match update.kind {
                 UpdateKind::Insert => {
-                    self.items.entry(update.index).
-                        set(Some(T::from(data_iter.next().unwrap())));
+                    let value = data_iter.next().unwrap().into();
+                    self.items
+                        .entry(update.index)
+                        .set(Some(value));
                 }
                 UpdateKind::Remove => {
                     self.items[update.index] = None;
                 }
             }
         }
 
-        let per_item_size = mem::size_of::<S>() + mem::size_of::<T>();
+        let per_item_size = mem::size_of::<I::Key>() + mem::size_of::<I::StoreData>();
         profile_counter.set(self.items.len(), per_item_size * self.items.len());
 
         debug_assert!(data_iter.next().is_none());
     }
 }
 
 /// Retrieve an item from the store via handle
-impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M>
-where
-    S: MallocSizeOf,
-    T: MallocSizeOf,
-    M: Copy
-{
-    type Output = T;
-    fn index(&self, handle: Handle<M>) -> &T {
+impl<I: Internable> ops::Index<Handle<I>> for DataStore<I> {
+    type Output = I::StoreData;
+    fn index(&self, handle: Handle<I>) -> &I::StoreData {
         self.items[handle.index as usize].as_ref().expect("Bad datastore lookup")
     }
 }
 
 /// Retrieve a mutable item from the store via handle
 /// Retrieve an item from the store via handle
-impl<S, T, M> ops::IndexMut<Handle<M>> for DataStore<S, T, M>
-where
-    S: MallocSizeOf,
-    T: MallocSizeOf,
-    M: Copy
-{
-    fn index_mut(&mut self, handle: Handle<M>) -> &mut T {
+impl<I: Internable> ops::IndexMut<Handle<I>> for DataStore<I> {
+    fn index_mut(&mut self, handle: Handle<I>) -> &mut I::StoreData {
         self.items[handle.index as usize].as_mut().expect("Bad datastore lookup")
     }
 }
 
 /// The main interning data structure. This lives in the
 /// scene builder thread, and handles hashing and interning
 /// unique data structures. It also manages a free-list for
 /// the items in the data store, which is synchronized via
 /// an update list of additions / removals.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
-pub struct Interner<S, D, M>
-where
-    S: Eq + Hash + Clone + Debug + MallocSizeOf,
-    D: MallocSizeOf,
-    M: Copy + MallocSizeOf,
-{
+pub struct Interner<I: Internable> {
     /// Uniquely map an interning key to a handle
-    map: FastHashMap<S, Handle<M>>,
+    map: FastHashMap<I::Key, Handle<I>>,
     /// List of free slots in the data store for re-use.
     free_list: Vec<usize>,
     /// Pending list of updates that need to be applied.
     updates: Vec<Update>,
     /// Pending new data to insert.
-    update_data: Vec<S>,
+    update_data: Vec<I::Key>,
     /// The current epoch for the interner.
     current_epoch: Epoch,
     /// The information associated with each interned
     /// item that can be accessed by the interner.
-    local_data: Vec<D>,
+    local_data: Vec<I::InternData>,
 }
 
-impl<S, D, M> ::std::default::Default for Interner<S, D, M>
-where
-    S: Eq + Hash + Clone + Debug + MallocSizeOf,
-    D: MallocSizeOf,
-    M: Copy + Debug + MallocSizeOf,
-{
+impl<I: Internable> Default for Interner<I> {
     fn default() -> Self {
         Interner {
             map: FastHashMap::default(),
             free_list: Vec::new(),
             updates: Vec::new(),
             update_data: Vec::new(),
             current_epoch: Epoch(1),
             local_data: Vec::new(),
         }
     }
 }
 
-impl<S, D, M> Interner<S, D, M>
-where
-    S: Eq + Hash + Clone + Debug + InternDebug + MallocSizeOf,
-    D: MallocSizeOf,
-    M: Copy + Debug + MallocSizeOf
-{
+impl<I: Internable> Interner<I> {
     /// Intern a data structure, and return a handle to
     /// that data. The handle can then be stored in the
     /// frame builder, and safely accessed via the data
     /// store that lives in the frame builder thread.
     /// The provided closure is invoked to build the
     /// local data about an interned structure if the
     /// key isn't already interned.
     pub fn intern<F>(
         &mut self,
-        data: &S,
-        f: F,
-    ) -> Handle<M> where F: FnOnce() -> D {
+        data: &I::Key,
+        fun: F,
+    ) -> Handle<I> where F: FnOnce() -> I::InternData {
         // Use get_mut rather than entry here to avoid
         // cloning the (sometimes large) key in the common
         // case, where the data already exists in the interner.
         if let Some(handle) = self.map.get_mut(data) {
             handle.epoch = self.current_epoch;
             return *handle;
         }
 
@@ -298,25 +273,25 @@ where
         data.on_interned(handle.uid);
 
         // Store this handle so the next time it is
         // interned, it gets re-used.
         self.map.insert(data.clone(), handle);
 
         // Create the local data for this item that is
         // being interned.
-        self.local_data.entry(index).set(f());
+        self.local_data.entry(index).set(fun());
 
         handle
     }
 
     /// Retrieve the pending list of updates for an interner
     /// that need to be applied to the data store. Also run
     /// a GC step that removes old entries.
-    pub fn end_frame_and_get_pending_updates(&mut self) -> UpdateList<S> {
+    pub fn end_frame_and_get_pending_updates(&mut self) -> UpdateList<I::Key> {
         let mut updates = self.updates.take_and_preallocate();
         let data = self.update_data.take_and_preallocate();
 
         let free_list = &mut self.free_list;
         let current_epoch = self.current_epoch.0;
 
         // First, run a GC step. Walk through the handles, and
         // if we find any that haven't been used for some time,
@@ -349,35 +324,41 @@ where
         // Begin the next epoch
         self.current_epoch = Epoch(self.current_epoch.0 + 1);
 
         updates
     }
 }
 
 /// Retrieve the local data for an item from the interner via handle
-impl<S, D, M> ops::Index<Handle<M>> for Interner<S, D, M>
-where
-    S: Eq + Clone + Hash + Debug + MallocSizeOf,
-    D: MallocSizeOf,
-    M: Copy + Debug + MallocSizeOf
-{
-    type Output = D;
-    fn index(&self, handle: Handle<M>) -> &D {
+impl<I: Internable> ops::Index<Handle<I>> for Interner<I> {
+    type Output = I::InternData;
+    fn index(&self, handle: Handle<I>) -> &I::InternData {
         &self.local_data[handle.index as usize]
     }
 }
 
-/// Implement `Internable` for a type that wants participate in interning.
-///
-/// see DisplayListFlattener::add_interned_primitive<P>
-pub trait Internable {
-    type Marker: Copy + Debug + MallocSizeOf;
-    type Source: Eq + Hash + Clone + Debug + MallocSizeOf;
-    type StoreData: From<Self::Source> + MallocSizeOf;
-    type InternData: MallocSizeOf;
+// The trick to make trait bounds configurable by features.
+mod dummy {
+    #[cfg(not(feature = "capture"))]
+    pub trait Serialize {}
+    #[cfg(not(feature = "capture"))]
+    impl<T> Serialize for T {}
+    #[cfg(not(feature = "replay"))]
+    pub trait Deserialize<'a> {}
+    #[cfg(not(feature = "replay"))]
+    impl<'a, T> Deserialize<'a> for T {}
+}
+#[cfg(feature = "capture")]
+use serde::Serialize as InternSerialize;
+#[cfg(not(feature = "capture"))]
+use self::dummy::Serialize as InternSerialize;
+#[cfg(feature = "replay")]
+use serde::Deserialize as InternDeserialize;
+#[cfg(not(feature = "replay"))]
+use self::dummy::Deserialize as InternDeserialize;
 
-    /// Build a new key from self with `info`.
-    fn build_key(
-        self,
-        info: &LayoutPrimitiveInfo,
-    ) -> Self::Source;
+/// Implement `Internable` for a type that wants to participate in interning.
+pub trait Internable: MallocSizeOf {
+    type Key: Eq + Hash + Clone + Debug + MallocSizeOf + InternDebug + InternSerialize + for<'a> InternDeserialize<'a>;
+    type StoreData: From<Self::Key> + MallocSizeOf + InternSerialize + for<'a> InternDeserialize<'a>;
+    type InternData: MallocSizeOf + InternSerialize + for<'a> InternDeserialize<'a>;
 }
deleted file mode 100644
--- a/gfx/wr/webrender/src/intern_types.rs
+++ /dev/null
@@ -1,114 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-macro_rules! common {
-    () => (
-        use ::intern;
-        #[allow(unused_imports)]
-        use ::prim_store::PrimitiveSceneData;
-        #[cfg_attr(feature = "capture", derive(Serialize))]
-        #[cfg_attr(feature = "replay", derive(Deserialize))]
-        #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
-        pub struct Marker;
-        pub type Handle = intern::Handle<Marker>;
-    )
-}
-
-pub mod clip {
-common!();
-use ::clip::{ClipItemKey, ClipNode};
-pub type Store = intern::DataStore<ClipItemKey, ClipNode, Marker>;
-pub type UpdateList = intern::UpdateList<ClipItemKey>;
-pub type Interner = intern::Interner<ClipItemKey, (), Marker>;
-}
-
-pub mod prim {
-common!();
-use ::prim_store::{PrimitiveKey, PrimitiveTemplate};
-pub type Store = intern::DataStore<PrimitiveKey, PrimitiveTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<PrimitiveKey>;
-pub type Interner = intern::Interner<PrimitiveKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod normal_border {
-common!();
-use ::prim_store::borders::{NormalBorderKey, NormalBorderTemplate};
-pub type Store = intern::DataStore<NormalBorderKey, NormalBorderTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<NormalBorderKey>;
-pub type Interner = intern::Interner<NormalBorderKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod image_border {
-common!();
-use ::prim_store::borders::{ImageBorderKey, ImageBorderTemplate};
-pub type Store = intern::DataStore<ImageBorderKey, ImageBorderTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<ImageBorderKey>;
-pub type Interner = intern::Interner<ImageBorderKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod image {
-common!();
-use ::prim_store::image::{ImageKey, ImageTemplate};
-pub type Store = intern::DataStore<ImageKey, ImageTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<ImageKey>;
-pub type Interner = intern::Interner<ImageKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod yuv_image {
-common!();
-use ::prim_store::image::{YuvImageKey, YuvImageTemplate};
-pub type Store = intern::DataStore<YuvImageKey, YuvImageTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<YuvImageKey>;
-pub type Interner = intern::Interner<YuvImageKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod line_decoration {
-use ::prim_store::line_dec::{LineDecorationKey, LineDecorationTemplate};
-common!();
-pub type Store = intern::DataStore<LineDecorationKey, LineDecorationTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<LineDecorationKey>;
-pub type Interner = intern::Interner<LineDecorationKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod linear_grad {
-common!();
-use ::prim_store::gradient::{LinearGradientKey, LinearGradientTemplate};
-pub type Store = intern::DataStore<LinearGradientKey, LinearGradientTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<LinearGradientKey>;
-pub type Interner = intern::Interner<LinearGradientKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod radial_grad {
-common!();
-use ::prim_store::gradient::{RadialGradientKey, RadialGradientTemplate};
-pub type Store = intern::DataStore<RadialGradientKey, RadialGradientTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<RadialGradientKey>;
-pub type Interner = intern::Interner<RadialGradientKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod picture {
-common!();
-use ::prim_store::picture::{PictureKey, PictureTemplate};
-pub type Store = intern::DataStore<PictureKey, PictureTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<PictureKey>;
-pub type Interner = intern::Interner<PictureKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod text_run {
-common!();
-use ::prim_store::text_run::{TextRunKey, TextRunTemplate};
-pub type Store = intern::DataStore<TextRunKey, TextRunTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<TextRunKey>;
-pub type Interner = intern::Interner<TextRunKey, PrimitiveSceneData, Marker>;
-}
-
-pub mod filterdata {
-common!();
-use ::filterdata::{SFilterDataKey, SFilterDataTemplate};
-pub type Store = intern::DataStore<SFilterDataKey, SFilterDataTemplate, Marker>;
-pub type UpdateList = intern::UpdateList<SFilterDataKey>;
-pub type Interner = intern::Interner<SFilterDataKey, (), Marker>;
-}
-
-
--- a/gfx/wr/webrender/src/lib.rs
+++ b/gfx/wr/webrender/src/lib.rs
@@ -99,17 +99,16 @@ mod glyph_cache;
 mod glyph_rasterizer;
 mod gpu_cache;
 #[cfg(feature = "pathfinder")]
 mod gpu_glyph_renderer;
 mod gpu_types;
 mod hit_test;
 mod image;
 mod intern;
-mod intern_types;
 mod internal_types;
 mod picture;
 mod prim_store;
 mod print_tree;
 mod record;
 mod render_backend;
 mod render_task;
 mod renderer;
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -3109,17 +3109,17 @@ impl PicturePrimitive {
                     device_pixel_scale,
                 );
 
                 let render_task_id = frame_state.render_tasks.add(picture_task);
                 frame_state.surfaces[surface_index.0].tasks.push(render_task_id);
                 PictureSurface::RenderTask(render_task_id)
             }
             PictureCompositeMode::ComponentTransferFilter(handle) => {
-                let filter_data = &mut data_stores.filterdata[handle];
+                let filter_data = &mut data_stores.filter_data[handle];
                 filter_data.update(frame_state);
 
                 let uv_rect_kind = calculate_uv_rect_kind(
                     &pic_rect,
                     &transform,
                     &clipped,
                     device_pixel_scale,
                     true,
--- a/gfx/wr/webrender/src/prim_store/borders.rs
+++ b/gfx/wr/webrender/src/prim_store/borders.rs
@@ -4,26 +4,25 @@
 
 use api::{
     AuHelpers, LayoutPrimitiveInfo, LayoutSideOffsets,
     LayoutSideOffsetsAu, LayoutSize, NormalBorder, PremultipliedColorF,
     Shadow, LayoutVector2D,
 };
 use border::create_border_segments;
 use border::NormalBorderAu;
-use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
+use display_list_flattener::{CreateShadow, IsVisible};
 use frame_builder::{FrameBuildingState};
 use gpu_cache::GpuDataRequest;
 use intern;
-use intern_types;
 use prim_store::{
     BorderSegmentInfo, BrushSegment, NinePatchDescriptor, PrimKey,
     PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
     PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData,
-    PrimitiveStore
+    PrimitiveStore, InternablePrimitive,
 };
 use resource_cache::ImageRequest;
 use storage;
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
 pub struct NormalBorderPrim {
@@ -44,32 +43,16 @@ impl NormalBorderKey {
             ),
             kind: normal_border,
         }
     }
 }
 
 impl intern::InternDebug for NormalBorderKey {}
 
-impl AsInstanceKind<NormalBorderDataHandle> for NormalBorderKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        data_handle: NormalBorderDataHandle,
-        _: &mut PrimitiveStore,
-        _reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        PrimitiveInstanceKind::NormalBorder {
-            data_handle,
-            cache_handles: storage::Range::empty(),
-        }
-    }
-}
-
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct NormalBorderData {
     pub brush_segments: Vec<BrushSegment>,
     pub border_segments: Vec<BorderSegmentInfo>,
     pub border: NormalBorder,
     pub widths: LayoutSideOffsets,
@@ -155,34 +138,46 @@ impl From<NormalBorderKey> for NormalBor
                 border_segments,
                 border,
                 widths,
             }
         }
     }
 }
 
-pub use intern_types::normal_border::Handle as NormalBorderDataHandle;
+pub type NormalBorderDataHandle = intern::Handle<NormalBorderPrim>;
 
 impl intern::Internable for NormalBorderPrim {
-    type Marker = intern_types::normal_border::Marker;
-    type Source = NormalBorderKey;
+    type Key = NormalBorderKey;
     type StoreData = NormalBorderTemplate;
     type InternData = PrimitiveSceneData;
+}
 
-    /// Build a new key from self with `info`.
-    fn build_key(
+impl InternablePrimitive for NormalBorderPrim {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> NormalBorderKey {
         NormalBorderKey::new(
             info,
             self,
         )
     }
+
+    fn make_instance_kind(
+        _key: NormalBorderKey,
+        data_handle: NormalBorderDataHandle,
+        _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        PrimitiveInstanceKind::NormalBorder {
+            data_handle,
+            cache_handles: storage::Range::empty(),
+        }
+    }
 }
 
 impl CreateShadow for NormalBorderPrim {
     fn create_shadow(&self, shadow: &Shadow) -> Self {
         let border = self.border.with_color(shadow.color.into());
         NormalBorderPrim {
             border,
             widths: self.widths,
@@ -220,30 +215,16 @@ impl ImageBorderKey {
             ),
             kind: image_border,
         }
     }
 }
 
 impl intern::InternDebug for ImageBorderKey {}
 
-impl AsInstanceKind<ImageBorderDataHandle> for ImageBorderKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        data_handle: ImageBorderDataHandle,
-        _: &mut PrimitiveStore,
-        _reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        PrimitiveInstanceKind::ImageBorder {
-            data_handle
-        }
-    }
-}
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct ImageBorderData {
     #[ignore_malloc_size_of = "Arc"]
     pub request: ImageRequest,
     pub brush_segments: Vec<BrushSegment>,
@@ -325,34 +306,45 @@ impl From<ImageBorderKey> for ImageBorde
             kind: ImageBorderData {
                 request: key.kind.request,
                 brush_segments,
             }
         }
     }
 }
 
-pub use intern_types::image_border::Handle as ImageBorderDataHandle;
+pub type ImageBorderDataHandle = intern::Handle<ImageBorder>;
 
 impl intern::Internable for ImageBorder {
-    type Marker = intern_types::image_border::Marker;
-    type Source = ImageBorderKey;
+    type Key = ImageBorderKey;
     type StoreData = ImageBorderTemplate;
     type InternData = PrimitiveSceneData;
+}
 
-    /// Build a new key from self with `info`.
-    fn build_key(
+impl InternablePrimitive for ImageBorder {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> ImageBorderKey {
         ImageBorderKey::new(
             info,
             self,
         )
     }
+
+    fn make_instance_kind(
+        _key: ImageBorderKey,
+        data_handle: ImageBorderDataHandle,
+        _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        PrimitiveInstanceKind::ImageBorder {
+            data_handle
+        }
+    }
 }
 
 impl IsVisible for ImageBorder {
     fn is_visible(&self) -> bool {
         true
     }
 }
 
--- a/gfx/wr/webrender/src/prim_store/gradient.rs
+++ b/gfx/wr/webrender/src/prim_store/gradient.rs
@@ -1,25 +1,24 @@
 /* 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::{
     ColorF, ColorU,ExtendMode, GradientStop, LayoutPoint, LayoutSize,
     LayoutPrimitiveInfo, PremultipliedColorF, LayoutVector2D,
 };
-use display_list_flattener::{AsInstanceKind, IsVisible};
+use display_list_flattener::IsVisible;
 use frame_builder::FrameBuildingState;
 use gpu_cache::{GpuCacheHandle, GpuDataRequest};
-use intern::{Internable, InternDebug};
-use intern_types;
+use intern::{Internable, InternDebug, Handle as InternHandle};
 use prim_store::{BrushSegment, GradientTileRange};
 use prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData};
 use prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore};
-use prim_store::{NinePatchDescriptor, PointKey, SizeKey};
+use prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive};
 use std::{hash, ops::{Deref, DerefMut}, mem};
 use util::pack_as_float;
 
 /// A hashable gradient stop that can be used in primitive keys.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, MallocSizeOf, PartialEq)]
 pub struct GradientStopKey {
@@ -72,32 +71,16 @@ impl LinearGradientKey {
             reverse_stops: linear_grad.reverse_stops,
             nine_patch: linear_grad.nine_patch,
         }
     }
 }
 
 impl InternDebug for LinearGradientKey {}
 
-impl AsInstanceKind<LinearGradientDataHandle> for LinearGradientKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        data_handle: LinearGradientDataHandle,
-        _prim_store: &mut PrimitiveStore,
-        _reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        PrimitiveInstanceKind::LinearGradient {
-            data_handle,
-            visible_tiles_range: GradientTileRange::empty(),
-        }
-    }
-}
-
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct LinearGradientTemplate {
     pub common: PrimTemplateCommonData,
     pub extend_mode: ExtendMode,
     pub start_point: LayoutPoint,
     pub end_point: LayoutPoint,
@@ -222,46 +205,61 @@ impl LinearGradientTemplate {
                 self.stops_opacity
             } else {
                PrimitiveOpacity::translucent()
             }
         }
     }
 }
 
-pub type LinearGradientDataHandle = intern_types::linear_grad::Handle;
+pub type LinearGradientDataHandle = InternHandle<LinearGradient>;
 
+#[derive(Debug, MallocSizeOf)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct LinearGradient {
     pub extend_mode: ExtendMode,
     pub start_point: PointKey,
     pub end_point: PointKey,
     pub stretch_size: SizeKey,
     pub tile_spacing: SizeKey,
     pub stops: Vec<GradientStopKey>,
     pub reverse_stops: bool,
     pub nine_patch: Option<Box<NinePatchDescriptor>>,
 }
 
 impl Internable for LinearGradient {
-    type Marker = intern_types::linear_grad::Marker;
-    type Source = LinearGradientKey;
+    type Key = LinearGradientKey;
     type StoreData = LinearGradientTemplate;
     type InternData = PrimitiveSceneData;
+}
 
-    /// Build a new key from self with `info`.
-    fn build_key(
+impl InternablePrimitive for LinearGradient {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> LinearGradientKey {
         LinearGradientKey::new(
             info.is_backface_visible,
             info.rect.size,
             self
         )
     }
+
+    fn make_instance_kind(
+        _key: LinearGradientKey,
+        data_handle: LinearGradientDataHandle,
+        _prim_store: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        PrimitiveInstanceKind::LinearGradient {
+            data_handle,
+            visible_tiles_range: GradientTileRange::empty(),
+        }
+    }
 }
 
 impl IsVisible for LinearGradient {
     fn is_visible(&self) -> bool {
         true
     }
 }
 
@@ -321,32 +319,16 @@ impl RadialGradientKey {
             tile_spacing: radial_grad.tile_spacing,
             nine_patch: radial_grad.nine_patch,
         }
     }
 }
 
 impl InternDebug for RadialGradientKey {}
 
-impl AsInstanceKind<RadialGradientDataHandle> for RadialGradientKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        data_handle: RadialGradientDataHandle,
-        _prim_store: &mut PrimitiveStore,
-        _reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        PrimitiveInstanceKind::RadialGradient {
-            data_handle,
-            visible_tiles_range: GradientTileRange::empty(),
-        }
-    }
-}
-
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct RadialGradientTemplate {
     pub common: PrimTemplateCommonData,
     pub extend_mode: ExtendMode,
     pub center: LayoutPoint,
     pub params: RadialGradientParams,
@@ -442,45 +424,60 @@ impl RadialGradientTemplate {
                 &self.stops,
             );
         }
 
         self.opacity = PrimitiveOpacity::translucent();
     }
 }
 
-pub type RadialGradientDataHandle = intern_types::radial_grad::Handle;
+pub type RadialGradientDataHandle = InternHandle<RadialGradient>;
 
+#[derive(Debug, MallocSizeOf)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct RadialGradient {
     pub extend_mode: ExtendMode,
     pub center: PointKey,
     pub params: RadialGradientParams,
     pub stretch_size: SizeKey,
     pub stops: Vec<GradientStopKey>,
     pub tile_spacing: SizeKey,
     pub nine_patch: Option<Box<NinePatchDescriptor>>,
 }
 
 impl Internable for RadialGradient {
-    type Marker = intern_types::radial_grad::Marker;
-    type Source = RadialGradientKey;
+    type Key = RadialGradientKey;
     type StoreData = RadialGradientTemplate;
     type InternData = PrimitiveSceneData;
+}
 
-    /// Build a new key from self with `info`.
-    fn build_key(
+impl InternablePrimitive for RadialGradient {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> RadialGradientKey {
         RadialGradientKey::new(
             info.is_backface_visible,
             info.rect.size,
             self,
         )
     }
+
+    fn make_instance_kind(
+        _key: RadialGradientKey,
+        data_handle: RadialGradientDataHandle,
+        _prim_store: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        PrimitiveInstanceKind::RadialGradient {
+            data_handle,
+            visible_tiles_range: GradientTileRange::empty(),
+        }
+    }
 }
 
 impl IsVisible for RadialGradient {
     fn is_visible(&self) -> bool {
         true
     }
 }
 
--- a/gfx/wr/webrender/src/prim_store/image.rs
+++ b/gfx/wr/webrender/src/prim_store/image.rs
@@ -3,26 +3,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{
     AlphaType, ColorDepth, ColorF, ColorU, DeviceIntRect, DeviceIntSideOffsets,
     DeviceIntSize, ImageRendering, LayoutRect, LayoutSize, LayoutPrimitiveInfo,
     PremultipliedColorF, Shadow, TileOffset, YuvColorSpace, YuvFormat, LayoutVector2D,
 };
 use api::ImageKey as ApiImageKey;
-use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
+use display_list_flattener::{CreateShadow, IsVisible};
 use frame_builder::FrameBuildingState;
 use gpu_cache::{GpuDataRequest};
-use intern::{Internable, InternDebug};
-use intern_types;
+use intern::{Internable, InternDebug, Handle as InternHandle};
 use prim_store::{
     EdgeAaSegmentMask, OpacityBindingIndex, PrimitiveInstanceKind,
     PrimitiveOpacity, PrimitiveSceneData, PrimKey, PrimKeyCommonData,
     PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex,
-    SizeKey
+    SizeKey, InternablePrimitive,
 };
 use render_task::{
     BlitSource, RenderTask, RenderTaskCacheEntryHandle, RenderTaskCacheKey,
     RenderTaskCacheKeyKind
 };
 use resource_cache::ImageRequest;
 use util::pack_as_float;
 
@@ -96,41 +95,16 @@ impl ImageKey {
             },
             kind: image,
         }
     }
 }
 
 impl InternDebug for ImageKey {}
 
-impl AsInstanceKind<ImageDataHandle> for ImageKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        data_handle: ImageDataHandle,
-        prim_store: &mut PrimitiveStore,
-        _reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        // TODO(gw): Refactor this to not need a separate image
-        //           instance (see ImageInstance struct).
-        let image_instance_index = prim_store.images.push(ImageInstance {
-            opacity_binding_index: OpacityBindingIndex::INVALID,
-            segment_instance_index: SegmentInstanceIndex::INVALID,
-            tight_local_clip_rect: LayoutRect::zero(),
-            visible_tiles: Vec::new(),
-        });
-
-        PrimitiveInstanceKind::Image {
-            data_handle,
-            image_instance_index,
-        }
-    }
-}
-
 // Where to find the texture data for an image primitive.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, MallocSizeOf)]
 pub enum ImageSource {
     // A normal image - just reference the texture cache.
     Default,
     // An image that is pre-rendered into the texture cache
@@ -333,35 +307,56 @@ impl From<ImageKey> for ImageTemplate {
 
         ImageTemplate {
             common,
             kind: image.kind.into(),
         }
     }
 }
 
-pub use intern_types::image::Handle as ImageDataHandle;
+pub type ImageDataHandle = InternHandle<Image>;
 
 impl Internable for Image {
-    type Marker = intern_types::image::Marker;
-    type Source = ImageKey;
+    type Key = ImageKey;
     type StoreData = ImageTemplate;
     type InternData = PrimitiveSceneData;
+}
 
-    /// Build a new key from self with `info`.
-    fn build_key(
+impl InternablePrimitive for Image {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> ImageKey {
         ImageKey::new(
             info.is_backface_visible,
             info.rect.size,
             self
         )
     }
+
+    fn make_instance_kind(
+        _key: ImageKey,
+        data_handle: ImageDataHandle,
+        prim_store: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        // TODO(gw): Refactor this to not need a separate image
+        //           instance (see ImageInstance struct).
+        let image_instance_index = prim_store.images.push(ImageInstance {
+            opacity_binding_index: OpacityBindingIndex::INVALID,
+            segment_instance_index: SegmentInstanceIndex::INVALID,
+            tight_local_clip_rect: LayoutRect::zero(),
+            visible_tiles: Vec::new(),
+        });
+
+        PrimitiveInstanceKind::Image {
+            data_handle,
+            image_instance_index,
+        }
+    }
 }
 
 impl CreateShadow for Image {
     fn create_shadow(&self, shadow: &Shadow) -> Self {
         Image {
             tile_spacing: self.tile_spacing,
             stretch_size: self.stretch_size,
             key: self.key,
@@ -408,32 +403,16 @@ impl YuvImageKey {
             },
             kind: yuv_image,
         }
     }
 }
 
 impl InternDebug for YuvImageKey {}
 
-impl AsInstanceKind<YuvImageDataHandle> for YuvImageKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        data_handle: YuvImageDataHandle,
-        _prim_store: &mut PrimitiveStore,
-        _reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        PrimitiveInstanceKind::YuvImage {
-            data_handle,
-            segment_instance_index: SegmentInstanceIndex::INVALID
-        }
-    }
-}
-
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct YuvImageData {
     pub color_depth: ColorDepth,
     pub yuv_key: [ApiImageKey; 3],
     pub format: YuvFormat,
     pub color_space: YuvColorSpace,
@@ -500,35 +479,47 @@ impl From<YuvImageKey> for YuvImageTempl
 
         YuvImageTemplate {
             common,
             kind: image.kind.into(),
         }
     }
 }
 
-pub use intern_types::yuv_image::Handle as YuvImageDataHandle;
+pub type YuvImageDataHandle = InternHandle<YuvImage>;
 
 impl Internable for YuvImage {
-    type Marker = intern_types::yuv_image::Marker;
-    type Source = YuvImageKey;
+    type Key = YuvImageKey;
     type StoreData = YuvImageTemplate;
     type InternData = PrimitiveSceneData;
+}
 
-    /// Build a new key from self with `info`.
-    fn build_key(
+impl InternablePrimitive for YuvImage {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> YuvImageKey {
         YuvImageKey::new(
             info.is_backface_visible,
             info.rect.size,
-            self
+            self,
         )
     }
+
+    fn make_instance_kind(
+        _key: YuvImageKey,
+        data_handle: YuvImageDataHandle,
+        _prim_store: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        PrimitiveInstanceKind::YuvImage {
+            data_handle,
+            segment_instance_index: SegmentInstanceIndex::INVALID
+        }
+    }
 }
 
 impl IsVisible for YuvImage {
     fn is_visible(&self) -> bool {
         true
     }
 }
 
new file mode 100644
--- /dev/null
+++ b/gfx/wr/webrender/src/prim_store/interned.rs
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// list of all interned primitives to match enumerate_interners!
+
+pub use prim_store::borders::{ImageBorder, NormalBorderPrim};
+pub use prim_store::image::{Image, YuvImage};
+pub use prim_store::line_dec::{LineDecoration};
+pub use prim_store::gradient::{LinearGradient, RadialGradient};
+pub use prim_store::picture::Picture;
+pub use prim_store::text_run::TextRun;
--- a/gfx/wr/webrender/src/prim_store/line_dec.rs
+++ b/gfx/wr/webrender/src/prim_store/line_dec.rs
@@ -2,27 +2,27 @@
  * 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::{
     ColorF, ColorU, LayoutPrimitiveInfo, LayoutSizeAu, LayoutVector2D,
     LineOrientation, LineStyle, PremultipliedColorF, Shadow,
 };
 use app_units::Au;
-use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
+use display_list_flattener::{CreateShadow, IsVisible};
 use frame_builder::{FrameBuildingState};
 use gpu_cache::GpuDataRequest;
 use intern;
-use intern_types;
 use prim_store::{
     PrimKey, PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
-    PrimitiveSceneData, PrimitiveStore,
+    InternablePrimitive, PrimitiveSceneData, PrimitiveStore,
 };
 use prim_store::PrimitiveInstanceKind;
 
+
 #[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct LineDecorationCacheKey {
     pub style: LineStyle,
     pub orientation: LineOrientation,
     pub wavy_line_thickness: Au,
     pub size: LayoutSizeAu,
@@ -54,32 +54,16 @@ impl LineDecorationKey {
             ),
             kind: line_dec,
         }
     }
 }
 
 impl intern::InternDebug for LineDecorationKey {}
 
-impl AsInstanceKind<LineDecorationDataHandle> for LineDecorationKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        data_handle: LineDecorationDataHandle,
-        _: &mut PrimitiveStore,
-        _reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        PrimitiveInstanceKind::LineDecoration {
-            data_handle,
-            cache_handle: None,
-        }
-    }
-}
-
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct LineDecorationData {
     pub cache_key: Option<LineDecorationCacheKey>,
     pub color: ColorF,
 }
 
@@ -130,34 +114,46 @@ impl From<LineDecorationKey> for LineDec
             kind: LineDecorationData {
                 cache_key: line_dec.kind.cache_key,
                 color: line_dec.kind.color.into(),
             }
         }
     }
 }
 
-pub use intern_types::line_decoration::Handle as LineDecorationDataHandle;
+pub type LineDecorationDataHandle = intern::Handle<LineDecoration>;
 
 impl intern::Internable for LineDecoration {
-    type Marker = intern_types::line_decoration::Marker;
-    type Source = LineDecorationKey;
+    type Key = LineDecorationKey;
     type StoreData = LineDecorationTemplate;
     type InternData = PrimitiveSceneData;
+}
 
-    /// Build a new key from self with `info`.
-    fn build_key(
+impl InternablePrimitive for LineDecoration {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> LineDecorationKey {
         LineDecorationKey::new(
             info,
             self,
         )
     }
+
+    fn make_instance_kind(
+        _key: LineDecorationKey,
+        data_handle: LineDecorationDataHandle,
+        _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        PrimitiveInstanceKind::LineDecoration {
+            data_handle,
+            cache_handle: None,
+        }
+    }
 }
 
 impl CreateShadow for LineDecoration {
     fn create_shadow(&self, shadow: &Shadow) -> Self {
         LineDecoration {
             color: shadow.color.into(),
             cache_key: self.cache_key.clone(),
         }
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -1,29 +1,26 @@
 /* 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::{BorderRadius, ClipMode, ColorF, PictureRect, ColorU, LayoutVector2D};
-use api::{DeviceIntRect, DevicePixelScale, DeviceRect, WorldVector2D};
+use api::{BorderRadius, ClipMode, ColorF};
 use api::{FilterOp, ImageRendering, TileOffset, RepeatMode, WorldPoint, WorldSize};
-use api::{LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, PicturePoint};
-use api::{PremultipliedColorF, PropertyBinding, Shadow, DeviceVector2D};
-use api::{WorldPixel, BoxShadowClipMode, WorldRect, LayoutToWorldScale};
-use api::{PicturePixel, RasterPixel, LineStyle, LineOrientation, AuHelpers};
-use api::{LayoutPrimitiveInfo};
-use api::DevicePoint;
+use api::{PremultipliedColorF, PropertyBinding, Shadow};
+use api::{BoxShadowClipMode, LineStyle, LineOrientation, AuHelpers};
+use api::{LayoutPrimitiveInfo, PrimitiveKeyKind};
+use api::units::*;
 use border::{get_max_scale_for_border, build_border_instances};
 use border::BorderSegmentCacheKey;
 use clip::{ClipStore};
 use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex, VisibleFace};
 use clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem};
 use debug_colors;
 use debug_render::DebugItem;
-use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
+use display_list_flattener::{CreateShadow, IsVisible};
 use euclid::{SideOffsets2D, TypedTransform3D, TypedRect, TypedScale, TypedSize2D};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
 use frame_builder::{PrimitiveContext, FrameVisibilityContext, FrameVisibilityState};
 use glyph_rasterizer::GlyphKey;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
 use gpu_types::{BrushFlags, SnapOffsets};
 use image::{Repetition};
 use intern;
@@ -55,16 +52,17 @@ use util::{scale_factors, clamp_to_scale
 use smallvec::SmallVec;
 
 pub mod borders;
 pub mod gradient;
 pub mod image;
 pub mod line_dec;
 pub mod picture;
 pub mod text_run;
+pub mod interned;
 
 /// Counter for unique primitive IDs for debug tracing.
 #[cfg(debug_assertions)]
 static NEXT_PRIM_ID: AtomicUsize = AtomicUsize::new(0);
 
 #[cfg(debug_assertions)]
 static PRIM_CHASE_ID: AtomicUsize = AtomicUsize::new(usize::MAX);
 
@@ -324,29 +322,16 @@ impl GpuCacheAddress {
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct PrimitiveSceneData {
     pub prim_size: LayoutSize,
     pub is_backface_visible: bool,
 }
 
-/// Information specific to a primitive type that
-/// uniquely identifies a primitive template by key.
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
-pub enum PrimitiveKeyKind {
-    /// Clear an existing rect, used for special effects on some platforms.
-    Clear,
-    Rectangle {
-        color: ColorU,
-    },
-}
-
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, MallocSizeOf, PartialEq)]
 pub struct RectangleKey {
     x: f32,
     y: f32,
     w: f32,
     h: f32,
@@ -629,60 +614,34 @@ impl PrimitiveKey {
             },
             kind,
         }
     }
 }
 
 impl intern::InternDebug for PrimitiveKey {}
 
-impl AsInstanceKind<PrimitiveDataHandle> for PrimitiveKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        data_handle: PrimitiveDataHandle,
-        _: &mut PrimitiveStore,
-        _reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        match self.kind {
-            PrimitiveKeyKind::Clear => {
-                PrimitiveInstanceKind::Clear {
-                    data_handle
-                }
-            }
-            PrimitiveKeyKind::Rectangle { .. } => {
-                PrimitiveInstanceKind::Rectangle {
-                    data_handle,
-                    opacity_binding_index: OpacityBindingIndex::INVALID,
-                    segment_instance_index: SegmentInstanceIndex::INVALID,
-                }
-            }
-        }
-    }
-}
-
 /// The shared information for a given primitive. This is interned and retained
 /// both across frames and display lists, by comparing the matching PrimitiveKey.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub enum PrimitiveTemplateKind {
     Rectangle {
         color: ColorF,
     },
     Clear,
 }
 
 /// Construct the primitive template data from a primitive key. This
 /// is invoked when a primitive key is created and the interner
 /// doesn't currently contain a primitive with this key.
-impl PrimitiveKeyKind {
-    fn into_template(self) -> PrimitiveTemplateKind {
-        match self {
+impl From<PrimitiveKeyKind> for PrimitiveTemplateKind {
+    fn from(kind: PrimitiveKeyKind) -> Self {
+        match kind {
             PrimitiveKeyKind::Clear => {
                 PrimitiveTemplateKind::Clear
             }
             PrimitiveKeyKind::Rectangle { color, .. } => {
                 PrimitiveTemplateKind::Rectangle {
                     color: color.into(),
                 }
             }
@@ -741,20 +700,20 @@ impl ops::Deref for PrimitiveTemplate {
 impl ops::DerefMut for PrimitiveTemplate {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.common
     }
 }
 
 impl From<PrimitiveKey> for PrimitiveTemplate {
     fn from(item: PrimitiveKey) -> Self {
-        let common = PrimTemplateCommonData::with_key_common(item.common);
-        let kind = item.kind.into_template();
-
-        PrimitiveTemplate { common, kind, }
+        PrimitiveTemplate {
+            common: PrimTemplateCommonData::with_key_common(item.common),
+            kind: item.kind.into(),
+        }
     }
 }
 
 impl PrimitiveTemplateKind {
     /// Write any GPU blocks for the primitive template to the given request object.
     fn write_prim_gpu_blocks(
         &self,
         request: &mut GpuDataRequest
@@ -790,36 +749,59 @@ impl PrimitiveTemplate {
             }
             PrimitiveTemplateKind::Rectangle { ref color, .. } => {
                 PrimitiveOpacity::from_alpha(color.a)
             }
         };
     }
 }
 
+type PrimitiveDataHandle = intern::Handle<PrimitiveKeyKind>;
+
 impl intern::Internable for PrimitiveKeyKind {
-    type Marker = ::intern_types::prim::Marker;
-    type Source = PrimitiveKey;
+    type Key = PrimitiveKey;
     type StoreData = PrimitiveTemplate;
     type InternData = PrimitiveSceneData;
-
-    fn build_key(
+}
+
+impl InternablePrimitive for PrimitiveKeyKind {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> PrimitiveKey {
         PrimitiveKey::new(
             info.is_backface_visible,
             info.rect.size,
             self,
         )
     }
+
+    fn make_instance_kind(
+        key: PrimitiveKey,
+        data_handle: PrimitiveDataHandle,
+        _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        match key.kind {
+            PrimitiveKeyKind::Clear => {
+                PrimitiveInstanceKind::Clear {
+                    data_handle
+                }
+            }
+            PrimitiveKeyKind::Rectangle { .. } => {
+                PrimitiveInstanceKind::Rectangle {
+                    data_handle,
+                    opacity_binding_index: OpacityBindingIndex::INVALID,
+                    segment_instance_index: SegmentInstanceIndex::INVALID,
+                }
+            }
+        }
+    }
 }
 
-use intern_types::prim::Handle as PrimitiveDataHandle;
-
 // Maintains a list of opacity bindings that have been collapsed into
 // the color of a single primitive. This is an important optimization
 // that avoids allocating an intermediate surface for most common
 // uses of opacity filters.
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 pub struct OpacityBinding {
     pub bindings: Vec<PropertyBinding<f32>>,
@@ -3701,16 +3683,34 @@ fn update_opacity_binding(
         1.0
     } else {
         let binding = &mut opacity_bindings[opacity_binding_index];
         binding.update(scene_properties);
         binding.current
     }
 }
 
+/// Trait for primitives that are directly internable.
+/// see DisplayListFlattener::add_primitive<P>
+pub trait InternablePrimitive: intern::Internable<InternData = PrimitiveSceneData> + Sized {
+    /// Build a new key from self with `info`.
+    fn into_key(
+        self,
+        info: &LayoutPrimitiveInfo,
+    ) -> Self::Key;
+
+    fn make_instance_kind(
+        key: Self::Key,
+        data_handle: intern::Handle<Self>,
+        prim_store: &mut PrimitiveStore,
+        reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind;
+}
+
+
 #[test]
 #[cfg(target_pointer_width = "64")]
 fn test_struct_sizes() {
     use std::mem;
     // The sizes of these structures are critical for performance on a number of
     // talos stress tests. If you get a failure here on CI, there's two possibilities:
     // (a) You made a structure smaller than it currently is. Great work! Update the
     //     test expectations and move on.
--- a/gfx/wr/webrender/src/prim_store/picture.rs
+++ b/gfx/wr/webrender/src/prim_store/picture.rs
@@ -3,23 +3,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{
     ColorU, FilterOp, LayoutSize, LayoutPrimitiveInfo, MixBlendMode,
     PropertyBinding, PropertyBindingId, LayoutVector2D,
 };
 use intern::ItemUid;
 use app_units::Au;
-use display_list_flattener::{AsInstanceKind, IsVisible};
-use intern::{Internable, InternDebug};
-use intern_types;
+use display_list_flattener::IsVisible;
+use intern::{Internable, InternDebug, Handle as InternHandle};
 use picture::PictureCompositeMode;
 use prim_store::{
     PrimKey, PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
     PrimitiveInstanceKind, PrimitiveSceneData, PrimitiveStore, VectorKey,
+    InternablePrimitive,
 };
 
 /// Represents a hashable description of how a picture primitive
 /// will be composited into its parent.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
 pub enum PictureCompositeKey {
@@ -156,31 +156,16 @@ impl PictureKey {
             },
             kind: pic,
         }
     }
 }
 
 impl InternDebug for PictureKey {}
 
-impl AsInstanceKind<PictureDataHandle> for PictureKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        _: PictureDataHandle,
-        _: &mut PrimitiveStore,
-        _reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        // Should never be hit as this method should not be
-        // called for pictures.
-        unreachable!();
-    }
-}
-
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct PictureData;
 
 pub type PictureTemplate = PrimTemplate<PictureData>;
 
 impl From<PictureKey> for PictureTemplate {
@@ -189,35 +174,46 @@ impl From<PictureKey> for PictureTemplat
 
         PictureTemplate {
             common,
             kind: PictureData,
         }
     }
 }
 
-pub use intern_types::picture::Handle as PictureDataHandle;
+pub type PictureDataHandle = InternHandle<Picture>;
 
 impl Internable for Picture {
-    type Marker = intern_types::picture::Marker;
-    type Source = PictureKey;
+    type Key = PictureKey;
     type StoreData = PictureTemplate;
     type InternData = PrimitiveSceneData;
+}
 
-    /// Build a new key from self with `info`.
-    fn build_key(
+impl InternablePrimitive for Picture {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> PictureKey {
         PictureKey::new(
             info.is_backface_visible,
             info.rect.size,
-            self
+            self,
         )
     }
+
+    fn make_instance_kind(
+        _key: PictureKey,
+        _: PictureDataHandle,
+        _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        // Should never be hit as this method should not be
+        // called for pictures.
+        unreachable!();
+    }
 }
 
 impl IsVisible for Picture {
     fn is_visible(&self) -> bool {
         true
     }
 }
 
--- a/gfx/wr/webrender/src/prim_store/text_run.rs
+++ b/gfx/wr/webrender/src/prim_store/text_run.rs
@@ -1,29 +1,28 @@
 /* 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::{ColorF, DevicePixelScale, GlyphInstance, LayoutPrimitiveInfo};
 use api::{LayoutToWorldTransform, RasterSpace};
 use api::{LayoutVector2D, Shadow};
-use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
+use display_list_flattener::{CreateShadow, IsVisible};
 use frame_builder::{FrameBuildingState, PictureContext};
 use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
 use gpu_cache::GpuCache;
 use gpu_types::RasterizationSpace;
 use intern;
-use intern_types;
 use prim_store::{PrimitiveOpacity, PrimitiveSceneData,  PrimitiveScratchBuffer};
 use prim_store::{PrimitiveStore, PrimKeyCommonData, PrimTemplateCommonData};
 use render_task::{RenderTaskTree};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ResourceCache};
 use util::{MatrixHelpers};
-use prim_store::PrimitiveInstanceKind;
+use prim_store::{InternablePrimitive, PrimitiveInstanceKind};
 use std::ops;
 use std::sync::Arc;
 use storage;
 use util::PrimaryArc;
 
 /// A run of glyphs, with associated font information.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -48,38 +47,16 @@ impl TextRunKey {
             glyphs: PrimaryArc(text_run.glyphs),
             shadow: text_run.shadow,
         }
     }
 }
 
 impl intern::InternDebug for TextRunKey {}
 
-impl AsInstanceKind<TextRunDataHandle> for TextRunKey {
-    /// Construct a primitive instance that matches the type
-    /// of primitive key.
-    fn as_instance_kind(
-        &self,
-        data_handle: TextRunDataHandle,
-        prim_store: &mut PrimitiveStore,
-        reference_frame_relative_offset: LayoutVector2D,
-    ) -> PrimitiveInstanceKind {
-        let run_index = prim_store.text_runs.push(TextRunPrimitive {
-            used_font: self.font.clone(),
-            glyph_keys_range: storage::Range::empty(),
-            reference_frame_relative_offset,
-            shadow: self.shadow,
-            raster_space: RasterizationSpace::Screen,
-            inverse_raster_scale: 1.0,
-        });
-
-        PrimitiveInstanceKind::TextRun{ data_handle, run_index }
-    }
-}
-
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct TextRunTemplate {
     pub common: PrimTemplateCommonData,
     pub font: FontInstance,
     #[ignore_malloc_size_of = "Measured via PrimaryArc"]
     pub glyphs: Arc<Vec<GlyphInstance>>,
@@ -152,40 +129,62 @@ impl TextRunTemplate {
                 request.push(gpu_block);
             }
 
             assert!(request.current_used_block_num() <= MAX_VERTEX_TEXTURE_WIDTH);
         }
     }
 }
 
-pub use intern_types::text_run::Handle as TextRunDataHandle;
+pub type TextRunDataHandle = intern::Handle<TextRun>;
 
+#[derive(Debug, MallocSizeOf)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct TextRun {
     pub font: FontInstance,
+    #[ignore_malloc_size_of = "Measured via PrimaryArc"]
     pub glyphs: Arc<Vec<GlyphInstance>>,
     pub shadow: bool,
 }
 
 impl intern::Internable for TextRun {
-    type Marker = intern_types::text_run::Marker;
-    type Source = TextRunKey;
+    type Key = TextRunKey;
     type StoreData = TextRunTemplate;
     type InternData = PrimitiveSceneData;
+}
 
-    /// Build a new key from self with `info`.
-    fn build_key(
+impl InternablePrimitive for TextRun {
+    fn into_key(
         self,
         info: &LayoutPrimitiveInfo,
     ) -> TextRunKey {
         TextRunKey::new(
             info,
             self,
         )
     }
+
+    fn make_instance_kind(
+        key: TextRunKey,
+        data_handle: TextRunDataHandle,
+        prim_store: &mut PrimitiveStore,
+        reference_frame_relative_offset: LayoutVector2D,
+    ) -> PrimitiveInstanceKind {
+        let run_index = prim_store.text_runs.push(TextRunPrimitive {
+            used_font: key.font.clone(),
+            glyph_keys_range: storage::Range::empty(),
+            reference_frame_relative_offset,
+            shadow: key.shadow,
+            raster_space: RasterizationSpace::Screen,
+            inverse_raster_scale: 1.0,
+        });
+
+        PrimitiveInstanceKind::TextRun{ data_handle, run_index }
+    }
 }
 
 impl CreateShadow for TextRun {
     fn create_shadow(&self, shadow: &Shadow) -> Self {
         let mut font = FontInstance {
             color: shadow.color.into(),
             ..self.font.clone()
         };
--- a/gfx/wr/webrender/src/profiler.rs
+++ b/gfx/wr/webrender/src/profiler.rs
@@ -448,17 +448,17 @@ pub struct IpcProfileCounters {
     pub build_time: TimeProfileCounter,
     pub consume_time: TimeProfileCounter,
     pub send_time: TimeProfileCounter,
     pub total_time: TimeProfileCounter,
     pub display_lists: ResourceProfileCounter,
 }
 
 macro_rules! declare_intern_profile_counters {
-    ( $( $name: ident, )+ ) => {
+    ( $( $name:ident : $ty:ty, )+ ) => {
         #[derive(Clone)]
         pub struct InternProfileCounters {
             $(
                 pub $name: ResourceProfileCounter,
             )+
         }
 
         impl InternProfileCounters {
@@ -517,29 +517,30 @@ impl BackendProfileCounters {
             },
             ipc: IpcProfileCounters {
                 build_time: TimeProfileCounter::new("Display List Build Time", false),
                 consume_time: TimeProfileCounter::new("Display List Consume Time", false),
                 send_time: TimeProfileCounter::new("Display List Send Time", false),
                 total_time: TimeProfileCounter::new("Total Display List Time", false),
                 display_lists: ResourceProfileCounter::new("Display Lists Sent"),
             },
+            //TODO: generate this by a macro
             intern: InternProfileCounters {
                 prim: ResourceProfileCounter::new("Interned primitives"),
                 image: ResourceProfileCounter::new("Interned images"),
                 image_border: ResourceProfileCounter::new("Interned image borders"),
                 line_decoration: ResourceProfileCounter::new("Interned line decorations"),
                 linear_grad: ResourceProfileCounter::new("Interned linear gradients"),
                 normal_border: ResourceProfileCounter::new("Interned normal borders"),
                 picture: ResourceProfileCounter::new("Interned pictures"),
                 radial_grad: ResourceProfileCounter::new("Interned radial gradients"),
                 text_run: ResourceProfileCounter::new("Interned text runs"),
                 yuv_image: ResourceProfileCounter::new("Interned YUV images"),
                 clip: ResourceProfileCounter::new("Interned clips"),
-                filterdata: ResourceProfileCounter::new("Interned filterdata"),
+                filter_data: ResourceProfileCounter::new("Interned filter data"),
             },
         }
     }
 
     pub fn reset(&mut self) {
         self.total_time.reset();
         self.ipc.total_time.reset();
         self.ipc.build_time.reset();
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -10,35 +10,37 @@
 
 use api::{ApiMsg, BuiltDisplayList, ClearCache, DebugCommand, DebugFlags};
 #[cfg(feature = "debugger")]
 use api::{BuiltDisplayListIter, SpecificDisplayItem};
 use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestFlags, HitTestResult};
 use api::{IdNamespace, MemoryReport, PipelineId, RenderNotifier, SceneMsg, ScrollClamping};
 use api::{ScrollLocation, ScrollNodeState, TransactionMsg, ResourceUpdate, BlobImageKey};
 use api::{NotificationRequest, Checkpoint};
+use api::{ClipIntern, FilterDataIntern, PrimitiveKeyKind};
 use api::units::*;
 use api::channel::{MsgReceiver, MsgSender, Payload};
 #[cfg(feature = "capture")]
 use api::CaptureBits;
 #[cfg(feature = "replay")]
 use api::CapturedDocument;
 use clip_scroll_tree::{SpatialNodeIndex, ClipScrollTree};
 #[cfg(feature = "debugger")]
 use debug_server;
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use glyph_rasterizer::{FontInstance};
 use gpu_cache::GpuCache;
 use hit_test::{HitTest, HitTester};
-use intern_types;
+use intern::DataStore;
 use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
 use picture::RetainedTiles;
 use prim_store::{PrimitiveScratchBuffer, PrimitiveInstance};
 use prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData};
+use prim_store::interned::*;
 use profiler::{BackendProfileCounters, IpcProfileCounters, ResourceProfileCounters};
 use record::ApiRecordingReceiver;
 use render_task::RenderTaskTreeCounters;
 use renderer::{AsyncPropertySampler, PipelineInfo};
 use resource_cache::ResourceCache;
 #[cfg(feature = "replay")]
 use resource_cache::PlainCacheOwn;
 #[cfg(any(feature = "capture", feature = "replay"))]
@@ -205,25 +207,25 @@ impl FrameStamp {
     pub const INVALID: FrameStamp = FrameStamp {
         id: FrameId(0),
         time: UNIX_EPOCH,
         document_id: DocumentId::INVALID,
     };
 }
 
 macro_rules! declare_data_stores {
-    ( $( $name: ident, )+ ) => {
+    ( $( $name:ident : $ty:ty, )+ ) => {
         /// A collection of resources that are shared by clips, primitives
         /// between display lists.
         #[cfg_attr(feature = "capture", derive(Serialize))]
         #[cfg_attr(feature = "replay", derive(Deserialize))]
         #[derive(Default)]
         pub struct DataStores {
             $(
-                pub $name: intern_types::$name::Store,
+                pub $name: DataStore<$ty>,
             )+
         }
 
         impl DataStores {
             /// Reports CPU heap usage.
             fn report_memory(&self, ops: &mut MallocSizeOfOps, r: &mut MemoryReport) {
                 $(
                     r.interning.data_stores.$name += self.$name.size_of(ops);
--- a/gfx/wr/webrender/src/scene_builder.rs
+++ b/gfx/wr/webrender/src/scene_builder.rs
@@ -1,27 +1,25 @@
 /* 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::{AsyncBlobImageRasterizer, BlobImageRequest, BlobImageParams, BlobImageResult};
 use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate, ExternalEvent, Epoch};
 use api::{BuiltDisplayList, ColorF, LayoutSize, NotificationRequest, Checkpoint, IdNamespace};
-use api::{MemoryReport};
+use api::{ClipIntern, FilterDataIntern, MemoryReport, PrimitiveKeyKind};
 use api::channel::MsgSender;
 #[cfg(feature = "capture")]
 use capture::CaptureConfig;
 use frame_builder::{FrameBuilderConfig, FrameBuilder};
 use clip_scroll_tree::ClipScrollTree;
 use display_list_flattener::DisplayListFlattener;
-use intern::{Internable, Interner};
-use intern_types;
+use intern::{Internable, Interner, UpdateList};
 use internal_types::{FastHashMap, FastHashSet};
 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
-use prim_store::{PrimitiveKeyKind};
 use prim_store::PrimitiveStoreStats;
 use prim_store::borders::{ImageBorder, NormalBorderPrim};
 use prim_store::gradient::{LinearGradient, RadialGradient};
 use prim_store::image::{Image, YuvImage};
 use prim_store::line_dec::LineDecoration;
 use prim_store::picture::Picture;
 use prim_store::text_run::TextRun;
 use resource_cache::{AsyncBlobImageInfo, FontInstanceMap};
@@ -30,16 +28,17 @@ use renderer::{PipelineInfo, SceneBuilde
 use scene::Scene;
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::mem::replace;
 use time::precise_time_ns;
 use util::drain_filter;
 use std::thread;
 use std::time::Duration;
 
+
 /// Represents the work associated to a transaction before scene building.
 pub struct Transaction {
     pub document_id: DocumentId,
     pub display_list_updates: Vec<DisplayListUpdate>,
     pub removed_pipelines: Vec<PipelineId>,
     pub epoch_updates: Vec<(PipelineId, Epoch)>,
     pub request_scene_build: Option<SceneRequest>,
     pub blob_requests: Vec<BlobImageParams>,
@@ -165,35 +164,43 @@ pub enum SceneBuilderResult {
 // scene swap was completed. We need a separate channel for this
 // so that they don't get mixed with SceneBuilderRequest messages.
 pub enum SceneSwapResult {
     Complete(Sender<()>),
     Aborted,
 }
 
 macro_rules! declare_interners {
-    ( $( $name: ident, )+ ) => {
+    ( $( $name:ident : $ty:ident, )+ ) => {
         /// This struct contains all items that can be shared between
         /// display lists. We want to intern and share the same clips,
         /// primitives and other things between display lists so that:
         /// - GPU cache handles remain valid, reducing GPU cache updates.
         /// - Comparison of primitives and pictures between two
         ///   display lists is (a) fast (b) done during scene building.
         #[cfg_attr(feature = "capture", derive(Serialize))]
         #[cfg_attr(feature = "replay", derive(Deserialize))]
         #[derive(Default)]
         pub struct Interners {
             $(
-                pub $name: intern_types::$name::Interner,
+                pub $name: Interner<$ty>,
             )+
         }
 
+        $(
+            impl AsMut<Interner<$ty>> for Interners {
+                fn as_mut(&mut self) -> &mut Interner<$ty> {
+                    &mut self.$name
+                }
+            }
+        )+
+
         pub struct InternerUpdates {
             $(
-                pub $name: intern_types::$name::UpdateList,
+                pub $name: UpdateList<<$ty as Internable>::Key>,
             )+
         }
 
         impl Interners {
             /// Reports CPU heap memory used by the interners.
             fn report_memory(
                 &self,
                 ops: &mut MallocSizeOfOps,
@@ -212,49 +219,16 @@ macro_rules! declare_interners {
                 }
             }
         }
     }
 }
 
 enumerate_interners!(declare_interners);
 
-// Access to `Interners` interners by `Internable`
-pub trait InternerMut<I: Internable>
-{
-    fn interner_mut(&mut self) -> &mut Interner<I::Source, I::InternData, I::Marker>;
-}
-
-macro_rules! impl_interner_mut {
-    ($($ty:ident: $mem:ident,)*) => {
-        $(impl InternerMut<$ty> for Interners {
-            fn interner_mut(&mut self) -> &mut Interner<
-                <$ty as Internable>::Source,
-                <$ty as Internable>::InternData,
-                <$ty as Internable>::Marker
-            > {
-                &mut self.$mem
-            }
-        })*
-    }
-}
-
-impl_interner_mut! {
-    Image: image,
-    ImageBorder: image_border,
-    LineDecoration: line_decoration,
-    LinearGradient: linear_grad,
-    NormalBorderPrim: normal_border,
-    Picture: picture,
-    PrimitiveKeyKind: prim,
-    RadialGradient: radial_grad,
-    TextRun: text_run,
-    YuvImage: yuv_image,
-}
-
 // A document in the scene builder contains the current scene,
 // as well as a persistent clip interner. This allows clips
 // to be de-duplicated, and persisted in the GPU cache between
 // display lists.
 struct Document {
     scene: Scene,
     interners: Interners,
     prim_store_stats: PrimitiveStoreStats,
--- a/gfx/wr/webrender_api/src/api.rs
+++ b/gfx/wr/webrender_api/src/api.rs
@@ -10,17 +10,17 @@ use std::cell::Cell;
 use std::fmt;
 use std::marker::PhantomData;
 use std::os::raw::c_void;
 use std::path::PathBuf;
 use std::sync::Arc;
 use std::u32;
 // local imports
 use {display_item as di, font};
-use color::ColorF;
+use color::{ColorU, ColorF};
 use display_list::{BuiltDisplayList, BuiltDisplayListDescriptor};
 use image::{BlobImageData, BlobImageKey, ImageData, ImageDescriptor, ImageKey};
 use units::*;
 
 
 pub type TileSize = u16;
 /// Documents are rendered in the ascending order of their associated layer values.
 pub type DocumentLayer = i8;
@@ -808,44 +808,60 @@ pub type PipelineSourceId = u32;
 pub struct PipelineId(pub PipelineSourceId, pub u32);
 
 impl PipelineId {
     pub fn dummy() -> Self {
         PipelineId(0, 0)
     }
 }
 
+#[derive(Copy, Clone, Debug, MallocSizeOf, Serialize, Deserialize)]
+pub enum ClipIntern {}
+#[derive(Copy, Clone, Debug, MallocSizeOf, Serialize, Deserialize)]
+pub enum FilterDataIntern {}
+
+/// Information specific to a primitive type that
+/// uniquely identifies a primitive template by key.
+#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash, Serialize, Deserialize)]
+pub enum PrimitiveKeyKind {
+    /// Clear an existing rect, used for special effects on some platforms.
+    Clear,
+    Rectangle {
+        color: ColorU,
+    },
+}
+
 /// Meta-macro to enumerate the various interner identifiers and types.
 ///
 /// IMPORTANT: Keep this synchronized with the list in mozilla-central located at
 /// gfx/webrender_bindings/webrender_ffi.h
 ///
 /// Note that this could be a lot less verbose if concat_idents! were stable. :-(
 #[macro_export]
 macro_rules! enumerate_interners {
     ($macro_name: ident) => {
         $macro_name! {
-            clip,
-            prim,
-            normal_border,
-            image_border,
-            image,
-            yuv_image,
-            line_decoration,
-            linear_grad,
-            radial_grad,
-            picture,
-            text_run,
-            filterdata,
+            clip: ClipIntern,
+            prim: PrimitiveKeyKind,
+            normal_border: NormalBorderPrim,
+            image_border: ImageBorder,
+            image: Image,
+            yuv_image: YuvImage,
+            line_decoration: LineDecoration,
+            linear_grad: LinearGradient,
+            radial_grad: RadialGradient,
+            picture: Picture,
+            text_run: TextRun,
+            filter_data: FilterDataIntern,
         }
     }
 }
 
 macro_rules! declare_interning_memory_report {
-    ( $( $name: ident, )+ ) => {
+    ( $( $name:ident: $ty:ident, )+ ) => {
         #[repr(C)]
         #[derive(AddAssign, Clone, Debug, Default, Deserialize, Serialize)]
         pub struct InternerSubReport {
             $(
                 pub $name: usize,
             )+
         }
     }
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -22,19 +22,16 @@
 #include "nsString.h"
 #include "nsTArray.h"
 #include "mozilla/nsRedirectHistoryEntry.h"
 #include "URIUtils.h"
 #include "mozilla/dom/nsCSPUtils.h"
 #include "mozilla/dom/nsCSPContext.h"
 
 namespace mozilla {
-namespace net {
-class OptionalLoadInfoArgs;
-}
 
 using mozilla::BasePrincipal;
 using mozilla::Maybe;
 using mozilla::dom::ServiceWorkerDescriptor;
 using namespace mozilla::net;
 
 namespace ipc {
 
@@ -363,20 +360,20 @@ nsresult RHEntryToRHEntryInfo(nsIRedirec
   nsCOMPtr<nsIPrincipal> principal;
   rv = aRHEntry->GetPrincipal(getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return PrincipalToPrincipalInfo(principal, &aRHEntryInfo->principalInfo());
 }
 
 nsresult LoadInfoToLoadInfoArgs(nsILoadInfo* aLoadInfo,
-                                OptionalLoadInfoArgs* aOptionalLoadInfoArgs) {
+                                Maybe<LoadInfoArgs>* aOptionalLoadInfoArgs) {
   if (!aLoadInfo) {
     // if there is no loadInfo, then there is nothing to serialize
-    *aOptionalLoadInfoArgs = void_t();
+    *aOptionalLoadInfoArgs = Nothing();
     return NS_OK;
   }
 
   nsresult rv = NS_OK;
   OptionalPrincipalInfo loadingPrincipalInfo = mozilla::void_t();
   if (aLoadInfo->LoadingPrincipal()) {
     PrincipalInfo loadingPrincipalInfoTemp;
     rv = PrincipalToPrincipalInfo(aLoadInfo->LoadingPrincipal(),
@@ -483,17 +480,17 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
   const Maybe<ServiceWorkerDescriptor>& controller = aLoadInfo->GetController();
   if (controller.isSome()) {
     ipcController = controller.ref().ToIPC();
   }
 
   nsAutoString cspNonce;
   Unused << NS_WARN_IF(NS_FAILED(aLoadInfo->GetCspNonce(cspNonce)));
 
-  *aOptionalLoadInfoArgs = LoadInfoArgs(
+  *aOptionalLoadInfoArgs = Some(LoadInfoArgs(
       loadingPrincipalInfo, triggeringPrincipalInfo, principalToInheritInfo,
       sandboxedLoadingPrincipalInfo, topLevelPrincipalInfo,
       topLevelStorageAreaPrincipalInfo, optionalResultPrincipalURI,
       aLoadInfo->GetSecurityFlags(), aLoadInfo->InternalContentPolicyType(),
       static_cast<uint32_t>(aLoadInfo->GetTainting()),
       aLoadInfo->GetUpgradeInsecureRequests(),
       aLoadInfo->GetBrowserUpgradeInsecureRequests(),
       aLoadInfo->GetBrowserWouldUpgradeInsecureRequests(),
@@ -513,30 +510,30 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
       ancestorPrincipals, aLoadInfo->AncestorOuterWindowIDs(), ipcClientInfo,
       ipcReservedClientInfo, ipcInitialClientInfo, ipcController,
       aLoadInfo->CorsUnsafeHeaders(), aLoadInfo->GetForcePreflight(),
       aLoadInfo->GetIsPreflight(), aLoadInfo->GetLoadTriggeredFromExternal(),
       aLoadInfo->GetServiceWorkerTaintingSynthesized(),
       aLoadInfo->GetDocumentHasUserInteracted(),
       aLoadInfo->GetDocumentHasLoaded(), cspNonce,
       aLoadInfo->GetIsFromProcessingFrameAttributes(),
-      aLoadInfo->GetOpenerPolicy());
+      aLoadInfo->GetOpenerPolicy()));
 
   return NS_OK;
 }
 
 nsresult LoadInfoArgsToLoadInfo(
-    const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
+    const Maybe<LoadInfoArgs>& aOptionalLoadInfoArgs,
     nsILoadInfo** outLoadInfo) {
-  if (aOptionalLoadInfoArgs.type() == OptionalLoadInfoArgs::Tvoid_t) {
+  if (aOptionalLoadInfoArgs.isNothing()) {
     *outLoadInfo = nullptr;
     return NS_OK;
   }
 
-  const LoadInfoArgs& loadInfoArgs = aOptionalLoadInfoArgs.get_LoadInfoArgs();
+  const LoadInfoArgs& loadInfoArgs = aOptionalLoadInfoArgs.ref();
 
   nsresult rv = NS_OK;
   nsCOMPtr<nsIPrincipal> loadingPrincipal;
   if (loadInfoArgs.requestingPrincipalInfo().type() !=
       OptionalPrincipalInfo::Tvoid_t) {
     loadingPrincipal =
         PrincipalInfoToPrincipal(loadInfoArgs.requestingPrincipalInfo(), &rv);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/ipc/glue/BackgroundUtils.h
+++ b/ipc/glue/BackgroundUtils.h
@@ -43,17 +43,17 @@ template <>
 struct ParamTraits<mozilla::OriginAttributes>
     : public detail::OriginAttributesParamTraits<mozilla::OriginAttributes> {};
 
 }  // namespace IPC
 
 namespace mozilla {
 namespace net {
 class ChildLoadInfoForwarderArgs;
-class OptionalLoadInfoArgs;
+class LoadInfoArgs;
 class ParentLoadInfoForwarderArgs;
 class RedirectHistoryEntryInfo;
 }  // namespace net
 
 namespace ipc {
 
 class ContentSecurityPolicy;
 class PrincipalInfo;
@@ -103,23 +103,23 @@ nsresult RHEntryToRHEntryInfo(
     nsIRedirectHistoryEntry* aRHEntry,
     mozilla::net::RedirectHistoryEntryInfo* aRHEntryInfo);
 
 /**
  * Convert a LoadInfo to LoadInfoArgs struct.
  */
 nsresult LoadInfoToLoadInfoArgs(
     nsILoadInfo* aLoadInfo,
-    mozilla::net::OptionalLoadInfoArgs* outOptionalLoadInfoArgs);
+    Maybe<mozilla::net::LoadInfoArgs>* outOptionalLoadInfoArgs);
 
 /**
  * Convert LoadInfoArgs to a LoadInfo.
  */
 nsresult LoadInfoArgsToLoadInfo(
-    const mozilla::net::OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
+    const Maybe<mozilla::net::LoadInfoArgs>& aOptionalLoadInfoArgs,
     nsILoadInfo** outLoadInfo);
 
 /**
  * Fills ParentLoadInfoForwarderArgs with properties we want to carry to child
  * processes.
  */
 void LoadInfoToParentLoadInfoForwarder(
     nsILoadInfo* aLoadInfo,
--- a/ipc/glue/ByteBuf.h
+++ b/ipc/glue/ByteBuf.h
@@ -66,17 +66,17 @@ class ByteBuf final {
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::ipc::ByteBuf> {
   typedef mozilla::ipc::ByteBuf paramType;
 
   // this is where we transfer the memory from the ByteBuf to IPDL, avoiding a
   // copy
-  static void Write(Message* aMsg, paramType& aParam) {
+  static void Write(Message* aMsg, paramType&& aParam) {
     WriteParam(aMsg, aParam.mLen);
     // hand over ownership of the buffer to the Message
     aMsg->WriteBytesZeroCopy(aParam.mData, aParam.mLen, aParam.mCapacity);
     aParam.mData = nullptr;
     aParam.mCapacity = 0;
     aParam.mLen = 0;
   }
 
--- a/ipc/glue/CrashReporterClient.h
+++ b/ipc/glue/CrashReporterClient.h
@@ -38,17 +38,17 @@ class CrashReporterClient {
 
     Shmem shmem;
     if (!AllocShmem(aToplevelProtocol, &shmem)) {
       return false;
     }
 
     InitSingletonWithShmem(shmem);
     Unused << aToplevelProtocol->SendInitCrashReporter(
-        shmem, CrashReporter::CurrentThreadId());
+        std::move(shmem), CrashReporter::CurrentThreadId());
     return true;
   }
 
   template <typename T>
   static bool AllocShmem(T* aToplevelProtocol, Shmem* aOutShmem) {
     // 16KB should be enough for most metadata - see bug 1278717 comment #11.
     static const size_t kShmemSize = 16 * 1024;
 
--- a/ipc/glue/IPDLParamTraits.h
+++ b/ipc/glue/IPDLParamTraits.h
@@ -95,23 +95,22 @@ static bool ReadIPDLParamList(const IPC:
 template <typename T>
 struct IPDLParamTraits<nsTArray<T>> {
   static inline void Write(IPC::Message* aMsg, IProtocol* aActor,
                            const nsTArray<T>& aParam) {
     WriteInternal(aMsg, aActor, aParam);
   }
 
   // Some serializers need to take a mutable reference to their backing object,
-  // such as Shmem segments and Byte Buffers. These serializers take the backing
-  // data and move it into the IPC layer for efficiency. They currently take
-  // these references as mutable lvalue references rather than rvalue
-  // references, (bug 1441651). This overload of Write on nsTArray is needed, as
-  // occasionally these types appear inside of IPDL arrays.
+  // such as Shmem segments and Byte Buffers. These serializers take the
+  // backing data and move it into the IPC layer for efficiency. This overload
+  // of Write on nsTArray is needed, as occasionally these types appear inside
+  // of IPDL arrays.
   static inline void Write(IPC::Message* aMsg, IProtocol* aActor,
-                           nsTArray<T>& aParam) {
+                           nsTArray<T>&& aParam) {
     WriteInternal(aMsg, aActor, aParam);
   }
 
   // This method uses infallible allocation so that an OOM failure will
   // show up as an OOM crash rather than an IPC FatalError.
   static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
                    IProtocol* aActor, nsTArray<T>* aResult) {
     uint32_t length;
@@ -158,17 +157,17 @@ struct IPDLParamTraits<nsTArray<T>> {
     WriteIPDLParam(aMsg, aActor, length);
 
     if (sUseWriteBytes) {
       auto pickledLength = CheckedInt<int>(length) * sizeof(T);
       MOZ_RELEASE_ASSERT(pickledLength.isValid());
       aMsg->WriteBytes(aParam.Elements(), pickledLength.value());
     } else {
       for (uint32_t index = 0; index < length; index++) {
-        WriteIPDLParam(aMsg, aActor, aParam.Elements()[index]);
+        WriteIPDLParam(aMsg, aActor, std::move(aParam.Elements()[index]));
       }
     }
   }
 
   // We write arrays of integer or floating-point data using a single pickling
   // call, rather than writing each element individually.  We deliberately do
   // not use mozilla::IsPod here because it is perfectly reasonable to have
   // a data structure T for which IsPod<T>::value is true, yet also have a
--- a/ipc/glue/Shmem.cpp
+++ b/ipc/glue/Shmem.cpp
@@ -430,17 +430,17 @@ IPC::Message* Shmem::ShareTo(PrivateIPDL
 }
 
 IPC::Message* Shmem::UnshareFrom(PrivateIPDLCaller, int32_t routingId) {
   AssertInvariants();
   return new ShmemDestroyed(routingId, mId);
 }
 
 void IPDLParamTraits<Shmem>::Write(IPC::Message* aMsg, IProtocol* aActor,
-                                   Shmem& aParam) {
+                                   Shmem&& aParam) {
   WriteIPDLParam(aMsg, aActor, aParam.mId);
 
   aParam.RevokeRights(Shmem::PrivateIPDLCaller());
   aParam.forget(Shmem::PrivateIPDLCaller());
 }
 
 bool IPDLParamTraits<Shmem>::Read(const IPC::Message* aMsg,
                                   PickleIterator* aIter, IProtocol* aActor,
--- a/ipc/glue/Shmem.h
+++ b/ipc/glue/Shmem.h
@@ -211,17 +211,17 @@ class Shmem final {
   size_t mSize;
   id_t mId;
 };
 
 template <>
 struct IPDLParamTraits<Shmem> {
   typedef Shmem paramType;
 
-  static void Write(IPC::Message* aMsg, IProtocol* aActor, paramType& aParam);
+  static void Write(IPC::Message* aMsg, IProtocol* aActor, paramType&& aParam);
   static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
                    IProtocol* aActor, paramType* aResult);
 
   static void Log(const paramType& aParam, std::wstring* aLog) {
     aLog->append(L"(shmem segment)");
   }
 };
 
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -575,27 +575,36 @@ def _cxxConstRefType(ipdltype, side):
     return t
 
 
 def _cxxTypeCanMoveSend(ipdltype):
     return ipdltype.isUniquePtr()
 
 
 def _cxxTypeNeedsMove(ipdltype):
+    if _cxxTypeNeedsMoveForSend(ipdltype):
+        return True
+
+    if ipdltype.isIPDL():
+        return ipdltype.isArray() or ipdltype.isEndpoint()
+
+    return False
+
+
+def _cxxTypeNeedsMoveForSend(ipdltype):
     if ipdltype.isUniquePtr():
         return True
 
     if ipdltype.isCxx():
         return ipdltype.isMoveonly()
 
     if ipdltype.isIPDL():
-        if ipdltype.isMaybe():
+        if ipdltype.isMaybe() or ipdltype.isArray():
             return _cxxTypeNeedsMove(ipdltype.basetype)
-        return (ipdltype.isArray() or
-                ipdltype.isShmem() or
+        return (ipdltype.isShmem() or
                 ipdltype.isByteBuf() or
                 ipdltype.isEndpoint())
 
     return False
 
 
 def _cxxTypeCanMove(ipdltype):
     return not (ipdltype.isIPDL() and ipdltype.isActor())
@@ -684,16 +693,18 @@ necessarily a C++ reference."""
 
     def constPtrToType(self, side):
         return _cxxConstPtrToType(self.ipdltype, side)
 
     def inType(self, side):
         """Return this decl's C++ Type with inparam semantics."""
         if self.ipdltype.isIPDL() and self.ipdltype.isActor():
             return self.bareType(side)
+        elif _cxxTypeNeedsMoveForSend(self.ipdltype):
+            return self.rvalueRefType(side)
         return self.constRefType(side)
 
     def moveType(self, side):
         """Return this decl's C++ Type with move semantics."""
         if self.ipdltype.isIPDL() and self.ipdltype.isActor():
             return self.bareType(side)
         return self.rvalueRefType(side)
 
@@ -964,17 +975,17 @@ class MessageDecl(ipdl.ast.MessageDecl):
         if self.decl.type.isCtor():
             name += 'Constructor'
         return name
 
     def sendMethod(self):
         name = _sendPrefix(self.decl.type) + self.baseName()
         if self.decl.type.isCtor():
             name += 'Constructor'
-        return ExprVar(name)
+        return name
 
     def hasReply(self):
         return (self.decl.type.hasReply()
                 or self.decl.type.isCtor()
                 or self.decl.type.isDtor())
 
     def hasAsyncReturns(self):
         return (self.decl.type.isAsync() and
@@ -1791,32 +1802,34 @@ class _ParamTraits():
         return ifstmt
 
     @classmethod
     def fatalError(cls, reason):
         return StmtExpr(ExprCall(ExprSelect(cls.actor, '->', 'FatalError'),
                                  args=[ExprLiteral.String(reason)]))
 
     @classmethod
-    def write(cls, var, msgvar, actor):
+    def write(cls, var, msgvar, actor, ipdltype=None):
         # WARNING: This doesn't set AutoForActor for you, make sure this is
         # only called when the actor is already correctly set.
+        if ipdltype and _cxxTypeNeedsMoveForSend(ipdltype):
+            var = ExprMove(var)
         return ExprCall(ExprVar('WriteIPDLParam'), args=[msgvar, actor, var])
 
     @classmethod
     def checkedWrite(cls, ipdltype, var, msgvar, sentinelKey, actor):
         assert sentinelKey
         block = Block()
 
         # Assert we aren't serializing a null non-nullable actor
         if ipdltype and ipdltype.isIPDL() and ipdltype.isActor() and not ipdltype.nullable:
             block.addstmt(_abortIfFalse(var, 'NULL actor value passed to non-nullable param'))
 
         block.addstmts([
-            StmtExpr(cls.write(var, msgvar, actor)),
+            StmtExpr(cls.write(var, msgvar, actor, ipdltype)),
             Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=True),
             StmtExpr(ExprCall(ExprSelect(msgvar, '->', 'WriteSentinel'),
                               args=[ExprLiteral.Int(hashfunc(sentinelKey))]))
         ])
         return block
 
     @classmethod
     def checkedRead(cls, ipdltype, var,
@@ -1953,17 +1966,17 @@ class _ParamTraits():
                     '==',
                     ExprCall(ExprSelect(cls.var, '->', 'GetIPCChannel'))
                 ),
                 ExprLiteral.String("Actor must be from the same channel as the"
                                    " actor it's being sent over"),
             ]))
         )
 
-        # IPC::WriteParam(..)
+        # IPC::WriteIPDLParam(..)
         write += [ifnull,
                   StmtExpr(cls.write(idvar, cls.msgvar, cls.actor))]
 
         # bool Read(..) impl
         actorvar = ExprVar('actor')
         read = [
             StmtDecl(Decl(Type('mozilla::Maybe', T=Type('mozilla::ipc::IProtocol',
                                                         ptr=True)), actorvar.name),
@@ -2090,17 +2103,17 @@ class _ParamTraits():
                 readcase.addstmt(cls.ifsideis(c.side,
                                               StmtBlock([cls.fatalError('wrong side!'),
                                                          StmtReturn.FALSE])))
                 c = c.other
             tmpvar = ExprVar('tmp')
             ct = c.bareType(fq=True)
             readcase.addstmts([
                 StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue(fq=True)),
-                StmtExpr(ExprAssn(ExprDeref(cls.var), tmpvar)),
+                StmtExpr(ExprAssn(ExprDeref(cls.var), ExprMove(tmpvar))),
                 cls._checkedRead(c.ipdltype,
                                  ExprAddrOf(ExprCall(ExprSelect(cls.var, '->',
                                                                 c.getTypeName()))),
                                  origenum,
                                  'variant ' + origenum + ' of union ' + uniontype.name()),
                 StmtReturn.TRUE,
             ])
             readswitch.addcase(caselabel, readcase)
@@ -2518,17 +2531,17 @@ def _generateCxxUnion(ud):
     for c in ud.components:
         copyctor = ConstructorDefn(ConstructorDecl(
             ud.name, params=[Decl(c.inType(), othervar.name)]))
         copyctor.addstmts([
             StmtExpr(c.callCtor(othervar)),
             StmtExpr(ExprAssn(mtypevar, c.enumvar()))])
         cls.addstmts([copyctor, Whitespace.NL])
 
-        if not _cxxTypeCanMove(c.ipdltype):
+        if not _cxxTypeCanMove(c.ipdltype) or _cxxTypeNeedsMoveForSend(c.ipdltype):
             continue
         movector = ConstructorDefn(ConstructorDecl(
             ud.name, params=[Decl(c.forceMoveType(), othervar.name)]))
         movector.addstmts([
             StmtExpr(c.callCtor(ExprMove(othervar))),
             StmtExpr(ExprAssn(mtypevar, c.enumvar()))])
         cls.addstmts([movector, Whitespace.NL])
 
@@ -2625,17 +2638,17 @@ def _generateCxxUnion(ud):
             maybeReconstruct(c, c.enumvar()),
             StmtExpr(c.callOperatorEq(rhsvar)),
             StmtExpr(ExprAssn(mtypevar, c.enumvar())),
             StmtReturn(ExprDeref(ExprVar.THIS))
         ])
         cls.addstmts([opeq, Whitespace.NL])
 
         # Union& operator=(T&&)
-        if not _cxxTypeCanMove(c.ipdltype):
+        if not _cxxTypeCanMove(c.ipdltype) or _cxxTypeNeedsMoveForSend(c.ipdltype):
             continue
 
         opeq = MethodDefn(MethodDecl(
             'operator=',
             params=[Decl(c.forceMoveType(), rhsvar.name)],
             ret=refClsType))
         opeq.addstmts([
             # might need to placement-delete old value first
@@ -4102,45 +4115,29 @@ class _GenerateProtocolActorCode(ipdl.as
         ifresolve.addelsestmts(desrej)
         ifresolve.addelsestmts(rejectcallback)
         case.addstmts(prologue)
         case.addstmts(getcallback)
         case.addstmt(ifresolve)
         case.addstmt(StmtReturn(_Result.Processed))
         return (lbl, case)
 
-    @staticmethod
-    def hasMoveableParams(md):
-        for param in md.decl.type.params:
-            if _cxxTypeCanMoveSend(param):
-                return True
-        return False
-
     def genAsyncSendMethod(self, md):
         method = MethodDefn(self.makeSendMethodDecl(md))
         msgvar, stmts = self.makeMessage(md, errfnSend)
         retvar, sendstmts = self.sendAsync(md, msgvar)
 
         method.addstmts(stmts
                         + [Whitespace.NL]
                         + self.genVerifyMessage(md.decl.type.verify, md.params,
                                                 errfnSend, ExprVar('msg__'))
                         + sendstmts
                         + [StmtReturn(retvar)])
 
-        if self.hasMoveableParams(md):
-            movemethod = MethodDefn(self.makeSendMethodDecl(md, paramsems='move'))
-            movemethod.addstmts(stmts
-                                + [Whitespace.NL]
-                                + self.genVerifyMessage(md.decl.type.verify, md.params,
-                                                        errfnSend, ExprVar('msg__'))
-                                + sendstmts
-                                + [StmtReturn(retvar)])
-        else:
-            movemethod = None
+        movemethod = None
 
         # Add the promise overload if we need one.
         if md.returns:
             promisemethod = MethodDefn(self.makeSendMethodDecl(md, promise=True))
             stmts = self.sendAsyncWithPromise(md)
             promisemethod.addstmts(stmts)
 
             (lbl, case) = self.genRecvAsyncReplyCase(md)
@@ -4169,31 +4166,17 @@ class _GenerateProtocolActorCode(ipdl.as
             + [Whitespace.NL,
                 StmtDecl(Decl(Type('Message'), replyvar.name))]
             + sendstmts
             + [failif]
             + desstmts
             + [Whitespace.NL,
                 StmtReturn.TRUE])
 
-        if self.hasMoveableParams(md):
-            movemethod = MethodDefn(self.makeSendMethodDecl(md, paramsems='move'))
-            movemethod.addstmts(
-                serstmts
-                + self.genVerifyMessage(md.decl.type.verify, md.params, errfnSend,
-                                        ExprVar('msg__'))
-                + [Whitespace.NL,
-                    StmtDecl(Decl(Type('Message'), replyvar.name))]
-                + sendstmts
-                + [failif]
-                + desstmts
-                + [Whitespace.NL,
-                    StmtReturn.TRUE])
-        else:
-            movemethod = None
+        movemethod = None
 
         return method, movemethod
 
     def genCtorRecvCase(self, md):
         lbl = CaseLabel(md.pqMsgId())
         case = StmtBlock()
         actorvar = md.actorDecl().var()
         actorhandle = self.handlevar
@@ -4663,17 +4646,17 @@ class _GenerateProtocolActorCode(ipdl.as
         rejectfn.addstmts([
             StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Reject'),
                               args=[ExprMove(ExprVar('aReason')),
                                     ExprVar('__func__')])),
         ])
 
         args = [p.var() for p in md.params] + [resolvefn, rejectfn]
         stmts += [Whitespace.NL,
-                  StmtExpr(ExprCall(ExprVar(md.sendMethod().name), args=args)),
+                  StmtExpr(ExprCall(ExprVar(md.sendMethod()), args=args)),
                   StmtReturn(retpromise)]
         return stmts
 
     def callAllocActor(self, md, retsems, side):
         return self.thisCall(
             _allocMethod(md.decl.type.constructedType(), side),
             args=md.makeCxxArgs(retsems=retsems, retcallsems='out',
                                 implicit=False))
@@ -4734,17 +4717,17 @@ class _GenerateProtocolActorCode(ipdl.as
             else:
                 returnsems = 'callback'
                 rettype = Type.VOID
         else:
             assert not promise
             returnsems = 'out'
             rettype = Type.BOOL
         decl = MethodDecl(
-            md.sendMethod().name,
+            md.sendMethod(),
             params=md.makeCxxParams(paramsems, returnsems=returnsems,
                                     side=self.side, implicit=implicit),
             warn_unused=(self.side == 'parent' and returnsems != 'callback'),
             ret=rettype)
         if md.decl.type.isCtor():
             decl.ret = md.actorDecl().bareType(self.side)
         return decl
 
--- a/ipc/ipdl/test/cxx/TestEndpointOpens.cpp
+++ b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp
@@ -163,17 +163,17 @@ mozilla::ipc::IPCResult TestEndpointOpen
   if (!gChildThread->Start()) {
     fail("starting child thread");
   }
 
   TestEndpointOpensOpenedChild* a = new TestEndpointOpensOpenedChild();
   gChildThread->message_loop()->PostTask(
       NewRunnableFunction("OpenChild", OpenChild, a, std::move(child)));
 
-  if (!SendStartSubprotocol(parent)) {
+  if (!SendStartSubprotocol(std::move(parent))) {
     fail("send StartSubprotocol");
   }
 
   return IPC_OK();
 }
 
 void TestEndpointOpensChild::ActorDestroy(ActorDestroyReason why) {
   // Stops the thread and joins it
--- a/ipc/ipdl/test/cxx/TestShmem.cpp
+++ b/ipc/ipdl/test/cxx/TestShmem.cpp
@@ -26,17 +26,18 @@ void TestShmemParent::Main() {
 
   char* ptr = mem.get<char>();
   memcpy(ptr, "Hello!", sizeof("Hello!"));
 
   char* unsafeptr = unsafe.get<char>();
   memcpy(unsafeptr, "Hello!", sizeof("Hello!"));
 
   Shmem unsafecopy = unsafe;
-  if (!SendGive(mem, unsafe, size)) fail("can't send Give()");
+  if (!SendGive(std::move(mem), std::move(unsafe), size))
+    fail("can't send Give()");
 
   // uncomment the following line for a (nondeterministic) surprise!
   // char c1 = *ptr;  (void)c1;
 
   // uncomment the following line for a deterministic surprise!
   // char c2 = *mem.get<char>(); (void)c2;
 
   // unsafe shmem gets rid of those checks
@@ -86,17 +87,18 @@ mozilla::ipc::IPCResult TestShmemChild::
     fail("expected message was not written");
 
   char* unsafeptr = unsafe.get<char>();
 
   memcpy(mem.get<char>(), "And yourself!", sizeof("And yourself!"));
   memcpy(unsafeptr, "And yourself!", sizeof("And yourself!"));
 
   Shmem unsafecopy = unsafe;
-  if (!SendTake(mem, unsafe, expectedSize)) fail("can't send Take()");
+  if (!SendTake(std::move(mem), std::move(unsafe), expectedSize))
+    fail("can't send Take()");
 
   // these checks also shouldn't fail in the child
   char uc1 = *unsafeptr;
   (void)uc1;
   char uc2 = *unsafecopy.get<char>();
   (void)uc2;
 
   return IPC_OK();
--- a/ipc/ipdl/test/cxx/TestUniquePtrIPC.cpp
+++ b/ipc/ipdl/test/cxx/TestUniquePtrIPC.cpp
@@ -28,17 +28,17 @@ void TestUniquePtrIPCParent::Main() {
 
   if (a4) {
     fail("somehow turned null ptr into non-null by sending it");
   }
 
   // Pass UniquePtr by reference
   UniquePtr<DummyStruct> b = MakeUnique<DummyStruct>(1);
 
-  if (!SendTestSendReference(b)) {
+  if (!SendTestSendReference(std::move(b))) {
     fail("failed sending UniquePtr by reference");
   }
   if (b) {
     fail("did not move UniquePtr sent by reference");
   }
 }
 
 // ---------------------------------------------------------------------------
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -5310,16 +5310,32 @@ static bool ObjectGlobal(JSContext* cx, 
   }
 
   obj = ToWindowProxyIfWindow(&obj->nonCCWGlobal());
 
   args.rval().setObject(*obj);
   return true;
 }
 
+static bool IsSameCompartment(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+  RootedObject callee(cx, &args.callee());
+
+  if (!args.get(0).isObject() || !args.get(1).isObject()) {
+    ReportUsageErrorASCII(cx, callee, "Both arguments must be objects");
+    return false;
+  }
+
+  RootedObject obj1(cx, UncheckedUnwrap(&args[0].toObject()));
+  RootedObject obj2(cx, UncheckedUnwrap(&args[1].toObject()));
+
+  args.rval().setBoolean(obj1->compartment() == obj2->compartment());
+  return true;
+}
+
 static bool FirstGlobalInCompartment(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject callee(cx, &args.callee());
 
   if (!args.get(0).isObject()) {
     ReportUsageErrorASCII(cx, callee, "Argument must be an object");
     return false;
   }
@@ -6304,16 +6320,21 @@ gc::ZealModeHelpText),
     JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal, 0, 0,
 "scriptedCallerGlobal()",
 "  Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"),
 
     JS_FN_HELP("objectGlobal", ObjectGlobal, 1, 0,
 "objectGlobal(obj)",
 "  Returns the object's global object or null if the object is a wrapper.\n"),
 
+    JS_FN_HELP("isSameCompartment", IsSameCompartment, 2, 0,
+"isSameCompartment(obj1, obj2)",
+"  Unwraps obj1 and obj2 and returns whether the unwrapped objects are\n"
+"  same-compartment.\n"),
+
     JS_FN_HELP("firstGlobalInCompartment", FirstGlobalInCompartment, 1, 0,
 "firstGlobalInCompartment(obj)",
 "  Returns the first global in obj's compartment.\n"),
 
     JS_FN_HELP("assertCorrectRealm", AssertCorrectRealm, 0, 0,
 "assertCorrectRealm()",
 "  Asserts cx->realm matches callee->realm.\n"),
 
--- a/js/src/jit-test/tests/basic/more-compartments-flag.js
+++ b/js/src/jit-test/tests/basic/more-compartments-flag.js
@@ -1,8 +1,8 @@
 // |jit-test| --more-compartments
 
 // With --more-compartments we should default to creating a new compartment for
 // new globals.
 
 var g = newGlobal();
-assertEq(objectGlobal(g), null); // CCW
+assertEq(isSameCompartment(this, g), false);
 assertEq(isProxy(g), true);
--- a/js/src/jit-test/tests/realms/basic.js
+++ b/js/src/jit-test/tests/realms/basic.js
@@ -1,16 +1,19 @@
 var g1 = newGlobal({sameCompartmentAs: this});
 var g2 = newGlobal({sameCompartmentAs: g1});
 g2.x = g1;
 gc();
 assertEq(objectGlobal(Math), this);
 assertEq(objectGlobal(g1.print), g1);
 assertEq(objectGlobal(g2.x), g1);
 
+assertEq(isSameCompartment(g1, g2), true);
+assertEq(isSameCompartment(g1, Math), true);
+
 // Different-compartment realms have wrappers.
 assertEq(objectGlobal(newGlobal({newCompartment: true}).Math), null);
 
 function testCrossRealmProto() {
     var g = newGlobal({sameCompartmentAs:this});
 
     for (var i = 0; i < 20; i++) {
         var o = Object.create(g.Math);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -293,17 +293,17 @@ bool JitRuntime::initialize(JSContext* c
   // TODO(bug 1530937): remove this after converting all VM functions.
   for (VMFunction* fun = VMFunction::functions; fun; fun = fun->next) {
     if (functionWrappers_->has(fun)) {
       // Duplicate VMFunction definition. See VMFunction::hash.
       continue;
     }
     JitSpew(JitSpew_Codegen, "# VM function wrapper (%s)", fun->name());
     uint32_t offset;
-    if (!generateVMWrapper(cx, masm, *fun, &offset)) {
+    if (!generateVMWrapper(cx, masm, *fun, fun->wrapped, &offset)) {
       return false;
     }
     if (!functionWrappers_->putNew(fun, offset)) {
       return false;
     }
   }
 
   JitSpew(JitSpew_Codegen, "# Emitting profiler exit frame tail stub");
--- a/js/src/jit/JitRealm.h
+++ b/js/src/jit/JitRealm.h
@@ -193,17 +193,18 @@ class JitRuntime {
                               MIRType type);
   void generateMallocStub(MacroAssembler& masm);
   void generateFreeStub(MacroAssembler& masm);
   JitCode* generateDebugTrapHandler(JSContext* cx);
   JitCode* generateBaselineDebugModeOSRHandler(
       JSContext* cx, uint32_t* noFrameRegPopOffsetOut);
 
   bool generateVMWrapper(JSContext* cx, MacroAssembler& masm,
-                         const VMFunctionData& f, uint32_t* wrapperOffset);
+                         const VMFunctionData& f, void* nativeFun,
+                         uint32_t* wrapperOffset);
   bool generateVMWrappers(JSContext* cx, MacroAssembler& masm);
 
   bool generateTLEventVM(MacroAssembler& masm, const VMFunctionData& f,
                          bool enter);
 
   inline bool generateTLEnterVM(MacroAssembler& masm, const VMFunctionData& f) {
     return generateTLEventVM(masm, f, /* enter = */ true);
   }
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -62,50 +62,59 @@ struct VMFunctionDataHelper<R (*)(JSCont
     return BitMask<TypeToArgProperties, uint32_t, 2, Args...>::result;
   }
   static constexpr uint32_t argumentPassedInFloatRegs() {
     return BitMask<TypeToPassInFloatReg, uint32_t, 2, Args...>::result;
   }
   static constexpr uint64_t argumentRootTypes() {
     return BitMask<TypeToRootType, uint64_t, 3, Args...>::result;
   }
-  constexpr VMFunctionDataHelper(Fun fun, const char* name,
-                                 PopValues extraValuesToPop = PopValues(0))
-      : VMFunctionData((void*)fun, name, explicitArgs(), argumentProperties(),
+  constexpr explicit VMFunctionDataHelper(
+      const char* name, PopValues extraValuesToPop = PopValues(0))
+      : VMFunctionData(name, explicitArgs(), argumentProperties(),
                        argumentPassedInFloatRegs(), argumentRootTypes(),
                        outParam(), outParamRootType(), returnType(),
                        extraValuesToPop.numValues, NonTailCall) {}
-  constexpr VMFunctionDataHelper(Fun fun, const char* name,
-                                 MaybeTailCall expectTailCall,
-                                 PopValues extraValuesToPop = PopValues(0))
-      : VMFunctionData((void*)fun, name, explicitArgs(), argumentProperties(),
+  constexpr explicit VMFunctionDataHelper(
+      const char* name, MaybeTailCall expectTailCall,
+      PopValues extraValuesToPop = PopValues(0))
+      : VMFunctionData(name, explicitArgs(), argumentProperties(),
                        argumentPassedInFloatRegs(), argumentRootTypes(),
                        outParam(), outParamRootType(), returnType(),
                        extraValuesToPop.numValues, expectTailCall) {}
 };
 
 // GCC warns when the signature does not have matching attributes (for example
 // MOZ_MUST_USE). Squelch this warning to avoid a GCC-only footgun.
 #if MOZ_IS_GCC
 #  pragma GCC diagnostic push
 #  pragma GCC diagnostic ignored "-Wignored-attributes"
 #endif
 
 // Generate VMFunctionData array.
 static constexpr VMFunctionData vmFunctions[] = {
-#define DEF_VMFUNCTION(name, fp) \
-  VMFunctionDataHelper<decltype(&(::fp))>(::fp, #name),
+#define DEF_VMFUNCTION(name, fp) VMFunctionDataHelper<decltype(&(::fp))>(#name),
     VMFUNCTION_LIST(DEF_VMFUNCTION)
 #undef DEF_VMFUNCTION
 };
 
 #if MOZ_IS_GCC
 #  pragma GCC diagnostic pop
 #endif
 
+// Generate array storing C++ function pointers. These pointers are not stored
+// in VMFunctionData because there's no good way to cast them to void* in
+// constexpr code. Compilers are smart enough to treat the const array below as
+// constexpr.
+static void* const vmFunctionTargets[] = {
+#define DEF_VMFUNCTION(name, fp) (void*)(::fp),
+    VMFUNCTION_LIST(DEF_VMFUNCTION)
+#undef DEF_VMFUNCTION
+};
+
 const VMFunctionData& GetVMFunction(VMFunctionId id) {
   return vmFunctions[size_t(id)];
 }
 
 bool JitRuntime::generateVMWrappers(JSContext* cx, MacroAssembler& masm) {
   // Generate all VM function wrappers.
 
   static constexpr size_t NumVMFunctions = size_t(VMFunctionId::Count);
@@ -129,17 +138,17 @@ bool JitRuntime::generateVMWrappers(JSCo
                  "VM function list must be sorted by name");
     }
     lastName = fun.name();
 #endif
 
     JitSpew(JitSpew_Codegen, "# VM function wrapper (%s)", fun.name());
 
     uint32_t offset;
-    if (!generateVMWrapper(cx, masm, fun, &offset)) {
+    if (!generateVMWrapper(cx, masm, fun, vmFunctionTargets[i], &offset)) {
       return false;
     }
 
     MOZ_ASSERT(functionWrapperOffsets_.length() == size_t(id));
     functionWrapperOffsets_.infallibleAppend(offset);
   }
 
   return true;
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -123,19 +123,16 @@ enum MaybeTailCall : bool { TailCall, No
 //      if (!callVM(FooInfo)) {
 //          return false;
 //      }
 //
 // After this, the result value is in the return value register.
 
 // Data for a VM function. All VMFunctionDatas are stored in a constexpr array.
 struct VMFunctionData {
-  // Address of the C function.
-  void* wrapped;
-
 #if defined(JS_JITSPEW) || defined(JS_TRACE_LOGGING)
   // Informative name of the wrapped function. The name should not be present
   // in release builds in order to save memory.
   const char* name_;
 #endif
 
   // Note: a maximum of seven root types is supported.
   enum RootType : uint8_t {
@@ -288,24 +285,24 @@ struct VMFunctionData {
     // few loop iterations)
     while (n) {
       count++;
       n &= n - 1;
     }
     return count;
   }
 
-  constexpr VMFunctionData(void* wrapped, const char* name,
-                           uint32_t explicitArgs, uint32_t argumentProperties,
+  constexpr VMFunctionData(const char* name, uint32_t explicitArgs,
+                           uint32_t argumentProperties,
                            uint32_t argumentPassedInFloatRegs,
                            uint64_t argRootTypes, DataType outParam,
                            RootType outParamRootType, DataType returnType,
                            uint8_t extraValuesToPop = 0,
                            MaybeTailCall expectTailCall = NonTailCall)
-      : wrapped(wrapped),
+      :
 #if defined(JS_JITSPEW) || defined(JS_TRACE_LOGGING)
         name_(name),
 #endif
         argumentRootTypes(argRootTypes),
         argumentProperties(argumentProperties),
         argumentPassedInFloatRegs(argumentPassedInFloatRegs),
         explicitArgs(explicitArgs),
         outParamRootType(outParamRootType),
@@ -319,17 +316,17 @@ struct VMFunctionData {
     MOZ_ASSERT(returnType == Type_Void || returnType == Type_Bool ||
                returnType == Type_Object);
   }
 
   // Note: clang-tidy suggests using |= auto| here but that generates extra
   // static initializers for old-style VMFunction definitions with Clang. We can
   // do this after bug 1530937 converts all of them.
   constexpr VMFunctionData(const VMFunctionData& o)
-      : wrapped(o.wrapped),
+      :
 #if defined(JS_JITSPEW) || defined(JS_TRACE_LOGGING)
         name_(o.name_),
 #endif
         argumentRootTypes(o.argumentRootTypes),
         argumentProperties(o.argumentProperties),
         argumentPassedInFloatRegs(o.argumentPassedInFloatRegs),
         explicitArgs(o.explicitArgs),
         outParamRootType(o.outParamRootType),
@@ -338,34 +335,39 @@ struct VMFunctionData {
         extraValuesToPop(o.extraValuesToPop),
         expectTailCall(o.expectTailCall) {
   }
 };
 
 // TODO(bug 1530937): remove VMFunction and FunctionInfo after converting all VM
 // functions to the new design.
 struct VMFunction : public VMFunctionData {
+  // Address of the C function.
+  void* wrapped;
+
   // Global linked list of all VMFunctions.
   static VMFunction* functions;
   VMFunction* next;
 
   constexpr VMFunction(void* wrapped, const char* name, uint32_t explicitArgs,
                        uint32_t argumentProperties,
                        uint32_t argumentPassedInFloatRegs,
                        uint64_t argRootTypes, DataType outParam,
                        RootType outParamRootType, DataType returnType,
                        uint8_t extraValuesToPop = 0,
                        MaybeTailCall expectTailCall = NonTailCall)
-      : VMFunctionData(wrapped, name, explicitArgs, argumentProperties,
+      : VMFunctionData(name, explicitArgs, argumentProperties,
                        argumentPassedInFloatRegs, argRootTypes, outParam,
                        outParamRootType, returnType, extraValuesToPop,
                        expectTailCall),
+        wrapped(wrapped),
         next(nullptr) {}
 
-  VMFunction(const VMFunction& o) : VMFunctionData(o), next(functions) {
+  VMFunction(const VMFunction& o)
+      : VMFunctionData(o), wrapped(o.wrapped), next(functions) {
     // Add this to the global list of VMFunctions.
     functions = this;
   }
 
   typedef const VMFunction* Lookup;
 
   static HashNumber hash(const VMFunction* f) {
     // The hash is based on the wrapped function, not the VMFunction*, to
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -699,17 +699,17 @@ JitRuntime::BailoutTable JitRuntime::gen
 void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
                                         Label* bailoutTail) {
   bailoutHandlerOffset_ = startTrampolineCode(masm);
 
   GenerateBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID, bailoutTail);
 }
 
 bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
-                                   const VMFunctionData& f,
+                                   const VMFunctionData& f, void* nativeFun,
                                    uint32_t* wrapperOffset) {
   MOZ_ASSERT(functionWrappers_);
 
   *wrapperOffset = startTrampolineCode(masm);
 
   AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
 
   static_assert(
@@ -820,17 +820,17 @@ bool JitRuntime::generateVMWrapper(JSCon
     }
   }
 
   // Copy the implicit outparam, if any.
   if (outReg != InvalidReg) {
     masm.passABIArg(outReg);
   }
 
-  masm.callWithABI(f.wrapped, MoveOp::GENERAL,
+  masm.callWithABI(nativeFun, MoveOp::GENERAL,
                    CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
   if (!generateTLExitVM(masm, f)) {
     return false;
   }
 
   // Test for failure.
   switch (f.failType()) {
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -528,17 +528,17 @@ JitRuntime::BailoutTable JitRuntime::gen
 void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
                                         Label* bailoutTail) {
   bailoutHandlerOffset_ = startTrampolineCode(masm);
 
   GenerateBailoutThunk(masm, bailoutTail);
 }
 
 bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
-                                   const VMFunctionData& f,
+                                   const VMFunctionData& f, void* nativeFun,
                                    uint32_t* wrapperOffset) {
   MOZ_ASSERT(functionWrappers_);
 
   *wrapperOffset = startTrampolineCode(masm);
 
   // Avoid conflicts with argument registers while discarding the result after
   // the function call.
   AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
@@ -652,17 +652,17 @@ bool JitRuntime::generateVMWrapper(JSCon
   // Copy the semi-implicit outparam, if any.
   // It is not a C++-abi outparam, which would get passed in the
   // outparam register, but a real parameter to the function, which
   // was stack-allocated above.
   if (outReg != InvalidReg) {
     masm.passABIArg(outReg);
   }
 
-  masm.callWithABI(f.wrapped, MoveOp::GENERAL,
+  masm.callWithABI(nativeFun, MoveOp::GENERAL,
                    CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
   if (!generateTLExitVM(masm, f)) {
     return false;
   }
 
   // SP is used to transfer stack across call boundaries.
   masm.initPseudoStackPtr();
--- a/js/src/jit/none/Trampoline-none.cpp
+++ b/js/src/jit/none/Trampoline-none.cpp
@@ -35,17 +35,17 @@ void JitRuntime::generateExceptionTailSt
 void JitRuntime::generateBailoutTailStub(MacroAssembler&, Label*) {
   MOZ_CRASH();
 }
 void JitRuntime::generateProfilerExitFrameTailStub(MacroAssembler&, Label*) {
   MOZ_CRASH();
 }
 
 bool JitRuntime::generateVMWrapper(JSContext*, MacroAssembler&,
-                                   const VMFunctionData&, uint32_t*) {
+                                   const VMFunctionData&, void*, uint32_t*) {
   MOZ_CRASH();
 }
 
 FrameSizeClass FrameSizeClass::FromDepth(uint32_t) { MOZ_CRASH(); }
 FrameSizeClass FrameSizeClass::ClassLimit() { MOZ_CRASH(); }
 uint32_t FrameSizeClass::frameSize() const { MOZ_CRASH(); }
 
 BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& iter,
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -586,17 +586,17 @@ JitRuntime::BailoutTable JitRuntime::gen
 void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
                                         Label* bailoutTail) {
   bailoutHandlerOffset_ = startTrampolineCode(masm);
 
   GenerateBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID, bailoutTail);
 }
 
 bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
-                                   const VMFunctionData& f,
+                                   const VMFunctionData& f, void* nativeFun,
                                    uint32_t* wrapperOffset) {
   MOZ_ASSERT(functionWrappers_);
 
   *wrapperOffset = startTrampolineCode(masm);
 
   // Avoid conflicts with argument registers while discarding the result after
   // the function call.
   AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
@@ -698,17 +698,17 @@ bool JitRuntime::generateVMWrapper(JSCon
     }
   }
 
   // Copy the implicit outparam, if any.
   if (outReg != InvalidReg) {
     masm.passABIArg(outReg);
   }
 
-  masm.callWithABI(f.wrapped, MoveOp::GENERAL,
+  masm.callWithABI(nativeFun, MoveOp::GENERAL,
                    CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
   if (!generateTLExitVM(masm, f)) {
     return false;
   }
 
   // Test for failure.
   switch (f.failType()) {
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -603,17 +603,17 @@ JitRuntime::BailoutTable JitRuntime::gen
 void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
                                         Label* bailoutTail) {
   bailoutHandlerOffset_ = startTrampolineCode(masm);
 
   GenerateBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID, bailoutTail);
 }
 
 bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
-                                   const VMFunctionData& f,
+                                   const VMFunctionData& f, void* nativeFun,
                                    uint32_t* wrapperOffset) {
   MOZ_ASSERT(functionWrappers_);
 
   *wrapperOffset = startTrampolineCode(masm);
 
   // Avoid conflicts with argument registers while discarding the result after
   // the function call.
   AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
@@ -715,17 +715,17 @@ bool JitRuntime::generateVMWrapper(JSCon
     }
   }
 
   // Copy the implicit outparam, if any.
   if (outReg != InvalidReg) {
     masm.passABIArg(outReg);
   }
 
-  masm.callWithABI(f.wrapped, MoveOp::GENERAL,
+  masm.callWithABI(nativeFun, MoveOp::GENERAL,
                    CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
   if (!generateTLExitVM(masm, f)) {
     return false;
   }
 
   // Test for failure.
   switch (f.failType()) {
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -2665,17 +2665,21 @@ bool JSStructuredCloneReader::readTransf
 
   for (uint64_t i = 0; i < numTransferables; i++) {
     auto pos = in.tell();
 
     if (!in.readPair(&tag, &data)) {
       return false;
     }
 
-    MOZ_ASSERT(tag != SCTAG_TRANSFER_MAP_PENDING_ENTRY);
+    if (tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY) {
+      ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE);
+      return false;
+    }
+
     RootedObject obj(cx);
 
     void* content;
     if (!in.readPtr(&content)) {
       return false;
     }
 
     uint64_t extraData;
@@ -2707,17 +2711,20 @@ bool JSStructuredCloneReader::readTransf
       auto guard = mozilla::MakeScopeExit([&] { in.seekTo(savedPos); });
       in.seekTo(pos);
       in.seekBy(static_cast<size_t>(extraData));
 
       uint32_t tag, data;
       if (!in.readPair(&tag, &data)) {
         return false;
       }
-      MOZ_ASSERT(tag == SCTAG_ARRAY_BUFFER_OBJECT);
+      if (tag != SCTAG_ARRAY_BUFFER_OBJECT) {
+        ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE);
+        return false;
+      }
       RootedValue val(cx);
       if (!readArrayBuffer(data, &val)) {
         return false;
       }
       obj = &val.toObject();
     } else {
       if (!callbacks || !callbacks->readTransfer) {
         ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE);
--- a/js/xpconnect/tests/chrome/test_sharedChromeCompartment.html
+++ b/js/xpconnect/tests/chrome/test_sharedChromeCompartment.html
@@ -12,48 +12,44 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript">
 "use strict";
 
 /** Test for Bug 1517694 **/
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 SimpleTest.waitForExplicitFinish();
 
-function isSameCompartment(global) {
-    // Note: if the global is in a different compartment, global.Math will be a
-    // cross-compartment wrapper (with class "Proxy").
-    return Cu.getClassName(global.Math, /* unwrap = */ false) === "Math";
-}
-
 function go() {
     var frame = $('subframe');
     frame.onload = null;
 
-    ok(isSameCompartment(frame.contentWindow),
+    var isSameCompartment = Cu.getJSTestingFunctions().isSameCompartment;
+
+    ok(isSameCompartment(window, frame.contentWindow),
        "System window is in same compartment");
 
     var sameCompSb = Cu.Sandbox(window);
-    ok(isSameCompartment(sameCompSb),
+    ok(isSameCompartment(window, sameCompSb),
        "System sandbox is in same compartment");
 
     var ownCompSb = Cu.Sandbox(window, {freshCompartment: true});
-    ok(!isSameCompartment(ownCompSb),
+    ok(!isSameCompartment(window, ownCompSb),
        "Sandbox created with freshCompartment is in separate compartment");
 
     // Sandbox created in fresh-compartment sandbox must be in shared
     // compartment.
     var sb = Cu.evalInSandbox(`
       let principal =
         Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal);
       Cu.Sandbox(principal)
     `, ownCompSb);
-    ok(isSameCompartment(sb),
+    ok(isSameCompartment(window, sb),
        "System sandbox created in different compartment is in system compartment");
 
-    is(Cu.getClassName(XPCOMUtils, /* unwrap = */ false), "Object",
+    ok(isSameCompartment(window, XPCOMUtils),
        "Object defined in JSM is in same compartment");
 
     SimpleTest.finish();
 }
 
   </script>
 </head>
 <body>
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -209,17 +209,16 @@ using namespace mozilla::layers;
 using namespace mozilla::gfx;
 using namespace mozilla::layout;
 using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
 typedef ScrollableLayerGuid::ViewID ViewID;
 
 CapturingContentInfo nsIPresShell::gCaptureInfo = {
     false /* mAllowed */, false /* mPointerLock */,
     false /* mRetargetToElement */, false /* mPreventDrag */};
-nsIContent* nsIPresShell::gKeyDownTarget;
 
 // RangePaintInfo is used to paint ranges to offscreen buffers
 struct RangePaintInfo {
   RefPtr<nsRange> mRange;
   nsDisplayListBuilder mBuilder;
   nsDisplayList mList;
 
   // offset of builder's reference frame to the root frame
@@ -628,16 +627,17 @@ bool nsIPresShell::DirtyRootsList::Frame
 }
 
 bool PresShell::sDisableNonTestMouseEvents = false;
 
 mozilla::LazyLogModule nsIPresShell::gLog("PresShell");
 
 mozilla::TimeStamp PresShell::EventHandler::sLastInputCreated;
 mozilla::TimeStamp PresShell::EventHandler::sLastInputProcessed;
+StaticRefPtr<Element> PresShell::EventHandler::sLastKeyDownEventTargetElement;
 
 bool PresShell::sProcessInteractable = false;
 
 static bool gVerifyReflowEnabled;
 
 bool nsIPresShell::GetVerifyReflowEnable() {
 #ifdef DEBUG
   static bool firstTime = true;
@@ -1198,19 +1198,17 @@ void PresShell::Destroy() {
 
     mDocAccessible->Shutdown();
     mDocAccessible = nullptr;
   }
 #endif  // ACCESSIBILITY
 
   MaybeReleaseCapturingContent();
 
-  if (gKeyDownTarget && gKeyDownTarget->OwnerDoc() == mDocument) {
-    NS_RELEASE(gKeyDownTarget);
-  }
+  EventHandler::OnPresShellDestroy(mDocument);
 
   if (mContentToScrollTo) {
     mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
     mContentToScrollTo = nullptr;
   }
 
   if (mPresContext) {
     // We need to notify the destroying the nsPresContext to ESM for
@@ -6541,73 +6539,36 @@ nsresult PresShell::EventHandler::Handle
 
     return NS_OK;
   }
 
   nsresult rv = NS_OK;
 
   PushCurrentEventInfo(nullptr, nullptr);
 
-  // key and IME related events go to the focused frame in this DOM window.
   if (aGUIEvent->IsTargetedAtFocusedContent()) {
     mPresShell->mCurrentEventContent = nullptr;
 
-    nsCOMPtr<nsPIDOMWindowOuter> window = GetDocument()->GetWindow();
-    nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
-    nsCOMPtr<nsIContent> eventTarget = nsFocusManager::GetFocusedDescendant(
-        window, nsFocusManager::eOnlyCurrentWindow,
-        getter_AddRefs(focusedWindow));
-
-    // otherwise, if there is no focused content or the focused content has
-    // no frame, just use the root content. This ensures that key events
-    // still get sent to the window properly if nothing is focused or if a
-    // frame goes away while it is focused.
-    if (!eventTarget || !eventTarget->GetPrimaryFrame()) {
-      eventTarget = GetDocument()->GetUnfocusedKeyEventTarget();
-    }
-
-    if (aGUIEvent->mMessage == eKeyDown) {
-      NS_IF_RELEASE(nsIPresShell::gKeyDownTarget);
-      NS_IF_ADDREF(nsIPresShell::gKeyDownTarget = eventTarget);
-    } else if ((aGUIEvent->mMessage == eKeyPress ||
-                aGUIEvent->mMessage == eKeyUp) &&
-               nsIPresShell::gKeyDownTarget) {
-      // If a different element is now focused for the keypress/keyup event
-      // than what was focused during the keydown event, check if the new
-      // focused element is not in a chrome document any more, and if so,
-      // retarget the event back at the keydown target. This prevents a
-      // content area from grabbing the focus from chrome in-between key
-      // events.
-      if (eventTarget) {
-        bool keyDownIsChrome = nsContentUtils::IsChromeDoc(
-            nsIPresShell::gKeyDownTarget->GetComposedDoc());
-        if (keyDownIsChrome !=
-                nsContentUtils::IsChromeDoc(eventTarget->GetComposedDoc()) ||
-            (keyDownIsChrome && TabParent::GetFrom(eventTarget))) {
-          eventTarget = nsIPresShell::gKeyDownTarget;
-        }
-      }
-
-      if (aGUIEvent->mMessage == eKeyUp) {
-        NS_RELEASE(nsIPresShell::gKeyDownTarget);
-      }
-    }
+    RefPtr<Element> eventTargetElement =
+        ComputeFocusedEventTargetElement(aGUIEvent);
 
     mPresShell->mCurrentEventFrame = nullptr;
-    Document* targetDoc = eventTarget ? eventTarget->OwnerDoc() : nullptr;
+    Document* targetDoc =
+        eventTargetElement ? eventTargetElement->OwnerDoc() : nullptr;
     if (targetDoc && targetDoc != GetDocument()) {
       PopCurrentEventInfo();
       nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell();
       if (shell) {
         rv = static_cast<PresShell*>(shell.get())
-                 ->HandleRetargetedEvent(aGUIEvent, aEventStatus, eventTarget);
+                 ->HandleRetargetedEvent(aGUIEvent, aEventStatus,
+                                         eventTargetElement);
       }
       return rv;
     } else {
-      mPresShell->mCurrentEventContent = eventTarget;
+      mPresShell->mCurrentEventContent = eventTargetElement;
     }
 
     if (!mPresShell->GetCurrentEventContent() ||
         !mPresShell->GetCurrentEventFrame() ||
         InZombieDocument(mPresShell->mCurrentEventContent)) {
       rv = RetargetEventToParent(aGUIEvent, aEventStatus);
       PopCurrentEventInfo();
       return rv;
@@ -7497,16 +7458,70 @@ PresShell::EventHandler::HandleEventWith
 
   EventHandler eventHandlerForCapturingContent(
       std::move(presShellForCapturingContent));
   return eventHandlerForCapturingContent.HandleEventWithTarget(
       aGUIEvent, nullptr, aPointerCapturingContent, aEventStatus, true, nullptr,
       overrideClickTarget);
 }
 
+Element* PresShell::EventHandler::ComputeFocusedEventTargetElement(
+    WidgetGUIEvent* aGUIEvent) {
+  MOZ_ASSERT(aGUIEvent);
+  MOZ_ASSERT(aGUIEvent->IsTargetedAtFocusedContent());
+
+  // key and IME related events go to the focused frame in this DOM window.
+  nsPIDOMWindowOuter* window = GetDocument()->GetWindow();
+  nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+  Element* eventTargetElement = nsFocusManager::GetFocusedDescendant(
+      window, nsFocusManager::eOnlyCurrentWindow,
+      getter_AddRefs(focusedWindow));
+
+  // otherwise, if there is no focused content or the focused content has
+  // no frame, just use the root content. This ensures that key events
+  // still get sent to the window properly if nothing is focused or if a
+  // frame goes away while it is focused.
+  if (!eventTargetElement || !eventTargetElement->GetPrimaryFrame()) {
+    eventTargetElement = GetDocument()->GetUnfocusedKeyEventTarget();
+  }
+
+  switch (aGUIEvent->mMessage) {
+    case eKeyDown:
+      sLastKeyDownEventTargetElement = eventTargetElement;
+      return eventTargetElement;
+    case eKeyPress:
+    case eKeyUp:
+      if (!sLastKeyDownEventTargetElement) {
+        return eventTargetElement;
+      }
+      // If a different element is now focused for the keypress/keyup event
+      // than what was focused during the keydown event, check if the new
+      // focused element is not in a chrome document any more, and if so,
+      // retarget the event back at the keydown target. This prevents a
+      // content area from grabbing the focus from chrome in-between key
+      // events.
+      if (eventTargetElement) {
+        bool keyDownIsChrome = nsContentUtils::IsChromeDoc(
+            sLastKeyDownEventTargetElement->GetComposedDoc());
+        if (keyDownIsChrome != nsContentUtils::IsChromeDoc(
+                                   eventTargetElement->GetComposedDoc()) ||
+            (keyDownIsChrome && TabParent::GetFrom(eventTargetElement))) {
+          eventTargetElement = sLastKeyDownEventTargetElement;
+        }
+      }
+
+      if (aGUIEvent->mMessage == eKeyUp) {
+        sLastKeyDownEventTargetElement = nullptr;
+      }
+      MOZ_FALLTHROUGH;
+    default:
+      return eventTargetElement;
+  }
+}
+
 Document* PresShell::GetPrimaryContentDocument() {
   nsPresContext* context = GetPresContext();
   if (!context || !context->IsRoot()) {
     return nullptr;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> shellAsTreeItem = context->GetDocShell();
   if (!shellAsTreeItem) {
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -11,16 +11,17 @@
 
 #include "MobileViewportManager.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/HTMLDocumentBinding.h"
 #include "mozilla/layers/FocusTarget.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ServoStyleSet.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "nsContentUtils.h"  // For AddScriptBlocker().
 #include "nsCRT.h"
 #include "nsIObserver.h"
 #include "nsIPresShell.h"
 #include "nsISelectionController.h"
 #include "nsIWidget.h"
 #include "nsPresContext.h"
@@ -571,16 +572,27 @@ class PresShell final : public nsIPresSh
     nsresult HandleEventWithTarget(WidgetEvent* aEvent,
                                    nsIFrame* aNewEventFrame,
                                    nsIContent* aNewEventContent,
                                    nsEventStatus* aEventStatus,
                                    bool aIsHandlingNativeEvent,
                                    nsIContent** aTargetContent,
                                    nsIContent* aOverrideClickTarget);
 
+    /**
+     * OnPresShellDestroy() is called when every PresShell instance is being
+     * destroyed.
+     */
+    static inline void OnPresShellDestroy(Document* aDocument) {
+      if (sLastKeyDownEventTargetElement &&
+          sLastKeyDownEventTargetElement->OwnerDoc() == aDocument) {
+        sLastKeyDownEventTargetElement = nullptr;
+      }
+    }
+
    private:
     static bool InZombieDocument(nsIContent* aContent);
     static nsIFrame* GetNearestFrameContainingPresShell(
         nsIPresShell* aPresShell);
     static already_AddRefed<nsIURI> GetDocumentURIToCompareWithBlacklist(
         PresShell& aPresShell);
 
     /**
@@ -961,16 +973,27 @@ class PresShell final : public nsIPresSh
      *                                  HandeEventWithTraget().
      */
     MOZ_CAN_RUN_SCRIPT
     nsresult HandleEventWithPointerCapturingContentWithoutItsFrame(
         nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
         nsIContent* aPointerCapturingContent, nsEventStatus* aEventStatus);
 
     /**
+     * ComputeFocusedEventTargetElement() returns event target element for
+     * aGUIEvent which should be handled with focused content.
+     * This may set/unset sLastKeyDownEventTarget if necessary.
+     *
+     * @param aGUIEvent                 The handling event.
+     * @return                          The element which should be the event
+     *                                  target of aGUIEvent.
+     */
+    Element* ComputeFocusedEventTargetElement(WidgetGUIEvent* aGUIEvent);
+
+    /**
      * XXX Needs better name.
      * HandleEventInternal() dispatches aEvent into the DOM tree and
      * notify EventStateManager of that.
      *
      * @param aEvent                    Event to be dispatched.
      * @param aEventStatus              [in/out] EventStatus of aEvent.
      * @param aIsHandlingNativeEvent    true if aGUIEvent represents a native
      *                                  event.
@@ -1065,16 +1088,17 @@ class PresShell final : public nsIPresSh
     }
     void PushDelayedEventIntoQueue(UniquePtr<DelayedEvent>&& aDelayedEvent) {
       mPresShell->mDelayedEvents.AppendElement(std::move(aDelayedEvent));
     }
 
     OwningNonNull<PresShell> mPresShell;
     static TimeStamp sLastInputCreated;
     static TimeStamp sLastInputProcessed;
+    static StaticRefPtr<Element> sLastKeyDownEventTargetElement;
   };
 
   /**
    * Helper method of EventHandler::HandleEvent().  This is called when the
    * event is dispatched without ref-point and dispatched by
    * EventHandler::HandleEvent().
    *
    * See EventHandler::HandleRetargetedEvent() for the detail of the arguments.
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -2046,18 +2046,16 @@ class nsIPresShell : public nsStubDocume
   bool mIsNeverPainting : 1;
 
   // Whether the most recent change to the pres shell resolution was
   // originated by the main thread.
   bool mResolutionUpdated : 1;
 
   uint32_t mPresShellId;
 
-  static nsIContent* gKeyDownTarget;
-
   // Cached font inflation values. This is done to prevent changing of font
   // inflation until a page is reloaded.
   uint32_t mFontSizeInflationEmPerLine;
   uint32_t mFontSizeInflationMinTwips;
   uint32_t mFontSizeInflationLineThreshold;
 
   // Whether we're currently under a FlushPendingNotifications.
   // This is used to handle flush reentry correctly.
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10770,21 +10770,17 @@ CompositorHitTestInfo nsIFrame::GetCompo
     // If the frame is a plugin frame and wants to handle wheel events as
     // default action, we should add the frame to dispatch-to-content region.
     nsPluginFrame* pluginFrame = do_QueryFrame(this);
     if (pluginFrame && pluginFrame->WantsToHandleWheelEventAsDefaultAction()) {
       result += CompositorHitTestFlags::eDispatchToContent;
     }
   }
 
-  nsIDocShell* docShell = nullptr;
-  if (PresShell()->GetDocument()) {
-    docShell = PresShell()->GetDocument()->GetDocShell();
-  }
-  if (dom::TouchEvent::PrefEnabled(docShell)) {
+  if (aBuilder->IsTouchEventPrefEnabledDoc()) {
     // Inherit the touch-action flags from the parent, if there is one. We do
     // this because of how the touch-action on a frame combines the touch-action
     // from ancestor DOM elements. Refer to the documentation in
     // TouchActionHelper.cpp for details; this code is meant to be equivalent to
     // that code, but woven into the top-down recursive display list building
     // process.
     CompositorHitTestInfo inheritedTouchAction =
         aBuilder->GetHitTestInfo() & CompositorHitTestTouchActionMask;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -20,16 +20,17 @@
 #include "gfxUtils.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/KeyframeEffect.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "mozilla/dom/SVGElement.h"
+#include "mozilla/dom/TouchEvent.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/layers/PLayerTransaction.h"
 #include "mozilla/ShapeUtils.h"
 #include "nsCSSRendering.h"
 #include "nsCSSRenderingGradients.h"
 #include "nsISelectionController.h"
 #include "nsIPresShell.h"
 #include "nsRegion.h"
@@ -1417,22 +1418,24 @@ void nsDisplayListBuilder::EnterPresShel
   state->mInsidePointerEventsNoneDoc = pointerEventsNone;
 
   state->mPresShellIgnoreScrollFrame =
       state->mPresShell->IgnoringViewportScrolling()
           ? state->mPresShell->GetRootScrollFrame()
           : nullptr;
 
   nsPresContext* pc = aReferenceFrame->PresContext();
-  nsCOMPtr<nsIDocShell> docShell = pc->GetDocShell();
+  mIsInChromePresContext = pc->IsChrome();
+  nsIDocShell* docShell = pc->GetDocShell();
+
   if (docShell) {
     docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
   }
 
-  mIsInChromePresContext = pc->IsChrome();
+  state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
 
   if (!buildCaret) {
     return;
   }
 
   RefPtr<nsCaret> caret = state->mPresShell->GetCaret();
   state->mCaretFrame = caret->GetPaintGeometry(&state->mCaretRect);
   if (state->mCaretFrame) {
@@ -1513,21 +1516,21 @@ void nsDisplayListBuilder::LeavePresShel
       if (!CurrentPresShellState()->mIsBackgroundOnly &&
           DisplayListIsContentful(aPaintedContents)) {
         pc->NotifyContentfulPaint();
       }
     }
   }
 
   ResetMarkedFramesForDisplayList(aReferenceFrame);
-  mPresShellStates.SetLength(mPresShellStates.Length() - 1);
+  mPresShellStates.RemoveLastElement();
 
   if (!mPresShellStates.IsEmpty()) {
     nsPresContext* pc = CurrentPresContext();
-    nsCOMPtr<nsIDocShell> docShell = pc->GetDocShell();
+    nsIDocShell* docShell = pc->GetDocShell();
     if (docShell) {
       docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
     }
     mIsInChromePresContext = pc->IsChrome();
   } else {
     mCurrentAGR = mRootAGR;
 
     for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -737,16 +737,20 @@ class nsDisplayListBuilder {
   void BuildCompositorHitTestInfoIfNeeded(nsIFrame* aFrame,
                                           nsDisplayList* aList,
                                           const bool aBuildNew);
 
   bool IsInsidePointerEventsNoneDoc() {
     return CurrentPresShellState()->mInsidePointerEventsNoneDoc;
   }
 
+  bool IsTouchEventPrefEnabledDoc() {
+    return CurrentPresShellState()->mTouchEventPrefEnabledDoc;
+  }
+
   bool GetAncestorHasApzAwareEventHandler() const {
     return mAncestorHasApzAwareEventHandler;
   }
 
   void SetAncestorHasApzAwareEventHandler(bool aValue) {
     mAncestorHasApzAwareEventHandler = aValue;
   }
 
@@ -1828,16 +1832,17 @@ class nsDisplayListBuilder {
     mozilla::Maybe<OutOfFlowDisplayData> mFixedBackgroundDisplayData;
     uint32_t mFirstFrameMarkedForDisplay;
     uint32_t mFirstFrameWithOOFData;
     bool mIsBackgroundOnly;
     // This is a per-document flag turning off event handling for all content
     // in the document, and is set when we enter a subdocument for a pointer-
     // events:none frame.
     bool mInsidePointerEventsNoneDoc;
+    bool mTouchEventPrefEnabledDoc;
     nsIFrame* mPresShellIgnoreScrollFrame;
   };
 
   PresShellState* CurrentPresShellState() {
     NS_ASSERTION(mPresShellStates.Length() > 0,
                  "Someone forgot to enter a presshell");
     return &mPresShellStates[mPresShellStates.Length() - 1];
   }
--- a/layout/style/test/test_parse_rule.html
+++ b/layout/style/test/test_parse_rule.html
@@ -8,16 +8,17 @@
 <!-- Note that the following style and div elements are duplicates
      of the ones written into the iframe; they are here for convienience
      in resolving the "standard" computed value for a given specification
 -->
 <style></style>
 <div id=a class='a b c' title='zxcv weeqweqeweasd&#13;&#10;a&#10;'></div>
 <script>
 SimpleTest.waitForExplicitFinish();
+SimpleTest.requestLongerTimeout(2);
 
 window.onload=function(){
 
 var base;
 
 // A short note about escaping: all of the strings in this test go through
 // Javascript unescaping before getting passed to CSS.  This means that
 // sequences like "\n" refer to a newline, a single backslash is written "\\",
--- a/media/mtransport/ipc/PWebrtcProxyChannel.ipdl
+++ b/media/mtransport/ipc/PWebrtcProxyChannel.ipdl
@@ -14,17 +14,17 @@ namespace net {
 
 async protocol PWebrtcProxyChannel
 {
   manager PNecko;
 
 parent:
   async AsyncOpen(nsCString aHost,
                   int32_t aPort,
-                  OptionalLoadInfoArgs aLoadInfoArgs,
+                  LoadInfoArgs? aLoadInfoArgs,
                   nsCString aAlpn);
   async Write(uint8_t[] aWriteData);
   async Close();
 
 child:
   async OnClose(nsresult aReason);
   async OnConnected();
   async OnRead(uint8_t[] aReadData);
--- a/media/mtransport/ipc/WebrtcProxyChannelChild.cpp
+++ b/media/mtransport/ipc/WebrtcProxyChannelChild.cpp
@@ -78,16 +78,16 @@ void WebrtcProxyChannelChild::AsyncOpen(
   AddIPDLReference();
 
   gNeckoChild->SetEventTargetForActor(this, GetMainThreadEventTarget());
   gNeckoChild->SendPWebrtcProxyChannelConstructor(this, aBrowser);
 
   nsCOMPtr<nsILoadInfo> loadInfo =
       new LoadInfo(aLoadingPrincipal, nullptr, nullptr, 0, 0);
 
-  OptionalLoadInfoArgs loadInfoArgs;
+  Maybe<LoadInfoArgs> loadInfoArgs;
   MOZ_ALWAYS_SUCCEEDS(LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs));
 
   SendAsyncOpen(aHost, aPort, loadInfoArgs, aAlpn);
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/media/mtransport/ipc/WebrtcProxyChannelParent.cpp
+++ b/media/mtransport/ipc/WebrtcProxyChannelParent.cpp
@@ -14,17 +14,17 @@
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
 mozilla::ipc::IPCResult WebrtcProxyChannelParent::RecvAsyncOpen(
     const nsCString& aHost, const int& aPort,
-    const OptionalLoadInfoArgs& aLoadInfoArgs, const nsCString& aAlpn) {
+    const Maybe<LoadInfoArgs>& aLoadInfoArgs, const nsCString& aAlpn) {
   LOG(("WebrtcProxyChannelParent::RecvAsyncOpen %p to %s:%d\n", this,
        aHost.get(), aPort));
 
   nsresult rv;
 
   nsCOMPtr<nsILoadInfo> loadInfo;
 
   rv = LoadInfoArgsToLoadInfo(aLoadInfoArgs, getter_AddRefs(loadInfo));
--- a/media/mtransport/ipc/WebrtcProxyChannelParent.h
+++ b/media/mtransport/ipc/WebrtcProxyChannelParent.h
@@ -20,17 +20,17 @@ class WebrtcProxyChannel;
 
 class WebrtcProxyChannelParent : public PWebrtcProxyChannelParent,
                                  public WebrtcProxyChannelCallback {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcProxyChannelParent, override)
 
   mozilla::ipc::IPCResult RecvAsyncOpen(
       const nsCString& aHost, const int& aPort,
-      const OptionalLoadInfoArgs& aLoadInfoArgs,
+      const Maybe<LoadInfoArgs>& aLoadInfoArgs,
       const nsCString& aAlpn) override;
 
   mozilla::ipc::IPCResult RecvWrite(nsTArray<uint8_t>&& aWriteData) override;
 
   mozilla::ipc::IPCResult RecvClose() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
@@ -581,21 +581,19 @@ void nr_ice_candidate_pair_set_state(nr_
 
 
     if(pair->state==NR_ICE_PAIR_STATE_FAILED ||
        pair->state==NR_ICE_PAIR_STATE_CANCELLED){
       nr_ice_component_failed_pair(pair->remote->component, pair);
     }
   }
 
-int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out)
+void nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, int log_level)
   {
-    /*r_log(LOG_ICE,LOG_DEBUG,"CAND-PAIR(%s): pair %s: state=%s, priority=0x%llx\n",pair->codeword,pair->as_string,nr_ice_cand_pair_states[pair->state],pair->priority);*/
-
-    return(0);
+    r_log(LOG_ICE,log_level,"CAND-PAIR(%s): pair %s: state=%s, priority=0x%llx\n",pair->codeword,pair->as_string,nr_ice_cand_pair_states[pair->state],pair->priority);
   }
 
 
 int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair)
   {
     if(pair->triggered_check_queue_entry.tqe_next ||
        pair->triggered_check_queue_entry.tqe_prev)
       return(0);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h
@@ -79,17 +79,17 @@ struct nr_ice_cand_pair_ {
   TAILQ_ENTRY(nr_ice_cand_pair_) check_queue_entry;   /* the check list */
   TAILQ_ENTRY(nr_ice_cand_pair_) triggered_check_queue_entry;  /* the trigger check queue */
 };
 
 int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp);
 int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
 int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
 void nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state);
-int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out);
+void nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, int log_level);
 void nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state);
 int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair);
 int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
 int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair);
 int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair);
 void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg);
 int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp);
 void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -1732,8 +1732,26 @@ int nr_ice_component_get_default_candida
     *candp = best_cand;
 
     _status=0;
   abort:
     return(_status);
 
   }
 
+
+void nr_ice_component_dump_state(nr_ice_component *comp, int log_level)
+  {
+    nr_ice_candidate *cand;
+
+    if (comp->local_component) {
+      r_log(LOG_ICE,log_level,"ICE(%s)/ICE-STREAM(%s): Remote component %d in state %d - dumping candidates",comp->ctx->label,comp->stream->label,comp->component_id,comp->state);
+    } else {
+      r_log(LOG_ICE,log_level,"ICE(%s)/ICE-STREAM(%s): Local component %d - dumping candidates",comp->ctx->label,comp->stream->label,comp->component_id);
+    }
+
+    cand=TAILQ_FIRST(&comp->candidates);
+    while(cand){
+      r_log(LOG_ICE,log_level,"ICE(%s)/ICE-STREAM(%s)/CAND(%s): %s",comp->ctx->label,comp->stream->label,cand->codeword,cand->label);
+      cand=TAILQ_NEXT(cand,entry_comp);
+    }
+  }
+
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.h
@@ -97,13 +97,14 @@ void nr_ice_component_check_if_failed(nr
 int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp);
 int nr_ice_component_set_failed(nr_ice_component *comp);
 int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp);
 int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair);
 int nr_ice_component_get_default_candidate(nr_ice_component *comp, nr_ice_candidate **candp, int ip_version);
 void nr_ice_component_consent_destroy(nr_ice_component *comp);
 void nr_ice_component_refresh_consent_now(nr_ice_component *comp);
 void nr_ice_component_disconnected(nr_ice_component *comp);
+void nr_ice_component_dump_state(nr_ice_component *comp, int log_level);
 
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
 #endif
--- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
@@ -378,17 +378,17 @@ static void nr_ice_media_stream_check_ti
       }
     }
 
     if(pair){
       nr_ice_candidate_pair_start(pair->pctx,pair); /* Ignore failures */
       NR_ASYNC_TIMER_SET(timer_val,nr_ice_media_stream_check_timer_cb,cb_arg,&stream->timer);
     }
     else {
-      r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): no pairs for %s",stream->pctx->label,stream->label);
+      r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): no FROZEN/WAITING pairs for %s",stream->pctx->label,stream->label);
     }
 
     _status=0;
   abort:
     if (_status) {
       // cb doesn't return anything, but we should probably log that we aborted
       // This also quiets the unused variable warnings.
       r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer cb for media stream %s abort with status: %d",
@@ -552,38 +552,46 @@ int nr_ice_media_stream_unfreeze_pairs_f
           default:
             break;
         }
       }
 
       str=STAILQ_NEXT(str,entry);
     }
 
-/*    nr_ice_media_stream_dump_state(stream->pctx,stream,stderr); */
-
-
     _status=0;
   abort:
     return(_status);
   }
 
 
-int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out)
+void nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int log_level)
   {
     nr_ice_cand_pair *pair;
+    nr_ice_component *comp;
 
-    /* r_log(LOG_ICE,LOG_DEBUG,"MEDIA-STREAM(%s): state dump", stream->label); */
+    if(stream->local_stream){
+      /* stream has a corresponding local_stream */
+      nr_ice_media_stream_dump_state(stream->local_stream->pctx,stream->local_stream, log_level);
+      r_log(LOG_ICE,log_level,"ICE-PEER(%s)/STREAM(%s): state dump", stream->pctx->label, stream->label);
+    } else {
+      r_log(LOG_ICE,log_level,"ICE(%s)/STREAM(%s): state dump", stream->ctx->label, stream->label);
+    }
+
     pair=TAILQ_FIRST(&stream->check_list);
     while(pair){
-      nr_ice_candidate_pair_dump_state(pair,out);
-
+      nr_ice_candidate_pair_dump_state(pair, log_level);
       pair=TAILQ_NEXT(pair,check_queue_entry);
     }
 
-    return(0);
+    comp=STAILQ_FIRST(&stream->components);
+    while(comp){
+      nr_ice_component_dump_state(comp, log_level);
+      comp=STAILQ_NEXT(comp,entry);
+    }
   }
 
 int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state)
   {
     /* Make no-change a no-op */
     if (state == str->ice_state)
       return 0;
 
@@ -599,16 +607,19 @@ int nr_ice_media_stream_set_state(nr_ice
       str->pctx->active_streams++;
     if(str->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)
       str->pctx->active_streams--;
 
     r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): %d active streams",
       str->pctx->label, str->pctx->active_streams);
 
     str->ice_state=state;
+    if (state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) {
+      nr_ice_media_stream_dump_state(str->pctx,str,LOG_ERR);
+    }
 
     return(0);
   }
 
 void nr_ice_media_stream_stop_checking(nr_ice_media_stream *str)
   {
     nr_ice_cand_pair *p;
     nr_ice_component *comp;
--- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h
@@ -88,17 +88,17 @@ int nr_ice_media_stream_finalize(nr_ice_
 int nr_ice_media_stream_initialize(struct nr_ice_ctx_ *ctx, nr_ice_media_stream *stream);
 int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp,int *attrctp);
 int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp);
 int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream);
 int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
 int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream, int *serviced);
 int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
 int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation);
-int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out);
+void nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int log_level);
 void nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component);
 void nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component);
 void nr_ice_media_stream_refresh_consent_all(nr_ice_media_stream *stream);
 void nr_ice_media_stream_disconnect_all_components(nr_ice_media_stream *stream);
 void nr_ice_media_stream_set_disconnected(nr_ice_media_stream *stream, int disconnected);
 int nr_ice_media_stream_check_if_connected(nr_ice_media_stream *stream);
 int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state);
 void nr_ice_media_stream_stop_checking(nr_ice_media_stream *str);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
@@ -621,38 +621,28 @@ void nr_ice_peer_ctx_stream_started_chec
       r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) is now checking",pctx->ctx->label,pctx->label);
       pctx->checks_started = 1;
       if (pctx->handler && pctx->handler->vtbl->ice_checking) {
         pctx->handler->vtbl->ice_checking(pctx->handler->obj, pctx);
       }
     }
   }
 
-#ifndef NDEBUG
-int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out)
+void nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx, int log_level)
   {
-    int r,_status;
     nr_ice_media_stream *stream;
 
-    fprintf(out,"PEER %s STATE DUMP\n",pctx->label);
-    fprintf(out,"==========================================\n");
+    r_log(LOG_ICE,log_level,"PEER %s STATE DUMP",pctx->label);
+    r_log(LOG_ICE,log_level,"==========================================");
     stream=STAILQ_FIRST(&pctx->peer_streams);
     while(stream){
-      if(r=nr_ice_media_stream_dump_state(pctx,stream,out))
-        ABORT(r);
-
-      stream=STAILQ_NEXT(stream,entry);
+      nr_ice_media_stream_dump_state(pctx,stream,log_level);
     }
-    fprintf(out,"==========================================\n");
-
-    _status=0;
-  abort:
-    return(_status);
+    r_log(LOG_ICE,log_level,"==========================================");
   }
-#endif
 
 void nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx *pctx)
   {
     nr_ice_media_stream *str;
 
     r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): refreshing consent on all streams",pctx->label);
 
     str=STAILQ_FIRST(&pctx->peer_streams);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
@@ -80,17 +80,17 @@ int nr_ice_peer_ctx_pair_candidates(nr_i
 int nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct);
 int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx);
 int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first);
 void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
 void nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx *pctx);
 void nr_ice_peer_ctx_disconnect_all_streams(nr_ice_peer_ctx *pctx);
 void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx);
 void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx);
-int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out);
+void nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx, int log_level);
 int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx);
 void nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx);
 int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp);
 int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
 int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id);
 int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand);
 void nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx *pctx);
 
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -25,23 +25,23 @@ class nsPIDOMWindowOuter;
 namespace mozilla {
 
 namespace dom {
 class PerformanceStorage;
 class XMLHttpRequestMainThread;
 }  // namespace dom
 
 namespace net {
-class OptionalLoadInfoArgs;
+class LoadInfoArgs;
 }  // namespace net
 
 namespace ipc {
 // we have to forward declare that function so we can use it as a friend.
 nsresult LoadInfoArgsToLoadInfo(
-    const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
+    const Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
     nsILoadInfo** outLoadInfo);
 }  // namespace ipc
 
 namespace net {
 
 typedef nsTArray<nsCOMPtr<nsIRedirectHistoryEntry>> RedirectHistoryArray;
 
 /**
@@ -126,17 +126,17 @@ class LoadInfo final : public nsILoadInf
            const nsAString& aCspNonce);
   LoadInfo(const LoadInfo& rhs);
 
   NS_IMETHOD GetRedirects(JSContext* aCx,
                           JS::MutableHandle<JS::Value> aRedirects,
                           const RedirectHistoryArray& aArra);
 
   friend nsresult mozilla::ipc::LoadInfoArgsToLoadInfo(
-      const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
+      const Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
       nsILoadInfo** outLoadInfo);
 
   ~LoadInfo() = default;
 
   void ComputeIsThirdPartyContext(nsPIDOMWindowOuter* aOuterWindow);
 
   // This function is the *only* function which can change the securityflags
   // of a loadinfo. It only exists because of the XHR code. Don't call it
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -123,25 +123,16 @@ struct LoadInfoArgs
   bool                        documentHasUserInteracted;
   bool                        documentHasLoaded;
   nsString                    cspNonce;
   bool                        isFromProcessingFrameAttributes;
   CrossOriginOpenerPolicy     openerPolicy;
 };
 
 /**
- * Not every channel necessarily has a loadInfo attached.
- */
-union OptionalLoadInfoArgs
-{
-  void_t;
-  LoadInfoArgs;
-};
-
-/**
  * This structure is used to carry selected properties of a LoadInfo
  * object to child processes to merge LoadInfo changes from the parent
  * process.  We don't want to use LoadInfoArgs for that since it's
  * too huge and we only care about small subpart of properties anyway.
  */
 struct ParentLoadInfoForwarderArgs
 {
   // WebExtextensions' WebRequest API allows extensions to intercept and
@@ -239,17 +230,17 @@ struct HttpChannelOpenArgs
   uint64_t                    startPos;
   nsCString                   entityID;
   bool                        chooseApplicationCache;
   nsCString                   appCacheClientID;
   bool                        allowSpdy;
   bool                        allowAltSvc;
   bool                        beConservative;
   uint32_t                    tlsFlags;
-  OptionalLoadInfoArgs        loadInfo;
+  LoadInfoArgs?               loadInfo;
   OptionalHttpResponseHead    synthesizedResponseHead;
   nsCString                   synthesizedSecurityInfoSerialization;
   uint32_t                    cacheKey;
   uint64_t                    requestContextID;
   OptionalCorsPreflightArgs   preflightArgs;
   uint32_t                    initialRwin;
   bool                        blockAuthPrompt;
   bool                        suspendAfterSynthesizeResponse;
@@ -289,17 +280,17 @@ union HttpChannelCreationArgs
 //-----------------------------------------------------------------------------
 
 struct FTPChannelOpenArgs
 {
   URIParams                 uri;
   uint64_t                  startPos;
   nsCString                 entityID;
   IPCStream?                uploadStream;
-  OptionalLoadInfoArgs      loadInfo;
+  LoadInfoArgs?             loadInfo;
   uint32_t                  loadFlags;
 };
 
 struct FTPChannelConnectArgs
 {
   uint32_t channelId;
 };
 
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -406,17 +406,17 @@ mozilla::ipc::IPCResult NeckoChild::Recv
     obsService->NotifyObservers(nullptr, NS_NETWORK_LINK_TOPIC,
                                 NS_ConvertUTF8toUTF16(type).get());
   }
   return IPC_OK();
 }
 
 PTrackingDummyChannelChild* NeckoChild::AllocPTrackingDummyChannelChild(
     nsIURI* aURI, nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
-    const OptionalLoadInfoArgs& aLoadInfo) {
+    const Maybe<LoadInfoArgs>& aLoadInfo) {
   return new TrackingDummyChannelChild();
 }
 
 bool NeckoChild::DeallocPTrackingDummyChannelChild(
     PTrackingDummyChannelChild* aActor) {
   delete static_cast<TrackingDummyChannelChild*>(aActor);
   return true;
 }
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -90,17 +90,17 @@ class NeckoChild : public PNeckoChild {
   mozilla::ipc::IPCResult RecvPredOnPredictPreconnect(const URIParams& aURI);
   mozilla::ipc::IPCResult RecvPredOnPredictDNS(const URIParams& aURI);
 
   mozilla::ipc::IPCResult RecvSpeculativeConnectRequest();
   mozilla::ipc::IPCResult RecvNetworkChangeNotification(nsCString const& type);
 
   PTrackingDummyChannelChild* AllocPTrackingDummyChannelChild(
       nsIURI* aURI, nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
-      const OptionalLoadInfoArgs& aLoadInfo);
+      const Maybe<LoadInfoArgs>& aLoadInfo);
 
   bool DeallocPTrackingDummyChannelChild(PTrackingDummyChannelChild* aChannel);
 };
 
 /**
  * Reference to the PNecko Child protocol.
  * Null if this is not a content process.
  */
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -98,22 +98,22 @@ static PBOverrideStatus PBOverrideStatus
     return (aSerialized.mOriginAttributes.mPrivateBrowsingId > 0)
                ? kPBOverride_Private
                : kPBOverride_NotPrivate;
   }
   return kPBOverride_Unset;
 }
 
 static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
-    const OptionalLoadInfoArgs& aOptionalLoadInfoArgs) {
-  if (aOptionalLoadInfoArgs.type() != OptionalLoadInfoArgs::TLoadInfoArgs) {
+    const Maybe<LoadInfoArgs>& aOptionalLoadInfoArgs) {
+  if (aOptionalLoadInfoArgs.isNothing()) {
     return nullptr;
   }
 
-  const LoadInfoArgs& loadInfoArgs = aOptionalLoadInfoArgs.get_LoadInfoArgs();
+  const LoadInfoArgs& loadInfoArgs = aOptionalLoadInfoArgs.ref();
   const OptionalPrincipalInfo& optionalPrincipalInfo =
       loadInfoArgs.requestingPrincipalInfo();
 
   if (optionalPrincipalInfo.type() != OptionalPrincipalInfo::TPrincipalInfo) {
     return nullptr;
   }
 
   const PrincipalInfo& principalInfo =
@@ -904,25 +904,24 @@ mozilla::ipc::IPCResult NeckoParent::Rec
     aResolve(invalidFD);
   }
 
   return IPC_OK();
 }
 
 PTrackingDummyChannelParent* NeckoParent::AllocPTrackingDummyChannelParent(
     nsIURI* aURI, nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
-    const OptionalLoadInfoArgs& aLoadInfo) {
+    const Maybe<LoadInfoArgs>& aLoadInfo) {
   RefPtr<TrackingDummyChannelParent> c = new TrackingDummyChannelParent();
   return c.forget().take();
 }
 
 mozilla::ipc::IPCResult NeckoParent::RecvPTrackingDummyChannelConstructor(
     PTrackingDummyChannelParent* aActor, nsIURI* aURI, nsIURI* aTopWindowURI,
-    const nsresult& aTopWindowURIResult,
-    const OptionalLoadInfoArgs& aLoadInfo) {
+    const nsresult& aTopWindowURIResult, const Maybe<LoadInfoArgs>& aLoadInfo) {
   TrackingDummyChannelParent* p =
       static_cast<TrackingDummyChannelParent*>(aActor);
 
   if (NS_WARN_IF(!aURI)) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo;
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -221,24 +221,24 @@ class NeckoParent : public PNeckoParent 
   mozilla::ipc::IPCResult RecvGetExtensionStream(
       const URIParams& aURI, GetExtensionStreamResolver&& aResolve);
 
   mozilla::ipc::IPCResult RecvGetExtensionFD(const URIParams& aURI,
                                              GetExtensionFDResolver&& aResolve);
 
   PTrackingDummyChannelParent* AllocPTrackingDummyChannelParent(
       nsIURI* aURI, nsIURI* aTopWindowURI, const nsresult& aTopWindowURIResult,
-      const OptionalLoadInfoArgs& aLoadInfo);
+      const Maybe<LoadInfoArgs>& aLoadInfo);
 
   bool DeallocPTrackingDummyChannelParent(PTrackingDummyChannelParent* aChild);
 
   virtual mozilla::ipc::IPCResult RecvPTrackingDummyChannelConstructor(
       PTrackingDummyChannelParent* aActor, nsIURI* aURI, nsIURI* aTopWindowURI,
       const nsresult& aTopWindowURIResult,
-      const OptionalLoadInfoArgs& aLoadInfo) override;
+      const Maybe<LoadInfoArgs>& aLoadInfo) override;
 
   mozilla::ipc::IPCResult RecvInitSocketProcessBridge(
       InitSocketProcessBridgeResolver&& aResolver);
 };
 
 }  // namespace net
 }  // namespace mozilla
 
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -109,17 +109,17 @@ parent:
   async PDataChannel(uint32_t channelId);
   async PSimpleChannel(uint32_t channelId);
   async PFileChannel(uint32_t channelId);
 
   async PChannelDiverter(ChannelDiverterArgs channel);
 
   async PTrackingDummyChannel(nsIURI uri, nsIURI aTopWindowURI,
                               nsresult aTopWindowURIResult,
-                              OptionalLoadInfoArgs loadInfo);
+                              LoadInfoArgs? loadInfo);
 
   /**
    * These are called from the child with the results of the auth prompt.
    * callbackId is the id that was passed in PBrowser::AsyncAuthPrompt,
    * corresponding to an nsIAuthPromptCallback
    */
   async OnAuthAvailable(uint64_t callbackId, nsString user,
                         nsString password, nsString domain);
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -101,17 +101,17 @@ bool FTPChannelParent::Init(const FTPCha
       return false;
   }
 }
 
 bool FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
                                    const uint64_t& aStartPos,
                                    const nsCString& aEntityID,
                                    const Maybe<IPCStream>& aUploadStream,
-                                   const OptionalLoadInfoArgs& aLoadInfoArgs,
+                                   const Maybe<LoadInfoArgs>& aLoadInfoArgs,
                                    const uint32_t& aLoadFlags) {
   nsresult rv;
 
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri) return false;
 
 #ifdef DEBUG
   LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n", this,
--- a/netwerk/protocol/ftp/FTPChannelParent.h
+++ b/netwerk/protocol/ftp/FTPChannelParent.h
@@ -73,17 +73,17 @@ class FTPChannelParent final : public PF
   nsresult ResumeForDiversion();
 
   // Asynchronously calls NotifyDiversionFailed.
   void FailDiversion(nsresult aErrorCode, bool aSkipResume = true);
 
   bool DoAsyncOpen(const URIParams& aURI, const uint64_t& aStartPos,
                    const nsCString& aEntityID,
                    const Maybe<IPCStream>& aUploadStream,
-                   const OptionalLoadInfoArgs& aLoadInfoArgs,
+                   const Maybe<LoadInfoArgs>& aLoadInfoArgs,
                    const uint32_t& aLoadFlags);
 
   // used to connect redirected-to channel in parent with just created
   // ChildChannel.  Used during HTTP->FTP redirects.
   bool ConnectChannel(const uint32_t& channelId);
 
   void DivertOnDataAvailable(const nsCString& data, const uint64_t& offset,
                              const uint32_t& count);
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -390,17 +390,17 @@ bool HttpChannelParent::DoAsyncOpen(
     const uint32_t& aLoadFlags, const RequestHeaderTuples& requestHeaders,
     const nsCString& requestMethod, const Maybe<IPCStream>& uploadStream,
     const bool& uploadStreamHasHeaders, const int16_t& priority,
     const uint32_t& classOfService, const uint8_t& redirectionLimit,
     const bool& allowSTS, const uint32_t& thirdPartyFlags,
     const bool& doResumeAt, const uint64_t& startPos, const nsCString& entityID,
     const bool& chooseApplicationCache, const nsCString& appCacheClientID,
     const bool& allowSpdy, const bool& allowAltSvc, const bool& beConservative,
-    const uint32_t& tlsFlags, const OptionalLoadInfoArgs& aLoadInfoArgs,
+    const uint32_t& tlsFlags, const Maybe<LoadInfoArgs>& aLoadInfoArgs,
     const OptionalHttpResponseHead& aSynthesizedResponseHead,
     const nsCString& aSecurityInfoSerialization, const uint32_t& aCacheKey,
     const uint64_t& aRequestContextID,
     const OptionalCorsPreflightArgs& aCorsPreflightArgs,
     const uint32_t& aInitialRwin, const bool& aBlockAuthPrompt,
     const bool& aSuspendAfterSynthesizeResponse,
     const bool& aAllowStaleCacheContent, const nsCString& aContentTypeHint,
     const uint32_t& aCorsMode, const uint32_t& aRedirectMode,
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -148,17 +148,17 @@ class HttpChannelParent final : public n
       const nsCString& requestMethod, const Maybe<IPCStream>& uploadStream,
       const bool& uploadStreamHasHeaders, const int16_t& priority,
       const uint32_t& classOfService, const uint8_t& redirectionLimit,
       const bool& allowSTS, const uint32_t& thirdPartyFlags,
       const bool& doResumeAt, const uint64_t& startPos,
       const nsCString& entityID, const bool& chooseApplicationCache,
       const nsCString& appCacheClientID, const bool& allowSpdy,
       const bool& allowAltSvc, const bool& beConservative,
-      const uint32_t& tlsFlags, const OptionalLoadInfoArgs& aLoadInfoArgs,
+      const uint32_t& tlsFlags, const Maybe<LoadInfoArgs>& aLoadInfoArgs,
       const OptionalHttpResponseHead& aSynthesizedResponseHead,
       const nsCString& aSecurityInfoSerialization, const uint32_t& aCacheKey,
       const uint64_t& aRequestContextID,
       const OptionalCorsPreflightArgs& aCorsPreflightArgs,
       const uint32_t& aInitialRwin, const bool& aBlockAuthPrompt,
       const bool& aSuspendAfterSynthesizeResponse,
       const bool& aAllowStaleCacheContent, const nsCString& aContentTypeHint,
       const uint32_t& aCorsMode, const uint32_t& aRedirectMode,
--- a/netwerk/protocol/http/HttpChannelParentListener.cpp
+++ b/netwerk/protocol/http/HttpChannelParentListener.cpp
@@ -172,17 +172,17 @@ nsresult HttpChannelParentListener::Trig
             RedirectChannelRegistrar::GetOrCreate();
         MOZ_ASSERT(registrar);
         rv = registrar->RegisterChannel(channel, &self->mRedirectChannelId);
         NS_ENSURE_SUCCESS(rv, rv);
 
         LOG(("Registered %p channel under id=%d", channel.get(),
              self->mRedirectChannelId));
 
-        OptionalLoadInfoArgs loadInfoArgs;
+        Maybe<LoadInfoArgs> loadInfoArgs;
         MOZ_ALWAYS_SUCCEEDS(LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs));
 
         uint32_t newLoadFlags = nsIRequest::LOAD_NORMAL;
         MOZ_ALWAYS_SUCCEEDS(channel->GetLoadFlags(&newLoadFlags));
 
         nsCOMPtr<nsIURI> uri;
         channel->GetURI(getter_AddRefs(uri));
 
--- a/netwerk/protocol/http/TrackingDummyChannelChild.cpp
+++ b/netwerk/protocol/http/TrackingDummyChannelChild.cpp
@@ -29,17 +29,17 @@ bool TrackingDummyChannelChild::Create(
     return true;
   }
 
   nsCOMPtr<nsIURI> topWindowURI;
   nsresult topWindowURIResult =
       httpChannelInternal->GetTopWindowURI(getter_AddRefs(topWindowURI));
 
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
-  OptionalLoadInfoArgs loadInfoArgs;
+  Maybe<LoadInfoArgs> loadInfoArgs;
   mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs);
 
   PTrackingDummyChannelChild* actor =
       gNeckoChild->SendPTrackingDummyChannelConstructor(
           aURI, topWindowURI, topWindowURIResult, loadInfoArgs);
   if (!actor) {
     return false;
   }
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5539,16 +5539,17 @@ nsresult nsHttpChannel::InstallCacheList
     // Entry is already doomed.
     // This may happen when expiration time is set to past and the entry
     // has been removed by the background eviction logic.
     return NS_OK;
   }
   if (rv == NS_ERROR_FILE_TOO_BIG) {
     LOG(("  entry would exceed max allowed size, not writing it [channel=%p]",
          this));
+    mCacheEntry->AsyncDoom(nullptr);
     return NS_OK;
   }
   if (NS_FAILED(rv)) return rv;
 
   if (mCacheOnlyMetadata) {
     LOG(("Not storing content, cacheOnlyMetadata set"));
     // We must open and then close the output stream of the cache entry.
     // This way we indicate the content has been written (despite with zero
--- a/netwerk/protocol/websocket/PWebSocket.ipdl
+++ b/netwerk/protocol/websocket/PWebSocket.ipdl
@@ -39,17 +39,17 @@ parent:
                   uint64_t aInnerWindowID,
                   nsCString aProtocol,
                   bool aSecure,
                   // ping values only meaningful if client set them
                   uint32_t aPingInterval,
                   bool aClientSetPingInterval,
                   uint32_t aPingTimeout,
                   bool aClientSetPingTimeout,
-                  OptionalLoadInfoArgs aLoadInfoArgs,
+                  LoadInfoArgs? aLoadInfoArgs,
                   OptionalTransportProvider aProvider,
                   nsCString aNegotiatedExtensions);
   async Close(uint16_t code, nsCString reason);
   async SendMsg(nsCString aMsg);
   async SendBinaryMsg(nsCString aMsg);
   async SendBinaryStream(IPCStream aStream, uint32_t aLength);
 
   async DeleteSelf();
--- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp
@@ -440,29 +440,29 @@ WebSocketChannelChild::AsyncOpen(nsIURI*
   if (cc->IsShuttingDown()) {
     return NS_ERROR_FAILURE;
   }
 
   // Corresponding release in DeallocPWebSocket
   AddIPDLReference();
 
   OptionalURIParams uri;
-  OptionalLoadInfoArgs loadInfoArgs;
+  Maybe<LoadInfoArgs> loadInfoArgs;
   OptionalTransportProvider transportProvider;
 
   if (!mIsServerSide) {
     uri = URIParams();
     SerializeURI(aURI, uri.get_URIParams());
     nsresult rv = LoadInfoToLoadInfoArgs(mLoadInfo, &loadInfoArgs);
     NS_ENSURE_SUCCESS(rv, rv);
 
     transportProvider = void_t();
   } else {
     uri = void_t();
-    loadInfoArgs = void_t();
+    loadInfoArgs = Nothing();
 
     MOZ_ASSERT(mServerTransportProvider);
     PTransportProviderChild* ipcChild;
     nsresult rv = mServerTransportProvider->GetIPCChild(&ipcChild);
     NS_ENSURE_SUCCESS(rv, rv);
 
     transportProvider = ipcChild;
   }
--- a/netwerk/protocol/websocket/WebSocketChannelParent.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.cpp
@@ -47,18 +47,17 @@ mozilla::ipc::IPCResult WebSocketChannel
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebSocketChannelParent::RecvAsyncOpen(
     const OptionalURIParams& aURI, const nsCString& aOrigin,
     const uint64_t& aInnerWindowID, const nsCString& aProtocol,
     const bool& aSecure, const uint32_t& aPingInterval,
     const bool& aClientSetPingInterval, const uint32_t& aPingTimeout,
-    const bool& aClientSetPingTimeout,
-    const OptionalLoadInfoArgs& aLoadInfoArgs,
+    const bool& aClientSetPingTimeout, const Maybe<LoadInfoArgs>& aLoadInfoArgs,
     const OptionalTransportProvider& aTransportProvider,
     const nsCString& aNegotiatedExtensions) {
   LOG(("WebSocketChannelParent::RecvAsyncOpen() %p\n", this));
 
   nsresult rv;
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsILoadInfo> loadInfo;
 
--- a/netwerk/protocol/websocket/WebSocketChannelParent.h
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.h
@@ -39,17 +39,17 @@ class WebSocketChannelParent : public PW
 
  private:
   mozilla::ipc::IPCResult RecvAsyncOpen(
       const OptionalURIParams& aURI, const nsCString& aOrigin,
       const uint64_t& aInnerWindowID, const nsCString& aProtocol,
       const bool& aSecure, const uint32_t& aPingInterval,
       const bool& aClientSetPingInterval, const uint32_t& aPingTimeout,
       const bool& aClientSetPingTimeout,
-      const OptionalLoadInfoArgs& aLoadInfoArgs,
+      const Maybe<LoadInfoArgs>& aLoadInfoArgs,
       const OptionalTransportProvider& aTransportProvider,
       const nsCString& aNegotiatedExtensions);
   mozilla::ipc::IPCResult RecvClose(const uint16_t& code,
                                     const nsCString& reason);
   mozilla::ipc::IPCResult RecvSendMsg(const nsCString& aMsg);
   mozilla::ipc::IPCResult RecvSendBinaryMsg(const nsCString& aMsg);
   mozilla::ipc::IPCResult RecvSendBinaryStream(const IPCStream& aStream,
                                                const uint32_t& aLength);
--- a/netwerk/test/moz.build
+++ b/netwerk/test/moz.build
@@ -1,15 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
-TEST_DIRS += ['httpserver', 'gtest']
+TEST_DIRS += ['httpserver', 'gtest', 'unit']
 
 BROWSER_CHROME_MANIFESTS += ['browser/browser.ini']
 MOCHITEST_MANIFESTS += ['mochitests/mochitest.ini']
 
 XPCSHELL_TESTS_MANIFESTS += [
     'unit/xpcshell.ini',
     'unit_ipc/xpcshell.ini',
 ]
--- a/netwerk/test/unit/head_channels.js
+++ b/netwerk/test/unit/head_channels.js
@@ -232,8 +232,27 @@ function OriginAttributes(appId, inIsola
   this.inIsolatedMozBrowser = inIsolatedMozBrowser;
   this.privateBrowsingId = privateId;
 }
 OriginAttributes.prototype = {
   appId: 0,
   inIsolatedMozBrowser: false,
   privateBrowsingId: 0
 };
+
+function readFile(file) {
+  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
+                  .createInstance(Ci.nsIFileInputStream);
+  fstream.init(file, -1, 0, 0);
+  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
+  fstream.close();
+  return data;
+}
+
+function addCertFromFile(certdb, filename, trustString) {
+  let certFile = do_get_file(filename, false);
+  let pem = readFile(certFile)
+              .replace(/-----BEGIN CERTIFICATE-----/, "")
+              .replace(/-----END CERTIFICATE-----/, "")
+              .replace(/[\r\n]/g, "");
+  certdb.addCertFromBase64(pem, trustString);
+}
+
rename from netwerk/test/unit/CA.cert.der
rename to netwerk/test/unit/http2-ca.pem
index 67157cabd08d9705a3ec762eba1ace9fb0f75f3f..db6f723d2e7adbfe52fc90a4884fe310639ed945
GIT binary patch
literal 1041
zc$|$>Nz<Z85WVv&=1zPen*xVER6zm7D#|7|kR6ppu*6?KcF(adlM#7&nUN9smY<(*
zL8N2%9|fHPtYHNN=r{O7IT$Ofswg1eYCtdqH4abd%N4h1(p(ZWx&h9^#(FnTe|Zj3
z{flW0DQ;d@7x>7gpbv;1pu~Zp4%iR@LOhI`Fw($=wbSj&`?b#?f}8s2bl~lNz}ry~
zaa8bam{B^AHb=$$A20y_4Yc8Z9j$BBQVw8r<vi(Uob#%WAn-!~1PoOIelJ*H_`c;U
zhgQ>v6Bc5&H{^WIFzhb=F(3iOm2>Zg6C)m@A>v41N`_%(UClvSex^jfb3)d<%vqS^
ztcaH>H4rYRC>xpBkl&w(GEJgXPgdQ*{m7_wZKd9f9oO8u>ppPIIS1mRUGPy#4oh~L
z-VDKqb{nBM6+{4%OLY}{SGJXyrqx&Z9bb~W|BOoiSYYv8D#97kgDZR3r=^FsG||GP
z+4K;UUXc#vTLBbo1IN&}WKPp27OVEw*q{jXPBo2Uab|vJF@;$;rYQD_Iu(PEyQ00B
zudMW#c8nUzD1|%Q#>KQpIOuZQP`={YQrvF4n{=U5?V}6N-QL8zaSN~hoAtdX*VL9<
zaxSPHNCBXCNClqrwZm7k0uf(xfCE7#{bic3XTB_bl~Omg^&eLEe4QwF_If6pYEw#T
zYlmwy(d-;V?W=<}d6+ut$$Qoog%$2&Fdx^1$Y9Hxd2qV+F$ovy>RtkE4G=H%OqHTg
zDh`af3&yHoTLwXi9>U`~c@Ns<<egm_NAT!Xv+&Zb<Ovhf)Oi6|l;bJ9_?d-uak3xF
zfhl|fmy+eySH2!I$lp&0S8C!}*UpATYS=8v;%@nb3H9NS%tp8vU#4E6DpB|fm+b1v
zX0t=G8^zaF66i<QyU!c#ZLso8DWJ!QKO0qC79*AB9cd0>u=aSp^7ojT&$}u0GLG<u
ypLu&8I{9rK7{b!m=hZ96CkSo@XBe+@Ye1TzEa@C;C!>H~_>20JaOA3grT$;9j7tmv
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/http2-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer: HTTP2 Test CA
+subject: HTTP2 Test CA
+validity:20170101-20270101
+extension:basicConstraints:cA,
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+#GeneratedTestCertificate('http2-ca.pem')
--- a/netwerk/test/unit/test_altsvc.js
+++ b/netwerk/test/unit/test_altsvc.js
@@ -37,23 +37,23 @@ function run_test() {
   altsvcpref2 = prefs.getBoolPref("network.http.altsvc.oe", true);
 
   prefs.setBoolPref("network.http.spdy.enabled", true);
   prefs.setBoolPref("network.http.spdy.enabled.http2", true);
   prefs.setBoolPref("network.http.altsvc.enabled", true);
   prefs.setBoolPref("network.http.altsvc.oe", true);
   prefs.setCharPref("network.dns.localDomains", "foo.example.com, bar.example.com");
 
-  // The moz-http2 cert is for foo.example.com and is signed by CA.cert.der
+  // The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem
   // so add that cert to the trust list as a signing cert. The same cert is used
   // for both h2FooRoute and h2BarRoute though it is only valid for
   // the foo.example.com domain name.
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
-  addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
+  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
 
   h1Foo = new HttpServer();
   h1Foo.registerPathHandler("/altsvc-test", h1Server);
   h1Foo.registerPathHandler("/.well-known/http-opportunistic", h1ServerWK);
   h1Foo.start(-1);
   h1Foo.identity.setPrimary("http", "foo.example.com", h1Foo.identity.primaryPort);
 
   h1Bar = new HttpServer();
@@ -111,31 +111,16 @@ function h1ServerWK(metadata, response) 
 function resetPrefs() {
   prefs.setBoolPref("network.http.spdy.enabled", spdypref);
   prefs.setBoolPref("network.http.spdy.enabled.http2", http2pref);
   prefs.setBoolPref("network.http.altsvc.enabled", altsvcpref1);
   prefs.setBoolPref("network.http.altsvc.oe", altsvcpref2);
   prefs.clearUserPref("network.dns.localDomains");
 }
 
-function readFile(file) {
-  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-                  .createInstance(Ci.nsIFileInputStream);
-  fstream.init(file, -1, 0, 0);
-  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
-  fstream.close();
-  return data;
-}
-
-function addCertFromFile(certdb, filename, trustString) {
-  let certFile = do_get_file(filename, false);
-  let der = readFile(certFile);
-  certdb.addCert(der, trustString);
-}
-
 function makeChan(origin) {
   return NetUtil.newChannel({
     uri: origin + "altsvc-test",
     loadUsingSystemPrincipal: true
   }).QueryInterface(Ci.nsIHttpChannel);
 }
 
 var origin;
--- a/netwerk/test/unit/test_anonymous-coalescing.js
+++ b/netwerk/test/unit/test_anonymous-coalescing.js
@@ -27,47 +27,32 @@ function run_test() {
   http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2");
   extpref = prefs.getBoolPref("network.http.originextension");
 
   prefs.setBoolPref("network.http.spdy.enabled", true);
   prefs.setBoolPref("network.http.spdy.enabled.http2", true);
   prefs.setBoolPref("network.http.originextension", true);
   prefs.setCharPref("network.dns.localDomains", "foo.example.com, alt1.example.com");
 
-  // The moz-http2 cert is for {foo, alt1, alt2}.example.com and is signed by CA.cert.der
+  // The moz-http2 cert is for {foo, alt1, alt2}.example.com and is signed by http2-ca.pem
   // so add that cert to the trust list as a signing cert.
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
-  addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
+  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
 
   doTest1();
 }
 
 function resetPrefs() {
   prefs.setBoolPref("network.http.spdy.enabled", spdypref);
   prefs.setBoolPref("network.http.spdy.enabled.http2", http2pref);
   prefs.setBoolPref("network.http.originextension", extpref);
   prefs.clearUserPref("network.dns.localDomains");
 }
 
-function readFile(file) {
-  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-                  .createInstance(Ci.nsIFileInputStream);
-  fstream.init(file, -1, 0, 0);
-  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
-  fstream.close();
-  return data;
-}
-
-function addCertFromFile(certdb, filename, trustString) {
-  let certFile = do_get_file(filename, false);
-  let der = readFile(certFile);
-  certdb.addCert(der, trustString);
-}
-
 function makeChan(origin) {
   return NetUtil.newChannel({
     uri: origin,
     loadUsingSystemPrincipal: true
   }).QueryInterface(Ci.nsIHttpChannel);
 }
 
 var nextTest;
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_bug1527293.js
@@ -0,0 +1,92 @@
+// Test bug 1527293
+//
+// Summary:
+// The purpose of this test is to check that a cache entry is doomed and not
+// reused when we don't write the content due to max entry size limit.
+//
+// Test step:
+// 1. Create http request for an entry whose size is bigger than we allow to
+//    cache. The response must contain Content-Range header so the content size
+//    is known in advance, but it must not contain Content-Length header because
+//    the bug isn't reproducible with it.
+// 2. After receiving and checking the content do the same request again.
+// 3. Check that the request isn't conditional, i.e. the entry from previous
+//    load was doomed.
+
+
+const {HttpServer} = ChromeUtils.import("resource://testing-common/httpd.js");
+const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+
+
+XPCOMUtils.defineLazyGetter(this, "URL", function() {
+  return "http://localhost:" + httpServer.identity.primaryPort;
+});
+
+var httpServer = null;
+
+function make_channel(url, callback, ctx) {
+  return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true});
+}
+
+// need something bigger than 1024 bytes
+const responseBody =
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +
+  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
+
+function contentHandler(metadata, response)
+{
+  response.setHeader("Content-Type", "text/plain");
+  response.setHeader("ETag", "Just testing");
+  response.setHeader("Cache-Control", "max-age=99999");
+
+  Assert.throws(() => { var etag = metadata.getHeader("If-None-Match"); },
+                /NS_ERROR_NOT_AVAILABLE/, "conditional request not expected");
+
+  response.setHeader("Accept-Ranges", "bytes");
+  let len = responseBody.length;
+  response.setHeader("Content-Range", "0-" + (len - 1) + "/" + len);
+  response.bodyOutputStream.write(responseBody, responseBody.length);
+}
+
+function run_test()
+{
+  // Static check
+  Assert.ok(responseBody.length > 1024);
+
+  do_get_profile();
+
+  Services.prefs.setIntPref("browser.cache.disk.max_entry_size", 1);
+  Services.prefs.setBoolPref("network.http.rcwn.enabled", false);
+
+  httpServer = new HttpServer();
+  httpServer.registerPathHandler("/content", contentHandler);
+  httpServer.start(-1);
+
+  var chan = make_channel(URL + "/content");
+  chan.asyncOpen(new ChannelListener(firstTimeThrough, null));
+
+  do_test_pending();
+}
+
+function firstTimeThrough(request, buffer)
+{
+  Assert.equal(buffer, responseBody);
+
+  var chan = make_channel(URL + "/content");
+  chan.asyncOpen(new ChannelListener(secondTimeThrough, null));
+}
+
+function secondTimeThrough(request, buffer)
+{
+  Assert.equal(buffer, responseBody);
+  httpServer.stop(do_test_finished);
+}
--- a/netwerk/test/unit/test_esni_dns_fetch.js
+++ b/netwerk/test/unit/test_esni_dns_fetch.js
@@ -30,21 +30,21 @@ function run_test() {
   prefs.setBoolPref("network.dns.native-is-localhost", true);
 
   // 0 - off, 1 - race, 2 TRR first, 3 TRR only, 4 shadow
   prefs.setIntPref("network.trr.mode", 2); // TRR first
   prefs.setBoolPref("network.trr.wait-for-portal", false);
   // don't confirm that TRR is working, just go!
   prefs.setCharPref("network.trr.confirmationNS", "skip");
 
-  // The moz-http2 cert is for foo.example.com and is signed by CA.cert.der
+  // The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem
   // so add that cert to the trust list as a signing cert.  // the foo.example.com domain name.
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
       .getService(Ci.nsIX509CertDB);
-  addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
+  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
   do_test_pending();
   run_dns_tests();
 }
 
 registerCleanupFunction(() => {
   prefs.clearUserPref("network.security.esni.enabled");
   prefs.clearUserPref("network.http.spdy.enabled");
   prefs.clearUserPref("network.http.spdy.enabled.http2");
@@ -58,31 +58,16 @@ registerCleanupFunction(() => {
   prefs.clearUserPref("network.trr.useGET");
   prefs.clearUserPref("network.trr.confirmationNS");
   prefs.clearUserPref("network.trr.bootstrapAddress");
   prefs.clearUserPref("network.trr.blacklist-duration");
   prefs.clearUserPref("network.trr.request-timeout");
 
 });
 
-function readFile(file) {
-  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-                  .createInstance(Ci.nsIFileInputStream);
-  fstream.init(file, -1, 0, 0);
-  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
-  fstream.close();
-  return data;
-}
-
-function addCertFromFile(certdb, filename, trustString) {
-  let certFile = do_get_file(filename, false);
-  let der = readFile(certFile);
-  certdb.addCert(der, trustString);
-}
-
 var test_answer="bXkgdm9pY2UgaXMgbXkgcGFzc3dvcmQ=";
 var test_answer_addr="127.0.0.1";
 
 // check that we do lookup by type fine
 var listenerEsni = {
   onLookupByTypeComplete: function(inRequest, inRecord, inStatus) {
     if (inRequest == listen) {
       Assert.ok(!inStatus);
--- a/netwerk/test/unit/test_header_Server_Timing.js
+++ b/netwerk/test/unit/test_header_Server_Timing.js
@@ -13,40 +13,25 @@ function make_and_open_channel(url, call
   chan.asyncOpen(new ChannelListener(callback, null, CL_ALLOW_UNKNOWN_CL));
 }
 
 var responseServerTiming = [{metric:"metric", duration:"123.4", description:"description"},
                             {metric:"metric2", duration:"456.78", description:"description1"}];
 var trailerServerTiming = [{metric:"metric3", duration:"789.11", description:"description2"},
                            {metric:"metric4", duration:"1112.13", description:"description3"}];
 
-function readFile(file) {
-  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-                  .createInstance(Ci.nsIFileInputStream);
-  fstream.init(file, -1, 0, 0);
-  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
-  fstream.close();
-  return data;
-}
-
-function addCertFromFile(certdb, filename, trustString) {
-  let certFile = do_get_file(filename, false);
-  let der = readFile(certFile);
-  certdb.addCert(der, trustString);
-}
-
 function run_test()
 {
   do_test_pending();
 
   // Set up to allow the cert presented by the server
   do_get_profile();
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
-  addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
+  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
 
   Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com");
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref("network.dns.localDomains");
   });
 
   var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
   var serverPort = env.get("MOZHTTP2_PORT");
--- a/netwerk/test/unit/test_http2.js
+++ b/netwerk/test/unit/test_http2.js
@@ -1177,69 +1177,16 @@ function run_next_test() {
   if (current_test < tests.length) {
     dump("starting test number " + current_test + "\n");
     tests[current_test]();
     current_test++;
     do_test_pending();
   }
 }
 
-// Support for making sure we can talk to the invalid cert the server presents
-var CertOverrideListener = function(host, port, bits) {
-  this.host = host;
-  if (port) {
-    this.port = port;
-  }
-  this.bits = bits;
-};
-
-CertOverrideListener.prototype = {
-  host: null,
-  port: -1,
-  bits: null,
-
-  getInterface: function(aIID) {
-    return this.QueryInterface(aIID);
-  },
-
-  QueryInterface: function(aIID) {
-    if (aIID.equals(Ci.nsIBadCertListener2) ||
-        aIID.equals(Ci.nsIInterfaceRequestor) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-  notifyCertProblem: function(socketInfo, secInfo, targetHost) {
-    var cert = secInfo.serverCert;
-    var cos = Cc["@mozilla.org/security/certoverride;1"].
-              getService(Ci.nsICertOverrideService);
-    cos.rememberValidityOverride(this.host, this.port, cert, this.bits, false);
-    dump("Certificate Override in place\n");
-    return true;
-  },
-};
-
-function addCertOverride(host, port, bits) {
-  var req = new XMLHttpRequest();
-  try {
-    var url;
-    if (port) {
-      url = "https://" + host + ":" + port + "/";
-    } else {
-      url = "https://" + host + "/";
-    }
-    req.open("GET", url, false);
-    req.channel.notificationCallbacks = new CertOverrideListener(host, port, bits);
-    req.send(null);
-  } catch (e) {
-    // This will fail since the server is not trusted yet
-  }
-}
-
 var prefs;
 var spdypref;
 var spdypush;
 var http2pref;
 var altsvcpref1;
 var altsvcpref2;
 var loadGroup;
 var serverPort;
@@ -1262,28 +1209,23 @@ function run_test() {
   dump("using port " + serverPort + "\n");
 
   // Set to allow the cert presented by our H2 server
   do_get_profile();
   prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
   speculativeLimit = prefs.getIntPref("network.http.speculative-parallel-limit");
   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
 
-  // The moz-http2 cert is for foo.example.com and is signed by CA.cert.der
+  // The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem
   // so add that cert to the trust list as a signing cert. Some older tests in
   // this suite use localhost with a TOFU exception, but new ones should use
   // foo.example.com
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
-  addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
-
-  addCertOverride("localhost", serverPort,
-                  Ci.nsICertOverrideService.ERROR_UNTRUSTED |
-                  Ci.nsICertOverrideService.ERROR_MISMATCH |
-                  Ci.nsICertOverrideService.ERROR_TIME);
+  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
 
   // Enable all versions of spdy to see that we auto negotiate http/2
   spdypref = prefs.getBoolPref("network.http.spdy.enabled");
   spdypush = prefs.getBoolPref("network.http.spdy.allow-push");
   http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2");
   altsvcpref1 = prefs.getBoolPref("network.http.altsvc.enabled");
   altsvcpref2 = prefs.getBoolPref("network.http.altsvc.oe", true);
 
@@ -1306,23 +1248,8 @@ function run_test() {
   httpserv2.registerPathHandler("/altsvc2", altsvcHttp1Server2);
   httpserv2.registerPathHandler("/.well-known/http-opportunistic", h1ServerWK2);
   httpserv2.start(-1);
   httpserv2.identity.setPrimary("http", "foo.example.com", httpserv2.identity.primaryPort);
 
   // And make go!
   run_next_test();
 }
-
-function readFile(file) {
-  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-                  .createInstance(Ci.nsIFileInputStream);
-  fstream.init(file, -1, 0, 0);
-  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
-  fstream.close();
-  return data;
-}
-
-function addCertFromFile(certdb, filename, trustString) {
-  let certFile = do_get_file(filename, false);
-  let der = readFile(certFile);
-  certdb.addCert(der, trustString);
-}
--- a/netwerk/test/unit/test_immutable.js
+++ b/netwerk/test/unit/test_immutable.js
@@ -21,49 +21,34 @@ function run_test() {
   rcwnpref = prefs.getBoolPref("network.http.rcwn.enabled");
 
   prefs.setBoolPref("network.http.spdy.enabled", true);
   prefs.setBoolPref("network.http.spdy.enabled.http2", true);
   prefs.setCharPref("network.dns.localDomains", "foo.example.com, bar.example.com");
   // Disable rcwn to make cache behavior deterministic.
   prefs.setBoolPref("network.http.rcwn.enabled", false);
 
-  // The moz-http2 cert is for foo.example.com and is signed by CA.cert.der
+  // The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem
   // so add that cert to the trust list as a signing cert.  // the foo.example.com domain name.
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
-  addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
+  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
 
   origin = "https://foo.example.com:" + h2Port;
   dump ("origin - " + origin + "\n");
   doTest1();
 }
 
 function resetPrefs() {
   prefs.setBoolPref("network.http.spdy.enabled", spdypref);
   prefs.setBoolPref("network.http.spdy.enabled.http2", http2pref);
   prefs.setBoolPref("network.http.rcwn.enabled", rcwnpref);
   prefs.clearUserPref("network.dns.localDomains");
 }
 
-function readFile(file) {
-  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-                  .createInstance(Ci.nsIFileInputStream);
-  fstream.init(file, -1, 0, 0);
-  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
-  fstream.close();
-  return data;
-}
-
-function addCertFromFile(certdb, filename, trustString) {
-  let certFile = do_get_file(filename, false);
-  let der = readFile(certFile);
-  certdb.addCert(der, trustString);
-}
-
 function makeChan(origin, path) {
   return NetUtil.newChannel({
     uri: origin + path,
     loadUsingSystemPrincipal: true
   }).QueryInterface(Ci.nsIHttpChannel);
 }
 
 var nextTest;
--- a/netwerk/test/unit/test_origin.js
+++ b/netwerk/test/unit/test_origin.js
@@ -21,47 +21,32 @@ function run_test() {
   http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2");
   extpref = prefs.getBoolPref("network.http.originextension");
 
   prefs.setBoolPref("network.http.spdy.enabled", true);
   prefs.setBoolPref("network.http.spdy.enabled.http2", true);
   prefs.setBoolPref("network.http.originextension", true);
   prefs.setCharPref("network.dns.localDomains", "foo.example.com, alt1.example.com");
 
-  // The moz-http2 cert is for {foo, alt1, alt2}.example.com and is signed by CA.cert.der
+  // The moz-http2 cert is for {foo, alt1, alt2}.example.com and is signed by http2-ca.pem
   // so add that cert to the trust list as a signing cert.
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
-  addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
+  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
 
   doTest1();
 }
 
 function resetPrefs() {
   prefs.setBoolPref("network.http.spdy.enabled", spdypref);
   prefs.setBoolPref("network.http.spdy.enabled.http2", http2pref);
   prefs.setBoolPref("network.http.originextension", extpref);
   prefs.clearUserPref("network.dns.localDomains");
 }
 
-function readFile(file) {
-  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-                  .createInstance(Ci.nsIFileInputStream);
-  fstream.init(file, -1, 0, 0);
-  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
-  fstream.close();
-  return data;
-}
-
-function addCertFromFile(certdb, filename, trustString) {
-  let certFile = do_get_file(filename, false);
-  let der = readFile(certFile);
-  certdb.addCert(der, trustString);
-}
-
 function makeChan(origin) {
   return NetUtil.newChannel({
     uri: origin,
     loadUsingSystemPrincipal: true
   }).QueryInterface(Ci.nsIHttpChannel);
 }
 
 var nextTest;
--- a/netwerk/test/unit/test_trr.js
+++ b/netwerk/test/unit/test_trr.js
@@ -33,21 +33,21 @@ function run_test() {
   prefs.setBoolPref("network.dns.native-is-localhost", true);
 
   // 0 - off, 1 - race, 2 TRR first, 3 TRR only, 4 shadow
   prefs.setIntPref("network.trr.mode", 2); // TRR first
   prefs.setBoolPref("network.trr.wait-for-portal", false);
   // don't confirm that TRR is working, just go!
   prefs.setCharPref("network.trr.confirmationNS", "skip");
 
-  // The moz-http2 cert is for foo.example.com and is signed by CA.cert.der
+  // The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem
   // so add that cert to the trust list as a signing cert.  // the foo.example.com domain name.
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
       .getService(Ci.nsIX509CertDB);
-  addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
+  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
   do_test_pending();
   run_dns_tests();
 }
 
 function resetTRRPrefs() {
   prefs.clearUserPref("network.trr.mode");
   prefs.clearUserPref("network.trr.uri");
   prefs.clearUserPref("network.trr.credentials");
@@ -64,31 +64,16 @@ function resetTRRPrefs() {
 registerCleanupFunction(() => {
   prefs.clearUserPref("network.http.spdy.enabled");
   prefs.clearUserPref("network.http.spdy.enabled.http2");
   prefs.clearUserPref("network.dns.localDomains");
   prefs.clearUserPref("network.dns.native-is-localhost");
   resetTRRPrefs();
 });
 
-function readFile(file) {
-  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-                  .createInstance(Ci.nsIFileInputStream);
-  fstream.init(file, -1, 0, 0);
-  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
-  fstream.close();
-  return data;
-}
-
-function addCertFromFile(certdb, filename, trustString) {
-  let certFile = do_get_file(filename, false);
-  let der = readFile(certFile);
-  certdb.addCert(der, trustString);
-}
-
 function testsDone()
 {
   do_test_finished();
   do_test_finished();
 }
 
 var test_loops;
 var test_answer="127.0.0.1";
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -1,12 +1,12 @@
 [DEFAULT]
 head = head_channels.js head_cache.js head_cache2.js
 support-files =
-  CA.cert.der
+  http2-ca.pem
   client_cert_chooser.js
   client_cert_chooser.manifest
   data/image.png
   data/system_root.lnk
   data/test_psl.txt
   data/test_readline1.txt
   data/test_readline2.txt
   data/test_readline3.txt
@@ -425,8 +425,9 @@ run-sequentially = node server exception
 [test_esni_dns_fetch.js]
 # http2-using tests require node available
 skip-if = os == "android"
 [test_network_connectivity_service.js]
 skip-if = os == "android" # DNSv6 issues on android
 [test_suspend_channel_on_authRetry.js]
 [test_suspend_channel_on_examine_merged_response.js]
 [test_crossOriginOpenerPolicy.js]
+[test_bug1527293.js]
--- a/netwerk/url-classifier/UrlClassifierCommon.cpp
+++ b/netwerk/url-classifier/UrlClassifierCommon.cpp
@@ -98,32 +98,32 @@ bool UrlClassifierCommon::ShouldEnableCl
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   if (UrlClassifierCommon::AddonMayLoad(aChannel, chanURI)) {
     return false;
   }
 
+  nsCOMPtr<nsIURI> topWinURI;
+  nsCOMPtr<nsIHttpChannelInternal> channel = do_QueryInterface(aChannel);
+  if (!channel) {
+    UC_LOG(("nsChannelClassifier: Not an HTTP channel"));
+    return false;
+  }
+
+  rv = channel->GetTopWindowURI(getter_AddRefs(topWinURI));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
   // Tracking protection will be enabled so return without updating
   // the security state. If any channels are subsequently cancelled
   // (page elements blocked) the state will be then updated.
   if (UC_LOG_ENABLED()) {
-    nsCOMPtr<nsIURI> topWinURI;
-    nsCOMPtr<nsIHttpChannelInternal> channel = do_QueryInterface(aChannel);
-    if (!channel) {
-      UC_LOG(("nsChannelClassifier: Not an HTTP channel"));
-      return false;
-    }
-
-    nsresult rv = channel->GetTopWindowURI(getter_AddRefs(topWinURI));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return false;
-    }
-
     nsCString chanSpec = chanURI->GetSpecOrDefault();
     chanSpec.Truncate(
         std::min(chanSpec.Length(), UrlClassifierCommon::sMaxSpecLength));
     nsCString topWinSpec = topWinURI ? topWinURI->GetSpecOrDefault()
                                      : NS_LITERAL_CSTRING("(null)");
     topWinSpec.Truncate(
         std::min(topWinSpec.Length(), UrlClassifierCommon::sMaxSpecLength));
     UC_LOG(
@@ -223,16 +223,17 @@ nsresult UrlClassifierCommon::CreatePair
   NS_ENSURE_SUCCESS(rv, rv);
   if (!chan) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIURI> topWinURI;
   rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
   NS_ENSURE_SUCCESS(rv, rv);
+
   if (!topWinURI) {
     if (UC_LOG_ENABLED()) {
       nsresult rv;
       nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(aChannel, &rv);
       nsCOMPtr<nsIURI> uri;
       rv = httpChan->GetURI(getter_AddRefs(uri));
       nsAutoCString spec;
       uri->GetAsciiSpec(spec);
--- a/taskcluster/ci/searchfox/kind.yml
+++ b/taskcluster/ci/searchfox/kind.yml
@@ -17,16 +17,17 @@ transforms:
 job-defaults:
     index:
         product: firefox
     treeherder:
         symbol: Searchfox(idx)
         kind: build
         tier: 2
     run-on-projects: []
+    optimization: null
 
 jobs:
     linux64-searchfox/debug:
         description: "Linux64 Debug Searchfox"
         index:
             job-name: linux64-searchfox-debug
         treeherder:
             platform: linux64/debug
--- a/taskcluster/docker/funsize-update-generator/Pipfile.lock
+++ b/taskcluster/docker/funsize-update-generator/Pipfile.lock
@@ -62,66 +62,66 @@
             "hashes": [
                 "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
                 "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
             ],
             "version": "==3.0.1"
         },
         "attrs": {
             "hashes": [
-                "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
-                "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
+                "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
+                "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
             ],
-            "version": "==18.2.0"
+            "version": "==19.1.0"
         },
         "backports.lzma": {
             "hashes": [
                 "sha256:50829db66f0445442f6c796bba0ca62d1f87f54760c4682b6d1489e729a43744"
             ],
             "version": "==0.0.13"
         },
         "certifi": {
             "hashes": [
                 "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
                 "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
             ],
             "version": "==2018.11.29"
         },
         "cffi": {
             "hashes": [
-                "sha256:0b5f895714a7a9905148fc51978c62e8a6cbcace30904d39dcd0d9e2265bb2f6",
-                "sha256:27cdc7ba35ee6aa443271d11583b50815c4bb52be89a909d0028e86c21961709",
-                "sha256:2d4a38049ea93d5ce3c7659210393524c1efc3efafa151bd85d196fa98fce50a",
-                "sha256:3262573d0d60fc6b9d0e0e6e666db0e5045cbe8a531779aa0deb3b425ec5a282",
-                "sha256:358e96cfffc185ab8f6e7e425c7bb028931ed08d65402fbcf3f4e1bff6e66556",
-                "sha256:37c7db824b5687fbd7ea5519acfd054c905951acc53503547c86be3db0580134",
-                "sha256:39b9554dfe60f878e0c6ff8a460708db6e1b1c9cc6da2c74df2955adf83e355d",
-                "sha256:42b96a77acf8b2d06821600fa87c208046decc13bd22a4a0e65c5c973443e0da",
-                "sha256:5b37dde5035d3c219324cac0e69d96495970977f310b306fa2df5910e1f329a1",
-                "sha256:5d35819f5566d0dd254f273d60cf4a2dcdd3ae3003dfd412d40b3fe8ffd87509",
-                "sha256:5df73aa465e53549bd03c819c1bc69fb85529a5e1a693b7b6cb64408dd3970d1",
-                "sha256:7075b361f7a4d0d4165439992d0b8a3cdfad1f302bf246ed9308a2e33b046bd3",
-                "sha256:7678b5a667b0381c173abe530d7bdb0e6e3b98e062490618f04b80ca62686d96",
-                "sha256:7dfd996192ff8a535458c17f22ff5eb78b83504c34d10eefac0c77b1322609e2",
-                "sha256:8a3be5d31d02c60f84c4fd4c98c5e3a97b49f32e16861367f67c49425f955b28",
-                "sha256:9812e53369c469506b123aee9dcb56d50c82fad60c5df87feb5ff59af5b5f55c",
-                "sha256:9b6f7ba4e78c52c1a291d0c0c0bd745d19adde1a9e1c03cb899f0c6efd6f8033",
-                "sha256:a85bc1d7c3bba89b3d8c892bc0458de504f8b3bcca18892e6ed15b5f7a52ad9d",
-                "sha256:aa6b9c843ad645ebb12616de848cc4e25a40f633ccc293c3c9fe34107c02c2ea",
-                "sha256:bae1aa56ee00746798beafe486daa7cfb586cd395c6ce822ba3068e48d761bc0",
-                "sha256:bae96e26510e4825d5910a196bf6b5a11a18b87d9278db6d08413be8ea799469",
-                "sha256:bd78df3b594013b227bf31d0301566dc50ba6f40df38a70ded731d5a8f2cb071",
-                "sha256:c2711197154f46d06f73542c539a0ff5411f1951fab391e0a4ac8359badef719",
-                "sha256:d998c20e3deed234fca993fd6c8314cb7cbfda05fd170f1bd75bb5d7421c3c5a",
-                "sha256:df4f840d77d9e37136f8e6b432fecc9d6b8730f18f896e90628712c793466ce6",
-                "sha256:f5653c2581acb038319e6705d4e3593677676df14b112f13e0b5b44b6a18df1a",
-                "sha256:f7c7aa485a2e2250d455148470ffd0195eecc3d845122635202d7467d6f7b4cf",
-                "sha256:f9e2c66a6493147de835f207f198540a56b26745ce4f272fbc7c2f2cfebeb729"
+                "sha256:00b97afa72c233495560a0793cdc86c2571721b4271c0667addc83c417f3d90f",
+                "sha256:0ba1b0c90f2124459f6966a10c03794082a2f3985cd699d7d63c4a8dae113e11",
+                "sha256:0bffb69da295a4fc3349f2ec7cbe16b8ba057b0a593a92cbe8396e535244ee9d",
+                "sha256:21469a2b1082088d11ccd79dd84157ba42d940064abbfa59cf5f024c19cf4891",
+                "sha256:2e4812f7fa984bf1ab253a40f1f4391b604f7fc424a3e21f7de542a7f8f7aedf",
+                "sha256:2eac2cdd07b9049dd4e68449b90d3ef1adc7c759463af5beb53a84f1db62e36c",
+                "sha256:2f9089979d7456c74d21303c7851f158833d48fb265876923edcb2d0194104ed",
+                "sha256:3dd13feff00bddb0bd2d650cdb7338f815c1789a91a6f68fdc00e5c5ed40329b",
+                "sha256:4065c32b52f4b142f417af6f33a5024edc1336aa845b9d5a8d86071f6fcaac5a",
+                "sha256:51a4ba1256e9003a3acf508e3b4f4661bebd015b8180cc31849da222426ef585",
+                "sha256:59888faac06403767c0cf8cfb3f4a777b2939b1fbd9f729299b5384f097f05ea",
+                "sha256:59c87886640574d8b14910840327f5cd15954e26ed0bbd4e7cef95fa5aef218f",
+                "sha256:610fc7d6db6c56a244c2701575f6851461753c60f73f2de89c79bbf1cc807f33",
+                "sha256:70aeadeecb281ea901bf4230c6222af0248c41044d6f57401a614ea59d96d145",
+                "sha256:71e1296d5e66c59cd2c0f2d72dc476d42afe02aeddc833d8e05630a0551dad7a",
+                "sha256:8fc7a49b440ea752cfdf1d51a586fd08d395ff7a5d555dc69e84b1939f7ddee3",
+                "sha256:9b5c2afd2d6e3771d516045a6cfa11a8da9a60e3d128746a7fe9ab36dfe7221f",
+                "sha256:9c759051ebcb244d9d55ee791259ddd158188d15adee3c152502d3b69005e6bd",
+                "sha256:b4d1011fec5ec12aa7cc10c05a2f2f12dfa0adfe958e56ae38dc140614035804",
+                "sha256:b4f1d6332339ecc61275bebd1f7b674098a66fea11a00c84d1c58851e618dc0d",
+                "sha256:c030cda3dc8e62b814831faa4eb93dd9a46498af8cd1d5c178c2de856972fd92",
+                "sha256:c2e1f2012e56d61390c0e668c20c4fb0ae667c44d6f6a2eeea5d7148dcd3df9f",
+                "sha256:c37c77d6562074452120fc6c02ad86ec928f5710fbc435a181d69334b4de1d84",
+                "sha256:c8149780c60f8fd02752d0429246088c6c04e234b895c4a42e1ea9b4de8d27fb",
+                "sha256:cbeeef1dc3c4299bd746b774f019de9e4672f7cc666c777cd5b409f0b746dac7",
+                "sha256:e113878a446c6228669144ae8a56e268c91b7f1fafae927adc4879d9849e0ea7",
+                "sha256:e21162bf941b85c0cda08224dade5def9360f53b09f9f259adb85fc7dd0e7b35",
+                "sha256:fb6934ef4744becbda3143d30c6604718871495a5e36c408431bf33d9c146889"
             ],
-            "version": "==1.12.1"
+            "version": "==1.12.2"
         },
         "chardet": {
             "hashes": [
                 "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
                 "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
             ],
             "version": "==3.0.4"
         },
@@ -135,37 +135,37 @@
         "construct": {
             "hashes": [
                 "sha256:2271a0efd0798679dea825ff47e22a4c550456a5db0ba8baa82f7eae0af0118c"
             ],
             "version": "==2.9.45"
         },
         "cryptography": {
             "hashes": [
-                "sha256:05b3ded5e88747d28ee3ef493f2b92cbb947c1e45cf98cfef22e6d38bb67d4af",
-                "sha256:06826e7f72d1770e186e9c90e76b4f84d90cdb917b47ff88d8dc59a7b10e2b1e",
-                "sha256:08b753df3672b7066e74376f42ce8fc4683e4fd1358d34c80f502e939ee944d2",
-                "sha256:2cd29bd1911782baaee890544c653bb03ec7d95ebeb144d714b0f5c33deb55c7",
-                "sha256:31e5637e9036d966824edaa91bf0aa39dc6f525a1c599f39fd5c50340264e079",
-                "sha256:42fad67d7072216a49e34f923d8cbda9edacbf6633b19a79655e88a1b4857063",
-                "sha256:4946b67235b9d2ea7d31307be9d5ad5959d6c4a8f98f900157b47abddf698401",
-                "sha256:522fdb2809603ee97a4d0ef2f8d617bc791eb483313ba307cb9c0a773e5e5695",
-                "sha256:6f841c7272645dd7c65b07b7108adfa8af0aaea57f27b7f59e01d41f75444c85",
-                "sha256:7d335e35306af5b9bc0560ca39f740dfc8def72749645e193dd35be11fb323b3",
-                "sha256:8504661ffe324837f5c4607347eeee4cf0fcad689163c6e9c8d3b18cf1f4a4ad",
-                "sha256:9260b201ce584d7825d900c88700aa0bd6b40d4ebac7b213857bd2babee9dbca",
-                "sha256:9a30384cc402eac099210ab9b8801b2ae21e591831253883decdb4513b77a3cd",
-                "sha256:9e29af877c29338f0cab5f049ccc8bd3ead289a557f144376c4fbc7d1b98914f",
-                "sha256:ab50da871bc109b2d9389259aac269dd1b7c7413ee02d06fe4e486ed26882159",
-                "sha256:b13c80b877e73bcb6f012813c6f4a9334fcf4b0e96681c5a15dac578f2eedfa0",
-                "sha256:bfe66b577a7118e05b04141f0f1ed0959552d45672aa7ecb3d91e319d846001e",
-                "sha256:e091bd424567efa4b9d94287a952597c05d22155a13716bf5f9f746b9dc906d3",
-                "sha256:fa2b38c8519c5a3aa6e2b4e1cf1a549b54acda6adb25397ff542068e73d1ed00"
+                "sha256:066f815f1fe46020877c5983a7e747ae140f517f1b09030ec098503575265ce1",
+                "sha256:210210d9df0afba9e000636e97810117dc55b7157c903a55716bb73e3ae07705",
+                "sha256:26c821cbeb683facb966045e2064303029d572a87ee69ca5a1bf54bf55f93ca6",
+                "sha256:2afb83308dc5c5255149ff7d3fb9964f7c9ee3d59b603ec18ccf5b0a8852e2b1",
+                "sha256:2db34e5c45988f36f7a08a7ab2b69638994a8923853dec2d4af121f689c66dc8",
+                "sha256:409c4653e0f719fa78febcb71ac417076ae5e20160aec7270c91d009837b9151",
+                "sha256:45a4f4cf4f4e6a55c8128f8b76b4c057027b27d4c67e3fe157fa02f27e37830d",
+                "sha256:48eab46ef38faf1031e58dfcc9c3e71756a1108f4c9c966150b605d4a1a7f659",
+                "sha256:6b9e0ae298ab20d371fc26e2129fd683cfc0cfde4d157c6341722de645146537",
+                "sha256:6c4778afe50f413707f604828c1ad1ff81fadf6c110cb669579dea7e2e98a75e",
+                "sha256:8c33fb99025d353c9520141f8bc989c2134a1f76bac6369cea060812f5b5c2bb",
+                "sha256:9873a1760a274b620a135054b756f9f218fa61ca030e42df31b409f0fb738b6c",
+                "sha256:9b069768c627f3f5623b1cbd3248c5e7e92aec62f4c98827059eed7053138cc9",
+                "sha256:9e4ce27a507e4886efbd3c32d120db5089b906979a4debf1d5939ec01b9dd6c5",
+                "sha256:acb424eaca214cb08735f1a744eceb97d014de6530c1ea23beb86d9c6f13c2ad",
+                "sha256:c8181c7d77388fe26ab8418bb088b1a1ef5fde058c6926790c8a0a3d94075a4a",
+                "sha256:d4afbb0840f489b60f5a580a41a1b9c3622e08ecb5eec8614d4fb4cd914c4460",
+                "sha256:d9ed28030797c00f4bc43c86bf819266c76a5ea61d006cd4078a93ebf7da6bfd",
+                "sha256:e603aa7bb52e4e8ed4119a58a03b60323918467ef209e6ff9db3ac382e5cf2c6"
             ],
-            "version": "==2.5"
+            "version": "==2.6.1"
         },
         "datadog": {
             "hashes": [
                 "sha256:cbaa6b4b2b88fd552605e6730f60d5437017bb76d6b701432eaafbc983735b79"
             ],
             "index": "pypi",
             "version": "==0.26.0"
         },
@@ -180,27 +180,34 @@
             "hashes": [
                 "sha256:24d7f2f94f7f3cb6061acb215685e5125fbcdc40a857eff9de22518820b0a4f4",
                 "sha256:702a91ade2968a82beb0db1e0766a6a273f33d4616a6ce8cde475d8e09853b20"
             ],
             "version": "==0.5.0"
         },
         "dictdiffer": {
             "hashes": [
-                "sha256:6de9370f3c0c7fb5cc8bdc9e10dbca6ff05c39d8e2e58a67eb98d32677a224ca",
-                "sha256:e4f94167d037f70c11c6a8e7e289d81c8c7117bc02132cd82a0ab8fcba43cc08"
+                "sha256:b6eed4cf74ed31ae9646257a9f802bb09e545ca817d5c0119d747b6a05b6a22d",
+                "sha256:cc398dc26600cdb9519b2c768157333a0967b24d64c3913077dd0794274395da"
             ],
-            "version": "==0.7.1"
+            "version": "==0.7.2"
         },
         "frozendict": {
             "hashes": [
                 "sha256:774179f22db2ef8a106e9c38d4d1f8503864603db08de2e33be5b778230f6e45"
             ],
             "version": "==1.2"
         },
+        "github3.py": {
+            "hashes": [
+                "sha256:15a115c18f7bfcf934dfef7ab103844eb9f620c586bad65967708926da47cbda",
+                "sha256:50833b5da35546b8cced0e8d7ff4c50a9afc2c8e46cc4d07dc4b66d26467c708"
+            ],
+            "version": "==1.3.0"
+        },
         "idna": {
             "hashes": [
                 "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
                 "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
             ],
             "version": "==2.8"
         },
         "idna-ssl": {
@@ -213,28 +220,35 @@
         "json-e": {
             "hashes": [
                 "sha256:d2914f785d93ecc4f0b2ad6e3f2791f33327eaa740a3c4917d68a9a485dd282d"
             ],
             "version": "==3.0.0"
         },
         "jsonschema": {
             "hashes": [
-                "sha256:000e68abd33c972a5248544925a0cae7d1125f9bf6c58280d37546b946769a08",
-                "sha256:6ff5f3180870836cae40f06fa10419f557208175f13ad7bc26caa77beb1f6e02"
+                "sha256:0c0a81564f181de3212efa2d17de1910f8732fa1b71c42266d983cd74304e20d",
+                "sha256:a5f6559964a3851f59040d3b961de5e68e70971afb88ba519d27e6a039efff1a"
             ],
-            "version": "==2.6.0"
+            "version": "==3.0.1"
+        },
+        "jwcrypto": {
+            "hashes": [
+                "sha256:a87ac0922d09d9a65011f76d99849f1fbad3d95439c7452cebf4ab0871c2b665",
+                "sha256:e6c517d8998956e531f0a1c158b2f324c29a532a9c4b677bc30b3be14d60ad4d"
+            ],
+            "version": "==0.6.0"
         },
         "mar": {
             "hashes": [
-                "sha256:516f840c63bf6769b76f928d0823c07ef9a836e8c2e988dd05eba92fc26e78ab",
-                "sha256:9699bd97eede5e289b16d174fa12b51977c89cd673ead5d44ff89d6d27f3ea58"
+                "sha256:1939df482f2d3f6221405da00d6286d77d2dd60d372a0fd37532a8f00544f64f",
+                "sha256:5d2904a063f0da625e37515fa9eb340082e69fa1c00fdbeb82d28b7ff3c51e28"
             ],
             "index": "pypi",
-            "version": "==3.0.0"
+            "version": "==3.1.0"
         },
         "mohawk": {
             "hashes": [
                 "sha256:b3f85ffa93a5c7d2f9cc591246ef9f8ac4a9fa716bfd5bae0377699a2d89d78c",
                 "sha256:e98b331d9fa9ece7b8be26094cbe2d57613ae882133cc755167268a984bc0ab3"
             ],
             "version": "==0.3.4"
         },
@@ -287,16 +301,22 @@
             "version": "==0.6.0"
         },
         "pycparser": {
             "hashes": [
                 "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
             ],
             "version": "==2.19"
         },
+        "pyrsistent": {
+            "hashes": [
+                "sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"
+            ],
+            "version": "==0.14.11"
+        },
         "python-dateutil": {
             "hashes": [
                 "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
                 "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
             ],
             "version": "==2.8.0"
         },
         "python-gnupg": {
@@ -335,21 +355,21 @@
                 "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
                 "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
             ],
             "index": "pypi",
             "version": "==2.21.0"
         },
         "scriptworker": {
             "hashes": [
-                "sha256:0545f12e0f546966f8033544958a74ee0c6dfe234db5ea7bb70b91dccc9a8f20",
-                "sha256:7d92ec8de7e125cd8927ac724761e3ef25840c6c8931214357405bb3e873b820"
+                "sha256:7b3db12bf468473b1549c05b5cc78effd8c89d7721dcb889ca45e971ba21252a",
+                "sha256:a23b02e86f4ee10e293cc2c5e4b7becd25c5e075286daab0a28d14dc945a810a"
             ],
             "index": "pypi",
-            "version": "==19.0.0"
+            "version": "==20.0.1"
         },
         "sh": {
             "hashes": [
                 "sha256:ae3258c5249493cebe73cb4e18253a41ed69262484bad36fdb3efcb8ad8870bb",
                 "sha256:b52bf5833ed01c7b5c5fb73a7f71b3d98d48e9b9b8764236237bdc7ecae850fc"
             ],
             "index": "pypi",
             "version": "==1.12.14"
@@ -387,16 +407,24 @@
             "hashes": [
                 "sha256:07b2c978670896022a43c4b915df8958bec4a6b84add7f2c87b2b728bda3ba64",
                 "sha256:f3f0e67e1d42de47b5c67c32c9b26641642e9170fe7e292991793705cd5fef7c",
                 "sha256:fb2cd053238d33a8ec939190f30cfd736c00653a85a2919415cecf7dc3d9da71"
             ],
             "markers": "python_version < '3.7'",
             "version": "==3.7.2"
         },
+        "uritemplate": {
+            "hashes": [
+                "sha256:01c69f4fe8ed503b2951bef85d996a9d22434d2431584b5b107b2981ff416fbd",
+                "sha256:1b9c467a940ce9fb9f50df819e8ddd14696f89b9a8cc87ac77952ba416e0a8fd",
+                "sha256:c02643cebe23fc8adb5e6becffe201185bf06c40bda5c0b4028a93f1527d011d"
+            ],
+            "version": "==3.0.0"
+        },
         "urllib3": {
             "hashes": [
                 "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
                 "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
             ],
             "version": "==1.24.1"
         },
         "yarl": {
--- a/taskcluster/docker/funsize-update-generator/scripts/funsize.py
+++ b/taskcluster/docker/funsize-update-generator/scripts/funsize.py
@@ -151,36 +151,38 @@ async def download(url, dest, mode=None)
                 os.chmod(dest, mode)
 
 
 async def run_command(cmd, cwd='/', env=None, label=None, silent=False):
     if not env:
         env = dict()
     process = await asyncio.create_subprocess_shell(cmd,
                                                     stdout=asyncio.subprocess.PIPE,
-                                                    stderr=asyncio.subprocess.STDOUT,
+                                                    stderr=asyncio.subprocess.PIPE,
                                                     cwd=cwd, env=env)
-    stdout, stderr = await process.communicate()
+    if label:
+        label = "{}: ".format(label)
+    else:
+        label = ""
 
-    await process.wait()
+    async def read_output(stream, label, printcmd):
+        while True:
+            line = await stream.readline()
+            if line == b'':
+                break
+            printcmd("%s%s", label, line.decode('utf-8'))
 
     if silent:
-        return
-
-    if not stderr:
-        stderr = ""
-    if not stdout:
-        stdout = ""
-
-    label = "{}: ".format(label)
-
-    for line in stdout.splitlines():
-        log.debug("%s%s", label, line.decode('utf-8'))
-    for line in stderr.splitlines():
-        log.warn("%s%s", label, line.decode('utf-8'))
+        await process.wait()
+    else:
+        await asyncio.gather(
+            read_output(process.stdout, label, log.info),
+            read_output(process.stderr, label, log.warn)
+            )
+        await process.wait()
 
 
 async def unpack(work_env, mar, dest_dir):
     os.mkdir(dest_dir)
     log.debug("Unwrapping %s", mar)
     env = work_env.env
     if not is_lzma_compressed_mar(mar):
         env['MAR_OLD_FORMAT'] = '1'
--- a/taskcluster/docker/periodic-updates/setup.sh
+++ b/taskcluster/docker/periodic-updates/setup.sh
@@ -17,16 +17,17 @@ apt-get update -q
 apt-get install -y --no-install-recommends \
     arcanist \
     bzip2 \
     ca-certificates \
     curl \
     jq \
     libdbus-glib-1-2 \
     libgtk-3-0 \
+    libx11-xcb1 \
     libxml2-utils \
     libxt6 \
     python \
     python3 \
     shellcheck \
     unzip \
     wget \
 
--- a/taskcluster/docs/optimization-process.rst
+++ b/taskcluster/docs/optimization-process.rst
@@ -60,16 +60,19 @@ The first two phases annotate each task 
 fate: removed, replaced, or retained. The tasks that are replaced also have a
 replacement taskId.
 
 The last phase constructs a subgraph containing the retained tasks, and
 simultaneously rewrites all dependencies to refer to taskIds instead of labels.
 To do so, it assigns a taskId to each retained task and uses the replacement
 taskId for all replaced tasks.
 
+The `soft-dependencies` are then solved for each task, by adding all the
+remaining tasks in the subgraph from that list to its `dependencies`.
+
 The result is an optimized taskgraph with tasks named by taskId instead of
 label. At this phase, the edges in the task graph diverge from the
 ``task.dependencies`` attributes, as the latter may contain dependencies
 outside of the taskgraph (for replacement tasks).
 
 As a side-effect, this phase also expands all ``{"task-reference": ".."}`` and
 ``{"artifact-reference": ".."}`` objects within the task definitions.
 
--- a/taskcluster/docs/taskgraph.rst
+++ b/taskcluster/docs/taskgraph.rst
@@ -60,16 +60,28 @@ the bread-and-butter build and test conf
 Dependencies
 ------------
 
 Dependencies between tasks are represented as labeled edges in the task graph.
 For example, a test task must depend on the build task creating the artifact it
 tests, and this dependency edge is named 'build'.  The task graph generation
 process later resolves these dependencies to specific taskIds.
 
+Dependencies are typically used to ensure that prerequisites to a task, such as
+creation of binary artifacts, are completed before that task runs. But
+dependencies can also be used to schedule follow-up work such as summarizing
+test results. In the latter case, the summarization task will "pull in" all of
+the tasks it depends on, even if those tasks might otherwise be optimized away.
+The fix for this situation is "soft dependencies".
+To add a task depending only on tasks remaining after the optimization process
+completed, you can use `soft-dependencies`, as a list of optimized tasks labels.
+This is useful for tasks that should not pull other tasks into the graph, but do
+need to run after them, if they are in the graph (signing task after an optional
+build or reporting on tasks outputs).
+
 Decision Task
 -------------
 
 The decision task is the first task created when a new graph begins.  It is
 responsible for creating the rest of the task graph.
 
 The decision task for pushes is defined in-tree, in ``.taskcluster.yml``.  That
 task description invokes ``mach taskcluster decision`` with some metadata about
--- a/taskcluster/taskgraph/generator.py
+++ b/taskcluster/taskgraph/generator.py
@@ -66,16 +66,17 @@ class Kind(object):
         trans_config = TransformConfig(self.name, self.path, config, parameters,
                                        kind_dependencies_tasks, self.graph_config)
         tasks = [Task(self.name,
                       label=task_dict['label'],
                       attributes=task_dict['attributes'],
                       task=task_dict['task'],
                       optimization=task_dict.get('optimization'),
                       dependencies=task_dict.get('dependencies'),
+                      soft_dependencies=task_dict.get('soft-dependencies'),
                       release_artifacts=task_dict.get('release-artifacts'),
                       )
                  for task_dict in transforms(trans_config, inputs)]
         return tasks
 
     @classmethod
     def load(cls, root_dir, graph_config, kind_name):
         path = os.path.join(root_dir, kind_name)
--- a/taskcluster/taskgraph/optimize.py
+++ b/taskcluster/taskgraph/optimize.py
@@ -209,16 +209,25 @@ def get_subgraph(target_task_graph, remo
     omit = removed_tasks | replaced_tasks
     for label, task in target_task_graph.tasks.iteritems():
         if label in omit:
             continue
         task.task_id = label_to_taskid[label]
         named_task_dependencies = {
             name: label_to_taskid[label]
             for name, label in named_links_dict.get(label, {}).iteritems()}
+
+        # Add remaining soft dependencies
+        if task.soft_dependencies:
+            named_task_dependencies.update({
+                label: label_to_taskid[label]
+                for label in task.soft_dependencies
+                if label in label_to_taskid and label not in omit
+            })
+
         task.task = resolve_task_references(task.label, task.task, named_task_dependencies)
         deps = task.task.setdefault('dependencies', [])
         deps.extend(sorted(named_task_dependencies.itervalues()))
         tasks_by_taskid[task.task_id] = task
 
     # resolve edges to taskIds
     edges_by_taskid = (
         (label_to_taskid.get(left), label_to_taskid.get(right), name)
--- a/taskcluster/taskgraph/task.py
+++ b/taskcluster/taskgraph/task.py
@@ -14,46 +14,50 @@ class Task(object):
 
     - kind: the name of the task kind
     - label; the label for this task
     - attributes: a dictionary of attributes for this task (used for filtering)
     - task: the task definition (JSON-able dictionary)
     - optimization: optimization to apply to the task (see taskgraph.optimize)
     - dependencies: tasks this one depends on, in the form {name: label}, for example
       {'build': 'build-linux64/opt', 'docker-image': 'build-docker-image-desktop-test'}
+    - soft_dependencies: tasks this one may depend on if they are available post
+      optimisation. They are set as a list of tasks label.
 
     And later, as the task-graph processing proceeds:
 
     - task_id -- TaskCluster taskId under which this task will be created
 
     This class is just a convenience wrapper for the data type and managing
     display, comparison, serialization, etc. It has no functionality of its own.
     """
 
     kind = attr.ib()
     label = attr.ib()
     attributes = attr.ib()
     task = attr.ib()
     task_id = attr.ib(default=None, init=False)
     optimization = attr.ib(default=None)
     dependencies = attr.ib(factory=dict)
+    soft_dependencies = attr.ib(factory=list)
     release_artifacts = attr.ib(
         converter=attr.converters.optional(frozenset),
         default=None,
     )
 
     def __attrs_post_init__(self):
         self.attributes['kind'] = self.kind
 
     def to_json(self):
         rv = {
             'kind': self.kind,
             'label': self.label,
             'attributes': self.attributes,
             'dependencies': self.dependencies,
+            'soft_dependencies': self.soft_dependencies,
             'optimization': self.optimization,
             'task': self.task,
         }
         if self.task_id:
             rv['task_id'] = self.task_id
         if self.release_artifacts:
             rv['release_artifacts'] = sorted(self.release_artifacts)
         return rv
@@ -67,13 +71,14 @@ class Task(object):
         """
         rv = cls(
             kind=task_dict['kind'],
             label=task_dict['label'],
             attributes=task_dict['attributes'],
             task=task_dict['task'],
             optimization=task_dict['optimization'],
             dependencies=task_dict.get('dependencies'),
+            soft_dependencies=task_dict.get('soft_dependencies'),
             release_artifacts=task_dict.get('release-artifacts'),
         )
         if 'task_id' in task_dict:
             rv.task_id = task_dict['task_id']
         return rv
--- a/taskcluster/taskgraph/test/test_taskgraph.py
+++ b/taskcluster/taskgraph/test/test_taskgraph.py
@@ -36,24 +36,26 @@ class TestTaskGraph(unittest.TestCase):
 
         self.assertEqual(res, {
             'a': {
                 'kind': 'test',
                 'label': 'a',
                 'attributes': {'attr': 'a-task', 'kind': 'test'},
                 'task': {'taskdef': True},
                 'dependencies': {'edgelabel': 'b'},
+                'soft_dependencies': [],
                 'optimization': None,
             },
             'b': {
                 'kind': 'test',
                 'label': 'b',
                 'attributes': {'kind': 'test'},
                 'task': {'task': 'def'},
                 'dependencies': {},
+                'soft_dependencies': [],
                 'optimization': {'seta': None},
             }
         })
 
     def test_round_trip(self):
         graph = TaskGraph(tasks={
             'a': Task(
                 kind='fancy',
--- a/taskcluster/taskgraph/transforms/job/__init__.py
+++ b/taskcluster/taskgraph/transforms/job/__init__.py
@@ -43,16 +43,17 @@ job_description_schema = Schema({
 
     # the following fields are passed directly through to the task description,
     # possibly modified by the run implementation.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details.
     Required('description'): task_description_schema['description'],
     Optional('attributes'): task_description_schema['attributes'],
     Optional('job-from'): task_description_schema['job-from'],
     Optional('dependencies'): task_description_schema['dependencies'],
+    Optional('soft-dependencies'): task_description_schema['soft-dependencies'],
     Optional('expires-after'): task_description_schema['expires-after'],
     Optional('routes'): task_description_schema['routes'],
     Optional('scopes'): task_description_schema['scopes'],
     Optional('tags'): task_description_schema['tags'],
     Optional('extra'): task_description_schema['extra'],
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('index'): task_description_schema['index'],
     Optional('run-on-projects'): task_description_schema['run-on-projects'],
@@ -242,16 +243,17 @@ def make_task_description(config, jobs):
         if job['run']['using'] != 'always-optimized':
             job['run'].setdefault('workdir', '/builds/worker')
 
         taskdesc = copy.deepcopy(job)
 
         # fill in some empty defaults to make run implementations easier
         taskdesc.setdefault('attributes', {})
         taskdesc.setdefault('dependencies', {})
+        taskdesc.setdefault('soft-dependencies', [])
         taskdesc.setdefault('routes', [])
         taskdesc.setdefault('scopes', [])
         taskdesc.setdefault('extra', {})
 
         # give the function for job.run.using on this worker implementation a
         # chance to set up the task description.
         configure_taskdesc_for_run(config, job, taskdesc, impl)
         del taskdesc['run']
--- a/taskcluster/taskgraph/transforms/partials.py
+++ b/taskcluster/taskgraph/transforms/partials.py
@@ -117,17 +117,17 @@ def make_task_description(config, jobs):
 
         level = config.params['level']
 
         worker = {
             'artifacts': _generate_task_output_files(dep_job, builds.keys(), locale),
             'implementation': 'docker-worker',
             'docker-image': {'in-tree': 'funsize-update-generator'},
             'os': 'linux',
-            'max-run-time': 3600,
+            'max-run-time': 600,
             'chain-of-trust': True,
             'taskcluster-proxy': True,
             'env': {
                 'SHA1_SIGNING_CERT': 'nightly_sha1',
                 'SHA384_SIGNING_CERT': 'nightly_sha384',
                 'DATADOG_API_SECRET':
                     'project/releng/gecko/build/level-{}/datadog-api-key'.format(level),
                 'EXTRA_PARAMS': '--arch={}'.format(architecture(attributes['build_platform'])),
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -63,16 +63,19 @@ task_description_schema = Schema({
     # relative path (from config.path) to the file task was defined in
     Optional('job-from'): basestring,
 
     # dependencies of this task, keyed by name; these are passed through
     # verbatim and subject to the interpretation of the Task's get_dependencies
     # method.
     Optional('dependencies'): {basestring: object},
 
+    # Soft dependencies of this task, as a list of tasks labels
+    Optional('soft-dependencies'): [basestring],
+
     Optional('requires'): Any('all-completed', 'all-resolved'),
 
     # expiration and deadline times, relative to task creation, with units
     # (e.g., "14 days").  Defaults are set based on the project.
     Optional('expires-after'): basestring,
     Optional('deadline-after'): basestring,
 
     # custom routes for this task; the default treeherder routes will be added
@@ -1766,16 +1769,17 @@ def build_task(config, tasks):
             if payload:
                 env = payload.setdefault('env', {})
                 env['MOZ_AUTOMATION'] = '1'
 
         yield {
             'label': task['label'],
             'task': task_def,
             'dependencies': task.get('dependencies', {}),
+            'soft-dependencies': task.get('soft-dependencies', []),
             'attributes': attributes,
             'optimization': task.get('optimization', None),
             'release-artifacts': task.get('release-artifacts', []),
         }
 
 
 @transforms.add
 def chain_of_trust(config, tasks):
--- a/testing/mozharness/configs/builds/releng_base_android_64_builds.py
+++ b/testing/mozharness/configs/builds/releng_base_android_64_builds.py
@@ -10,16 +10,22 @@ config = {
         'multi-l10n',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     'max_build_output_timeout': 0,
     'secret_files': [
         {'filename': '/builds/gapi.data',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
+        {'filename': '/builds/gls-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gls-gapi.data',
+         'min_scm_level': 1},
+        {'filename': '/builds/sb-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/sb-gapi.data',
+         'min_scm_level': 1},
         {'filename': '/builds/mozilla-fennec-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-fennec-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
         {'filename': '/builds/adjust-sdk.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk.token',
          'min_scm_level': 2, 'default-file': '{abs_src_dir}/mobile/android/base/adjust-sdk-sandbox.token'},
         {'filename': '/builds/adjust-sdk-beta.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk-beta.token',
--- a/testing/mozharness/configs/builds/releng_base_linux_32_builds.py
+++ b/testing/mozharness/configs/builds/releng_base_linux_32_builds.py
@@ -14,16 +14,22 @@ config = {
         'build',
         'check-test',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     'secret_files': [
         {'filename': '/builds/gapi.data',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
+        {'filename': '/builds/gls-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gls-gapi.data',
+         'min_scm_level': 1},
+        {'filename': '/builds/sb-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/sb-gapi.data',
+         'min_scm_level': 1},
         {'filename': '/builds/mozilla-desktop-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-desktop-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
         {'filename': '/builds/adjust-sdk.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk.token',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
         {'filename': '/builds/adjust-sdk-beta.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk-beta.token',
--- a/testing/mozharness/configs/builds/releng_base_linux_64_builds.py
+++ b/testing/mozharness/configs/builds/releng_base_linux_64_builds.py
@@ -13,16 +13,22 @@ config = {
         'build',
         'check-test',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     'secret_files': [
         {'filename': '/builds/gapi.data',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
+        {'filename': '/builds/gls-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gls-gapi.data',
+         'min_scm_level': 1},
+        {'filename': '/builds/sb-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/sb-gapi.data',
+         'min_scm_level': 1},
         {'filename': '/builds/mozilla-desktop-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-desktop-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
         {'filename': '/builds/adjust-sdk.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk.token',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
         {'filename': '/builds/adjust-sdk-beta.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk-beta.token',
--- a/testing/mozharness/configs/builds/releng_base_mac_64_cross_builds.py
+++ b/testing/mozharness/configs/builds/releng_base_mac_64_cross_builds.py
@@ -8,16 +8,22 @@ config = {
     'default_actions': [
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     'secret_files': [
         {'filename': '/builds/gapi.data',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
+        {'filename': '/builds/gls-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gls-gapi.data',
+         'min_scm_level': 1},
+        {'filename': '/builds/sb-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/sb-gapi.data',
+         'min_scm_level': 1},
         {'filename': '/builds/mozilla-desktop-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-desktop-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
     ],
     'enable_check_test': False,
     'vcs_share_base': '/builds/hg-shared',
     #########################################################################
 
--- a/testing/mozharness/configs/builds/releng_base_windows_32_mingw_builds.py
+++ b/testing/mozharness/configs/builds/releng_base_windows_32_mingw_builds.py
@@ -8,16 +8,22 @@ config = {
     # code block and also make sure this is synced with
     # releng_base_linux_64_builds.py
 
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     'secret_files': [
         {'filename': '/builds/gapi.data',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
+        {'filename': '/builds/gls-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gls-gapi.data',
+         'min_scm_level': 1},
+        {'filename': '/builds/sb-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/sb-gapi.data',
+         'min_scm_level': 1},
         {'filename': '/builds/mozilla-desktop-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-desktop-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
         {'filename': '/builds/adjust-sdk.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk.token',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
         {'filename': '/builds/adjust-sdk-beta.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk-beta.token',
--- a/testing/mozharness/configs/builds/releng_base_windows_64_mingw_builds.py
+++ b/testing/mozharness/configs/builds/releng_base_windows_64_mingw_builds.py
@@ -8,16 +8,22 @@ config = {
     # code block and also make sure this is synced with
     # releng_base_linux_64_builds.py
 
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     'secret_files': [
         {'filename': '/builds/gapi.data',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
+        {'filename': '/builds/gls-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gls-gapi.data',
+         'min_scm_level': 1},
+        {'filename': '/builds/sb-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/sb-gapi.data',
+         'min_scm_level': 1},
         {'filename': '/builds/mozilla-desktop-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-desktop-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
         {'filename': '/builds/adjust-sdk.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk.token',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
         {'filename': '/builds/adjust-sdk-beta.token',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/adjust-sdk-beta.token',
--- a/testing/mozharness/configs/builds/releng_sub_linux_configs/64_artifact.py
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_artifact.py
@@ -5,16 +5,22 @@ config = {
     'default_actions': [
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     'secret_files': [
         {'filename': '/builds/gapi.data',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
+        {'filename': '/builds/gls-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gls-gapi.data',
+         'min_scm_level': 1},
+        {'filename': '/builds/sb-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/sb-gapi.data',
+         'min_scm_level': 1},
         {'filename': '/builds/mozilla-desktop-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-desktop-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
     ],
     'vcs_share_base': '/builds/hg-shared',
     #########################################################################
 
 
--- a/testing/mozharness/configs/builds/taskcluster_base_windows.py
+++ b/testing/mozharness/configs/builds/taskcluster_base_windows.py
@@ -26,13 +26,19 @@ config = {
     },
     'upload_env': {
         'UPLOAD_PATH': os.path.join(os.getcwd(), 'public', 'build'),
     },
     'secret_files': [
         {'filename': 'gapi.data',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
+        {'filename': 'gls-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gls-gapi.data',
+         'min_scm_level': 1},
+        {'filename': 'sb-gapi.data',
+         'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/sb-gapi.data',
+         'min_scm_level': 1},
         {'filename': 'mozilla-desktop-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-desktop-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
     ],
 }
--- a/testing/mozharness/configs/single_locale/tc_android-api-16.py
+++ b/testing/mozharness/configs/single_locale/tc_android-api-16.py
@@ -19,16 +19,22 @@ config = {
 
     "upload_env": {
         'UPLOAD_PATH': '/builds/worker/artifacts/',
     },
     'secret_files': [
         {'filename': '/builds/gapi.data',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
+        {'filename': '/builds/gls-gapi.data',