Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 28 Apr 2016 16:34:59 +0200
changeset 295516 45e67d6b5a6137898898c23ae1fe60ddfcd59ed1
parent 295515 03eac758d8d7b0ce6b284bc1cf17512fe7a1ca2e (current diff)
parent 295287 4292da9df16b220f30921100282264a34cd2ff03 (diff)
child 295517 4dc48f3688f053e2fa74bf6c7beb4dff274ffc1f
push id75951
push userkwierso@gmail.com
push dateFri, 29 Apr 2016 23:01:24 +0000
treeherdermozilla-inbound@f8896b71e9bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
b2g/components/MultiscreenHandler.jsm
ipc/chromium/src/base/tracked.cc
ipc/chromium/src/base/tracked.h
ipc/chromium/src/base/tracked_objects.cc
ipc/chromium/src/base/tracked_objects.h
js/xpconnect/idl/nsIRemoteTagService.idl
netwerk/base/nsNetUtil.inl
testing/mozharness/configs/android/android_panda_releng.py
testing/web-platform/meta/html/semantics/forms/the-input-element/checkbox.html.ini
testing/web-platform/tests/web-animations/timing-model/active-time.html
testing/web-platform/tests/web-animations/timing-model/current-iteration.html
testing/web-platform/tests/web-animations/timing-model/simple-iteration-progress.html
toolkit/themes/shared/narrate/narrate.svg
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -41,18 +41,25 @@ DocAccessibleParent::RecvShowEvent(const
   }
 
   uint32_t newChildIdx = aData.Idx();
   if (newChildIdx > parent->ChildrenCount()) {
     NS_ERROR("invalid index to add child at");
     return true;
   }
 
-  DebugOnly<uint32_t> consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
+  uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
   MOZ_ASSERT(consumed == aData.NewTree().Length());
+
+  // XXX This shouldn't happen, but if we failed to add children then the below
+  // is pointless and can crash.
+  if (!consumed) {
+    return true;
+  }
+
 #ifdef DEBUG
   for (uint32_t i = 0; i < consumed; i++) {
     uint64_t id = aData.NewTree()[i].ID();
     MOZ_ASSERT(mAccessibles.GetEntry(id));
   }
 #endif
 
   MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -16,17 +16,16 @@ Cu.import("resource://gre/modules/AppsUt
 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');
 Cu.import('resource://gre/modules/SystemUpdateService.jsm');
 
 if (isGonk) {
-  Cu.import('resource://gre/modules/MultiscreenHandler.jsm');
   Cu.import('resource://gre/modules/NetworkStatsService.jsm');
   Cu.import('resource://gre/modules/ResourceStatsService.jsm');
 }
 
 Cu.import('resource://gre/modules/KillSwitchMain.jsm');
 
 // Identity
 Cu.import('resource://gre/modules/SignInToWebsite.jsm');
--- a/b2g/chrome/content/shell_remote.js
+++ b/b2g/chrome/content/shell_remote.js
@@ -1,71 +1,139 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* 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";
 
-var {utils: Cu} = Components;
+var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/SystemAppProxy.jsm");
 
 function debug(aStr) {
   // dump(" -*- ShellRemote.js: " + aStr + "\n");
 }
 
 var remoteShell = {
 
+  _started: false,
+
   get homeURL() {
     let systemAppManifestURL = Services.io.newURI(this.systemAppManifestURL, null, null);
     let shellRemoteURL = Services.prefs.getCharPref("b2g.multiscreen.system_remote_url");
     shellRemoteURL = Services.io.newURI(shellRemoteURL, null, systemAppManifestURL);
     return shellRemoteURL.spec;
   },
 
   get systemAppManifestURL() {
     return Services.prefs.getCharPref("b2g.system_manifest_url");
   },
 
-  _started: false,
-
   hasStarted: function () {
     return this._started;
   },
 
   start: function () {
     this._started = true;
+    this._isEventListenerReady = false;
+    this.id = window.location.hash.substring(1);
 
     let homeURL = this.homeURL;
     if (!homeURL) {
       debug("ERROR! Remote home URL undefined.");
       return;
     }
     let manifestURL = this.systemAppManifestURL;
-    // <html:iframe id="remote-systemapp"
-    //              mozbrowser="true" allowfullscreen="true"
+    // <html:iframe id="this.id"
+    //              mozbrowser="true"
+    //              allowfullscreen="true"
     //              src="blank.html"/>
     let systemAppFrame =
       document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
-    systemAppFrame.setAttribute("id", "remote-systemapp");
+    systemAppFrame.setAttribute("id", this.id);
     systemAppFrame.setAttribute("mozbrowser", "true");
     systemAppFrame.setAttribute("mozapp", manifestURL);
     systemAppFrame.setAttribute("allowfullscreen", "true");
     systemAppFrame.setAttribute("src", "blank.html");
 
     let container = document.getElementById("container");
     this.contentBrowser = container.appendChild(systemAppFrame);
     this.contentBrowser.src = homeURL + window.location.hash;
+
+    window.addEventListener("unload", this);
+    this.contentBrowser.addEventListener("mozbrowserloadstart", this);
   },
 
   stop: function () {
+    window.removeEventListener("unload", this);
+    this.contentBrowser.removeEventListener("mozbrowserloadstart", this);
+    this.contentBrowser.removeEventListener("mozbrowserlocationchange", this, true);
+    SystemAppProxy.unregisterFrameWithId(this.id);
+  },
+
+  notifyContentStart: function(evt) {
+    this.contentBrowser.removeEventListener("mozbrowserloadstart", this);
+    this.contentBrowser.removeEventListener("mozbrowserlocationchange", this, true);
+
+    SystemAppProxy.registerFrameWithId(remoteShell.id, remoteShell.contentBrowser);
+    SystemAppProxy.addEventListenerWithId(this.id, "mozContentEvent", this);
+
+    let content = this.contentBrowser.contentWindow;
+    content.addEventListener("load", this, true);
+  },
+
+  notifyContentWindowLoaded: function () {
+    SystemAppProxy.setIsLoadedWithId(this.id);
+  },
+
+  notifyEventListenerReady: function () {
+    if (this._isEventListenerReady) {
+      Cu.reportError("shell_remote.js: SystemApp has already been declared as being ready.");
+      return;
+    }
+    this._isEventListenerReady = true;
+    SystemAppProxy.setIsReadyWithId(this.id);
   },
 
+  handleEvent: function(evt) {
+    debug("Got an event: " + evt.type);
+    let content = this.contentBrowser.contentWindow;
+
+    switch(evt.type) {
+      case "mozContentEvent":
+        if (evt.detail.type === "system-message-listener-ready") {
+          this.notifyEventListenerReady();
+        }
+        break;
+      case "load":
+        if (content.document.location == "about:blank") {
+          return;
+        }
+        content.removeEventListener("load", this, true);
+        this.notifyContentWindowLoaded();
+        break;
+      case "mozbrowserloadstart":
+        if (content.document.location == "about:blank") {
+          this.contentBrowser.addEventListener("mozbrowserlocationchange", this, true);
+          return;
+        }
+      case "mozbrowserlocationchange":
+        if (content.document.location == "about:blank") {
+          return;
+        }
+        this.notifyContentStart();
+        break;
+      case "unload":
+        this.stop();
+        break;
+    }
+  }
 };
 
 window.onload = function() {
   if (remoteShell.hasStarted() == false) {
     remoteShell.start();
   }
 };
 
deleted file mode 100644
--- a/b2g/components/MultiscreenHandler.jsm
+++ /dev/null
@@ -1,92 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["MultiscreenHandler"];
-
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-function debug(aStr) {
-  // dump("MultiscreenHandler: " + aStr + "\n");
-}
-
-var window = Services.wm.getMostRecentWindow("navigator:browser");
-
-// Multi-screen support on b2g. The following implementation will open a new
-// top-level window once we receive a display connected event.
-var MultiscreenHandler = {
-
-  topLevelWindows: new Map(),
-
-  init: function init() {
-    Services.obs.addObserver(this, "display-changed", false);
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
-  },
-
-  uninit: function uninit() {
-    Services.obs.removeObserver(this, "display-changed");
-    Services.obs.removeObserver(this, "xpcom-shutdown");
-  },
-
-  observe: function observe(aSubject, aTopic, aData) {
-    switch (aTopic) {
-      case "display-changed":
-        this.handleDisplayChangeEvent(aSubject);
-        break
-      case "xpcom-shutdown":
-        this.uninit();
-        break
-    }
-  },
-
-  openTopLevelWindow: function openTopLevelWindow(aDisplay) {
-    if (this.topLevelWindows.get(aDisplay.id)) {
-      debug("Top level window for display id: " + aDisplay.id + " has been opened.");
-      return;
-    }
-
-    let flags = Services.prefs.getCharPref("toolkit.defaultChromeFeatures") +
-                ",mozDisplayId=" + aDisplay.id;
-    let remoteShellURL = Services.prefs.getCharPref("b2g.multiscreen.chrome_remote_url") +
-                         "#" + aDisplay.id;
-    let win = Services.ww.openWindow(null, remoteShellURL, "myTopWindow" + aDisplay.id, flags, null);
-
-    this.topLevelWindows.set(aDisplay.id, win);
-  },
-
-  closeTopLevelWindow: function closeTopLevelWindow(aDisplay) {
-    let win = this.topLevelWindows.get(aDisplay.id);
-
-    if (win) {
-      win.close();
-      this.topLevelWindows.delete(aDisplay.id);
-    }
-  },
-
-  handleDisplayChangeEvent: function handleDisplayChangeEvent(aSubject) {
-
-    let display = aSubject.QueryInterface(Ci.nsIDisplayInfo);
-    let name = "multiscreen.enabled";
-    let req = window.navigator.mozSettings.createLock().get(name);
-
-    req.addEventListener("success", () => {
-      let isMultiscreenEnabled = req.result[name];
-      if (display.connected) {
-        if (isMultiscreenEnabled) {
-          this.openTopLevelWindow(display);
-        }
-      } else {
-        this.closeTopLevelWindow(display);
-      }
-    });
-  },
-
-};
-
-MultiscreenHandler.init();
-this.MultiscreenHandler = MultiscreenHandler;
--- a/b2g/components/PresentationRequestUIGlue.js
+++ b/b2g/components/PresentationRequestUIGlue.js
@@ -1,85 +1,112 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * 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("-*- PresentationRequestUIGlue: " + aMsg + "\n");
+  // dump("-*- PresentationRequestUIGlue: " + aMsg + "\n");
 }
 
 const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
-function PresentationRequestUIGlue() {
-  // This is to store the session ID / resolver binding.
-  // An example of the object literal is shown below:
-  //
-  // {
-  //   "sessionId1" : resolver1,
-  //   ...
-  // }
-  this._resolvers = {};
-
-  // Listen to the result for the opened iframe from front-end.
-  SystemAppProxy.addEventListener("mozPresentationContentEvent", aEvent => {
-    let detail = aEvent.detail;
-
-    switch (detail.type) {
-      case "presentation-receiver-launched": {
-        let sessionId = detail.id;
-        let resolver = this._resolvers[sessionId];
-        if (!resolver) {
-          debug("No correspondent resolver for session ID: " + sessionId);
-          return;
-        }
-
-        delete this._resolvers[sessionId];
-        resolver.resolve(detail.frame);
-        break;
-      }
-      case "presentation-receiver-permission-denied": {
-        let sessionId = detail.id;
-        let resolver = this._resolvers[sessionId];
-        if (!resolver) {
-          debug("No correspondent resolver for session ID: " + sessionId);
-          return;
-        }
-
-        delete this._resolvers[sessionId];
-        resolver.reject();
-        break;
-      }
-      default:
-        return;
-      }
-  });
-}
+function PresentationRequestUIGlue() { }
 
 PresentationRequestUIGlue.prototype = {
 
-  sendRequest: function(aUrl, aSessionId) {
-    return new Promise(function(aResolve, aReject) {
-      this._resolvers[aSessionId] = {
-        resolve: aResolve,
-        reject: aReject,
+  sendRequest: function(aUrl, aSessionId, aDevice) {
+    let localDevice = aDevice.QueryInterface(Ci.nsIPresentationLocalDevice);
+    if (localDevice) {
+      return this.sendTo1UA(aUrl, aSessionId, localDevice.windowId);
+    } else {
+      return this.sendTo2UA(aUrl, aSessionId);
+    }
+  },
+
+  // For 1-UA scenario
+  sendTo1UA: function(aUrl, aSessionId, aWindowId) {
+    return new Promise((aResolve, aReject) => {
+      let handler = (evt) => {
+        if (evt.type === "unload") {
+          SystemAppProxy.removeEventListenerWithId(aWindowId,
+                                                   "unload",
+                                                   handler);
+          SystemAppProxy.removeEventListenerWithId(aWindowId,
+                                                   "mozPresentationContentEvent",
+                                                   handler);
+          aReject();
+        }
+        if (evt.type === "mozPresentationContentEvent" &&
+            evt.detail.id == aSessionId) {
+          SystemAppProxy.removeEventListenerWithId(aWindowId,
+                                                   "unload",
+                                                   handler);
+          SystemAppProxy.removeEventListenerWithId(aWindowId,
+                                                   "mozPresentationContentEvent",
+                                                   handler);
+          this.appLaunchCallback(evt.detail, aResolve, aReject);
+        }
+      };
+      // If system(-remote) app is closed.
+      SystemAppProxy.addEventListenerWithId(aWindowId,
+                                            "unload",
+                                            handler);
+      // Listen to the result for the opened iframe from front-end.
+      SystemAppProxy.addEventListenerWithId(aWindowId,
+                                            "mozPresentationContentEvent",
+                                            handler);
+      SystemAppProxy.sendCustomEventWithId(aWindowId,
+                                           "mozPresentationChromeEvent",
+                                           { type: "presentation-launch-receiver",
+                                             url: aUrl,
+                                             id: aSessionId });
+    });
+  },
+
+  // For 2-UA scenario
+  sendTo2UA: function(aUrl, aSessionId) {
+    return new Promise((aResolve, aReject) => {
+      let handler = (evt) => {
+        if (evt.type === "mozPresentationContentEvent" &&
+            evt.detail.id == aSessionId) {
+          SystemAppProxy.removeEventListener("mozPresentationContentEvent",
+                                            handler);
+          this.appLaunchCallback(evt.detail, aResolve, aReject);
+        }
       };
 
+      // Listen to the result for the opened iframe from front-end.
+      SystemAppProxy.addEventListener("mozPresentationContentEvent",
+                                      handler);
       SystemAppProxy._sendCustomEvent("mozPresentationChromeEvent",
                                       { type: "presentation-launch-receiver",
                                         url: aUrl,
                                         id: aSessionId });
-    }.bind(this));
+    });
+  },
+
+  appLaunchCallback: function(aDetail, aResolve, aReject) {
+    switch(aDetail.type) {
+      case "presentation-receiver-launched":
+        aResolve(aDetail.frame);
+        break;
+      case "presentation-receiver-permission-denied":
+        aReject();
+        break;
+    }
   },
 
   classID: Components.ID("{ccc8a839-0b64-422b-8a60-fb2af0e376d0}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationRequestUIGlue]);
--- a/b2g/components/SystemAppProxy.jsm
+++ b/b2g/components/SystemAppProxy.jsm
@@ -1,218 +1,377 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 
 this.EXPORTED_SYMBOLS = ['SystemAppProxy'];
 
+const kMainSystemAppId = 'main';
+
 var SystemAppProxy = {
-  _frame: null,
-  _isLoaded: false,
-  _isReady: false,
+  _frameInfoMap: new Map(),
   _pendingLoadedEvents: [],
   _pendingReadyEvents: [],
   _pendingListeners: [],
 
-  // To call when a new system app iframe is created
-  registerFrame: function (frame) {
-    this._isReady = false;
-    this._frame = frame;
+  // To call when a main system app iframe is created
+  // Only used for main system app.
+  registerFrame: function systemApp_registerFrame(frame) {
+    this.registerFrameWithId(kMainSystemAppId, frame);
+  },
+
+  // To call when a new system(-remote) app iframe is created with ID
+  registerFrameWithId: function systemApp_registerFrameWithId(frameId,
+                                                              frame) {
+    // - Frame ID of main system app is predefined as 'main'.
+    // - Frame ID of system-remote app is defined themselves.
+    //
+    // frameInfo = {
+    //    isReady: ...,
+    //    isLoaded: ...,
+    //    frame: ...
+    // }
+
+    let frameInfo = { frameId: frameId,
+                      isReady: false,
+                      isLoaded: false,
+                      frame: frame };
 
-    // Register all DOM event listeners added before we got a ref to the app iframe
+    this._frameInfoMap.set(frameId, frameInfo);
+
+    // Register all DOM event listeners added before we got a ref to
+    // this system app iframe.
     this._pendingListeners
-        .forEach((args) =>
-                 this.addEventListener.apply(this, args));
-    this._pendingListeners = [];
+        .forEach(args => {
+          if (args[0] === frameInfo.frameId) {
+            this.addEventListenerWithId.apply(this, args);
+          }
+        });
+    // Removed registered event listeners.
+    this._pendingListeners =
+      this._pendingListeners
+          .filter(args => { return args[0] != frameInfo.frameId; });
+  },
+
+  unregisterFrameWithId: function systemApp_unregisterFrameWithId(frameId) {
+    this._frameInfoMap.delete(frameId);
+    // remove all pending event listener to the deleted system(-remote) app
+    this._pendingListeners = this._pendingListeners.filter(
+      args => { return args[0] != frameId; });
+    this._pendingReadyEvents = this._pendingReadyEvents.filter(
+        ([evtFrameId]) => { return evtFrameId != frameId });
+    this._pendingLoadedEvents = this._pendingLoadedEvents.filter(
+        ([evtFrameId]) => { return evtFrameId != frameId });
   },
 
-  // Get the system app frame
-  getFrame: function () {
-    return this._frame;
+  // Get the main system app frame
+  _getMainSystemAppInfo: function systemApp_getMainSystemAppInfo() {
+    return this._frameInfoMap.get(kMainSystemAppId);
+  },
+
+  // Get the main system app frame
+  // Only used for the main system app.
+  getFrame: function systemApp_getFrame() {
+    return this.getFrameWithId(kMainSystemAppId);
+  },
+
+  // Get the frame of the specific system app
+  getFrameWithId: function systemApp_getFrameWithId(frameId) {
+    let frameInfo = this._frameInfoMap.get(frameId);
+
+    if (!frameInfo) {
+      throw new Error('no frame ID is ' + frameId);
+    }
+    if (!frameInfo.frame) {
+      throw new Error('no content window');
+    }
+    return frameInfo.frame;
   },
 
-  // To call when the load event of the System app document is triggered.
+  // To call when the load event of the main system app document is triggered.
   // i.e. everything that is not lazily loaded are run and done.
-  setIsLoaded: function () {
-    if (this._isLoaded) {
-      Cu.reportError('SystemApp has already been declared as being loaded.');
+  // Only used for the main system app.
+  setIsLoaded: function systemApp_setIsLoaded() {
+    this.setIsLoadedWithId(kMainSystemAppId);
+  },
+
+  // To call when the load event of the specific system app document is
+  // triggered. i.e. everything that is not lazily loaded are run and done.
+  setIsLoadedWithId: function systemApp_setIsLoadedWithId(frameId) {
+    let frameInfo = this._frameInfoMap.get(frameId);
+    if (!frameInfo) {
+      throw new Error('no frame ID is ' + frameId);
     }
-    this._isLoaded = true;
+
+    if (frameInfo.isLoaded) {
+      if (frameInfo.frameId === kMainSystemAppId) {
+        Cu.reportError('SystemApp has already been declared as being loaded.');
+      }
+      else {
+        Cu.reportError('SystemRemoteApp (ID: ' + frameInfo.frameId + ') ' +
+                       'has already been declared as being loaded.');
+      }
+    }
+
+    frameInfo.isLoaded = true;
 
     // Dispatch all events being queued while the system app was still loading
     this._pendingLoadedEvents
-        .forEach(([type, details]) =>
-                 this._sendCustomEvent(type, details, true));
-    this._pendingLoadedEvents = [];
+        .forEach(([evtFrameId, evtType, evtDetails]) => {
+          if (evtFrameId === frameInfo.frameId) {
+            this.sendCustomEventWithId(evtFrameId, evtType, evtDetails, true);
+          }
+        });
+    // Remove sent events.
+    this._pendingLoadedEvents =
+      this._pendingLoadedEvents
+          .filter(([evtFrameId]) => { return evtFrameId != frameInfo.frameId });
   },
 
-  // To call when it is ready to receive events
+  // To call when the main system app is ready to receive events
   // i.e. when system-message-listener-ready mozContentEvent is sent.
-  setIsReady: function () {
-    if (!this._isLoaded) {
+  // Only used for the main system app.
+  setIsReady: function systemApp_setIsReady() {
+    this.setIsReadyWithId(kMainSystemAppId);
+  },
+
+  // To call when the specific system(-remote) app is ready to receive events
+  // i.e. when system-message-listener-ready mozContentEvent is sent.
+  setIsReadyWithId: function systemApp_setIsReadyWithId(frameId) {
+    let frameInfo = this._frameInfoMap.get(frameId);
+    if (!frameInfo) {
+      throw new Error('no frame ID is ' + frameId);
+    }
+
+    if (!frameInfo.isLoaded) {
       Cu.reportError('SystemApp.setIsLoaded() should be called before setIsReady().');
     }
 
-    if (this._isReady) {
+    if (frameInfo.isReady) {
       Cu.reportError('SystemApp has already been declared as being ready.');
     }
-    this._isReady = true;
+
+    frameInfo.isReady = true;
 
     // Dispatch all events being queued while the system app was still not ready
     this._pendingReadyEvents
-        .forEach(([type, details]) =>
-                 this._sendCustomEvent(type, details));
-    this._pendingReadyEvents = [];
+        .forEach(([evtFrameId, evtType, evtDetails]) => {
+          if (evtFrameId === frameInfo.frameId) {
+            this.sendCustomEventWithId(evtFrameId, evtType, evtDetails);
+          }
+        });
+
+    // Remove sent events.
+    this._pendingReadyEvents =
+      this._pendingReadyEvents
+          .filter(([evtFrameId]) => { return evtFrameId != frameInfo.frameId });
   },
 
   /*
-   * Common way to send an event to the system app.
+   * Common way to send an event to the main system app.
+   * Only used for the main system app.
    *
    * // In gecko code:
    *   SystemAppProxy.sendCustomEvent('foo', { data: 'bar' });
    * // In system app:
    *   window.addEventListener('foo', function (event) {
    *     event.details == 'bar'
    *   });
    *
    *   @param type      The custom event type.
    *   @param details   The event details.
    *   @param noPending Set to true to emit this event even before the system
    *                    app is ready.
    *                    Event is always pending if the app is not loaded yet.
+   *   @param target    The element who dispatch this event.
    *
    *   @returns event?  Dispatched event, or null if the event is pending.
    */
   _sendCustomEvent: function systemApp_sendCustomEvent(type,
                                                        details,
                                                        noPending,
                                                        target) {
-    let content = this._frame ? this._frame.contentWindow : null;
+    let args = Array.prototype.slice.call(arguments);
+    return this.sendCustomEventWithId
+               .apply(this, [kMainSystemAppId].concat(args));
+  },
 
+  /*
+   * Common way to send an event to the specific system app.
+   *
+   * // In gecko code (send custom event from main system app):
+   *   SystemAppProxy.sendCustomEventWithId('main', 'foo', { data: 'bar' });
+   * // In system app:
+   *   window.addEventListener('foo', function (event) {
+   *     event.details == 'bar'
+   *   });
+   *
+   *   @param frameId   Specify the system(-remote) app who dispatch this event.
+   *   @param type      The custom event type.
+   *   @param details   The event details.
+   *   @param noPending Set to true to emit this event even before the system
+   *                    app is ready.
+   *                    Event is always pending if the app is not loaded yet.
+   *   @param target    The element who dispatch this event.
+   *
+   *   @returns event?  Dispatched event, or null if the event is pending.
+   */
+  sendCustomEventWithId: function systemApp_sendCustomEventWithId(frameId,
+                                                                  type,
+                                                                  details,
+                                                                  noPending,
+                                                                  target) {
+    let frameInfo = this._frameInfoMap.get(frameId);
+    let content = (frameInfo && frameInfo.frame) ?
+                  frameInfo.frame.contentWindow : null;
     // If the system app isn't loaded yet,
     // queue events until someone calls setIsLoaded
-    if (!content || !this._isLoaded) {
+    if (!content || !(frameInfo && frameInfo.isLoaded)) {
       if (noPending) {
-        this._pendingLoadedEvents.push([type, details]);
+        this._pendingLoadedEvents.push([frameId, type, details]);
       } else {
-        this._pendingReadyEvents.push([type, details]);
+        this._pendingReadyEvents.push([frameId, type, details]);
       }
-
       return null;
     }
 
     // If the system app isn't ready yet,
     // queue events until someone calls setIsReady
-    if (!this._isReady && !noPending) {
-      this._pendingReadyEvents.push([type, details]);
+    if (!(frameInfo && frameInfo.isReady) && !noPending) {
+      this._pendingReadyEvents.push([frameId, type, details]);
       return null;
     }
 
     let event = content.document.createEvent('CustomEvent');
 
     let payload;
     // If the root object already has __exposedProps__,
     // we consider the caller already wrapped (correctly) the object.
     if ('__exposedProps__' in details) {
       payload = details;
     } else {
       payload = details ? Cu.cloneInto(details, content) : {};
     }
 
-    if ((target || content) === this._frame.contentWindow) {
-      dump('XXX FIXME : Dispatch a ' + type + ': ' + details.type + "\n");
+    if ((target || content) === frameInfo.frame.contentWindow) {
+      dump('XXX FIXME : Dispatch a ' + type + ': ' + details.type + '\n');
     }
 
     event.initCustomEvent(type, true, false, payload);
     (target || content).dispatchEvent(event);
 
     return event;
   },
 
   // Now deprecated, use sendCustomEvent with a custom event name
   dispatchEvent: function systemApp_dispatchEvent(details, target) {
     return this._sendCustomEvent('mozChromeEvent', details, false, target);
   },
 
   dispatchKeyboardEvent: function systemApp_dispatchKeyboardEvent(type, details) {
     try {
-      let content = this._frame ? this._frame.contentWindow : null;
+      let frameInfo = this._getMainSystemAppInfo();
+      let content = (frameInfo && frameInfo.frame) ? frameInfo.frame.contentWindow
+                                                   : null;
       if (!content) {
-        throw new Error("no content window");
+        throw new Error('no content window');
       }
-
       // If we don't already have a TextInputProcessor, create one now
       if (!this.TIP) {
-        this.TIP = Cc["@mozilla.org/text-input-processor;1"]
+        this.TIP = Cc['@mozilla.org/text-input-processor;1']
           .createInstance(Ci.nsITextInputProcessor);
         if (!this.TIP) {
-          throw new Error("failed to create textInputProcessor");
+          throw new Error('failed to create textInputProcessor');
         }
       }
 
       if (!this.TIP.beginInputTransactionForTests(content)) {
         this.TIP = null;
-        throw new Error("beginInputTransaction failed");
+        throw new Error('beginInputTransaction failed');
       }
 
-      let e = new content.KeyboardEvent("", { key: details.key, });
+      let e = new content.KeyboardEvent('', { key: details.key, });
 
       if (type === 'keydown') {
         this.TIP.keydown(e);
       }
       else if (type === 'keyup') {
         this.TIP.keyup(e);
       }
       else {
-        throw new Error("unexpected event type: " + type);
+        throw new Error('unexpected event type: ' + type);
       }
     }
     catch(e) {
-      dump("dispatchKeyboardEvent: " + e + "\n");
+      dump('dispatchKeyboardEvent: ' + e + '\n');
     }
   },
 
-  // Listen for dom events on the system app
+  // Listen for dom events on the main system app
   addEventListener: function systemApp_addEventListener() {
-    let content = this._frame ? this._frame.contentWindow : null;
-    if (!content) {
+    let args = Array.prototype.slice.call(arguments);
+    this.addEventListenerWithId.apply(this, [kMainSystemAppId].concat(args));
+  },
+
+  // Listen for dom events on the specific system app
+  addEventListenerWithId: function systemApp_addEventListenerWithId(frameId,
+                                                                    ...args) {
+    let frameInfo = this._frameInfoMap.get(frameId);
+
+    if (!frameInfo) {
       this._pendingListeners.push(arguments);
       return false;
     }
 
-    content.addEventListener.apply(content, arguments);
+    let content = frameInfo.frame.contentWindow;
+    content.addEventListener.apply(content, args);
     return true;
   },
 
+  // remove the event listener from the main system app
   removeEventListener: function systemApp_removeEventListener(name, listener) {
-    let content = this._frame ? this._frame.contentWindow : null;
-    if (content) {
-      content.removeEventListener.apply(content, arguments);
-    } else {
+    this.removeEventListenerWithId.apply(this, [kMainSystemAppId, name, listener]);
+  },
+
+  // remove the event listener from the specific system app
+  removeEventListenerWithId: function systemApp_removeEventListenerWithId(frameId,
+                                                                          name,
+                                                                          listener) {
+    let frameInfo = this._frameInfoMap.get(frameId);
+
+    if (frameInfo) {
+      let content = frameInfo.frame.contentWindow;
+      content.removeEventListener.apply(content, [name, listener]);
+    }
+    else {
       this._pendingListeners = this._pendingListeners.filter(
         args => {
-          return args[0] != name || args[1] != listener;
+          return args[0] != frameId || args[1] != name || args[2] != listener;
         });
     }
   },
 
-  getFrames: function systemApp_getFrames() {
-    let systemAppFrame = this._frame;
-    if (!systemAppFrame) {
-      return [];
+  // Get all frame in system app
+  getFrames: function systemApp_getFrames(frameId) {
+    let frameList = [];
+
+    for (let frameId of this._frameInfoMap.keys()) {
+      let frameInfo = this._frameInfoMap.get(frameId);
+      let systemAppFrame = frameInfo.frame;
+      let subFrames = systemAppFrame.contentDocument.querySelectorAll('iframe');
+      frameList.push(systemAppFrame);
+      for (let i = 0; i < subFrames.length; ++i) {
+        frameList.push(subFrames[i]);
+      }
     }
-    let list = [systemAppFrame];
-    let frames = systemAppFrame.contentDocument.querySelectorAll('iframe');
-    for (let i = 0; i < frames.length; i++) {
-      list.push(frames[i]);
-    }
-    return list;
+    return frameList;
   }
 };
 this.SystemAppProxy = SystemAppProxy;
-
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -63,17 +63,16 @@ EXTRA_JS_MODULES += [
     'DebuggerActors.js',
     'ErrorPage.jsm',
     'Frames.jsm',
     'FxAccountsMgmtService.jsm',
     'KillSwitchMain.jsm',
     'LogCapture.jsm',
     'LogParser.jsm',
     'LogShake.jsm',
-    'MultiscreenHandler.jsm',
     'OrientationChangeHandler.jsm',
     'PersistentDataBlock.jsm',
     'SafeMode.jsm',
     'Screenshot.jsm',
     'SignInToWebsite.jsm',
     'SystemAppProxy.jsm',
     'TelURIParser.jsm',
     'WebappsUpdater.jsm',
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6018,18 +6018,21 @@
           let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
 
           if (event.shiftKey)
             bgLoad = !bgLoad;
 
           let tab = this._getDragTargetTab(event, true);
           if (!tab) {
             // We're adding a new tab.
+            let userContextId = this.selectedItem.getAttribute("usercontextid");
             let newIndex = this._getDropIndex(event, true);
-            let newTab = this.tabbrowser.loadOneTab(url, {inBackground: bgLoad, allowThirdPartyFixup: true});
+            let newTab = this.tabbrowser.loadOneTab(url, {inBackground: bgLoad,
+                                                          allowThirdPartyFixup: true,
+                                                          userContextId});
             this.tabbrowser.moveTabTo(newTab, newIndex);
           } else {
             // Load in an existing tab.
             try {
               let webNav = Ci.nsIWebNavigation;
               let flags = webNav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
                           webNav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
               this.tabbrowser.getBrowserForTab(tab).loadURIWithFlags(url, flags);
--- a/browser/components/contextualidentity/test/browser/browser.ini
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -1,9 +1,13 @@
 [DEFAULT]
 skip-if = buildapp == "mulet"
 support-files =
   empty_file.html
   file_reflect_cookie_into_title.html
+  serviceworker.html
+  worker.js
 
 [browser_usercontext.js]
+[browser_usercontextid_tabdrop.js]
 [browser_windowName.js]
 [browser_windowOpen.js]
+[browser_serviceworkers.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_serviceworkers.js
@@ -0,0 +1,110 @@
+let { classes: Cc, interfaces: Ci } = Components;
+
+let swm = Cc["@mozilla.org/serviceworkers/manager;1"].
+          getService(Ci.nsIServiceWorkerManager);
+
+const BASE_ORIGIN = "https://example.com";
+const URI = BASE_ORIGIN +
+  "/browser/browser/components/contextualidentity/test/browser/serviceworker.html";
+const NUM_USER_CONTEXTS = 3;
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+function openTabInUserContext(uri, userContextId) {
+  // open the tab in the correct userContextId
+  let tab = gBrowser.addTab(uri, {userContextId});
+
+  // select tab and make sure its browser is focused
+  gBrowser.selectedTab = tab;
+  tab.ownerDocument.defaultView.focus();
+
+  return tab;
+}
+
+add_task(function* setup() {
+  // make sure userContext is enabled.
+  SpecialPowers.pushPrefEnv({"set": [
+    ["privacy.userContext.enabled", true]
+  ]});
+});
+
+add_task(function* cleanup() {
+  // make sure we don't leave any prefs set for the next tests
+  registerCleanupFunction(function() {
+    SpecialPowers.popPrefEnv();
+  });
+});
+
+let infos = [];
+
+add_task(function* test() {
+  // Open the same URI in multiple user contexts, and make sure we have a
+  // separate service worker in each of the contexts
+  for (let userContextId = 0; userContextId < NUM_USER_CONTEXTS; userContextId++) {
+    // Open a tab in given user contexts
+    let tab = openTabInUserContext(URI, userContextId);
+
+    // wait for tab load
+    yield BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
+
+    // remove the tab
+    gBrowser.removeTab(tab);
+  }
+
+  if (!allRegistered()) {
+    yield promiseAllRegistered();
+  }
+  ok(true, "all service workers are registered");
+
+  // Unregistered all service workers added in this test
+  for (let info of infos) {
+    yield promiseUnregister(info);
+  }
+});
+
+function allRegistered() {
+  let results = [];
+  let registrations = swm.getAllRegistrations();
+  for (let i = 0; i < registrations.length; i++) {
+    let info = registrations.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
+    let principal = info.principal;
+    if (principal.originNoSuffix === BASE_ORIGIN) {
+      results[principal.userContextId] = true;
+      infos[principal.userContextId] = info;
+    }
+  }
+  for (let userContextId = 0; userContextId < NUM_USER_CONTEXTS; userContextId++) {
+    if (!results[userContextId]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+function promiseAllRegistered() {
+  return new Promise(function(resolve) {
+    let listener = {
+      onRegister: function() {
+        if (allRegistered()) {
+          swm.removeListener(listener);
+          resolve();
+        }
+      }
+    }
+    swm.addListener(listener);
+  });
+}
+
+function promiseUnregister(info) {
+  return new Promise(function(resolve) {
+    swm.unregister(info.principal, {
+      unregisterSucceeded: function(aState) {
+        ok(aState, "ServiceWorkerRegistration exists");
+        resolve();
+      },
+      unregisterFailed: function(aState) {
+        ok(false, "unregister should succeed");
+      }
+    }, info.scope);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js
@@ -0,0 +1,134 @@
+"use strict";
+
+let ChromeUtils = {};
+Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
+
+/**
+ * Dragging an URL to a tab without userContextId set.
+ */
+add_task(function* () {
+  let tab = gBrowser.addTab("http://example.com/");
+  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+  let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+  let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://test1.example.com/");
+
+  // A drop type of "link" onto an existing tab would normally trigger a
+  // load in that same tab, but tabbrowser code in _getDragTargetTab treats
+  // drops on the outer edges of a tab differently (loading a new tab
+  // instead). Make events created by synthesizeDrop have all of their
+  // coordinates set to 0 (screenX/screenY), so they're treated as drops
+  // on the outer edge of the tab, thus they open new tabs.
+  let event = {
+    clientX: 0,
+    clientY: 0,
+    screenX: 0,
+    screenY: 0,
+  };
+  ChromeUtils.synthesizeDrop(tab, tab, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window, undefined, event);
+
+  yield awaitDrop;
+
+  let tab2 = yield newTabPromise;
+  Assert.ok(!tab2.hasAttribute("usercontextid"), "Tab shouldn't have usercontextid attribute");
+
+  yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+  yield ContentTask.spawn(tab2.linkedBrowser, {}, function* () {
+    Assert.equal(content.document.documentURI, "http://test1.example.com/");
+    Assert.equal(content.document.nodePrincipal.originAttributes.userContextId, 0);
+
+    // referrer is empty when urls are dragged to new or existing tabs.
+    // If this changes in the future, it would be okay to send the referrer
+    // in this case because we are creating a new tab with the default
+    // usercontextid as the original tab.
+    Assert.equal(content.document.referrer, "", "referrer should be empty");
+  });
+
+  yield BrowserTestUtils.removeTab(tab);
+  yield BrowserTestUtils.removeTab(tab2);
+});
+
+/**
+ * When dragging an URL to a new tab, the new tab should have the same
+ * userContextId as the original tab.
+ */
+add_task(function* () {
+  let tab = gBrowser.addTab("http://example.com/", {userContextId: 1});
+  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+  let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+  let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://test1.example.com/");
+
+  // A drop type of "link" onto an existing tab would normally trigger a
+  // load in that same tab, but tabbrowser code in _getDragTargetTab treats
+  // drops on the outer edges of a tab differently (loading a new tab
+  // instead). Make events created by synthesizeDrop have all of their
+  // coordinates set to 0 (screenX/screenY), so they're treated as drops
+  // on the outer edge of the tab, thus they open new tabs.
+  let event = {
+    clientX: 0,
+    clientY: 0,
+    screenX: 0,
+    screenY: 0,
+  };
+  ChromeUtils.synthesizeDrop(tab, tab, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window, undefined, event);
+
+  yield awaitDrop;
+
+  let tab2 = yield newTabPromise;
+  Assert.equal(tab2.getAttribute("usercontextid"), 1);
+
+  yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+  yield ContentTask.spawn(tab2.linkedBrowser, {}, function* () {
+    Assert.equal(content.document.documentURI, "http://test1.example.com/");
+    Assert.equal(content.document.nodePrincipal.originAttributes.userContextId, 1);
+
+    // referrer is empty when urls are dragged to new or existing tabs.
+    // If this changes in the future, it would be okay to send the referrer
+    // in this case because we are creating a new tab with the same
+    // usercontextid as the original tab.
+    Assert.equal(content.document.referrer, "", "referrer should be empty");
+  });
+
+  yield BrowserTestUtils.removeTab(tab);
+  yield BrowserTestUtils.removeTab(tab2);
+});
+
+/**
+ * When dragging a URL from one tab or link on a tab to an existing tab, the
+ * existing tab should not change its userContextId.
+ * Ex: if you drag a link from tab 1 with userContext 1 to tab 2 with
+ * userContext 2, the link will open in tab 2 with userContext 2.
+ */
+add_task(function* () {
+  let tab = gBrowser.addTab("http://example.com/", {userContextId: 1});
+  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+  let tab2 = gBrowser.addTab("http://example.org/", {userContextId: 2});
+  yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+  let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+
+  ChromeUtils.synthesizeDrop(tab, tab2, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window);
+
+  yield awaitDrop;
+  Assert.equal(tab2.getAttribute("usercontextid"), 2);
+
+  yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+  yield ContentTask.spawn(tab2.linkedBrowser, {}, function* () {
+    Assert.equal(content.document.documentURI, "http://test1.example.com/");
+    Assert.equal(content.document.nodePrincipal.originAttributes.userContextId, 2);
+
+    // referrer is empty when urls are dragged to new or existing tabs.
+    // If this changes in the future, we should ensure that we are not sending
+    // a referrer for this case!  When opening links across user contexts, we
+    // don't want the referrer to follow the user from one context to another.
+    Assert.equal(content.document.referrer, "", "referrer should be empty");
+  });
+
+  yield BrowserTestUtils.removeTab(tab);
+  yield BrowserTestUtils.removeTab(tab2);
+});
copy from dom/workers/test/serviceworkers/serviceworker.html
copy to browser/components/contextualidentity/test/browser/serviceworker.html
copy from dom/workers/test/serviceworkers/worker.js
copy to browser/components/contextualidentity/test/browser/worker.js
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -882,22 +882,26 @@ FeedWriter.prototype = {
 
   /**
    * Returns the original URI object of the feed and ensures that this
    * component is only ever invoked from the preview document.
    * @param aWindow
    *        The window of the document invoking the BrowserFeedWriter
    */
   _getOriginalURI(aWindow) {
-    let chan = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-               getInterface(Ci.nsIWebNavigation).
-               QueryInterface(Ci.nsIDocShell).currentDocumentChannel;
+    let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIWebNavigation)
+                          .QueryInterface(Ci.nsIDocShell);
+    let chan = docShell.currentDocumentChannel;
 
-    let nullPrincipal = Cc["@mozilla.org/nullprincipal;1"].
-                        createInstance(Ci.nsIPrincipal);
+    // We probably need to call InheritFromDocShellToDoc for this, but right now
+    // we can't call it from JS.
+    let attrs = docShell.getOriginAttributes();
+    let ssm = Services.scriptSecurityManager;
+    let nullPrincipal = ssm.createNullPrincipal(attrs);
 
     let resolvedURI = Cc["@mozilla.org/network/io-service;1"].
                       getService(Ci.nsIIOService).
                       newChannel2("about:feeds",
                                   null,
                                   null,
                                   null, // aLoadingNode
                                   nullPrincipal,
@@ -1180,23 +1184,29 @@ FeedWriter.prototype = {
   function FW__setFaviconForWebReader(aReaderUrl, aMenuItem) {
     let readerURI = makeURI(aReaderUrl);
     if (!/^https?$/.test(readerURI.scheme)) {
       // Don't try to get a favicon for non http(s) URIs.
       return;
     }
     let faviconURI = makeURI(readerURI.prePath + "/favicon.ico");
     let self = this;
-    let usePrivateBrowsing = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                         .getInterface(Ci.nsIWebNavigation)
-                                         .QueryInterface(Ci.nsIDocShell)
-                                         .QueryInterface(Ci.nsILoadContext)
-                                         .usePrivateBrowsing;
-    let nullPrincipal = Cc["@mozilla.org/nullprincipal;1"]
-                          .createInstance(Ci.nsIPrincipal);
+
+    let docShell = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
+                               .getInterface(Ci.nsIWebNavigation)
+                               .QueryInterface(Ci.nsIDocShell);
+    let usePrivateBrowsing = docShell.QueryInterface(Ci.nsILoadContext)
+                                     .usePrivateBrowsing;
+
+    // We probably need to call InheritFromDocShellToDoc for this, but right now
+    // we can't call it from JS.
+    let attrs = docShell.getOriginAttributes();
+    let ssm = Services.scriptSecurityManager;
+    let nullPrincipal = ssm.createNullPrincipal(attrs);
+
     this._faviconService.setAndFetchFaviconForPage(readerURI, faviconURI, false,
       usePrivateBrowsing ? this._faviconService.FAVICON_LOAD_PRIVATE
                          : this._faviconService.FAVICON_LOAD_NON_PRIVATE,
       function (aURI, aDataLen, aData, aMimeType) {
         if (aDataLen > 0) {
           let dataURL = "data:" + aMimeType + ";base64," +
                         btoa(String.fromCharCode.apply(null, aData));
           aMenuItem.setAttribute('image', dataURL);
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -56,13 +56,10 @@ MOZ_APP_STATIC_INI=1
 MOZ_WEBGL_CONFORMANT=1
 # Enable navigator.mozPay
 MOZ_PAY=1
 # Enable activities. These are used for FxOS developers currently.
 MOZ_ACTIVITIES=1
 MOZ_JSDOWNLOADS=1
 MOZ_RUST_MP4PARSE=1
 
-# Enable checking that add-ons are signed by the trusted root
-MOZ_ADDON_SIGNING=1
-
 # Include the DevTools client, not just the server (which is the default)
 MOZ_DEVTOOLS=all
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -1715,23 +1715,24 @@ Experiments.ExperimentEntry.prototype = 
 
   /*
    * Run the jsfilter function from the manifest in a sandbox and return the
    * result (forced to boolean).
    */
   _runFilterFunction: Task.async(function* (jsfilter) {
     this._log.trace("runFilterFunction() - filter: " + jsfilter);
 
-    const nullprincipal = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal);
+    let ssm = Services.scriptSecurityManager;
+    const nullPrincipal = ssm.createNullPrincipal({});
     let options = {
       sandboxName: "telemetry experiments jsfilter sandbox",
       wantComponents: false,
     };
 
-    let sandbox = Cu.Sandbox(nullprincipal, options);
+    let sandbox = Cu.Sandbox(nullPrincipal, options);
     try {
       Cu.evalInSandbox(jsfilter, sandbox);
     } catch (e) {
       this._log.error("runFilterFunction() - failed to eval jsfilter: " + e.message);
       throw ["jsfilter-evalfailed"];
     }
 
     let currentEnvironment = yield TelemetryEnvironment.onInitialized();
--- a/browser/modules/Feeds.jsm
+++ b/browser/modules/Feeds.jsm
@@ -15,16 +15,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/RecentWindow.jsm");
 
 const { interfaces: Ci, classes: Cc } = Components;
 
 this.Feeds = {
   init() {
     let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
     mm.addMessageListener("WCCR:registerProtocolHandler", this);
+    mm.addMessageListener("WCCR:registerContentHandler", this);
 
     Services.ppmm.addMessageListener("WCCR:setAutoHandler", this);
     Services.ppmm.addMessageListener("FeedConverter:addLiveBookmark", this);
   },
 
   receiveMessage(aMessage) {
     let data = aMessage.data;
     switch (aMessage.name) {
--- a/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css
+++ b/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css
@@ -47,17 +47,17 @@ a:hover:active {
   color: var(--color-blue-darker);
 }
 
 a:visited {
   color: var(--color-blue-darker);
 }
 
 .about-content-container {
-  width: 780px;
+  max-width: 780px;
 }
 
 section.section-main {
   margin-bottom: 48px;
   -moz-margin-start: var(--icon-margin);
   -moz-padding-start: 24px;
 }
 
--- a/build/mozconfig.common
+++ b/build/mozconfig.common
@@ -11,11 +11,16 @@
 # of this file.
 
 mk_add_options AUTOCLOBBER=1
 
 ac_add_options --enable-crashreporter
 
 ac_add_options --enable-release
 
+# Enable checking that add-ons are signed by the trusted root
+MOZ_ADDON_SIGNING=${MOZ_ADDON_SIGNING-1}
+# Disable enforcing that add-ons are signed by the trusted root
+MOZ_REQUIRE_SIGNING=${MOZ_REQUIRE_SIGNING-0}
+
 ac_add_options --enable-js-shell
 
 . "$topsrcdir/build/mozconfig.automation"
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -548,26 +548,26 @@ BasePrincipal::CreateCodebasePrincipal(n
 {
   // If the URI is supposed to inherit the security context of whoever loads it,
   // we shouldn't make a codebase principal for it.
   bool inheritsPrincipal;
   nsresult rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
                                     &inheritsPrincipal);
   nsCOMPtr<nsIPrincipal> principal;
   if (NS_FAILED(rv) || inheritsPrincipal) {
-    return nsNullPrincipal::Create();
+    return nsNullPrincipal::Create(aAttrs);
   }
 
   // Check whether the URI knows what its principal is supposed to be.
   nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
   if (uriPrinc) {
     nsCOMPtr<nsIPrincipal> principal;
     uriPrinc->GetPrincipal(getter_AddRefs(principal));
     if (!principal) {
-      return nsNullPrincipal::Create();
+      return nsNullPrincipal::Create(aAttrs);
     }
     RefPtr<BasePrincipal> concrete = Cast(principal);
     return concrete.forget();
   }
 
   // Mint a codebase principal.
   RefPtr<nsPrincipal> codebase = new nsPrincipal();
   rv = codebase->Init(aURI, aAttrs);
--- a/caps/nsJSPrincipals.cpp
+++ b/caps/nsJSPrincipals.cpp
@@ -119,24 +119,55 @@ nsJSPrincipals::ReadPrincipals(JSContext
         xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
         return false;
     }
 
     return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
 }
 
 static bool
+ReadSuffixAndSpec(JSStructuredCloneReader* aReader,
+                  PrincipalOriginAttributes& aAttrs,
+                  nsACString& aSpec)
+{
+    uint32_t suffixLength, specLength;
+    if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
+        return false;
+    }
+
+    nsAutoCString suffix;
+    suffix.SetLength(suffixLength);
+    if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
+        return false;
+    }
+
+    aAttrs.PopulateFromSuffix(suffix);
+
+    aSpec.SetLength(specLength);
+    if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool
 ReadPrincipalInfo(JSStructuredCloneReader* aReader,
                   uint32_t aTag,
                   PrincipalInfo& aInfo)
 {
     if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
         aInfo = SystemPrincipalInfo();
     } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
-        aInfo = NullPrincipalInfo();
+        PrincipalOriginAttributes attrs;
+        nsAutoCString dummy;
+        if (!ReadSuffixAndSpec(aReader, attrs, dummy)) {
+            return false;
+        }
+        aInfo = NullPrincipalInfo(attrs);
     } else if (aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
         uint32_t length, unused;
         if (!JS_ReadUint32Pair(aReader, &length, &unused)) {
             return false;
         }
 
         ExpandedPrincipalInfo expanded;
 
@@ -150,35 +181,22 @@ ReadPrincipalInfo(JSStructuredCloneReade
             if (!ReadPrincipalInfo(aReader, tag, sub)) {
                 return false;
             }
             expanded.whitelist().AppendElement(sub);
         }
 
         aInfo = expanded;
     } else if (aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
-        uint32_t suffixLength, specLength;
-        if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
+        PrincipalOriginAttributes attrs;
+        nsAutoCString spec;
+        if (!ReadSuffixAndSpec(aReader, attrs, spec)) {
             return false;
         }
 
-        nsAutoCString suffix;
-        suffix.SetLength(suffixLength);
-        if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
-            return false;
-        }
-
-        nsAutoCString spec;
-        spec.SetLength(specLength);
-        if (!JS_ReadBytes(aReader, spec.BeginWriting(), specLength)) {
-            return false;
-        }
-
-        PrincipalOriginAttributes attrs;
-        attrs.PopulateFromSuffix(suffix);
         aInfo = ContentPrincipalInfo(attrs, spec);
     } else {
         MOZ_CRASH("unexpected principal structured clone tag");
     }
 
     return true;
 }
 
@@ -210,20 +228,35 @@ nsJSPrincipals::ReadKnownPrincipalType(J
         return false;
     }
 
     *aOutPrincipals = get(prin.forget().take());
     return true;
 }
 
 static bool
+WriteSuffixAndSpec(JSStructuredCloneWriter* aWriter,
+                   const PrincipalOriginAttributes& aAttrs,
+                   const nsCString& aSpec)
+{
+  nsAutoCString suffix;
+  aAttrs.CreateSuffix(suffix);
+
+  return JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) &&
+         JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
+         JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length());
+}
+
+static bool
 WritePrincipalInfo(JSStructuredCloneWriter* aWriter, const PrincipalInfo& aInfo)
 {
     if (aInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
-        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0);
+        const NullPrincipalInfo& nullInfo = aInfo;
+        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0) &&
+               WriteSuffixAndSpec(aWriter, nullInfo.attrs(), EmptyCString());
     }
     if (aInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
         return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
     }
     if (aInfo.type() == PrincipalInfo::TExpandedPrincipalInfo) {
         const ExpandedPrincipalInfo& expanded = aInfo;
         if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_EXPANDED_PRINCIPAL, 0) ||
             !JS_WriteUint32Pair(aWriter, expanded.whitelist().Length(), 0)) {
@@ -235,22 +268,18 @@ WritePrincipalInfo(JSStructuredCloneWrit
                 return false;
             }
         }
         return true;
     }
 
     MOZ_ASSERT(aInfo.type() == PrincipalInfo::TContentPrincipalInfo);
     const ContentPrincipalInfo& cInfo = aInfo;
-    nsAutoCString suffix;
-    cInfo.attrs().CreateSuffix(suffix);
     return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
-           JS_WriteUint32Pair(aWriter, suffix.Length(), cInfo.spec().Length()) &&
-           JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
-           JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length());
+           WriteSuffixAndSpec(aWriter, cInfo.attrs(), cInfo.spec());
 }
 
 bool
 nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter)
 {
     PrincipalInfo info;
     if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
         xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
--- a/caps/nsNullPrincipal.cpp
+++ b/caps/nsNullPrincipal.cpp
@@ -7,16 +7,17 @@
 /**
  * This is the principal that has no rights and can't be accessed by
  * anything other than itself and chrome; null principals are not
  * same-origin with anything but themselves.
  */
 
 #include "mozilla/ArrayUtils.h"
 
+#include "nsDocShell.h"
 #include "nsNullPrincipal.h"
 #include "nsNullPrincipalURI.h"
 #include "nsMemory.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsIClassInfoImpl.h"
 #include "nsNetCID.h"
 #include "nsError.h"
 #include "nsIScriptSecurityManager.h"
@@ -35,25 +36,38 @@ NS_IMPL_CI_INTERFACE_GETTER(nsNullPrinci
                             nsIPrincipal,
                             nsISerializable)
 
 /* static */ already_AddRefed<nsNullPrincipal>
 nsNullPrincipal::CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom)
 {
   RefPtr<nsNullPrincipal> nullPrin = new nsNullPrincipal();
   nsresult rv = nullPrin->Init(Cast(aInheritFrom)->OriginAttributesRef());
-  return NS_SUCCEEDED(rv) ? nullPrin.forget() : nullptr;
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+  return nullPrin.forget();
+}
+
+/* static */ already_AddRefed<nsNullPrincipal>
+nsNullPrincipal::CreateWithInheritedAttributes(nsIDocShell* aDocShell)
+{
+  PrincipalOriginAttributes attrs;
+  attrs.InheritFromDocShellToDoc(nsDocShell::Cast(aDocShell)->GetOriginAttributes(), nullptr);
+
+  RefPtr<nsNullPrincipal> nullPrin = new nsNullPrincipal();
+  nsresult rv = nullPrin->Init(attrs);
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+  return nullPrin.forget();
 }
 
 /* static */ already_AddRefed<nsNullPrincipal>
 nsNullPrincipal::Create(const PrincipalOriginAttributes& aOriginAttributes)
 {
   RefPtr<nsNullPrincipal> nullPrin = new nsNullPrincipal();
   nsresult rv = nullPrin->Init(aOriginAttributes);
-  NS_ENSURE_SUCCESS(rv, nullptr);
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
 
   return nullPrin.forget();
 }
 
 nsresult
 nsNullPrincipal::Init(const PrincipalOriginAttributes& aOriginAttributes)
 {
   mOriginAttributes = aOriginAttributes;
--- a/caps/nsNullPrincipal.h
+++ b/caps/nsNullPrincipal.h
@@ -15,16 +15,17 @@
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCOMPtr.h"
 #include "nsIContentSecurityPolicy.h"
 
 #include "mozilla/BasePrincipal.h"
 
+class nsIDocShell;
 class nsIURI;
 
 #define NS_NULLPRINCIPAL_CID \
 { 0xbd066e5f, 0x146f, 0x4472, \
   { 0x83, 0x31, 0x7b, 0xfd, 0x05, 0xb1, 0xed, 0x90 } }
 #define NS_NULLPRINCIPAL_CONTRACTID "@mozilla.org/nullprincipal;1"
 
 #define NS_NULLPRINCIPAL_SCHEME "moz-nullprincipal"
@@ -42,20 +43,20 @@ public:
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
   nsresult GetOriginInternal(nsACString& aOrigin) override;
 
-  // Returns null on failure.
-  static already_AddRefed<nsNullPrincipal> CreateWithInheritedAttributes(nsIPrincipal *aInheritFrom);
+  static already_AddRefed<nsNullPrincipal> CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom);
 
-  // Returns null on failure.
+  static already_AddRefed<nsNullPrincipal> CreateWithInheritedAttributes(nsIDocShell* aDocShell);
+
   static already_AddRefed<nsNullPrincipal>
   Create(const mozilla::PrincipalOriginAttributes& aOriginAttributes = mozilla::PrincipalOriginAttributes());
 
   nsresult Init(const mozilla::PrincipalOriginAttributes& aOriginAttributes = mozilla::PrincipalOriginAttributes());
 
   virtual void GetScriptLocation(nsACString &aStr) override;
 
   PrincipalKind Kind() override { return eNullPrincipal; }
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -343,24 +343,22 @@ nsScriptSecurityManager::GetChannelResul
     nsCOMPtr<nsILoadInfo> loadInfo;
     aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
     if (loadInfo) {
         if (loadInfo->GetLoadingSandboxed()) {
             RefPtr<nsNullPrincipal> prin;
             if (loadInfo->LoadingPrincipal()) {
               prin =
                 nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal());
-              NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
             } else {
               NeckoOriginAttributes nAttrs;
               loadInfo->GetOriginAttributes(&nAttrs);
               PrincipalOriginAttributes pAttrs;
               pAttrs.InheritFromNecko(nAttrs);
               prin = nsNullPrincipal::Create(pAttrs);
-              NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
             }
             prin.forget(aPrincipal);
             return NS_OK;
         }
 
         if (loadInfo->GetForceInheritPrincipal()) {
             NS_ADDREF(*aPrincipal = loadInfo->TriggeringPrincipal());
             return NS_OK;
@@ -1169,17 +1167,16 @@ NS_IMETHODIMP
 nsScriptSecurityManager::CreateNullPrincipal(JS::Handle<JS::Value> aOriginAttributes,
                                              JSContext* aCx, nsIPrincipal** aPrincipal)
 {
   PrincipalOriginAttributes attrs;
   if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
       return NS_ERROR_INVALID_ARG;
   }
   nsCOMPtr<nsIPrincipal> prin = nsNullPrincipal::Create(attrs);
-  NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
   prin.forget(aPrincipal);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CreateExpandedPrincipal(nsIPrincipal** aPrincipalArray, uint32_t aLength,
                                                  nsIPrincipal** aResult)
 {
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -115,18 +115,22 @@ function createDummyDocument() {
     nodeName: "iframe",
     namespaceURI: "http://www.w3.org/1999/xhtml",
     allowJavascript: false,
     allowPlugins: false,
     allowAuth: false
   });
   let docShell = getDocShell(frame);
   let eventTarget = docShell.chromeEventHandler;
-  docShell.createAboutBlankContentViewer(Cc["@mozilla.org/nullprincipal;1"]
-                                         .createInstance(Ci.nsIPrincipal));
+  let ssm = Services.scriptSecurityManager;
+
+  // We probably need to call InheritFromDocShellToDoc to get the correct origin
+  // attributes, but right now we can't call it from JS.
+  let nullPrincipal = ssm.createNullPrincipal(docShell.getOriginAttributes());
+  docShell.createAboutBlankContentViewer(nullPrincipal);
   let window = docShell.contentViewer.DOMDocument.defaultView;
   window.location = "data:text/html,<html></html>";
   let deferred = promise.defer();
   eventTarget.addEventListener("DOMContentLoaded", function handler() {
     eventTarget.removeEventListener("DOMContentLoaded", handler, false);
     deferred.resolve(window.document);
     frame.remove();
   }, false);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1470,42 +1470,47 @@ nsDocShell::LoadURI(nsIURI* aURI,
   //     from the current document. If none of these things are true, then
   // (4) we pass a null owner into the channel, and an owner will be
   //     created later from the channel's internal data.
   //
   // NOTE: This all only works because the only thing the owner is used
   //       for in InternalLoad is data:, javascript:, and about:blank
   //       URIs.  For other URIs this would all be dead wrong!
 
+  nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
   if (owner && mItemType != typeChrome) {
-    nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
     if (nsContentUtils::IsSystemPrincipal(ownerPrincipal)) {
       if (ownerIsExplicit) {
         return NS_ERROR_DOM_SECURITY_ERR;
       }
       owner = nullptr;
       inheritOwner = true;
     } else if (nsContentUtils::IsExpandedPrincipal(ownerPrincipal)) {
       if (ownerIsExplicit) {
         return NS_ERROR_DOM_SECURITY_ERR;
       }
       // Don't inherit from the current page.  Just do the safe thing
       // and pretend that we were loaded by a nullprincipal.
-      owner = nsNullPrincipal::Create();
+      //
+      // We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't
+      // have origin attributes.
+      owner = nsNullPrincipal::CreateWithInheritedAttributes(this);
       inheritOwner = false;
     }
   }
   if (!owner && !inheritOwner && !ownerIsExplicit) {
     // See if there's system or chrome JS code running
     inheritOwner = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
   }
 
   if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_OWNER) {
     inheritOwner = false;
-    owner = nsNullPrincipal::Create();
+    owner = ownerPrincipal ?
+              nsNullPrincipal::CreateWithInheritedAttributes(ownerPrincipal) :
+              nsNullPrincipal::CreateWithInheritedAttributes(this);
   }
 
   uint32_t flags = 0;
 
   if (inheritOwner) {
     flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
   }
 
@@ -7943,17 +7948,16 @@ nsDocShell::CreateAboutBlankContentViewe
 
   nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
     nsContentUtils::FindInternalContentViewer(NS_LITERAL_CSTRING("text/html"));
 
   if (docFactory) {
     nsCOMPtr<nsIPrincipal> principal;
     if (mSandboxFlags & SANDBOXED_ORIGIN) {
       principal = nsNullPrincipal::CreateWithInheritedAttributes(aPrincipal);
-      NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
     } else {
       principal = aPrincipal;
     }
     // generate (about:blank) document to load
     docFactory->CreateBlankDocument(mLoadGroup, principal,
                                     getter_AddRefs(blankDoc));
     if (blankDoc) {
       // Hack: set the base URI manually, since this document never
@@ -12065,26 +12069,24 @@ nsDocShell::AddToSessionHistory(nsIURI* 
       nsCOMPtr<nsILoadInfo> loadInfo;
       aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
       if (loadInfo) {
         // For now keep storing just the principal in the SHEntry.
         if (loadInfo->GetLoadingSandboxed()) {
           if (loadInfo->LoadingPrincipal()) {
             owner = nsNullPrincipal::CreateWithInheritedAttributes(
               loadInfo->LoadingPrincipal());
-            NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
           } else {
             // get the OriginAttributes
             NeckoOriginAttributes nAttrs;
             loadInfo->GetOriginAttributes(&nAttrs);
             PrincipalOriginAttributes pAttrs;
             pAttrs.InheritFromNecko(nAttrs);
 
             owner = nsNullPrincipal::Create(pAttrs);
-            NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
           }
         } else if (loadInfo->GetForceInheritPrincipal()) {
           owner = loadInfo->TriggeringPrincipal();
         }
       }
     }
   }
 
@@ -12247,18 +12249,17 @@ nsDocShell::LoadHistoryEntry(nsISHEntry*
       // user prevented it). Interrupt the history load.
       return NS_OK;
     }
 
     if (!owner) {
       // Ensure that we have an owner.  Otherwise javascript: URIs will
       // pick it up from the about:blank page we just loaded, and we
       // don't really want even that in this case.
-      owner = nsNullPrincipal::Create();
-      NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
+      owner = nsNullPrincipal::CreateWithInheritedAttributes(this);
     }
   }
 
   /* If there is a valid postdata *and* the user pressed
    * reload or shift-reload, take user's permission before we
    * repost the data to the server.
    */
   if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
@@ -13929,18 +13930,17 @@ nsDocShell::StopDocumentLoad(void)
 NS_IMETHODIMP
 nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
 {
   *aPrintPreview = nullptr;
 #if NS_PRINT_PREVIEW
   nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
   if (!print || !print->IsInitializedForPrintPreview()) {
     Stop(nsIWebNavigation::STOP_ALL);
-    nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
-    NS_ENSURE_STATE(principal);
+    nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::CreateWithInheritedAttributes(this);
     nsresult rv = CreateAboutBlankContentViewer(principal, nullptr);
     NS_ENSURE_SUCCESS(rv, rv);
     print = do_QueryInterface(mContentViewer);
     NS_ENSURE_STATE(print);
     print->InitializeForPrintPreview();
   }
   nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
   result.forget(aPrintPreview);
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -194,17 +194,17 @@ Animation::SetStartTime(const Nullable<T
 
   CancelPendingTasks();
   if (mReady) {
     // We may have already resolved mReady, but in that case calling
     // MaybeResolve is a no-op, so that's okay.
     mReady->MaybeResolve(this);
   }
 
-  UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
+  UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async);
   if (IsRelevant()) {
     nsNodeUtils::AnimationChanged(this);
   }
   PostUpdate();
 }
 
 // https://w3c.github.io/web-animations/#current-time
 Nullable<TimeDuration>
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_dom_KeyframeEffect_h
 #define mozilla_dom_KeyframeEffect_h
 
 #include "nsAutoPtr.h"
 #include "nsCSSProperty.h"
 #include "nsCSSValue.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsIDocument.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "mozilla/AnimationPerformanceWarning.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ComputedTiming.h"
 #include "mozilla/ComputedTimingFunction.h"
 #include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords
 #include "mozilla/Maybe.h"
--- a/dom/animation/test/chrome/test_restyles.html
+++ b/dom/animation/test/chrome/test_restyles.html
@@ -364,12 +364,85 @@ waitForAllPaints(function() {
     var markers = yield observeStyling(5);
     is(markers.length, 1,
        'Animations running on the compositor should update style ' +
        'when timing.duration is made longer than the current time');
 
     yield ensureElementRemoval(div);
   });
 
+  add_task(function* script_animation_on_display_none_element() {
+    var div = addDiv(null);
+    var animation = div.animate({ backgroundColor: [ 'red', 'blue' ] },
+                                100 * MS_PER_SEC);
+
+    yield animation.ready;
+
+    div.style.display = 'none';
+
+    // We need to wait a frame to apply display:none style.
+    yield waitForFrame();
+
+    is(animation.playState, 'running',
+       'Script animations keep running even when the target element has ' +
+       '"display: none" style');
+
+    ok(!animation.isRunningOnCompositor,
+       'Script animations on "display:none" element should not run on the ' +
+       'compositor');
+
+    var markers = yield observeStyling(5);
+    is(markers.length, 0,
+       'Script animations on "display: none" element should not update styles');
+
+    div.style.display = '';
+
+    // We need to wait a frame to unapply display:none style.
+    yield waitForFrame();
+
+    var markers = yield observeStyling(5);
+    is(markers.length, 5,
+       'Script animations restored from "display: none" state should update ' +
+       'styles');
+
+    yield ensureElementRemoval(div);
+  });
+
+  add_task_if_omta_enabled(function* compositable_script_animation_on_display_none_element() {
+    var div = addDiv(null);
+    var animation = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
+
+    yield animation.ready;
+
+    div.style.display = 'none';
+
+    // We need to wait a frame to apply display:none style.
+    yield waitForFrame();
+
+    is(animation.playState, 'running',
+       'Opacity script animations keep running even when the target element ' +
+       'has "display: none" style');
+
+    todo(!animation.isRunningOnCompositor,
+         'Opacity script animations on "display:none" element should not ' +
+         'run on the compositor');
+
+    var markers = yield observeStyling(5);
+    is(markers.length, 0,
+       'Opacity script animations on "display: none" element should not ' +
+       'update styles');
+
+    div.style.display = '';
+
+    // We need to wait a frame to unapply display:none style.
+    yield waitForFrame();
+
+    ok(animation.isRunningOnCompositor,
+       'Opacity script animations restored from "display: none" should be ' +
+       'run on the compositor');
+
+    yield ensureElementRemoval(div);
+  });
+
 });
 
 </script>
 </body>
--- a/dom/base/Attr.cpp
+++ b/dom/base/Attr.cpp
@@ -213,25 +213,23 @@ Attr::GetSpecified(bool* aSpecified)
   NS_ENSURE_ARG_POINTER(aSpecified);
   *aSpecified = Specified();
   return NS_OK;
 }
 
 Element*
 Attr::GetOwnerElement(ErrorResult& aRv)
 {
-  OwnerDoc()->WarnOnceAbout(nsIDocument::eOwnerElement);
   return GetElement();
 }
 
 NS_IMETHODIMP
 Attr::GetOwnerElement(nsIDOMElement** aOwnerElement)
 {
   NS_ENSURE_ARG_POINTER(aOwnerElement);
-  OwnerDoc()->WarnOnceAbout(nsIDocument::eOwnerElement);
 
   Element* element = GetElement();
   if (element) {
     return CallQueryInterface(element, aOwnerElement);
   }
 
   *aOwnerElement = nullptr;
 
--- a/dom/base/DOMParser.cpp
+++ b/dom/base/DOMParser.cpp
@@ -347,17 +347,16 @@ DOMParser::Init(nsIPrincipal* principal,
     NS_ENSURE_SUCCESS(rv, rv);
     mOriginalPrincipal = mPrincipal;
   } else {
     mOriginalPrincipal = mPrincipal;
     if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
       // Don't give DOMParsers the system principal.  Use a null
       // principal instead.
       mPrincipal = nsNullPrincipal::Create();
-      NS_ENSURE_TRUE(mPrincipal, NS_ERROR_FAILURE);
 
       if (!mDocumentURI) {
         rv = mPrincipal->GetURI(getter_AddRefs(mDocumentURI));
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
   
@@ -463,18 +462,16 @@ DOMParser::SetUpDocument(DocumentFlavor 
   nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
     do_QueryReferent(mScriptHandlingObject);
   nsresult rv;
   if (!mPrincipal) {
     NS_ENSURE_TRUE(!mAttemptedInit, NS_ERROR_NOT_INITIALIZED);
     AttemptedInitMarker marker(&mAttemptedInit);
 
     nsCOMPtr<nsIPrincipal> prin = nsNullPrincipal::Create();
-    NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
-
     rv = Init(prin, nullptr, nullptr, scriptHandlingObject);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_ASSERTION(mPrincipal, "Must have principal by now");
   NS_ASSERTION(mDocumentURI, "Must have document URI by now");
 
   // Here we have to cheat a little bit...  Setting the base URI won't
--- a/dom/base/URLSearchParams.cpp
+++ b/dom/base/URLSearchParams.cpp
@@ -332,17 +332,24 @@ URLSearchParams::WrapObject(JSContext* a
 
 /* static */ already_AddRefed<URLSearchParams>
 URLSearchParams::Constructor(const GlobalObject& aGlobal,
                              const nsAString& aInit,
                              ErrorResult& aRv)
 {
   RefPtr<URLSearchParams> sp =
     new URLSearchParams(aGlobal.GetAsSupports(), nullptr);
-  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit));
+
+  NS_ConvertUTF16toUTF8 input(aInit);
+
+  if (StringBeginsWith(input, NS_LITERAL_CSTRING("?"))) {
+    sp->ParseInput(Substring(input, 1, input.Length() - 1));
+  } else {
+    sp->ParseInput(input);
+  }
 
   return sp.forget();
 }
 
 /* static */ already_AddRefed<URLSearchParams>
 URLSearchParams::Constructor(const GlobalObject& aGlobal,
                              URLSearchParams& aInit,
                              ErrorResult& aRv)
--- a/dom/base/WindowNamedPropertiesHandler.h
+++ b/dom/base/WindowNamedPropertiesHandler.h
@@ -31,16 +31,21 @@ public:
                  JS::Handle<JS::PropertyDescriptor> aDesc,
                  JS::ObjectOpResult &result) const override;
   virtual bool
   ownPropNames(JSContext* aCx, JS::Handle<JSObject*> aProxy, unsigned flags,
                JS::AutoIdVector& aProps) const override;
   virtual bool
   delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
           JS::ObjectOpResult &aResult) const override;
+
+  // No need for getPrototypeIfOrdinary here: this object shouldn't have a
+  // lazy prototype, so this trap would never be called (and the inherited
+  // version, from BaseProxyHandler, just crashes).
+
   virtual bool
   preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy,
                     JS::ObjectOpResult& aResult) const override
   {
     return aResult.failCantPreventExtensions();
   }
   virtual bool
   isExtensible(JSContext* aCx, JS::Handle<JSObject*> aProxy,
--- a/dom/base/contentAreaDropListener.js
+++ b/dom/base/contentAreaDropListener.js
@@ -78,17 +78,17 @@ ContentAreaDropListener.prototype =
                    getService(Ci.nsIScriptSecurityManager);
     let sourceNode = dataTransfer.mozSourceNode;
     let flags = secMan.STANDARD;
     if (disallowInherit)
       flags |= secMan.DISALLOW_INHERIT_PRINCIPAL;
 
     // Use file:/// as the default uri so that drops of file URIs are always allowed
     let principal = sourceNode ? sourceNode.nodePrincipal
-                               : secMan.getSimpleCodebasePrincipal(ioService.newURI("file:///", null, null));
+                               : secMan.createCodebasePrincipal(ioService.newURI("file:///", null, null), {});
 
     secMan.checkLoadURIStrWithPrincipal(principal, uriString, flags);
 
     return uriString;
   },
 
   canDropLink: function(aEvent, aAllowSameDocument)
   {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -4501,18 +4501,17 @@ nsContentUtils::ParseFragmentXML(const n
 nsresult
 nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
                                    nsAString& aResultBuffer,
                                    uint32_t aFlags,
                                    uint32_t aWrapCol)
 {
   nsCOMPtr<nsIURI> uri;
   NS_NewURI(getter_AddRefs(uri), "about:blank");
-  nsCOMPtr<nsIPrincipal> principal =
-    do_CreateInstance(NS_NULLPRINCIPAL_CONTRACTID);
+  nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
   nsCOMPtr<nsIDOMDocument> domDocument;
   nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
                                   EmptyString(),
                                   EmptyString(),
                                   nullptr,
                                   uri,
                                   uri,
                                   principal,
--- a/dom/base/nsDOMTokenList.cpp
+++ b/dom/base/nsDOMTokenList.cpp
@@ -69,17 +69,17 @@ nsDOMTokenList::IndexedGetter(uint32_t a
     aFound = true;
     attr->AtomAt(aIndex)->ToString(aResult);
   } else {
     aFound = false;
   }
 }
 
 void
-nsDOMTokenList::SetValue(const nsAString& aValue, mozilla::ErrorResult& rv)
+nsDOMTokenList::SetValue(const nsAString& aValue, ErrorResult& rv)
 {
   if (!mElement) {
     return;
   }
 
   rv = mElement->SetAttr(kNameSpaceID_None, mAttrAtom, aValue, true);
 }
 
@@ -177,17 +177,17 @@ nsDOMTokenList::Add(const nsTArray<nsStr
     return;
   }
 
   const nsAttrValue* attr = GetParsedAttr();
   AddInternal(attr, aTokens);
 }
 
 void
-nsDOMTokenList::Add(const nsAString& aToken, mozilla::ErrorResult& aError)
+nsDOMTokenList::Add(const nsAString& aToken, ErrorResult& aError)
 {
   AutoTArray<nsString, 1> tokens;
   tokens.AppendElement(aToken);
   Add(tokens, aError);
 }
 
 void
 nsDOMTokenList::RemoveInternal(const nsAttrValue* aAttr,
@@ -263,17 +263,17 @@ nsDOMTokenList::Remove(const nsTArray<ns
   if (!attr) {
     return;
   }
 
   RemoveInternal(attr, aTokens);
 }
 
 void
-nsDOMTokenList::Remove(const nsAString& aToken, mozilla::ErrorResult& aError)
+nsDOMTokenList::Remove(const nsAString& aToken, ErrorResult& aError)
 {
   AutoTArray<nsString, 1> tokens;
   tokens.AppendElement(aToken);
   Remove(tokens, aError);
 }
 
 bool
 nsDOMTokenList::Toggle(const nsAString& aToken,
@@ -304,16 +304,53 @@ nsDOMTokenList::Toggle(const nsAString& 
       isPresent = true;
     }
   }
 
   return isPresent;
 }
 
 void
+nsDOMTokenList::Replace(const nsAString& aToken,
+                        const nsAString& aNewToken,
+                        ErrorResult& aError)
+{
+  // Doing this here instead of using `CheckToken` because if aToken had invalid
+  // characters, and aNewToken is empty, the returned error should be a
+  // SyntaxError, not an InvalidCharacterError.
+  if (aNewToken.IsEmpty()) {
+    aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return;
+  }
+
+  aError = CheckToken(aToken);
+  if (aError.Failed()) {
+    return;
+  }
+
+  aError = CheckToken(aNewToken);
+  if (aError.Failed()) {
+    return;
+  }
+
+  const nsAttrValue* attr = GetParsedAttr();
+  if (!attr || !attr->Contains(aToken)) {
+    return;
+  }
+
+  AutoTArray<nsString, 1> tokens;
+
+  tokens.AppendElement(aToken);
+  RemoveInternal(attr, tokens);
+
+  tokens[0] = aNewToken;
+  AddInternal(attr, tokens);
+}
+
+void
 nsDOMTokenList::Stringify(nsAString& aResult)
 {
   if (!mElement) {
     aResult.Truncate();
     return;
   }
 
   mElement->GetAttr(kNameSpaceID_None, mAttrAtom, aResult);
--- a/dom/base/nsDOMTokenList.h
+++ b/dom/base/nsDOMTokenList.h
@@ -58,20 +58,23 @@ public:
   void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aResult);
   bool Contains(const nsAString& aToken, mozilla::ErrorResult& aError);
   void Add(const nsAString& aToken, mozilla::ErrorResult& aError);
   void Add(const nsTArray<nsString>& aTokens,
            mozilla::ErrorResult& aError);
   void Remove(const nsAString& aToken, mozilla::ErrorResult& aError);
   void Remove(const nsTArray<nsString>& aTokens,
               mozilla::ErrorResult& aError);
+  void Replace(const nsAString& aToken,
+               const nsAString& aNewToken,
+               mozilla::ErrorResult& aError);
   bool Toggle(const nsAString& aToken,
               const mozilla::dom::Optional<bool>& force,
               mozilla::ErrorResult& aError);
-  
+
   void GetValue(nsAString& aResult) { Stringify(aResult); }
   void SetValue(const nsAString& aValue, mozilla::ErrorResult& rv);
   void Stringify(nsAString& aResult);
 
 protected:
   virtual ~nsDOMTokenList();
 
   nsresult CheckToken(const nsAString& aStr);
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -375,21 +375,22 @@ nsDOMWindowUtils::SetDisplayPortForEleme
     if (displayport.IsEmpty() &&
         rootFrame == nsLayoutUtils::GetDisplayRootFrame(rootFrame)) {
       nsCOMPtr<nsIWidget> widget = GetWidget();
       if (widget) {
         bool isRetainingManager;
         LayerManager* manager = widget->GetLayerManager(&isRetainingManager);
         if (isRetainingManager) {
           manager->BeginTransaction();
+          using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
           nsLayoutUtils::PaintFrame(nullptr, rootFrame, nsRegion(),
                                     NS_RGB(255, 255, 255),
                                     nsDisplayListBuilderMode::PAINTING,
-                                    nsLayoutUtils::PAINT_WIDGET_LAYERS |
-                                    nsLayoutUtils::PAINT_EXISTING_TRANSACTION);
+                                    PaintFrameFlags::PAINT_WIDGET_LAYERS |
+                                    PaintFrameFlags::PAINT_EXISTING_TRANSACTION);
         }
       }
     }
   }
 
   return NS_OK;
 }
 
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -12,17 +12,16 @@
 
 DEPRECATED_OPERATION(GetAttributeNode)
 DEPRECATED_OPERATION(SetAttributeNode)
 DEPRECATED_OPERATION(GetAttributeNodeNS)
 DEPRECATED_OPERATION(SetAttributeNodeNS)
 DEPRECATED_OPERATION(RemoveAttributeNode)
 DEPRECATED_OPERATION(CreateAttribute)
 DEPRECATED_OPERATION(CreateAttributeNS)
-DEPRECATED_OPERATION(OwnerElement)
 DEPRECATED_OPERATION(NodeValue)
 DEPRECATED_OPERATION(TextContent)
 DEPRECATED_OPERATION(EnablePrivilege)
 DEPRECATED_OPERATION(DOMExceptionCode)
 DEPRECATED_OPERATION(NoExposedProps)
 DEPRECATED_OPERATION(MutationEvent)
 DEPRECATED_OPERATION(Components)
 DEPRECATED_OPERATION(PrefixedVisibilityAPI)
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -900,18 +900,35 @@ nsFrameLoader::SwapWithOtherRemoteLoader
   }
 
   if (mRemoteBrowser->IsIsolatedMozBrowserElement() !=
       aOther->mRemoteBrowser->IsIsolatedMozBrowserElement() ||
       mRemoteBrowser->HasOwnApp() != aOther->mRemoteBrowser->HasOwnApp()) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  if (mRemoteBrowser->OriginAttributesRef() !=
-      aOther->mRemoteBrowser->OriginAttributesRef()) {
+  // When we swap docShells, maybe we have to deal with a new page created just
+  // for this operation. In this case, the browser code should already have set
+  // the correct userContextId attribute value in the owning XULElement, but our
+  // docShell, that has been created way before) doesn't know that that
+  // happened.
+  // This is the reason why now we must retrieve the correct value from the
+  // usercontextid attribute before comparing our originAttributes with the
+  // other one.
+  DocShellOriginAttributes ourOriginAttributes =
+    mRemoteBrowser->OriginAttributesRef();
+  rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  DocShellOriginAttributes otherOriginAttributes =
+    aOther->mRemoteBrowser->OriginAttributesRef();
+  rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  if (ourOriginAttributes != otherOriginAttributes) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   bool ourHasHistory =
     mIsTopLevelContent &&
     ourContent->IsXULElement(nsGkAtoms::browser) &&
     !ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory);
   bool otherHasHistory =
@@ -1263,18 +1280,35 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
   }
 
   if (ourDocshell->GetIsIsolatedMozBrowserElement() !=
       otherDocshell->GetIsIsolatedMozBrowserElement() ||
       ourDocshell->GetIsApp() != otherDocshell->GetIsApp()) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  if (ourDocshell->GetOriginAttributes() !=
-      otherDocshell->GetOriginAttributes()) {
+  // When we swap docShells, maybe we have to deal with a new page created just
+  // for this operation. In this case, the browser code should already have set
+  // the correct userContextId attribute value in the owning XULElement, but our
+  // docShell, that has been created way before) doesn't know that that
+  // happened.
+  // This is the reason why now we must retrieve the correct value from the
+  // usercontextid attribute before comparing our originAttributes with the
+  // other one.
+  DocShellOriginAttributes ourOriginAttributes =
+    ourDocshell->GetOriginAttributes();
+  rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  DocShellOriginAttributes otherOriginAttributes =
+    otherDocshell->GetOriginAttributes();
+  rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  if (ourOriginAttributes != otherOriginAttributes) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   if (mInSwap || aOther->mInSwap) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
   AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell,
                                      ourEventTarget, otherEventTarget);
@@ -2020,23 +2054,19 @@ nsFrameLoader::MaybeCreateDocShell()
     }
 
     attrs.mAppId = containingAppId;
     attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
     mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_BROWSER);
   }
 
   // Grab the userContextId from owner if XUL
-  nsAutoString userContextIdStr;
-  if ((namespaceID == kNameSpaceID_XUL) &&
-      mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, userContextIdStr) &&
-      !userContextIdStr.IsEmpty()) {
-    nsresult rv;
-    attrs.mUserContextId = userContextIdStr.ToInteger(&rv);
-    NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv = PopulateUserContextIdFromAttribute(attrs);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
 
   if (OwnerIsMozBrowserOrAppFrame()) {
     // For inproc frames, set the docshell properties.
     nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
     nsAutoString name;
@@ -3239,8 +3269,29 @@ nsFrameLoader::GetNewTabContext(MutableT
                                ownApp,
                                containingApp,
                                attrs,
                                signedPkgOrigin);
   NS_ENSURE_STATE(tabContextUpdated);
 
   return NS_OK;
 }
+
+nsresult
+nsFrameLoader::PopulateUserContextIdFromAttribute(DocShellOriginAttributes& aAttr)
+{
+  if (aAttr.mUserContextId ==
+        nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID)  {
+    // Grab the userContextId from owner if XUL
+    nsAutoString userContextIdStr;
+    int32_t namespaceID = mOwnerContent->GetNameSpaceID();
+    if ((namespaceID == kNameSpaceID_XUL) &&
+        mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid,
+                               userContextIdStr) &&
+        !userContextIdStr.IsEmpty()) {
+      nsresult rv;
+      aAttr.mUserContextId = userContextIdStr.ToInteger(&rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  return NS_OK;
+}
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -33,16 +33,19 @@ class nsIInProcessContentFrameMessageMan
 class AutoResetInShow;
 class AutoResetInFrameSwap;
 class nsITabParent;
 class nsIDocShellTreeItem;
 class nsIDocShellTreeOwner;
 class mozIApplication;
 
 namespace mozilla {
+
+class DocShellOriginAttributes;
+
 namespace dom {
 class ContentParent;
 class PBrowserParent;
 class TabParent;
 class MutableTabContext;
 } // namespace dom
 
 namespace ipc {
@@ -334,16 +337,19 @@ private:
                             const nsACString& aPackageId = EmptyCString());
 
   enum TabParentChange {
     eTabParentRemoved,
     eTabParentChanged
   };
   void MaybeUpdatePrimaryTabParent(TabParentChange aChange);
 
+  nsresult
+  PopulateUserContextIdFromAttribute(mozilla::DocShellOriginAttributes& aAttr);
+
   nsCOMPtr<nsIDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURIToLoad;
   mozilla::dom::Element* mOwnerContent; // WEAK
 
   // After the frameloader has been removed from the DOM but before all of the
   // messages from the frame have been received, we keep a strong reference to
   // our <browser> element.
   RefPtr<mozilla::dom::Element> mOwnerContentStrong;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -677,16 +677,22 @@ public:
                               JS::Handle<JS::PropertyDescriptor> desc,
                               JS::ObjectOpResult &result) const override;
   virtual bool ownPropertyKeys(JSContext *cx,
                                JS::Handle<JSObject*> proxy,
                                JS::AutoIdVector &props) const override;
   virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<jsid> id,
                        JS::ObjectOpResult &result) const override;
+
+  virtual bool getPrototypeIfOrdinary(JSContext* cx,
+                                      JS::Handle<JSObject*> proxy,
+                                      bool* isOrdinary,
+                                      JS::MutableHandle<JSObject*> protop) const override;
+
   virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
                          JS::MutableHandle<JSObject*> vp) const override;
   virtual bool preventExtensions(JSContext* cx,
                                  JS::Handle<JSObject*> proxy,
                                  JS::ObjectOpResult& result) const override;
   virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
                             const override;
   virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
@@ -904,16 +910,37 @@ nsOuterWindowProxy::delete_(JSContext *c
     // Indexed, but not supported.  Spec says return true.
     return result.succeed();
   }
 
   return js::Wrapper::delete_(cx, proxy, id, result);
 }
 
 bool
+nsOuterWindowProxy::getPrototypeIfOrdinary(JSContext* cx,
+                                           JS::Handle<JSObject*> proxy,
+                                           bool* isOrdinary,
+                                           JS::MutableHandle<JSObject*> protop) const
+{
+  // Window's [[GetPrototypeOf]] trap isn't the ordinary definition:
+  //
+  //   https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof
+  //
+  // We nonetheless can implement it here using a non-"lazy" [[Prototype]],
+  // because wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp)
+  // supply all the non-ordinary behavior.
+  //
+  // But from a spec point of view, it's the exact same object in both cases --
+  // only the observer's changed.  So both cases *must* report non-ordinary,
+  // even if non-"lazy" [[Prototype]] usually means ordinary.
+  *isOrdinary = false;
+  return true;
+}
+
+bool
 nsOuterWindowProxy::preventExtensions(JSContext* cx,
                                       JS::Handle<JSObject*> proxy,
                                       JS::ObjectOpResult& result) const
 {
   // If [[Extensible]] could be false, then navigating a window could navigate
   // to a window that's [[Extensible]] after being at one that wasn't: an
   // invariant violation.  So never change a window's extensibility.
   return result.failCantPreventExtensions();
--- a/dom/base/nsNodeInfoManager.cpp
+++ b/dom/base/nsNodeInfoManager.cpp
@@ -177,17 +177,16 @@ nsresult
 nsNodeInfoManager::Init(nsIDocument *aDocument)
 {
   NS_ENSURE_TRUE(mNodeInfoHash, NS_ERROR_OUT_OF_MEMORY);
 
   NS_PRECONDITION(!mPrincipal,
                   "Being inited when we already have a principal?");
 
   mPrincipal = nsNullPrincipal::Create();
-  NS_ENSURE_TRUE(mPrincipal, NS_ERROR_FAILURE);
 
   if (aDocument) {
     mBindingManager = new nsBindingManager(aDocument);
   }
 
   mDefaultPrincipal = mPrincipal;
 
   mDocument = aDocument;
--- a/dom/base/nsTreeSanitizer.cpp
+++ b/dom/base/nsTreeSanitizer.cpp
@@ -1512,18 +1512,17 @@ nsTreeSanitizer::InitializeStatics()
     sElementsMathML->PutEntry(*kElementsMathML[i]);
   }
 
   sAttributesMathML = new nsTHashtable<nsISupportsHashKey>(ArrayLength(kAttributesMathML));
   for (uint32_t i = 0; kAttributesMathML[i]; i++) {
     sAttributesMathML->PutEntry(*kAttributesMathML[i]);
   }
 
-  nsCOMPtr<nsIPrincipal> principal =
-      do_CreateInstance(NS_NULLPRINCIPAL_CONTRACTID);
+  nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
   principal.forget(&sNullPrincipal);
 }
 
 void
 nsTreeSanitizer::ReleaseStatics()
 {
   delete sElementsHTML;
   sElementsHTML = nullptr;
--- a/dom/base/test/test_urlSearchParams.html
+++ b/dom/base/test/test_urlSearchParams.html
@@ -84,16 +84,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       { input: '%a=a', data: { '%a' : ['a'] } },
       { input: '%a_=a', data: { '%a_' : ['a'] } },
       { input: '%61=a', data: { 'a' : ['a'] } },
       { input: '%=a', data: { '%' : ['a'] } },
       { input: '%a=a', data: { '%a' : ['a'] } },
       { input: '%a_=a', data: { '%a_' : ['a'] } },
       { input: '%61=a', data: { 'a' : ['a'] } },
       { input: '%61+%4d%4D=', data: { 'a MM' : [''] } },
+      { input: '?a=1', data: { 'a' : ['1'] } },
+      { input: '?', data: {} },
+      { input: '?=b', data: { '' : ['b'] } },
     ];
 
     for (var i = 0; i < checks.length; ++i) {
       var u = new URLSearchParams(checks[i].input);
 
       var count = 0;
       for (var key in checks[i].data) {
         ++count;
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -94,19 +94,16 @@ SimpleGlobalObject::Create(GlobalType gl
   JSAutoRequest ar(cx);
 
   JS::CompartmentOptions options;
   options.creationOptions().setInvisibleToDebugger(true);
 
   nsCOMPtr<nsIPrincipal> principal;
   if (NS_IsMainThread()) {
     principal = nsNullPrincipal::Create();
-    if (!principal) {
-      return nullptr;
-    }
   }
 
   JS::Rooted<JSObject*> global(cx,
     JS_NewGlobalObject(cx, js::Jsvalify(&SimpleGlobalClass),
                        nsJSPrincipals::get(principal),
                        JS::DontFireOnNewGlobalHook, options));
 
   if (!global) {
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
@@ -124,17 +124,17 @@ public:
   }
 
 private:
   BluetoothA2dpInterface* mInterface;
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothA2dpManager::InitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                    nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -166,56 +166,56 @@ private:
 // static
 void
 BluetoothA2dpManager::InitA2dpInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sBtA2dpInterface) {
     BT_LOGR("Bluetooth A2DP interface is already initalized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch A2DP Init runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no Bluetooth interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch A2DP OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch A2DP OnError runnable");
     }
     return;
   }
 
   auto a2dpInterface = btInf->GetBluetoothA2dpInterface();
 
   if (NS_WARN_IF(!a2dpInterface)) {
     // If there's no A2DP interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch A2DP OnError runnable");
     }
     return;
   }
 
   // Set notification handler _before_ registering the module. It could
@@ -324,17 +324,17 @@ public:
     }
   }
 
 private:
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothA2dpManager::DeinitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                      nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -360,43 +360,43 @@ private:
 // static
 void
 BluetoothA2dpManager::DeinitA2dpInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!sBtA2dpInterface) {
     BT_LOGR("Bluetooth A2DP interface has not been initalized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch A2DP Deinit runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no backend interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch A2DP OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch A2DP OnError runnable");
     }
     return;
   }
 
   setupInterface->UnregisterModule(
--- a/dom/bluetooth/bluedroid/BluetoothAvrcpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothAvrcpManager.cpp
@@ -167,17 +167,17 @@ public:
   }
 
 private:
   BluetoothAvrcpInterface* mInterface;
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothAvrcpManager::InitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                    nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -206,56 +206,56 @@ private:
 // static
 void
 BluetoothAvrcpManager::InitAvrcpInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sBtAvrcpInterface) {
     BT_LOGR("Bluetooth AVRCP interface is already initalized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch AVRCP Init runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no Bluetooth interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch AVRCP OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch AVRCP OnError runnable");
     }
     return;
   }
 
   auto avrcpInterface = btInf->GetBluetoothAvrcpInterface();
 
   if (NS_WARN_IF(!avrcpInterface)) {
     // If there's no AVRCP interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch AVRCP OnError runnable");
     }
     return;
   }
 
   // Set notification handler _before_ registering the module. It could
@@ -345,17 +345,17 @@ public:
     }
   }
 
 private:
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothAvrcpManager::DeinitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                      nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -381,43 +381,43 @@ private:
 // static
 void
 BluetoothAvrcpManager::DeinitAvrcpInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!sBtAvrcpInterface) {
     BT_LOGR("Bluetooth AVRCP interface has not been initalized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch AVRCP Deinit runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no backend interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch AVRCP OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch AVRCP OnError runnable");
     }
     return;
   }
 
   setupInterface->UnregisterModule(
@@ -429,17 +429,17 @@ void
 BluetoothAvrcpManager::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   sInShutdown = true;
   Disconnect(nullptr);
   sBluetoothAvrcpManager = nullptr;
 }
 
-class BluetoothAvrcpManager::ConnectRunnable final : public nsRunnable
+class BluetoothAvrcpManager::ConnectRunnable final : public Runnable
 {
 public:
   ConnectRunnable(BluetoothAvrcpManager* aManager)
     : mManager(aManager)
   {
     MOZ_ASSERT(mManager);
   }
   NS_METHOD Run() override
@@ -462,17 +462,17 @@ BluetoothAvrcpManager::Connect(const Blu
   // AVRCP doesn't require connecting. We just set the remote address here.
   mDeviceAddress = aDeviceAddress;
   mController = aController;
   SetConnected(true);
 
   NS_DispatchToMainThread(new ConnectRunnable(this));
 }
 
-class BluetoothAvrcpManager::DisconnectRunnable final : public nsRunnable
+class BluetoothAvrcpManager::DisconnectRunnable final : public Runnable
 {
 public:
   DisconnectRunnable(BluetoothAvrcpManager* aManager)
     : mManager(aManager)
   {
     MOZ_ASSERT(mManager);
   }
   NS_METHOD Run() override
--- a/dom/bluetooth/bluedroid/BluetoothGattManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothGattManager.cpp
@@ -460,17 +460,17 @@ public:
   }
 
 private:
   BluetoothGattInterface* mInterface;
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothGattManager::InitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                    nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -496,56 +496,56 @@ private:
 // static
 void
 BluetoothGattManager::InitGattInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sBluetoothGattInterface) {
     BT_LOGR("Bluetooth GATT interface is already initalized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch GATT Init runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no Bluetooth interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch GATT OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch GATT OnError runnable");
     }
     return;
   }
 
   auto gattInterface = btInf->GetBluetoothGattInterface();
 
   if (NS_WARN_IF(!gattInterface)) {
     // If there's no GATT interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch GATT OnError runnable");
     }
     return;
   }
 
   if (!sClients) {
@@ -610,17 +610,17 @@ public:
     }
   }
 
 private:
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothGattManager::DeinitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                      nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -646,43 +646,43 @@ private:
 // static
 void
 BluetoothGattManager::DeinitGattInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!sBluetoothGattInterface) {
     BT_LOGR("Bluetooth GATT interface has not been initalized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch GATT Deinit runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no backend interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch GATT OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch GATT OnError runnable");
     }
     return;
   }
 
   setupInterface->UnregisterModule(
--- a/dom/bluetooth/bluedroid/BluetoothHidManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothHidManager.cpp
@@ -89,17 +89,17 @@ public:
   }
 
 private:
   BluetoothHidInterface* mInterface;
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothHidManager::InitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                    nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -125,56 +125,56 @@ private:
 // static
 void
 BluetoothHidManager::InitHidInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sBluetoothHidInterface) {
     BT_LOGR("Bluetooth HID interface is already initialized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HID Init runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no backend interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HID OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HID OnError runnable");
     }
     return;
   }
 
   auto hidinterface = btInf->GetBluetoothHidInterface();
 
   if (NS_WARN_IF(!hidinterface)) {
     // If there's no HID interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HID OnError runnable");
     }
     return;
   }
 
   // Set notification handler _before_ registering the module. It could
@@ -241,17 +241,17 @@ public:
     }
   }
 
 private:
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothHidManager::DeinitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                      nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -277,43 +277,43 @@ private:
 // static
 void
 BluetoothHidManager::DeinitHidInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!sBluetoothHidInterface) {
     BT_LOGR("Bluetooth Hid interface has not been initialized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HID Deinit runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no backend interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HID OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HID OnError runnable");
     }
     return;
   }
 
   setupInterface->UnregisterModule(
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
@@ -97,17 +97,17 @@ BluetoothOppManager::Observe(nsISupports
     HandleShutdown();
     return NS_OK;
   }
 
   MOZ_ASSERT(false, "BluetoothOppManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
-class BluetoothOppManager::SendSocketDataTask final : public nsRunnable
+class BluetoothOppManager::SendSocketDataTask final : public Runnable
 {
 public:
   SendSocketDataTask(UniquePtr<uint8_t[]> aStream, uint32_t aSize)
     : mStream(Move(aStream))
     , mSize(aSize)
   {
     MOZ_ASSERT(!NS_IsMainThread());
   }
@@ -121,17 +121,17 @@ public:
     return NS_OK;
   }
 
 private:
   UniquePtr<uint8_t[]> mStream;
   uint32_t mSize;
 };
 
-class BluetoothOppManager::ReadFileTask final : public nsRunnable
+class BluetoothOppManager::ReadFileTask final : public Runnable
 {
 public:
   ReadFileTask(nsIInputStream* aInputStream,
                uint32_t aRemoteMaxPacketSize) : mInputStream(aInputStream)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mAvailablePacketSize = aRemoteMaxPacketSize - kPutRequestHeaderSize;
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -113,17 +113,17 @@ class BluetoothHfpManager::CloseScoTask 
 private:
   void Run() override
   {
     MOZ_ASSERT(sBluetoothHfpManager);
     sBluetoothHfpManager->DisconnectSco();
   }
 };
 
-class BluetoothHfpManager::CloseScoRunnable : public nsRunnable
+class BluetoothHfpManager::CloseScoRunnable : public Runnable
 {
 public:
   NS_IMETHOD Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     MessageLoop::current()->PostDelayedTask(
       FROM_HERE, new CloseScoTask(), sBusyToneInterval);
@@ -314,17 +314,17 @@ public:
   }
 
 private:
   BluetoothHandsfreeInterface* mInterface;
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothHfpManager::InitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                    nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -350,56 +350,56 @@ private:
 // static
 void
 BluetoothHfpManager::InitHfpInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sBluetoothHfpInterface) {
     BT_LOGR("Bluetooth Handsfree interface is already initalized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HFP Init runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no backend interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HFP OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HFP OnError runnable");
     }
     return;
   }
 
   auto interface = btInf->GetBluetoothHandsfreeInterface();
 
   if (NS_WARN_IF(!interface)) {
     // If there's no HFP interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HFP OnError runnable");
     }
     return;
   }
 
   // Set notification handler _before_ registering the module. It could
@@ -466,17 +466,17 @@ public:
     }
   }
 
 private:
   RefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class BluetoothHfpManager::DeinitProfileResultHandlerRunnable final
-  : public nsRunnable
+  : public Runnable
 {
 public:
   DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
                                      nsresult aRv)
     : mRes(aRes)
     , mRv(aRv)
   {
     MOZ_ASSERT(mRes);
@@ -502,43 +502,43 @@ private:
 // static
 void
 BluetoothHfpManager::DeinitHfpInterface(BluetoothProfileResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!sBluetoothHfpInterface) {
     BT_LOGR("Bluetooth Handsfree interface has not been initialized.");
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HFP Deinit runnable");
     }
     return;
   }
 
   auto btInf = BluetoothInterface::GetInstance();
 
   if (NS_WARN_IF(!btInf)) {
     // If there's no backend interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HFP OnError runnable");
     }
     return;
   }
 
   auto setupInterface = btInf->GetBluetoothSetupInterface();
 
   if (NS_WARN_IF(!setupInterface)) {
     // If there's no Setup interface, we dispatch a runnable
     // that calls the profile result handler.
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
     if (NS_FAILED(NS_DispatchToMainThread(r))) {
       BT_LOGR("Failed to dispatch HFP OnError runnable");
     }
     return;
   }
 
   setupInterface->UnregisterModule(
--- a/dom/bluetooth/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp
@@ -508,17 +508,17 @@ GetPairedDevicesFilter(const BluetoothVa
     if (deviceProperties[p].name().EqualsLiteral("Paired")) {
       return deviceProperties[p].value().get_bool();
     }
   }
 
   return false;
 }
 
-class DistributeBluetoothSignalTask : public nsRunnable
+class DistributeBluetoothSignalTask : public Runnable
 {
 public:
   DistributeBluetoothSignalTask(const BluetoothSignal& aSignal)
     : mSignal(aSignal)
   {
   }
 
   nsresult Run()
@@ -531,17 +531,17 @@ public:
 
     return NS_OK;
   }
 
 private:
   BluetoothSignal mSignal;
 };
 
-class ControlPropertyChangedHandler : public nsRunnable
+class ControlPropertyChangedHandler : public Runnable
 {
 public:
   ControlPropertyChangedHandler(const BluetoothSignal& aSignal)
     : mSignal(aSignal)
   {
   }
 
   nsresult Run()
@@ -563,17 +563,17 @@ public:
     avrcp->SetConnected(connected);
     return NS_OK;
   }
 
 private:
   BluetoothSignal mSignal;
 };
 
-class SinkPropertyChangedHandler : public nsRunnable
+class SinkPropertyChangedHandler : public Runnable
 {
 public:
   SinkPropertyChangedHandler(const BluetoothSignal& aSignal)
     : mSignal(aSignal)
   {
   }
 
   NS_IMETHOD
@@ -593,17 +593,17 @@ public:
     a2dp->HandleSinkPropertyChanged(mSignal);
     return NS_OK;
   }
 
 private:
   BluetoothSignal mSignal;
 };
 
-class InputPropertyChangedHandler : public nsRunnable
+class InputPropertyChangedHandler : public Runnable
 {
 public:
   InputPropertyChangedHandler(const BluetoothSignal& aSignal)
     : mSignal(aSignal)
   {
   }
 
   NS_IMETHOD
@@ -632,17 +632,17 @@ class TryFiringAdapterAddedTask : public
 {
 public:
   void Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 };
 
-class TryFiringAdapterAddedRunnable : public nsRunnable
+class TryFiringAdapterAddedRunnable : public Runnable
 {
 public:
   TryFiringAdapterAddedRunnable(bool aDelay)
     : mDelay(aDelay)
   { }
 
   nsresult Run()
   {
@@ -714,17 +714,17 @@ UnpackObjectPathMessage(DBusMessage* aMs
         LOG_AND_FREE_DBUS_ERROR(&err);
       }
     } else {
       aValue = NS_ConvertUTF8toUTF16(object_path);
     }
   }
 }
 
-class PrepareProfileManagersRunnable : public nsRunnable
+class PrepareProfileManagersRunnable : public Runnable
 {
 public:
   nsresult Run()
   {
     BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
     if (!hfp || !hfp->Listen()) {
       BT_WARNING("Failed to start listening for BluetoothHfpManager!");
       return NS_ERROR_FAILURE;
@@ -803,17 +803,17 @@ UnpackVoidMessage(DBusMessage* aMsg, DBu
 
 static void
 GetVoidCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable)
 {
   RunDBusCallback(aMsg, aBluetoothReplyRunnable,
                   UnpackVoidMessage);
 }
 
-class ReplyErrorToProfileManager : public nsRunnable
+class ReplyErrorToProfileManager : public Runnable
 {
 public:
   ReplyErrorToProfileManager(BluetoothServiceClass aServiceClass,
                              bool aConnect,
                              const nsAString& aErrorString)
     : mServiceClass(aServiceClass)
     , mConnect(aConnect)
     , mErrorString(aErrorString)
@@ -1721,34 +1721,34 @@ public:
       &services, ArrayLength(sServices), DBUS_TYPE_INVALID);
 
     NS_ENSURE_TRUE_VOID(success);
 
     Unused << handler.forget(); /* picked up by callback handler */
   }
 };
 
-class PrepareAdapterRunnable : public nsRunnable
+class PrepareAdapterRunnable : public Runnable
 {
 public:
   PrepareAdapterRunnable()
   { }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     Task* task = new AddReservedServiceRecordsTask();
     DispatchToDBusThread(task);
 
     return NS_OK;
   }
 };
 
-class RequestPlayStatusTask : public nsRunnable
+class RequestPlayStatusTask : public Runnable
 {
 public:
   RequestPlayStatusTask()
   {
     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
   }
 
   nsresult Run()
@@ -2011,17 +2011,17 @@ EventFilter(DBusConnection* aConn, DBusM
   }
 
   if (!errorStr.IsEmpty()) {
     BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
   }
 
   BluetoothSignal signal(signalName, signalPath, v);
-  RefPtr<nsRunnable> task;
+  RefPtr<Runnable> task;
   if (signalInterface.EqualsLiteral(DBUS_SINK_IFACE)) {
     task = new SinkPropertyChangedHandler(signal);
   } else if (signalInterface.EqualsLiteral(DBUS_CTL_IFACE)) {
     task = new ControlPropertyChangedHandler(signal);
   } else if (signalInterface.EqualsLiteral(DBUS_INPUT_IFACE)) {
     task = new InputPropertyChangedHandler(signal);
   } else {
     task = new DistributeBluetoothSignalTask(signal);
@@ -2081,43 +2081,43 @@ public:
   }
 
   void Run()
   {
     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
 
     if (sDBusConnection) {
       BT_WARNING("DBus connection has already been established.");
-      RefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
+      RefPtr<Runnable> runnable = new BluetoothService::ToggleBtAck(true);
       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
         BT_WARNING("Failed to dispatch to main thread!");
       }
       return;
     }
 
     // Add a filter for all incoming messages_base
     if (!dbus_connection_add_filter(mConnection->GetConnection(),
                                     EventFilter, nullptr, nullptr)) {
       BT_WARNING("Cannot create DBus Event Filter for DBus Thread!");
-      RefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
+      RefPtr<Runnable> runnable = new BluetoothService::ToggleBtAck(false);
       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
         BT_WARNING("Failed to dispatch to main thread!");
       }
       return;
     }
 
     mConnection->Watch();
 
     if (!sPairingReqTable) {
       sPairingReqTable = new nsDataHashtable<BluetoothAddressHashKey, DBusMessage* >;
     }
 
     sDBusConnection = mConnection.release();
 
-    RefPtr<nsRunnable> runnable =
+    RefPtr<Runnable> runnable =
       new BluetoothService::ToggleBtAck(true);
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       BT_WARNING("Failed to dispatch to main thread!");
       return;
     }
 
     /* Normally we'll receive the signal 'AdapterAdded' with the adapter object
      * path from the DBus daemon during start up. So, there's no need to query
@@ -2137,40 +2137,40 @@ public:
       }
     }
   }
 
 private:
   UniquePtr<RawDBusConnection> mConnection;
 };
 
-class StartBluetoothRunnable final : public nsRunnable
+class StartBluetoothRunnable final : public Runnable
 {
 public:
   NS_IMETHOD Run()
   {
     // This could block. It should never be run on the main thread.
     MOZ_ASSERT(!NS_IsMainThread()); // BT thread
 
 #ifdef MOZ_WIDGET_GONK
     if (!sBluedroid.Enable()) {
       BT_WARNING("Bluetooth not available.");
-      RefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
+      RefPtr<Runnable> runnable = new BluetoothService::ToggleBtAck(false);
       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
         BT_WARNING("Failed to dispatch to main thread!");
       }
       return NS_ERROR_FAILURE;
     }
 #endif
 
     RawDBusConnection* connection = new RawDBusConnection();
     nsresult rv = connection->EstablishDBusConnection();
     if (NS_FAILED(rv)) {
       BT_WARNING("Failed to establish connection to BlueZ daemon");
-      RefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
+      RefPtr<Runnable> runnable = new BluetoothService::ToggleBtAck(false);
       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
         BT_WARNING("Failed to dispatch to main thread!");
       }
       return NS_ERROR_FAILURE;
     }
 
     DBusError err;
     dbus_error_init(&err);
@@ -2197,25 +2197,25 @@ public:
   }
 };
 
 nsresult
 BluetoothDBusService::StartInternal(BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(!aRunnable);
 
-  RefPtr<nsRunnable> runnable = new StartBluetoothRunnable();
+  RefPtr<Runnable> runnable = new StartBluetoothRunnable();
   nsresult rv = DispatchToBtThread(runnable);
   if (NS_FAILED(rv)) {
     BT_WARNING("Failed to dispatch to BT thread!");
   }
   return rv;
 }
 
-class DisableBluetoothRunnable final : public nsRunnable
+class DisableBluetoothRunnable final : public Runnable
 {
 public:
   NS_IMETHOD Run()
   {
     if (NS_IsMainThread()) {
       // Clear |sControllerArray| here while we're on the main thread
       sControllerArray.Clear();
       // Forward this runnable to BT thread
@@ -2225,17 +2225,17 @@ public:
 #ifdef MOZ_WIDGET_GONK
     MOZ_ASSERT(sBluedroid.IsEnabled());
     // Disable() return true on success, so we need to invert it
     bool isEnabled = !sBluedroid.Disable();
 #else
     bool isEnabled = false;
 #endif
 
-    RefPtr<nsRunnable> runnable =
+    RefPtr<Runnable> runnable =
       new BluetoothService::ToggleBtAck(isEnabled);
     nsresult rv = NS_DispatchToMainThread(runnable);
     if (NS_FAILED(rv)) {
       BT_WARNING("Failed to dispatch to main thread!");
     }
     return rv;
   }
 };
@@ -2247,17 +2247,17 @@ public:
   { }
 
   void Run()
   {
     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
 
     if (!sDBusConnection) {
       BT_WARNING("DBus connection has not been established.");
-      RefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
+      RefPtr<Runnable> runnable = new BluetoothService::ToggleBtAck(false);
       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
         BT_WARNING("Failed to dispatch to main thread!");
       }
       return;
     }
 
     for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
       dbus_bus_remove_match(sDBusConnection->GetConnection(),
@@ -2290,24 +2290,24 @@ public:
 
     // This command closes the DBus connection and all its instances
     // of DBusWatch will be removed and free'd.
     sDBusConnection = nullptr;
 
     // We can only dispatch to the BT thread if we're on the main
     // thread. Thus we dispatch our runnable to the main thread
     // from where it will forward itself to the BT thread.
-    RefPtr<nsRunnable> runnable = new DisableBluetoothRunnable();
+    RefPtr<Runnable> runnable = new DisableBluetoothRunnable();
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       BT_WARNING("Failed to dispatch to BT thread!");
     }
   }
 };
 
-class StopBluetoothRunnable final : public nsRunnable
+class StopBluetoothRunnable final : public Runnable
 {
 public:
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread()); // BT thread
 
     // This could block. It should never be run on the main thread.
     MonitorAutoLock lock(*sStopBluetoothMonitor);
@@ -2321,17 +2321,17 @@ public:
   }
 };
 
 nsresult
 BluetoothDBusService::StopInternal(BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(!aRunnable);
 
-  RefPtr<nsRunnable> runnable = new StopBluetoothRunnable();
+  RefPtr<Runnable> runnable = new StopBluetoothRunnable();
   nsresult rv = DispatchToBtThread(runnable);
   if (NS_FAILED(rv)) {
     BT_WARNING("Failed to dispatch to BT thread!");
   }
   return rv;
 }
 
 class DefaultAdapterPathReplyHandler : public DBusReplyHandler
@@ -3509,17 +3509,17 @@ BluetoothDBusService::ToggleCalls(Blueto
 
   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
   hfp->ToggleCalls();
 
   DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
 }
 #endif // MOZ_B2G_RIL
 
-class OnUpdateSdpRecordsRunnable : public nsRunnable
+class OnUpdateSdpRecordsRunnable : public Runnable
 {
 public:
   OnUpdateSdpRecordsRunnable(const BluetoothAddress& aDeviceAddress,
                              BluetoothProfileManagerBase* aManager)
     : mDeviceAddress(aDeviceAddress)
     , mManager(aManager)
   {
     MOZ_ASSERT(!mDeviceAddress.IsCleared());
@@ -3536,17 +3536,17 @@ public:
     return NS_OK;
   }
 
 private:
   BluetoothAddress mDeviceAddress;
   BluetoothProfileManagerBase* mManager;
 };
 
-class OnGetServiceChannelRunnable : public nsRunnable
+class OnGetServiceChannelRunnable : public Runnable
 {
 public:
   OnGetServiceChannelRunnable(const BluetoothAddress& aDeviceAddress,
                               const BluetoothUuid& aServiceUuid,
                               int aChannel,
                               BluetoothProfileManagerBase* aManager)
     : mDeviceAddress(aDeviceAddress)
     , mServiceUuid(aServiceUuid)
@@ -3598,17 +3598,17 @@ public:
     // in BluetoothProfileManagerBase::OnGetServiceChannel.
 
     int channel = -1;
 
     if (aReply && (dbus_message_get_type(aReply) != DBUS_MESSAGE_TYPE_ERROR)) {
       channel = dbus_returns_int32(aReply);
     }
 
-    RefPtr<nsRunnable> r =
+    RefPtr<Runnable> r =
       new OnGetServiceChannelRunnable(mDeviceAddress, mServiceUUID, channel,
                                       mBluetoothProfileManager);
     nsresult rv = NS_DispatchToMainThread(r);
     NS_ENSURE_SUCCESS_VOID(rv);
   }
 
 private:
   BluetoothAddress mDeviceAddress;
@@ -3691,20 +3691,20 @@ BluetoothDBusService::GetServiceChannel(
   DispatchToDBusThread(task);
 #else
   // FIXME/Bug 793977 qdot: Just set something for desktop, until we have a
   // parser for the GetServiceAttributes xml block
   //
   // Even though we are on the main thread already, we need to dispatch a
   // runnable here. OnGetServiceChannel needs mRunnable to be set, which
   // happens after GetServiceChannel returns.
-  RefPtr<nsRunnable> r = new OnGetServiceChannelRunnable(aDeviceAddress,
-                                                           aServiceUUID,
-                                                           1,
-                                                           aManager);
+  RefPtr<Runnable> r = new OnGetServiceChannelRunnable(aDeviceAddress,
+                                                         aServiceUUID,
+                                                         1,
+                                                         aManager);
   NS_DispatchToMainThread(r);
 #endif
 
   return NS_OK;
 }
 
 class UpdateSdpRecordsTask : public Task
 {
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -1485,17 +1485,17 @@ BluetoothHfpManager::HandleCallStateChan
   mCurrentCallArray[aCallIndex].mIsConference = aIsConference;
 
   // Same logic as implementation in ril_worker.js
   if (aNumber.Length() && aNumber[0] == '+') {
     mCurrentCallArray[aCallIndex].mType = TOA_INTERNATIONAL;
   }
   mCurrentCallArray[aCallIndex].mNumber = aNumber;
 
-  RefPtr<nsRunnable> sendRingTask;
+  RefPtr<Runnable> sendRingTask;
   nsString address;
 
   switch (aCallState) {
     case nsITelephonyService::CALL_STATE_HELD:
       switch (prevCallState) {
         case nsITelephonyService::CALL_STATE_CONNECTED: {
           uint32_t numActive =
             GetNumberOfCalls(nsITelephonyService::CALL_STATE_CONNECTED);
--- a/dom/bluetooth/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothOppManager.cpp
@@ -92,17 +92,17 @@ BluetoothOppManager::Observe(nsISupports
     HandleShutdown();
     return NS_OK;
   }
 
   MOZ_ASSERT(false, "BluetoothOppManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
-class SendSocketDataTask : public nsRunnable
+class SendSocketDataTask : public Runnable
 {
 public:
   SendSocketDataTask(UniquePtr<uint8_t[]> aStream, uint32_t aSize)
     : mStream(Move(aStream))
     , mSize(aSize)
   {
     MOZ_ASSERT(!NS_IsMainThread());
   }
@@ -116,17 +116,17 @@ public:
     return NS_OK;
   }
 
 private:
   UniquePtr<uint8_t[]> mStream;
   uint32_t mSize;
 };
 
-class ReadFileTask : public nsRunnable
+class ReadFileTask : public Runnable
 {
 public:
   ReadFileTask(nsIInputStream* aInputStream,
                uint32_t aRemoteMaxPacketSize) : mInputStream(aInputStream)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mAvailablePacketSize = aRemoteMaxPacketSize - kPutRequestHeaderSize;
--- a/dom/bluetooth/common/BluetoothReplyRunnable.h
+++ b/dom/bluetooth/common/BluetoothReplyRunnable.h
@@ -20,17 +20,17 @@ namespace dom {
 class Promise;
 }
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothReply;
 
-class BluetoothReplyRunnable : public nsRunnable
+class BluetoothReplyRunnable : public Runnable
 {
 public:
   NS_DECL_NSIRUNNABLE
 
   BluetoothReplyRunnable(nsIDOMDOMRequest* aReq,
                          Promise* aPromise = nullptr);
 
   void SetReply(BluetoothReply* aReply);
@@ -91,17 +91,17 @@ protected:
   virtual bool
   ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue) override
   {
     aValue.setUndefined();
     return true;
   }
 };
 
-class BluetoothReplyTaskQueue : public nsRunnable
+class BluetoothReplyTaskQueue : public Runnable
 {
 public:
   NS_DECL_NSIRUNNABLE
 
   class SubReplyRunnable : public BluetoothReplyRunnable
   {
   public:
     SubReplyRunnable(nsIDOMDOMRequest* aReq,
--- a/dom/bluetooth/common/BluetoothService.cpp
+++ b/dom/bluetooth/common/BluetoothService.cpp
@@ -429,17 +429,17 @@ BluetoothService::StartBluetooth(bool aI
     // Switch Bluetooth on
     nsresult rv = StartInternal(aRunnable);
     if (NS_FAILED(rv)) {
       BT_WARNING("Bluetooth service failed to start!");
       return rv;
     }
   } else {
     BT_WARNING("Bluetooth has already been enabled before.");
-    RefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
+    RefPtr<Runnable> runnable = new BluetoothService::ToggleBtAck(true);
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       BT_WARNING("Failed to dispatch to main thread!");
     }
   }
 
   return NS_OK;
 }
 
@@ -460,17 +460,17 @@ BluetoothService::StopBluetooth(bool aIs
     // Any connected Bluetooth profile would be disconnected.
     nsresult rv = StopInternal(aRunnable);
     if (NS_FAILED(rv)) {
       BT_WARNING("Bluetooth service failed to stop!");
       return rv;
     }
   } else {
     BT_WARNING("Bluetooth has already been enabled/disabled before.");
-    RefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
+    RefPtr<Runnable> runnable = new BluetoothService::ToggleBtAck(false);
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       BT_WARNING("Failed to dispatch to main thread!");
     }
   }
 
   return NS_OK;
 }
 
--- a/dom/bluetooth/common/BluetoothService.h
+++ b/dom/bluetooth/common/BluetoothService.h
@@ -36,17 +36,17 @@ class BluetoothService : public nsIObser
 {
   class ToggleBtTask;
   friend class ToggleBtTask;
 
   class StartupTask;
   friend class StartupTask;
 
 public:
-  class ToggleBtAck : public nsRunnable
+  class ToggleBtAck : public Runnable
   {
   public:
     ToggleBtAck(bool aEnabled);
     NS_IMETHOD Run();
 
   private:
     bool mEnabled;
   };
--- a/dom/bluetooth/common/webapi/BluetoothGattServer.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothGattServer.cpp
@@ -892,19 +892,19 @@ public:
     mPromise = nullptr;
   }
 
 private:
   virtual void OnSuccessFired() override
   {
     mService->AssignAppUuid(mServer->mAppUuid);
 
-    RefPtr<nsRunnable> runnable = new AddServiceTaskQueue(mServer,
-                                                            mService,
-                                                            mPromise);
+    RefPtr<Runnable> runnable = new AddServiceTaskQueue(mServer,
+                                                          mService,
+                                                          mPromise);
     nsresult rv = NS_DispatchToMainThread(runnable.forget());
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mServer->mPendingService = nullptr;
       mPromise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
     }
   }
 
--- a/dom/camera/TestGonkCameraHardware.cpp
+++ b/dom/camera/TestGonkCameraHardware.cpp
@@ -148,17 +148,17 @@ TestGonkCameraHardwareListener::HandleEv
     if (!NS_WARN_IF(!event)) {
       nsString errorType;
 
       event->GetMessage(errorType);
       if (errorType.EqualsLiteral("picture")) {
         OnTakePictureError(mTarget);
       } else if (errorType.EqualsLiteral("system")) {
         if (!NS_WARN_IF(!mCameraThread)) {
-          class DeferredSystemFailure : public nsRunnable
+          class DeferredSystemFailure : public Runnable
           {
           public:
             DeferredSystemFailure(nsGonkCameraControl* aTarget)
               : mTarget(aTarget)
             { }
 
             NS_IMETHODIMP
             Run()
@@ -223,17 +223,17 @@ TestGonkCameraHardwareListener::HandleEv
       NS_ConvertUTF16toUTF8(eventType).get());
   }
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(TestGonkCameraHardwareListener, nsIDOMEventListener)
 
-class TestGonkCameraHardware::ControlMessage : public nsRunnable
+class TestGonkCameraHardware::ControlMessage : public Runnable
 {
 public:
   ControlMessage(TestGonkCameraHardware* aTestHw)
     : mTestHw(aTestHw)
   { }
 
   NS_IMETHOD
   Run() override
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -741,16 +741,18 @@ WebGLContext::GetFramebufferAttachmentPa
     case LOCAL_GL_READ_FRAMEBUFFER:
         fb = mBoundReadFramebuffer;
         break;
 
     default:
         MOZ_CRASH("Bad target.");
     }
 
+    MakeContextCurrent();
+
     if (fb)
         return fb->GetAttachmentParameter(funcName, cx, target, attachment, pname, &rv);
 
     ////////////////////////////////////
 
     if (!IsWebGL2()) {
         ErrorInvalidOperation("%s: Querying against the default framebuffer is not"
                               " allowed in WebGL 1.",
--- a/dom/canvas/test/webgl-mochitest/test_fuzzing_bugs.html
+++ b/dom/canvas/test/webgl-mochitest/test_fuzzing_bugs.html
@@ -48,19 +48,49 @@ function webGL2ClearBufferXXX_bug1252414
   gl.clearBufferiv(gl.COLOR, 0, new Int32Array(10));
   gl.clearBufferuiv(gl.COLOR, 0, new Uint32Array(10));
   gl.clearBufferfv(gl.DEPTH, 0, new Float32Array(10));
   gl.clearBufferfi(gl.DEPTH_STENCIL, 0, 0.0, 0);
 
   ok(true, 'WebGL2 clearBufferXXX call during loseContext');
 }
 
+// Test gl function for multiple gl contexts switch.
+function getFramebufferAttachmentParameter_bug1267100()
+{
+  var canvas1 = document.createElement('canvas');
+  var canvas2 = document.createElement('canvas');
+
+  var gl1 = null;
+  gl1 = canvas1.getContext('webgl') || canvas1.getContext('experimental-webgl');
+  if (!gl1) {
+    todo(false, 'WebGL getFramebufferAttachmentParameter test, webgl is unavailable.');
+    return;
+  }
+  var gl2 = null;
+  gl2 = canvas2.getContext('webgl') || canvas2.getContext('experimental-webgl');
+  if (!gl2) {
+    todo(false, 'WebGL getFramebufferAttachmentParameter test, webgl is unavailable.');
+    return;
+  }
+
+  gl1.bindFramebuffer(gl1.FRAMEBUFFER, gl1.createFramebuffer());
+  gl2.scissor(0, 1, 2, 3);
+  // The gl call should still work even though we use another gl context before.
+  gl1.getFramebufferAttachmentParameter(gl1.FRAMEBUFFER,
+                                        gl1.COLOR_ATTACHMENT0,
+                                        gl1.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
+
+  ok(true, 'WebGL test getFramebufferAttachmentParameter');
+}
+
 function run() {
   webGL2ClearBufferXXX_bug1252414();
   framebufferTexture2D_bug1257593();
+  getFramebufferAttachmentParameter_bug1267100();
 
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 
 try {
   var prefArrArr = [
--- a/dom/datastore/DataStoreDB.cpp
+++ b/dom/datastore/DataStoreDB.cpp
@@ -100,19 +100,16 @@ DataStoreDB::~DataStoreDB()
 }
 
 nsresult
 DataStoreDB::CreateFactoryIfNeeded()
 {
   if (!mFactory) {
     nsresult rv;
     nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
-    if (!principal) {
-      return NS_ERROR_FAILURE;
-    }
 
     nsIXPConnect* xpc = nsContentUtils::XPConnect();
     MOZ_ASSERT(xpc);
 
     AutoJSAPI jsapi;
     jsapi.Init();
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> global(cx);
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -1027,17 +1027,18 @@ DataTransfer::GetTransferable(uint32_t a
   bool added = false;
   bool handlingCustomFormats = true;
   uint32_t totalCustomLength = 0;
 
   const char* knownFormats[] = {
     kTextMime, kHTMLMime, kNativeHTMLMime, kRTFMime,
     kURLMime, kURLDataMime, kURLDescriptionMime, kURLPrivateMime,
     kPNGImageMime, kJPEGImageMime, kGIFImageMime, kNativeImageMime,
-    kFileMime, kFilePromiseMime, kFilePromiseDirectoryMime,
+    kFileMime, kFilePromiseMime, kFilePromiseURLMime,
+    kFilePromiseDestFilename, kFilePromiseDirectoryMime,
     kMozTextInternal, kHTMLContext, kHTMLInfo };
 
   /*
    * Two passes are made here to iterate over all of the types. First, look for
    * any types that are not in the list of known types. For this pass,
    * handlingCustomFormats will be true. Data that corresponds to unknown types
    * will be pulled out and inserted into a single type (kCustomTypesMime) by
    * writing the data into a stream.
--- a/dom/fmradio/FMRadioService.h
+++ b/dom/fmradio/FMRadioService.h
@@ -15,17 +15,17 @@
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
 #include "nsIObserver.h"
 #include "nsXULAppAPI.h"
 
 BEGIN_FMRADIO_NAMESPACE
 
-class FMRadioReplyRunnable : public nsRunnable
+class FMRadioReplyRunnable : public Runnable
 {
 public:
   FMRadioReplyRunnable() : mResponseType(SuccessResponse()) {}
   virtual ~FMRadioReplyRunnable() {}
 
   void
   SetReply(const FMRadioResponseType& aResponseType)
   {
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -532,20 +532,30 @@ HTMLInputElement::nsFilePickerShownCallb
     HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(
       mInput->OwnerDoc(), lastUsedDir);
   }
 
   // The text control frame (if there is one) isn't going to send a change
   // event because it will think this is done by a script.
   // So, we can safely send one by ourself.
   mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
-  return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
-                                              static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
-                                              NS_LITERAL_STRING("change"), true,
-                                              false);
+
+  nsresult rv = NS_OK;
+  rv = nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
+                                            static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
+                                            NS_LITERAL_STRING("input"), true,
+                                            false);
+  NS_WARN_IF(NS_FAILED(rv));
+
+  rv = nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
+                                            static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
+                                            NS_LITERAL_STRING("change"), true,
+                                            false);
+
+  return rv;
 }
 
 NS_IMPL_ISUPPORTS(HTMLInputElement::nsFilePickerShownCallback,
                   nsIFilePickerShownCallback)
 
 class nsColorPickerShownCallback final
   : public nsIColorPickerShownCallback
 {
@@ -3844,16 +3854,22 @@ HTMLInputElement::PostHandleEvent(EventC
         }
       } else if (oldType == NS_FORM_INPUT_CHECKBOX) {
         bool originalIndeterminateValue =
           !!(aVisitor.mItemFlags & NS_ORIGINAL_INDETERMINATE_VALUE);
         SetIndeterminateInternal(originalIndeterminateValue, false);
         DoSetChecked(originalCheckedValue, true, true);
       }
     } else {
+      // Fire input event and then change event.
+      nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
+                                           static_cast<nsIDOMHTMLInputElement*>(this),
+                                           NS_LITERAL_STRING("input"), true,
+                                           false);
+
       nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                            static_cast<nsIDOMHTMLInputElement*>(this),
                                            NS_LITERAL_STRING("change"), true,
                                            false);
 #ifdef ACCESSIBILITY
       // Fire an event to notify accessibility
       if (mType == NS_FORM_INPUT_CHECKBOX) {
         FireEventForAccessibility(this, aVisitor.mPresContext,
--- a/dom/html/test/forms/test_change_event.html
+++ b/dom/html/test/forms/test_change_event.html
@@ -161,16 +161,25 @@ https://bugzilla.mozilla.org/show_bug.cg
       //for radio and and checkboxes, we require that change event should ONLY be dispatched on setting the value.
       else {
         input = document.getElementById("input_" + NonTextInputTypes[i]);
         input.focus();
         input.click();
         is(NonTextInputChange[i], 1, NonTextInputTypes[i] + " input element should have dispatched change event.");
         input.blur();
         is(NonTextInputChange[i], 1, "Change event shouldn't be dispatched on " + NonTextInputTypes[i] + " input element");
+
+        // Test that change event is not dispatched if click event is cancelled.
+        function preventDefault(e) {
+          e.preventDefault();
+        }
+        input.addEventListener("click", preventDefault, false);
+        input.click();
+        is(NonTextInputChange[i], 1, "Change event shouldn't be dispatched if click event is cancelled");
+        input.removeEventListener("click", preventDefault, false);
       }
     }
 
     // Special case type=number
     var number = document.getElementById("input_number");
     number.focus();
     synthesizeKey("a", {});
     number.blur();
--- a/dom/html/test/forms/test_input_event.html
+++ b/dom/html/test/forms/test_input_event.html
@@ -60,17 +60,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   function testUserInput() {
     // Simulating an OK click and with a file name return.
     MockFilePicker.useBlobFile();
     MockFilePicker.returnValue = MockFilePicker.returnOK;
     var input = document.getElementById('fileInput');
     input.focus();
 
     input.addEventListener("input", function (aEvent) {
-      ok(false, "input event doesn't apply to type='file'");
+      ok(true, "input event should have been dispatched on file input.");
     }, false);
 
     input.click();
     setTimeout(testUserInput2, 0);
   }
 
   function testUserInput2() {
     // Some generic checks for types that support the input event.
@@ -130,24 +130,45 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(textareaInput, 2, "input event should have been dispatched");
     textarea.value = 'bar';
     is(textareaInput, 2, "input event should not have been dispatched");
     synthesizeKey("VK_BACK_SPACE", {});
     is(textareaInput, 3, "input event should have been dispatched");
     textarea.blur();
     is(textareaInput, 3, "input event should not have been dispatched");
 
-    // Some dummy tests for types that do not support the input event.
+    // Non-text input tests:
     for (var i = 0; i < NonTextTypes.length; ++i) {
-      input = document.getElementById("input_" + NonTextTypes[i]);
-      input.focus();
-      input.click();
-      is(NonTextInput[i], 0, "input event doesn't apply");
-      input.blur();
-      is(NonTextInput[i], 0, "input event doesn't apply");
+      // Button, submit, image and reset input type tests.
+      if (i < 4) {
+        input = document.getElementById("input_" + NonTextTypes[i]);
+        input.focus();
+        input.click();
+        is(NonTextInput[i], 0, "input event doesn't apply");
+        input.blur();
+        is(NonTextInput[i], 0, "input event doesn't apply");
+      }
+      // For radio and checkboxes, input event should be dispatched.
+      else {
+        input = document.getElementById("input_" + NonTextTypes[i]);
+        input.focus();
+        input.click();
+        is(NonTextInput[i], 1, "input event should have been dispatched");
+        input.blur();
+        is(NonTextInput[i], 1, "input event should not have been dispatched");
+
+        // Test that input event is not dispatched if click event is cancelled.
+        function preventDefault(e) {
+          e.preventDefault();
+        }
+        input.addEventListener("click", preventDefault, false);
+        input.click();
+        is(NonTextInput[i], 1, "input event shouldn't be dispatched if click event is cancelled");
+        input.removeEventListener("click", preventDefault, false);
+      }
     }
 
     // Type changes.
     var input = document.createElement('input');
     input.type = 'text';
     input.value = 'foo';
     input.oninput = function() {
       ok(false, "we shouldn't get an input event when the type changes");
--- a/dom/ipc/ContentBridgeChild.cpp
+++ b/dom/ipc/ContentBridgeChild.cpp
@@ -22,24 +22,24 @@ NS_IMPL_ISUPPORTS(ContentBridgeChild,
                   nsIContentChild)
 
 ContentBridgeChild::ContentBridgeChild(Transport* aTransport)
   : mTransport(aTransport)
 {}
 
 ContentBridgeChild::~ContentBridgeChild()
 {
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport));
+  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
+  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 void
 ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   MessageLoop::current()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy));
 }
 
 /*static*/ ContentBridgeChild*
 ContentBridgeChild::Create(Transport* aTransport, ProcessId aOtherPid)
 {
   RefPtr<ContentBridgeChild> bridge =
     new ContentBridgeChild(aTransport);
--- a/dom/ipc/ContentBridgeParent.cpp
+++ b/dom/ipc/ContentBridgeParent.cpp
@@ -21,28 +21,28 @@ NS_IMPL_ISUPPORTS(ContentBridgeParent,
                   nsIObserver)
 
 ContentBridgeParent::ContentBridgeParent(Transport* aTransport)
   : mTransport(aTransport)
 {}
 
 ContentBridgeParent::~ContentBridgeParent()
 {
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport));
+  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
+  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 void
 ContentBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
     os->RemoveObserver(this, "content-child-shutdown");
   }
   MessageLoop::current()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(this, &ContentBridgeParent::DeferredDestroy));
 }
 
 /*static*/ ContentBridgeParent*
 ContentBridgeParent::Create(Transport* aTransport, ProcessId aOtherPid)
 {
   RefPtr<ContentBridgeParent> bridge =
     new ContentBridgeParent(aTransport);
@@ -164,17 +164,16 @@ ContentBridgeParent::DeallocPBrowserPare
 }
 
 void
 ContentBridgeParent::NotifyTabDestroyed()
 {
   int32_t numLiveTabs = ManagedPBrowserParent().Count();
   if (numLiveTabs == 1) {
     MessageLoop::current()->PostTask(
-      FROM_HERE,
       NewRunnableMethod(this, &ContentBridgeParent::Close));
   }
 }
 
 // This implementation is identical to ContentParent::GetCPOWManager but we can't
 // move it to nsIContentParent because it calls ManagedPJavaScriptParent() which
 // only exists in PContentParent and PContentBridgeParent.
 jsipc::CPOWManager*
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1513,17 +1513,17 @@ ContentChild::RecvBidiKeyboardNotify(con
   // possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
   PuppetBidiKeyboard* bidi = static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
   if (bidi) {
     bidi->SetIsLangRTL(aIsLangRTL);
   }
   return true;
 }
 
-static CancelableTask* sFirstIdleTask;
+static CancelableRunnable* sFirstIdleTask;
 
 static void FirstIdle(void)
 {
   MOZ_ASSERT(sFirstIdleTask);
   sFirstIdleTask = nullptr;
   ContentChild::GetSingleton()->SendFirstIdle();
 }
 
@@ -1594,18 +1594,19 @@ ContentChild::RecvPBrowserConstructor(PB
     os->NotifyObservers(tc, "tab-child-created", nullptr);
   }
 
   static bool hasRunOnce = false;
   if (!hasRunOnce) {
       hasRunOnce = true;
 
     MOZ_ASSERT(!sFirstIdleTask);
-    sFirstIdleTask = NewRunnableFunction(FirstIdle);
-    MessageLoop::current()->PostIdleTask(FROM_HERE, sFirstIdleTask);
+    RefPtr<CancelableRunnable> firstIdleTask = NewRunnableFunction(FirstIdle);
+    sFirstIdleTask = firstIdleTask;
+    MessageLoop::current()->PostIdleTask(firstIdleTask.forget());
 
     // Redo InitProcessAttributes() when the app or browser is really
     // launching so the attributes will be correct.
     mID = aCpID;
     mIsForApp = aIsForApp;
     mIsForBrowser = aIsForBrowser;
     InitProcessAttributes();
   }
@@ -1704,17 +1705,17 @@ ContentChild::RecvNotifyPresentationRece
 
 bool
 ContentChild::RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId)
 {
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   NS_WARN_IF(!service);
 
-  NS_WARN_IF(NS_FAILED(service->UntrackSessionInfo(aSessionId)));
+  NS_WARN_IF(NS_FAILED(service->UntrackSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER)));
 
   return true;
 }
 
 bool
 ContentChild::RecvNotifyGMPsChanged()
 {
   GMPDecoderModule::UpdateUsableCodecs();
@@ -2621,18 +2622,17 @@ ContentChild::RecvAppInit()
   // BrowserElementChild.js.
   if (mIsForApp || mIsForBrowser) {
     PreloadSlowThings();
   }
 
 #ifdef MOZ_NUWA_PROCESS
   if (IsNuwaProcess()) {
     ContentChild::GetSingleton()->RecvGarbageCollect();
-    MessageLoop::current()->PostTask(
-      FROM_HERE, NewRunnableFunction(OnFinishNuwaPreparation));
+    MessageLoop::current()->PostTask(NewRunnableFunction(OnFinishNuwaPreparation));
   }
 #endif
 
   return true;
 }
 
 bool
 ContentChild::RecvLastPrivateDocShellDestroyed()
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -795,18 +795,17 @@ ContentParent::JoinAllSubprocesses()
     printf_stderr("There are no live subprocesses.");
     return;
   }
 
   printf_stderr("Subprocesses are still alive.  Doing emergency join.\n");
 
   bool done = false;
   Monitor monitor("mozilla.dom.ContentParent.JoinAllSubprocesses");
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                   NewRunnableFunction(
+  XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(
                                      &ContentParent::JoinProcessesIOThread,
                                      &processes, &monitor, &done));
   {
     MonitorAutoLock lock(monitor);
     while (!done) {
       lock.Wait();
     }
   }
@@ -2175,17 +2174,16 @@ ContentParent::ActorDestroy(ActorDestroy
     }
     }
   }
 
   // Destroy any processes created by this ContentParent
   for(uint32_t i = 0; i < childIDArray.Length(); i++) {
     ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]);
     MessageLoop::current()->PostTask(
-      FROM_HERE,
       NewRunnableMethod(cp, &ContentParent::ShutDownProcess,
                         SEND_SHUTDOWN_MESSAGE));
   }
   cpm->RemoveContentProcess(this->ChildID());
 
   if (mDriverCrashGuard) {
     mDriverCrashGuard->NotifyCrashed();
   }
@@ -2259,17 +2257,16 @@ ContentParent::NotifyTabDestroyed(const 
   // because of popup windows.  When the last one closes, shut
   // us down.
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID());
   if (tabIds.Length() == 1) {
     // In the case of normal shutdown, send a shutdown message to child to
     // allow it to perform shutdown tasks.
     MessageLoop::current()->PostTask(
-      FROM_HERE,
       NewRunnableMethod(this, &ContentParent::ShutDownProcess,
                         SEND_SHUTDOWN_MESSAGE));
   }
 }
 
 jsipc::CPOWManager*
 ContentParent::GetCPOWManager()
 {
@@ -3624,17 +3621,16 @@ ContentParent::KillHard(const char* aRea
   }
 
   if (mSubprocess) {
     mSubprocess->SetAlreadyDead();
   }
 
   // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
   XRE_GetIOMessageLoop()->PostTask(
-    FROM_HERE,
     NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated,
                         otherProcessHandle, /*force=*/true));
 }
 
 bool
 ContentParent::IsPreallocated() const
 {
   return mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL;
--- a/dom/ipc/NuwaChild.cpp
+++ b/dom/ipc/NuwaChild.cpp
@@ -30,17 +30,17 @@ using namespace mozilla::dom;
 
 namespace mozilla {
 namespace dom {
 
 #ifdef MOZ_NUWA_PROCESS
 
 namespace {
 
-class CallNuwaSpawn: public nsRunnable
+class CallNuwaSpawn: public Runnable
 {
 public:
   NS_IMETHOD Run()
   {
     NuwaSpawn();
     if (IsNuwaProcess()) {
       return NS_OK;
     }
@@ -180,17 +180,17 @@ GetProtoFdInfos(NuwaProtoFdInfo* aInfoLi
   }
 
   if (i > NUWA_TOPLEVEL_MAX) {
     NS_RUNTIMEABORT("Too many top level protocols!");
   }
   *aInfoSize = i;
 }
 
-class RunAddNewIPCProcess : public nsRunnable
+class RunAddNewIPCProcess : public mozilla::Runnable
 {
 public:
   RunAddNewIPCProcess(pid_t aPid,
                       nsTArray<mozilla::ipc::ProtocolFdMapping>& aMaps)
       : mPid(aPid)
   {
     mMaps.SwapElements(aMaps);
   }
--- a/dom/ipc/PreallocatedProcessManager.cpp
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -200,31 +200,29 @@ PreallocatedProcessManagerImpl::Enable()
 void
 PreallocatedProcessManagerImpl::AllocateAfterDelay()
 {
   if (!mEnabled || mPreallocatedAppProcess) {
     return;
   }
 
   MessageLoop::current()->PostDelayedTask(
-    FROM_HERE,
     NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle),
     Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
                          DEFAULT_ALLOCATE_DELAY));
 }
 
 void
 PreallocatedProcessManagerImpl::AllocateOnIdle()
 {
   if (!mEnabled || mPreallocatedAppProcess) {
     return;
   }
 
   MessageLoop::current()->PostIdleTask(
-    FROM_HERE,
     NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow));
 }
 
 void
 PreallocatedProcessManagerImpl::AllocateNow()
 {
   if (!mEnabled || mPreallocatedAppProcess) {
     return;
@@ -240,20 +238,20 @@ PreallocatedProcessManagerImpl::Schedule
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mPreallocateAppProcessTask) {
     // Make sure there is only one request running.
     return;
   }
 
-  mPreallocateAppProcessTask = NewRunnableMethod(
+  RefPtr<CancelableTask> task = NewRunnableMethod(
     this, &PreallocatedProcessManagerImpl::DelayedNuwaFork);
-  MessageLoop::current()->PostDelayedTask(
-    FROM_HERE, mPreallocateAppProcessTask,
+  mPreallocateAppProcessTask = task;
+  MessageLoop::current()->PostDelayedTask(task.forget(),
     Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
                          DEFAULT_ALLOCATE_DELAY));
 }
 
 void
 PreallocatedProcessManagerImpl::DelayedNuwaFork()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -260,17 +260,18 @@ HangMonitorChild::HangMonitorChild(Proce
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 }
 
 HangMonitorChild::~HangMonitorChild()
 {
   // For some reason IPDL doesn't automatically delete the channel for a
   // bridged protocol (bug 1090570). So we have to do it ourselves.
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(GetTransport()));
+  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
+  XRE_GetIOMessageLoop()->PostTask(task.forget());
 
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sInstance == this);
   sInstance = nullptr;
 }
 
 void
 HangMonitorChild::Shutdown()
@@ -298,17 +299,16 @@ HangMonitorChild::ActorDestroy(ActorDest
 {
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
 
   mIPCOpen = false;
 
   // We use a task here to ensure that IPDL is finished with this
   // HangMonitorChild before it gets deleted on the main thread.
   MonitorLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(this, &HangMonitorChild::ShutdownOnThread));
 }
 
 bool
 HangMonitorChild::RecvTerminateScript()
 {
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
 
@@ -386,17 +386,16 @@ HangMonitorChild::NotifySlowScript(nsITa
   TabId id;
   if (aTabChild) {
     RefPtr<TabChild> tabChild = static_cast<TabChild*>(aTabChild);
     id = tabChild->GetTabId();
   }
   nsAutoCString filename(aFileName);
 
   MonitorLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(this, &HangMonitorChild::NotifySlowScriptAsync,
                       id, filename, aLineNo));
   return SlowScriptAction::Continue;
 }
 
 bool
 HangMonitorChild::IsDebuggerStartupComplete()
 {
@@ -417,17 +416,16 @@ HangMonitorChild::NotifyPluginHang(uint3
 {
   // main thread in the child
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   mSentReport = true;
 
   // bounce to background thread
   MonitorLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(this,
                       &HangMonitorChild::NotifyPluginHangAsync,
                       aPluginId));
 }
 
 void
 HangMonitorChild::NotifyPluginHangAsync(uint32_t aPluginId)
 {
@@ -443,17 +441,16 @@ HangMonitorChild::NotifyPluginHangAsync(
 void
 HangMonitorChild::ClearHang()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mSentReport) {
     // bounce to background thread
     MonitorLoop()->PostTask(
-      FROM_HERE,
       NewRunnableMethod(this, &HangMonitorChild::ClearHangAsync));
 
     MonitorAutoLock lock(mMonitor);
     mSentReport = false;
     mTerminateScript = false;
     mStartDebugger = false;
     mFinishedStartingDebugger = false;
   }
@@ -482,17 +479,18 @@ HangMonitorParent::HangMonitorParent(Pro
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   mReportHangs = mozilla::Preferences::GetBool("dom.ipc.reportProcessHangs", false);
 }
 
 HangMonitorParent::~HangMonitorParent()
 {
   // For some reason IPDL doesn't automatically delete the channel for a
   // bridged protocol (bug 1090570). So we have to do it ourselves.
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(GetTransport()));
+  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
+  XRE_GetIOMessageLoop()->PostTask(task.forget());
 
 #ifdef MOZ_CRASHREPORTER
   MutexAutoLock lock(mBrowserCrashDumpHashLock);
 
   for (auto iter = mBrowserCrashDumpIds.Iter(); !iter.Done(); iter.Next()) {
     nsString crashId = iter.UserData();
     if (!crashId.IsEmpty()) {
       CrashReporter::DeleteMinidumpFilesForID(crashId);
@@ -509,17 +507,16 @@ HangMonitorParent::Shutdown()
   MonitorAutoLock lock(mMonitor);
 
   if (mProcess) {
     mProcess->Clear();
     mProcess = nullptr;
   }
 
   MonitorLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(this, &HangMonitorParent::ShutdownOnThread));
 
   while (!mShutdownDone) {
     mMonitor.Wait();
   }
 }
 
 void
@@ -831,17 +828,16 @@ HangMonitoredProcess::TerminateScript()
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!mActor) {
     return NS_ERROR_UNEXPECTED;
   }
 
   ProcessHangMonitor::Get()->MonitorLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(mActor, &HangMonitorParent::TerminateScript));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HangMonitoredProcess::BeginStartingDebugger()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
@@ -849,17 +845,16 @@ HangMonitoredProcess::BeginStartingDebug
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!mActor) {
     return NS_ERROR_UNEXPECTED;
   }
 
   ProcessHangMonitor::Get()->MonitorLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(mActor, &HangMonitorParent::BeginStartingDebugger));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HangMonitoredProcess::EndStartingDebugger()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
@@ -867,17 +862,16 @@ HangMonitoredProcess::EndStartingDebugge
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!mActor) {
     return NS_ERROR_UNEXPECTED;
   }
 
   ProcessHangMonitor::Get()->MonitorLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(mActor, &HangMonitorParent::EndStartingDebugger));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HangMonitoredProcess::TerminatePlugin()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
@@ -1047,34 +1041,32 @@ mozilla::CreateHangMonitorParent(Content
 
   ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
   HangMonitorParent* parent = new HangMonitorParent(monitor);
 
   HangMonitoredProcess* process = new HangMonitoredProcess(parent, aContentParent);
   parent->SetProcess(process);
 
   monitor->MonitorLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(parent, &HangMonitorParent::Open,
                       aTransport, aOtherPid, XRE_GetIOMessageLoop()));
 
   return parent;
 }
 
 PProcessHangMonitorChild*
 mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport,
                                 base::ProcessId aOtherPid)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
   HangMonitorChild* child = new HangMonitorChild(monitor);
 
   monitor->MonitorLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(child, &HangMonitorChild::Open,
                       aTransport, aOtherPid, XRE_GetIOMessageLoop()));
 
   return child;
 }
 
 MessageLoop*
 ProcessHangMonitor::MonitorLoop()
--- a/dom/json/nsJSON.cpp
+++ b/dom/json/nsJSON.cpp
@@ -407,17 +407,16 @@ nsJSON::DecodeInternal(JSContext* cx,
   if (!mURI) {
     NS_NewURI(getter_AddRefs(mURI), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
     if (!mURI)
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsresult rv;
   nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
-  NS_ENSURE_TRUE(nullPrincipal, NS_ERROR_FAILURE);
 
   // The ::Decode function is deprecated [Bug 675797] and the following
   // channel is never openend, so it does not matter what securityFlags
   // we pass to NS_NewInputStreamChannel here.
   rv = NS_NewInputStreamChannel(getter_AddRefs(jsonChannel),
                                 mURI,
                                 aStream,
                                 nullPrincipal,
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -413,17 +413,16 @@ nsresult nsJSChannel::Init(nsIURI *aURI)
     mIOThunk = new nsJSThunk();
 
     // Create a stock input stream channel...
     // Remember, until AsyncOpen is called, the script will not be evaluated
     // and the underlying Input Stream will not be created...
     nsCOMPtr<nsIChannel> channel;
 
     nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
-    NS_ENSURE_TRUE(nullPrincipal, NS_ERROR_FAILURE);
 
     // If the resultant script evaluation actually does return a value, we
     // treat it as html.
     // The following channel is never openend, so it does not matter what
     // securityFlags we pass; let's follow the principle of least privilege.
     rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
                                   aURI,
                                   mIOThunk,
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -52,17 +52,16 @@ FormValidationStepMismatchOneValue=Pleas
 FormValidationBadInputNumber=Please enter a number.
 GetAttributeNodeWarning=Use of getAttributeNode() is deprecated. Use getAttribute() instead.
 SetAttributeNodeWarning=Use of setAttributeNode() is deprecated. Use setAttribute() instead.
 GetAttributeNodeNSWarning=Use of getAttributeNodeNS() is deprecated. Use getAttributeNS() instead.
 SetAttributeNodeNSWarning=Use of setAttributeNodeNS() is deprecated. Use setAttributeNS() instead.
 RemoveAttributeNodeWarning=Use of removeAttributeNode() is deprecated. Use removeAttribute() instead.
 CreateAttributeWarning=Use of document.createAttribute() is deprecated. Use element.setAttribute() instead.
 CreateAttributeNSWarning=Use of document.createAttributeNS() is deprecated. Use element.setAttributeNS() instead.
-OwnerElementWarning=Use of attributes' ownerElement attribute is deprecated.
 NodeValueWarning=Use of attributes' nodeValue attribute is deprecated. Use value instead.
 TextContentWarning=Use of attributes' textContent attribute is deprecated. Use value instead.
 EnablePrivilegeWarning=Use of enablePrivilege is deprecated.  Please use code that runs with the system principal (e.g. an extension) instead.
 nsIJSONDecodeDeprecatedWarning=nsIJSON.decode is deprecated.  Please use JSON.parse instead.
 nsIJSONEncodeDeprecatedWarning=nsIJSON.encode is deprecated.  Please use JSON.stringify instead.
 nsIDOMWindowInternalWarning=Use of nsIDOMWindowInternal is deprecated. Use nsIDOMWindow instead.
 FullscreenDeniedDisabled=Request for fullscreen was denied because Fullscreen API is disabled by user preference.
 FullscreenDeniedFocusedPlugin=Request for fullscreen was denied because a windowed plugin is focused.
--- a/dom/media/DecoderDoctorDiagnostics.cpp
+++ b/dom/media/DecoderDoctorDiagnostics.cpp
@@ -4,19 +4,21 @@
  * 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 "DecoderDoctorDiagnostics.h"
 
 #include "mozilla/dom/DecoderDoctorNotificationBinding.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
+#include "nsContentUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"
 #include "nsIObserverService.h"
+#include "nsIScriptError.h"
 #include "nsITimer.h"
 #include "nsIWeakReference.h"
 #include "nsPluginHost.h"
 
 static mozilla::LazyLogModule sDecoderDoctorLog("DecoderDoctor");
 #define DD_LOG(level, arg, ...) MOZ_LOG(sDecoderDoctorLog, level, (arg, ##__VA_ARGS__))
 #define DD_DEBUG(arg, ...) DD_LOG(mozilla::LogLevel::Debug, arg, ##__VA_ARGS__)
 #define DD_INFO(arg, ...) DD_LOG(mozilla::LogLevel::Info, arg, ##__VA_ARGS__)
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -253,18 +253,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   mEstimatedDuration(mTaskQueue, NullableTimeUnit(),
                     "MediaDecoderStateMachine::mEstimatedDuration (Mirror)"),
   mExplicitDuration(mTaskQueue, Maybe<double>(),
                     "MediaDecoderStateMachine::mExplicitDuration (Mirror)"),
   mPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_LOADING,
              "MediaDecoderStateMachine::mPlayState (Mirror)"),
   mNextPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_PAUSED,
                  "MediaDecoderStateMachine::mNextPlayState (Mirror)"),
-  mLogicallySeeking(mTaskQueue, false,
-                    "MediaDecoderStateMachine::mLogicallySeeking (Mirror)"),
   mVolume(mTaskQueue, 1.0, "MediaDecoderStateMachine::mVolume (Mirror)"),
   mLogicalPlaybackRate(mTaskQueue, 1.0,
                        "MediaDecoderStateMachine::mLogicalPlaybackRate (Mirror)"),
   mPreservesPitch(mTaskQueue, true,
                   "MediaDecoderStateMachine::mPreservesPitch (Mirror)"),
   mSameOriginMedia(mTaskQueue, false,
                    "MediaDecoderStateMachine::mSameOriginMedia (Mirror)"),
   mMediaPrincipalHandle(mTaskQueue, PRINCIPAL_HANDLE_NONE,
@@ -326,17 +324,16 @@ MediaDecoderStateMachine::Initialization
   MOZ_ASSERT(OnTaskQueue());
 
   // Connect mirrors.
   mBuffered.Connect(mReader->CanonicalBuffered());
   mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration());
   mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
   mPlayState.Connect(aDecoder->CanonicalPlayState());
   mNextPlayState.Connect(aDecoder->CanonicalNextPlayState());
-  mLogicallySeeking.Connect(aDecoder->CanonicalLogicallySeeking());
   mVolume.Connect(aDecoder->CanonicalVolume());
   mLogicalPlaybackRate.Connect(aDecoder->CanonicalPlaybackRate());
   mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch());
   mSameOriginMedia.Connect(aDecoder->CanonicalSameOriginMedia());
   mMediaPrincipalHandle.Connect(aDecoder->CanonicalMediaPrincipalHandle());
   mPlaybackBytesPerSecond.Connect(aDecoder->CanonicalPlaybackBytesPerSecond());
   mPlaybackRateReliable.Connect(aDecoder->CanonicalPlaybackRateReliable());
   mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition());
@@ -350,17 +347,16 @@ MediaDecoderStateMachine::Initialization
   mWatchManager.Watch(mVideoCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
   mWatchManager.Watch(mEstimatedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mExplicitDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mObservedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
-  mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
 }
 
 media::MediaSink*
 MediaDecoderStateMachine::CreateAudioSink()
 {
   RefPtr<MediaDecoderStateMachine> self = this;
   auto audioSinkCreator = [self] () {
     MOZ_ASSERT(self->OnTaskQueue());
@@ -1304,22 +1300,16 @@ void MediaDecoderStateMachine::PlayState
   // when the state machine notices the decoder's state change to PLAYING.
   if (mState == DECODER_STATE_BUFFERING) {
     StartDecoding();
   }
 
   ScheduleStateMachine();
 }
 
-void MediaDecoderStateMachine::LogicallySeekingChanged()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  ScheduleStateMachine();
-}
-
 void MediaDecoderStateMachine::BufferedRangeUpdated()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // While playing an unseekable stream of unknown duration, mObservedDuration
   // is updated (in AdvanceFrame()) as we play. But if data is being downloaded
   // faster than played, mObserved won't reflect the end of playable data
   // since we haven't played the frame at the end of buffered data. So update
@@ -2069,17 +2059,16 @@ MediaDecoderStateMachine::FinishShutdown
   mMetadataManager.Disconnect();
 
   // Disconnect canonicals and mirrors before shutting down our task queue.
   mBuffered.DisconnectIfConnected();
   mEstimatedDuration.DisconnectIfConnected();
   mExplicitDuration.DisconnectIfConnected();
   mPlayState.DisconnectIfConnected();
   mNextPlayState.DisconnectIfConnected();
-  mLogicallySeeking.DisconnectIfConnected();
   mVolume.DisconnectIfConnected();
   mLogicalPlaybackRate.DisconnectIfConnected();
   mPreservesPitch.DisconnectIfConnected();
   mSameOriginMedia.DisconnectIfConnected();
   mMediaPrincipalHandle.DisconnectIfConnected();
   mPlaybackBytesPerSecond.DisconnectIfConnected();
   mPlaybackRateReliable.DisconnectIfConnected();
   mDecoderPosition.DisconnectIfConnected();
@@ -2133,17 +2122,16 @@ nsresult MediaDecoderStateMachine::RunSt
         StopPlayback();
       }
 
       // Start playback if necessary so that the clock can be properly queried.
       MaybeStartPlayback();
 
       UpdatePlaybackPositionPeriodically();
       NS_ASSERTION(!IsPlaying() ||
-                   mLogicallySeeking ||
                    IsStateMachineScheduled(),
                    "Must have timer scheduled");
       return NS_OK;
     }
 
     case DECODER_STATE_BUFFERING: {
       TimeStamp now = TimeStamp::Now();
       NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
@@ -2199,17 +2187,16 @@ nsresult MediaDecoderStateMachine::RunSt
       // once to ensure the current playback position is advanced to the
       // end of the media, and so that we update the readyState.
       if ((HasVideo() && !mVideoCompleted) ||
           (HasAudio() && !mAudioCompleted)) {
         // Start playback if necessary to play the remaining media.
         MaybeStartPlayback();
         UpdatePlaybackPositionPeriodically();
         NS_ASSERTION(!IsPlaying() ||
-                     mLogicallySeeking ||
                      IsStateMachineScheduled(),
                      "Must have timer scheduled");
         return NS_OK;
       }
 
       // StopPlayback in order to reset the IsPlaying() state so audio
       // is restarted correctly.
       StopPlayback();
@@ -2286,17 +2273,17 @@ MediaDecoderStateMachine::GetClock(TimeS
   return clockTime;
 }
 
 void
 MediaDecoderStateMachine::UpdatePlaybackPositionPeriodically()
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  if (!IsPlaying() || mLogicallySeeking) {
+  if (!IsPlaying()) {
     return;
   }
 
   if (mAudioCaptured) {
     DiscardStreamData();
   }
 
   // Cap the current time to the larger of the audio and video end time.
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -462,19 +462,16 @@ protected:
   // Create and start the media sink.
   // The decoder monitor must be held with exactly one lock count.
   // Called on the state machine thread.
   void StartMediaSink();
 
   // Notification method invoked when mPlayState changes.
   void PlayStateChanged();
 
-  // Notification method invoked when mLogicallySeeking changes.
-  void LogicallySeekingChanged();
-
   // Sets internal state which causes playback of media to pause.
   // The decoder monitor must be held.
   void StopPlayback();
 
   // If the conditions are right, sets internal state which causes playback
   // of media to begin or resume.
   // Must be called with the decode monitor held.
   void MaybeStartPlayback();
@@ -975,17 +972,16 @@ private:
   Mirror<media::NullableTimeUnit> mEstimatedDuration;
 
   // The duration explicitly set by JS, mirrored from the main thread.
   Mirror<Maybe<double>> mExplicitDuration;
 
   // The current play state and next play state, mirrored from the main thread.
   Mirror<MediaDecoder::PlayState> mPlayState;
   Mirror<MediaDecoder::PlayState> mNextPlayState;
-  Mirror<bool> mLogicallySeeking;
 
   // Volume of playback. 0.0 = muted. 1.0 = full volume.
   Mirror<double> mVolume;
 
   // TODO: The separation between mPlaybackRate and mLogicalPlaybackRate is a
   // kludge to preserve existing fragile logic while converting this setup to
   // state-mirroring. Some hero should clean this up.
   Mirror<double> mLogicalPlaybackRate;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -298,24 +298,31 @@ MediaFormatReader::OnDemuxerInitDone(nsr
 
   bool audioActive = !!mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
   if (audioActive) {
     mAudio.mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
     if (!mAudio.mTrackDemuxer) {
       mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
       return;
     }
-    mInfo.mAudio = *mAudio.mTrackDemuxer->GetInfo()->GetAsAudioInfo();
-    UniquePtr<TrackInfo> info(mAudio.mTrackDemuxer->GetInfo());
-    for (const MetadataTag& tag : info->mTags) {
-      tags->Put(tag.mKey, tag.mValue);
+    UniquePtr<TrackInfo> audioInfo = mAudio.mTrackDemuxer->GetInfo();
+    // We actively ignore audio tracks that we know we can't play.
+    audioActive = audioInfo && audioInfo->IsValid();
+    if (audioActive) {
+      mInfo.mAudio = *audioInfo->GetAsAudioInfo();
+      for (const MetadataTag& tag : audioInfo->mTags) {
+        tags->Put(tag.mKey, tag.mValue);
+      }
+      mAudio.mCallback = new DecoderCallback(this, TrackInfo::kAudioTrack);
+      mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered();
+      mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock();
+    } else {
+      mAudio.mTrackDemuxer->BreakCycles();
+      mAudio.mTrackDemuxer = nullptr;
     }
-    mAudio.mCallback = new DecoderCallback(this, TrackInfo::kAudioTrack);
-    mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered();
-    mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock();
   }
 
   UniquePtr<EncryptionInfo> crypto = mDemuxer->GetCrypto();
 
   mIsEncrypted = crypto && crypto->IsEncrypted();
 
   if (mDecoder && crypto && crypto->IsEncrypted()) {
 #ifdef MOZ_EME
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -29,16 +29,19 @@ public:
               const nsACString& aValue)
     : mKey(aKey)
     , mValue(aValue)
   {}
   nsCString mKey;
   nsCString mValue;
 };
 
+  // Maximum channel number we can currently handle (7.1)
+#define MAX_AUDIO_CHANNELS 8
+
 class TrackInfo {
 public:
   enum TrackType {
     kUndefinedTrack,
     kAudioTrack,
     kVideoTrack,
     kTextTrack
   };
@@ -299,17 +302,17 @@ public:
     , mExtendedProfile(aOther.mExtendedProfile)
     , mCodecSpecificConfig(aOther.mCodecSpecificConfig)
     , mExtraData(aOther.mExtraData)
   {
   }
 
   bool IsValid() const override
   {
-    return mChannels > 0 && mRate > 0;
+    return mChannels > 0 && mChannels <= MAX_AUDIO_CHANNELS && mRate > 0;
   }
 
   AudioInfo* GetAsAudioInfo() override
   {
     return this;
   }
 
   const AudioInfo* GetAsAudioInfo() const override
@@ -511,19 +514,16 @@ private:
   UniquePtr<TrackInfo> mInfo;
   // A unique ID, guaranteed to change when changing streams.
   uint32_t mStreamSourceID;
 
 public:
   const nsCString& mMimeType;
 };
 
-// Maximum channel number we can currently handle (7.1)
-#define MAX_AUDIO_CHANNELS 8
-
 class AudioConfig {
 public:
   enum Channel {
     CHANNEL_INVALID = -1,
     CHANNEL_MONO = 0,
     CHANNEL_LEFT,
     CHANNEL_RIGHT,
     CHANNEL_CENTER,
@@ -653,16 +653,21 @@ public:
       mRate == aOther.mRate && mFormat == aOther.mFormat &&
       mInterleaved == aOther.mInterleaved;
   }
   bool operator!=(const AudioConfig& aOther) const
   {
     return !(*this == aOther);
   }
 
+  bool IsValid() const
+  {
+    return mChannelLayout.IsValid() && Format() != FORMAT_NONE && Rate() > 0;
+  }
+
   static const char* FormatToString(SampleFormat aFormat);
   static uint32_t SampleSize(SampleFormat aFormat);
   static uint32_t FormatToBits(SampleFormat aFormat);
 
 private:
   // Channels configuration.
   ChannelLayout mChannelLayout;
 
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -241,17 +241,17 @@ HostHasPermission(nsIURI &docURI)
   } while (end < domainWhiteList.Length());
 
   return false;
 }
 
 // Generic class for running long media operations like Start off the main
 // thread, and then (because nsDOMMediaStreams aren't threadsafe),
 // ProxyReleases mStream since it's cycle collected.
-class MediaOperationTask : public Task
+class MediaOperationTask : public Runnable
 {
 public:
   // so we can send Stop without AddRef()ing from the MSG thread
   MediaOperationTask(MediaOperation aType,
     GetUserMediaCallbackMediaStreamListener* aListener,
     DOMMediaStream* aStream,
     OnTracksAvailableCallback* aOnTracksAvailableCallback,
     AudioDevice* aAudioDevice,
@@ -275,45 +275,45 @@ public:
   ~MediaOperationTask()
   {
     // MediaStreams can be released on any thread.
   }
 
   void
   ReturnCallbackError(nsresult rv, const char* errorLog);
 
-  void
-  Run()
+  NS_IMETHOD
+  Run() override
   {
     SourceMediaStream *source = mListener->GetSourceStream();
     // No locking between these is required as all the callbacks for the
     // same MediaStream will occur on the same thread.
     if (!source) // means the stream was never Activated()
-      return;
+      return NS_OK;
 
     switch (mType) {
       case MEDIA_START:
         {
           NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
           nsresult rv;
 
           if (mAudioDevice) {
             rv = mAudioDevice->GetSource()->Start(source, kAudioTrack,
                                                   mListener->GetPrincipalHandle());
             if (NS_FAILED(rv)) {
               ReturnCallbackError(rv, "Starting audio failed");
-              return;
+              return NS_OK;
             }
           }
           if (mVideoDevice) {
             rv = mVideoDevice->GetSource()->Start(source, kVideoTrack,
                                                   mListener->GetPrincipalHandle());
             if (NS_FAILED(rv)) {
               ReturnCallbackError(rv, "Starting video failed");
-              return;
+              return NS_OK;
             }
           }
           // Start() queued the tracks to be added synchronously to avoid races
           source->FinishAddTracks();
 
           source->SetPullEnabled(true);
           source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
 
@@ -372,16 +372,18 @@ public:
           }
         }
         break;
 
       default:
         MOZ_ASSERT(false,"invalid MediaManager operation");
         break;
     }
+
+    return NS_OK;
   }
 
 private:
   MediaOperation mType;
   RefPtr<DOMMediaStream> mStream;
   nsAutoPtr<OnTracksAvailableCallback> mOnTracksAvailableCallback;
   RefPtr<AudioDevice> mAudioDevice; // threadsafe
   RefPtr<VideoDevice> mVideoDevice; // threadsafe
@@ -905,17 +907,17 @@ public:
         RefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
         const MediaSourceEnum mSource;
         const TrackID mTrackID;
         const RefPtr<const PeerIdentity> mPeerIdentity;
       };
 
       nsCOMPtr<nsIPrincipal> principal;
       if (mPeerIdentity) {
-        principal = nsNullPrincipal::Create();
+        principal = nsNullPrincipal::CreateWithInheritedAttributes(window->GetExtantDoc()->NodePrincipal());
       } else {
         principal = window->GetExtantDoc()->NodePrincipal();
       }
 
       // Normal case, connect the source stream to the track union stream to
       // avoid us blocking. Pass a simple TrackSourceGetter for potential
       // fake tracks. Apart from them gUM never adds tracks dynamically.
       domStream =
@@ -969,21 +971,22 @@ public:
     // Note: includes JS callbacks; must be released on MainThread
     TracksAvailableCallback* tracksAvailableCallback =
       new TracksAvailableCallback(mManager, mOnSuccess, mWindowID, domStream);
 
     // Dispatch to the media thread to ask it to start the sources,
     // because that can take a while.
     // Pass ownership of domStream to the MediaOperationTask
     // to ensure it's kept alive until the MediaOperationTask runs (at least).
-    MediaManager::PostTask(FROM_HERE,
+    RefPtr<Runnable> mediaOperation =
         new MediaOperationTask(MEDIA_START, mListener, domStream,
                                tracksAvailableCallback,
                                mAudioDevice, mVideoDevice,
-                               false, mWindowID, mOnFailure.forget()));
+                               false, mWindowID, mOnFailure.forget());
+    MediaManager::PostTask(mediaOperation.forget());
     // We won't need mOnFailure now.
     mOnFailure = nullptr;
 
     if (!MediaManager::IsPrivateBrowsing(window)) {
       // Call GetOriginKey again, this time w/persist = true, to promote
       // deviceIds to persistent, in case they're not already. Fire'n'forget.
       RefPtr<Pledge<nsCString>> p = media::GetOriginKey(mOrigin, false, true);
     }
@@ -1063,18 +1066,17 @@ MediaManager::SelectSettings(
 {
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<PledgeChar> p = new PledgeChar();
   uint32_t id = mOutstandingCharPledges.Append(*p);
 
   // Algorithm accesses device capabilities code and must run on media thread.
   // Modifies passed-in aSources.
 
-  MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, aConstraints,
-                                                 aSources]() mutable {
+  MediaManager::PostTask(NewTaskFrom([id, aConstraints, aSources]() mutable {
     auto& sources = **aSources;
 
     // Since the advanced part of the constraints algorithm needs to know when
     // a candidate set is overconstrained (zero members), we must split up the
     // list into videos and audios, and put it back together again at the end.
 
     nsTArray<RefPtr<VideoDevice>> videos;
     nsTArray<RefPtr<AudioDevice>> audios;
@@ -1100,37 +1102,37 @@ MediaManager::SelectSettings(
     }
     if (audios.Length() && IsOn(aConstraints.mAudio)) {
       badConstraint = MediaConstraintsHelper::SelectSettings(
           GetInvariant(aConstraints.mAudio), audios);
       for (auto& audio : audios) {
         sources.AppendElement(audio);
       }
     }
-    NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, badConstraint]() mutable {
+    NS_DispatchToMainThread(NewRunnableFrom([id, badConstraint]() mutable {
       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
       RefPtr<PledgeChar> p = mgr->mOutstandingCharPledges.Remove(id);
       if (p) {
         p->Resolve(badConstraint);
       }
       return NS_OK;
-    })));
+    }));
   }));
   return p.forget();
 }
 
 /**
  * Runs on a seperate thread and is responsible for enumerating devices.
  * Depending on whether a picture or stream was asked for, either
  * ProcessGetUserMedia is called, and the results are sent back to the DOM.
  *
  * Do not run this on the main thread. The success and error callbacks *MUST*
  * be dispatched on the main thread!
  */
-class GetUserMediaTask : public Task
+class GetUserMediaTask : public Runnable
 {
 public:
   GetUserMediaTask(
     const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aOnSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
     uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
     MediaEnginePrefs &aPrefs,
@@ -1163,18 +1165,18 @@ public:
     MOZ_ASSERT(!mOnSuccess);
     MOZ_ASSERT(!mOnFailure);
 
     NS_DispatchToMainThread(runnable.forget());
     // Do after ErrorCallbackRunnable Run()s, as it checks active window list
     NS_DispatchToMainThread(do_AddRef(new GetUserMediaListenerRemove(mWindowID, mListener)));
   }
 
-  void
-  Run()
+  NS_IMETHOD
+  Run() override
   {
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(mOnSuccess);
     MOZ_ASSERT(mOnFailure);
     MOZ_ASSERT(mDeviceChosen);
 
     // Allocate a video or audio device and return a MediaStream via
     // a GetUserMediaStreamRunnable.
@@ -1183,43 +1185,44 @@ public:
 
     if (mAudioDevice) {
       rv = mAudioDevice->Allocate(GetInvariant(mConstraints.mAudio),
                                   mPrefs, mOrigin);
       if (NS_FAILED(rv)) {
         LOG(("Failed to allocate audiosource %d",rv));
         Fail(NS_LITERAL_STRING("SourceUnavailableError"),
              NS_LITERAL_STRING("Failed to allocate audiosource"));
-        return;
+        return NS_OK;
       }
     }
     if (mVideoDevice) {
       rv = mVideoDevice->Allocate(GetInvariant(mConstraints.mVideo),
                                   mPrefs, mOrigin);
       if (NS_FAILED(rv)) {
         LOG(("Failed to allocate videosource %d\n",rv));
         if (mAudioDevice) {
           mAudioDevice->GetSource()->Deallocate();
         }
         Fail(NS_LITERAL_STRING("SourceUnavailableError"),
              NS_LITERAL_STRING("Failed to allocate videosource"));
-        return;
+        return NS_OK;
       }
     }
     PeerIdentity* peerIdentity = nullptr;
     if (!mConstraints.mPeerIdentity.IsEmpty()) {
       peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
     }
 
     NS_DispatchToMainThread(do_AddRef(
         new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID,
                                        mListener, mOrigin, mAudioDevice,
                                        mVideoDevice, peerIdentity)));
     MOZ_ASSERT(!mOnSuccess);
     MOZ_ASSERT(!mOnFailure);
+    return NS_OK;
   }
 
   nsresult
   Denied(const nsAString& aName,
          const nsAString& aMessage = EmptyString())
   {
     MOZ_ASSERT(mOnSuccess);
     MOZ_ASSERT(mOnFailure);
@@ -1301,17 +1304,17 @@ public:
   // This object must take ownership of task
   GetUserMediaRunnableWrapper(GetUserMediaTask* task) :
     mTask(task) {
   }
 
   ~GetUserMediaRunnableWrapper() {
   }
 
-  NS_IMETHOD Run() {
+  NS_IMETHOD Run() override {
     mTask->Run();
     return NS_OK;
   }
 
 private:
   nsAutoPtr<GetUserMediaTask> mTask;
 };
 #endif
@@ -1346,20 +1349,20 @@ MediaManager::EnumerateRawDevices(uint64
     }
   }
 
   if (!aFake) {
     // Fake tracks only make sense when we have a fake stream.
     aFakeTracks = false;
   }
 
-  MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, aWindowId, audioLoopDev,
-                                                 videoLoopDev, aVideoType,
-                                                 aAudioType, aFake,
-                                                 aFakeTracks]() mutable {
+  MediaManager::PostTask(NewTaskFrom([id, aWindowId, audioLoopDev,
+                                      videoLoopDev, aVideoType,
+                                      aAudioType, aFake,
+                                      aFakeTracks]() mutable {
     // Only enumerate what's asked for, and only fake cams and mics.
     bool hasVideo = aVideoType != MediaSourceEnum::Other;
     bool hasAudio = aAudioType != MediaSourceEnum::Other;
     bool fakeCams = aFake && aVideoType == MediaSourceEnum::Camera;
     bool fakeMics = aFake && aAudioType == MediaSourceEnum::Microphone;
 
     RefPtr<MediaEngine> fakeBackend, realBackend;
     if (fakeCams || fakeMics) {
@@ -1384,28 +1387,28 @@ MediaManager::EnumerateRawDevices(uint64
       nsTArray<RefPtr<AudioDevice>> audios;
       GetSources(fakeMics? fakeBackend : realBackend, aAudioType,
                  &MediaEngine::EnumerateAudioDevices, audios, audioLoopDev);
       for (auto& source : audios) {
         result->AppendElement(source);
       }
     }
     SourceSet* handoff = result.release();
-    NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, handoff]() mutable {
+    NS_DispatchToMainThread(NewRunnableFrom([id, handoff]() mutable {
       UniquePtr<SourceSet> result(handoff); // grab result
       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
       if (!mgr) {
         return NS_OK;
       }
       RefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
       if (p) {
         p->Resolve(result.release());
       }
       return NS_OK;
-    })));
+    }));
   }));
   return p.forget();
 }
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mBackend(nullptr) {
   mPrefs.mFreq         = 1000; // 1KHz test tone
@@ -1593,26 +1596,29 @@ MediaManager::StartupInit()
       free(pAdapterInfo);
     }
   }
 #endif
 }
 
 /* static */
 void
-MediaManager::PostTask(const tracked_objects::Location& from_here, Task* task)
+MediaManager::PostTask(already_AddRefed<Runnable> task)
 {
   if (sInShutdown) {
     // Can't safely delete task here since it may have items with specific
     // thread-release requirements.
+    // XXXkhuey well then who is supposed to delete it?! We don't signal
+    // that we failed ...
+    MOZ_CRASH();
     return;
   }
   NS_ASSERTION(Get(), "MediaManager singleton?");
   NS_ASSERTION(Get()->mMediaThread, "No thread yet");
-  Get()->mMediaThread->message_loop()->PostTask(from_here, task);
+  Get()->mMediaThread->message_loop()->PostTask(Move(task));
 }
 
 /* static */ nsresult
 MediaManager::NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow,
                                           const nsString& aMsg,
                                           const bool& aIsAudio,
                                           const bool& aIsVideo)
 {
@@ -2118,21 +2124,21 @@ MediaManager::GetUserMedia(nsPIDOMWindow
           rv = devicesCopy->AppendElement(device);
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return;
           }
         }
       }
 
       // Pass callbacks and MediaStreamListener along to GetUserMediaTask.
-      nsAutoPtr<GetUserMediaTask> task (new GetUserMediaTask(c, onSuccess.forget(),
-                                                             onFailure.forget(),
-                                                             windowID, listener,
-                                                             prefs, origin,
-                                                             devices->release()));
+      RefPtr<GetUserMediaTask> task (new GetUserMediaTask(c, onSuccess.forget(),
+                                                          onFailure.forget(),
+                                                          windowID, listener,
+                                                          prefs, origin,
+                                                          devices->release()));
       // Store the task w/callbacks.
       mActiveCallbacks.Put(callID, task.forget());
 
       // Add a WindowID cross-reference so OnNavigation can tear things down
       nsTArray<nsString>* array;
       if (!mCallIds.Get(windowID, &array)) {
         array = new nsTArray<nsString>();
         mCallIds.Put(windowID, array);
@@ -2370,19 +2376,19 @@ MediaManager::GetUserMediaDevices(nsPIDO
   // Ignore passed-in constraints, instead locate + return already-constrained list.
 
   nsTArray<nsString>* callIDs;
   if (!mCallIds.Get(aWindowId, &callIDs)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   for (auto& callID : *callIDs) {
-    GetUserMediaTask* task;
+    RefPtr<GetUserMediaTask> task;
     if (!aCallID.Length() || aCallID == callID) {
-      if (mActiveCallbacks.Get(callID, &task)) {
+      if (mActiveCallbacks.Get(callID, getter_AddRefs(task))) {
         nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*task->mSourceSet);
         onSuccess->OnSuccess(array);
         return NS_OK;
       }
     }
   }
   return NS_ERROR_UNEXPECTED;
 }
@@ -2611,25 +2617,25 @@ MediaManager::Shutdown()
   mCallIds.Clear();
 #ifdef MOZ_WEBRTC
   StopWebRtcLog();
 #endif
 
   // Because mMediaThread is not an nsThread, we must dispatch to it so it can
   // clean up BackgroundChild. Continue stopping thread once this is done.
 
-  class ShutdownTask : public Task
+  class ShutdownTask : public Runnable
   {
   public:
     ShutdownTask(MediaManager* aManager,
-                 Runnable* aReply)
+                 already_AddRefed<Runnable> aReply)
       : mManager(aManager)
       , mReply(aReply) {}
   private:
-    void
+    NS_IMETHOD
     Run() override
     {
       LOG(("MediaManager Thread Shutdown"));
       MOZ_ASSERT(MediaManager::IsInMediaThread());
       // Must shutdown backend on MediaManager thread, since that's where we started it from!
       {
         if (mManager->mBackend) {
           mManager->mBackend->Shutdown(); // ok to invoke multiple times
@@ -2637,49 +2643,52 @@ MediaManager::Shutdown()
       }
       mozilla::ipc::BackgroundChild::CloseForCurrentThread();
       // must explicitly do this before dispatching the reply, since the reply may kill us with Stop()
       mManager->mBackend = nullptr; // last reference, will invoke Shutdown() again
 
       if (NS_FAILED(NS_DispatchToMainThread(mReply.forget()))) {
         LOG(("Will leak thread: DispatchToMainthread of reply runnable failed in MediaManager shutdown"));
       }
+
+      return NS_OK;
     }
     RefPtr<MediaManager> mManager;
     RefPtr<Runnable> mReply;
   };
 
   // Post ShutdownTask to execute on mMediaThread and pass in a lambda
   // callback to be executed back on this thread once it is done.
   //
   // The lambda callback "captures" the 'this' pointer for member access.
   // This is safe since this is guaranteed to be here since sSingleton isn't
   // cleared until the lambda function clears it.
 
   // note that this == sSingleton
   RefPtr<MediaManager> that(sSingleton);
   // Release the backend (and call Shutdown()) from within the MediaManager thread
   // Don't use MediaManager::PostTask() because we're sInShutdown=true here!
-  mMediaThread->message_loop()->PostTask(FROM_HERE, new ShutdownTask(this,
+  RefPtr<ShutdownTask> shutdown = new ShutdownTask(this,
       media::NewRunnableFrom([this, that]() mutable {
     LOG(("MediaManager shutdown lambda running, releasing MediaManager singleton and thread"));
     if (mMediaThread) {
       mMediaThread->Stop();
     }
 
     // Remove async shutdown blocker
 
     nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetShutdownPhase();
     shutdownPhase->RemoveBlocker(sSingleton->mShutdownBlocker);
 
     // we hold a ref to 'that' which is the same as sSingleton
     sSingleton = nullptr;
 
     return NS_OK;
-  })));
+  }));
+  mMediaThread->message_loop()->PostTask(shutdown.forget());
 }
 
 nsresult
 MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
   const char16_t* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -2692,18 +2701,18 @@ MediaManager::Observe(nsISupports* aSubj
     }
   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
     // Clear memory of private-browsing-specific deviceIds. Fire and forget.
     media::SanitizeOriginKeys(0, true);
     return NS_OK;
   } else if (!strcmp(aTopic, "getUserMedia:privileged:allow") ||
              !strcmp(aTopic, "getUserMedia:response:allow")) {
     nsString key(aData);
-    nsAutoPtr<GetUserMediaTask> task;
-    mActiveCallbacks.RemoveAndForget(key, task);
+    RefPtr<GetUserMediaTask> task;
+    mActiveCallbacks.Remove(key, getter_AddRefs(task));
     if (!task) {
       return NS_OK;
     }
 
     if (aSubject) {
       // A particular device or devices were chosen by the user.
       // NOTE: does not allow setting a device to null; assumes nullptr
       nsCOMPtr<nsISupportsArray> array(do_QueryInterface(aSubject));
@@ -2740,33 +2749,33 @@ MediaManager::Observe(nsISupports* aSubj
         }
       }
     }
 
     if (sInShutdown) {
       return task->Denied(NS_LITERAL_STRING("In shutdown"));
     }
     // Reuse the same thread to save memory.
-    MediaManager::PostTask(FROM_HERE, task.forget());
+    MediaManager::PostTask(task.forget());
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
     nsString errorMessage(NS_LITERAL_STRING("SecurityError"));
 
     if (aSubject) {
       nsCOMPtr<nsISupportsString> msg(do_QueryInterface(aSubject));
       MOZ_ASSERT(msg);
       msg->GetData(errorMessage);
       if (errorMessage.IsEmpty())
         errorMessage.AssignLiteral(MOZ_UTF16("InternalError"));
     }
 
     nsString key(aData);
-    nsAutoPtr<GetUserMediaTask> task;
-    mActiveCallbacks.RemoveAndForget(key, task);
+    RefPtr<GetUserMediaTask> task;
+    mActiveCallbacks.Remove(key, getter_AddRefs(task));
     if (task) {
       task->Denied(errorMessage);
     }
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:revoke")) {
     nsresult rv;
     // may be windowid or screen:windowid
@@ -3076,22 +3085,23 @@ GetUserMediaCallbackMediaStreamListener:
   if (mStopped) {
     return;
   }
 
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
   // Pass a ref to us (which is threadsafe) so it can query us for the
   // source stream info.
-  MediaManager::PostTask(FROM_HERE,
+  RefPtr<MediaOperationTask> mediaOperation =
     new MediaOperationTask(MEDIA_STOP,
                            this, nullptr, nullptr,
                            !mAudioStopped ? mAudioDevice.get() : nullptr,
                            !mVideoStopped ? mVideoDevice.get() : nullptr,
-                           false, mWindowID, nullptr));
+                           false, mWindowID, nullptr);
+  MediaManager::PostTask(mediaOperation.forget());
   mStopped = mAudioStopped = mVideoStopped = true;
 }
 
 // Doesn't kill audio
 void
 GetUserMediaCallbackMediaStreamListener::StopSharing()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -3141,19 +3151,19 @@ GetUserMediaCallbackMediaStreamListener:
     p->Resolve(false);
     return p.forget();
   }
 
   RefPtr<MediaManager> mgr = MediaManager::GetInstance();
   uint32_t id = mgr->mOutstandingVoidPledges.Append(*p);
   uint64_t windowId = aWindow->WindowID();
 
-  MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, windowId,
-                                                 audioDevice, videoDevice,
-                                                 aConstraints]() mutable {
+  MediaManager::PostTask(NewTaskFrom([id, windowId,
+                                      audioDevice, videoDevice,
+                                      aConstraints]() mutable {
     MOZ_ASSERT(MediaManager::IsInMediaThread());
     RefPtr<MediaManager> mgr = MediaManager::GetInstance();
     const char* badConstraint = nullptr;
     nsresult rv = NS_OK;
 
     if (audioDevice) {
       rv = audioDevice->Restart(aConstraints, mgr->mPrefs);
       if (rv == NS_ERROR_NOT_AVAILABLE) {
@@ -3166,18 +3176,18 @@ GetUserMediaCallbackMediaStreamListener:
       rv = videoDevice->Restart(aConstraints, mgr->mPrefs);
       if (rv == NS_ERROR_NOT_AVAILABLE) {
         nsTArray<RefPtr<VideoDevice>> videos;
         videos.AppendElement(videoDevice);
         badConstraint = MediaConstraintsHelper::SelectSettings(aConstraints,
                                                                videos);
       }
     }
-    NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, windowId, rv,
-                                                       badConstraint]() mutable {
+    NS_DispatchToMainThread(NewRunnableFrom([id, windowId, rv,
+                                             badConstraint]() mutable {
       MOZ_ASSERT(NS_IsMainThread());
       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
       if (!mgr) {
         return NS_OK;
       }
       RefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
       if (p) {
         if (NS_SUCCEEDED(rv)) {
@@ -3199,17 +3209,17 @@ GetUserMediaCallbackMediaStreamListener:
                   new MediaStreamError(window->AsInner(),
                                        NS_LITERAL_STRING("InternalError"));
               p->Reject(error);
             }
           }
         }
       }
       return NS_OK;
-    })));
+    }));
   }));
   return p.forget();
 }
 
 // Stop backend for track
 
 void
 GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID)
@@ -3236,22 +3246,23 @@ GetUserMediaCallbackMediaStreamListener:
   }
 
   if ((stopAudio || mAudioStopped || !mAudioDevice) &&
       (stopVideo || mVideoStopped || !mVideoDevice)) {
     Stop();
     return;
   }
 
-  MediaManager::PostTask(FROM_HERE,
+  RefPtr<MediaOperationTask> mediaOperation =
     new MediaOperationTask(MEDIA_STOP_TRACK,
                            this, nullptr, nullptr,
                            stopAudio ? mAudioDevice.get() : nullptr,
                            stopVideo ? mVideoDevice.get() : nullptr,
-                           false , mWindowID, nullptr));
+                           false , mWindowID, nullptr);
+  MediaManager::PostTask(mediaOperation.forget());
   mAudioStopped |= stopAudio;
   mVideoStopped |= stopVideo;
 }
 
 void
 GetUserMediaCallbackMediaStreamListener::NotifyFinished()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -3266,21 +3277,22 @@ GetUserMediaCallbackMediaStreamListener:
   }
 }
 
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph,
                                                                bool aHasListeners)
 {
-  MediaManager::PostTask(FROM_HERE,
+  RefPtr<MediaOperationTask> mediaOperation =
     new MediaOperationTask(MEDIA_DIRECT_LISTENERS,
                            this, nullptr, nullptr,
                            mAudioDevice, mVideoDevice,
-                           aHasListeners, mWindowID, nullptr));
+                           aHasListeners, mWindowID, nullptr);
+  MediaManager::PostTask(mediaOperation.forget());
 }
 
 // this can be in response to our own RemoveListener() (via ::Remove()), or
 // because the DOM GC'd the DOMLocalMediaStream/etc we're attached to.
 void
 GetUserMediaCallbackMediaStreamListener::NotifyRemoved()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -419,17 +419,17 @@ public:
   static already_AddRefed<MediaManager> GetInstance();
 
   // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
   // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
   // from MediaManager thread.
   static MediaManager* Get();
   static MediaManager* GetIfExists();
   static void StartupInit();
-  static void PostTask(const tracked_objects::Location& from_here, Task* task);
+  static void PostTask(already_AddRefed<Runnable> task);
 #ifdef DEBUG
   static bool IsInMediaThread();
 #endif
 
   static bool Exists()
   {
     return !!sSingleton;
   }
@@ -531,17 +531,17 @@ private:
   void IterateWindowListeners(nsPIDOMWindowInner *aWindow,
                               WindowListenerCallback aCallback,
                               void *aData);
 
   void StopMediaStreams();
 
   // ONLY access from MainThread so we don't need to lock
   WindowTable mActiveWindows;
-  nsClassHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
+  nsRefPtrHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
   nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
 
   // Always exists
   nsAutoPtr<base::Thread> mMediaThread;
   nsCOMPtr<nsIAsyncShutdownBlocker> mShutdownBlocker;
 
   // ONLY accessed from MediaManagerThread
   RefPtr<MediaEngine> mBackend;
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -620,18 +620,19 @@ GMPChild::AllocPGMPContentChild(Transpor
 
 void
 GMPChild::GMPContentChildActorDestroy(GMPContentChild* aGMPContentChild)
 {
   for (uint32_t i = mGMPContentChildren.Length(); i > 0; i--) {
     UniquePtr<GMPContentChild>& toDestroy = mGMPContentChildren[i - 1];
     if (toDestroy.get() == aGMPContentChild) {
       SendPGMPContentChildDestroyed();
-      MessageLoop::current()->PostTask(FROM_HERE,
-                                       new DeleteTask<GMPContentChild>(toDestroy.release()));
+      RefPtr<DeleteTask<GMPContentChild>> task =
+        new DeleteTask<GMPContentChild>(toDestroy.release());
+      MessageLoop::current()->PostTask(task.forget());
       mGMPContentChildren.RemoveElementAt(i - 1);
       break;
     }
   }
 }
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPContentChild.cpp
+++ b/dom/media/gmp/GMPContentChild.cpp
@@ -17,18 +17,18 @@ GMPContentChild::GMPContentChild(GMPChil
   : mGMPChild(aChild)
 {
   MOZ_COUNT_CTOR(GMPContentChild);
 }
 
 GMPContentChild::~GMPContentChild()
 {
   MOZ_COUNT_DTOR(GMPContentChild);
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                   new DeleteTask<Transport>(GetTransport()));
+  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
+  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 MessageLoop*
 GMPContentChild::GMPMessageLoop()
 {
   return mGMPChild->GMPMessageLoop();
 }
 
--- a/dom/media/gmp/GMPContentParent.cpp
+++ b/dom/media/gmp/GMPContentParent.cpp
@@ -38,18 +38,18 @@ GMPContentParent::GMPContentParent(GMPPa
   if (mParent) {
     SetDisplayName(mParent->GetDisplayName());
     SetPluginId(mParent->GetPluginId());
   }
 }
 
 GMPContentParent::~GMPContentParent()
 {
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                   new DeleteTask<Transport>(GetTransport()));
+  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
+  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 class ReleaseGMPContentParent : public Runnable
 {
 public:
   explicit ReleaseGMPContentParent(GMPContentParent* aToRelease)
     : mToRelease(aToRelease)
   {
--- a/dom/media/gmp/GMPDecryptorChild.cpp
+++ b/dom/media/gmp/GMPDecryptorChild.cpp
@@ -56,18 +56,18 @@ GMPDecryptorChild::CallOnGMPThread(Metho
 {
   if (ON_GMP_THREAD()) {
     // Use forwarding reference when we can.
     CallMethod(aMethod, Forward<ParamType>(aParams)...);
   } else {
     // Use const reference when we have to.
     auto m = &GMPDecryptorChild::CallMethod<
         decltype(aMethod), typename AddConstReference<ParamType>::Type...>;
-    auto t = NewRunnableMethod(this, m, aMethod, Forward<ParamType>(aParams)...);
-    mPlugin->GMPMessageLoop()->PostTask(FROM_HERE, t);
+    RefPtr<mozilla::Runnable> t = NewRunnableMethod(this, m, aMethod, Forward<ParamType>(aParams)...);
+    mPlugin->GMPMessageLoop()->PostTask(t.forget());
   }
 }
 
 void
 GMPDecryptorChild::Init(GMPDecryptor* aSession)
 {
   MOZ_ASSERT(aSession);
   mSession = aSession;
@@ -165,18 +165,18 @@ GMPDecryptorChild::KeyStatusChanged(cons
 }
 
 void
 GMPDecryptorChild::Decrypted(GMPBuffer* aBuffer, GMPErr aResult)
 {
   if (!ON_GMP_THREAD()) {
     // We should run this whole method on the GMP thread since the buffer needs
     // to be deleted after the SendDecrypted call.
-    auto t = NewRunnableMethod(this, &GMPDecryptorChild::Decrypted, aBuffer, aResult);
-    mPlugin->GMPMessageLoop()->PostTask(FROM_HERE, t);
+    RefPtr<Runnable> t = NewRunnableMethod(this, &GMPDecryptorChild::Decrypted, aBuffer, aResult);
+    mPlugin->GMPMessageLoop()->PostTask(t.forget());
     return;
   }
 
   if (!aBuffer) {
     NS_WARNING("GMPDecryptorCallback passed bull GMPBuffer");
     return;
   }
 
--- a/dom/media/gmp/GMPPlatform.cpp
+++ b/dom/media/gmp/GMPPlatform.cpp
@@ -68,17 +68,17 @@ public:
   void Post()
   {
     // We assert here for two reasons.
     // 1) Nobody should be blocking the main thread.
     // 2) This prevents deadlocks when doing sync calls to main which if the
     //    main thread tries to do a sync call back to the calling thread.
     MOZ_ASSERT(!IsOnChildMainThread());
 
-    mMessageLoop->PostTask(FROM_HERE, NewRunnableMethod(this, &SyncRunnable::Run));
+    mMessageLoop->PostTask(NewRunnableMethod(this, &SyncRunnable::Run));
     MonitorAutoLock lock(mMonitor);
     while (!mDone) {
       lock.Wait();
     }
   }
 
   void Run()
   {
@@ -116,17 +116,17 @@ CreateThread(GMPThread** aThread)
 GMPErr
 RunOnMainThread(GMPTask* aTask)
 {
   if (!aTask || !sMainLoop) {
     return GMPGenericErr;
   }
 
   RefPtr<Runnable> r = new Runnable(aTask);
-  sMainLoop->PostTask(FROM_HERE, NewRunnableMethod(r.get(), &Runnable::Run));
+  sMainLoop->PostTask(NewRunnableMethod(r.get(), &Runnable::Run));
 
   return GMPNoErr;
 }
 
 GMPErr
 SyncRunOnMainThread(GMPTask* aTask)
 {
   if (!aTask || !sMainLoop || IsOnChildMainThread()) {
@@ -249,17 +249,17 @@ GMPThreadImpl::Post(GMPTask* aTask)
     if (!started) {
       NS_WARNING("Unable to start GMPThread!");
       return;
     }
   }
 
   RefPtr<Runnable> r = new Runnable(aTask);
 
-  mThread.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(r.get(), &Runnable::Run));
+  mThread.message_loop()->PostTask(NewRunnableMethod(r.get(), &Runnable::Run));
 }
 
 void
 GMPThreadImpl::Join()
 {
   {
     MutexAutoLock lock(mMutex);
     if (mThread.IsRunning()) {
--- a/dom/media/gmp/GMPProcessParent.cpp
+++ b/dom/media/gmp/GMPProcessParent.cpp
@@ -80,17 +80,17 @@ GMPProcessParent::Launch(int32_t aTimeou
 
   return SyncLaunch(args, aTimeoutMs, base::GetCurrentProcessArchitecture());
 }
 
 void
 GMPProcessParent::Delete(nsCOMPtr<nsIRunnable> aCallback)
 {
   mDeletedCallback = aCallback;
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(this, &GMPProcessParent::DoDelete));
+  XRE_GetIOMessageLoop()->PostTask(NewRunnableMethod(this, &GMPProcessParent::DoDelete));
 }
 
 void
 GMPProcessParent::DoDelete()
 {
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   Join();
 
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -247,18 +247,18 @@ GeckoMediaPluginServiceChild::RemoveGMPC
 }
 
 GMPServiceChild::GMPServiceChild()
 {
 }
 
 GMPServiceChild::~GMPServiceChild()
 {
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                   new DeleteTask<Transport>(GetTransport()));
+  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
+  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 PGMPContentParent*
 GMPServiceChild::AllocPGMPContentParent(Transport* aTransport,
                                         ProcessId aOtherPid)
 {
   MOZ_ASSERT(!mContentParents.GetWeak(aOtherPid));
 
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -1776,18 +1776,18 @@ GeckoMediaPluginServiceParent::ClearStor
   if (NS_FAILED(DeleteDir(path))) {
     NS_WARNING("Failed to delete GMP storage directory");
   }
   NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL);
 }
 
 GMPServiceParent::~GMPServiceParent()
 {
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                   new DeleteTask<Transport>(GetTransport()));
+  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
+  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 bool
 GMPServiceParent::RecvLoadGMP(const nsCString& aNodeId,
                               const nsCString& aAPI,
                               nsTArray<nsCString>&& aTags,
                               nsTArray<ProcessId>&& aAlreadyBridgedTo,
                               ProcessId* aId,
--- a/dom/media/gmp/GMPStorageChild.cpp
+++ b/dom/media/gmp/GMPStorageChild.cpp
@@ -10,17 +10,17 @@
 #define ON_GMP_THREAD() (mPlugin->GMPMessageLoop() == MessageLoop::current())
 
 #define CALL_ON_GMP_THREAD(_func, ...) \
   do { \
     if (ON_GMP_THREAD()) { \
       _func(__VA_ARGS__); \
     } else { \
       mPlugin->GMPMessageLoop()->PostTask( \
-        FROM_HERE, NewRunnableMethod(this, &GMPStorageChild::_func, ##__VA_ARGS__) \
+        NewRunnableMethod(this, &GMPStorageChild::_func, ##__VA_ARGS__) \
       ); \
     } \
   } while(false)
 
 static nsTArray<uint8_t>
 ToArray(const uint8_t* aData, uint32_t aDataSize)
 {
   nsTArray<uint8_t> data;
--- a/dom/media/gmp/GMPVideoDecoderChild.cpp
+++ b/dom/media/gmp/GMPVideoDecoderChild.cpp
@@ -221,18 +221,17 @@ GMPVideoDecoderChild::Alloc(size_t aSize
   MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
 
   bool rv;
 #ifndef SHMEM_ALLOC_IN_CHILD
   ++mNeedShmemIntrCount;
   rv = CallNeedShmem(aSize, aMem);
   --mNeedShmemIntrCount;
   if (mPendingDecodeComplete) {
-    auto t = NewRunnableMethod(this, &GMPVideoDecoderChild::RecvDecodingComplete);
-    mPlugin->GMPMessageLoop()->PostTask(FROM_HERE, t);
+    mPlugin->GMPMessageLoop()->PostTask(NewRunnableMethod(this, &GMPVideoDecoderChild::RecvDecodingComplete));
   }
 #else
 #ifdef GMP_SAFE_SHMEM
   rv = AllocShmem(aSize, aType, aMem);
 #else
   rv = AllocUnsafeShmem(aSize, aType, aMem);
 #endif
 #endif
--- a/dom/media/gmp/GMPVideoEncoderChild.cpp
+++ b/dom/media/gmp/GMPVideoEncoderChild.cpp
@@ -202,18 +202,17 @@ GMPVideoEncoderChild::Alloc(size_t aSize
   MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
 
   bool rv;
 #ifndef SHMEM_ALLOC_IN_CHILD
   ++mNeedShmemIntrCount;
   rv = CallNeedShmem(aSize, aMem);
   --mNeedShmemIntrCount;
   if (mPendingEncodeComplete) {
-    auto t = NewRunnableMethod(this, &GMPVideoEncoderChild::RecvEncodingComplete);
-    mPlugin->GMPMessageLoop()->PostTask(FROM_HERE, t);
+    mPlugin->GMPMessageLoop()->PostTask(NewRunnableMethod(this, &GMPVideoEncoderChild::RecvEncodingComplete));
   }
 #else
 #ifdef GMP_SAFE_SHMEM
   rv = AllocShmem(aSize, aType, aMem);
 #else
   rv = AllocUnsafeShmem(aSize, aType, aMem);
 #endif
 #endif
--- a/dom/media/mediasink/DecodedAudioDataSink.h
+++ b/dom/media/mediasink/DecodedAudioDataSink.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(DecodedAudioDataSink_h__)
 #define DecodedAudioDataSink_h__
 
 #include "AudioSink.h"
 #include "AudioStream.h"
 #include "MediaEventSource.h"
+#include "MediaQueue.h"
 #include "MediaInfo.h"
 #include "mozilla/RefPtr.h"
 #include "nsISupportsImpl.h"
 
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
--- a/dom/media/omx/MediaOmxReader.cpp
+++ b/dom/media/omx/MediaOmxReader.cpp
@@ -26,29 +26,30 @@ using namespace mozilla::gfx;
 using namespace mozilla::media;
 using namespace android;
 
 namespace mozilla {
 
 extern LazyLogModule gMediaDecoderLog;
 #define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
 
-class MediaOmxReader::ProcessCachedDataTask : public Task
+class MediaOmxReader::ProcessCachedDataTask : public Runnable
 {
 public:
   ProcessCachedDataTask(MediaOmxReader* aOmxReader, int64_t aOffset)
   : mOmxReader(aOmxReader),
     mOffset(aOffset)
   { }
 
-  void Run()
+  NS_IMETHOD Run() override
   {
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(mOmxReader.get());
     mOmxReader->ProcessCachedData(mOffset);
+    return NS_OK;
   }
 
 private:
   RefPtr<MediaOmxReader> mOmxReader;
   int64_t                  mOffset;
 };
 
 // When loading an MP3 stream from a file, we need to parse the file's
@@ -100,18 +101,18 @@ private:
       mLength -= length;
       mOffset += length;
     }
 
     if (static_cast<uint64_t>(mOffset) < mFullLength) {
       // We cannot read data in the main thread because it
       // might block for too long. Instead we post an IO task
       // to the IO thread if there is more data available.
-      XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-          new ProcessCachedDataTask(mOmxReader.get(), mOffset));
+      RefPtr<Runnable> task = new ProcessCachedDataTask(mOmxReader.get(), mOffset);
+      XRE_GetIOMessageLoop()->PostTask(task.forget());
     }
   }
 
   RefPtr<MediaOmxReader>         mOmxReader;
   uint64_t                         mLength;
   int64_t                          mOffset;
   uint64_t                         mFullLength;
 };
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp
+++ b/dom/media/platforms/agnostic/OpusDecoder.cpp
@@ -98,20 +98,20 @@ OpusDataDecoder::DecodeHeader(const unsi
   MOZ_ASSERT(!mDecodedHeader);
   mDecodedHeader = true;
 
   mOpusParser = new OpusParser;
   if (!mOpusParser->DecodeHeader(const_cast<unsigned char*>(aData), aLength)) {
     return NS_ERROR_FAILURE;
   }
   int channels = mOpusParser->mChannels;
-  // No channel mapping for more than 8 channels.
-  if (channels > 8) {
-    OPUS_DEBUG("No channel mapping for more than 8 channels. Source is %d channels",
-               channels);
+
+  AudioConfig::ChannelLayout layout(channels);
+  if (!layout.IsValid()) {
+    OPUS_DEBUG("Invalid channel mapping. Source is %d channels", channels);
     return NS_ERROR_FAILURE;
   }
 
   AudioConfig::ChannelLayout vorbisLayout(
     channels, VorbisDataDecoder::VorbisLayout(channels));
   AudioConfig::ChannelLayout smpteLayout(channels);
   static_assert(sizeof(mOpusParser->mMappingTable) / sizeof(mOpusParser->mMappingTable[0]) >= MAX_AUDIO_CHANNELS,
                        "Invalid size set");
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -100,16 +100,21 @@ VorbisDataDecoder::Init()
     LOG(LogLevel::Warning,
         ("Invalid Vorbis header: container and codec rate do not match!"));
   }
   if (mInfo.mChannels != (uint32_t)mVorbisDsp.vi->channels) {
     LOG(LogLevel::Warning,
         ("Invalid Vorbis header: container and codec channels do not match!"));
   }
 
+  AudioConfig::ChannelLayout layout(mVorbisDsp.vi->channels);
+  if (!layout.IsValid()) {
+    return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
+  }
+
   return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
 }
 
 nsresult
 VorbisDataDecoder::DecodeHeader(const unsigned char* aData, size_t aLength)
 {
   bool bos = mPacketCount == 0;
   ogg_packet pkt = InitVorbisPacket(aData, aLength, bos, false, 0, mPacketCount++);
@@ -218,16 +223,19 @@ VorbisDataDecoder::DoDecode(MediaRawData
       NS_WARNING("Int overflow adding total_duration and aTstampUsecs");
       return -1;
     };
 
     if (!mAudioConverter) {
       AudioConfig in(AudioConfig::ChannelLayout(channels, VorbisLayout(channels)),
                      rate);
       AudioConfig out(channels, rate);
+      if (!in.IsValid() || !out.IsValid()) {
+       return -1;
+      }
       mAudioConverter = MakeUnique<AudioConverter>(in, out);
     }
     MOZ_ASSERT(mAudioConverter->CanWorkInPlace());
     AudioSampleBuffer data(Move(buffer));
     data = mAudioConverter->Process(Move(data));
 
     aTotalFrames += frames;
     mCallback->Output(new AudioData(aOffset,
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -196,16 +196,20 @@ public:
   nsresult Output(BufferInfo::Param aInfo, void* aBuffer,
                   MediaFormat::Param aFormat, const TimeUnit& aDuration)
   {
     // The output on Android is always 16-bit signed
     nsresult rv;
     int32_t numChannels;
     NS_ENSURE_SUCCESS(rv =
         aFormat->GetInteger(NS_LITERAL_STRING("channel-count"), &numChannels), rv);
+    AudioConfig::ChannelLayout layout(numChannels);
+    if (!layout.IsValid()) {
+      return NS_ERROR_FAILURE;
+    }
 
     int32_t sampleRate;
     NS_ENSURE_SUCCESS(rv =
         aFormat->GetInteger(NS_LITERAL_STRING("sample-rate"), &sampleRate), rv);
 
     int32_t size;
     NS_ENSURE_SUCCESS(rv = aInfo->Size(&size), rv);
 
--- a/dom/media/platforms/apple/AppleATDecoder.cpp
+++ b/dom/media/platforms/apple/AppleATDecoder.cpp
@@ -278,16 +278,19 @@ AppleATDecoder::DecodeSample(MediaRawDat
 
   AudioSampleBuffer data(outputData.Elements(), outputData.Length());
   if (!data.Data()) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   if (mChannelLayout && !mAudioConverter) {
     AudioConfig in(*mChannelLayout.get(), rate);
     AudioConfig out(channels, rate);
+    if (!in.IsValid() || !out.IsValid()) {
+      return NS_ERROR_FAILURE;
+    }
     mAudioConverter = MakeUnique<AudioConverter>(in, out);
   }
   if (mAudioConverter) {
     MOZ_ASSERT(mAudioConverter->CanWorkInPlace());
     data = mAudioConverter->Process(Move(data));
   }
 
   RefPtr<AudioData> audio = new AudioData(aSample->mOffset,
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -123,16 +123,22 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePac
     if (bytesConsumed < 0) {
       NS_WARNING("FFmpeg audio decoder error.");
       mCallback->Error();
       return;
     }
 
     if (decoded) {
       uint32_t numChannels = mCodecContext->channels;
+      AudioConfig::ChannelLayout layout(numChannels);
+      if (!layout.IsValid()) {
+        mCallback->Error();
+        return;
+      }
+
       uint32_t samplingRate = mCodecContext->sample_rate;
 
       AlignedAudioBuffer audio =
         CopyAndPackAudio(mFrame, numChannels, mFrame->nb_samples);
 
       media::TimeUnit duration =
         FramesToTimeUnit(mFrame->nb_samples, samplingRate);
       if (!audio || !duration.IsValid()) {
--- a/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
@@ -192,16 +192,20 @@ GonkAudioDecoderManager::Output(int64_t 
       int32_t codec_sample_rate = 0;
 
       if (!audioCodecFormat->findInt32("channel-count", &codec_channel_count) ||
         !audioCodecFormat->findInt32("sample-rate", &codec_sample_rate)) {
         return NS_ERROR_UNEXPECTED;
       }
 
       // Update AudioInfo
+      AudioConfig::ChannelLayout layout(codec_channel_count);
+      if (!layout.IsValid()) {
+        return NS_ERROR_FAILURE;
+      }
       mAudioChannels = codec_channel_count;
       mAudioRate = codec_sample_rate;
 
       return Output(aStreamOffset, aOutData);
     }
     case android::INFO_OUTPUT_BUFFERS_CHANGED:
     {
       GADM_LOG("Info Output Buffers Changed");
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -190,16 +190,21 @@ WMFAudioMFTManager::UpdateOutputType()
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   hr = type->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &mAudioRate);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   hr = type->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &mAudioChannels);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
+  AudioConfig::ChannelLayout layout(mAudioChannels);
+  if (!layout.IsValid()) {
+    return E_FAIL;
+  }
+
   return S_OK;
 }
 
 HRESULT
 WMFAudioMFTManager::Output(int64_t aStreamOffset,
                            RefPtr<MediaData>& aOutData)
 {
   aOutData = nullptr;
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -160,35 +160,35 @@ CamerasParent::Observe(nsISupports *aSub
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   MOZ_ASSERT(obs);
   obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
   StopVideoCapture();
   return NS_OK;
 }
 
 nsresult
-CamerasParent::DispatchToVideoCaptureThread(Runnable *event)
+CamerasParent::DispatchToVideoCaptureThread(Runnable* event)
 {
   // Don't try to dispatch if we're already on the right thread.
   // There's a potential deadlock because the mThreadMonitor is likely
   // to be taken already.
   MOZ_ASSERT(!mVideoCaptureThread ||
              mVideoCaptureThread->thread_id() != PlatformThread::CurrentId());
 
   MonitorAutoLock lock(mThreadMonitor);
 
   while(mChildIsAlive && mWebRTCAlive &&
         (!mVideoCaptureThread || !mVideoCaptureThread->IsRunning())) {
     mThreadMonitor.Wait();
   }
   if (!mVideoCaptureThread || !mVideoCaptureThread->IsRunning()) {
     return NS_ERROR_FAILURE;
   }
-  mVideoCaptureThread->message_loop()->PostTask(FROM_HERE,
-                                                new RunnableTask(event));
+  RefPtr<Runnable> addrefedEvent = event;
+  mVideoCaptureThread->message_loop()->PostTask(addrefedEvent.forget());
   return NS_OK;
 }
 
 void
 CamerasParent::StopVideoCapture()
 {
   LOG((__PRETTY_FUNCTION__));
   // We are called from the main thread (xpcom-shutdown) or
--- a/dom/media/systemservices/CamerasParent.h
+++ b/dom/media/systemservices/CamerasParent.h
@@ -124,17 +124,18 @@ protected:
   void StopCapture(const int& aCapEngine, const int& capnum);
   int ReleaseCaptureDevice(const int& aCapEngine, const int& capnum);
 
   bool SetupEngine(CaptureEngine aCapEngine);
   bool EnsureInitialized(int aEngine);
   void CloseEngines();
   void StopIPC();
   void StopVideoCapture();
-  nsresult DispatchToVideoCaptureThread(Runnable *event);
+  // Can't take already_AddRefed because it can fail in stupid ways.
+  nsresult DispatchToVideoCaptureThread(Runnable* event);
 
   EngineHelper mEngines[CaptureEngine::MaxEngine];
   nsTArray<CallbackHelper*> mCallbacks;
 
   // image buffers
   mozilla::ShmemPool mShmemPool;
 
   // PBackground parent thread
--- a/dom/media/systemservices/CamerasUtils.h
+++ b/dom/media/systemservices/CamerasUtils.h
@@ -34,27 +34,12 @@ public:
     return NS_OK;
   }
 
 private:
   ~ThreadDestructor() {}
   nsCOMPtr<nsIThread> mThread;
 };
 
-class RunnableTask : public Task
-{
-public:
-  explicit RunnableTask(Runnable* aRunnable)
-    : mRunnable(aRunnable) {}
-
-  void Run() override {
-    mRunnable->Run();
-  }
-
-private:
-  ~RunnableTask() {}
-  RefPtr<Runnable> mRunnable;
-};
-
 }
 }
 
 #endif // mozilla_CameraUtils_h
--- a/dom/media/systemservices/MediaSystemResourceManager.cpp
+++ b/dom/media/systemservices/MediaSystemResourceManager.cpp
@@ -34,30 +34,16 @@ MediaSystemResourceManager::Shutdown()
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   if (sSingleton) {
     sSingleton->CloseIPC();
     sSingleton = nullptr;
   }
 }
 
-class RunnableCallTask : public Task
-{
-public:
-  explicit RunnableCallTask(nsIRunnable* aRunnable)
-    : mRunnable(aRunnable) {}
-
-  void Run() override
-  {
-    mRunnable->Run();
-  }
-protected:
-  nsCOMPtr<nsIRunnable> mRunnable;
-};
-
 /* static */ void
 MediaSystemResourceManager::Init()
 {
   if (!ImageBridgeChild::IsCreated()) {
     NS_WARNING("ImageBridge does not exist");
     return;
   }
 
@@ -72,28 +58,27 @@ MediaSystemResourceManager::Init()
     }
     return;
   }
 
   ReentrantMonitor barrier("MediaSystemResourceManager::Init");
   ReentrantMonitorAutoEnter autoMon(barrier);
   bool done = false;
 
-  nsCOMPtr<nsIRunnable> runnable =
+  RefPtr<Runnable> runnable =
     NS_NewRunnableFunction([&]() {
       if (!sSingleton) {
         sSingleton = new MediaSystemResourceManager();
       }
       ReentrantMonitorAutoEnter autoMon(barrier);
       done = true;
       barrier.NotifyAll();
     });
 
-  ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
-    FROM_HERE, new RunnableCallTask(runnable));
+  ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(runnable.forget());
 
   // should stop the thread until done.
   while (!done) {
     barrier.Wait();
   }
 
 }
 
@@ -209,17 +194,16 @@ MediaSystemResourceManager::Acquire(Medi
   }
   // State Check
   if (aClient->mResourceState != MediaSystemResourceClient::RESOURCE_STATE_START) {
     HandleAcquireResult(aClient->mId, false);
     return;
   }
   aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_WAITING;
   ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(
       this,
       &MediaSystemResourceManager::DoAcquire,
       aClient->mId));
 }
 
 bool
 MediaSystemResourceManager::AcquireSyncNoWait(MediaSystemResourceClient* aClient)
@@ -253,17 +237,16 @@ MediaSystemResourceManager::AcquireSyncN
     }
     // Hold barrier Monitor until acquire task end.
     aClient->mAcquireSyncWaitMonitor = &barrier;
     aClient->mAcquireSyncWaitDone = &done;
     aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_WAITING;
   }
 
   ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
-    FROM_HERE,
     NewRunnableMethod(
       this,
       &MediaSystemResourceManager::DoAcquire,
       aClient->mId));
 
   // should stop the thread until done.
   while (!done) {
     barrier.Wait();
@@ -321,17 +304,16 @@ MediaSystemResourceManager::ReleaseResou
 
       aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_END;
       return;
     }
 
     aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_END;
 
     ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
-      FROM_HERE,
       NewRunnableMethod(
         this,
         &MediaSystemResourceManager::DoRelease,
         aClient->mId));
   }
 }
 
 void
@@ -350,17 +332,16 @@ MediaSystemResourceManager::RecvResponse
   HandleAcquireResult(aId, aSuccess);
 }
 
 void
 MediaSystemResourceManager::HandleAcquireResult(uint32_t aId, bool aSuccess)
 {
   if (!InImageBridgeChildThread()) {
     ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
-      FROM_HERE,
       NewRunnableMethod(
         this,
         &MediaSystemResourceManager::HandleAcquireResult,
         aId,
         aSuccess));
     return;
   }
 
--- a/dom/media/systemservices/MediaUtils.h
+++ b/dom/media/systemservices/MediaUtils.h
@@ -195,41 +195,46 @@ private:
   Run()
   {
     return mOnRun();
   }
   OnRunType mOnRun;
 };
 
 template<typename OnRunType>
-LambdaRunnable<OnRunType>*
+already_AddRefed<LambdaRunnable<OnRunType>>
 NewRunnableFrom(OnRunType&& aOnRun)
 {
-  return new LambdaRunnable<OnRunType>(Forward<OnRunType>(aOnRun));
+  typedef LambdaRunnable<OnRunType> LambdaType;
+  RefPtr<LambdaType> lambda = new LambdaType(Forward<OnRunType>(aOnRun));
+  return lambda.forget();
 }
 
 template<typename OnRunType>
-class LambdaTask : public Task
+class LambdaTask : public Runnable
 {
 public:
   explicit LambdaTask(OnRunType&& aOnRun) : mOnRun(Move(aOnRun)) {}
 private:
-  void
-  Run()
+  NS_IMETHOD
+  Run() override
   {
-    return mOnRun();
+    mOnRun();
+    return NS_OK;
   }
   OnRunType mOnRun;
 };
 
 template<typename OnRunType>
-LambdaTask<OnRunType>*
+already_AddRefed<LambdaTask<OnRunType>>
 NewTaskFrom(OnRunType&& aOnRun)
 {
-  return new LambdaTask<OnRunType>(Forward<OnRunType>(aOnRun));
+  typedef LambdaTask<OnRunType> LambdaType;
+  RefPtr<LambdaType> lambda = new LambdaType(Forward<OnRunType>(aOnRun));
+  return lambda.forget();
 }
 
 /* media::CoatCheck - There and back again. Park an object in exchange for an id.
  *
  * A common problem with calling asynchronous functions that do work on other
  * threads or processes is how to pass in a heap object for use once the
  * function completes, without requiring that object to have threadsafe
  * refcounting, contain mutexes, be marshaled, or leak if things fail
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -523,30 +523,32 @@ AudioContext::CreateOscillator(ErrorResu
   RefPtr<OscillatorNode> oscillatorNode =
     new OscillatorNode(this);
   return oscillatorNode.forget();
 }
 
 already_AddRefed<PeriodicWave>
 AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
                                  const Float32Array& aImagData,
+                                 const PeriodicWaveConstraints& aConstraints,
                                  ErrorResult& aRv)
 {
   aRealData.ComputeLengthAndData();
   aImagData.ComputeLengthAndData();
 
   if (aRealData.Length() != aImagData.Length() ||
       aRealData.Length() == 0) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
 
   RefPtr<PeriodicWave> periodicWave =
     new PeriodicWave(this, aRealData.Data(), aImagData.Data(),
-                     aImagData.Length(), aRv);
+                     aImagData.Length(), aConstraints.mDisableNormalization,
+                     aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   return periodicWave.forget();
 }
 
 AudioListener*
 AudioContext::Listener()
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -63,16 +63,17 @@ class GlobalObject;
 class MediaStreamAudioDestinationNode;
 class MediaStreamAudioSourceNode;
 class OscillatorNode;
 class PannerNode;
 class ScriptProcessorNode;
 class StereoPannerNode;
 class WaveShaperNode;
 class PeriodicWave;
+struct PeriodicWaveConstraints;
 class Promise;
 enum class OscillatorType : uint32_t;
 
 // This is addrefed by the OscillatorNodeEngine on the main thread
 // and then used from the MSG thread.
 // It can be released either from the graph thread or the main thread.
 class BasicWaveFormCache
 {
@@ -250,16 +251,17 @@ public:
   already_AddRefed<BiquadFilterNode>
   CreateBiquadFilter(ErrorResult& aRv);
 
   already_AddRefed<OscillatorNode>
   CreateOscillator(ErrorResult& aRv);
 
   already_AddRefed<PeriodicWave>
   CreatePeriodicWave(const Float32Array& aRealData, const Float32Array& aImagData,
+                     const PeriodicWaveConstraints& aConstraints,
                      ErrorResult& aRv);
 
   already_AddRefed<Promise>
   DecodeAudioData(const ArrayBuffer& aBuffer,
                   const Optional<OwningNonNull<DecodeSuccessCallback> >& aSuccessCallback,
                   const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback,
                   ErrorResult& aRv);
 
--- a/dom/media/webaudio/OscillatorNode.cpp
+++ b/dom/media/webaudio/OscillatorNode.cpp
@@ -37,31 +37,33 @@ public:
     , mFrequency(440.f)
     , mDetune(0.f)
     , mType(OscillatorType::Sine)
     , mPhase(0.)
     , mFinalFrequency(0.)
     , mPhaseIncrement(0.)
     , mRecomputeParameters(true)
     , mCustomLength(0)
+    , mCustomDisableNormalization(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
     mBasicWaveFormCache = aDestination->Context()->GetBasicWaveFormCache();
   }
 
   void SetSourceStream(AudioNodeStream* aSource)
   {
     mSource = aSource;
   }
 
   enum Parameters {
     FREQUENCY,
     DETUNE,
     TYPE,
-    PERIODICWAVE,
+    PERIODICWAVE_LENGTH,
+    DISABLE_NORMALIZATION,
     START,
     STOP,
   };
   void RecvTimelineEvent(uint32_t aIndex,
                          AudioTimelineEvent& aEvent) override
   {
     mRecomputeParameters = true;
 
@@ -99,16 +101,17 @@ public:
   {
     switch (aIndex) {
       case TYPE:
         // Set the new type.
         mType = static_cast<OscillatorType>(aParam);
         if (mType == OscillatorType::Sine) {
           // Forget any previous custom data.
           mCustomLength = 0;
+          mCustomDisableNormalization = false;
           mCustom = nullptr;
           mPeriodicWave = nullptr;
           mRecomputeParameters = true;
         }
         switch (mType) {
           case OscillatorType::Sine:
             mPhase = 0.0;
             break;
@@ -119,34 +122,41 @@ public:
             break;
           case OscillatorType::Custom:
             break;
           default:
             NS_ERROR("Bad OscillatorNodeEngine type parameter.");
         }
         // End type switch.
         break;
-      case PERIODICWAVE:
+      case PERIODICWAVE_LENGTH:
         MOZ_ASSERT(aParam >= 0, "negative custom array length");
         mCustomLength = static_cast<uint32_t>(aParam);
         break;
+      case DISABLE_NORMALIZATION:
+        MOZ_ASSERT(aParam >= 0, "negative custom array length");
+        mCustomDisableNormalization = static_cast<uint32_t>(aParam);
+        break;
       default:
         NS_ERROR("Bad OscillatorNodeEngine Int32Parameter.");
     }
     // End index switch.
   }
 
   void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) override
   {
     MOZ_ASSERT(mCustomLength, "Custom buffer sent before length");
     mCustom = aBuffer;
     MOZ_ASSERT(mCustom->GetChannels() == 2,
                "PeriodicWave should have sent two channels");
     mPeriodicWave = WebCore::PeriodicWave::create(mSource->SampleRate(),
-    mCustom->GetData(0), mCustom->GetData(1), mCustomLength);
+                                                  mCustom->GetData(0),
+                                                  mCustom->GetData(1),
+                                                  mCustomLength,
+                                                  mCustomDisableNormalization);
   }
 
   void IncrementPhase()
   {
     const float twoPiFloat = float(2 * M_PI);
     mPhase += mPhaseIncrement;
     if (mPhase > twoPiFloat) {
       mPhase -= twoPiFloat;
@@ -388,16 +398,17 @@ public:
   OscillatorType mType;
   float mPhase;
   float mFinalFrequency;
   float mPhaseIncrement;
   bool mRecomputeParameters;
   RefPtr<ThreadSharedFloatArrayBufferList> mCustom;
   RefPtr<BasicWaveFormCache> mBasicWaveFormCache;
   uint32_t mCustomLength;
+  bool mCustomDisableNormalization;
   RefPtr<WebCore::PeriodicWave> mPeriodicWave;
 };
 
 OscillatorNode::OscillatorNode(AudioContext* aContext)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
@@ -467,18 +478,20 @@ OscillatorNode::SendTypeToStream()
 }
 
 void OscillatorNode::SendPeriodicWaveToStream()
 {
   NS_ASSERTION(mType == OscillatorType::Custom,
                "Sending custom waveform to engine thread with non-custom type");
   MOZ_ASSERT(mStream, "Missing node stream.");
   MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object.");
-  SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE,
+  SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE_LENGTH,
                              mPeriodicWave->DataLength());
+  SendInt32ParameterToStream(OscillatorNodeEngine::DISABLE_NORMALIZATION,
+                             mPeriodicWave->DisableNormalization());
   RefPtr<ThreadSharedFloatArrayBufferList> data =
     mPeriodicWave->GetThreadSharedBuffer();
   mStream->SetBuffer(data.forget());
 }
 
 void
 OscillatorNode::Start(double aWhen, ErrorResult& aRv)
 {
--- a/dom/media/webaudio/PeriodicWave.cpp
+++ b/dom/media/webaudio/PeriodicWave.cpp
@@ -15,18 +15,20 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Pe
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PeriodicWave, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PeriodicWave, Release)
 
 PeriodicWave::PeriodicWave(AudioContext* aContext,
                            const float* aRealData,
                            const float* aImagData,
                            const uint32_t aLength,
+                           const bool aDisableNormalization,
                            ErrorResult& aRv)
   : mContext(aContext)
+  , mDisableNormalization(aDisableNormalization)
 {
   MOZ_ASSERT(aContext);
 
   // Caller should have checked this and thrown.
   MOZ_ASSERT(aLength > 0);
   mLength = aLength;
 
   // Copy coefficient data. The two arrays share an allocation.
--- a/dom/media/webaudio/PeriodicWave.h
+++ b/dom/media/webaudio/PeriodicWave.h
@@ -19,16 +19,17 @@ namespace dom {
 
 class PeriodicWave final : public nsWrapperCache
 {
 public:
   PeriodicWave(AudioContext* aContext,
                const float* aRealData,
                const float* aImagData,
                const uint32_t aLength,
+               const bool aDisableNormalization,
                ErrorResult& aRv);
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PeriodicWave)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PeriodicWave)
 
   AudioContext* GetParentObject() const
   {
     return mContext;
@@ -36,28 +37,34 @@ public:
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   uint32_t DataLength() const
   {
     return mLength;
   }
 
+  bool DisableNormalization() const
+  {
+    return mDisableNormalization;
+  }
+
   ThreadSharedFloatArrayBufferList* GetThreadSharedBuffer() const
   {
     return mCoefficients;
   }
 
   size_t SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const;
   size_t SizeOfIncludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const;
 
 private:
   ~PeriodicWave() {}
 
   RefPtr<AudioContext> mContext;
   RefPtr<ThreadSharedFloatArrayBufferList> mCoefficients;
   uint32_t mLength;
+  bool mDisableNormalization;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/blink/PeriodicWave.cpp
+++ b/dom/media/webaudio/blink/PeriodicWave.cpp
@@ -40,23 +40,25 @@ using namespace mozilla;
 using mozilla::dom::OscillatorType;
 
 namespace WebCore {
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::create(float sampleRate,
                      const float* real,
                      const float* imag,
-                     size_t numberOfComponents)
+                     size_t numberOfComponents,
+                     bool disableNormalization)
 {
     bool isGood = real && imag && numberOfComponents > 0;
     MOZ_ASSERT(isGood);
     if (isGood) {
         RefPtr<PeriodicWave> periodicWave =
-            new PeriodicWave(sampleRate, numberOfComponents);
+            new PeriodicWave(sampleRate, numberOfComponents,
+                             disableNormalization);
 
         // Limit the number of components used to those for frequencies below the
         // Nyquist of the fixed length inverse FFT.
         size_t halfSize = periodicWave->m_periodicWaveSize / 2;
         numberOfComponents = std::min(numberOfComponents, halfSize);
         periodicWave->m_numberOfComponents = numberOfComponents;
         periodicWave->m_realComponents = new AudioFloatArray(numberOfComponents);
         periodicWave->m_imagComponents = new AudioFloatArray(numberOfComponents);
@@ -69,53 +71,54 @@ PeriodicWave::create(float sampleRate,
     }
     return nullptr;
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createSine(float sampleRate)
 {
     RefPtr<PeriodicWave> periodicWave =
-        new PeriodicWave(sampleRate, MinPeriodicWaveSize);
+        new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
     periodicWave->generateBasicWaveform(OscillatorType::Sine);
     return periodicWave.forget();
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createSquare(float sampleRate)
 {
     RefPtr<PeriodicWave> periodicWave =
-        new PeriodicWave(sampleRate, MinPeriodicWaveSize);
+        new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
     periodicWave->generateBasicWaveform(OscillatorType::Square);
     return periodicWave.forget();
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createSawtooth(float sampleRate)
 {
     RefPtr<PeriodicWave> periodicWave =
-        new PeriodicWave(sampleRate, MinPeriodicWaveSize);
+        new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
     periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
     return periodicWave.forget();
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createTriangle(float sampleRate)
 {
     RefPtr<PeriodicWave> periodicWave =
-        new PeriodicWave(sampleRate, MinPeriodicWaveSize);
+        new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
     periodicWave->generateBasicWaveform(OscillatorType::Triangle);
     return periodicWave.forget();
 }
 
-PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents)
+PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents, bool disableNormalization)
     : m_sampleRate(sampleRate)
     , m_centsPerRange(CentsPerRange)
     , m_maxPartialsInBandLimitedTable(0)
     , m_normalizationScale(1.0f)
+    , m_disableNormalization(disableNormalization)
 {
     float nyquist = 0.5 * m_sampleRate;
 
     if (numberOfComponents <= MinPeriodicWaveSize) {
         m_periodicWaveSize = MinPeriodicWaveSize;
     } else {
         unsigned npow2 = powf(2.0f, floorf(logf(numberOfComponents - 1.0)/logf(2.0f) + 1.0f));
         m_periodicWaveSize = std::min(MaxPeriodicWaveSize, npow2);
@@ -266,26 +269,28 @@ void PeriodicWave::createBandLimitedTabl
     m_bandLimitedTables[rangeIndex] = table;
 
     // Apply an inverse FFT to generate the time-domain table data.
     float* data = m_bandLimitedTables[rangeIndex]->Elements();
     frame.GetInverseWithoutScaling(data);
 
     // For the first range (which has the highest power), calculate
     // its peak value then compute normalization scale.
-    if (!rangeIndex) {
+    if (!m_disableNormalization && !rangeIndex) {
         float maxValue;
         maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
 
         if (maxValue)
             m_normalizationScale = 1.0f / maxValue;
     }
 
     // Apply normalization scale.
-    AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize);
+    if (!m_disableNormalization) {
+      AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize);
+    }
 }
 
 void PeriodicWave::generateBasicWaveform(OscillatorType shape)
 {
     const float piFloat = float(M_PI);
     unsigned fftSize = periodicWaveSize();
     unsigned halfSize = fftSize / 2;
 
--- a/dom/media/webaudio/blink/PeriodicWave.h
+++ b/dom/media/webaudio/blink/PeriodicWave.h
@@ -49,17 +49,18 @@ public:
     static already_AddRefed<PeriodicWave> createSawtooth(float sampleRate);
     static already_AddRefed<PeriodicWave> createTriangle(float sampleRate);
 
     // Creates an arbitrary periodic wave given the frequency components
     // (Fourier coefficients).
     static already_AddRefed<PeriodicWave> create(float sampleRate,
                                                  const float* real,
                                                  const float* imag,
-                                                 size_t numberOfComponents);
+                                                 size_t numberOfComponents,
+                                                 bool disableNormalization);
 
     // Returns pointers to the lower and higher wave data for the pitch range
     // containing the given fundamental frequency. These two tables are in
     // adjacent "pitch" ranges where the higher table will have the maximum
     // number of partials which won't alias when played back at this
     // fundamental frequency. The lower wave is the next range containing fewer
     // partials than the higher wave. Interpolation between these two tables
     // can be made according to tableInterpolationFactor. Where values
@@ -71,17 +72,17 @@ public:
     float rateScale() const { return m_rateScale; }
 
     unsigned periodicWaveSize() const { return m_periodicWaveSize; }
     float sampleRate() const { return m_sampleRate; }
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 private:
-    explicit PeriodicWave(float sampleRate, size_t numberOfComponents);
+    explicit PeriodicWave(float sampleRate, size_t numberOfComponents, bool disableNormalization);
     ~PeriodicWave() {}
 
     void generateBasicWaveform(mozilla::dom::OscillatorType);
 
     float m_sampleRate;
     unsigned m_periodicWaveSize;
     unsigned m_numberOfRanges;
     float m_centsPerRange;
@@ -103,14 +104,15 @@ private:
     unsigned maxNumberOfPartials() const;
 
     unsigned numberOfPartialsForRange(unsigned rangeIndex) const;
 
     // Creates table for specified index based on fundamental frequency.
     void createBandLimitedTables(float fundamentalFrequency, unsigned rangeIndex);
     unsigned m_maxPartialsInBandLimitedTable;
     float m_normalizationScale;
+    bool m_disableNormalization;
     nsTArray<nsAutoPtr<AlignedAudioFloatArray> > m_bandLimitedTables;
 };
 
 } // namespace WebCore
 
 #endif // PeriodicWave_h
--- a/dom/media/webaudio/blink/ReverbConvolver.cpp
+++ b/dom/media/webaudio/blink/ReverbConvolver.cpp
@@ -153,18 +153,18 @@ ReverbConvolver::ReverbConvolver(const f
 
     // Start up background thread
     // FIXME: would be better to up the thread priority here.  It doesn't need to be real-time, but higher than the default...
     if (this->useBackgroundThreads() && m_backgroundStages.Length() > 0) {
         if (!m_backgroundThread.Start()) {
           NS_WARNING("Cannot start convolver thread.");
           return;
         }
-        CancelableTask* task = NewRunnableMethod(this, &ReverbConvolver::backgroundThreadEntry);
-        m_backgroundThread.message_loop()->PostTask(FROM_HERE, task);
+        m_backgroundThread.message_loop()->PostTask(
+	  NewRunnableMethod(this, &ReverbConvolver::backgroundThreadEntry));
     }
 }
 
 ReverbConvolver::~ReverbConvolver()
 {
     // Wait for background thread to stop
     if (useBackgroundThreads() && m_backgroundThread.IsRunning()) {
         m_wantsToExit = true;
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -166,16 +166,17 @@ skip-if = (toolkit == 'gonk' && !debug) 
 [test_pannerNodeAbove.html]
 [test_pannerNodeChannelCount.html]
 [test_pannerNodeHRTFSymmetry.html]
 [test_pannerNodeTail.html]
 [test_pannerNode_maxDistance.html]
 [test_stereoPannerNode.html]
 [test_stereoPannerNodePassThrough.html]
 [test_periodicWave.html]
+[test_periodicWaveDisableNormalization.html]
 [test_periodicWaveBandLimiting.html]
 [test_ScriptProcessorCollected1.html]
 [test_scriptProcessorNode.html]
 [test_scriptProcessorNodeChannelCount.html]
 [test_scriptProcessorNodePassThrough.html]
 [test_scriptProcessorNode_playbackTime1.html]
 [test_scriptProcessorNodeZeroInputOutput.html]
 [test_scriptProcessorNodeNotConnected.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_periodicWaveDisableNormalization.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test PeriodicWave disableNormalization Parameter</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// We create PerodicWave instances containing two tones and compare it to
+// buffers created directly in JavaScript by adding the two waves together.
+// Two of the PeriodicWaves are normalized, the other is not. This test is
+// a modification of test_periodicWave.html.
+//
+// These constants are borrowed from test_periodicWave.html and modified
+// so that the realPeak (which is the normalization factor) will be small
+// enough that the errors are within the bounds for the test.
+const realMax = 99;
+var real = new Float32Array(realMax + 1);
+real[1] = 2.0; // fundamental
+real[realMax] = 0.25;
+
+const realPeak = real[1] + real[realMax];
+const realFundamental = 19.0;
+
+const testLength = 4096;
+
+addLoadEvent(function() {
+  runTest();
+});
+
+var gTest = {
+  createGraph: function(context) {
+    var merger = context.createChannelMerger();
+
+    var osc0 = context.createOscillator();
+    var osc1 = context.createOscillator();
+    var osc2 = context.createOscillator();
+
+    osc0.setPeriodicWave(context.
+                         createPeriodicWave(real,
+                                            new Float32Array(real.length),
+                                            {disableNormalization: false}));
+    osc1.setPeriodicWave(context.
+                         createPeriodicWave(real,
+                                            new Float32Array(real.length)));
+    osc2.setPeriodicWave(context.
+                         createPeriodicWave(real,
+                                            new Float32Array(real.length),
+                                            {disableNormalization: true}));
+
+    osc0.frequency.value = realFundamental;
+    osc1.frequency.value = realFundamental;
+    osc2.frequency.value = realFundamental;
+
+    osc0.start();
+    osc1.start();
+    osc2.start();
+
+    osc0.connect(merger, 0, 0);
+    osc1.connect(merger, 0, 1);
+    osc2.connect(merger, 0, 2);
+
+    return merger;
+  },
+  createExpectedBuffers: function(context) {
+    var buffer = context.createBuffer(3, testLength, context.sampleRate);
+
+    for (var i = 0; i < buffer.length; ++i) {
+
+      buffer.getChannelData(0)[i] = 1.0 / realPeak *
+        (real[1] * Math.cos(2 * Math.PI * realFundamental * i /
+                            context.sampleRate) +
+         real[realMax] * Math.cos(2 * Math.PI * realMax * realFundamental * i /
+                            context.sampleRate));
+
+      buffer.getChannelData(1)[i] = buffer.getChannelData(0)[i];
+
+      // TODO: We need to scale by a factor of two to make the results work
+      //       out here. This seems suspicious, see Bug 1266737.
+      buffer.getChannelData(2)[i] = 2.0 *
+        (real[1] * Math.cos(2 * Math.PI * realFundamental * i /
+                            context.sampleRate) +
+         real[realMax] * Math.cos(2 * Math.PI * realMax * realFundamental * i /
+                            context.sampleRate));
+    }
+    return buffer;
+  },
+  'numberOfChannels': 3,
+};
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/nfc/gonk/NfcService.cpp
+++ b/dom/nfc/gonk/NfcService.cpp
@@ -166,17 +166,17 @@ NfcConsumer::Send(const CommandOptions& 
   // TODO: Zero-copy buffer transfers
   mStreamSocket->SendSocketData(
     new UnixSocketRawData(parcel.data(), parcel.dataSize()));
 
   return NS_OK;
 }
 
 // Runnable used dispatch the NfcEventOptions on the main thread.
-class NfcConsumer::DispatchNfcEventRunnable final : public nsRunnable
+class NfcConsumer::DispatchNfcEventRunnable final : public Runnable
 {
 public:
   DispatchNfcEventRunnable(NfcService* aNfcService, const EventOptions& aEvent)
     : mNfcService(aNfcService)
     , mEvent(aEvent)
   {
     MOZ_ASSERT(mNfcService);
   }
@@ -387,17 +387,17 @@ NfcConsumer::OnConnectSuccess(int aIndex
       break;
     }
     case STREAM_SOCKET:
       /* nothing to do */
       break;
   }
 }
 
-class NfcConsumer::ShutdownServiceRunnable final : public nsRunnable
+class NfcConsumer::ShutdownServiceRunnable final : public Runnable
 {
 public:
   ShutdownServiceRunnable(NfcService* aNfcService)
     : mNfcService(aNfcService)
   {
     MOZ_ASSERT(mNfcService);
   }
 
@@ -459,17 +459,17 @@ NfcService::FactoryCreate()
 
   RefPtr<NfcService> service(gNfcService);
   return service.forget();
 }
 
 /**
  * |StartConsumerRunnable| calls |NfcConsumer::Start| on the NFC thread.
  */
-class NfcService::StartConsumerRunnable final : public nsRunnable
+class NfcService::StartConsumerRunnable final : public Runnable
 {
 public:
   StartConsumerRunnable(NfcConsumer* aNfcConsumer)
     : mNfcConsumer(aNfcConsumer)
   {
     MOZ_ASSERT(mNfcConsumer);
   }
 
@@ -512,17 +512,17 @@ NfcService::Start(nsINfcGonkEventListene
   return NS_OK;
 }
 
 /**
  * |CleanupRunnable| deletes instances of the NFC consumer and
  * thread on the main thread. This has to be down after shutting
  * down the NFC consumer on the NFC thread.
  */
-class NfcService::CleanupRunnable final : public nsRunnable
+class NfcService::CleanupRunnable final : public Runnable
 {
 public:
   CleanupRunnable(NfcConsumer* aNfcConsumer,
                   already_AddRefed<nsIThread> aThread)
     : mNfcConsumer(aNfcConsumer)
     , mThread(aThread)
   {
     MOZ_ASSERT(mNfcConsumer);
@@ -546,17 +546,17 @@ private:
   nsCOMPtr<nsIThread> mThread;
 };
 
 /**
  * |ShutdownConsumerRunnable| calls |NfcConsumer::Shutdown| on the
  * NFC thread. Optionally, it can dispatch a |CleanupRunnable| to
  * the main thread for cleaning up the NFC resources.
  */
-class NfcService::ShutdownConsumerRunnable final : public nsRunnable
+class NfcService::ShutdownConsumerRunnable final : public Runnable
 {
 public:
   ShutdownConsumerRunnable(NfcConsumer* aNfcConsumer, bool aCleanUp)
     : mNfcConsumer(aNfcConsumer)
     , mCleanUp(aCleanUp)
   {
     MOZ_ASSERT(mNfcConsumer);
   }
@@ -598,17 +598,17 @@ NfcService::Shutdown()
   Unused << mThread.forget();
 
   return NS_OK;
 }
 
 /**
  * |SendRunnable| calls |NfcConsumer::Send| on the NFC thread.
  */
-class NfcService::SendRunnable final : public nsRunnable
+class NfcService::SendRunnable final : public Runnable
 {
 public:
   SendRunnable(NfcConsumer* aNfcConsumer, const CommandOptions& aOptions)
     : mNfcConsumer(aNfcConsumer)
     , mOptions(aOptions)
   {
     MOZ_ASSERT(mNfcConsumer);
   }
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -641,17 +641,16 @@ nsresult nsPluginHost::FindProxyForURL(c
   }
 
   // make a temporary channel from the argument url
   nsCOMPtr<nsIURI> uri;
   res = NS_NewURI(getter_AddRefs(uri), nsDependentCString(url));
   NS_ENSURE_SUCCESS(res, res);
 
   nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
-  NS_ENSURE_TRUE(nullPrincipal, NS_ERROR_FAILURE);
   // The following channel is never openend, so it does not matter what
   // securityFlags we pass; let's follow the principle of least privilege.
   nsCOMPtr<nsIChannel> tempChannel;
   res = NS_NewChannel(getter_AddRefs(tempChannel), uri, nullPrincipal,
                       nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                       nsIContentPolicy::TYPE_OTHER);
   NS_ENSURE_SUCCESS(res, res);
 
--- a/dom/plugins/ipc/BrowserStreamChild.cpp
+++ b/dom/plugins/ipc/BrowserStreamChild.cpp
@@ -185,17 +185,17 @@ BrowserStreamChild::NPN_DestroyStream(NP
     SendNPN_DestroyStream(reason);
 
   EnsureDeliveryPending();
 }
 
 void
 BrowserStreamChild::EnsureDeliveryPending()
 {
-  MessageLoop::current()->PostTask(FROM_HERE,
+  MessageLoop::current()->PostTask(
     mDeliveryTracker.NewRunnableMethod(&BrowserStreamChild::Deliver));
 }
 
 void
 BrowserStreamChild::Deliver()
 {
   while (kStreamOpen == mStreamStatus && mPendingData.Length()) {
     if (DeliverPendingData() && kStreamOpen == mStreamStatus) {
--- a/dom/plugins/ipc/ChildAsyncCall.cpp
+++ b/dom/plugins/ipc/ChildAsyncCall.cpp
@@ -14,36 +14,39 @@ namespace plugins {
 ChildAsyncCall::ChildAsyncCall(PluginInstanceChild* instance,
                                PluginThreadCallback aFunc, void* aUserData)
   : mInstance(instance)
   , mFunc(aFunc)
   , mData(aUserData)
 {
 }
 
-void
+nsresult
 ChildAsyncCall::Cancel()
 {
   mInstance = nullptr;
   mFunc = nullptr;
   mData = nullptr;
+  return NS_OK;
 }
 
 void
 ChildAsyncCall::RemoveFromAsyncList()
 {
   if (mInstance) {
     MutexAutoLock lock(mInstance->mAsyncCallMutex);
     mInstance->mPendingAsyncCalls.RemoveElement(this);
   }
 }
 
-void
+NS_IMETHODIMP
 ChildAsyncCall::Run()
 {
   RemoveFromAsyncList();
 
   if (mFunc)
     mFunc(mData);
+
+  return NS_OK;
 }
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/ChildAsyncCall.h
+++ b/dom/plugins/ipc/ChildAsyncCall.h
@@ -4,33 +4,33 @@
 /* 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_plugins_ChildAsyncCall_h
 #define mozilla_plugins_ChildAsyncCall_h
 
 #include "PluginMessageUtils.h"
-#include "base/task.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace plugins {
 
 typedef void (*PluginThreadCallback)(void*);
 
 class PluginInstanceChild;
 
-class ChildAsyncCall : public CancelableTask
+class ChildAsyncCall : public CancelableRunnable
 {
 public:
   ChildAsyncCall(PluginInstanceChild* instance,
                  PluginThreadCallback aFunc, void* aUserData);
 
-  void Run() override;
-  void Cancel() override;
+  NS_IMETHOD Run() override;
+  nsresult Cancel() override;
   
 protected:
   PluginInstanceChild* mInstance;
   PluginThreadCallback mFunc;
   void* mData;
 
   void RemoveFromAsyncList();
 };
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -190,18 +190,16 @@ PluginInstanceChild::PluginInstanceChild
     , mLayersRendering(false)
 #ifdef XP_WIN
     , mCurrentSurfaceActor(nullptr)
     , mBackSurfaceActor(nullptr)
 #endif
     , mAccumulatedInvalidRect(0,0,0,0)
     , mIsTransparent(false)
     , mSurfaceType(gfxSurfaceType::Max)
-    , mCurrentInvalidateTask(nullptr)
-    , mCurrentAsyncSetWindowTask(nullptr)
     , mPendingPluginCall(false)
     , mDoAlphaExtraction(false)
     , mHasPainted(false)
     , mSurfaceDifferenceRect(0,0,0,0)
     , mDestroyed(false)
 #ifdef XP_WIN
     , mLastKeyEventConsumed(false)
 #endif // #ifdef XP_WIN
@@ -2619,48 +2617,50 @@ PluginInstanceChild::FlashThrottleAsyncM
 { 
     if (mInstance) {
         return mWindowed ? mInstance->mPluginWndProc :
                            mInstance->mWinlessThrottleOldWndProc;
     }
     return nullptr;
 }
  
-void
+NS_IMETHODIMP
 PluginInstanceChild::FlashThrottleAsyncMsg::Run()
 {
     RemoveFromAsyncList();
 
     // GetProc() checks mInstance, and pulls the procedure from
     // PluginInstanceChild. We don't transport sub-class procedure
     // ptrs around in FlashThrottleAsyncMsg msgs.
     if (!GetProc())
-        return;
+        return NS_OK;
   
     // deliver the event to flash 
     CallWindowProc(GetProc(), GetWnd(), GetMsg(), GetWParam(), GetLParam());
+    return NS_OK;
 }
 
 void
 PluginInstanceChild::FlashThrottleMessage(HWND aWnd,
                                           UINT aMsg,
                                           WPARAM aWParam,
                                           LPARAM aLParam,
                                           bool isWindowed)
 {
     // We reuse ChildAsyncCall so we get the cancelation work
     // that's done in Destroy.
-    FlashThrottleAsyncMsg* task = new FlashThrottleAsyncMsg(this,
-        aWnd, aMsg, aWParam, aLParam, isWindowed);
+    RefPtr<FlashThrottleAsyncMsg> task =
+        new FlashThrottleAsyncMsg(this, aWnd, aMsg, aWParam,
+                                  aLParam, isWindowed);
     {
         MutexAutoLock lock(mAsyncCallMutex);
         mPendingAsyncCalls.AppendElement(task);
     }
-    MessageLoop::current()->PostDelayedTask(FROM_HERE,
-        task, kFlashWMUSERMessageThrottleDelayMs);
+    MessageLoop::current()->PostDelayedTask(task.forget(),
+                                            kFlashWMUSERMessageThrottleDelayMs);
 }
 
 #endif // OS_WIN
 
 bool
 PluginInstanceChild::AnswerSetPluginFocus()
 {
     MOZ_LOG(GetPluginLog(), LogLevel::Debug, ("%s", FULLFUNCTION));
@@ -2792,44 +2792,45 @@ public:
                        const bool aSeekable)
         : ChildAsyncCall(aInstance, nullptr, nullptr)
         , mBrowserStreamChild(aBrowserStreamChild)
         , mMimeType(aMimeType)
         , mSeekable(aSeekable)
     {
     }
 
-    void Run() override
+    NS_IMETHOD Run() override
     {
         RemoveFromAsyncList();
 
         uint16_t stype = NP_NORMAL;
         NPError rv = mInstance->DoNPP_NewStream(mBrowserStreamChild, mMimeType,
                                                 mSeekable, &stype);
         DebugOnly<bool> sendOk =
             mBrowserStreamChild->SendAsyncNPP_NewStreamResult(rv, stype);
         MOZ_ASSERT(sendOk);
+        return NS_OK;
     }
 
 private:
     BrowserStreamChild* mBrowserStreamChild;
     const nsCString     mMimeType;
     const bool          mSeekable;
 };
 
 bool
 PluginInstanceChild::RecvAsyncNPP_NewStream(PBrowserStreamChild* actor,
                                             const nsCString& mimeType,
                                             const bool& seekable)
 {
     // Reusing ChildAsyncCall so that the task is cancelled properly on Destroy
     BrowserStreamChild* child = static_cast<BrowserStreamChild*>(actor);
-    NewStreamAsyncCall* task = new NewStreamAsyncCall(this, child, mimeType,
-                                                      seekable);
-    PostChildAsyncCall(task);
+    RefPtr<NewStreamAsyncCall> task =
+        new NewStreamAsyncCall(this, child, mimeType, seekable);
+    PostChildAsyncCall(task.forget());
     return true;
 }
 
 PBrowserStreamChild*
 PluginInstanceChild::AllocPBrowserStreamChild(const nsCString& url,
                                               const uint32_t& length,
                                               const uint32_t& lastmodified,
                                               PStreamNotifyChild* notifyData,
@@ -3298,17 +3299,18 @@ PluginInstanceChild::RecvAsyncSetWindow(
     // RPC call, and both Flash and Java don't expect to receive setwindow calls
     // at arbitrary times.
     mCurrentAsyncSetWindowTask =
         NewRunnableMethod<PluginInstanceChild,
                           void (PluginInstanceChild::*)(const gfxSurfaceType&, const NPRemoteWindow&, bool),
                           const gfxSurfaceType&, const NPRemoteWindow&, bool>
         (this, &PluginInstanceChild::DoAsyncSetWindow,
          aSurfaceType, aWindow, true);
-    MessageLoop::current()->PostTask(FROM_HERE, mCurrentAsyncSetWindowTask);
+    RefPtr<Runnable> addrefedTask = mCurrentAsyncSetWindowTask;
+    MessageLoop::current()->PostTask(addrefedTask.forget());
 
     return true;
 }
 
 void
 PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
                                       const NPRemoteWindow& aWindow,
                                       bool aIsAsync)
@@ -4217,17 +4219,18 @@ PluginInstanceChild::AsyncShowPluginFram
     // paints via paint events - it will drive painting via its own events
     // and/or DidComposite callbacks.
     if (IsUsingDirectDrawing()) {
         return;
     }
 
     mCurrentInvalidateTask =
         NewRunnableMethod(this, &PluginInstanceChild::InvalidateRectDelayed);
-    MessageLoop::current()->PostTask(FROM_HERE, mCurrentInvalidateTask);
+    RefPtr<Runnable> addrefedTask = mCurrentInvalidateTask;
+    MessageLoop::current()->PostTask(addrefedTask.forget());
 }
 
 void
 PluginInstanceChild::InvalidateRect(NPRect* aInvalidRect)
 {
     NS_ASSERTION(aInvalidRect, "Null pointer!");
 
 #ifdef OS_WIN
@@ -4373,28 +4376,30 @@ PluginInstanceChild::UnscheduleTimer(uin
         return;
 
     mTimers.RemoveElement(id, ChildTimer::IDComparator());
 }
 
 void
 PluginInstanceChild::AsyncCall(PluginThreadCallback aFunc, void* aUserData)
 {
-    ChildAsyncCall* task = new ChildAsyncCall(this, aFunc, aUserData);
-    PostChildAsyncCall(task);
+    RefPtr<ChildAsyncCall> task = new ChildAsyncCall(this, aFunc, aUserData);
+    PostChildAsyncCall(task.forget());
 }
 
 void
-PluginInstanceChild::PostChildAsyncCall(ChildAsyncCall* aTask)
+PluginInstanceChild::PostChildAsyncCall(already_AddRefed<ChildAsyncCall> aTask)
 {
+    RefPtr<ChildAsyncCall> task = aTask;
+
     {
         MutexAutoLock lock(mAsyncCallMutex);
-        mPendingAsyncCalls.AppendElement(aTask);
-    }
-    ProcessChild::message_loop()->PostTask(FROM_HERE, aTask);
+        mPendingAsyncCalls.AppendElement(task);
+    }
+    ProcessChild::message_loop()->PostTask(task.forget());
 }
 
 void
 PluginInstanceChild::SwapSurfaces()
 {
     RefPtr<gfxASurface> tmpsurf = mCurrentSurface;
 #ifdef XP_WIN
     PPluginSurfaceChild* tmpactor = mCurrentSurfaceActor;
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -254,17 +254,17 @@ public:
     void Invalidate();
 #endif // definied(MOZ_WIDGET_COCOA)
 
     uint32_t ScheduleTimer(uint32_t interval, bool repeat, TimerFunc func);
     void UnscheduleTimer(uint32_t id);
 
     void AsyncCall(PluginThreadCallback aFunc, void* aUserData);
     // This function is a more general version of AsyncCall
-    void PostChildAsyncCall(ChildAsyncCall* aTask);
+    void PostChildAsyncCall(already_AddRefed<ChildAsyncCall> aTask);
 
     int GetQuirks();
 
     void NPN_URLRedirectResponse(void* notifyData, NPBool allow);
 
 
     NPError NPN_InitAsyncSurface(NPSize *size, NPImageFormat format,
                                  void *initData, NPAsyncSurface *surface);
@@ -379,17 +379,17 @@ private:
           : ChildAsyncCall(aInst, nullptr, nullptr),
           mWnd(aWnd),
           mMsg(aMsg),
           mWParam(aWParam),
           mLParam(aLParam),
           mWindowed(isWindowed)
         {}
 
-        void Run() override;
+        NS_IMETHOD Run() override;
 
         WNDPROC GetProc();
         HWND GetWnd() { return mWnd; }
         UINT GetMsg() { return mMsg; }
         WPARAM GetWParam() { return mWParam; }
         LPARAM GetLParam() { return mLParam; }
 
       private:
@@ -441,17 +441,17 @@ private:
     };
     nsRefPtrHashtable<nsPtrHashKey<NPAsyncSurface>, DirectBitmap> mDirectBitmaps;
 
 #if defined(XP_WIN)
     nsDataHashtable<nsPtrHashKey<NPAsyncSurface>, WindowsHandle> mDxgiSurfaces;
 #endif
 
     mozilla::Mutex mAsyncInvalidateMutex;
-    CancelableTask *mAsyncInvalidateTask;
+    CancelableRunnable *mAsyncInvalidateTask;
 
     // Cached scriptable actors to avoid IPC churn
     PluginScriptableObjectChild* mCachedWindowActor;
     PluginScriptableObjectChild* mCachedElementActor;
 
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     NPSetWindowCallbackStruct mWsInfo;
 #ifdef MOZ_WIDGET_GTK
@@ -632,20 +632,20 @@ private:
     // and does not remember their transparent state
     // and p->getvalue return always false
     bool mIsTransparent;
 
     // Surface type optimized of parent process
     gfxSurfaceType mSurfaceType;
 
     // Keep InvalidateRect task pointer to be able Cancel it on Destroy
-    CancelableTask *mCurrentInvalidateTask;
+    RefPtr<CancelableRunnable> mCurrentInvalidateTask;
 
     // Keep AsyncSetWindow task pointer to be able to Cancel it on Destroy
-    CancelableTask *mCurrentAsyncSetWindowTask;
+    RefPtr<CancelableRunnable> mCurrentAsyncSetWindowTask;
 
     // True while plugin-child in plugin call
     // Use to prevent plugin paint re-enter
     bool mPendingPluginCall;
 
     // On some platforms, plugins may not support rendering to a surface with
     // alpha, or not support rendering to an image surface.
     // In those cases we need to draw to a temporary platform surface; we cache
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -146,17 +146,16 @@ PluginInstanceParent::PluginInstancePare
     , mNestedEventState(false)
 #endif // defined(XP_WIN)
 #if defined(XP_MACOSX)
     , mShWidth(0)
     , mShHeight(0)
     , mShColorSpace(nullptr)
 #endif
 #if defined(XP_WIN)
-    , mCaptureRefreshTask(nullptr)
     , mValidFirstCapture(false)
     , mIsScrolling(false)
 #endif
 {
 #if defined(OS_WIN)
     if (!sPluginInstanceList) {
         sPluginInstanceList = new nsClassHashtable<nsVoidPtrHashKey, PluginInstanceParent>();
     }
@@ -1220,17 +1219,18 @@ void
 PluginInstanceParent::ScheduleScrollCapture(int aTimeout)
 {
     if (mCaptureRefreshTask) {
         return;
     }
     CAPTURE_LOG("delayed scroll capture requested.");
     mCaptureRefreshTask =
         NewRunnableMethod(this, &PluginInstanceParent::ScheduledUpdateScrollCaptureCallback);
-    MessageLoop::current()->PostDelayedTask(FROM_HERE, mCaptureRefreshTask,
+    RefPtr<Runnable> addrefedTask = mCaptureRefreshTask;
+    MessageLoop::current()->PostDelayedTask(addrefedTask.forget(),
                                             kScrollCaptureDelayMs);
 }
 
 void
 PluginInstanceParent::ScheduledUpdateScrollCaptureCallback()
 {
     CAPTURE_LOG("taking delayed scrollcapture.");
     mCaptureRefreshTask = nullptr;
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -469,17 +469,17 @@ private:
 
 #if defined(XP_WIN)
     void ScheduleScrollCapture(int aTimeout);
     void ScheduledUpdateScrollCaptureCallback();
     bool UpdateScrollCapture(bool& aRequestNewCapture);
     void CancelScheduledScrollCapture();
 
     RefPtr<gfxASurface> mScrollCapture;
-    CancelableTask* mCaptureRefreshTask;
+    RefPtr<CancelableRunnable> mCaptureRefreshTask;
     bool mValidFirstCapture;
     bool mIsScrolling;
 #endif
 };
 
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginMessageUtils.cpp
+++ b/dom/plugins/ipc/PluginMessageUtils.cpp
@@ -60,18 +60,18 @@ NPRemoteWindow::NPRemoteWindow() :
 {
   clipRect.top = 0;
   clipRect.left = 0;
   clipRect.bottom = 0;
   clipRect.right = 0;
 }
 
 ipc::RacyInterruptPolicy
-MediateRace(const MessageChannel::Message& parent,
-            const MessageChannel::Message& child)
+MediateRace(const MessageChannel::MessageInfo& parent,
+            const MessageChannel::MessageInfo& child)
 {
   switch (parent.type()) {
   case PPluginInstance::Msg_Paint__ID:
   case PPluginInstance::Msg_NPP_SetWindow__ID:
   case PPluginInstance::Msg_NPP_HandleEvent_Shmem__ID:
   case PPluginInstance::Msg_NPP_HandleEvent_IOSurface__ID:
     // our code relies on the frame list not changing during paints and
     // reflows
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -40,18 +40,18 @@ using layers::SurfaceDescriptorX11;
 
 enum ScriptableObjectType
 {
   LocalObject,
   Proxy
 };
 
 mozilla::ipc::RacyInterruptPolicy
-MediateRace(const mozilla::ipc::MessageChannel::Message& parent,
-            const mozilla::ipc::MessageChannel::Message& child);
+MediateRace(const mozilla::ipc::MessageChannel::MessageInfo& parent,
+            const mozilla::ipc::MessageChannel::MessageInfo& child);
 
 std::string
 MungePluginDsoPath(const std::string& path);
 std::string
 UnmungePluginDsoPath(const std::string& munged);
 
 extern mozilla::LogModule* GetPluginLog();
 
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -161,17 +161,18 @@ PluginModuleChild::PluginModuleChild(boo
 
 PluginModuleChild::~PluginModuleChild()
 {
     if (mTransport) {
         // For some reason IPDL doesn't automatically delete the channel for a
         // bridged protocol (bug 1090570). So we have to do it ourselves. This
         // code is only invoked for PluginModuleChild instances created via
         // bridging; otherwise mTransport is null.
-        XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport));
+        RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
+        XRE_GetIOMessageLoop()->PostTask(task.forget());
     }
 
     if (mIsChrome) {
         MOZ_ASSERT(gChromeInstance == this);
 
         // We don't unload the plugin library in case it uses atexit handlers or
         // other similar hooks.
 
@@ -826,17 +827,19 @@ PluginModuleChild::ActorDestroy(ActorDes
 {
     if (!mIsChrome) {
         PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
         if (chromeInstance) {
             chromeInstance->SendNotifyContentModuleDestroyed();
         }
 
         // Destroy ourselves once we finish other teardown activities.
-        MessageLoop::current()->PostTask(FROM_HERE, new DeleteTask<PluginModuleChild>(this));
+        RefPtr<DeleteTask<PluginModuleChild>> task =
+            new DeleteTask<PluginModuleChild>(this);
+        MessageLoop::current()->PostTask(task.forget());
         return;
     }
 
     if (AbnormalShutdown == why) {
         NS_WARNING("shutting down early because of crash!");
         ProcessChild::QuickExit();
     }
 
@@ -2201,36 +2204,38 @@ class AsyncNewResultSender : public Chil
 {
 public:
     AsyncNewResultSender(PluginInstanceChild* aInstance, NPError aResult)
         : ChildAsyncCall(aInstance, nullptr, nullptr)
         , mResult(aResult)
     {
     }
 
-    void Run() override
+    NS_IMETHOD Run() override
     {
         RemoveFromAsyncList();
         DebugOnly<bool> sendOk = mInstance->SendAsyncNPP_NewResult(mResult);
         MOZ_ASSERT(sendOk);
+        return NS_OK;
     }
 
 private:
     NPError  mResult;
 };
 
 static void
 RunAsyncNPP_New(void* aChildInstance)
 {
     MOZ_ASSERT(aChildInstance);
     PluginInstanceChild* childInstance =
         static_cast<PluginInstanceChild*>(aChildInstance);
     NPError rv = childInstance->DoNPP_New();
-    AsyncNewResultSender* task = new AsyncNewResultSender(childInstance, rv);
-    childInstance->PostChildAsyncCall(task);
+    RefPtr<AsyncNewResultSender> task =
+        new AsyncNewResultSender(childInstance, rv);
+    childInstance->PostChildAsyncCall(task.forget());
 }
 
 bool
 PluginModuleChild::RecvAsyncNPP_New(PPluginInstanceChild* aActor)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     PluginInstanceChild* childInstance =
         reinterpret_cast<PluginInstanceChild*>(aActor);
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -60,17 +60,18 @@ static const int kNestedLoopDetectorInte
 
 class PluginInstanceChild;
 
 class PluginModuleChild : public PPluginModuleChild
 {
     typedef mozilla::dom::PCrashReporterChild PCrashReporterChild;
 protected:
     virtual mozilla::ipc::RacyInterruptPolicy
-    MediateInterruptRace(const Message& parent, const Message& child) override
+    MediateInterruptRace(const MessageInfo& parent,
+                         const MessageInfo& child) override
     {
         return MediateRace(parent, child);
     }
 
     virtual bool ShouldContinueFromReplyTimeout() override;
 
     virtual bool RecvSettingChanged(const PluginSettings& aSettings) override;
 
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -145,17 +145,17 @@ mozilla::plugins::SetupBridge(uint32_t a
     return true;
 }
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 
 /**
  * Use for executing CreateToolhelp32Snapshot off main thread
  */
-class mozilla::plugins::FinishInjectorInitTask : public CancelableTask
+class mozilla::plugins::FinishInjectorInitTask : public mozilla::CancelableRunnable
 {
 public:
     FinishInjectorInitTask()
         : mMutex("FlashInjectorInitTask::mMutex")
         , mParent(nullptr)
         , mMainThreadMsgLoop(MessageLoop::current())
     {
         MOZ_ASSERT(NS_IsMainThread());
@@ -164,44 +164,41 @@ public:
     void Init(PluginModuleChromeParent* aParent)
     {
         MOZ_ASSERT(aParent);
         mParent = aParent;
     }
 
     void PostToMainThread()
     {
+        RefPtr<Runnable> self = this;
         mSnapshot.own(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
-        bool deleteThis = false;
         {   // Scope for lock
             mozilla::MutexAutoLock lock(mMutex);
             if (mMainThreadMsgLoop) {
-                mMainThreadMsgLoop->PostTask(FROM_HERE, this);
-            } else {
-                deleteThis = true;
+                mMainThreadMsgLoop->PostTask(self.forget());
             }
         }
-        if (deleteThis) {
-            delete this;
-        }
     }
 
-    void Run() override
+    NS_IMETHOD Run() override
     {
         mParent->DoInjection(mSnapshot);
         // We don't need to hold this lock during DoInjection, but we do need
         // to obtain it before returning from Run() to ensure that
         // PostToMainThread has completed before we return.
         mozilla::MutexAutoLock lock(mMutex);
+        return NS_OK;
     }
 
-    void Cancel() override
+    nsresult Cancel() override
     {
         mozilla::MutexAutoLock lock(mMutex);
         mMainThreadMsgLoop = nullptr;
+        return NS_OK;
     }
 
 private:
     mozilla::Mutex            mMutex;
     nsAutoHandle              mSnapshot;
     PluginModuleChromeParent* mParent;
     MessageLoop*              mMainThreadMsgLoop;
 };
@@ -705,18 +702,19 @@ PluginModuleParent::~PluginModuleParent(
 PluginModuleContentParent::PluginModuleContentParent(bool aAllowAsyncInit)
     : PluginModuleParent(false, aAllowAsyncInit)
 {
     Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref, this);
 }
 
 PluginModuleContentParent::~PluginModuleContentParent()
 {
-    XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                     new DeleteTask<Transport>(GetTransport()));
+    RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
+    XRE_GetIOMessageLoop()->PostTask(task.forget());
+                                     
 
     Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
 }
 
 bool PluginModuleChromeParent::sInstantiated = false;
 
 PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
                                                    uint32_t aPluginId,
@@ -901,17 +899,16 @@ PluginModuleChromeParent::CleanupFromTim
 {
     if (mShutdown) {
       return;
     }
 
     if (!OkToCleanup()) {
         // there's still plugin code on the C++ stack, try again
         MessageLoop::current()->PostDelayedTask(
-            FROM_HERE,
             mChromeTaskFactory.NewRunnableMethod(
                 &PluginModuleChromeParent::CleanupFromTimeout, aFromHangUI), 10);
         return;
     }
 
     /* If the plugin container was terminated by the Plugin Hang UI, 
        then either the I/O thread detects a channel error, or the 
        main thread must set the error (whomever gets there first).
@@ -1166,17 +1163,16 @@ CreatePluginMinidump(base::ProcessId pro
 }
 #endif
 
 bool
 PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
 {
     if (mIsFlashPlugin) {
         MessageLoop::current()->PostTask(
-            FROM_HERE,
             mTaskFactory.NewRunnableMethod(
                 &PluginModuleChromeParent::NotifyFlashHang));
     }
 
 #ifdef XP_WIN
     if (LaunchHangUI()) {
         return true;
     }
@@ -1344,17 +1340,16 @@ PluginModuleChromeParent::TerminateChild
       mPluginCpuUsageOnHang.Clear();
     }
 #endif
 
     // this must run before the error notification from the channel,
     // or not at all
     bool isFromHangUI = aMsgLoop != MessageLoop::current();
     aMsgLoop->PostTask(
-        FROM_HERE,
         mChromeTaskFactory.NewRunnableMethod(
             &PluginModuleChromeParent::CleanupFromTimeout, isFromHangUI));
 
     if (!childOpened || !KillProcess(geckoChildProcess, 1, false)) {
         NS_WARNING("failed to kill subprocess!");
     }
 }
 
@@ -1594,17 +1589,16 @@ PluginModuleParent::ActorDestroy(ActorDe
 {
     switch (why) {
     case AbnormalShutdown: {
         mShutdown = true;
         // Defer the PluginCrashed method so that we don't re-enter
         // and potentially modify the actor child list while enumerating it.
         if (mPlugin)
             MessageLoop::current()->PostTask(
-                FROM_HERE,
                 mTaskFactory.NewRunnableMethod(
                     &PluginModuleParent::NotifyPluginCrashed));
         break;
     }
     case NormalShutdown:
         mShutdown = true;
         break;
 
@@ -1650,17 +1644,16 @@ PluginModuleParent::NotifyFlashHang()
 }
 
 void
 PluginModuleParent::NotifyPluginCrashed()
 {
     if (!OkToCleanup()) {
         // there's still plugin code on the C++ stack.  try again
         MessageLoop::current()->PostDelayedTask(
-            FROM_HERE,
             mTaskFactory.NewRunnableMethod(
                 &PluginModuleParent::NotifyPluginCrashed), 10);
         return;
     }
 
     if (mPlugin)
         mPlugin->PluginCrashed(mPluginDumpID, mBrowserDumpID);
 }
@@ -3151,17 +3144,16 @@ PluginModuleChromeParent::InitializeInje
                           NS_LITERAL_CSTRING(FLASH_PLUGIN_PREFIX)))
         return;
 
     TimeStamp th32Start = TimeStamp::Now();
     mFinishInitTask = mChromeTaskFactory.NewTask<FinishInjectorInitTask>();
     mFinishInitTask->Init(this);
     if (!::QueueUserWorkItem(&PluginModuleChromeParent::GetToolhelpSnapshot,
                              mFinishInitTask, WT_EXECUTEDEFAULT)) {
-        delete mFinishInitTask;
         mFinishInitTask = nullptr;
         return;
     }
     TimeStamp th32End = TimeStamp::Now();
     mTimeBlocked += (th32End - th32Start);
 }
 
 void
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -142,17 +142,18 @@ public:
     virtual void SetHasLocalInstance() override {
         mHadLocalInstance = true;
     }
 
     int GetQuirks() { return mQuirks; }
 
 protected:
     virtual mozilla::ipc::RacyInterruptPolicy
-    MediateInterruptRace(const Message& parent, const Message& child) override
+    MediateInterruptRace(const MessageInfo& parent,
+                         const MessageInfo& child) override
     {
         return MediateRace(parent, child);
     }
 
     virtual bool
     RecvBackUpXResources(const FileDescriptor& aXSocketFd) override;
 
     virtual bool AnswerProcessSomeEvents() override;
@@ -602,33 +603,34 @@ private:
     void InitializeInjector();
     void DoInjection(const nsAutoHandle& aSnapshot);
     static DWORD WINAPI GetToolhelpSnapshot(LPVOID aContext);
 
     void OnCrash(DWORD processID) override;
 
     DWORD mFlashProcess1;
     DWORD mFlashProcess2;
-    mozilla::plugins::FinishInjectorInitTask* mFinishInitTask;
+    RefPtr<mozilla::plugins::FinishInjectorInitTask> mFinishInitTask;
 #endif
 
     void OnProcessLaunched(const bool aSucceeded);
 
     class LaunchedTask : public LaunchCompleteTask
     {
     public:
         explicit LaunchedTask(PluginModuleChromeParent* aModule)
             : mModule(aModule)
         {
             MOZ_ASSERT(aModule);
         }
 
-        void Run() override
+        NS_IMETHOD Run() override
         {
             mModule->OnProcessLaunched(mLaunchSucceeded);
+            return NS_OK;
         }
 
     private:
         PluginModuleChromeParent* mModule;
     };
 
     friend class LaunchedTask;
 
--- a/dom/plugins/ipc/PluginProcessParent.cpp
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -197,18 +197,17 @@ PluginProcessParent::Delete()
   MessageLoop* currentLoop = MessageLoop::current();
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
 
   if (currentLoop == ioLoop) {
       delete this;
       return;
   }
 
-  ioLoop->PostTask(FROM_HERE,
-                   NewRunnableMethod(this, &PluginProcessParent::Delete));
+  ioLoop->PostTask(NewRunnableMethod(this, &PluginProcessParent::Delete));
 }
 
 void
 PluginProcessParent::SetCallRunnableImmediately(bool aCallImmediately)
 {
     mRunCompleteTaskImmediately = aCallImmediately;
 }
 
@@ -242,27 +241,27 @@ PluginProcessParent::WaitUntilConnected(
 }
 
 void
 PluginProcessParent::OnChannelConnected(int32_t peer_pid)
 {
     GeckoChildProcessHost::OnChannelConnected(peer_pid);
     if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
         mLaunchCompleteTask->SetLaunchSucceeded();
-        mMainMsgLoop->PostTask(FROM_HERE, mTaskFactory.NewRunnableMethod(
+        mMainMsgLoop->PostTask(mTaskFactory.NewRunnableMethod(
                                    &PluginProcessParent::RunLaunchCompleteTask));
     }
 }
 
 void
 PluginProcessParent::OnChannelError()
 {
     GeckoChildProcessHost::OnChannelError();
     if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
-        mMainMsgLoop->PostTask(FROM_HERE, mTaskFactory.NewRunnableMethod(
+        mMainMsgLoop->PostTask(mTaskFactory.NewRunnableMethod(
                                    &PluginProcessParent::RunLaunchCompleteTask));
     }
 }
 
 bool
 PluginProcessParent::IsConnected()
 {
     mozilla::MonitorAutoLock lock(mMonitor);
--- a/dom/plugins/ipc/PluginProcessParent.h
+++ b/dom/plugins/ipc/PluginProcessParent.h
@@ -20,17 +20,17 @@
 #include "mozilla/plugins/TaskFactory.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsIRunnable.h"
 
 namespace mozilla {
 namespace plugins {
 
-class LaunchCompleteTask : public Task
+class LaunchCompleteTask : public Runnable
 {
 public:
     LaunchCompleteTask()
         : mLaunchSucceeded(false)
     {
     }
 
     void SetLaunchSucceeded() { mLaunchSucceeded = true; }
--- a/dom/plugins/ipc/TaskFactory.h
+++ b/dom/plugins/ipc/TaskFactory.h
@@ -31,67 +31,72 @@ private:
   public:
     template<typename... Args>
     explicit TaskWrapper(RevocableStore* store, Args&&... args)
       : TaskType(mozilla::Forward<Args>(args)...)
       , revocable_(store)
     {
     }
 
-    virtual void Run() {
+    NS_IMETHOD Run() override {
       if (!revocable_.revoked())
         TaskType::Run();
+      return NS_OK;
     }
 
   private:
     Revocable revocable_;
   };
 
 public:
   explicit TaskFactory(T* object) : object_(object) { }
 
   template <typename TaskParamType, typename... Args>
-  inline TaskParamType* NewTask(Args&&... args)
+  inline already_AddRefed<TaskParamType> NewTask(Args&&... args)
   {
     typedef TaskWrapper<TaskParamType> TaskWrapper;
-    TaskWrapper* task = new TaskWrapper(this, mozilla::Forward<Args>(args)...);
-    return task;
+    RefPtr<TaskWrapper> task =
+      new TaskWrapper(this, mozilla::Forward<Args>(args)...);
+    return task.forget();
   }
 
   template <class Method>
-  inline Task* NewRunnableMethod(Method method) {
+  inline already_AddRefed<Runnable> NewRunnableMethod(Method method) {
     typedef TaskWrapper<RunnableMethod<Method, Tuple0> > TaskWrapper;
 
-    TaskWrapper* task = new TaskWrapper(this);
+    RefPtr<TaskWrapper> task = new TaskWrapper(this);
     task->Init(object_, method, base::MakeTuple());
-    return task;
+    return task.forget();
   }
 
   template <class Method, class A>
-  inline Task* NewRunnableMethod(Method method, const A& a) {
+  inline already_AddRefed<Runnable> NewRunnableMethod(Method method, const A& a) {
     typedef TaskWrapper<RunnableMethod<Method, Tuple1<A> > > TaskWrapper;
 
-    TaskWrapper* task = new TaskWrapper(this);
+    RefPtr<TaskWrapper> task = new TaskWrapper(this);
     task->Init(object_, method, base::MakeTuple(a));
-    return task;
+    return task.forget();
   }
 
 protected:
   template <class Method, class Params>
-  class RunnableMethod : public Task {
+  class RunnableMethod : public Runnable {
    public:
     RunnableMethod() { }
 
     void Init(T* obj, Method meth, const Params& params) {
       obj_ = obj;
       meth_ = meth;
       params_ = params;
     }
 
-    virtual void Run() { DispatchToMethod(obj_, meth_, params_); }
+    NS_IMETHOD Run() override {
+      DispatchToMethod(obj_, meth_, params_);
+      return NS_OK;
+    }
 
    private:
     T* obj_;
     Method meth_;
     Params params_;
   };
 
 private:
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -45,16 +45,17 @@ NS_IMETHODIMP
 PresentationRequesterCallback::NotifySuccess()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // At the sender side, this function must get called after the transport
   // channel is ready. So we simply set the connection state as connected.
   RefPtr<PresentationConnection> connection =
     PresentationConnection::Create(mRequest->GetOwner(), mSessionId,
+                                   nsIPresentationService::ROLE_CONTROLLER,
                                    PresentationConnectionState::Connected);
   if (NS_WARN_IF(!connection)) {
     mPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
     return NS_OK;
   }
 
   mPromise->MaybeResolve(connection);
 
--- a/dom/presentation/PresentationConnection.cpp
+++ b/dom/presentation/PresentationConnection.cpp
@@ -30,68 +30,75 @@ NS_IMPL_ADDREF_INHERITED(PresentationCon
 NS_IMPL_RELEASE_INHERITED(PresentationConnection, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationConnection)
   NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionListener)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 PresentationConnection::PresentationConnection(nsPIDOMWindowInner* aWindow,
                                                const nsAString& aId,
+                                               const uint8_t aRole,
                                                PresentationConnectionState aState)
   : DOMEventTargetHelper(aWindow)
   , mId(aId)
   , mState(aState)
 {
+  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+             aRole == nsIPresentationService::ROLE_RECEIVER);
+  mRole = aRole;
 }
 
 /* virtual */ PresentationConnection::~PresentationConnection()
 {
 }
 
 /* static */ already_AddRefed<PresentationConnection>
 PresentationConnection::Create(nsPIDOMWindowInner* aWindow,
                                const nsAString& aId,
+                               const uint8_t aRole,
                                PresentationConnectionState aState)
 {
+  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+             aRole == nsIPresentationService::ROLE_RECEIVER);
   RefPtr<PresentationConnection> connection =
-    new PresentationConnection(aWindow, aId, aState);
+    new PresentationConnection(aWindow, aId, aRole, aState);
   return NS_WARN_IF(!connection->Init()) ? nullptr : connection.forget();
 }
 
 bool
 PresentationConnection::Init()
 {
   if (NS_WARN_IF(mId.IsEmpty())) {
     return false;
   }
 
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if(NS_WARN_IF(!service)) {
     return false;
   }
 
-  nsresult rv = service->RegisterSessionListener(mId, this);
+  nsresult rv = service->RegisterSessionListener(mId, mRole, this);
   if(NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   return true;
 }
 
 void
 PresentationConnection::Shutdown()
 {
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return;
   }
 
-  nsresult rv = service->UnregisterSessionListener(mId);
+  nsresult rv = service->UnregisterSessionListener(mId, mRole);
   NS_WARN_IF(NS_FAILED(rv));
 }
 
 /* virtual */ void
 PresentationConnection::DisconnectFromOwner()
 {
   Shutdown();
   DOMEventTargetHelper::DisconnectFromOwner();
@@ -128,17 +135,17 @@ PresentationConnection::Send(const nsASt
 
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if(NS_WARN_IF(!service)) {
     aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
-  nsresult rv = service->SendSessionMessage(mId, aData);
+  nsresult rv = service->SendSessionMessage(mId, mRole, aData);
   if(NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
   }
 }
 
 void
 PresentationConnection::Close(ErrorResult& aRv)
 {
@@ -161,17 +168,17 @@ PresentationConnection::Terminate(ErrorR
 
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if(NS_WARN_IF(!service)) {
     aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
-  NS_WARN_IF(NS_FAILED(service->TerminateSession(mId)));
+  NS_WARN_IF(NS_FAILED(service->TerminateSession(mId, mRole)));
 }
 
 NS_IMETHODIMP
 PresentationConnection::NotifyStateChange(const nsAString& aSessionId,
                                           uint16_t aState)
 {
   if (!aSessionId.Equals(mId)) {
     return NS_ERROR_INVALID_ARG;
@@ -202,17 +209,17 @@ PresentationConnection::NotifyStateChang
   // Unregister session listener if the session is no longer connected.
   if (mState == PresentationConnectionState::Terminated) {
     nsCOMPtr<nsIPresentationService> service =
       do_GetService(PRESENTATION_SERVICE_CONTRACTID);
     if (NS_WARN_IF(!service)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
-    nsresult rv = service->UnregisterSessionListener(mId);
+    nsresult rv = service->UnregisterSessionListener(mId, mRole);
     if(NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return DispatchStateChangeEvent();
 }
 
--- a/dom/presentation/PresentationConnection.h
+++ b/dom/presentation/PresentationConnection.h
@@ -20,16 +20,17 @@ class PresentationConnection final : pub
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationConnection,
                                            DOMEventTargetHelper)
   NS_DECL_NSIPRESENTATIONSESSIONLISTENER
 
   static already_AddRefed<PresentationConnection> Create(nsPIDOMWindowInner* aWindow,
                                                          const nsAString& aId,
+                                                         const uint8_t aRole,
                                                          PresentationConnectionState aState);
 
   virtual void DisconnectFromOwner() override;
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL (public APIs)
@@ -45,28 +46,30 @@ public:
   void Terminate(ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(statechange);
   IMPL_EVENT_HANDLER(message);
 
 private:
   PresentationConnection(nsPIDOMWindowInner* aWindow,
                          const nsAString& aId,
+                         const uint8_t aRole,
                          PresentationConnectionState aState);
 
   ~PresentationConnection();
 
   bool Init();
 
   void Shutdown();
 
   nsresult DispatchStateChangeEvent();
 
   nsresult DispatchMessageEvent(JS::Handle<JS::Value> aData);
 
   nsString mId;
+  uint8_t mRole;
   PresentationConnectionState mState;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PresentationConnection_h
--- a/dom/presentation/PresentationDataChannelSessionTransport.js
+++ b/dom/presentation/PresentationDataChannelSessionTransport.js
@@ -15,17 +15,16 @@ function log(aMsg) {
 }
 
 const PRESENTATIONTRANSPORT_CID = Components.ID("{dd2bbf2f-3399-4389-8f5f-d382afb8b2d6}");
 const PRESENTATIONTRANSPORT_CONTRACTID = "mozilla.org/presentation/datachanneltransport;1";
 
 const PRESENTATIONTRANSPORTBUILDER_CID = Components.ID("{215b2f62-46e2-4004-a3d1-6858e56c20f3}");
 const PRESENTATIONTRANSPORTBUILDER_CONTRACTID = "mozilla.org/presentation/datachanneltransportbuilder;1";
 
-
 function PresentationDataChannelDescription(aDataChannelSDP) {
   this._dataChannelSDP = JSON.stringify(aDataChannelSDP);
 }
 
 PresentationDataChannelDescription.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
   get type() {
     return nsIPresentationChannelDescription.TYPE_DATACHANNEL;
@@ -36,71 +35,70 @@ PresentationDataChannelDescription.proto
   get tcpPort() {
     return null;
   },
   get dataChannelSDP() {
     return this._dataChannelSDP;
   }
 };
 
-
 function PresentationTransportBuilder() {
   log("PresentationTransportBuilder construct");
   this._isControlChannelNeeded = true;
 }
 
 PresentationTransportBuilder.prototype = {
   classID: PRESENTATIONTRANSPORTBUILDER_CID,
   contractID: PRESENTATIONTRANSPORTBUILDER_CONTRACTID,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDataChannelSessionTransportBuilder,
                                          Ci.nsIPresentationControlChannelListener,
                                          Ci.nsITimerCallback]),
 
-  buildDataChannelTransport: function(aType, aWindow, aControlChannel, aListener) {
-    if (!aType || !aWindow || !aControlChannel || !aListener) {
+  buildDataChannelTransport: function(aRole, aWindow, aControlChannel, aListener) {
+    if (!aRole || !aWindow || !aControlChannel || !aListener) {
       log("buildDataChannelTransport with illegal parameters");
       throw Cr.NS_ERROR_ILLEGAL_VALUE;
     }
 
     if (this._window) {
       log("buildDataChannelTransport has started.");
       throw Cr.NS_ERROR_UNEXPECTED;
     }
 
-    log("buildDataChannelTransport with type " + aType);
-    this._type = aType;
+    log("buildDataChannelTransport with role " + aRole);
+    this._role = aRole;
     this._window = aWindow;
     this._controlChannel = aControlChannel.QueryInterface(Ci.nsIPresentationControlChannel);
     this._controlChannel.listener = this;
     this._listener = aListener.QueryInterface(Ci.nsIPresentationSessionTransportBuilderListener);
 
     // TODO bug 1227053 set iceServers from |nsIPresentationDevice|
     this._peerConnection = new this._window.RTCPeerConnection();
 
     // |this._controlChannel == null| will throw since the control channel is
     // abnormally closed.
     this._peerConnection.onicecandidate = aEvent => aEvent.candidate &&
       this._controlChannel.sendIceCandidate(JSON.stringify(aEvent.candidate));
 
     this._peerConnection.onnegotiationneeded = () => {
-      log("onnegotiationneeded with type " + this._type);
+      log("onnegotiationneeded with role " + this._role);
       this._peerConnection.createOffer()
           .then(aOffer => this._peerConnection.setLocalDescription(aOffer))
           .then(() => this._controlChannel
                           .sendOffer(new PresentationDataChannelDescription(this._peerConnection.localDescription)))
           .catch(e => this._reportError(e));
     }
 
-    switch (this._type) {
-      case Ci.nsIPresentationSessionTransportBuilder.TYPE_SENDER:
+    switch (this._role) {
+      case Ci.nsIPresentationService.ROLE_CONTROLLER:
         this._dataChannel = this._peerConnection.createDataChannel("presentationAPI");
         this._setDataChannel();
         break;
 
-      case Ci.nsIPresentationSessionTransportBuilder.TYPE_RECEIVER:
+      case Ci.nsIPresentationService.ROLE_RECEIVER:
         this._peerConnection.ondatachannel = aEvent => {
           this._dataChannel = aEvent.channel;
           this._setDataChannel();
         }
         break;
       default:
        throw Cr.NS_ERROR_ILLEGAL_VALUE;
     }
@@ -128,17 +126,17 @@ PresentationTransportBuilder.prototype =
 
   _reportError: function(aError) {
     log("report Error " + aError.name + ":" + aError.message);
     this._cleanup(Cr.NS_ERROR_FAILURE);
   },
 
   _setDataChannel: function() {
     this._dataChannel.onopen = () => {
-      log("data channel is open, notify the listener, type " + this._type);
+      log("data channel is open, notify the listener, role " + this._role);
 
       // Handoff the ownership of _peerConnection and _dataChannel to
       // _sessionTransport
       this._sessionTransport = new PresentationTransport();
       this._sessionTransport.init(this._peerConnection, this._dataChannel);
       this._peerConnection = this._dataChannel = null;
 
       this._listener.onSessionTransport(this._sessionTransport);
@@ -163,17 +161,17 @@ PresentationTransportBuilder.prototype =
       this._dataChannel = null;
     }
 
     if (this._peerConnection) {
       this._peerConnection.close();
       this._peerConnection = null;
     }
 
-    this._type = null;
+    this._role = null;
     this._window = null;
 
     if (this._controlChannel) {
       this._controlChannel.close(aReason);
       this._controlChannel = null;
     }
 
     this._listener = null;
@@ -182,56 +180,56 @@ PresentationTransportBuilder.prototype =
     if (this._timer) {
       this._timer.cancel();
       this._timer = null;
     }
   },
 
   // nsIPresentationControlChannelListener
   onOffer: function(aOffer) {
-    if (this._type !== Ci.nsIPresentationSessionTransportBuilder.TYPE_RECEIVER ||
+    if (this._role !== Ci.nsIPresentationService.ROLE_RECEIVER ||
           this._sessionTransport) {
       log("onOffer status error");
       this._cleanup(Cr.NS_ERROR_FAILURE);
     }
 
-    log("onOffer: " + aOffer.dataChannelSDP + " with type " + this._type);
+    log("onOffer: " + aOffer.dataChannelSDP + " with role " + this._role);
 
     let offer = new this._window
                         .RTCSessionDescription(JSON.parse(aOffer.dataChannelSDP));
 
     this._peerConnection.setRemoteDescription(offer)
         .then(() => this._peerConnection.signalingState == "stable" ||
                       this._peerConnection.createAnswer())
         .then(aAnswer => this._peerConnection.setLocalDescription(aAnswer))
         .then(() => {
           this._isControlChannelNeeded = false;
           this._controlChannel
               .sendAnswer(new PresentationDataChannelDescription(this._peerConnection.localDescription))
         }).catch(e => this._reportError(e));
   },
 
   onAnswer: function(aAnswer) {
-    if (this._type !== Ci.nsIPresentationSessionTransportBuilder.TYPE_SENDER ||
+    if (this._role !== Ci.nsIPresentationService.ROLE_CONTROLLER ||
           this._sessionTransport) {
       log("onAnswer status error");
       this._cleanup(Cr.NS_ERROR_FAILURE);
     }
 
-    log("onAnswer: " + aAnswer.dataChannelSDP + " with type " + this._type);
+    log("onAnswer: " + aAnswer.dataChannelSDP + " with role " + this._role);
 
     let answer = new this._window
                          .RTCSessionDescription(JSON.parse(aAnswer.dataChannelSDP));
 
     this._peerConnection.setRemoteDescription(answer).catch(e => this._reportError(e));
     this._isControlChannelNeeded = false;
   },
 
   onIceCandidate: function(aCandidate) {
-    log("onIceCandidate: " + aCandidate + " with type " + this._type);
+    log("onIceCandidate: " + aCandidate + " with role " + this._role);
     let candidate = new this._window.RTCIceCandidate(JSON.parse(aCandidate));
     this._peerConnection.addIceCandidate(candidate).catch(e => this._reportError(e));
   },
 
   notifyOpened: function() {
     log("notifyOpened, should be opened beforehand");
   },
 
@@ -242,17 +240,16 @@ PresentationTransportBuilder.prototype =
       this._cleanup(aReason);
     } else if (this._isControlChannelNeeded) {
       this._cleanup(Cr.NS_ERROR_FAILURE);
     }
     this._controlChannel = null;
   },
 };
 
-
 function PresentationTransport() {
   this._messageQueue = [];
   this._closeReason = Cr.NS_OK;
 }
 
 PresentationTransport.prototype = {
   classID: PRESENTATIONTRANSPORT_CID,
   contractID: PRESENTATIONTRANSPORT_CONTRACTID,
@@ -282,17 +279,16 @@ PresentationTransport.prototype = {
       if (!this._enableDataNotification || !this._callback) {
         log("queue message");
         this._messageQueue.push(aEvent.data);
         return;
       }
       this._callback.notifyData(aEvent.data);
     };
 
-
     this._dataChannel.onerror = aError => {
       log("data channel onerror " + aError.name + ":" + aError.message);
       if (this._callback) {
         this._callback.notifyTransportClosed(Cr.NS_ERROR_FAILURE);
       }
       this._cleanup();
     }
   },
--- a/dom/presentation/PresentationReceiver.cpp
+++ b/dom/presentation/PresentationReceiver.cpp
@@ -167,16 +167,17 @@ PresentationReceiver::NotifySessionConne
   }
 
   if (NS_WARN_IF(aWindowId != GetOwner()->WindowID())) {
     return NS_ERROR_INVALID_ARG;
   }
 
   RefPtr<PresentationConnection> connection =
     PresentationConnection::Create(GetOwner(), aSessionId,
+                                   nsIPresentationService::ROLE_RECEIVER,
                                    PresentationConnectionState::Closed);
   if (NS_WARN_IF(!connection)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   mConnections.AppendElement(connection);
 
   // Resolve pending |GetConnection| promises if any.
   if (!mPendingGetConnectionPromises.IsEmpty()) {
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ipc/PresentationIPCService.h"
 #include "mozilla/Services.h"
 #include "mozIApplication.h"
 #include "nsIAppsService.h"
@@ -95,17 +96,18 @@ PresentationDeviceRequest::Select(nsIPre
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Update device in the session info.
   RefPtr<PresentationSessionInfo> info =
-    static_cast<PresentationService*>(service.get())->GetSessionInfo(mId);
+    static_cast<PresentationService*>(service.get())->
+      GetSessionInfo(mId, nsIPresentationService::ROLE_CONTROLLER);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   info->SetDevice(aDevice);
 
   // Establish a control channel. If we failed to do so, the callback is called
   // with an error message.
   nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
@@ -128,17 +130,18 @@ PresentationDeviceRequest::Cancel()
 {
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<PresentationSessionInfo> info =
-    static_cast<PresentationService*>(service.get())->GetSessionInfo(mId);
+    static_cast<PresentationService*>(service.get())->
+      GetSessionInfo(mId, nsIPresentationService::ROLE_CONTROLLER);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return info->ReplyError(NS_ERROR_DOM_ABORT_ERR);
 }
 
 /*
@@ -219,17 +222,18 @@ PresentationService::Observe(nsISupports
 
 void
 PresentationService::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mAvailabilityListeners.Clear();
   mRespondingListeners.Clear();
-  mSessionInfo.Clear();
+  mSessionInfoAtController.Clear();
+  mSessionInfoAtReceiver.Clear();
   mRespondingSessionIds.Clear();
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (obs) {
     obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     obs->RemoveObserver(this, PRESENTATION_DEVICE_CHANGE_TOPIC);
     obs->RemoveObserver(this, PRESENTATION_SESSION_REQUEST_TOPIC);
   }
@@ -306,41 +310,42 @@ PresentationService::HandleSessionReques
 
   if (NS_WARN_IF(isApp && !IsAppInstalled(uri))) {
     ctrlChannel->Close(NS_ERROR_DOM_NOT_FOUND_ERR);
     return NS_OK;
   }
 #endif
 
   // Create or reuse session info.
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(sessionId);
+  RefPtr<PresentationSessionInfo> info =
+    GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER);
   if (NS_WARN_IF(info)) {
     // TODO Bug 1195605. Update here after session join/resume becomes supported.
     ctrlChannel->Close(NS_ERROR_DOM_OPERATION_ERR);
     return NS_ERROR_DOM_ABORT_ERR;
   }
 
   info = new PresentationPresentingInfo(url, sessionId, device);
   rv = info->Init(ctrlChannel);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ctrlChannel->Close(rv);
     return rv;
   }
 
-  mSessionInfo.Put(sessionId, info);
+  mSessionInfoAtReceiver.Put(sessionId, info);
 
   // Notify the receiver to launch.
   nsCOMPtr<nsIPresentationRequestUIGlue> glue =
     do_CreateInstance(PRESENTATION_REQUEST_UI_GLUE_CONTRACTID);
   if (NS_WARN_IF(!glue)) {
     ctrlChannel->Close(NS_ERROR_DOM_OPERATION_ERR);
     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
   nsCOMPtr<nsISupports> promise;
-  rv = glue->SendRequest(url, sessionId, getter_AddRefs(promise));
+  rv = glue->SendRequest(url, sessionId, device, getter_AddRefs(promise));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ctrlChannel->Close(rv);
     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
   nsCOMPtr<Promise> realPromise = do_QueryInterface(promise);
   static_cast<PresentationPresentingInfo*>(info.get())->SetPromise(realPromise);
 
   return NS_OK;
@@ -394,17 +399,17 @@ PresentationService::StartSession(const 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(!aSessionId.IsEmpty());
 
   // Create session info  and set the callback. The callback is called when the
   // request is finished.
   RefPtr<PresentationSessionInfo> info =
     new PresentationControllingInfo(aUrl, aSessionId, aCallback);
-  mSessionInfo.Put(aSessionId, info);
+  mSessionInfoAtController.Put(aSessionId, info);
 
   // Only track the info when an actual window ID, which would never be 0, is
   // provided (for an in-process sender page).
   if (aWindowId != 0) {
     mRespondingSessionIds.Put(aWindowId, new nsString(aSessionId));
     mRespondingWindowIds.Put(aSessionId, aWindowId);
   }
 
@@ -463,51 +468,60 @@ PresentationService::StartSession(const 
   }
 
   // Reject if designated device is not available.
   return info->ReplyError(NS_ERROR_DOM_NOT_FOUND_ERR);
 }
 
 NS_IMETHODIMP
 PresentationService::SendSessionMessage(const nsAString& aSessionId,
+                                        uint8_t aRole,
                                         const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aData.IsEmpty());
   MOZ_ASSERT(!aSessionId.IsEmpty());
+  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+             aRole == nsIPresentationService::ROLE_RECEIVER);
 
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
+  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return info->Send(aData);
 }
 
 NS_IMETHODIMP
-PresentationService::CloseSession(const nsAString& aSessionId)
+PresentationService::CloseSession(const nsAString& aSessionId,
+                                  uint8_t aRole)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aSessionId.IsEmpty());
+  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+             aRole == nsIPresentationService::ROLE_RECEIVER);
 
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
+  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return info->Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED);
 }
 
 NS_IMETHODIMP
-PresentationService::TerminateSession(const nsAString& aSessionId)
+PresentationService::TerminateSession(const nsAString& aSessionId,
+                                      uint8_t aRole)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aSessionId.IsEmpty());
+  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+             aRole == nsIPresentationService::ROLE_RECEIVER);
 
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
+  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return info->Close(NS_OK, nsIPresentationSessionListener::STATE_TERMINATED);
 }
 
 NS_IMETHODIMP
@@ -529,22 +543,25 @@ PresentationService::UnregisterAvailabil
   MOZ_ASSERT(NS_IsMainThread());
 
   mAvailabilityListeners.RemoveElement(aListener);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationService::RegisterSessionListener(const nsAString& aSessionId,
+                                             uint8_t aRole,
                                              nsIPresentationSessionListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aListener);
+  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+             aRole == nsIPresentationService::ROLE_RECEIVER);
 
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
+  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
   if (NS_WARN_IF(!info)) {
     // Notify the listener of TERMINATED since no correspondent session info is
     // available possibly due to establishment failure. This would be useful at
     // the receiver side, since a presentation session is created at beginning
     // and here is the place to realize the underlying establishment fails.
     nsresult rv = aListener->NotifyStateChange(aSessionId,
                                                nsIPresentationSessionListener::STATE_TERMINATED);
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -552,24 +569,27 @@ PresentationService::RegisterSessionList
     }
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return info->SetListener(aListener);
 }
 
 NS_IMETHODIMP
-PresentationService::UnregisterSessionListener(const nsAString& aSessionId)
+PresentationService::UnregisterSessionListener(const nsAString& aSessionId,
+                                               uint8_t aRole)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+             aRole == nsIPresentationService::ROLE_RECEIVER);
 
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
+  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
   if (info) {
     NS_WARN_IF(NS_FAILED(info->Close(NS_OK, nsIPresentationSessionListener::STATE_TERMINATED)));
-    UntrackSessionInfo(aSessionId);
+    UntrackSessionInfo(aSessionId, aRole);
     return info->SetListener(nullptr);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationService::RegisterRespondingListener(uint64_t aWindowId,
                                                 nsIPresentationRespondingListener* aListener)
@@ -609,36 +629,44 @@ PresentationService::GetExistentSessionI
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationService::NotifyReceiverReady(const nsAString& aSessionId,
                                          uint64_t aWindowId)
 {
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
+  RefPtr<PresentationSessionInfo> info =
+    GetSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Only track the responding info when an actual window ID, which would never
   // be 0, is provided (for an in-process receiver page).
   if (aWindowId != 0) {
     mRespondingSessionIds.Put(aWindowId, new nsString(aSessionId));
     mRespondingWindowIds.Put(aSessionId, aWindowId);
   }
 
   return static_cast<PresentationPresentingInfo*>(info.get())->NotifyResponderReady();
 }
 
 NS_IMETHODIMP
-PresentationService::UntrackSessionInfo(const nsAString& aSessionId)
+PresentationService::UntrackSessionInfo(const nsAString& aSessionId,
+                                        uint8_t aRole)
 {
+  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+             aRole == nsIPresentationService::ROLE_RECEIVER);
   // Remove the session info.
-  mSessionInfo.Remove(aSessionId);
+  if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
+    mSessionInfoAtController.Remove(aSessionId);
+  } else {
+    mSessionInfoAtReceiver.Remove(aSessionId);
+  }
 
   // Remove the in-process responding info if there's still any.
   uint64_t windowId = 0;
   if (mRespondingWindowIds.Get(aSessionId, &windowId)) {
     mRespondingWindowIds.Remove(aSessionId);
     mRespondingSessionIds.Remove(windowId);
   }
 
@@ -652,19 +680,22 @@ PresentationService::GetWindowIdBySessio
   if (mRespondingWindowIds.Get(aSessionId, aWindowId)) {
     return NS_OK;
   }
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 bool
 PresentationService::IsSessionAccessible(const nsAString& aSessionId,
+                                         const uint8_t aRole,
                                          base::ProcessId aProcessId)
 {
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
+  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+             aRole == nsIPresentationService::ROLE_RECEIVER);
+  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
   if (NS_WARN_IF(!info)) {
     return false;
   }
   return info->IsAccessible(aProcessId);
 }
 
 already_AddRefed<nsIPresentationService>
 NS_CreatePresentationService()
--- a/dom/presentation/PresentationService.h
+++ b/dom/presentation/PresentationService.h
@@ -28,24 +28,33 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIPRESENTATIONSERVICE
 
   PresentationService();
   bool Init();
 
   already_AddRefed<PresentationSessionInfo>
-  GetSessionInfo(const nsAString& aSessionId)
+  GetSessionInfo(const nsAString& aSessionId, const uint8_t aRole)
   {
+    MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+               aRole == nsIPresentationService::ROLE_RECEIVER);
+
     RefPtr<PresentationSessionInfo> info;
-    return mSessionInfo.Get(aSessionId, getter_AddRefs(info)) ?
-           info.forget() : nullptr;
+    if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
+      return mSessionInfoAtController.Get(aSessionId, getter_AddRefs(info)) ?
+             info.forget() : nullptr;
+    } else {
+      return mSessionInfoAtReceiver.Get(aSessionId, getter_AddRefs(info)) ?
+             info.forget() : nullptr;
+    }
   }
 
   bool IsSessionAccessible(const nsAString& aSessionId,
+                           const uint8_t aRole,
                            base::ProcessId aProcessId);
 
 private:
   ~PresentationService();
   void HandleShutdown();
   nsresult HandleDeviceChange();
   nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest);
   void NotifyAvailableChange(bool aIsAvailable);
@@ -56,17 +65,18 @@ private:
 
   // Store the responding listener based on the window ID of the (in-process or
   // OOP) receiver page.
   // TODO Bug 1195605 - Support many-to-one session.
   // So far responding listeners are registered but |notifySessionConnect| hasn't
   // been called in any place until many-to-one session becomes supported.
   nsRefPtrHashtable<nsUint64HashKey, nsIPresentationRespondingListener> mRespondingListeners;
 
-  nsRefPtrHashtable<nsStringHashKey, PresentationSessionInfo> mSessionInfo;
+  nsRefPtrHashtable<nsStringHashKey, PresentationSessionInfo> mSessionInfoAtController;
+  nsRefPtrHashtable<nsStringHashKey, PresentationSessionInfo> mSessionInfoAtReceiver;
 
   // Store the mapping between the window ID of the in-process page and the ID
   // of the responding session. It's used for an in-process receiver page to
   // retrieve the correspondent session ID. Besides, also keep the mapping
   // between the responding session ID and the window ID to help look up the
   // window ID.
   nsClassHashtable<nsUint64HashKey, nsString> mRespondingSessionIds;
   nsDataHashtable<nsStringHashKey, uint64_t> mRespondingWindowIds;
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -320,17 +320,17 @@ PresentationSessionInfo::ReplyError(nsre
 /* virtual */ nsresult
 PresentationSessionInfo::UntrackFromService()
 {
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-  static_cast<PresentationService*>(service.get())->UntrackSessionInfo(mSessionId);
+  static_cast<PresentationService*>(service.get())->UntrackSessionInfo(mSessionId, mRole);
 
   return NS_OK;
 }
 
 nsPIDOMWindowInner*
 PresentationSessionInfo::GetWindow()
 {
   nsCOMPtr<nsIPresentationService> service =
@@ -655,17 +655,17 @@ PresentationControllingInfo::NotifyOpene
 
   if (NS_WARN_IF(!builder)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   mBuilder = builder;
   mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
 
-  return builder->BuildDataChannelTransport(nsIPresentationSessionTransportBuilder::TYPE_SENDER,
+  return builder->BuildDataChannelTransport(nsIPresentationService::ROLE_CONTROLLER,
                                             GetWindow(),
                                             mControlChannel,
                                             this);
 
 }
 
 NS_IMETHODIMP
 PresentationControllingInfo::NotifyClosed(nsresult aReason)
@@ -784,16 +784,20 @@ void
 PresentationPresentingInfo::Shutdown(nsresult aReason)
 {
   PresentationSessionInfo::Shutdown(aReason);
 
   if (mTimer) {
     mTimer->Cancel();
   }
 
+  if (mDevice) {
+    mDevice->Disconnect();
+  }
+  mDevice = nullptr;
   mLoadingCallback = nullptr;
   mRequesterDescription = nullptr;
   mPromise = nullptr;
 }
 
 // nsIPresentationSessionTransportBuilderListener
 NS_IMETHODIMP
 PresentationPresentingInfo::OnSessionTransport(nsIPresentationSessionTransport* transport)
@@ -869,17 +873,17 @@ PresentationPresentingInfo::InitTranspor
       do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1");
 
     if (NS_WARN_IF(!builder)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     mBuilder = builder;
     mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
-    rv = builder->BuildDataChannelTransport(nsIPresentationSessionTransportBuilder::TYPE_RECEIVER,
+    rv = builder->BuildDataChannelTransport(nsIPresentationService::ROLE_RECEIVER,
                                             GetWindow(),
                                             mControlChannel,
                                             this);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // delegate |onOffer| to builder
@@ -905,17 +909,17 @@ PresentationPresentingInfo::UntrackFromS
   }
 
   // Remove the session info (and the in-process responding info if there's any).
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-  static_cast<PresentationService*>(service.get())->UntrackSessionInfo(mSessionId);
+  static_cast<PresentationService*>(service.get())->UntrackSessionInfo(mSessionId, mRole);
 
   return NS_OK;
 }
 
 bool
 PresentationPresentingInfo::IsAccessible(base::ProcessId aProcessId)
 {
   // Only the specific content process should access the responder info.
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -33,40 +33,49 @@ class PresentationSessionInfo : public n
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK
   NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDERLISTENER
 
   PresentationSessionInfo(const nsAString& aUrl,
                           const nsAString& aSessionId,
+                          const uint8_t aRole,
                           nsIPresentationServiceCallback* aCallback)
     : mUrl(aUrl)
     , mSessionId(aSessionId)
     , mIsResponderReady(false)
     , mIsTransportReady(false)
     , mState(nsIPresentationSessionListener::STATE_CLOSED)
     , mCallback(aCallback)
   {
     MOZ_ASSERT(!mUrl.IsEmpty());
     MOZ_ASSERT(!mSessionId.IsEmpty());
+    MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+               aRole == nsIPresentationService::ROLE_RECEIVER);
+    mRole = aRole;
   }
 
   virtual nsresult Init(nsIPresentationControlChannel* aControlChannel);
 
   const nsAString& GetUrl() const
   {
     return mUrl;
   }
 
   const nsAString& GetSessionId() const
   {
     return mSessionId;
   }
 
+  uint8_t GetRole() const
+  {
+    return mRole;
+  }
+
   void SetCallback(nsIPresentationServiceCallback* aCallback)
   {
     mCallback = aCallback;
   }
 
   nsresult SetListener(nsIPresentationSessionListener* aListener);
 
   void SetDevice(nsIPresentationDevice* aDevice)
@@ -132,16 +141,19 @@ protected:
 
   // Should be nsIPresentationChannelDescription::TYPE_TCP/TYPE_DATACHANNEL
   uint8_t mTransportType = 0;
 
   nsPIDOMWindowInner* GetWindow();
 
   nsString mUrl;
   nsString mSessionId;
+  // mRole should be nsIPresentationService::ROLE_CONTROLLER
+  //              or nsIPresentationService::ROLE_RECEIVER.
+  uint8_t mRole;
   bool mIsResponderReady;
   bool mIsTransportReady;
   uint32_t mState; // CONNECTED, CLOSED, TERMINATED
   nsCOMPtr<nsIPresentationServiceCallback> mCallback;
   nsCOMPtr<nsIPresentationSessionListener> mListener;
   nsCOMPtr<nsIPresentationDevice> mDevice;
   nsCOMPtr<nsIPresentationSessionTransport> mTransport;
   nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
@@ -155,17 +167,20 @@ class PresentationControllingInfo final 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
   NS_DECL_NSISERVERSOCKETLISTENER
 
   PresentationControllingInfo(const nsAString& aUrl,
                               const nsAString& aSessionId,
                               nsIPresentationServiceCallback* aCallback)
-    : PresentationSessionInfo(aUrl, aSessionId, aCallback)
+    : PresentationSessionInfo(aUrl,
+                              aSessionId,
+                              nsIPresentationService::ROLE_CONTROLLER,
+                              aCallback)
   {
     MOZ_ASSERT(mCallback);
   }
 
   nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
 
 private:
   ~PresentationControllingInfo()
@@ -203,20 +218,22 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
   NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDERLISTENER
   NS_DECL_NSITIMERCALLBACK
 
   PresentationPresentingInfo(const nsAString& aUrl,
                              const nsAString& aSessionId,
                              nsIPresentationDevice* aDevice)
-    : PresentationSessionInfo(aUrl, aSessionId, nullptr)
+    : PresentationSessionInfo(aUrl,
+                              aSessionId,
+                              nsIPresentationService::ROLE_RECEIVER,
+                              nullptr)
   {
     MOZ_ASSERT(aDevice);
-
     SetDevice(aDevice);
   }
 
   nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
 
   nsresult NotifyResponderReady();
 
   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
--- a/dom/presentation/PresentationTCPSessionTransport.cpp
+++ b/dom/presentation/PresentationTCPSessionTransport.cpp
@@ -102,28 +102,27 @@ PresentationTCPSessionTransport::BuildTC
   }
   mListener = aListener;
 
   nsresult rv = CreateStream();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  mType = nsIPresentationSessionTransportBuilder::TYPE_SENDER;
+  mRole = nsIPresentationService::ROLE_CONTROLLER;
 
   nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this);
   nsCOMPtr<nsIRunnable> onSessionTransportRunnable =
     NS_NewRunnableMethodWithArgs
       <nsIPresentationSessionTransport*>(mListener,
                                          &nsIPresentationSessionTransportBuilderListener::OnSessionTransport,
                                          sessionTransport);
 
   NS_DispatchToCurrentThread(onSessionTransportRunnable);
 
-
   nsCOMPtr<nsIRunnable> setReadyStateRunnable =
     NS_NewRunnableMethodWithArgs<ReadyState>(this,
                                              &PresentationTCPSessionTransport::SetReadyState,
                                              ReadyState::OPEN);
   return NS_DispatchToCurrentThread(setReadyStateRunnable);
 }
 
 NS_IMETHODIMP
@@ -185,17 +184,17 @@ PresentationTCPSessionTransport::BuildTC
 
   mTransport->SetEventSink(this, mainThread);
 
   rv = CreateStream();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  mType = nsIPresentationSessionTransportBuilder::TYPE_RECEIVER;
+  mRole = nsIPresentationService::ROLE_RECEIVER;
 
   nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this);
   nsCOMPtr<nsIRunnable> runnable =
     NS_NewRunnableMethodWithArgs
       <nsIPresentationSessionTransport*>(mListener,
                                          &nsIPresentationSessionTransportBuilderListener::OnSessionTransport,
                                          sessionTransport);
   return NS_DispatchToCurrentThread(runnable);
--- a/dom/presentation/PresentationTCPSessionTransport.h
+++ b/dom/presentation/PresentationTCPSessionTransport.h
@@ -80,17 +80,17 @@ private:
     return mDataNotificationEnabled && mReadyState == ReadyState::OPEN;
   }
 
   ReadyState mReadyState;
   bool mAsyncCopierActive;
   nsresult mCloseStatus;
   bool mDataNotificationEnabled;
 
-  uint8_t mType = 0;
+  uint8_t mRole = 0;
 
   // Raw socket streams
   nsCOMPtr<nsISocketTransport> mTransport;
   nsCOMPtr<nsIInputStream> mSocketInputStream;
   nsCOMPtr<nsIOutputStream> mSocketOutputStream;
 
   // Input stream machinery
   nsCOMPtr<nsIInputStreamPump> mInputStreamPump;
--- a/dom/presentation/interfaces/moz.build
+++ b/dom/presentation/interfaces/moz.build
@@ -6,16 +6,17 @@
 
 XPIDL_SOURCES += [
     'nsIPresentationControlChannel.idl',
     'nsIPresentationDevice.idl',
     'nsIPresentationDeviceManager.idl',
     'nsIPresentationDevicePrompt.idl',
     'nsIPresentationDeviceProvider.idl',
     'nsIPresentationListener.idl',
+    'nsIPresentationLocalDevice.idl',
     'nsIPresentationRequestUIGlue.idl',
     'nsIPresentationService.idl',
     'nsIPresentationSessionRequest.idl',
     'nsIPresentationSessionTransport.idl',
     'nsIPresentationSessionTransportBuilder.idl',
     'nsITCPPresentationServer.idl',
 ]
 
--- a/dom/presentation/interfaces/nsIPresentationDevice.idl
+++ b/dom/presentation/interfaces/nsIPresentationDevice.idl
@@ -13,22 +13,27 @@ interface nsIPresentationControlChannel;
 interface nsIPresentationDevice : nsISupports
 {
   // The unique Id for the device. UUID is recommanded.
   readonly attribute AUTF8String id;
 
   // The human-readable name of this device.
   readonly attribute AUTF8String name;
 
-  //TODO expose more info in order to fulfill UX spec
+  // TODO expose more info in order to fulfill UX spec
   // The category of this device, could be "wifi", "bluetooth", "hdmi", etc.
   readonly attribute AUTF8String type;
 
   /*
    * Establish a control channel to this device.
    * @param url The URL requested to open by remote device.
    * @param presentationId The Id for representing this session.
    * @returns The control channel for this session.
    * @throws  NS_ERROR_FAILURE if the establishment fails
    */
   nsIPresentationControlChannel establishControlChannel(in DOMString url,
                                                         in DOMString presentationId);
+
+  // Do something when presentation session is disconnected.
+  void disconnect();
 };
+
+
new file mode 100644
--- /dev/null
+++ b/dom/presentation/interfaces/nsIPresentationLocalDevice.idl
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIPresentationDevice.idl"
+
+/*
+ * Local device.
+ * This device is used for 1-UA use case. The result for display is rendered by
+ * this host device.
+ */
+[scriptable, uuid(dd239720-cab6-4fb5-9025-cba23f1bbc2d)]
+interface nsIPresentationLocalDevice : nsIPresentationDevice
+{
+  // (1-UA only) The property is used to get the window ID of 1-UA device.
+  readonly attribute AUTF8String windowId;
+};
--- a/dom/presentation/interfaces/nsIPresentationRequestUIGlue.idl
+++ b/dom/presentation/interfaces/nsIPresentationRequestUIGlue.idl
@@ -1,25 +1,29 @@
 /* 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 "nsISupports.idl"
 
+interface nsIPresentationDevice;
+
 %{C++
 #define PRESENTATION_REQUEST_UI_GLUE_CONTRACTID \
   "@mozilla.org/presentation/requestuiglue;1"
 %}
 
 [scriptable, uuid(faa45119-6fb5-496c-aa4c-f740177a38b5)]
 interface nsIPresentationRequestUIGlue : nsISupports
 {
   /*
-   * This method is called to open the responding app/page when a presentation
-   * request comes in at receiver side.
+   * This method is called to open the responding app/page when
+   * a presentation request comes in at receiver side.
    *
    * @param url       The url of the request.
    * @param sessionId The session ID of the request.
    *
    * @return A promise that resolves to the opening frame.
    */
-  nsISupports sendRequest(in DOMString url, in DOMString sessionId);
+  nsISupports sendRequest(in DOMString url,
+                          in DOMString sessionId,
+                          in nsIPresentationDevice device);
 };
--- a/dom/presentation/interfaces/nsIPresentationService.idl
+++ b/dom/presentation/interfaces/nsIPresentationService.idl
@@ -31,16 +31,19 @@ interface nsIPresentationServiceCallback
    * @param error: error message.
    */
   void notifyError(in nsresult error);
 };
 
 [scriptable, uuid(de42b741-5619-4650-b961-c2cebb572c95)]
 interface nsIPresentationService : nsISupports
 {
+  const unsigned short ROLE_CONTROLLER = 0x1;
+  const unsigned short ROLE_RECEIVER = 0x2;
+
   /*
    * Start a new presentation session and display a prompt box which asks users
    * to select a device.
    *
    * @param url: The url of presenting page.
    * @param sessionId: An ID to identify presentation session.
    * @param origin: The url of requesting page.
    * @param deviceId: The specified device of handling this request, null string
@@ -60,34 +63,40 @@ interface nsIPresentationService : nsISu
                     in DOMString deviceId,
                     in unsigned long long windowId,
                     in nsIPresentationServiceCallback callback);
 
   /*
    * Send the message to the session.
    *
    * @param sessionId: An ID to identify presentation session.
+   * @param role: Identify the function called by controller or receiver.
    * @param data: the message being sent out.
    */
   void sendSessionMessage(in DOMString sessionId,
+							            in uint8_t role,
                           in DOMString data);
 
   /*
    * Close the session.
    *
    * @param sessionId: An ID to identify presentation session.
+   * @param role: Identify the function called by controller or receiver.
    */
-  void closeSession(in DOMString sessionId);
+  void closeSession(in DOMString sessionId,
+                    in uint8_t role);
 
   /*
    * Terminate the session.
    *
    * @param sessionId: An ID to identify presentation session.
+   * @param role: Identify the function called by controller or receiver.
    */
-  void terminateSession(in DOMString sessionId);
+  void terminateSession(in DOMString sessionId,
+                        in uint8_t role);
 
   /*
    * Register an availability listener. Must be called from the main thread.
    *
    * @param listener: The listener to register.
    */
   void registerAvailabilityListener(in nsIPresentationAvailabilityListener listener);
 
@@ -96,27 +105,31 @@ interface nsIPresentationService : nsISu
    * @param listener: The listener to unregister.
    */
   void unregisterAvailabilityListener(in nsIPresentationAvailabilityListener listener);
 
   /*
    * Register a session listener. Must be called from the main thread.
    *
    * @param sessionId: An ID to identify presentation session.
+   * @param role: Identify the function called by controller or receiver.
    * @param listener: The listener to register.
    */
   void registerSessionListener(in DOMString sessionId,
+                               in uint8_t role,
                                in nsIPresentationSessionListener listener);
 
   /*
    * Unregister a session listener. Must be called from the main thread.
    *
    * @param sessionId: An ID to identify presentation session.
+   * @param role: Identify the function called by controller or receiver.
    */
-  void unregisterSessionListener(in DOMString sessionId);
+  void unregisterSessionListener(in DOMString sessionId,
+                                 in uint8_t role);
 
   /*
    * Register a responding listener. Must be called from the main thread.
    *
    * @param windowId: The window ID associated with the listener.
    * @param listener: The listener to register.
    */
   void registerRespondingListener(in unsigned long long windowId,
@@ -149,16 +162,17 @@ interface nsIPresentationService : nsISu
    */
   void notifyReceiverReady(in DOMString sessionId,
                            [optional] in unsigned long long windowId);
 
   /*
    * Untrack the relevant info about the presentation session if there's any.
    *
    * @param sessionId: An ID to identify presentation session.
+   * @param role: Identify the function called by controller or receiver.
    */
-  void untrackSessionInfo(in DOMString sessionId);
+  void untrackSessionInfo(in DOMString sessionId, in uint8_t role);
 
   /*
    * The windowId for building RTCDataChannel session transport
    */
   unsigned long long getWindowIdBySessionId(in DOMString sessionId);
 };
--- a/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl
+++ b/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl
@@ -17,18 +17,16 @@ interface nsIPresentationSessionTranspor
   void onSessionTransport(in nsIPresentationSessionTransport transport);
 
   void onError(in nsresult reason);
 };
 
 [scriptable, uuid(2fdbe67d-80f9-48dc-8237-5bef8fa19801)]
 interface nsIPresentationSessionTransportBuilder : nsISupports
 {
-  const unsigned short TYPE_SENDER = 1;
-  const unsigned short TYPE_RECEIVER = 2;
 };
 
 /**
  * Builder for TCP session transport
  */
 [scriptable, uuid(cde36d6e-f471-4262-a70d-f932a26b21d9)]
 interface nsIPresentationTCPSessionTransportBuilder : nsIPresentationSessionTransportBuilder
 {
--- a/dom/presentation/ipc/PPresentation.ipdl
+++ b/dom/presentation/ipc/PPresentation.ipdl
@@ -18,27 +18,30 @@ struct StartSessionRequest
   nsString sessionId;
   nsString origin;
   nsString deviceId;
 };
 
 struct SendSessionMessageRequest
 {
   nsString sessionId;
+  uint8_t role;
   nsString data;
 };
 
 struct CloseSessionRequest
 {
   nsString sessionId;
+  uint8_t role;
 };
 
 struct TerminateSessionRequest
 {
   nsString sessionId;
+  uint8_t role;
 };
 
 union PresentationIPCRequest
 {
   StartSessionRequest;
   SendSessionMessageRequest;
   CloseSessionRequest;
   TerminateSessionRequest;
@@ -56,18 +59,18 @@ child:
   async NotifySessionConnect(uint64_t aWindowId, nsString aSessionId);
 
 parent:
   async __delete__();
 
   async RegisterAvailabilityHandler();
   async UnregisterAvailabilityHandler();
 
-  async RegisterSessionHandler(nsString aSessionId);
-  async UnregisterSessionHandler(nsString aSessionId);
+  async RegisterSessionHandler(nsString aSessionId, uint8_t aRole);
+  async UnregisterSessionHandler(nsString aSessionId, uint8_t aRole);
 
   async RegisterRespondingHandler(uint64_t aWindowId);
   async UnregisterRespondingHandler(uint64_t aWindowId);
 
   async PPresentationRequest(PresentationIPCRequest aRequest);
 
   async NotifyReceiverReady(nsString aSessionId);
 };
--- a/dom/presentation/ipc/PresentationIPCService.cpp
+++ b/dom/presentation/ipc/PresentationIPCService.cpp
@@ -61,39 +61,43 @@ PresentationIPCService::StartSession(con
   return SendRequest(aCallback, StartSessionRequest(nsString(aUrl),
                                                     nsString(aSessionId),
                                                     nsString(aOrigin),
                                                     nsString(aDeviceId)));
 }
 
 NS_IMETHODIMP
 PresentationIPCService::SendSessionMessage(const nsAString& aSessionId,
+                                           uint8_t aRole,
                                            const nsAString& aData)
 {
   MOZ_ASSERT(!aSessionId.IsEmpty());
   MOZ_ASSERT(!aData.IsEmpty());
 
   return SendRequest(nullptr, SendSessionMessageRequest(nsString(aSessionId),
+                                                        aRole,
                                                         nsString(aData)));
 }
 
 NS_IMETHODIMP
-PresentationIPCService::CloseSession(const nsAString& aSessionId)
+PresentationIPCService::CloseSession(const nsAString& aSessionId,
+                                     uint8_t aRole)
 {
   MOZ_ASSERT(!aSessionId.IsEmpty());
 
-  return SendRequest(nullptr, CloseSessionRequest(nsString(aSessionId)));
+  return SendRequest(nullptr, CloseSessionRequest(nsString(aSessionId), aRole));
 }
 
 NS_IMETHODIMP
-PresentationIPCService::TerminateSession(const nsAString& aSessionId)
+PresentationIPCService::TerminateSession(const nsAString& aSessionId,
+                                         uint8_t aRole)
 {
   MOZ_ASSERT(!aSessionId.IsEmpty());
 
-  return SendRequest(nullptr, TerminateSessionRequest(nsString(aSessionId)));
+  return SendRequest(nullptr, TerminateSessionRequest(nsString(aSessionId), aRole));
 }
 
 nsresult
 PresentationIPCService::SendRequest(nsIPresentationServiceCallback* aCallback,
                                     const PresentationIPCRequest& aRequest)
 {
   if (sPresentationChild) {
     PresentationRequestChild* actor = new PresentationRequestChild(aCallback);
@@ -125,38 +129,40 @@ PresentationIPCService::UnregisterAvaila
   if (sPresentationChild) {
     NS_WARN_IF(!sPresentationChild->SendUnregisterAvailabilityHandler());
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationIPCService::RegisterSessionListener(const nsAString& aSessionId,
+                                                uint8_t aRole,
                                                 nsIPresentationSessionListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aListener);
 
   mSessionListeners.Put(aSessionId, aListener);
   if (sPresentationChild) {
-    NS_WARN_IF(!sPresentationChild->SendRegisterSessionHandler(nsString(aSessionId)));
+    NS_WARN_IF(!sPresentationChild->SendRegisterSessionHandler(nsString(aSessionId), aRole));
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::UnregisterSessionListener(const nsAString& aSessionId)
+PresentationIPCService::UnregisterSessionListener(const nsAString& aSessionId,
+                                                  uint8_t aRole)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  UntrackSessionInfo(aSessionId);
+  UntrackSessionInfo(aSessionId, aRole);
 
   mSessionListeners.Remove(aSessionId);
   if (sPresentationChild) {
-    NS_WARN_IF(!sPresentationChild->SendUnregisterSessionHandler(nsString(aSessionId)));
+    NS_WARN_IF(!sPresentationChild->SendUnregisterSessionHandler(nsString(aSessionId), aRole));
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationIPCService::RegisterRespondingListener(uint64_t aWindowId,
                                                    nsIPresentationRespondingListener* aListener)
 {
@@ -273,17 +279,18 @@ PresentationIPCService::NotifyReceiverRe
 
   // Release mCallback after using aSessionId
   // because aSessionId is held by mCallback.
   mCallback = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::UntrackSessionInfo(const nsAString& aSessionId)
+PresentationIPCService::UntrackSessionInfo(const nsAString& aSessionId,
+                                           uint8_t aRole)
 {
   // Remove the OOP responding info (if it has never been used).
   uint64_t windowId = 0;
   if (mRespondingWindowIds.Get(aSessionId, &windowId)) {
     mRespondingWindowIds.Remove(aSessionId);
     mRespondingSessionIds.Remove(windowId);
   }
 
--- a/dom/presentation/ipc/PresentationParent.cpp
+++ b/dom/presentation/ipc/PresentationParent.cpp
@@ -40,20 +40,27 @@ PresentationParent::Init()
   return NS_WARN_IF(!mService) ? false : true;
 }
 
 void
 PresentationParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mActorDestroyed = true;
 
-  for (uint32_t i = 0; i < mSessionIds.Length(); i++) {
-    NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(mSessionIds[i])));
+  for (uint32_t i = 0; i < mSessionIdsAtController.Length(); i++) {
+    NS_WARN_IF(NS_FAILED(mService->
+      UnregisterSessionListener(mSessionIdsAtController[i], nsIPresentationService::ROLE_CONTROLLER)));
   }
-  mSessionIds.Clear();
+  mSessionIdsAtController.Clear();
+
+  for (uint32_t i = 0; i < mSessionIdsAtReceiver.Length(); i++) {
+    NS_WARN_IF(NS_FAILED(mService->
+      UnregisterSessionListener(mSessionIdsAtReceiver[i], nsIPresentationService::ROLE_RECEIVER)));
+  }
+  mSessionIdsAtReceiver.Clear();
 
   for (uint32_t i = 0; i < mWindowIds.Length(); i++) {
     NS_WARN_IF(NS_FAILED(mService->UnregisterRespondingListener(mWindowIds[i])));
   }
   mWindowIds.Clear();
 
   mService->UnregisterAvailabilityListener(this);
   mService = nullptr;
@@ -123,38 +130,48 @@ bool
 PresentationParent::RecvUnregisterAvailabilityHandler()
 {
   MOZ_ASSERT(mService);
   NS_WARN_IF(NS_FAILED(mService->UnregisterAvailabilityListener(this)));
   return true;
 }
 
 /* virtual */ bool
-PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId)
+PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId,
+                                               const uint8_t& aRole)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
   // compromised child process can't fake the ID.
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
-                  IsSessionAccessible(aSessionId, OtherPid()))) {
+                  IsSessionAccessible(aSessionId, aRole, OtherPid()))) {
     return true;
   }
 
-  mSessionIds.AppendElement(aSessionId);
-  NS_WARN_IF(NS_FAILED(mService->RegisterSessionListener(aSessionId, this)));
+  if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
+    mSessionIdsAtController.AppendElement(aSessionId);
+  } else {
+    mSessionIdsAtReceiver.AppendElement(aSessionId);
+  }
+  NS_WARN_IF(NS_FAILED(mService->RegisterSessionListener(aSessionId, aRole, this)));
   return true;
 }
 
 /* virtual */ bool
-PresentationParent::RecvUnregisterSessionHandler(const nsString& aSessionId)
+PresentationParent::RecvUnregisterSessionHandler(const nsString& aSessionId,
+                                                 const uint8_t& aRole)
 {
   MOZ_ASSERT(mService);
-  mSessionIds.RemoveElement(aSessionId);
-  NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(aSessionId)));
+  if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
+    mSessionIdsAtController.RemoveElement(aSessionId);
+  } else {
+    mSessionIdsAtReceiver.RemoveElement(aSessionId);
+  }
+  NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(aSessionId, aRole)));
   return true;
 }
 
 /* virtual */ bool
 PresentationParent::RecvRegisterRespondingHandler(const uint64_t& aWindowId)
 {
   MOZ_ASSERT(mService);
 
@@ -262,60 +279,61 @@ PresentationRequestParent::DoRequest(con
 nsresult
 PresentationRequestParent::DoRequest(const SendSessionMessageRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
   // compromised child process can't fake the ID.
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
-                  IsSessionAccessible(aRequest.sessionId(), OtherPid()))) {
+                  IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
     return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
   }
 
   nsresult rv = mService->SendSessionMessage(aRequest.sessionId(),
+                                             aRequest.role(),
                                              aRequest.data());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NotifyError(rv);
   }
   return NotifySuccess();
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const CloseSessionRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
   // compromised child process can't fake the ID.
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
-                  IsSessionAccessible(aRequest.sessionId(), OtherPid()))) {
+                  IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
     return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
   }
 
-  nsresult rv = mService->CloseSession(aRequest.sessionId());
+  nsresult rv = mService->CloseSession(aRequest.sessionId(), aRequest.role());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NotifyError(rv);
   }
   return NotifySuccess();
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const TerminateSessionRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
   // compromised child process can't fake the ID.
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
-                  IsSessionAccessible(aRequest.sessionId(), OtherPid()))) {
+                  IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
     return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
   }
 
-  nsresult rv = mService->TerminateSession(aRequest.sessionId());
+  nsresult rv = mService->TerminateSession(aRequest.sessionId(), aRequest.role());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NotifyError(rv);
   }
   return NotifySuccess();
 }
 
 NS_IMETHODIMP
 PresentationRequestParent::NotifySuccess()
--- a/dom/presentation/ipc/PresentationParent.h
+++ b/dom/presentation/ipc/PresentationParent.h
@@ -43,32 +43,35 @@ public:
   DeallocPPresentationRequestParent(PPresentationRequestParent* aActor) override;
 
   virtual bool Recv__delete__() override;
 
   virtual bool RecvRegisterAvailabilityHandler() override;
 
   virtual bool RecvUnregisterAvailabilityHandler() override;
 
-  virtual bool RecvRegisterSessionHandler(const nsString& aSessionId) override;
+  virtual bool RecvRegisterSessionHandler(const nsString& aSessionId,
+                                          const uint8_t& aRole) override;
 
-  virtual bool RecvUnregisterSessionHandler(const nsString& aSessionId) override;
+  virtual bool RecvUnregisterSessionHandler(const nsString& aSessionId,
+                                            const uint8_t& aRole) override;
 
   virtual bool RecvRegisterRespondingHandler(const uint64_t& aWindowId) override;
 
   virtual bool RecvUnregisterRespondingHandler(const uint64_t& aWindowId) override;
 
   virtual bool RecvNotifyReceiverReady(const nsString& aSessionId) override;
 
 private:
   virtual ~PresentationParent();
 
   bool mActorDestroyed;
   nsCOMPtr<nsIPresentationService> mService;
-  nsTArray<nsString> mSessionIds;
+  nsTArray<nsString> mSessionIdsAtController;
+  nsTArray<nsString> mSessionIdsAtReceiver;
   nsTArray<uint64_t> mWindowIds;
 };
 
 class PresentationRequestParent final : public PPresentationRequestParent
                                       , public nsIPresentationServiceCallback
 {
   friend class PresentationParent;
 
new file mode 100644
--- /dev/null
+++ b/dom/presentation/provider/DisplayDeviceProvider.cpp
@@ -0,0 +1,462 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DisplayDeviceProvider.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/unused.h"
+#include "nsIObserverService.h"
+#include "nsIServiceManager.h"
+#include "nsITCPPresentationServer.h"
+#include "nsIWindowWatcher.h"
+#include "nsNetUtil.h"
+#include "nsPIDOMWindow.h"
+#include "nsSimpleURI.h"
+#include "nsTCPDeviceInfo.h"
+#include "nsThreadUtils.h"
+
+static mozilla::LazyLogModule gDisplayDeviceProviderLog("DisplayDeviceProvider");
+
+#define LOG(format) MOZ_LOG(gDisplayDeviceProviderLog, mozilla::LogLevel::Debug, format)
+
+#define DISPLAY_CHANGED_NOTIFICATION "display-changed"
+#define DEFAULT_CHROME_FEATURES_PREF "toolkit.defaultChromeFeatures"
+#define CHROME_REMOTE_URL_PREF       "b2g.multiscreen.chrome_remote_url"
+
+namespace mozilla {
+namespace dom {
+namespace presentation {
+
+/**
+ * This wrapper is used to break circular-reference problem.
+ */
+class DisplayDeviceProviderWrappedListener final
+  : public nsITCPPresentationServerListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_FORWARD_SAFE_NSITCPPRESENTATIONSERVERLISTENER(mListener)
+
+  explicit DisplayDeviceProviderWrappedListener() = default;
+
+  nsresult SetListener(DisplayDeviceProvider* aListener)
+  {
+    mListener = aListener;
+    return NS_OK;
+  }
+
+private:
+  virtual ~DisplayDeviceProviderWrappedListener() = default;
+
+  DisplayDeviceProvider* mListener = nullptr;
+};
+
+NS_IMPL_ISUPPORTS(DisplayDeviceProviderWrappedListener,
+                  nsITCPPresentationServerListener)
+
+NS_IMPL_ISUPPORTS(DisplayDeviceProvider::HDMIDisplayDevice,
+                  nsIPresentationDevice,
+                  nsIPresentationLocalDevice)
+
+// nsIPresentationDevice
+NS_IMETHODIMP
+DisplayDeviceProvider::HDMIDisplayDevice::GetId(nsACString& aId)
+{
+  aId = mWindowId;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DisplayDeviceProvider::HDMIDisplayDevice::GetName(nsACString& aName)
+{
+  aName = mName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DisplayDeviceProvider::HDMIDisplayDevice::GetType(nsACString& aType)
+{
+  aType = mType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DisplayDeviceProvider::HDMIDisplayDevice::GetWindowId(nsACString& aWindowId)
+{
+  aWindowId = mWindowId;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DisplayDeviceProvider::HDMIDisplayDevice
+                     ::EstablishControlChannel(const nsAString& aUrl,
+                                               const nsAString& aPresentationId,
+                                               nsIPresentationControlChannel** aControlChannel)
+{
+  nsresult rv = OpenTopLevelWindow();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  RefPtr<DisplayDeviceProvider> provider = mProvider.get();
+  if (NS_WARN_IF(!provider)) {
+    return NS_ERROR_FAILURE;
+  }
+  return provider->RequestSession(this, aUrl, aPresentationId, aControlChannel);
+}
+
+NS_IMETHODIMP
+DisplayDeviceProvider::HDMIDisplayDevice::Disconnect()
+{
+  nsresult rv = CloseTopLevelWindow();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+  }
+  return NS_OK;;
+}
+
+nsresult
+DisplayDeviceProvider::HDMIDisplayDevice::OpenTopLevelWindow()
+{
+  MOZ_ASSERT(!mWindow);
+
+  nsresult rv;
+  nsAutoCString flags(Preferences::GetCString(DEFAULT_CHROME_FEATURES_PREF));
+  if (flags.IsEmpty()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  flags.AppendLiteral(",mozDisplayId=");
+  flags.AppendInt(mScreenId);
+
+  nsAutoCString remoteShellURLString(Preferences::GetCString(CHROME_REMOTE_URL_PREF));
+  remoteShellURLString.AppendLiteral("#");
+  remoteShellURLString.Append(mWindowId);
+
+  // URI validation
+  nsCOMPtr<nsIURI> remoteShellURL;
+  rv = NS_NewURI(getter_AddRefs(remoteShellURL), remoteShellURLString);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = remoteShellURL->GetSpec(remoteShellURLString);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
+  MOZ_ASSERT(ww);
+
+  rv = ww->OpenWindow(nullptr,
+                      remoteShellURLString.get(),
+                      "_blank",
+                      flags.get(),
+                      nullptr,
+                      getter_AddRefs(mWindow));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DisplayDeviceProvider::HDMIDisplayDevice::CloseTopLevelWindow()
+{
+  MOZ_ASSERT(mWindow);
+
+  nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(mWindow);
+  nsresult rv = piWindow->Close();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(DisplayDeviceProvider,
+                  nsIObserver,
+                  nsIPresentationDeviceProvider,
+                  nsITCPPresentationServerListener)
+
+DisplayDeviceProvider::~DisplayDeviceProvider()
+{
+  Uninit();
+}
+
+nsresult
+DisplayDeviceProvider::Init()
+{
+  // Provider must be initialized only once.
+  if (mInitialized) {
+    return NS_OK;
+  }
+
+  nsresult rv;
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  MOZ_ASSERT(obs);
+
+  obs->AddObserver(this, DISPLAY_CHANGED_NOTIFICATION, false);
+
+  mDevice = new HDMIDisplayDevice(this);
+
+  mWrappedListener = new DisplayDeviceProviderWrappedListener();
+  rv = mWrappedListener->SetListener(this);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mPresentationServer = do_CreateInstance(TCP_PRESENTATION_SERVER_CONTACT_ID,
+                                          &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = StartTCPService();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mInitialized = true;
+  return NS_OK;
+}
+
+nsresult
+DisplayDeviceProvider::Uninit()
+{
+  // Provider must be deleted only once.
+  if (!mInitialized) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->RemoveObserver(this, DISPLAY_CHANGED_NOTIFICATION);
+  }
+
+  // Remove device from device manager when the provider is uninit
+  RemoveExternalScreen();
+
+  mInitialized = false;
+  mWrappedListener->SetListener(nullptr);
+  return NS_OK;
+}
+
+nsresult
+DisplayDeviceProvider::StartTCPService()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsresult rv;
+  rv =  mPresentationServer->SetId(NS_LITERAL_CSTRING("DisplayDeviceProvider"));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  uint16_t servicePort;
+  rv = mPresentationServer->GetPort(&servicePort);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  /*
+   * If |servicePort| is non-zero, it means PresentationServer is running.
+   * Otherwise, we should make it start serving.
+   */
+  if (!servicePort) {
+    rv = mPresentationServer->SetListener(mWrappedListener);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = mPresentationServer->StartService(0);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = mPresentationServer->GetPort(&servicePort);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  mPort = servicePort;
+
+  return NS_OK;
+}
+
+nsresult
+DisplayDeviceProvider::AddExternalScreen()
+{
+  MOZ_ASSERT(mDeviceListener);
+
+  nsresult rv;
+  nsCOMPtr<nsIPresentationDeviceListener> listener;
+  rv = GetListener(getter_AddRefs(listener));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = listener->AddDevice(mDevice);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DisplayDeviceProvider::RemoveExternalScreen()
+{
+  MOZ_ASSERT(mDeviceListener);
+
+  nsresult rv;
+  nsCOMPtr<nsIPresentationDeviceListener> listener;
+  rv = GetListener(getter_AddRefs(listener));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = listener->RemoveDevice(mDevice);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mDevice->Disconnect();
+  return NS_OK;
+}
+
+// nsIPresentationDeviceProvider
+NS_IMETHODIMP
+DisplayDeviceProvider::GetListener(nsIPresentationDeviceListener** aListener)
+{
+  if (NS_WARN_IF(!aListener)) {
+    return NS_ERROR_INVALID_POINTER;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIPresentationDeviceListener> listener =
+    do_QueryReferent(mDeviceListener, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+  }
+
+  listener.forget(aListener);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DisplayDeviceProvider::SetListener(nsIPresentationDeviceListener* aListener)
+{
+  mDeviceListener = do_GetWeakReference(aListener);
+  nsresult rv = mDeviceListener ? Init() : Uninit();
+  if(NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DisplayDeviceProvider::ForceDiscovery()
+{
+  return NS_OK;
+}
+
+// nsITCPPresentationServerListener
+NS_IMETHODIMP
+DisplayDeviceProvider::OnPortChange(uint16_t aPort)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mPort = aPort;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DisplayDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo,
+                                      const nsAString& aUrl,
+                                      const nsAString& aPresentationId,
+                                      nsIPresentationControlChannel* aControlChannel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aDeviceInfo);
+  MOZ_ASSERT(aControlChannel);
+
+  nsresult rv;
+
+  nsCOMPtr<nsIPresentationDeviceListener> listener;
+  rv = GetListener(getter_AddRefs(listener));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(!listener);
+
+  rv = listener->OnSessionRequest(mDevice,
+                                  aUrl,
+                                  aPresentationId,
+                                  aControlChannel);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+// nsIObserver
+NS_IMETHODIMP
+DisplayDeviceProvider::Observe(nsISupports* aSubject,
+                               const char* aTopic,
+                               const char16_t* aData)
+{
+  if (!strcmp(aTopic, DISPLAY_CHANGED_NOTIFICATION)) {
+    nsCOMPtr<nsIDisplayInfo> displayInfo = do_QueryInterface(aSubject);
+    MOZ_ASSERT(displayInfo);
+
+    int32_t type;
+    bool isConnected;
+    displayInfo->GetConnected(&isConnected);
+    // XXX The ID is as same as the type of display.
+    // See Bug 1138287 and nsScreenManagerGonk::AddScreen() for more detail.
+    displayInfo->GetId(&type);
+
+    if (type == DisplayType::DISPLAY_EXTERNAL) {
+      nsresult rv = isConnected ? AddExternalScreen() : RemoveExternalScreen();
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DisplayDeviceProvider::RequestSession(HDMIDisplayDevice* aDevice,
+                                      const nsAString& aUrl,
+                                      const nsAString& aPresentationId,
+                                      nsIPresentationControlChannel** aControlChannel)
+{
+  MOZ_ASSERT(aDevice);
+  MOZ_ASSERT(mPresentationServer);
+  NS_ENSURE_ARG_POINTER(aControlChannel);
+  *aControlChannel = nullptr;
+
+  nsCOMPtr<nsITCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(),
+                                                            aDevice->Address(),
+                                                            mPort);
+
+  return mPresentationServer->RequestSession(deviceInfo,
+                                             aUrl,
+                                             aPresentationId,
+                                             aControlChannel);
+}
+
+} // namespace presentation
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/presentation/provider/DisplayDeviceProvider.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_presentation_provider_DisplayDeviceProvider_h
+#define mozilla_dom_presentation_provider_DisplayDeviceProvider_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/WeakPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIDOMWindow.h"
+#include "nsIDisplayInfo.h"
+#include "nsIObserver.h"
+#include "nsIPresentationDeviceProvider.h"
+#include "nsIPresentationLocalDevice.h"
+#include "nsITCPPresentationServer.h"
+#include "nsIWindowWatcher.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+namespace dom {
+namespace presentation {
+
+// Consistent definition with the definition in
+// widget/gonk/libdisplay/GonkDisplay.h.
+enum DisplayType {
+    DISPLAY_PRIMARY,
+    DISPLAY_EXTERNAL,
+    DISPLAY_VIRTUAL,
+    NUM_DISPLAY_TYPES
+};
+
+class DisplayDeviceProviderWrappedListener;
+
+class DisplayDeviceProvider final : public nsIObserver
+                                  , public nsIPresentationDeviceProvider
+                                  , public nsITCPPresentationServerListener
+                                  , public SupportsWeakPtr<DisplayDeviceProvider>
+{
+private:
+  class HDMIDisplayDevice final : public nsIPresentationLocalDevice
+  {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIPRESENTATIONDEVICE
+    NS_DECL_NSIPRESENTATIONLOCALDEVICE
+
+    // mScreenId is as same as the definition of display type.
+    explicit HDMIDisplayDevice(DisplayDeviceProvider* aProvider)
+      : mScreenId(DisplayType::DISPLAY_EXTERNAL)
+      , mName("HDMI")
+      , mType("external")
+      , mWindowId("hdmi")
+      , mAddress("127.0.0.1")
+      , mProvider(aProvider)
+    {}
+
+    nsresult OpenTopLevelWindow();
+    nsresult CloseTopLevelWindow();
+
+    const nsCString& Id() const { return mWindowId; }
+    const nsCString& Address() const { return mAddress; }
+
+  private:
+    virtual ~HDMIDisplayDevice() = default;
+
+    // Due to the limitation of nsWinodw, mScreenId must be an integer.
+    // And mScreenId is also align to the display type defined in
+    // widget/gonk/libdisplay/GonkDisplay.h.
+    // HDMI display is DisplayType::DISPLAY_EXTERNAL.
+    uint32_t mScreenId;
+    nsCString mName;
+    nsCString mType;
+    nsCString mWindowId;
+    nsCString mAddress;
+
+    nsCOMPtr<mozIDOMWindowProxy> mWindow;
+    // weak pointer
+    // Provider hold a strong pointer to the device. Use weak pointer to prevent
+    // the reference cycle.
+    WeakPtr<DisplayDeviceProvider> mProvider;
+  };
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSIPRESENTATIONDEVICEPROVIDER
+  NS_DECL_NSITCPPRESENTATIONSERVERLISTENER
+  // For using WeakPtr when MOZ_REFCOUNTED_LEAK_CHECKING defined
+  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(DisplayDeviceProvider)
+
+  nsresult RequestSession(HDMIDisplayDevice* aDevice,
+                          const nsAString& aUrl,
+                          const nsAString& aPresentationId,
+                          nsIPresentationControlChannel** aControlChannel);
+private:
+  virtual ~DisplayDeviceProvider();
+
+  nsresult Init();
+  nsresult Uninit();
+
+  nsresult AddExternalScreen();
+  nsresult RemoveExternalScreen();
+
+  nsresult StartTCPService();
+