merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 15 Aug 2017 11:42:11 +0200
changeset 374793 564e82f0f289af976da01c2d50507017bbc152b5
parent 374683 b4b58cf12108a0b49db77781bdcab8cc481fb64b (current diff)
parent 374792 fb6f3d43901e91d9ad8f202fb3f31d1c17a2bc89 (diff)
child 374794 df4da5b8599a07277245d6afe9a932587c0a3585
child 374835 ed58aa140f4c9de0ddcc6302cc3cd77e55e65ec2
child 374898 e4cc320d4cea5bc6cbf9752d633998733b1e13c7
push id48794
push usercbook@mozilla.com
push dateTue, 15 Aug 2017 11:13:09 +0000
treeherderautoland@df4da5b8599a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone57.0a1
first release with
nightly linux32
564e82f0f289 / 57.0a1 / 20170815100349 / files
nightly linux64
564e82f0f289 / 57.0a1 / 20170815100349 / files
nightly mac
564e82f0f289 / 57.0a1 / 20170815100349 / files
nightly win32
564e82f0f289 / 57.0a1 / 20170815100349 / files
nightly win64
564e82f0f289 / 57.0a1 / 20170815100349 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/base/content/browser.css
browser/base/content/browser.js
browser/themes/shared/jar.inc.mn
browser/themes/shared/tabs.inc.css
dom/indexedDB/test/extensions/bootstrap.js
dom/indexedDB/test/extensions/indexedDB-test@mozilla.org.xpi
dom/indexedDB/test/extensions/install.rdf
dom/indexedDB/test/extensions/moz.build
dom/indexedDB/test/test_globalObjects_other.xul
layout/reftests/printing/test-async-print.html
toolkit/components/extensions/ext-geolocation.js
toolkit/mozapps/extensions/internal/XPIProvider.jsm
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -452,18 +452,18 @@ var BrowserPageActions = {
     }
     let actionID = this._actionIDForNodeID(node.id);
     let action = PageActions.actionForID(actionID);
     if (!action) {
       // The given node may be an ancestor of a node corresponding to an action,
       // like how #star-button is contained in #star-button-box, the latter
       // being the bookmark action's node.  Look up the ancestor chain.
       for (let n = node.parentNode; n && !action; n = n.parentNode) {
-        if (n.id == "urlbar-icons" || n.localName == "panelview") {
-          // We reached the urlbar icons container or the panelview container.
+        if (n.id == "page-action-buttons" || n.localName == "panelview") {
+          // We reached the page-action-buttons or panelview container.
           // Stop looking; no acton was found.
           break;
         }
         actionID = this._actionIDForNodeID(n.id);
         action = PageActions.actionForID(actionID);
       }
     }
     return action;
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -658,18 +658,18 @@ html|input.urlbar-input[textoverflow]:no
 #PopupAutoCompleteRichResult.showSearchSuggestionsNotification > richlistbox {
   transition: none;
 }
 
 #DateTimePickerPanel[active="true"] {
   -moz-binding: url("chrome://global/content/bindings/datetimepopup.xml#datetime-popup");
 }
 
-#urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon,
-#urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon-wrapper > .urlbar-icon,
+#urlbar[pageproxystate="invalid"] > #page-action-buttons > .urlbar-icon,
+#urlbar[pageproxystate="invalid"] > #page-action-buttons > .urlbar-icon-wrapper > .urlbar-icon,
 .urlbar-go-button[pageproxystate="valid"],
 .urlbar-go-button:not([parentfocused="true"]),
 #urlbar[pageproxystate="invalid"] > #identity-box > #blocked-permissions-container,
 #urlbar[pageproxystate="invalid"] > #identity-box > #notification-popup-box,
 #urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon-labels {
   visibility: collapse;
 }
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -875,17 +875,17 @@
                   <label id="identity-icon-label" class="plain" flex="1"/>
                   <label id="identity-icon-country-label" class="plain"/>
                 </hbox>
               </box>
               <box id="urlbar-display-box" align="center">
                 <label id="switchtab" class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
                 <label id="extension" class="urlbar-display urlbar-display-extension" value="&urlbar.extension.label;"/>
               </box>
-              <hbox id="urlbar-icons">
+              <hbox id="page-action-buttons">
                 <image id="page-report-button"
                        class="urlbar-icon"
                        hidden="true"
                        tooltiptext="&pageReportIcon.tooltip;"
                        onmousedown="gPopupBlockerObserver.onReportButtonMousedown(event);"/>
                 <image id="reader-mode-button"
                        class="urlbar-icon"
                        hidden="true"
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -52,18 +52,17 @@ add_task(async function() {
     let loader = Cc["@mozilla.org/moz/jsloader;1"].getService(Ci.xpcIJSModuleLoader);
     sendAsyncMessage("Test:LoadedScripts", {
       /* Keep only the file name for components, as the path is an absolute file
          URL rather than a resource:// URL like for modules. */
       components: loader.loadedComponents().map(f => f.replace(/.*\//, "")),
       modules: loader.loadedModules(),
       services: Object.keys(Cc).filter(c => {
         try {
-          Cm.isServiceInstantiatedByContractID(c, Ci.nsISupports);
-          return true;
+          return Cm.isServiceInstantiatedByContractID(c, Ci.nsISupports);
         } catch (e) {
           return false;
         }
       })
     });
   } + ")()", false);
 
   let loadedList = await promise;
--- a/browser/components/extensions/ext-browser.json
+++ b/browser/components/extensions/ext-browser.json
@@ -75,16 +75,20 @@
   "history": {
     "url": "chrome://browser/content/ext-history.js",
     "schema": "chrome://browser/content/schemas/history.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["history"]
     ]
   },
+  "identity": {
+    "schema": "chrome://extensions/content/schemas/identity.json",
+    "scopes": ["addon_parent"]
+  },
   "menusInternal": {
     "url": "chrome://browser/content/ext-menus.js",
     "schema": "chrome://browser/content/schemas/menus.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["menusInternal"]
     ]
   },
--- a/browser/components/extensions/ext-pageAction.js
+++ b/browser/components/extensions/ext-pageAction.js
@@ -158,33 +158,33 @@ this.pageAction = class extends Extensio
     let style = `
       --webextension-urlbar-image: url("${getIcon(16)}");
       --webextension-urlbar-image-2x: url("${getIcon(32)}");
     `;
 
     return {style};
   }
 
-  // Create an |image| node and add it to the |urlbar-icons|
+  // Create an |image| node and add it to the |page-action-buttons|
   // container in the given window.
   addButton(window) {
     let document = window.document;
 
     let button = document.createElement("image");
     button.id = this.id;
     button.setAttribute("class", "urlbar-icon");
 
     button.addEventListener("click", this); // eslint-disable-line mozilla/balanced-listeners
 
     if (this.extension.hasPermission("menus") ||
         this.extension.hasPermission("contextMenus")) {
       document.addEventListener("popupshowing", this);
     }
 
-    document.getElementById("urlbar-icons").appendChild(button);
+    document.getElementById("page-action-buttons").appendChild(button);
 
     return button;
   }
 
   // Returns the page action button for the given window, creating it if
   // it doesn't already exist.
   getButton(window) {
     if (!this.buttons.has(window)) {
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -68,16 +68,17 @@ skip-if = (os == 'win' && !debug) # bug 
 [browser_ext_currentWindow.js]
 [browser_ext_devtools_inspectedWindow.js]
 [browser_ext_devtools_inspectedWindow_eval_bindings.js]
 [browser_ext_devtools_inspectedWindow_reload.js]
 [browser_ext_devtools_network.js]
 [browser_ext_devtools_page.js]
 [browser_ext_devtools_panel.js]
 [browser_ext_devtools_panels_elements.js]
+skip-if = true # bug 1382487
 [browser_ext_geckoProfiler_symbolicate.js]
 [browser_ext_getViews.js]
 [browser_ext_identity_indication.js]
 [browser_ext_incognito_views.js]
 [browser_ext_incognito_popup.js]
 [browser_ext_lastError.js]
 [browser_ext_menus.js]
 [browser_ext_omnibox.js]
--- a/browser/components/originattributes/test/browser/browser.ini
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -59,16 +59,17 @@ support-files =
   worker_deblobify.js
 
 [browser_broadcastChannel.js]
 [browser_cache.js]
 [browser_cookieIsolation.js]
 [browser_favicon_firstParty.js]
 [browser_favicon_userContextId.js]
 [browser_firstPartyIsolation.js]
+[browser_firstPartyIsolation_about_newtab.js]
 [browser_firstPartyIsolation_aboutPages.js]
 [browser_firstPartyIsolation_blobURI.js]
 [browser_firstPartyIsolation_js_uri.js]
 [browser_localStorageIsolation.js]
 [browser_blobURLIsolation.js]
 [browser_imageCacheIsolation.js]
 [browser_sharedworker.js]
 [browser_httpauth.js]
--- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js
@@ -152,17 +152,17 @@ add_task(async function test_aboutURL() 
 
       // We load pages with URI_SAFE_FOR_UNTRUSTED_CONTENT set, this means they
       // are not loaded with System Principal but with codebase principal.
       // Also we skip pages with HIDE_FROM_ABOUTABOUT, some of them may have
       // errors while loading.
       if ((flags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) &&
           !(flags & Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT) &&
           networkURLs.indexOf(aboutType) == -1 &&
-          // Exclude about:newtab see Bug 1021667
+          // handle about:newtab in browser_firstPartyIsolation_about_newtab.js
           aboutType !== "newtab") {
         aboutURLs.push(aboutType);
       }
     } catch (e) {
       // getService might have thrown if the component doesn't actually
       // implement nsIAboutModule
     }
   }
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_about_newtab.js
@@ -0,0 +1,40 @@
+add_task(async function setup() {
+  Services.prefs.setBoolPref("privacy.firstparty.isolate", true);
+
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("privacy.firstparty.isolate");
+  });
+});
+
+/**
+ * Test about:newtab will have firstPartytDomain set when we enable the pref.
+ *
+ * We split about:newtab from browser_firstPartyIsolation_aboutPages.js because
+ * about:newtab needs special care.
+ *
+ * In the original test browser_firstPartyIsolation_aboutPages.js, when calling
+ * tabbrowser.addTab, if it found out the uri is about:newtab, it will use
+ * the preloaded browser, however the preloaded browser is loaded before when we
+ * turn on the firstPartyIsolation pref, which won't have the pref set.
+ *
+ * To prevent to use the preloaded browser, a simple trick is open a window
+ * first.
+ **/
+add_task(async function test_aboutNewTab() {
+  let win = await BrowserTestUtils.openNewBrowserWindow({remote: false});
+  let gbrowser = win.gBrowser;
+  let tab = BrowserTestUtils.addTab(gbrowser, "about:newtab");
+  await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+  let attrs = { firstPartyDomain: "about.ef2a7dd5-93bc-417f-a698-142c3116864f.mozilla" };
+  await ContentTask.spawn(tab.linkedBrowser, { attrs }, async function(args) {
+    info("principal " + content.document.nodePrincipal.origin);
+    Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
+                 args.attrs.firstPartyDomain, "about:newtab should have firstPartyDomain set");
+    Assert.ok(content.document.nodePrincipal.isCodebasePrincipal,
+              "The principal should be a codebase principal.");
+  });
+
+  gbrowser.removeTab(tab);
+  win.close();
+});
--- a/browser/components/tests/startupRecorder.js
+++ b/browser/components/tests/startupRecorder.js
@@ -41,18 +41,17 @@ startupRecorder.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   record(name) {
     this.data.code[name] = {
       components: this.loader.loadedComponents(),
       modules: this.loader.loadedModules(),
       services: Object.keys(Cc).filter(c => {
         try {
-          Cm.isServiceInstantiatedByContractID(c, Ci.nsISupports);
-          return true;
+          return Cm.isServiceInstantiatedByContractID(c, Ci.nsISupports);
         } catch (e) {
           return false;
         }
       })
     };
   },
 
   observe(subject, topic, data) {
--- a/browser/extensions/pocket/bootstrap.js
+++ b/browser/extensions/pocket/bootstrap.js
@@ -207,17 +207,17 @@ var PocketPageAction = {
           animatableImage.id = "pocket-animatable-image";
           let pocketButton = doc.createElement("image");
           pocketButton.id = "pocket-button";
           pocketButton.classList.add("urlbar-icon");
 
           wrapper.appendChild(pocketButton);
           wrapper.appendChild(animatableBox);
           animatableBox.appendChild(animatableImage);
-          let iconBox = doc.getElementById("urlbar-icons");
+          let iconBox = doc.getElementById("page-action-buttons");
           iconBox.appendChild(wrapper);
           wrapper.hidden = true;
           wrapper.addEventListener("click", event => {
             PocketPageAction.onUrlbarNodeClicked(event);
           });
         },
         onPlacedInPanel(panelNode, urlbarNode) {
           PocketOverlay.onWindowOpened(panelNode.ownerGlobal);
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -358,16 +358,17 @@ Section "-Application" APP_IDX
 
     ReadRegStr $0 HKLM "Software\mozilla.org\Mozilla" "CurrentVersion"
     ${If} "$0" != "${GREVersion}"
       WriteRegStr HKLM "Software\mozilla.org\Mozilla" "CurrentVersion" "${GREVersion}"
     ${EndIf}
   ${EndIf}
 
   ${RemoveDeprecatedKeys}
+  ${Set32to64DidMigrateReg}
 
   ; The previous installer adds several regsitry values to both HKLM and HKCU.
   ; We now try to add to HKLM and if that fails to HKCU
 
   ; The order that reg keys and values are added is important if you use the
   ; uninstall log to remove them on uninstall. When using the uninstall log you
   ; MUST add children first so they will be removed first on uninstall so they
   ; will be empty when the key is deleted. This allows the uninstaller to
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -86,16 +86,17 @@
   ${EndIf}
 
   ; Adds a pinned Task Bar shortcut (see MigrateTaskBarShortcut for details).
   ${MigrateTaskBarShortcut}
 
   ${UpdateShortcutBranding}
 
   ${RemoveDeprecatedKeys}
+  ${Set32to64DidMigrateReg}
 
   ${SetAppKeys}
   ${FixClassKeys}
   ${SetUninstallKeys}
   ${If} $TmpVal == "HKLM"
     ${SetStartMenuInternet} HKLM
   ${ElseIf} $TmpVal == "HKCU"
     ${SetStartMenuInternet} HKCU
@@ -612,16 +613,83 @@
   WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "http"   "FirefoxURL$2"
   WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "https"  "FirefoxURL$2"
 
   ; Registered Application
   WriteRegStr ${RegKey} "Software\RegisteredApplications" "$1" "$0\Capabilities"
 !macroend
 !define SetStartMenuInternet "!insertmacro SetStartMenuInternet"
 
+; Add registry keys to support the Firefox 32 bit to 64 bit migration. These
+; registry entries are not removed on uninstall at this time. After the Firefox
+; 32 bit to 64 bit migration effort is completed these registry entries can be
+; removed during install, post update, and uninstall.
+!macro Set32to64DidMigrateReg
+  ${GetLongPath} "$INSTDIR" $1
+  ; These registry keys are always in the 32 bit hive since they are never
+  ; needed by a Firefox 64 bit install unless it has been updated from Firefox
+  ; 32 bit.
+  SetRegView 32
+
+!ifdef HAVE_64BIT_BUILD
+
+  ; Running Firefox 64 bit on Windows 64 bit
+  ClearErrors
+  ReadRegDWORD $2 HKLM "Software\Mozilla\${AppName}\32to64DidMigrate" "$1"
+  ; If there were no errors then the system was updated from Firefox 32 bit to
+  ; Firefox 64 bit and if the value is already 1 then the registry value has
+  ; already been updated in the HKLM registry.
+  ${IfNot} ${Errors}
+  ${AndIf} $2 != 1
+    ClearErrors
+    WriteRegDWORD HKLM "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 1
+    ${If} ${Errors}
+      ; There was an error writing to HKLM so just write it to HKCU
+      WriteRegDWORD HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 1
+    ${Else}
+      ; This will delete the value from HKCU if it exists
+      DeleteRegValue HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1"
+    ${EndIf}
+  ${EndIf}
+
+  ClearErrors
+  ReadRegDWORD $2 HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1"
+  ; If there were no errors then the system was updated from Firefox 32 bit to
+  ; Firefox 64 bit and if the value is already 1 then the registry value has
+  ; already been updated in the HKCU registry.
+  ${IfNot} ${Errors}
+  ${AndIf} $2 != 1
+    WriteRegDWORD HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 1
+  ${EndIf}
+
+!else
+
+  ; Running Firefox 32 bit
+  ${If} ${RunningX64}
+    ; Running Firefox 32 bit on a Windows 64 bit system
+    ClearErrors
+    ReadRegDWORD $2 HKLM "Software\Mozilla\${AppName}\32to64DidMigrate" "$1"
+    ; If there were errors the value doesn't exist yet.
+    ${If} ${Errors}
+      ClearErrors
+      WriteRegDWORD HKLM "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 0
+      ; If there were errors write the value in HKCU.
+      ${If} ${Errors}
+        WriteRegDWORD HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 0
+      ${EndIf}
+    ${EndIf}
+  ${EndIf}
+
+!endif
+
+  ClearErrors
+  SetRegView lastused
+!macroend
+!define Set32to64DidMigrateReg "!insertmacro Set32to64DidMigrateReg"
+
 ; The IconHandler reference for FirefoxHTML can end up in an inconsistent state
 ; due to changes not being detected by the IconHandler for side by side
 ; installs (see bug 268512). The symptoms can be either an incorrect icon or no
 ; icon being displayed for files associated with Firefox (does not use SHCTX).
 !macro FixShellIconHandler RegKey
   ; Find the correct key to update, either FirefoxHTML or FirefoxHTML-[PathHash]
   StrCpy $3 "FirefoxHTML-$AppUserModelID"
   ClearErrors
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -116,17 +116,17 @@
 }
 
 #navigator-toolbox:not(:hover) > #nav-bar:not([customizing="true"]) > #nav-bar-customization-target > #urlbar-container > #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
   opacity: 0;
 }
 
 /* Page action urlbar buttons */
 
-#urlbar-icons {
+#page-action-buttons {
   -moz-box-align: center;
   /* Add more space between the last icon and the urlbar's edge. */
   margin-inline-end: 3px;
 }
 
 .urlbar-icon {
   padding: 0 6px;
   /* 16x16 icon with border-box sizing */
--- a/devtools/client/netmonitor/src/components/response-panel.js
+++ b/devtools/client/netmonitor/src/components/response-panel.js
@@ -151,35 +151,34 @@ const ResponsePanel = createClass({
 
     if (json) {
       if (jsonpCallback) {
         sectionName = L10N.getFormatStr("jsonpScopeName", jsonpCallback);
       } else {
         sectionName = JSON_SCOPE_NAME;
       }
       object[sectionName] = json;
-    } else {
-      sectionName = RESPONSE_PAYLOAD;
+    }
 
-      object[sectionName] = {
-        EDITOR_CONFIG: {
-          text,
-          mode: mimeType.replace(/;.+/, ""),
-        },
-      };
-    }
+    // Others like text/html, text/plain, application/javascript
+    object[RESPONSE_PAYLOAD] = {
+      EDITOR_CONFIG: {
+        text,
+        mode: json ? "application/json" : mimeType.replace(/;.+/, ""),
+      },
+    };
 
     return (
       div({ className: "panel-container" },
         error && div({ className: "response-error-header", title: error },
           error
         ),
         PropertiesView({
           object,
           filterPlaceHolder: JSON_FILTER_TEXT,
-          sectionNames: [sectionName],
+          sectionNames: Object.keys(object),
         }),
       )
     );
   }
 });
 
 module.exports = ResponsePanel;
--- a/devtools/client/netmonitor/test/browser_net_content-type.js
+++ b/devtools/client/netmonitor/test/browser_net_content-type.js
@@ -165,17 +165,17 @@ add_task(function* () {
       is(tabpanel.querySelector(".response-error-header") === null,
         true,
         "The response error header doesn't display");
       let jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
       is(jsonView.textContent !== L10N.getStr("jsonScopeName"),
         box != "json",
         "The response json view doesn't display");
       is(tabpanel.querySelector(".CodeMirror-code") === null,
-        box != "textarea",
+        (box !== "textarea" && box !== "json"),
         "The response editor doesn't display");
       is(tabpanel.querySelector(".response-image-box") === null,
         box != "image",
         "The response image view doesn't display");
     }
 
     switch (type) {
       case "xml": {
@@ -203,18 +203,18 @@ add_task(function* () {
 
         is(text, "function() { return 'Hello JS!'; }",
           "The text shown in the source editor is incorrect for the js request.");
         break;
       }
       case "json": {
         checkVisibility("json");
 
-        is(tabpanel.querySelectorAll(".tree-section").length, 1,
-          "There should be 1 tree sections displayed in this tabpanel.");
+        is(tabpanel.querySelectorAll(".tree-section").length, 2,
+          "There should be 2 tree sections displayed in this tabpanel.");
         is(tabpanel.querySelectorAll(".empty-notice").length, 0,
           "The empty notice should not be displayed in this tabpanel.");
 
         is(tabpanel.querySelector(".tree-section .treeLabel").textContent,
           L10N.getStr("jsonScopeName"),
           "The json view section doesn't have the correct title.");
 
         let labels = tabpanel
--- a/devtools/client/netmonitor/test/browser_net_json-b64.js
+++ b/devtools/client/netmonitor/test/browser_net_json-b64.js
@@ -18,37 +18,37 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
-  wait = waitForDOM(document, "#response-panel");
+  wait = waitForDOM(document, "#response-panel .CodeMirror-code");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   yield wait;
 
   let tabpanel = document.querySelector("#response-panel");
 
   is(tabpanel.querySelector(".response-error-header") === null, true,
     "The response error header doesn't have the intended visibility.");
   let jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
   is(jsonView.textContent === L10N.getStr("jsonScopeName"), true,
     "The response json view has the intended visibility.");
-  is(tabpanel.querySelector(".CodeMirror-code") === null, true,
-    "The response editor doesn't have the intended visibility.");
+  is(tabpanel.querySelector(".CodeMirror-code") === null, false,
+    "The response editor has the intended visibility.");
   is(tabpanel.querySelector(".response-image-box") === null, true,
     "The response image box doesn't have the intended visibility.");
 
-  is(tabpanel.querySelectorAll(".tree-section").length, 1,
-    "There should be 1 tree sections displayed in this tabpanel.");
+  is(tabpanel.querySelectorAll(".tree-section").length, 2,
+    "There should be 2 tree sections displayed in this tabpanel.");
   is(tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length, 1,
     "There should be 1 json properties displayed in this tabpanel.");
   is(tabpanel.querySelectorAll(".empty-notice").length, 0,
     "The empty notice should not be displayed in this tabpanel.");
 
   let labels = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
   let values = tabpanel
--- a/devtools/client/netmonitor/test/browser_net_json-long.js
+++ b/devtools/client/netmonitor/test/browser_net_json-long.js
@@ -43,17 +43,17 @@ add_task(function* () {
       statusText: "OK",
       type: "json",
       fullMimeType: "text/json; charset=utf-8",
       size: L10N.getFormatStr("networkMenu.sizeKB",
         L10N.numberWithDecimals(85975 / 1024, 2)),
       time: true
     });
 
-  wait = waitForDOM(document, "#response-panel");
+  wait = waitForDOM(document, "#response-panel .CodeMirror-code");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   yield wait;
 
   testResponseTab();
 
@@ -62,23 +62,23 @@ add_task(function* () {
   function testResponseTab() {
     let tabpanel = document.querySelector("#response-panel");
 
     is(tabpanel.querySelector(".response-error-header") === null, true,
       "The response error header doesn't have the intended visibility.");
     let jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
     is(jsonView.textContent === L10N.getStr("jsonScopeName"), true,
       "The response json view has the intended visibility.");
-    is(tabpanel.querySelector(".CodeMirror-code") === null, true,
-      "The response editor doesn't have the intended visibility.");
+    is(tabpanel.querySelector(".CodeMirror-code") === null, false,
+      "The response editor has the intended visibility.");
     is(tabpanel.querySelector(".response-image-box") === null, true,
       "The response image box doesn't have the intended visibility.");
 
-    is(tabpanel.querySelectorAll(".tree-section").length, 1,
-      "There should be 1 tree sections displayed in this tabpanel.");
+    is(tabpanel.querySelectorAll(".tree-section").length, 2,
+      "There should be 2 tree sections displayed in this tabpanel.");
     is(tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length, 2047,
       "There should be 2047 json properties displayed in this tabpanel.");
     is(tabpanel.querySelectorAll(".empty-notice").length, 0,
       "The empty notice should not be displayed in this tabpanel.");
 
     is(tabpanel.querySelector(".tree-section .treeLabel").textContent,
       L10N.getStr("jsonScopeName"),
       "The json view section doesn't have the correct title.");
--- a/devtools/client/netmonitor/test/browser_net_json-malformed.js
+++ b/devtools/client/netmonitor/test/browser_net_json-malformed.js
@@ -57,17 +57,17 @@ add_task(function* () {
   is(tabpanel.querySelector(".response-error-header").getAttribute("title"),
     "SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data" +
       " at line 1 column 40 of the JSON data",
     "The response error header doesn't have the intended tooltiptext attribute.");
   let jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
   is(jsonView.textContent === L10N.getStr("jsonScopeName"), false,
     "The response json view doesn't have the intended visibility.");
   is(tabpanel.querySelector(".CodeMirror-code") === null, false,
-    "The response editor doesn't have the intended visibility.");
+    "The response editor has the intended visibility.");
   is(tabpanel.querySelector(".response-image-box") === null, true,
     "The response image box doesn't have the intended visibility.");
 
   is(document.querySelector(".CodeMirror-line").textContent,
     "{ \"greeting\": \"Hello malformed JSON!\" },",
     "The text shown in the source editor is incorrect.");
 
   yield teardown(monitor);
--- a/devtools/client/netmonitor/test/browser_net_json-null.js
+++ b/devtools/client/netmonitor/test/browser_net_json-null.js
@@ -22,18 +22,18 @@ add_task(function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   yield openResponsePanel();
   checkResponsePanelDisplaysJSON();
 
   let tabpanel = document.querySelector("#response-panel");
-  is(tabpanel.querySelectorAll(".tree-section").length, 1,
-    "There should be 1 tree sections displayed in this tabpanel.");
+  is(tabpanel.querySelectorAll(".tree-section").length, 2,
+    "There should be 2 tree sections displayed in this tabpanel.");
   is(tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length, 1,
     "There should be 1 json properties displayed in this tabpanel.");
   is(tabpanel.querySelectorAll(".empty-notice").length, 0,
     "The empty notice should not be displayed in this tabpanel.");
 
   let labels = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
   let values = tabpanel
@@ -50,27 +50,27 @@ add_task(function* () {
    */
   function checkResponsePanelDisplaysJSON() {
     let panel = document.querySelector("#response-panel");
     is(panel.querySelector(".response-error-header") === null, true,
       "The response error header doesn't have the intended visibility.");
     let jsonView = panel.querySelector(".tree-section .treeLabel") || {};
     is(jsonView.textContent === L10N.getStr("jsonScopeName"), true,
       "The response json view has the intended visibility.");
-    is(panel.querySelector(".CodeMirror-code") === null, true,
-      "The response editor doesn't have the intended visibility.");
+    is(panel.querySelector(".CodeMirror-code") === null, false,
+      "The response editor has the intended visibility.");
     is(panel.querySelector(".response-image-box") === null, true,
       "The response image box doesn't have the intended visibility.");
   }
 
   /**
    * Open the netmonitor details panel and switch to the response tab.
    * Returns a promise that will resolve when the response panel DOM element is available.
    */
   function openResponsePanel() {
-    let onReponsePanelReady = waitForDOM(document, "#response-panel");
+    let onReponsePanelReady = waitForDOM(document, "#response-panel .CodeMirror-code");
     EventUtils.sendMouseEvent({ type: "click" },
       document.querySelector(".network-details-panel-toggle"));
     EventUtils.sendMouseEvent({ type: "click" },
       document.querySelector("#response-tab"));
     return onReponsePanelReady;
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
@@ -37,17 +37,17 @@ add_task(function* () {
       status: 200,
       statusText: "OK",
       type: "x-bigcorp-json",
       fullMimeType: "text/x-bigcorp-json; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
       time: true
     });
 
-  wait = waitForDOM(document, "#response-panel");
+  wait = waitForDOM(document, "#response-panel .CodeMirror-code");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   yield wait;
 
   testResponseTab();
 
@@ -56,23 +56,23 @@ add_task(function* () {
   function testResponseTab() {
     let tabpanel = document.querySelector("#response-panel");
 
     is(tabpanel.querySelector(".response-error-header") === null, true,
       "The response error header doesn't have the intended visibility.");
     let jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
     is(jsonView.textContent === L10N.getStr("jsonScopeName"), true,
       "The response json view has the intended visibility.");
-    is(tabpanel.querySelector(".CodeMirror-code") === null, true,
-      "The response editor doesn't have the intended visibility.");
+    is(tabpanel.querySelector(".CodeMirror-code") === null, false,
+      "The response editor has the intended visibility.");
     is(tabpanel.querySelector(".response-image-box") === null, true,
       "The response image box doesn't have the intended visibility.");
 
-    is(tabpanel.querySelectorAll(".tree-section").length, 1,
-      "There should be 1 tree sections displayed in this tabpanel.");
+    is(tabpanel.querySelectorAll(".tree-section").length, 2,
+      "There should be 2 tree sections displayed in this tabpanel.");
     is(tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length, 1,
       "There should be 1 json properties displayed in this tabpanel.");
     is(tabpanel.querySelectorAll(".empty-notice").length, 0,
       "The empty notice should not be displayed in this tabpanel.");
 
     let labels = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
     let values = tabpanel
--- a/devtools/client/netmonitor/test/browser_net_json_text_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_text_mime.js
@@ -38,17 +38,17 @@ add_task(function* () {
       status: 200,
       statusText: "OK",
       type: "plain",
       fullMimeType: "text/plain; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
       time: true
     });
 
-  wait = waitForDOM(document, "#response-panel");
+  wait = waitForDOM(document, "#response-panel .CodeMirror-code");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   yield wait;
 
   testResponseTab();
 
@@ -57,23 +57,23 @@ add_task(function* () {
   function testResponseTab() {
     let tabpanel = document.querySelector("#response-panel");
 
     is(tabpanel.querySelector(".response-error-header") === null, true,
       "The response error header doesn't have the intended visibility.");
     let jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
     is(jsonView.textContent === L10N.getStr("jsonScopeName"), true,
       "The response json view has the intended visibility.");
-    is(tabpanel.querySelector(".CodeMirror-code") === null, true,
-      "The response editor doesn't have the intended visibility.");
+    is(tabpanel.querySelector(".CodeMirror-code") === null, false,
+      "The response editor has the intended visibility.");
     is(tabpanel.querySelector(".response-image-box") === null, true,
       "The response image box doesn't have the intended visibility.");
 
-    is(tabpanel.querySelectorAll(".tree-section").length, 1,
-      "There should be 1 tree sections displayed in this tabpanel.");
+    is(tabpanel.querySelectorAll(".tree-section").length, 2,
+      "There should be 2 tree sections displayed in this tabpanel.");
     is(tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length, 1,
       "There should be 1 json properties displayed in this tabpanel.");
     is(tabpanel.querySelectorAll(".empty-notice").length, 0,
       "The empty notice should not be displayed in this tabpanel.");
 
     let labels = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
     let values = tabpanel
--- a/devtools/client/netmonitor/test/browser_net_jsonp.js
+++ b/devtools/client/netmonitor/test/browser_net_jsonp.js
@@ -52,49 +52,52 @@ add_task(function* () {
       status: 200,
       statusText: "OK",
       type: "json",
       fullMimeType: "text/json; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 54),
       time: true
     });
 
-  wait = waitForDOM(document, "#response-panel");
+  info("Testing first request");
+  wait = waitForDOM(document, "#response-panel .CodeMirror-code");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   yield wait;
 
   testResponseTab("$_0123Fun", "Hello JSONP!");
 
-  wait = waitForDOM(document, "#response-panel .tree-section");
+  info("Testing second request");
+  wait = waitForDOM(document, "#response-panel .CodeMirror-code");
+
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[1]);
   yield wait;
 
   testResponseTab("$_4567Sad", "Hello weird JSONP!");
 
   yield teardown(monitor);
 
   function testResponseTab(func, greeting) {
     let tabpanel = document.querySelector("#response-panel");
 
     is(tabpanel.querySelector(".response-error-header") === null, true,
       "The response error header doesn't have the intended visibility.");
     is(tabpanel.querySelector(".tree-section .treeLabel").textContent,
       L10N.getFormatStr("jsonpScopeName", func),
       "The response json view has the intened visibility and correct title.");
-    is(tabpanel.querySelector(".CodeMirror-code") === null, true,
-      "The response editor doesn't have the intended visibility.");
+    is(tabpanel.querySelector(".CodeMirror-code") === null, false,
+      "The response editor has the intended visibility.");
     is(tabpanel.querySelector(".responseImageBox") === null, true,
       "The response image box doesn't have the intended visibility.");
 
-    is(tabpanel.querySelectorAll(".tree-section").length, 1,
-      "There should be 1 tree sections displayed in this tabpanel.");
+    is(tabpanel.querySelectorAll(".tree-section").length, 2,
+      "There should be 2 tree sections displayed in this tabpanel.");
     is(tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length, 1,
       "There should be 1 json properties displayed in this tabpanel.");
     is(tabpanel.querySelectorAll(".empty-notice").length, 0,
       "The empty notice should not be displayed in this tabpanel.");
 
     let labels = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
     let values = tabpanel
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1310,16 +1310,26 @@ nsFrameMessageManager::GetProcessMessage
   *aPMM = nullptr;
   if (mCallback) {
     nsCOMPtr<nsIMessageSender> pmm = mCallback->GetProcessMessageManager();
     pmm.swap(*aPMM);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameMessageManager::GetRemoteType(nsAString& aRemoteType)
+{
+  aRemoteType.Truncate();
+  if (mCallback) {
+    return mCallback->DoGetRemoteType(aRemoteType);
+  }
+  return NS_OK;
+}
+
 namespace {
 
 struct MessageManagerReferentCount
 {
   MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
   size_t mStrong;
   size_t mWeakAlive;
   size_t mWeakDead;
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -87,16 +87,26 @@ public:
     return NS_OK;
   }
 
   virtual nsIMessageSender* GetProcessMessageManager() const
   {
     return nullptr;
   }
 
+  virtual nsresult DoGetRemoteType(nsAString& aRemoteType) const
+  {
+    aRemoteType.Truncate();
+    nsIMessageSender* parent = GetProcessMessageManager();
+    if (parent) {
+      return parent->GetRemoteType(aRemoteType);
+    }
+    return NS_OK;
+  }
+
 protected:
   bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
                                        StructuredCloneData& aData,
                                        ClonedMessageData& aClonedData);
   bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
                                       StructuredCloneData& aData,
                                       ClonedMessageData& aClonedData);
 };
--- a/dom/base/nsIMessageManager.idl
+++ b/dom/base/nsIMessageManager.idl
@@ -289,16 +289,22 @@ interface nsIMessageSender : nsIMessageL
  /**
   * For remote browsers there is always a corresponding process message
   * manager. The intention of this attribute is to link leaf level frame
   * message managers on the parent side with the corresponding process
   * message managers (if there is one). For any other cases this property
   * is null.
   */
   readonly attribute nsIMessageSender processMessageManager;
+
+ /**
+  * For remote browsers, this contains the remoteType of the content child.
+  * Otherwise, it is empty.
+  */
+  readonly attribute AString remoteType;
 };
 
 /**
  * Message "broadcasters" don't have a single "other side" that they
  * send messages to, but rather a set of subordinate message managers.
  * For example, broadcasting a message through a window message
  * manager will broadcast the message to all frame message managers
  * within its window.
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -425,21 +425,32 @@ nsRange::UnregisterCommonAncestor(nsINod
 {
   NS_PRECONDITION(aNode, "bad arg");
   NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node");
   nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
     aNode->GetExistingCommonAncestorRanges();
   MOZ_ASSERT(ranges);
   NS_ASSERTION(ranges->GetEntry(this), "unknown range");
 
+  bool isNativeAnon = aNode->IsInNativeAnonymousSubtree();
+  bool removed = false;
+
   if (ranges->Count() == 1) {
     aNode->ClearCommonAncestorForRangeInSelection();
-    aNode->GetCommonAncestorRangesPtr().reset();
+    if (!isNativeAnon) {
+      // For nodes which are in native anonymous subtrees, we optimize away the
+      // cost of deallocating the hashtable here because we may need to create
+      // it again shortly afterward.  We don't do this for all nodes in order
+      // to avoid the additional memory usage unconditionally.
+      aNode->GetCommonAncestorRangesPtr().reset();
+      removed = true;
+    }
     UnmarkDescendants(aNode);
-  } else {
+  }
+  if (!removed) {
     ranges->RemoveEntry(this);
   }
 }
 
 /******************************************************
  * nsIMutationObserver implementation
  ******************************************************/
 
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -9,16 +9,17 @@ support-files =
 [test_bug1285128.html]
 [test_bug1293174_implicit_pointer_capture_for_touch_1.html]
   support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
 [test_bug1293174_implicit_pointer_capture_for_touch_2.html]
   support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
 [test_bug1303704.html]
 [test_bug1315862.html]
 [test_bug1323158.html]
+  disabled = disabled # Bug 1389086 - Intermittent failures
 [test_empty_file.html]
   disabled = disabled # Bug 1150091 - Issue with support-files
 [test_pointerevent_attributes_hoverable_pointers-manual.html]
   support-files =
     pointerevent_attributes_hoverable_pointers-manual.html
     ./resources/pointerevent_attributes_hoverable_pointers-iframe.html
 [test_pointerevent_attributes_nohover_pointers-manual.html]
   support-files =
--- a/dom/indexedDB/moz.build
+++ b/dom/indexedDB/moz.build
@@ -2,18 +2,16 @@
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM: IndexedDB")
 
-TEST_DIRS += ['test/extensions']
-
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
 XPCSHELL_TESTS_MANIFESTS += [
     'test/unit/xpcshell-child-process.ini',
--- a/dom/indexedDB/test/chrome.ini
+++ b/dom/indexedDB/test/chrome.ini
@@ -1,5 +1,4 @@
 [DEFAULT]
 support-files = chromeHelpers.js
 
 [test_globalObjects_chrome.xul]
-[test_globalObjects_other.xul]
deleted file mode 100644
--- a/dom/indexedDB/test/extensions/bootstrap.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-function testForExpectedSymbols(stage, data) {
-  const expectedSymbols = [ "IDBKeyRange", "indexedDB" ];
-  for (var symbol of expectedSymbols) {
-    Services.prefs.setBoolPref("indexeddbtest.bootstrap." + stage + "." +
-                               symbol, symbol in this);
-  }
-}
-
-function GlobalObjectsComponent() {
-  this.wrappedJSObject = this;
-}
-
-GlobalObjectsComponent.prototype =
-{
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
-
-  runTest() {
-    const name = "Splendid Test";
-
-    let ok = this.ok;
-    let finishTest = this.finishTest;
-
-    let keyRange = IDBKeyRange.only(42);
-    ok(keyRange, "Got keyRange");
-
-    let request = indexedDB.open(name, 1);
-    request.onerror = function(event) {
-      ok(false, "indexedDB error, '" + event.target.error.name + "'");
-      finishTest();
-    }
-    request.onsuccess = function(event) {
-      let db = event.target.result;
-      ok(db, "Got database");
-      finishTest();
-    }
-  }
-};
-
-var gFactory = {
-  register() {
-    var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-
-    var classID = Components.ID("{d6f85dcb-537d-447e-b783-75d4b405622d}");
-    var description = "IndexedDBTest";
-    var contractID = "@mozilla.org/dom/indexeddb/GlobalObjectsComponent;1";
-    var factory = XPCOMUtils._getFactory(GlobalObjectsComponent);
-
-    registrar.registerFactory(classID, description, contractID, factory);
-
-    this.unregister = function() {
-      registrar.unregisterFactory(classID, factory);
-      delete this.unregister;
-    };
-  }
-};
-
-function install(data, reason) {
-  testForExpectedSymbols("install");
-}
-
-function startup(data, reason) {
-  testForExpectedSymbols("startup");
-  gFactory.register();
-}
-
-function shutdown(data, reason) {
-  testForExpectedSymbols("shutdown");
-  gFactory.unregister();
-}
-
-function uninstall(data, reason) {
-  testForExpectedSymbols("uninstall");
-}
deleted file mode 100644
index bbe2430e261f95e33adc1b970b2a6e53db627bfd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/dom/indexedDB/test/extensions/install.rdf
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:name>IndexedDBTest</em:name>
-    <em:description>IndexedDB functions for use in testing.</em:description>
-    <em:creator>Mozilla</em:creator>
-    <em:version>2016.03.09</em:version>
-    <em:id>indexedDB-test@mozilla.org</em:id>
-    <em:type>2</em:type>
-    <em:bootstrap>true</em:bootstrap>
-    <em:targetApplication>
-      <Description>
-        <!-- Firefox -->
-        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-        <em:minVersion>45.0</em:minVersion>
-        <em:maxVersion>*</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-    <em:targetApplication>
-      <Description>
-        <!-- Fennec -->
-        <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
-        <em:minVersion>45.0</em:minVersion>
-        <em:maxVersion>*</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-  </Description>
-</RDF>
deleted file mode 100644
--- a/dom/indexedDB/test/extensions/moz.build
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- 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/.
-
-XPI_NAME = 'indexedDB'
-
-FINAL_TARGET_FILES += [
-    'bootstrap.js',
-    'install.rdf',
-]
-
-TEST_HARNESS_FILES.testing.mochitest.extensions += [
-    'indexedDB-test@mozilla.org.xpi',
-]
deleted file mode 100644
--- a/dom/indexedDB/test/test_globalObjects_other.xul
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
-<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<window title="Mozilla Bug 832883"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        onload="runTest();">
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-
-  <script type="application/javascript">
-  <![CDATA[
-  function* testSteps() {
-    // Test for IDBKeyRange and indexedDB availability in bootstrap files.
-    let test = Cc["@mozilla.org/dom/indexeddb/GlobalObjectsComponent;1"].
-               createInstance(Ci.nsISupports).wrappedJSObject;
-    test.ok = ok;
-    test.finishTest = continueToNextStep;
-    test.runTest();
-    yield undefined;
-
-    Cu.import("resource://gre/modules/AddonManager.jsm");
-    AddonManager.getAddonByID("indexedDB-test@mozilla.org",
-                              grabEventAndContinueHandler);
-    let addon = yield undefined;
-    addon.uninstall();
-
-    Cu.import("resource://gre/modules/Services.jsm");
-    for (var stage of [ "install", "startup", "shutdown", "uninstall" ]) {
-      for (var symbol of [ "IDBKeyRange", "indexedDB" ]) {
-        let pref = Services.prefs.getBoolPref("indexeddbtest.bootstrap." + stage +
-                                              "." + symbol, false);
-        ok(pref, "Symbol '" + symbol + "' present during '" + stage + "'");
-      }
-    }
-
-    finishTest();
-    yield undefined;
-  }
-
-  window.runTest = function() {
-    SimpleTest.waitForExplicitFinish();
-
-    testGenerator.next();
-  }
-  ]]>
-  </script>
-
-  <script type="text/javascript" src="chromeHelpers.js"></script>
-
-  <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=832883"
-     target="_blank">Mozilla Bug 832883</a>
-  </body>
-</window>
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2665,16 +2665,17 @@ NS_IMPL_CYCLE_COLLECTION_0(ContentParent
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentParent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentParent)
   NS_INTERFACE_MAP_ENTRY(nsIContentParent)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback)
   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionErrorCallback)
+  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMETHODIMP
 ContentParent::Observe(nsISupports* aSubject,
                        const char* aTopic,
                        const char16_t* aData)
 {
@@ -2846,16 +2847,30 @@ ContentParent::Observe(nsISupports* aSub
     } else if ((!nsCRT::strcmp(aData, u"added")) ||
                (!nsCRT::strcmp(aData, u"changed"))) {
       cs->AddCookie(xpcCookie);
     }
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+ContentParent::GetInterface(const nsIID& aIID, void** aResult)
+{
+  NS_ENSURE_ARG_POINTER(aResult);
+
+  if (aIID.Equals(NS_GET_IID(nsIMessageSender))) {
+    nsCOMPtr<nsIMessageSender> mm = GetMessageManager();
+    mm.forget(aResult);
+    return NS_OK;
+  }
+
+  return NS_NOINTERFACE;
+}
+
 mozilla::ipc::IPCResult
 ContentParent::RecvInitBackground(Endpoint<PBackgroundParent>&& aEndpoint)
 {
   if (!BackgroundParent::Alloc(this, Move(aEndpoint))) {
     return IPC_FAIL(this, "BackgroundParent::Alloc failed");
   }
 
   return IPC_OK();
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -21,16 +21,17 @@
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 
 #include "nsDataHashtable.h"
 #include "nsPluginTags.h"
 #include "nsFrameMessageManager.h"
 #include "nsHashKeys.h"
+#include "nsIInterfaceRequestor.h"
 #include "nsIObserver.h"
 #include "nsIThreadInternal.h"
 #include "nsIDOMGeoPositionCallback.h"
 #include "nsIDOMGeoPositionErrorCallback.h"
 #include "nsRefPtrHashtable.h"
 #include "PermissionMessageUtils.h"
 #include "DriverCrashGuard.h"
 
@@ -104,16 +105,17 @@ class ContentBridgeParent;
 class GetFilesHelper;
 class MemoryReportRequestHost;
 
 class ContentParent final : public PContentParent
                           , public nsIContentParent
                           , public nsIObserver
                           , public nsIDOMGeoPositionCallback
                           , public nsIDOMGeoPositionErrorCallback
+                          , public nsIInterfaceRequestor
                           , public gfx::gfxVarReceiver
                           , public mozilla::LinkedListElement<ContentParent>
                           , public gfx::GPUProcessListener
                           , public mozilla::MemoryReportingProcess
 {
   typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
   typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
   typedef mozilla::ipc::PFileDescriptorSetParent PFileDescriptorSetParent;
@@ -193,16 +195,22 @@ public:
                 uint64_t aNextTabParentId);
 
   static void GetAll(nsTArray<ContentParent*>& aArray);
 
   static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
 
   const nsAString& GetRemoteType() const;
 
+  virtual nsresult DoGetRemoteType(nsAString& aRemoteType) const override
+  {
+    aRemoteType = GetRemoteType();
+    return NS_OK;
+  }
+
   enum CPIteratorPolicy {
     eLive,
     eAll
   };
 
   class ContentParentIterator {
   private:
     ContentParent* mCurrent;
@@ -305,16 +313,17 @@ public:
                                                        nsresult* aRv) override;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIDOMGEOPOSITIONCALLBACK
   NS_DECL_NSIDOMGEOPOSITIONERRORCALLBACK
+  NS_DECL_NSIINTERFACEREQUESTOR
 
   /**
    * MessageManagerCallback methods that we override.
    */
   virtual bool DoLoadMessageManagerScript(const nsAString& aURL,
                                           bool aRunInGlobalScope) override;
 
   virtual nsresult DoSendAsyncMessage(JSContext* aCx,
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MediaEngineCameraVideoSource_h
 #define MediaEngineCameraVideoSource_h
 
 #include "MediaEngine.h"
 
 #include "nsDirectoryServiceDefs.h"
+#include "mozilla/Unused.h"
 
 // conflicts with #include of scoped_ptr.h
 #undef FF
 // Avoid warnings about redefinition of WARN_UNUSED_RESULT
 #include "ipc/IPCMessageUtils.h"
 
 // WebRTC includes
 #include "webrtc/modules/video_capture/video_capture_defines.h"
@@ -55,16 +56,30 @@ public:
   {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   uint32_t GetBestFitnessDistance(
       const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
       const nsString& aDeviceId) const override;
 
+  void Shutdown() override
+  {
+    MonitorAutoLock lock(mMonitor);
+    // Release mImage and it's resources just in case -- also we can be
+    // held by something in a CC chain, and not be deleted until final-cc,
+    // which is too late for releasing images.  (This should be null'd on
+    // Stop(), but apparently Stop() may not get called in this case
+    // somehow.) (Bug 1374164)
+
+    Unused << NS_WARN_IF(mImage);
+
+    mImage = nullptr;
+  }
+
 protected:
   struct CapabilityCandidate {
     explicit CapabilityCandidate(uint8_t index, uint32_t distance = 0)
     : mIndex(index), mDistance(distance) {}
 
     size_t mIndex;
     uint32_t mDistance;
   };
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -1533,29 +1533,41 @@ HTMLEditor::RelativeFontChangeHelper(int
   if (aSizeChange != 1 && aSizeChange != -1) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   // If this is a font node with size, put big/small inside it.
   if (aNode->IsHTMLElement(nsGkAtoms::font) &&
       aNode->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::size)) {
     // Cycle through children and adjust relative font size.
-    for (uint32_t i = aNode->GetChildCount(); i--; ) {
-      nsresult rv = RelativeFontChangeOnNode(aSizeChange, aNode->GetChildAt(i));
+    AutoTArray<nsCOMPtr<nsIContent>, 10> childList;
+    for (nsIContent* child = aNode->GetFirstChild();
+         child; child = child->GetNextSibling()) {
+      childList.AppendElement(child);
+    }
+
+    for (const auto& child: childList) {
+      nsresult rv = RelativeFontChangeOnNode(aSizeChange, child);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // RelativeFontChangeOnNode already calls us recursively,
     // so we don't need to check our children again.
     return NS_OK;
   }
 
   // Otherwise cycle through the children.
-  for (uint32_t i = aNode->GetChildCount(); i--; ) {
-    nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode->GetChildAt(i));
+  AutoTArray<nsCOMPtr<nsIContent>, 10> childList;
+  for (nsIContent* child = aNode->GetFirstChild();
+       child; child = child->GetNextSibling()) {
+    childList.AppendElement(child);
+  }
+
+  for (const auto& child: childList) {
+    nsresult rv = RelativeFontChangeHelper(aSizeChange, child);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::RelativeFontChangeOnNode(int32_t aSizeChange,
@@ -1611,18 +1623,24 @@ HTMLEditor::RelativeFontChangeOnNode(int
 
     return NS_OK;
   }
 
   // none of the above?  then cycle through the children.
   // MOOSE: we should group the children together if possible
   // into a single "big" or "small".  For the moment they are
   // each getting their own.
-  for (uint32_t i = aNode->GetChildCount(); i--; ) {
-    nsresult rv = RelativeFontChangeOnNode(aSizeChange, aNode->GetChildAt(i));
+  AutoTArray<nsCOMPtr<nsIContent>, 10> childList;
+  for (nsIContent* child = aNode->GetFirstChild();
+       child; child = child->GetNextSibling()) {
+    childList.AppendElement(child);
+  }
+
+  for (const auto& child: childList) {
+    nsresult rv = RelativeFontChangeOnNode(aSizeChange, child);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::GetFontFaceState(bool* aMixed,
--- a/editor/libeditor/SelectionState.cpp
+++ b/editor/libeditor/SelectionState.cpp
@@ -642,20 +642,16 @@ RangeUpdater::DidMoveNode(nsINode* aOldP
 RangeItem::RangeItem()
 {
 }
 
 RangeItem::~RangeItem()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION(RangeItem, mStartContainer, mEndContainer)
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(RangeItem, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(RangeItem, Release)
-
 void
 RangeItem::StoreRange(nsRange* aRange)
 {
   MOZ_ASSERT(aRange);
   mStartContainer = aRange->GetStartContainer();
   mStartOffset = aRange->StartOffset();
   mEndContainer = aRange->GetEndContainer();
   mEndOffset = aRange->EndOffset();
@@ -667,9 +663,23 @@ RangeItem::GetRange()
   RefPtr<nsRange> range = new nsRange(mStartContainer);
   if (NS_FAILED(range->SetStartAndEnd(mStartContainer, mStartOffset,
                                       mEndContainer, mEndOffset))) {
     return nullptr;
   }
   return range.forget();
 }
 
+void
+RangeItem::Unlink()
+{
+  ImplCycleCollectionUnlink(mStartContainer);
+  ImplCycleCollectionUnlink(mEndContainer);
+}
+
+void
+RangeItem::Traverse(nsCycleCollectionTraversalCallback& aCallback, uint32_t aFlags)
+{
+  CycleCollectionNoteChild(aCallback, mStartContainer.get(), "mStartContainer", aFlags);
+  CycleCollectionNoteChild(aCallback, mEndContainer.get(), "mEndContainer", aFlags);
+}
+
 } // namespace mozilla
--- a/editor/libeditor/SelectionState.h
+++ b/editor/libeditor/SelectionState.h
@@ -32,25 +32,57 @@ struct RangeItem final
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~RangeItem();
 
 public:
   void StoreRange(nsRange* aRange);
   already_AddRefed<nsRange> GetRange();
 
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(RangeItem)
-  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(RangeItem)
+  NS_INLINE_DECL_REFCOUNTING(RangeItem)
+
+  void Unlink();
+  void Traverse(nsCycleCollectionTraversalCallback& aCallback, uint32_t aFlags);
 
   nsCOMPtr<nsINode> mStartContainer;
   int32_t mStartOffset;
   nsCOMPtr<nsINode> mEndContainer;
   int32_t mEndOffset;
 };
 
+inline void
+ImplCycleCollectionUnlink(RangeItem& aItem)
+{
+  aItem.Unlink();
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            RangeItem& aItem,
+                            const char* aName,
+                            uint32_t aFlags = 0)
+{
+  aItem.Traverse(aCallback, aFlags);
+}
+
+inline void
+ImplCycleCollectionUnlink(RefPtr<RangeItem>& aItem)
+{
+  aItem->Unlink();
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            RefPtr<RangeItem>& aItem,
+                            const char* aName,
+                            uint32_t aFlags = 0)
+{
+  aItem->Traverse(aCallback, aFlags);
+}
+
 /**
  * mozilla::SelectionState
  *
  * Class for recording selection info.  Stores selection as collection of
  * { {startnode, startoffset} , {endnode, endoffset} } tuples.  Can't store
  * ranges since dom gravity will possibly change the ranges.
  */
 
--- a/gfx/2d/DrawTargetCapture.cpp
+++ b/gfx/2d/DrawTargetCapture.cpp
@@ -26,31 +26,30 @@ DrawTargetCaptureImpl::~DrawTargetCaptur
 DrawTargetCaptureImpl::DrawTargetCaptureImpl(BackendType aBackend,
                                              const IntSize& aSize,
                                              SurfaceFormat aFormat)
   : mSize(aSize)
 {
   RefPtr<DrawTarget> screenRefDT =
       gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
 
+  mFormat = aFormat;
   if (aBackend == screenRefDT->GetBackendType()) {
     mRefDT = screenRefDT;
   } else {
     // If you got here, we have to create a new ref DT to create
     // backend specific assets like paths / gradients. Try to
     // create the same backend type as the screen ref dt.
     gfxWarning() << "Creating a RefDT in DrawTargetCapture.";
 
     // Create a 1x1 size ref dt to create assets
     // If we have to snapshot, we'll just create the real DT
     IntSize size(1, 1);
     mRefDT = Factory::CreateDrawTarget(aBackend, size, mFormat);
   }
-
-  mFormat = aFormat;
 }
 
 bool
 DrawTargetCaptureImpl::Init(const IntSize& aSize, DrawTarget* aRefDT)
 {
   if (!aRefDT) {
     return false;
   }
--- a/gfx/2d/DrawTargetTiled.cpp
+++ b/gfx/2d/DrawTargetTiled.cpp
@@ -36,18 +36,18 @@ DrawTargetTiled::Init(const TileSet& aTi
     uint32_t newXMost = max(mRect.XMost(),
                             mTiles[i].mTileOrigin.x + mTiles[i].mDrawTarget->GetSize().width);
     uint32_t newYMost = max(mRect.YMost(),
                             mTiles[i].mTileOrigin.y + mTiles[i].mDrawTarget->GetSize().height);
     mRect.x = min(mRect.x, mTiles[i].mTileOrigin.x);
     mRect.y = min(mRect.y, mTiles[i].mTileOrigin.y);
     mRect.width = newXMost - mRect.x;
     mRect.height = newYMost - mRect.y;
-    mTiles[i].mDrawTarget->SetTransform(Matrix::Translation(mTiles[i].mTileOrigin.x,
-                                                            mTiles[i].mTileOrigin.y));
+    mTiles[i].mDrawTarget->SetTransform(Matrix::Translation(-mTiles[i].mTileOrigin.x,
+                                                            -mTiles[i].mTileOrigin.y));
   }
   mFormat = mTiles[0].mDrawTarget->GetFormat();
   SetPermitSubpixelAA(IsOpaque(mFormat));
   return true;
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetTiled::Snapshot()
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -403,16 +403,25 @@ ShouldLimitDeviceResets(uint32_t count, 
   } else if (hasCountLimit) {
     return triggeredCount;
   }
 
   return false;
 }
 
 void
+GPUProcessManager::ResetCompositors()
+{
+  // Note: this will recreate devices in addition to recreating compositors.
+  // This isn't optimal, but this is only used on linux where acceleration
+  // isn't enabled by default, and this way we don't need a new code path.
+  SimulateDeviceReset();
+}
+
+void
 GPUProcessManager::SimulateDeviceReset()
 {
   // Make sure we rebuild environment and configuration for accelerated features.
   gfxPlatform::GetPlatform()->CompositorUpdated();
 
   if (mProcess) {
     OnRemoteProcessDeviceReset(mProcess);
   } else {
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -138,16 +138,19 @@ public:
   //
   // Note that a layer tree id is always allocated, even if this returns false.
   bool AllocateAndConnectLayerTreeId(
     PCompositorBridgeChild* aCompositorBridge,
     base::ProcessId aOtherPid,
     uint64_t* aOutLayersId,
     CompositorOptions* aOutCompositorOptions);
 
+  // Destroy and recreate all of the compositors
+  void ResetCompositors();
+
   void OnProcessLaunchComplete(GPUProcessHost* aHost) override;
   void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override;
   void SimulateDeviceReset();
   void DisableWebRender();
   void OnInProcessDeviceReset();
   void OnRemoteProcessDeviceReset(GPUProcessHost* aHost) override;
   void NotifyListenersOnCompositeDeviceReset();
 
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PaintThread.h"
 
 #include "base/task.h"
 #include "gfxPrefs.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/SyncObject.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SyncRunnable.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
@@ -165,54 +167,85 @@ PaintThread::PaintContentsAsync(Composit
     mDrawTargetsToFlush.AppendElement(target);
   }
 }
 
 void
 PaintThread::FinishedLayerBatch()
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  RefPtr<PaintThread> self = this;
+  RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::EndAsyncPaintingLayer",
+  [self]() -> void
+  {
+    self->EndAsyncPaintingLayer();
+  });
+
+  if (!gfxPrefs::LayersOMTPForceSync()) {
+    sThread->Dispatch(task.forget());
+  } else {
+    SyncRunnable::DispatchToThread(sThread, task);
+  }
+}
+
+void
+PaintThread::EndAsyncPaintingLayer()
+{
+  MOZ_ASSERT(IsOnPaintThread());
+  // Textureclient forces a flush once we "end paint", so
+  // users of this texture expect all the drawing to be complete.
+  // Force a flush now.
+  for (size_t i = 0; i < mDrawTargetsToFlush.Length(); i++) {
+    mDrawTargetsToFlush[i]->Flush();
+  }
+
+  mDrawTargetsToFlush.Clear();
+}
+
+void
+PaintThread::SynchronizePaintTextures(SyncObjectClient* aSyncObject)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aSyncObject);
+
   RefPtr<CompositorBridgeChild> cbc;
   if (!gfxPrefs::LayersOMTPForceSync()) {
     cbc = CompositorBridgeChild::Get();
   }
 
+  RefPtr<SyncObjectClient> syncObject(aSyncObject);
   RefPtr<PaintThread> self = this;
-  RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::EndAsyncPainting",
-  [self, cbc]() -> void
+  RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::SyncTextureData",
+    [self, cbc, syncObject]() -> void
   {
-    self->EndAsyncPainting(cbc);
+    self->SyncTextureData(cbc, syncObject);
   });
 
   if (cbc) {
     sThread->Dispatch(task.forget());
   } else {
     SyncRunnable::DispatchToThread(sThread, task);
   }
 }
 
 void
-PaintThread::EndAsyncPainting(CompositorBridgeChild* aBridge)
+PaintThread::SyncTextureData(CompositorBridgeChild* aBridge,
+                             SyncObjectClient* aSyncObject)
 {
   MOZ_ASSERT(IsOnPaintThread());
-  // Textureclient forces a flush once we "end paint", so
-  // users of this texture expect all the drawing to be complete.
-  // Force a flush now.
-  for (size_t i = 0; i < mDrawTargetsToFlush.Length(); i++) {
-    mDrawTargetsToFlush[i]->Flush();
-  }
+  MOZ_ASSERT(aSyncObject);
 
-  mDrawTargetsToFlush.Clear();
+  aSyncObject->Synchronize();
 
   if (aBridge) {
-    aBridge->NotifyFinishedAsyncPaintLayer();
+    aBridge->NotifyFinishedAsyncPaintTransaction();
   }
 }
 
-
 void
 PaintThread::PaintContents(CapturedPaintState* aState,
                            PrepDrawTargetForPaintingCallback aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aState);
 
   // If painting asynchronously, we need to acquire the compositor bridge which
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -70,16 +70,24 @@ public:
   void PaintContents(CapturedPaintState* aState,
                      PrepDrawTargetForPaintingCallback aCallback);
 
   // To be called on the main thread. Signifies that the current
   // batch of CapturedPaintStates* for PaintContents have been recorded
   // and the main thread is finished recording this layer.
   void FinishedLayerBatch();
 
+  // Must be called on the main thread. Tells the paint thread
+  // to schedule a sync textures after all async paints are done.
+  // NOTE: The other paint thread functions are on a per PAINT
+  // or per paint layer basis. This MUST be called at the end
+  // of a layer transaction as multiple paints can occur
+  // with multiple layers. We only have to do this once per transaction.
+  void SynchronizePaintTextures(SyncObjectClient* aSyncObject);
+
   // Sync Runnables need threads to be ref counted,
   // But this thread lives through the whole process.
   // We're only temporarily using sync runnables so
   // Override release/addref but don't do anything.
   void Release();
   void AddRef();
 
   // Helper for asserts.
@@ -87,17 +95,19 @@ public:
 
 private:
   bool Init();
   void ShutdownOnPaintThread();
   void InitOnPaintThread();
   void PaintContentsAsync(CompositorBridgeChild* aBridge,
                           CapturedPaintState* aState,
                           PrepDrawTargetForPaintingCallback aCallback);
-  void EndAsyncPainting(CompositorBridgeChild* aBridge);
+  void EndAsyncPaintingLayer();
+  void SyncTextureData(CompositorBridgeChild* aBridge,
+                       SyncObjectClient* aSyncObject);
 
   static StaticAutoPtr<PaintThread> sSingleton;
   static StaticRefPtr<nsIThread> sThread;
   static PlatformThreadId sThreadId;
 
   // This shouldn't be very many elements, so a list should be fine.
   // Should only be accessed on the paint thread.
   nsTArray<RefPtr<gfx::DrawTarget>> mDrawTargetsToFlush;
--- a/gfx/layers/apz/testutil/APZTestData.h
+++ b/gfx/layers/apz/testutil/APZTestData.h
@@ -81,17 +81,16 @@ private:
   DataStore mPaints;
   DataStore mRepaintRequests;
 
   void LogTestDataImpl(DataStore& aDataStore,
                        SequenceNumber aSequenceNumber,
                        ViewID aScrollId,
                        const std::string& aKey,
                        const std::string& aValue) {
-    MOZ_ASSERT(gfxPrefs::APZTestLoggingEnabled(), "don't call me");
     auto bucketIterator = aDataStore.find(aSequenceNumber);
     if (bucketIterator == aDataStore.end()) {
       MOZ_ASSERT(false, "LogTestDataImpl called with nonexistent sequence number");
       return;
     }
     Bucket& bucket = bucketIterator->second;
     ScrollFrameData& scrollFrameData = bucket[aScrollId];  // create if doesn't exist
     MOZ_ASSERT(scrollFrameData.find(aKey) == scrollFrameData.end()
@@ -100,18 +99,19 @@ private:
   }
 };
 
 // A helper class for logging data for a paint.
 class APZPaintLogHelper {
 public:
   APZPaintLogHelper(APZTestData* aTestData, SequenceNumber aPaintSequenceNumber)
     : mTestData(aTestData),
-      mPaintSequenceNumber(aPaintSequenceNumber)
-  {}
+      mPaintSequenceNumber(aPaintSequenceNumber) {
+    MOZ_ASSERT(!aTestData || gfxPrefs::APZTestLoggingEnabled(), "don't call me");
+  }
 
   template <typename Value>
   void LogTestData(FrameMetrics::ViewID aScrollId,
                    const std::string& aKey,
                    const Value& aValue) const {
     if (mTestData) {  // avoid stringifying if mTestData == nullptr
       LogTestData(aScrollId, aKey, ToString(aValue));
     }
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -95,16 +95,17 @@ ClientLayerManager::ClientLayerManager(n
   , mLatestTransactionId(0)
   , mLastPaintTime(TimeDuration::Forever())
   , mTargetRotation(ROTATION_0)
   , mRepeatTransaction(false)
   , mIsRepeatTransaction(false)
   , mTransactionIncomplete(false)
   , mCompositorMightResample(false)
   , mNeedsComposite(false)
+  , mTextureSyncOnPaintThread(false)
   , mPaintSequenceNumber(0)
   , mDeviceResetSequenceNumber(0)
   , mForwarder(new ShadowLayerForwarder(this))
 {
   MOZ_COUNT_CTOR(ClientLayerManager);
   mMemoryPressureObserver = new MemoryPressureObserver(this);
 
   if (XRE_IsContentProcess()) {
@@ -353,16 +354,17 @@ ClientLayerManager::EndTransactionIntern
 #endif
 
   NS_ASSERTION(InConstruction(), "Should be in construction phase");
   mPhase = PHASE_DRAWING;
 
   ClientLayer* root = ClientLayer::ToClientLayer(GetRoot());
 
   mTransactionIncomplete = false;
+  mTextureSyncOnPaintThread = false;
 
   // Apply pending tree updates before recomputing effective
   // properties.
   GetRoot()->ApplyPendingUpdatesToSubtree();
 
   mPaintedLayerCallback = aCallback;
   mPaintedLayerCallbackData = aCallbackData;
 
@@ -720,21 +722,27 @@ ClientLayerManager::StopFrameTimeRecordi
 
 void
 ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
 {
   AutoProfilerTracing tracing("Paint", "ForwardTransaction");
   TimeStamp start = TimeStamp::Now();
 
   // Skip the synchronization for buffer since we also skip the painting during
-  // device-reset status.
+  // device-reset status. With OMTP, we have to wait for async paints
+  // before we synchronize and it's done on the paint thread.
   if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
     if (mForwarder->GetSyncObject() &&
         mForwarder->GetSyncObject()->IsSyncObjectValid()) {
-      mForwarder->GetSyncObject()->Synchronize();
+      if (mTextureSyncOnPaintThread) {
+        // We have to wait for all async paints to finish to do this
+        PaintThread::Get()->SynchronizePaintTextures(mForwarder->GetSyncObject());
+      } else {
+        mForwarder->GetSyncObject()->Synchronize();
+      }
     }
   }
 
   mPhase = PHASE_FORWARD;
 
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(!mIsRepeatTransaction);
   TimeStamp transactionStart;
   if (!mTransactionIdAllocator->GetTransactionStart().IsNull()) {
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -12,16 +12,17 @@
 #include "gfxPrefs.h"
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/LinkedList.h"         // For LinkedList
 #include "mozilla/WidgetUtils.h"        // for ScreenRotation
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/FocusTarget.h"  // for FocusTarget
 #include "mozilla/layers/LayersTypes.h"  // for BufferMode, LayersBackend, etc
+#include "mozilla/layers/PaintThread.h" // For PaintThread
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowLayerForwarder, etc
 #include "mozilla/layers/APZTestData.h" // for APZTestData
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsIObserver.h"                // for nsIObserver
 #include "nsISupportsImpl.h"            // for Layer::Release, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsTArray.h"                   // for nsTArray
 #include "nscore.h"                     // for nsAString
@@ -153,16 +154,17 @@ public:
   void HandleMemoryPressure();
 
   void SetRepeatTransaction() { mRepeatTransaction = true; }
   bool GetRepeatTransaction() { return mRepeatTransaction; }
 
   bool IsRepeatTransaction() { return mIsRepeatTransaction; }
 
   void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+  void SetNeedTextureSyncOnPaintThread() { mTextureSyncOnPaintThread = true; }
 
   bool HasShadowTarget() { return !!mShadowTarget; }
 
   void SetShadowTarget(gfxContext* aTarget) { mShadowTarget = aTarget; }
 
   bool CompositorMightResample() { return mCompositorMightResample; } 
   
   DrawPaintedLayerCallback GetPaintedLayerCallback() const
@@ -344,16 +346,17 @@ private:
 
   // Used to repeat the transaction right away (to avoid rebuilding
   // a display list) to support progressive drawing.
   bool mRepeatTransaction;
   bool mIsRepeatTransaction;
   bool mTransactionIncomplete;
   bool mCompositorMightResample;
   bool mNeedsComposite;
+  bool mTextureSyncOnPaintThread;
 
   // An incrementing sequence number for paints.
   // Incremented in BeginTransaction(), but not for repeat transactions.
   uint32_t mPaintSequenceNumber;
 
   // A sequence number for checking whether we have not yet acknowledged
   // a device reset.
   uint64_t mDeviceResetSequenceNumber;
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -263,16 +263,17 @@ ClientPaintedLayer::PaintOffMainThread()
     didUpdate = true;
   }
 
   PaintThread::Get()->FinishedLayerBatch();
   mContentClient->EndPaint(nullptr);
 
   if (didUpdate) {
     UpdateContentClient(state);
+    ClientManager()->SetNeedTextureSyncOnPaintThread();
   }
   return true;
 }
 
 void
 ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
 {
   RenderMaskLayers(this);
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -1618,16 +1618,17 @@ SyncObjectD3D11Host::Synchronize()
     gfxCriticalNote << "GFX: AL_D3D11 abandoned sync";
   }
 
   return true;
 }
 
 SyncObjectD3D11Client::SyncObjectD3D11Client(SyncHandle aSyncHandle, ID3D11Device* aDevice)
  : mSyncHandle(aSyncHandle)
+ , mSyncLock("SyncObjectD3D11")
 {
   if (!aDevice) {
     mDevice = DeviceManagerDx::Get()->GetContentDevice();
     return;
   }
 
   mDevice = aDevice;
 }
@@ -1673,19 +1674,29 @@ SyncObjectD3D11Client::IsSyncObjectValid
 {
   RefPtr<ID3D11Device> dev = DeviceManagerDx::Get()->GetContentDevice();
   if (!dev || (NS_IsMainThread() && dev != mDevice)) {
     return false;
   }
   return true;
 }
 
+// We have only 1 sync object. As a thing that somehow works,
+// we copy each of the textures that need to be synced with the compositor
+// into our sync object and only use a lock for this sync object.
+// This way, we don't have to sync every texture we send to the compositor.
+// We only have to do this once per transaction.
 void
 SyncObjectD3D11Client::Synchronize()
 {
+  // Since this can be called from either the Paint or Main thread.
+  // We don't want this to race since we initialize the sync texture here
+  // too.
+  MutexAutoLock syncLock(mSyncLock);
+
   if (!mSyncedTextures.size()) {
     return;
   }
   if (!Init()) {
     return;
   }
 
   HRESULT hr;
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -479,16 +479,17 @@ public:
 private:
   bool Init();
 
   SyncHandle mSyncHandle;
   RefPtr<ID3D11Device> mDevice;
   RefPtr<ID3D11Texture2D> mSyncTexture;
   RefPtr<IDXGIKeyedMutex> mKeyedMutex;
   std::vector<ID3D11Texture2D*> mSyncedTextures;
+  Mutex mSyncLock;
 };
 
 inline uint32_t GetMaxTextureSizeForFeatureLevel(D3D_FEATURE_LEVEL aFeatureLevel)
 {
   int32_t maxTextureSize;
   switch (aFeatureLevel) {
   case D3D_FEATURE_LEVEL_11_1:
   case D3D_FEATURE_LEVEL_11_0:
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -1184,25 +1184,28 @@ CompositorBridgeChild::NotifyFinishedAsy
   aState->mTextureClient = nullptr;
   if (aState->mTextureClientOnWhite) {
     aState->mTextureClientOnWhite->DropPaintThreadRef();
     aState->mTextureClientOnWhite = nullptr;
   }
 }
 
 void
-CompositorBridgeChild::NotifyFinishedAsyncPaintLayer()
+CompositorBridgeChild::NotifyFinishedAsyncPaintTransaction()
 {
   MOZ_ASSERT(PaintThread::IsOnPaintThread());
   MonitorAutoLock lock(mPaintLock);
+  // Since this should happen after ALL paints are done and
+  // at the end of a transaction, this should always be true.
+  MOZ_RELEASE_ASSERT(mOutstandingAsyncPaints == 0);
 
   // It's possible that we painted so fast that the main thread never reached
   // the code that starts delaying messages. If so, mIsWaitingForPaint will be
   // false, and we can safely return.
-  if (mIsWaitingForPaint && mOutstandingAsyncPaints == 0) {
+  if (mIsWaitingForPaint) {
     ResumeIPCAfterAsyncPaint();
 
     // Notify the main thread in case it's blocking. We do this unconditionally
     // to avoid deadlocking.
     lock.Notify();
   }
 }
 
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -237,18 +237,19 @@ public:
   void FlushAsyncPaints();
 
   // Must only be called from the paint thread. Notifies the CompositorBridge
   // that the paint thread has finished an asynchronous paint request.
   void NotifyFinishedAsyncPaint(CapturedPaintState* aState);
 
   // Must only be called from the paint thread. Notifies the CompositorBridge
   // that the paint thread has finished ALL async requests from a given
-  // ClientPaintedLayer's batch.
-  void NotifyFinishedAsyncPaintLayer();
+  // transaction. We can resume IPC transactions after ALL
+  // async paints are done.
+  void NotifyFinishedAsyncPaintTransaction();
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CompositorBridgeChild();
 
   // Must only be called from the paint thread. If the main thread is delaying
   // IPC messages, this forwards all such delayed IPC messages to the I/O thread
   // and resumes IPC.
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -458,19 +458,21 @@ CompositorBridgeParent::StopAndClearReso
       if (lts->mWrBridge) {
         lts->mWrBridge->Destroy();
         lts->mWrBridge = nullptr;
       }
       lts->mParent = nullptr;
     });
     mWrBridge->Destroy();
     mWrBridge = nullptr;
-    mAsyncImageManager->Destroy();
-    // WebRenderAPI should be already destructed
-    mAsyncImageManager = nullptr;
+    if (mAsyncImageManager) {
+      mAsyncImageManager->Destroy();
+      // WebRenderAPI should be already destructed
+      mAsyncImageManager = nullptr;
+    }
   }
 
   if (mCompositor) {
     mCompositor->DetachWidget();
     mCompositor->Destroy();
     mCompositor = nullptr;
   }
 
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -261,31 +261,16 @@ public:
 
 
   PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override;
   bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
 
   void ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive) override { }
 
   /**
-   * Request that the compositor be recreated due to a shared device reset.
-   * This must be called on the main thread, and blocks until a task posted
-   * to the compositor thread has completed.
-   *
-   * Note that this posts a task directly, rather than using synchronous
-   * IPDL, and waits on a monitor notification from the compositor thread.
-   * We do this as a best-effort attempt to jump any IPDL messages that
-   * have not yet been posted (and are sitting around in the IO pipe), to
-   * minimize the amount of time the main thread is blocked.
-   */
-  bool ResetCompositor(const nsTArray<LayersBackend>& aBackendHints,
-                       uint64_t aSeqNo,
-                       TextureFactoryIdentifier* aOutIdentifier);
-
-  /**
    * This forces the is-first-paint flag to true. This is intended to
    * be called by the widget code when it loses its viewport information
    * (or for whatever reason wants to refresh the viewport information).
    * The information refresh happens because the compositor will call
    * SetFirstPaintViewport on the next frame of composition.
    */
   mozilla::ipc::IPCResult RecvForceIsFirstPaint() override;
 
--- a/gfx/layers/ipc/CompositorManagerChild.cpp
+++ b/gfx/layers/ipc/CompositorManagerChild.cpp
@@ -239,32 +239,36 @@ CompositorManagerChild::ProcessingError(
   if (aCode != MsgDropped) {
     gfxDevCrash(gfx::LogReason::ProcessingError) << "Processing error in CompositorBridgeChild: " << int(aCode);
   }
 }
 
 already_AddRefed<nsIEventTarget>
 CompositorManagerChild::GetSpecificMessageEventTarget(const Message& aMsg)
 {
-  if (aMsg.type() != PCompositorBridge::Msg_DidComposite__ID) {
-    return nullptr;
+  if (aMsg.type() == PCompositorBridge::Msg_DidComposite__ID) {
+    uint64_t layersId;
+    PickleIterator iter(aMsg);
+    if (!IPC::ReadParam(&aMsg, &iter, &layersId)) {
+      return nullptr;
+    }
+
+    TabChild* tabChild = TabChild::GetFrom(layersId);
+    if (!tabChild) {
+      return nullptr;
+    }
+
+    return do_AddRef(tabChild->TabGroup()->EventTargetFor(TaskCategory::Other));
   }
 
-  uint64_t layersId;
-  PickleIterator iter(aMsg);
-  if (!IPC::ReadParam(&aMsg, &iter, &layersId)) {
-    return nullptr;
+  if (aMsg.type() == PCompositorBridge::Msg_ParentAsyncMessages__ID) {
+    return do_AddRef(SystemGroup::EventTargetFor(TaskCategory::Other));
   }
 
-  TabChild* tabChild = TabChild::GetFrom(layersId);
-  if (!tabChild) {
-    return nullptr;
-  }
-
-  return do_AddRef(tabChild->TabGroup()->EventTargetFor(TaskCategory::Other));
+  return nullptr;
 }
 
 void
 CompositorManagerChild::SetReplyTimeout()
 {
 #ifndef DEBUG
   // Add a timeout for release builds to kill GPU process when it hangs.
   // Don't apply timeout when using web render as it tend to timeout frequently.
--- a/gfx/skia/skia/src/core/SkScan_AAAPath.cpp
+++ b/gfx/skia/skia/src/core/SkScan_AAAPath.cpp
@@ -1491,20 +1491,20 @@ static void aaa_walk_edges(SkAnalyticEdg
             if (isRite) {
                 if (useDeferred) {
                     deferred_blit(leftE, currE, left, leftDY, y, nextY, isIntegralNextY,
                             leftEnds, currEnds, blitter, maskRow, isUsingMask, noRealBlitter,
                             leftClip, rightClip, yShift);
                 } else {
                     SkFixed rite = currE->fX;
                     currE->goY(nextY, yShift);
-                    leftE->fX = SkTMax(leftClip, leftE->fX);
+                    SkFixed nextLeft = SkTMax(leftClip, leftE->fX);
                     rite = SkTMin(rightClip, rite);
-                    currE->fX = SkTMin(rightClip, currE->fX);
-                    blit_trapezoid_row(blitter, y >> 16, left, rite, leftE->fX, currE->fX,
+                    SkFixed nextRite = SkTMin(rightClip, currE->fX);
+                    blit_trapezoid_row(blitter, y >> 16, left, rite, nextLeft, nextRite,
                             leftDY, currE->fDY, fullAlpha, maskRow, isUsingMask,
                             noRealBlitter || (fullAlpha == 0xFF && (
                                     edges_too_close(prevRite, left, leftE->fX) ||
                                     edges_too_close(currE, currE->fNext, nextY)
                             )),
                             true);
                     prevRite = SkFixedCeilToInt(SkTMax(rite, currE->fX));
                 }
--- a/gfx/src/nsFontMetrics.h
+++ b/gfx/src/nsFontMetrics.h
@@ -44,17 +44,17 @@ struct nsBoundingMetrics;
  * reasonably well with the Western font that is loaded at Init time.
  */
 class nsFontMetrics final
 {
 public:
     typedef gfxTextRun::Range Range;
     typedef mozilla::gfx::DrawTarget DrawTarget;
 
-    struct Params
+    struct MOZ_STACK_CLASS Params
     {
       nsIAtom* language = nullptr;
       bool explicitLanguage = false;
       gfxFont::Orientation orientation = gfxFont::eHorizontal;
       gfxUserFontSet* userFontSet = nullptr;
       gfxTextPerfMetrics* textPerf = nullptr;
     };
 
@@ -245,17 +245,19 @@ private:
     }
 
     const gfxFont::Metrics&
     GetMetrics(const gfxFont::Orientation aFontOrientation) const;
 
     nsFont mFont;
     RefPtr<gfxFontGroup> mFontGroup;
     nsCOMPtr<nsIAtom> mLanguage;
-    nsDeviceContext* mDeviceContext;
+    // Pointer to the device context for which this fontMetrics object was
+    // created.
+    nsDeviceContext* MOZ_NON_OWNING_REF mDeviceContext;
     int32_t mP2A;
 
     // The font orientation (horizontal or vertical) for which these metrics
     // have been initialized. This determines which line metrics (ascent and
     // descent) they will return.
     gfxFont::Orientation mOrientation;
 
     // These fields may be set by clients to control the behavior of methods
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -332,17 +332,17 @@ protected:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIOBSERVER
     };
 
     void DestroyFont(gfxFont *aFont);
 
     static gfxFontCache *gGlobalCache;
 
-    struct Key {
+    struct MOZ_STACK_CLASS Key {
         const gfxFontEntry* mFontEntry;
         const gfxFontStyle* mStyle;
         const gfxCharacterMap* mUnicodeRangeMap;
         Key(const gfxFontEntry* aFontEntry, const gfxFontStyle* aStyle,
             const gfxCharacterMap* aUnicodeRangeMap)
             : mFontEntry(aFontEntry), mStyle(aStyle),
               mUnicodeRangeMap(aUnicodeRangeMap)
         {}
@@ -362,17 +362,21 @@ protected:
         bool KeyEquals(const KeyTypePointer aKey) const;
         static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
         static PLDHashNumber HashKey(const KeyTypePointer aKey) {
             return mozilla::HashGeneric(aKey->mStyle->Hash(), aKey->mFontEntry,
                                         aKey->mUnicodeRangeMap);
         }
         enum { ALLOW_MEMMOVE = true };
 
-        gfxFont* mFont;
+        // The cache tracks gfxFont objects whose refcount has dropped to zero,
+        // so they are not immediately deleted but may be "resurrected" if they
+        // have not yet expired from the tracker when they are needed again.
+        // See the custom AddRef/Release methods in gfxFont.
+        gfxFont* MOZ_UNSAFE_REF("tracking for deferred deletion") mFont;
     };
 
     nsTHashtable<HashEntry> mFonts;
 
     static void WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache);
     nsCOMPtr<nsITimer>      mWordCacheExpirationTimer;
 };
 
@@ -538,17 +542,17 @@ class gfxTextRunFactory {
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxTextRunFactory)
 
 public:
     typedef mozilla::gfx::DrawTarget DrawTarget;
 
     /**
      * This record contains all the parameters needed to initialize a textrun.
      */
-    struct Parameters {
+    struct MOZ_STACK_CLASS Parameters {
         // Shape text params suggesting where the textrun will be rendered
         DrawTarget   *mDrawTarget;
         // Pointer to arbitrary user data (which should outlive the textrun)
         void         *mUserData;
         // A description of which characters have been stripped from the original
         // DOM string to produce the characters in the textrun. May be null
         // if that information is not relevant.
         gfxSkipChars *mSkipChars;
@@ -2216,17 +2220,17 @@ protected:
 
 // proportion of ascent used for x-height, if unable to read value from font
 #define DEFAULT_XHEIGHT_FACTOR 0.56f
 
 // Parameters passed to gfxFont methods for drawing glyphs from a textrun.
 // The TextRunDrawParams are set up once per textrun; the FontDrawParams
 // are dependent on the specific font, so they are set per GlyphRun.
 
-struct TextRunDrawParams {
+struct MOZ_STACK_CLASS TextRunDrawParams {
     RefPtr<mozilla::gfx::DrawTarget> dt;
     gfxContext              *context;
     gfxFont::Spacing        *spacing;
     gfxTextRunDrawCallbacks *callbacks;
     mozilla::SVGContextPaint *runContextPaint;
     mozilla::gfx::Color      fontSmoothingBGColor;
     gfxFloat                 direction;
     double                   devPerApp;
@@ -2235,31 +2239,31 @@ struct TextRunDrawParams {
     const mozilla::gfx::StrokeOptions *strokeOpts;
     const mozilla::gfx::DrawOptions   *drawOpts;
     DrawMode                 drawMode;
     bool                     isVerticalRun;
     bool                     isRTL;
     bool                     paintSVGGlyphs;
 };
 
-struct FontDrawParams {
+struct MOZ_STACK_CLASS FontDrawParams {
     RefPtr<mozilla::gfx::ScaledFont>            scaledFont;
     RefPtr<mozilla::gfx::GlyphRenderingOptions> renderingOptions;
     mozilla::SVGContextPaint *contextPaint;
     mozilla::gfx::Matrix     *passedInvMatrix;
     mozilla::gfx::Matrix      matInv;
     double                    synBoldOnePixelOffset;
     int32_t                   extraStrikes;
     mozilla::gfx::DrawOptions drawOptions;
     bool                      isVerticalFont;
     bool                      haveSVGGlyphs;
     bool                      haveColorGlyphs;
 };
 
-struct EmphasisMarkDrawParams {
+struct MOZ_STACK_CLASS EmphasisMarkDrawParams {
     gfxContext* context;
     gfxFont::Spacing* spacing;
     gfxTextRun* mark;
     gfxFloat advance;
     gfxFloat direction;
     bool isVertical;
 };
 
--- a/gfx/thebes/gfxGraphiteShaper.h
+++ b/gfx/thebes/gfxGraphiteShaper.h
@@ -40,18 +40,22 @@ protected:
 
     static float GrGetAdvance(const void* appFontHandle, uint16_t glyphid);
 
     gr_face *mGrFace; // owned by the font entry; shaper must call
                       // gfxFontEntry::ReleaseGrFace when finished with it
     gr_font *mGrFont; // owned by the shaper itself
 
     struct CallbackData {
-        gfxFont* mFont;
-        mozilla::gfx::DrawTarget* mDrawTarget;
+        // mFont is a pointer to the font that owns this shaper, so it will
+        // remain valid throughout our lifetime
+        gfxFont* MOZ_NON_OWNING_REF mFont;
+        // initialized to a DrawTarget owned by our caller on every call to
+        // ShapeText
+        mozilla::gfx::DrawTarget* MOZ_NON_OWNING_REF mDrawTarget;
     };
 
     CallbackData mCallbackData;
     bool mFallbackToSmallCaps; // special fallback for the petite-caps case
 
     // Convert HTML 'lang' (BCP47) to Graphite language code
     static uint32_t GetGraphiteTagForLang(const nsCString& aLang);
     static nsTHashtable<nsUint32HashKey> *sLanguageTags;
--- a/gfx/thebes/gfxHarfBuzzShaper.h
+++ b/gfx/thebes/gfxHarfBuzzShaper.h
@@ -18,17 +18,19 @@ public:
     virtual ~gfxHarfBuzzShaper();
 
     /*
      * For HarfBuzz font callback functions, font_data is a ptr to a
      * FontCallbackData struct
      */
     struct FontCallbackData {
         gfxHarfBuzzShaper* mShaper;
-        mozilla::gfx::DrawTarget* mDrawTarget;
+        // initialized to a DrawTarget owned by our caller on every call to
+        // ShapeText
+        mozilla::gfx::DrawTarget* MOZ_NON_OWNING_REF mDrawTarget;
     };
 
     bool Initialize();
 
     bool ShapeText(DrawTarget      *aDrawTarget,
                    const char16_t *aText,
                    uint32_t         aOffset,
                    uint32_t         aLength,
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -62,17 +62,19 @@ public:
     }
     static PLDHashNumber HashKey(const gfxCharacterMap *aCharMap) {
         return aCharMap->mHash;
     }
 
     enum { ALLOW_MEMMOVE = true };
 
 protected:
-    gfxCharacterMap *mCharMap;
+    // charMaps are not owned by the shared cmap cache, but it will be notified
+    // by gfxCharacterMap::Release() when an entry is about to be deleted
+    gfxCharacterMap* MOZ_NON_OWNING_REF mCharMap;
 };
 
 // gfxPlatformFontList is an abstract class for the global font list on the system;
 // concrete subclasses for each platform implement the actual interface to the system fonts.
 // This class exists because we cannot rely on the platform font-finding APIs to behave
 // in sensible/similar ways, particularly with rich, complex OpenType families,
 // so we do our own font family/style management here instead.
 
--- a/gfx/thebes/gfxSVGGlyphs.h
+++ b/gfx/thebes/gfxSVGGlyphs.h
@@ -135,17 +135,19 @@ public:
 
 private:
     Element *GetGlyphElement(uint32_t aGlyphId);
 
     nsClassHashtable<nsUint32HashKey, gfxSVGGlyphsDocument> mGlyphDocs;
     nsBaseHashtable<nsUint32HashKey, Element*, Element*> mGlyphIdMap;
 
     hb_blob_t *mSVGData;
-    gfxFontEntry *mFontEntry;
+
+    // pointer to the font entry that owns this gfxSVGGlyphs object
+    gfxFontEntry* MOZ_NON_OWNING_REF mFontEntry;
 
     const struct Header {
         mozilla::AutoSwap_PRUint16 mVersion;
         mozilla::AutoSwap_PRUint32 mDocIndexOffset;
         mozilla::AutoSwap_PRUint32 mColorPalettesOffset;
     } *mHeader;
 
     struct IndexEntry {
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -556,17 +556,17 @@ HasNonOpaqueNonTransparentColor(gfxConte
         if (0.f < aCurrentColorOut.a && aCurrentColorOut.a < 1.f) {
             return true;
         }
     }
     return false;
 }
 
 // helper class for double-buffering drawing with non-opaque color
-struct BufferAlphaColor {
+struct MOZ_STACK_CLASS BufferAlphaColor {
     explicit BufferAlphaColor(gfxContext *aContext)
         : mContext(aContext)
     {
 
     }
 
     ~BufferAlphaColor() {}
 
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -236,17 +236,17 @@ public:
         // Only called if the hyphen width is requested.
         virtual already_AddRefed<DrawTarget> GetDrawTarget() const = 0;
 
         // Return the appUnitsPerDevUnit value to be used when measuring.
         // Only called if the hyphen width is requested.
         virtual uint32_t GetAppUnitsPerDevUnit() const = 0;
     };
 
-    struct DrawParams
+    struct MOZ_STACK_CLASS DrawParams
     {
         gfxContext* context;
         DrawMode drawMode = DrawMode::GLYPH_FILL;
         nscolor textStrokeColor = 0;
         gfxPattern* textStrokePattern = nullptr;
         const mozilla::gfx::StrokeOptions *strokeOpts = nullptr;
         const mozilla::gfx::DrawOptions *drawOpts = nullptr;
         PropertyProvider* provider = nullptr;
@@ -459,17 +459,17 @@ public:
     // of cases, a gfxTextRun contains just a single GlyphRun.)
     struct GlyphRun {
         RefPtr<gfxFont> mFont; // never null in a valid GlyphRun
         uint32_t        mCharacterOffset; // into original UTF16 string
         mozilla::gfx::ShapedTextFlags mOrientation; // gfxTextRunFactory::TEXT_ORIENT_* value
         uint8_t         mMatchType;
     };
 
-    class GlyphRunIterator {
+    class MOZ_STACK_CLASS GlyphRunIterator {
     public:
         GlyphRunIterator(const gfxTextRun *aTextRun, Range aRange)
           : mTextRun(aTextRun)
           , mStartOffset(aRange.start)
           , mEndOffset(aRange.end) {
             mNextIndex = mTextRun->FindFirstGlyphRunContaining(aRange.start);
         }
         bool NextRun();
@@ -808,18 +808,22 @@ private:
         MOZ_ASSERT(mHasGlyphRunArray && mGlyphRunArray.Length() == 1);
         GlyphRun tmp = mozilla::Move(mGlyphRunArray[0]);
         mGlyphRunArray.~nsTArray<GlyphRun>();
         new (&mSingleGlyphRun) GlyphRun(mozilla::Move(tmp));
         mHasGlyphRunArray = false;
     }
 
     void             *mUserData;
-    gfxFontGroup     *mFontGroup; // addrefed on creation, but our reference
-                                  // may be released by ReleaseFontGroup()
+
+    // mFontGroup is usually a strong reference, but refcounting is managed
+    // manually because it may be explicitly released by ReleaseFontGroup()
+    // in the case where the font group actually owns the textrun.
+    gfxFontGroup* MOZ_OWNING_REF mFontGroup;
+
     gfxSkipChars      mSkipChars;
 
     nsTextFrameUtils::Flags mFlags2; // additional flags (see also gfxShapedText::mFlags)
 
     bool              mSkipDrawing; // true if the font group we used had a user font
                                     // download that's in progress, so we should hide text
                                     // until the download completes (or timeout fires)
     bool              mReleasedFontGroup; // we already called NS_RELEASE on
@@ -1124,18 +1128,20 @@ protected:
         }
 
         bool EqualsUserFont(const gfxUserFontEntry* aUserFont) const;
 
     private:
         RefPtr<gfxFontFamily> mFamily;
         // either a font or a font entry exists
         union {
-            gfxFont*            mFont;
-            gfxFontEntry*       mFontEntry;
+            // Whichever of these fields is actually present will be a strong
+            // reference, with refcounting handled manually.
+            gfxFont* MOZ_OWNING_REF      mFont;
+            gfxFontEntry* MOZ_OWNING_REF mFontEntry;
         };
         bool                    mNeedsBold   : 1;
         bool                    mFontCreated : 1;
         bool                    mLoading     : 1;
         bool                    mInvalid     : 1;
         bool                    mCheckForFallbackFaces : 1;
     };
 
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -166,17 +166,17 @@ gfxUserFontEntry::CreateFontInstance(con
 {
     NS_NOTREACHED("should only be creating a gfxFont"
                   " with an actual platform font entry");
 
     // userfont entry is a container, can't create font from the container
     return nullptr;
 }
 
-class gfxOTSContext : public ots::OTSContext {
+class MOZ_STACK_CLASS gfxOTSContext : public ots::OTSContext {
 public:
     explicit gfxOTSContext(gfxUserFontEntry* aUserFontEntry)
         : mUserFontEntry(aUserFontEntry)
     {
         // Whether to apply OTS validation to OpenType Layout tables
         mCheckOTLTables = gfxPrefs::ValidateOTLTables();
         // Whether to preserve Variation tables in downloaded fonts
         mKeepVariationTables = gfxPrefs::KeepVariationTables();
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -741,14 +741,14 @@ protected:
     uint8_t                  mFontDisplay; // timing of userfont fallback
 
     RefPtr<gfxFontEntry>   mPlatformFontEntry;
     nsTArray<gfxFontFaceSrc> mSrcList;
     uint32_t                 mSrcIndex; // index of loading src item
     // This field is managed by the nsFontFaceLoader. In the destructor and Cancel()
     // methods of nsFontFaceLoader this reference is nulled out.
     nsFontFaceLoader* MOZ_NON_OWNING_REF mLoader; // current loader for this entry, if any
-    gfxUserFontSet*          mFontSet; // font-set which owns this userfont entry
+    gfxUserFontSet*   MOZ_NON_OWNING_REF mFontSet; // font-set which owns this userfont entry
     RefPtr<gfxFontSrcPrincipal> mPrincipal;
 };
 
 
 #endif /* GFX_USER_FONT_SET_H */
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -780,34 +780,34 @@ pub unsafe extern "C" fn wr_api_set_root
                                                       pipeline_id: WrPipelineId,
                                                       content_size: LayoutSize,
                                                       dl_descriptor: BuiltDisplayListDescriptor,
                                                       dl_data: *mut u8,
                                                       dl_size: usize) {
     let color = if color.a == 0.0 {
         None
     } else {
-        Some(color.into())
+        Some(color)
     };
     // See the documentation of set_display_list in api.rs. I don't think
     // it makes a difference in gecko at the moment(until APZ is figured out)
     // but I suppose it is a good default.
     let preserve_frame_state = true;
 
     let dl_slice = make_slice(dl_data, dl_size);
     let mut dl_vec = Vec::new();
     // XXX: see if we can get rid of the copy here
     dl_vec.extend_from_slice(dl_slice);
     let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor);
 
     dh.api.set_display_list(dh.document_id,
                             epoch,
                             color,
                             LayoutSize::new(viewport_width, viewport_height),
-                            (pipeline_id, content_size.into(), dl),
+                            (pipeline_id, content_size, dl),
                             preserve_frame_state,
                             ResourceUpdates::new());
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_api_clear_root_display_list(dh: &mut DocumentHandle,
                                                         epoch: WrEpoch,
                                                         pipeline_id: WrPipelineId) {
@@ -924,17 +924,17 @@ pub struct WebRenderFrameBuilder {
     pub scroll_clips_defined: HashSet<ClipId>,
 }
 
 impl WebRenderFrameBuilder {
     pub fn new(root_pipeline_id: WrPipelineId,
                content_size: LayoutSize) -> WebRenderFrameBuilder {
         WebRenderFrameBuilder {
             root_pipeline_id: root_pipeline_id,
-            dl_builder: webrender_api::DisplayListBuilder::new(root_pipeline_id, content_size.into()),
+            dl_builder: webrender_api::DisplayListBuilder::new(root_pipeline_id, content_size),
             scroll_clips_defined: HashSet::new(),
         }
     }
 }
 
 pub struct WrState {
     pipeline_id: WrPipelineId,
     frame_builder: WebRenderFrameBuilder,
@@ -999,18 +999,16 @@ pub extern "C" fn wr_dp_push_stacking_co
                                               transform: *const LayoutTransform,
                                               transform_style: TransformStyle,
                                               perspective: *const LayoutTransform,
                                               mix_blend_mode: MixBlendMode,
                                               filters: *const WrFilterOp,
                                               filter_count: usize) {
     assert!(unsafe { !is_in_render_thread() });
 
-    let bounds = bounds.into();
-
     let c_filters = make_slice(filters, filter_count);
     let mut filters : Vec<FilterOp> = c_filters.iter().map(|c_filter| {
         match c_filter.filter_type {
             WrFilterOpType::Blur => FilterOp::Blur(c_filter.argument),
             WrFilterOpType::Brightness => FilterOp::Brightness(c_filter.argument),
             WrFilterOpType::Contrast => FilterOp::Contrast(c_filter.argument),
             WrFilterOpType::Grayscale => FilterOp::Grayscale(c_filter.argument),
             WrFilterOpType::HueRotate => FilterOp::HueRotate(c_filter.argument),
@@ -1058,23 +1056,22 @@ pub extern "C" fn wr_dp_push_stacking_co
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_stacking_context(state: &mut WrState) {
     assert!(unsafe { !is_in_render_thread() });
     state.frame_builder.dl_builder.pop_stacking_context();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_define_clip(state: &mut WrState,
-                                    rect: LayoutRect,
+                                    clip_rect: LayoutRect,
                                     complex: *const WrComplexClipRegion,
                                     complex_count: usize,
                                     mask: *const WrImageMask)
                                     -> u64 {
     assert!(unsafe { is_in_main_thread() });
-    let clip_rect: LayoutRect = rect.into();
     let complex_slice = make_slice(complex, complex_count);
     let complex_iter = complex_slice.iter().map(|x| x.into());
     let mask : Option<ImageMask> = unsafe { mask.as_ref() }.map(|x| x.into());
 
     let clip_id = state.frame_builder.dl_builder.define_clip(None, clip_rect, complex_iter, mask);
     // return the u64 id value from inside the ClipId::Clip(..)
     match clip_id {
         ClipId::Clip(id, nesting_index, pipeline_id) => {
@@ -1104,18 +1101,16 @@ pub extern "C" fn wr_dp_push_scroll_laye
                                           scroll_id: u64,
                                           content_rect: LayoutRect,
                                           clip_rect: LayoutRect) {
     assert!(unsafe { is_in_main_thread() });
     let clip_id = ClipId::new(scroll_id, state.pipeline_id);
     // Avoid defining multiple scroll clips with the same clip id, as that
     // results in undefined behaviour or assertion failures.
     if !state.frame_builder.scroll_clips_defined.contains(&clip_id) {
-        let content_rect: LayoutRect = content_rect.into();
-        let clip_rect: LayoutRect = clip_rect.into();
 
         state.frame_builder.dl_builder.define_scroll_frame(
             Some(clip_id), content_rect, clip_rect, vec![], None,
             ScrollSensitivity::Script);
         state.frame_builder.scroll_clips_defined.insert(clip_id);
     }
     state.frame_builder.dl_builder.push_clip_id(clip_id);
 }
@@ -1128,17 +1123,17 @@ pub extern "C" fn wr_dp_pop_scroll_layer
 
 #[no_mangle]
 pub extern "C" fn wr_scroll_layer_with_id(dh: &mut DocumentHandle,
                                           pipeline_id: WrPipelineId,
                                           scroll_id: u64,
                                           new_scroll_origin: LayoutPoint) {
     assert!(unsafe { is_in_compositor_thread() });
     let clip_id = ClipId::new(scroll_id, pipeline_id);
-    dh.api.scroll_node_with_id(dh.document_id, new_scroll_origin.into(), clip_id, ScrollClamping::NoClamping);
+    dh.api.scroll_node_with_id(dh.document_id, new_scroll_origin, clip_id, ScrollClamping::NoClamping);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_clip_and_scroll_info(state: &mut WrState,
                                                   scroll_id: u64,
                                                   clip_id: *const u64) {
     assert!(unsafe { is_in_main_thread() });
     let scroll_id = ClipId::new(scroll_id, state.pipeline_id);
@@ -1159,47 +1154,47 @@ pub extern "C" fn wr_dp_pop_clip_and_scr
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_iframe(state: &mut WrState,
                                     rect: LayoutRect,
                                     pipeline_id: WrPipelineId) {
     assert!(unsafe { is_in_main_thread() });
 
-    state.frame_builder.dl_builder.push_iframe(rect.into(), None, pipeline_id);
+    state.frame_builder.dl_builder.push_iframe(rect, None, pipeline_id);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_rect(state: &mut WrState,
                                   rect: LayoutRect,
                                   clip: LayoutRect,
                                   color: ColorF) {
     assert!(unsafe { !is_in_render_thread() });
 
-    state.frame_builder.dl_builder.push_rect(rect.into(),
-                                             Some(LocalClip::Rect(clip.into())),
-                                             color.into());
+    state.frame_builder.dl_builder.push_rect(rect,
+                                             Some(LocalClip::Rect(clip)),
+                                             color);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_image(state: &mut WrState,
                                    bounds: LayoutRect,
                                    clip: LayoutRect,
                                    stretch_size: LayoutSize,
                                    tile_spacing: LayoutSize,
                                    image_rendering: ImageRendering,
                                    key: WrImageKey) {
     assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
     state.frame_builder
          .dl_builder
-         .push_image(bounds.into(),
-                     Some(LocalClip::Rect(clip.into())),
-                     stretch_size.into(),
-                     tile_spacing.into(),
+         .push_image(bounds,
+                     Some(LocalClip::Rect(clip)),
+                     stretch_size,
+                     tile_spacing,
                      image_rendering,
                      key);
 }
 
 /// Push a 3 planar yuv image.
 #[no_mangle]
 pub extern "C" fn wr_dp_push_yuv_planar_image(state: &mut WrState,
                                               bounds: LayoutRect,
@@ -1208,17 +1203,17 @@ pub extern "C" fn wr_dp_push_yuv_planar_
                                               image_key_1: WrImageKey,
                                               image_key_2: WrImageKey,
                                               color_space: WrYuvColorSpace,
                                               image_rendering: ImageRendering) {
     assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
     state.frame_builder
          .dl_builder
-         .push_yuv_image(bounds.into(),
+         .push_yuv_image(bounds,
                          Some(LocalClip::Rect(clip.into())),
                          YuvData::PlanarYCbCr(image_key_0, image_key_1, image_key_2),
                          color_space,
                          image_rendering);
 }
 
 /// Push a 2 planar NV12 image.
 #[no_mangle]
@@ -1228,17 +1223,17 @@ pub extern "C" fn wr_dp_push_yuv_NV12_im
                                             image_key_0: WrImageKey,
                                             image_key_1: WrImageKey,
                                             color_space: WrYuvColorSpace,
                                             image_rendering: ImageRendering) {
     assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
     state.frame_builder
          .dl_builder
-         .push_yuv_image(bounds.into(),
+         .push_yuv_image(bounds,
                          Some(LocalClip::Rect(clip.into())),
                          YuvData::NV12(image_key_0, image_key_1),
                          color_space,
                          image_rendering);
 }
 
 /// Push a yuv interleaved image.
 #[no_mangle]
@@ -1247,17 +1242,17 @@ pub extern "C" fn wr_dp_push_yuv_interle
                                                    clip: LayoutRect,
                                                    image_key_0: WrImageKey,
                                                    color_space: WrYuvColorSpace,
                                                    image_rendering: ImageRendering) {
     assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
     state.frame_builder
          .dl_builder
-         .push_yuv_image(bounds.into(),
+         .push_yuv_image(bounds,
                          Some(LocalClip::Rect(clip.into())),
                          YuvData::InterleavedYCbCr(image_key_0),
                          color_space,
                          image_rendering);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_text(state: &mut WrState,
@@ -1272,17 +1267,17 @@ pub extern "C" fn wr_dp_push_text(state:
 
     let glyph_slice = make_slice(glyphs, glyph_count as usize);
 
     let colorf = ColorF::new(color.r, color.g, color.b, color.a);
 
     let glyph_options = None; // TODO
     state.frame_builder
          .dl_builder
-         .push_text(bounds.into(),
+         .push_text(bounds,
                     Some(LocalClip::Rect(clip.into())),
                     &glyph_slice,
                     font_key,
                     colorf,
                     Au::from_f32_px(glyph_size),
                     glyph_options);
 }
 
@@ -1302,19 +1297,19 @@ pub extern "C" fn wr_dp_push_border(stat
                                                    left: left.into(),
                                                    right: right.into(),
                                                    top: top.into(),
                                                    bottom: bottom.into(),
                                                    radius: radius.into(),
                                                });
     state.frame_builder
          .dl_builder
-         .push_border(rect.into(),
+         .push_border(rect,
                       Some(LocalClip::Rect(clip.into())),
-                      widths.into(),
+                      widths,
                       border_details);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border_image(state: &mut WrState,
                                           rect: LayoutRect,
                                           clip: LayoutRect,
                                           widths: BorderWidths,
@@ -1460,21 +1455,21 @@ pub extern "C" fn wr_dp_push_radial_grad
     let gradient = state.frame_builder
                         .dl_builder
                         .create_radial_gradient(center.into(),
                                                 radius.into(),
                                                 stops_vector,
                                                 extend_mode.into());
     state.frame_builder
          .dl_builder
-         .push_radial_gradient(rect.into(),
+         .push_radial_gradient(rect,
                                Some(LocalClip::Rect(clip.into())),
                                gradient,
-                               tile_size.into(),
-                               tile_spacing.into());
+                               tile_size,
+                               tile_spacing);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_box_shadow(state: &mut WrState,
                                         rect: LayoutRect,
                                         clip: LayoutRect,
                                         box_bounds: LayoutRect,
                                         offset: LayoutVector2D,
@@ -1482,21 +1477,21 @@ pub extern "C" fn wr_dp_push_box_shadow(
                                         blur_radius: f32,
                                         spread_radius: f32,
                                         border_radius: f32,
                                         clip_mode: BoxShadowClipMode) {
     assert!(unsafe { is_in_main_thread() });
 
     state.frame_builder
          .dl_builder
-         .push_box_shadow(rect.into(),
+         .push_box_shadow(rect,
                           Some(LocalClip::Rect(clip.into())),
-                          box_bounds.into(),
+                          box_bounds,
                           offset,
-                          color.into(),
+                          color,
                           blur_radius,
                           spread_radius,
                           border_radius,
                           clip_mode);
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_api_finalize_builder(state: &mut WrState,
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -769,17 +769,17 @@ WR_FUNC;
 WR_INLINE
 void wr_dp_begin(WrState *aState,
                  uint32_t aWidth,
                  uint32_t aHeight)
 WR_FUNC;
 
 WR_INLINE
 uint64_t wr_dp_define_clip(WrState *aState,
-                           LayoutRect aRect,
+                           LayoutRect aClipRect,
                            const WrComplexClipRegion *aComplex,
                            size_t aComplexCount,
                            const WrImageMask *aMask)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_end(WrState *aState)
 WR_FUNC;
--- a/image/Image.cpp
+++ b/image/Image.cpp
@@ -152,20 +152,22 @@ ImageResource::SendOnUnlockedDraw(uint32
   if (!mProgressTracker) {
     return;
   }
 
   if (!(aFlags & FLAG_ASYNC_NOTIFY)) {
     mProgressTracker->OnUnlockedDraw();
   } else {
     NotNull<RefPtr<ImageResource>> image = WrapNotNull(this);
-    NS_DispatchToMainThread(NS_NewRunnableFunction(
+    nsCOMPtr<nsIEventTarget> eventTarget = mProgressTracker->GetEventTarget();
+    nsCOMPtr<nsIRunnable> ev = NS_NewRunnableFunction(
       "image::ImageResource::SendOnUnlockedDraw", [=]() -> void {
         RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
         if (tracker) {
           tracker->OnUnlockedDraw();
         }
-      }));
+      });
+    eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
   }
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -451,22 +451,30 @@ RasterImage::WillDrawOpaqueNow()
 void
 RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
 {
   MOZ_ASSERT(mProgressTracker);
 
   bool animatedFramesDiscarded =
     mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
 
+  nsCOMPtr<nsIEventTarget> eventTarget;
+  if (mProgressTracker) {
+    eventTarget = mProgressTracker->GetEventTarget();
+  } else {
+    eventTarget = do_GetMainThread();
+  }
+
   RefPtr<RasterImage> image = this;
-  NS_DispatchToMainThread(NS_NewRunnableFunction(
+  nsCOMPtr<nsIRunnable> ev = NS_NewRunnableFunction(
                             "RasterImage::OnSurfaceDiscarded",
                             [=]() -> void {
     image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
-  }));
+  });
+  eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
 }
 
 void
 RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aAnimatedFramesDiscarded && mAnimationState) {
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -241,21 +241,21 @@ public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(ImageSurfaceCache)
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageSurfaceCache)
 
   typedef
     nsRefPtrHashtable<nsGenericHashKey<SurfaceKey>, CachedSurface> SurfaceTable;
 
   bool IsEmpty() const { return mSurfaces.Count() == 0; }
 
-  void Insert(NotNull<CachedSurface*> aSurface)
+  MOZ_MUST_USE bool Insert(NotNull<CachedSurface*> aSurface)
   {
     MOZ_ASSERT(!mLocked || aSurface->IsPlaceholder() || aSurface->IsLocked(),
                "Inserting an unlocked surface for a locked image");
-    mSurfaces.Put(aSurface->GetSurfaceKey(), aSurface);
+    return mSurfaces.Put(aSurface->GetSurfaceKey(), aSurface, fallible);
   }
 
   void Remove(NotNull<CachedSurface*> aSurface)
   {
     MOZ_ASSERT(mSurfaces.GetWeak(aSurface->GetSurfaceKey()),
         "Should not be removing a surface we don't have");
 
     mSurfaces.Remove(aSurface->GetSurfaceKey());
@@ -475,28 +475,34 @@ public:
     }
 
     NotNull<RefPtr<CachedSurface>> surface =
       WrapNotNull(new CachedSurface(aProvider));
 
     // We require that locking succeed if the image is locked and we're not
     // inserting a placeholder; the caller may need to know this to handle
     // errors correctly.
-    if (cache->IsLocked() && !surface->IsPlaceholder()) {
+    bool mustLock = cache->IsLocked() && !surface->IsPlaceholder();
+    if (mustLock) {
       surface->SetLocked(true);
       if (!surface->IsLocked()) {
         return InsertOutcome::FAILURE;
       }
     }
 
     // Insert.
     MOZ_ASSERT(cost <= mAvailableCost, "Inserting despite too large a cost");
-    cache->Insert(surface);
+    if (!cache->Insert(surface)) {
+      if (mustLock) {
+        surface->SetLocked(false);
+      }
+      return InsertOutcome::FAILURE;
+    }
+
     StartTracking(surface, aAutoLock);
-
     return InsertOutcome::SUCCESS;
   }
 
   void Remove(NotNull<CachedSurface*> aSurface,
               const StaticMutexAutoLock& aAutoLock)
   {
     ImageKey imageKey = aSurface->GetImageKey();
 
@@ -904,17 +910,18 @@ private:
 
   struct SurfaceTracker : public ExpirationTrackerImpl<CachedSurface, 2,
                                                        StaticMutex,
                                                        StaticMutexAutoLock>
   {
     explicit SurfaceTracker(uint32_t aSurfaceCacheExpirationTimeMS)
       : ExpirationTrackerImpl<CachedSurface, 2,
                               StaticMutex, StaticMutexAutoLock>(
-          aSurfaceCacheExpirationTimeMS, "SurfaceTracker")
+          aSurfaceCacheExpirationTimeMS, "SurfaceTracker",
+          SystemGroup::EventTargetFor(TaskCategory::Other))
     { }
 
   protected:
     void NotifyExpiredLocked(CachedSurface* aSurface,
                              const StaticMutexAutoLock& aAutoLock) override
     {
       sInstance->Remove(WrapNotNull(aSurface), aAutoLock);
     }
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -1099,17 +1099,18 @@ public:
   imgCacheExpirationTracker();
 
 protected:
   void NotifyExpired(imgCacheEntry* entry) override;
 };
 
 imgCacheExpirationTracker::imgCacheExpirationTracker()
  : nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000,
-                                         "imgCacheExpirationTracker")
+                                         "imgCacheExpirationTracker",
+                                         SystemGroup::EventTargetFor(TaskCategory::Other))
 { }
 
 void
 imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
 {
   // Hold on to a reference to this entry, because the expiration tracker
   // mechanism doesn't.
   RefPtr<imgCacheEntry> kungFuDeathGrip(entry);
--- a/image/imgRequest.cpp
+++ b/image/imgRequest.cpp
@@ -342,17 +342,20 @@ void
 imgRequest::Cancel(nsresult aStatus)
 {
   /* The Cancel() method here should only be called by this class. */
   LOG_SCOPE(gImgLog, "imgRequest::Cancel");
 
   if (NS_IsMainThread()) {
     ContinueCancel(aStatus);
   } else {
-    NS_DispatchToMainThread(new imgRequestMainThreadCancel(this, aStatus));
+    RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
+    nsCOMPtr<nsIEventTarget> eventTarget = progressTracker->GetEventTarget();
+    nsCOMPtr<nsIRunnable> ev = new imgRequestMainThreadCancel(this, aStatus);
+    eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
   }
 }
 
 void
 imgRequest::ContinueCancel(nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -1136,32 +1139,45 @@ imgRequest::OnDataAvailable(nsIRequest* 
   if (newPartPending) {
     NewPartResult result = PrepareForNewPart(aRequest, aInStr, aCount, mURI,
                                              isMultipart, image,
                                              progressTracker, mInnerWindowId);
     bool succeeded = result.mSucceeded;
 
     if (result.mImage) {
       image = result.mImage;
+      nsCOMPtr<nsIEventTarget> eventTarget;
 
       // Update our state to reflect this new part.
       {
         MutexAutoLock lock(mMutex);
         mImage = image;
+
+        // We only get an event target if we are not on the main thread, because
+        // we have to dispatch in that case. If we are on the main thread, but
+        // on a different scheduler group than ProgressTracker would give us,
+        // that is okay because nothing in imagelib requires that, just our
+        // listeners (which have their own checks).
+        if (!NS_IsMainThread()) {
+          eventTarget = mProgressTracker->GetEventTarget();
+          MOZ_ASSERT(eventTarget);
+        }
+
         mProgressTracker = nullptr;
       }
 
       // Some property objects are not threadsafe, and we need to send
       // OnImageAvailable on the main thread, so finish on the main thread.
-      if (NS_IsMainThread()) {
+      if (!eventTarget) {
+        MOZ_ASSERT(NS_IsMainThread());
         FinishPreparingForNewPart(result);
       } else {
         nsCOMPtr<nsIRunnable> runnable =
           new FinishPreparingForNewPartRunnable(this, Move(result));
-        NS_DispatchToMainThread(runnable);
+        eventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
       }
     }
 
     if (!succeeded) {
       // Something went wrong; probably a content type issue.
       Cancel(NS_IMAGELIB_ERROR_FAILURE);
       return NS_BINDING_ABORTED;
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/setter-argc.js
@@ -0,0 +1,52 @@
+// Check that setters throw TypeError when passed no arguments, instead of crashing.
+
+function check(obj) {
+  let proto = Object.getPrototypeOf(obj);
+  let props = Object.getOwnPropertyNames(proto);
+  for (let prop of props) {
+    let desc = Object.getOwnPropertyDescriptor(proto, prop);
+    if (desc.set) {
+      print("bleah: " + uneval(prop));
+      assertEq(typeof desc.set, 'function');
+      try {
+        desc.set.call(obj);
+        assertEq("should have thrown TypeError", false);
+      } catch (e) {
+        assertEq(e instanceof TypeError, true);
+      }
+    }
+  }
+}
+
+var dbg = new Debugger;
+var g = newGlobal();
+var gw = dbg.addDebuggee(g);
+
+// Debugger
+check(dbg);
+
+// Debugger.Memory
+check(dbg.memory);
+
+// Debugger.Object
+g.eval('function f() { debugger; }');
+var fw = gw.getOwnPropertyDescriptor('f').value;
+check(fw);
+
+// Debugger.Script
+check(fw.script);
+
+// Debugger.Source
+check(fw.script.source);
+
+// Debugger.Environment
+check(fw.environment);
+
+// Debugger.Frame
+var log = '';
+dbg.onDebuggerStatement = function(frame) {
+  log += 'd';
+  check(frame);
+}
+g.eval('f()');
+assertEq(log, 'd');
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -7636,19 +7636,21 @@ DebuggerSource_getIntroductionType(JSCon
     }
 
     return true;
 }
 
 static bool
 DebuggerSource_setSourceMapURL(JSContext* cx, unsigned argc, Value* vp)
 {
-    THIS_DEBUGSOURCE_SOURCE(cx, argc, vp, "sourceMapURL", args, obj, sourceObject);
+    THIS_DEBUGSOURCE_SOURCE(cx, argc, vp, "set sourceMapURL", args, obj, sourceObject);
     ScriptSource* ss = sourceObject->source();
     MOZ_ASSERT(ss);
+    if (!args.requireAtLeast(cx, "set sourceMapURL", 1))
+        return false;
 
     JSString* str = ToString<CanGC>(cx, args[0]);
     if (!str)
         return false;
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, str))
         return false;
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1701,16 +1701,22 @@ SandboxOptions::Parse()
 }
 
 static nsresult
 AssembleSandboxMemoryReporterName(JSContext* cx, nsCString& sandboxName)
 {
     // Use a default name when the caller did not provide a sandboxName.
     if (sandboxName.IsEmpty())
         sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]");
+#ifndef DEBUG
+    // Adding the caller location is fairly expensive, so in non-debug builds,
+    // only add it if we don't have an explicit sandbox name.
+    else
+        return NS_OK;
+#endif
 
     // Get the xpconnect native call context.
     XPCCallContext* cc = XPCJSContext::Get()->GetCallContext();
     NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG);
 
     // Get the current source info from xpc.
     nsCOMPtr<nsIStackFrame> frame;
     nsXPConnect::XPConnect()->GetCurrentJSStack(getter_AddRefs(frame));
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -246,26 +246,33 @@ bool CompartmentPrivate::TryParseLocatio
     if (!aURI)
         return false;
 
     // Need to parse the URI.
     if (location.IsEmpty())
         return false;
 
     // Handle Sandbox location strings.
-    // A sandbox string looks like this:
+    // A sandbox string looks like this, for anonymous sandboxes, and builds
+    // where Sandbox location tagging is enabled:
+    //
     // <sandboxName> (from: <js-stack-frame-filename>:<lineno>)
+    //
     // where <sandboxName> is user-provided via Cu.Sandbox()
     // and <js-stack-frame-filename> and <lineno> is the stack frame location
     // from where Cu.Sandbox was called.
+    //
+    // Otherwise, it is simply the caller-provided name, which is usually a URI.
+    //
     // <js-stack-frame-filename> furthermore is "free form", often using a
     // "uri -> uri -> ..." chain. The following code will and must handle this
     // common case.
+    //
     // It should be noted that other parts of the code may already rely on the
-    // "format" of these strings, such as the add-on SDK.
+    // "format" of these strings.
 
     static const nsDependentCString from("(from: ");
     static const nsDependentCString arrow(" -> ");
     static const size_t fromLength = from.Length();
     static const size_t arrowLength = arrow.Length();
 
     // See: XPCComponents.cpp#AssembleSandboxMemoryReporterName
     int32_t idx = location.Find(from);
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -5,20 +5,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Sharable code and data for wrapper around JSObjects. */
 
 #include "xpcprivate.h"
 #include "jsprf.h"
 #include "nsArrayEnumerator.h"
 #include "nsContentUtils.h"
+#include "nsINamed.h"
 #include "nsIScriptError.h"
 #include "nsWrapperCache.h"
 #include "AccessCheck.h"
 #include "nsJSUtils.h"
+#include "nsPrintfCString.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
@@ -444,16 +446,107 @@ public:
     }
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(WrappedJSIdentity,
                               NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID)
 
 /***************************************************************************/
 
+namespace {
+
+class WrappedJSNamed final : public nsINamed
+{
+    nsCString mName;
+
+    ~WrappedJSNamed() {}
+
+public:
+    NS_DECL_ISUPPORTS
+
+    explicit WrappedJSNamed(const nsACString& aName) : mName(aName) {}
+
+    NS_IMETHOD GetName(nsACString& aName) override
+    {
+        aName = mName;
+        aName.AppendLiteral(":JS");
+        return NS_OK;
+    }
+};
+
+NS_IMPL_ISUPPORTS(WrappedJSNamed, nsINamed)
+
+nsCString
+GetFunctionName(JSContext* cx, HandleObject obj)
+{
+    RootedObject inner(cx, js::UncheckedUnwrap(obj));
+    JSAutoCompartment ac(cx, inner);
+
+    RootedFunction fun(cx, JS_GetObjectFunction(inner));
+    if (!fun) {
+        // If the object isn't a function, it's likely that it has a single
+        // function property (for things like nsITimerCallback). In this case,
+        // return the name of that function property.
+
+        Rooted<IdVector> idArray(cx, IdVector(cx));
+        if (!JS_Enumerate(cx, inner, &idArray)) {
+            JS_ClearPendingException(cx);
+            return nsCString("error");
+        }
+
+        if (idArray.length() != 1)
+            return nsCString("nonfunction");
+
+        RootedId id(cx, idArray[0]);
+        RootedValue v(cx);
+        if (!JS_GetPropertyById(cx, inner, id, &v)) {
+            JS_ClearPendingException(cx);
+            return nsCString("nonfunction");
+        }
+
+        if (!v.isObject())
+            return nsCString("nonfunction");
+
+        RootedObject vobj(cx, &v.toObject());
+        return GetFunctionName(cx, vobj);
+    }
+
+    RootedString funName(cx, JS_GetFunctionDisplayId(fun));
+    RootedScript script(cx, JS_GetFunctionScript(cx, fun));
+    const char* filename = script ? JS_GetScriptFilename(script) : "anonymous";
+    const char* filenameSuffix = strrchr(filename, '/');
+
+    if (filenameSuffix) {
+        filenameSuffix++;
+    } else {
+        filenameSuffix = filename;
+    }
+
+    nsCString displayName("anonymous");
+    if (funName) {
+        nsCString* displayNamePtr = &displayName;
+        RootedValue funNameVal(cx, StringValue(funName));
+        if (!XPCConvert::JSData2Native(&displayNamePtr, funNameVal, nsXPTType::T_UTF8STRING,
+                                       nullptr, nullptr))
+        {
+            JS_ClearPendingException(cx);
+            return nsCString("anonymous");
+        }
+    }
+
+    displayName.Append('[');
+    displayName.Append(filenameSuffix, strlen(filenameSuffix));
+    displayName.Append(']');
+    return displayName;
+}
+
+} // anonymous namespace
+
+/***************************************************************************/
+
 // static
 bool
 nsXPCWrappedJSClass::IsWrappedJS(nsISupports* aPtr)
 {
     void* result;
     NS_PRECONDITION(aPtr, "null pointer");
     return aPtr &&
            NS_OK == aPtr->QueryInterface(NS_GET_IID(WrappedJSIdentity), &result) &&
@@ -585,16 +678,26 @@ nsXPCWrappedJSClass::DelegatedQueryInter
             // We need to go through the QueryInterface logic to make
             // this return the right thing for the various 'special'
             // interfaces; e.g.  nsIPropertyBag.
             rv = wrapper->QueryInterface(aIID, aInstancePtr);
             return rv;
         }
     }
 
+    // If we're asked to QI to nsINamed, we pretend that this is possible. We'll
+    // try to return a name that makes sense for the wrapped JS value.
+    if (aIID.Equals(NS_GET_IID(nsINamed))) {
+        RootedObject obj(RootingCx(), self->GetJSObject());
+        nsCString name = GetFunctionName(ccx, obj);
+        RefPtr<WrappedJSNamed> named = new WrappedJSNamed(name);
+        *aInstancePtr = named.forget().take();
+        return NS_OK;
+    }
+
     // else...
     // no can do
     *aInstancePtr = nullptr;
     return NS_NOINTERFACE;
 }
 
 JSObject*
 nsXPCWrappedJSClass::GetRootJSObject(JSContext* cx, JSObject* aJSObjArg)
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_function_names.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var Cu = Components.utils;
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function callback() {}
+
+let sandbox = Cu.Sandbox(this);
+let callbackWrapped = Cu.evalInSandbox("(function wrapped() {})", sandbox);
+
+function run_test() {
+  let functions = [
+    [{ notify: callback }, "callback[test_function_names.js]:JS"],
+    [{ notify: { notify: callback } }, "callback[test_function_names.js]:JS"],
+    [callback, "callback[test_function_names.js]:JS"],
+    [function() {}, "run_test/functions<[test_function_names.js]:JS"],
+    [function foobar() {}, "foobar[test_function_names.js]:JS"],
+    [function Δ() {}, "Δ[test_function_names.js]:JS"],
+    [{ notify1: callback, notify2: callback }, "nonfunction:JS"],
+    [{ notify: 10 }, "nonfunction:JS"],
+    [{}, "nonfunction:JS"],
+    [{ notify: callbackWrapped }, "wrapped[test_function_names.js]:JS"],
+  ];
+
+  // Use the observer service so we can get double-wrapped functions.
+  var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+
+  function observer(subject, topic, data)
+  {
+    let named = subject.QueryInterface(Ci.nsINamed);
+    do_check_eq(named.name, data);
+    dump(`name: ${named.name}\n`);
+  }
+  obs.addObserver(observer, "test-obs-fun", false);
+
+  for (let [f, requiredName] of functions) {
+    obs.notifyObservers(f, "test-obs-fun", requiredName);
+  }
+}
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -131,8 +131,9 @@ head = head_watchdog.js
 [test_xpcwn_tamperproof.js]
 [test_xrayed_iterator.js]
 [test_xray_named_element_access.js]
 [test_xray_SavedFrame.js]
 [test_xray_SavedFrame-02.js]
 [test_xray_regexp.js]
 [test_resolve_dead_promise.js]
 [test_asyncLoadSubScriptError.js]
+[test_function_names.js]
--- a/layout/reftests/backgrounds/reftest.list
+++ b/layout/reftests/backgrounds/reftest.list
@@ -154,18 +154,18 @@ HTTP == background-referrer.html backgro
 == attachment-local-positioning-2.html attachment-local-positioning-2-ref.html
 == attachment-local-positioning-3.html attachment-local-positioning-3-ref.html
 == attachment-local-positioning-4.html attachment-local-positioning-4-ref.html
 == attachment-local-positioning-5.html attachment-local-positioning-5-ref.html
 
 == attachment-local-clipping-color-1.html attachment-local-clipping-color-1-ref.html
 == attachment-local-clipping-color-2.html attachment-local-clipping-color-1-ref.html  # Same ref as the previous test.
 == attachment-local-clipping-color-3.html attachment-local-clipping-color-3-ref.html
-fuzzy-if(skiaContent,1,300) fuzzy-if(webrender,14-14,1134-1134) == attachment-local-clipping-color-4.html attachment-local-clipping-color-4-ref.html
-fuzzy-if(skiaContent,1,400) fuzzy-if(webrender,14-14,1134-1134) == attachment-local-clipping-color-5.html attachment-local-clipping-color-4-ref.html
+fuzzy-if(skiaContent,1,300) fuzzy-if(webrender,14-14,1119-1134) == attachment-local-clipping-color-4.html attachment-local-clipping-color-4-ref.html
+fuzzy-if(skiaContent,1,400) fuzzy-if(webrender,14-14,1119-1134) == attachment-local-clipping-color-5.html attachment-local-clipping-color-4-ref.html
 fuzzy(50,500) fuzzy-if(skiaContent,51,320) fails-if(webrender) == attachment-local-clipping-color-6.html attachment-local-clipping-color-6-ref.html
 
 == attachment-local-clipping-image-1.html attachment-local-clipping-image-1-ref.html
 == attachment-local-clipping-image-2.html attachment-local-clipping-image-1-ref.html  # Same ref as the previous test.
 == attachment-local-clipping-image-3.html attachment-local-clipping-image-3-ref.html
 # The next three tests are fuzzy due to bug 1128229.
 fuzzy(16,69) fuzzy-if(skiaContent,95,2200) == attachment-local-clipping-image-4.html attachment-local-clipping-image-4-ref.html
 fuzzy(16,69) fuzzy-if(skiaContent,95,2200) == attachment-local-clipping-image-5.html attachment-local-clipping-image-4-ref.html
--- a/layout/reftests/border-radius/reftest.list
+++ b/layout/reftests/border-radius/reftest.list
@@ -47,17 +47,17 @@ fuzzy-if(skiaContent,17,62) == clipping-
 fuzzy-if(true,1,20) fuzzy-if(d2d,64,196) fuzzy-if(cocoaWidget,1,180) fuzzy-if(Android,140,237) == clipping-4-canvas.html clipping-4-ref.html # bug 732535
 fuzzy-if(Android,5,54) fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,10) fuzzy-if(skiaContent,1,172) == clipping-4-image.html clipping-4-ref.html
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,10) fuzzy-if(skiaContent,1,77) == clipping-4-overflow-hidden.html clipping-4-ref.html
 == clipping-5-canvas.html clipping-5-refc.html
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,5) == clipping-5-image.html clipping-5-refi.html
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,5) fuzzy-if(skiaContent,1,77) == clipping-5-overflow-hidden.html clipping-5-ref.html
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,5) fuzzy-if(Android,5,21) fuzzy-if(skiaContent,1,97) == clipping-5-refi.html clipping-5-ref.html
 fuzzy-if(true,1,7) fuzzy-if(d2d,48,94) fuzzy-if(cocoaWidget,1,99) fuzzy-if(Android,99,115) fuzzy-if(skiaContent,1,77) == clipping-5-refc.html clipping-5-ref.html # bug 732535
-fuzzy-if(winWidget,105,71) fuzzy-if(Android,8,469) fuzzy-if(skiaContent,7,58) fuzzy-if(d3d11&&advancedLayers,120,319) fuzzy-if(webrender,7-7,62-62) fuzzy-if(winWidget&&stylo,137,226-319) == clipping-6.html clipping-6-ref.html # PaintedLayer and MaskLayer with transforms that aren't identical
+fuzzy-if(winWidget,105,71) fuzzy-if(Android,8,469) fuzzy-if(skiaContent,7,58) fuzzy-if(d3d11&&advancedLayers,120,319) fuzzy-if(webrender,7-7,59-62) fuzzy-if(winWidget&&stylo,137,226-319) == clipping-6.html clipping-6-ref.html # PaintedLayer and MaskLayer with transforms that aren't identical
 fuzzy-if(true,2,29) fuzzy-if(d2d,46,50) fuzzy-if(Android,255,586) fuzzy-if(skiaContent,28,96) == clipping-7.html clipping-7-ref.html # ColorLayer and MaskLayer with transforms that aren't identical. Reference image rendered without using layers (which causes fuzzy failures).
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,5) == clipping-and-zindex-1.html clipping-and-zindex-1-ref.html
 fuzzy-if(cocoaWidget,1,4) fuzzy-if(d3d11&&advancedLayers,30,3) == intersecting-clipping-1-canvas.html intersecting-clipping-1-refc.html
 == intersecting-clipping-1-image.html intersecting-clipping-1-refi.html
 == intersecting-clipping-1-overflow-hidden.html intersecting-clipping-1-ref.html
 fuzzy-if(Android,5,105) fuzzy-if(d2d,1,20) fuzzy-if(skiaContent,1,300) == intersecting-clipping-1-refi.html intersecting-clipping-1-ref.html
 fuzzy-if(true,1,33) fuzzy-if(d2d,48,350) fuzzy-if(cocoaWidget,1,332) fuzzy-if(Android,124,440) fuzzy-if(skiaContent,1,135) fuzzy-if(d3d11&&advancedLayers,48,353) == intersecting-clipping-1-refc.html intersecting-clipping-1-ref.html # bug 732535
 
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1133,17 +1133,17 @@ fails-if(!styloVsGecko) == 428810-3e-rtl
 == 431341-1.html 431341-1-ref.html
 == 431341-2.html 431341-2-ref.html
 == 431520-1.html 431520-1-ref.html
 == 431948-1.html 431948-1-ref.html
 == 433640-1.html 433640-1-ref.html
 == 433700.html 433700-ref.html
 == 436356-1.html 436356-1-ref.html
 == 436356-2.html 436356-2-ref.html
-fuzzy-if(skiaContent,3,1) == 438537-1.html 438537-1-ref.html
+fuzzy-if(skiaContent,4,2) == 438537-1.html 438537-1-ref.html
 == 438981-1.xhtml about:blank
 == 438987-1.html 438987-1-ref.html
 fuzzy-if(skiaContent,1,3280) == 438987-2a.html 438987-2-ref.html
 fuzzy-if(skiaContent,1,3280) == 438987-2b.html 438987-2-ref.html
 fuzzy-if(skiaContent,1,3280) == 438987-2c.html 438987-2-ref.html
 != about:blank 438987-2-ref.html # check that backgrounds work at all
 == 439004-1.html 439004-1-ref.html
 == 439639-1.html 439639-1-ref.html
@@ -1658,17 +1658,17 @@ fuzzy-if(skiaContent,1,500) == 634232-1.
 fuzzy-if(skiaContent,3,120000) == 635302-1.html 635302-1-ref.html
 fuzzy(1,68) fuzzy-if(gtkWidget,1,70) fails-if(Android) fuzzy-if(skiaContent&&!Android,1,300) == 635373-1.html 635373-1-ref.html
 random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,20,118) fuzzy-if(skiaContent&&!Android,2,550) == 635373-2.html 635373-2-ref.html
 random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,20,116) fuzzy-if(skiaContent&&!Android,2,650) == 635373-3.html 635373-3-ref.html
 HTTP(..) == 635639-1.html 635639-1-ref.html
 HTTP(..) == 635639-2.html 635639-2-ref.html
 random == 637597-1.html 637597-1-ref.html # bug 637597 was never really fixed!
 fuzzy-if(Android,8,500) == 637852-1.html 637852-1-ref.html
-fuzzy-if(Android,8,500) fuzzy-if(skiaContent,2,1) fuzzy-if(webrender,3,19) == 637852-2.html 637852-2-ref.html
+fuzzy-if(Android,8,500) fuzzy-if(skiaContent,3,1) fuzzy-if(webrender,3,19) == 637852-2.html 637852-2-ref.html
 fuzzy-if(Android,8,500) == 637852-3.html 637852-3-ref.html
 fails-if(webrender&&asyncPan) == 641770-1.html 641770-1-ref.html # bug 1374326 for webrender+APZ
 == 641856-1.html 641856-1-ref.html
 == 645491-1.html 645491-1-ref.html
 == 645647-1.html 645647-1-ref.html
 == 645647-2.html 645647-2-ref.html
 == 645768-1.html 645768-1-ref.html
 fails-if(layersGPUAccelerated&&cocoaWidget) fuzzy-if(!layersGPUAccelerated,41,260) fuzzy-if(skiaContent,57,11000) == 650228-1.html 650228-1-ref.html # Quartz alpha blending doesn't match GL alpha blending
--- a/layout/reftests/printing/reftest.list
+++ b/layout/reftests/printing/reftest.list
@@ -1,17 +1,17 @@
 # Sanity check
 == blank.html blank.html
 
 # Bugs
 == 272830-1.html 272830-1-ref.html
 == 318022-1.html 318022-1-ref.html
 == 403669-1.html 403669-1-ref.html
 == 381497-n.html 381497-f.html
-== test-async-print.html 272830-1-ref.html
+== test-async-paged.html 272830-1-ref.html
 == 129941-1a.html 129941-1-ref.html
 == 129941-1b.html 129941-1-ref.html
 == 129941-1c.html 129941-1-ref.html
 == 129941-1d.html 129941-1-ref.html
 == 129941-1e.html 129941-1-ref.html
 == 609227-1.html 609227-1-ref.html
 == 609227-2a.html 609227-2-ref.html
 == 609227-2b.html 609227-2-ref.html
rename from layout/reftests/printing/test-async-print.html
rename to layout/reftests/printing/test-async-paged.html
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -560,16 +560,21 @@ pref("apz.fling_curve_threshold_inches_p
 // apz.fling_friction and apz.fling_stopped_threshold are currently ignored by Fennec.
 pref("apz.fling_friction", "0.004");
 pref("apz.fling_stopped_threshold", "0.0");
 pref("apz.max_velocity_inches_per_ms", "0.07");
 pref("apz.overscroll.enabled", true);
 pref("apz.touch_move_tolerance", "0.03");
 pref("apz.touch_start_tolerance", "0.06");
 
+#ifdef NIGHTLY_BUILD
+// Temporary fix of Bug 1390145 for Fennec Nightly
+pref("apz.frame_delay.enabled", false);
+#endif
+
 pref("layers.progressive-paint", true);
 pref("layers.low-precision-buffer", true);
 pref("layers.low-precision-resolution", "0.25");
 pref("layers.low-precision-opacity", "1.0");
 // We want to limit layers for two reasons:
 // 1) We can't scroll smoothly if we have to many draw calls
 // 2) Pages that have too many layers consume too much memory and crash.
 // By limiting the number of layers on mobile we're making the main thread
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -127,28 +127,16 @@ public class GeckoAppShell
             extras.putString("Version", AppConstants.MOZ_APP_VERSION);
             extras.putString("BuildID", AppConstants.MOZ_APP_BUILDID);
             extras.putString("Vendor", AppConstants.MOZ_APP_VENDOR);
             extras.putString("ReleaseChannel", AppConstants.MOZ_UPDATE_CHANNEL);
             return extras;
         }
 
         @Override
-        public void uncaughtException(final Thread thread, final Throwable exc) {
-            if (GeckoThread.isState(GeckoThread.State.EXITING) ||
-                    GeckoThread.isState(GeckoThread.State.EXITED)) {
-                // We've called System.exit. All exceptions after this point are Android
-                // berating us for being nasty to it.
-                return;
-            }
-
-            super.uncaughtException(thread, exc);
-        }
-
-        @Override
         public boolean reportException(final Thread thread, final Throwable exc) {
             try {
                 if (exc instanceof OutOfMemoryError) {
                     final SharedPreferences prefs =
                             GeckoSharedPrefs.forApp(getApplicationContext());
                     final SharedPreferences.Editor editor = prefs.edit();
                     editor.putBoolean(PREFS_OOM_EXCEPTION, true);
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java
@@ -74,27 +74,38 @@ public final class HardwareUtils {
     public static boolean isTelevision() {
         return sIsTelevision;
     }
 
     public static int getMemSize() {
         return SysInfo.getMemSize();
     }
 
+    private static String getPreferredAbi() {
+        String abi = null;
+        if (Build.VERSION.SDK_INT >= 21) {
+            abi = Build.SUPPORTED_ABIS[0];
+        }
+        if (abi == null) {
+            abi = Build.CPU_ABI;
+        }
+        return abi;
+    }
+
     public static boolean isARMSystem() {
-        return Build.CPU_ABI != null && Build.CPU_ABI.equals("armeabi-v7a");
+        return "armeabi-v7a".equals(getPreferredAbi());
     }
 
     public static boolean isARM64System() {
         // 64-bit support was introduced in 21.
-        return Build.VERSION.SDK_INT >= 21 && "arm64-v8a".equals(Build.SUPPORTED_ABIS[0]);
+        return "arm64-v8a".equals(getPreferredAbi());
     }
 
     public static boolean isX86System() {
-        if (Build.CPU_ABI != null && Build.CPU_ABI.equals("x86")) {
+        if ("x86".equals(getPreferredAbi())) {
             return true;
         }
         if (Build.VERSION.SDK_INT >= 21) {
             // On some devices we have to look into the kernel release string.
             try {
                 return Os.uname().release.contains("-x86_");
             } catch (final Exception e) {
                 Log.w(LOGTAG, "Cannot get uname", e);
@@ -104,17 +115,17 @@ public final class HardwareUtils {
     }
 
     public static String getRealAbi() {
         if (isX86System() && isARMSystem()) {
             // Some x86 devices try to make us believe we're ARM,
             // in which case CPU_ABI is not reliable.
             return "x86";
         }
-        return Build.CPU_ABI;
+        return getPreferredAbi();
     }
 
     /**
      * @return false if the current system is not supported (e.g. APK/system ABI mismatch).
      */
     public static boolean isSupportedSystem() {
         // We've had crash reports from users on API 10 (with minSDK==15). That shouldn't even install,
         // but since it does we need to protect against it:
@@ -136,12 +147,13 @@ public final class HardwareUtils {
             return false;
         }
 
         if ((isSystemX86 && isAppX86) || (isSystemARM && isAppARM) ||
             (isSystemARM64 && (isAppARM64 || isAppARM))) {
             return true;
         }
 
-        Log.w(LOGTAG, "Unknown app/system ABI combination: " + BuildConfig.MOZ_APP_ABI + " / " + Build.CPU_ABI);
+        Log.w(LOGTAG, "Unknown app/system ABI combination: " +
+                      BuildConfig.MOZ_APP_ABI + " / " + getRealAbi());
         return true;
     }
 }
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -2015,17 +2015,16 @@ STS_PRCloseOnSocketTransport(PRFileDesc 
 }
 
 void
 nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
 {
     mLock.AssertCurrentThreadOwns();
 
     NS_ASSERTION(mFD == fd, "wrong fd");
-    SOCKET_LOG(("JIMB: ReleaseFD_Locked: mFDref = %" PRIuPTR "\n", mFDref));
 
     if (--mFDref == 0) {
         if (gIOService->IsNetTearingDown() &&
             ((PR_IntervalNow() - gIOService->NetTearingDownStarted()) >
              gSocketTransportService->MaxTimeForPrClosePref())) {
           // If shutdown last to long, let the socket leak and do not close it.
           SOCKET_LOG(("Intentional leak"));
         } else if (OnSocketThread()) {
--- a/testing/web-platform/tests/intersection-observer/timestamp.html
+++ b/testing/web-platform/tests/intersection-observer/timestamp.html
@@ -13,38 +13,27 @@ pre, #log {
   height: calc(100vh + 100px);
 }
 
 </style>
 <div id="leading-space" class="spacer"></div>
 <div id="trailing-space" class="spacer"></div>
 
 <script>
+// Pick this number to be comfortably greater than the length of two frames at 60Hz.
+var timeSkew = 40;
+
 var topWindowEntries = [];
 var iframeWindowEntries = [];
 var targetIframe;
-var topWindowTimeOnTestStart;
-var topWindowTimeBeforeCreatingIframe;
 var topWindowTimeBeforeNotification;
 var iframeWindowTimeBeforeNotification;
-var timeSkew;
-
-function waitFor(numFrames, callback) {
-  if (numFrames <= 0) {
-    callback();
-    return;
-  }
-  window.requestAnimationFrame(waitFor.bind(null, numFrames - 1, callback));
-}
 
 async_test(function(t) {
-  topWindowTimeOnTestStart = performance.now();
-  waitFor(3, function () {
-    topWindowTimeBeforeCreatingIframe = performance.now();
-    timeSkew = topWindowTimeBeforeCreatingIframe - topWindowTimeOnTestStart;
+  t.step_timeout(function() {
     targetIframe = document.createElement("iframe");
     assert_true(!!targetIframe, "iframe exists");
     targetIframe.src = "resources/timestamp-subframe.html";
     var trailingSpace = document.getElementById("trailing-space");
     assert_true(!!trailingSpace, "trailing-space exists");
     trailingSpace.parentNode.insertBefore(targetIframe, trailingSpace);
     targetIframe.onload = function() {
       var target = targetIframe.contentDocument.getElementById("target");
@@ -61,17 +50,17 @@ async_test(function(t) {
       // from iframe window.
       observer = targetIframe.contentDocument.createObserver(function(newEntries) {
         iframeWindowEntries = iframeWindowEntries.concat(newEntries);
       });
       observer.observe(target);
       runTestCycle(step1, "First rAF after iframe is loaded.");
       t.done();
     };
-  });
+  }, timeSkew);
 }, "Check that timestamps correspond to the to execution context that created the observer.");
 
 function step1() {
   document.scrollingElement.scrollTop = 200;
   targetIframe.contentDocument.scrollingElement.scrollTop = 250;
   topWindowTimeBeforeNotification = performance.now();
   iframeWindowTimeBeforeNotification = targetIframe.contentWindow.performance.now();
   runTestCycle(step2, "Generate notifications.");
@@ -79,26 +68,24 @@ function step1() {
   assert_equals(iframeWindowEntries.length, 1, "One notification to iframe observer.");
 }
 
 function step2() {
   document.scrollingElement.scrollTop = 0;
   var topWindowTimeAfterNotification = performance.now();
   var iframeWindowTimeAfterNotification = targetIframe.contentWindow.performance.now();
 
-  // Test results are only significant if there's a gap between
-  // top window time and iframe window time.
-  assert_greater_than(topWindowTimeBeforeNotification, iframeWindowTimeAfterNotification,
-    "Time ranges for top and iframe windows are disjoint. Times: " +
-      [topWindowTimeOnTestStart, topWindowTimeBeforeCreatingIframe,
-       topWindowTimeBeforeNotification, topWindowTimeAfterNotification,
-       iframeWindowTimeBeforeNotification, iframeWindowTimeAfterNotification,
-       topWindowEntries[1].time - topWindowTimeBeforeNotification,
-       iframeWindowEntries[1].time - iframeWindowTimeBeforeNotification
-      ]);
+  assert_approx_equals(
+      topWindowEntries[1].time - topWindowTimeBeforeNotification,
+      iframeWindowEntries[1].time - iframeWindowTimeBeforeNotification,
+      // Since all intersections are computed in a tight loop between 2 frames,
+      // an epsilon of 16ms (the length of one frame at 60Hz) turned out to be
+      // reliable, even at slow frame rates.
+      16,
+      "Notification times are relative to the expected time origins");
 
   assert_equals(topWindowEntries.length, 2, "Top window observer has two notifications.");
   assert_between_inclusive(
       topWindowEntries[1].time,
       topWindowTimeBeforeNotification,
       topWindowTimeAfterNotification,
       "Notification to top window observer is within the expected range.");
 
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -36,20 +36,16 @@ const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.importGlobalProperties(["TextEncoder"]);
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
-/* globals processCount */
-
-XPCOMUtils.defineLazyPreferenceGetter(this, "processCount", "dom.ipc.processCount.extension");
-
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManager: "resource://gre/modules/AddonManager.jsm",
   AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
   AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
   ExtensionCommon: "resource://gre/modules/ExtensionCommon.jsm",
   ExtensionPermissions: "resource://gre/modules/ExtensionPermissions.jsm",
   ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm",
   ExtensionTestCommon: "resource://testing-common/ExtensionTestCommon.jsm",
@@ -70,16 +66,17 @@ XPCOMUtils.defineLazyGetter(
 Cu.import("resource://gre/modules/ExtensionParent.jsm");
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetters(this, {
   aomStartup: ["@mozilla.org/addons/addon-manager-startup;1", "amIAddonManagerStartup"],
   uuidGen: ["@mozilla.org/uuid-generator;1", "nsIUUIDGenerator"],
 });
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "processCount", "dom.ipc.processCount.extension");
 XPCOMUtils.defineLazyPreferenceGetter(this, "useRemoteWebExtensions",
                                       "extensions.webextensions.remote", false);
 
 var {
   GlobalManager,
   ParentAPIManager,
   StartupCache,
   apiManager: Management,
@@ -226,18 +223,16 @@ function getExtensionUUID(id) {
 // For extensions that have called setUninstallURL(), send an event
 // so the browser can display the URL.
 var UninstallObserver = {
   initialized: false,
 
   init() {
     if (!this.initialized) {
       AddonManager.addAddonListener(this);
-      XPCOMUtils.defineLazyPreferenceGetter(this, "leaveStorage", LEAVE_STORAGE_PREF, false);
-      XPCOMUtils.defineLazyPreferenceGetter(this, "leaveUuid", LEAVE_UUID_PREF, false);
       this.initialized = true;
     }
   },
 
   onUninstalling(addon) {
     let extension = GlobalManager.extensionMap.get(addon.id);
     if (extension) {
       // Let any other interested listeners respond
@@ -247,17 +242,17 @@ var UninstallObserver = {
   },
 
   onUninstalled(addon) {
     let uuid = UUIDMap.get(addon.id, false);
     if (!uuid) {
       return;
     }
 
-    if (!this.leaveStorage) {
+    if (!Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false)) {
       // Clear browser.local.storage
       AsyncShutdown.profileChangeTeardown.addBlocker(
         `Clear Extension Storage ${addon.id}`,
         ExtensionStorage.clear(addon.id));
 
       // Clear any IndexedDB storage created by the extension
       let baseURI = Services.io.newURI(`moz-extension://${uuid}/`);
       let principal = Services.scriptSecurityManager.createCodebasePrincipal(
@@ -272,17 +267,17 @@ var UninstallObserver = {
 
       // Remove any permissions related to the unlimitedStorage permission
       // if we are also removing all the data stored by the extension.
       Services.perms.removeFromPrincipal(principal, "WebExtensions-unlimitedStorage");
       Services.perms.removeFromPrincipal(principal, "indexedDB");
       Services.perms.removeFromPrincipal(principal, "persistent-storage");
     }
 
-    if (!this.leaveUuid) {
+    if (!Services.prefs.getBoolPref(LEAVE_UUID_PREF, false)) {
       // Clear the entry in the UUID map
       UUIDMap.remove(addon.id);
     }
   },
 };
 
 UninstallObserver.init();
 
@@ -292,16 +287,17 @@ UninstallObserver.init();
 // primarily related to manifest parsing and localization, which is
 // useful prior to extension installation or initialization.
 //
 // No functionality of this class is guaranteed to work before
 // |loadManifest| has been called, and completed.
 this.ExtensionData = class {
   constructor(rootURI) {
     this.rootURI = rootURI;
+    this.resourceURL = rootURI.spec;
 
     this.manifest = null;
     this.id = null;
     this.uuid = null;
     this.localeData = null;
     this._promiseLocales = null;
 
     this.apiNames = new Set();
@@ -524,77 +520,100 @@ this.ExtensionData = class {
 
       if (this.localeData) {
         context.preprocessors.localize = (value, context) => this.localize(value);
       }
 
       let normalized = Schemas.normalize(this.manifest, "manifest.WebExtensionManifest", context);
       if (normalized.error) {
         this.manifestError(normalized.error);
-      } else {
-        return normalized.value;
+        return null;
+      }
+
+      let manifest = normalized.value;
+
+      let id;
+      try {
+        if (manifest.applications.gecko.id) {
+          id = manifest.applications.gecko.id;
+        }
+      } catch (e) {
+        // Errors are handled by the type checks above.
       }
+
+      let apiNames = new Set();
+      let dependencies = new Set();
+      let hostPermissions = new Set();
+      let permissions = new Set();
+
+      for (let perm of manifest.permissions) {
+        if (perm === "geckoProfiler") {
+          const acceptedExtensions = Services.prefs.getStringPref("extensions.geckoProfiler.acceptedExtensionIds", "");
+          if (!acceptedExtensions.split(",").includes(id)) {
+            this.manifestError("Only whitelisted extensions are allowed to access the geckoProfiler.");
+            continue;
+          }
+        }
+
+        let type = classifyPermission(perm);
+        if (type.origin) {
+          let matcher = new MatchPattern(perm, {ignorePath: true});
+
+          perm = matcher.pattern;
+          hostPermissions.add(perm);
+        } else if (type.api) {
+          apiNames.add(type.api);
+        }
+
+        permissions.add(perm);
+      }
+
+      // An extension always gets permission to its own url.
+      if (this.id) {
+        let matcher = new MatchPattern(this.getURL(), {ignorePath: true});
+        hostPermissions.add(matcher.pattern);
+      }
+
+      for (let api of apiNames) {
+        dependencies.add(`${api}@experiments.addons.mozilla.org`);
+      }
+
+      // Normalize all patterns to contain a single leading /
+      let webAccessibleResources = (manifest.web_accessible_resources || [])
+          .map(path => path.replace(/^\/*/, "/"));
+
+      return {apiNames, dependencies, hostPermissions, id, manifest, permissions,
+              webAccessibleResources};
     });
   }
 
   // Reads the extension's |manifest.json| file, and stores its
   // parsed contents in |this.manifest|.
   async loadManifest() {
-    [this.manifest] = await Promise.all([
+    let [manifestData] = await Promise.all([
       this.parseManifest(),
       Management.lazyInit(),
     ]);
 
-    if (!this.manifest) {
+    if (!manifestData) {
       return;
     }
 
-    try {
-      // Do not override the add-on id that has been already assigned.
-      if (!this.id && this.manifest.applications.gecko.id) {
-        this.id = this.manifest.applications.gecko.id;
-      }
-    } catch (e) {
-      // Errors are handled by the type checks above.
+    // Do not override the add-on id that has been already assigned.
+    if (!this.id) {
+      this.id = manifestData.id;
     }
 
-    let whitelist = [];
-    for (let perm of this.manifest.permissions) {
-      if (perm === "geckoProfiler") {
-        const acceptedExtensions = Services.prefs.getStringPref("extensions.geckoProfiler.acceptedExtensionIds", "");
-        if (!acceptedExtensions.split(",").includes(this.id)) {
-          this.manifestError("Only whitelisted extensions are allowed to access the geckoProfiler.");
-          continue;
-        }
-      }
-
-      let type = classifyPermission(perm);
-      if (type.origin) {
-        let matcher = new MatchPattern(perm, {ignorePath: true});
+    this.manifest = manifestData.manifest;
+    this.apiNames = manifestData.apiNames;
+    this.dependencies = manifestData.dependencies;
+    this.permissions = manifestData.permissions;
 
-        whitelist.push(matcher);
-        perm = matcher.pattern;
-      } else if (type.api) {
-        this.apiNames.add(type.api);
-      }
-
-      this.permissions.add(perm);
-    }
-
-    // An extension always gets permission to its own url.
-    if (this.id) {
-      let matcher = new MatchPattern(this.getURL(), {ignorePath: true});
-      whitelist.push(matcher);
-    }
-
-    this.whiteListedHosts = new MatchPatternSet(whitelist);
-
-    for (let api of this.apiNames) {
-      this.dependencies.add(`${api}@experiments.addons.mozilla.org`);
-    }
+    this.webAccessibleResources = manifestData.webAccessibleResources.map(res => new MatchGlob(res));
+    this.whiteListedHosts = new MatchPatternSet(manifestData.hostPermissions);
 
     return this.manifest;
   }
 
   localizeMessage(...args) {
     return this.localeData.localizeMessage(...args);
   }
 
@@ -942,56 +961,61 @@ this.Extension = class extends Extension
       });
   }
 
   parseManifest() {
     return StartupCache.manifests.get([this.id, this.version, Services.locale.getAppLocaleAsLangTag()],
                                       () => super.parseManifest());
   }
 
-  loadManifest() {
-    return super.loadManifest().then(manifest => {
-      if (this.errors.length) {
-        return Promise.reject({errors: this.errors});
-      }
+  async loadManifest() {
+    let manifest = await super.loadManifest();
 
+    if (this.errors.length) {
+      return Promise.reject({errors: this.errors});
+    }
+
+    if (this.apiNames.size) {
       // Load Experiments APIs that this extension depends on.
-      return Promise.all(
-        Array.from(this.apiNames, api => ExtensionCommon.ExtensionAPIs.load(api))
-      ).then(apis => {
-        for (let API of apis) {
-          this.apis.push(new API(this));
-        }
+      let apis = await Promise.all(
+        Array.from(this.apiNames, api => ExtensionCommon.ExtensionAPIs.load(api)));
 
-        return manifest;
-      });
-    });
+      for (let API of apis) {
+        this.apis.push(new API(this));
+      }
+    }
+
+    return manifest;
   }
 
   // Representation of the extension to send to content
   // processes. This should include anything the content process might
   // need.
   serialize() {
     return {
       id: this.id,
       uuid: this.uuid,
       instanceId: this.instanceId,
       manifest: this.manifest,
-      resourceURL: this.addonData.resourceURI.spec,
+      resourceURL: this.resourceURL,
       baseURL: this.baseURI.spec,
-      content_scripts: this.manifest.content_scripts || [],  // eslint-disable-line camelcase
+      contentScripts: this.contentScripts,
       webAccessibleResources: this.webAccessibleResources.map(res => res.glob),
       whiteListedHosts: this.whiteListedHosts.patterns.map(pat => pat.pattern),
       localeData: this.localeData.serialize(),
       permissions: this.permissions,
       principal: this.principal,
       optionalPermissions: this.manifest.optional_permissions,
     };
   }
 
+  get contentScripts() {
+    return this.manifest.content_scripts || [];
+  }
+
   broadcast(msg, data) {
     return new Promise(resolve => {
       let {ppmm} = Services;
       let children = new Set();
       for (let i = 0; i < ppmm.childCount; i++) {
         children.add(ppmm.getChildAt(i));
       }
 
@@ -1070,38 +1094,54 @@ this.Extension = class extends Extension
         this.defaultLocale);
 
       locale = matches[0];
     }
 
     return super.initLocale(locale);
   }
 
-  initUnlimitedStoragePermission() {
-    const principal = this.principal;
+  updatePermissions(reason) {
+    const {principal} = this;
 
-    // Check if the site permission has already been set for the extension by the WebExtensions
-    // internals (instead of being manually allowed by the user).
-    const hasSitePermission = Services.perms.testPermissionFromPrincipal(
-      principal, "WebExtensions-unlimitedStorage"
-    );
+    const testPermission = perm =>
+      Services.perms.testPermissionFromPrincipal(principal, perm);
 
-    if (this.hasPermission("unlimitedStorage")) {
-      // Set the indexedDB permission and a custom "WebExtensions-unlimitedStorage" to remember
-      // that the permission hasn't been selected manually by the user.
-      Services.perms.addFromPrincipal(principal, "WebExtensions-unlimitedStorage",
-                                      Services.perms.ALLOW_ACTION);
-      Services.perms.addFromPrincipal(principal, "indexedDB", Services.perms.ALLOW_ACTION);
-      Services.perms.addFromPrincipal(principal, "persistent-storage", Services.perms.ALLOW_ACTION);
-    } else if (hasSitePermission) {
-      // Remove the indexedDB permission if it has been enabled using the
-      // unlimitedStorage WebExtensions permissions.
-      Services.perms.removeFromPrincipal(principal, "WebExtensions-unlimitedStorage");
-      Services.perms.removeFromPrincipal(principal, "indexedDB");
-      Services.perms.removeFromPrincipal(principal, "persistent-storage");
+    // Only update storage permissions when the extension changes in
+    // some way.
+    if (reason !== "APP_STARTUP" && reason !== "APP_SHUTDOWN") {
+      if (this.hasPermission("unlimitedStorage")) {
+        // Set the indexedDB permission and a custom "WebExtensions-unlimitedStorage" to remember
+        // that the permission hasn't been selected manually by the user.
+        Services.perms.addFromPrincipal(principal, "WebExtensions-unlimitedStorage",
+                                        Services.perms.ALLOW_ACTION);
+        Services.perms.addFromPrincipal(principal, "indexedDB", Services.perms.ALLOW_ACTION);
+        Services.perms.addFromPrincipal(principal, "persistent-storage", Services.perms.ALLOW_ACTION);
+      } else {
+        // Remove the indexedDB permission if it has been enabled using the
+        // unlimitedStorage WebExtensions permissions.
+        Services.perms.removeFromPrincipal(principal, "WebExtensions-unlimitedStorage");
+        Services.perms.removeFromPrincipal(principal, "indexedDB");
+        Services.perms.removeFromPrincipal(principal, "persistent-storage");
+      }
+    }
+
+    // Never change geolocation permissions at shutdown, since it uses a
+    // session-only permission.
+    if (reason !== "APP_SHUTDOWN") {
+      if (this.hasPermission("geolocation")) {
+        if (testPermission("geo") === Services.perms.UNKNOWN_ACTION) {
+          Services.perms.addFromPrincipal(principal, "geo",
+                                          Services.perms.ALLOW_ACTION,
+                                          Services.perms.EXPIRE_SESSION);
+        }
+      } else if (reason !== "APP_STARTUP" &&
+                 testPermission("geo") === Services.perms.ALLOW_ACTION) {
+        Services.perms.removeFromPrincipal(principal, "geo");
+      }
     }
   }
 
   startup() {
     this.startupPromise = this._startup();
 
     return this.startupPromise;
   }
@@ -1155,27 +1195,20 @@ this.Extension = class extends Extension
       }
       if (perms.origins.length > 0) {
         let patterns = this.whiteListedHosts.patterns.map(host => host.pattern);
 
         this.whiteListedHosts = new MatchPatternSet([...patterns, ...perms.origins],
                                                     {ignorePath: true});
       }
 
-      // Normalize all patterns to contain a single leading /
-      let resources = (this.manifest.web_accessible_resources || [])
-          .map(path => path.replace(/^\/*/, "/"));
-
-      this.webAccessibleResources = resources.map(res => new MatchGlob(res));
+      this.policy.active = false;
+      this.policy = processScript.initExtension(this);
 
-
-      this.policy.active = false;
-      this.policy = processScript.initExtension(this.serialize(), this);
-
-      this.initUnlimitedStoragePermission();
+      this.updatePermissions(this.startupReason);
 
       // The "startup" Management event sent on the extension instance itself
       // is emitted just before the Management "startup" event,
       // and it is used to run code that needs to be executed before
       // any of the "startup" listeners.
       this.emit("startup", this);
       Management.emit("startup", this);
 
@@ -1276,16 +1309,18 @@ this.Extension = class extends Extension
       StartupCache.clearAddonData(this.id);
     }
 
     let data = Services.ppmm.initialProcessData;
     data["Extension:Extensions"] = data["Extension:Extensions"].filter(e => e.id !== this.id);
 
     Services.ppmm.removeMessageListener(this.MESSAGE_EMIT_EVENT, this);
 
+    this.updatePermissions(this.shutdownReason);
+
     if (!this.manifest) {
       this.policy.active = false;
 
       return this.cleanupGeneratedFile();
     }
 
     GlobalManager.uninit(this);
 
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -494,17 +494,17 @@ class BrowserExtensionContent extends Ev
     this.id = data.id;
     this.uuid = data.uuid;
     this.instanceId = data.instanceId;
 
     this.MESSAGE_EMIT_EVENT = `Extension:EmitEvent:${this.instanceId}`;
     Services.cpmm.addMessageListener(this.MESSAGE_EMIT_EVENT, this);
 
     defineLazyGetter(this, "scripts", () => {
-      return data.content_scripts.map(scriptData => new ExtensionContent.Script(this, scriptData));
+      return data.contentScripts.map(scriptData => new ExtensionContent.Script(this, scriptData));
     });
 
     this.webAccessibleResources = data.webAccessibleResources.map(res => new MatchGlob(res));
     this.whiteListedHosts = new MatchPatternSet(data.whiteListedHosts, {ignorePath: true});
     this.permissions = data.permissions;
     this.optionalPermissions = data.optionalPermissions;
     this.principal = data.principal;
 
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -936,17 +936,17 @@ class SchemaAPIManager extends EventEmit
 
     this.modules = new Map();
     this.modulePaths = {children: new Map(), modules: new Set()};
     this.manifestKeys = new Map();
     this.eventModules = new DefaultMap(() => new Set());
 
     this._modulesJSONLoaded = false;
 
-    this.schemaURLs = new Set();
+    this.schemaURLs = new Map();
 
     this.apis = new DefaultWeakMap(() => new Map());
 
     this._scriptScopes = [];
   }
 
   async loadModuleJSON(urls) {
     function fetchJSON(url) {
@@ -997,17 +997,20 @@ class SchemaAPIManager extends EventEmit
       details.namespaceName = name;
 
       if (this.modules.has(name)) {
         throw new Error(`Module '${name}' already registered`);
       }
       this.modules.set(name, details);
 
       if (details.schema) {
-        this.schemaURLs.add(details.schema);
+        let content = (details.scopes &&
+                       (details.scopes.includes("content_parent") ||
+                        details.scopes.includes("content_child")));
+        this.schemaURLs.set(details.schema, {content});
       }
 
       for (let event of details.events || []) {
         this.eventModules.get(event).add(name);
       }
 
       for (let key of details.manifest || []) {
         if (this.manifestKeys.has(key)) {
@@ -1240,17 +1243,17 @@ class SchemaAPIManager extends EventEmit
    * Create a global object that is used as the shared global for all ext-*.js
    * scripts that are loaded via `loadScript`.
    *
    * @returns {object} A sandbox that is used as the global by `loadScript`.
    */
   _createExtGlobal() {
     let global = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), {
       wantXrays: false,
-      sandboxName: `Namespace of ext-*.js scripts for ${this.processType}`,
+      sandboxName: `Namespace of ext-*.js scripts for ${this.processType} (from: resource://gre/modules/ExtensionCommon.jsm)`,
     });
 
     Object.assign(global, {global, Cc, Ci, Cu, Cr, XPCOMUtils, ChromeWorker, ExtensionAPI, ExtensionCommon, MatchPattern, MatchPatternSet, StructuredCloneHolder, extensions: this});
 
     Cu.import("resource://gre/modules/AppConstants.jsm", global);
 
     XPCOMUtils.defineLazyGetter(global, "console", getConsole);
 
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -113,23 +113,23 @@ let apiManager = new class extends Schem
       this.initModuleData(await modulesPromise);
 
       for (let script of scripts) {
         script.executeInGlobal(this.global);
       }
 
       // Load order matters here. The base manifest defines types which are
       // extended by other schemas, so needs to be loaded first.
-      return Schemas.load(BASE_SCHEMA).then(() => {
+      return Schemas.load(BASE_SCHEMA, AppConstants.DEBUG).then(() => {
         let promises = [];
         for (let [/* name */, url] of XPCOMUtils.enumerateCategoryEntries(CATEGORY_EXTENSION_SCHEMAS)) {
           promises.push(Schemas.load(url));
         }
-        for (let url of this.schemaURLs) {
-          promises.push(Schemas.load(url));
+        for (let [url, {content}] of this.schemaURLs) {
+          promises.push(Schemas.load(url, content));
         }
         for (let url of schemaURLs) {
           promises.push(Schemas.load(url));
         }
         return Promise.all(promises);
       });
     })();
 
@@ -442,17 +442,17 @@ defineLazyGetter(ProxyContextParent.prot
   return can;
 });
 
 defineLazyGetter(ProxyContextParent.prototype, "apiObj", function() {
   return this.apiCan.root;
 });
 
 defineLazyGetter(ProxyContextParent.prototype, "sandbox", function() {
-  return Cu.Sandbox(this.principal);
+  return Cu.Sandbox(this.principal, {sandboxName: this.uri.spec});
 });
 
 /**
  * The parent side of proxied API context for extension content script
  * running in ExtensionContent.jsm.
  */
 class ContentScriptContextParent extends ProxyContextParent {
 }
@@ -559,41 +559,67 @@ class DevToolsExtensionPageContextParent
 
     super.shutdown();
   }
 }
 
 ParentAPIManager = {
   proxyContexts: new Map(),
 
+  parentMessageManagers: new Set(),
+
   init() {
     Services.obs.addObserver(this, "message-manager-close");
+    Services.obs.addObserver(this, "ipc:content-created");
 
     Services.mm.addMessageListener("API:CreateProxyContext", this);
     Services.mm.addMessageListener("API:CloseProxyContext", this, true);
     Services.mm.addMessageListener("API:Call", this);
     Services.mm.addMessageListener("API:AddListener", this);
     Services.mm.addMessageListener("API:RemoveListener", this);
+
+    this.schemaHook = this.schemaHook.bind(this);
   },
 
-  observe(subject, topic, data) {
+  attachMessageManager(extension, processMessageManager) {
+    extension.parentMessageManager = processMessageManager;
+  },
+
+  async observe(subject, topic, data) {
     if (topic === "message-manager-close") {
       let mm = subject;
       for (let [childId, context] of this.proxyContexts) {
         if (context.parentMessageManager === mm) {
           this.closeProxyContext(childId);
         }
       }
 
       // Reset extension message managers when their child processes shut down.
       for (let extension of GlobalManager.extensionMap.values()) {
         if (extension.parentMessageManager === mm) {
           extension.parentMessageManager = null;
         }
       }
+
+      this.parentMessageManagers.delete(mm);
+    } else if (topic === "ipc:content-created") {
+      let mm = subject.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIMessageSender);
+      if (mm.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE) {
+        this.parentMessageManagers.add(mm);
+        mm.sendAsyncMessage("Schema:Add", Schemas.schemaJSON);
+
+        Schemas.schemaHook = this.schemaHook;
+      }
+    }
+  },
+
+  schemaHook(schemas) {
+    for (let mm of this.parentMessageManagers) {
+      mm.sendAsyncMessage("Schema:Add", schemas);
     }
   },
 
   shutdownExtension(extensionId) {
     for (let [childId, context] of this.proxyContexts) {
       if (context.extension.id == extensionId) {
         context.shutdown();
         this.proxyContexts.delete(childId);
@@ -643,17 +669,17 @@ ParentAPIManager = {
     let context;
     if (envType == "addon_parent" || envType == "devtools_parent") {
       let processMessageManager = (target.messageManager.processMessageManager ||
                                    Services.ppmm.getChildAt(0));
 
       if (!extension.parentMessageManager) {
         let expectedRemoteType = extension.remote ? E10SUtils.EXTENSION_REMOTE_TYPE : null;
         if (target.remoteType === expectedRemoteType) {
-          extension.parentMessageManager = processMessageManager;
+          this.attachMessageManager(extension, processMessageManager);
         }
       }
 
       if (processMessageManager !== extension.parentMessageManager) {
         throw new Error("Attempt to create privileged extension parent from incorrect child process");
       }
 
       if (envType == "addon_parent") {
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -231,35 +231,48 @@ class EventEmitter {
     };
     this[ONCE_MAP].set(listener, wrapper);
 
     this.on(event, wrapper);
   }
 
 
   /**
-   * Triggers all listeners for the given event, and returns a promise
-   * which resolves when all listeners have been called, and any
-   * promises they have returned have likewise resolved.
+   * Triggers all listeners for the given event. If any listeners return
+   * a value, returns a promise which resolves when all returned
+   * promises have resolved. Otherwise, returns undefined.
    *
    * @param {string} event
    *       The name of the event to emit.
    * @param {any} args
    *        Arbitrary arguments to pass to the listener functions, after
    *        the event name.
-   * @returns {Promise}
+   * @returns {Promise?}
    */
   emit(event, ...args) {
-    let listeners = this[LISTENERS].get(event) || new Set();
+    let listeners = this[LISTENERS].get(event);
+
+    if (listeners) {
+      let promises = [];
 
-    let promises = Array.from(listeners, listener => {
-      return runSafeSyncWithoutClone(listener, event, ...args);
-    });
+      for (let listener of listeners) {
+        try {
+          let result = listener(event, ...args);
+          if (result !== undefined) {
+            promises.push(result);
+          }
+        } catch (e) {
+          Cu.reportError(e);
+        }
+      }
 
-    return Promise.all(promises);
+      if (promises.length) {
+        return Promise.all(promises);
+      }
+    }
   }
 }
 
 /**
  * A set with a limited number of slots, which flushes older entries as
  * newer ones are added.
  *
  * @param {integer} limit
--- a/toolkit/components/extensions/MessageChannel.jsm
+++ b/toolkit/components/extensions/MessageChannel.jsm
@@ -100,26 +100,22 @@ this.EXPORTED_SYMBOLS = ["MessageChannel
 
 /* globals MessageChannel */
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
-                                  "resource://gre/modules/ExtensionUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
-                                  "resource://gre/modules/PromiseUtils.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "MessageManagerProxy",
-                            () => ExtensionUtils.MessageManagerProxy);
+const {
+  MessageManagerProxy,
+} = ExtensionUtils;
 
 /**
  * Handles the mapping and dispatching of messages to their registered
  * handlers. There is one broker per message manager and class of
  * messages. Each class of messages is mapped to one native message
  * name, e.g., "MessageChannel:Message", and is dispatched to handlers
  * based on an internal message name, e.g., "Extension:ExecuteScript".
  */
@@ -533,17 +529,21 @@ this.MessageChannel = {
       } catch (e) {
         // Caller is not expecting a reply, so dump the error to the console.
         Cu.reportError(e);
         return Promise.reject(e);
       }
       return Promise.resolve();  // Not expecting any reply.
     }
 
-    let deferred = PromiseUtils.defer();
+    let deferred = {};
+    deferred.promise = new Promise((resolve, reject) => {
+      deferred.resolve = resolve;
+      deferred.reject = reject;
+    });
     deferred.sender = recipient;
     deferred.messageManager = target;
     deferred.channelId = channelId;
 
     this._addPendingResponse(deferred);
 
     // The channel ID is used as the message name when routing responses.
     // Add a message listener to the response broker, and remove it once
--- a/toolkit/components/extensions/Schemas.jsm
+++ b/toolkit/components/extensions/Schemas.jsm
@@ -2636,16 +2636,20 @@ this.Schemas = {
   initialized: false,
 
   REVOKE: Symbol("@@revoke"),
 
   // Maps a schema URL to the JSON contained in that schema file. This
   // is useful for sending the JSON across processes.
   schemaJSON: new Map(),
 
+  // A separate map of schema JSON which should be available in all
+  // content processes.
+  contentSchemaJSON: new Map(),
+
   // Map[<schema-name> -> Map[<symbol-name> -> Entry]]
   // This keeps track of all the schemas that have been loaded so far.
   rootNamespace: new Namespace("", []),
 
   getNamespace(name) {
     return this.rootNamespace.getNamespace(name);
   },
 
@@ -2692,17 +2696,19 @@ this.Schemas = {
     }
 
     this.flushSchemas();
   },
 
   receiveMessage(msg) {
     switch (msg.name) {
       case "Schema:Add":
-        this.schemaJSON.set(msg.data.url, msg.data.schema);
+        for (let [url, schema] of msg.data) {
+          this.schemaJSON.set(url, schema);
+        }
         this.flushSchemas();
         break;
 
       case "Schema:Delete":
         this.schemaJSON.delete(msg.data.url);
         this.flushSchemas();
         break;
     }
@@ -2762,39 +2768,45 @@ this.Schemas = {
       this._loadCachedSchemasPromise = StartupCache.schemas.getAll().then(results => {
         return results;
       });
     }
 
     return this._loadCachedSchemasPromise;
   },
 
-  addSchema(url, schema) {
+  addSchema(url, schema, content = false) {
     this.schemaJSON.set(url, schema);
 
-    let data = Services.ppmm.initialProcessData;
-    data["Extension:Schemas"] = this.schemaJSON;
-
-    Services.ppmm.broadcastAsyncMessage("Schema:Add", {url, schema});
+    if (content) {
+      this.contentSchemaJSON.set(url, schema);
+
+      let data = Services.ppmm.initialProcessData;
+      data["Extension:Schemas"] = this.contentSchemaJSON;
+
+      Services.ppmm.broadcastAsyncMessage("Schema:Add", [[url, schema]]);
+    } else if (this.schemaHook) {
+      this.schemaHook([[url, schema]]);
+    }
 
     this.flushSchemas();
   },
 
-  async load(url) {
+  async load(url, content = false) {
     if (!isParentProcess) {
       return;
     }
 
     let schemaCache = await this.loadCachedSchemas();
 
     let blob = (schemaCache.get(url) ||
                 await StartupCache.schemas.get(url, readJSONAndBlobbify));
 
     if (!this.schemaJSON.has(url)) {
-      this.addSchema(url, blob);
+      this.addSchema(url, blob, content);
     }
   },
 
   unload(url) {
     this.schemaJSON.delete(url);
 
     let data = Services.ppmm.initialProcessData;
     data["Extension:Schemas"] = this.schemaJSON;
deleted file mode 100644
--- a/toolkit/components/extensions/ext-geolocation.js
+++ /dev/null
@@ -1,31 +0,0 @@
-"use strict";
-
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
-                                  "resource://gre/modules/Services.jsm");
-
-// If the user has changed the permission on the addon to something other than
-// always allow, then we want to preserve that choice.  We only set the
-// permission if it is not set (unknown_action), and we only remove the
-// permission on shutdown if it is always allow.
-
-this.geolocation = class extends ExtensionAPI {
-  onStartup() {
-    let {extension} = this;
-
-    if (extension.hasPermission("geolocation") &&
-        Services.perms.testPermission(extension.principal.URI, "geo") == Services.perms.UNKNOWN_ACTION) {
-      Services.perms.add(extension.principal.URI, "geo",
-                         Services.perms.ALLOW_ACTION,
-                         Services.perms.EXPIRE_SESSION);
-    }
-  }
-
-  onShutdown() {
-    let {extension} = this;
-
-    if (extension.hasPermission("geolocation") &&
-        Services.perms.testPermission(extension.principal.URI, "geo") == Services.perms.ALLOW_ACTION) {
-      Services.perms.remove(extension.principal.URI, "geo");
-    }
-  }
-};
--- a/toolkit/components/extensions/ext-toolkit.js
+++ b/toolkit/components/extensions/ext-toolkit.js
@@ -73,17 +73,8 @@ global.getContainerForCookieStoreId = fu
   return null;
 };
 
 global.isValidCookieStoreId = function(storeId) {
   return isDefaultCookieStoreId(storeId) ||
          isPrivateCookieStoreId(storeId) ||
          isContainerCookieStoreId(storeId);
 };
-
-if (AppConstants.MOZ_BUILD_APP === "browser") {
-  extensions.registerModules({
-    identity: {
-      schema: "chrome://extensions/content/schemas/identity.json",
-      scopes: ["addon_parent"],
-    },
-  });
-}
--- a/toolkit/components/extensions/ext-toolkit.json
+++ b/toolkit/components/extensions/ext-toolkit.json
@@ -46,25 +46,21 @@
     "scopes": ["addon_parent"],
     "paths": [
       ["downloads"]
     ]
   },
   "extension": {
     "url": "chrome://extensions/content/ext-extension.js",
     "schema": "chrome://extensions/content/schemas/extension.json",
-    "scopes": ["addon_parent"],
+    "scopes": ["addon_parent", "content_child"],
     "paths": [
       ["extension"]
     ]
   },
-  "geolocation": {
-    "url": "chrome://extensions/content/ext-geolocation.js",
-    "events": ["startup"]
-  },
   "i18n": {
     "url": "chrome://extensions/content/ext-i18n.js",
     "schema": "chrome://extensions/content/schemas/i18n.json",
     "scopes": ["addon_parent", "content_child", "devtools_child"],
     "paths": [
       ["i18n"]
     ]
   },
@@ -135,17 +131,17 @@
     "schema": "chrome://extensions/content/schemas/storage.json",
     "scopes": ["addon_parent", "content_parent", "devtools_parent"],
     "paths": [
       ["storage"]
     ]
   },
   "test": {
     "schema": "chrome://extensions/content/schemas/test.json",
-    "scopes": []
+    "scopes": ["content_child"]
   },
   "theme": {
     "url": "chrome://extensions/content/ext-theme.js",
     "schema": "chrome://extensions/content/schemas/theme.json",
     "scopes": ["addon_parent"],
     "manifest": ["theme"],
     "paths": [
       ["theme"]
--- a/toolkit/components/extensions/extension-process-script.js
+++ b/toolkit/components/extensions/extension-process-script.js
@@ -7,24 +7,24 @@
  * This script contains the minimum, skeleton content process code that we need
  * in order to lazily load other extension modules when they are first
  * necessary. Anything which is not likely to be needed immediately, or shortly
  * after startup, in *every* browser process live outside of this file.
  */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
+Cu.import("resource://gre/modules/MessageChannel.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   ExtensionChild: "resource://gre/modules/ExtensionChild.jsm",
   ExtensionContent: "resource://gre/modules/ExtensionContent.jsm",
   ExtensionPageChild: "resource://gre/modules/ExtensionPageChild.jsm",
-  MessageChannel: "resource://gre/modules/MessageChannel.jsm",
 });
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "console", () => ExtensionUtils.getConsole());
 
 const {
   DefaultWeakMap,
@@ -49,17 +49,25 @@ function parseScriptOptions(options) {
     excludeGlobs: options.exclude_globs && options.exclude_globs.map(glob => new MatchGlob(glob)),
 
     jsPaths: options.js || [],
     cssPaths: options.css || [],
   };
 }
 
 var extensions = new DefaultWeakMap(policy => {
-  let extension = new ExtensionChild.BrowserExtensionContent(policy.initData);
+  let data = policy.initData;
+  if (data.serialize) {
+    // We have an actual Extension rather than serialized extension
+    // data, so serialize it now to make sure we have consistent inputs
+    // between parent and child processes.
+    data = data.serialize();
+  }
+
+  let extension = new ExtensionChild.BrowserExtensionContent(data);
   extension.policy = policy;
   return extension;
 });
 
 var contentScripts = new DefaultWeakMap(matcher => {
   return new ExtensionContent.Script(extensions.get(matcher.extension),
                                      matcher);
 });
@@ -285,44 +293,53 @@ ExtensionManager = {
         procData["Extension:Schemas"] = new Map();
       }
       this.schemaJSON = procData["Extension:Schemas"];
 
       Services.cpmm.addMessageListener("Schema:Add", this);
     }
   },
 
-  initExtensionPolicy(data, extension) {
-    let policy = WebExtensionPolicy.getByID(data.id);
+  initExtensionPolicy(extension) {
+    let policy = WebExtensionPolicy.getByID(extension.id);
     if (!policy) {
-      let localizeCallback = (
-        extension ? extension.localize.bind(extension)
-                  : str => extensions.get(policy).localize(str));
+      let localizeCallback, allowedOrigins, webAccessibleResources;
+      if (extension.localize) {
+        // We have a real Extension object.
+        localizeCallback = extension.localize.bind(extension);
+        allowedOrigins = extension.whiteListedHosts;
+        webAccessibleResources = extension.webAccessibleResources;
+      } else {
+        // We have serialized extension data;
+        localizeCallback = str => extensions.get(policy).localize(str);
+        allowedOrigins = new MatchPatternSet(extension.whiteListedHosts);
+        webAccessibleResources = extension.webAccessibleResources.map(host => new MatchGlob(host));
+      }
 
       policy = new WebExtensionPolicy({
-        id: data.id,
-        mozExtensionHostname: data.uuid,
-        baseURL: data.resourceURL,
+        id: extension.id,
+        mozExtensionHostname: extension.uuid,
+        baseURL: extension.resourceURL,
 
-        permissions: Array.from(data.permissions),
-        allowedOrigins: new MatchPatternSet(data.whiteListedHosts),
-        webAccessibleResources: data.webAccessibleResources.map(host => new MatchGlob(host)),
+        permissions: Array.from(extension.permissions),
+        allowedOrigins,
+        webAccessibleResources,
 
-        contentSecurityPolicy: data.manifest.content_security_policy,
+        contentSecurityPolicy: extension.manifest.content_security_policy,
 
         localizeCallback,
 
-        backgroundScripts: (data.manifest.background &&
-                            data.manifest.background.scripts),
+        backgroundScripts: (extension.manifest.background &&
+                            extension.manifest.background.scripts),
 
-        contentScripts: (data.manifest.content_scripts || []).map(parseScriptOptions),
+        contentScripts: extension.contentScripts.map(parseScriptOptions),
       });
 
       policy.active = true;
-      policy.initData = data;
+      policy.initData = extension;
     }
     return policy;
   },
 
   initExtension(data) {
     let policy = this.initExtensionPolicy(data);
 
     DocumentManager.initExtension(policy);
@@ -355,17 +372,19 @@ ExtensionManager = {
 
       case "Extension:FlushJarCache": {
         ExtensionUtils.flushJarCache(data.path);
         Services.cpmm.sendAsyncMessage("Extension:FlushJarCacheComplete");
         break;
       }
 
       case "Schema:Add": {
-        this.schemaJSON.set(data.url, data.schema);
+        for (let [url, schema] of data) {
+          this.schemaJSON.set(url, schema);
+        }
         break;
       }
     }
   },
 };
 
 function ExtensionProcessScript() {
   if (!ExtensionProcessScript.singleton) {
@@ -382,18 +401,18 @@ ExtensionProcessScript.prototype = {
 
   get wrappedJSObject() { return this; },
 
   getFrameData(global, force) {
     let extGlobal = DocumentManager.globals.get(global);
     return extGlobal && extGlobal.getFrameData(force);
   },
 
-  initExtension(data, extension) {
-    return ExtensionManager.initExtensionPolicy(data, extension);
+  initExtension(extension) {
+    return ExtensionManager.initExtensionPolicy(extension);
   },
 
   initExtensionDocument(policy, doc) {
     if (DocumentManager.globals.has(getMessageManager(doc.defaultView))) {
       DocumentManager.loadInto(policy, doc.defaultView);
     }
   },
 
--- a/toolkit/components/extensions/jar.mn
+++ b/toolkit/components/extensions/jar.mn
@@ -8,17 +8,16 @@ toolkit.jar:
     content/extensions/ext-alarms.js
     content/extensions/ext-backgroundPage.js
     content/extensions/ext-browser-content.js
     content/extensions/ext-browserSettings.js
     content/extensions/ext-contextualIdentities.js
     content/extensions/ext-cookies.js
     content/extensions/ext-downloads.js
     content/extensions/ext-extension.js
-    content/extensions/ext-geolocation.js
     content/extensions/ext-i18n.js
     content/extensions/ext-idle.js
     content/extensions/ext-management.js
     content/extensions/ext-notifications.js
     content/extensions/ext-permissions.js
     content/extensions/ext-privacy.js
     content/extensions/ext-protocolHandlers.js
     content/extensions/ext-proxy.js
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -48,17 +48,17 @@ tags = addons
 [test_TelemetryControllerBuildID.js]
 [test_TelemetrySendOldPings.js]
 skip-if = os == "android" # Disabled due to intermittent orange on Android
 tags = addons
 [test_TelemetrySession.js]
 tags = addons
 [test_TelemetrySession_activeTicks.js]
 [test_ThreadHangStats.js]
-skip-if = os == "android" || os == "linux" # BHR is disabled on linux (bug 1365309)
+skip-if = os == "android" || asan # BHR is disabled on android (bug 1368520). ASAN does not have frame pointers.
 run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high
 [test_TelemetrySend.js]
 [test_ChildHistograms.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
 tags = addons
 [test_ChildScalars.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
 [test_TelemetryReportingPolicy.js]
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -445,16 +445,17 @@ var snapshotFormatters = {
     addRowFromKey("features", "webgl1Extensions");
     addRowFromKey("features", "webgl2WSIInfo");
     addRowFromKey("features", "webgl2Renderer");
     addRowFromKey("features", "webgl2Version");
     addRowFromKey("features", "webgl2DriverExtensions");
     addRowFromKey("features", "webgl2Extensions");
     addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
     addRowFromKey("features", "direct2DEnabled", "#Direct2D");
+    addRowFromKey("features", "offMainThreadPaintEnabled");
 
     if ("directWriteEnabled" in data) {
       let message = data.directWriteEnabled;
       if ("directWriteVersion" in data)
         message += " (" + data.directWriteVersion + ")";
       addRow("features", "#DirectWrite", message);
       delete data.directWriteEnabled;
       delete data.directWriteVersion;
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties
@@ -95,16 +95,17 @@ bugLink = bug %1$S
 unknownFailure = Blocklisted; failure code %1$S
 d3d11layersCrashGuard = D3D11 Compositor
 d3d11videoCrashGuard = D3D11 Video Decoder
 d3d9videoCrashGuard = D3D9 Video Decoder
 glcontextCrashGuard = OpenGL
 resetOnNextRestart = Reset on Next Restart
 gpuProcessKillButton = Terminate GPU Process
 gpuDeviceResetButton = Trigger Device Reset
+offMainThreadPaintEnabled = Off Main Thread Painting Enabled
 
 audioBackend = Audio Backend
 maxAudioChannels = Max Channels
 channelLayout = Preferred Channel Layout
 sampleRate = Preferred Sample Rate
 
 minLibVersions = Expected minimum version
 loadedLibVersions = Version in use
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -433,16 +433,17 @@ var dataProviders = {
       adapterDriverVersion2: "driverVersion2",
       adapterDriverDate2: "driverDate2",
       isGPU2Active: null,
 
       D2DEnabled: "direct2DEnabled",
       DWriteEnabled: "directWriteEnabled",
       DWriteVersion: "directWriteVersion",
       cleartypeParameters: "clearTypeParameters",
+      OffMainThreadPaintEnabled: "offMainThreadPaintEnabled",
     };
 
     for (let prop in gfxInfoProps) {
       try {
         data[gfxInfoProps[prop] || prop] = gfxInfo[prop];
       } catch (e) {}
     }
 
--- a/toolkit/modules/tests/browser/browser_Troubleshoot.js
+++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js
@@ -328,16 +328,19 @@ const SNAPSHOT_SCHEMA = {
           type: "boolean",
         },
         directWriteEnabled: {
           type: "boolean",
         },
         directWriteVersion: {
           type: "string",
         },
+        offMainThreadPaintEnabled: {
+          type: "boolean",
+        },
         clearTypeParameters: {
           type: "string",
         },
         webgl1Renderer: {
           type: "string",
         },
         webgl1Version: {
           type: "string",
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -4208,17 +4208,16 @@ this.XPIProvider = {
         getService(Ci.nsIAddonInterposition);
       Cu.setAddonInterposition(aId, interposition);
       Cu.allowCPOWsInAddon(aId, true);
     }
 
     if (!aFile.exists()) {
       activeAddon.bootstrapScope =
         new Cu.Sandbox(principal, { sandboxName: aFile.path,
-                                    wantGlobalProperties: ["indexedDB"],
                                     addonId: aId,
                                     metadata: { addonID: aId } });
       logger.error("Attempted to load bootstrap scope from missing directory " + aFile.path);
       return;
     }
 
     if (isWebExtension(aType)) {
       activeAddon.bootstrapScope = Extension.getBootstrapScope(aId, aFile);
@@ -4226,17 +4225,16 @@ this.XPIProvider = {
       let uri = getURIForResourceInFile(aFile, "bootstrap.js").spec;
       if (aType == "dictionary")
         uri = "resource://gre/modules/addons/SpellCheckDictionaryBootstrap.js"
       else if (aType == "apiextension")
         uri = "resource://gre/modules/addons/APIExtensionBootstrap.js"
 
       activeAddon.bootstrapScope =
         new Cu.Sandbox(principal, { sandboxName: uri,
-                                    wantGlobalProperties: ["indexedDB"],
                                     addonId: aId,
                                     metadata: { addonID: aId, URI: uri } });
 
       try {
         // Copy the reason values from the global object into the bootstrap scope.
         for (let name in BOOTSTRAP_REASONS)
           activeAddon.bootstrapScope[name] = BOOTSTRAP_REASONS[name];
 
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -188,16 +188,19 @@ const APPID_TO_TOPIC = {
   "{33cb9019-c295-46dd-be21-8c4936574bee}": "xul-window-visible",
 };
 
 var gUpdateMutexHandle = null;
 
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                   "resource://gre/modules/UpdateUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
+                                  "resource://gre/modules/WindowsRegistry.jsm");
+
 XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() {
   return getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() {
   return Services.strings.createBundle(URI_UPDATES_PROPERTIES);
 });
 
@@ -2808,16 +2811,69 @@ Checker.prototype = {
    */
   _request: null,
 
   /**
    * The nsIUpdateCheckListener callback
    */
   _callback: null,
 
+  _getCanMigrate: function UC__getCanMigrate() {
+    if (AppConstants.platform != "win") {
+      return false;
+    }
+
+    // The first element of the array is whether the build target is 32 or 64
+    // bit and the third element of the array is whether the client's Windows OS
+    // system processor is 32 or 64 bit.
+    let aryABI = UpdateUtils.ABI.split("-");
+    if (aryABI[0] != "x86" || aryABI[2] != "x64") {
+      return false;
+    }
+
+    let wrk = Cc["@mozilla.org/windows-registry-key;1"].
+              createInstance(Ci.nsIWindowsRegKey);
+
+    let regPath = "SOFTWARE\\Mozilla\\" + Services.appinfo.name +
+                  "\\32to64DidMigrate";
+    let regValHKCU = WindowsRegistry.readRegKey(wrk.ROOT_KEY_CURRENT_USER,
+                                                regPath, "Never", wrk.WOW64_32);
+    let regValHKLM = WindowsRegistry.readRegKey(wrk.ROOT_KEY_LOCAL_MACHINE,
+                                                regPath, "Never", wrk.WOW64_32);
+    // The Never registry key value allows configuring a system to never migrate
+    // any of the installations.
+    if (regValHKCU === 1 || regValHKLM === 1) {
+      LOG("Checker:_getCanMigrate - all installations should not be migrated");
+      return false;
+    }
+
+    let appBaseDirPath = getAppBaseDir().path;
+    regValHKCU = WindowsRegistry.readRegKey(wrk.ROOT_KEY_CURRENT_USER, regPath,
+                                            appBaseDirPath, wrk.WOW64_32);
+    regValHKLM = WindowsRegistry.readRegKey(wrk.ROOT_KEY_LOCAL_MACHINE, regPath,
+                                            appBaseDirPath, wrk.WOW64_32);
+    // When the registry value is 1 for the installation directory path value
+    // name then the installation has already been migrated once or the system
+    // was configured to not migrate that installation.
+    if (regValHKCU === 1 || regValHKLM === 1) {
+      LOG("Checker:_getCanMigrate - this installation should not be migrated");
+      return false;
+    }
+
+    // When the registry value is 0 for the installation directory path value
+    // name then the installation has updated to Firefox 56 and can be migrated.
+    if (regValHKCU === 0 || regValHKLM === 0) {
+      LOG("Checker:_getCanMigrate - this installation can be migrated");
+      return true;
+    }
+
+    LOG("Checker:_getCanMigrate - no registry entries for this installation");
+    return false;
+  },
+
   /**
    * The URL of the update service XML file to connect to that contains details
    * about available updates.
    */
   getUpdateURL: async function UC_getUpdateURL(force) {
     this._forced = force;
 
     let url = Services.prefs.getDefaultBranch(null).
@@ -2829,16 +2885,20 @@ Checker.prototype = {
     }
 
     url = await UpdateUtils.formatUpdateURL(url);
 
     if (force) {
       url += (url.indexOf("?") != -1 ? "&" : "?") + "force=1";
     }
 
+    if (this._getCanMigrate()) {
+      url += (url.indexOf("?") != -1 ? "&" : "?") + "mig64=1";
+    }
+
     LOG("Checker:getUpdateURL - update URL: " + url);
     return url;
   },
 
   /**
    * See nsIUpdateService.idl
    */
   checkForUpdates: function UC_checkForUpdates(listener, force) {
--- a/toolkit/themes/osx/global/tabbox.css
+++ b/toolkit/themes/osx/global/tabbox.css
@@ -30,34 +30,41 @@ tabs {
 tabbox > tabs {
   padding: 0 10px;
   margin-bottom: -12px;
   position: relative;
 }
 
 tab {
   -moz-appearance: tab;
+  padding-top: 1px;
 }
 
 tab:-moz-focusring {
   /* Tab focus rings need to overlay adjacent tabs. */
   position: relative;
 }
 
 tab:first-of-type {
   padding-inline-start: 2px;
 }
 
 tab:last-of-type {
   padding-inline-end: 2px;
 }
 
-tab[visuallyselected="true"] {
+tab[visuallyselected="true"]:not(:-moz-window-inactive) {
   color: #FFF;
-  text-shadow: rgba(0, 0, 0, 0.4) 0 1px;
+}
+
+@media not all and (-moz-mac-yosemite-theme) {
+  tab[visuallyselected="true"] {
+    color: #FFF;
+    text-shadow: rgba(0, 0, 0, 0.4) 0 1px;
+  }
 }
 
 .tab-middle {
   padding: 1px 6px 2px;
 }
 
 .tabs-left,
 .tabs-right  {
--- a/tools/profiler/core/platform-linux-android.cpp
+++ b/tools/profiler/core/platform-linux-android.cpp
@@ -252,51 +252,31 @@ Sampler::Sampler(PSLockRef aLock)
   : mMyPid(getpid())
   // We don't know what the sampler thread's ID will be until it runs, so set
   // mSamplerTid to a dummy value and fill it in for real in
   // SuspendAndSampleAndResumeThread().
   , mSamplerTid(-1)
 {
 #if defined(USE_EHABI_STACKWALK)
   mozilla::EHABIStackWalkInit();
-#elif defined(USE_LUL_STACKWALK)
-  bool createdLUL = false;
-  lul::LUL* lul = CorePS::Lul(aLock);
-  if (!lul) {
-    CorePS::SetLul(aLock, MakeUnique<lul::LUL>(logging_sink_for_LUL));
-    // Read all the unwind info currently available.
-    lul = CorePS::Lul(aLock);
-    read_procmaps(lul);
-    createdLUL = true;
-  }
 #endif
 
+  // NOTE: We don't initialize LUL here, instead initializing it in
+  // SamplerThread's constructor. This is because with the
+  // profiler_suspend_and_sample_thread entry point, we want to be able to
+  // sample without waiting for LUL to be initialized.
+
   // Request profiling signals.
   struct sigaction sa;
   sa.sa_sigaction = MOZ_SIGNAL_TRAMPOLINE(SigprofHandler);
   sigemptyset(&sa.sa_mask);
   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   if (sigaction(SIGPROF, &sa, &mOldSigprofHandler) != 0) {
     MOZ_CRASH("Error installing SIGPROF handler in the profiler");
   }
-
-#if defined(USE_LUL_STACKWALK)
-  if (createdLUL) {
-    // Switch into unwind mode. After this point, we can't add or remove any
-    // unwind info to/from this LUL instance. The only thing we can do with
-    // it is Unwind() calls.
-    lul->EnableUnwinding();
-
-    // Has a test been requested?
-    if (PR_GetEnv("MOZ_PROFILER_LUL_TEST")) {
-      int nTests = 0, nTestsPassed = 0;
-      RunLulUnitTests(&nTests, &nTestsPassed, lul);
-    }
-  }
-#endif
 }
 
 void
 Sampler::Disable(PSLockRef aLock)
 {
   // Restore old signal handler. This is global state so it's important that
   // we do it now, while gPSMutex is locked.
   sigaction(SIGPROF, &mOldSigprofHandler, 0);
@@ -408,16 +388,37 @@ ThreadEntry(void* aArg)
 
 SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration,
                              double aIntervalMilliseconds)
   : Sampler(aLock)
   , mActivityGeneration(aActivityGeneration)
   , mIntervalMicroseconds(
       std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5))))
 {
+#if defined(USE_LUL_STACKWALK)
+  lul::LUL* lul = CorePS::Lul(aLock);
+  if (!lul) {
+    CorePS::SetLul(aLock, MakeUnique<lul::LUL>(logging_sink_for_LUL));
+    // Read all the unwind info currently available.
+    lul = CorePS::Lul(aLock);
+    read_procmaps(lul);
+
+    // Switch into unwind mode. After this point, we can't add or remove any
+    // unwind info to/from this LUL instance. The only thing we can do with
+    // it is Unwind() calls.
+    lul->EnableUnwinding();
+
+    // Has a test been requested?
+    if (PR_GetEnv("MOZ_PROFILER_LUL_TEST")) {
+      int nTests = 0, nTestsPassed = 0;
+      RunLulUnitTests(&nTests, &nTestsPassed, lul);
+    }
+  }
+#endif
+
   // Start the sampling thread. It repeatedly sends a SIGPROF signal. Sending
   // the signal ourselves instead of relying on itimer provides much better
   // accuracy.
   if (pthread_create(&mThread, nullptr, ThreadEntry, this) != 0) {
     MOZ_CRASH("pthread_create failed");
   }
 }
 
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -106,16 +106,37 @@
 #endif
 
 // Linux builds use LUL, which uses DWARF info to unwind stacks.
 #if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux)
 # define HAVE_NATIVE_UNWIND
 # define USE_LUL_STACKWALK
 # include "lul/LulMain.h"
 # include "lul/platform-linux-lul.h"
+
+// On linux we use LUL for periodic samples and synchronous samples, but we use
+// FramePointerStackWalk for backtrace samples when MOZ_PROFILING is enabled.
+// (See the comment at the top of the file for a definition of
+// periodic/synchronous/backtrace.).
+//
+// FramePointerStackWalk can produce incomplete stacks when the current entry is
+// in a shared library without framepointers, however LUL can take a long time
+// to initialize, which is undesirable for consumers of
+// profiler_suspend_and_sample_thread like the Background Hang Reporter.
+# if defined(MOZ_PROFILING)
+#  define USE_FRAME_POINTER_STACK_WALK
+# endif
+#endif
+
+// We can only stackwalk without expensive initialization on platforms which
+// support FramePointerStackWalk or MozStackWalk. LUL Stackwalking requires
+// initializing LUL, and EHABIStackWalk requires initializing EHABI, both of
+// which can be expensive.
+#if defined(USE_FRAME_POINTER_STACK_WALK) || defined(USE_MOZ_STACK_WALK)
+# define HAVE_FASTINIT_NATIVE_UNWIND
 #endif
 
 #ifdef MOZ_VALGRIND
 # include <valgrind/memcheck.h>
 #else
 # define VALGRIND_MAKE_MEM_DEFINED(_addr,_len)   ((void)0)
 #endif
 
@@ -1015,55 +1036,72 @@ static void
 StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
 {
   NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
   MOZ_ASSERT(nativeStack->mCount < MAX_NATIVE_FRAMES);
   nativeStack->mSPs[nativeStack->mCount] = aSP;
   nativeStack->mPCs[nativeStack->mCount] = aPC;
   nativeStack->mCount++;
 }
-
+#endif
+
+#if defined(USE_FRAME_POINTER_STACK_WALK)
 static void
-DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
-                  const Registers& aRegs, NativeStack& aNativeStack)
+DoFramePointerBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
+                        const Registers& aRegs, NativeStack& aNativeStack)
 {
   // WARNING: this function runs within the profiler's "critical section".
   // WARNING: this function might be called while the profiler is inactive, and
   //          cannot rely on ActivePS.
 
   // Start with the current function. We use 0 as the frame number here because
-  // the FramePointerStackWalk() and MozStackWalkThread() calls below will use
-  // 1..N. This is a bit weird but it doesn't matter because
-  // StackWalkCallback() doesn't use the frame number argument.
+  // the FramePointerStackWalk() call below will use 1..N. This is a bit weird
+  // but it doesn't matter because StackWalkCallback() doesn't use the frame
+  // number argument.
   StackWalkCallback(/* frameNum */ 0, aRegs.mPC, aRegs.mSP, &aNativeStack);
 
   uint32_t maxFrames = uint32_t(MAX_NATIVE_FRAMES - aNativeStack.mCount);
 
-#if defined(USE_FRAME_POINTER_STACK_WALK)
   void* stackEnd = aThreadInfo.StackTop();
   if (aRegs.mFP >= aRegs.mSP && aRegs.mFP <= stackEnd) {
     FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
                           &aNativeStack, reinterpret_cast<void**>(aRegs.mFP),
                           stackEnd);
   }
-#elif defined(USE_MOZ_STACK_WALK)
+}
+#endif
+
+#if defined(USE_MOZ_STACK_WALK)
+static void
+DoMozStackWalkBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
+                        const Registers& aRegs, NativeStack& aNativeStack)
+{
+  // WARNING: this function runs within the profiler's "critical section".
+  // WARNING: this function might be called while the profiler is inactive, and
+  //          cannot rely on ActivePS.
+
+  // Start with the current function. We use 0 as the frame number here because
+  // the MozStackWalkThread() call below will use 1..N. This is a bit weird but
+  // it doesn't matter because StackWalkCallback() doesn't use the frame number
+  // argument.
+  StackWalkCallback(/* frameNum */ 0, aRegs.mPC, aRegs.mSP, &aNativeStack);
+
+  uint32_t maxFrames = uint32_t(MAX_NATIVE_FRAMES - aNativeStack.mCount);
+
   HANDLE thread = GetThreadHandle(aThreadInfo.GetPlatformData());
   MOZ_ASSERT(thread);
   MozStackWalkThread(StackWalkCallback, /* skipFrames */ 0, maxFrames,
                      &aNativeStack, thread, /* context */ nullptr);
-#else
-# error "bad configuration"
-#endif
 }
 #endif
 
 #ifdef USE_EHABI_STACKWALK
 static void
-DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
-                  const Registers& aRegs, NativeStack& aNativeStack)
+DoEHABIBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
+                 const Registers& aRegs, NativeStack& aNativeStack)
 {
   // WARNING: this function runs within the profiler's "critical section".
   // WARNING: this function might be called while the profiler is inactive, and
   //          cannot rely on ActivePS.
 
   const mcontext_t* mcontext = &aRegs.mContext->uc_mcontext;
   mcontext_t savedContext;
   NotNull<RacyThreadInfo*> racyInfo = aThreadInfo.RacyInfo();
@@ -1133,18 +1171,18 @@ ASAN_memcpy(void* aDst, const void* aSrc
 
   for (size_t i = 0; i < aLen; i++) {
     dst[i] = src[i];
   }
 }
 #endif
 
 static void
-DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
-                  const Registers& aRegs, NativeStack& aNativeStack)
+DoLULBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
+               const Registers& aRegs, NativeStack& aNativeStack)
 {
   // WARNING: this function runs within the profiler's "critical section".
   // WARNING: this function might be called while the profiler is inactive, and
   //          cannot rely on ActivePS.
 
   const mcontext_t* mc = &aRegs.mContext->uc_mcontext;
 
   lul::UnwindRegs startRegs;
@@ -1258,16 +1296,40 @@ DoNativeBacktrace(PSLockRef aLock, const
   // three global memory operations.
   lul->mStats.mContext += 1;
   lul->mStats.mCFI     += aNativeStack.mCount - 1 - framePointerFramesAcquired;
   lul->mStats.mFP      += framePointerFramesAcquired;
 }
 
 #endif
 
+#ifdef HAVE_NATIVE_UNWIND
+static void
+DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
+                  const Registers& aRegs, NativeStack& aNativeStack)
+{
+  // This method determines which stackwalker is used for periodic and
+  // synchronous samples. (Backtrace samples are treated differently, see
+  // profiler_suspend_and_sample_thread() for details). The only part of the
+  // ordering that matters is that LUL must precede FRAME_POINTER, because on
+  // Linux they can both be present.
+#if defined(USE_LUL_STACKWALK)
+  DoLULBacktrace(aLock, aThreadInfo, aRegs, aNativeStack);
+#elif defined(USE_EHABI_STACKWALK)
+  DoEHABIBacktrace(aLock, aThreadInfo, aRegs, aNativeStack);
+#elif defined(USE_FRAME_POINTER_STACK_WALK)
+  DoFramePointerBacktrace(aLock, aThreadInfo, aRegs, aNativeStack);
+#elif defined(USE_MOZ_STACK_WALK)
+  DoMozStackWalkBacktrace(aLock, aThreadInfo, aRegs, aNativeStack);
+#else
+  #error "Invalid configuration"
+#endif
+}
+#endif
+
 // Writes some components shared by periodic and synchronous profiles to
 // ActivePS's ProfileBuffer. (This should only be called from DoSyncSample()
 // and DoPeriodicSample().)
 //
 // The grammar for entry sequences is in a comment above
 // ProfileBuffer::StreamSamplesToJSON.
 static inline void
 DoSharedSample(PSLockRef aLock, bool aIsSynchronous,
@@ -3362,19 +3424,28 @@ profiler_suspend_and_sample_thread(
 
     if (info->ThreadId() == aThreadId) {
       // Suspend, sample, and then resume the target thread.
       Sampler sampler(lock);
       sampler.SuspendAndSampleAndResumeThread(lock, *info,
                                               [&](const Registers& aRegs) {
         // The target thread is now suspended. Collect a native backtrace, and
         // call the callback.
-#if defined(HAVE_NATIVE_UNWIND)
+#if defined(HAVE_FASTINIT_NATIVE_UNWIND)
         if (aSampleNative) {
-          DoNativeBacktrace(lock, *info, aRegs, nativeStack);
+          // We can only use FramePointerStackWalk or MozStackWalk from
+          // suspend_and_sample_thread as other stackwalking methods may not be
+          // initialized.
+# if defined(USE_FRAME_POINTER_STACK_WALK)
+          DoFramePointerBacktrace(lock, *info, aRegs, nativeStack);
+# elif defined(USE_MOZ_STACK_WALK)
+          DoMozStackWalkBacktrace(lock, *info, aRegs, nativeStack);
+# else
+#  error "Invalid configuration"
+# endif
         }
 #endif
         aCallback(nativeStack.mPCs, nativeStack.mCount, info->IsMainThread());
       });
 
       // NOTE: Make sure to disable the sampler before it is destroyed, in case
       // the profiler is running at the same time.
       sampler.Disable(lock);
@@ -3409,19 +3480,28 @@ profiler_suspend_and_sample_thread(int a
 
       // Suspend, sample, and then resume the target thread.
       Sampler sampler(lock);
       sampler.SuspendAndSampleAndResumeThread(lock, *info,
                                               [&](const Registers& aRegs) {
         // The target thread is now suspended. Collect a native backtrace, and
         // call the callback.
         bool isSynchronous = false;
-#if defined(HAVE_NATIVE_UNWIND)
+#if defined(HAVE_FASTINIT_NATIVE_UNWIND)
         if (aSampleNative) {
-          DoNativeBacktrace(lock, *info, aRegs, nativeStack);
+          // We can only use FramePointerStackWalk or MozStackWalk from
+          // suspend_and_sample_thread as other stackwalking methods may not be
+          // initialized.
+# if defined(USE_FRAME_POINTER_STACK_WALK)
+          DoFramePointerBacktrace(lock, *info, aRegs, nativeStack);
+# elif defined(USE_MOZ_STACK_WALK)
+          DoMozStackWalkBacktrace(lock, *info, aRegs, nativeStack);
+# else
+#  error "Invalid configuration"
+# endif
 
           MergeStacks(aFeatures, isSynchronous, *info, aRegs, nativeStack,
                       aCollector);
         } else
 #endif
         {
           MergeStacks(aFeatures, isSynchronous, *info, aRegs, nativeStack,
                       aCollector);
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -1469,16 +1469,23 @@ GfxInfoBase::GetActiveCrashGuards(JSCont
 NS_IMETHODIMP
 GfxInfoBase::GetWebRenderEnabled(bool* aWebRenderEnabled)
 {
   *aWebRenderEnabled = gfxVars::UseWebRender();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+GfxInfoBase::GetOffMainThreadPaintEnabled(bool* aOffMainThreadPaintEnabled)
+{
+  *aOffMainThreadPaintEnabled = gfxConfig::IsEnabled(Feature::OMTP);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 GfxInfoBase::GetIsHeadless(bool* aIsHeadless)
 {
   *aIsHeadless = gfxPlatform::IsHeadless();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GfxInfoBase::GetContentBackend(nsAString & aContentBackend)
--- a/widget/GfxInfoBase.h
+++ b/widget/GfxInfoBase.h
@@ -58,16 +58,17 @@ public:
   NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetFeatureLog(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetActiveCrashGuards(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetContentBackend(nsAString & aContentBackend) override;
   NS_IMETHOD GetUsingGPUProcess(bool *aOutValue) override;
   NS_IMETHOD GetWebRenderEnabled(bool* aWebRenderEnabled) override;
   NS_IMETHOD GetIsHeadless(bool* aIsHeadless) override;
+  NS_IMETHOD GetOffMainThreadPaintEnabled(bool* aOffMainThreadPaintEnabled) override;
 
   // Initialization function. If you override this, you must call this class's
   // version of Init first.
   // We need Init to be called separately from the constructor so we can
   // register as an observer after all derived classes have been constructed
   // and we know we have a non-zero refcount.
   // Ideally, Init() would be void-return, but the rules of
   // NS_GENERIC_FACTORY_CONSTRUCTOR_INIT require it be nsresult return.
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -858,19 +858,16 @@ public:
     {
         return mSurface;
     }
 
 private:
     void OnResumedCompositor()
     {
         MOZ_ASSERT(NS_IsMainThread());
-        if (!mWindow) {
-            return; // Already shut down.
-        }
 
         // When we receive this, the compositor has already been told to
         // resume. (It turns out that waiting till we reach here to tell
         // the compositor to resume takes too long, resulting in a black
         // flash.) This means it's now safe for layer updates to occur.
         // Since we might have prevented one or more draw events from
         // occurring while the compositor was paused, we need to schedule
         // a draw event now.
@@ -982,17 +979,21 @@ public:
 
             void Run() override
             {
                 MOZ_ASSERT(NS_IsMainThread());
 
                 JNIEnv* const env = jni::GetGeckoThreadEnv();
                 LayerViewSupport* const lvs = GetNative(
                         LayerView::Compositor::LocalRef(env, mCompositor));
-                MOZ_CATCH_JNI_EXCEPTION(env);
+
+                if (!lvs || !lvs->mWindow) {
+                    env->ExceptionClear();
+                    return; // Already shut down.
+                }
 
                 lvs->OnResumedCompositor();
             }
         };
 
         // Use priority queue for timing-sensitive event.
         nsAppShell::PostEvent(MakeUnique<LayerViewEvent>(
                 MakeUnique<OnResumedEvent>(aObj)));
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -112,16 +112,17 @@ using namespace mozilla::widget;
 #include "gfxPlatformGtk.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "gfxUtils.h"
 #include "Layers.h"
 #include "GLContextProvider.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/HelpersCairo.h"
+#include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 
 #ifdef MOZ_X11
 #include "X11CompositorWidget.h"
 #include "gfxXlibSurface.h"
 #include "WindowSurfaceX11Image.h"
 #include "WindowSurfaceX11SHM.h"
@@ -220,18 +221,20 @@ static void     hierarchy_changed_cb    
                                            GtkWidget *previous_toplevel);
 static gboolean window_state_event_cb     (GtkWidget *widget,
                                            GdkEventWindowState *event);
 static void     theme_changed_cb          (GtkSettings *settings,
                                            GParamSpec *pspec,
                                            nsWindow *data);
 static void     check_resize_cb           (GtkContainer* container,
                                            gpointer user_data);
-static void     composited_changed_cb     (GtkWidget* widget,
-                                           gpointer user_data);
+static void     screen_composited_changed_cb     (GdkScreen* screen,
+                                                  gpointer user_data);
+static void     widget_composited_changed_cb     (GtkWidget* widget,
+                                                  gpointer user_data);
 
 #if (MOZ_WIDGET_GTK == 3)
 static void     scale_changed_cb          (GtkWidget* widget,
                                            GParamSpec* aPSpec,
                                            gpointer aPointer);
 #endif
 #if GTK_CHECK_VERSION(3,4,0)
 static gboolean touch_event_cb            (GtkWidget* aWidget,
@@ -3396,17 +3399,16 @@ nsWindow::OnCompositedChanged()
 {
   if (mWidgetListener) {
     nsIPresShell* presShell = mWidgetListener->GetPresShell();
     if (presShell) {
       // Update CSD after the change in alpha visibility
       presShell->ThemeChanged();
     }
   }
-  CleanLayerManagerRecursive();
 }
 
 void
 nsWindow::DispatchDragEvent(EventMessage aMsg, const LayoutDeviceIntPoint& aRefPoint,
                             guint aTime)
 {
     WidgetDragEvent event(true, aMsg, this);
 
@@ -3865,18 +3867,28 @@ nsWindow::Create(nsIWidget* aParent,
         g_signal_connect(mShell, "configure_event",
                          G_CALLBACK(configure_event_cb), nullptr);
         g_signal_connect(mShell, "delete_event",
                          G_CALLBACK(delete_event_cb), nullptr);
         g_signal_connect(mShell, "window_state_event",
                          G_CALLBACK(window_state_event_cb), nullptr);
         g_signal_connect(mShell, "check-resize",
                          G_CALLBACK(check_resize_cb), nullptr);
+
+        GdkScreen *screen = gtk_widget_get_screen(mShell);
+
         g_signal_connect(mShell, "composited-changed",
-                         G_CALLBACK(composited_changed_cb), nullptr);
+                         G_CALLBACK(widget_composited_changed_cb), nullptr);
+
+        if (!g_signal_handler_find(screen, G_SIGNAL_MATCH_FUNC,
+                                   0, 0, nullptr,
+                                   FuncToGpointer(screen_composited_changed_cb), 0)) {
+            g_signal_connect(screen, "composited-changed",
+                             G_CALLBACK(screen_composited_changed_cb), nullptr);
+        }
 
         GtkSettings* default_settings = gtk_settings_get_default();
         g_signal_connect_after(default_settings,
                                "notify::gtk-theme-name",
                                G_CALLBACK(theme_changed_cb), this);
         g_signal_connect_after(default_settings,
                                "notify::gtk-font-name",
                                G_CALLBACK(theme_changed_cb), this);
@@ -5904,17 +5916,23 @@ check_resize_cb (GtkContainer* container
     RefPtr<nsWindow> window = get_window_for_gtk_widget(GTK_WIDGET(container));
     if (!window) {
       return;
     }
     window->OnCheckResize();
 }
 
 static void
-composited_changed_cb (GtkWidget* widget, gpointer user_data)
+screen_composited_changed_cb (GdkScreen* screen, gpointer user_data)
+{
+    GPUProcessManager::Get()->ResetCompositors();
+}
+
+static void
+widget_composited_changed_cb (GtkWidget* widget, gpointer user_data)
 {
     RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
     if (!window) {
       return;
     }
     window->OnCompositedChanged();
 }
 
--- a/widget/nsIGfxInfo.idl
+++ b/widget/nsIGfxInfo.idl
@@ -20,16 +20,17 @@ interface nsIGfxInfo : nsISupports
   readonly attribute DOMString cleartypeParameters;
 
   /*
    * These are valid across all platforms.
    */
   readonly attribute DOMString ContentBackend;
   readonly attribute boolean WebRenderEnabled;
   readonly attribute boolean isHeadless;
+  readonly attribute boolean OffMainThreadPaintEnabled;
 
   // XXX: Switch to a list of devices, rather than explicitly numbering them.
 
   /**
    * The name of the display adapter.
    */
   readonly attribute DOMString adapterDescription;
   readonly attribute DOMString adapterDescription2;
--- a/xpcom/base/nsCycleCollectionParticipant.h
+++ b/xpcom/base/nsCycleCollectionParticipant.h
@@ -7,22 +7,27 @@
 #ifndef nsCycleCollectionParticipant_h__
 #define nsCycleCollectionParticipant_h__
 
 #include "mozilla/MacroArgs.h"
 #include "mozilla/MacroForEach.h"
 #include "nsCycleCollectionNoteChild.h"
 #include "js/RootingAPI.h"
 
+/**
+ * Note: the following two IIDs only differ in one bit in the last byte.  This
+ * is a hack and is intentional in order to speed up the comparison inside
+ * NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED.
+ */
 #define NS_XPCOMCYCLECOLLECTIONPARTICIPANT_IID                                 \
 {                                                                              \
-    0x9674489b,                                                                \
-    0x1f6f,                                                                    \
-    0x4550,                                                                    \
-    { 0xa7, 0x30, 0xcc, 0xae, 0xdd, 0x10, 0x4c, 0xf9 }                         \
+    0xc61eac14,                                                                \
+    0x5f7a,                                                                    \
+    0x4481,                                                                    \
+    { 0x96, 0x5e, 0x7e, 0xaa, 0x6e, 0xff, 0xa8, 0x5e }                         \
 }
 
 /**
  * Special IID to get at the base nsISupports for a class. Usually this is the
  * canonical nsISupports pointer, but in the case of tearoffs for example it is
  * the base nsISupports pointer of the tearoff. This allow the cycle collector
  * to have separate nsCycleCollectionParticipant's for tearoffs or aggregated
  * classes.
@@ -288,47 +293,85 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsXPCOMCyc
   } else
 
 #define NS_IMPL_QUERY_CYCLE_COLLECTION_ISUPPORTS(_class)                       \
   if ( aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ) {                 \
     *aInstancePtr = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);       \
     return NS_OK;                                                              \
   } else
 
+// The IIDs for nsXPCOMCycleCollectionParticipant and nsCycleCollectionISupports
+// are special in that they only differ in their last byte.  This allows for the
+// optimization below where we first check the first three words of the IID and
+// if we find a match we check the last word to decide which case we have to
+// deal with.
 #define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class)                        \
-  NS_IMPL_QUERY_CYCLE_COLLECTION(_class)
+  if (TopThreeWordsEquals(aIID, NS_GET_IID(nsXPCOMCycleCollectionParticipant), \
+                          NS_GET_IID(nsCycleCollectionISupports)) &&           \
+       /* The calls to LowWordEquals here are repeated inside the if branch.   \
+          This is due to the fact that we need to maintain the if/else chain   \
+          for these macros, so that the control flow never enters the if branch\
+          unless if we're certain one of the LowWordEquals() branches will get \
+          executed. */                                                         \
+      (LowWordEquals(aIID, NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ||   \
+       LowWordEquals(aIID, NS_GET_IID(nsCycleCollectionISupports)))) {         \
+    if (LowWordEquals(aIID, NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {  \
+      *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(_class);                 \
+      return NS_OK;                                                            \
+    }                                                                          \
+    if (LowWordEquals(aIID, NS_GET_IID(nsCycleCollectionISupports))) {         \
+      *aInstancePtr = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);     \
+      return NS_OK;                                                            \
+    } else {                                                                   \
+      /* Avoid warnings about foundInterface being left uninitialized. */      \
+      foundInterface = nullptr;                                                \
+    }                                                                          \
+  } else
 
 #define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class)              \
   NS_IMPL_QUERY_CYCLE_COLLECTION_ISUPPORTS(_class)
 
 #define NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class)                      \
   NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class)                              \
   NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class)
 
 #define NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(_class)                        \
   NS_INTERFACE_MAP_BEGIN(_class)                                               \
     NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class)
 
 #define NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(_class)              \
   NS_INTERFACE_MAP_BEGIN(_class)                                               \
-    NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class)
+    NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class)                            \
+    NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class)
 
 #define NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(_class)  \
   if (rv == NS_OK) return rv; \
   nsISupports* foundInterface; \
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class)
 
+// The IIDs for nsXPCOMCycleCollectionParticipant and nsCycleCollectionISupports
+// are special in that they only differ in their last byte.  This allows for the
+// optimization below where we first check the first three words of the IID and
+// if we find a match we check the last word to decide which case we have to
+// deal with.
 #define NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(_class)            \
   NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr)    \
   {                                                                           \
     NS_PRECONDITION(aInstancePtr, "null out param");                          \
                                                                               \
-    if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {       \
-      *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(_class);                \
-      return NS_OK;                                                           \
+    if (TopThreeWordsEquals(aIID, NS_GET_IID(nsXPCOMCycleCollectionParticipant), \
+                            NS_GET_IID(nsCycleCollectionISupports))) {        \
+      if (LowWordEquals(aIID, NS_GET_IID(nsXPCOMCycleCollectionParticipant))) { \
+        *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(_class);              \
+        return NS_OK;                                                         \
+      }                                                                       \
+      if (LowWordEquals(aIID, NS_GET_IID(nsCycleCollectionISupports))) {      \
+        *aInstancePtr = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);  \
+        return NS_OK;                                                         \
+      }                                                                       \
     }                                                                         \
     nsresult rv;
 
 #define NS_CYCLE_COLLECTION_UPCAST(obj, clazz)                                \
   NS_CYCLE_COLLECTION_CLASSNAME(clazz)::Upcast(obj)
 
 #ifdef DEBUG
 #define NS_CHECK_FOR_RIGHT_PARTICIPANT(_ptr) _ptr->CheckForRightParticipant()
@@ -930,9 +973,36 @@ static NS_CYCLE_COLLECTION_INNERCLASS NS
   NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__)                                 \
   NS_IMPL_CYCLE_COLLECTION_UNLINK_END                                          \
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base)             \
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__)                               \
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 #define NS_CYCLE_COLLECTION_NOTE_EDGE_NAME CycleCollectionNoteEdgeName
 
+/**
+ * Equivalency of the high three words where two IIDs have the same
+ * top three words but not the same low word.
+ */
+inline bool TopThreeWordsEquals(const nsID& aID,
+                                const nsID& aOther1,
+                                const nsID& aOther2)
+{
+  MOZ_ASSERT((((uint32_t*)&aOther1.m0)[0] == ((uint32_t*)&aOther2.m0)[0]) &&
+             (((uint32_t*)&aOther1.m0)[1] == ((uint32_t*)&aOther2.m0)[1]) &&
+             (((uint32_t*)&aOther1.m0)[2] == ((uint32_t*)&aOther2.m0)[2]) &&
+             (((uint32_t*)&aOther1.m0)[3] != ((uint32_t*)&aOther2.m0)[3]));
+
+  return ((((uint32_t*)&aID.m0)[0] == ((uint32_t*)&aOther1.m0)[0]) &&
+          (((uint32_t*)&aID.m0)[1] == ((uint32_t*)&aOther1.m0)[1]) &&
+          (((uint32_t*)&aID.m0)[2] == ((uint32_t*)&aOther1.m0)[2]));
+}
+
+/**
+ * Equivalency of the fourth word where the two IIDs have the same
+ * top three words but not the same low word.
+ */
+inline bool LowWordEquals(const nsID& aID, const nsID& aOther)
+{
+  return (((uint32_t*)&aID.m0)[3] == ((uint32_t*)&aOther.m0)[3]);
+}
+
 #endif // nsCycleCollectionParticipant_h__
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -1311,28 +1311,30 @@ nsComponentManagerImpl::IsServiceInstant
     aClass.ToProvidedString(cid);
     aIID.ToProvidedString(iid);
     fprintf(stderr, "Checking for service on shutdown. Denied.\n"
             "         CID: %s\n         IID: %s\n", cid, iid);
 #endif /* SHOW_DENIED_ON_SHUTDOWN */
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+  nsresult rv = NS_OK;
   nsFactoryEntry* entry;
 
   {
     SafeMutexAutoLock lock(mLock);
     entry = mFactories.Get(aClass);
   }
 
   if (entry && entry->mServiceObject) {
     nsCOMPtr<nsISupports> service;
     rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
     *aResult = (service != nullptr);
+  } else {
+    *aResult = false;
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsComponentManagerImpl::IsServiceInstantiatedByContractID(
     const char* aContractID,
@@ -1351,27 +1353,29 @@ nsComponentManagerImpl::IsServiceInstant
     char iid[NSID_LENGTH];
     aIID.ToProvidedString(iid);
     fprintf(stderr, "Checking for service on shutdown. Denied.\n"
             "  ContractID: %s\n         IID: %s\n", aContractID, iid);
 #endif /* SHOW_DENIED_ON_SHUTDOWN */
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+  nsresult rv = NS_OK;
   nsFactoryEntry* entry;
   {
     SafeMutexAutoLock lock(mLock);
     entry = mContractIDs.Get(nsDependentCString(aContractID));
   }
 
   if (entry && entry->mServiceObject) {
     nsCOMPtr<nsISupports> service;
     rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
     *aResult = (service != nullptr);
+  } else {
+    *aResult = false;
   }
   return rv;
 }
 
 
 NS_IMETHODIMP
 nsComponentManagerImpl::GetServiceByContractID(const char* aContractID,
                                                const nsIID& aIID,
--- a/xpcom/components/nsIServiceManager.idl
+++ b/xpcom/components/nsIServiceManager.idl
@@ -40,23 +40,22 @@ interface nsIServiceManager : nsISupport
     void getServiceByContractID(in string aContractID,
 				in nsIIDRef aIID, 
 				[iid_is(aIID),retval] out nsQIResult result);
 
     /**
      * isServiceInstantiated
      *
      * isServiceInstantiated will return a true if the service has already
-     * been created, or throw otherwise
+     * been created, or false otherwise. Throws if the service does not
+     * implement the given IID.
      *
      * @param aClass or aContractID : aClass or aContractID of object 
      *                                instance requested
      * @param aIID : IID of interface requested
-     * @throws NS_ERROR_SERVICE_NOT_AVAILABLE if the service hasn't been 
-     *         instantiated
      * @throws NS_NOINTERFACE if the IID given isn't supported by the object
      */
     boolean isServiceInstantiated(in nsCIDRef aClass, in nsIIDRef aIID);
     boolean isServiceInstantiatedByContractID(in string aContractID, in nsIIDRef aIID);
 };
 
 
 %{C++
--- a/xpcom/threads/ThreadStackHelper.h
+++ b/xpcom/threads/ThreadStackHelper.h
@@ -25,22 +25,22 @@
 // Support pseudostack and native stack on these platforms.
 #if defined(XP_LINUX) || defined(XP_WIN) || defined(XP_MACOSX)
 #  ifdef MOZ_GECKO_PROFILER
 #    define MOZ_THREADSTACKHELPER_PSEUDO
 #    define MOZ_THREADSTACKHELPER_NATIVE
 #  endif
 #endif
 
-// NOTE: Currently, due to a problem with LUL stackwalking initialization taking
-// a long time (bug 1365309), we don't perform pseudostack or native stack
-// walking on Linux.
-#if defined(XP_LINUX)
+
+// Android x86 builds consistently crash in the Background Hang Reporter. bug
+// 1368520.
+#if defined(__ANDROID__)
+#  undef MOZ_THREADSTACKHELPER_PSEUDO
 #  undef MOZ_THREADSTACKHELPER_NATIVE
-#  undef MOZ_THREADSTACKHELPER_PSEUDO
 #endif
 
 namespace mozilla {
 
 /**
  * ThreadStackHelper is used to retrieve the profiler pseudo-stack of a
  * thread, as an alternative of using the profiler to take a profile.
  * The target thread first declares an ThreadStackHelper instance;
--- a/xpcom/threads/nsINamed.idl
+++ b/xpcom/threads/nsINamed.idl
@@ -15,10 +15,10 @@ interface nsINamed : nsISupports
     /*
      * A string describing the purpose of the runnable/timer/whatever. Useful
      * for debugging. This attribute is read-only, but you can change it to a
      * compile-time string literal with setName.
      *
      * WARNING: This attribute will be included in telemetry, so it should
      * never contain privacy sensitive information.
      */
-    readonly attribute ACString name;
+    readonly attribute AUTF8String name;
 };