merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sat, 03 Jun 2017 20:15:27 +0200
changeset 412659 130efc657df7e7fe291cc42307f3eb3cb0484dfc
parent 412623 3b1dcffbad51682211afbf018ccf2912bb1c1bea (current diff)
parent 412658 90df8b3f52d60d79b2730153f97300039019b694 (diff)
child 412660 52e096553d7747c667474d3eee67c4e63379d5c3
child 412671 64c3afa3fabf81d5246676bccd559ff8db1be66e
child 412684 31d8f2665ea0dd7c26b79b86656fc9f61feee26d
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone55.0a1
first release with
nightly linux32
130efc657df7 / 55.0a1 / 20170604100232 / files
nightly linux64
130efc657df7 / 55.0a1 / 20170604100232 / files
nightly mac
130efc657df7 / 55.0a1 / 20170604030205 / files
nightly win32
130efc657df7 / 55.0a1 / 20170604030205 / files
nightly win64
130efc657df7 / 55.0a1 / 20170604030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: IdRgoZ9hiXq
devtools/client/framework/test/browser_devtools_api.js
dom/base/messageWakeupService.js
dom/base/messageWakeupService.manifest
mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
mobile/android/base/moz.build
mobile/android/geckoview/src/main/java/org/mozilla/gecko/ContextGetter.java
security/manager/ssl/StaticHPKPins.h
security/manager/ssl/nsSTSPreloadList.errors
security/manager/ssl/nsSTSPreloadList.inc
taskcluster/ci/build/linux.yml
taskcluster/taskgraph/transforms/gecko_v2_whitelist.py
testing/mochitest/tests/browser/browser_fail_add_task_uncaught_rejection.js
testing/mochitest/tests/browser/browser_fail_uncaught_rejection_expected.js
testing/mochitest/tests/browser/browser_fail_uncaught_rejection_expected_multi.js
testing/mochitest/tests/browser/browser_uncaught_rejection_expected.js
testing/mozharness/mozharness/mozilla/building/buildbase.py
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -850,92 +850,107 @@ function makePreview(row) {
     newImage.id = "thepreviewimage";
     var physWidth = 0, physHeight = 0;
     var width = 0, height = 0;
 
     if ((item.HTMLLinkElement || item.HTMLInputElement ||
          item.HTMLImageElement || item.SVGImageElement ||
          (item.HTMLObjectElement && mimeType && mimeType.startsWith("image/")) ||
          isBG) && isProtocolAllowed) {
-      newImage.setAttribute("src", url);
-      physWidth = newImage.width || 0;
-      physHeight = newImage.height || 0;
+      // We need to wait for the image to finish loading before using width & height
+      newImage.addEventListener("loadend", function() {
+        physWidth = newImage.width || 0;
+        physHeight = newImage.height || 0;
+
+        // "width" and "height" attributes must be set to newImage,
+        // even if there is no "width" or "height attribute in item;
+        // otherwise, the preview image cannot be displayed correctly.
+        // Since the image might have been loaded out-of-process, we expect
+        // the item to tell us its width / height dimensions. Failing that
+        // the item should tell us the natural dimensions of the image. Finally
+        // failing that, we'll assume that the image was never loaded in the
+        // other process (this can be true for favicons, for example), and so
+        // we'll assume that we can use the natural dimensions of the newImage
+        // we just created. If the natural dimensions of newImage are not known
+        // then the image is probably broken.
+        if (!isBG) {
+          newImage.width = ("width" in item && item.width) || newImage.naturalWidth;
+          newImage.height = ("height" in item && item.height) || newImage.naturalHeight;
+        } else {
+          // the Width and Height of an HTML tag should not be used for its background image
+          // (for example, "table" can have "width" or "height" attributes)
+          newImage.width = item.naturalWidth || newImage.naturalWidth;
+          newImage.height = item.naturalHeight || newImage.naturalHeight;
+        }
+
+        if (item.SVGImageElement) {
+          newImage.width = item.SVGImageElementWidth;
+          newImage.height = item.SVGImageElementHeight;
+        }
+
+        width = newImage.width;
+        height = newImage.height;
+
+        document.getElementById("theimagecontainer").collapsed = false
+        document.getElementById("brokenimagecontainer").collapsed = true;
 
-      // "width" and "height" attributes must be set to newImage,
-      // even if there is no "width" or "height attribute in item;
-      // otherwise, the preview image cannot be displayed correctly.
-      // Since the image might have been loaded out-of-process, we expect
-      // the item to tell us its width / height dimensions. Failing that
-      // the item should tell us the natural dimensions of the image. Finally
-      // failing that, we'll assume that the image was never loaded in the
-      // other process (this can be true for favicons, for example), and so
-      // we'll assume that we can use the natural dimensions of the newImage
-      // we just created. If the natural dimensions of newImage are not known
-      // then the image is probably broken.
-      if (!isBG) {
-        newImage.width = ("width" in item && item.width) || newImage.naturalWidth;
-        newImage.height = ("height" in item && item.height) || newImage.naturalHeight;
+        let imageSize = "";
+        if (url) {
+          if (width != physWidth || height != physHeight) {
+            imageSize = gBundle.getFormattedString("mediaDimensionsScaled",
+                                                   [formatNumber(physWidth),
+                                                    formatNumber(physHeight),
+                                                    formatNumber(width),
+                                                    formatNumber(height)]);
+          } else {
+            imageSize = gBundle.getFormattedString("mediaDimensions",
+                                                   [formatNumber(width),
+                                                    formatNumber(height)]);
+          }
+        }
+        setItemValue("imagedimensiontext", imageSize);
+      }, {once: true});
+
+      newImage.setAttribute("src", url);
+    } else {
+      // Handle the case where newImage is not used for width & height
+      if (item.HTMLVideoElement && isProtocolAllowed) {
+        newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
+        newImage.id = "thepreviewimage";
+        newImage.src = url;
+        newImage.controls = true;
+        width = physWidth = item.videoWidth;
+        height = physHeight = item.videoHeight;
+
+        document.getElementById("theimagecontainer").collapsed = false;
+        document.getElementById("brokenimagecontainer").collapsed = true;
+      } else if (item.HTMLAudioElement && isProtocolAllowed) {
+        newImage = new Audio;
+        newImage.id = "thepreviewimage";
+        newImage.src = url;
+        newImage.controls = true;
+        isAudio = true;
+
+        document.getElementById("theimagecontainer").collapsed = false;
+        document.getElementById("brokenimagecontainer").collapsed = true;
       } else {
-        // the Width and Height of an HTML tag should not be used for its background image
-        // (for example, "table" can have "width" or "height" attributes)
-        newImage.width = item.naturalWidth || newImage.naturalWidth;
-        newImage.height = item.naturalHeight || newImage.naturalHeight;
-      }
-
-      if (item.SVGImageElement) {
-        newImage.width = item.SVGImageElementWidth;
-        newImage.height = item.SVGImageElementHeight;
+        // fallback image for protocols not allowed (e.g., javascript:)
+        // or elements not [yet] handled (e.g., object, embed).
+        document.getElementById("brokenimagecontainer").collapsed = false;
+        document.getElementById("theimagecontainer").collapsed = true;
       }
 
-      width = newImage.width;
-      height = newImage.height;
-
-      document.getElementById("theimagecontainer").collapsed = false
-      document.getElementById("brokenimagecontainer").collapsed = true;
-    } else if (item.HTMLVideoElement && isProtocolAllowed) {
-      newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
-      newImage.id = "thepreviewimage";
-      newImage.src = url;
-      newImage.controls = true;
-      width = physWidth = item.videoWidth;
-      height = physHeight = item.videoHeight;
-
-      document.getElementById("theimagecontainer").collapsed = false;
-      document.getElementById("brokenimagecontainer").collapsed = true;
-    } else if (item.HTMLAudioElement && isProtocolAllowed) {
-      newImage = new Audio;
-      newImage.id = "thepreviewimage";
-      newImage.src = url;
-      newImage.controls = true;
-      isAudio = true;
-
-      document.getElementById("theimagecontainer").collapsed = false;
-      document.getElementById("brokenimagecontainer").collapsed = true;
-    } else {
-      // fallback image for protocols not allowed (e.g., javascript:)
-      // or elements not [yet] handled (e.g., object, embed).
-      document.getElementById("brokenimagecontainer").collapsed = false;
-      document.getElementById("theimagecontainer").collapsed = true;
-    }
-
-    let imageSize = "";
-    if (url && !isAudio) {
-      if (width != physWidth || height != physHeight) {
-        imageSize = gBundle.getFormattedString("mediaDimensionsScaled",
-                                               [formatNumber(physWidth),
-                                                formatNumber(physHeight),
-                                                formatNumber(width),
-                                                formatNumber(height)]);
-      } else {
+      let imageSize = "";
+      if (url && !isAudio) {
         imageSize = gBundle.getFormattedString("mediaDimensions",
                                                [formatNumber(width),
                                                 formatNumber(height)]);
       }
+      setItemValue("imagedimensiontext", imageSize);
     }
-    setItemValue("imagedimensiontext", imageSize);
 
     makeBlockImage(url);
 
     imageContainer.removeChild(oldImage);
     imageContainer.appendChild(newImage);
   });
 }
 
--- a/browser/base/content/test/general/browser_aboutSupport_newtab_security_state.js
+++ b/browser/base/content/test/general/browser_aboutSupport_newtab_security_state.js
@@ -1,11 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: window.location is null");
+
+
 add_task(async function checkIdentityOfAboutSupport() {
   let tab = gBrowser.loadOneTab("about:support", {
     referrerURI: null,
     inBackground: false,
     allowThirdPartyFixup: false,
     relatedToCurrent: false,
     skipAnimation: true,
     allowMixedContent: false,
--- a/browser/base/content/test/general/browser_scope.js
+++ b/browser/base/content/test/general/browser_scope.js
@@ -1,4 +1,10 @@
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
+
 function test() {
   ok(!!gBrowser, "gBrowser exists");
   is(gBrowser, getBrowser(), "both ways of getting tabbrowser work");
 }
--- a/browser/base/content/test/general/browser_tabs_owner.js
+++ b/browser/base/content/test/general/browser_tabs_owner.js
@@ -1,8 +1,20 @@
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: gBrowser._finalizeTabSwitch is not a function");
+
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: gBrowser._finalizeTabSwitch is not a function");
+
 function test() {
   BrowserTestUtils.addTab(gBrowser);
   BrowserTestUtils.addTab(gBrowser);
   BrowserTestUtils.addTab(gBrowser);
 
   var tabs = gBrowser.tabs;
   var owner;
 
--- a/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js
+++ b/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js
@@ -8,18 +8,18 @@ function test() {
   gBrowser.selectedBrowser.addEventListener("load", function() {
     var doc = gBrowser.contentDocument;
     var testImg = doc.getElementById("test-image");
     var pageInfo = BrowserPageInfo(gBrowser.selectedBrowser.currentURI.spec,
                                    "mediaTab", testImg);
 
     pageInfo.addEventListener("load", function() {
       pageInfo.onFinished.push(function() {
-        executeSoon(function() {
-          var pageInfoImg = pageInfo.document.getElementById("thepreviewimage");
+        var pageInfoImg = pageInfo.document.getElementById("thepreviewimage");
+        pageInfoImg.addEventListener("loadend", function() {
 
           is(pageInfoImg.src, testImg.src, "selected image has the correct source");
           is(pageInfoImg.width, testImg.width, "selected image has the correct width");
           is(pageInfoImg.height, testImg.height, "selected image has the correct height");
 
           pageInfo.close();
           gBrowser.removeCurrentTab();
           finish();
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -23,32 +23,29 @@ const startupPhases = {
   // Consider loading your code after first paint instead,
   // eg. from nsBrowserGlue.js' _onFirstWindowLoaded method).
   "before profile selection": {whitelist: {
     components: new Set([
       "nsBrowserGlue.js",
       "MainProcessSingleton.js",
 
       // Bugs to fix: The following components shouldn't be initialized that early.
-      "WebContentConverter.js",
-      "nsSessionStartup.js",
-      "PushComponents.js",
+      "WebContentConverter.js", // bug 1369443
+      "nsSessionStartup.js", // bug 1369456
+      "PushComponents.js", // bug 1369436
     ]),
     modules: new Set([
       "resource://gre/modules/AppConstants.jsm",
       "resource://gre/modules/XPCOMUtils.jsm",
       "resource://gre/modules/Services.jsm",
 
       // Bugs to fix: Probably loaded too early, needs investigation.
-      "resource://gre/modules/Log.jsm",
-      "resource://gre/modules/AsyncPrefs.jsm",
-      "resource://gre/modules/RemotePageManager.jsm",
-      "resource://gre/modules/TelemetryStopwatch.jsm",
-      "resource://gre/modules/PrivateBrowsingUtils.jsm",
-      "resource://gre/modules/Promise.jsm"
+      "resource://gre/modules/AsyncPrefs.jsm", // bug 1369460
+      "resource://gre/modules/RemotePageManager.jsm", // bug 1369466
+      "resource://gre/modules/Promise.jsm" // bug 1368456
     ])
   }},
 
   // For the following phases of startup we have only a black list for now
 
   // We are at this phase after creating the first browser window (ie. after final-ui-startup).
   "before opening first browser window": {blacklist: {
     components: new Set([
--- a/browser/base/content/test/siteIdentity/browser_no_mcb_for_loopback.js
+++ b/browser/base/content/test/siteIdentity/browser_no_mcb_for_loopback.js
@@ -3,24 +3,16 @@
 
 // The test loads a HTTPS web page with active content from HTTP loopback URLs
 // and makes sure that the mixed content flags on the docshell are not set.
 //
 // Note that the URLs referenced within the test page intentionally use the
 // unassigned port 8 because we don't want to actually load anything, we just
 // want to check that the URLs are not blocked.
 
-// The following rejections should not be left uncaught. This test has been
-// whitelisted until the issue is fixed.
-if (!gMultiProcessBrowser) {
-  Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-  PromiseTestUtils.expectUncaughtRejection(/NetworkError/);
-  PromiseTestUtils.expectUncaughtRejection(/NetworkError/);
-}
-
 const TEST_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "test_no_mcb_for_loopback.html";
 
 const LOOPBACK_PNG_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://127.0.0.1:8888") + "moz.png";
 
 const PREF_BLOCK_DISPLAY = "security.mixed_content.block_display_content";
 const PREF_BLOCK_ACTIVE = "security.mixed_content.block_active_content";
 
 registerCleanupFunction(function() {
--- a/browser/base/content/test/social/browser_social_activation.js
+++ b/browser/base/content/test/social/browser_social_activation.js
@@ -1,12 +1,19 @@
 /* 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/. */
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: Assert is null");
+
+
 var SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
 
 var tabsToRemove = [];
 
 function removeProvider(provider) {
   return new Promise(resolve => {
     // a full install sets the manifest into a pref, addProvider alone doesn't,
     // make sure we uninstall if the manifest was added.
--- a/browser/base/content/test/sync/browser_aboutAccounts.js
+++ b/browser/base/content/test/sync/browser_aboutAccounts.js
@@ -1,12 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: window.location is null");
+
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
   "resource://gre/modules/FxAccounts.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
   "resource://gre/modules/FileUtils.jsm");
 
 const CHROME_BASE = "chrome://mochitests/content/browser/browser/base/content/test/sync/";
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js
@@ -1,21 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// The rejection "The fetching process for the media resource was aborted by the
-// user agent at the user's request." is left unhandled in some cases. This bug
-// should be fixed, but for the moment this file is whitelisted.
-//
-// NOTE: Whitelisting a class of rejections should be limited. Normally you
-//       should use "expectUncaughtRejection" to flag individual failures.
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.whitelistRejectionsGlobally(/aborted by the user agent/);
-
 const permissionError = "error: NotAllowedError: The request is not allowed " +
     "by the user agent or the platform in the current context.";
 
 const notFoundError =
     "error: NotFoundError: The object can not be found here.";
 
 var gTests = [
 
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -103,18 +103,16 @@ class BasePopup {
     this.closePopup();
   }
 
   destroy() {
     this.extension.forgetOnClose(this);
 
     this.destroyed = true;
     this.browserLoadedDeferred.reject(new Error("Popup destroyed"));
-    // Ignore unhandled rejections if the "attach" method is not called.
-    this.browserLoaded.catch(() => {});
 
     BasePopup.instances.get(this.window).delete(this.extension);
 
     return this.browserReady.then(() => {
       if (this.browser) {
         this.destroyBrowser(this.browser, true);
         this.browser.remove();
       }
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
@@ -1,19 +1,10 @@
 "use strict";
 
-// Various "Missing host permission" rejections are left uncaught. This may be
-// caused by issues in the test, in the testing framework, or in production
-// code. This bug should be fixed, but for the moment this file is whitelisted.
-//
-// NOTE: Whitelisting a class of rejections should be limited. Normally you
-//       should use "expectUncaughtRejection" to flag individual failures.
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.whitelistRejectionsGlobally(/Missing host permission/);
-
 // This is a pretty terrible hack, but it's the best we can do until we
 // support |executeScript| callbacks and |lastError|.
 async function testHasNoPermission(params) {
   let contentSetup = params.contentSetup || (() => Promise.resolve());
 
   async function background(contentSetup) {
     browser.runtime.onMessage.addListener((msg, sender) => {
       browser.test.assertEq(msg, "second script ran", "second script ran");
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -16,26 +16,16 @@
  *          imageBuffer imageBufferFromDataURI
  *          getListStyleImage getPanelForNode
  *          awaitExtensionPanel awaitPopupResize
  *          promiseContentDimensions alterContent
  *          promisePrefChangeObserved openContextMenuInFrame
  *          promiseAnimationFrame
  */
 
-// There are shutdown issues for which multiple rejections are left uncaught.
-// This bug should be fixed, but for the moment this directory is whitelisted.
-//
-// NOTE: Entire directory whitelisting should be kept to a minimum. Normally you
-//       should use "expectUncaughtRejection" to flag individual failures.
-const {PromiseTestUtils} = Cu.import("resource://testing-common/PromiseTestUtils.jsm", {});
-PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
-PromiseTestUtils.whitelistRejectionsGlobally(/No matching message handler/);
-PromiseTestUtils.whitelistRejectionsGlobally(/Receiving end does not exist/);
-
 const {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm", {});
 const {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm", {});
 
 // We run tests under two different configurations, from browser.ini and
 // browser-remote.ini. When running from browser-remote.ini, the tests are
 // copied to the sub-directory "test-oop-extensions", which we detect here, and
 // use to select our configuration.
 if (gTestPath.includes("test-oop-extensions")) {
--- a/browser/components/feeds/WebContentConverter.js
+++ b/browser/components/feeds/WebContentConverter.js
@@ -1,16 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 function LOG(str) {
   dump("*** " + str + "\n");
 }
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -1305,17 +1305,17 @@ var SessionStoreInternal = {
         }
         TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
         this.initializeWindow(aWindow, initialState);
         TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
 
         // Let everyone know we're done.
         this._deferredInitialized.resolve();
       }
-    }).catch(console.error);
+    }, console.error);
   },
 
   /**
    * On window close...
    * - remove event listeners from tabs
    * - save all window data
    * @param aWindow
    *        Window reference
--- a/browser/components/sessionstore/nsSessionStartup.js
+++ b/browser/components/sessionstore/nsSessionStartup.js
@@ -32,28 +32,28 @@
 /* :::::::: Constants and Helpers ::::::::::::::: */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
-Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
   "resource://gre/modules/Console.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
   "resource:///modules/sessionstore/SessionFile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "StartupPerformance",
   "resource:///modules/sessionstore/StartupPerformance.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CrashMonitor",
   "resource://gre/modules/CrashMonitor.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 const STATE_RUNNING_STR = "running";
 
 // 'browser.startup.page' preference value to resume the previous session.
 const BROWSER_STARTUP_RESUME_SESSION = 3;
 
 function debug(aMsg) {
   aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
--- a/browser/components/sessionstore/test/browser_354894_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_354894_perwindowpb.js
@@ -19,25 +19,16 @@
  * not enabled on that platform (platform shim; the application is kept running
  * although there are no windows left)
  * @note There is a difference when closing a browser window with
  * BrowserTryToCloseWindow() as opposed to close(). The former will make
  * nsSessionStore restore a window next time it gets a chance and will post
  * notifications. The latter won't.
  */
 
-// The rejection "RecentWindow.getMostRecentBrowserWindow(...) is null" is left
-// unhandled in some cases. This bug should be fixed, but for the moment this
-// file is whitelisted.
-//
-// NOTE: Whitelisting a class of rejections should be limited. Normally you
-//       should use "expectUncaughtRejection" to flag individual failures.
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.whitelistRejectionsGlobally(/getMostRecentBrowserWindow/);
-
 // Some urls that might be opened in tabs and/or popups
 // Do not use about:blank:
 // That one is reserved for special purposes in the tests
 const TEST_URLS = ["about:mozilla", "about:buildconfig"];
 
 // Number of -request notifications to except
 // remember to adjust when adding new tests
 const NOTIFICATIONS_EXPECTED = 6;
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/noopt-debug
@@ -0,0 +1,7 @@
+# Developers often build with these options for a better debugging experience.
+. "$topsrcdir/browser/config/mozconfigs/linux64/debug"
+
+# We add this last to guard against inadvertent changes in the debug config.
+# It may conflict with settings from mozconfig.override, but that seems
+# unlikely.
+ac_add_options --disable-optimize
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/macosx64/cross-noopt-debug
@@ -0,0 +1,7 @@
+# Developers often build with these options for a better debugging experience.
+. "$topsrcdir/browser/config/mozconfigs/macosx64/debug"
+
+# We add this last to guard against inadvertent changes in the debug config.
+# It may conflict with settings from mozconfig.override, but that seems
+# unlikely.
+ac_add_options --disable-optimize
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/win32/noopt-debug
@@ -0,0 +1,7 @@
+# Developers often build with these options for a better debugging experience.
+. "$topsrcdir/browser/config/mozconfigs/win32/debug"
+
+# We add this last to guard against inadvertent changes in the debug config.
+# It may conflict with settings from mozconfig.override, but that seems
+# unlikely.
+ac_add_options --disable-optimize
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/win64/noopt-debug
@@ -0,0 +1,7 @@
+# Developers often build with these options for a better debugging experience.
+. "$topsrcdir/browser/config/mozconfigs/win64/debug"
+
+# We add this last to guard against inadvertent changes in the debug config.
+# It may conflict with settings from mozconfig.override, but that seems
+# unlikely.
+ac_add_options --disable-optimize
--- a/browser/config/tooltool-manifests/win64/clang.manifest
+++ b/browser/config/tooltool-manifests/win64/clang.manifest
@@ -1,19 +1,19 @@
 [
   {
     "size": 266240,
     "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
     "algorithm": "sha512",
     "filename": "mozmake.exe"
   },
   {
-    "version": "rustc 1.17.0 (56124baa9 2017-04-24) repack with cargo 0.19.0-beta.1 (03efb7fc8 2017-04-23)",
-    "size": 83368214,
-    "digest": "2f1dda29e495e9707a0e22cde5c80525bd202be9a3381dd053edf08e1a54cd7fa4106e8f1cec336daaae62252ef02cf5b76079c0495a90f1ac0b823a16fd27dc",
+    "version": "1.18.0-beta.4 (0308c9865 2017-05-27)",
+    "size": 98338423,
+    "digest": "126dd7054f4d2a705e3abf0f916b62904ae5097b70f67c9d048e031e42a19208c41885c0d479a6f3591f2c74725c4e80ed3dfa4452301618cc7ac0f0820d5683",
     "algorithm": "sha512",
     "visibility": "public",
     "filename": "rustc.tar.bz2",
     "unpack": true
   },
   {
     "version": "sccache rev d3aa1116844b50c03015266d2f48235509fa7deb",
     "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/win64/releng.manifest
+++ b/browser/config/tooltool-manifests/win64/releng.manifest
@@ -1,19 +1,19 @@
 [
   {
     "size": 266240,
     "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
     "algorithm": "sha512",
     "filename": "mozmake.exe"
   },
   {
-    "version": "rustc 1.17.0 (56124baa9 2017-04-24) repack with cargo 0.19.0-beta.1 (03efb7fc8 2017-04-23)",
-    "size": 83368214,
-    "digest": "2f1dda29e495e9707a0e22cde5c80525bd202be9a3381dd053edf08e1a54cd7fa4106e8f1cec336daaae62252ef02cf5b76079c0495a90f1ac0b823a16fd27dc",
+    "version": "1.18.0-beta.4 (0308c9865 2017-05-27)",
+    "size": 98338423,
+    "digest": "126dd7054f4d2a705e3abf0f916b62904ae5097b70f67c9d048e031e42a19208c41885c0d479a6f3591f2c74725c4e80ed3dfa4452301618cc7ac0f0820d5683",
     "algorithm": "sha512",
     "visibility": "public",
     "filename": "rustc.tar.bz2",
     "unpack": true
   },
   {
     "version": "sccache rev d3aa1116844b50c03015266d2f48235509fa7deb",
     "algorithm": "sha512",
--- a/browser/extensions/shield-recipe-client/lib/PreferenceExperiments.jsm
+++ b/browser/extensions/shield-recipe-client/lib/PreferenceExperiments.jsm
@@ -250,18 +250,17 @@ this.PreferenceExperiments = {
         `An observer for the preference experiment ${experimentName} is already active.`
       );
     }
 
     const observerInfo = {
       preferenceName,
       observer(newValue) {
         if (newValue !== preferenceValue) {
-          PreferenceExperiments.stop(experimentName, false)
-                               .catch(Cu.reportError);
+          PreferenceExperiments.stop(experimentName, false);
         }
       },
     };
     experimentObservers.set(experimentName, observerInfo);
     Preferences.observe(preferenceName, observerInfo.observer);
   },
 
   /**
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -506,18 +506,16 @@
 @RESPATH@/components/CaptivePortalDetectComponents.manifest
 @RESPATH@/components/captivedetect.js
 @RESPATH@/components/servicesComponents.manifest
 @RESPATH@/components/cryptoComponents.manifest
 @RESPATH@/components/TelemetryStartup.js
 @RESPATH@/components/TelemetryStartup.manifest
 @RESPATH@/components/XULStore.js
 @RESPATH@/components/XULStore.manifest
-@RESPATH@/components/messageWakeupService.js
-@RESPATH@/components/messageWakeupService.manifest
 @RESPATH@/components/recording-cmdline.js
 @RESPATH@/components/recording-cmdline.manifest
 @RESPATH@/components/htmlMenuBuilder.js
 @RESPATH@/components/htmlMenuBuilder.manifest
 
 @RESPATH@/components/NotificationStorage.js
 @RESPATH@/components/NotificationStorage.manifest
 @RESPATH@/components/Push.js
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -378,17 +378,17 @@ this.TabCrashHandler = {
     // with the empty string.
     if (!includeURL) {
       extraExtraKeyVals["URL"] = "";
     }
 
     CrashSubmit.submit(dumpID, {
       recordSubmission: true,
       extraExtraKeyVals,
-    }).catch(Cu.reportError);
+    }).then(null, Cu.reportError);
 
     this.prefs.setBoolPref("sendReport", true);
     this.prefs.setBoolPref("includeURL", includeURL);
     this.prefs.setBoolPref("emailMe", emailMe);
     if (emailMe) {
       this.prefs.setCharPref("email", email);
     } else {
       this.prefs.setCharPref("email", "");
@@ -876,17 +876,17 @@ this.UnsubmittedCrashHandler = {
    *        The array of reportIDs to submit.
    */
   submitReports(reportIDs) {
     for (let reportID of reportIDs) {
       CrashSubmit.submit(reportID, {
         extraExtraKeyVals: {
           "SubmittedFromInfobar": true,
         },
-      }).catch(Cu.reportError);
+      });
     }
   },
 };
 
 this.PluginCrashReporter = {
   /**
    * Makes the PluginCrashReporter ready to hear about and
    * submit crash reports.
@@ -990,17 +990,17 @@ this.PluginCrashReporter = {
     let { pluginDumpID, browserDumpID } = this.crashReports.get(runID);
 
     let submissionPromise = CrashSubmit.submit(pluginDumpID, {
       recordSubmission: true,
       extraExtraKeyVals: keyVals,
     });
 
     if (browserDumpID)
-      CrashSubmit.submit(browserDumpID).catch(Cu.reportError);
+      CrashSubmit.submit(browserDumpID);
 
     this.broadcastState(runID, "submitting");
 
     submissionPromise.then(() => {
       this.broadcastState(runID, "success");
     }, () => {
       this.broadcastState(runID, "failed");
     });
--- a/devtools/client/debugger/new/test/mochitest/.eslintrc
+++ b/devtools/client/debugger/new/test/mochitest/.eslintrc
@@ -21,16 +21,17 @@
     "is": false,
     "isnot": false,
     "ok": false,
     "registerCleanupFunction": false,
     "requestLongerTimeout": false,
     "SimpleTest": false,
     "SpecialPowers": false,
     "TestUtils": false,
+    "thisTestLeaksUncaughtRejectionsAndShouldBeFixed": false,
     "todo": false,
     "todo_is": false,
     "todo_isnot": false,
     "waitForClipboard": false,
     "waitForExplicitFinish": false,
     "waitForFocus": false,
 
     // Globals introduced in debugger-specific head.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-01.js
@@ -1,13 +1,19 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejections should be fixed.
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("[object Object]");
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed(
+  "TypeError: this.transport is null");
+
 /**
  * Tests that event listeners aren't fetched when the events tab isn't selected.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_event-listeners-02.html";
 
 function test() {
   let options = {
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
@@ -8,23 +8,16 @@
  */
 
 "use strict";
 
 const TAB_URL = EXAMPLE_URL + "doc_promise-get-rejection-stack.html";
 const { PromisesFront } = require("devtools/shared/fronts/promises");
 var events = require("sdk/event/core");
 
-// The code in the document above leaves an uncaught rejection. This is only
-// reported to the testing framework if the code is loaded in the main process.
-if (!gMultiProcessBrowser) {
-  Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-  PromiseTestUtils.expectUncaughtRejection(/hello/);
-}
-
 const TEST_DATA = [
   {
     functionDisplayName: "returnPromise/<",
     line: 19,
     column: 47
   },
   {
     functionDisplayName: "returnPromise",
--- a/devtools/client/debugger/test/mochitest/browser_dbg_terminate-on-tab-close.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_terminate-on-tab-close.js
@@ -1,24 +1,21 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejections should be fixed.
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("[object Object]");
+
 /**
  * Tests that debuggee scripts are terminated on tab closure.
  */
 
-// The following rejection should not be left uncaught. This test has been
-// whitelisted until the issue is fixed.
-if (!gMultiProcessBrowser) {
-  Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-  PromiseTestUtils.expectUncaughtRejection(/error\.message is undefined/);
-}
-
 const TAB_URL = EXAMPLE_URL + "doc_terminate-on-tab-close.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
   initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-console-04.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-console-04.js
@@ -1,20 +1,12 @@
 // Check that the date and regexp previewers work in the console of a worker debugger.
 
 "use strict";
 
-// The following intermittent rejection should not be left uncaught. This test
-// has been whitelisted until the issue is fixed.
-//
-// NOTE: Whitelisting a class of rejections should be limited. Normally you
-//       should use "expectUncaughtRejection" to flag individual failures.
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.whitelistRejectionsGlobally(/[object Object]/);
-
 const TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
 const WORKER_URL = "code_WorkerActor.attachThread-worker.js";
 
 add_task(function* testPausedByConsole() {
   let {client, tab, workerClient, toolbox} =
     yield initWorkerDebugger(TAB_URL, WORKER_URL);
 
   info("Check Date objects can be used in the console");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
@@ -1,17 +1,16 @@
 // Check to make sure that a worker can be attached to a toolbox
 // directly, and that the toolbox has expected properties.
 
 "use strict";
 
-// The following "connectionClosed" rejection should not be left uncaught. This
-// test has been whitelisted until the issue is fixed.
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.expectUncaughtRejection(/[object Object]/);
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejections should be fixed.
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("[object Object]");
 
 var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
 var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
 
 add_task(function* () {
   yield pushPrefs(["devtools.scratchpad.enabled", true]);
 
   DebuggerServer.init();
--- a/devtools/client/framework/test/browser_devtools_api.js
+++ b/devtools/client/framework/test/browser_devtools_api.js
@@ -1,13 +1,22 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejections should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
+
+// When running in a standalone directory, we get this error
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.doc is undefined");
+
 // Tests devtools API
 
 "use strict";
 
 const toolId1 = "test-tool-1";
 const toolId2 = "test-tool-2";
 
 function test() {
--- a/devtools/client/framework/test/browser_toolbox_tool_ready.js
+++ b/devtools/client/framework/test/browser_toolbox_tool_ready.js
@@ -2,16 +2,23 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 requestLongerTimeout(5);
 
+/**
+ * Whitelisting this test.
+ * As part of bug 1077403, the leaking uncaught rejection should be fixed.
+ */
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Shader Editor is " +
+  "still waiting for a WebGL context to be created.");
+
 function performChecks(target) {
   return Task.spawn(function* () {
     let toolIds = gDevTools.getToolDefinitionArray()
                            .filter(def => def.isTargetSupported(target))
                            .map(def => def.id);
 
     let toolbox;
     for (let index = 0; index < toolIds.length; index++) {
--- a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+++ b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
@@ -1,15 +1,22 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+/**
+ * Whitelisting this test.
+ * As part of bug 1077403, the leaking uncaught rejection should be fixed.
+ */
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Shader Editor is " +
+  "still waiting for a WebGL context to be created.");
+
 const { DebuggerServer } = require("devtools/server/main");
 const { DebuggerClient } = require("devtools/shared/client/main");
 
 // Bug 1277805: Too slow for debug runs
 requestLongerTimeout(2);
 
 /**
  * Bug 979536: Ensure fronts are destroyed after toolbox close.
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -14,32 +14,16 @@ const {classes: Cc, interfaces: Ci, util
   = Components;
 
 function scopedCuImport(path) {
   const scope = {};
   Cu.import(path, scope);
   return scope;
 }
 
-// There are shutdown issues for which multiple rejections are left uncaught.
-// This bug should be fixed, but for the moment devtools are whitelisted.
-//
-// NOTE: Entire directory whitelisting should be kept to a minimum. Normally you
-//       should use "expectUncaughtRejection" to flag individual failures.
-const {PromiseTestUtils} = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
-PromiseTestUtils.whitelistRejectionsGlobally(/Component not initialized/);
-PromiseTestUtils.whitelistRejectionsGlobally(/Connection closed/);
-PromiseTestUtils.whitelistRejectionsGlobally(/destroy/);
-PromiseTestUtils.whitelistRejectionsGlobally(/is no longer, usable/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\._urls is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.tabTarget is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.toolbox is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.webConsoleClient is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.worker is null/);
-
 const {console} = scopedCuImport("resource://gre/modules/Console.jsm");
 const {ScratchpadManager} = scopedCuImport("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 const {loader, require} = scopedCuImport("resource://devtools/shared/Loader.jsm");
 
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {TargetFactory} = require("devtools/client/framework/target");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const flags = require("devtools/shared/flags");
--- a/devtools/client/inspector/computed/test/browser_computed_style-editor-link.js
+++ b/devtools/client/inspector/computed/test/browser_computed_style-editor-link.js
@@ -1,14 +1,18 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
+
 // Tests the links from the computed view to the style editor.
 
 const STYLESHEET_URL = "data:text/css," + encodeURIComponent(
   ".highlight {color: blue}");
 
 const DOCUMENT_URL = "data:text/html;charset=utf-8," + encodeURIComponent(
   `<html>
    <head>
--- a/devtools/client/inspector/markup/test/browser_markup_links_06.js
+++ b/devtools/client/inspector/markup/test/browser_markup_links_06.js
@@ -2,21 +2,16 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that the contextual menu items shown when clicking on linked attributes
 // for <script> and <link> tags actually open the right tools.
 
-// The following rejection should not be left uncaught. This test has been
-// whitelisted until the issue is fixed.
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.expectUncaughtRejection(/NS_ERROR_NOT_INITIALIZED/);
-
 const TEST_URL = URL_ROOT + "doc_markup_links.html";
 
 add_task(function* () {
   let {toolbox, inspector} = yield openInspectorForURL(TEST_URL);
 
   info("Select a node with a cssresource attribute");
   yield selectNode("link", inspector);
 
--- a/devtools/client/inspector/rules/test/browser_rules_style-editor-link.js
+++ b/devtools/client/inspector/rules/test/browser_rules_style-editor-link.js
@@ -1,14 +1,18 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+// FIXME: Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
+
 // Test the links from the rule-view to the styleeditor
 
 const STYLESHEET_URL = "data:text/css," + encodeURIComponent(
   ["#first {",
    "color: blue",
    "}"].join("\n"));
 
 const EXTERNAL_STYLESHEET_FILE_NAME = "doc_style_editor_link.css";
--- a/devtools/client/inspector/test/browser_inspector_highlighter-comments.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-comments.js
@@ -1,15 +1,21 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("false");
+
 // Test that hovering over the markup-view's containers doesn't always show the
 // highlighter, depending on the type of node hovered over.
 
 const TEST_PAGE = URL_ROOT +
   "doc_inspector_highlighter-comments.html";
 
 add_task(function* () {
   let {inspector, testActor} = yield openInspectorForURL(TEST_PAGE);
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
@@ -2,24 +2,16 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Tests if requests render correct information in the menu UI.
  */
 
-// The following intermittent rejections should not be left uncaught. This test
-// has been whitelisted until the issue is fixed.
-//
-// NOTE: Whitelisting a class of rejections should be limited. Normally you
-//       should use "expectUncaughtRejection" to flag individual failures.
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.whitelistRejectionsGlobally(/requestItem is undefined/);
-
 function test() {
   // Disable tcp fast open, because it is setting a response header indicator
   // (bug 1352274). TCP Fast Open is not present on all platforms therefore the
   // number of response headers will vary depending on the platform.
   Services.prefs.setBoolPref("network.tcp.tcp_fastopen_enable", false);
 
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
--- a/devtools/client/projecteditor/test/browser_projecteditor_editing_01.js
+++ b/devtools/client/projecteditor/test/browser_projecteditor_editing_01.js
@@ -1,14 +1,20 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
+
 loadHelperScript("helper_edits.js");
 
 // Test ProjectEditor basic functionality
 add_task(function* () {
   let projecteditor = yield addProjectEditorTabForTempDirectory();
   let TEMP_PATH = projecteditor.project.allPaths()[0];
 
   is(getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
--- a/devtools/client/projecteditor/test/browser_projecteditor_editors_image.js
+++ b/devtools/client/projecteditor/test/browser_projecteditor_editors_image.js
@@ -1,14 +1,20 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
+
 loadHelperScript("helper_edits.js");
 
 // Test ProjectEditor image editor functionality
 add_task(function* () {
   let projecteditor = yield addProjectEditorTabForTempDirectory();
   let TEMP_PATH = projecteditor.project.allPaths()[0];
 
   is(getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
--- a/devtools/client/projecteditor/test/browser_projecteditor_immediate_destroy.js
+++ b/devtools/client/projecteditor/test/browser_projecteditor_immediate_destroy.js
@@ -1,14 +1,21 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.window is null");
+
 // Test that projecteditor can be destroyed in various states of loading
 // without causing any leaks or exceptions.
 
 add_task(function* () {
 
   info("Testing tab closure when projecteditor is in various states");
   let loaderUrl = "chrome://mochitests/content/browser/devtools/client/projecteditor/test/projecteditor-test.xul";
 
--- a/devtools/client/projecteditor/test/browser_projecteditor_saveall.js
+++ b/devtools/client/projecteditor/test/browser_projecteditor_saveall.js
@@ -1,14 +1,20 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
+
 loadHelperScript("helper_edits.js");
 
 // Test ProjectEditor basic functionality
 add_task(function* () {
   let projecteditor = yield addProjectEditorTabForTempDirectory();
   let TEMP_PATH = projecteditor.project.allPaths()[0];
 
   is(getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
--- a/devtools/client/projecteditor/test/browser_projecteditor_tree_selection_02.js
+++ b/devtools/client/projecteditor/test/browser_projecteditor_tree_selection_02.js
@@ -1,14 +1,20 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
+
 // Test that files get reselected in the tree when their editor
 // is focused.  https://bugzilla.mozilla.org/show_bug.cgi?id=1011116.
 
 add_task(function* () {
   let projecteditor = yield addProjectEditorTabForTempDirectory();
   let TEMP_PATH = projecteditor.project.allPaths()[0];
 
   is(getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
--- a/devtools/client/projecteditor/test/head.js
+++ b/devtools/client/projecteditor/test/head.js
@@ -1,30 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var Cu = Components.utils;
-
-// There are shutdown issues for which multiple rejections are left uncaught.
-// This bug should be fixed, but for the moment devtools are whitelisted.
-//
-// NOTE: Entire directory whitelisting should be kept to a minimum. Normally you
-//       should use "expectUncaughtRejection" to flag individual failures.
-const {PromiseTestUtils} = Cu.import("resource://testing-common/PromiseTestUtils.jsm", {});
-PromiseTestUtils.whitelistRejectionsGlobally(/Component not initialized/);
-PromiseTestUtils.whitelistRejectionsGlobally(/Connection closed/);
-PromiseTestUtils.whitelistRejectionsGlobally(/destroy/);
-PromiseTestUtils.whitelistRejectionsGlobally(/is no longer, usable/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\._urls is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.tabTarget is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.toolbox is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.webConsoleClient is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.worker is null/);
-
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const {TargetFactory} = require("devtools/client/framework/target");
 const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
 const promise = require("promise");
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const ProjectEditor = require("devtools/client/projecteditor/lib/projecteditor");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
--- a/devtools/client/responsivedesign/test/browser_responsive_cmd.js
+++ b/devtools/client/responsivedesign/test/browser_responsive_cmd.js
@@ -1,13 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
+
 function test() {
   let manager = ResponsiveUIManager;
   let done;
 
   function isOpen() {
     return gBrowser.getBrowserContainer(gBrowser.selectedBrowser)
                    .hasAttribute("responsivemode");
   }
--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_shadereditor.js
+++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_shadereditor.js
@@ -1,13 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed(
+  "Error: Shader Editor is still waiting for a WebGL context to be created.");
+
 const TEST_URI = "data:text/html;charset=utf-8," +
   "<p>browser_telemetry_toolboxtabs_shadereditor.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 const TOOL_PREF = "devtools.shadereditor.enabled";
 
--- a/devtools/client/shared/test/browser_toolbar_tooltip.js
+++ b/devtools/client/shared/test/browser_toolbar_tooltip.js
@@ -1,15 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that the developer toolbar works properly
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed(
+  "Protocol error (unknownError): Error: Got an invalid root window in DocumentWalker"
+);
+
 const TEST_URI = "data:text/html;charset=utf-8,<p>Tooltip Tests</p>";
 const PREF_DEVTOOLS_THEME = "devtools.theme";
 
 registerCleanupFunction(() => {
   // Set preferences back to their original values
   Services.prefs.clearUserPref(PREF_DEVTOOLS_THEME);
 });
 
--- a/devtools/client/webaudioeditor/test/browser_wa_first-run.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_first-run.js
@@ -1,11 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Connection closed");
+
 /**
  * Tests that the reloading/onContentLoaded hooks work.
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_reset-01.js
@@ -1,11 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Connection closed");
+
 /**
  * Tests that reloading a tab will properly listen for the `start-context`
  * event and reshow the tools after reloading.
  */
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-04.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_reset-04.js
@@ -1,11 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Connection closed");
+
 /**
  * Tests that switching to an iframe works fine.
  */
 
 add_task(function* () {
   Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
 
   let { target, panel, toolbox } = yield initWebAudioEditor(IFRAME_CONTEXT_URL);
--- a/devtools/client/webaudioeditor/test/head.js
+++ b/devtools/client/webaudioeditor/test/head.js
@@ -1,30 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-// There are shutdown issues for which multiple rejections are left uncaught.
-// This bug should be fixed, but for the moment devtools are whitelisted.
-//
-// NOTE: Entire directory whitelisting should be kept to a minimum. Normally you
-//       should use "expectUncaughtRejection" to flag individual failures.
-const { PromiseTestUtils } = Cu.import("resource://testing-common/PromiseTestUtils.jsm", {});
-PromiseTestUtils.whitelistRejectionsGlobally(/Component not initialized/);
-PromiseTestUtils.whitelistRejectionsGlobally(/Connection closed/);
-PromiseTestUtils.whitelistRejectionsGlobally(/destroy/);
-PromiseTestUtils.whitelistRejectionsGlobally(/is no longer, usable/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\._urls is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.tabTarget is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.toolbox is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.webConsoleClient is null/);
-PromiseTestUtils.whitelistRejectionsGlobally(/this\.worker is null/);
-
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { Task } = require("devtools/shared/task");
 var Services = require("Services");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { DebuggerServer } = require("devtools/server/main");
 var { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 
--- a/devtools/client/webconsole/test/browser_webconsole_output_01.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_01.js
@@ -4,16 +4,18 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejection should be fixed.
 //
 
 "use strict";
 
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null");
+
 // Test the webconsole output for various types of objects.
 
 const TEST_URI = "data:text/html;charset=utf8,test for console output - 01";
 
 var {DebuggerServer} = require("devtools/server/main");
 
 var longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 4)).join("a");
 var initialString = longString.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
--- a/devtools/client/webconsole/test/browser_webconsole_output_04.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_04.js
@@ -4,16 +4,18 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejection should be fixed.
 //
 
 "use strict";
 
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null");
+
 // Test the webconsole output for various types of objects.
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-console-output-04.html";
 
 var inputTests = [
   // 0
   {
--- a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_01.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_01.js
@@ -3,16 +3,20 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejections should be fixed.
 
 "use strict";
 
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed(null);
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed(
+  "TypeError: this.toolbox is null");
+
 // Test the webconsole output for various types of DOM Nodes.
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-console-output-dom-elements.html";
 
 var inputTests = [
   {
     input: "testBodyNode()",
--- a/devtools/client/webconsole/test/browser_webconsole_output_events.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_events.js
@@ -3,16 +3,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejection should be fixed.
 
 "use strict";
 
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null");
+
 // Test the webconsole output for DOM events.
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-console-output-events.html";
 
 add_task(function* () {
   yield loadTab(TEST_URI);
 
--- a/devtools/server/actors/webextension-inspected-window.js
+++ b/devtools/server/actors/webextension-inspected-window.js
@@ -285,17 +285,17 @@ var WebExtensionInspectedWindowActor = p
             });
 
             this.customizedReload.start()
                 .then(() => {
                   delete this.customizedReload;
                 })
                 .catch(err => {
                   delete this.customizedReload;
-                  console.error(err);
+                  throw err;
                 });
           } catch (err) {
             // Cancel the customized reload (if any) on exception during the
             // reload setup.
             if (this.customizedReload) {
               this.customizedReload.stop(err);
             }
 
deleted file mode 100644
--- a/dom/base/messageWakeupService.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-const CATEGORY_WAKEUP_REQUEST = "wakeup-request";
-
-function MessageWakeupService() { };
-
-MessageWakeupService.prototype =
-{
-  classID:          Components.ID("{f9798742-4f7b-4188-86ba-48b116412b29}"),
-  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIObserver]),
-
-  messagesData: [],
-
-  get messageManager() {
-    if (!this._messageManager)
-      this._messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"].
-                             getService(Ci.nsIMessageListenerManager);
-    return this._messageManager;
-  },
-
-  requestWakeup: function(aMessageName, aCid, aIid, aMethod) {
-    this.messagesData[aMessageName] = {
-      cid: aCid,
-      iid: aIid,
-      method: aMethod,
-    };
-
-    this.messageManager.addMessageListener(aMessageName, this);
-  },
-
-  receiveMessage: function(aMessage) {
-    let data = this.messagesData[aMessage.name];
-    // TODO: When bug 593407 is ready, stop doing the wrappedJSObject hack
-    //       and use this line instead:
-    //                  QueryInterface(Ci.nsIMessageListener);
-    let service = Cc[data.cid][data.method](Ci[data.iid]).
-                  wrappedJSObject;
-
-    // The receiveMessage() call itself may spin an event loop, and we
-    // do not want to swap listeners in that - it would cause the current
-    // message to be answered by two listeners. So, we call that first,
-    // then queue the swap for the next event loop
-    let ret = service.receiveMessage(aMessage);
-
-    if (data.timer) {
-      // Handle the case of two such messages happening in quick succession
-      data.timer.cancel();
-      data.timer = null;
-    }
-
-    data.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    let self = this;
-    data.timer.initWithCallback(function() {
-      self.messageManager.addMessageListener(aMessage.name, service);
-      self.messageManager.removeMessageListener(aMessage.name, self);
-      delete self.messagesData[aMessage.name];
-    }, 0, Ci.nsITimer.TYPE_ONE_SHOT);
-
-    return ret;
-  },
-
-  observe: function TM_observe(aSubject, aTopic, aData) {
-    switch (aTopic) {
-      case "profile-after-change":
-        {
-          var catMan = Cc["@mozilla.org/categorymanager;1"].
-                           getService(Ci.nsICategoryManager);
-          var entries = catMan.enumerateCategory(CATEGORY_WAKEUP_REQUEST);
-          while (entries.hasMoreElements()) {
-            var entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
-            var value = catMan.getCategoryEntry(CATEGORY_WAKEUP_REQUEST, entry);
-            var parts = value.split(",");
-            var cid = parts[0];
-            var iid = parts[1];
-            var method = parts[2];
-            var messages = parts.slice(3);
-            messages.forEach(function(messageName) {
-              this.requestWakeup(messageName, cid, iid, method);
-            }, this);
-          }
-        }
-        break;
-    }
-  },
-};
-
-var components = [MessageWakeupService];
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
-
deleted file mode 100644
--- a/dom/base/messageWakeupService.manifest
+++ /dev/null
@@ -1,4 +0,0 @@
-component {f9798742-4f7b-4188-86ba-48b116412b29} messageWakeupService.js
-contract @mozilla.org/content/messagewakeupservice;1 {f9798742-4f7b-4188-86ba-48b116412b29}
-category profile-after-change messageWakeupService @mozilla.org/content/messagewakeupservice;1
-
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -398,18 +398,16 @@ SOURCES += [
 # nsTextFragment.cpp
 if CONFIG['INTEL_ARCHITECTURE']:
     SOURCES += ['nsTextFragmentSSE2.cpp']
     SOURCES['nsTextFragmentSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
 
 EXTRA_COMPONENTS += [
     'contentAreaDropListener.js',
     'contentAreaDropListener.manifest',
-    'messageWakeupService.js',
-    'messageWakeupService.manifest',
     'ProcessSelector.js',
     'ProcessSelector.manifest',
     'SlowScriptDebug.js',
     'SlowScriptDebug.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'DOMRequestHelper.jsm',
--- a/dom/base/test/browser_timeout_throttling_with_audio_playback.js
+++ b/dom/base/test/browser_timeout_throttling_with_audio_playback.js
@@ -1,15 +1,8 @@
-// The tab closing code leaves an uncaught rejection. This test has been
-// whitelisted until the issue is fixed.
-if (!gMultiProcessBrowser) {
-  Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-  PromiseTestUtils.expectUncaughtRejection(/is no longer, usable/);
-}
-
 const kBaseURI = "http://mochi.test:8888/browser/dom/base/test/empty.html";
 const kPluginJS = "chrome://mochitests/content/browser/dom/base/test/plugin.js";
 var testURLs = [
   "http://mochi.test:8888/browser/dom/base/test/file_audioLoop.html",
   "http://mochi.test:8888/browser/dom/base/test/file_audioLoopInIframe.html",
   "http://mochi.test:8888/browser/dom/base/test/file_pluginAudio.html",
   "http://mochi.test:8888/browser/dom/base/test/file_webaudioLoop.html",
 ];
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6777,18 +6777,20 @@ def getWrapTemplateForType(type, descrip
         if type.nullable():
             conversion = CGIfElseWrapper(
                 "%s.IsNull()" % result,
                 CGGeneric(setNull()),
                 CGGeneric(conversion)).define()
         return conversion, False
 
     if type.isCallback() or type.isCallbackInterface():
-        wrapCode = setObject(
-            "*GetCallbackFromCallbackObject(%(result)s)",
+        # Callbacks can store null if we nuked the compartments their
+        # objects lived in.
+        wrapCode = setObjectOrNull(
+            "GetCallbackFromCallbackObject(%(result)s)",
             wrapAsType=type)
         if type.nullable():
             wrapCode = (
                 "if (%(result)s) {\n" +
                 indent(wrapCode) +
                 "} else {\n" +
                 indent(setNull()) +
                 "}\n")
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1694,19 +1694,19 @@ MediaManager::EnumerateRawDevices(uint64
     if (aVideoType == MediaSourceEnum::Camera) {
       videoLoopDev = Preferences::GetCString("media.video_loopback_dev");
     }
     if (aAudioType == MediaSourceEnum::Microphone) {
       audioLoopDev = Preferences::GetCString("media.audio_loopback_dev");
     }
   }
 
-  MediaManager::PostTask(NewTaskFrom([id, aWindowId, audioLoopDev,
-                                      videoLoopDev, aVideoType,
-                                      aAudioType, aFake]() mutable {
+  RefPtr<Runnable> task = NewTaskFrom([id, aWindowId, audioLoopDev,
+                                       videoLoopDev, aVideoType,
+                                       aAudioType, aFake]() mutable {
     // Only enumerate what's asked for, and only fake cams and mics.
     bool hasVideo = aVideoType != MediaSourceEnum::Other;
     bool hasAudio = aAudioType != MediaSourceEnum::Other;
     bool fakeCams = aFake && aVideoType == MediaSourceEnum::Camera;
     bool fakeMics = aFake && aAudioType == MediaSourceEnum::Microphone;
 
     RefPtr<MediaEngine> fakeBackend, realBackend;
     if (fakeCams || fakeMics) {
@@ -1743,17 +1743,38 @@ MediaManager::EnumerateRawDevices(uint64
         return NS_OK;
       }
       RefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
       if (p) {
         p->Resolve(result.release());
       }
       return NS_OK;
     }));
-  }));
+  });
+
+  if (!aFake &&
+      (aVideoType == MediaSourceEnum::Camera ||
+       aAudioType == MediaSourceEnum::Microphone) &&
+      Preferences::GetBool("media.navigator.permission.device", false)) {
+    // Need to ask permission to retrieve list of all devices;
+    // notify frontend observer and wait for callback notification to post task.
+    const char16_t* const type =
+      (aVideoType != MediaSourceEnum::Camera)     ? u"audio" :
+      (aAudioType != MediaSourceEnum::Microphone) ? u"video" :
+                                                    u"all";
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    obs->NotifyObservers(static_cast<nsIRunnable*>(task),
+                         "getUserMedia:ask-device-permission",
+                         type);
+  } else {
+    // Don't need to ask permission to retrieve list of all devices;
+    // post the retrieval task immediately.
+    MediaManager::PostTask(task.forget());
+  }
+
   return p.forget();
 }
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mBackend(nullptr) {
   mPrefs.mFreq         = 1000; // 1KHz test tone
   mPrefs.mWidth        = 0; // adaptive default
@@ -1836,16 +1857,17 @@ MediaManager::Get() {
       MOZ_CRASH();
     }
 
     LOG(("New Media thread for gum"));
 
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
       obs->AddObserver(sSingleton, "last-pb-context-exited", false);
+      obs->AddObserver(sSingleton, "getUserMedia:got-device-permission", false);
       obs->AddObserver(sSingleton, "getUserMedia:privileged:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
       obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
       obs->AddObserver(sSingleton, "phone-state-changed", false);
     }
     // else MediaManager won't work properly and will leak (see bug 837874)
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
@@ -3014,16 +3036,23 @@ MediaManager::Observe(nsISupports* aSubj
       GetPrefs(branch,NS_ConvertUTF16toUTF8(aData).get());
       LOG(("%s: %dx%d @%dfps (min %d)", __FUNCTION__,
            mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mMinFPS));
     }
   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
     // Clear memory of private-browsing-specific deviceIds. Fire and forget.
     media::SanitizeOriginKeys(0, true);
     return NS_OK;
+  } else if (!strcmp(aTopic, "getUserMedia:got-device-permission")) {
+    MOZ_ASSERT(aSubject);
+    nsCOMPtr<nsIRunnable> task = do_QueryInterface(aSubject);
+    MediaManager::PostTask(NewTaskFrom([task] {
+      task->Run();
+    }));
+    return NS_OK;
   } else if (!strcmp(aTopic, "getUserMedia:privileged:allow") ||
              !strcmp(aTopic, "getUserMedia:response:allow")) {
     nsString key(aData);
     RefPtr<GetUserMediaTask> task;
     mActiveCallbacks.Remove(key, getter_AddRefs(task));
     if (!task) {
       return NS_OK;
     }
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -268,17 +268,21 @@ GeckoMediaPluginServiceParent::Observe(n
       }
       if (crashNow) {
         nsCOMPtr<nsIThread> gmpThread;
         {
           MutexAutoLock lock(mMutex);
           gmpThread = mGMPThread;
         }
         if (gmpThread) {
-          gmpThread->Dispatch(WrapRunnable(this,
+          // Note: the GeckoMediaPluginServiceParent singleton is kept alive by a
+          // static refptr that is only cleared in the final stage of
+          // shutdown after everything else is shutdown, so this RefPtr<> is not
+          // strictly necessary so long as that is true, but it's safer.
+          gmpThread->Dispatch(WrapRunnable(RefPtr<GeckoMediaPluginServiceParent>(this),
                                            &GeckoMediaPluginServiceParent::CrashPlugins),
                               NS_DISPATCH_NORMAL);
         }
       }
     }
   } else if (!strcmp("profile-change-teardown", aTopic)) {
     mWaitingForPluginsSyncShutdown = true;
 
--- a/dom/media/tests/mochitest/dataChannel.js
+++ b/dom/media/tests/mochitest/dataChannel.js
@@ -42,25 +42,27 @@ var commandsWaitForDataChannel = [
     return test.pcRemote.nextDataChannel.then(channel => channel.opened);
   },
 ];
 
 var commandsCheckDataChannel = [
   function SEND_MESSAGE(test) {
     var message = "Lorem ipsum dolor sit amet";
 
+    info("Sending message:" + message);
     return test.send(message).then(result => {
       is(result.data, message, "Message correctly transmitted from pcLocal to pcRemote.");
     });
   },
 
   function SEND_BLOB(test) {
     var contents = "At vero eos et accusam et justo duo dolores et ea rebum.";
     var blob = new Blob([contents], { "type" : "text/plain" });
 
+    info("Sending blob");
     return test.send(blob).then(result => {
       ok(result.data instanceof Blob, "Received data is of instance Blob");
       is(result.data.size, blob.size, "Received data has the correct size.");
 
       return getBlobContent(result.data);
     }).then(recv_contents =>
             is(recv_contents, contents, "Received data has the correct content."));
   },
@@ -75,44 +77,47 @@ var commandsCheckDataChannel = [
       is(targetChannel.binaryType, "blob", targetChannel + " is of binary type 'blob'");
     });
   },
 
   function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL(test) {
     var channels = test.pcRemote.dataChannels;
     var message = "I am the Omega";
 
+    info("Sending message:" + message);
     return test.send(message).then(result => {
       is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
       is(result.data, message, "Received message has the correct content.");
     });
   },
 
 
   function SEND_MESSAGE_THROUGH_FIRST_CHANNEL(test) {
     var message = "Message through 1st channel";
     var options = {
       sourceChannel: test.pcLocal.dataChannels[0],
       targetChannel: test.pcRemote.dataChannels[0]
     };
 
+    info("Sending message:" + message);
     return test.send(message, options).then(result => {
       is(test.pcRemote.dataChannels.indexOf(result.channel), 0, "1st channel used");
       is(result.data, message, "Received message has the correct content.");
     });
   },
 
 
   function SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL(test) {
     var message = "Return a message also through 1st channel";
     var options = {
       sourceChannel: test.pcRemote.dataChannels[0],
       targetChannel: test.pcLocal.dataChannels[0]
     };
 
+    info("Sending message:" + message);
     return test.send(message, options).then(result => {
       is(test.pcLocal.dataChannels.indexOf(result.channel), 0, "1st channel used");
       is(result.data, message, "Return message has the correct content.");
     });
   },
 
   function CREATE_NEGOTIATED_DATA_CHANNEL(test) {
     var options = {
@@ -156,16 +161,17 @@ var commandsCheckDataChannel = [
       */
     });
   },
 
   function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL2(test) {
     var channels = test.pcRemote.dataChannels;
     var message = "I am the walrus; Goo goo g'joob";
 
+    info("Sending message:" + message);
     return test.send(message).then(result => {
       is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
       is(result.data, message, "Received message has the correct content.");
     });
   },
 
   function CREATE_NEGOTIATED_MAXPACKET_LIFE_DATA_CHANNEL(test) {
     var options = {
@@ -182,30 +188,32 @@ var commandsCheckDataChannel = [
 
     });
   },
 
   function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL3(test) {
     var channels = test.pcRemote.dataChannels;
     var message = "Nice to see you working maxPacketLifeTime";
 
+    info("Sending message:" + message);
     return test.send(message).then(result => {
       is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
       is(result.data, message, "Received message has the correct content.");
     });
   }
 ];
 
 var commandsCheckLargeXfer = [
   function SEND_BIG_BUFFER(test) {
     var size = 512*1024; // SCTP internal buffer is 256K, so we'll have ~256K queued
     var buffer = new ArrayBuffer(size);
     // note: type received is always blob for binary data
     var options = {};
     options.bufferedAmountLowThreshold = 64*1024;
+    info("Sending arraybuffer");
     return test.send(buffer, options).then(result => {
       ok(result.data instanceof Blob, "Received data is of instance Blob");
       is(result.data.size, size, "Received data has the correct size.");
     });
   },
 ];
 
 function addInitialDataChannel(chain) {
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -96,16 +96,17 @@ using mozilla::plugins::PluginModuleCont
 #include "mozilla/a11y/Compatibility.h"
 #endif
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 #include <android/log.h>
 #include "android_npapi.h"
 #include "ANPBase.h"
+#include "FennecJNIWrappers.h"
 #include "GeneratedJNIWrappers.h"
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #endif
 
 #include "nsIAudioChannelAgent.h"
 #include "AudioChannelService.h"
 
@@ -2077,20 +2078,23 @@ NPError
 
     case kSupportedDrawingModel_ANPGetValue: {
       LOG("get supported drawing model");
       return NPERR_GENERIC_ERROR;
     }
 
     case kJavaContext_ANPGetValue: {
       LOG("get java context");
-      auto ret = java::GeckoAppShell::GetContext();
-      if (!ret)
+
+      auto ret = npp && jni::IsFennec()
+          ? java::GeckoApp::GetPluginContext()
+          : java::GeckoAppShell::GetApplicationContext();
+      if (!ret) {
         return NPERR_GENERIC_ERROR;
-
+      }
       *static_cast<jobject*>(result) = ret.Forget();
       return NPERR_NO_ERROR;
     }
 
     case kAudioTrackInterfaceV1_ANPGetValue: {
       LOG("get audio interface v1");
       ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result;
       InitAudioTrackInterfaceV1(i);
--- a/dom/tests/browser/helper_largeAllocation.js
+++ b/dom/tests/browser/helper_largeAllocation.js
@@ -288,17 +288,16 @@ function* largeAllocSuccessTests() {
     // process which was created for the normal content to be loaded into as the
     // browsing context was booted out of the fresh process. If we discover that
     // this was not a fresh process, we'll need to wait for another process.
     // Start listening now.
     epc = expectProcessCreated();
     if (!(yield getInLAProc(aBrowser))) {
       yield epc;
     } else {
-      epc.catch(() => {});
       epc.kill();
     }
 
     let pid3 = yield getPID(aBrowser);
 
     isnot(pid1, pid3, "PIDs 1 and 3 should not match");
     isnot(pid2, pid3, "PIDs 2 and 3 should not match");
     is(true, yield getInLAProc(aBrowser));
--- a/gfx/layers/ipc/UiCompositorControllerParent.cpp
+++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp
@@ -260,17 +260,22 @@ UiCompositorControllerParent::Initialize
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AddRef();
   LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
   MOZ_ASSERT(state);
   MOZ_ASSERT(state->mParent);
   state->mUiControllerParent = this;
 #if defined(MOZ_WIDGET_ANDROID)
-  state->mParent->GetAPZCTreeManager()->InitializeDynamicToolbarAnimator(mRootLayerTreeId);
+  RefPtr<APZCTreeManager> manager = state->mParent->GetAPZCTreeManager();
+  // Since this is called from the UI thread. It is possible the compositor has already
+  // started shutting down and the APZCTreeManager could be a nullptr.
+  if (manager) {
+    manager->InitializeDynamicToolbarAnimator(mRootLayerTreeId);
+  }
 #endif
 }
 
 void
 UiCompositorControllerParent::Open(Endpoint<PUiCompositorControllerParent>&& aEndpoint)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (!aEndpoint.Bind(this)) {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1157,16 +1157,22 @@ LogTextPerfStats(gfxTextPerfMetrics* aTe
   }
 }
 
 void
 PresShell::Destroy()
 {
   // Do not add code before this line please!
   if (mHaveShutDown) {
+    // If we never got a root frame the root view could exist now still.
+    // In that case assert that it has no children and no frame.
+    MOZ_RELEASE_ASSERT(!mViewManager || !mViewManager->GetRootView() ||
+      (!mViewManager->GetRootView()->GetFrame() &&
+       !mViewManager->GetRootView()->GetFirstChild()));
+    MOZ_RELEASE_ASSERT(!mFrameConstructor || !mFrameConstructor->GetRootFrame());
     return;
   }
 
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
     "destroy called on presshell while scripts not blocked");
 
   // dump out cumulative text perf metrics
   gfxTextPerfMetrics* tp;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -404,16 +404,18 @@ protected:
   int32_t mHintCharsetSource;
   nsCString mHintCharset;
   nsCString mForceCharacterSet;
   
   bool mIsPageMode;
   bool mInitializedForPrintPreview;
   bool mHidden;
   bool mPrintRelated; // Only use for asserts.
+  bool mPresShellDestroyed; // Only use for asserts.
+  bool mDestroyWasFull; // Only use for asserts.
 };
 
 namespace mozilla {
 
 /**
  * A RAII class for automatic dispatch of the 'beforeprint' and 'afterprint'
  * events ('beforeprint' on construction, 'afterprint' on destruction).
  *
@@ -538,17 +540,19 @@ nsDocumentViewer::nsDocumentViewer()
 #ifdef DEBUG
     mDebugFile(nullptr),
 #endif // DEBUG
 #endif // NS_PRINTING
     mHintCharsetSource(kCharsetUninitialized),
     mIsPageMode(false),
     mInitializedForPrintPreview(false),
     mHidden(false),
-    mPrintRelated(false)
+    mPrintRelated(false),
+    mPresShellDestroyed(true),
+    mDestroyWasFull(false)
 {
   PrepareToStartLoad();
 }
 
 void
 nsDocumentViewer::SetPrintRelated()
 {
   if (!mPrintRelated) {
@@ -575,26 +579,41 @@ NS_INTERFACE_MAP_END
 
 nsDocumentViewer::~nsDocumentViewer()
 {
   if (mDocument) {
     Close(nullptr);
     mDocument->Destroy();
   }
 
+  nsIFrame* vmRootFrame =
+    mViewManager && mViewManager->GetRootView()
+      ? mViewManager->GetRootView()->GetFrame()
+      : nullptr;
+  nsIFrame* psRootFrame = mPresShell ? mPresShell->GetRootFrame() : nullptr;
+  MOZ_RELEASE_ASSERT(vmRootFrame == psRootFrame);
+
   NS_ASSERTION(!mPresShell && !mPresContext,
                "User did not call nsIContentViewer::Destroy");
   if (mPresShell || mPresContext) {
     // Make sure we don't hand out a reference to the content viewer to
     // the SHEntry!
     mSHEntry = nullptr;
-
+    mDestroyWasFull = false;
     Destroy();
+    MOZ_RELEASE_ASSERT(mDestroyWasFull);
   }
 
+  MOZ_RELEASE_ASSERT(mPresShellDestroyed);
+
+  MOZ_RELEASE_ASSERT(!mPresShell || !mPresShell->GetRootFrame());
+  MOZ_RELEASE_ASSERT(!mViewManager || !mViewManager->GetRootView() ||
+    (!mViewManager->GetRootView()->GetFrame() &&
+     !mViewManager->GetRootView()->GetFirstChild()));
+
   if (mSelectionListener) {
     mSelectionListener->Disconnect();
   }
 
   if (mFocusListener) {
     mFocusListener->Disconnect();
   }
 
@@ -703,16 +722,17 @@ nsDocumentViewer::InitPresentationStuff(
   StyleSetHandle styleSet = CreateStyleSet(mDocument);
 
   // Now make the shell for the document
   mPresShell = mDocument->CreateShell(mPresContext, mViewManager, styleSet);
   if (!mPresShell) {
     styleSet->Delete();
     return NS_ERROR_FAILURE;
   }
+  mPresShellDestroyed = false;
 
   // We're done creating the style set
   styleSet->EndUpdate();
 
   if (aDoInitialReflow) {
     // Since Initialize() will create frames for *all* items
     // that are currently in the document tree, we need to flush
     // any pending notifications to prevent the content sink from
@@ -1791,16 +1811,18 @@ nsDocumentViewer::Destroy()
   if (mPresContext) {
     DestroyPresContext();
   }
 
   mWindow = nullptr;
   mViewManager = nullptr;
   mContainer = WeakPtr<nsDocShell>();
 
+  mDestroyWasFull = true;
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::Stop(void)
 {
   NS_ASSERTION(mDocument, "Stop called too early or too late");
   if (mDocument) {
@@ -4652,25 +4674,43 @@ nsDocumentViewer::SetIsHidden(bool aHidd
 {
   mHidden = aHidden;
   return NS_OK;
 }
 
 void
 nsDocumentViewer::DestroyPresShell()
 {
+  nsIFrame* vmRootFrame =
+    mViewManager && mViewManager->GetRootView()
+      ? mViewManager->GetRootView()->GetFrame()
+      : nullptr;
+  nsIFrame* psRootFrame = mPresShell ? mPresShell->GetRootFrame() : nullptr;
+  MOZ_RELEASE_ASSERT(vmRootFrame == psRootFrame);
+
   // Break circular reference (or something)
   mPresShell->EndObservingDocument();
 
   RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
   if (selection && mSelectionListener)
     selection->RemoveSelectionListener(mSelectionListener);
 
   nsAutoScriptBlocker scriptBlocker;
+  bool hadRootFrame = !!mPresShell->GetRootFrame();
   mPresShell->Destroy();
+  mPresShellDestroyed = true;
+  MOZ_RELEASE_ASSERT(!mPresShell->GetRootFrame());
+  // destroying the frame tree via presshell destroy should have done this
+  if (hadRootFrame) {
+    MOZ_RELEASE_ASSERT(!mViewManager || !mViewManager->GetRootView());
+  }
+  MOZ_RELEASE_ASSERT(!mViewManager || !mViewManager->GetRootView() ||
+    (!mViewManager->GetRootView()->GetFrame() &&
+     !mViewManager->GetRootView()->GetFirstChild()));
+
   mPresShell = nullptr;
 }
 
 void
 nsDocumentViewer::DestroyPresContext()
 {
   mPresContext->Detach();
   mPresContext = nullptr;
--- a/media/ffvpx/ffvpxcommon.mozbuild
+++ b/media/ffvpx/ffvpxcommon.mozbuild
@@ -38,17 +38,20 @@ ALLOW_COMPILER_WARNINGS = True
 if CONFIG['GNU_CC']:
     CFLAGS += [
         '-Wno-parentheses',
         '-Wno-pointer-sign',
         '-Wno-sign-compare',
         '-Wno-switch',
         '-Wno-type-limits',
         '-Wno-unused-function',
+        # XXX This does not seem to have any effect on some versions of GCC.
         '-Wno-deprecated-declarations',
+        '-Wno-discarded-qualifiers',
+        '-Wno-maybe-uninitialized',
     ]
     if CONFIG['CLANG_CXX']:
         CFLAGS += [
             '-Wno-incompatible-pointer-types-discards-qualifiers',
             '-Wno-logical-op-parentheses',
         ]
     # Force visibility of cpu and av_log symbols.
     CFLAGS += ['-include', 'libavutil_visibility.h']
--- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
@@ -304,16 +304,17 @@ WebrtcGmpVideoEncoder::InitEncoderForSiz
 int32_t
 WebrtcGmpVideoEncoder::Encode(const webrtc::VideoFrame& aInputImage,
                               const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                               const std::vector<webrtc::FrameType>* aFrameTypes)
 {
   MOZ_ASSERT(aInputImage.width() >= 0 && aInputImage.height() >= 0);
   // Would be really nice to avoid this sync dispatch, but it would require a
   // copy of the frame, since it doesn't appear to actually have a refcount.
+  // Passing 'this' is safe since this is synchronous.
   mGMPThread->Dispatch(
       WrapRunnable(this,
                    &WebrtcGmpVideoEncoder::Encode_g,
                    &aInputImage,
                    aCodecSpecificInfo,
                    aFrameTypes),
       NS_DISPATCH_SYNC);
 
@@ -771,16 +772,17 @@ WebrtcGmpVideoDecoder::Decode(const webr
                               const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                               int64_t aRenderTimeMs)
 {
   int32_t ret;
   MOZ_ASSERT(mGMPThread);
   MOZ_ASSERT(!NS_IsMainThread());
   // Would be really nice to avoid this sync dispatch, but it would require a
   // copy of the frame, since it doesn't appear to actually have a refcount.
+  // Passing 'this' is safe since this is synchronous.
   mozilla::SyncRunnable::DispatchToThread(mGMPThread,
                 WrapRunnableRet(&ret, this,
                                 &WebrtcGmpVideoDecoder::Decode_g,
                                 aInputImage,
                                 aMissingFrames,
                                 aFragmentation,
                                 aCodecSpecificInfo,
                                 aRenderTimeMs));
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -1132,16 +1132,17 @@ PeerConnectionMedia::ShutdownMediaTransp
   Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_403S,
                        stats.turn_403s);
   Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_438S,
                        stats.turn_438s);
 #endif
 
   mIceCtxHdlr = nullptr;
 
+  // we're holding a ref to 'this' that's released by SelfDestruct_m
   mMainThread->Dispatch(WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),
                         NS_DISPATCH_NORMAL);
 }
 
 LocalSourceStreamInfo*
 PeerConnectionMedia::GetLocalStreamByIndex(int aIndex)
 {
   ASSERT_ON_THREAD(mMainThread);
--- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
@@ -868,16 +868,18 @@ WebrtcGlobalChild::RecvGetLogRequest(con
     return IPC_OK();
   }
 
   nsresult rv;
   nsCOMPtr<nsIEventTarget> stsThread =
     do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
 
   if (NS_SUCCEEDED(rv) && stsThread) {
+    // this is a singleton, so we shouldn't need to hold a ref for the
+    // request (and can't just add a ref here anyways)
     rv = RUN_ON_THREAD(stsThread,
                        WrapRunnableNM(&GetLogging_s, this, aRequestId, aPattern.get()),
                        NS_DISPATCH_NORMAL);
 
     if (NS_SUCCEEDED(rv)) {
       return IPC_OK();
     }
   }
--- a/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc
@@ -220,16 +220,17 @@ bool DeviceInfoAndroid::FindCameraIndex(
   return FindCameraIndexByName(deviceUniqueIdUTF8, index);
 }
 
 int32_t DeviceInfoAndroid::Init() {
   return 0;
 }
 
 uint32_t DeviceInfoAndroid::NumberOfDevices() {
+  Refresh();
   return g_camera_info->size();
 }
 
 int32_t DeviceInfoAndroid::GetDeviceName(
     uint32_t deviceNumber,
     char* deviceNameUTF8,
     uint32_t deviceNameLength,
     char* deviceUniqueIdUTF8,
--- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
@@ -70,18 +70,18 @@ public class VideoCaptureDeviceInfoAndro
   }
 
   // Returns information about all cameras on the device.
   // Since this reflects static information about the hardware present, there is
   // no need to call this function more than once in a single process.  It is
   // marked "private" as it is only called by native code.
   @WebRTCJNITarget
   private static CaptureCapabilityAndroid[] getDeviceInfo() {
-      final boolean hasPermissions = Permissions.waitFor(
-              (Activity) GeckoAppShell.getContext(), Manifest.permission.CAMERA);
+      final boolean hasPermissions = Permissions.has(
+              GeckoAppShell.getApplicationContext(), Manifest.permission.CAMERA);
 
       if (hasPermissions) {
           return createDeviceList();
       } else {
           return new CaptureCapabilityAndroid[0];
       }
   }
 
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -919,8 +919,11 @@ pref("webchannel.allowObject.urlWhitelis
 
 pref("media.openUnsupportedTypeWithExternalApp", true);
 
 pref("dom.keyboardevent.dispatch_during_composition", true);
 
 #if CPU_ARCH == aarch64
 pref("javascript.options.native_regexp", false);
 #endif
+
+// Ask for permission when enumerating WebRTC devices.
+pref("media.navigator.permission.device", true);
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -1788,17 +1788,17 @@ public class BrowserApp extends GeckoApp
                             menu.findItem(R.id.help).setEnabled(true);
                         }
                     }
                 });
 
                 // Display notification for Mozilla data reporting, if data should be collected.
                 if (AppConstants.MOZ_DATA_REPORTING &&
                         Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) {
-                    DataReportingNotification.checkAndNotifyPolicy(GeckoAppShell.getContext());
+                    DataReportingNotification.checkAndNotifyPolicy(this);
                 }
                 break;
 
             case "Gecko:DelayedStartup":
                 EventDispatcher.getInstance().unregisterUiThreadListener(this, "Gecko:DelayedStartup");
 
                 // Force tabs panel inflation once the initial pageload is finished.
                 ensureTabsPanelExists();
@@ -1984,22 +1984,22 @@ public class BrowserApp extends GeckoApp
                 break;
 
             case "Experiments:GetActive":
                 final List<String> experiments = SwitchBoard.getActiveExperiments(this);
                 callback.sendSuccess(experiments.toArray(new String[experiments.size()]));
                 break;
 
             case "Experiments:SetOverride":
-                Experiments.setOverride(getContext(), message.getString("name"),
+                Experiments.setOverride(this, message.getString("name"),
                                         message.getBoolean("isEnabled"));
                 break;
 
             case "Experiments:ClearOverride":
-                Experiments.clearOverride(getContext(), message.getString("name"));
+                Experiments.clearOverride(this, message.getString("name"));
                 break;
 
             case "Favicon:Request":
                 final String url = message.getString("url");
                 final boolean shouldSkipNetwork = message.getBoolean("skipNetwork");
 
                 if (TextUtils.isEmpty(url)) {
                     callback.sendError(null);
@@ -2035,17 +2035,17 @@ public class BrowserApp extends GeckoApp
 
             case "Sanitize:ClearHistory":
                 BrowserDB.from(getProfile()).clearHistory(
                         getContentResolver(), message.getBoolean("clearSearchHistory", false));
                 callback.sendSuccess(null);
                 break;
 
             case "Sanitize:ClearSyncedTabs":
-                FennecTabsRepository.deleteNonLocalClientsAndTabs(getContext());
+                FennecTabsRepository.deleteNonLocalClientsAndTabs(this);
                 callback.sendSuccess(null);
                 break;
 
             case "Settings:Show":
                 final Intent settingsIntent = new Intent(this, GeckoPreferences.class);
                 final String resource = message.getString(GeckoPreferences.INTENT_EXTRA_RESOURCES);
 
                 GeckoPreferences.setResourceToOpen(settingsIntent, resource);
@@ -2064,42 +2064,42 @@ public class BrowserApp extends GeckoApp
 
                 Telemetry.addToHistogram("PLACES_PAGES_COUNT", db.getCount(cr, "history"));
                 Telemetry.addToHistogram("FENNEC_BOOKMARKS_COUNT", db.getCount(cr, "bookmarks"));
                 Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT",
                         (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0));
                 Telemetry.addToHistogram("FENNEC_CUSTOM_HOMEPAGE",
                         (Tabs.hasHomepage(this) ? 1 : 0));
 
-                final SharedPreferences prefs = GeckoSharedPrefs.forProfile(getContext());
+                final SharedPreferences prefs = GeckoSharedPrefs.forProfile(this);
                 final boolean hasCustomHomepanels =
                         prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY) ||
                         prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY_OLD);
 
                 Telemetry.addToHistogram("FENNEC_HOMEPANELS_CUSTOM", hasCustomHomepanels ? 1 : 0);
 
                 Telemetry.addToHistogram("FENNEC_READER_VIEW_CACHE_SIZE",
-                        SavedReaderViewHelper.getSavedReaderViewHelper(getContext())
+                        SavedReaderViewHelper.getSavedReaderViewHelper(this)
                                              .getDiskSpacedUsedKB());
 
                 if (Versions.feature16Plus) {
                     Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT",
                             (isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0));
                 }
 
                 Telemetry.addToHistogram("FENNEC_ORBOT_INSTALLED",
-                    ContextUtils.isPackageInstalled(getContext(), "org.torproject.android") ? 1 : 0);
+                    ContextUtils.isPackageInstalled(this, "org.torproject.android") ? 1 : 0);
                 break;
 
             case "Website:AppInstalled":
                 final String name = message.getString("name");
                 final String startUrl = message.getString("start_url");
                 final String manifestPath = message.getString("manifest_path");
                 final LoadFaviconResult loadIconResult = FaviconDecoder
-                    .decodeDataURI(getContext(), message.getString("icon"));
+                    .decodeDataURI(this, message.getString("icon"));
                 if (loadIconResult != null) {
                     final Bitmap icon = loadIconResult
                         .getBestBitmap(GeckoAppShell.getPreferredIconSize());
                     GeckoApplication.createAppShortcut(name, startUrl, manifestPath, icon);
                 } else {
                     Log.e(LOGTAG, "Failed to load icon!");
                 }
 
@@ -2826,19 +2826,19 @@ public class BrowserApp extends GeckoApp
                     delegate.onActivityResult(this, requestCode, resultCode, data);
                 }
 
                 super.onActivityResult(requestCode, resultCode, data);
         }
     }
 
     private void showFirstrunPager() {
-        if (Experiments.isInExperimentLocal(getContext(), Experiments.ONBOARDING3_A)) {
+        if (Experiments.isInExperimentLocal(this, Experiments.ONBOARDING3_A)) {
             Telemetry.startUISession(TelemetryContract.Session.EXPERIMENT, Experiments.ONBOARDING3_A);
-            GeckoSharedPrefs.forProfile(getContext()).edit().putString(Experiments.PREF_ONBOARDING_VERSION, Experiments.ONBOARDING3_A).apply();
+            GeckoSharedPrefs.forProfile(this).edit().putString(Experiments.PREF_ONBOARDING_VERSION, Experiments.ONBOARDING3_A).apply();
             Telemetry.stopUISession(TelemetryContract.Session.EXPERIMENT, Experiments.ONBOARDING3_A);
             return;
         }
 
         if (mFirstrunAnimationContainer == null) {
             final ViewStub firstrunPagerStub = (ViewStub) findViewById(R.id.firstrun_pager_stub);
             mFirstrunAnimationContainer = (FirstrunAnimationContainer) firstrunPagerStub.inflate();
             mFirstrunAnimationContainer.load(getApplicationContext(), getSupportFragmentManager());
@@ -4117,17 +4117,17 @@ public class BrowserApp extends GeckoApp
             startActivity(intent);
         } else {
             // By default this listener is used for lists where the offline reader-view icon
             // is shown - hence we need to redirect to the reader-view page by default.
             // However there are some cases where we might not want to use this, e.g.
             // for topsites where we do not indicate that a page is an offline reader-view bookmark too.
             final String pageURL;
             if (!flags.contains(OnUrlOpenListener.Flags.NO_READER_VIEW)) {
-                pageURL = SavedReaderViewHelper.getReaderURLIfCached(getContext(), url);
+                pageURL = SavedReaderViewHelper.getReaderURLIfCached(this, url);
             } else {
                 pageURL = url;
             }
 
             if (!maybeSwitchToTab(pageURL, flags)) {
                 openUrlAndStopEditing(pageURL);
                 clearSelectedTabApplicationId();
             }
@@ -4141,17 +4141,17 @@ public class BrowserApp extends GeckoApp
             throw new IllegalArgumentException("url must not be null");
         }
         if (flags == null) {
             throw new IllegalArgumentException("flags must not be null");
         }
 
         // We only use onUrlOpenInBackgroundListener for the homepanel context menus, hence
         // we should always be checking whether we want the readermode version
-        final String pageURL = SavedReaderViewHelper.getReaderURLIfCached(getContext(), url);
+        final String pageURL = SavedReaderViewHelper.getReaderURLIfCached(this, url);
 
         final boolean isPrivate = flags.contains(OnUrlOpenInBackgroundListener.Flags.PRIVATE);
 
         int loadFlags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_BACKGROUND;
         if (isPrivate) {
             loadFlags |= Tabs.LOADURL_PRIVATE;
         }
 
--- a/mobile/android/base/java/org/mozilla/gecko/DownloadsIntegration.java
+++ b/mobile/android/base/java/org/mozilla/gecko/DownloadsIntegration.java
@@ -13,16 +13,17 @@ import org.mozilla.gecko.util.EventCallb
 import org.mozilla.gecko.util.GeckoBundle;
 
 import java.io.File;
 import java.lang.IllegalArgumentException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import android.app.Activity;
 import android.app.DownloadManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.media.MediaScannerConnection;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 import android.net.Uri;
 import android.os.Environment;
@@ -98,17 +99,19 @@ public class DownloadsIntegration implem
 
     private static boolean useSystemDownloadManager() {
         if (!AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
             return false;
         }
 
         int state = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         try {
-            state = GeckoAppShell.getContext().getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");
+            final PackageManager pm = GeckoAppShell.getApplicationContext()
+                                                   .getPackageManager();
+            state = pm.getApplicationEnabledSetting("com.android.providers.downloads");
         } catch (IllegalArgumentException e) {
             // Download Manager package does not exist
             return false;
         }
 
         return (PackageManager.COMPONENT_ENABLED_STATE_ENABLED == state ||
                 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT == state);
     }
@@ -156,37 +159,46 @@ public class DownloadsIntegration implem
                 mimeType = UNKNOWN_MIME_TYPES.get(0);
             } else {
                 mimeType = aMimeType;
             }
         }
 
         if (useSystemDownloadManager()) {
             final File f = new File(aFile);
-            final DownloadManager dm = (DownloadManager) GeckoAppShell.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
+            final DownloadManager dm = (DownloadManager)
+                    GeckoAppShell.getApplicationContext()
+                                 .getSystemService(Context.DOWNLOAD_SERVICE);
             dm.addCompletedDownload(f.getName(),
                     f.getName(),
                     true, // Media scanner should scan this
                     mimeType,
                     f.getAbsolutePath(),
                     Math.max(1, f.length()), // Some versions of Android require downloads to be at least length 1
                     false); // Don't show a notification.
         } else {
-            final Context context = GeckoAppShell.getContext();
-            final GeckoMediaScannerClient client = new GeckoMediaScannerClient(context, aFile, mimeType);
+            final Activity currentActivity =
+                    GeckoActivityMonitor.getInstance().getCurrentActivity();
+            if (currentActivity == null) {
+                return;
+            }
+            final GeckoMediaScannerClient client =
+                    new GeckoMediaScannerClient(currentActivity, aFile, mimeType);
             client.connect();
         }
     }
 
     public static void removeDownload(final Download download) {
         if (!useSystemDownloadManager()) {
             return;
         }
 
-        final DownloadManager dm = (DownloadManager) GeckoAppShell.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
+        final DownloadManager dm = (DownloadManager)
+                GeckoAppShell.getApplicationContext()
+                             .getSystemService(Context.DOWNLOAD_SERVICE);
 
         Cursor c = null;
         try {
             c = dm.query((new DownloadManager.Query()).setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL));
             if (c == null || !c.moveToFirst()) {
                 return;
             }
 
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -123,17 +123,16 @@ import static org.mozilla.gecko.Tabs.INT
 import static org.mozilla.gecko.Tabs.INTENT_EXTRA_TAB_ID;
 import static org.mozilla.gecko.Tabs.INVALID_TAB_ID;
 import static org.mozilla.gecko.mma.MmaDelegate.DOWNLOAD_VIDEOS_OR_ANY_OTHER_MEDIA;
 import static org.mozilla.gecko.mma.MmaDelegate.LOADS_ARTICLES;
 
 public abstract class GeckoApp extends GeckoActivity
                                implements AnchoredPopup.OnVisibilityChangeListener,
                                           BundleEventListener,
-                                          ContextGetter,
                                           GeckoMenu.Callback,
                                           GeckoMenu.MenuPresenter,
                                           GeckoView.ContentListener,
                                           ScreenOrientationDelegate,
                                           Tabs.OnTabsChangedListener,
                                           ViewTreeObserver.OnGlobalLayoutListener {
 
     private static final String LOGTAG = "GeckoApp";
@@ -388,22 +387,16 @@ public abstract class GeckoApp extends G
             super(message);
         }
     }
 
     void toggleChrome(final boolean aShow) { }
 
     void focusChrome() { }
 
-    @Override
-    public Context getContext() {
-        return this;
-    }
-
-    @Override
     public SharedPreferences getSharedPreferences() {
         return GeckoSharedPrefs.forApp(this);
     }
 
     public SharedPreferences getSharedPreferencesForProfile() {
         return GeckoSharedPrefs.forProfile(this);
     }
 
@@ -1074,16 +1067,21 @@ public abstract class GeckoApp extends G
                 }
             });
         }
     }
 
     @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
     private static native void onFullScreenPluginHidden(View view);
 
+    @WrapForJNI(calledFrom = "gecko")
+    private static Context getPluginContext() {
+        return GeckoActivityMonitor.getInstance().getCurrentActivity();
+    }
+
     private void showSetImageResult(final boolean success, final int message, final String path) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 if (!success) {
                     SnackbarBuilder.builder(GeckoApp.this)
                             .message(message)
                             .duration(Snackbar.LENGTH_LONG)
@@ -1274,23 +1272,16 @@ public abstract class GeckoApp extends G
         // business later and dispose of the reference.
         GeckoLoader.setLastIntent(intent);
 
         // Workaround for <http://code.google.com/p/android/issues/detail?id=20915>.
         try {
             Class.forName("android.os.AsyncTask");
         } catch (ClassNotFoundException e) { }
 
-        // GeckoAppShell is tightly coupled to us, rather than
-        // the app context, because various parts of Fennec (e.g.,
-        // GeckoScreenOrientation) use GAS to access the Activity in
-        // the guise of fetching a Context.
-        // When that's fixed, `this` can change to
-        // `(GeckoApplication) getApplication()` here.
-        GeckoAppShell.setContextGetter(this);
         GeckoAppShell.setScreenOrientationDelegate(this);
 
         // Tell Stumbler to register a local broadcast listener to listen for preference intents.
         // We do this via intents since we can't easily access Stumbler directly,
         // as it might be compiled outside of Fennec.
         getApplicationContext().sendBroadcast(
                 new Intent(INTENT_REGISTER_STUMBLER_LISTENER)
         );
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -3,17 +3,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import android.app.Application;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.net.Uri;
@@ -51,18 +50,17 @@ import org.mozilla.gecko.util.GeckoBundl
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.PRNGFixes;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import java.io.File;
 import java.lang.reflect.Method;
 import java.util.UUID;
 
-public class GeckoApplication extends Application
-    implements ContextGetter {
+public class GeckoApplication extends Application {
     private static final String LOG_TAG = "GeckoApplication";
     private static final String MEDIA_DECODING_PROCESS_CRASH = "MEDIA_DECODING_PROCESS_CRASH";
 
     private boolean mInBackground;
     private boolean mPausedGecko;
     private boolean mIsInitialResume;
 
     private LightweightTheme mLightweightTheme;
@@ -131,26 +129,16 @@ public class GeckoApplication extends Ap
         final Context context = GeckoAppShell.getApplicationContext();
         final Intent intent = new Intent();
         intent.setClass(context, Restarter.class)
               .putExtra("pid", Process.myPid())
               .putExtra(Intent.EXTRA_INTENT, restartIntent);
         context.startService(intent);
     }
 
-    @Override
-    public Context getContext() {
-        return this;
-    }
-
-    @Override
-    public SharedPreferences getSharedPreferences() {
-        return GeckoSharedPrefs.forApp(this);
-    }
-
     /**
      * We need to do locale work here, because we need to intercept
      * each hit to onConfigurationChanged.
      */
     @Override
     public void onConfigurationChanged(Configuration config) {
         Log.d(LOG_TAG, "onConfigurationChanged: " + config.locale +
                        ", background: " + mInBackground);
--- a/mobile/android/base/java/org/mozilla/gecko/ThumbnailHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/ThumbnailHelper.java
@@ -62,17 +62,17 @@ public final class ThumbnailHelper {
 
     private final ArrayList<Tab> mPendingThumbnails;    // synchronized access only
     private volatile int mPendingWidth;
     private int mWidth;
     private int mHeight;
     private ByteBuffer mBuffer;
 
     private ThumbnailHelper() {
-        final Resources res = GeckoAppShell.getContext().getResources();
+        final Resources res = GeckoAppShell.getApplicationContext().getResources();
 
         mPendingThumbnails = new ArrayList<>();
         try {
             mPendingWidth = (int) res.getDimension(R.dimen.tab_thumbnail_width);
         } catch (Resources.NotFoundException nfe) {
         }
         mWidth = -1;
         mHeight = -1;
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/PromptListItem.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/PromptListItem.java
@@ -25,17 +25,17 @@ public class PromptListItem {
     public final boolean showAsActions;
     public final boolean isParent;
 
     public Intent mIntent;
     public boolean mSelected;
     public Drawable mIcon;
 
     PromptListItem(GeckoBundle aObject) {
-        Context context = GeckoAppShell.getContext();
+        final Context context = GeckoAppShell.getApplicationContext();
         label = aObject.getString("label", "");
         isGroup = aObject.getBoolean("isGroup");
         inGroup = aObject.getBoolean("inGroup");
         disabled = aObject.getBoolean("disabled");
         id = aObject.getInt("id");
         mSelected = aObject.getBoolean("selected");
 
         GeckoBundle obj = aObject.getBundle("showAsActions");
--- a/mobile/android/base/java/org/mozilla/gecko/updater/PostUpdateHandler.java
+++ b/mobile/android/base/java/org/mozilla/gecko/updater/PostUpdateHandler.java
@@ -50,17 +50,17 @@ public class PostUpdateHandler extends B
     /**
      * Copies the /assets/features folder out of the APK and into the app's data directory.
      */
     private void copyFeaturesFromAPK(BrowserApp browserApp) {
         Log.d(LOGTAG, "Copying system add-ons from APK to dataDir");
 
         final String dataDir = browserApp.getApplicationInfo().dataDir;
         final SharedPreferences prefs = GeckoSharedPrefs.forApp(browserApp);
-        final AssetManager assetManager = browserApp.getContext().getAssets();
+        final AssetManager assetManager = browserApp.getAssets();
 
         try {
             final String[] assetNames = assetManager.list("features");
 
             for (int i = 0; i < assetNames.length; i++) {
                 final String assetPath = "features/" + assetNames[i];
 
                 Log.d(LOGTAG, "Copying '" + assetPath + "' from APK to dataDir");
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -277,15 +277,15 @@ public class WebAppActivity extends Sing
     }
 
     private Bitmap readIconFromManifest(JSONObject manifest) {
         final String iconStr = manifest.optString("cached_icon", null);
         if (iconStr == null) {
             return null;
         }
         final LoadFaviconResult loadIconResult = FaviconDecoder
-            .decodeDataURI(getContext(), iconStr);
+            .decodeDataURI(this, iconStr);
         if (loadIconResult == null) {
             return null;
         }
         return loadIconResult.getBestBitmap(GeckoAppShell.getPreferredIconSize());
     }
 }
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -363,17 +363,16 @@ if CONFIG['MOZ_WEBRTC']:
     wrjar.javac_flags += ['-Xlint:all,-deprecation,-cast']
 
 gvjar = add_java_jar('gecko-view')
 
 gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x
                   for x in [
     'AndroidGamepadManager.java',
     'Clipboard.java',
-    'ContextGetter.java',
     'CrashHandler.java',
     'EventDispatcher.java',
     'GeckoAccessibility.java',
     'GeckoAppShell.java',
     'GeckoBatteryManager.java',
     'GeckoEditable.java',
     'GeckoEditableChild.java',
     'GeckoEditableClient.java',
--- a/mobile/android/chrome/content/WebrtcUI.js
+++ b/mobile/android/chrome/content/WebrtcUI.js
@@ -23,19 +23,25 @@ var WebrtcUI = {
   //        break;
   //      ...
   //      default:
   //        return stockObserve.call(this, aSubject, aTopic, aData);
   //
   // See browser/modules/webrtcUI.jsm for details.
 
   observe: function(aSubject, aTopic, aData) {
-    if (aTopic === "getUserMedia:request") {
+    if (aTopic === "getUserMedia:ask-device-permission") {
       RuntimePermissions
-        .waitForPermissions(this._determineNeededRuntimePermissions(aSubject))
+        .waitForPermissions(this._determineNeededRuntimePermissions(aData))
+        .then(_ => {
+          Services.obs.notifyObservers(aSubject, "getUserMedia:got-device-permission");
+        });
+    } else if (aTopic === "getUserMedia:request") {
+      RuntimePermissions
+        .checkPermissions(this._determineNeededRuntimePermissions(aSubject))
         .then((permissionGranted) => {
           if (permissionGranted) {
             WebrtcUI.handleGumRequest(aSubject, aTopic, aData);
           } else {
             Services.obs.notifyObservers(null, "getUserMedia:response:deny", aSubject.callID);
           }});
     } else if (aTopic === "PeerConnection:request") {
       this.handlePCRequest(aSubject, aTopic, aData);
@@ -173,17 +179,26 @@ var WebrtcUI = {
       },
       positive: true
     }];
   },
 
   _determineNeededRuntimePermissions: function(aSubject) {
     let permissions = [];
 
-    let constraints = aSubject.getConstraints();
+    let constraints;
+    if (typeof aSubject === "string") {
+      constraints = {
+        video: aSubject === "video" || aSubject === "all",
+        audio: aSubject === "audio" || aSubject === "all",
+      };
+    } else {
+      constraints = aSubject.getConstraints();
+    }
+
     if (constraints.video) {
       permissions.push(RuntimePermissions.CAMERA);
     }
     if (constraints.audio) {
       permissions.push(RuntimePermissions.RECORD_AUDIO);
     }
 
     return permissions;
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -154,17 +154,18 @@ lazilyLoadedBrowserScripts.forEach(funct
 
 var lazilyLoadedObserverScripts = [
   ["MemoryObserver", ["memory-pressure", "Memory:Dump"], "chrome://browser/content/MemoryObserver.js"],
   ["ConsoleAPI", ["console-api-log-event"], "chrome://browser/content/ConsoleAPI.js"],
 ];
 
 if (AppConstants.MOZ_WEBRTC) {
   lazilyLoadedObserverScripts.push(
-    ["WebrtcUI", ["getUserMedia:request",
+    ["WebrtcUI", ["getUserMedia:ask-device-permission",
+                  "getUserMedia:request",
                   "PeerConnection:request",
                   "recording-device-events",
                   "VideoCapture:Paused",
                   "VideoCapture:Resumed"], "chrome://browser/content/WebrtcUI.js"])
 }
 
 lazilyLoadedObserverScripts.forEach(function (aScript) {
   let [name, notifications, script] = aScript;
--- a/mobile/android/components/ContentPermissionPrompt.js
+++ b/mobile/android/components/ContentPermissionPrompt.js
@@ -2,18 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 const Cc = Components.classes;
 
+Cu.import("resource://gre/modules/RuntimePermissions.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
 
 const kEntities = {
   "contacts": "contacts",
   "desktop-notification": "desktopNotification2",
   "geolocation": "geolocation",
   "flyweb-publish-server": "flyWebPublishServer",
 };
 
@@ -26,30 +27,30 @@ const PROMPT_FOR_UNKNOWN = [
 
 function ContentPermissionPrompt() {}
 
 ContentPermissionPrompt.prototype = {
   classID: Components.ID("{C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
 
-  handleExistingPermission: function handleExistingPermission(request, type, denyUnknown) {
+  handleExistingPermission: function handleExistingPermission(request, type, denyUnknown, callback) {
     let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type);
     if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
-      request.allow();
+      callback(/* allow */ true);
       return true;
     }
 
     if (result == Ci.nsIPermissionManager.DENY_ACTION) {
-      request.cancel();
+      callback(/* allow */ false);
       return true;
     }
 
     if (denyUnknown && result == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
-      request.cancel();
+      callback(/* allow */ false);
       return true;
     }
 
     return false;
   },
 
   getChromeWindow: function getChromeWindow(aWindow) {
      let chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -74,55 +75,73 @@ ContentPermissionPrompt.prototype = {
     let isApp = request.principal.appId !== Ci.nsIScriptSecurityManager.NO_APP_ID && request.principal.appId !== Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID;
 
     // Only allow exactly one permission rquest here.
     let types = request.types.QueryInterface(Ci.nsIArray);
     if (types.length != 1) {
       request.cancel();
       return;
     }
+
     let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
 
+    let callback = (allow) => {
+      if (!allow) {
+        request.cancel();
+        return;
+      }
+      if (perm.type === "geolocation") {
+        RuntimePermissions.waitForPermissions(
+          RuntimePermissions.ACCESS_FINE_LOCATION).then((granted) => {
+            (granted ? request.allow : request.cancel)();
+          });
+        return;
+      }
+      request.allow();
+    };
+
     // Returns true if the request was handled
     let access = (perm.access && perm.access !== "unused") ?
                  (perm.type + "-" + perm.access) : perm.type;
     if (this.handleExistingPermission(request, access,
-          /* denyUnknown */ isApp || PROMPT_FOR_UNKNOWN.indexOf(perm.type) < 0))
+          /* denyUnknown */ isApp || PROMPT_FOR_UNKNOWN.indexOf(perm.type) < 0, callback)) {
        return;
+    }
 
     let chromeWin = this.getChromeForRequest(request);
     let tab = chromeWin.BrowserApp.getTabForWindow(request.window.top);
-    if (!tab)
+    if (!tab) {
       return;
+    }
 
     let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
     let entityName = kEntities[perm.type];
 
     let buttons = [{
       label: browserBundle.GetStringFromName(entityName + ".dontAllow"),
       callback: function(aChecked) {
         // If the user checked "Don't ask again" or this is a desktopNotification, make a permanent exception
         if (aChecked || entityName == "desktopNotification2")
           Services.perms.addFromPrincipal(request.principal, access, Ci.nsIPermissionManager.DENY_ACTION);
 
-        request.cancel();
+        callback(/* allow */ false);
       }
     },
     {
       label: browserBundle.GetStringFromName(entityName + ".allow"),
       callback: function(aChecked) {
         // If the user checked "Don't ask again" or this is a desktopNotification, make a permanent exception
         if (aChecked || entityName == "desktopNotification2") {
           Services.perms.addFromPrincipal(request.principal, access, Ci.nsIPermissionManager.ALLOW_ACTION);
         } else if (isApp) {
           // Otherwise allow the permission for the current session if the request comes from an app
           Services.perms.addFromPrincipal(request.principal, access, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION);
         }
 
-        request.allow();
+        callback(/* allow */ true);
       },
       positive: true
     }];
 
     let requestor = chromeWin.BrowserApp.manifest ? "'" + chromeWin.BrowserApp.manifest.name + "'" : request.principal.URI.host;
     let message = browserBundle.formatStringFromName(entityName + ".ask", [requestor], 1);
     // desktopNotification doesn't have a checkbox
     let options;
--- a/mobile/android/components/geckoview/GeckoViewPrompt.js
+++ b/mobile/android/components/geckoview/GeckoViewPrompt.js
@@ -135,36 +135,37 @@ PromptFactory.prototype = {
       // Cancel: !result
       if (!result || result.choices === undefined) {
         return;
       }
 
       let dispatchEvents = false;
       if (!aElement.multiple) {
         let elem = map[result.choices[0]];
-        if (elem) {
+        if (elem && elem instanceof win.HTMLOptionElement) {
           dispatchEvents = !elem.selected;
           elem.selected = true;
         } else {
           Cu.reportError("Invalid id for select result: " + result.choices[0]);
         }
       } else {
         for (let i = 0; i < id; i++) {
           let elem = map[i];
           let index = result.choices.indexOf(String(i));
-          if (elem.selected != (index >= 0)) {
+          if (elem instanceof win.HTMLOptionElement &&
+              elem.selected !== (index >= 0)) {
             // Current selected is not the same as the new selected state.
             dispatchEvents = true;
             elem.selected = !elem.selected;
-            result.choices[index] = undefined;
           }
+          result.choices[index] = undefined;
         }
         for (let i = 0; i < result.choices.length; i++) {
           if (result.choices[i] !== undefined && result.choices[i] !== null) {
-            Cu.reportError("Invalid id for select result: " + result.choices[0]);
+            Cu.reportError("Invalid id for select result: " + result.choices[i]);
             break;
           }
         }
       }
 
       if (dispatchEvents) {
         this._dispatchEvents(aElement);
       }
deleted file mode 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/ContextGetter.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * 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/. */
-
-package org.mozilla.gecko;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-public interface ContextGetter {
-    Context getContext();
-    SharedPreferences getSharedPreferences();
-}
-
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -110,17 +110,17 @@ public class GeckoAppShell
     private static final CrashHandler CRASH_HANDLER = new CrashHandler() {
         @Override
         protected String getAppPackageName() {
             return AppConstants.ANDROID_PACKAGE_NAME;
         }
 
         @Override
         protected Context getAppContext() {
-            return sContextGetter != null ? getApplicationContext() : null;
+            return getApplicationContext();
         }
 
         @Override
         protected Bundle getCrashExtras(final Thread thread, final Throwable exc) {
             final Bundle extras = super.getCrashExtras(thread, exc);
 
             extras.putString("ProductName", AppConstants.MOZ_APP_BASENAME);
             extras.putString("ProductID", AppConstants.MOZ_APP_ID);
@@ -142,18 +142,19 @@ public class GeckoAppShell
 
             super.uncaughtException(thread, exc);
         }
 
         @Override
         public boolean reportException(final Thread thread, final Throwable exc) {
             try {
                 if (exc instanceof OutOfMemoryError) {
-                    SharedPreferences prefs = getSharedPreferences();
-                    SharedPreferences.Editor editor = prefs.edit();
+                    final SharedPreferences prefs =
+                            GeckoSharedPrefs.forApp(getApplicationContext());
+                    final SharedPreferences.Editor editor = prefs.edit();
                     editor.putBoolean(PREFS_OOM_EXCEPTION, true);
 
                     // Synchronously write to disk so we know it's done before we
                     // shutdown
                     editor.commit();
                 }
 
                 reportJavaCrash(exc, getExceptionStackTrace(exc));
@@ -280,17 +281,18 @@ public class GeckoAppShell
         CRASH_HANDLER.uncaughtException(null, e);
     }
 
     private static float getLocationAccuracy(Location location) {
         float radius = location.getAccuracy();
         return (location.hasAccuracy() && radius > 0) ? radius : 1001;
     }
 
-    @SuppressLint("MissingPermission") // Permissions are explicitly checked for in enableLocation()
+    // Permissions are explicitly checked when requesting content permission.
+    @SuppressLint("MissingPermission")
     private static Location getLastKnownLocation(LocationManager lm) {
         Location lastKnownLocation = null;
         List<String> providers = lm.getAllProviders();
 
         for (String provider : providers) {
             Location location = lm.getLastKnownLocation(provider);
             if (location == null) {
                 continue;
@@ -308,65 +310,68 @@ public class GeckoAppShell
                 lastKnownLocation = location;
             }
         }
 
         return lastKnownLocation;
     }
 
     @WrapForJNI(calledFrom = "gecko")
-    @SuppressLint("MissingPermission") // Permissions are explicitly checked for within this method
-    private static void enableLocation(final boolean enable) {
-        final Runnable requestLocation = new Runnable() {
-            @Override
-            public void run() {
-                LocationManager lm = getLocationManager(getApplicationContext());
-                if (lm == null) {
-                    return;
+    // Permissions are explicitly checked when requesting content permission.
+    @SuppressLint("MissingPermission")
+    /* package */ static void enableLocation(final boolean enable) {
+        if (!ThreadUtils.isOnUiThread()) {
+            ThreadUtils.postToUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        enableLocation(enable);
+                    } catch (final SecurityException e) {
+                        Log.e(LOGTAG, "No location permission", e);
+                    }
                 }
+            });
+            return;
+        }
 
-                if (!enable) {
-                    lm.removeUpdates(getLocationListener());
-                    return;
-                }
-
-                Location lastKnownLocation = getLastKnownLocation(lm);
-                if (lastKnownLocation != null) {
-                    getLocationListener().onLocationChanged(lastKnownLocation);
-                }
+        LocationManager lm = getLocationManager(getApplicationContext());
+        if (lm == null) {
+            return;
+        }
 
-                Criteria criteria = new Criteria();
-                criteria.setSpeedRequired(false);
-                criteria.setBearingRequired(false);
-                criteria.setAltitudeRequired(false);
-                if (locationHighAccuracyEnabled) {
-                    criteria.setAccuracy(Criteria.ACCURACY_FINE);
-                    criteria.setCostAllowed(true);
-                    criteria.setPowerRequirement(Criteria.POWER_HIGH);
-                } else {
-                    criteria.setAccuracy(Criteria.ACCURACY_COARSE);
-                    criteria.setCostAllowed(false);
-                    criteria.setPowerRequirement(Criteria.POWER_LOW);
-                }
+        if (!enable) {
+            lm.removeUpdates(getLocationListener());
+            return;
+        }
+
+        Location lastKnownLocation = getLastKnownLocation(lm);
+        if (lastKnownLocation != null) {
+            getLocationListener().onLocationChanged(lastKnownLocation);
+        }
 
-                String provider = lm.getBestProvider(criteria, true);
-                if (provider == null)
-                    return;
+        Criteria criteria = new Criteria();
+        criteria.setSpeedRequired(false);
+        criteria.setBearingRequired(false);
+        criteria.setAltitudeRequired(false);
+        if (locationHighAccuracyEnabled) {
+            criteria.setAccuracy(Criteria.ACCURACY_FINE);
+            criteria.setCostAllowed(true);
+            criteria.setPowerRequirement(Criteria.POWER_HIGH);
+        } else {
+            criteria.setAccuracy(Criteria.ACCURACY_COARSE);
+            criteria.setCostAllowed(false);
+            criteria.setPowerRequirement(Criteria.POWER_LOW);
+        }
 
-                Looper l = Looper.getMainLooper();
-                lm.requestLocationUpdates(provider, 100, 0.5f, getLocationListener(), l);
-            }
-        };
+        String provider = lm.getBestProvider(criteria, true);
+        if (provider == null)
+            return;
 
-        Permissions
-                .from((Activity) getContext())
-                .withPermissions(Manifest.permission.ACCESS_FINE_LOCATION)
-                .onUIThread()
-                .doNotPromptIf(!enable)
-                .run(requestLocation);
+        Looper l = Looper.getMainLooper();
+        lm.requestLocationUpdates(provider, 100, 0.5f, getLocationListener(), l);
     }
 
     private static LocationManager getLocationManager(Context context) {
         try {
             return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
         } catch (NoSuchFieldError e) {
             // Some Tegras throw exceptions about missing the CONTROL_LOCATION_UPDATES permission,
             // which allows enabling/disabling location update notifications from the cell radio.
@@ -1621,44 +1626,26 @@ public class GeckoAppShell
             return null;
         } catch (android.content.pm.PackageManager.NameNotFoundException nnfe) {
             Log.w(LOGTAG, "Couldn't find package.", nnfe);
             return null;
         }
     }
 
     private static Context sApplicationContext;
-    private static ContextGetter sContextGetter;
-
-    @Deprecated
-    @WrapForJNI
-    public static Context getContext() {
-        return sContextGetter.getContext();
-    }
-
-    public static void setContextGetter(ContextGetter cg) {
-        sContextGetter = cg;
-    }
 
     @WrapForJNI
     public static Context getApplicationContext() {
         return sApplicationContext;
     }
 
     public static void setApplicationContext(final Context context) {
         sApplicationContext = context;
     }
 
-    public static SharedPreferences getSharedPreferences() {
-        if (sContextGetter == null) {
-            throw new IllegalStateException("No ContextGetter; cannot fetch prefs.");
-        }
-        return sContextGetter.getSharedPreferences();
-    }
-
     public interface GeckoInterface {
         public boolean openUriExternal(String targetURI, String mimeType, String packageName, String className, String action, String title);
 
         public String[] getHandlersForMimeType(String mimeType, String action);
         public String[] getHandlersForURL(String url, String action);
     };
 
     private static GeckoInterface sGeckoInterface;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -38,18 +38,17 @@ import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 
-public class GeckoView extends LayerView
-    implements ContextGetter {
+public class GeckoView extends LayerView {
 
     private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView";
     private static final String LOGTAG = "GeckoView";
 
     private static final boolean DEBUG = false;
 
     /* package */ enum State implements NativeQueue.State {
         @WrapForJNI INITIAL(0),
@@ -313,22 +312,16 @@ public class GeckoView extends LayerView
         if (GeckoThread.initMainProcess(GeckoProfile.get(appContext),
                                         geckoArgs,
                                         /* debugging */ false)) {
             GeckoThread.launch();
         }
     }
 
     private void init(final Context context, final GeckoViewSettings settings) {
-        // Set the GeckoInterface if the context is an activity and the
-        // GeckoInterface has not already been set
-        if (context instanceof Activity && GeckoAppShell.getGeckoInterface() == null) {
-            GeckoAppShell.setContextGetter(this);
-        }
-
         preload(context);
 
         // Perform common initialization for Fennec/GeckoView.
         GeckoAppShell.setLayerView(this);
 
         initializeView();
         mListener.registerListeners();
 
@@ -1053,25 +1046,16 @@ public class GeckoView extends LayerView
             }
             default: {
                 callback.sendError("Invalid type");
                 break;
             }
         }
     }
 
-    protected String getSharedPreferencesFile() {
-        return DEFAULT_SHARED_PREFERENCES_FILE;
-    }
-
-    @Override
-    public SharedPreferences getSharedPreferences() {
-        return getContext().getSharedPreferences(getSharedPreferencesFile(), 0);
-    }
-
     public EventDispatcher getEventDispatcher() {
         return mEventDispatcher;
     }
 
     public interface ProgressListener {
         static final int STATE_IS_BROKEN = 1;
         static final int STATE_IS_SECURE = 2;
         static final int STATE_IS_INSECURE = 4;
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java
@@ -299,16 +299,21 @@ final class BasicGeckoViewPrompt impleme
         final Activity activity = getActivity(view);
         if (activity == null) {
             callback.dismiss();
             return;
         }
         final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
         addStandardLayout(builder, title, msg, callback);
 
+        final ListView list = new ListView(builder.getContext());
+        if (type == CHOICE_TYPE_MULTIPLE) {
+            list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+        }
+
         final ArrayAdapter<GeckoBundle> adapter = new ArrayAdapter<GeckoBundle>(
                 builder.getContext(), android.R.layout.simple_list_item_1) {
             private static final int TYPE_MENU_ITEM = 0;
             private static final int TYPE_MENU_CHECK = 1;
             private static final int TYPE_SEPARATOR = 2;
             private static final int TYPE_GROUP = 3;
             private static final int TYPE_SINGLE = 4;
             private static final int TYPE_MULTIPLE = 5;
@@ -385,32 +390,34 @@ final class BasicGeckoViewPrompt impleme
                     view = mInflater.inflate(layoutId, parent, false);
                 }
 
                 final GeckoBundle item = getItem(position);
                 final TextView text = (TextView) view;
                 text.setEnabled(!item.getBoolean("disabled"));
                 text.setText(item.getString("label"));
                 if (view instanceof CheckedTextView) {
-                    ((CheckedTextView) view).setChecked(item.getBoolean("selected"));
+                    final boolean selected = item.getBoolean("selected");
+                    if (itemType == TYPE_MULTIPLE) {
+                        list.setItemChecked(position, selected);
+                    } else {
+                        ((CheckedTextView) view).setChecked(selected);
+                    }
                 }
                 return view;
             }
         };
         addChoiceItems(type, adapter, choices, /* indent */ null);
 
-        final ListView list = new ListView(builder.getContext());
         list.setAdapter(adapter);
-        if (type == CHOICE_TYPE_MULTIPLE) {
-            list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-        }
         builder.setView(list);
 
-        final AlertDialog dialog = builder.create();
+        final AlertDialog dialog;
         if (type == CHOICE_TYPE_SINGLE || type == CHOICE_TYPE_MENU) {
+            dialog = builder.create();
             list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                 @Override
                 public void onItemClick(final AdapterView<?> parent, final View v,
                                         final int position, final long id) {
                     final GeckoBundle item = adapter.getItem(position);
                     if (type == CHOICE_TYPE_MENU) {
                         final GeckoBundle[] children = item.getBundleArray("items");
                         if (children != null) {
@@ -444,19 +451,20 @@ final class BasicGeckoViewPrompt impleme
                     final int len = adapter.getCount();
                     ArrayList<String> items = new ArrayList<>(len);
                     for (int i = 0; i < len; i++) {
                         final GeckoBundle item = adapter.getItem(i);
                         if (item.getBoolean("selected")) {
                             items.add(item.getString("id"));
                         }
                     }
-                    callback.confirm(items.toArray(new GeckoBundle[items.size()]));
+                    callback.confirm(items.toArray(new String[items.size()]));
                 }
             });
+            dialog = builder.create();
         } else {
             throw new UnsupportedOperationException();
         }
         dialog.show();
     }
 
     private static int parseColor(final String value, final int def) {
         try {
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -346,18 +346,16 @@
 @BINPATH@/components/nsWebHandlerApp.manifest
 @BINPATH@/components/nsWebHandlerApp.js
 @BINPATH@/components/satchel.manifest
 @BINPATH@/components/nsFormAutoComplete.js
 @BINPATH@/components/FormHistoryStartup.js
 @BINPATH@/components/nsInputListAutoComplete.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
-@BINPATH@/components/messageWakeupService.js
-@BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/nsINIProcessor.manifest
 @BINPATH@/components/nsINIProcessor.js
 @BINPATH@/components/servicesComponents.manifest
 
 #ifndef MOZ_GECKOVIEW_JAR
 @BINPATH@/components/TelemetryStartup.js
 @BINPATH@/components/TelemetryStartup.manifest
 #endif
--- a/mobile/android/modules/RuntimePermissions.jsm
+++ b/mobile/android/modules/RuntimePermissions.jsm
@@ -6,21 +6,23 @@
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 this.EXPORTED_SYMBOLS = ["RuntimePermissions"];
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 // See: http://developer.android.com/reference/android/Manifest.permission.html
+const ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
 const CAMERA = "android.permission.CAMERA";
+const RECORD_AUDIO = "android.permission.RECORD_AUDIO";
 const WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
-const RECORD_AUDIO = "android.permission.RECORD_AUDIO";
 
 var RuntimePermissions = {
+  ACCESS_FINE_LOCATION: ACCESS_FINE_LOCATION,
   CAMERA: CAMERA,
   RECORD_AUDIO: RECORD_AUDIO,
   WRITE_EXTERNAL_STORAGE: WRITE_EXTERNAL_STORAGE,
 
   /**
    * Check whether the permissions have been granted or not. If needed prompt the user to accept the permissions.
    *
    * @returns A promise resolving to true if all the permissions have been granted or false if any of the
--- a/mobile/android/tests/browser/chrome/test_jni.html
+++ b/mobile/android/tests/browser/chrome/test_jni.html
@@ -19,17 +19,17 @@ Migrated from Robocop: https://bugzilla.
     var jenv = null;
     try {
       jenv = JNI.GetForThread();
 
       // Test a simple static method.
       var geckoAppShell = JNI.LoadClass(jenv, "org.mozilla.gecko.GeckoAppShell", {
         static_methods: [
           { name: "getPreferredIconSize", sig: "()I" },
-          { name: "getContext", sig: "()Landroid/content/Context;" },
+          { name: "getApplicationContext", sig: "()Landroid/content/Context;" },
         ],
       });
 
       let iconSize = -1;
       iconSize = geckoAppShell.getPreferredIconSize();
       isnot(iconSize, -1, "icon size is valid");
 
       // Test GeckoNetworkManager methods that are accessed by PaymentsUI.js.
@@ -51,17 +51,17 @@ Migrated from Robocop: https://bugzilla.
           { name: "getClass", sig: "()Ljava/lang/Class;" },
         ],
       });
       JNI.LoadClass(jenv, "java.lang.Class", {
         methods: [
           { name: "getName", sig: "()Ljava/lang/String;" },
         ],
       });
-      is("org.mozilla.gecko.BrowserApp", JNI.ReadString(jenv, geckoAppShell.getContext().getClass().getName()), "class name is correct");
+      is("org.mozilla.gecko.GeckoApplication", JNI.ReadString(jenv, geckoAppShell.getApplicationContext().getClass().getName()), "class name is correct");
     } finally {
       if (jenv) {
         JNI.UnloadClasses(jenv);
       }
     }
   }
 
   test_JNI();
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -63,16 +63,18 @@
 
 static bool sctp_initialized;
 
 namespace mozilla {
 
 LazyLogModule gDataChannelLog("DataChannel");
 static LazyLogModule gSCTPLog("SCTP");
 
+#define SCTP_LOG(args) MOZ_LOG(mozilla::gSCTPLog, mozilla::LogLevel::Debug, args)
+
 class DataChannelShutdown : public nsIObserver
 {
 public:
   // This needs to be tied to some form object that is guaranteed to be
   // around (singleton likely) unless we want to shutdown sctp whenever
   // we're not using it (and in which case we'd keep a refcnt'd object
   // ref'd by each DataChannelConnection to release the SCTP usrlib via
   // sctp_finish). Right now, the single instance of this class is
@@ -195,17 +197,17 @@ debug_printf(const char *format, ...)
 
   if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)) {
     va_start(ap, format);
 #ifdef _WIN32
     if (vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, format, ap) > 0) {
 #else
     if (VsprintfLiteral(buffer, format, ap) > 0) {
 #endif
-      PR_LogPrint("%s", buffer);
+      SCTP_LOG(("%s", buffer));
     }
     va_end(ap);
   }
 }
 
 DataChannelConnection::DataChannelConnection(DataConnectionListener *listener) :
    mLock("netwerk::sctp::DataChannelConnection")
 {
@@ -636,17 +638,17 @@ DataChannelConnection::ProcessQueuedOpen
 void
 DataChannelConnection::SctpDtlsInput(TransportFlow *flow,
                                      const unsigned char *data, size_t len)
 {
   if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)) {
     char *buf;
 
     if ((buf = usrsctp_dumppacket((void *)data, len, SCTP_DUMP_INBOUND)) != nullptr) {
-      PR_LogPrint("%s", buf);
+      SCTP_LOG(("%s", buf));
       usrsctp_freedumpbuffer(buf);
     }
   }
   // Pass the data to SCTP
   usrsctp_conninput(static_cast<void *>(this), data, len, 0);
 }
 
 int
@@ -666,17 +668,17 @@ DataChannelConnection::SctpDtlsOutput(vo
 {
   DataChannelConnection *peer = static_cast<DataChannelConnection *>(addr);
   int res;
 
   if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)) {
     char *buf;
 
     if ((buf = usrsctp_dumppacket(buffer, length, SCTP_DUMP_OUTBOUND)) != nullptr) {
-      PR_LogPrint("%s", buf);
+      SCTP_LOG(("%s", buf));
       usrsctp_freedumpbuffer(buf);
     }
   }
   // We're async proxying even if on the STSThread because this is called
   // with internal SCTP locks held in some cases (such as in usrsctp_connect()).
   // SCTP has an option for Apple, on IP connections only, to release at least
   // one of the locks before calling a packet output routine; with changes to
   // the underlying SCTP stack this might remove the need to use an async proxy.
--- a/taskcluster/ci/build/linux.yml
+++ b/taskcluster/ci/build/linux.yml
@@ -411,16 +411,42 @@ linux64-stylo/debug:
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
         secrets: true
         custom-build-variant-cfg: stylo-debug
         tooltool-downloads: public
         need-xvfb: true
     run-on-projects: [ 'stylo', 'autoland', 'mozilla-inbound', 'mozilla-central', 'try' ]
 
+linux64-noopt/debug:
+    description: "Linux64 No-optimize Debug"
+    index:
+        product: firefox
+        job-name: linux64-noopt-debug
+    treeherder:
+        platform: linux64-noopt/debug
+        symbol: tc(B)
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+    worker:
+        max-run-time: 3600
+    run:
+        using: mozharness
+        actions: [get-secrets build generate-build-stats check-test update]
+        config:
+            - builds/releng_base_linux_64_builds.py
+            - balrog/production.py
+        script: "mozharness/scripts/fx_desktop_build.py"
+        secrets: true
+        custom-build-variant-cfg: noopt-debug
+        tooltool-downloads: public
+        keep-artifacts: false
+        need-xvfb: true
+    run-on-projects: [ 'stylo', 'autoland', 'mozilla-inbound', 'mozilla-central', 'try' ]
+
 linux64-jsdcov/opt:
     description: "Linux64-JSDCov Opt"
     index:
         product: firefox
         job-name: linux64-jsdcov-opt
     treeherder:
         platform: linux64-jsdcov/opt
         symbol: tc(B)
--- a/taskcluster/ci/build/macosx.yml
+++ b/taskcluster/ci/build/macosx.yml
@@ -36,16 +36,42 @@ macosx64/opt:
         actions: [get-secrets build generate-build-stats update]
         config:
             - builds/releng_base_mac_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
         secrets: true
         tooltool-downloads: internal
 
+macosx64-noopt/debug:
+    description: "MacOS X x64 No-optimize Debug"
+    index:
+        product: firefox
+        job-name: macosx64-noopt-debug
+    treeherder:
+        platform: osx-10-7-noopt/debug
+        symbol: tc(B)
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker:
+        docker-image: {in-tree: desktop-build}
+        max-run-time: 36000
+    run:
+        using: mozharness
+        actions: [get-secrets build generate-build-stats update]
+        config:
+            - builds/releng_base_mac_64_cross_builds.py
+            - balrog/production.py
+        script: "mozharness/scripts/fx_desktop_build.py"
+        secrets: true
+        custom-build-variant-cfg: cross-noopt-debug
+        tooltool-downloads: internal
+        keep-artifacts: false
+    run-on-projects: [ 'stylo', 'autoland', 'mozilla-inbound', 'mozilla-central', 'try' ]
+
 macosx64-add-on-devel/opt:
     description: "MacOS X x64 add-on-devel"
     index:
         product: firefox
         job-name: macosx64-add-on-devel
     treeherder:
         platform: osx-10-7-add-on-devel/opt
         symbol: tc(B)
--- a/taskcluster/ci/build/windows.yml
+++ b/taskcluster/ci/build/windows.yml
@@ -166,16 +166,54 @@ win64-add-on-devel/opt:
      run:
          using: mozharness
          script: "mozharness/scripts/fx_desktop_build.py"
          config:
              - builds/taskcluster_firefox_windows_64_addondevel.py 
              - balrog/production.py
      run-on-projects: [ 'mozilla-beta', 'mozilla-release', 'mozilla-esr45' ]
 
+win64-noopt/debug:
+    description: "Win64 No-optimize Debug"
+    index:
+        product: firefox
+        job-name: win64-noopt-debug
+    treeherder:
+        platform: windows2012-64-noopt/debug
+        symbol: tc(B)
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
+    worker:
+        max-run-time: 7200
+    run:
+        using: mozharness
+        script: mozharness/scripts/fx_desktop_build.py
+        config:
+            - builds/taskcluster_firefox_win64_noopt_debug.py
+    run-on-projects: [ 'stylo', 'autoland', 'mozilla-inbound', 'mozilla-central', 'try' ]
+
+win32-noopt/debug:
+    description: "Win32 No-optimize Debug"
+    index:
+        product: firefox
+        job-name: win32-noopt-debug
+    treeherder:
+        platform: windows2012-32-noopt/debug
+        symbol: tc(B)
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
+    worker:
+        max-run-time: 7200
+    run:
+        using: mozharness
+        script: mozharness/scripts/fx_desktop_build.py
+        config:
+            - builds/taskcluster_firefox_win32_noopt_debug.py
+    run-on-projects: [ 'stylo', 'autoland', 'mozilla-inbound', 'mozilla-central', 'try' ]
+
 win64-asan/debug:
     description: "Win64 Debug ASAN"
     index:
         product: firefox
         job-name: win64-asan-debug
     treeherder:
         platform: windows2012-64/asan
         symbol: tc(Bd)
--- a/taskcluster/taskgraph/transforms/gecko_v2_whitelist.py
+++ b/taskcluster/taskgraph/transforms/gecko_v2_whitelist.py
@@ -36,26 +36,28 @@ JOB_NAME_WHITELIST = set([
     'linux64-base-toolchains-debug',
     'linux64-base-toolchains-opt',
     'linux64-fuzzing-asan-opt',
     'linux64-ccov-opt',
     'linux64-clang-tidy',
     'linux64-debug',
     'linux64-devedition',
     'linux64-jsdcov-opt',
+    'linux64-noopt-debug',
     'linux64-opt',
     'linux64-pgo',
     'linux64-st-an-debug',
     'linux64-st-an-opt',
     'linux64-stylo-debug',
     'linux64-stylo-opt',
     'linux64-valgrind-opt',
     'macosx64-add-on-devel',
     'macosx64-clang-tidy',
     'macosx64-debug',
+    'macosx64-noopt-debug',
     'macosx64-opt',
     'macosx64-st-an-debug',
     'macosx64-st-an-opt',
     'shell-haz-debug',
     'sm-arm-sim-debug',
     'sm-arm64-sim-debug',
     'sm-asan-opt',
     'sm-compacting-debug',
@@ -66,23 +68,25 @@ JOB_NAME_WHITELIST = set([
     'sm-package-opt',
     'sm-plain-opt',
     'sm-plaindebug-debug',
     'sm-rootanalysis-debug',
     'sm-tsan-opt',
     'win32-add-on-devel',
     'win32-clang-tidy',
     'win32-debug',
+    'win32-noopt-debug',
     'win32-opt',
     'win32-pgo',
     'win32-st-an-debug',
     'win32-st-an-opt',
     'win64-add-on-devel',
     'win64-clang-tidy',
     'win64-debug',
+    'win64-noopt-debug',
     'win64-opt',
     'win64-pgo',
     'win64-st-an-debug',
     'win64-st-an-opt',
     'win64-asan-debug',
     'win64-asan-opt',
 ])
 
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -245,28 +245,34 @@ MarionetteComponent.prototype.suppressSa
   }, {once: true});
 };
 
 MarionetteComponent.prototype.init = function () {
   if (this.running || !this.enabled || !this.finalUIStartup) {
     return;
   }
 
-  let s;
-  try {
-    Cu.import("chrome://marionette/content/server.js");
-    s = new server.TCPListener(prefs.port);
-    s.start();
-    this.logger.info(`Listening on port ${s.port}`);
-  } finally {
-    if (s) {
-      this.server = s;
-      this.running = true;
-    }
-  }
+  // Delay initialization until we are done with delayed startup...
+  Services.tm.mainThread.idleDispatch(() => {
+    // ... and with startup tests.
+    Services.tm.mainThread.idleDispatch(() => {
+      let s;
+      try {
+        Cu.import("chrome://marionette/content/server.js");
+        s = new server.TCPListener(prefs.port);
+        s.start();
+        this.logger.info(`Listening on port ${s.port}`);
+      } finally {
+        if (s) {
+          this.server = s;
+          this.running = true;
+        }
+      }
+    });
+  });
 };
 
 MarionetteComponent.prototype.uninit = function () {
   if (!this.running) {
     return;
   }
   this.server.stop();
   this.running = false;
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -149,27 +149,52 @@ function Tester(aTests, structuredLogger
 
   this.MemoryStats = simpleTestScope.MemoryStats;
   this.Task = Task;
   this.ContentTask = Components.utils.import("resource://testing-common/ContentTask.jsm", null).ContentTask;
   this.BrowserTestUtils = Components.utils.import("resource://testing-common/BrowserTestUtils.jsm", null).BrowserTestUtils;
   this.TestUtils = Components.utils.import("resource://testing-common/TestUtils.jsm", null).TestUtils;
   this.Task.Debugging.maintainStack = true;
   this.Promise = Components.utils.import("resource://gre/modules/Promise.jsm", null).Promise;
-  this.PromiseTestUtils = Components.utils.import("resource://testing-common/PromiseTestUtils.jsm", null).PromiseTestUtils;
   this.Assert = Components.utils.import("resource://testing-common/Assert.jsm", null).Assert;
 
-  this.PromiseTestUtils.init();
-
   this.SimpleTestOriginal = {};
   SIMPLETEST_OVERRIDES.forEach(m => {
     this.SimpleTestOriginal[m] = this.SimpleTest[m];
   });
 
   this._coverageCollector = null;
+
+  this._toleratedUncaughtRejections = null;
+  this._uncaughtErrorObserver = ({message, date, fileName, stack, lineNumber}) => {
+    let ex = message;
+    if (fileName || lineNumber) {
+      ex = {
+        fileName: fileName,
+        lineNumber: lineNumber,
+        message: message,
+        toString: function() {
+          return message;
+        }
+      };
+    }
+
+    // We may have a whitelist of rejections we wish to tolerate.
+    let pass = this._toleratedUncaughtRejections &&
+      this._toleratedUncaughtRejections.indexOf(message) != -1;
+    let name = "A promise chain failed to handle a rejection: ";
+    if (pass) {
+      name = "WARNING: (PLEASE FIX THIS AS PART OF BUG 1077403) " + name;
+    }
+
+    this.currentTest.addResult(new testResult({
+      pass, name, ex, todo: pass, stack,
+      allowFailure: this.currentTest.allowFailure,
+    }));
+  };
 }
 Tester.prototype = {
   EventUtils: {},
   SimpleTest: {},
   Task: null,
   ContentTask: null,
   ExtensionTestUtils: null,
   Assert: null,
@@ -214,16 +239,19 @@ Tester.prototype = {
     this._globalProperties = Object.keys(window);
     this._globalPropertyWhitelist = [
       "navigator", "constructor", "top",
       "Application",
       "__SS_tabsToRestore", "__SSi",
       "webConsoleCommandController",
     ];
 
+    this.Promise.Debugging.clearUncaughtErrorObservers();
+    this.Promise.Debugging.addUncaughtErrorObserver(this._uncaughtErrorObserver);
+
     if (this.tests.length)
       this.waitForGraphicsTestWindowToBeGone(this.nextTest.bind(this));
     else
       this.finish();
   },
 
   waitForGraphicsTestWindowToBeGone(aCallback) {
     let windowsEnum = Services.wm.getEnumerator(null);
@@ -311,33 +339,34 @@ Tester.prototype = {
                                     "finished window state check in " + time + "ms");
     }
 
     // Make sure the window is raised before each test.
     this.SimpleTest.waitForFocus(aCallback);
   },
 
   finish: function Tester_finish(aSkipSummary) {
+    this.Promise.Debugging.flushUncaughtErrors();
+
     var passCount = this.tests.reduce((a, f) => a + f.passCount, 0);
     var failCount = this.tests.reduce((a, f) => a + f.failCount, 0);
     var todoCount = this.tests.reduce((a, f) => a + f.todoCount, 0);
 
     // Include failures from window state checking prior to running the first test
     failCount += this.failuresFromInitialWindowState;
 
     if (this.repeat > 0) {
       --this.repeat;
       this.currentTestIndex = -1;
       this.nextTest();
     } else {
       TabDestroyObserver.destroy();
       Services.console.unregisterListener(this);
-
-      // It's important to terminate the module to avoid crashes on shutdown.
-      this.PromiseTestUtils.uninit();
+      this.Promise.Debugging.clearUncaughtErrorObservers();
+      this._treatUncaughtRejectionsAsFailures = false;
 
       // In the main process, we print the ShutdownLeaksCollector message here.
       let pid = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).processID;
       dump("Completed ShutdownLeaks collections in process " + pid + "\n");
 
       this.structuredLogger.info("TEST-START | Shutdown");
 
       if (this.tests.length) {
@@ -388,16 +417,17 @@ Tester.prototype = {
     } catch (ex) {
       // Swallow exception so we don't lead to another error being reported,
       // throwing us into an infinite loop
     }
   },
 
   nextTest: Task.async(function*() {
     if (this.currentTest) {
+      this.Promise.Debugging.flushUncaughtErrors();
       if (this._coverageCollector) {
         this._coverageCollector.recordTestCoverage(this.currentTest.path);
       }
 
       // Run cleanup functions for the current test before moving on to the
       // next one.
       let testScope = this.currentTest.scope;
       while (testScope.__cleanupFunctions.length > 0) {
@@ -419,16 +449,18 @@ Tester.prototype = {
           this.currentTest.todoCount === 0) {
         this.currentTest.addResult(new testResult({
           name: "This test contains no passes, no fails and no todos. Maybe" +
                 " it threw a silent exception? Make sure you use" +
                 " waitForExplicitFinish() if you need it.",
         }));
       }
 
+      this.Promise.Debugging.flushUncaughtErrors();
+
       let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
       if (winUtils.isTestControllingRefreshes) {
         this.currentTest.addResult(new testResult({
           name: "test left refresh driver under test control",
         }));
         winUtils.restoreNormalRefresh();
       }
@@ -436,20 +468,16 @@ Tester.prototype = {
       if (this.SimpleTest.isExpectingUncaughtException()) {
         this.currentTest.addResult(new testResult({
           name: "expectUncaughtException was called but no uncaught" +
                 " exception was detected!",
           allowFailure: this.currentTest.allowFailure,
         }));
       }
 
-      this.PromiseTestUtils.ensureDOMPromiseRejectionsProcessed();
-      this.PromiseTestUtils.assertNoUncaughtRejections();
-      this.PromiseTestUtils.assertNoMoreExpectedRejections();
-
       Object.keys(window).forEach(function (prop) {
         if (parseInt(prop) == prop) {
           // This is a string which when parsed as an integer and then
           // stringified gives the original string.  As in, this is in fact a
           // string representation of an integer, so an index into
           // window.frames.  Skip those.
           return;
         }
@@ -715,17 +743,16 @@ Tester.prototype = {
       } : {
         name: message,
         pass: true,
         stack,
         allowFailure: currentTest.allowFailure,
       }));
     });
 
-    this.PromiseTestUtils.Assert = this.currentTest.scope.Assert;
     this.ContentTask.setTestScope(currentScope);
 
     // Allow Assert.jsm methods to be tacked to the current scope.
     this.currentTest.scope.export_assertions = function() {
       for (let func in this.Assert) {
         this[func] = this.Assert[func].bind(this.Assert);
       }
     };
@@ -757,41 +784,41 @@ Tester.prototype = {
        }));
       }
     }
 
     // Import the test script.
     try {
       this._scriptLoader.loadSubScript(this.currentTest.path,
                                        this.currentTest.scope);
+      this.Promise.Debugging.flushUncaughtErrors();
       // Run the test
       this.lastStartTime = Date.now();
       if (this.currentTest.scope.__tasks) {
         // This test consists of tasks, added via the `add_task()` API.
         if ("test" in this.currentTest.scope) {
           throw "Cannot run both a add_task test and a normal test at the same time.";
         }
         let Promise = this.Promise;
-        let PromiseTestUtils = this.PromiseTestUtils;
         this.Task.spawn(function*() {
           let task;
           while ((task = this.__tasks.shift())) {
             this.SimpleTest.info("Entering test " + task.name);
             try {
               yield task();
             } catch (ex) {
               currentTest.addResult(new testResult({
                 name: "Uncaught exception",
                 pass: this.SimpleTest.isExpectingUncaughtException(),
                 ex,
                 stack: (typeof ex == "object" && "stack" in ex) ? ex.stack : null,
                 allowFailure: currentTest.allowFailure,
               }));
             }
-            PromiseTestUtils.assertNoUncaughtRejections();
+            Promise.Debugging.flushUncaughtErrors();
             this.SimpleTest.info("Leaving test " + task.name);
           }
           this.finish();
         }.bind(currentScope));
       } else if (typeof this.currentTest.scope.test == "function") {
         this.currentTest.scope.test();
       } else {
         throw "This test didn't call add_task, nor did it define a generatorTest() function, nor did it define a test() function, so we don't know how to run it.";
@@ -1029,16 +1056,23 @@ function testScope(aTester, aTest, expec
   this.expectUncaughtException = function test_expectUncaughtException(aExpecting) {
     self.SimpleTest.expectUncaughtException(aExpecting);
   };
 
   this.ignoreAllUncaughtExceptions = function test_ignoreAllUncaughtExceptions(aIgnoring) {
     self.SimpleTest.ignoreAllUncaughtExceptions(aIgnoring);
   };
 
+  this.thisTestLeaksUncaughtRejectionsAndShouldBeFixed = function(...rejections) {
+    if (!aTester._toleratedUncaughtRejections) {
+      aTester._toleratedUncaughtRejections = [];
+    }
+    aTester._toleratedUncaughtRejections.push(...rejections);
+  };
+
   this.expectAssertions = function test_expectAssertions(aMin, aMax) {
     let min = aMin;
     let max = aMax;
     if (typeof(max) == "undefined") {
       max = min;
     }
     if (typeof(min) != "number" || typeof(max) != "number" ||
         min < 0 || max < min) {
--- a/testing/mochitest/tests/browser/browser.ini
+++ b/testing/mochitest/tests/browser/browser.ini
@@ -5,39 +5,35 @@ support-files =
 [browser_add_task.js]
 [browser_async.js]
 [browser_browserLoaded_content_loaded.js]
 [browser_BrowserTestUtils.js]
 support-files =
   dummy.html
 [browser_fail.js]
 [browser_fail_add_task.js]
-[browser_fail_add_task_uncaught_rejection.js]
 [browser_fail_async.js]
 [browser_fail_if.js]
 fail-if = true
 [browser_fail_throw.js]
 [browser_fail_timeout.js]
 skip-if = true # Disabled beacuse it takes too long (bug 1178959)
 [browser_fail_uncaught_rejection.js]
-[browser_fail_uncaught_rejection_expected.js]
-[browser_fail_uncaught_rejection_expected_multi.js]
 [browser_fail_unexpectedTimeout.js]
 skip-if = true # Disabled beacuse it takes too long (bug 1178959)
 [browser_getTestFile.js]
 support-files =
   test-dir/*
   waitForFocusPage.html
 [browser_head.js]
 [browser_pass.js]
 [browser_parameters.js]
 [browser_popupNode.js]
 [browser_popupNode_check.js]
 [browser_privileges.js]
 [browser_requestLongerTimeout.js]
 skip-if = true # Disabled beacuse it takes too long (bug 1178959)
 [browser_sanityException.js]
 [browser_sanityException2.js]
-[browser_uncaught_rejection_expected.js]
 [browser_waitForFocus.js]
 skip-if = (os == "win" && e10s && debug)
 [browser_zz_fail_openwindow.js]
 skip-if = true # this catches outside of the main loop to find an extra window
deleted file mode 100644
--- a/testing/mochitest/tests/browser/browser_fail_add_task_uncaught_rejection.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-setExpectedFailuresForSelfTest(8);
-
-// Keep "JSMPromise" separate so "Promise" still refers to native Promises.
-let JSMPromise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
-
-async function rejectOnNextTick(error) {
-  await Promise.resolve();
-
-  Promise.reject(error);
-  JSMPromise.reject(error);
-}
-
-add_task(function failWithoutError() {
-  yield rejectOnNextTick(undefined);
-});
-
-add_task(function failWithString() {
-  yield rejectOnNextTick("This is a string");
-});
-
-add_task(function failWithInt() {
-  yield rejectOnNextTick(42);
-});
-
-// This one should display a stack trace
-add_task(function failWithError() {
-  yield rejectOnNextTick(new Error("This is an error"));
-});
--- a/testing/mochitest/tests/browser/browser_fail_uncaught_rejection.js
+++ b/testing/mochitest/tests/browser/browser_fail_uncaught_rejection.js
@@ -1,19 +1,6 @@
-setExpectedFailuresForSelfTest(3);
-
-// Keep "JSMPromise" separate so "Promise" still refers to native Promises.
-let JSMPromise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
+setExpectedFailuresForSelfTest(1);
 
 function test() {
+  Components.utils.import("resource://gre/modules/Promise.jsm", this);
   Promise.reject(new Error("Promise rejection."));
-  JSMPromise.reject(new Error("Promise.jsm rejection."));
-  (async () => {
-    throw "Synchronous rejection from async function.";
-  })();
-
-  // The following rejections are caught, so they won't result in failures.
-  Promise.reject(new Error("Promise rejection.")).catch(() => {});
-  JSMPromise.reject(new Error("Promise.jsm rejection.")).catch(() => {});
-  (async () => {
-    throw "Synchronous rejection from async function.";
-  })().catch(() => {});
 }
deleted file mode 100644
--- a/testing/mochitest/tests/browser/browser_fail_uncaught_rejection_expected.js
+++ /dev/null
@@ -1,10 +0,0 @@
-setExpectedFailuresForSelfTest(1);
-
-// The test will fail because there is only one of two expected rejections.
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.expectUncaughtRejection(/Promise rejection./);
-PromiseTestUtils.expectUncaughtRejection(/Promise rejection./);
-
-function test() {
-  Promise.reject(new Error("Promise rejection."));
-}
deleted file mode 100644
--- a/testing/mochitest/tests/browser/browser_fail_uncaught_rejection_expected_multi.js
+++ /dev/null
@@ -1,9 +0,0 @@
-setExpectedFailuresForSelfTest(1);
-
-// The test will fail because an expected uncaught rejection is actually caught.
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.expectUncaughtRejection(/Promise rejection./);
-
-function test() {
-  Promise.reject(new Error("Promise rejection.")).catch(() => {});
-}
deleted file mode 100644
--- a/testing/mochitest/tests/browser/browser_uncaught_rejection_expected.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// Keep "JSMPromise" separate so "Promise" still refers to native Promises.
-let JSMPromise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
-
-Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-PromiseTestUtils.whitelistRejectionsGlobally(/Whitelisted rejection./);
-PromiseTestUtils.expectUncaughtRejection(/Promise.jsm rejection./);
-PromiseTestUtils.expectUncaughtRejection(/Promise.jsm rejection./);
-PromiseTestUtils.expectUncaughtRejection(/Promise rejection./);
-PromiseTestUtils.expectUncaughtRejection(/Promise rejection./);
-
-function test() {
-  Promise.reject(new Error("Promise rejection."));
-  Promise.reject(new Error("Promise rejection."));
-  Promise.reject(new Error("Whitelisted rejection."));
-  JSMPromise.reject(new Error("Promise.jsm rejection."));
-  JSMPromise.reject(new Error("Promise.jsm rejection."));
-  JSMPromise.reject(new Error("Whitelisted rejection."));
-}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_noopt_debug.py
@@ -0,0 +1,42 @@
+import os
+
+MOZ_OBJDIR = 'obj-firefox'
+
+config = {
+    'default_actions': [
+        'clobber',
+        'clone-tools',
+        'checkout-sources',
+        'setup-mock',
+        'build',
+        # 'generate-build-stats',
+        'upload-files',
+        'sendchange',
+        'check-test',
+        'update',  # decided by query_is_nightly()
+    ],
+    'stage_platform': 'linux64-noopt-debug',
+    'debug_build': True,
+    'enable_signing': False,
+    'enable_talos_sendchange': False,
+    'env': {
+        'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
+        'DISPLAY': ':2',
+        'HG_SHARE_BASE_DIR': '/builds/hg-shared',
+        'MOZ_OBJDIR': MOZ_OBJDIR,
+        'MOZ_CRASHREPORTER_NO_REPORT': '1',
+        'CCACHE_DIR': '/builds/ccache',
+        'CCACHE_COMPRESS': '1',
+        'CCACHE_UMASK': '002',
+        'LC_ALL': 'C',
+        'XPCOM_DEBUG_BREAK': 'stack-and-abort',
+        # 64 bit specific
+        'PATH': '/tools/buildbot/bin:/usr/local/bin:/usr/lib64/ccache:/bin:\
+/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/tools/git/bin:/tools/python27/bin:\
+/tools/python27-mercurial/bin:/home/cltbld/bin',
+        'LD_LIBRARY_PATH': '/tools/gcc-4.3.3/installed/lib64:\
+%s/dist/bin' % (MOZ_OBJDIR,),
+        'TINDERBOX_OUTPUT': '1',
+    },
+    'src_mozconfig': 'browser/config/mozconfigs/linux64/noopt-debug',
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_cross_noopt_debug.py
@@ -0,0 +1,43 @@
+import os
+
+MOZ_OBJDIR = 'obj-firefox'
+
+config = {
+    'default_actions': [
+        'clobber',
+        'clone-tools',
+        'checkout-sources',
+        # 'setup-mock',
+        'build',
+        # 'generate-build-stats',
+        'upload-files',
+        'sendchange',
+        'update',  # decided by query_is_nightly()
+    ],
+    'stage_platform': 'macosx64-noopt-debug',
+    'debug_build': True,
+    'objdir': 'obj-firefox',
+    'enable_talos_sendchange': False,
+    #### 64 bit build specific #####
+    'env': {
+        'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
+        'HG_SHARE_BASE_DIR': '/builds/hg-shared',
+        'MOZ_OBJDIR': 'obj-firefox',
+        'TINDERBOX_OUTPUT': '1',
+        'TOOLTOOL_CACHE': '/builds/tooltool_cache',
+        'TOOLTOOL_HOME': '/builds',
+        'MOZ_CRASHREPORTER_NO_REPORT': '1',
+        'CCACHE_DIR': '/builds/ccache',
+        'CCACHE_COMPRESS': '1',
+        'CCACHE_UMASK': '002',
+        'LC_ALL': 'C',
+        'XPCOM_DEBUG_BREAK': 'stack-and-abort',
+        ## 64 bit specific
+        'PATH': '/tools/python/bin:/tools/buildbot/bin:/opt/local/bin:/usr/bin:'
+                '/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin',
+        ##
+    },
+    'src_mozconfig': 'browser/config/mozconfigs/macosx64/cross-noopt-debug',
+    'artifact_flag_build_variant_in_try': 'cross-debug-artifact',
+    #######################
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_win32_noopt_debug.py
@@ -0,0 +1,84 @@
+import os
+import sys
+
+config = {
+    #########################################################################
+    ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
+    # if you are updating this with custom 32 bit keys/values please add them
+    # below under the '32 bit specific' code block otherwise, update in this
+    # code block and also make sure this is synced between:
+    # - taskcluster_firefox_win32_debug
+    # - taskcluster_firefox_win32_opt
+    # - taskcluster_firefox_win64_debug
+    # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
+    # - taskcluster_firefox_win32_noopt_debug
+    # - taskcluster_firefox_win64_noopt_debug
+
+    'default_actions': [
+        'clone-tools',
+        'build',
+        'generate-build-stats',
+        'check-test',
+    ],
+    'exes': {
+        'virtualenv': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'python', 'virtualenv', 'virtualenv.py'
+            )
+        ],
+    },
+    'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
+    # decides whether we want to use moz_sign_cmd in env
+    'enable_signing': True,
+    'vcs_share_base': os.path.join('y:', os.sep, 'hg-shared'),
+    'objdir': 'obj-firefox',
+    'tooltool_script': [
+      sys.executable,
+      os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')
+    ],
+    'tooltool_bootstrap': 'setup.sh',
+    'enable_count_ctors': False,
+    'max_build_output_timeout': 60 * 80,
+    #########################################################################
+
+
+     #########################################################################
+     ###### 32 bit specific ######
+    'base_name': 'WINNT_5.2_%(branch)s',
+    'platform': 'win32',
+    'stage_platform': 'win32-noopt-debug',
+    'debug_build': True,
+    'publish_nightly_en_US_routes': True,
+    'env': {
+        'BINSCOPE': os.path.join(
+            os.environ['ProgramFiles(x86)'], 'Microsoft', 'SDL BinScope', 'BinScope.exe'
+        ),
+        'HG_SHARE_BASE_DIR': os.path.join('y:', os.sep, 'hg-shared'),
+        'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
+        'MOZ_CRASHREPORTER_NO_REPORT': '1',
+        'MOZ_OBJDIR': 'obj-firefox',
+        'PDBSTR_PATH': 'C:/Program Files (x86)/Windows Kits/10/Debuggers/x86/srcsrv/pdbstr.exe',
+        'TINDERBOX_OUTPUT': '1',
+        'TOOLTOOL_CACHE': 'c:/builds/tooltool_cache',
+        'TOOLTOOL_HOME': '/c/builds',
+        'XPCOM_DEBUG_BREAK': 'stack-and-abort',
+        'MSYSTEM': 'MINGW32',
+    },
+    'upload_env': {
+        'UPLOAD_HOST': 'localhost',
+        'UPLOAD_PATH': os.path.join(os.getcwd(), 'public', 'build'),
+    },
+    "check_test_env": {
+        'MINIDUMP_STACKWALK': '%(abs_tools_dir)s\\breakpad\\win32\\minidump_stackwalk.exe',
+        'MINIDUMP_SAVE_PATH': '%(base_work_dir)s\\minidumps',
+    },
+    'src_mozconfig': 'browser\\config\\mozconfigs\\win32\\noopt-debug',
+    'tooltool_manifest_src': 'browser\\config\\tooltool-manifests\\win32\\releng.manifest',
+    'artifact_flag_build_variant_in_try': None,
+    #########################################################################
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_win64_noopt_debug.py
@@ -0,0 +1,77 @@
+import os
+import sys
+
+config = {
+    #########################################################################
+    ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
+    # if you are updating this with custom 64 bit keys/values please add them
+    # below under the '64 bit specific' code block otherwise, update in this
+    # code block and also make sure this is synced between:
+    # - taskcluster_firefox_win32_debug
+    # - taskcluster_firefox_win32_opt
+    # - taskcluster_firefox_win64_debug
+    # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
+
+    'default_actions': [
+        'clone-tools',
+        'build',
+        'check-test',
+    ],
+    'exes': {
+        'virtualenv': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'python', 'virtualenv', 'virtualenv.py'
+            )
+        ],
+    },
+    'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
+    # decides whether we want to use moz_sign_cmd in env
+    'enable_signing': True,
+    'vcs_share_base': os.path.join('y:', os.sep, 'hg-shared'),
+    'objdir': 'obj-firefox',
+    'tooltool_script': [
+      sys.executable,
+      os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')
+    ],
+    'tooltool_bootstrap': 'setup.sh',
+    'enable_count_ctors': False,
+    'max_build_output_timeout': 60 * 80,
+    #########################################################################
+
+
+     #########################################################################
+     ###### 64 bit specific ######
+    'base_name': 'WINNT_6.1_x86-64_%(branch)s',
+    'platform': 'win64',
+    'stage_platform': 'win64-noopt-debug',
+    'debug_build': True,
+    'publish_nightly_en_US_routes': True,
+    'env': {
+        'HG_SHARE_BASE_DIR': os.path.join('y:', os.sep, 'hg-shared'),
+        'MOZ_CRASHREPORTER_NO_REPORT': '1',
+        'MOZ_OBJDIR': 'obj-firefox',
+        'PDBSTR_PATH': 'C:/Program Files (x86)/Windows Kits/10/Debuggers/x64/srcsrv/pdbstr.exe',
+        'TINDERBOX_OUTPUT': '1',
+        'TOOLTOOL_CACHE': 'c:/builds/tooltool_cache',
+        'TOOLTOOL_HOME': '/c/builds',
+        'XPCOM_DEBUG_BREAK': 'stack-and-abort',
+        'MSYSTEM': 'MINGW32',
+    },
+    'upload_env': {
+        'UPLOAD_HOST': 'localhost',
+        'UPLOAD_PATH': os.path.join(os.getcwd(), 'public', 'build'),
+    },
+    "check_test_env": {
+        'MINIDUMP_STACKWALK': '%(abs_tools_dir)s\\breakpad\\win64\\minidump_stackwalk.exe',
+        'MINIDUMP_SAVE_PATH': '%(base_work_dir)s\\minidumps',
+    },
+    'src_mozconfig': 'browser\\config\\mozconfigs\\win64\\noopt-debug',
+    'tooltool_manifest_src': 'browser\\config\\tooltool-manifests\\win64\\releng.manifest',
+    'artifact_flag_build_variant_in_try': None,
+    #########################################################################
+}
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -346,26 +346,28 @@ class BuildOptionParser(object):
         'add-on-devel': 'builds/releng_sub_%s_configs/%s_add-on-devel.py',
         'asan': 'builds/releng_sub_%s_configs/%s_asan.py',
         'asan-tc': 'builds/releng_sub_%s_configs/%s_asan_tc.py',
         'fuzzing-asan-tc': 'builds/releng_sub_%s_configs/%s_fuzzing_asan_tc.py',
         'tsan': 'builds/releng_sub_%s_configs/%s_tsan.py',
         'cross-debug': 'builds/releng_sub_%s_configs/%s_cross_debug.py',
         'cross-debug-st-an': 'builds/releng_sub_%s_configs/%s_cross_debug_st_an.py',
         'cross-debug-artifact': 'builds/releng_sub_%s_configs/%s_cross_debug_artifact.py',
+        'cross-noopt-debug': 'builds/releng_sub_%s_configs/%s_cross_noopt_debug.py',
         'cross-opt-st-an': 'builds/releng_sub_%s_configs/%s_cross_opt_st_an.py',
         'cross-artifact': 'builds/releng_sub_%s_configs/%s_cross_artifact.py',
         'debug': 'builds/releng_sub_%s_configs/%s_debug.py',
         'asan-and-debug': 'builds/releng_sub_%s_configs/%s_asan_and_debug.py',
         'asan-tc-and-debug': 'builds/releng_sub_%s_configs/%s_asan_tc_and_debug.py',
         'stat-and-debug': 'builds/releng_sub_%s_configs/%s_stat_and_debug.py',
         'code-coverage': 'builds/releng_sub_%s_configs/%s_code_coverage.py',
         'source': 'builds/releng_sub_%s_configs/%s_source.py',
         'stylo': 'builds/releng_sub_%s_configs/%s_stylo.py',
         'stylo-debug': 'builds/releng_sub_%s_configs/%s_stylo_debug.py',
+        'noopt-debug': 'builds/releng_sub_%s_configs/%s_noopt_debug.py',
         'api-15-gradle-dependencies': 'builds/releng_sub_%s_configs/%s_api_15_gradle_dependencies.py',
         'api-15': 'builds/releng_sub_%s_configs/%s_api_15.py',
         'api-15-old-id': 'builds/releng_sub_%s_configs/%s_api_15_old_id.py',
         'api-15-artifact': 'builds/releng_sub_%s_configs/%s_api_15_artifact.py',
         'api-15-debug': 'builds/releng_sub_%s_configs/%s_api_15_debug.py',
         'api-15-debug-artifact': 'builds/releng_sub_%s_configs/%s_api_15_debug_artifact.py',
         'api-15-gradle': 'builds/releng_sub_%s_configs/%s_api_15_gradle.py',
         'api-15-gradle-artifact': 'builds/releng_sub_%s_configs/%s_api_15_gradle_artifact.py',
--- a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noopener.html.ini
+++ b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noopener.html.ini
@@ -1,10 +1,11 @@
 [open-features-tokenization-noopener.html]
   type: testharness
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1364696
   [tokenization should skip window features separators before `name`]
     expected: FAIL
 
   [feature `name` should be converted to ASCII lowercase]
     expected: FAIL
 
   [after `name`, tokenization should skip window features separators that are not "=" or ","]
     expected: FAIL
--- a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-screenx-screeny.html.ini
+++ b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-screenx-screeny.html.ini
@@ -1,9 +1,8 @@
 [open-features-tokenization-screenx-screeny.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1364696
   ["screenx==141" should set left position of opened window]
     expected: FAIL
 
   ["screeny==142" should set top position of opened window]
     expected: FAIL
 
--- a/toolkit/components/startup/tests/browser/browser_bug537449.js
+++ b/toolkit/components/startup/tests/browser/browser_bug537449.js
@@ -1,14 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
+
 SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
 
 const TEST_URL = "http://example.com/browser/toolkit/components/startup/tests/browser/beforeunload.html";
 
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, TEST_URL);
--- a/toolkit/components/telemetry/TelemetryStopwatch.jsm
+++ b/toolkit/components/telemetry/TelemetryStopwatch.jsm
@@ -3,17 +3,20 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 this.EXPORTED_SYMBOLS = ["TelemetryStopwatch"];
 
-Cu.import("resource://gre/modules/Log.jsm", this);
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Log",
+  "resource://gre/modules/Log.jsm");
+
 var Telemetry = Cc["@mozilla.org/base/telemetry;1"]
                   .getService(Ci.nsITelemetry);
 
 // Weak map does not allow using null objects as keys. These objects are used
 // as 'null' placeholders.
 const NULL_OBJECT = {};
 const NULL_KEY = {};
 
--- a/toolkit/content/tests/browser/browser_block_webAudio.js
+++ b/toolkit/content/tests/browser/browser_block_webAudio.js
@@ -1,17 +1,10 @@
 const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_webAudio.html";
 
-// The tab closing code leaves an uncaught rejection. This test has been
-// whitelisted until the issue is fixed.
-if (!gMultiProcessBrowser) {
-  Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-  PromiseTestUtils.expectUncaughtRejection(/is no longer, usable/);
-}
-
 add_task(async function setup_test_preference() {
   await SpecialPowers.pushPrefEnv({"set": [
     ["media.useAudioChannelService.testing", true],
     ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function block_web_audio() {
--- a/toolkit/content/tests/browser/browser_mute_webAudio.js
+++ b/toolkit/content/tests/browser/browser_mute_webAudio.js
@@ -1,15 +1,8 @@
-// The tab closing code leaves an uncaught rejection. This test has been
-// whitelisted until the issue is fixed.
-if (!gMultiProcessBrowser) {
-  Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
-  PromiseTestUtils.expectUncaughtRejection(/is no longer, usable/);
-}
-
 const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_webAudio.html";
 
 async function click_icon(tab) {
   let icon = document.getAnonymousElementByAttribute(tab, "anonid", "soundplaying-icon");
 
   await hover_icon(icon, document.getElementById("tabbrowser-tab-tooltip"));
   EventUtils.synthesizeMouseAtCenter(icon, {button: 0});
   leave_icon(icon);
--- a/toolkit/crashreporter/crashreporter.mozbuild
+++ b/toolkit/crashreporter/crashreporter.mozbuild
@@ -13,16 +13,19 @@ LOCAL_INCLUDES += [
 if CONFIG['_MSC_VER']:
     CXXFLAGS += [
         '-wd4005', # macro redefinition
     ]
 elif CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-unused-local-typedefs',
         '-Wno-shadow',
+        '-Wno-deprecated-declarations',
+        '-Wno-bool-compare',
+        '-Wno-unused-but-set-variable',
     ]
 
 if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
     CXXFLAGS += [
         '-Wno-implicit-fallthrough',
         '-Wno-c++11-narrowing',
     ]
 
--- a/toolkit/modules/tests/modules/PromiseTestUtils.jsm
+++ b/toolkit/modules/tests/modules/PromiseTestUtils.jsm
@@ -48,23 +48,16 @@ this.PromiseTestUtils = {
    * When an uncaught rejection is detected, it is ignored if one of the
    * functions in this array returns true when called with the rejection details
    * as its only argument. When a function matches an expected rejection, it is
    * then removed from the array.
    */
   _rejectionIgnoreFns: [],
 
   /**
-   * If any of the functions in this array returns true when called with the
-   * rejection details as its only argument, the rejection is ignored. This
-   * happens after the "_rejectionIgnoreFns" array is processed.
-   */
-  _globalRejectionIgnoreFns: [],
-
-  /**
    * Called only by the test infrastructure, registers the rejection observers.
    *
    * This should be called only once, and a matching "uninit" call must be made
    * or the tests will crash on shutdown.
    */
   init() {
     if (this._initialized) {
       Cu.reportError("This object was already initialized.");
@@ -152,31 +145,23 @@ this.PromiseTestUtils = {
       let reason = PromiseDebugging.getState(promise).reason;
       if (reason === this._ensureDOMPromiseRejectionsProcessedReason) {
         // Ignore the special promise for ensureDOMPromiseRejectionsProcessed.
         return;
       }
       message = reason.message || ("" + reason);
     } catch (ex) {}
 
-    // We should convert the rejection stack to a string immediately. This is
-    // because the object might not be available when we report the rejection
-    // later, if the error occurred in a context that has been unloaded.
-    let stack = "(Unable to convert rejection stack to string.)";
-    try {
-      stack = "" + PromiseDebugging.getRejectionStack(promise);
-    } catch (ex) {}
-
     // It's important that we don't store any reference to the provided Promise
     // object or its value after this function returns in order to avoid leaks.
     this._rejections.push({
       id: PromiseDebugging.getPromiseID(promise),
       message,
       date: new Date(),
-      stack,
+      stack: PromiseDebugging.getRejectionStack(promise),
     });
   },
 
   // UncaughtRejectionObserver
   onConsumed(promise) {
     // We don't expect that many unhandled rejections will appear at the same
     // time, so the algorithm doesn't need to be optimized for that case.
     let id = PromiseDebugging.getPromiseID(promise);
@@ -205,29 +190,16 @@ this.PromiseTestUtils = {
    */
   expectUncaughtRejection(regExpOrCheckFn) {
     let checkFn = !("test" in regExpOrCheckFn) ? regExpOrCheckFn :
                   rejection => regExpOrCheckFn.test(rejection.message);
     this._rejectionIgnoreFns.push(checkFn);
   },
 
   /**
-   * Whitelists an entire class of Promise rejections. Usage of this function
-   * should be kept to a minimum because it has a broad scope and doesn't
-   * prevent new unhandled rejections of this class from being added.
-   *
-   * @param regExp
-   *        This should match the error message of the rejection.
-   */
-  whitelistRejectionsGlobally(regExp) {
-    this._globalRejectionIgnoreFns.push(
-      rejection => regExp.test(rejection.message));
-  },
-
-  /**
    * Fails the test if there are any uncaught rejections at this time that have
    * not been whitelisted using expectUncaughtRejection.
    *
    * Depending on the configuration of the test suite, this function might only
    * report the details of the first uncaught rejection that was generated.
    *
    * This is called by the test suite at the end of each test function.
    */
@@ -242,21 +214,16 @@ this.PromiseTestUtils = {
       // If one of the ignore functions matches, ignore the rejection, then
       // remove the function so that each function only matches one rejection.
       let index = this._rejectionIgnoreFns.findIndex(f => f(rejection));
       if (index != -1) {
         this._rejectionIgnoreFns.splice(index, 1);
         continue;
       }
 
-      // Check the global whitelisting functions.
-      if (this._globalRejectionIgnoreFns.some(fn => fn(rejection))) {
-        continue;
-      }
-
       // Report the error. This operation can throw an exception, depending on
       // the configuration of the test suite that handles the assertion.
       Assert.ok(false,
                 `A promise chain failed to handle a rejection:` +
                 ` ${rejection.message} - rejection date: ${rejection.date}` +
                 ` - stack: ${rejection.stack}`);
     }
   },
@@ -268,12 +235,10 @@ this.PromiseTestUtils = {
    * This is called by the test suite at the end of each test file.
    */
   assertNoMoreExpectedRejections() {
     // Only log this condition is there is a failure.
     if (this._rejectionIgnoreFns.length > 0) {
       Assert.equal(this._rejectionIgnoreFns.length, 0,
              "Unable to find a rejection expected by expectUncaughtRejection.");
     }
-    // Reset the list of expected rejections in case the test suite continues.
-    this._rejectionIgnoreFns = [];
   },
 };
--- a/toolkit/mozapps/extensions/test/browser/browser_bug570760.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug570760.js
@@ -1,12 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("");
+
 // Bug 570760 - Make ctrl-f and / focus the search box in the add-ons manager
 
 var gManagerWindow;
 var focusCount = 0;
 
 function test() {
   waitForExplicitFinish();
 
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_auth3.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_auth3.js
@@ -1,13 +1,20 @@
 // ----------------------------------------------------------------------------
 // Test whether an install fails when authentication is required and it is
 // canceled
 // This verifies bug 312473
 
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
+
+
 function test() {
   Harness.authenticationCallback = get_auth_info;
   Harness.downloadFailedCallback = download_failed;
   Harness.installEndedCallback = install_ended;
   Harness.installsCompletedCallback = finish_test;
   Harness.setup();
 
   var pm = Services.perms;
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_auth4.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_auth4.js
@@ -1,8 +1,16 @@
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
+
+
+// ----------------------------------------------------------------------------
 // Test whether a request for auth for an XPI switches to the appropriate tab
 var gNewTab;
 
 function test() {
   Harness.authenticationCallback = get_auth_info;
   Harness.downloadFailedCallback = download_failed;
   Harness.installEndedCallback = install_ended;
   Harness.installsCompletedCallback = finish_test;
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js
@@ -40,16 +40,17 @@ module.exports = {
     "ignoreAllUncaughtExceptions": false,
     "info": false,
     "is": false,
     "isnot": false,
     "ok": false,
     "privateNoteIntentionalCrash": false,
     "registerCleanupFunction": false,
     "requestLongerTimeout": false,
+    "thisTestLeaksUncaughtRejectionsAndShouldBeFixed": false,
     "todo": false,
     "todo_is": false,
     "todo_isnot": false,
     "waitForClipboard": false,
     "waitForExplicitFinish": false,
     "waitForFocus": false
   },
 
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -63,16 +63,20 @@ void nsView::DropMouseGrabbing()
 }
 
 nsView::~nsView()
 {
   MOZ_COUNT_DTOR(nsView);
 
   bool printRelated = mViewManager && mViewManager->GetPrintRelated();
 
+  if (mViewManager && (mViewManager->GetRootView() == this)) {
+    MOZ_RELEASE_ASSERT(!GetFirstChild());
+  }
+
   while (GetFirstChild())
   {
     nsView* child = GetFirstChild();
     if (child->GetViewManager() == mViewManager) {
       child->Destroy();
     } else {
       // just unhook it. Someone else will want to destroy this.
       RemoveChild(child);
@@ -467,16 +471,18 @@ void nsView::InsertChild(nsView *aChild,
       //insert after sibling
       aChild->SetNextSibling(aSibling->GetNextSibling());
       aSibling->SetNextSibling(aChild);
     }
     else
     {
       aChild->SetNextSibling(mFirstChild);
       mFirstChild = aChild;
+      MOZ_RELEASE_ASSERT(!mFirstChild || mFrame ||
+        mFirstChild->GetViewManager() != GetViewManager());
     }
     aChild->SetParent(this);
 
     // If we just inserted a root view, then update the RootViewManager
     // on all view managers in the new subtree.
 
     nsViewManager *vm = aChild->GetViewManager();
     if (vm->GetRootView() == aChild)
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -685,42 +685,18 @@ AndroidBridge::GetCurrentNetworkInformat
 }
 
 jobject
 AndroidBridge::GetGlobalContextRef() {
     if (sGlobalContext) {
         return sGlobalContext;
     }
 
-    JNIEnv* const env = GetEnvForThread();
-    AutoLocalJNIFrame jniFrame(env, 4);
-
-    auto context = GeckoAppShell::GetContext();
-    if (!context) {
-        ALOG_BRIDGE("%s: Could not GetContext()", __FUNCTION__);
-        return 0;
-    }
-    jclass contextClass = env->FindClass("android/content/Context");
-    if (!contextClass) {
-        ALOG_BRIDGE("%s: Could not find Context class.", __FUNCTION__);
-        return 0;
-    }
-    jmethodID mid = env->GetMethodID(contextClass, "getApplicationContext",
-                                     "()Landroid/content/Context;");
-    if (!mid) {
-        ALOG_BRIDGE("%s: Could not find getApplicationContext.", __FUNCTION__);
-        return 0;
-    }
-    jobject appContext = env->CallObjectMethod(context.Get(), mid);
-    if (!appContext) {
-        ALOG_BRIDGE("%s: getApplicationContext failed.", __FUNCTION__);
-        return 0;
-    }
-
-    sGlobalContext = env->NewGlobalRef(appContext);
+    auto context = GeckoAppShell::GetApplicationContext();
+    sGlobalContext = Object::GlobalRef(context).Forget();
     MOZ_ASSERT(sGlobalContext);
     return sGlobalContext;
 }
 
 /* Implementation file */
 NS_IMPL_ISUPPORTS(nsAndroidBridge,
                   nsIAndroidEventDispatcher,
                   nsIAndroidBridge,
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -236,24 +236,16 @@ auto GeckoAppShell::GetApplicationContex
 constexpr char GeckoAppShell::GetConnection_t::name[];
 constexpr char GeckoAppShell::GetConnection_t::signature[];
 
 auto GeckoAppShell::GetConnection(mozilla::jni::String::Param a0) -> mozilla::jni::Object::LocalRef
 {
     return mozilla::jni::Method<GetConnection_t>::Call(GeckoAppShell::Context(), nullptr, a0);
 }
 
-constexpr char GeckoAppShell::GetContext_t::name[];
-constexpr char GeckoAppShell::GetContext_t::signature[];
-
-auto GeckoAppShell::GetContext() -> mozilla::jni::Object::LocalRef
-{
-    return mozilla::jni::Method<GetContext_t>::Call(GeckoAppShell::Context(), nullptr);
-}
-
 constexpr char GeckoAppShell::GetCurrentBatteryInformation_t::name[];
 constexpr char GeckoAppShell::GetCurrentBatteryInformation_t::signature[];
 
 auto GeckoAppShell::GetCurrentBatteryInformation() -> mozilla::jni::DoubleArray::LocalRef
 {
     return mozilla::jni::Method<GetCurrentBatteryInformation_t>::Call(GeckoAppShell::Context(), nullptr);
 }
 
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -701,35 +701,16 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     static auto GetConnection(mozilla::jni::String::Param) -> mozilla::jni::Object::LocalRef;
 
-    struct GetContext_t {
-        typedef GeckoAppShell Owner;
-        typedef mozilla::jni::Object::LocalRef ReturnType;
-        typedef mozilla::jni::Object::Param SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "getContext";
-        static constexpr char signature[] =
-                "()Landroid/content/Context;";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static auto GetContext() -> mozilla::jni::Object::LocalRef;
-
     struct GetCurrentBatteryInformation_t {
         typedef GeckoAppShell Owner;
         typedef mozilla::jni::DoubleArray::LocalRef ReturnType;
         typedef mozilla::jni::DoubleArray::Param SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "getCurrentBatteryInformation";
         static constexpr char signature[] =
                 "()[D";
--- a/widget/android/fennec/FennecJNIWrappers.cpp
+++ b/widget/android/fennec/FennecJNIWrappers.cpp
@@ -47,16 +47,24 @@ const char GeckoApp::name[] =
 constexpr char GeckoApp::AddPluginView_t::name[];
 constexpr char GeckoApp::AddPluginView_t::signature[];
 
 auto GeckoApp::AddPluginView(mozilla::jni::Object::Param a0) -> void
 {
     return mozilla::jni::Method<AddPluginView_t>::Call(GeckoApp::Context(), nullptr, a0);
 }
 
+constexpr char GeckoApp::GetPluginContext_t::name[];
+constexpr char GeckoApp::GetPluginContext_t::signature[];
+
+auto GeckoApp::GetPluginContext() -> mozilla::jni::Object::LocalRef
+{
+    return mozilla::jni::Method<GetPluginContext_t>::Call(GeckoApp::Context(), nullptr);
+}
+
 constexpr char GeckoApp::LaunchOrBringToFront_t::name[];
 constexpr char GeckoApp::LaunchOrBringToFront_t::signature[];
 
 auto GeckoApp::LaunchOrBringToFront() -> void
 {
     return mozilla::jni::Method<LaunchOrBringToFront_t>::Call(GeckoApp::Context(), nullptr);
 }
 
--- a/widget/android/fennec/FennecJNIWrappers.h
+++ b/widget/android/fennec/FennecJNIWrappers.h
@@ -151,16 +151,35 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::GECKO;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     static auto AddPluginView(mozilla::jni::Object::Param) -> void;
 
+    struct GetPluginContext_t {
+        typedef GeckoApp Owner;
+        typedef mozilla::jni::Object::LocalRef ReturnType;
+        typedef mozilla::jni::Object::Param SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "getPluginContext";
+        static constexpr char signature[] =
+                "()Landroid/content/Context;";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::GECKO;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto GetPluginContext() -> mozilla::jni::Object::LocalRef;
+
     struct LaunchOrBringToFront_t {
         typedef GeckoApp Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "launchOrBringToFront";
         static constexpr char signature[] =
                 "()V";
--- a/xpcom/build/LateWriteChecks.cpp
+++ b/xpcom/build/LateWriteChecks.cpp
@@ -153,16 +153,19 @@ LateWriteObserver::Observe(IOInterposeOb
   int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
   if (fd == -1) {
     MOZ_CRASH("Um, how did we get here?");
   }
 
   stream = _fdopen(fd, "w");
 #else
   int fd = mkstemp(name);
+  if (fd == -1) {
+    MOZ_CRASH("mkstemp failed");
+  }
   stream = fdopen(fd, "w");
 #endif
 
   SHA1Stream sha1Stream(stream);
 
   size_t numModules = stack.GetNumModules();
   sha1Stream.Printf("%u\n", (unsigned)numModules);
   for (size_t i = 0; i < numModules; ++i) {