Merge m-c to b2g-inbound a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 13 Jan 2015 17:46:54 -0800
changeset 223748 146058338f8582290c7ba0bc8de9756c2a93da97
parent 223747 36fa4461cdea349ae9fec5426e9eae0f250439ac (current diff)
parent 223675 63006936ab99b4ea5db20d1d7d67ce6c7f16ebf5 (diff)
child 223749 86bcc35413b4311d023f6b115c1c8556d9af5243
push id10811
push usercbook@mozilla.com
push dateWed, 14 Jan 2015 13:52:17 +0000
treeherderfx-team@b8d38d19dbd9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
Merge m-c to b2g-inbound a=merge
browser/components/places/BrowserPlaces.manifest
browser/components/places/PlacesProtocolHandler.js
testing/web-platform/fetchlogs.py
testing/web-platform/harness/wptrunner/update.py
testing/web-platform/meta/workers/semantics/interface-objects/001.html.ini
testing/web-platform/tests/app-uri/resources/idlharness.js
testing/web-platform/tests/app-uri/resources/testharness.css
testing/web-platform/tests/app-uri/resources/testharness.js
testing/web-platform/tests/app-uri/resources/testharnessreport.js
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/resources/WebIDLParser.js
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/resources/apisample.htm
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/resources/apisample2.htm
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/resources/apisample3.htm
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/resources/idlharness.js
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/resources/readme.md
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/resources/testharness.css
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/resources/testharness.js
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/resources/testharnessreport.js
testing/web-platform/tests/workers/semantics/interface-objects/001.html
testing/web-platform/update.py
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1083,8 +1083,11 @@ pref("dom.mozSettings.SettingsDB.verbose
 pref("dom.mozSettings.SettingsManager.verbose.enabled", false);
 pref("dom.mozSettings.SettingsRequestManager.verbose.enabled", false);
 pref("dom.mozSettings.SettingsService.verbose.enabled", false);
 
 // Controlling whether we want to allow forcing some Settings
 // IndexedDB transactions to be opened as readonly or keep everything as
 // readwrite.
 pref("dom.mozSettings.allowForceReadOnly", false);
+
+// RequestSync API is enabled by default on B2G.
+pref("dom.requestSync.enabled", true);
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -10,28 +10,30 @@ Cu.import('resource://gre/modules/AlarmS
 Cu.import('resource://gre/modules/ActivitiesService.jsm');
 Cu.import('resource://gre/modules/NotificationDB.jsm');
 Cu.import('resource://gre/modules/Payment.jsm');
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import('resource://gre/modules/UserAgentOverrides.jsm');
 Cu.import('resource://gre/modules/Keyboard.jsm');
 Cu.import('resource://gre/modules/ErrorPage.jsm');
 Cu.import('resource://gre/modules/AlertsHelper.jsm');
+Cu.import('resource://gre/modules/RequestSyncService.jsm');
 #ifdef MOZ_WIDGET_GONK
 Cu.import('resource://gre/modules/NetworkStatsService.jsm');
 Cu.import('resource://gre/modules/ResourceStatsService.jsm');
 #endif
 
 // Identity
 Cu.import('resource://gre/modules/SignInToWebsite.jsm');
 SignInToWebsiteController.init();
 
 Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
 Cu.import('resource://gre/modules/DownloadsAPI.jsm');
 Cu.import('resource://gre/modules/MobileIdentityManager.jsm');
+Cu.import('resource://gre/modules/PresentationDeviceInfoManager.jsm');
 
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
 Cu.import('resource://gre/modules/Webapps.jsm');
 DOMApplicationRegistry.allAppsLaunchable = true;
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'env',
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -101,8 +101,12 @@ category command-line-handler m-b2gcmds 
 # MobileIdentityUIGlue.js
 component {83dbe26a-81f3-4a75-9541-3d0b7ca496b5} MobileIdentityUIGlue.js
 contract @mozilla.org/services/mobileid-ui-glue;1 {83dbe26a-81f3-4a75-9541-3d0b7ca496b5}
 
 # B2GAppMigrator.js
 component {7211ece0-b458-4635-9afc-f8d7f376ee95} B2GAppMigrator.js
 contract @mozilla.org/app-migrator;1 {7211ece0-b458-4635-9afc-f8d7f376ee95}
 
+# B2GPresentationDevicePrompt.js
+component {4a300c26-e99b-4018-ab9b-c48cf9bc4de1} B2GPresentationDevicePrompt.js
+contract @mozilla.org/presentation-device/prompt;1 {4a300c26-e99b-4018-ab9b-c48cf9bc4de1}
+
new file mode 100644
--- /dev/null
+++ b/b2g/components/B2GPresentationDevicePrompt.js
@@ -0,0 +1,87 @@
+/* -*- 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/. */
+
+"use strict";
+
+function debug(aMsg) {
+  //dump("-*- B2GPresentationDevicePrompt: " + aMsg + "\n");
+}
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+const kB2GPRESENTATIONDEVICEPROMPT_CONTRACTID = "@mozilla.org/presentation-device/prompt;1";
+const kB2GPRESENTATIONDEVICEPROMPT_CID        = Components.ID("{4a300c26-e99b-4018-ab9b-c48cf9bc4de1}");
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
+                                  "resource://gre/modules/SystemAppProxy.jsm");
+
+function B2GPresentationDevicePrompt() {}
+
+B2GPresentationDevicePrompt.prototype = {
+  classID: kB2GPRESENTATIONDEVICEPROMPT_CID,
+  contractID: kB2GPRESENTATIONDEVICEPROMPT_CONTRACTID,
+  classDescription: "B2G Presentation Device Prompt",
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevicePrompt]),
+
+  // nsIPresentationDevicePrompt
+  promptDeviceSelection: function(aRequest) {
+    let self = this;
+    let requestId = Cc["@mozilla.org/uuid-generator;1"]
+                      .getService(Ci.nsIUUIDGenerator).generateUUID().toString();
+
+    SystemAppProxy.addEventListener("mozContentEvent", function contentEvent(aEvent) {
+      let detail = aEvent.detail;
+      if (detail.id !== requestId) {
+        return;
+      }
+
+      SystemAppProxy.removeEventListener("mozContentEvent", contentEvent);
+
+      switch (detail.type) {
+        case "presentation-select-result":
+          debug("device " + detail.deviceId + " is selected by user");
+          let device = self._getDeviceById(detail.deviceId);
+          if (!device) {
+            debug("cancel request because device is not found");
+            aRequest.cancel();
+          }
+          aRequest.select(device);
+          break;
+        case "presentation-select-deny":
+          debug("request canceled by user");
+          aRequest.cancel();
+          break;
+      }
+    });
+
+    let detail = {
+      type: "presentation-select-device",
+      origin: aRequest.origin,
+      requestURL: aRequest.requestURL,
+      id: requestId,
+    };
+
+    SystemAppProxy.dispatchEvent(detail);
+  },
+
+  _getDeviceById: function(aDeviceId) {
+    let deviceManager = Cc["@mozilla.org/presentation-device/manager;1"]
+                          .getService(Ci.nsIPresentationDeviceManager);
+    let devices = deviceManager.getAvailableDevices().QueryInterface(Ci.nsIArray);
+
+    for (let i = 0; i < devices.length; i++) {
+      let device = devices.queryElementAt(i, Ci.nsIPresentationDevice);
+      if (device.id === aDeviceId) {
+        return device;
+      }
+    }
+
+    return null;
+  },
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([B2GPresentationDevicePrompt]);
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -6,16 +6,17 @@
 
 DIRS += ['test']
 
 EXTRA_COMPONENTS += [
     'ActivitiesGlue.js',
     'AlertsService.js',
     'B2GAboutRedirector.js',
     'B2GAppMigrator.js',
+    'B2GPresentationDevicePrompt.js',
     'ContentPermissionPrompt.js',
     'FilePicker.js',
     'FxAccountsUIGlue.js',
     'HelperAppDialog.js',
     'InterAppCommUIGlue.js',
     'MailtoProtocolHandler.js',
     'MobileIdentityUIGlue.js',
     'OMAContentHandler.js',
--- a/b2g/components/test/mochitest/mochitest.ini
+++ b/b2g/components/test/mochitest/mochitest.ini
@@ -1,14 +1,16 @@
 [DEFAULT]
 run-if = toolkit == "gonk"
 support-files =
   permission_handler_chrome.js
   SandboxPromptTest.html
   filepicker_path_handler_chrome.js
   systemapp_helper.js
+  presentation_prompt_handler_chrome.js
 
 [test_filepicker_path.html]
 [test_permission_deny.html]
 [test_permission_gum_remember.html]
 skip-if = true # Bug 1019572 - frequent timeouts
 [test_sandbox_permission.html]
 [test_systemapp.html]
+[test_presentation_device_prompt.html]
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/mochitest/presentation_prompt_handler_chrome.js
@@ -0,0 +1,94 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+'use strict';
+
+ function debug(str) {
+   dump('presentation_prompt_handler_chrome: ' + str + '\n');
+ }
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+const { XPCOMUtils } = Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+const { SystemAppProxy } = Cu.import('resource://gre/modules/SystemAppProxy.jsm');
+
+const manager = Cc["@mozilla.org/presentation-device/manager;1"]
+                  .getService(Ci.nsIPresentationDeviceManager);
+
+const prompt = Cc['@mozilla.org/presentation-device/prompt;1']
+                 .getService(Ci.nsIPresentationDevicePrompt);
+
+function TestPresentationDevice(options) {
+  this.id = options.id;
+  this.name = options.name;
+  this.type = options.type;
+}
+
+TestPresentationDevice.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
+  establishSessionTransport: function() {
+    return null;
+  },
+};
+
+function TestPresentationRequest(options) {
+  this.origin = options.origin;
+  this.requestURL = options.requestURL;
+}
+
+TestPresentationRequest.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceRequest]),
+  select: function(device) {
+    let result = {
+      type: 'select',
+      device: {
+        id: device.id,
+        name: device.name,
+        type: device.type,
+      },
+    };
+    sendAsyncMessage('presentation-select-result', result);
+  },
+  cancel: function() {
+    let result = {
+      type: 'cancel',
+    };
+    sendAsyncMessage('presentation-select-result', result);
+  },
+};
+
+var testDevice = null;
+
+addMessageListener('setup', function(device_options) {
+  testDevice = new TestPresentationDevice(device_options);
+  manager.QueryInterface(Ci.nsIPresentationDeviceListener).addDevice(testDevice);
+  sendAsyncMessage('setup-complete');
+});
+
+let eventHandler = function(evt) {
+  if (!evt.detail || evt.detail.type !== 'presentation-select-device') {
+    return;
+  }
+
+  sendAsyncMessage('presentation-select-device', evt.detail);
+};
+
+SystemAppProxy.addEventListener('mozChromeEvent', eventHandler);
+
+// need to remove ChromeEvent listener after test finished.
+addMessageListener('teardown', function() {
+  if (testDevice) {
+    manager.removeDevice(testDevice);
+  }
+  SystemAppProxy.removeEventListener('mozChromeEvent', eventHandler);
+});
+
+addMessageListener('trigger-device-prompt', function(request_options) {
+  let request = new TestPresentationRequest(request_options);
+  prompt.promptDeviceSelection(request);
+});
+
+addMessageListener('presentation-select-response', function(detail) {
+  SystemAppProxy._sendCustomEvent('mozContentEvent', detail);
+});
+
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/mochitest/test_presentation_device_prompt.html
@@ -0,0 +1,145 @@
+<!DOCTYPE HTML>
+<html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Presentation Device Selection</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Test for Presentation Device Selection</a>
+<script type="application/javascript;version=1.8">
+
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+var contentEventHandler = null;
+
+var gUrl = SimpleTest.getTestFileURL('presentation_prompt_handler_chrome.js');
+var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+function testSetup() {
+  info('setup for device selection');
+  return new Promise(function(resolve, reject) {
+    let device = {
+      id: 'test-id',
+      name: 'test-name',
+      type: 'test-type',
+    };
+    gScript.addMessageListener('setup-complete', function() {
+      resolve(device);
+    });
+    gScript.sendAsyncMessage('setup', device);
+  });
+}
+
+function testSelected(device) {
+  info('test device selected by user');
+  return new Promise(function(resolve, reject) {
+    let request = {
+      origin: 'test-origin',
+      requestURL: 'test-requestURL',
+    };
+
+    gScript.addMessageListener('presentation-select-device', function contentEventHandler(detail) {
+      gScript.removeMessageListener('presentation-select-device', contentEventHandler);
+      ok(true, 'receive user prompt for device selection');
+      is(detail.origin, request.origin, 'expected origin');
+      is(detail.requestURL, request.requestURL, 'expected requestURL');
+      let response = {
+        id: detail.id,
+        type: 'presentation-select-result',
+        deviceId: device.id,
+      };
+      gScript.sendAsyncMessage('presentation-select-response', response);
+
+      gScript.addMessageListener('presentation-select-result', function resultHandler(result) {
+        gScript.removeMessageListener('presentation-select-result', resultHandler);
+        is(result.type, 'select', 'expect device selected');
+        is(result.device.id, device.id, 'expected device id');
+        is(result.device.name, device.name, 'expected device name');
+        is(result.device.type, device.type, 'expected devcie type');
+        resolve();
+      });
+    });
+
+    gScript.sendAsyncMessage('trigger-device-prompt', request);
+  });
+}
+
+function testSelectedNotExisted() {
+  info('test selected device doesn\'t exist');
+  return new Promise(function(resolve, reject) {
+    gScript.addMessageListener('presentation-select-device', function contentEventHandler(detail) {
+      gScript.removeMessageListener('presentation-select-device', contentEventHandler);
+      ok(true, 'receive user prompt for device selection');
+      let response = {
+        id: detail.id,
+        type: 'presentation-select-deny',
+        deviceId: undefined, // simulate device Id that doesn't exist
+      };
+      gScript.sendAsyncMessage('presentation-select-response', response);
+
+      gScript.addMessageListener('presentation-select-result', function resultHandler(result) {
+        gScript.removeMessageListener('presentation-select-result', resultHandler);
+        is(result.type, 'cancel', 'expect user cancel');
+        resolve();
+      });
+    });
+
+    let request = {
+      origin: 'test-origin',
+      requestURL: 'test-requestURL',
+    };
+    gScript.sendAsyncMessage('trigger-device-prompt', request);
+  });
+}
+
+function testDenied() {
+  info('test denial of device selection');
+  return new Promise(function(resolve, reject) {
+    gScript.addMessageListener('presentation-select-device', function contentEventHandler(detail) {
+      gScript.removeMessageListener('presentation-select-device', contentEventHandler);
+      ok(true, 'receive user prompt for device selection');
+      let response = {
+        id: detail.id,
+        type: 'presentation-select-deny',
+      };
+      gScript.sendAsyncMessage('presentation-select-response', response);
+
+      gScript.addMessageListener('presentation-select-result', function resultHandler(result) {
+        gScript.removeMessageListener('presentation-select-result', resultHandler);
+        is(result.type, 'cancel', 'expect user cancel');
+        resolve();
+      });
+    });
+
+    let request = {
+      origin: 'test-origin',
+      requestURL: 'test-requestURL',
+    };
+    gScript.sendAsyncMessage('trigger-device-prompt', request);
+  });
+}
+
+function runTests() {
+  testSetup()
+  .then(testSelected)
+  .then(testSelectedNotExisted)
+  .then(testDenied)
+  .then(function() {
+    info('test finished, teardown');
+    gScript.sendAsyncMessage('teardown');
+    gScript.destroy();
+    SimpleTest.finish();
+  });
+}
+
+window.addEventListener('load', runTests);
+</script>
+</pre>
+</body>
+</html>
--- a/b2g/config/dolphin/config.json
+++ b/b2g/config/dolphin/config.json
@@ -22,17 +22,18 @@
         "{workdir}/sources.xml",
         "{workdir}/profile.sh",
         ["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
         ["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
         ["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
     ],
     "env": {
         "VARIANT": "user",
-        "MOZILLA_OFFICIAL": "1"
+        "MOZILLA_OFFICIAL": "1",
+        "B2G_UPDATE_CHANNEL": "nightly"
     },
     "b2g_manifest": "dolphin.xml",
     "b2g_manifest_intree": true,
     "additional_source_tarballs": [],
     "gecko_l10n_root": "https://hg.mozilla.org/l10n-central",
     "gaia": {
         "l10n": {
             "vcs": "hgtool",
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -205,16 +205,17 @@
 #ifdef MOZ_WEBSPEECH
 @BINPATH@/components/dom_webspeechrecognition.xpt
 #endif
 @BINPATH@/components/dom_xbl.xpt
 @BINPATH@/components/dom_xpath.xpt
 @BINPATH@/components/dom_xul.xpt
 @BINPATH@/components/dom_time.xpt
 @BINPATH@/components/dom_engineeringmode.xpt
+@BINPATH@/components/dom_presentation.xpt
 @BINPATH@/components/downloads.xpt
 @BINPATH@/components/editor.xpt
 @BINPATH@/components/embed_base.xpt
 @BINPATH@/components/extensions.xpt
 @BINPATH@/components/exthandler.xpt
 @BINPATH@/components/exthelper.xpt
 @BINPATH@/components/fastfind.xpt
 @BINPATH@/components/feeds.xpt
@@ -337,16 +338,19 @@
 @BINPATH@/components/xpcom_xpti.xpt
 @BINPATH@/components/xpconnect.xpt
 @BINPATH@/components/xulapp.xpt
 @BINPATH@/components/xul.xpt
 @BINPATH@/components/xultmpl.xpt
 @BINPATH@/components/zipwriter.xpt
 
 ; JavaScript components
+@BINPATH@/components/RequestSync.manifest
+@BINPATH@/components/RequestSyncManager.js
+@BINPATH@/components/RequestSyncScheduler.js
 @BINPATH@/components/ChromeNotifications.js
 @BINPATH@/components/ChromeNotifications.manifest
 @BINPATH@/components/ConsoleAPI.manifest
 @BINPATH@/components/ConsoleAPIStorage.js
 @BINPATH@/components/BrowserElementParent.manifest
 @BINPATH@/components/BrowserElementParent.js
 @BINPATH@/components/ContactManager.js
 @BINPATH@/components/ContactManager.manifest
@@ -368,17 +372,16 @@
 @BINPATH@/components/fuelApplication.manifest
 @BINPATH@/components/fuelApplication.js
 @BINPATH@/components/WebContentConverter.js
 @BINPATH@/components/BrowserComponents.manifest
 @BINPATH@/components/nsBrowserContentHandler.js
 @BINPATH@/components/nsBrowserGlue.js
 @BINPATH@/components/nsSetDefaultBrowser.manifest
 @BINPATH@/components/nsSetDefaultBrowser.js
-@BINPATH@/components/BrowserPlaces.manifest
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsTryToClose.manifest
 @BINPATH@/components/nsTryToClose.js
 @BINPATH@/components/passwordmgr.manifest
 @BINPATH@/components/nsLoginInfo.js
 @BINPATH@/components/nsLoginManager.js
 @BINPATH@/components/nsLoginManagerPrompter.js
 @BINPATH@/components/NetworkGeolocationProvider.manifest
@@ -396,16 +399,18 @@
 @BINPATH@/components/Downloads.manifest
 @BINPATH@/components/DownloadLegacy.js
 @BINPATH@/components/nsSidebar.manifest
 @BINPATH@/components/nsSidebar.js
 @BINPATH@/components/nsAsyncShutdown.manifest
 @BINPATH@/components/nsAsyncShutdown.js
 @BINPATH@/components/htmlMenuBuilder.js
 @BINPATH@/components/htmlMenuBuilder.manifest
+@BINPATH@/components/PresentationDeviceInfoManager.manifest
+@BINPATH@/components/PresentationDeviceInfoManager.js
 
 ; WiFi, NetworkManager, NetworkStats
 #ifdef MOZ_WIDGET_GONK
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 @BINPATH@/components/DOMWifiP2pManager.js
 @BINPATH@/components/DOMWifiP2pManager.manifest
 @BINPATH@/components/NetworkInterfaceListService.js
@@ -492,17 +497,16 @@
 @BINPATH@/components/nsLivemarkService.js
 @BINPATH@/components/nsTaggingService.js
 @BINPATH@/components/nsPlacesDBFlush.js
 @BINPATH@/components/nsPlacesAutoComplete.manifest
 @BINPATH@/components/nsPlacesAutoComplete.js
 @BINPATH@/components/UnifiedComplete.manifest
 @BINPATH@/components/UnifiedComplete.js
 @BINPATH@/components/nsPlacesExpiration.js
-@BINPATH@/components/PlacesProtocolHandler.js
 @BINPATH@/components/PlacesCategoriesStarter.js
 @BINPATH@/components/nsDefaultCLH.manifest
 @BINPATH@/components/nsDefaultCLH.js
 @BINPATH@/components/nsContentPrefService.manifest
 @BINPATH@/components/nsContentPrefService.js
 @BINPATH@/components/nsContentDispatchChooser.manifest
 @BINPATH@/components/nsContentDispatchChooser.js
 @BINPATH@/components/nsHandlerService.manifest
@@ -856,16 +860,17 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/TelProtocolHandler.js
 @BINPATH@/components/B2GAboutRedirector.js
 @BINPATH@/components/FilePicker.js
 @BINPATH@/components/HelperAppDialog.js
 @BINPATH@/components/DownloadsUI.js
 @BINPATH@/components/InterAppCommUIGlue.js
 @BINPATH@/components/SystemMessageGlue.js
 @BINPATH@/components/B2GAppMigrator.js
+@BINPATH@/components/B2GPresentationDevicePrompt.js
 
 #ifndef MOZ_WIDGET_GONK
 @BINPATH@/components/SimulatorScreen.js
 #endif
 
 @BINPATH@/components/FxAccountsUIGlue.js
 @BINPATH@/components/services_fxaccounts.xpt
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1372,16 +1372,18 @@ pref("devtools.inspector.activeSidebar",
 pref("devtools.inspector.markupPreview", false);
 pref("devtools.inspector.remote", false);
 // Expand pseudo-elements by default in the rule-view
 pref("devtools.inspector.show_pseudo_elements", true);
 // The default size for image preview tooltips in the rule-view/computed-view/markup-view
 pref("devtools.inspector.imagePreviewTooltipSize", 300);
 // Enable user agent style inspection in rule-view
 pref("devtools.inspector.showUserAgentStyles", false);
+// Show all native anonymous content (like controls in <video> tags)
+pref("devtools.inspector.showAllAnonymousContent", false);
 
 // DevTools default color unit
 pref("devtools.defaultColorUnit", "hex");
 
 // Enable the Responsive UI tool
 pref("devtools.responsiveUI.no-reload-notification", false);
 
 // Enable the Debugger
@@ -1776,17 +1778,17 @@ pref("media.gmp-gmpopenh264.provider.ena
 pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/");
 
 #ifdef NIGHTLY_BUILD
 pref("browser.polaris.enabled", false);
 pref("privacy.trackingprotection.ui.enabled", false);
 #endif
 
 #ifdef NIGHTLY_BUILD
-pref("browser.tabs.remote.autostart.1", false);
+pref("browser.tabs.remote.autostart.1", true);
 #endif
 
 // Temporary pref to allow printing in e10s windows on some platforms.
 #ifdef UNIX_BUT_NOT_MAC
 pref("print.enable_e10s_testing", false);
 #else
 pref("print.enable_e10s_testing", true);
 #endif
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -545,16 +545,17 @@
               <menuitem id="menu_pageInfo"
                         accesskey="&pageInfoCmd.accesskey;"
                         label="&pageInfoCmd.label;"
 #ifndef XP_WIN
                         key="key_viewInfo"
 #endif
                         command="View:PageInfo"/>
               <menu id="menu_mirrorTabCmd"
+                    hidden="true"
                     accesskey="&mirrorTabCmd.accesskey;"
                     label="&mirrorTabCmd.label;">
                 <menupopup id="menu_mirrorTab-popup"
                            onpopupshowing="populateMirrorTabMenu(this)"/>
               </menu>
 #ifndef XP_UNIX
               <menuseparator id="prefSep"/>
               <menuitem id="menu_preferences"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2935,17 +2935,17 @@ function getMeOutOfHere() {
 
 function BrowserFullScreen()
 {
   window.fullScreen = !window.fullScreen;
 }
 
 function mirrorShow(popup) {
   let services = CastingApps.getServicesForMirroring();
-  popup.ownerDocument.getElementById("menu_mirrorTabCmd").disabled = !services.length;
+  popup.ownerDocument.getElementById("menu_mirrorTabCmd").hidden = !services.length;
 }
 
 function mirrorMenuItemClicked(event) {
   gBrowser.selectedBrowser.messageManager.sendAsyncMessage("SecondScreen:tab-mirror",
                                                            {service: event.originalTarget._service});
 }
 
 function populateMirrorTabMenu(popup) {
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -244,17 +244,18 @@ Sanitizer.prototype = {
         if (this.range)
           PlacesUtils.history.removeVisitsByTimeframe(this.range[0], this.range[1]);
         else
           PlacesUtils.history.removeAllPages();
 
         try {
           var os = Components.classes["@mozilla.org/observer-service;1"]
                              .getService(Components.interfaces.nsIObserverService);
-          os.notifyObservers(null, "browser:purge-session-history", "");
+          let clearStartingTime = this.range ? String(this.range[0]) : "";
+          os.notifyObservers(null, "browser:purge-session-history", clearStartingTime);
         }
         catch (e) { }
 
         try {
           var predictor = Components.classes["@mozilla.org/network/predictor;1"]
                                     .getService(Components.interfaces.nsINetworkPredictor);
           predictor.reset();
         } catch (e) { }
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
@@ -249,10 +249,25 @@ let tests = [
       PopupNotifications._update();
     },
     onShown: function (popup) {
       checkPopup(popup, this.notifyObj2);
       this.notification1.remove();
       this.notification2.remove();
     },
     onHidden: function(popup) { }
+  },
+  // The anchor icon should be shown for notifications in background windows.
+  { id: "Test#13",
+    run: function() {
+      let notifyObj = new BasicNotification(this.id);
+      notifyObj.options.dismissed = true;
+      let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
+      whenDelayedStartupFinished(win, function() {
+        showNotification(notifyObj);
+        let anchor = document.getElementById("default-notification-icon");
+        is(anchor.getAttribute("showing"), "true", "the anchor is shown");
+        win.close();
+        goNext();
+      });
+    }
   }
 ];
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -274,16 +274,18 @@ loop.store.ActiveRoomStore = (function()
       this.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
     },
 
     /**
      * Handles the action that signifies when media permission has been
      * granted and starts joining the room.
      */
     gotMediaPermission: function() {
+      this.setStoreState({roomState: ROOM_STATES.JOINING});
+
       this._mozLoop.rooms.join(this._storeState.roomToken,
         function(error, responseData) {
           if (error) {
             this.dispatchAction(new sharedActions.RoomFailure({error: error}));
             return;
           }
 
           this.dispatchAction(new sharedActions.JoinedRoom({
@@ -456,17 +458,18 @@ loop.store.ActiveRoomStore = (function()
 
       this._sdkDriver.disconnectSession();
 
       if (this._timeout) {
         clearTimeout(this._timeout);
         delete this._timeout;
       }
 
-      if (this._storeState.roomState === ROOM_STATES.JOINED ||
+      if (this._storeState.roomState === ROOM_STATES.JOINING ||
+          this._storeState.roomState === ROOM_STATES.JOINED ||
           this._storeState.roomState === ROOM_STATES.SESSION_CONNECTED ||
           this._storeState.roomState === ROOM_STATES.HAS_PARTICIPANTS) {
         this._mozLoop.rooms.leave(this._storeState.roomToken,
           this._storeState.sessionToken);
       }
 
       this.setStoreState({roomState: nextState || ROOM_STATES.ENDED});
     },
--- a/browser/components/loop/content/shared/js/feedbackViews.js
+++ b/browser/components/loop/content/shared/js/feedbackViews.js
@@ -70,17 +70,17 @@ loop.shared.views.FeedbackView = (functi
       return {pending: false};
     },
 
     _getCategories: function() {
       return {
         audio_quality: l10n.get("feedback_category_audio_quality"),
         video_quality: l10n.get("feedback_category_video_quality"),
         disconnected : l10n.get("feedback_category_was_disconnected"),
-        confusing:     l10n.get("feedback_category_confusing"),
+        confusing:     l10n.get("feedback_category_confusing2"),
         other:         l10n.get("feedback_category_other2")
       };
     },
 
     _getCategoryFields: function() {
       var categories = this._getCategories();
       return Object.keys(categories).map(function(category, key) {
         return (
@@ -137,17 +137,17 @@ loop.shared.views.FeedbackView = (functi
         happy: false,
         category: this.state.category,
         description: this.state.description
       }));
     },
 
     render: function() {
       return (
-        React.createElement(FeedbackLayout, {title: l10n.get("feedback_what_makes_you_sad"), 
+        React.createElement(FeedbackLayout, {title: l10n.get("feedback_category_list_heading"), 
                         reset: this.props.reset}, 
           React.createElement("form", {onSubmit: this.handleFormSubmit}, 
             this._getCategoryFields(), 
             React.createElement("p", null, 
               React.createElement("input", {type: "text", ref: "description", name: "description", 
                 className: "feedback-description", 
                 onChange: this.handleDescriptionFieldChange, 
                 value: this.state.description, 
--- a/browser/components/loop/content/shared/js/feedbackViews.jsx
+++ b/browser/components/loop/content/shared/js/feedbackViews.jsx
@@ -70,17 +70,17 @@ loop.shared.views.FeedbackView = (functi
       return {pending: false};
     },
 
     _getCategories: function() {
       return {
         audio_quality: l10n.get("feedback_category_audio_quality"),
         video_quality: l10n.get("feedback_category_video_quality"),
         disconnected : l10n.get("feedback_category_was_disconnected"),
-        confusing:     l10n.get("feedback_category_confusing"),
+        confusing:     l10n.get("feedback_category_confusing2"),
         other:         l10n.get("feedback_category_other2")
       };
     },
 
     _getCategoryFields: function() {
       var categories = this._getCategories();
       return Object.keys(categories).map(function(category, key) {
         return (
@@ -137,17 +137,17 @@ loop.shared.views.FeedbackView = (functi
         happy: false,
         category: this.state.category,
         description: this.state.description
       }));
     },
 
     render: function() {
       return (
-        <FeedbackLayout title={l10n.get("feedback_what_makes_you_sad")}
+        <FeedbackLayout title={l10n.get("feedback_category_list_heading")}
                         reset={this.props.reset}>
           <form onSubmit={this.handleFormSubmit}>
             {this._getCategoryFields()}
             <p>
               <input type="text" ref="description" name="description"
                 className="feedback-description"
                 onChange={this.handleDescriptionFieldChange}
                 value={this.state.description}
--- a/browser/components/loop/content/shared/js/roomStates.js
+++ b/browser/components/loop/content/shared/js/roomStates.js
@@ -11,16 +11,18 @@ loop.store.ROOM_STATES = {
     // The initial state of the room
     INIT: "room-init",
     // The store is gathering the room data
     GATHER: "room-gather",
     // The store has got the room data
     READY: "room-ready",
     // Obtaining media from the user
     MEDIA_WAIT: "room-media-wait",
+    // Joining the room is taking place
+    JOINING: "room-joining",
     // The room is known to be joined on the loop-server
     JOINED: "room-joined",
     // The room is connected to the sdk server.
     SESSION_CONNECTED: "room-session-connected",
     // There are participants in the room.
     HAS_PARTICIPANTS: "room-has-participants",
     // There was an issue with the room
     FAILED: "room-failed",
--- a/browser/components/loop/content/shared/js/websocket.js
+++ b/browser/components/loop/content/shared/js/websocket.js
@@ -82,17 +82,19 @@ loop.CallConnectionWebSocket = (function
 
     /**
      * Closes the websocket. This shouldn't be the normal action as the server
      * will normally close the socket. Only in bad error cases, or where we need
      * to close the socket just before closing the window (to avoid an error)
      * should we call this.
      */
     close: function() {
-      this.socket.close();
+      if (this.socket) {
+        this.socket.close();
+      }
     },
 
     _clearConnectionFlags: function() {
       clearTimeout(this.connectDetails.timeout);
       delete this.connectDetails;
     },
 
     /**
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -89,16 +89,17 @@ loop.standaloneRoomViews = (function(moz
           return (
             React.createElement("div", {className: "room-inner-info-area"}, 
               React.createElement("p", {className: "prompt-media-message"}, 
                 msg
               )
             )
           );
         }
+        case ROOM_STATES.JOINING:
         case ROOM_STATES.JOINED:
         case ROOM_STATES.SESSION_CONNECTED: {
           return (
             React.createElement("div", {className: "room-inner-info-area"}, 
               React.createElement("p", {className: "empty-room-message"}, 
                 mozL10n.get("rooms_only_occupant_label")
               )
             )
@@ -289,17 +290,17 @@ loop.standaloneRoomViews = (function(moz
       document.body.classList.add("is-standalone-room");
     },
 
     componentWillUnmount: function() {
       this.stopListening(this.props.activeRoomStore);
     },
 
     /**
-     * Watches for when we transition to JOINED room state, so we can request
+     * Watches for when we transition to MEDIA_WAIT room state, so we can request
      * user media access.
      *
      * @param  {Object} nextProps (Unused)
      * @param  {Object} nextState Next state object.
      */
     componentWillUpdate: function(nextProps, nextState) {
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -89,16 +89,17 @@ loop.standaloneRoomViews = (function(moz
           return (
             <div className="room-inner-info-area">
               <p className="prompt-media-message">
                 {msg}
               </p>
             </div>
           );
         }
+        case ROOM_STATES.JOINING:
         case ROOM_STATES.JOINED:
         case ROOM_STATES.SESSION_CONNECTED: {
           return (
             <div className="room-inner-info-area">
               <p className="empty-room-message">
                 {mozL10n.get("rooms_only_occupant_label")}
               </p>
             </div>
@@ -289,17 +290,17 @@ loop.standaloneRoomViews = (function(moz
       document.body.classList.add("is-standalone-room");
     },
 
     componentWillUnmount: function() {
       this.stopListening(this.props.activeRoomStore);
     },
 
     /**
-     * Watches for when we transition to JOINED room state, so we can request
+     * Watches for when we transition to MEDIA_WAIT room state, so we can request
      * user media access.
      *
      * @param  {Object} nextProps (Unused)
      * @param  {Object} nextState Next state object.
      */
     componentWillUpdate: function(nextProps, nextState) {
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
--- a/browser/components/loop/standalone/content/l10n/en-US/loop.properties
+++ b/browser/components/loop/standalone/content/l10n/en-US/loop.properties
@@ -63,22 +63,22 @@ vendor_alttext={{vendorShortname}} logo
 call_url_creation_date_label=(from {{call_url_creation_date}})
 call_progress_getting_media_description={{clientShortname}} requires access to your camera and microphone.
 call_progress_getting_media_title=Waiting for media…
 call_progress_connecting_description=Connecting…
 call_progress_ringing_description=Ringing…
 fxos_app_needed=Please install the {{fxosAppName}} app from the Firefox Marketplace.
 
 feedback_call_experience_heading2=How was your conversation?
-feedback_what_makes_you_sad=What makes you sad?
 feedback_thank_you_heading=Thank you for your feedback!
+feedback_category_list_heading=What made you sad?
 feedback_category_audio_quality=Audio quality
 feedback_category_video_quality=Video quality
 feedback_category_was_disconnected=Was disconnected
-feedback_category_confusing=Confusing
+feedback_category_confusing2=Confusing controls
 feedback_category_other2=Other
 feedback_custom_category_text_placeholder=What went wrong?
 feedback_submit_button=Submit
 feedback_back_button=Back
 ## LOCALIZATION NOTE (feedback_window_will_close_in2):
 ## Gaia l10n format; see https://github.com/mozilla-b2g/gaia/blob/f108c706fae43cd61628babdd9463e7695b2496e/apps/email/locales/email.en-US.properties#L387
 ## In this item, don't translate the part between {{..}}
 feedback_window_will_close_in2={[ plural(countdown) ]}
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -350,16 +350,22 @@ describe("loop.store.ActiveRoomStore", f
     });
   });
 
   describe("#gotMediaPermission", function() {
     beforeEach(function() {
       store.setStoreState({roomToken: "tokenFake"});
     });
 
+    it("should set the room state to JOINING", function() {
+      store.gotMediaPermission();
+
+      expect(store.getStoreState().roomState).eql(ROOM_STATES.JOINING);
+    });
+
     it("should call rooms.join on mozLoop", function() {
       store.gotMediaPermission();
 
       sinon.assert.calledOnce(fakeMozLoop.rooms.join);
       sinon.assert.calledWith(fakeMozLoop.rooms.join, "tokenFake");
     });
 
     it("should dispatch `JoinedRoom` on success", function() {
@@ -672,16 +678,27 @@ describe("loop.store.ActiveRoomStore", f
     it("should call mozLoop.rooms.leave", function() {
       store.windowUnload();
 
       sinon.assert.calledOnce(fakeMozLoop.rooms.leave);
       sinon.assert.calledWithExactly(fakeMozLoop.rooms.leave,
         "fakeToken", "1627384950");
     });
 
+    it("should call mozLoop.rooms.leave if the room state is JOINING",
+      function() {
+        store.setStoreState({roomState: ROOM_STATES.JOINING});
+
+        store.windowUnload();
+
+        sinon.assert.calledOnce(fakeMozLoop.rooms.leave);
+        sinon.assert.calledWithExactly(fakeMozLoop.rooms.leave,
+          "fakeToken", "1627384950");
+      });
+
     it("should set the state to CLOSING", function() {
       store.windowUnload();
 
       expect(store._storeState.roomState).eql(ROOM_STATES.CLOSING);
     });
   });
 
   describe("#leaveRoom", function() {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2464,29 +2464,29 @@ let DefaultBrowserCheck = {
       this._notification = notificationBox.appendNotification(promptMessage, "default-browser",
                                                               iconURL, priority, buttons,
                                                               callback);
     } else {
       // Modal prompt
       let promptTitle = shellBundle.getString("setDefaultBrowserTitle");
       let promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
                                                          [brandShortName]);
-      let dontAskLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
-                                                        [brandShortName]);
+      let askLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
+                                                    [brandShortName]);
 
       let ps = Services.prompt;
-      let dontAsk = { value: false };
+      let shouldAsk = { value: true };
       let buttonFlags = (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
                         (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1) +
                         ps.BUTTON_POS_0_DEFAULT;
       let rv = ps.confirmEx(win, promptTitle, promptMessage, buttonFlags,
-                            yesButton, notNowButton, null, dontAskLabel, dontAsk);
+                            yesButton, notNowButton, null, askLabel, shouldAsk);
       if (rv == 0) {
         this.setAsDefault();
-      } else if (dontAsk.value) {
+      } else if (!shouldAsk.value) {
         ShellService.shouldCheckDefaultBrowser = false;
       }
     }
   },
 
   _onNotificationEvent: function(eventType) {
     if (eventType == "removed") {
       let doc = this._notification.ownerDocument;
@@ -2497,17 +2497,17 @@ let DefaultBrowserCheck = {
     }
   },
 };
 
 #ifdef E10S_TESTING_ONLY
 let E10SUINotification = {
   // Increase this number each time we want to roll out an
   // e10s testing period to Nightly users.
-  CURRENT_NOTICE_COUNT: 3,
+  CURRENT_NOTICE_COUNT: 4,
   CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
   PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
 
   checkStatus: function() {
     let skipE10sChecks = false;
     try {
       skipE10sChecks = (UpdateChannel.get() != "nightly") ||
                        Services.prefs.getBoolPref("browser.tabs.remote.autostart.disabled-because-using-a11y");
deleted file mode 100644
--- a/browser/components/places/BrowserPlaces.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-component {6bcb9bde-9018-4443-a071-c32653469597} PlacesProtocolHandler.js
-contract @mozilla.org/network/protocol;1?name=place {6bcb9bde-9018-4443-a071-c32653469597}
deleted file mode 100644
--- a/browser/components/places/PlacesProtocolHandler.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
- * vim: sw=2 ts=2 sts=2 et
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-Components.utils.import("resource://gre/modules/NetUtil.jsm");
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const SCHEME = "place";
-const URL = "chrome://browser/content/places/content-ui/controller.xhtml";
-
-function PlacesProtocolHandler() {}
-
-PlacesProtocolHandler.prototype = {
-  scheme: SCHEME,
-  defaultPort: -1,
-  protocolFlags: Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
-                 Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE |
-                 Ci.nsIProtocolHandler.URI_NORELATIVE |
-                 Ci.nsIProtocolHandler.URI_NOAUTH,
-
-  newURI: function PPH_newURI(aSpec, aOriginCharset, aBaseUri) {
-    let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
-    uri.spec = aSpec;
-    return uri;
-  },
-
-  newChannel: function PPH_newChannel(aUri) {
-    let chan = NetUtil.newChannel(URL);
-    chan.originalURI = aUri;
-    return chan;
-  },
-
-  allowPort: function PPH_allowPort(aPort, aScheme) {
-    return false;
-  },
-
-  QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIProtocolHandler
-  ]),
-
-  classID: Components.ID("{6bcb9bde-9018-4443-a071-c32653469597}")
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PlacesProtocolHandler]);
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -821,22 +821,22 @@ this.PlacesUIUtils = {
     this._openNodeIn(aNode, window.whereToOpenLink(aEvent, false, true), window);
   },
 
   /**
    * Loads the node's URL in the appropriate tab or window or as a
    * web panel.
    * see also openUILinkIn
    */
-  openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView) {
+  openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView, aPrivate) {
     let window = aView.ownerWindow;
-    this._openNodeIn(aNode, aWhere, window);
+    this._openNodeIn(aNode, aWhere, window, aPrivate);
   },
 
-  _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow) {
+  _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow, aPrivate=false) {
     if (aNode && PlacesUtils.nodeIsURI(aNode) &&
         this.checkURLSecurity(aNode, aWindow)) {
       let isBookmark = PlacesUtils.nodeIsBookmark(aNode);
 
       if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
         if (isBookmark)
           this.markPageAsFollowedBookmark(aNode.uri);
         else
@@ -850,18 +850,20 @@ this.PlacesUIUtils = {
                        .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) {
           let browserWin = this._getTopBrowserWin();
           if (browserWin) {
             browserWin.openWebPanel(aNode.title, aNode.uri);
             return;
           }
         }
       }
+
       aWindow.openUILinkIn(aNode.uri, aWhere, {
-        inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground")
+        inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground"),
+        private: aPrivate,
       });
     }
   },
 
   /**
    * Helper for guessing scheme from an url string.
    * Used to avoid nsIURI overhead in frequently called UI functions.
    *
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -2,16 +2,18 @@
 /* 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/. */
 
 XPCOMUtils.defineLazyModuleGetter(this, "ForgetAboutSite",
                                   "resource://gre/modules/ForgetAboutSite.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 // XXXmano: we should move most/all of these constants to PlacesUtils
 const ORGANIZER_ROOT_BOOKMARKS = "place:folder=BOOKMARKS_MENU&excludeItems=1&queryType=1";
 
 // No change to the view, preserve current selection
 const RELOAD_ACTION_NOTHING = 0;
 // Inserting items new to the view, select the inserted rows
 const RELOAD_ACTION_INSERT = 1;
@@ -173,16 +175,17 @@ PlacesController.prototype = {
       if (this._view.selType != "single") {
         let rootNode = this._view.result.root;
         if (rootNode.containerOpen && rootNode.childCount > 0)
           return true;
       }
       return false;
     case "placesCmd_open":
     case "placesCmd_open:window":
+    case "placesCmd_open:privatewindow":
     case "placesCmd_open:tab":
       var selectedNode = this._view.selectedNode;
       return selectedNode && PlacesUtils.nodeIsURI(selectedNode);
     case "placesCmd_new:folder":
       return this._canInsert();
     case "placesCmd_new:bookmark":
       return this._canInsert();
     case "placesCmd_new:separator":
@@ -258,16 +261,19 @@ PlacesController.prototype = {
       this.selectAll();
       break;
     case "placesCmd_open":
       PlacesUIUtils.openNodeIn(this._view.selectedNode, "current", this._view);
       break;
     case "placesCmd_open:window":
       PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view);
       break;
+    case "placesCmd_open:privatewindow":
+      PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view, true);
+      break;
     case "placesCmd_open:tab":
       PlacesUIUtils.openNodeIn(this._view.selectedNode, "tab", this._view);
       break;
     case "placesCmd_new:folder":
       this.newItem("folder");
       break;
     case "placesCmd_new:bookmark":
       this.newItem("bookmark");
@@ -596,17 +602,20 @@ PlacesController.prototype = {
     var visibleItemsBeforeSep = false;
     var usableItemCount = 0;
     for (var i = 0; i < aPopup.childNodes.length; ++i) {
       var item = aPopup.childNodes[i];
       if (item.localName != "menuseparator") {
         // We allow pasting into tag containers, so special case that.
         var hideIfNoIP = item.getAttribute("hideifnoinsertionpoint") == "true" &&
                          noIp && !(ip && ip.isTag && item.id == "placesContext_paste");
-        var shouldHideItem = hideIfNoIP || !this._shouldShowMenuItem(item, metadata);
+        var hideIfPrivate = item.getAttribute("hideifprivatebrowsing") == "true" &&
+                            PrivateBrowsingUtils.isWindowPrivate(window);
+        var shouldHideItem = hideIfNoIP || hideIfPrivate ||
+                             !this._shouldShowMenuItem(item, metadata);
         item.hidden = item.disabled = shouldHideItem;
 
         if (!item.hidden) {
           visibleItemsBeforeSep = true;
           usableItemCount++;
 
           // Show the separator above the menu-item if any
           if (separator) {
@@ -1685,16 +1694,17 @@ function goUpdatePlacesCommands() {
   var placesController = doGetPlacesControllerForCommand("placesCmd_open");
   function updatePlacesCommand(aCommand) {
     goSetCommandEnabled(aCommand, placesController &&
                                   placesController.isCommandEnabled(aCommand));
   }
 
   updatePlacesCommand("placesCmd_open");
   updatePlacesCommand("placesCmd_open:window");
+  updatePlacesCommand("placesCmd_open:privatewindow");
   updatePlacesCommand("placesCmd_open:tab");
   updatePlacesCommand("placesCmd_new:folder");
   updatePlacesCommand("placesCmd_new:bookmark");
   updatePlacesCommand("placesCmd_new:separator");
   updatePlacesCommand("placesCmd_show:info");
   updatePlacesCommand("placesCmd_moveBookmarks");
   updatePlacesCommand("placesCmd_reload");
   updatePlacesCommand("placesCmd_sortBy:name");
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -872,26 +872,31 @@ var gEditItemOverlay = {
                                   this._folderTree.columns.getFirstColumn());
   },
 
   // nsIDOMEventListener
   handleEvent: function EIO_nsIDOMEventListener(aEvent) {
     switch (aEvent.type) {
     case "CheckboxStateChange":
       // Update the tags field when items are checked/unchecked in the listbox
-      var tags = this._getTagsArrayFromTagField();
+      let tags = this._getTagsArrayFromTagField();
+      let tagCheckbox = aEvent.target;
+
+      let curTagIndex = tags.indexOf(tagCheckbox.label);
 
-      if (aEvent.target.checked) {
-        if (tags.indexOf(aEvent.target.label) == -1)
-          tags.push(aEvent.target.label);
+      let tagsSelector = this._element("tagsSelector");
+      tagsSelector.selectedItem = tagCheckbox;
+
+      if (tagCheckbox.checked) {
+        if (curTagIndex == -1)
+          tags.push(tagCheckbox.label);
       }
       else {
-        var indexOfItem = tags.indexOf(aEvent.target.label);
-        if (indexOfItem != -1)
-          tags.splice(indexOfItem, 1);
+        if (curTagIndex != -1)
+          tags.splice(curTagIndex, 1);
       }
       this._element("tagsField").value = tags.join(", ");
       this._updateTags();
       break;
     case "blur":
       let replaceFn = (str, firstLetter) => firstLetter.toUpperCase();
       let nodeName = aEvent.target.id.replace(/editBMPanel_(\w)/, replaceFn);
       this["on" + nodeName + "Blur"]();
--- a/browser/components/places/content/placesOverlay.xul
+++ b/browser/components/places/content/placesOverlay.xul
@@ -49,16 +49,18 @@
   <commandset id="placesCommands"
               commandupdater="true"
               events="focus,sort,places"
               oncommandupdate="goUpdatePlacesCommands();">
     <command id="placesCmd_open"
              oncommand="goDoPlacesCommand('placesCmd_open');"/>
     <command id="placesCmd_open:window"
              oncommand="goDoPlacesCommand('placesCmd_open:window');"/>
+    <command id="placesCmd_open:privatewindow"
+             oncommand="goDoPlacesCommand('placesCmd_open:privatewindow');"/>
     <command id="placesCmd_open:tab"
              oncommand="goDoPlacesCommand('placesCmd_open:tab');"/>
 
     <command id="placesCmd_new:bookmark"
              oncommand="goDoPlacesCommand('placesCmd_new:bookmark');"/>
     <command id="placesCmd_new:folder"
              oncommand="goDoPlacesCommand('placesCmd_new:folder');"/>
     <command id="placesCmd_new:separator"
@@ -124,16 +126,23 @@
               selectiontype="multiple"
               selection="link"/>
     <menuitem id="placesContext_open:newwindow"
               command="placesCmd_open:window"
               label="&cmd.open_window.label;"
               accesskey="&cmd.open_window.accesskey;"
               selectiontype="single"
               selection="link"/>
+    <menuitem id="placesContext_open:newprivatewindow"
+              command="placesCmd_open:privatewindow"
+              label="&cmd.open_private_window.label;"
+              accesskey="&cmd.open_private_window.accesskey;"
+              selectiontype="single"
+              selection="link"
+              hideifprivatebrowsing="true"/>
     <menuseparator id="placesContext_openSeparator"/>
     <menuitem id="placesContext_new:bookmark"
               command="placesCmd_new:bookmark"
               label="&cmd.new_bookmark.label;"
               accesskey="&cmd.new_bookmark.accesskey;"
               selectiontype="any"
               hideifnoinsertionpoint="true"/>
     <menuitem id="placesContext_new:folder"
--- a/browser/components/places/moz.build
+++ b/browser/components/places/moz.build
@@ -5,16 +5,11 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini']
 BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
 
 JAR_MANIFESTS += ['jar.mn']
 
-EXTRA_COMPONENTS += [
-    'BrowserPlaces.manifest',
-    'PlacesProtocolHandler.js',
-]
-
 EXTRA_PP_JS_MODULES += [
     'PlacesUIUtils.jsm',
 ]
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -19,16 +19,22 @@ var gSearchPane = {
         document.location.hash = "";
       return;
     }
 
     gEngineView = new EngineView(new EngineStore());
     document.getElementById("engineList").view = gEngineView;
     this.buildDefaultEngineDropDown();
 
+    window.addEventListener("click", this, false);
+    window.addEventListener("command", this, false);
+    window.addEventListener("dragstart", this, false);
+    window.addEventListener("keypress", this, false);
+    window.addEventListener("select", this, false);
+
     Services.obs.addObserver(this, "browser-search-engine-modified", false);
     window.addEventListener("unload", () => {
       Services.obs.removeObserver(this, "browser-search-engine-modified", false);
     });
   },
 
   buildDefaultEngineDropDown: function() {
     // This is called each time something affects the list of engines.
@@ -58,16 +64,59 @@ var gSearchPane = {
         item.setAttribute("image", uri);
       }
       item.engine = e;
       if (e.name == currentEngine)
         list.selectedItem = item;
     });
   },
 
+  handleEvent: function(aEvent) {
+    switch (aEvent.type) {
+      case "click":
+        if (aEvent.target.id == "addEngines" && aEvent.button == 0) {
+          Services.wm.getMostRecentWindow('navigator:browser')
+                     .BrowserSearch.loadAddEngines();
+        }
+        break;
+      case "command":
+        switch (aEvent.target.id) {
+          case "":
+            if (aEvent.target.parentNode &&
+                aEvent.target.parentNode.parentNode &&
+                aEvent.target.parentNode.parentNode.id == "defaultEngine") {
+              gSearchPane.setDefaultEngine();
+            }
+            break;
+          case "restoreDefaultSearchEngines":
+            gSearchPane.onRestoreDefaults();
+            break;
+          case "removeEngineButton":
+            gSearchPane.remove();
+            break;
+        }
+        break;
+      case "dragstart":
+        if (aEvent.target.id == "engineChildren") {
+          onDragEngineStart(aEvent);
+        }
+        break;
+      case "keypress":
+        if (aEvent.target.id == "engineList") {
+          gSearchPane.onTreeKeyPress(aEvent);
+        }
+        break;
+      case "select":
+        if (aEvent.target.id == "engineList") {
+          gSearchPane.onTreeSelect();
+        }
+        break;
+    }
+  },
+
   observe: function(aEngine, aTopic, aVerb) {
     if (aTopic == "browser-search-engine-modified") {
       aEngine.QueryInterface(Components.interfaces.nsISearchEngine);
       switch (aVerb) {
       case "engine-added":
         gEngineView._engineStore.addEngine(aEngine);
         gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
         gSearchPane.buildDefaultEngineDropDown();
--- a/browser/components/preferences/in-content/search.xul
+++ b/browser/components/preferences/in-content/search.xul
@@ -23,53 +23,50 @@
           data-category="paneSearch">
       <label class="header-name">&paneSearch.title;</label>
     </hbox>
 
     <!-- Default Search Engine -->
     <groupbox id="defaultEngineGroup" align="start" data-category="paneSearch">
       <caption label="&defaultSearchEngine.label;"/>
       <label>&chooseYourDefaultSearchEngine.label;</label>
-      <menulist id="defaultEngine" oncommand="gSearchPane.setDefaultEngine();">
+      <menulist id="defaultEngine">
         <menupopup/>
       </menulist>
       <checkbox id="suggestionsInSearchFieldsCheckbox"
                 label="&provideSearchSuggestions.label;"
                 accesskey="&provideSearchSuggestions.accesskey;"
                 preference="browser.search.suggest.enabled"/>
     </groupbox>
 
     <groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch">
       <caption label="&oneClickSearchEngines.label;"/>
       <label>&chooseWhichOneToDisplay.label;</label>
 
       <tree id="engineList" flex="1" rows="8" hidecolumnpicker="true" editable="true"
-            seltype="single" onselect="gSearchPane.onTreeSelect();"
-            onkeypress="gSearchPane.onTreeKeyPress(event);">
-        <treechildren id="engineChildren" flex="1"
-                      ondragstart="onDragEngineStart(event);"/>
+            seltype="single">
+        <treechildren id="engineChildren" flex="1"/>
         <treecols>
-          <treecol id="engineShown" type="checkbox" style="min-width: 26px;" editable="true"/>
+          <treecol id="engineShown" type="checkbox" editable="true"/>
           <treecol id="engineName" flex="4" label="&engineNameColumn.label;"/>
           <treecol id="engineKeyword" flex="1" label="&engineKeywordColumn.label;" editable="true"/>
         </treecols>
       </tree>
 
       <hbox>
         <button id="restoreDefaultSearchEngines"
                 label="&restoreDefaultSearchEngines.label;"
                 accesskey="&restoreDefaultSearchEngines.accesskey;"
-                oncommand="gSearchPane.onRestoreDefaults();"/>
+                />
         <spacer flex="1"/>
         <button id="removeEngineButton"
                 label="&removeEngine.label;"
                 accesskey="&removeEngine.accesskey;"
                 disabled="true"
-                oncommand="gSearchPane.remove();"/>
+                />
       </hbox>
 
       <separator class="thin"/>
 
-      <hbox pack="start" style="margin-bottom: 1em">
-        <label id="addEngines" class="text-link" value="&addMoreSearchEngines.label;"
-               onclick="if (event.button == 0) { Services.wm.getMostRecentWindow('navigator:browser').BrowserSearch.loadAddEngines(); }"/>
+      <hbox id="addEnginesBox" pack="start">
+        <label id="addEngines" class="text-link" value="&addMoreSearchEngines.label;"/>
       </hbox>
     </groupbox>
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -5,21 +5,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Used to detect minification for automatic pretty printing
 const SAMPLE_SIZE = 50; // no of lines
 const INDENT_COUNT_THRESHOLD = 5; // percentage
 const CHARACTER_LIMIT = 250; // line character limit
 
-// Maps known URLs to friendly source group names
+// Maps known URLs to friendly source group names and put them at the
+// bottom of source list.
 const KNOWN_SOURCE_GROUPS = {
   "Add-on SDK": "resource://gre/modules/commonjs/",
 };
 
+KNOWN_SOURCE_GROUPS[L10N.getStr("evalGroupLabel")] = "eval";
+
 /**
  * Functions handling the sources UI.
  */
 function SourcesView() {
   dumpn("SourcesView was instantiated");
 
   this.togglePrettyPrint = this.togglePrettyPrint.bind(this);
   this.toggleBlackBoxing = this.toggleBlackBoxing.bind(this);
@@ -165,24 +168,32 @@ SourcesView.prototype = Heritage.extend(
       }
     });
   },
 
   _parseUrl: function(aSource) {
     let fullUrl = aSource.url || aSource.introductionUrl;
     let url = fullUrl.split(" -> ").pop();
     let label = aSource.addonPath ? aSource.addonPath : SourceUtils.getSourceLabel(url);
+    let group;
 
     if (!aSource.url && aSource.introductionUrl) {
-      label += ' > eval';
+      label += ' > ' + aSource.introductionType;
+      group = L10N.getStr("evalGroupLabel");
+    }
+    else if(aSource.addonID) {
+      group = aSource.addonID;
+    }
+    else {
+      group = SourceUtils.getSourceGroup(url);
     }
 
     return {
       label: label,
-      group: aSource.addonID ? aSource.addonID : SourceUtils.getSourceGroup(url),
+      group: group,
       unicodeUrl: NetworkHelper.convertToUnicode(unescape(fullUrl))
     };
   },
 
   /**
    * Adds a breakpoint to this sources container.
    *
    * @param object aBreakpointClient
--- a/browser/devtools/debugger/test/browser_dbg_sources-eval-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_sources-eval-01.js
@@ -23,12 +23,17 @@ function test() {
       is(gSources.values.length, 1, "Should have 1 source");
 
       let newSource = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE);
       callInTab(gTab, "evalSource");
       yield newSource;
 
       is(gSources.values.length, 2, "Should have 2 sources");
 
+      let item = gSources.getItemForAttachment(e => e.label.indexOf("> eval") !== -1);
+      ok(item, "Source label is incorrect.");
+      is(item.attachment.group, gDebugger.L10N.getStr('evalGroupLabel'),
+         'Source group is incorrect');
+
       yield closeDebuggerAndFinish(gPanel);
     });
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_sources-eval-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_sources-eval-02.js
@@ -27,17 +27,18 @@ function test() {
       let newSource = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE);
       callInTab(gTab, "evalSourceWithSourceURL");
       yield newSource;
 
       is(gSources.values.length, 2, "Should have 2 sources");
 
       let item = gSources.getItemForAttachment(e => e.label == "bar.js");
       ok(item, "Source label is incorrect.");
-      ok(item.attachment.group === 'http://example.com', 'Source group is incorrect');
+      is(item.attachment.group, 'http://example.com',
+         'Source group is incorrect');
 
       let shown = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN);
       gSources.selectedItem = item;
       yield shown;
 
       ok(gEditor.getText().indexOf('bar = function() {') === 0,
          'Correct source is shown');
 
--- a/browser/devtools/framework/toolbox-process-window.js
+++ b/browser/devtools/framework/toolbox-process-window.js
@@ -46,16 +46,17 @@ let connect = Task.async(function*() {
   });
 });
 
 // Certain options should be toggled since we can assume chrome debugging here
 function setPrefDefaults() {
   Services.prefs.setBoolPref("devtools.inspector.showUserAgentStyles", true);
   Services.prefs.setBoolPref("devtools.profiler.ui.show-platform-data", true);
   Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", false);
+  Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
 }
 
 window.addEventListener("load", function() {
   let cmdClose = document.getElementById("toolbox-cmd-close");
   cmdClose.addEventListener("command", onCloseCommand);
   setPrefDefaults();
   connect().catch(Cu.reportError);
 });
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -1498,17 +1498,19 @@ Toolbox.prototype = {
   /**
    * Initialize the inspector/walker/selection/highlighter fronts.
    * Returns a promise that resolves when the fronts are initialized
    */
   initInspector: function() {
     if (!this._initInspector) {
       this._initInspector = Task.spawn(function*() {
         this._inspector = InspectorFront(this._target.client, this._target.form);
-        this._walker = yield this._inspector.getWalker();
+        this._walker = yield this._inspector.getWalker(
+          {showAllAnonymousContent: Services.prefs.getBoolPref("devtools.inspector.showAllAnonymousContent")}
+        );
         this._selection = new Selection(this._walker);
 
         if (this.highlighterUtils.isRemoteHighlightable()) {
           this.walker.on("highlighter-ready", this._highlighterReady);
           this.walker.on("highlighter-hide", this._highlighterHidden);
 
           let autohide = !gDevTools.testing;
           this._highlighter = yield this._inspector.getHighlighter(autohide);
--- a/browser/devtools/markupview/test/browser.ini
+++ b/browser/devtools/markupview/test/browser.ini
@@ -29,16 +29,17 @@ support-files =
   lib_jquery_1.7_min.js
   lib_jquery_1.11.1_min.js
   lib_jquery_2.1.1_min.js
 
 [browser_markupview_anonymous_01.js]
 [browser_markupview_anonymous_02.js]
 skip-if = e10s # scratchpad.xul is not loading in e10s window
 [browser_markupview_anonymous_03.js]
+[browser_markupview_anonymous_04.js]
 [browser_markupview_copy_image_data.js]
 [browser_markupview_css_completion_style_attribute.js]
 [browser_markupview_events.js]
 skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
 [browser_markupview_events-overflow.js]
 skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
 [browser_markupview_events_jquery_1.0.js]
 skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
--- a/browser/devtools/markupview/test/browser_markupview_anonymous_01.js
+++ b/browser/devtools/markupview/test/browser_markupview_anonymous_01.js
@@ -22,9 +22,23 @@ add_task(function*() {
 
   info ("Checking the normal child element");
   let span = children.nodes[1];
   yield isEditingMenuEnabled(span, inspector);
 
   info ("Checking the ::after pseudo element");
   let after = children.nodes[2];
   yield isEditingMenuDisabled(after, inspector);
+
+  let native = yield getNodeFront("#native", inspector);
+
+  // Markup looks like: <div><video controls /></div>
+  let nativeChildren = yield inspector.walker.children(native);
+  is (nativeChildren.nodes.length, 1, "Children returned from walker");
+
+  info ("Checking the video element");
+  let video = nativeChildren.nodes[0];
+  ok (!video.isAnonymous, "<video> is not anonymous");
+
+  let videoChildren = yield inspector.walker.children(video);
+  is (videoChildren.nodes.length, 0,
+    "No native children returned from walker for <video> by default");
 });
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/browser_markupview_anonymous_04.js
@@ -0,0 +1,36 @@
+/* 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";
+
+// Test native anonymous content in the markupview with devtools.inspector.showAllAnonymousContent
+// set to true
+const TEST_URL = TEST_URL_ROOT + "doc_markup_anonymous.html";
+
+add_task(function*() {
+  Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
+
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  let native = yield getNodeFront("#native", inspector);
+
+  // Markup looks like: <div><video controls /></div>
+  let nativeChildren = yield inspector.walker.children(native);
+  is (nativeChildren.nodes.length, 1, "Children returned from walker");
+
+  info ("Checking the video element");
+  let video = nativeChildren.nodes[0];
+  ok (!video.isAnonymous, "<video> is not anonymous");
+
+  let videoChildren = yield inspector.walker.children(video);
+  is (videoChildren.nodes.length, 3, "<video> has native anonymous children");
+
+  for (let node of videoChildren.nodes) {
+    ok (node.isAnonymous, "Child is anonymous");
+    ok (!node._form.isXBLAnonymous, "Child is not XBL anonymous");
+    ok (!node._form.isShadowAnonymous, "Child is not shadow anonymous");
+    ok (node._form.isNativeAnonymous, "Child is native anonymous");
+    yield isEditingMenuDisabled(node, inspector);
+  }
+});
--- a/browser/devtools/markupview/test/doc_markup_anonymous.html
+++ b/browser/devtools/markupview/test/doc_markup_anonymous.html
@@ -15,16 +15,18 @@
     }
   </style>
 </head>
 <body>
   <div id="pseudo"><span>middle</span></div>
 
   <div id="shadow">light dom</div>
 
+  <div id="native"><video controls></video></div>
+
   <script>
   var host = document.querySelector('#shadow');
   if (host.createShadowRoot) {
     var root = host.createShadowRoot();
     root.innerHTML = '<h3>Shadow DOM</h3><select multiple></select>';
   }
   </script>
 </body>
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -27,16 +27,17 @@ registerCleanupFunction(() => gDevTools.
 // Clear preferences that may be set during the course of tests.
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
   Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
   Services.prefs.clearUserPref("devtools.dump.emit");
   Services.prefs.clearUserPref("devtools.markup.pagesize");
   Services.prefs.clearUserPref("dom.webcomponents.enabled");
+  Services.prefs.clearUserPref("devtools.inspector.showAllAnonymousContent");
 });
 
 // Auto close the toolbox and close the test tabs when the test ends
 registerCleanupFunction(function*() {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   yield gDevTools.closeToolbox(target);
 
   while (gBrowser.tabs.length > 1) {
--- a/browser/devtools/performance/modules/io.js
+++ b/browser/devtools/performance/modules/io.js
@@ -13,17 +13,18 @@ loader.lazyImporter(this, "FileUtils",
 loader.lazyImporter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 
 // This identifier string is used to tentatively ascertain whether or not
 // a JSON loaded from disk is actually something generated by this tool.
 // It isn't, of course, a definitive verification, but a Good Enough™
 // approximation before continuing the import. Don't localize this.
 const PERF_TOOL_SERIALIZER_IDENTIFIER = "Recorded Performance Data";
-const PERF_TOOL_SERIALIZER_VERSION = 1;
+const PERF_TOOL_SERIALIZER_LEGACY_VERSION = 1;
+const PERF_TOOL_SERIALIZER_CURRENT_VERSION = 2;
 
 /**
  * Helpers for importing/exporting JSON.
  */
 let PerformanceIO = {
   /**
    * Gets a nsIScriptableUnicodeConverter instance with a default UTF-8 charset.
    * @return object
@@ -46,17 +47,17 @@ let PerformanceIO = {
    * @return object
    *         A promise that is resolved once streaming finishes, or rejected
    *         if there was an error.
    */
   saveRecordingToFile: function(recordingData, file) {
     let deferred = promise.defer();
 
     recordingData.fileType = PERF_TOOL_SERIALIZER_IDENTIFIER;
-    recordingData.version = PERF_TOOL_SERIALIZER_VERSION;
+    recordingData.version = PERF_TOOL_SERIALIZER_CURRENT_VERSION;
 
     let string = JSON.stringify(recordingData);
     let inputStream = this.getUnicodeConverter().convertToInputStream(string);
     let outputStream = FileUtils.openSafeFileOutputStream(file);
 
     NetUtil.asyncCopy(inputStream, outputStream, deferred.resolve);
     return deferred.promise;
   },
@@ -83,20 +84,69 @@ let PerformanceIO = {
       } catch (e) {
         deferred.reject(new Error("Could not read recording data file."));
         return;
       }
       if (recordingData.fileType != PERF_TOOL_SERIALIZER_IDENTIFIER) {
         deferred.reject(new Error("Unrecognized recording data file."));
         return;
       }
-      if (recordingData.version != PERF_TOOL_SERIALIZER_VERSION) {
+      if (!isValidSerializerVersion(recordingData.version)) {
         deferred.reject(new Error("Unsupported recording data file version."));
         return;
       }
+      if (recordingData.version === PERF_TOOL_SERIALIZER_LEGACY_VERSION) {
+        recordingData = convertLegacyData(recordingData);
+      }
       deferred.resolve(recordingData);
     });
 
     return deferred.promise;
   }
 };
 
 exports.PerformanceIO = PerformanceIO;
+
+/**
+ * Returns a boolean indicating whether or not the passed in `version`
+ * is supported by this serializer.
+ *
+ * @param number version
+ * @return boolean
+ */
+function isValidSerializerVersion (version) {
+  return !!~[
+    PERF_TOOL_SERIALIZER_LEGACY_VERSION,
+    PERF_TOOL_SERIALIZER_CURRENT_VERSION
+  ].indexOf(version);
+}
+
+
+/**
+ * Takes recording data (with version `1`, from the original profiler tool), and
+ * massages the data to be line with the current performance tool's property names
+ * and values.
+ *
+ * @param object legacyData
+ * @return object
+ */
+function convertLegacyData (legacyData) {
+  let { profilerData, ticksData, recordingDuration } = legacyData;
+
+  // The `profilerData` stays, and the previously unrecorded fields
+  // just are empty arrays.
+  let data = {
+    markers: [],
+    frames: [],
+    memory: [],
+    ticks: ticksData,
+    profilerData: profilerData,
+    // Data from the original profiler won't contain `interval` fields,
+    // but a recording duration, as well as the current time, which can be used
+    // to infer the interval startTime and endTime.
+    interval: {
+      startTime: profilerData.currentTime - recordingDuration,
+      endTime: profilerData.currentTime
+    }
+  };
+
+  return data;
+}
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -34,8 +34,9 @@ support-files =
 [browser_perf-overview-selection-03.js]
 [browser_perf-shared-connection-02.js]
 [browser_perf-shared-connection-03.js]
 # [browser_perf-shared-connection-04.js] bug 1077464
 [browser_perf-ui-recording.js]
 [browser_perf_recordings-io-01.js]
 [browser_perf_recordings-io-02.js]
 [browser_perf_recordings-io-03.js]
+[browser_perf_recordings-io-04.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf_recordings-io-04.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the performance tool can import profiler data from the
+ * original profiler tool.
+ */
+
+let test = Task.async(function*() {
+  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
+  let { EVENTS, PerformanceController } = panel.panelWin;
+
+  yield startRecording(panel);
+  yield stopRecording(panel);
+
+  // Get data from the current profiler
+  let data = PerformanceController.getAllData();
+
+  // Create a structure from the data that mimics the old profiler's data.
+  // Different name for `ticks`, different way of storing time,
+  // and no memory, markers data.
+  let oldProfilerData = {
+    recordingDuration: data.interval.endTime - data.interval.startTime,
+    ticksData: data.ticks,
+    profilerData: data.profilerData,
+    fileType: "Recorded Performance Data",
+    version: 1
+  };
+
+  // Save recording as an old profiler data.
+  let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
+  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+  yield asyncCopy(oldProfilerData, file);
+
+  // Import recording.
+
+  let rerendered = waitForWidgetsRendered(panel);
+  let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
+  yield PerformanceController.importRecording("", file);
+
+  yield imported;
+  ok(true, "The original profiler data appears to have been successfully imported.");
+
+  yield rerendered;
+  ok(true, "The imported data was re-rendered.");
+
+  // Verify imported recording.
+
+  let importedData = PerformanceController.getAllData();
+
+  is(importedData.startTime, data.startTime,
+    "The imported legacy data was successfully converted for the current tool (1).");
+  is(importedData.endTime, data.endTime,
+    "The imported legacy data was successfully converted for the current tool (2).");
+  is(importedData.markers.toSource(), [].toSource(),
+    "The imported legacy data was successfully converted for the current tool (3).");
+  is(importedData.memory.toSource(), [].toSource(),
+    "The imported legacy data was successfully converted for the current tool (4).");
+  is(importedData.ticks.toSource(), data.ticks.toSource(),
+    "The imported legacy data was successfully converted for the current tool (5).");
+  is(importedData.profilerData.toSource(), data.profilerData.toSource(),
+    "The imported legacy data was successfully converted for the current tool (6).");
+
+  yield teardown(panel);
+  finish();
+});
+
+function getUnicodeConverter() {
+  let className = "@mozilla.org/intl/scriptableunicodeconverter";
+  let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = "UTF-8";
+  return converter;
+}
+
+function asyncCopy(data, file) {
+  let deferred = Promise.defer();
+
+  let string = JSON.stringify(data);
+  let inputStream = getUnicodeConverter().convertToInputStream(string);
+  let outputStream = FileUtils.openSafeFileOutputStream(file);
+
+  NetUtil.asyncCopy(inputStream, outputStream, status => {
+    if (!Components.isSuccessCode(status)) {
+      deferred.reject(new Error("Could not save data to file."));
+    }
+    deferred.resolve();
+  });
+
+  return deferred.promise;
+}
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -141,16 +141,17 @@ skip-if = e10s # Bug 1042253 - webconsol
 skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests (expectUncaughtException)
 [browser_console.js]
 [browser_console_addonsdk_loader_exception.js]
 [browser_console_clear_on_reload.js]
 [browser_console_click_focus.js]
 [browser_console_consolejsm_output.js]
 [browser_console_dead_objects.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_console_copy_entire_message_context_menu.js]
 [browser_console_error_source_click.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
 [browser_console_filters.js]
 [browser_console_iframe_messages.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
 [browser_console_keyboard_accessibility.js]
 [browser_console_log_inspectable_object.js]
 [browser_console_native_getters.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_console_copy_entire_message_context_menu.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test copying of the entire console message when right-clicked
+// with no other text selected. See Bug 1100562.
+
+function test() {
+  let hud;
+  let outputNode;
+  let contextMenu;
+
+  const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
+  const TEST_FILE = TEST_URI.substr(TEST_URI.lastIndexOf("/"));
+
+  Task.spawn(runner).then(finishTest);
+
+  function* runner() {
+    const {tab} = yield loadTab(TEST_URI);
+    hud = yield openConsole(tab);
+    outputNode = hud.outputNode;
+    contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu");
+
+    registerCleanupFunction(() => {
+      hud = outputNode = contextMenu = null;
+    });
+
+    hud.jsterm.clearOutput();
+    content.console.log("bug 1100562");
+
+    let [results] = yield waitForMessages({
+      webconsole: hud,
+      messages: [{
+        text: "bug 1100562",
+        category: CATEGORY_WEBDEV,
+        severity: SEVERITY_LOG,
+      }]
+    });
+
+    outputNode.focus();
+    let message = [...results.matched][0];
+    message.scrollIntoView();
+
+    yield waitForContextMenu(contextMenu, message, copyFromPopup, testContextMenuCopy);
+
+    function copyFromPopup() {
+      let copyItem = contextMenu.querySelector("#cMenu_copy");
+      copyItem.doCommand();
+
+      let controller = top.document.commandDispatcher.getControllerForCommand("cmd_copy");
+      is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
+    }
+
+    function testContextMenuCopy() {
+      waitForClipboard((str) => { return message.textContent.trim() == str.trim(); },
+        () => { goDoCommand("cmd_copy") },
+        () => {}, () => {}
+      );
+    }
+
+    yield closeConsole(tab);
+  }
+}
\ No newline at end of file
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -2837,16 +2837,19 @@ WebConsoleFrame.prototype = {
 
   /**
    * Copies the selected items to the system clipboard.
    *
    * @param object aOptions
    *        - linkOnly:
    *        An optional flag to copy only URL without timestamp and
    *        other meta-information. Default is false.
+   *        - contextmenu:
+   *        An optional flag to copy the last clicked item which brought
+   *        up the context menu if nothing is selected. Default is false.
    */
   copySelectedItems: function WCF_copySelectedItems(aOptions)
   {
     aOptions = aOptions || { linkOnly: false, contextmenu: false };
 
     // Gather up the selected items and concatenate their clipboard text.
     let strings = [];
 
@@ -2859,17 +2862,17 @@ WebConsoleFrame.prototype = {
       // Ensure the selected item hasn't been filtered by type or string.
       if (!item.classList.contains("filtered-by-type") &&
           !item.classList.contains("filtered-by-string")) {
         let timestampString = l10n.timestampString(item.timestamp);
         if (aOptions.linkOnly) {
           strings.push(item.url);
         }
         else {
-          strings.push("[" + timestampString + "] " + item.clipboardText);
+          strings.push(item.clipboardText);
         }
       }
     }
 
     clipboardHelper.copyString(strings.join("\n"), this.document);
   },
 
   /**
@@ -4778,16 +4781,24 @@ CommandController.prototype = {
     this.owner.openSelectedItemInTab();
   },
 
   copyURL: function CommandController_copyURL()
   {
     this.owner.copySelectedItems({ linkOnly: true, contextmenu: true });
   },
 
+  /**
+   * Copies the last clicked message.
+   */
+  copyLastClicked: function CommandController_copy()
+  {
+    this.owner.copySelectedItems({ linkOnly: false, contextmenu: true });
+  },
+
   supportsCommand: function CommandController_supportsCommand(aCommand)
   {
     if (!this.owner || !this.owner.output) {
       return false;
     }
     return this.isCommandEnabled(aCommand);
   },
 
@@ -4796,16 +4807,22 @@ CommandController.prototype = {
     switch (aCommand) {
       case "consoleCmd_openURL":
       case "consoleCmd_copyURL": {
         // Only enable URL-related actions if node is Net Activity.
         let selectedItem = this.owner.output.getSelectedMessages(1)[0] ||
                            this.owner._contextMenuHandler.lastClickedMessage;
         return selectedItem && "url" in selectedItem;
       }
+      case "cmd_copy": {
+        // Only copy if we right-clicked the console and there's no selected text.
+        // With text selected, we want to fall back onto the default copy behavior.
+        return this.owner._contextMenuHandler.lastClickedMessage &&
+              !this.owner.output.getSelectedMessages(1)[0];
+      }
       case "consoleCmd_clearOutput":
       case "cmd_selectAll":
       case "cmd_find":
         return true;
       case "cmd_fontSizeEnlarge":
       case "cmd_fontSizeReduce":
       case "cmd_fontSizeReset":
       case "cmd_close":
@@ -4821,16 +4838,19 @@ CommandController.prototype = {
         this.openURL();
         break;
       case "consoleCmd_copyURL":
         this.copyURL();
         break;
       case "consoleCmd_clearOutput":
         this.owner.jsterm.clearOutput(true);
         break;
+      case "cmd_copy":
+        this.copyLastClicked();
+        break;
       case "cmd_find":
         this.owner.filterBox.focus();
         break;
       case "cmd_selectAll":
         this.selectAll();
         break;
       case "cmd_fontSizeEnlarge":
         this.owner.changeFontSize("+");
--- a/browser/devtools/webide/content/addons.js
+++ b/browser/devtools/webide/content/addons.js
@@ -42,18 +42,17 @@ function BuildItem(addon, type) {
   function onAddonUpdate(event, arg) {
     switch (event) {
       case "update":
         progress.removeAttribute("value");
         li.setAttribute("status", addon.status);
         status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
         break;
       case "failure":
-        console.error(arg);
-        window.alert(arg);
+        window.parent.UI.reportError("error_operationFail", arg);
         break;
       case "progress":
         if (arg == -1) {
           progress.removeAttribute("value");
         } else {
           progress.value = arg;
         }
         break;
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -232,16 +232,17 @@
 @RESPATH@/components/dom_workers.xpt
 @RESPATH@/components/dom_xbl.xpt
 @RESPATH@/components/dom_xpath.xpt
 @RESPATH@/components/dom_xul.xpt
 #ifdef MOZ_GAMEPAD
 @RESPATH@/components/dom_gamepad.xpt
 #endif
 @RESPATH@/components/dom_payment.xpt
+@RESPATH@/components/dom_presentation.xpt
 @RESPATH@/components/downloads.xpt
 @RESPATH@/components/editor.xpt
 @RESPATH@/components/embed_base.xpt
 @RESPATH@/components/extensions.xpt
 @RESPATH@/components/exthandler.xpt
 @RESPATH@/components/exthelper.xpt
 @RESPATH@/components/fastfind.xpt
 @RESPATH@/components/feeds.xpt
@@ -372,17 +373,16 @@
 @RESPATH@/browser/components/WebContentConverter.js
 @RESPATH@/browser/components/BrowserComponents.manifest
 @RESPATH@/browser/components/nsBrowserContentHandler.js
 @RESPATH@/browser/components/nsBrowserGlue.js
 @RESPATH@/browser/components/nsSetDefaultBrowser.manifest
 @RESPATH@/browser/components/nsSetDefaultBrowser.js
 @RESPATH@/browser/components/BrowserDownloads.manifest
 @RESPATH@/browser/components/DownloadsStartup.js
-@RESPATH@/browser/components/BrowserPlaces.manifest
 @RESPATH@/browser/components/devtools-clhandler.manifest
 @RESPATH@/browser/components/devtools-clhandler.js
 @RESPATH@/browser/components/webideCli.js
 @RESPATH@/browser/components/webideComponents.manifest
 @RESPATH@/browser/components/Experiments.manifest
 @RESPATH@/browser/components/ExperimentsService.js
 @RESPATH@/browser/components/translation.manifest
 @RESPATH@/components/Downloads.manifest
@@ -447,17 +447,16 @@
 @RESPATH@/components/toolkitplaces.manifest
 @RESPATH@/components/nsLivemarkService.js
 @RESPATH@/components/nsTaggingService.js
 @RESPATH@/components/nsPlacesAutoComplete.manifest
 @RESPATH@/components/nsPlacesAutoComplete.js
 @RESPATH@/components/UnifiedComplete.manifest
 @RESPATH@/components/UnifiedComplete.js
 @RESPATH@/components/nsPlacesExpiration.js
-@RESPATH@/browser/components/PlacesProtocolHandler.js
 @RESPATH@/components/PlacesCategoriesStarter.js
 @RESPATH@/components/ColorAnalyzer.js
 @RESPATH@/components/PageThumbsProtocol.js
 @RESPATH@/components/nsDefaultCLH.manifest
 @RESPATH@/components/nsDefaultCLH.js
 @RESPATH@/components/nsContentPrefService.manifest
 @RESPATH@/components/nsContentPrefService.js
 @RESPATH@/components/nsContentDispatchChooser.manifest
@@ -537,16 +536,20 @@
 @RESPATH@/components/nsDOMIdentity.js
 @RESPATH@/components/nsIDService.js
 @RESPATH@/components/Identity.manifest
 @RESPATH@/components/recording-cmdline.js
 @RESPATH@/components/recording-cmdline.manifest
 @RESPATH@/components/htmlMenuBuilder.js
 @RESPATH@/components/htmlMenuBuilder.manifest
 
+@RESPATH@/components/RequestSync.manifest
+@RESPATH@/components/RequestSyncManager.js
+@RESPATH@/components/RequestSyncScheduler.js
+
 @RESPATH@/components/PermissionSettings.js
 @RESPATH@/components/PermissionSettings.manifest
 @RESPATH@/components/ContactManager.js
 @RESPATH@/components/ContactManager.manifest
 @RESPATH@/components/PhoneNumberService.js
 @RESPATH@/components/PhoneNumberService.manifest
 @RESPATH@/components/NotificationStorage.js
 @RESPATH@/components/NotificationStorage.manifest
@@ -599,16 +602,19 @@
 
 #ifdef MOZ_WEBSPEECH
 @RESPATH@/components/dom_webspeechsynth.xpt
 #endif
 
 @RESPATH@/components/nsAsyncShutdown.manifest
 @RESPATH@/components/nsAsyncShutdown.js
 
+@RESPATH@/components/PresentationDeviceInfoManager.manifest
+@RESPATH@/components/PresentationDeviceInfoManager.js
+
 ; InputMethod API
 @RESPATH@/components/MozKeyboard.js
 @RESPATH@/components/InputMethod.manifest
 
 #ifdef MOZ_DEBUG
 @RESPATH@/components/TestInterfaceJS.js
 @RESPATH@/components/TestInterfaceJS.manifest
 #endif
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.properties
@@ -310,8 +310,10 @@ watchExpressionsSeparatorLabel=\ →
 functionSearchSeparatorLabel=←
 
 # LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears
 # as a description in the notification panel popup, when multiple debuggers are
 # open in separate tabs and the user tries to resume them in the wrong order.
 # The substitution parameter is the URL of the last paused window that must be
 # resumed first.
 resumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S
+
+evalGroupLabel=Evaluated Sources
\ No newline at end of file
--- a/browser/locales/en-US/chrome/browser/loop/loop.properties
+++ b/browser/locales/en-US/chrome/browser/loop/loop.properties
@@ -266,22 +266,22 @@ legal_text_privacy = Privacy Notice
 
 ## LOCALIZATION NOTE (powered_by_beforeLogo, powered_by_afterLogo):
 ## These 2 strings are displayed before and after a 'Telefonica'
 ## logo.
 powered_by_beforeLogo=Powered by
 powered_by_afterLogo=
 
 feedback_call_experience_heading2=How was your conversation?
-feedback_what_makes_you_sad=What makes you sad?
 feedback_thank_you_heading=Thank you for your feedback!
+feedback_category_list_heading=What made you sad?
 feedback_category_audio_quality=Audio quality
 feedback_category_video_quality=Video quality
 feedback_category_was_disconnected=Was disconnected
-feedback_category_confusing=Confusing
+feedback_category_confusing2=Confusing controls
 feedback_category_other2=Other
 feedback_custom_category_text_placeholder=What went wrong?
 feedback_submit_button=Submit
 feedback_back_button=Back
 ## LOCALIZATION NOTE (feedback_window_will_close_in2):
 ## Semicolon-separated list of plural forms. See:
 ## http://developer.mozilla.org/en/docs/Localization_and_Plurals
 ## In this item, don't translate the part between {{..}}
--- a/browser/locales/en-US/chrome/browser/places/places.dtd
+++ b/browser/locales/en-US/chrome/browser/places/places.dtd
@@ -47,16 +47,18 @@
 <!ENTITY cmd.delete.accesskey           "D">
 <!ENTITY cmd.deleteDomainData.label     "Forget About This Site">
 <!ENTITY cmd.deleteDomainData.accesskey "F">
 
 <!ENTITY cmd.open.label                  "Open">
 <!ENTITY cmd.open.accesskey              "O">
 <!ENTITY cmd.open_window.label           "Open in a New Window">
 <!ENTITY cmd.open_window.accesskey       "N">
+<!ENTITY cmd.open_private_window.label     "Open in a New Private Window">
+<!ENTITY cmd.open_private_window.accesskey "P">
 <!ENTITY cmd.open_tab.label              "Open in a New Tab">
 <!ENTITY cmd.open_tab.accesskey          "w">
 <!ENTITY cmd.open_all_in_tabs.label      "Open All in Tabs">
 <!ENTITY cmd.open_all_in_tabs.accesskey  "O">
 
 <!ENTITY cmd.properties.label      "Properties">
 <!ENTITY cmd.properties.accesskey  "i">
 
--- a/browser/locales/generic/profile/bookmarks.html.in
+++ b/browser/locales/generic/profile/bookmarks.html.in
@@ -14,14 +14,14 @@
 <DL><p>
     <DT><H3 PERSONAL_TOOLBAR_FOLDER="true" ID="rdf:#$FvPhC3">@bookmarks_toolbarfolder@</H3>
 <DD>@bookmarks_toolbarfolder_description@
     <DL><p>
         <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/central/" ID="rdf:#$GvPhC3">@getting_started@</A>
     </DL><p>
     <DT><H3 ID="rdf:#$ZvPhC3">@firefox_heading@</H3>
     <DL><p>
-        <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/help/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$22iCK1">@firefox_help@</A>
-        <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/customize/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$32iCK1">@firefox_customize@</A>
-        <DT><A HREF="https://www.mozilla.org/@AB_CD@/contribute/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$42iCK1">@firefox_community@</A>
-        <DT><A HREF="https://www.mozilla.org/@AB_CD@/about/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$52iCK1">@firefox_about@</A>
+        <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/help/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=" ID="rdf:#$22iCK1">@firefox_help@</A>
+        <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/customize/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=" ID="rdf:#$32iCK1">@firefox_customize@</A>
+        <DT><A HREF="https://www.mozilla.org/@AB_CD@/contribute/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=" ID="rdf:#$42iCK1">@firefox_community@</A>
+        <DT><A HREF="https://www.mozilla.org/@AB_CD@/about/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=" ID="rdf:#$52iCK1">@firefox_about@</A>
     </DL><p>
 </DL><p>
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -528,16 +528,17 @@ menuitem:not([type]):not(.menuitem-toolt
 }
 
 #menu_pageInfo,
 #context-viewinfo,
 #context-viewframeinfo {
   list-style-image: url("moz-icon://stock/gtk-info?size=menu");
 }
 
+#placesContext_open\:newprivatewindow,
 #privateBrowsingItem {
   list-style-image: url("chrome://browser/skin/Privacy-16.png");
 }
 
 #placesContext_show\:info {
   list-style-image: url("moz-icon://stock/gtk-properties?size=menu");
 }
 
@@ -1772,17 +1773,17 @@ richlistitem[type~="action"][actiontype=
 #tabbrowser-tabs {
   /* override the global style to allow the selected tab to be above the nav-bar */
   z-index: auto;
 }
 
 #TabsToolbar {
   min-height: 0;
   padding: 0;
-  margin-bottom: -@tabToolbarNavbarOverlap@;
+  margin-bottom: calc(-1 * var(--tab-toolbar-navbar-overlap));
 }
 
 #TabsToolbar:not(:-moz-lwtheme) {
   -moz-appearance: menubar;
   color: -moz-menubartext;
 }
 
 #toolbar-menubar:not([autohide="true"]):not(:-moz-lwtheme):-moz-system-metric(menubar-drag),
@@ -1847,17 +1848,17 @@ richlistitem[type~="action"][actiontype=
 .tabbrowser-arrowscrollbox > .scrollbutton-down > .toolbarbutton-icon {
   -moz-appearance: none;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-appearance: none;
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png");
-  margin: 0 0 @tabToolbarNavbarOverlap@;
+  margin: 0 0 var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-up,
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.png);
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up[disabled],
@@ -1875,17 +1876,17 @@ richlistitem[type~="action"][actiontype=
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down[notifybgtab] {
   background-color: Highlight;
   transition: none;
 }
 
 #TabsToolbar .toolbarbutton-1 {
-  margin-bottom: @tabToolbarNavbarOverlap@;
+  margin-bottom: var(--tab-toolbar-navbar-overlap);
 }
 
 #alltabs-button {
   list-style-image: url("chrome://browser/skin/tabbrowser/alltabs.png");
 }
 
 #TabsToolbar[brighttext] > #alltabs-button,
 #TabsToolbar[brighttext] > toolbarpaletteitem > #alltabs-button {
--- a/browser/themes/linux/places/places.css
+++ b/browser/themes/linux/places/places.css
@@ -195,16 +195,21 @@ menuitem[cmd="cmd_selectAll"][disabled] 
 }
 
 #placesContext_open\:newwindow,
 menuitem[command="placesCmd_open:window"] {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 80px 16px 64px);
 }
 
+#placesContext_open\:newprivatewindow,
+menuitem[command="placesCmd_open:privatewindow"] {
+  list-style-image: url("chrome://browser/skin/Privacy-16.png");
+}
+
 #placesContext_open\:newtab,
 menuitem[command="placesCmd_open:tab"] {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 64px 16px 48px);
 }
 
 #placesContext_show\:info,
 menuitem[command="placesCmd_show:info"] {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -112,17 +112,17 @@
   height: 22px; /* The native titlebar on OS X is 22px tall. */
 }
 
 /**
  * For tabs in titlebar on OS X, we stretch the titlebar down so that the
  * tabstrip can overlap it.
  */
 #main-window[tabsintitlebar] > #titlebar {
-  min-height: calc(@tabMinHeight@ + var(--space-above-tabbar) - @tabToolbarNavbarOverlap@);
+  min-height: calc(var(--tab-min-height) + var(--space-above-tabbar) - var(--tab-toolbar-navbar-overlap));
 }
 
 /**
  * We also vertically center the window buttons.
  */
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-buttonbox-container,
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
   margin-top: @windowButtonMarginTop@;
@@ -186,29 +186,29 @@ toolbarseparator {
 
 /* Draw the bottom border of the tabs toolbar when it's not using
    -moz-appearance: toolbar. */
 #main-window:-moz-any([sizemode="fullscreen"],[customize-entered]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #main-window:not([tabsintitlebar]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-lwtheme {
   border-top: 1px solid hsla(0,0%,0%,.3);
   background-clip: padding-box;
-  margin-top: -@tabToolbarNavbarOverlap@;
+  margin-top: calc(-1 * var(--tab-toolbar-navbar-overlap));
   /* Position the toolbar above the bottom of background tabs */
   position: relative;
   z-index: 1;
 }
 
 /* Always draw a border on Yosemite to ensure the border is well-defined there
  * (the default border is too light). */
 @media (-moz-mac-yosemite-theme) {
   #main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:not(:-moz-lwtheme) {
     border-top: 1px solid hsla(0,0%,0%,.2);
     background-clip: padding-box;
-    margin-top: -@tabToolbarNavbarOverlap@;
+    margin-top: calc(-1 * var(--tab-toolbar-navbar-overlap));
     /* Position the toolbar above the bottom of background tabs */
     position: relative;
     z-index: 1;
   }
 }
 
 #nav-bar-customization-target {
   padding: 4px;
@@ -3179,17 +3179,17 @@ toolbarbutton.chevron > .toolbarbutton-m
   #TabsToolbar[brighttext] .tab-close-button.close-icon:not([selected=true]):not(:hover) {
     -moz-image-region: rect(0, 128px, 32px, 96px);
   }
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-image-region: rect(0, 13px, 20px, 0);
-  margin: 0 0 @tabToolbarNavbarOverlap@;
+  margin: 0 0 var(--tab-toolbar-navbar-overlap);
   padding: 0 4px;
   border: none;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up {
   -moz-border-end: 2px solid transparent;
 }
 
@@ -3305,17 +3305,17 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 #TabsToolbar .toolbarbutton-1:not([type="menu-button"]),
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
   padding: 0 1px;
 }
 
 #TabsToolbar .toolbarbutton-1 {
-  margin-bottom: @tabToolbarNavbarOverlap@;
+  margin-bottom: var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   padding-left: 4px;
   padding-right: 4px;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
@@ -4724,17 +4724,17 @@ window > chatbox {
   }
   #main-window[privatebrowsingmode=temporary]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator {
     background-image: url("chrome://browser/skin/privatebrowsing-mask-short@2x.png");
   }
 }
 
 @media (-moz-mac-lion-theme) {
   #TabsToolbar > .private-browsing-indicator {
-    transform: translateY(calc(0px - var(--space-above-tabbar)));
+    transform: translateY(calc(-1 * var(--space-above-tabbar)));
     /* We offset by 38px for mask graphic, plus 4px to account for the
      * margin-left, which sums to 42px.
      */
     margin-right: -42px;
   }
 
   #main-window[privatebrowsingmode=temporary] .titlebar-placeholder[type="fullscreen-button"],
   #main-window[privatebrowsingmode=temporary] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
--- a/browser/themes/shared/browser.inc
+++ b/browser/themes/shared/browser.inc
@@ -6,10 +6,8 @@
 
 %ifdef XP_MACOSX
 % Prior to 10.7 there wasn't a native fullscreen button so we use #restore-button to exit fullscreen
 % and want it to behave like other toolbar buttons.
 %define primaryToolbarButtons @primaryToolbarButtons@, #restore-button
 %endif
 
 %define inAnyPanel :-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true])
-%define tabToolbarNavbarOverlap 1px
-%define tabMinHeight 31px
--- a/browser/themes/shared/devtools/images/performance-icons.svg
+++ b/browser/themes/shared/devtools/images/performance-icons.svg
@@ -22,21 +22,21 @@
   <rect x="13px" y="7px" width="2px" height="9px" rx="1" ry="1"/>
 </g>
 <g id="details-waterfall">
   <rect x="0px" y="3px" width="9px" height="2.5px" rx="1" ry="1"/>
   <rect x="5px" y="7px" width="8px" height="2.5px" rx="1" ry="1"/>
   <rect x="7px" y="11px" width="9px" height="2.5px" rx="1" ry="1"/>
 </g>
 <g id="details-call-tree">
-  <rect x="0px" y="3px" width="16px" height="2px"/>
-  <rect x="3px" y="6px" width="7px" height="2px"/>
-  <rect x="6px" y="9px" width="6px" height="2px"/>
-  <rect x="9px" y="12px" width="5px" height="2px"/>
+  <rect x="0px" y="3px" width="16px" height="2px" rx="1" ry="1"/>
+  <rect x="3px" y="6px" width="7px" height="2px" rx="1" ry="1"/>
+  <rect x="6px" y="9px" width="6px" height="2px" rx="1" ry="1"/>
+  <rect x="9px" y="12px" width="5px" height="2px" rx="1" ry="1"/>
 </g>
 <g id="details-flamegraph">
-  <rect x="0px" y="3px" width="16px" height="2px"/>
-  <rect x="0px" y="6px" width="8px" height="2px"/>
-  <rect x="10px" y="6px" width="6px" height="2px"/>
-  <rect x="2px" y="9px" width="6px" height="2px"/>
-  <rect x="5px" y="12px" width="3px" height="2px"/>
+  <rect x="0px" y="3px" width="16px" height="2px" rx="1" ry="1"/>
+  <rect x="0px" y="6px" width="8px" height="2px" rx="1" ry="1"/>
+  <rect x="10px" y="6px" width="6px" height="2px" rx="1" ry="1"/>
+  <rect x="2px" y="9px" width="6px" height="2px" rx="1" ry="1"/>
+  <rect x="5px" y="12px" width="3px" height="2px" rx="1" ry="1"/>
 </g>
 </svg>
--- a/browser/themes/shared/incontentprefs/search.css
+++ b/browser/themes/shared/incontentprefs/search.css
@@ -38,8 +38,16 @@
 
 #engineList treechildren::-moz-tree-drop-feedback {
   background-color: Highlight;
   width: 10000px; /* 100% doesn't work; 10k is hopefully larger than any window
                      we may have, overflow isn't visible. */
   height: 2px;
   -moz-margin-start: 0;
 }
+
+#engineShown {
+  min-width: 26px;
+}
+
+#addEnginesBox {
+  margin-bottom: 1em;
+}
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -1,31 +1,36 @@
 %if 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/. */
 %endif
 
+:root {
+  --tab-toolbar-navbar-overlap: 1px;
+  --tab-min-height: 31px;
+}
+
 %define tabCurveWidth 30px
 %define tabCurveHalfWidth 15px
 
 /* image preloading hack */
 #tabbrowser-tabs::before {
   /* Because of bug 853415, we need to ordinal this to the first position: */
   -moz-box-ordinal-group: 0;
   content: '';
   display: block;
   background-image:
     url(chrome://browser/skin/tabbrowser/tab-background-end.png),
     url(chrome://browser/skin/tabbrowser/tab-background-middle.png),
     url(chrome://browser/skin/tabbrowser/tab-background-start.png);
 }
 
 #tabbrowser-tabs {
-  min-height: @tabMinHeight@;
+  min-height: var(--tab-min-height);
 }
 
 .tabbrowser-tab,
 .tabs-newtab-button {
   -moz-appearance: none;
   background-color: transparent;
   border-radius: 0;
   border-width: 0;
@@ -113,17 +118,17 @@
 }
 
 /* Tab Overflow */
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]),
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
   background-image: url(chrome://browser/skin/tabbrowser/tab-overflow-indicator.png);
   background-size: 100% 100%;
   width: 14px;
-  margin-bottom: @tabToolbarNavbarOverlap@;
+  margin-bottom: var(--tab-toolbar-navbar-overlap);
   pointer-events: none;
   position: relative;
   z-index: 3; /* the selected tab's z-index + 1 */
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:-moz-locale-dir(rtl),
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:-moz-locale-dir(ltr) {
   transform: scaleX(-1);
@@ -150,17 +155,17 @@
 }
 
 .tab-background-start[selected=true]::after,
 .tab-background-start[selected=true]::before,
 .tab-background-start,
 .tab-background-end,
 .tab-background-end[selected=true]::after,
 .tab-background-end[selected=true]::before {
-  min-height: @tabMinHeight@;
+  min-height: var(--tab-min-height);
   width: @tabCurveWidth@;
 }
 
 .tabbrowser-tab:not([selected=true]),
 .tabbrowser-tab:-moz-lwtheme {
   color: inherit;
 }
 
@@ -290,30 +295,30 @@
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
 #tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::before {
   height: 100%;
   position: absolute;
 }
 
 .tabbrowser-tab[pinned][titlechanged]:not([selected="true"]) > .tab-stack > .tab-content {
   background-image: radial-gradient(farthest-corner at center bottom, rgb(255,255,255) 3%, rgba(186,221,251,0.75) 20%, rgba(127,179,255,0.25) 40%, transparent 70%);
-  background-position: center bottom @tabToolbarNavbarOverlap@;
+  background-position: center bottom var(--tab-toolbar-navbar-overlap);
   background-repeat: no-repeat;
   background-size: 85% 100%;
 }
 
 /* Background tab separators (3px wide).
    Also show separators beside the selected tab when dragging it. */
 #tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
 .tabbrowser-tab:not([selected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
 #tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([selected]):not([beforehovered]):not(:hover)::after {
   -moz-margin-start: -1.5px;
   -moz-margin-end: -1.5px;
   background-image: url(chrome://browser/skin/tabbrowser/tab-separator.png);
-  background-position: left bottom @tabToolbarNavbarOverlap@;
+  background-position: left bottom var(--tab-toolbar-navbar-overlap);
   background-repeat: no-repeat;
   background-size: 3px 100%;
   content: "";
   display: -moz-box;
   width: 3px;
 }
 
 /* Handle a case where the last separator in a customized tab bar with a
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -882,17 +882,17 @@ toolbarbutton[sdk-button="true"][cui-are
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-appearance: none;
   border-style: none;
   padding: 0 3px;
 }
 
 #TabsToolbar .toolbarbutton-1 {
-  margin-bottom: @tabToolbarNavbarOverlap@;
+  margin-bottom: var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar .toolbarbutton-1:not([disabled=true]):hover,
 #TabsToolbar .toolbarbutton-1[open],
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover {
   background-image: linear-gradient(transparent, rgba(255,255,255,.5)),
@@ -1781,17 +1781,17 @@ toolbarbutton[type="socialmark"] > .tool
   text-shadow: none;
 }
 
 /* Tabstrip */
 
 #TabsToolbar {
   min-height: 0;
   padding: 0;
-  margin-bottom: -@tabToolbarNavbarOverlap@; /* overlap the nav-bar's top border */
+  margin-bottom: calc(-1 * var(--tab-toolbar-navbar-overlap)); /* overlap the nav-bar's top border */
 }
 
 %ifndef WINDOWS_AERO
 @media (-moz-windows-default-theme) {
   #main-window[sizemode=normal] #TabsToolbar {
     padding-left: 2px;
     padding-right: 2px;
   }
@@ -1876,17 +1876,17 @@ toolbarbutton[type="socialmark"] > .tool
   border: none;
 }
 
 /* Tab scrollbox arrow, tabstrip new tab and all-tabs buttons */
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png");
-  margin: 0 0 @tabToolbarNavbarOverlap@;
+  margin: 0 0 var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-up,
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.png);
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up[disabled],
--- a/dom/apps/PermissionsTable.jsm
+++ b/dom/apps/PermissionsTable.jsm
@@ -521,16 +521,22 @@ this.PermissionsTable =  { geolocation: 
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            "before-after-keyboard-event": {
                              app: DENY_ACTION,
                              trusted: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
+                           },
+                           "presentation-device-manage": {
+                             app: DENY_ACTION,
+                             trusted: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
                            }
                          };
 
 /**
  * Append access modes to the permission name as suffixes.
  *   e.g. permission name 'contacts' with ['read', 'write'] =
  *   ['contacts-read', contacts-write']
  * @param string aPermName
--- a/dom/base/AnonymousContent.cpp
+++ b/dom/base/AnonymousContent.cpp
@@ -124,16 +124,17 @@ AnonymousContent::GetElementById(const n
     nsIAtom* id = kid->AsElement()->GetID();
     if (id && id == elementId) {
       return kid->AsElement();
     }
   }
   return nullptr;
 }
 
-JSObject*
-AnonymousContent::WrapObject(JSContext* aCx)
+bool
+AnonymousContent::WrapObject(JSContext* aCx,
+                             JS::MutableHandle<JSObject*> aReflector)
 {
-  return AnonymousContentBinding::Wrap(aCx, this);
+  return AnonymousContentBinding::Wrap(aCx, this, aReflector);
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/base/AnonymousContent.h
+++ b/dom/base/AnonymousContent.h
@@ -22,17 +22,17 @@ class AnonymousContent MOZ_FINAL
 public:
   // Ref counting and cycle collection
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnonymousContent)
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AnonymousContent)
 
   explicit AnonymousContent(Element* aContentNode);
   nsCOMPtr<Element> GetContentNode();
   void SetContentNode(Element* aContentNode);
-  JSObject* WrapObject(JSContext* aCx);
+  bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 
   // WebIDL methods
   void SetTextContentForElement(const nsAString& aElementId,
                                 const nsAString& aText,
                                 ErrorResult& aRv);
 
   void GetTextContentForElement(const nsAString& aElementId,
                                 DOMString& aText,
--- a/dom/base/DOMMatrix.cpp
+++ b/dom/base/DOMMatrix.cpp
@@ -18,16 +18,21 @@
 #include "nsAutoPtr.h"
 #include <math.h>
 
 namespace mozilla {
 namespace dom {
 
 static const double radPerDegree = 2.0 * M_PI / 360.0;
 
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMMatrixReadOnly, mParent)
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMatrixReadOnly, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMatrixReadOnly, Release)
+
 already_AddRefed<DOMMatrix>
 DOMMatrixReadOnly::Translate(double aTx,
                              double aTy,
                              double aTz) const
 {
   nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
   retval->TranslateSelf(aTx, aTy, aTz);
 
@@ -298,21 +303,16 @@ DOMMatrixReadOnly::Stringify(nsAString& 
       M41(), M42(), M43(), M44());
   } else {
     matrixStr.AppendPrintf("matrix(%g, %g, %g, %g, %g, %g)", A(), B(), C(), D(), E(), F());
   }
 
   aResult = matrixStr;
 }
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMMatrix, mParent)
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMatrix, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMatrix, Release)
-
 already_AddRefed<DOMMatrix>
 DOMMatrix::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
   nsRefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
   return obj.forget();
 }
 
 already_AddRefed<DOMMatrix>
--- a/dom/base/DOMMatrix.h
+++ b/dom/base/DOMMatrix.h
@@ -35,16 +35,19 @@ public:
   {
     if (other.mMatrix2D) {
       mMatrix2D = new gfx::Matrix(*other.mMatrix2D);
     } else {
       mMatrix3D = new gfx::Matrix4x4(*other.mMatrix3D);
     }
   }
 
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMMatrixReadOnly)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMMatrixReadOnly)
+
 #define GetMatrixMember(entry2D, entry3D, default) \
 { \
   if (mMatrix3D) { \
     return mMatrix3D->entry3D; \
   } \
   return mMatrix2D->entry2D; \
 }
 
@@ -125,19 +128,18 @@ public:
                                              JS::MutableHandle<JSObject*> aResult,
                                              ErrorResult& aRv) const;
   void                        Stringify(nsAString& aResult);
 protected:
   nsCOMPtr<nsISupports>     mParent;
   nsAutoPtr<gfx::Matrix>    mMatrix2D;
   nsAutoPtr<gfx::Matrix4x4> mMatrix3D;
 
-  ~DOMMatrixReadOnly()
-  {
-  }
+  virtual ~DOMMatrixReadOnly() {}
+
 private:
   DOMMatrixReadOnly() = delete;
   DOMMatrixReadOnly(const DOMMatrixReadOnly&) = delete;
   DOMMatrixReadOnly& operator=(const DOMMatrixReadOnly&) = delete;
 };
 
 class DOMMatrix MOZ_FINAL : public DOMMatrixReadOnly
 {
@@ -145,19 +147,16 @@ public:
   explicit DOMMatrix(nsISupports* aParent)
     : DOMMatrixReadOnly(aParent)
   {}
 
   DOMMatrix(nsISupports* aParent, const DOMMatrixReadOnly& other)
     : DOMMatrixReadOnly(aParent, other)
   {}
 
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMMatrix)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMMatrix)
-
   static already_AddRefed<DOMMatrix>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
   static already_AddRefed<DOMMatrix>
   Constructor(const GlobalObject& aGlobal, const nsAString& aTransformList, ErrorResult& aRv);
   static already_AddRefed<DOMMatrix>
   Constructor(const GlobalObject& aGlobal, const DOMMatrixReadOnly& aOther, ErrorResult& aRv);
   static already_AddRefed<DOMMatrix>
   Constructor(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv);
@@ -241,16 +240,14 @@ public:
                                  double aZ,
                                  double aAngle);
   DOMMatrix* SkewXSelf(double aSx);
   DOMMatrix* SkewYSelf(double aSy);
   DOMMatrix* InvertSelf();
   DOMMatrix* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv);
 private:
   void Ensure3DMatrix();
-
-  ~DOMMatrix() {}
 };
 
 }
 }
 
 #endif /*MOZILLA_DOM_DOMMATRIX_H_*/
--- a/dom/base/DOMPoint.cpp
+++ b/dom/base/DOMPoint.cpp
@@ -7,20 +7,20 @@
 
 #include "mozilla/dom/DOMPointBinding.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsAutoPtr.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMPoint, mParent)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMPointReadOnly, mParent)
 
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMPoint, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMPoint, Release)
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMPointReadOnly, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMPointReadOnly, Release)
 
 already_AddRefed<DOMPoint>
 DOMPoint::Constructor(const GlobalObject& aGlobal, const DOMPointInit& aParams,
                       ErrorResult& aRV)
 {
   nsRefPtr<DOMPoint> obj =
     new DOMPoint(aGlobal.GetAsSupports(), aParams.mX, aParams.mY,
                  aParams.mZ, aParams.mW);
--- a/dom/base/DOMPoint.h
+++ b/dom/base/DOMPoint.h
@@ -28,39 +28,39 @@ public:
     : mParent(aParent)
     , mX(aX)
     , mY(aY)
     , mZ(aZ)
     , mW(aW)
   {
   }
 
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMPointReadOnly)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMPointReadOnly)
+
   double X() const { return mX; }
   double Y() const { return mY; }
   double Z() const { return mZ; }
   double W() const { return mW; }
 
 protected:
+  virtual ~DOMPointReadOnly() {}
+
   nsCOMPtr<nsISupports> mParent;
   double mX, mY, mZ, mW;
 };
 
 class DOMPoint MOZ_FINAL : public DOMPointReadOnly
 {
-  ~DOMPoint() {}
-
 public:
   explicit DOMPoint(nsISupports* aParent, double aX = 0.0, double aY = 0.0,
                     double aZ = 0.0, double aW = 1.0)
     : DOMPointReadOnly(aParent, aX, aY, aZ, aW)
   {}
 
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMPoint)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMPoint)
-
   static already_AddRefed<DOMPoint>
   Constructor(const GlobalObject& aGlobal, const DOMPointInit& aParams,
               ErrorResult& aRV);
   static already_AddRefed<DOMPoint>
   Constructor(const GlobalObject& aGlobal, double aX, double aY,
               double aZ, double aW, ErrorResult& aRV);
 
   nsISupports* GetParentObject() const { return mParent; }
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1843,16 +1843,43 @@ Navigator::MozHasPendingMessage(const ns
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
   return result;
 }
 
 void
+Navigator::MozSetMessageHandlerPromise(Promise& aPromise,
+                                       ErrorResult& aRv)
+{
+  // The WebIDL binding is responsible for the pref check here.
+  aRv = EnsureMessagesManager();
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  bool result = false;
+  aRv = mMessagesManager->MozIsHandlingMessage(&result);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  if (!result) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return;
+  }
+
+  aRv = mMessagesManager->MozSetMessageHandlerPromise(&aPromise);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+}
+
+void
 Navigator::MozSetMessageHandler(const nsAString& aType,
                                 systemMessageCallback* aCallback,
                                 ErrorResult& aRv)
 {
   // The WebIDL binding is responsible for the pref check here.
   nsresult rv = EnsureMessagesManager();
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -230,16 +230,18 @@ public:
   TVManager* GetTv();
   network::Connection* GetConnection(ErrorResult& aRv);
   nsDOMCameraManager* GetMozCameras(ErrorResult& aRv);
   MediaDevices* GetMediaDevices(ErrorResult& aRv);
   void MozSetMessageHandler(const nsAString& aType,
                             systemMessageCallback* aCallback,
                             ErrorResult& aRv);
   bool MozHasPendingMessage(const nsAString& aType, ErrorResult& aRv);
+  void MozSetMessageHandlerPromise(Promise& aPromise, ErrorResult& aRv);
+
 #ifdef MOZ_B2G
   already_AddRefed<Promise> GetMobileIdAssertion(const MobileIdOptions& options,
                                                  ErrorResult& aRv);
 #endif
 #ifdef MOZ_B2G_RIL
   MobileConnectionArray* GetMozMobileConnections(ErrorResult& aRv);
   IccManager* GetMozIccManager(ErrorResult& aRv);
 #endif // MOZ_B2G_RIL
--- a/dom/base/NodeIterator.cpp
+++ b/dom/base/NodeIterator.cpp
@@ -283,16 +283,16 @@ void NodeIterator::ContentRemoved(nsIDoc
                                   nsIContent *aPreviousSibling)
 {
     nsINode *container = NODE_FROM(aContainer, aDocument);
 
     mPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling);
     mWorkingPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling);
 }
 
-JSObject*
-NodeIterator::WrapObject(JSContext *cx)
+bool
+NodeIterator::WrapObject(JSContext *cx, JS::MutableHandle<JSObject*> aReflector)
 {
-    return NodeIteratorBinding::Wrap(cx, this);
+    return NodeIteratorBinding::Wrap(cx, this, aReflector);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/NodeIterator.h
+++ b/dom/base/NodeIterator.h
@@ -64,17 +64,17 @@ public:
         return NextOrPrevNode(&NodePointer::MoveToNext, aResult);
     }
     already_AddRefed<nsINode> PreviousNode(ErrorResult& aResult)
     {
         return NextOrPrevNode(&NodePointer::MoveToPrevious, aResult);
     }
     // The XPCOM Detach() is fine for our purposes
 
-    JSObject* WrapObject(JSContext *cx);
+    bool WrapObject(JSContext *cx, JS::MutableHandle<JSObject*> aReflector);
 
 private:
     virtual ~NodeIterator();
 
     struct NodePointer {
         NodePointer() : mNode(nullptr) {}
         NodePointer(nsINode *aNode, bool aBeforeNode);
 
--- a/dom/base/TreeWalker.cpp
+++ b/dom/base/TreeWalker.cpp
@@ -445,16 +445,16 @@ TreeWalker::NextSiblingInternal(bool aRe
             return nullptr;
         }
         if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
             return nullptr;
         }
     }
 }
 
-JSObject*
-TreeWalker::WrapObject(JSContext *cx)
+bool
+TreeWalker::WrapObject(JSContext *aCx, JS::MutableHandle<JSObject*> aReflector)
 {
-    return TreeWalkerBinding::Wrap(cx, this);
+    return TreeWalkerBinding::Wrap(aCx, this, aReflector);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/TreeWalker.h
+++ b/dom/base/TreeWalker.h
@@ -60,17 +60,17 @@ public:
     already_AddRefed<nsINode> ParentNode(ErrorResult& aResult);
     already_AddRefed<nsINode> FirstChild(ErrorResult& aResult);
     already_AddRefed<nsINode> LastChild(ErrorResult& aResult);
     already_AddRefed<nsINode> PreviousSibling(ErrorResult& aResult);
     already_AddRefed<nsINode> NextSibling(ErrorResult& aResult);
     already_AddRefed<nsINode> PreviousNode(ErrorResult& aResult);
     already_AddRefed<nsINode> NextNode(ErrorResult& aResult);
 
-    JSObject* WrapObject(JSContext *cx);
+    bool WrapObject(JSContext *aCx, JS::MutableHandle<JSObject*> aReflector);
 
 private:
     nsCOMPtr<nsINode> mCurrentNode;
 
     /*
      * Implements FirstChild and LastChild which only vary in which direction
      * they search.
      * @param aReversed Controls whether we search forwards or backwards
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -41,20 +41,20 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 URL::URL(nsIURI* aURI)
   : mURI(aURI)
 {
 }
 
-JSObject*
-URL::WrapObject(JSContext* aCx)
+bool
+URL::WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector)
 {
-  return URLBinding::Wrap(aCx, this);
+  return URLBinding::Wrap(aCx, this, aReflector);
 }
 
 /* static */ already_AddRefed<URL>
 URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
                  URL& aBase, ErrorResult& aRv)
 {
   nsresult rv;
   nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -38,18 +38,18 @@ class URL MOZ_FINAL : public URLSearchPa
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(URL)
 
   explicit URL(nsIURI* aURI);
 
   // WebIDL methods
-  JSObject*
-  WrapObject(JSContext* aCx);
+  bool
+  WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 
   static already_AddRefed<URL>
   Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
               URL& aBase, ErrorResult& aRv);
   static already_AddRefed<URL>
   Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
               const nsAString& aBase, ErrorResult& aRv);
 
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -641,23 +641,24 @@ WebSocketImpl::DoOnMessageAvailable(cons
   }
 
   if (readyState == WebSocket::OPEN) {
     // Dispatch New Message
     nsresult rv = mWebSocket->CreateAndDispatchMessageEvent(aMsg, isBinary);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch the message event");
     }
-  } else {
-    // CLOSING should be the only other state where it's possible to get msgs
-    // from channel: Spec says to drop them.
-    MOZ_ASSERT(readyState == WebSocket::CLOSING,
-               "Received message while CONNECTING or CLOSED");
+
+    return NS_OK;
   }
 
+  // CLOSING should be the only other state where it's possible to get msgs
+  // from channel: Spec says to drop them.
+  MOZ_ASSERT(readyState == WebSocket::CLOSING,
+             "Received message while CONNECTING or CLOSED");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketImpl::OnMessageAvailable(nsISupports* aContext,
                                   const nsACString& aMsg)
 {
   AssertIsOnTargetThread();
@@ -713,24 +714,27 @@ WebSocketImpl::OnStart(nsISupports* aCon
     mChannel->GetProtocol(mWebSocket->mEstablishedProtocol);
   }
 
   mChannel->GetExtensions(mWebSocket->mEstablishedExtensions);
   UpdateURI();
 
   mWebSocket->SetReadyState(WebSocket::OPEN);
 
+  // Let's keep the object alive because the webSocket can be CCed in the
+  // onopen callback.
+  nsRefPtr<WebSocket> webSocket = mWebSocket;
+
   // Call 'onopen'
-  rv = mWebSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
+  rv = webSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch the open event");
   }
 
-  mWebSocket->UpdateMustKeepAlive();
-
+  webSocket->UpdateMustKeepAlive();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketImpl::OnStop(nsISupports* aContext, nsresult aStatusCode)
 {
   AssertIsOnTargetThread();
 
@@ -1591,33 +1595,37 @@ WebSocketImpl::DispatchConnectionCloseEv
   AssertIsOnTargetThread();
 
   if (mDisconnectingOrDisconnected) {
     return;
   }
 
   mWebSocket->SetReadyState(WebSocket::CLOSED);
 
+  // Let's keep the object alive because the webSocket can be CCed in the
+  // onerror or in the onclose callback.
+  nsRefPtr<WebSocket> webSocket = mWebSocket;
+
   // Call 'onerror' if needed
   if (mFailed) {
     nsresult rv =
-      mWebSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
+      webSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch the error event");
     }
   }
 
-  nsresult rv = mWebSocket->CreateAndDispatchCloseEvent(mCloseEventWasClean,
-                                                        mCloseEventCode,
-                                                        mCloseEventReason);
+  nsresult rv = webSocket->CreateAndDispatchCloseEvent(mCloseEventWasClean,
+                                                       mCloseEventCode,
+                                                       mCloseEventReason);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch the close event");
   }
 
-  mWebSocket->UpdateMustKeepAlive();
+  webSocket->UpdateMustKeepAlive();
   Disconnect();
 }
 
 nsresult
 WebSocket::CreateAndDispatchSimpleEvent(const nsAString& aName)
 {
   MOZ_ASSERT(mImpl);
   AssertIsOnTargetThread();
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5295,18 +5295,18 @@ nsIDocument::RemoveAnonymousContent(Anon
 
   nsCOMPtr<Element> container = shell->GetCanvasFrame()
                                      ->GetCustomContentContainer();
   if (!container) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
-  // Iterate over know customContents to get and remove the right one
-  for (int32_t i = mAnonymousContents.Length() - 1; i >= 0; --i) {
+  // Iterate over mAnonymousContents to find and remove the given node.
+  for (size_t i = 0, len = mAnonymousContents.Length(); i < len; ++i) {
     if (mAnonymousContents[i] == &aContent) {
       // Get the node from the customContent
       nsCOMPtr<Element> node = aContent.GetContentNode();
 
       // Remove the entry in mAnonymousContents
       mAnonymousContents.RemoveElementAt(i);
 
       // Remove the node from its container
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2300,17 +2300,17 @@ nsFrameLoader::CreateStaticClone(nsIFram
 
   viewer->SetDOMDocument(clonedDOMDoc);
   return NS_OK;
 }
 
 bool
 nsFrameLoader::DoLoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope)
 {
-  auto* tabParent = static_cast<TabParent*>(GetRemoteBrowser());
+  mozilla::dom::PBrowserParent* tabParent = GetRemoteBrowser();
   if (tabParent) {
     return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope);
   }
   nsRefPtr<nsInProcessTabChildGlobal> tabChild =
     static_cast<nsInProcessTabChildGlobal*>(GetTabChildGlobalAsEventTarget());
   if (tabChild) {
     tabChild->LoadFrameScript(aURL, aRunInGlobalScope);
   }
@@ -2416,17 +2416,23 @@ nsFrameLoader::EnsureMessageManager()
 {
   NS_ENSURE_STATE(mOwnerContent);
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (!mIsTopLevelContent && !OwnerIsBrowserOrAppFrame() && !mRemoteFrame) {
+  if (!mIsTopLevelContent &&
+      !OwnerIsBrowserOrAppFrame() &&
+      !mRemoteFrame &&
+      !(mOwnerContent->IsXUL() &&
+        mOwnerContent->AttrValueIs(kNameSpaceID_None,
+                                   nsGkAtoms::forcemessagemanager,
+                                   nsGkAtoms::_true, eCaseMatters))) {
     return NS_OK;
   }
 
   bool useRemoteProcess = ShouldUseRemoteProcess();
   if (mMessageManager) {
     if (useRemoteProcess && mRemoteBrowserShown) {
       mMessageManager->InitWithCallback(this);
     }
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2112,16 +2112,17 @@ GK_ATOM(DeleteTxnName, "Deleting")
 
 // IPC stuff
 GK_ATOM(Remote, "remote")
 GK_ATOM(RemoteId, "_remote_id")
 GK_ATOM(DisplayPort, "_displayport")
 GK_ATOM(DisplayPortMargins, "_displayportmargins")
 GK_ATOM(DisplayPortBase, "_displayportbase")
 GK_ATOM(AsyncScrollLayerCreationFailed, "_asyncscrolllayercreationfailed")
+GK_ATOM(forcemessagemanager, "forcemessagemanager")
 
 // Names for system metrics
 GK_ATOM(color_picker_available, "color-picker-available")
 GK_ATOM(scrollbar_start_backward, "scrollbar-start-backward")
 GK_ATOM(scrollbar_start_forward, "scrollbar-start-forward")
 GK_ATOM(scrollbar_end_backward, "scrollbar-end-backward")
 GK_ATOM(scrollbar_end_forward, "scrollbar-end-forward")
 GK_ATOM(scrollbar_thumb_proportional, "scrollbar-thumb-proportional")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2294,19 +2294,19 @@ CreateNativeGlobalForInner(JSContext* aC
   }
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
                         TreatAsRemoteXUL(aPrincipal);
   uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
   flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK;
 
-  aGlobal.set(WindowBinding::Wrap(aCx, aNewInner, aNewInner, options,
-                                  nsJSPrincipals::get(aPrincipal), false));
-  if (!aGlobal || !xpc::InitGlobalObject(aCx, aGlobal, flags)) {
+  if (!WindowBinding::Wrap(aCx, aNewInner, aNewInner, options,
+                           nsJSPrincipals::get(aPrincipal), false, aGlobal) ||
+      !xpc::InitGlobalObject(aCx, aGlobal, flags)) {
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal);
 
   // Set the location information for the new global, so that tools like
   // about:memory may use that information
   xpc::SetLocationForGlobal(aGlobal, aURI);
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -662,57 +662,80 @@ DefineWebIDLBindingUnforgeableProperties
                                                     JS::Handle<JSObject*> obj,
                                                     const NativeProperties* properties);
 
 bool
 DefineWebIDLBindingPropertiesOnXPCObject(JSContext* cx,
                                          JS::Handle<JSObject*> obj,
                                          const NativeProperties* properties);
 
+#define HAS_MEMBER_TYPEDEFS                                               \
+private:                                                                  \
+  typedef char yes[1];                                                    \
+  typedef char no[2]
+
 #ifdef _MSC_VER
 #define HAS_MEMBER_CHECK(_name)                                           \
-  template<typename V> static yes& Check(char (*)[(&V::_name == 0) + 1])
+  template<typename V> static yes& Check##_name(char (*)[(&V::_name == 0) + 1])
 #else
 #define HAS_MEMBER_CHECK(_name)                                           \
-  template<typename V> static yes& Check(char (*)[sizeof(&V::_name) + 1])
+  template<typename V> static yes& Check##_name(char (*)[sizeof(&V::_name) + 1])
 #endif
 
-#define HAS_MEMBER(_name)                                                 \
-template<typename T>                                                      \
-class Has##_name##Member {                                                \
-  typedef char yes[1];                                                    \
-  typedef char no[2];                                                     \
-  HAS_MEMBER_CHECK(_name);                                                \
-  template<typename V> static no& Check(...);                             \
+#define HAS_MEMBER(_memberName, _valueName)                               \
+private:                                                                  \
+  HAS_MEMBER_CHECK(_memberName);                                          \
+  template<typename V> static no& Check##_memberName(...);                \
                                                                           \
 public:                                                                   \
-  static bool const Value = sizeof(Check<T>(nullptr)) == sizeof(yes);     \
+  static bool const _valueName =                                          \
+    sizeof(Check##_memberName<T>(nullptr)) == sizeof(yes)
+
+template<class T>
+struct NativeHasMember
+{
+  HAS_MEMBER_TYPEDEFS;
+
+  HAS_MEMBER(GetParentObject, GetParentObject);
+  HAS_MEMBER(JSBindingFinalized, JSBindingFinalized);
+  HAS_MEMBER(WrapObject, WrapObject);
 };
 
-HAS_MEMBER(WrapObject)
-
-// HasWrapObject<T>::Value will be true if T has a WrapObject member but it's
-// not nsWrapperCache::WrapObject.
-template<typename T>
-struct HasWrapObject
+template<class T>
+struct IsSmartPtr
 {
-private:
-  typedef char yes[1];
-  typedef char no[2];
-  typedef JSObject* (nsWrapperCache::*WrapObject)(JSContext*,
-                                                  JS::Handle<JSObject*>);
-  template<typename U, U> struct SFINAE;
-  template <typename V> static no& Check(SFINAE<WrapObject, &V::WrapObject>*);
-  template <typename V> static yes& Check(...);
+  HAS_MEMBER_TYPEDEFS;
+
+  HAS_MEMBER(get, value);
+};
+
+template<class T>
+struct IsRefcounted
+{
+  HAS_MEMBER_TYPEDEFS;
+
+  HAS_MEMBER(AddRef, HasAddref);
+  HAS_MEMBER(Release, HasRelease);
 
 public:
-  static bool const Value = HasWrapObjectMember<T>::Value &&
-                            sizeof(Check<T>(nullptr)) == sizeof(yes);
+  static bool const value = HasAddref && HasRelease;
+
+private:
+  // This struct only works if T is fully declared (not just forward declared).
+  // The IsBaseOf check will ensure that, we don't really need it for any other
+  // reason (the static assert will of course always be true).
+  static_assert(!IsBaseOf<nsISupports, T>::value || IsRefcounted::value,
+                "Classes derived from nsISupports are refcounted!");
+
 };
 
+#undef HAS_MEMBER
+#undef HAS_MEMBER_CHECK
+#undef HAS_MEMBER_TYPEDEFS
+
 #ifdef DEBUG
 template <class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
 struct
 CheckWrapperCacheCast
 {
   static bool Check()
   {
     return reinterpret_cast<uintptr_t>(
@@ -971,16 +994,17 @@ GetOrCreateDOMReflectorNoWrap(JSContext*
 // WrapObject() method taking a JSContext and a scope.
 template <class T>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx,
                                      JS::Handle<JSObject*> scopeArg,
                                      T* value,
                                      JS::MutableHandle<JS::Value> rval)
 {
+  static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
   MOZ_ASSERT(value);
   // We try to wrap in the compartment of the underlying object of "scope"
   JS::Rooted<JSObject*> obj(cx);
   {
     // scope for the JSAutoCompartment so that we restore the compartment
     // before we call JS_WrapValue.
     Maybe<JSAutoCompartment> ac;
     // Maybe<Handle> doesn't so much work, and in any case, adding
@@ -990,40 +1014,39 @@ WrapNewBindingNonWrapperCachedObject(JSC
     if (js::IsWrapper(scope)) {
       scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false);
       if (!scope)
         return false;
       ac.emplace(cx, scope);
     }
 
     MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
-    obj = value->WrapObject(cx);
-  }
-
-  if (!obj) {
-    return false;
+    if (!value->WrapObject(cx, &obj)) {
+      return false;
+    }
   }
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   rval.set(JS::ObjectValue(*obj));
   return JS_WrapValue(cx, rval);
 }
 
 // Create a JSObject wrapping "value", for cases when "value" is a
 // non-wrapper-cached owned object using WebIDL bindings.  "value" must implement a
 // WrapObject() method taking a JSContext, a scope, and a boolean outparam that
 // is true if the JSObject took ownership
 template <class T>
 inline bool
-WrapNewBindingNonWrapperCachedOwnedObject(JSContext* cx,
-                                          JS::Handle<JSObject*> scopeArg,
-                                          nsAutoPtr<T>& value,
-                                          JS::MutableHandle<JS::Value> rval)
+WrapNewBindingNonWrapperCachedObject(JSContext* cx,
+                                     JS::Handle<JSObject*> scopeArg,
+                                     nsAutoPtr<T>& value,
+                                     JS::MutableHandle<JS::Value> rval)
 {
+  static_assert(!IsRefcounted<T>::value, "Only pass owned classes in here.");
   // We do a runtime check on value, because otherwise we might in
   // fact end up wrapping a null and invoking methods on it later.
   if (!value) {
     NS_RUNTIMEABORT("Don't try to wrap null objects");
   }
   // We try to wrap in the compartment of the underlying object of "scope"
   JS::Rooted<JSObject*> obj(cx);
   {
@@ -1036,37 +1059,33 @@ WrapNewBindingNonWrapperCachedOwnedObjec
     JS::Rooted<JSObject*> scope(cx, scopeArg);
     if (js::IsWrapper(scope)) {
       scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false);
       if (!scope)
         return false;
       ac.emplace(cx, scope);
     }
 
-    bool tookOwnership = false;
     MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
-    obj = value->WrapObject(cx, &tookOwnership);
-    MOZ_ASSERT_IF(obj, tookOwnership);
-    if (tookOwnership) {
-      value.forget();
+    if (!value->WrapObject(cx, &obj)) {
+      return false;
     }
-  }
-
-  if (!obj) {
-    return false;
+
+    value.forget();
   }
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   rval.set(JS::ObjectValue(*obj));
   return JS_WrapValue(cx, rval);
 }
 
-// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
-template <template <typename> class SmartPtr, typename T>
+// Helper for smart pointers (nsRefPtr/nsCOMPtr).
+template <template <typename> class SmartPtr, typename T,
+          typename U=typename EnableIf<IsRefcounted<T>::value, T>::Type>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
                                      const SmartPtr<T>& value,
                                      JS::MutableHandle<JS::Value> rval)
 {
   return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval);
 }
 
@@ -1095,19 +1114,18 @@ HandleNewBindingWrappingFailure(JSContex
 
   qsObjectHelper helper(value, GetWrapperCache(value));
   return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval,
                                                   helper, nullptr, true);
 }
 
 // Helper for calling HandleNewBindingWrappingFailure with smart pointers
 // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
-HAS_MEMBER(get)
-
-template <class T, bool isSmartPtr=HasgetMember<T>::Value>
+
+template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
 struct HandleNewBindingWrappingFailureHelper
 {
   static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope,
                           const T& value, JS::MutableHandle<JS::Value> rval)
   {
     return HandleNewBindingWrappingFailure(cx, scope, value.get(), rval);
   }
 };
@@ -1462,17 +1480,17 @@ struct WrapNativeParentFallback<T, true 
   static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
   {
     return WrapNativeISupportsParent(cx, parent, cache);
   }
 };
 
 // Wrapping of our native parent, for cases when it's a WebIDL object (though
 // possibly preffed off).
-template<typename T, bool hasWrapObject=HasWrapObject<T>::Value >
+template<typename T, bool hasWrapObject=NativeHasMember<T>::WrapObject>
 struct WrapNativeParentHelper
 {
   static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
   {
     MOZ_ASSERT(cache);
 
     JSObject* obj;
     if ((obj = cache->GetWrapper())) {
@@ -1488,17 +1506,17 @@ struct WrapNativeParentHelper
 
     return obj;
   }
 };
 
 // Wrapping of our native parent, for cases when it's not a WebIDL object.  In
 // this case it must be nsISupports.
 template<typename T>
-struct WrapNativeParentHelper<T, false >
+struct WrapNativeParentHelper<T, false>
 {
   static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
   {
     JSObject* obj;
     if (cache && (obj = cache->GetWrapper())) {
 #ifdef DEBUG
       NS_ASSERTION(WrapNativeISupportsParent(cx, parent, cache) == obj,
                    "Unexpected object in nsWrapperCache");
@@ -1546,19 +1564,17 @@ WrapNativeParent(JSContext* cx, T* p, ns
 // things like the nsWrapperCache for it.
 template<typename T>
 static inline JSObject*
 WrapNativeParent(JSContext* cx, const T& p)
 {
   return WrapNativeParent(cx, GetParentPointer(p), GetWrapperCache(p), GetUseXBLScope(p));
 }
 
-HAS_MEMBER(GetParentObject)
-
-template<typename T, bool WrapperCached=HasGetParentObjectMember<T>::Value>
+template<typename T, bool WrapperCached=NativeHasMember<T>::GetParentObject>
 struct GetParentObject
 {
   static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj)
   {
     MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx));
     T* native = UnwrapDOMObject<T>(obj);
     JSObject* wrappedParent = WrapNativeParent(cx, native->GetParentObject());
     return wrappedParent ? js::GetGlobalForObjectCrossCompartment(wrappedParent) : nullptr;
@@ -1627,32 +1643,33 @@ WrapCallThisObject<JS::Rooted<JSObject*>
     return nullptr;
   }
 
   return obj;
 }
 
 // Helper for calling GetOrCreateDOMReflector with smart pointers
 // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
-template <class T, bool isSmartPtr=HasgetMember<T>::Value>
+template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
 struct GetOrCreateDOMReflectorHelper
 {
   static inline bool GetOrCreate(JSContext* cx, const T& value,
                                  JS::MutableHandle<JS::Value> rval)
   {
     return GetOrCreateDOMReflector(cx, value.get(), rval);
   }
 };
 
 template <class T>
 struct GetOrCreateDOMReflectorHelper<T, false>
 {
   static inline bool GetOrCreate(JSContext* cx, T& value,
                                  JS::MutableHandle<JS::Value> rval)
   {
+    static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
     return GetOrCreateDOMReflector(cx, &value, rval);
   }
 };
 
 template<class T>
 inline bool
 GetOrCreateDOMReflector(JSContext* cx, T& value,
                         JS::MutableHandle<JS::Value> rval)
@@ -1668,17 +1685,17 @@ inline bool
 GetOrCreateDOMReflector(JSContext* cx, JS::Handle<JSObject*> scope, T& value,
                         JS::MutableHandle<JS::Value> rval)
 {
   return GetOrCreateDOMReflector(cx, value, rval);
 }
 
 // Helper for calling GetOrCreateDOMReflectorNoWrap with smart pointers
 // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
-template <class T, bool isSmartPtr=HasgetMember<T>::Value>
+template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
 struct GetOrCreateDOMReflectorNoWrapHelper
 {
   static inline bool GetOrCreate(JSContext* cx, const T& value,
                                  JS::MutableHandle<JS::Value> rval)
   {
     return GetOrCreateDOMReflectorNoWrap(cx, value.get(), rval);
   }
 };
@@ -1707,17 +1724,17 @@ inline JSObject*
 GetCallbackFromCallbackObject(T* aObj)
 {
   return aObj->Callback();
 }
 
 // Helper for getting the callback JSObject* of a smart ptr around a
 // CallbackObject or a reference to a CallbackObject or something like
 // that.
-template <class T, bool isSmartPtr=HasgetMember<T>::Value>
+template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
 struct GetCallbackFromCallbackObjectHelper
 {
   static inline JSObject* Get(const T& aObj)
   {
     return GetCallbackFromCallbackObject(aObj.get());
   }
 };
 
@@ -2498,40 +2515,17 @@ UseDOMXray(JSObject* obj)
 inline bool
 HasConstructor(JSObject* obj)
 {
   return JS_IsNativeFunction(obj, Constructor) ||
          js::GetObjectClass(obj)->construct;
 }
  #endif
  
-// Transfer reference in ptr to smartPtr.
-template<class T>
-inline void
-Take(nsRefPtr<T>& smartPtr, T* ptr)
-{
-  smartPtr = dont_AddRef(ptr);
-}
-
-// Transfer ownership of ptr to smartPtr.
-template<class T>
-inline void
-Take(nsAutoPtr<T>& smartPtr, T* ptr)
-{
-  smartPtr = ptr;
-}
-
-inline void
-MustInheritFromNonRefcountedDOMObject(NonRefcountedDOMObject*)
-{
-}
-
-HAS_MEMBER(JSBindingFinalized)
-
-template<class T, bool hasCallback=HasJSBindingFinalizedMember<T>::Value>
+template<class T, bool hasCallback=NativeHasMember<T>::JSBindingFinalized>
 struct JSBindingFinalized
 {
   static void Finalized(T* self)
   {
   }
 };
 
 template<class T>
@@ -2748,34 +2742,138 @@ ToSupportsIsCorrect(T* aObject)
 template<class T>
 bool
 ToSupportsIsOnPrimaryInheritanceChain(T* aObject, nsWrapperCache* aCache)
 {
   return CastingAssertions<T>::ToSupportsIsOnPrimaryInheritanceChain(aObject,
                                                                      aCache);
 }
 
-template<class T, template <typename> class SmartPtr,
+// The BindingJSObjectCreator class is supposed to be used by a caller that
+// wants to create and initialise a binding JSObject. After initialisation has
+// been successfully completed it should call ForgetObject().
+// The BindingJSObjectCreator object will root the JSObject until ForgetObject()
+// is called on it. If the native object for the binding is refcounted it will
+// also hold a strong reference to it, that reference is transferred to the
+// JSObject (which holds the native in a slot) when ForgetObject() is called. If
+// the BindingJSObjectCreator object is destroyed and ForgetObject() was never
+// called on it then the JSObject's slot holding the native will be set to
+// undefined, and for a refcounted native the strong reference will be released.
+template<class T>
+class MOZ_STACK_CLASS BindingJSObjectCreator
+{
+public:
+  explicit BindingJSObjectCreator(JSContext* aCx)
+    : mReflector(aCx)
+  {
+  }
+
+  ~BindingJSObjectCreator()
+  {
+    if (mReflector) {
+      js::SetReservedOrProxyPrivateSlot(mReflector, DOM_OBJECT_SLOT,
+                                        JS::UndefinedValue());
+    }
+  }
+
+  void
+  CreateProxyObject(JSContext* aCx, const js::Class* aClass,
+                    const DOMProxyHandler* aHandler,
+                    JS::Handle<JSObject*> aProto,
+                    JS::Handle<JSObject*> aParent, T* aNative,
+                    JS::MutableHandle<JSObject*> aReflector)
+  {
+    js::ProxyOptions options;
+    options.setClass(aClass);
+    JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aNative));
+    aReflector.set(js::NewProxyObject(aCx, aHandler, proxyPrivateVal, aProto,
+                                      aParent, options));
+    if (aReflector) {
+      mNative = aNative;
+      mReflector = aReflector;
+    }
+  }
+
+  void
+  CreateObject(JSContext* aCx, const JSClass* aClass,
+               JS::Handle<JSObject*> aProto, JS::Handle<JSObject*> aParent,
+               T* aNative, JS::MutableHandle<JSObject*> aReflector)
+  {
+    aReflector.set(JS_NewObject(aCx, aClass, aProto, aParent));
+    if (aReflector) {
+      js::SetReservedSlot(aReflector, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
+      mNative = aNative;
+      mReflector = aReflector;
+    }
+  }
+
+  void
+  InitializationSucceeded()
+  {
+    void* dummy;
+    mNative.forget(&dummy);
+    mReflector = nullptr;
+  }
+
+private:
+  struct OwnedNative
+  {
+    // Make sure the native objects inherit from NonRefcountedDOMObject so
+    // that we log their ctor and dtor.
+    static_assert(IsBaseOf<NonRefcountedDOMObject, T>::value,
+                  "Non-refcounted objects with DOM bindings should inherit "
+                  "from NonRefcountedDOMObject.");
+
+    OwnedNative&
+    operator=(T* aNative)
+    {
+      return *this;
+    }
+
+    // This signature sucks, but it's the only one that will make a nsRefPtr
+    // just forget about its pointer without warning.
+    void
+    forget(void**)
+    {
+    }
+  };
+
+  JS::Rooted<JSObject*> mReflector;
+  typename Conditional<IsRefcounted<T>::value, nsRefPtr<T>, OwnedNative>::Type mNative;
+};
+
+template<class T,
          bool isISupports=IsBaseOf<nsISupports, T>::value>
 class DeferredFinalizer
 {
-  typedef nsTArray<SmartPtr<T> > SmartPtrArray;
+  typedef typename Conditional<IsRefcounted<T>::value,
+                               nsRefPtr<T>, nsAutoPtr<T>>::Type SmartPtr;
+  typedef nsTArray<SmartPtr> SmartPtrArray;
+
+  template<class U>
+  static inline void
+  AppendAndTake(nsTArray<nsRefPtr<U>>& smartPtrArray, U* ptr)
+  {
+    smartPtrArray.AppendElement(dont_AddRef(ptr));
+  }
+  template<class U>
+  static inline void
+  AppendAndTake(nsTArray<nsAutoPtr<U>>& smartPtrArray, U* ptr)
+  {
+    smartPtrArray.AppendElement(ptr);
+  }
 
   static void*
   AppendDeferredFinalizePointer(void* aData, void* aObject)
   {
     SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
     if (!pointers) {
       pointers = new SmartPtrArray();
     }
-
-    T* self = static_cast<T*>(aObject);
-
-    SmartPtr<T>* defer = pointers->AppendElement();
-    Take(*defer, self);
+    AppendAndTake(*pointers, static_cast<T*>(aObject));
     return pointers;
   }
   static bool
   DeferredFinalize(uint32_t aSlice, void* aData)
   {
     MOZ_ASSERT(aSlice > 0, "nonsensical/useless call with aSlice == 0");
     SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
     uint32_t oldLen = pointers->Length();
@@ -2795,32 +2893,32 @@ public:
   static void
   AddForDeferredFinalization(T* aObject)
   {
     cyclecollector::DeferredFinalize(AppendDeferredFinalizePointer,
                                      DeferredFinalize, aObject);
   }
 };
 
-template<class T, template <typename> class SmartPtr>
-class DeferredFinalizer<T, SmartPtr, true>
+template<class T>
+class DeferredFinalizer<T, true>
 {
 public:
   static void
   AddForDeferredFinalization(T* aObject)
   {
     cyclecollector::DeferredFinalize(reinterpret_cast<nsISupports*>(aObject));
   }
 };
 
-template<class T, template <typename> class SmartPtr>
+template<class T>
 static void
 AddForDeferredFinalization(T* aObject)
 {
-  DeferredFinalizer<T, SmartPtr>::AddForDeferredFinalization(aObject);
+  DeferredFinalizer<T>::AddForDeferredFinalization(aObject);
 }
 
 // This returns T's CC participant if it participates in CC or null if it
 // doesn't. This also returns null for classes that don't inherit from
 // nsISupports (QI should be used to get the participant for those).
 template<class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
 class GetCCParticipant
 {
@@ -3104,27 +3202,36 @@ WrappedJSToDictionary(nsISupports* aObje
 
 template<class T, class S>
 inline nsRefPtr<T>
 StrongOrRawPtr(already_AddRefed<S>&& aPtr)
 {
   return aPtr.template downcast<T>();
 }
 
-template<class T>
-inline T*
+template<class T,
+         class ReturnType=typename Conditional<IsRefcounted<T>::value, T*,
+                                               nsAutoPtr<T>>::Type>
+inline ReturnType
 StrongOrRawPtr(T* aPtr)
 {
-  return aPtr;
+  return ReturnType(aPtr);
 }
 
 template<class T, template<typename> class SmartPtr, class S>
 inline void
 StrongOrRawPtr(SmartPtr<S>&& aPtr) = delete;
 
+template<class T>
+struct StrongPtrForMember
+{
+  typedef typename Conditional<IsRefcounted<T>::value,
+                               nsRefPtr<T>, nsAutoPtr<T>>::Type Type;
+};
+
 inline
 JSObject*
 GetErrorPrototype(JSContext* aCx, JS::Handle<JSObject*> aForObj)
 {
   return JS_GetErrorPrototype(aCx);
 }
 
 } // namespace dom
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -39,32 +39,16 @@
 #                   in the WebIDL.
 #   * wrapperCache: True if this object is a wrapper cache.  Objects that are
 #                   not can only be returned from a limited set of methods,
 #                   cannot be prefable, and must ensure that they disallow
 #                   XPConnect wrapping.  Always false for callback interfaces.
 #                   Always true for worker descriptors for non-callback
 #                   interfaces.  Defaults to true for non-worker non-callback
 #                   descriptors.
-#   * nativeOwnership: Describes how the native object is held. 3 possible
-#                      types: worker object ('worker'), non-refcounted object
-#                      ('owned'), refcounted object ('refcounted').
-#                      Non-refcounted objects need to inherit from
-#                      mozilla::dom::NonRefcountedDOMObject and preferably use
-#                      MOZ_COUNT_CTOR/MOZ_COUNT_DTOR in their
-#                      constructor/destructor so they participate in leak
-#                      logging.
-#                      This mostly determines how the finalizer releases the
-#                      binding's hold on the native object. For a worker object
-#                      it'll call Release, for a non-refcounted object it'll
-#                      call delete through XPConnect's deferred finalization
-#                      mechanism, for a refcounted object it'll call Release
-#                      through XPConnect's deferred finalization mechanism.
-#                      'worker' opts into old style worker models. Defaults to
-#                      'refcounted'.
 #
 #   The following fields are either a string, an array (defaults to an empty
 #   array) or a dictionary with three possible keys (all, getterOnly and
 #   setterOnly) each having such an array as the value
 #
 #   * implicitJSContext - attributes and methods specified in the .webidl file
 #                         that require a JSContext as the first argument
 #
@@ -1222,25 +1206,25 @@ DOMInterfaces = {
 
 'Text': {
     # Total hack to allow binding code to realize that nsTextNode can
     # in fact be cast to Text.
     'headerFile': 'nsTextNode.h',
 },
 
 'TextDecoder': {
-    'nativeOwnership': 'owned',
+    'wrapperCache': False
 },
 
 'TextEncoder': {
-    'nativeOwnership': 'owned',
+    'wrapperCache': False
 },
 
 'TextMetrics': {
-    'nativeOwnership': 'owned',
+    'wrapperCache': False
 },
 
 'TimeRanges': {
     'wrapperCache': False
 },
 
 'TouchList': {
     'headerFile': 'mozilla/dom/TouchEvent.h',
@@ -1266,18 +1250,23 @@ DOMInterfaces = {
 'URL' : [{
     'wrapperCache': False,
 },
 {
     'workers': True,
     'wrapperCache': False,
 }],
 
+'VRFieldOfView': {
+    'wrapperCache': False,
+},
+
 'VRFieldOfViewReadOnly': {
-    'concrete': False
+    'concrete': False,
+    'wrapperCache': False,
 },
 
 'VRDevice': {
     'concrete': False
 },
 
 'VTTCue': {
     'nativeType': 'mozilla::dom::TextTrackCue'
@@ -1584,17 +1573,16 @@ DOMInterfaces = {
 },
 
 'XPathEvaluator': {
     'wrapperCache': False
 },
 
 'XPathExpression': {
     'wrapperCache': False,
-    'nativeOwnership': 'owned',
 },
 
 'XSLTProcessor': {
     'nativeType': 'txMozillaXSLTProcessor',
 },
 
 'XULDocument': {
     'headerFile': 'XULDocument.h'
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1560,34 +1560,26 @@ class CGAddPropertyHook(CGAbstractClassH
             // We don't want to preserve if we don't have a wrapper.
             if (self->GetWrapperPreserveColor()) {
               PreserveWrapper(self);
             }
             return true;
             """)
 
 
-def DeferredFinalizeSmartPtr(descriptor):
-    if descriptor.nativeOwnership == 'owned':
-        smartPtr = 'nsAutoPtr'
-    else:
-        smartPtr = 'nsRefPtr'
-    return smartPtr
-
-
 def finalizeHook(descriptor, hookName, freeOp):
     finalize = "JSBindingFinalized<%s>::Finalized(self);\n" % descriptor.nativeType
     if descriptor.wrapperCache:
         finalize += "ClearWrapper(self, self);\n"
     if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
         finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n"
     if descriptor.isGlobal():
         finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp
-    finalize += ("AddForDeferredFinalization<%s, %s >(self);\n" %
-                 (descriptor.nativeType, DeferredFinalizeSmartPtr(descriptor)))
+    finalize += ("AddForDeferredFinalization<%s>(self);\n" %
+                 descriptor.nativeType)
     return CGIfWrapper(CGGeneric(finalize), "self")
 
 
 class CGClassFinalizeHook(CGAbstractClassHook):
     """
     A hook for finalize, used to release our native object.
     """
     def __init__(self, descriptor):
@@ -3060,60 +3052,45 @@ class CGConstructorEnabled(CGAbstractMet
         else:
           conditionsWrapper = CGGeneric("return true;\n")
 
         body.append(conditionsWrapper)
         return body.define()
 
 
 def CreateBindingJSObject(descriptor, properties):
+    objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType
+
     # We don't always need to root obj, but there are a variety
     # of cases where we do, so for simplicity, just always root it.
-    objDecl = "JS::Rooted<JSObject*> obj(aCx);\n"
     if descriptor.proxy:
         create = dedent(
             """
-            JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aObject));
-            js::ProxyOptions options;
-            options.setClass(&Class.mBase);
-            obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
-                                 proxyPrivateVal, proto, global, options);
-            if (!obj) {
-              return nullptr;
+            creator.CreateProxyObject(aCx, &Class.mBase, DOMProxyHandler::getInstance(),
+                                      proto, global, aObject, aReflector);
+            if (!aReflector) {
+              return false;
             }
 
             """)
         if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
             create += dedent("""
-                js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO,
+                js::SetProxyExtra(aReflector, JSPROXYSLOT_EXPANDO,
                                   JS::PrivateValue(&aObject->mExpandoAndGeneration));
 
                 """)
     else:
         create = dedent(
             """
-            obj = JS_NewObject(aCx, Class.ToJSClass(), proto, global);
-            if (!obj) {
-              return nullptr;
-            }
-
-            js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
+            creator.CreateObject(aCx, Class.ToJSClass(), proto, global, aObject, aReflector);
+            if (!aReflector) {
+              return false;
+            }
             """)
-    create = objDecl + create
-
-    if descriptor.nativeOwnership == 'refcounted':
-        create += "NS_ADDREF(aObject);\n"
-    else:
-        create += dedent("""
-            // Make sure the native objects inherit from NonRefcountedDOMObject so that we
-            // log their ctor and dtor.
-            MustInheritFromNonRefcountedDOMObject(aObject);
-            *aTookOwnership = true;
-            """)
-    return create
+    return objDecl + create
 
 
 def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""):
     """
     properties is a PropertyArrays instance
     """
     unforgeables = []
 
@@ -3176,17 +3153,17 @@ def InitUnforgeableProperties(descriptor
         return ""
 
     if descriptor.proxy:
         unforgeableProperties = CGGeneric(
             "// Unforgeable properties on proxy-based bindings are stored in an object held\n"
             "// by the interface prototype object.\n")
     else:
         unforgeableProperties = CGWrapper(
-            InitUnforgeablePropertiesOnObject(descriptor, "obj", properties, "nullptr"),
+            InitUnforgeablePropertiesOnObject(descriptor, "aReflector", properties, "false"),
             pre=(
                 "// Important: do unforgeable property setup after we have handed\n"
                 "// over ownership of the C++ object to obj as needed, so that if\n"
                 "// we fail and it ends up GCed it won't have problems in the\n"
                 "// finalizer trying to drop its ownership of the C++ object.\n"))
     return CGWrapper(unforgeableProperties, pre="\n").define()
 
 
@@ -3214,73 +3191,72 @@ def InitMemberSlots(descriptor, wrapperC
     ClearWrapper.
     """
     if not descriptor.interface.hasMembersInSlots():
         return ""
     if wrapperCache:
         clearWrapper = "  aCache->ClearWrapper();\n"
     else:
         clearWrapper = ""
-    return ("if (!UpdateMemberSlots(aCx, obj, aObject)) {\n"
+    return ("if (!UpdateMemberSlots(aCx, aReflector, aObject)) {\n"
             "%s"
-            "  return nullptr;\n"
+            "  return false;\n"
             "}\n" % clearWrapper)
 
 
 class CGWrapWithCacheMethod(CGAbstractMethod):
     """
     Create a wrapper JSObject for a given native that implements nsWrapperCache.
 
     properties should be a PropertyArrays instance.
     """
     def __init__(self, descriptor, properties):
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'),
                 Argument(descriptor.nativeType + '*', 'aObject'),
-                Argument('nsWrapperCache*', 'aCache')]
-        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+                Argument('nsWrapperCache*', 'aCache'),
+                Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
+        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
         self.properties = properties
 
     def definition_body(self):
         return fill(
             """
             $*{assertion}
 
             MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
                        "nsISupports must be on our primary inheritance chain");
 
             JS::Rooted<JSObject*> parent(aCx, WrapNativeParent(aCx, aObject->GetParentObject()));
             if (!parent) {
-              return nullptr;
+              return false;
             }
 
             // That might have ended up wrapping us already, due to the wonders
-            // of XBL.  Check for that, and bail out as needed.  Scope so we don't
-            // collide with the "obj" we declare in CreateBindingJSObject.
-            {
-              JSObject* obj = aCache->GetWrapper();
-              if (obj) {
-                return obj;
-              }
+            // of XBL.  Check for that, and bail out as needed.
+            aReflector.set(aCache->GetWrapper());
+            if (aReflector) {
+              return true;
             }
 
             JSAutoCompartment ac(aCx, parent);
             JS::Rooted<JSObject*> global(aCx, js::GetGlobalForObjectCrossCompartment(parent));
             JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx, global);
             if (!proto) {
-              return nullptr;
+              return false;
             }
 
             $*{createObject}
 
             $*{unforgeable}
 
-            aCache->SetWrapper(obj);
+            aCache->SetWrapper(aReflector);
             $*{slots}
-            return obj;
+            creator.InitializationSucceeded();
+            return true;
             """,
             assertion=AssertInheritanceChain(self.descriptor),
             createObject=CreateBindingJSObject(self.descriptor, self.properties),
             unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
             slots=InitMemberSlots(self.descriptor, True))
 
 
 class CGWrapMethod(CGAbstractMethod):
@@ -3288,53 +3264,56 @@ class CGWrapMethod(CGAbstractMethod):
         # XXX can we wrap if we don't have an interface prototype object?
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'),
                 Argument('T*', 'aObject')]
         CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args,
                                   inline=True, templateArgs=["class T"])
 
     def definition_body(self):
-        return "return Wrap(aCx, aObject, aObject);\n"
+        return dedent("""
+            JS::Rooted<JSObject*> reflector(aCx);
+            return Wrap(aCx, aObject, aObject, &reflector) ? reflector.get() : nullptr;
+            """)
 
 
 class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
     """
     Create a wrapper JSObject for a given native that does not implement
     nsWrapperCache.
 
     properties should be a PropertyArrays instance.
     """
     def __init__(self, descriptor, properties):
         # XXX can we wrap if we don't have an interface prototype object?
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'),
-                Argument(descriptor.nativeType + '*', 'aObject')]
-        if descriptor.nativeOwnership == 'owned':
-            args.append(Argument('bool*', 'aTookOwnership'))
-        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+                Argument(descriptor.nativeType + '*', 'aObject'),
+                Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
+        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
         self.properties = properties
 
     def definition_body(self):
         return fill(
             """
             $*{assertions}
 
             JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
             JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx, global);
             if (!proto) {
-              return nullptr;
+              return false;
             }
 
             $*{createObject}
 
             $*{unforgeable}
 
             $*{slots}
-            return obj;
+            creator.InitializationSucceeded();
+            return true;
             """,
             assertions=AssertInheritanceChain(self.descriptor),
             createObject=CreateBindingJSObject(self.descriptor, self.properties),
             unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
             slots=InitMemberSlots(self.descriptor, False))
 
 
 class CGWrapGlobalMethod(CGAbstractMethod):
@@ -3346,70 +3325,70 @@ class CGWrapGlobalMethod(CGAbstractMetho
     """
     def __init__(self, descriptor, properties):
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'),
                 Argument(descriptor.nativeType + '*', 'aObject'),
                 Argument('nsWrapperCache*', 'aCache'),
                 Argument('JS::CompartmentOptions&', 'aOptions'),
                 Argument('JSPrincipals*', 'aPrincipal'),
-                Argument('bool', 'aInitStandardClasses')]
-        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+                Argument('bool', 'aInitStandardClasses'),
+                Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
+        CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
         self.descriptor = descriptor
         self.properties = properties
 
     def definition_body(self):
         if self.properties.hasNonChromeOnly():
             properties = "GlobalPropertiesAreOwn() ? &sNativeProperties : nullptr"
         else:
             properties = "nullptr"
         if self.properties.hasChromeOnly():
             chromeProperties = "GlobalPropertiesAreOwn() && nsContentUtils::ThreadsafeIsCallerChrome() ? &sChromeOnlyNativeProperties : nullptr"
         else:
             chromeProperties = "nullptr"
 
         if self.descriptor.workers:
             fireOnNewGlobal = """// XXXkhuey can't do this yet until workers can lazy resolve.
-// JS_FireOnNewGlobalObject(aCx, obj);
+// JS_FireOnNewGlobalObject(aCx, aReflector);
 """
         else:
             fireOnNewGlobal = ""
 
         return fill(
             """
             $*{assertions}
             MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
                        "nsISupports must be on our primary inheritance chain");
 
-            JS::Rooted<JSObject*> obj(aCx);
             CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx,
                                              aObject,
                                              aCache,
                                              Class.ToJSClass(),
                                              aOptions,
                                              aPrincipal,
                                              aInitStandardClasses,
-                                             &obj);
-            if (!obj) {
-              return nullptr;
-            }
-
-            // obj is a new global, so has a new compartment.  Enter it
+                                             aReflector);
+            if (!aReflector) {
+              return false;
+            }
+
+            // aReflector is a new global, so has a new compartment.  Enter it
             // before doing anything with it.
-            JSAutoCompartment ac(aCx, obj);
-
-            if (!DefineProperties(aCx, obj, ${properties}, ${chromeProperties})) {
-              return nullptr;
+            JSAutoCompartment ac(aCx, aReflector);
+
+            if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) {
+              return false;
             }
             $*{unforgeable}
 
             $*{slots}
             $*{fireOnNewGlobal}
 
-            return obj;
+            return true;
             """,
             assertions=AssertInheritanceChain(self.descriptor),
             nativeType=self.descriptor.nativeType,
             properties=properties,
             chromeProperties=chromeProperties,
             unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
             slots=InitMemberSlots(self.descriptor, True),
             fireOnNewGlobal=fireOnNewGlobal)
@@ -4699,22 +4678,16 @@ def getJSToNativeConversionInfo(type, de
         # addrefing anyway, so there is no point trying to avoid it here and it
         # makes other things simpler since we can assume the return value is a
         # strong ref.
         forceOwningType = ((descriptor.interface.isCallback() and
                             not descriptor.workers) or
                            isMember or
                            isCallbackReturnValue)
 
-        if forceOwningType and descriptor.nativeOwnership == 'owned':
-            raise TypeError("Interface %s has 'owned' nativeOwnership, so we "
-                            "don't know how to keep it alive in %s" %
-                            (descriptor.interface.identifier.name,
-                             sourceDescription))
-
         typeName = descriptor.nativeType
         typePtr = typeName + "*"
 
         # Compute a few things:
         #  - declType is the type we want to return as the first element of our
         #    tuple.
         #  - holderType is the type we want to return as the third element
         #    of our tuple.
@@ -4728,16 +4701,18 @@ def getJSToNativeConversionInfo(type, de
                 declType = typePtr
         else:
             if forceOwningType:
                 declType = "OwningNonNull<" + typeName + ">"
             else:
                 declType = "NonNull<" + typeName + ">"
 
         templateBody = ""
+        if forceOwningType:
+            templateBody += 'static_assert(IsRefcounted<%s>::value, "We can only store refcounted classes.");' % typeName
         if not descriptor.skipGen and not descriptor.interface.isConsequential() and not descriptor.interface.isExternal():
             if failureCode is not None:
                 templateBody += str(CastableObjectUnwrapper(
                     descriptor,
                     "&${val}.toObject()",
                     "${declName}",
                     failureCode))
             else:
@@ -5764,25 +5739,21 @@ def getWrapTemplateForType(type, descrip
             wrappingCode = ("if (!%s) {\n" % (result) +
                             indent(setNull()) +
                             "}\n")
         else:
             wrappingCode = ""
 
         if not descriptor.interface.isExternal() and not descriptor.skipGen:
             if descriptor.wrapperCache:
-                assert descriptor.nativeOwnership != 'owned'
                 wrapMethod = "GetOrCreateDOMReflector"
             else:
                 if not returnsNewObject:
                     raise MethodNotNewObjectError(descriptor.interface.identifier.name)
-                if descriptor.nativeOwnership == 'owned':
-                    wrapMethod = "WrapNewBindingNonWrapperCachedOwnedObject"
-                else:
-                    wrapMethod = "WrapNewBindingNonWrapperCachedObject"
+                wrapMethod = "WrapNewBindingNonWrapperCachedObject"
             wrap = "%s(cx, ${obj}, %s, ${jsvalHandle})" % (wrapMethod, result)
             if not descriptor.hasXPConnectImpls:
                 # Can only fail to wrap as a new-binding object
                 # if they already threw an exception.
                 #XXX Assertion disabled for now, see bug 991271.
                 failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" +
                           exceptionCode)
             else:
@@ -6093,21 +6064,18 @@ def getRetvalDeclarationForType(returnTy
         result = CGGeneric(returnType.unroll().inner.identifier.name)
         if returnType.nullable():
             result = CGTemplatedType("Nullable", result)
         return result, None, None, None, None
     if returnType.isGeckoInterface():
         result = CGGeneric(descriptorProvider.getDescriptor(
             returnType.unroll().inner.identifier.name).nativeType)
         conversion = None
-        if descriptorProvider.getDescriptor(
-                returnType.unroll().inner.identifier.name).nativeOwnership == 'owned':
-            result = CGTemplatedType("nsAutoPtr", result)
-        elif isMember:
-            result = CGTemplatedType("nsRefPtr", result)
+        if isMember:
+            result = CGGeneric("StrongPtrForMember<%s>::Type" % result.define())
         else:
             conversion = CGGeneric("StrongOrRawPtr<%s>" % result.define())
             result = CGGeneric("auto")
         return result, None, None, None, conversion
     if returnType.isCallback():
         name = returnType.unroll().identifier.name
         return CGGeneric("nsRefPtr<%s>" % name), None, None, None, None
     if returnType.isAny():
@@ -6688,18 +6656,17 @@ class CGPerSignatureCall(CGThing):
     def isFallible(self):
         return 'infallible' not in self.extendedAttributes
 
     def wrap_return_value(self):
         wrapCode = ""
 
         returnsNewObject = memberReturnsNewObject(self.idlNode)
         if (returnsNewObject and
-            self.returnType.isGeckoInterface() and
-            not self.descriptor.getDescriptor(self.returnType.unroll().inner.identifier.name).nativeOwnership == 'owned'):
+            self.returnType.isGeckoInterface()):
             wrapCode += dedent(
                 """
                 static_assert(!IsPointer<decltype(result)>::value,
                               "NewObject implies that we need to keep the object alive with a strong reference.");
                 """)
 
         setSlot = self.idlNode.isAttr() and self.idlNode.slotIndex is not None
         if setSlot:
@@ -10989,26 +10956,27 @@ def memberProperties(m, descriptor):
     return props
 
 class CGDescriptor(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
 
         assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
 
-        if descriptor.nativeOwnership == 'owned' and (
-                descriptor.interface.hasChildInterfaces() or
-                descriptor.interface.parent):
-            raise TypeError("Owned interface cannot have a parent or children")
-
         self._deps = descriptor.interface.getDeps()
 
         cgThings = []
         cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" %
                                   descriptor.nativeType))
+        parent = descriptor.interface.parent
+        if parent:
+            cgThings.append(CGGeneric("static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
+                                      "              \"Can't inherit from an interface with a different ownership model.\");\n" %
+                                      toBindingNamespace(descriptor.parentPrototypeName)))
+
         # These are set to true if at least one non-static
         # method/getter/setter or jsonifier exist on the interface.
         (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasLenientSetter,
             hasPromiseReturningMethod) = False, False, False, False, False, False
         jsonifierMethod = None
         crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
         for n in descriptor.interface.namedConstructors:
             cgThings.append(CGClassConstructor(descriptor, n,
@@ -12208,17 +12176,20 @@ class CGForwardDeclarations(CGWrapper):
 
 
 class CGBindingRoot(CGThing):
     """
     Root codegen class for binding generation. Instantiate the class, and call
     declare or define to generate header or cpp code (respectively).
     """
     def __init__(self, config, prefix, webIDLFile):
-        bindingHeaders = {}
+        bindingHeaders = dict.fromkeys((
+            'mozilla/dom/NonRefcountedDOMObject.h',
+            ),
+            True)
         bindingDeclareHeaders = dict.fromkeys((
             'mozilla/dom/BindingDeclarations.h',
             'mozilla/dom/Nullable.h',
             'mozilla/ErrorResult.h',
             ),
             True)
 
         descriptors = config.getDescriptors(webIDLFile=webIDLFile,
@@ -12250,18 +12221,16 @@ class CGBindingRoot(CGThing):
         bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"]
 
         def descriptorRequiresPreferences(desc):
             iface = desc.interface
             return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface])
 
         bindingHeaders["mozilla/Preferences.h"] = any(
             descriptorRequiresPreferences(d) for d in descriptors)
-        bindingHeaders["mozilla/dom/NonRefcountedDOMObject.h"] = any(
-            d.nativeOwnership == 'owned' for d in descriptors)
         bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
             d.concrete and d.proxy for d in descriptors)
 
         def descriptorHasChromeOnly(desc):
             return (any(isChromeOnly(a) for a in desc.interface.members) or
                     desc.interface.getExtendedAttribute("ChromeOnly") is not None or
                     # JS-implemented interfaces with an interface object get a
                     # chromeonly _create method.
@@ -13061,50 +13030,40 @@ class CGExampleClass(CGBindingImplClass)
     """
     Codegen for the actual example class implementation for this descriptor
     """
     def __init__(self, descriptor):
         CGBindingImplClass.__init__(self, descriptor,
                                     CGExampleMethod, CGExampleGetter, CGExampleSetter,
                                     wantGetParent=descriptor.wrapperCache)
 
-        self.refcounted = descriptor.nativeOwnership == "refcounted"
-
         self.parentIface = descriptor.interface.parent
         if self.parentIface:
             self.parentDesc = descriptor.getDescriptor(
                 self.parentIface.identifier.name)
             bases = [ClassBase(self.nativeLeafName(self.parentDesc))]
         else:
-            bases = []
-            if self.refcounted:
-                bases.append(ClassBase("nsISupports /* Change nativeOwnership in the binding configuration if you don't want this */"))
-                if descriptor.wrapperCache:
-                    bases.append(ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"))
-            else:
-                bases.append(ClassBase("NonRefcountedDOMObject"))
-
-        if self.refcounted:
-            destructorVisibility = "protected"
-            if self.parentIface:
-                extradeclarations = (
-                    "public:\n"
-                    "  NS_DECL_ISUPPORTS_INHERITED\n"
-                    "  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
-                    "\n" % (self.nativeLeafName(descriptor),
-                            self.nativeLeafName(self.parentDesc)))
-            else:
-                extradeclarations = (
-                    "public:\n"
-                    "  NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
-                    "  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n"
-                    "\n" % self.nativeLeafName(descriptor))
-        else:
-            destructorVisibility = "public"
-            extradeclarations = ""
+            bases = [ ClassBase("nsISupports /* or NonRefcountedDOMObject if this is a non-refcounted object */") ]
+            if descriptor.wrapperCache:
+                bases.append(ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"))
+
+        destructorVisibility = "protected"
+        if self.parentIface:
+            extradeclarations = (
+                "public:\n"
+                "  NS_DECL_ISUPPORTS_INHERITED\n"
+                "  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
+                "\n" % (self.nativeLeafName(descriptor),
+                        self.nativeLeafName(self.parentDesc)))
+        else:
+            extradeclarations = (
+                "public:\n"
+                "  NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
+                "  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n"
+                "\n" % self.nativeLeafName(descriptor))
 
         if descriptor.interface.hasChildInterfaces():
             decorators = ""
         else:
             decorators = "MOZ_FINAL"
 
         CGClass.__init__(self, self.nativeLeafName(descriptor),
                          bases=bases,
@@ -13112,64 +13071,52 @@ class CGExampleClass(CGBindingImplClass)
                                                         visibility="public")],
                          destructor=ClassDestructor(visibility=destructorVisibility),
                          methods=self.methodDecls,
                          decorators=decorators,
                          extradeclarations=extradeclarations)
 
     def define(self):
         # Just override CGClass and do our own thing
-        if self.refcounted:
-            ctordtor = dedent("""
-                ${nativeType}::${nativeType}()
-                {
-                }
-
-                ${nativeType}::~${nativeType}()
-                {
-                }
-                """)
-        else:
-            ctordtor = dedent("""
-                ${nativeType}::${nativeType}()
-                {
-                  MOZ_COUNT_CTOR(${nativeType});
-                }
-
-                ${nativeType}::~${nativeType}()
-                {
-                  MOZ_COUNT_DTOR(${nativeType});
-                }
+        ctordtor = dedent("""
+            ${nativeType}::${nativeType}()
+            {
+                // Add |MOZ_COUNT_CTOR(${nativeType});| for a non-refcounted object.
+            }
+
+            ${nativeType}::~${nativeType}()
+            {
+                // Add |MOZ_COUNT_DTOR(${nativeType});| for a non-refcounted object.
+            }
+            """)
+
+        if self.parentIface:
+            ccImpl = dedent("""
+
+                // Only needed for refcounted objects.
+                NS_IMPL_CYCLE_COLLECTION_INHERITED_0(${nativeType}, ${parentType})
+                NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
+                NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
+                NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
+                NS_INTERFACE_MAP_END_INHERITING(${parentType})
+
                 """)
-
-        if self.refcounted:
-            if self.parentIface:
-                ccImpl = dedent("""
-
-                    NS_IMPL_CYCLE_COLLECTION_INHERITED_0(${nativeType}, ${parentType})
-                    NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
-                    NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
-                    NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
-                    NS_INTERFACE_MAP_END_INHERITING(${parentType})
-
-                    """)
-            else:
-                ccImpl = dedent("""
-
-                    NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
-                    NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
-                    NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
-                    NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
-                      NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-                      NS_INTERFACE_MAP_ENTRY(nsISupports)
-                    NS_INTERFACE_MAP_END
-
-                    """)
-        else:
-            ccImpl = ""
+        else:
+            ccImpl = dedent("""
+
+                // Only needed for refcounted objects.
+                NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
+                NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
+                NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
+                NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
+                  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+                  NS_INTERFACE_MAP_ENTRY(nsISupports)
+                NS_INTERFACE_MAP_END
+
+                """)
 
         classImpl = ccImpl + ctordtor + "\n" + dedent("""
             JSObject*
             ${nativeType}::WrapObject(JSContext* aCx)
             {
               return ${ifaceName}Binding::Wrap(aCx, this);
             }
 
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -463,26 +463,20 @@ class Descriptor(DescriptorProvider):
                     raise SyntaxError("%s supports named properties but does "
                                       "not have a named getter.\n%s" %
                                       (self.interface, self.interface.location))
                 iface = self.interface
                 while iface:
                     iface.setUserData('hasProxyDescendant', True)
                     iface = iface.parent
 
-        self.nativeOwnership = desc.get('nativeOwnership', 'refcounted')
-        if not self.nativeOwnership in ('owned', 'refcounted'):
-            raise TypeError("Descriptor for %s has unrecognized value (%s) "
-                            "for nativeOwnership" %
-                            (self.interface.identifier.name, self.nativeOwnership))
         if desc.get('wantsQI', None) != None:
             self._wantsQI = desc.get('wantsQI', None)
         self.wrapperCache = (not self.interface.isCallback() and
-                             (self.nativeOwnership != 'owned' and
-                              desc.get('wrapperCache', True)))
+                             desc.get('wrapperCache', True))
 
         def make_name(name):
             return name + "_workers" if self.workers else name
         self.name = make_name(interface.identifier.name)
 
         # self.extendedAttributes is a dict of dicts, keyed on
         # all/getterOnly/setterOnly and then on member name. Values are an
         # array of extended attributes.
--- a/dom/bindings/NonRefcountedDOMObject.h
+++ b/dom/bindings/NonRefcountedDOMObject.h
@@ -7,22 +7,22 @@
 #ifndef mozilla_dom_NonRefcountedDOMObject_h__
 #define mozilla_dom_NonRefcountedDOMObject_h__
 
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace dom {
 
-// Natives for DOM classes with 'owned' as the value for nativeOwnership in
-// Bindings.conf need to inherit from this class.
+// Natives for DOM classes that aren't refcounted need to inherit from this
+// class.
 // If you're seeing objects of this class leak then natives for one of the DOM
-// classes with 'owned' as the value for nativeOwnership in Bindings.conf is
-// leaking. If the native for that class has MOZ_COUNT_CTOR/DTOR in its
-// constructor/destructor then it should show up in the leak log too.
+// classes inheriting from it is leaking. If the native for that class has
+// MOZ_COUNT_CTOR/DTOR in its constructor/destructor then it should show up in
+// the leak log too.
 class NonRefcountedDOMObject
 {
 protected:
   NonRefcountedDOMObject()
   {
     MOZ_COUNT_CTOR(NonRefcountedDOMObject);
   }
   ~NonRefcountedDOMObject()
--- a/dom/bindings/StructuredClone.cpp
+++ b/dom/bindings/StructuredClone.cpp
@@ -27,17 +27,19 @@ ReadStructuredCloneImageData(JSContext* 
 
   // Protect the result from a moving GC in ~nsRefPtr.
   JS::Rooted<JSObject*> result(aCx);
   {
     // Construct the ImageData.
     nsRefPtr<ImageData> imageData = new ImageData(width, height,
                                                   dataArray.toObject());
     // Wrap it in a JS::Value.
-    result = imageData->WrapObject(aCx);
+    if (!imageData->WrapObject(aCx, &result)) {
+      return nullptr;
+    }
   }
   return result;
 }
 
 bool
 WriteStructuredCloneImageData(JSContext* aCx, JSStructuredCloneWriter* aWriter,
                               ImageData* aImageData)
 {
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -85,17 +85,17 @@ public:
 
 NS_DEFINE_STATIC_IID_ACCESSOR(TestExternalInterface, NS_TEST_EXTERNAL_INTERFACE_IID)
 
 class TestNonWrapperCacheInterface : public nsISupports
 {
 public:
   NS_DECL_ISUPPORTS
 
-  virtual JSObject* WrapObject(JSContext* cx);
+  bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 };
 
 class OnlyForUseInConstructor : public nsISupports,
                                 public nsWrapperCache
 {
 public:
   NS_DECL_ISUPPORTS
   // We need a GetParentObject to make binding codegen happy
--- a/dom/canvas/ImageData.cpp
+++ b/dom/canvas/ImageData.cpp
@@ -103,16 +103,16 @@ void
 ImageData::DropData()
 {
   if (mData) {
     mData = nullptr;
     mozilla::DropJSObjects(this);
   }
 }
 
-JSObject*
-ImageData::WrapObject(JSContext* cx)
+bool
+ImageData::WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector)
 {
-  return ImageDataBinding::Wrap(cx, this);
+  return ImageDataBinding::Wrap(aCx, this, aReflector);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/canvas/ImageData.h
+++ b/dom/canvas/ImageData.h
@@ -68,17 +68,17 @@ public:
     aData.set(GetDataObject());
   }
   JSObject* GetDataObject() const
   {
     JS::ExposeObjectToActiveJS(mData);
     return mData;
   }
 
-  JSObject* WrapObject(JSContext* cx);
+  bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 
 private:
   void HoldData();
   void DropData();
 
   ImageData() = delete;
 
   uint32_t mWidth, mHeight;
--- a/dom/canvas/TextMetrics.h
+++ b/dom/canvas/TextMetrics.h
@@ -25,19 +25,19 @@ public:
     MOZ_COUNT_DTOR(TextMetrics);
   }
 
   float Width() const
   {
     return width;
   }
 
-  JSObject* WrapObject(JSContext* aCx, bool* aTookOwnership)
+  bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector)
   {
-    return TextMetricsBinding::Wrap(aCx, this, aTookOwnership);
+    return TextMetricsBinding::Wrap(aCx, this, aReflector);
   }
 
 private:
   float width;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/canvas/WebGLActiveInfo.cpp
+++ b/dom/canvas/WebGLActiveInfo.cpp
@@ -6,15 +6,16 @@
 #include "WebGLActiveInfo.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 
-JSObject*
-WebGLActiveInfo::WrapObject(JSContext* cx)
+bool
+WebGLActiveInfo::WrapObject(JSContext* aCx,
+                            JS::MutableHandle<JSObject*> aReflector)
 {
-    return dom::WebGLActiveInfoBinding::Wrap(cx, this);
+    return dom::WebGLActiveInfoBinding::Wrap(aCx, this, aReflector);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLActiveInfo.h
+++ b/dom/canvas/WebGLActiveInfo.h
@@ -30,17 +30,17 @@ public:
     GLenum Type() const {
         return mType;
     }
 
     void GetName(nsString& retval) const {
         retval = mName;
     }
 
-    JSObject* WrapObject(JSContext* cx);
+    bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 
    NS_INLINE_DECL_REFCOUNTING(WebGLActiveInfo)
 
 private:
     // Private destructor, to discourage deletion outside of Release():
     ~WebGLActiveInfo()
     {
     }
--- a/dom/canvas/WebGLShaderPrecisionFormat.cpp
+++ b/dom/canvas/WebGLShaderPrecisionFormat.cpp
@@ -5,15 +5,16 @@
 
 #include "WebGLShaderPrecisionFormat.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
-JSObject*
-WebGLShaderPrecisionFormat::WrapObject(JSContext* cx)
+bool
+WebGLShaderPrecisionFormat::WrapObject(JSContext* aCx,
+                                       JS::MutableHandle<JSObject*> aReflector)
 {
-    return dom::WebGLShaderPrecisionFormatBinding::Wrap(cx, this);
+    return dom::WebGLShaderPrecisionFormatBinding::Wrap(aCx, this, aReflector);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLShaderPrecisionFormat.h
+++ b/dom/canvas/WebGLShaderPrecisionFormat.h
@@ -19,17 +19,17 @@ public:
     WebGLShaderPrecisionFormat(WebGLContext* context, GLint rangeMin,
                                GLint rangeMax, GLint precision)
         : WebGLContextBoundObject(context)
         , mRangeMin(rangeMin)
         , mRangeMax(rangeMax)
         , mPrecision(precision)
     { }
 
-    JSObject* WrapObject(JSContext* cx);
+    bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 
     // WebIDL WebGLShaderPrecisionFormat API
     GLint RangeMin() const {
         return mRangeMin;
     }
 
     GLint RangeMax() const {
         return mRangeMax;
--- a/dom/canvas/WebGLUniformLocation.cpp
+++ b/dom/canvas/WebGLUniformLocation.cpp
@@ -7,20 +7,21 @@
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLProgram.h"
 #include "WebGLShader.h"
 
 namespace mozilla {
 
-JSObject*
-WebGLUniformLocation::WrapObject(JSContext* cx)
+bool
+WebGLUniformLocation::WrapObject(JSContext* aCx,
+                                 JS::MutableHandle<JSObject*> aReflector)
 {
-    return dom::WebGLUniformLocationBinding::Wrap(cx, this);
+    return dom::WebGLUniformLocationBinding::Wrap(aCx, this, aReflector);
 }
 
 WebGLUniformLocation::WebGLUniformLocation(WebGLContext* context,
                                            WebGLProgram* program,
                                            GLint location,
                                            const WebGLUniformInfo& info)
     : WebGLContextBoundObject(context)
     , mProgram(program)
--- a/dom/canvas/WebGLUniformLocation.h
+++ b/dom/canvas/WebGLUniformLocation.h
@@ -26,17 +26,17 @@ public:
 
     const WebGLUniformInfo& Info() const { return mInfo; }
 
     WebGLProgram* Program() const { return mProgram; }
     GLint Location() const { return mLocation; }
     uint32_t ProgramGeneration() const { return mProgramGeneration; }
     int ElementSize() const { return mElementSize; }
 
-    JSObject* WrapObject(JSContext* cx);
+    bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocation)
     NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WebGLUniformLocation)
 
 protected:
     ~WebGLUniformLocation() { }
 
     // nsRefPtr, not WebGLRefPtr, so that we don't prevent the program from being explicitly deleted.
--- a/dom/datastore/DataStoreCursor.cpp
+++ b/dom/datastore/DataStoreCursor.cpp
@@ -24,20 +24,21 @@ NS_IMPL_CYCLE_COLLECTION(DataStoreCursor
 
 already_AddRefed<DataStoreCursor>
 DataStoreCursor::Constructor(GlobalObject& aGlobal, ErrorResult& aRv)
 {
   nsRefPtr<DataStoreCursor> cursor = new DataStoreCursor();
   return cursor.forget();
 }
 
-JSObject*
-DataStoreCursor::WrapObject(JSContext* aCx)
+bool
+DataStoreCursor::WrapObject(JSContext* aCx,
+                            JS::MutableHandle<JSObject*> aReflector)
 {
-  return DataStoreCursorBinding::Wrap(aCx, this);
+  return DataStoreCursorBinding::Wrap(aCx, this, aReflector);
 }
 
 already_AddRefed<DataStore>
 DataStoreCursor::GetStore(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mCursor);
 
@@ -67,9 +68,9 @@ DataStoreCursor::SetDataStoreCursorImpl(
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mCursor);
 
   mCursor = &aCursor;
 }
 
 } //namespace dom
-} //namespace mozilla
\ No newline at end of file
+} //namespace mozilla
--- a/dom/datastore/DataStoreCursor.h
+++ b/dom/datastore/DataStoreCursor.h
@@ -28,17 +28,17 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(DataStoreCursor)
 
   // WebIDL (internal functions)
 
   static already_AddRefed<DataStoreCursor> Constructor(GlobalObject& aGlobal,
                                                        ErrorResult& aRv);
 
-  JSObject* WrapObject(JSContext *aCx);
+  bool WrapObject(JSContext *aCx, JS::MutableHandle<JSObject*> aReflector);
 
   // WebIDL (public APIs)
 
   already_AddRefed<DataStore> GetStore(ErrorResult& aRv);
 
   already_AddRefed<Promise> Next(ErrorResult& aRv);
 
   void Close(ErrorResult& aRv);
@@ -51,9 +51,9 @@ public:
 private:
   ~DataStoreCursor() {}
   nsRefPtr<DataStoreCursorImpl> mCursor;
 };
 
 } //namespace dom
 } //namespace mozilla
 
-#endif
\ No newline at end of file
+#endif
--- a/dom/encoding/TextDecoder.h
+++ b/dom/encoding/TextDecoder.h
@@ -44,19 +44,19 @@ public:
     MOZ_COUNT_CTOR(TextDecoder);
   }
 
   ~TextDecoder()
   {
     MOZ_COUNT_DTOR(TextDecoder);
   }
 
-  JSObject* WrapObject(JSContext* aCx, bool* aTookOwnership)
+  bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector)
   {
-    return TextDecoderBinding::Wrap(aCx, this, aTookOwnership);
+    return TextDecoderBinding::Wrap(aCx, this, aReflector);
   }
 
   /**
    * Validates provided label and throws an exception if invalid label.
    *
    * @param aLabel       The encoding label (case insensitive) provided.
    * @param aFatal       indicates whether to throw an 'EncodingError'
    *                     exception or not when decoding.
--- a/dom/encoding/TextEncoder.h
+++ b/dom/encoding/TextEncoder.h
@@ -37,19 +37,19 @@ public:
   TextEncoder()
   {
   }
 
   virtual
   ~TextEncoder()
   {}
 
-  JSObject* WrapObject(JSContext* aCx, bool* aTookOwnership)
+  bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector)
   {
-    return TextEncoderBinding::Wrap(aCx, this, aTookOwnership);
+    return TextEncoderBinding::Wrap(aCx, this, aReflector);
   }
 
 protected:
 
   /**
    * Validates provided encoding and throws an exception if invalid encoding.
    * If no encoding is provided then mEncoding is default initialised to "utf-8".
    *
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -2,85 +2,148 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Fetch.h"
 
 #include "nsIDocument.h"
 #include "nsIGlobalObject.h"
-#include "nsIStringStream.h"
+#include "nsIStreamLoader.h"
+#include "nsIThreadRetargetableRequest.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsIUnicodeEncoder.h"
 
 #include "nsDOMString.h"
 #include "nsNetUtil.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/FetchDriver.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/URLSearchParams.h"
-#include "mozilla/dom/WorkerScope.h"
-#include "mozilla/dom/workers/Workers.h"
 
 #include "InternalResponse.h"
+
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
+#include "WorkerScope.h"
+#include "Workers.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
 
-class WorkerFetchResolver MOZ_FINAL : public FetchDriverObserver
+class WorkerFetchResolver MOZ_FINAL : public FetchDriverObserver,
+                                      public WorkerFeature
 {
+  friend class MainThreadFetchRunnable;
+  friend class WorkerFetchResponseEndRunnable;
   friend class WorkerFetchResponseRunnable;
-  friend class ResolveFetchWithBodyRunnable;
 
-  // This promise proxy is for the Promise returned by a call to fetch() that
-  // is resolved with a Response instance.
-  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
-  // Passed from main thread to worker thread after being initialized (except
-  // for the body.
-  nsRefPtr<InternalResponse> mInternalResponse;
+  workers::WorkerPrivate* mWorkerPrivate;
+
+  Mutex mCleanUpLock;
+  bool mCleanedUp;
+  // The following are initialized and used exclusively on the worker thread.
+  nsRefPtr<Promise> mFetchPromise;
+  nsRefPtr<Response> mResponse;
 public:
 
-  WorkerFetchResolver(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise);
+  WorkerFetchResolver(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise)
+    : mWorkerPrivate(aWorkerPrivate)
+    , mCleanUpLock("WorkerFetchResolver")
+    , mCleanedUp(false)
+    , mFetchPromise(aPromise)
+  {
+  }
 
   void
   OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE;
 
+  void
+  OnResponseEnd() MOZ_OVERRIDE;
+
+  bool
+  Notify(JSContext* aCx, Status aStatus) MOZ_OVERRIDE
+  {
+    if (aStatus > Running) {
+      CleanUp(aCx);
+    }
+    return true;
+  }
+
+  void
+  CleanUp(JSContext* aCx)
+  {
+    MutexAutoLock lock(mCleanUpLock);
+
+    if (mCleanedUp) {
+      return;
+    }
+
+    MOZ_ASSERT(mWorkerPrivate);
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
+
+    mWorkerPrivate->RemoveFeature(aCx, this);
+    CleanUpUnchecked();
+  }
+
+  void
+  CleanUpUnchecked()
+  {
+    mResponse = nullptr;
+    if (mFetchPromise) {
+      mFetchPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+      mFetchPromise = nullptr;
+    }
+    mCleanedUp = true;
+  }
+
   workers::WorkerPrivate*
-  GetWorkerPrivate() { return mPromiseProxy->GetWorkerPrivate(); }
+  GetWorkerPrivate() const
+  {
+    // It's ok to race on |mCleanedUp|, because it will never cause us to fire
+    // the assertion when we should not.
+    MOZ_ASSERT(!mCleanedUp);
+    return mWorkerPrivate;
+  }
 
 private:
-  ~WorkerFetchResolver();
+  ~WorkerFetchResolver()
+  {
+    MOZ_ASSERT(mCleanedUp);
+    MOZ_ASSERT(!mFetchPromise);
+  }
 };
 
 class MainThreadFetchResolver MOZ_FINAL : public FetchDriverObserver
 {
   nsRefPtr<Promise> mPromise;
-  nsRefPtr<InternalResponse> mInternalResponse;
+  nsRefPtr<Response> mResponse;
 
   NS_DECL_OWNINGTHREAD
 public:
   explicit MainThreadFetchResolver(Promise* aPromise);
 
   void
   OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE;
 
+  void
+  OnResponseEnd() MOZ_OVERRIDE;
+
 private:
   ~MainThreadFetchResolver();
 };
 
 class MainThreadFetchRunnable : public nsRunnable
 {
   nsRefPtr<WorkerFetchResolver> mResolver;
   nsRefPtr<InternalRequest> mRequest;
@@ -89,26 +152,37 @@ public:
   MainThreadFetchRunnable(WorkerPrivate* aWorkerPrivate,
                           Promise* aPromise,
                           InternalRequest* aRequest)
     : mResolver(new WorkerFetchResolver(aWorkerPrivate, aPromise))
     , mRequest(aRequest)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
+    if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), mResolver)) {
+      NS_WARNING("Could not add WorkerFetchResolver feature to worker");
+      mResolver->CleanUpUnchecked();
+      mResolver = nullptr;
+    }
   }
 
   NS_IMETHODIMP
   Run()
   {
     AssertIsOnMainThread();
-    nsRefPtr<FetchDriver> fetch = new FetchDriver(mRequest);
+    // AddFeature() call failed, don't bother running.
+    if (!mResolver) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIPrincipal> principal = mResolver->GetWorkerPrivate()->GetPrincipal();
+    nsCOMPtr<nsILoadGroup> loadGroup = mResolver->GetWorkerPrivate()->GetLoadGroup();
+    nsRefPtr<FetchDriver> fetch = new FetchDriver(mRequest, principal, loadGroup);
     nsresult rv = fetch->Fetch(mResolver);
     // Right now we only support async fetch, which should never directly fail.
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 };
 
 already_AddRefed<Promise>
@@ -138,18 +212,32 @@ FetchRequest(nsIGlobalObject* aGlobal, c
     aRv = GetRequestReferrer(aGlobal, r, ref);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
     r->SetReferrer(ref);
   }
 
   if (NS_IsMainThread()) {
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
+    if (!window) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
+    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+    if (!doc) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
     nsRefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p);
-    nsRefPtr<FetchDriver> fetch = new FetchDriver(r);
+    nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
+    nsRefPtr<FetchDriver> fetch =
+      new FetchDriver(r, doc->NodePrincipal(), loadGroup);
     aRv = fetch->Fetch(resolver);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
   } else {
     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(worker);
     nsRefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(worker, p, r);
@@ -166,82 +254,125 @@ MainThreadFetchResolver::MainThreadFetch
 {
 }
 
 void
 MainThreadFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
 {
   NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
   AssertIsOnMainThread();
-  mInternalResponse = aResponse;
 
   nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
+  mResponse = new Response(go, aResponse);
+  mPromise->MaybeResolve(mResponse);
+}
 
-  nsRefPtr<Response> response = new Response(go, aResponse);
-  mPromise->MaybeResolve(response);
+void
+MainThreadFetchResolver::OnResponseEnd()
+{
+  NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
+  AssertIsOnMainThread();
+  MOZ_ASSERT(mResponse);
 }
 
 MainThreadFetchResolver::~MainThreadFetchResolver()
 {
   NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
 }
 
-class WorkerFetchResponseRunnable : public WorkerRunnable
+class WorkerFetchResponseRunnable MOZ_FINAL : public WorkerRunnable
 {
   nsRefPtr<WorkerFetchResolver> mResolver;
+  // Passed from main thread to worker thread after being initialized.
+  nsRefPtr<InternalResponse> mInternalResponse;
 public:
-  explicit WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver)
+  WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver, InternalResponse* aResponse)
     : WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
     , mResolver(aResolver)
+    , mInternalResponse(aResponse)
   {
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
 
     nsRefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
-    nsRefPtr<Response> response = new Response(global, mResolver->mInternalResponse);
+    mResolver->mResponse = new Response(global, mInternalResponse);
 
-    nsRefPtr<Promise> promise = mResolver->mPromiseProxy->GetWorkerPromise();
-    MOZ_ASSERT(promise);
-    promise->MaybeResolve(response);
+    nsRefPtr<Promise> promise = mResolver->mFetchPromise.forget();
+    promise->MaybeResolve(mResolver->mResponse);
 
-    mResolver->mPromiseProxy->CleanUp(aCx);
     return true;
   }
 };
 
-WorkerFetchResolver::WorkerFetchResolver(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
+class WorkerFetchResponseEndRunnable MOZ_FINAL : public WorkerRunnable
 {
-  mPromiseProxy = PromiseWorkerProxy::Create(aWorkerPrivate, aPromise);
-}
+  nsRefPtr<WorkerFetchResolver> mResolver;
+public:
+  explicit WorkerFetchResponseEndRunnable(WorkerFetchResolver* aResolver)
+    : WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
+    , mResolver(aResolver)
+  {
+  }
 
-WorkerFetchResolver::~WorkerFetchResolver()
-{
-}
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
+    MOZ_ASSERT(mResolver->mResponse);
+
+    mResolver->CleanUp(aCx);
+    return true;
+  }
+};
 
 void
 WorkerFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
 {
   AssertIsOnMainThread();
-  mInternalResponse = aResponse;
+
+  MutexAutoLock lock(mCleanUpLock);
+  if (mCleanedUp) {
+    return;
+  }
 
   nsRefPtr<WorkerFetchResponseRunnable> r =
-    new WorkerFetchResponseRunnable(this);
+    new WorkerFetchResponseRunnable(this, aResponse);
 
   AutoSafeJSContext cx;
   if (!r->Dispatch(cx)) {
     NS_WARNING("Could not dispatch fetch resolve");
   }
 }
 
+void
+WorkerFetchResolver::OnResponseEnd()
+{
+  AssertIsOnMainThread();
+  MutexAutoLock lock(mCleanUpLock);
+  if (mCleanedUp) {
+    return;
+  }
+
+  nsRefPtr<WorkerFetchResponseEndRunnable> r =
+    new WorkerFetchResponseEndRunnable(this);
+
+  AutoSafeJSContext cx;
+  if (!r->Dispatch(cx)) {
+    NS_WARNING("Could not dispatch fetch resolve end");
+  }
+}
+
 // Empty string for no-referrer. FIXME(nsm): Does returning empty string
 // actually lead to no-referrer in the base channel?
 // The actual referrer policy and stripping is dealt with by HttpBaseChannel,
 // this always returns the full API referrer URL of the relevant global.
 nsresult
 GetRequestReferrer(nsIGlobalObject* aGlobal, const InternalRequest* aRequest, nsCString& aReferrer)
 {
   if (aRequest->ReferrerIsURL()) {
@@ -304,26 +435,26 @@ ExtractFromBlob(const File& aFile, nsIIn
                 nsCString& aContentType)
 {
   nsRefPtr<FileImpl> impl = aFile.Impl();
   nsresult rv = impl->GetInternalStream(aStream);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  nsString type;
+  nsAutoString type;
   impl->GetType(type);
   aContentType = NS_ConvertUTF16toUTF8(type);
   return NS_OK;
 }
 
 nsresult
 ExtractFromUSVString(const nsString& aStr,
-                             nsIInputStream** aStream,
-                             nsCString& aContentType)
+                     nsIInputStream** aStream,
+                     nsCString& aContentType)
 {
   nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
   if (!encoder) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   int32_t destBufferLen;
   nsresult rv = encoder->GetMaxLength(aStr.get(), aStr.Length(), &destBufferLen);
@@ -357,17 +488,17 @@ ExtractFromURLSearchParams(const URLSear
                            nsIInputStream** aStream,
                            nsCString& aContentType)
 {
   nsAutoString serialized;
   aParams.Stringify(serialized);
   aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
   return NS_NewStringInputStream(aStream, serialized);
 }
-}
+} // anonymous namespace
 
 nsresult
 ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentType)
 {
   MOZ_ASSERT(aStream);
 
@@ -418,167 +549,624 @@ ExtractByteStreamFromBody(const ArrayBuf
     return ExtractFromURLSearchParams(params, aStream, aContentType);
   }
 
   NS_NOTREACHED("Should never reach here");
   return NS_ERROR_FAILURE;
 }
 
 namespace {
-nsresult
-DecodeUTF8(const nsCString& aBuffer, nsString& aDecoded)
+class StreamDecoder MOZ_FINAL
+{
+  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
+  nsString mDecoded;
+
+public:
+  StreamDecoder()
+    : mDecoder(EncodingUtils::DecoderForEncoding("UTF-8"))
+  {
+    MOZ_ASSERT(mDecoder);
+  }
+
+  nsresult
+  AppendText(const char* aSrcBuffer, uint32_t aSrcBufferLen)
+  {
+    int32_t destBufferLen;
+    nsresult rv =
+      mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen, &destBufferLen);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!mDecoded.SetCapacity(mDecoded.Length() + destBufferLen, fallible_t())) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    char16_t* destBuffer = mDecoded.BeginWriting() + mDecoded.Length();
+    int32_t totalChars = mDecoded.Length();
+
+    int32_t srcLen = (int32_t) aSrcBufferLen;
+    int32_t outLen = destBufferLen;
+    rv = mDecoder->Convert(aSrcBuffer, &srcLen, destBuffer, &outLen);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+    totalChars += outLen;
+    mDecoded.SetLength(totalChars);
+
+    return NS_OK;
+  }
+
+  nsString&
+  GetText()
+  {
+    return mDecoded;
+  }
+};
+
+/*
+ * Called on successfully reading the complete stream.
+ */
+template <class Derived>
+class ContinueConsumeBodyRunnable MOZ_FINAL : public WorkerRunnable
+{
+  // This has been addrefed before this runnable is dispatched,
+  // released in WorkerRun().
+  FetchBody<Derived>* mFetchBody;
+  nsresult mStatus;
+  uint32_t mLength;
+  uint8_t* mResult;
+
+public:
+  ContinueConsumeBodyRunnable(FetchBody<Derived>* aFetchBody, nsresult aStatus,
+                              uint32_t aLength, uint8_t* aResult)
+    : WorkerRunnable(aFetchBody->mWorkerPrivate, WorkerThreadModifyBusyCount)
+    , mFetchBody(aFetchBody)
+    , mStatus(aStatus)
+    , mLength(aLength)
+    , mResult(aResult)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
+  {
+    mFetchBody->ContinueConsumeBody(mStatus, mLength, mResult);
+    return true;
+  }
+};
+
+// OnStreamComplete always adopts the buffer, utility class to release it in
+// a couple of places.
+class MOZ_STACK_CLASS AutoFreeBuffer MOZ_FINAL {
+  uint8_t* mBuffer;
+
+public:
+  explicit AutoFreeBuffer(uint8_t* aBuffer)
+    : mBuffer(aBuffer)
+  {}
+
+  ~AutoFreeBuffer()
+  {
+    moz_free(mBuffer);
+  }
+
+  void
+  Reset()
+  {
+    mBuffer= nullptr;
+  }
+};
+
+template <class Derived>
+class FailConsumeBodyWorkerRunnable : public MainThreadWorkerControlRunnable
+{
+  FetchBody<Derived>* mBody;
+public:
+  explicit FailConsumeBodyWorkerRunnable(FetchBody<Derived>* aBody)
+    : MainThreadWorkerControlRunnable(aBody->mWorkerPrivate)
+    , mBody(aBody)
+  {
+    AssertIsOnMainThread();
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
+  {
+    mBody->ContinueConsumeBody(NS_ERROR_FAILURE, 0, nullptr);
+    return true;
+  }
+};
+
+/*
+ * In case of failure to create a stream pump or dispatch stream completion to
+ * worker, ensure we cleanup properly. Thread agnostic.
+ */
+template <class Derived>
+class MOZ_STACK_CLASS AutoFailConsumeBody MOZ_FINAL
+{
+  FetchBody<Derived>* mBody;
+public:
+  explicit AutoFailConsumeBody(FetchBody<Derived>* aBody)
+    : mBody(aBody)
+  { }
+
+  ~AutoFailConsumeBody()
+  {
+    AssertIsOnMainThread();
+    if (mBody) {
+      if (mBody->mWorkerPrivate) {
+        nsRefPtr<FailConsumeBodyWorkerRunnable<Derived>> r =
+          new FailConsumeBodyWorkerRunnable<Derived>(mBody);
+        AutoSafeJSContext cx;
+        if (!r->Dispatch(cx)) {
+          MOZ_CRASH("We are going to leak");
+        }
+      } else {
+        mBody->ContinueConsumeBody(NS_ERROR_FAILURE, 0, nullptr);
+      }
+    }
+  }
+
+  void
+  DontFail()
+  {
+    mBody = nullptr;
+  }
+};
+
+template <class Derived>
+class ConsumeBodyDoneObserver : public nsIStreamLoaderObserver
 {
-  nsCOMPtr<nsIUnicodeDecoder> decoder =
-    EncodingUtils::DecoderForEncoding("UTF-8");
-  if (!decoder) {
+  FetchBody<Derived>* mFetchBody;
+
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  explicit ConsumeBodyDoneObserver(FetchBody<Derived>* aFetchBody)
+    : mFetchBody(aFetchBody)
+  { }
+
+  NS_IMETHOD
+  OnStreamComplete(nsIStreamLoader* aLoader,
+                   nsISupports* aCtxt,
+                   nsresult aStatus,
+                   uint32_t aResultLength,
+                   const uint8_t* aResult)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    // If the binding requested cancel, we don't need to call
+    // ContinueConsumeBody, since that is the originator.
+    if (aStatus == NS_BINDING_ABORTED) {
+      return NS_OK;
+    }
+
+    uint8_t* nonconstResult = const_cast<uint8_t*>(aResult);
+    if (mFetchBody->mWorkerPrivate) {
+      // This way if the runnable dispatch fails, the body is still released.
+      AutoFailConsumeBody<Derived> autoFail(mFetchBody);
+      nsRefPtr<ContinueConsumeBodyRunnable<Derived>> r =
+        new ContinueConsumeBodyRunnable<Derived>(mFetchBody,
+                                        aStatus,
+                                        aResultLength,
+                                        nonconstResult);
+      AutoSafeJSContext cx;
+      if (r->Dispatch(cx)) {
+        autoFail.DontFail();
+      } else {
+        NS_WARNING("Could not dispatch ConsumeBodyRunnable");
+        // Return failure so that aResult is freed.
+        return NS_ERROR_FAILURE;
+      }
+    } else {
+      mFetchBody->ContinueConsumeBody(aStatus, aResultLength, nonconstResult);
+    }
+
+    // FetchBody is responsible for data.
+    return NS_SUCCESS_ADOPTED_DATA;
+  }
+
+private:
+  virtual ~ConsumeBodyDoneObserver()
+  { }
+};
+
+template <class Derived>
+NS_IMPL_ADDREF(ConsumeBodyDoneObserver<Derived>)
+template <class Derived>
+NS_IMPL_RELEASE(ConsumeBodyDoneObserver<Derived>)
+template <class Derived>
+NS_INTERFACE_MAP_BEGIN(ConsumeBodyDoneObserver<Derived>)
+  NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamLoaderObserver)
+NS_INTERFACE_MAP_END
+
+template <class Derived>
+class BeginConsumeBodyRunnable MOZ_FINAL : public nsRunnable
+{
+  FetchBody<Derived>* mFetchBody;
+public:
+  explicit BeginConsumeBodyRunnable(FetchBody<Derived>* aBody)
+    : mFetchBody(aBody)
+  { }
+
+  NS_IMETHOD
+  Run() MOZ_OVERRIDE
+  {
+    mFetchBody->BeginConsumeBodyMainThread();
+    return NS_OK;
+  }
+};
+
+template <class Derived>
+class CancelPumpRunnable MOZ_FINAL : public WorkerMainThreadRunnable
+{
+  FetchBody<Derived>* mBody;
+public:
+  explicit CancelPumpRunnable(FetchBody<Derived>* aBody)
+    : WorkerMainThreadRunnable(aBody->mWorkerPrivate)
+    , mBody(aBody)
+  { }
+
+  bool
+  MainThreadRun() MOZ_OVERRIDE
+  {
+    mBody->CancelPump();
+    return true;
+  }
+};
+} // anonymous namespace
+
+template <class Derived>
+class FetchBodyFeature MOZ_FINAL : public workers::WorkerFeature
+{
+  // This is addrefed before the feature is created, and is released in ContinueConsumeBody()
+  // so we can hold a rawptr.
+  FetchBody<Derived>* mBody;
+
+public:
+  explicit FetchBodyFeature(FetchBody<Derived>* aBody)
+    : mBody(aBody)
+  { }
+
+  ~FetchBodyFeature()
+  { }
+
+  bool Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(aStatus > workers::Running);
+    mBody->ContinueConsumeBody(NS_BINDING_ABORTED, 0, nullptr);
+    return true;
+  }
+};
+
+template <class Derived>
+FetchBody<Derived>::FetchBody()
+  : mFeature(nullptr)
+  , mBodyUsed(false)
+  , mReadDone(false)
+{
+  if (!NS_IsMainThread()) {
+    mWorkerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(mWorkerPrivate);
+  } else {
+    mWorkerPrivate = nullptr;
+  }
+}
+
+template
+FetchBody<Request>::FetchBody();
+
+template
+FetchBody<Response>::FetchBody();
+
+// Returns true if addref succeeded.
+// Always succeeds on main thread.
+// May fail on worker if RegisterFeature() fails. In that case, it will release
+// the object before returning false.
+template <class Derived>
+bool
+FetchBody<Derived>::AddRefObject()
+{
+  AssertIsOnTargetThread();
+  DerivedClass()->AddRef();
+
+  if (mWorkerPrivate && !mFeature) {
+    if (!RegisterFeature()) {
+      ReleaseObject();
+      return false;
+    }
+  }
+  return true;
+}
+
+template <class Derived>
+void
+FetchBody<Derived>::ReleaseObject()
+{
+  AssertIsOnTargetThread();
+
+  if (mWorkerPrivate && mFeature) {
+    UnregisterFeature();
+  }
+
+  DerivedClass()->Release();
+}
+
+template <class Derived>
+bool
+FetchBody<Derived>::RegisterFeature()
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  MOZ_ASSERT(!mFeature);
+  mFeature = new FetchBodyFeature<Derived>(this);
+
+  if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), mFeature)) {
+    NS_WARNING("Failed to add feature");
+    mFeature = nullptr;
+    return false;
+  }
+
+  return true;
+}
+
+template <class Derived>
+void
+FetchBody<Derived>::UnregisterFeature()
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  MOZ_ASSERT(mFeature);
+
+  mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), mFeature);
+  mFeature = nullptr;
+}
+
+template <class Derived>
+void
+FetchBody<Derived>::CancelPump()
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(mConsumeBodyPump);
+  mConsumeBodyPump->Cancel(NS_BINDING_ABORTED);
+}
+
+// Return value is used by ConsumeBody to bubble the error code up to WebIDL so
+// mConsumePromise doesn't have to be rejected on early exit.
+template <class Derived>
+nsresult
+FetchBody<Derived>::BeginConsumeBody()
+{
+  AssertIsOnTargetThread();
+  MOZ_ASSERT(!mFeature);
+  MOZ_ASSERT(mConsumePromise);
+
+  // The FetchBody is not thread-safe refcounted. We addref it here and release
+  // it once the stream read is finished.
+  if (!AddRefObject()) {
     return NS_ERROR_FAILURE;
   }
 
-  int32_t destBufferLen;
-  nsresult rv =
-    decoder->GetMaxLength(aBuffer.get(), aBuffer.Length(), &destBufferLen);
+  nsCOMPtr<nsIRunnable> r = new BeginConsumeBodyRunnable<Derived>(this);
+  nsresult rv = NS_DispatchToMainThread(r);
   if (NS_WARN_IF(NS_FAILED(rv))) {
+    ReleaseObject();
     return rv;
   }
+  return NS_OK;
+}
 
-  if (!aDecoded.SetCapacity(destBufferLen, fallible_t())) {
-    return NS_ERROR_OUT_OF_MEMORY;
+/*
+ * BeginConsumeBodyMainThread() will automatically reject the consume promise
+ * and clean up on any failures, so there is no need for callers to do so,
+ * reflected in a lack of error return code.
+ */
+template <class Derived>
+void
+FetchBody<Derived>::BeginConsumeBodyMainThread()
+{
+  AssertIsOnMainThread();
+  AutoFailConsumeBody<Derived> autoReject(DerivedClass());
+  nsresult rv;
+  nsCOMPtr<nsIInputStream> stream;
+  DerivedClass()->GetBody(getter_AddRefs(stream));
+  if (!stream) {
+    NS_WARNING("Could not get stream");
+    return;
+  }
+
+  nsCOMPtr<nsIInputStreamPump> pump;
+  rv = NS_NewInputStreamPump(getter_AddRefs(pump),
+                             stream);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  nsRefPtr<ConsumeBodyDoneObserver<Derived>> p = new ConsumeBodyDoneObserver<Derived>(this);
+  nsCOMPtr<nsIStreamLoader> loader;
+  rv = NS_NewStreamLoader(getter_AddRefs(loader), p);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = pump->AsyncRead(loader, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
   }
 
-  char16_t* destBuffer = aDecoded.BeginWriting();
-  int32_t srcLen = (int32_t) aBuffer.Length();
-  int32_t outLen = destBufferLen;
-  rv = decoder->Convert(aBuffer.get(), &srcLen, destBuffer, &outLen);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  // Now that everything succeeded, we can assign the pump to a pointer that
+  // stays alive for the lifetime of the FetchBody.
+  mConsumeBodyPump = new nsMainThreadPtrHolder<nsIInputStreamPump>(pump);
+  // It is ok for retargeting to fail and reads to happen on the main thread.
+  autoReject.DontFail();
+
+  // Try to retarget, otherwise fall back to main thread.
+  nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(pump);
+  if (rr) {
+    nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+    rv = rr->RetargetDeliveryTo(sts);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      NS_WARNING("Retargeting failed");
+    }
+  }
+}
+
+template <class Derived>
+void
+FetchBody<Derived>::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength, uint8_t* aResult)
+{
+  AssertIsOnTargetThread();
+  // Just a precaution to ensure ContinueConsumeBody is not called out of
+  // sync with a body read.
+  MOZ_ASSERT(mBodyUsed);
+  MOZ_ASSERT(!mReadDone);
+  MOZ_ASSERT_IF(mWorkerPrivate, mFeature);
+  mReadDone = true;
+
+  AutoFreeBuffer autoFree(aResult);
+
+  MOZ_ASSERT(mConsumePromise);
+  nsRefPtr<Promise> localPromise = mConsumePromise.forget();
+
+  nsRefPtr<Derived> kungfuDeathGrip = DerivedClass();
+  ReleaseObject();
+
+  if (NS_WARN_IF(NS_FAILED(aStatus))) {
+    localPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+
+    // If binding aborted, cancel the pump. We can't assert mConsumeBodyPump.
+    // In the (admittedly rare) situation that BeginConsumeBodyMainThread()
+    // context switches out, and the worker thread gets canceled before the
+    // pump is setup, mConsumeBodyPump will be null.
+    // We've to use the !! form since non-main thread pointer access on
+    // a nsMainThreadPtrHandle is not permitted.
+    if (aStatus == NS_BINDING_ABORTED && !!mConsumeBodyPump) {
+      if (NS_IsMainThread()) {
+        CancelPump();
+      } else {
+        MOZ_ASSERT(mWorkerPrivate);
+        // In case of worker thread, we block the worker while the request is
+        // canceled on the main thread. This ensures that OnStreamComplete has
+        // a valid FetchBody around to call CancelPump and we don't release the
+        // FetchBody on the main thread.
+        nsRefPtr<CancelPumpRunnable<Derived>> r =
+          new CancelPumpRunnable<Derived>(this);
+        if (!r->Dispatch(mWorkerPrivate->GetJSContext())) {
+          NS_WARNING("Could not dispatch CancelPumpRunnable. Nothing we can do here");
+        }
+      }
+    }
   }
 
-  MOZ_ASSERT(outLen <= destBufferLen);
-  aDecoded.SetLength(outLen);
-  return NS_OK;
-}
+  // Release the pump and then early exit if there was an error.
+  // Uses NS_ProxyRelease internally, so this is safe.
+  mConsumeBodyPump = nullptr;
+
+  // Don't warn here since we warned above.
+  if (NS_FAILED(aStatus)) {
+    return;
+  }
+
+  // Finish successfully consuming body according to type.
+  MOZ_ASSERT(aResult);
+
+  AutoJSAPI api;
+  api.Init(DerivedClass()->GetParentObject());
+  JSContext* cx = api.cx();
+
+  switch (mConsumeType) {
+    case CONSUME_ARRAYBUFFER: {
+      JS::Rooted<JSObject*> arrayBuffer(cx);
+      arrayBuffer = JS_NewArrayBufferWithContents(cx, aResultLength, reinterpret_cast<void *>(aResult));
+      if (!arrayBuffer) {
+        JS_ClearPendingException(cx);
+        localPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
+        NS_WARNING("OUT OF MEMORY");
+        return;
+      }
+
+      JS::Rooted<JS::Value> val(cx);
+      val.setObjectOrNull(arrayBuffer);
+      localPromise->MaybeResolve(cx, val);
+      // ArrayBuffer takes over ownership.
+      autoFree.Reset();
+      return;
+    }
+    case CONSUME_BLOB: {
+      nsRefPtr<File> blob =
+        File::CreateMemoryFile(DerivedClass()->GetParentObject(),
+                               reinterpret_cast<void *>(aResult), aResultLength, NS_ConvertUTF8toUTF16(mMimeType));
+
+      if (!blob) {
+        localPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
+        return;
+      }
+
+      localPromise->MaybeResolve(blob);
+      // File takes over ownership.
+      autoFree.Reset();
+      return;
+    }
+    case CONSUME_TEXT:
+      // fall through handles early exit.
+    case CONSUME_JSON: {
+      StreamDecoder decoder;
+      decoder.AppendText(reinterpret_cast<char*>(aResult), aResultLength);
+
+      nsString& decoded = decoder.GetText();
+      if (mConsumeType == CONSUME_TEXT) {
+        localPromise->MaybeResolve(decoded);
+        return;
+      }
+
+      JS::Rooted<JS::Value> json(cx);
+      if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
+        JS::Rooted<JS::Value> exn(cx);
+        if (JS_GetPendingException(cx, &exn)) {
+          JS_ClearPendingException(cx);
+          localPromise->MaybeReject(cx, exn);
+        }
+        return;
+      }
+
+      localPromise->MaybeResolve(cx, json);
+      return;
+    }
+  }
+
+  NS_NOTREACHED("Unexpected consume body type");
 }
 
 template <class Derived>
 already_AddRefed<Promise>
 FetchBody<Derived>::ConsumeBody(ConsumeType aType, ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise = Promise::Create(DerivedClass()->GetParentObject(), aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
+  mConsumeType = aType;
   if (BodyUsed()) {
     aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR);
     return nullptr;
   }
 
   SetBodyUsed();
 
-  // While the spec says to do this asynchronously, all the body constructors
-  // right now only accept bodies whose streams are backed by an in-memory
-  // buffer that can be read without blocking. So I think this is fine.
-  nsCOMPtr<nsIInputStream> stream;
-  DerivedClass()->GetBody(getter_AddRefs(stream));
-
-  if (!stream) {
-    aRv = NS_NewByteInputStream(getter_AddRefs(stream), "", 0,
-                                NS_ASSIGNMENT_COPY);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
-    }
-  }
-
-  AutoJSAPI api;
-  api.Init(DerivedClass()->GetParentObject());
-  JSContext* cx = api.cx();
-
-  // We can make this assertion because for now we only support memory backed
-  // structures for the body argument for a Request.
-  MOZ_ASSERT(NS_InputStreamIsBuffered(stream));
-  nsCString buffer;
-  uint64_t len;
-  aRv = stream->Available(&len);
+  mConsumePromise = Promise::Create(DerivedClass()->GetParentObject(), aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  aRv = NS_ReadInputStreamToString(stream, buffer, len);
+  aRv = BeginConsumeBody();
   if (NS_WARN_IF(aRv.Failed())) {
+    mConsumePromise = nullptr;
     return nullptr;
   }
 
-  buffer.SetLength(len);
-
-  switch (aType) {
-    case CONSUME_ARRAYBUFFER: {
-      JS::Rooted<JSObject*> arrayBuffer(cx);
-      arrayBuffer =
-        ArrayBuffer::Create(cx, buffer.Length(),
-                            reinterpret_cast<const uint8_t*>(buffer.get()));
-      JS::Rooted<JS::Value> val(cx);
-      val.setObjectOrNull(arrayBuffer);
-      promise->MaybeResolve(cx, val);
-      return promise.forget();
-    }
-    case CONSUME_BLOB: {
-      // XXXnsm it is actually possible to avoid these duplicate allocations
-      // for the Blob case by having the Blob adopt the stream's memory
-      // directly, but I've not added a special case for now.
-      //
-      // FIXME(nsm): Use nsContentUtils::CreateBlobBuffer once blobs have been fixed on
-      // workers.
-      uint32_t blobLen = buffer.Length();
-      void* blobData = moz_malloc(blobLen);
-      nsRefPtr<File> blob;
-      if (blobData) {
-        memcpy(blobData, buffer.BeginReading(), blobLen);
-        blob = File::CreateMemoryFile(DerivedClass()->GetParentObject(), blobData, blobLen,
-                                      NS_ConvertUTF8toUTF16(mMimeType));
-      } else {
-        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-        return nullptr;
-      }
-
-      promise->MaybeResolve(blob);
-      return promise.forget();
-    }
-    case CONSUME_JSON: {
-      nsAutoString decoded;
-      aRv = DecodeUTF8(buffer, decoded);
-      if (NS_WARN_IF(aRv.Failed())) {
-        return nullptr;
-      }
-
-      JS::Rooted<JS::Value> json(cx);
-      if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
-        JS::Rooted<JS::Value> exn(cx);
-        if (JS_GetPendingException(cx, &exn)) {
-          JS_ClearPendingException(cx);
-          promise->MaybeReject(cx, exn);
-        }
-      }
-      promise->MaybeResolve(cx, json);
-      return promise.forget();
-    }
-    case CONSUME_TEXT: {
-      nsAutoString decoded;
-      aRv = DecodeUTF8(buffer, decoded);
-      if (NS_WARN_IF(aRv.Failed())) {
-        return nullptr;
-      }
-
-      promise->MaybeResolve(decoded);
-      return promise.forget();
-    }
-  }
-
-  NS_NOTREACHED("Unexpected consume body type");
-  // Silence warnings.
-  return nullptr;
+  nsRefPtr<Promise> promise = mConsumePromise;
+  return promise.forget();
 }
 
 template
 already_AddRefed<Promise>
 FetchBody<Request>::ConsumeBody(ConsumeType aType, ErrorResult& aRv);
 
 template
 already_AddRefed<Promise>
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -1,23 +1,30 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Fetch_h
 #define mozilla_dom_Fetch_h
 
+#include "nsIInputStreamPump.h"
+#include "nsIStreamLoader.h"
+
 #include "nsCOMPtr.h"
 #include "nsError.h"
+#include "nsProxyRelease.h"
 #include "nsString.h"
+
+#include "mozilla/DebugOnly.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/RequestBinding.h"
+#include "mozilla/dom/workers/bindings/WorkerFeature.h"
 
-class nsIInputStream;
+class nsIOutputStream;
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class ArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams;
 class InternalRequest;
 class OwningArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams;
@@ -48,16 +55,51 @@ ExtractByteStreamFromBody(const OwningAr
 /*
  * Non-owning version.
  */
 nsresult
 ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentType);
 
+template <class Derived> class FetchBodyFeature;
+
+/*
+ * FetchBody's body consumption uses nsIInputStreamPump to read from the
+ * underlying stream to a block of memory, which is then adopted by
+ * ContinueConsumeBody() and converted to the right type based on the JS
+ * function called.
+ *
+ * Use of the nsIInputStreamPump complicates things on the worker thread.
+ * The solution used here is similar to WebSockets.
+ * The difference is that we are only interested in completion and not data
+ * events, and nsIInputStreamPump can only deliver completion on the main thread.
+ *
+ * Before starting the pump on the main thread, we addref the FetchBody to keep
+ * it alive. Then we add a feature, to track the status of the worker.
+ *
+ * ContinueConsumeBody() is the function that cleans things up in both success
+ * and error conditions and so all callers call it with the appropriate status.
+ *
+ * Once the read is initiated on the main thread there are two possibilities.
+ *
+ * 1) Pump finishes before worker has finished Running.
+ *    In this case we adopt the data and dispatch a runnable to the worker,
+ *    which derefs FetchBody and removes the feature and resolves the Promise.
+ *
+ * 2) Pump still working while worker has stopped Running.
+ *    The feature is Notify()ed and ContinueConsumeBody() is called with
+ *    NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
+ *    ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
+ *    held by it) until pump->Cancel() is called. OnStreamComplete() will not
+ *    do anything if the error code is NS_BINDING_ABORTED, so we don't have to
+ *    worry about keeping anything alive.
+ *
+ * The pump is always released on the main thread.
+ */
 template <class Derived>
 class FetchBody {
 public:
   bool
   BodyUsed() { return mBodyUsed; }
 
   already_AddRefed<Promise>
   ArrayBuffer(ErrorResult& aRv)
@@ -78,50 +120,102 @@ public:
   }
 
   already_AddRefed<Promise>
   Text(ErrorResult& aRv)
   {
     return ConsumeBody(CONSUME_TEXT, aRv);
   }
 
+  // Utility public methods accessed by various runnables.
+  void
+  BeginConsumeBodyMainThread();
+
+  void
+  ContinueConsumeBody(nsresult aStatus, uint32_t aLength, uint8_t* aResult);
+
+  void
+  CancelPump();
+
+  // Always set whenever the FetchBody is created on the worker thread.
+  workers::WorkerPrivate* mWorkerPrivate;
+
+  // Set when consuming the body is attempted on a worker.
+  // Unset when consumption is done/aborted.
+  nsAutoPtr<workers::WorkerFeature> mFeature;
+
 protected:
-  FetchBody()
-    : mBodyUsed(false)
+  FetchBody();
+
+  virtual ~FetchBody()
   {
   }
 
   void
   SetBodyUsed()
   {
     mBodyUsed = true;
   }
 
   void
   SetMimeType(ErrorResult& aRv);
-
 private:
   enum ConsumeType
   {
     CONSUME_ARRAYBUFFER,
     CONSUME_BLOB,
     // FormData not supported right now,
     CONSUME_JSON,
     CONSUME_TEXT,
   };
 
   Derived*
   DerivedClass() const
   {
     return static_cast<Derived*>(const_cast<FetchBody*>(this));
   }
 
+  nsresult
+  BeginConsumeBody();
+
   already_AddRefed<Promise>
   ConsumeBody(ConsumeType aType, ErrorResult& aRv);
 
+  bool
+  AddRefObject();
+
+  void
+  ReleaseObject();
+
+  bool
+  RegisterFeature();
+
+  void
+  UnregisterFeature();
+
+  bool
+  IsOnTargetThread()
+  {
+    return NS_IsMainThread() == !mWorkerPrivate;
+  }
+
+  void
+  AssertIsOnTargetThread()
+  {
+    MOZ_ASSERT(IsOnTargetThread());
+  }
+
+  // Only ever set once, always on target thread.
   bool mBodyUsed;
   nsCString mMimeType;
+
+  // Only touched on target thread.
+  ConsumeType mConsumeType;
+  nsRefPtr<Promise> mConsumePromise;
+  DebugOnly<bool> mReadDone;
+
+  nsMainThreadPtrHandle<nsIInputStreamPump> mConsumeBodyPump;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Fetch_h
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -1,41 +1,57 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/FetchDriver.h"
 
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpHeaderVisitor.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsIThreadRetargetableRequest.h"
+#include "nsIUploadChannel2.h"
 
 #include "nsContentPolicyUtils.h"
 #include "nsDataHandler.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsNetUtil.h"
+#include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/workers/Workers.h"
 
 #include "Fetch.h"
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 namespace mozilla {
 namespace dom {
 
-FetchDriver::FetchDriver(InternalRequest* aRequest)
-  : mRequest(aRequest)
+NS_IMPL_ISUPPORTS(FetchDriver, nsIStreamListener)
+
+FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
+                         nsILoadGroup* aLoadGroup)
+  : mPrincipal(aPrincipal)
+  , mLoadGroup(aLoadGroup)
+  , mRequest(aRequest)
   , mFetchRecursionCount(0)
+  , mResponseAvailableCalled(false)
 {
 }
 
 FetchDriver::~FetchDriver()
 {
+  // We assert this since even on failures, we should call
+  // FailWithNetworkError().
+  MOZ_ASSERT(mResponseAvailableCalled);
 }
 
 nsresult
 FetchDriver::Fetch(FetchDriverObserver* aObserver)
 {
   workers::AssertIsOnMainThread();
   mObserver = aObserver;
 
@@ -49,17 +65,21 @@ FetchDriver::Fetch(bool aCORSFlag)
   MOZ_ASSERT(mFetchRecursionCount == 0);
   mFetchRecursionCount++;
 
   // FIXME(nsm): Deal with HSTS.
 
   if (!mRequest->IsSynchronous() && mFetchRecursionCount <= 1) {
     nsCOMPtr<nsIRunnable> r =
       NS_NewRunnableMethodWithArg<bool>(this, &FetchDriver::ContinueFetch, aCORSFlag);
-    return NS_DispatchToCurrentThread(r);
+    nsresult rv = NS_DispatchToCurrentThread(r);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      FailWithNetworkError();
+    }
+    return rv;
   }
 
   MOZ_CRASH("Synchronous fetch not supported");
 }
 
 nsresult
 FetchDriver::ContinueFetch(bool aCORSFlag)
 {
@@ -78,26 +98,17 @@ FetchDriver::ContinueFetch(bool aCORSFla
   // FIXME(nsm): Bug 1039846: Add CSP checks
 
   nsAutoCString scheme;
   rv = requestURI->GetScheme(scheme);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return FailWithNetworkError();
   }
 
-  nsAutoCString originURL;
-  mRequest->GetOrigin(originURL);
-  nsCOMPtr<nsIURI> originURI;
-  rv = NS_NewURI(getter_AddRefs(originURI), originURL, nullptr, nullptr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return FailWithNetworkError();
-  }
-
-  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-  rv = ssm->CheckSameOriginURI(requestURI, originURI, false);
+  rv = mPrincipal->CheckMayLoad(requestURI, false /* report */, false /* allowIfInheritsPrincipal */);
   if ((!aCORSFlag && NS_SUCCEEDED(rv)) ||
       (scheme.EqualsLiteral("data") && mRequest->SameOriginDataURL()) ||
       scheme.EqualsLiteral("about")) {
     return BasicFetch();
   }
 
   if (mRequest->Mode() == RequestMode::Same_origin) {
     return FailWithNetworkError();
@@ -107,99 +118,111 @@ FetchDriver::ContinueFetch(bool aCORSFla
     mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUE);
     return BasicFetch();
   }
 
   if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
     return FailWithNetworkError();
   }
 
+  bool corsPreflight = false;
   if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
       (mRequest->UnsafeRequest() && (mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders()))) {
-    // FIXME(nsm): Set corsPreflight;
+    corsPreflight = true;
   }
 
   mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS);
-  // FIXME(nsm): HttpFetch.
-  return FailWithNetworkError();
+  return HttpFetch(true /* aCORSFlag */, corsPreflight);
 }
 
 nsresult
 FetchDriver::BasicFetch()
 {
   nsAutoCString url;
   mRequest->GetURL(url);
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri),
                  url,
                  nullptr,
                  nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    return rv;
+  }
 
-  nsCString scheme;
+  nsAutoCString scheme;
   rv = uri->GetScheme(scheme);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    return rv;
+  }
 
   if (scheme.LowerCaseEqualsLiteral("about")) {
     if (url.EqualsLiteral("about:blank")) {
       nsRefPtr<InternalResponse> response =
         new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
       ErrorResult result;
       response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
                                   NS_LITERAL_CSTRING("text/html;charset=utf-8"),
                                   result);
       MOZ_ASSERT(!result.Failed());
       nsCOMPtr<nsIInputStream> body;
       rv = NS_NewCStringInputStream(getter_AddRefs(body), EmptyCString());
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return FailWithNetworkError();
+        FailWithNetworkError();
+        return rv;
       }
 
       response->SetBody(body);
       BeginResponse(response);
       return SucceedWithResponse();
     }
     return FailWithNetworkError();
   }
 
   if (scheme.LowerCaseEqualsLiteral("blob")) {
     nsRefPtr<FileImpl> blobImpl;
     rv = NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl));
     FileImpl* blob = static_cast<FileImpl*>(blobImpl.get());
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return FailWithNetworkError();
+      FailWithNetworkError();
+      return rv;
     }
 
     nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
     {
       ErrorResult result;
       uint64_t size = blob->GetSize(result);
       if (NS_WARN_IF(result.Failed())) {
-        return FailWithNetworkError();
+        FailWithNetworkError();
+        return result.ErrorCode();
       }
 
       nsAutoString sizeStr;
       sizeStr.AppendInt(size);
       response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), NS_ConvertUTF16toUTF8(sizeStr), result);
       if (NS_WARN_IF(result.Failed())) {
-        return FailWithNetworkError();
+        FailWithNetworkError();
+        return result.ErrorCode();
       }
 
       nsAutoString type;
       blob->GetType(type);
       response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), NS_ConvertUTF16toUTF8(type), result);
       if (NS_WARN_IF(result.Failed())) {
-        return FailWithNetworkError();
+        FailWithNetworkError();
+        return result.ErrorCode();
       }
     }
 
     nsCOMPtr<nsIInputStream> stream;
     rv = blob->GetInternalStream(getter_AddRefs(stream));
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return FailWithNetworkError();
+      FailWithNetworkError();
+      return rv;
     }
 
     response->SetBody(stream);
     BeginResponse(response);
     return SucceedWithResponse();
   }
 
   if (scheme.LowerCaseEqualsLiteral("data")) {
@@ -240,25 +263,181 @@ FetchDriver::BasicFetch()
     }
 
     return FailWithNetworkError();
   }
 
   if (scheme.LowerCaseEqualsLiteral("file")) {
   } else if (scheme.LowerCaseEqualsLiteral("http") ||
              scheme.LowerCaseEqualsLiteral("https")) {
-    // FIXME(nsm): HttpFetch.
-    return FailWithNetworkError();
+    return HttpFetch();
   }
 
   return FailWithNetworkError();
 }
 
 nsresult
-FetchDriver::BeginResponse(InternalResponse* aResponse)
+FetchDriver::HttpFetch(bool aCORSFlag, bool aPreflightCORSFlag, bool aAuthenticationFlag)
+{
+  mResponse = nullptr;
+
+  // XXXnsm: The ServiceWorker interception should happen automatically.
+  return ContinueHttpFetchAfterServiceWorker();
+}
+
+nsresult
+FetchDriver::ContinueHttpFetchAfterServiceWorker()
+{
+  if (!mResponse) {
+    // FIXME(nsm): Set skip SW flag.
+    // FIXME(nsm): Deal with CORS flags cases which will also call
+    // ContinueHttpFetchAfterCORSPreflight().
+    return ContinueHttpFetchAfterCORSPreflight();
+  }
+
+  // Otherwise ServiceWorker replied with a response.
+  return ContinueHttpFetchAfterNetworkFetch();
+}
+
+nsresult
+FetchDriver::ContinueHttpFetchAfterCORSPreflight()
+{
+  // mResponse is currently the CORS response.
+  // We may have to pass it via argument.
+  if (mResponse && mResponse->IsError()) {
+    return FailWithNetworkError();
+  }
+
+  return HttpNetworkFetch();
+}
+
+nsresult
+FetchDriver::HttpNetworkFetch()
+{
+  // We don't create a HTTPRequest copy since Necko sets the information on the
+  // nsIHttpChannel instead.
+
+  nsresult rv;
+
+  nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    return rv;
+  }
+
+  nsAutoCString url;
+  mRequest->GetURL(url);
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_NewURI(getter_AddRefs(uri),
+                          url,
+                          nullptr,
+                          nullptr,
+                          ios);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    return rv;
+  }
+
+  MOZ_ASSERT(mLoadGroup);
+  nsCOMPtr<nsIChannel> chan;
+  rv = NS_NewChannel(getter_AddRefs(chan),
+                     uri,
+                     mPrincipal,
+                     nsILoadInfo::SEC_NORMAL,
+                     mRequest->GetContext(),
+                     mLoadGroup,
+                     nullptr, /* aCallbacks */
+                     nsIRequest::LOAD_NORMAL,
+                     ios);
+  mLoadGroup = nullptr;
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    return rv;
+  }
+
+  nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
+  if (httpChan) {
+    nsAutoCString method;
+    mRequest->GetMethod(method);
+    rv = httpChan->SetRequestMethod(method);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      FailWithNetworkError();
+      return rv;
+    }
+
+    nsAutoTArray<InternalHeaders::Entry, 5> headers;
+    mRequest->Headers()->GetEntries(headers);
+    for (uint32_t i = 0; i < headers.Length(); ++i) {
+      httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */);
+    }
+
+    MOZ_ASSERT(mRequest->ReferrerIsURL());
+    nsCString referrer = mRequest->ReferrerAsURL();
+    if (!referrer.IsEmpty()) {
+      nsCOMPtr<nsIURI> uri;
+      rv = NS_NewURI(getter_AddRefs(uri), referrer, nullptr, nullptr, ios);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      rv = httpChan->SetReferrer(uri);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+
+    if (mRequest->ForceOriginHeader()) {
+      nsAutoString origin;
+      rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        FailWithNetworkError();
+        return rv;
+      }
+      httpChan->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
+                                 NS_ConvertUTF16toUTF8(origin),
+                                 false /* merge */);
+    }
+  }
+
+  nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
+  if (uploadChan) {
+    nsAutoCString contentType;
+    ErrorResult result;
+    mRequest->Headers()->Get(NS_LITERAL_CSTRING("content-type"), contentType, result);
+    // This is an error because the Request constructor explicitly extracts and
+    // sets a content-type per spec.
+    if (result.Failed()) {
+      return result.ErrorCode();
+    }
+
+    nsCOMPtr<nsIInputStream> bodyStream;
+    mRequest->GetBody(getter_AddRefs(bodyStream));
+    if (bodyStream) {
+      nsAutoCString method;
+      mRequest->GetMethod(method);
+      rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+  }
+  return chan->AsyncOpen(this, nullptr);
+}
+
+nsresult
+FetchDriver::ContinueHttpFetchAfterNetworkFetch()
+{
+  workers::AssertIsOnMainThread();
+  MOZ_ASSERT(mResponse);
+  MOZ_ASSERT(!mResponse->IsError());
+
+  return SucceedWithResponse();
+}
+
+already_AddRefed<InternalResponse>
+FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse)
 {
   MOZ_ASSERT(aResponse);
   nsAutoCString reqURL;
   mRequest->GetURL(reqURL);
   aResponse->SetUrl(reqURL);
 
   // FIXME(nsm): Handle mixed content check, step 7 of fetch.
 
@@ -274,28 +453,171 @@ FetchDriver::BeginResponse(InternalRespo
       filteredResponse = InternalResponse::OpaqueResponse();
       break;
     default:
       MOZ_CRASH("Unexpected case");
   }
 
   MOZ_ASSERT(filteredResponse);
   mObserver->OnResponseAvailable(filteredResponse);
-  return NS_OK;
+  mResponseAvailableCalled = true;
+  return filteredResponse.forget();
+}
+
+void
+FetchDriver::BeginResponse(InternalResponse* aResponse)
+{
+  nsRefPtr<InternalResponse> r = BeginAndGetFilteredResponse(aResponse);
+  // Release the ref.
 }
 
 nsresult
 FetchDriver::SucceedWithResponse()
 {
+  mObserver->OnResponseEnd();
   return NS_OK;
 }
 
 nsresult
 FetchDriver::FailWithNetworkError()
 {
+  MOZ_ASSERT(mObserver);
   nsRefPtr<InternalResponse> error = InternalResponse::NetworkError();
   mObserver->OnResponseAvailable(error);
-  // FIXME(nsm): Some sort of shutdown?
+  mResponseAvailableCalled = true;
+  mObserver->OnResponseEnd();
+  return NS_OK;
+}
+
+namespace {
+class FillResponseHeaders MOZ_FINAL : public nsIHttpHeaderVisitor {
+  InternalResponse* mResponse;
+
+  ~FillResponseHeaders()
+  { }
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit FillResponseHeaders(InternalResponse* aResponse)
+    : mResponse(aResponse)
+  {
+  }
+
+  NS_IMETHOD
+  VisitHeader(const nsACString & aHeader, const nsACString & aValue) MOZ_OVERRIDE
+  {
+    ErrorResult result;
+    mResponse->Headers()->Append(aHeader, aValue, result);
+    return result.ErrorCode();
+  }
+};
+
+NS_IMPL_ISUPPORTS(FillResponseHeaders, nsIHttpHeaderVisitor)
+} // anonymous namespace
+
+NS_IMETHODIMP
+FetchDriver::OnStartRequest(nsIRequest* aRequest,
+                            nsISupports* aContext)
+{
+  MOZ_ASSERT(!mPipeOutputStream);
+  nsresult rv;
+  aRequest->GetStatus(&rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    return rv;
+  }
+
+  nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest);
+  // For now we only support HTTP.
+  MOZ_ASSERT(channel);
+
+  aRequest->GetStatus(&rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    return rv;
+  }
+
+  uint32_t responseStatus;
+  channel->GetResponseStatus(&responseStatus);
+
+  nsAutoCString statusText;
+  channel->GetResponseStatusText(statusText);
+
+  nsRefPtr<InternalResponse> response = new InternalResponse(responseStatus, statusText);
+
+  nsRefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
+  rv = channel->VisitResponseHeaders(visitor);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    NS_WARNING("Failed to visit all headers.");
+  }
+
+  mResponse = BeginAndGetFilteredResponse(response);
+
+  // We open a pipe so that we can immediately set the pipe's read end as the
+  // response's body. Setting the segment size to UINT32_MAX means that the
+  // pipe has infinite space. The nsIChannel will continue to buffer data in
+  // xpcom events even if we block on a fixed size pipe.  It might be possible
+  // to suspend the channel and then resume when there is space available, but
+  // for now use an infinite pipe to avoid blocking.
+  nsCOMPtr<nsIInputStream> pipeInputStream;
+  rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
+                  getter_AddRefs(mPipeOutputStream),
+                  0, /* default segment size */
+                  UINT32_MAX /* infinite pipe */);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    // Cancel request.
+    return rv;
+  }
+
+  mResponse->SetBody(pipeInputStream);
+
+  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    // Cancel request.
+    return rv;
+  }
+
+  // Try to retarget off main thread.
+  nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest);
+  if (rr) {
+    rr->RetargetDeliveryTo(sts);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FetchDriver::OnDataAvailable(nsIRequest* aRequest,
+                             nsISupports* aContext,
+                             nsIInputStream* aInputStream,
+                             uint64_t aOffset,
+                             uint32_t aCount)
+{
+  uint32_t aRead;
+  MOZ_ASSERT(mResponse);
+  MOZ_ASSERT(mPipeOutputStream);
+
+  nsresult rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
+                                           mPipeOutputStream,
+                                           aCount, &aRead);
+  return rv;
+}
+
+NS_IMETHODIMP
+FetchDriver::OnStopRequest(nsIRequest* aRequest,
+                           nsISupports* aContext,
+                           nsresult aStatusCode)
+{
+  MOZ_ASSERT(mPipeOutputStream);
+  mPipeOutputStream->Close();
+
+  if (NS_FAILED(aStatusCode)) {
+    FailWithNetworkError();
+    return aStatusCode;
+  }
+
+  ContinueHttpFetchAfterNetworkFetch();
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/FetchDriver.h
+++ b/dom/fetch/FetchDriver.h
@@ -1,59 +1,86 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FetchDriver_h
 #define mozilla_dom_FetchDriver_h
 
+#include "nsAutoPtr.h"
 #include "nsIStreamListener.h"
 #include "nsRefPtr.h"
 
-class nsPIDOMWindow;
+#include "mozilla/DebugOnly.h"
+
+class nsIOutputStream;
+class nsILoadGroup;
+class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 
+class BlobSet;
 class InternalRequest;
 class InternalResponse;
 
 class FetchDriverObserver
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FetchDriverObserver);
   virtual void OnResponseAvailable(InternalResponse* aResponse) = 0;
+  virtual void OnResponseEnd() = 0;
 
 protected:
   virtual ~FetchDriverObserver()
   { };
 };
 
-class FetchDriver MOZ_FINAL
+class FetchDriver MOZ_FINAL : public nsIStreamListener
 {
-  NS_INLINE_DECL_REFCOUNTING(FetchDriver)
 public:
-  explicit FetchDriver(InternalRequest* aRequest);
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+
+  explicit FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
+                       nsILoadGroup* aLoadGroup);
   NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
 
 private:
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<nsILoadGroup> mLoadGroup;
   nsRefPtr<InternalRequest> mRequest;
+  nsRefPtr<InternalResponse> mResponse;
+  nsCOMPtr<nsIOutputStream> mPipeOutputStream;
   nsRefPtr<FetchDriverObserver> mObserver;
   uint32_t mFetchRecursionCount;
 
+  DebugOnly<bool> mResponseAvailableCalled;
+
   FetchDriver() = delete;
   FetchDriver(const FetchDriver&) = delete;
   FetchDriver& operator=(const FetchDriver&) = delete;
   ~FetchDriver();
 
   nsresult Fetch(bool aCORSFlag);
   nsresult ContinueFetch(bool aCORSFlag);
   nsresult BasicFetch();
+  nsresult HttpFetch(bool aCORSFlag = false, bool aPreflightCORSFlag = false, bool aAuthenticationFlag = false);
+  nsresult ContinueHttpFetchAfterServiceWorker();
+  nsresult ContinueHttpFetchAfterCORSPreflight();
+  nsresult HttpNetworkFetch();
+  nsresult ContinueHttpFetchAfterNetworkFetch();
+  // Returns the filtered response sent to the observer.
+  already_AddRefed<InternalResponse>
+  BeginAndGetFilteredResponse(InternalResponse* aResponse);
+  // Utility since not all cases need to do any post processing of the filtered
+  // response.
+  void BeginResponse(InternalResponse* aResponse);
   nsresult FailWithNetworkError();
-  nsresult BeginResponse(InternalResponse* aResponse);
   nsresult SucceedWithResponse();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FetchDriver_h
--- a/dom/fetch/InternalHeaders.cpp
+++ b/dom/fetch/InternalHeaders.cpp
@@ -319,10 +319,17 @@ InternalHeaders::CORSHeaders(InternalHea
         acExposedNames.Contains(entry.mName, comp)) {
       cors->Append(entry.mName, entry.mValue, result);
       MOZ_ASSERT(!result.Failed());
     }
   }
 
   return cors.forget();
 }
+
+void
+InternalHeaders::GetEntries(nsTArray<InternalHeaders::Entry>& aEntries) const
+{
+  MOZ_ASSERT(aEntries.IsEmpty());
+  aEntries.AppendElements(mList);
+}
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/InternalHeaders.h
+++ b/dom/fetch/InternalHeaders.h
@@ -24,30 +24,31 @@ namespace dom {
 
 template<typename T> class MozMap;
 class HeadersOrByteStringSequenceSequenceOrByteStringMozMap;
 
 class InternalHeaders MOZ_FINAL
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalHeaders)
 
-private:
+public:
   struct Entry
   {
     Entry(const nsACString& aName, const nsACString& aValue)
       : mName(aName)
       , mValue(aValue)
     { }
 
     Entry() { }
 
     nsCString mName;
     nsCString mValue;
   };
 
+private:
   HeadersGuardEnum mGuard;
   nsTArray<Entry> mList;
 
 public:
   explicit InternalHeaders(HeadersGuardEnum aGuard = HeadersGuardEnum::None)
     : mGuard(aGuard)
   {
   }
@@ -80,16 +81,19 @@ public:
 
   bool HasOnlySimpleHeaders() const;
 
   static already_AddRefed<InternalHeaders>
   BasicHeaders(InternalHeaders* aHeaders);
 
   static already_AddRefed<InternalHeaders>
   CORSHeaders(InternalHeaders* aHeaders);
+
+  void
+  GetEntries(nsTArray<InternalHeaders::Entry>& aEntries) const;
 private:
   virtual ~InternalHeaders();
 
   static bool IsSimpleHeader(const nsACString& aName,
                              const nsACString& aValue);
   static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv);
   static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv);
   bool IsImmutable(ErrorResult& aRv) const;
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -24,35 +24,20 @@ InternalRequest::GetRequestConstructorCo
   nsRefPtr<InternalRequest> copy = new InternalRequest();
   copy->mURL.Assign(mURL);
   copy->SetMethod(mMethod);
   copy->mHeaders = new InternalHeaders(*mHeaders);
 
   copy->mBodyStream = mBodyStream;
   copy->mPreserveContentCodings = true;
 
-  if (NS_IsMainThread()) {
-    nsIPrincipal* principal = aGlobal->PrincipalOrNull();
-    MOZ_ASSERT(principal);
-    aRv = nsContentUtils::GetASCIIOrigin(principal, copy->mOrigin);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
-    }
-  } else {
-    workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(worker);
-    worker->AssertIsOnWorkerThread();
-
-    workers::WorkerPrivate::LocationInfo& location = worker->GetLocationInfo();
-    copy->mOrigin = NS_ConvertUTF16toUTF8(location.mOrigin);
-  }
-
   copy->mContext = nsIContentPolicy::TYPE_FETCH;
   copy->mMode = mMode;
   copy->mCredentialsMode = mCredentialsMode;
+  copy->mCacheMode = mCacheMode;
   return copy.forget();
 }
 
 InternalRequest::~InternalRequest()
 {
 }
 
 } // namespace dom
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -56,16 +56,17 @@ public:
   explicit InternalRequest()
     : mMethod("GET")
     , mHeaders(new InternalHeaders(HeadersGuardEnum::None))
     , mContextFrameType(FRAMETYPE_NONE)
     , mReferrerType(REFERRER_CLIENT)
     , mMode(RequestMode::No_cors)
     , mCredentialsMode(RequestCredentials::Omit)
     , mResponseTainting(RESPONSETAINT_BASIC)
+    , mCacheMode(RequestCache::Default)
     , mRedirectCount(0)
     , mAuthenticationFlag(false)
     , mForceOriginHeader(false)
     , mManualRedirect(false)
     , mPreserveContentCodings(false)
       // FIXME(nsm): This should be false by default, but will lead to the
       // algorithm never loading data: URLs right now. See Bug 1018872 about
       // how certain contexts will override it to set it to true. Fetch
@@ -79,23 +80,23 @@ public:
   }
 
   explicit InternalRequest(const InternalRequest& aOther)
     : mMethod(aOther.mMethod)
     , mURL(aOther.mURL)
     , mHeaders(aOther.mHeaders)
     , mBodyStream(aOther.mBodyStream)
     , mContext(aOther.mContext)
-    , mOrigin(aOther.mOrigin)
     , mContextFrameType(aOther.mContextFrameType)
     , mReferrerType(aOther.mReferrerType)
     , mReferrerURL(aOther.mReferrerURL)
     , mMode(aOther.mMode)
     , mCredentialsMode(aOther.mCredentialsMode)
     , mResponseTainting(aOther.mResponseTainting)
+    , mCacheMode(aOther.mCacheMode)
     , mRedirectCount(aOther.mRedirectCount)
     , mAuthenticationFlag(aOther.mAuthenticationFlag)
     , mForceOriginHeader(aOther.mForceOriginHeader)
     , mManualRedirect(aOther.mManualRedirect)
     , mPreserveContentCodings(aOther.mPreserveContentCodings)
     , mSameOriginDataURL(aOther.mSameOriginDataURL)
     , mSandboxedStorageAreaURLs(aOther.mSandboxedStorageAreaURLs)
     , mSkipServiceWorker(aOther.mSkipServiceWorker)
@@ -196,16 +197,22 @@ public:
   }
 
   void
   SetResponseTainting(ResponseTainting aTainting)
   {
     mResponseTainting = aTainting;
   }
 
+  RequestCache
+  GetCacheMode() const
+  {
+    return mCacheMode;
+  }
+
   nsContentPolicyType
   GetContext() const
   {
     return mContext;
   }
 
   bool
   UnsafeRequest() const
@@ -220,31 +227,27 @@ public:
   }
 
   bool
   ForceOriginHeader()
   {
     return mForceOriginHeader;
   }
 
-  void
-  GetOrigin(nsCString& aOrigin) const
-  {
-    aOrigin.Assign(mOrigin);
-  }
-
   bool
   SameOriginDataURL() const
   {
     return mSameOriginDataURL;
   }
 
   void
   SetBody(nsIInputStream* aStream)
   {
+    // A request's body may not be reset once set.
+    MOZ_ASSERT(!mBodyStream);
     mBodyStream = aStream;
   }
 
   // Will return the original stream!
   // Use a tee or copy if you don't want to erase the original.
   void
   GetBody(nsIInputStream** aStream)
   {
@@ -269,27 +272,26 @@ private:
   nsCString mURL;
   nsRefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBodyStream;
 
   // nsContentPolicyType does not cover the complete set defined in the spec,
   // but it is a good start.
   nsContentPolicyType mContext;
 
-  nsCString mOrigin;
-
   ContextFrameType mContextFrameType;
   ReferrerType mReferrerType;
 
   // When mReferrerType is REFERRER_URL.
   nsCString mReferrerURL;
 
   RequestMode mMode;
   RequestCredentials mCredentialsMode;
   ResponseTainting mResponseTainting;
+  RequestCache mCacheMode;
 
   uint32_t mRedirectCount;
 
   bool mAuthenticationFlag;
   bool mForceOriginHeader;
   bool mManualRedirect;
   bool mPreserveContentCodings;
   bool mSameOriginDataURL;
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -99,16 +99,18 @@ public:
   {
     nsCOMPtr<nsIInputStream> stream = mBody;
     stream.forget(aStream);
   }
 
   void
   SetBody(nsIInputStream* aBody)
   {
+    // A request's body may not be reset once set.
+    MOZ_ASSERT(!mBody);
     mBody = aBody;
   }
 
 private:
   ~InternalResponse()
   { }
 
   // Used to create filtered responses.
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -53,22 +53,27 @@ Request::Constructor(const GlobalObject&
                      const RequestInit& aInit, ErrorResult& aRv)
 {
   nsRefPtr<InternalRequest> request;
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 
   if (aInput.IsRequest()) {
     nsRefPtr<Request> inputReq = &aInput.GetAsRequest();
-    if (inputReq->BodyUsed()) {
-      aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR);
-      return nullptr;
+    nsCOMPtr<nsIInputStream> body;
+    inputReq->GetBody(getter_AddRefs(body));
+    if (body) {
+      if (inputReq->BodyUsed()) {
+        aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR);
+        return nullptr;
+      } else {
+        inputReq->SetBodyUsed();
+      }
     }
 
-    inputReq->SetBodyUsed();
     request = inputReq->GetInternalRequest();
   } else {
     request = new InternalRequest();
   }
 
   request = request->GetRequestConstructorCopy(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
@@ -118,16 +123,26 @@ Request::Constructor(const GlobalObject&
         return nullptr;
       }
     }
     request->SetURL(NS_ConvertUTF16toUTF8(requestURL));
     fallbackMode = RequestMode::Cors;
     fallbackCredentials = RequestCredentials::Omit;
   }
 
+  // CORS-with-forced-preflight is not publicly exposed and should not be
+  // considered a valid value.
+  if (aInit.mMode.WasPassed() &&
+      aInit.mMode.Value() == RequestMode::Cors_with_forced_preflight) {
+    NS_NAMED_LITERAL_STRING(sourceDescription, "'mode' member of RequestInit");
+    NS_NAMED_LITERAL_STRING(value, "cors-with-forced-preflight");
+    NS_NAMED_LITERAL_STRING(type, "RequestMode");
+    aRv.ThrowTypeError(MSG_INVALID_ENUM_VALUE, &sourceDescription, &value, &type);
+    return nullptr;
+  }
   RequestMode mode = aInit.mMode.WasPassed() ? aInit.mMode.Value() : fallbackMode;
   RequestCredentials credentials =
     aInit.mCredentials.WasPassed() ? aInit.mCredentials.Value()
                                    : fallbackCredentials;
 
   if (mode != RequestMode::EndGuard_) {
     request->SetMode(mode);
   }
--- a/dom/fetch/Request.h
+++ b/dom/fetch/Request.h
@@ -21,18 +21,18 @@ namespace mozilla {
 namespace dom {
 
 class Headers;
 class InternalHeaders;
 class Promise;
 class RequestOrUSVString;
 
 class Request MOZ_FINAL : public nsISupports
+                        , public FetchBody<Request>
                         , public nsWrapperCache
-                        , public FetchBody<Request>
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Request)
 
 public:
   Request(nsIGlobalObject* aOwner, InternalRequest* aRequest);
 
   JSObject*
@@ -51,25 +51,34 @@ public:
   GetMethod(nsCString& aMethod) const
   {
     aMethod = mRequest->mMethod;
   }
 
   RequestMode
   Mode() const
   {
+    if (mRequest->mMode == RequestMode::Cors_with_forced_preflight) {
+      return RequestMode::Cors;
+    }
     return mRequest->mMode;
   }
 
   RequestCredentials
   Credentials() const
   {
     return mRequest->mCredentialsMode;
   }
 
+  RequestCache
+  Cache() const
+  {
+    return mRequest->GetCacheMode();
+  }
+
   void
   GetReferrer(DOMString& aReferrer) const
   {
     if (mRequest->ReferrerIsNone()) {
       aReferrer.AsAString() = EmptyString();
       return;
     }
 
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -20,18 +20,18 @@ namespace mozilla {
 namespace dom {
 
 class ArrayBufferOrArrayBufferViewOrUSVStringOrURLSearchParams;
 class Headers;
 class InternalHeaders;
 class Promise;
 
 class Response MOZ_FINAL : public nsISupports
+                         , public FetchBody<Response>
                          , public nsWrapperCache
-                         , public FetchBody<Response>
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Response)
 
 public:
   Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse);
 
   Response(const Response& aOther) = delete;
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -1253,17 +1253,17 @@ Geolocation::Update(nsIDOMGeoPosition *a
   }
 
   if (aSomewhere) {
     nsCOMPtr<nsIDOMGeoPositionCoords> coords;
     aSomewhere->GetCoords(getter_AddRefs(coords));
     if (coords) {
       double accuracy = -1;
       coords->GetAccuracy(&accuracy);
-      mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY, accuracy);
+      mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY_EXPONENTIAL, accuracy);
     }
   }
 
   for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
     mPendingCallbacks[i-1]->Update(aSomewhere);
     RemoveRequest(mPendingCallbacks[i-1]);
   }
 
--- a/dom/html/TimeRanges.cpp
+++ b/dom/html/TimeRanges.cpp
@@ -161,16 +161,16 @@ TimeRanges::Find(double aTime, double aE
   for (index_type i = 0; i < mRanges.Length(); ++i) {
     if (aTime < mRanges[i].mEnd && (aTime + aError) >= mRanges[i].mStart) {
       return i;
     }
   }
   return NoIndex;
 }
 
-JSObject*
-TimeRanges::WrapObject(JSContext* aCx)
+bool
+TimeRanges::WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector)
 {
-  return TimeRangesBinding::Wrap(aCx, this);
+  return TimeRangesBinding::Wrap(aCx, this, aReflector);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/TimeRanges.h
+++ b/dom/html/TimeRanges.h
@@ -45,17 +45,17 @@ public:
   void Normalize(double aError = 0.0);
 
   // Mutate this TimeRange to be the union of this and aOtherRanges.
   void Union(const TimeRanges* aOtherRanges, double aError);
 
   // Mutate this TimeRange to be the intersection of this and aOtherRanges.
   void Intersection(const TimeRanges* aOtherRanges);
 
-  JSObject* WrapObject(JSContext* aCx);
+  bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 
   uint32_t Length() const
   {
     return mRanges.Length();
   }
 
   virtual double Start(uint32_t aIndex, ErrorResult& aRv);
 
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -242,20 +242,20 @@ IDBKeyRange::DropJSObjects()
   mCachedLowerVal.setUndefined();
   mCachedUpperVal.setUndefined();
   mHaveCachedLowerVal = false;
   mHaveCachedUpperVal = false;
   mRooted = false;
   mozilla::DropJSObjects(this);
 }
 
-JSObject*
-IDBKeyRange::WrapObject(JSContext* aCx)
+bool
+IDBKeyRange::WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector)
 {
-  return IDBKeyRangeBinding::Wrap(aCx, this);
+  return IDBKeyRangeBinding::Wrap(aCx, this, aReflector);
 }
 
 void
 IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
                       ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
 
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -135,18 +135,18 @@ public:
 
   nsresult
   BindToStatement(mozIStorageStatement* aStatement) const;
 
   void
   DropJSObjects();
 
   // WebIDL
-  JSObject*
-  WrapObject(JSContext* aCx);
+  bool
+  WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 
   nsISupports*
   GetParentObject() const
   {
     return mGlobal;
   }
 
   void
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1797,18 +1797,18 @@ ContentChild::ActorDestroy(ActorDestroyR
     XRE_ShutdownChildProcess();
 }
 
 void
 ContentChild::ProcessingError(Result what)
 {
     switch (what) {
     case MsgDropped:
-        QuickExit();
-
+        NS_WARNING("MsgDropped in ContentChild");
+        return;
     case MsgNotKnown:
         NS_RUNTIMEABORT("aborting because of MsgNotKnown");
     case MsgNotAllowed:
         NS_RUNTIMEABORT("aborting because of MsgNotAllowed");
     case MsgPayloadError:
         NS_RUNTIMEABORT("aborting because of MsgPayloadError");
     case MsgProcessingError:
         NS_RUNTIMEABORT("aborting because of MsgProcessingError");
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -989,21 +989,16 @@ ContentParent::RecvFindPlugins(const uin
 ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                   Element* aFrameElement,
                                   ContentParent* aOpenerContentParent)
 {
     if (!sCanLaunchSubprocesses) {
         return nullptr;
     }
 
-    if (TabParent* parent = TabParent::GetNextTabParent()) {
-        parent->SetOwnerElement(aFrameElement);
-        return parent;
-    }
-
     ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
     bool isInContentProcess = (XRE_GetProcessType() != GeckoProcessType_Default);
     TabId tabId;
 
     nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
     TabId openerTabId;
     if (docShell) {
         openerTabId = TabParent::GetTabIdFrom(docShell);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -74,22 +74,16 @@ struct ShowInfo
 {
   nsString name;
   bool fullscreenAllowed;
   bool isPrivate;
   float dpi;
   double defaultScale;
 };
 
-struct FrameScriptInfo
-{
-    nsString url;
-    bool runInGlobalScope;
-};
-
 prio(normal upto urgent) intr protocol PBrowser
 {
     manager PContent or PContentBridge;
 
     manages PColorPicker;
     manages PDocumentRenderer;
     manages PContentPermissionRequest;
     manages PFilePicker;
@@ -118,26 +112,25 @@ parent:
     /**
      * When child sends this message, parent should move focus to
      * the next or previous focusable element.
      */
     MoveFocus(bool forward);
 
     Event(RemoteDOMEvent aEvent);
 
-    sync CreateWindow(PBrowser aNewTab,
-                      uint32_t aChromeFlags,
+    intr CreateWindow(uint32_t aChromeFlags,
                       bool aCalledFromJS,
                       bool aPositionSpecified,
                       bool aSizeSpecified,
                       nsString aURI,
                       nsString aName,
                       nsString aFeatures,
                       nsString aBaseURI)
-      returns (bool windowOpened, FrameScriptInfo[] frameScripts);
+      returns (bool windowIsNew, PBrowser window);
 
     sync SyncMessage(nsString aMessage, ClonedMessageData aData,
                      CpowEntry[] aCpows, Principal aPrincipal)
       returns (nsString[] retval);
 
     prio(high) sync RpcMessage(nsString aMessage, ClonedMessageData aData,
                                CpowEntry[] aCpows, Principal aPrincipal)
       returns (nsString[] retval);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1424,63 +1424,103 @@ TabChild::ProvideWindow(nsIDOMWindow* aP
                         nsIDOMWindow** aReturn)
 {
     *aReturn = nullptr;
 
     // If aParent is inside an <iframe mozbrowser> or <iframe mozapp> and this
     // isn't a request to open a modal-type window, we're going to create a new
     // <iframe mozbrowser/mozapp> and return its window here.
     nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
-    bool iframeMoz = (docshell && docshell->GetIsInBrowserOrApp() &&
-                      !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
-                                        nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
-                                        nsIWebBrowserChrome::CHROME_OPENAS_CHROME)));
-
-    if (!iframeMoz) {
-      int32_t openLocation =
-        nsWindowWatcher::GetWindowOpenLocation(aParent, aChromeFlags, aCalledFromJS,
-                                               aPositionSpecified, aSizeSpecified);
-
-      // If it turns out we're opening in the current browser, just hand over the
-      // current browser's docshell.
-      if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
-        nsCOMPtr<nsIWebBrowser> browser = do_GetInterface(WebNavigation());
-        *aWindowIsNew = false;
-        return browser->GetContentDOMWindow(aReturn);
-      }
+    if (docshell && docshell->GetIsInBrowserOrApp() &&
+        !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
+                          nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
+                          nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
+
+      // Note that BrowserFrameProvideWindow may return NS_ERROR_ABORT if the
+      // open window call was canceled.  It's important that we pass this error
+      // code back to our caller.
+      return BrowserFrameProvideWindow(aParent, aURI, aName, aFeatures,
+                                       aWindowIsNew, aReturn);
+    }
+
+    int32_t openLocation =
+      nsWindowWatcher::GetWindowOpenLocation(aParent, aChromeFlags, aCalledFromJS,
+                                             aPositionSpecified, aSizeSpecified);
+
+    // If it turns out we're opening in the current browser, just hand over the
+    // current browser's docshell.
+    if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
+      nsCOMPtr<nsIWebBrowser> browser = do_GetInterface(WebNavigation());
+      *aWindowIsNew = false;
+      return browser->GetContentDOMWindow(aReturn);
+    }
+
+    // Otherwise, we're opening a new tab or a new window. We have to contact
+    // TabParent in order to do either.
+
+    PBrowserChild* newChild;
+
+    nsAutoCString uriString;
+    if (aURI) {
+      aURI->GetSpec(uriString);
     }
 
-    // Note that ProvideWindowCommon may return NS_ERROR_ABORT if the
-    // open window call was canceled.  It's important that we pass this error
-    // code back to our caller.
-    return ProvideWindowCommon(aParent,
-                               iframeMoz,
-                               aChromeFlags,
-                               aCalledFromJS,
-                               aPositionSpecified,
-                               aSizeSpecified,
-                               aURI,
-                               aName,
-                               aFeatures,
-                               aWindowIsNew,
-                               aReturn);
+    nsCOMPtr<nsIDOMDocument> domDoc;
+    aParent->GetDocument(getter_AddRefs(domDoc));
+    if (!domDoc) {
+      NS_ERROR("Could retrieve document from nsIBaseWindow");
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIDocument> doc;
+    doc = do_QueryInterface(domDoc);
+    if (!doc) {
+      NS_ERROR("Document from nsIBaseWindow didn't QI to nsIDocument");
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
+    if (!baseURI) {
+      NS_ERROR("nsIDocument didn't return a base URI");
+      return NS_ERROR_FAILURE;
+    }
+
+    nsAutoCString baseURIString;
+    baseURI->GetSpec(baseURIString);
+
+    nsAutoString nameString;
+    nameString.Assign(aName);
+    nsAutoCString features;
+
+    // We can assume that if content is requesting to open a window from a remote
+    // tab, then we want to enforce that the new window is also a remote tab.
+    features.Assign(aFeatures);
+    features.Append(",remote");
+
+    if (!CallCreateWindow(aChromeFlags, aCalledFromJS, aPositionSpecified,
+                          aSizeSpecified, NS_ConvertUTF8toUTF16(uriString),
+                          nameString, NS_ConvertUTF8toUTF16(features),
+                          NS_ConvertUTF8toUTF16(baseURIString),
+                          aWindowIsNew, &newChild)) {
+        return NS_ERROR_NOT_AVAILABLE;
+    }
+
+    nsCOMPtr<nsIDOMWindow> win =
+        do_GetInterface(static_cast<TabChild*>(newChild)->WebNavigation());
+    win.forget(aReturn);
+    return NS_OK;
 }
 
 nsresult
-TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener,
-                              bool aIframeMoz,
-                              uint32_t aChromeFlags,
-                              bool aCalledFromJS,
-                              bool aPositionSpecified,
-                              bool aSizeSpecified,
-                              nsIURI* aURI,
-                              const nsAString& aName,
-                              const nsACString& aFeatures,
-                              bool* aWindowIsNew,
-                              nsIDOMWindow** aReturn)
+TabChild::BrowserFrameProvideWindow(nsIDOMWindow* aOpener,
+                                    nsIURI* aURI,
+                                    const nsAString& aName,
+                                    const nsACString& aFeatures,
+                                    bool* aWindowIsNew,
+                                    nsIDOMWindow** aReturn)
 {
   *aReturn = nullptr;
 
   ContentChild* cc = ContentChild::GetSingleton();
   const TabId openerTabId = GetTabId();
 
   // We must use PopupIPCTabContext here; ContentParent will not accept the
   // result of this->AsIPCTabContext() (which will be a
@@ -1494,80 +1534,38 @@ TabChild::ProvideWindowCommon(nsIDOMWind
 
   TabId tabId;
   cc->SendAllocateTabId(openerTabId,
                         ipcContext,
                         cc->GetID(),
                         &tabId);
 
   nsRefPtr<TabChild> newChild = new TabChild(ContentChild::GetSingleton(), tabId,
-                                             /* TabContext */ *this, aChromeFlags);
+                                             /* TabContext */ *this, /* chromeFlags */ 0);
   if (NS_FAILED(newChild->Init())) {
     return NS_ERROR_ABORT;
   }
 
   context.opener() = this;
   unused << Manager()->SendPBrowserConstructor(
       // We release this ref in DeallocPBrowserChild
       nsRefPtr<TabChild>(newChild).forget().take(),
-      tabId, IPCTabContext(context, mScrolling), aChromeFlags,
+      tabId, IPCTabContext(context, mScrolling), /* chromeFlags */ 0,
       cc->GetID(), cc->IsForApp(), cc->IsForBrowser());
 
   nsAutoCString spec;
   if (aURI) {
     aURI->GetSpec(spec);
   }
 
   NS_ConvertUTF8toUTF16 url(spec);
   nsString name(aName);
-  nsAutoCString features(aFeatures);
-  nsTArray<FrameScriptInfo> frameScripts;
-
-  if (aIframeMoz) {
-    newChild->SendBrowserFrameOpenWindow(this, url, name,
-                                         NS_ConvertUTF8toUTF16(features),
-                                         aWindowIsNew);
-  } else {
-    nsCOMPtr<nsIDOMDocument> domDoc;
-    aOpener->GetDocument(getter_AddRefs(domDoc));
-    if (!domDoc) {
-      NS_ERROR("Could retrieve document from nsIBaseWindow");
-      return NS_ERROR_FAILURE;
-    }
-
-    nsCOMPtr<nsIDocument> doc;
-    doc = do_QueryInterface(domDoc);
-    if (!doc) {
-      NS_ERROR("Document from nsIBaseWindow didn't QI to nsIDocument");
-      return NS_ERROR_FAILURE;
-    }
-
-    nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
-    if (!baseURI) {
-      NS_ERROR("nsIDocument didn't return a base URI");
-      return NS_ERROR_FAILURE;
-    }
-
-    nsAutoCString baseURIString;
-    baseURI->GetSpec(baseURIString);
-
-    // We can assume that if content is requesting to open a window from a remote
-    // tab, then we want to enforce that the new window is also a remote tab.
-    features.AppendLiteral(",remote");
-
-    if (!SendCreateWindow(newChild,
-                          aChromeFlags, aCalledFromJS, aPositionSpecified,
-                          aSizeSpecified, url,
-                          name, NS_ConvertUTF8toUTF16(features),
-                          NS_ConvertUTF8toUTF16(baseURIString),
-                          aWindowIsNew,
-                          &frameScripts)) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-  }
+  NS_ConvertUTF8toUTF16 features(aFeatures);
+  newChild->SendBrowserFrameOpenWindow(this, url, name,
+                                       features, aWindowIsNew);
   if (!*aWindowIsNew) {
     PBrowserChild::Send__delete__(newChild);
     return NS_ERROR_ABORT;
   }
 
   ScrollingBehavior scrolling = DEFAULT_SCROLLING;
   TextureFactoryIdentifier textureFactoryIdentifier;
   uint64_t layersId = 0;
@@ -1580,23 +1578,16 @@ TabChild::ProvideWindowCommon(nsIDOMWind
     PRenderFrameChild::Send__delete__(renderFrame);
     renderFrame = nullptr;
   }
 
   // Unfortunately we don't get a window unless we've shown the frame.  That's
   // pretty bogus; see bug 763602.
   newChild->DoFakeShow(scrolling, textureFactoryIdentifier, layersId, renderFrame);
 
-  for (size_t i = 0; i < frameScripts.Length(); i++) {
-    FrameScriptInfo& info = frameScripts[i];
-    if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
-      MOZ_CRASH();
-    }
-  }
-
   nsCOMPtr<nsIDOMWindow> win = do_GetInterface(newChild->WebNavigation());
   win.forget(aReturn);
   return NS_OK;
 }
 
 bool
 TabChild::HasValidInnerSize()
 {
@@ -1918,24 +1909,17 @@ TabChild::DoFakeShow(const ScrollingBeha
 }
 
 void
 TabChild::ApplyShowInfo(const ShowInfo& aInfo)
 {
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
   if (docShell) {
     nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
-    if (IsBrowserOrApp()) {
-      // B2G allows window.name to be set by changing the name attribute on the
-      // <iframe mozbrowser> element. window.open calls cause this attribute to
-      // be set to the correct value. A normal <xul:browser> element has no such
-      // attribute. The data we get here comes from reading the attribute, so we
-      // shouldn't trust it for <xul:browser> elements.
-      item->SetName(aInfo.name());
-    }
+    item->SetName(aInfo.name());
     docShell->SetFullscreenAllowed(aInfo.fullscreenAllowed());
     if (aInfo.isPrivate()) {
       bool nonBlank;
       docShell->GetHasLoadedNonBlankURI(&nonBlank);
       if (nonBlank) {
         nsContentUtils::ReportToConsoleNonLocalized(
           NS_LITERAL_STRING("We should not switch to Private Browsing after loading a document."),
           nsIScriptError::warningFlag,
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -569,27 +569,22 @@ private:
     // isn't a tap, then we CancelTapTracking().  In the meantime, we
     // may detect a context-menu event, and if so we
     // FireContextMenuEvent().
     void FireContextMenuEvent();
     void CancelTapTracking();
     void UpdateTapState(const WidgetTouchEvent& aEvent, nsEventStatus aStatus);
 
     nsresult
-    ProvideWindowCommon(nsIDOMWindow* aOpener,
-                        bool aIframeMoz,
-                        uint32_t aChromeFlags,
-                        bool aCalledFromJS,
-                        bool aPositionSpecified,
-                        bool aSizeSpecified,
-                        nsIURI* aURI,
-                        const nsAString& aName,
-                        const nsACString& aFeatures,
-                        bool* aWindowIsNew,
-                        nsIDOMWindow** aReturn);
+    BrowserFrameProvideWindow(nsIDOMWindow* aOpener,
+                              nsIURI* aURI,
+                              const nsAString& aName,
+                              const nsACString& aFeatures,
+                              bool* aWindowIsNew,
+                              nsIDOMWindow** aReturn);
 
     bool HasValidInnerSize();
 
     void SendPendingTouchPreventedResponse(bool aPreventDefault,
                                            const ScrollableLayerGuid& aGuid);
 
     // Adds the scrollable layer target to the target list, and returns whether
     // or not the caller should wait for a refresh to send a target
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -267,17 +267,16 @@ TabParent::TabParent(nsIContentParent* a
   , mManager(aManager)
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
   , mAppPackageFileDescriptorSent(false)
   , mSendOfflineStatus(true)
   , mChromeFlags(aChromeFlags)
   , mInitedByParent(false)
   , mTabId(aTabId)
-  , mSkipLoad(false)
 {
   MOZ_ASSERT(aManager);
 }
 
 TabParent::~TabParent()
 {
 }
 
@@ -445,107 +444,75 @@ TabParent::RecvEvent(const RemoteDOMEven
 
   event->SetOwner(target);
 
   bool dummy;
   target->DispatchEvent(event, &dummy);
   return true;
 }
 
-struct MOZ_STACK_CLASS TabParent::AutoUseNewTab MOZ_FINAL
+bool
+TabParent::AnswerCreateWindow(const uint32_t& aChromeFlags,
+                              const bool& aCalledFromJS,
+                              const bool& aPositionSpecified,
+                              const bool& aSizeSpecified,
+                              const nsString& aURI,
+                              const nsString& aName,
+                              const nsString& aFeatures,
+                              const nsString& aBaseURI,
+                              bool* aWindowIsNew,
+                              PBrowserParent** aRetVal)
 {
-public:
-  AutoUseNewTab(TabParent* aNewTab, bool* aWindowIsNew)
-   : mNewTab(aNewTab), mWindowIsNew(aWindowIsNew)
-  {
-    MOZ_ASSERT(!TabParent::sNextTabParent);
-    TabParent::sNextTabParent = aNewTab;
-    aNewTab->mSkipLoad = true;
-  }
-
-  ~AutoUseNewTab()
-  {
-    mNewTab->mSkipLoad = false;
-
-    if (TabParent::sNextTabParent) {
-      MOZ_ASSERT(TabParent::sNextTabParent == mNewTab);
-      TabParent::sNextTabParent = nullptr;
-      *mWindowIsNew = false;
-    }
-  }
-
-private:
-  TabParent* mNewTab;
-  bool* mWindowIsNew;
-};
-
-bool
-TabParent::RecvCreateWindow(PBrowserParent* aNewTab,
-                            const uint32_t& aChromeFlags,
-                            const bool& aCalledFromJS,
-                            const bool& aPositionSpecified,
-                            const bool& aSizeSpecified,
-                            const nsString& aURI,
-                            const nsString& aName,
-                            const nsString& aFeatures,
-                            const nsString& aBaseURI,
-                            bool* aWindowIsNew,
-                            InfallibleTArray<FrameScriptInfo>* aFrameScripts)
-{
-  // We always expect to open a new window here. If we don't, it's an error.
-  *aWindowIsNew = true;
-
   if (IsBrowserOrApp()) {
     return false;
   }
 
   nsresult rv;
   nsCOMPtr<nsPIWindowWatcher> pwwatch =
     do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, false);
 
-  TabParent* newTab = static_cast<TabParent*>(aNewTab);
-
   nsCOMPtr<nsIContent> frame(do_QueryInterface(mFrameElement));
   NS_ENSURE_TRUE(frame, false);
 
   nsCOMPtr<nsIDOMWindow> parent = do_QueryInterface(frame->OwnerDoc()->GetWindow());
   NS_ENSURE_TRUE(parent, false);
 
   int32_t openLocation =
     nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS,
                                            aPositionSpecified, aSizeSpecified);
 
   MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
              openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
 
+  *aWindowIsNew = true;
+
   // Opening new tabs is the easy case...
   if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
     NS_ENSURE_TRUE(mBrowserDOMWindow, false);
 
     bool isPrivate;
     nsCOMPtr<nsILoadContext> loadContext = GetLoadContext();
     loadContext->GetUsePrivateBrowsing(&isPrivate);
 
     nsCOMPtr<nsIOpenURIInFrameParams> params = new nsOpenURIInFrameParams();
     params->SetReferrer(aBaseURI);
     params->SetIsPrivate(isPrivate);
 
-    AutoUseNewTab aunt(newTab, aWindowIsNew);
-
     nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
     mBrowserDOMWindow->OpenURIInFrame(nullptr, params,
-                                      openLocation,
+                                      nsIBrowserDOMWindow::OPEN_NEWTAB,
                                       nsIBrowserDOMWindow::OPEN_NEW,
                                       getter_AddRefs(frameLoaderOwner));
-    if (!frameLoaderOwner) {
-      *aWindowIsNew = false;
-    }
+    NS_ENSURE_TRUE(frameLoaderOwner, false);
 
-    aFrameScripts->SwapElements(newTab->mDelayedFrameScripts);
+    nsRefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
+    NS_ENSURE_TRUE(frameLoader, false);
+
+    *aRetVal = frameLoader->GetRemoteBrowser();
     return true;
   }
 
   // WindowWatcher is going to expect a valid URI to open a window
   // to. If it can't find one, it's going to attempt to figure one
   // out on its own, which is problematic because it can't access
   // the document for the remote browser we're opening. Luckily,
   // TabChild has sent us a baseURI with which we can ensure that
@@ -558,72 +525,40 @@ TabParent::RecvCreateWindow(PBrowserPare
   rv = NS_NewURI(getter_AddRefs(finalURI), NS_ConvertUTF16toUTF8(aURI).get(), baseURI);
   NS_ENSURE_SUCCESS(rv, false);
 
   nsAutoCString finalURIString;
   finalURI->GetSpec(finalURIString);
 
   nsCOMPtr<nsIDOMWindow> window;
 
-  AutoUseNewTab aunt(newTab, aWindowIsNew);
-
   rv = pwwatch->OpenWindow2(parent, finalURIString.get(),
                             NS_ConvertUTF16toUTF8(aName).get(),
                             NS_ConvertUTF16toUTF8(aFeatures).get(), aCalledFromJS,
                             false, false, this, nullptr, getter_AddRefs(window));
   NS_ENSURE_SUCCESS(rv, false);
 
   nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(window);
   NS_ENSURE_TRUE(pwindow, false);
 
   nsRefPtr<nsIDocShell> newDocShell = pwindow->GetDocShell();
   NS_ENSURE_TRUE(newDocShell, false);
 
   nsCOMPtr<nsITabParent> newRemoteTab = newDocShell->GetOpenedRemote();
   NS_ENSURE_TRUE(newRemoteTab, false);
 
-  MOZ_ASSERT(static_cast<TabParent*>(newRemoteTab.get()) == newTab);
-
-  aFrameScripts->SwapElements(newTab->mDelayedFrameScripts);
+  *aRetVal = static_cast<TabParent*>(newRemoteTab.get());
   return true;
 }
 
-TabParent* TabParent::sNextTabParent;
-
-/* static */ TabParent*
-TabParent::GetNextTabParent()
-{
-  TabParent* result = sNextTabParent;
-  sNextTabParent = nullptr;
-  return result;
-}
-
-bool
-TabParent::SendLoadRemoteScript(const nsString& aURL,
-                                const bool& aRunInGlobalScope)
-{
-  if (mSkipLoad) {
-    mDelayedFrameScripts.AppendElement(FrameScriptInfo(aURL, aRunInGlobalScope));
-    return true;
-  }
-
-  MOZ_ASSERT(mDelayedFrameScripts.IsEmpty());
-  return PBrowserParent::SendLoadRemoteScript(aURL, aRunInGlobalScope);
-}
-
 void
 TabParent::LoadURL(nsIURI* aURI)
 {
     MOZ_ASSERT(aURI);
 
-    if (mSkipLoad) {
-        // Don't send the message if the child wants to load its own URL.
-        return;
-    }
-
     if (mIsDestroyed) {
         return;
     }
 
     nsCString spec;
     aURI->GetSpec(spec);
 
     if (!mShown) {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -129,27 +129,26 @@ public:
     virtual bool RecvEvent(const RemoteDOMEvent& aEvent) MOZ_OVERRIDE;
     virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent) MOZ_OVERRIDE;
     virtual bool RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent) MOZ_OVERRIDE;
     virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
                                             const nsString& aURL,
                                             const nsString& aName,
                                             const nsString& aFeatures,
                                             bool* aOutWindowOpened) MOZ_OVERRIDE;
-    virtual bool RecvCreateWindow(PBrowserParent* aOpener,
-                                  const uint32_t& aChromeFlags,
-                                  const bool& aCalledFromJS,
-                                  const bool& aPositionSpecified,
-                                  const bool& aSizeSpecified,
-                                  const nsString& aURI,
-                                  const nsString& aName,
-                                  const nsString& aFeatures,
-                                  const nsString& aBaseURI,
-                                  bool* aWindowIsNew,
-                                  InfallibleTArray<FrameScriptInfo>* aFrameScripts) MOZ_OVERRIDE;
+    virtual bool AnswerCreateWindow(const uint32_t& aChromeFlags,
+                                    const bool& aCalledFromJS,
+                                    const bool& aPositionSpecified,
+                                    const bool& aSizeSpecified,
+                                    const nsString& aURI,
+                                    const nsString& aName,
+                                    const nsString& aFeatures,
+                                    const nsString& aBaseURI,
+                                    bool* aWindowIsNew,
+                                    PBrowserParent** aRetVal) MOZ_OVERRIDE;
     virtual bool RecvSyncMessage(const nsString& aMessage,
                                  const ClonedMessageData& aData,
                                  const InfallibleTArray<CpowEntry>& aCpows,
                                  const IPC::Principal& aPrincipal,
                                  InfallibleTArray<nsString>* aJSONRetVal) MOZ_OVERRIDE;
     virtual bool RecvRpcMessage(const nsString& aMessage,
                                 const ClonedMessageData& aData,
                                 const InfallibleTArray<CpowEntry>& aCpows,
@@ -348,21 +347,16 @@ public:
      * Native widget remoting protocol for use with windowed plugins with e10s.
      */
     virtual PPluginWidgetParent* AllocPPluginWidgetParent() MOZ_OVERRIDE;
     virtual bool DeallocPPluginWidgetParent(PPluginWidgetParent* aActor) MOZ_OVERRIDE;
 
     void SetInitedByParent() { mInitedByParent = true; }
     bool IsInitedByParent() const { return mInitedByParent; }
 
-    static TabParent* GetNextTabParent();
-
-    bool SendLoadRemoteScript(const nsString& aURL,
-                              const bool& aRunInGlobalScope);
-
 protected:
     bool ReceiveMessage(const nsString& aMessage,
                         bool aSync,
                         const StructuredCloneData* aCloneData,
                         CpowHolder* aCpows,
                         nsIPrincipal* aPrincipal,
                         InfallibleTArray<nsString>* aJSONRetVal = nullptr);
 
@@ -469,45 +463,16 @@ private:
     // When true, the TabParent is initialized without child side's request.
     // When false, the TabParent is initialized by window.open() from child side.
     bool mInitedByParent;
 
     nsCOMPtr<nsILoadContext> mLoadContext;
 
     TabId mTabId;
 
-    // Helper class for RecvCreateWindow.
-    struct AutoUseNewTab;
-
-    // When loading a new tab or window via window.open, the child process sends
-    // a new PBrowser to use. We store that tab in sNextTabParent and then
-    // proceed through the browser's normal paths to create a new
-    // window/tab. When it comes time to create a new TabParent, we instead use
-    // sNextTabParent.
-    static TabParent* sNextTabParent;
-
-    // When loading a new tab or window via window.open, the child is
-    // responsible for loading the URL it wants into the new
-    // TabChild. Simultaneously, though, the parent sends a LoadURL message to
-    // every new PBrowser (usually for about:blank). This message usually
-    // arrives after the child has started to load the URL it wants, and
-    // overrides it. To prevent this, we set mSkipLoad to true when creating the
-    // new tab. This flag prevents the unwanted LoadURL message from being sent
-    // by the parent.
-    bool mSkipLoad;
-
-    // When loading a new tab or window via window.open, we want to ensure that
-    // frame scripts for that tab are loaded before any scripts start to run in
-    // the window. We can't load the frame scripts the normal way, using
-    // separate IPC messages, since they won't be processed by the child until
-    // returning to the event loop, which is too late. Instead, we queue up
-    // frame scripts that we intend to load and send them as part of the
-    // CreateWindow response. Then TabChild loads them immediately.
-    nsTArray<FrameScriptInfo> mDelayedFrameScripts;
-
 private:
     // This is used when APZ needs to find the TabParent associated with a layer
     // to dispatch events.
     typedef nsDataHashtable<nsUint64HashKey, TabParent*> LayerToTabParentTable;
     static LayerToTabParentTable* sLayerToTabParentTable;
 
     static void AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent);
     static void RemoveTabParentFromTable(uint64_t aLayersId);
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -238,16 +238,22 @@ public:
   MediaTaskQueue* GetTaskQueue() {
     return mTaskQueue;
   }
 
   void ClearDecoder() {
     mDecoder = nullptr;
   }
 
+  // Returns true if the reader implements RequestAudioData()
+  // and RequestVideoData() asynchronously, rather than using the
+  // implementation in this class to adapt the old synchronous to
+  // the newer async model.
+  virtual bool IsAsync() const { return false; }
+
 protected:
   virtual ~MediaDecoderReader();
 
   // Overrides of this function should decodes an unspecified amount of
   // audio data, enqueuing the audio data in mAudioQueue. Returns true
   // when there's more audio to decode, false if the audio is finished,
   // end of file has been reached, or an un-recoverable read error has
   // occured. This function blocks until the decode is complete.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -197,16 +197,18 @@ MediaDecoderStateMachine::MediaDecoderSt
   mVideoFrameEndTime(-1),
   mDecodedVideoEndTime(-1),
   mVolume(1.0),
   mPlaybackRate(1.0),
   mPreservesPitch(true),
   mAmpleVideoFrames(2),
   mLowAudioThresholdUsecs(LOW_AUDIO_USECS),
   mAmpleAudioThresholdUsecs(AMPLE_AUDIO_USECS),
+  mIsAudioPrerolling(false),
+  mIsVideoPrerolling(false),
   mAudioRequestStatus(RequestStatus::Idle),
   mVideoRequestStatus(RequestStatus::Idle),
   mAudioCaptured(false),
   mPositionChangeQueued(false),
   mAudioCompleted(false),
   mGotDurationFromMetaData(false),
   mDispatchedEventToDecode(false),
   mStopAudioThread(true),
@@ -607,25 +609,30 @@ MediaDecoderStateMachine::NeedToSkipToNe
     return false;
   }
 
   // We'll skip the video decode to the nearest keyframe if we're low on
   // audio, or if we're low on video, provided we're not running low on
   // data to decode. If we're running low on downloaded data to decode,
   // we won't start keyframe skipping, as we'll be pausing playback to buffer
   // soon anyway and we'll want to be able to display frames immediately
-  // after buffering finishes.
-  bool isLowOnDecodedAudio = !mIsAudioPrerolling && IsAudioDecoding() &&
+  // after buffering finishes. We ignore the low audio calculations for
+  // readers that are async, as since their audio decode runs on a different
+  // task queue it should never run low and skipping won't help their decode.
+  bool isLowOnDecodedAudio = !mReader->IsAsync() &&
+                             !mIsAudioPrerolling && IsAudioDecoding() &&
                              (GetDecodedAudioDuration() <
                               mLowAudioThresholdUsecs * mPlaybackRate);
   bool isLowOnDecodedVideo = !mIsVideoPrerolling &&
                              (mDecodedVideoEndTime - GetClock() <
                               LOW_VIDEO_THRESHOLD_USECS * mPlaybackRate);
-  if ((isLowOnDecodedAudio || isLowOnDecodedVideo) && !HasLowUndecodedData()) {
-    DECODER_LOG("Skipping video decode to the next keyframe");
+  bool lowUndecoded = HasLowUndecodedData();
+  if ((isLowOnDecodedAudio || isLowOnDecodedVideo) && !lowUndecoded) {
+    DECODER_LOG("Skipping video decode to the next keyframe lowAudio=%d lowVideo=%d lowUndecoded=%d async=%d",
+                isLowOnDecodedAudio, isLowOnDecodedVideo, lowUndecoded, mReader->IsAsync());
     return true;
   }
 
   return false;
 }
 
 void
 MediaDecoderStateMachine::DecodeVideo()
@@ -989,20 +996,24 @@ MediaDecoderStateMachine::OnVideoDecoded
     }
 
     case DECODER_STATE_DECODING: {
       Push(video);
       if (mIsVideoPrerolling && DonePrerollingVideo()) {
         StopPrerollingVideo();
       }
 
-      // If the requested video sample was slow to arrive, increase the
-      // amount of audio we buffer to ensure that we don't run out of audio.
-      // TODO: Detect when we're truly async, and don't do this if so, as
-      // it's not necessary.
+      // For non async readers, if the requested video sample was slow to
+      // arrive, increase the amount of audio we buffer to ensure that we
+      // don't run out of audio. This is unnecessary for async readers,
+      // since they decode audio and video on different threads so they
+      // are unlikely to run out of decoded audio.
+      if (mReader->IsAsync()) {
+        return;
+      }
       TimeDuration decodeTime = TimeStamp::Now() - mVideoDecodeStartTime;
       if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > mLowAudioThresholdUsecs &&
           !HasLowUndecodedData())
       {
         mLowAudioThresholdUsecs =
           std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), AMPLE_AUDIO_USECS);
         mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs,
                                               mAmpleAudioThresholdUsecs);
--- a/dom/media/MediaQueue.h
+++ b/dom/media/MediaQueue.h
@@ -104,22 +104,22 @@ template <class T> class MediaQueue : pr
   void Finish() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mEndOfStream = true;
   }
 
   // Returns the approximate number of microseconds of items in the queue.
   int64_t Duration() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    if (GetSize() < 2) {
+    if (GetSize() == 0) {
       return 0;
     }
     T* last = Peek();
     T* first = PeekFront();
-    return last->mTime - first->mTime;
+    return last->GetEndTime() - first->mTime;
   }
 
   void LockedForEach(nsDequeFunctor& aFunctor) const {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     ForEach(aFunctor);
   }
 
   // Extracts elements from the queue into aResult, in order.
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -77,16 +77,18 @@ public:
   virtual void ReleaseMediaResources() MOZ_OVERRIDE;
   virtual void SetSharedDecoderManager(SharedDecoderManager* aManager)
     MOZ_OVERRIDE;
 
   virtual nsresult ResetDecode() MOZ_OVERRIDE;
 
   virtual nsRefPtr<ShutdownPromise> Shutdown() MOZ_OVERRIDE;
 
+  virtual bool IsAsync() const MOZ_OVERRIDE { return true; }
+
 private:
 
   bool InitDemuxer();
   void ReturnOutput(MediaData* aData, TrackType aTrack);
 
   // Sends input to decoder for aTrack, and output to the state machine,
   // if necessary.
   void Update(TrackType aTrack);
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -175,17 +175,17 @@ GeckoMediaPluginService::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
   MOZ_ASSERT(obsService);
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "profile-change-teardown", false)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "last-pb-context-exited", false)));
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "gmp-clear-storage", false)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "browser:purge-session-history", false)));
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     prefs->AddObserver("media.gmp.plugin.crash", this, false);
   }
 
 #ifndef MOZ_WIDGET_GONK
   // Directory service is main thread only, so cache the profile dir here
@@ -319,21 +319,33 @@ GeckoMediaPluginService::Observe(nsISupp
     }
   } else if (!strcmp("last-pb-context-exited", aTopic)) {
     // When Private Browsing mode exits, all we need to do is clear
     // mTempNodeIds. This drops all the node ids we've cached in memory
     // for PB origin-pairs. If we try to open an origin-pair for non-PB
     // mode, we'll get the NodeId salt stored on-disk, and if we try to
     // open a PB mode origin-pair, we'll re-generate new salt.
     mTempNodeIds.Clear();
-  } else if (!strcmp("gmp-clear-storage", aTopic)) {
-    nsresult rv = GMPDispatch(
-      NS_NewRunnableMethod(this, &GeckoMediaPluginService::ClearStorage));
-    NS_ENSURE_SUCCESS(rv, rv);
+  } else if (!strcmp("browser:purge-session-history", aTopic)) {
+    // Clear everything!
+    if (!aSomeData || nsDependentString(aSomeData).IsEmpty()) {
+      return GMPDispatch(NS_NewRunnableMethod(
+          this, &GeckoMediaPluginService::ClearStorage));
+    }
+
+    // Clear nodeIds/records modified after |t|.
+    nsresult rv;
+    PRTime t = nsDependentString(aSomeData).ToInteger64(&rv, 10);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    return GMPDispatch(NS_NewRunnableMethodWithArg<PRTime>(
+        this, &GeckoMediaPluginService::ClearRecentHistoryOnGMPThread, t));
   }
+
   return NS_OK;
 }
 
 nsresult
 GeckoMediaPluginService::GMPDispatch(nsIRunnable* event, uint32_t flags)
 {
   nsCOMPtr<nsIRunnable> r(event);
   nsCOMPtr<nsIThread> thread;
@@ -1208,56 +1220,63 @@ struct NodeFilter {
   bool operator()(GMPParent* aParent) {
     return mNodeIDs.Contains(aParent->GetNodeId());
   }
 private:
   const nsTArray<nsCString>& mNodeIDs;
 };
 
 void
-GeckoMediaPluginService::ForgetThisSiteOnGMPThread(const nsACString& aOrigin)
+GeckoMediaPluginService::ClearNodeIdAndPlugin(DirectoryFilter& aFilter)
 {
-#define ERR_RET(x) NS_ENSURE_SUCCESS_VOID(x)
-#define ERR_CONT(x) if (NS_FAILED(x)) { continue; }
-
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  LOGD(("%s::%s: origin=%s", __CLASS__, __FUNCTION__, aOrigin.Data()));
-
   nsresult rv;
   nsCOMPtr<nsIFile> path;
 
   // $profileDir/gmp/
-  ERR_RET(GetStorageDir(getter_AddRefs(path)));
+  rv = GetStorageDir(getter_AddRefs(path));
+  if (NS_FAILED(rv)) {
+    return;
+  }
 
   // $profileDir/gmp/id/
-  ERR_RET(path->AppendNative(NS_LITERAL_CSTRING("id")));
+  rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
+  if (NS_FAILED(rv)) {
+    return;
+  }
 
   // Iterate all sub-folders of $profileDir/gmp/id/
   nsCOMPtr<nsISimpleEnumerator> iter;
-  ERR_RET(path->GetDirectoryEntries(getter_AddRefs(iter)));
+  rv = path->GetDirectoryEntries(getter_AddRefs(iter));
+  if (NS_FAILED(rv)) {
+    return;
+  }
 
   bool hasMore = false;
   nsTArray<nsCString> nodeIDsToClear;
   while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
     nsCOMPtr<nsISupports> supports;
-    ERR_CONT(iter->GetNext(getter_AddRefs(supports)));
+    rv = iter->GetNext(getter_AddRefs(supports));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
 
     // $profileDir/gmp/id/$hash
     nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
-    ERR_CONT(rv);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
 
     // Skip non-directory files.
     bool isDirectory = false;
-    ERR_CONT(dirEntry->IsDirectory(&isDirectory));
-    if (!isDirectory) {
+    rv = dirEntry->IsDirectory(&isDirectory);
+    if (NS_FAILED(rv) || !isDirectory) {
       continue;
     }
 
-    // Check if origin or topLevelOrigin match the origin being forgotten.
-    if (!MatchOrigin(dirEntry, aOrigin)) {
+    if (!aFilter(dirEntry)) {
       continue;
     }
 
     nsAutoCString salt;
     if (NS_SUCCEEDED(ReadSalt(dirEntry, salt))) {
       // Keep node IDs to clear data/plugins associated with them later.
       nodeIDsToClear.AppendElement(salt);
       // Also remove node IDs from the table.
@@ -1268,53 +1287,171 @@ GeckoMediaPluginService::ForgetThisSiteO
       NS_WARNING("Failed to delete the directory for the origin pair");
     }
   }
 
   // Kill plugins that have node IDs to be cleared.
   KillPlugins(mPlugins, mMutex, NodeFilter(nodeIDsToClear));
 
   // Clear all matching $profileDir/gmp/storage/$nodeId/
-  ERR_RET(GetStorageDir(getter_AddRefs(path)));
-  ERR_RET(path->AppendNative(NS_LITERAL_CSTRING("storage")));
+  rv = GetStorageDir(getter_AddRefs(path));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  rv = path->AppendNative(NS_LITERAL_CSTRING("storage"));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
   for (size_t i = 0; i < nodeIDsToClear.Length(); i++) {
     nsCOMPtr<nsIFile> dirEntry;
-    ERR_CONT(path->Clone(getter_AddRefs(dirEntry)));
-    ERR_CONT(dirEntry->AppendNative(nodeIDsToClear[i]));
+    rv = path->Clone(getter_AddRefs(dirEntry));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    rv = dirEntry->AppendNative(nodeIDsToClear[i]);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
     if (NS_FAILED(DeleteDir(dirEntry))) {
       NS_WARNING("Failed to delete GMP storage directory for the node");
     }
   }
-
-#undef ERR_RET
-#undef ERR_CONT
 }
 
-NS_IMETHODIMP
-GeckoMediaPluginService::ForgetThisSite(const nsAString& aOrigin)
+void
+GeckoMediaPluginService::ForgetThisSiteOnGMPThread(const nsACString& aOrigin)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  return GMPDispatch(NS_NewRunnableMethodWithArg<nsCString>(
-      this, &GeckoMediaPluginService::ForgetThisSiteOnGMPThread,
-      NS_ConvertUTF16toUTF8(aOrigin)));
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+  LOGD(("%s::%s: origin=%s", __CLASS__, __FUNCTION__, aOrigin.Data()));
+
+  struct OriginFilter : public DirectoryFilter {
+    explicit OriginFilter(const nsACString& aOrigin) : mOrigin(aOrigin) {}
+    virtual bool operator()(nsIFile* aPath) {
+      return MatchOrigin(aPath, mOrigin);
+    }
+  private:
+    const nsACString& mOrigin;
+  } filter(aOrigin);
+
+  ClearNodeIdAndPlugin(filter);
 }
 
 class StorageClearedTask : public nsRunnable {
 public:
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
     MOZ_ASSERT(obsService);
     if (obsService) {
       obsService->NotifyObservers(nullptr, "gmp-clear-storage-complete", nullptr);
     }
     return NS_OK;
   }
 };
 
+void
+GeckoMediaPluginService::ClearRecentHistoryOnGMPThread(PRTime aSince)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+  LOGD(("%s::%s: since=%lld", __CLASS__, __FUNCTION__, (int64_t)aSince));
+
+  nsCOMPtr<nsIFile> storagePath;
+  nsCOMPtr<nsIFile> temp;
+  if (NS_SUCCEEDED(GetStorageDir(getter_AddRefs(temp))) &&
+      NS_SUCCEEDED(temp->AppendNative(NS_LITERAL_CSTRING("storage")))) {
+    storagePath = temp.forget();
+  }
+
+  struct MTimeFilter : public DirectoryFilter {
+    explicit MTimeFilter(PRTime aSince, already_AddRefed<nsIFile> aPath)
+      : mSince(aSince), mStoragePath(aPath) {}
+
+    // Return true if any files under aPath is modified after |mSince|.
+    bool IsModifiedAfter(nsIFile* aPath) {
+      PRTime lastModified;
+      nsresult rv = aPath->GetLastModifiedTime(&lastModified);
+      if (NS_SUCCEEDED(rv) && lastModified >= mSince) {
+        return true;
+      }
+      // Check sub-directories recursively
+      nsCOMPtr<nsISimpleEnumerator> iter;
+      rv = aPath->GetDirectoryEntries(getter_AddRefs(iter));
+      if (NS_FAILED(rv)) {
+        return false;
+      }
+
+      bool hasMore = false;
+      while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
+        nsCOMPtr<nsISupports> supports;
+        rv = iter->GetNext(getter_AddRefs(supports));
+        if (NS_FAILED(rv)) {
+          continue;
+        }
+
+        nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
+        if (NS_FAILED(rv)) {
+          continue;
+        }
+
+        if (IsModifiedAfter(path)) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    // |aPath| is $profileDir/gmp/id/$hash
+    virtual bool operator()(nsIFile* aPath) {
+      if (IsModifiedAfter(aPath)) {
+        return true;
+      }
+
+      nsAutoCString salt;
+      nsresult rv = ReadSalt(aPath, salt);
+      if (NS_FAILED(rv)) {
+        return false;
+      }
+
+      // $profileDir/gmp/storage/
+      if (!mStoragePath) {
+        return false;
+      }
+      // $profileDir/gmp/storage/$nodeId/
+      nsCOMPtr<nsIFile> path;
+      rv = mStoragePath->Clone(getter_AddRefs(path));
+      if (NS_FAILED(rv)) {
+        return false;
+      }
+
+      rv = path->AppendNative(salt);
+      return NS_SUCCEEDED(rv) && IsModifiedAfter(path);
+    }
+  private:
+    const PRTime mSince;
+    const nsCOMPtr<nsIFile> mStoragePath;
+  } filter(aSince, storagePath.forget());
+
+  ClearNodeIdAndPlugin(filter);
+
+  NS_DispatchToMainThread(new StorageClearedTask(), NS_DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginService::ForgetThisSite(const nsAString& aOrigin)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return GMPDispatch(NS_NewRunnableMethodWithArg<nsCString>(
+      this, &GeckoMediaPluginService::ForgetThisSiteOnGMPThread,
+      NS_ConvertUTF16toUTF8(aOrigin)));
+}
+
 static bool IsNodeIdValid(GMPParent* aParent) {
   return !aParent->GetNodeId().IsEmpty();
 }
 
 void
 GeckoMediaPluginService::ClearStorage()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -71,17 +71,24 @@ private:
   void LoadFromEnvironment();
   void ProcessPossiblePlugin(nsIFile* aDir);
 
   void AddOnGMPThread(const nsAString& aSearchDir);
   void RemoveOnGMPThread(const nsAString& aSearchDir);
 
   nsresult SetAsyncShutdownTimeout();
 
+  struct DirectoryFilter {
+    virtual bool operator()(nsIFile* aPath) = 0;
+    ~DirectoryFilter() {}
+  };
+  void ClearNodeIdAndPlugin(DirectoryFilter& aFilter);
+
   void ForgetThisSiteOnGMPThread(const nsACString& aOrigin);
+  void ClearRecentHistoryOnGMPThread(PRTime aSince);
 
 protected:
   friend class GMPParent;
   void ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld);
 private:
   GMPParent* ClonePlugin(const GMPParent* aOriginal);
 
   class PathRunnable : public nsRunnable
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -115,50 +115,72 @@ GetGMPThread()
 {
   nsRefPtr<GeckoMediaPluginService> service =
     GeckoMediaPluginService::GetGeckoMediaPluginService();
   nsCOMPtr<nsIThread> thread;
   EXPECT_TRUE(NS_SUCCEEDED(service->GetThread(getter_AddRefs(thread))));
   return thread.forget();
 }
 
+/**
+ * Enumerate files under |aPath| (non-recursive).
+ */
 template<typename T>
 static nsresult
-EnumerateDir(const nsACString& aDir, T&& aDirIter)
+EnumerateDir(nsIFile* aPath, T&& aDirIter)
+{
+  nsCOMPtr<nsISimpleEnumerator> iter;
+  nsresult rv = aPath->GetDirectoryEntries(getter_AddRefs(iter));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  bool hasMore = false;
+  while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
+    nsCOMPtr<nsISupports> supports;
+    rv = iter->GetNext(getter_AddRefs(supports));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    nsCOMPtr<nsIFile> entry(do_QueryInterface(supports, &rv));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    aDirIter(entry);
+  }
+  return NS_OK;
+}
+
+/**
+ * Enumerate files under $profileDir/gmp/$aDir/ (non-recursive).
+ */
+template<typename T>
+static nsresult
+EnumerateGMPStorageDir(const nsACString& aDir, T&& aDirIter)
 {
   nsRefPtr<GeckoMediaPluginService> service =
     GeckoMediaPluginService::GetGeckoMediaPluginService();
   MOZ_ASSERT(service);
 
   // $profileDir/gmp/
   nsCOMPtr<nsIFile> path;
   nsresult rv = service->GetStorageDir(getter_AddRefs(path));
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   // $profileDir/gmp/$aDir/
   rv = path->AppendNative(aDir);
-  NS_ENSURE_SUCCESS(rv, rv);
-  // Iterate all sub-folders of $profileDir/gmp/$aDir/
-  nsCOMPtr<nsISimpleEnumerator> iter;
-  rv = path->GetDirectoryEntries(getter_AddRefs(iter));
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-  bool hasMore = false;
-  while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
-    nsCOMPtr<nsISupports> supports;
-    rv = iter->GetNext(getter_AddRefs(supports));
-    if (NS_FAILED(rv)) {
-      continue;
-    }
-    nsCOMPtr<nsIFile> entry(do_QueryInterface(supports, &rv));
-    if (NS_FAILED(rv)) {
-      continue;
-    }
-    aDirIter(entry);
-  }
-  return NS_OK;
+  return EnumerateDir(path, aDirIter);
 }
 
 class GMPShutdownObserver : public nsIRunnable
                           , public nsIObserver {
 public:
   GMPShutdownObserver(nsIRunnable* aShutdownTask,
                       nsIRunnable* Continuation,
                       const nsACString& aNodeId)
@@ -222,31 +244,37 @@ public:
   }
   const char* mTopic;
 };
 
 class ClearGMPStorageTask : public nsIRunnable
                           , public nsIObserver {
 public:
   ClearGMPStorageTask(nsIRunnable* Continuation,
-                      nsIThread* aTarget)
+                      nsIThread* aTarget, PRTime aSince)
     : mContinuation(Continuation)
     , mTarget(aTarget)
+    , mSince(aSince)
   {}
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   NS_IMETHOD Run() MOZ_OVERRIDE {
     MOZ_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIObserverService> observerService =
         mozilla::services::GetObserverService();
     EXPECT_TRUE(observerService);
     observerService->AddObserver(this, "gmp-clear-storage-complete", false);
     if (observerService) {
-      observerService->NotifyObservers(nullptr, "gmp-clear-storage", nullptr);
+      nsAutoString str;
+      if (mSince >= 0) {
+        str.AppendInt(static_cast<int64_t>(mSince));
+      }
+      observerService->NotifyObservers(
+          nullptr, "browser:purge-session-history", str.Data());
     }
     return NS_OK;
   }
 
   NS_IMETHOD Observe(nsISupports* aSubject,
                      const char* aTopic,
                      const char16_t* aSomeData) MOZ_OVERRIDE
   {
@@ -259,25 +287,27 @@ public:
     }
     return NS_OK;
   }
 
 private:
   virtual ~ClearGMPStorageTask() {}
   nsRefPtr<nsIRunnable> mContinuation;
   nsCOMPtr<nsIThread> mTarget;
+  const PRTime mSince;
 };
 
 NS_IMPL_ISUPPORTS(ClearGMPStorageTask, nsIRunnable, nsIObserver)
 
 static void
 ClearGMPStorage(nsIRunnable* aContinuation,
-                nsIThread* aTarget)
+                nsIThread* aTarget, PRTime aSince = -1)
 {
-  nsRefPtr<ClearGMPStorageTask> task(new ClearGMPStorageTask(aContinuation, aTarget));
+  nsRefPtr<ClearGMPStorageTask> task(
+      new ClearGMPStorageTask(aContinuation, aTarget, aSince));
   NS_DispatchToMainThread(task, NS_DISPATCH_NORMAL);
 }
 
 static void
 SimulatePBModeExit()
 {
   NS_DispatchToMainThread(new NotifyObserversTask("last-pb-context-exited"), NS_DISPATCH_SYNC);
 }
@@ -503,17 +533,17 @@ class GMPStorageTest : public GMPDecrypt
   private:
     NodeInfo* mNodeInfo;
   };
 
   void TestForgetThisSite_CollectSiteInfo() {
     nsAutoPtr<NodeInfo> siteInfo(
         new NodeInfo(NS_LITERAL_CSTRING("example1.com")));
     // Collect nodeIds that are expected to remain for later comparison.
-    EnumerateDir(NS_LITERAL_CSTRING("id"), NodeIdCollector(siteInfo));
+    EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), NodeIdCollector(siteInfo));
     // Invoke "Forget this site" on the main thread.
     NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<nsAutoPtr<NodeInfo>>(
         this, &GMPStorageTest::TestForgetThisSite_Forget, siteInfo));
   }
 
   void TestForgetThisSite_Forget(nsAutoPtr<NodeInfo> aSiteInfo) {
     nsRefPtr<GeckoMediaPluginService> service =
         GeckoMediaPluginService::GetGeckoMediaPluginService();
@@ -566,25 +596,186 @@ class GMPStorageTest : public GMPDecrypt
     ~StorageVerifier() {
       EXPECT_TRUE(mExpectedRemainingNodeIds.IsEmpty());
     }
   private:
     nsTArray<nsCString> mExpectedRemainingNodeIds;
   };
 
   void TestForgetThisSite_Verify(nsAutoPtr<NodeInfo> aSiteInfo) {
-    nsresult rv = EnumerateDir(
+    nsresult rv = EnumerateGMPStorageDir(
         NS_LITERAL_CSTRING("id"), NodeIdVerifier(aSiteInfo));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
 
-    rv = EnumerateDir(
+    rv = EnumerateGMPStorageDir(
         NS_LITERAL_CSTRING("storage"), StorageVerifier(aSiteInfo));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
   }
 
+  /**
+   * 1. Generate some storage data.
+   * 2. Find the max mtime |t| in $profileDir/gmp/id/.
+   * 3. Pass |t| to clear recent history.
+   * 4. Check if all directories in $profileDir/gmp/id/ and
+   *    $profileDir/gmp/storage are removed.
+   */
+  void TestClearRecentHistory1() {
+    AssertIsOnGMPThread();
+    EXPECT_TRUE(IsGMPStorageIsEmpty());
+
+    // Generate storage data for some site.
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false);
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
+        this, &GMPStorageTest::TestClearRecentHistory1_Clear);
+    Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
+    Update(NS_LITERAL_CSTRING("test-storage"));
+
+  }
+
+  /**
+   * 1. Generate some storage data.
+   * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
+   * 3. Pass |t| to clear recent history.
+   * 4. Check if all directories in $profileDir/gmp/id/ and
+   *    $profileDir/gmp/storage are removed.
+   */
+  void TestClearRecentHistory2() {
+    AssertIsOnGMPThread();
+    EXPECT_TRUE(IsGMPStorageIsEmpty());
+
+    // Generate storage data for some site.
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false);
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
+        this, &GMPStorageTest::TestClearRecentHistory2_Clear);
+    Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
+    Update(NS_LITERAL_CSTRING("test-storage"));
+
+  }
+
+  /**
+   * 1. Generate some storage data.
+   * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
+   * 3. Pass |t+1| to clear recent history.
+   * 4. Check if all directories in $profileDir/gmp/id/ and
+   *    $profileDir/gmp/storage remain unchanged.
+   */
+  void TestClearRecentHistory3() {
+    AssertIsOnGMPThread();
+    EXPECT_TRUE(IsGMPStorageIsEmpty());
+
+    // Generate storage data for some site.
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false);
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
+        this, &GMPStorageTest::TestClearRecentHistory3_Clear);
+    Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
+    Update(NS_LITERAL_CSTRING("test-storage"));
+
+  }
+
+  class MaxMTimeFinder {
+  public:
+    MaxMTimeFinder() : mMaxTime(0) {}
+    void operator()(nsIFile* aFile) {
+      PRTime lastModified;
+      nsresult rv = aFile->GetLastModifiedTime(&lastModified);
+      if (NS_SUCCEEDED(rv) && lastModified > mMaxTime) {
+        mMaxTime = lastModified;
+      }
+      EnumerateDir(aFile, *this);
+    }
+    PRTime GetResult() const { return mMaxTime; }
+  private:
+    PRTime mMaxTime;
+  };
+
+  void TestClearRecentHistory1_Clear() {
+    MaxMTimeFinder f;
+    nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), f);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
+        this, &GMPStorageTest::TestClearRecentHistory_CheckEmpty);
+    nsCOMPtr<nsIThread> t(GetGMPThread());
+    ClearGMPStorage(r, t, f.GetResult());
+  }
+
+  void TestClearRecentHistory2_Clear() {
+    MaxMTimeFinder f;
+    nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), f);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
+        this, &GMPStorageTest::TestClearRecentHistory_CheckEmpty);
+    nsCOMPtr<nsIThread> t(GetGMPThread());
+    ClearGMPStorage(r, t, f.GetResult());
+  }
+
+  void TestClearRecentHistory3_Clear() {
+    MaxMTimeFinder f;
+    nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), f);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
+        this, &GMPStorageTest::TestClearRecentHistory_CheckNonEmpty);
+    nsCOMPtr<nsIThread> t(GetGMPThread());
+    ClearGMPStorage(r, t, f.GetResult() + 1);
+  }
+
+  class FileCounter {
+  public:
+    FileCounter() : mCount(0) {}
+    void operator()(nsIFile* aFile) {
+      ++mCount;
+    }
+    int GetCount() const { return mCount; }
+  private:
+    int mCount;
+  };
+
+  void TestClearRecentHistory_CheckEmpty() {
+    FileCounter c1;
+    nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+    // There should be no files under $profileDir/gmp/id/
+    EXPECT_EQ(c1.GetCount(), 0);
+
+    FileCounter c2;
+    rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+    // There should be no files under $profileDir/gmp/storage/
+    EXPECT_EQ(c2.GetCount(), 0);
+
+    SetFinished();
+  }
+
+  void TestClearRecentHistory_CheckNonEmpty() {
+    FileCounter c1;
+    nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+    // There should be one directory under $profileDir/gmp/id/
+    EXPECT_EQ(c1.GetCount(), 1);
+
+    FileCounter c2;
+    rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+    // There should be one directory under $profileDir/gmp/storage/
+    EXPECT_EQ(c2.GetCount(), 1);
+
+    SetFinished();
+  }
+
   void TestCrossOriginStorage() {
     EXPECT_TRUE(!mDecryptor);
 
     // Open decryptor on one, origin, write a record, and test that that
     // record can't be read on another origin.
     CreateDecryptor(NS_LITERAL_STRING("example3.com"),
                     NS_LITERAL_STRING("example4.com"),
                     false);
@@ -986,16 +1177,31 @@ TEST(GeckoMediaPlugins, GMPStorageBasic)
   runner->DoTest(&GMPStorageTest::TestBasicStorage);
 }
 
 TEST(GeckoMediaPlugins, GMPStorageForgetThisSite) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestForgetThisSite);
 }
 
+TEST(GeckoMediaPlugins, GMPStorageClearRecentHistory1) {
+  nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
+  runner->DoTest(&GMPStorageTest::TestClearRecentHistory1);
+}
+
+TEST(GeckoMediaPlugins, GMPStorageClearRecentHistory2) {
+  nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
+  runner->DoTest(&GMPStorageTest::TestClearRecentHistory2);
+}
+
+TEST(GeckoMediaPlugins, GMPStorageClearRecentHistory3) {
+  nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
+  runner->DoTest(&GMPStorageTest::TestClearRecentHistory3);
+}
+
 TEST(GeckoMediaPlugins, GMPStorageCrossOrigin) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestCrossOriginStorage);
 }
 
 TEST(GeckoMediaPlugins, GMPStoragePrivateBrowsing) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestPBStorage);
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -125,16 +125,21 @@ public:
 
   // Return true if the Ended method has been called
   bool IsEnded();
 
 #ifdef MOZ_EME
   nsresult SetCDMProxy(CDMProxy* aProxy);
 #endif
 
+  virtual bool IsAsync() const MOZ_OVERRIDE {
+    return (!mAudioReader || mAudioReader->IsAsync()) &&
+           (!mVideoReader || mVideoReader->IsAsync());
+  }
+
 private:
   // Switch the current audio/video reader to the reader that
   // contains aTarget (or up to aError after target). Both
   // aTarget and aError are in microseconds.
   bool SwitchAudioReader(int64_t aTarget, int64_t aError = 0);
   bool SwitchVideoReader(int64_t aTarget, int64_t aError = 0);
   void RequestAudioDataComplete(int64_t aTime);
   void RequestAudioDataFailed(nsresult aResult);
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -136,25 +136,25 @@ MediaCodecReader::TrackInputCopier::Copy
          aSourceBuffer->range_length());
 
   return true;
 }
 
 MediaCodecReader::Track::Track(Type type)
   : mType(type)
   , mSourceIsStopped(true)
-  , mDurationLock("MediaCodecReader::Track::mDurationLock")
   , mDurationUs(INT64_C(0))
   , mInputIndex(sInvalidInputIndex)
   , mInputEndOfStream(false)
   , mOutputEndOfStream(false)
   , mSeekTimeUs(sInvalidTimestampUs)
   , mFlushed(false)
   , mDiscontinuity(false)
   , mTaskQueue(nullptr)
+  , mTrackMonitor("MediaCodecReader::mTrackMonitor")
 {
   MOZ_ASSERT(mType != kUnknown, "Should have a valid Track::Type");
 }
 
 // Append the value of |kKeyValidSamples| to the end of each vorbis buffer.
 // https://github.com/mozilla-b2g/platform_frameworks_av/blob/master/media/libstagefright/OMXCodec.cpp#L3128
 // https://github.com/mozilla-b2g/platform_frameworks_av/blob/master/media/libstagefright/NuMediaExtractor.cpp#L472
 bool
@@ -180,27 +180,29 @@ MediaCodecReader::VorbisInputCopier::Cop
          &numPageSamples, sizeof(numPageSamples));
 
   return true;
 }
 
 MediaCodecReader::AudioTrack::AudioTrack()
   : Track(kAudio)
 {
+  mAudioPromise.SetMonitor(&mTrackMonitor);
 }
 
 MediaCodecReader::VideoTrack::VideoTrack()
   : Track(kVideo)
   , mWidth(0)
   , mHeight(0)
   , mStride(0)
   , mSliceHeight(0)
   , mColorFormat(0)
   , mRotation(0)
 {
+  mVideoPromise.SetMonitor(&mTrackMonitor);
 }
 
 MediaCodecReader::CodecBufferInfo::CodecBufferInfo()
   : mIndex(0)
   , mOffset(0)
   , mSize(0)
   , mTimeUs(0)
   , mFlags(0)
@@ -343,85 +345,85 @@ MediaCodecReader::ReleaseMediaResources(
     mAudioTrack.mSourceIsStopped = true;
   }
   ReleaseCriticalResources();
 }
 
 nsRefPtr<ShutdownPromise>
 MediaCodecReader::Shutdown()
 {
-  MOZ_ASSERT(mAudioPromise.IsEmpty());
-  MOZ_ASSERT(mVideoPromise.IsEmpty());
+  MOZ_ASSERT(mAudioTrack.mAudioPromise.IsEmpty());
+  MOZ_ASSERT(mVideoTrack.mVideoPromise.IsEmpty());
   ReleaseResources();
   return MediaDecoderReader::Shutdown();
 }
 
 void
 MediaCodecReader::DispatchAudioTask()
 {
-  if (mAudioTrack.mTaskQueue && mAudioTrack.mTaskQueue->IsEmpty()) {
+  if (mAudioTrack.mTaskQueue) {
     RefPtr<nsIRunnable> task =
       NS_NewRunnableMethod(this,
                            &MediaCodecReader::DecodeAudioDataTask);
     mAudioTrack.mTaskQueue->Dispatch(task);
   }
 }
 
 void
 MediaCodecReader::DispatchVideoTask(int64_t aTimeThreshold)
 {
-  if (mVideoTrack.mTaskQueue && mVideoTrack.mTaskQueue->IsEmpty()) {
+  if (mVideoTrack.mTaskQueue) {
     RefPtr<nsIRunnable> task =
       NS_NewRunnableMethodWithArg<int64_t>(this,
                                            &MediaCodecReader::DecodeVideoFrameTask,
                                            aTimeThreshold);
     mVideoTrack.mTaskQueue->Dispatch(task);
   }
 }
 
 nsRefPtr<MediaDecoderReader::AudioDataPromise>
 MediaCodecReader::RequestAudioData()
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   MOZ_ASSERT(HasAudio());
 
-  nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
+  MonitorAutoLock al(mAudioTrack.mTrackMonitor);
   if (CheckAudioResources()) {
     DispatchAudioTask();
   }
-
-  return p;
+  MOZ_ASSERT(mAudioTrack.mAudioPromise.IsEmpty());
+  return mAudioTrack.mAudioPromise.Ensure(__func__);
 }
 
 nsRefPtr<MediaDecoderReader::VideoDataPromise>
 MediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
                                    int64_t aTimeThreshold)
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   MOZ_ASSERT(HasVideo());
 
-  nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
   int64_t threshold = sInvalidTimestampUs;
   if (aSkipToNextKeyframe && IsValidTimestampUs(aTimeThreshold)) {
-    mVideoTrack.mTaskQueue->Flush();
     threshold = aTimeThreshold;
   }
+
+  MonitorAutoLock al(mVideoTrack.mTrackMonitor);
   if (CheckVideoResources()) {
     DispatchVideoTask(threshold);
   }
-
-  return p;
+  MOZ_ASSERT(mVideoTrack.mVideoPromise.IsEmpty());
+  return mVideoTrack.mVideoPromise.Ensure(__func__);
 }
 
-bool
+void
 MediaCodecReader::DecodeAudioDataSync()
 {
   if (mAudioTrack.mCodec == nullptr || !mAudioTrack.mCodec->allocated() ||
       mAudioTrack.mOutputEndOfStream) {
-    return false;
+    return;
   }
 
   // Get one audio output data from MediaCodec
   CodecBufferInfo bufferInfo;
   status_t status;
   TimeStamp timeout = TimeStamp::Now() +
                       TimeDuration::FromSeconds(sMaxAudioDecodeDurationS);
   while (true) {
@@ -434,40 +436,39 @@ MediaCodecReader::DecodeAudioDataSync()
     if (status == OK || status == ERROR_END_OF_STREAM) {
       break;
     } else if (status == -EAGAIN) {
       if (TimeStamp::Now() > timeout) {
         // Don't let this loop run for too long. Try it again later.
         if (CheckAudioResources()) {
           DispatchAudioTask();
         }
-        return true;
+        return;
       }
       continue; // Try it again now.
     } else if (status == INFO_FORMAT_CHANGED) {
       if (UpdateAudioInfo()) {
         continue; // Try it again now.
       } else {
-        return false;
+        return;
       }
     } else {
-      return false;
+      return;
     }
   }
 
-  bool result = false;
   if (bufferInfo.mBuffer != nullptr && bufferInfo.mSize > 0 &&
       bufferInfo.mBuffer->data() != nullptr) {
     // This is the approximate byte position in the stream.
     int64_t pos = mDecoder->GetResource()->Tell();
 
     uint32_t frames = bufferInfo.mSize /
                       (mInfo.mAudio.mChannels * sizeof(AudioDataValue));
 
-    result = mAudioCompactor.Push(
+    mAudioCompactor.Push(
       pos,
       bufferInfo.mTimeUs,
       mInfo.mAudio.mRate,
       frames,
       mInfo.mAudio.mChannels,
       AudioCompactor::NativeCopy(
         bufferInfo.mBuffer->data() + bufferInfo.mOffset,
         bufferInfo.mSize,
@@ -475,57 +476,54 @@ MediaCodecReader::DecodeAudioDataSync()
   }
 
   if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
       (status == ERROR_END_OF_STREAM)) {
     AudioQueue().Finish();
   }
   mAudioTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
 
-  return result;
 }
 
-bool
+void
 MediaCodecReader::DecodeAudioDataTask()
 {
-  bool result = DecodeAudioDataSync();
+  DecodeAudioDataSync();
+  MonitorAutoLock al(mAudioTrack.mTrackMonitor);
   if (AudioQueue().GetSize() > 0) {
     nsRefPtr<AudioData> a = AudioQueue().PopFront();
     if (a) {
       if (mAudioTrack.mDiscontinuity) {
         a->mDiscontinuity = true;
         mAudioTrack.mDiscontinuity = false;
       }
-      mAudioPromise.Resolve(a, __func__);
+      mAudioTrack.mAudioPromise.Resolve(a, __func__);
     }
+  } else if (AudioQueue().AtEndOfStream()) {
+    mAudioTrack.mAudioPromise.Reject(END_OF_STREAM, __func__);
   }
-  else if (AudioQueue().AtEndOfStream()) {
-    mAudioPromise.Reject(END_OF_STREAM, __func__);
-  }
-  return result;
 }
 
-bool
+void
 MediaCodecReader::DecodeVideoFrameTask(int64_t aTimeThreshold)
 {
-  bool result = DecodeVideoFrameSync(aTimeThreshold);
+  DecodeVideoFrameSync(aTimeThreshold);
+  MonitorAutoLock al(mVideoTrack.mTrackMonitor);
   if (VideoQueue().GetSize() > 0) {
     nsRefPtr<VideoData> v = VideoQueue().PopFront();
     if (v) {
       if (mVideoTrack.mDiscontinuity) {
         v->mDiscontinuity = true;
         mVideoTrack.mDiscontinuity = false;
       }
-      mVideoPromise.Resolve(v, __func__);
+      mVideoTrack.mVideoPromise.Resolve(v, __func__);
     }
+  } else if (VideoQueue().AtEndOfStream()) {
+    mVideoTrack.mVideoPromise.Reject(END_OF_STREAM, __func__);
   }
-  else if (VideoQueue().AtEndOfStream()) {
-    mVideoPromise.Reject(END_OF_STREAM, __func__);
-  }
-  return result;
 }
 
 bool
 MediaCodecReader::HasAudio()
 {
   return mInfo.mAudio.mHasAudio;
 }
 
@@ -647,25 +645,25 @@ MediaCodecReader::ParseDataSegment(const
     mMP3FrameParser->Parse(aBuffer, aLength, aOffset);
 
     duration = mMP3FrameParser->GetDuration();
   }
 
   bool durationUpdateRequired = false;
 
   {
-    MutexAutoLock al(mAudioTrack.mDurationLock);
+    MonitorAutoLock al(mAudioTrack.mTrackMonitor);
     if (duration > mAudioTrack.mDurationUs) {
       mAudioTrack.mDurationUs = duration;
       durationUpdateRequired = true;
     }
   }
 
   if (durationUpdateRequired && HasVideo()) {
-    MutexAutoLock al(mVideoTrack.mDurationLock);
+    MonitorAutoLock al(mVideoTrack.mTrackMonitor);
     durationUpdateRequired = duration > mVideoTrack.mDurationUs;
   }
 
   if (durationUpdateRequired) {
     MOZ_ASSERT(mDecoder);
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->UpdateEstimatedMediaDuration(duration);
   }
@@ -715,22 +713,22 @@ MediaCodecReader::ReadMetadata(MediaInfo
 
   if (!UpdateVideoInfo()) {
     return NS_ERROR_FAILURE;
   }
 
   // Set the total duration (the max of the audio and video track).
   int64_t audioDuration = INT64_C(-1);
   {
-    MutexAutoLock al(mAudioTrack.mDurationLock);
+    MonitorAutoLock al(mAudioTrack.mTrackMonitor);
     audioDuration = mAudioTrack.mDurationUs;
   }
   int64_t videoDuration = INT64_C(-1);
   {
-    MutexAutoLock al(mVideoTrack.mDurationLock);
+    MonitorAutoLock al(mVideoTrack.mTrackMonitor);
     videoDuration = mVideoTrack.mDurationUs;
   }
   int64_t duration = audioDuration > videoDuration ? audioDuration : videoDuration;
   if (duration >= INT64_C(0)) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaDuration(duration);
   }
 
@@ -752,22 +750,22 @@ MediaCodecReader::ReadMetadata(MediaInfo
 
   return NS_OK;
 }
 
 nsresult
 MediaCodecReader::ResetDecode()
 {
   if (CheckAudioResources()) {
-    mAudioTrack.mTaskQueue->Flush();
+    mAudioTrack.mTaskQueue->AwaitIdle();
     FlushCodecData(mAudioTrack);
     mAudioTrack.mDiscontinuity = true;
   }
   if (CheckVideoResources()) {
-    mVideoTrack.mTaskQueue->Flush();
+    mVideoTrack.mTaskQueue->AwaitIdle();
     FlushCodecData(mVideoTrack);
     mVideoTrack.mDiscontinuity = true;
   }
 
   return MediaDecoderReader::ResetDecode();
 }
 
 void
@@ -841,22 +839,22 @@ MediaCodecReader::ReleaseAllTextureClien
     return;
   }
   printf_stderr("All TextureClients should be released already");
 
   mTextureClientIndexes.Enumerate(MediaCodecReader::ReleaseTextureClient, this);
   mTextureClientIndexes.Clear();
 }
 
-bool
+void
 MediaCodecReader::DecodeVideoFrameSync(int64_t aTimeThreshold)
 {
   if (mVideoTrack.mCodec == nullptr || !mVideoTrack.mCodec->allocated() ||
       mVideoTrack.mOutputEndOfStream) {
-    return false;
+    return;
   }
 
   // Get one video output data from MediaCodec
   CodecBufferInfo bufferInfo;
   status_t status;
   TimeStamp timeout = TimeStamp::Now() +
                       TimeDuration::FromSeconds(sMaxVideoDecodeDurationS);
   while (true) {
@@ -869,31 +867,30 @@ MediaCodecReader::DecodeVideoFrameSync(i
     if (status == OK || status == ERROR_END_OF_STREAM) {
       break;
     } else if (status == -EAGAIN) {
       if (TimeStamp::Now() > timeout) {
         // Don't let this loop run for too long. Try it again later.
         if (CheckVideoResources()) {
           DispatchVideoTask(aTimeThreshold);
         }
-        return true;
+        return;
       }
       continue; // Try it again now.
     } else if (status == INFO_FORMAT_CHANGED) {
       if (UpdateVideoInfo()) {
         continue; // Try it again now.
       } else {
-        return false;
+        return;
       }
     } else {
-      return false;
+      return;
     }
   }
 
-  bool result = false;
   nsRefPtr<VideoData> v;
   RefPtr<TextureClient> textureClient;
   sp<GraphicBuffer> graphicBuffer;
   if (bufferInfo.mBuffer != nullptr) {
     // This is the approximate byte position in the stream.
     int64_t pos = mDecoder->GetResource()->Tell();
 
     if (mVideoTrack.mNativeWindow != nullptr &&
@@ -925,17 +922,17 @@ MediaCodecReader::DecodeVideoFrameSync(i
 
         yuv420p_buffer = GetColorConverterBuffer(mVideoTrack.mWidth,
                                                  mVideoTrack.mHeight);
         if (mColorConverter.convertDecoderOutputToI420(
               bufferInfo.mBuffer->data(), mVideoTrack.mWidth, mVideoTrack.mHeight,
               crop, yuv420p_buffer) != OK) {
           mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
           NS_WARNING("Unable to convert color format");
-          return false;
+          return;
         }
 
         stride = mVideoTrack.mWidth;
         slice_height = mVideoTrack.mHeight;
       }
 
       size_t yuv420p_y_size = stride * slice_height;
       size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
@@ -972,37 +969,34 @@ MediaCodecReader::DecodeVideoFrameSync(i
                             1, // We don't know the duration.
                             b,
                             bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_SYNCFRAME,
                             -1,
                             mVideoTrack.mRelativePictureRect);
     }
 
     if (v) {
-      result = true;
       VideoQueue().Push(v);
     } else {
       NS_WARNING("Unable to create VideoData");
     }
   }
 
   if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
       (status == ERROR_END_OF_STREAM)) {
     VideoQueue().Finish();
   }
 
-  if (v != nullptr && textureClient != nullptr && graphicBuffer != nullptr && result) {
+  if (v != nullptr && textureClient != nullptr && graphicBuffer != nullptr) {
     MutexAutoLock al(mTextureClientIndexesLock);
     mTextureClientIndexes.Put(textureClient.get(), bufferInfo.mIndex);
     textureClient->SetRecycleCallback(MediaCodecReader::TextureClientRecycleCallback, this);
   } else {
     mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
   }
-
-  return result;
 }
 
 nsRefPtr<MediaDecoderReader::SeekPromise>
 MediaCodecReader::Seek(int64_t aTime,
                        int64_t aStartTime,
                        int64_t aEndTime,
                        int64_t aCurrentTime)
 {
@@ -1039,27 +1033,16 @@ MediaCodecReader::Seek(int64_t aTime,
       if (format->findInt64(kKeyTime, &timestamp) &&
           IsValidTimestampUs(timestamp)) {
         mVideoTrack.mSeekTimeUs = timestamp;
         mAudioTrack.mSeekTimeUs = timestamp;
       }
       format = nullptr;
     }
     source_buffer->release();
-
-    MOZ_ASSERT(mVideoTrack.mTaskQueue->IsEmpty());
-    DispatchVideoTask(mVideoTrack.mSeekTimeUs);
-
-    if (CheckAudioResources()) {
-      MOZ_ASSERT(mAudioTrack.mTaskQueue->IsEmpty());
-      DispatchAudioTask();
-    }
-  } else if (CheckAudioResources()) {// Audio only
-    MOZ_ASSERT(mAudioTrack.mTaskQueue->IsEmpty());
-    DispatchAudioTask();
   }
   return SeekPromise::CreateAndResolve(aTime, __func__);
 }
 
 bool
 MediaCodecReader::IsMediaSeekable()
 {
   // Check the MediaExtract flag if the source is seekable.
@@ -1451,17 +1434,17 @@ MediaCodecReader::TriggerIncrementalPars
           signalObject->Wait();
         }
       }
       duration = mMP3FrameParser->GetDuration();
     }
   }
 
   {
-    MutexAutoLock al(mAudioTrack.mDurationLock);
+    MonitorAutoLock al(mAudioTrack.mTrackMonitor);
     if (duration > mAudioTrack.mDurationUs) {
       mAudioTrack.mDurationUs = duration;
     }
   }
 
   return true;
 }
 
@@ -1469,31 +1452,31 @@ bool
 MediaCodecReader::UpdateDuration()
 {
   // read audio duration
   if (mAudioTrack.mSource != nullptr) {
     sp<MetaData> audioFormat = mAudioTrack.mSource->getFormat();
     if (audioFormat != nullptr) {
       int64_t duration = INT64_C(0);
       if (audioFormat->findInt64(kKeyDuration, &duration)) {
-        MutexAutoLock al(mAudioTrack.mDurationLock);
+        MonitorAutoLock al(mAudioTrack.mTrackMonitor);
         if (duration > mAudioTrack.mDurationUs) {
           mAudioTrack.mDurationUs = duration;
         }
       }
     }
   }
 
   // read video duration
   if (mVideoTrack.mSource != nullptr) {
     sp<MetaData> videoFormat = mVideoTrack.mSource->getFormat();
     if (videoFormat != nullptr) {
       int64_t duration = INT64_C(0);
       if (videoFormat->findInt64(kKeyDuration, &duration)) {
-        MutexAutoLock al(mVideoTrack.mDurationLock);
+        MonitorAutoLock al(mVideoTrack.mTrackMonitor);
         if (duration > mVideoTrack.mDurationUs) {
           mVideoTrack.mDurationUs = duration;
         }
       }
     }
   }
 
   return true;
--- a/dom/media/omx/MediaCodecReader.h
+++ b/dom/media/omx/MediaCodecReader.h
@@ -105,16 +105,18 @@ public:
        int64_t aStartTime,
        int64_t aEndTime,
        int64_t aCurrentTime) MOZ_OVERRIDE;
 
   virtual bool IsMediaSeekable() MOZ_OVERRIDE;
 
   virtual android::sp<android::MediaSource> GetAudioOffloadTrack();
 
+  virtual bool IsAsync() const MOZ_OVERRIDE { return true; }
+
 protected:
   struct TrackInputCopier
   {
     virtual ~TrackInputCopier();
 
     virtual bool Copy(android::MediaBuffer* aSourceBuffer,
                       android::sp<android::ABuffer> aCodecBuffer);
   };
@@ -141,32 +143,32 @@ protected:
     android::sp<android::GonkNativeWindow> mNativeWindow;
 #if ANDROID_VERSION >= 21
     android::sp<android::IGraphicBufferProducer> mGraphicBufferProducer;
 #endif
 
     // pipeline copier
     nsAutoPtr<TrackInputCopier> mInputCopier;
 
-    // media parameters
-    Mutex mDurationLock; // mDurationUs might be read or updated from multiple
-                         // threads.
+    // Protected by mTrackMonitor.
+    // mDurationUs might be read or updated from multiple threads.
     int64_t mDurationUs;
 
     // playback parameters
     CheckedUint32 mInputIndex;
     // mDiscontinuity, mFlushed, mInputEndOfStream, mInputEndOfStream,
     // mSeekTimeUs don't be protected by a lock because the
     // mTaskQueue->Flush() will flush all tasks.
     bool mInputEndOfStream;
     bool mOutputEndOfStream;
     int64_t mSeekTimeUs;
     bool mFlushed; // meaningless when mSeekTimeUs is invalid.
     bool mDiscontinuity;
     nsRefPtr<MediaTaskQueue> mTaskQueue;
+    Monitor mTrackMonitor;
 
   private:
     // Forbidden
     Track(const Track &rhs) = delete;
     const Track &operator=(const Track&) = delete;
   };
 
   // Receive a message from MessageHandler.
@@ -235,16 +237,18 @@ private:
   {
     virtual bool Copy(android::MediaBuffer* aSourceBuffer,
                       android::sp<android::ABuffer> aCodecBuffer);
   };
 
   struct AudioTrack : public Track
   {
     AudioTrack();
+    // Protected by mTrackMonitor.
+    MediaPromiseHolder<AudioDataPromise> mAudioPromise;
 
   private:
     // Forbidden
     AudioTrack(const AudioTrack &rhs) = delete;
     const AudioTrack &operator=(const AudioTrack &rhs) = delete;
   };
 
   struct VideoTrack : public Track
@@ -255,16 +259,18 @@ private:
     int32_t mHeight;
     int32_t mStride;
     int32_t mSliceHeight;
     int32_t mColorFormat;
     int32_t mRotation;
     nsIntSize mFrameSize;
     nsIntRect mPictureRect;
     gfx::IntRect mRelativePictureRect;
+    // Protected by mTrackMonitor.
+    MediaPromiseHolder<VideoDataPromise> mVideoPromise;
 
   private:
     // Forbidden
     VideoTrack(const VideoTrack &rhs) = delete;
     const VideoTrack &operator=(const VideoTrack &rhs) = delete;
   };
 
   struct CodecBufferInfo
@@ -365,20 +371,20 @@ private:
                                bool aAsync,
                                android::wp<android::MediaCodecProxy::CodecResourceListener> aListener);
   static bool ConfigureMediaCodec(Track& aTrack);
   void DestroyMediaCodecs();
   static void DestroyMediaCodec(Track& aTrack);
 
   bool CreateTaskQueues();
   void ShutdownTaskQueues();
-  bool DecodeVideoFrameTask(int64_t aTimeThreshold);
-  bool DecodeVideoFrameSync(int64_t aTimeThreshold);
-  bool DecodeAudioDataTask();
-  bool DecodeAudioDataSync();
+  void DecodeVideoFrameTask(int64_t aTimeThreshold);
+  void DecodeVideoFrameSync(int64_t aTimeThreshold);
+  void DecodeAudioDataTask();
+  void DecodeAudioDataSync();
   void DispatchVideoTask(int64_t aTimeThreshold);
   void DispatchAudioTask();
   inline bool CheckVideoResources() {
     return (HasVideo() && mVideoTrack.mSource != nullptr &&
             mVideoTrack.mTaskQueue);
   }
 
   inline bool CheckAudioResources() {
@@ -431,19 +437,16 @@ private:
   Mutex mTextureClientIndexesLock;
   nsDataHashtable<nsPtrHashKey<TextureClient>, size_t> mTextureClientIndexes;
 
   // media tracks
   AudioTrack mAudioTrack;
   VideoTrack mVideoTrack;
   AudioTrack mAudioOffloadTrack; // only Track::mSource is valid
 
-  MediaPromiseHolder<AudioDataPromise> mAudioPromise;
-  MediaPromiseHolder<VideoDataPromise> mVideoPromise;
-
   // color converter
   android::I420ColorConverterHelper mColorConverter;
   nsAutoArrayPtr<uint8_t> mColorConverterBuffer;
   size_t mColorConverterBufferSize;
 
   // incremental parser
   Monitor mParserMonitor;
   bool mParseDataFromCache;
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -193,16 +193,19 @@ function SetupEME(test, token, params)
     "loadedmetadata", "loadstart", "pause", "play", "playing", "progress",
     "stalled", "suspend", "waiting",
   ].forEach(function (e) {
     v.addEventListener(e, function(event) {
       Log(token, "" + e);
     }, false);
   });
 
+  // Finish the test when error is encountered.
+  v.onerror = bail(token + " got error event");
+
   var onSetKeysFail = (params && params.onSetKeysFail)
     ? params.onSetKeysFail
     : bail(token + " Failed to set MediaKeys on <video> element");
 
   v.addEventListener("encrypted", function(ev) {
     Log(token, "got encrypted event");
     var options = [
       {
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -51,17 +51,19 @@ var gProgressTests = [
 
 // Used by test_played.html
 var gPlayedTests = [
   { name:"big.wav", type:"audio/x-wav", duration:9.0 },
   { name:"seek.ogv", type:"video/ogg", duration:3.966 },
   { name:"seek.webm", type:"video/webm", duration:3.966 },
   { name:"gizmo.mp4", type:"video/mp4", duration:5.56 },
   { name:"owl.mp3", type:"audio/mpeg", duration:3.29 },
-  { name:"vbr.mp3", type:"audio/mpeg", duration:10.0 },
+  // Disable vbr.mp3 to see if it reduces the error of AUDCLNT_E_CPUUSAGE_EXCEEDED.
+  // See bug 1110922 comment 26.
+  //{ name:"vbr.mp3", type:"audio/mpeg", duration:10.0 },
   { name:"bug495794.ogg", type:"audio/ogg", duration:0.3 }
 ];
 
 // Used by test_mozLoadFrom.  Need one test file per decoder backend, plus
 // anything for testing clone-specific bugs.
 var cloneKey = Math.floor(Math.random()*100000000);
 var gCloneTests = gSmallTests.concat([
   // Actual duration is ~200ms, we have Content-Duration lie about it.
--- a/dom/media/test/test_eme_canvas_blocked.html
+++ b/dom/media/test/test_eme_canvas_blocked.html
@@ -33,18 +33,16 @@ function startTest(test, token)
       ctx.drawImage(video, 0, 0);
     } catch (ex) {
       threwError = true;
     }
     ok(threwError, TimeStamp(token) + " - Should throw an error when trying to draw EME video to canvas.");
     manager.finished(token);
   });
 
-  v.addEventListener("error", bail(token + " got error event"));
-
   LoadTest(test, v, token);
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
 var prefs = [
--- a/dom/media/test/test_eme_persistent_sessions.html
+++ b/dom/media/test/test_eme_persistent_sessions.html
@@ -122,18 +122,16 @@ function startTest(test, token)
           manager.finished(token);
         }, bail(token + " failure to load session."));
 
       },
       sessionType: "persistent",
     }
   );
 
-  v.addEventListener("error", bail(token + " got error event"));
-
   LoadTest(test, v, token);
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
 var prefs = [
--- a/dom/media/test/test_eme_playback.html
+++ b/dom/media/test/test_eme_playback.html
@@ -76,18 +76,16 @@ function startTest(test, token)
       for (var kid in session.keyIdsReceived) {
         ok(session.keyIdsReceived[kid], TimeStamp(token) + " key with id " + kid + " was usable as expected");
       }
     }
 
     manager.finished(token);
    });
 
-  v.addEventListener("error", bail(token + " got error event"));
-
   LoadTest(test, v, token).then(function(){v.play();}, bail(token + " failed to load"));
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
 var prefs = [
--- a/dom/media/test/test_eme_stream_capture_blocked.html
+++ b/dom/media/test/test_eme_stream_capture_blocked.html
@@ -23,28 +23,26 @@ function startTest(test, token)
   var case1token = token + "_case1";
   var setKeysFailed = function() {
     ok(true, TimeStamp(case1token) + " setMediaKeys failed as expected.");
     manager.finished(case1token);
   };
   var v1 = SetupEME(test, case1token,  { onSetKeysFail: setKeysFailed });
   var context = new AudioContext();
   var node = context.createMediaElementSource(v1);
-  v1.addEventListener("error", bail(case1token + " got error event"));
   v1.addEventListener("loadeddata", function(ev) {
     ok(false, TimeStamp(case1token) + " should never reach loadeddata, as setMediaKeys should fail");
   });
   manager.started(case1token);
   LoadTest(test, v1, case1token);
 
 
   // Case 2. creating a MediaElementSource on a media element with a MediaKeys should fail.
   var case2token = token + "_case2";
   var v2 = SetupEME(test, case2token);
-  v2.addEventListener("error", bail(case2token + " got error event"));
   v2.addEventListener("loadeddata", function(ev) {
     ok(true, case2token + " should reach loadeddata");
     var threw = false;
     try {
       var context = new AudioContext();
       var node = context.createMediaElementSource(v2);
     } catch (e) {
       threw = true;
@@ -54,17 +52,16 @@ function startTest(test, token)
   });
   manager.started(case2token);
   LoadTest(test, v2, case2token);
 
 
   // Case 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
   var case3token = token + "_case3";
   var v3 = SetupEME(test, case3token);
-  v3.addEventListener("error", bail(case3token + " got error event"));
   v3.addEventListener("loadeddata", function(ev) {
     ok(true, TimeStamp(case3token) + " should reach loadeddata");
     var threw = false;
     try {
       var stream = v3.mozCaptureStreamUntilEnded();
     } catch (e) {
       threw = true;
     }
--- a/dom/media/test/test_info_leak.html
+++ b/dom/media/test/test_info_leak.html
@@ -81,17 +81,17 @@ function listener(evt) {
     // other events to come in. Note: we don't expect any events to come
     // in, unless we've leaked some info, and 1 second should be enough time
     // for the leak to show up.
     setTimeout(function() {finish(v);}, 1000); 
   }
 }
 
 function createMedia(type, src, token) {
-  var tag = getMajorMimeType(test.type);
+  var tag = getMajorMimeType(type);
   var v = document.createElement(tag);
   for (var i=0; i<gEventTypes.length; i++) {
     v.addEventListener(gEventTypes[i], listener, false);
   }
   v.preload = "metadata";
   v.src = src;
   v.name = src;
   document.body.appendChild(v);
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -41,16 +41,17 @@ try {
 
 const kMessages =["SystemMessageManager:GetPendingMessages",
                   "SystemMessageManager:HasPendingMessages",
                   "SystemMessageManager:Register",
                   "SystemMessageManager:Unregister",
                   "SystemMessageManager:Message:Return:OK",
                   "SystemMessageManager:AskReadyToRegister",
                   "SystemMessageManager:HandleMessagesDone",
+                  "SystemMessageManager:HandleMessageDone",
                   "child-process-shutdown"]
 
 function debug(aMsg) {
   // dump("-- SystemMessageInternal " + Date.now() + " : " + aMsg + "\n");
 }