Merge m-c to fx-team, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 14 Sep 2015 17:28:21 -0700
changeset 295088 e543c88468c04cb43c926a6bb2631a59b47e7e04
parent 295087 08cf2a208a6077bdc2160f3a21fe948f042702f1 (current diff)
parent 295050 c69e31de9aec2b9e8c0b3128ac6dc3897aa7381f (diff)
child 295089 d81b35757962722421c4cd36dc88cf2a0eee0d7d
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.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 m-c to fx-team, a=merge
dom/webidl/PresentationAvailableEvent.webidl
testing/web-platform/meta/html/semantics/scripting-1/the-template-element/template-element/template-descendant-frameset.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/methods/non-element-nodes-001.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/methods/test-003.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/extensions-to-event-interface/event-path-001.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/extensions-to-event-interface/event-path-002.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/activeElement-confirm-return-null.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-004.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-006.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-007.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/the-content-html-element/test-001.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/the-content-html-element/test-002.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/the-content-html-element/test-003.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/the-content-html-element/test-005.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-001.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-002.html.ini
testing/web-platform/meta/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-004.html.ini
testing/web-platform/meta/shadow-dom/events/event-dispatch/test-003.html.ini
testing/web-platform/meta/shadow-dom/events/events-that-are-always-stopped/test-001.html.ini
testing/web-platform/meta/shadow-dom/events/events-that-are-always-stopped/test-002.html.ini
testing/web-platform/meta/shadow-dom/events/events-that-are-always-stopped/test-003.html.ini
testing/web-platform/meta/shadow-dom/events/events-that-are-always-stopped/test-005.html.ini
testing/web-platform/meta/shadow-dom/events/events-that-are-always-stopped/test-007.html.ini
testing/web-platform/meta/shadow-dom/events/events-that-are-always-stopped/test-008.html.ini
testing/web-platform/meta/shadow-dom/events/events-that-are-always-stopped/test-009.html.ini
testing/web-platform/meta/shadow-dom/events/retargeting-focus-events/test-002.html.ini
testing/web-platform/meta/shadow-dom/events/retargeting-focus-events/test-003.html.ini
testing/web-platform/meta/shadow-dom/events/retargeting-relatedtarget/test-001.html.ini
testing/web-platform/meta/shadow-dom/events/retargeting-relatedtarget/test-002.html.ini
testing/web-platform/meta/shadow-dom/events/retargeting-relatedtarget/test-003.html.ini
testing/web-platform/meta/shadow-dom/events/test-001.html.ini
testing/web-platform/meta/shadow-dom/html-elements-and-their-shadow-trees/test-001.html.ini
testing/web-platform/meta/shadow-dom/html-elements-and-their-shadow-trees/test-003.html.ini
testing/web-platform/meta/shadow-dom/html-elements-and-their-shadow-trees/test-004.html.ini
testing/web-platform/meta/shadow-dom/html-elements-in-shadow-trees/html-forms/test-003.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/composition/test-001.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/content-pseudo-element/test-001.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/content-pseudo-element/test-002.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/custom-pseudo-elements/test-001.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/lower-boundary-encapsulation/test-004.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/rendering-shadow-trees/test-001.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/upper-boundary-encapsulation/test-005.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/upper-boundary-encapsulation/test-007.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/upper-boundary-encapsulation/test-009.html.ini
testing/web-platform/meta/shadow-dom/styles/css-variables/test-001.html.ini
testing/web-platform/meta/shadow-dom/styles/deep-combinator/deep-combinator-001.html.ini
testing/web-platform/meta/shadow-dom/styles/shadow-pseudoelement/shadow-pseudoelement-001.html.ini
testing/web-platform/meta/shadow-dom/styles/test-001.html.ini
testing/web-platform/meta/shadow-dom/styles/test-005.html.ini
testing/web-platform/meta/shadow-dom/styles/test-007.html.ini
testing/web-platform/meta/shadow-dom/styles/test-008.html.ini
testing/web-platform/meta/shadow-dom/styles/test-009.html.ini
testing/web-platform/meta/shadow-dom/styles/test-010.html.ini
testing/web-platform/meta/shadow-dom/user-interaction/active-element/test-001.html.ini
testing/web-platform/meta/shadow-dom/user-interaction/active-element/test-002.html.ini
testing/web-platform/meta/shadow-dom/user-interaction/focus-navigation/test-001.html.ini
testing/web-platform/meta/shadow-dom/user-interaction/focus-navigation/test-002.html.ini
testing/web-platform/meta/shadow-dom/user-interaction/focus-navigation/test-003.html.ini
testing/web-platform/meta/shadow-dom/user-interaction/focus-navigation/test-004.html.ini
testing/web-platform/meta/shadow-dom/user-interaction/ranges-and-selections/test-002.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/clients-matchall-client-types.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-fallback.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/unregister-controller.https.html.ini
testing/web-platform/tests/shadow-dom/LICENSE
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/attributes/test-001.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/attributes/test-005.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/attributes/test-006.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/methods/elements-001.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/methods/non-element-nodes-001.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/methods/test-001.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/methods/test-002.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/methods/test-003.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-event-interface/event-path-001.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/extensions-to-event-interface/event-path-002.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/activeElement-confirm-return-null.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-009.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-010.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-011.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-012.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-013.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-014.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-001.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-004.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-006.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-007.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-010.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/the-content-html-element/test-001.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/the-content-html-element/test-002.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/the-content-html-element/test-003.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/the-content-html-element/test-005.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/the-content-html-element/test-006.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-001.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-002.html
testing/web-platform/tests/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-004.html
testing/web-platform/tests/shadow-dom/events/event-dispatch/test-001.html
testing/web-platform/tests/shadow-dom/events/event-dispatch/test-002.html
testing/web-platform/tests/shadow-dom/events/event-dispatch/test-003.html
testing/web-platform/tests/shadow-dom/events/event-retargeting/test-001.html
testing/web-platform/tests/shadow-dom/events/event-retargeting/test-002.html
testing/web-platform/tests/shadow-dom/events/event-retargeting/test-003.html
testing/web-platform/tests/shadow-dom/events/event-retargeting/test-004.html
testing/web-platform/tests/shadow-dom/events/events-that-are-always-stopped/test-001.html
testing/web-platform/tests/shadow-dom/events/events-that-are-always-stopped/test-002.html
testing/web-platform/tests/shadow-dom/events/events-that-are-always-stopped/test-003.html
testing/web-platform/tests/shadow-dom/events/events-that-are-always-stopped/test-004.html
testing/web-platform/tests/shadow-dom/events/events-that-are-always-stopped/test-005.html
testing/web-platform/tests/shadow-dom/events/events-that-are-always-stopped/test-006.html
testing/web-platform/tests/shadow-dom/events/events-that-are-always-stopped/test-007.html
testing/web-platform/tests/shadow-dom/events/events-that-are-always-stopped/test-008.html
testing/web-platform/tests/shadow-dom/events/events-that-are-always-stopped/test-009.html
testing/web-platform/tests/shadow-dom/events/retargeting-focus-events/test-001.html
testing/web-platform/tests/shadow-dom/events/retargeting-focus-events/test-002.html
testing/web-platform/tests/shadow-dom/events/retargeting-focus-events/test-003.html
testing/web-platform/tests/shadow-dom/events/retargeting-relatedtarget/test-001.html
testing/web-platform/tests/shadow-dom/events/retargeting-relatedtarget/test-002.html
testing/web-platform/tests/shadow-dom/events/retargeting-relatedtarget/test-003.html
testing/web-platform/tests/shadow-dom/events/test-001.html
testing/web-platform/tests/shadow-dom/html-elements-and-their-shadow-trees/test-001.html
testing/web-platform/tests/shadow-dom/html-elements-and-their-shadow-trees/test-002.html
testing/web-platform/tests/shadow-dom/html-elements-and-their-shadow-trees/test-003.html
testing/web-platform/tests/shadow-dom/html-elements-and-their-shadow-trees/test-004.html
testing/web-platform/tests/shadow-dom/html-elements-in-shadow-trees/html-forms/test-001.html
testing/web-platform/tests/shadow-dom/html-elements-in-shadow-trees/html-forms/test-002.html
testing/web-platform/tests/shadow-dom/html-elements-in-shadow-trees/html-forms/test-003.html
testing/web-platform/tests/shadow-dom/html-elements-in-shadow-trees/inert-html-elements/test-001.html
testing/web-platform/tests/shadow-dom/html-elements-in-shadow-trees/inert-html-elements/test-002.html
testing/web-platform/tests/shadow-dom/resources/blank.html
testing/web-platform/tests/shadow-dom/resources/bobs_page.html
testing/web-platform/tests/shadow-dom/shadow-trees/composition/test-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/content-pseudo-element/test-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/content-pseudo-element/test-002.html
testing/web-platform/tests/shadow-dom/shadow-trees/custom-pseudo-elements/test-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/lower-boundary-encapsulation/distribution-001-ref.html
testing/web-platform/tests/shadow-dom/shadow-trees/lower-boundary-encapsulation/distribution-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/lower-boundary-encapsulation/distribution-002-ref.html
testing/web-platform/tests/shadow-dom/shadow-trees/lower-boundary-encapsulation/distribution-002.html
testing/web-platform/tests/shadow-dom/shadow-trees/lower-boundary-encapsulation/distribution-003.html
testing/web-platform/tests/shadow-dom/shadow-trees/lower-boundary-encapsulation/test-003.html
testing/web-platform/tests/shadow-dom/shadow-trees/lower-boundary-encapsulation/test-004.html
testing/web-platform/tests/shadow-dom/shadow-trees/lower-boundary-encapsulation/test-005.html
testing/web-platform/tests/shadow-dom/shadow-trees/nested-shadow-trees/nested_tree_reftest-ref.html
testing/web-platform/tests/shadow-dom/shadow-trees/nested-shadow-trees/nested_tree_reftest.html
testing/web-platform/tests/shadow-dom/shadow-trees/nested-shadow-trees/test-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/rendering-shadow-trees/test-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/reprojection/reprojection-001-ref.html
testing/web-platform/tests/shadow-dom/shadow-trees/reprojection/reprojection-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/reprojection/reprojection-002-ref.html
testing/web-platform/tests/shadow-dom/shadow-trees/reprojection/reprojection-002.html
testing/web-platform/tests/shadow-dom/shadow-trees/reprojection/test-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/satisfying-matching-criteria/test-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/satisfying-matching-criteria/test-002.html
testing/web-platform/tests/shadow-dom/shadow-trees/satisfying-matching-criteria/test-003.html
testing/web-platform/tests/shadow-dom/shadow-trees/satisfying-matching-criteria/test-004.html
testing/web-platform/tests/shadow-dom/shadow-trees/satisfying-matching-criteria/test-005.html
testing/web-platform/tests/shadow-dom/shadow-trees/shadow-root-001-ref.html
testing/web-platform/tests/shadow-dom/shadow-trees/shadow-root-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/shadow-root-002-ref.html
testing/web-platform/tests/shadow-dom/shadow-trees/shadow-root-002.html
testing/web-platform/tests/shadow-dom/shadow-trees/text-decoration-001-ref.html
testing/web-platform/tests/shadow-dom/shadow-trees/text-decoration-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-002.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/ownerdocument-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/ownerdocument-002.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/selectors-api-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/selectors-api-002.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/shadow-root-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/test-005.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/test-007.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/test-009.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/test-011.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/window-named-properties-001.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/window-named-properties-002.html
testing/web-platform/tests/shadow-dom/shadow-trees/upper-boundary-encapsulation/window-named-properties-003.html
testing/web-platform/tests/shadow-dom/styles/css-variables/test-001.html
testing/web-platform/tests/shadow-dom/styles/deep-combinator/deep-combinator-001.html
testing/web-platform/tests/shadow-dom/styles/not-apply-in-shadow-root-001-ref.html
testing/web-platform/tests/shadow-dom/styles/not-apply-in-shadow-root-001.html
testing/web-platform/tests/shadow-dom/styles/shadow-pseudoelement/shadow-pseudoelement-001.html
testing/web-platform/tests/shadow-dom/styles/test-001.html
testing/web-platform/tests/shadow-dom/styles/test-003.html
testing/web-platform/tests/shadow-dom/styles/test-005.html
testing/web-platform/tests/shadow-dom/styles/test-007.html
testing/web-platform/tests/shadow-dom/styles/test-008.html
testing/web-platform/tests/shadow-dom/styles/test-009.html
testing/web-platform/tests/shadow-dom/styles/test-010.html
testing/web-platform/tests/shadow-dom/testcommon.js
testing/web-platform/tests/shadow-dom/user-interaction/active-element/test-001.html
testing/web-platform/tests/shadow-dom/user-interaction/active-element/test-002.html
testing/web-platform/tests/shadow-dom/user-interaction/editing/inheritance-of-content-editable-001.html
testing/web-platform/tests/shadow-dom/user-interaction/focus-navigation/test-001.html
testing/web-platform/tests/shadow-dom/user-interaction/focus-navigation/test-002.html
testing/web-platform/tests/shadow-dom/user-interaction/focus-navigation/test-003.html
testing/web-platform/tests/shadow-dom/user-interaction/focus-navigation/test-004.html
testing/web-platform/tests/shadow-dom/user-interaction/ranges-and-selections/test-001.html
testing/web-platform/tests/shadow-dom/user-interaction/ranges-and-selections/test-002.html
testing/web-platform/tests/websockets/handlers/handshake_sleep_1_wsh.py
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1829,20 +1829,22 @@ Accessible::DispatchClickEvent(nsIConten
 
   nsSize size = frame->GetSize();
 
   nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
   int32_t x = presContext->AppUnitsToDevPixels(point.x + size.width / 2);
   int32_t y = presContext->AppUnitsToDevPixels(point.y + size.height / 2);
 
   // Simulate a touch interaction by dispatching touch events with mouse events.
-  nsCoreUtils::DispatchTouchEvent(NS_TOUCH_START, x, y, aContent, frame, presShell, widget);
+  nsCoreUtils::DispatchTouchEvent(eTouchStart, x, y, aContent, frame,
+                                  presShell, widget);
   nsCoreUtils::DispatchMouseEvent(eMouseDown, x, y, aContent, frame,
                                   presShell, widget);
-  nsCoreUtils::DispatchTouchEvent(NS_TOUCH_END, x, y, aContent, frame, presShell, widget);
+  nsCoreUtils::DispatchTouchEvent(eTouchEnd, x, y, aContent, frame,
+                                  presShell, widget);
   nsCoreUtils::DispatchMouseEvent(eMouseUp, x, y, aContent, frame,
                                   presShell, widget);
 }
 
 void
 Accessible::ScrollToPoint(uint32_t aCoordinateType, int32_t aX, int32_t aY)
 {
   nsIFrame* frame = GetFrame();
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -303,21 +303,25 @@ this.AccessFu = { // jshint ignore:line
 
   observe: function observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case 'Accessibility:Settings':
         this._systemPref = JSON.parse(aData).enabled;
         this._enableOrDisable();
         break;
       case 'Accessibility:NextObject':
-        this.Input.moveCursor('moveNext', 'Simple', 'gesture');
+      case 'Accessibility:PreviousObject':
+      {
+        let rule = aData ?
+          aData.substr(0, 1).toUpperCase() + aData.substr(1).toLowerCase() :
+          'Simple';
+        let method = aTopic.replace(/Accessibility:(\w+)Object/, 'move$1');
+        this.Input.moveCursor(method, rule, 'gesture');
         break;
-      case 'Accessibility:PreviousObject':
-        this.Input.moveCursor('movePrevious', 'Simple', 'gesture');
-        break;
+      }
       case 'Accessibility:ActivateObject':
         this.Input.activateCurrent(JSON.parse(aData));
         break;
       case 'Accessibility:LongPress':
         this.Input.sendContextMenuMessage();
         break;
       case 'Accessibility:ScrollForward':
         this.Input.androidScroll('forward');
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -103,18 +103,19 @@ Presenter.prototype = {
    *   virtual cursor position.
    */
   tabSelected: function tabSelected(aDocContext, aVCContext) {}, // jshint ignore:line
 
   /**
    * The viewport has changed, either a scroll, pan, zoom, or
    *    landscape/portrait toggle.
    * @param {Window} aWindow window of viewport that changed.
+   * @param {PivotContext} aCurrentContext context of last pivot change.
    */
-  viewportChanged: function viewportChanged(aWindow) {}, // jshint ignore:line
+  viewportChanged: function viewportChanged(aWindow, aCurrentContext) {}, // jshint ignore:line
 
   /**
    * We have entered or left text editing mode.
    */
   editingModeChanged: function editingModeChanged(aIsEditing) {}, // jshint ignore:line
 
   /**
    * Announce something. Typically an app state change.
@@ -137,39 +138,36 @@ Presenter.prototype = {
    */
   liveRegion: function liveRegionShown(aContext, aIsPolite, aIsHide, // jshint ignore:line
     aModifiedText) {} // jshint ignore:line
 };
 
 /**
  * Visual presenter. Draws a box around the virtual cursor's position.
  */
-function VisualPresenter() {
-  this._displayedAccessibles = new WeakMap();
-}
+function VisualPresenter() {}
 
 VisualPresenter.prototype = Object.create(Presenter.prototype);
 
 VisualPresenter.prototype.type = 'Visual';
 
 /**
  * The padding in pixels between the object and the highlight border.
  */
 VisualPresenter.prototype.BORDER_PADDING = 2;
 
 VisualPresenter.prototype.viewportChanged =
-  function VisualPresenter_viewportChanged(aWindow) {
-    let currentDisplay = this._displayedAccessibles.get(aWindow);
-    if (!currentDisplay) {
+  function VisualPresenter_viewportChanged(aWindow, aCurrentContext) {
+    if (!aCurrentContext) {
       return null;
     }
 
-    let currentAcc = currentDisplay.accessible;
-    let start = currentDisplay.startOffset;
-    let end = currentDisplay.endOffset;
+    let currentAcc = aCurrentContext.accessibleForBounds;
+    let start = aCurrentContext.startOffset;
+    let end = aCurrentContext.endOffset;
     if (Utils.isAliveAndVisible(currentAcc)) {
       let bounds = (start === -1 && end === -1) ? Utils.getBounds(currentAcc) :
                    Utils.getTextBounds(currentAcc, start, end);
 
       return {
         type: this.type,
         details: {
           eventType: 'viewport-change',
@@ -184,21 +182,16 @@ VisualPresenter.prototype.viewportChange
 
 VisualPresenter.prototype.pivotChanged =
   function VisualPresenter_pivotChanged(aContext) {
     if (!aContext.accessible) {
       // XXX: Don't hide because another vc may be using the highlight.
       return null;
     }
 
-    this._displayedAccessibles.set(aContext.accessible.document.window,
-                                   { accessible: aContext.accessibleForBounds,
-                                     startOffset: aContext.startOffset,
-                                     endOffset: aContext.endOffset });
-
     try {
       aContext.accessibleForBounds.scrollTo(
         Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
 
       let bounds = (aContext.startOffset === -1 && aContext.endOffset === -1) ?
             aContext.bounds : Utils.getTextBounds(aContext.accessibleForBounds,
                                                   aContext.startOffset,
                                                   aContext.endOffset);
@@ -408,31 +401,43 @@ AndroidPresenter.prototype.textSelection
 
     return {
       type: this.type,
       details: androidEvents
     };
   };
 
 AndroidPresenter.prototype.viewportChanged =
-  function AndroidPresenter_viewportChanged(aWindow) {
+  function AndroidPresenter_viewportChanged(aWindow, aCurrentContext) {
     if (Utils.AndroidSdkVersion < 14) {
       return null;
     }
 
+    let events = [{
+      eventType: this.ANDROID_VIEW_SCROLLED,
+      text: [],
+      scrollX: aWindow.scrollX,
+      scrollY: aWindow.scrollY,
+      maxScrollX: aWindow.scrollMaxX,
+      maxScrollY: aWindow.scrollMaxY
+    }];
+
+    if (Utils.AndroidSdkVersion >= 16 && aCurrentContext) {
+      let currentAcc = aCurrentContext.accessibleForBounds;
+      if (Utils.isAliveAndVisible(currentAcc)) {
+        events.push({
+          eventType: this.ANDROID_VIEW_ACCESSIBILITY_FOCUSED,
+          bounds: Utils.getBounds(currentAcc)
+        });
+      }
+    }
+
     return {
       type: this.type,
-      details: [{
-        eventType: this.ANDROID_VIEW_SCROLLED,
-        text: [],
-        scrollX: aWindow.scrollX,
-        scrollY: aWindow.scrollY,
-        maxScrollX: aWindow.scrollMaxX,
-        maxScrollY: aWindow.scrollMaxY
-      }]
+      details: events
     };
   };
 
 AndroidPresenter.prototype.editingModeChanged =
   function AndroidPresenter_editingModeChanged(aIsEditing) {
     return this.announce(UtteranceGenerator.genForEditingMode(aIsEditing));
   };
 
@@ -678,20 +683,28 @@ this.Presentation = { // jshint ignore:l
       'mobile/android': [VisualPresenter, AndroidPresenter],
       'b2g': [VisualPresenter, B2GPresenter],
       'browser': [VisualPresenter, B2GPresenter, AndroidPresenter]
     };
     this.presenters = [new P() for (P of presenterMap[Utils.MozBuildApp])]; // jshint ignore:line
     return this.presenters;
   },
 
+  get displayedAccessibles() {
+    delete this.displayedAccessibles;
+    this.displayedAccessibles = new WeakMap();
+    return this.displayedAccessibles;
+  },
+
   pivotChanged: function Presentation_pivotChanged(
     aPosition, aOldPosition, aReason, aStartOffset, aEndOffset, aIsUserInput) {
     let context = new PivotContext(
       aPosition, aOldPosition, aStartOffset, aEndOffset);
+    this.displayedAccessibles.set(context.accessible.document.window, context);
+
     return [p.pivotChanged(context, aReason, aIsUserInput)
       for each (p in this.presenters)]; // jshint ignore:line
   },
 
   actionInvoked: function Presentation_actionInvoked(aObject, aActionName) {
     return [p.actionInvoked(aObject, aActionName) // jshint ignore:line
       for each (p in this.presenters)]; // jshint ignore:line
   },
@@ -719,17 +732,19 @@ this.Presentation = { // jshint ignore:l
   },
 
   tabStateChanged: function Presentation_tabStateChanged(aDocObj, aPageState) {
     return [p.tabStateChanged(aDocObj, aPageState) // jshint ignore:line
       for each (p in this.presenters)]; // jshint ignore:line
   },
 
   viewportChanged: function Presentation_viewportChanged(aWindow) {
-    return [p.viewportChanged(aWindow) for each (p in this.presenters)]; // jshint ignore:line
+    let context = this.displayedAccessibles.get(aWindow);
+    return [p.viewportChanged(aWindow, context) // jshint ignore:line
+      for each (p in this.presenters)]; // jshint ignore:line
   },
 
   editingModeChanged: function Presentation_editingModeChanged(aIsEditing) {
     return [p.editingModeChanged(aIsEditing) for each (p in this.presenters)]; // jshint ignore:line
   },
 
   announce: function Presentation_announce(aAnnouncement) {
     // XXX: Typically each presenter uses the UtteranceGenerator,
--- a/accessible/jsat/Traversal.jsm
+++ b/accessible/jsat/Traversal.jsm
@@ -291,16 +291,46 @@ this.TraversalRules = { // jshint ignore
       // We want to ignore anchors, only focus real links.
       if (Utils.getState(aAccessible).contains(States.LINKED)) {
         return Filters.MATCH;
       } else {
         return Filters.IGNORE;
       }
     }),
 
+  /* For TalkBack's "Control" granularity. Form conrols and links */
+  Control: new BaseTraversalRule(
+    [Roles.PUSHBUTTON,
+     Roles.SPINBUTTON,
+     Roles.TOGGLE_BUTTON,
+     Roles.BUTTONDROPDOWN,
+     Roles.BUTTONDROPDOWNGRID,
+     Roles.COMBOBOX,
+     Roles.LISTBOX,
+     Roles.ENTRY,
+     Roles.PASSWORD_TEXT,
+     Roles.PAGETAB,
+     Roles.RADIOBUTTON,
+     Roles.RADIO_MENU_ITEM,
+     Roles.SLIDER,
+     Roles.CHECKBUTTON,
+     Roles.CHECK_MENU_ITEM,
+     Roles.SWITCH,
+     Roles.LINK,
+     Roles.MENUITEM],
+    function Control_match(aAccessible)
+    {
+      // We want to ignore anchors, only focus real links.
+      if (aAccessible.role == Roles.LINK &&
+          !Utils.getState(aAccessible).contains(States.LINKED)) {
+        return Filters.IGNORE;
+      }
+      return Filters.MATCH;
+    }),
+
   List: new BaseTraversalRule(
     [Roles.LIST,
      Roles.DEFINITION_LIST],
     null, null, true),
 
   PageTab: new BaseTraversalRule(
     [Roles.PAGETAB]),
 
--- a/accessible/tests/mochitest/jsat/test_traversal.html
+++ b/accessible/tests/mochitest/jsat/test_traversal.html
@@ -125,16 +125,27 @@
                               'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
                               'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2',
                               'switch-1', 'This is a MathML formula ', 'math-1',
                               'with some text after.']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Landmark, null,
                              ['header-1', 'main-1', 'footer-1']);
 
+
+      queueTraversalSequence(gQueue, docAcc, TraversalRules.Control, null,
+                             ['input-1-1', 'label-1-2', 'button-1-1',
+                              'radio-1-1', 'radio-1-2', 'input-1-3',
+                              'input-1-4', 'button-1-2', 'checkbox-1-1',
+                              'select-1-1', 'select-1-2', 'checkbox-1-2',
+                              'select-1-3', 'input-1-5', 'button-1-3',
+                              'button-2-1', 'button-2-2', 'button-2-3',
+                              'button-2-4', 'link-0', 'checkbox-1-5',
+                              'link-1', 'link-2', 'link-3', 'switch-1']);
+
       gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(function () {
       /* We open a new browser because we need to test with a top-level content
          document. */
       openBrowserWindow(
--- a/accessible/windows/ProxyWrappers.h
+++ b/accessible/windows/ProxyWrappers.h
@@ -26,26 +26,47 @@ class ProxyAccessibleWrap : public Acces
   virtual void Shutdown() override
   {
     mBits.proxy = nullptr;
   }
 };
 
 class HyperTextProxyAccessibleWrap : public HyperTextAccessibleWrap
 {
+public:
   HyperTextProxyAccessibleWrap(ProxyAccessible* aProxy) :
     HyperTextAccessibleWrap(nullptr, nullptr)
   {
     mType = eProxyType;
     mBits.proxy = aProxy;
   }
 
   virtual void Shutdown() override { mBits.proxy = nullptr; }
 };
 
+class DocProxyAccessibleWrap : public HyperTextProxyAccessibleWrap
+{
+public:
+  DocProxyAccessibleWrap(ProxyAccessible* aProxy) :
+    HyperTextProxyAccessibleWrap(aProxy)
+  { mGenericTypes |= eDocument; }
+
+  void AddID(uint32_t aID, AccessibleWrap* aAcc)
+    { mIDToAccessibleMap.Put(aID, aAcc); }
+  void RemoveID(uint32_t aID) { mIDToAccessibleMap.Remove(aID); }
+  AccessibleWrap* GetAccessibleByID(uint32_t aID) const
+    { return mIDToAccessibleMap.Get(aID); }
+
+private:
+  /*
+   * This provides a mapping from 32 bit id to accessible objects.
+   */
+  nsDataHashtable<nsUint32HashKey, AccessibleWrap*> mIDToAccessibleMap;
+};
+
 template<typename T>
 inline ProxyAccessible*
 HyperTextProxyFor(T* aWrapper)
 {
   static_assert(mozilla::IsBaseOf<IUnknown, T>::value, "only IAccessible* should be passed in");
   auto wrapper = static_cast<HyperTextProxyAccessibleWrap*>(aWrapper);
   return wrapper->IsProxy() ? wrapper->Proxy() : nullptr;
 }
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -4,22 +4,24 @@
  * 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 "AccessibleWrap.h"
 #include "Accessible-inl.h"
 
 #include "Compatibility.h"
 #include "DocAccessible-inl.h"
+#include "mozilla/a11y/DocAccessibleParent.h"
 #include "EnumVariant.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "nsIAccessibleEvent.h"
 #include "nsWinUtils.h"
 #include "mozilla/a11y/ProxyAccessible.h"
+#include "ProxyWrappers.h"
 #include "ServiceProvider.h"
 #include "Relation.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "sdnAccessible.h"
 #include "States.h"
 
 #ifdef A11Y_LOG
@@ -53,18 +55,16 @@ const uint32_t USE_ROLE_STRING = 0;
 //#define DEBUG_LEAKS
 
 #ifdef DEBUG_LEAKS
 static gAccessibles = 0;
 #endif
 
 #ifdef _WIN64
 IDSet AccessibleWrap::sIDGen;
-
-static const uint32_t kNoID = 0;
 #endif
 
 static const int32_t kIEnumVariantDisconnected = -1;
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccessibleWrap
 ////////////////////////////////////////////////////////////////////////////////
 AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
@@ -1269,44 +1269,74 @@ AccessibleWrap::HandleAccEvent(AccEvent*
       accessible->Role() == roles::COMBOBOX_OPTION) {
       ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID);
     }
   }
 
   return NS_OK;
 }
 
+DocProxyAccessibleWrap*
+AccessibleWrap::DocProxyWrapper() const
+{
+  MOZ_ASSERT(IsProxy());
+
+  ProxyAccessible* proxy = Proxy();
+  if (!proxy) {
+    return nullptr;
+  }
+
+  AccessibleWrap* acc = WrapperFor(proxy->Document());
+  MOZ_ASSERT(acc->IsDoc());
+
+ return static_cast<DocProxyAccessibleWrap*>(acc);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // AccessibleWrap
 
 //------- Helper methods ---------
 
 int32_t
 AccessibleWrap::GetChildIDFor(Accessible* aAccessible)
 {
   // A child ID of the window is required, when we use NotifyWinEvent,
   // so that the 3rd party application can call back and get the IAccessible
   // the event occurred on.
 
 #ifdef _WIN64
-  if (!aAccessible || !aAccessible->Document())
+  if (!aAccessible || (!aAccessible->Document() && !aAccessible->IsProxy()))
     return 0;
 
   uint32_t* id = & static_cast<AccessibleWrap*>(aAccessible)->mID;
   if (*id != kNoID)
     return *id;
 
   *id = sIDGen.GetID();
-  DocAccessibleWrap* doc =
-    static_cast<DocAccessibleWrap*>(aAccessible->Document());
-  doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible));
+
+  if (aAccessible->IsProxy()) {
+    DocProxyAccessibleWrap* doc =
+      static_cast<AccessibleWrap*>(aAccessible)->DocProxyWrapper();
+    doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible));
+  } else {
+    DocAccessibleWrap* doc =
+      static_cast<DocAccessibleWrap*>(aAccessible->Document());
+    doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible));
+  }
 
   return *id;
 #else
-  return - reinterpret_cast<intptr_t>(aAccessible);
+  int32_t id = - reinterpret_cast<intptr_t>(aAccessible);
+  if (aAccessible->IsProxy()) {
+    DocProxyAccessibleWrap* doc =
+      static_cast<AccessibleWrap*>(aAccessible)->DocProxyWrapper();
+    doc->AddID(id, static_cast<AccessibleWrap*>(aAccessible));
+  }
+
+  return id;
 #endif
 }
 
 HWND
 AccessibleWrap::GetHWNDFor(Accessible* aAccessible)
 {
   if (!aAccessible) {
     return nullptr;
--- a/accessible/windows/msaa/AccessibleWrap.h
+++ b/accessible/windows/msaa/AccessibleWrap.h
@@ -21,16 +21,17 @@
 // Inheriting from both XPCOM and MSCOM interfaces causes a lot of warnings
 // about virtual functions being hidden by each other. This is done by
 // design, so silence the warning.
 #pragma GCC diagnostic ignored "-Woverloaded-virtual"
 #endif
 
 namespace mozilla {
 namespace a11y {
+class DocProxyAccessibleWrap;
 
 class AccessibleWrap : public Accessible,
                        public ia2Accessible,
                        public ia2AccessibleComponent,
                        public ia2AccessibleHyperlink,
                        public ia2AccessibleValue
 {
 public: // construction, destruction
@@ -171,24 +172,34 @@ public: // construction, destruction
    * Find an accessible by the given child ID in cached documents.
    */
   Accessible* GetXPAccessibleFor(const VARIANT& aVarChild);
 
   virtual void GetNativeInterface(void **aOutAccessible) override;
 
   static IDispatch* NativeAccessible(Accessible* aAccessible);
 
+#ifdef _WIN64
+  uint32_t GetExistingID() const { return mID; }
+  static const uint32_t kNoID = 0;
+#endif
+
 protected:
   virtual ~AccessibleWrap();
 
 #ifdef _WIN64
   uint32_t mID;
 #endif
 
   /**
+   * Return the wrapper for the document's proxy.
+   */
+  DocProxyAccessibleWrap* DocProxyWrapper() const;
+
+  /**
    * Creates ITypeInfo for LIBID_Accessibility if it's needed and returns it.
    */
   static ITypeInfo* GetTI(LCID lcid);
 
   static ITypeInfo* gTypeInfo;
 
 #ifdef _WIN64
   static IDSet sIDGen;
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -30,32 +30,51 @@ void
 a11y::PlatformShutdown()
 {
   ::DestroyCaret();
 
   nsWinUtils::ShutdownWindowEmulation();
 }
 
 void
-a11y::ProxyCreated(ProxyAccessible* aProxy, uint32_t)
+a11y::ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces)
 {
-  ProxyAccessibleWrap* wrapper = new ProxyAccessibleWrap(aProxy);
+  AccessibleWrap* wrapper = nullptr;
+  if (aInterfaces & Interfaces::DOCUMENT) {
+    wrapper = new DocProxyAccessibleWrap(aProxy);
+  } else if (aInterfaces & Interfaces::HYPERTEXT) {
+    wrapper = new HyperTextProxyAccessibleWrap(aProxy);
+  } else {
+    wrapper = new ProxyAccessibleWrap(aProxy);
+  }
+
   wrapper->AddRef();
   aProxy->SetWrapper(reinterpret_cast<uintptr_t>(wrapper));
 }
 
 void
 a11y::ProxyDestroyed(ProxyAccessible* aProxy)
 {
-  ProxyAccessibleWrap* wrapper =
-    reinterpret_cast<ProxyAccessibleWrap*>(aProxy->GetWrapper());
+  AccessibleWrap* wrapper =
+    reinterpret_cast<AccessibleWrap*>(aProxy->GetWrapper());
   MOZ_ASSERT(wrapper);
   if (!wrapper)
     return;
 
+  auto doc =
+    static_cast<DocProxyAccessibleWrap*>(WrapperFor(aProxy->Document()));
+#ifdef _WIN64
+  uint32_t id = wrapper->GetExistingID();
+  if (id != AccessibleWrap::kNoID) {
+    doc->RemoveID(id);
+  }
+#else
+  doc->RemoveID(-reinterpret_cast<int32_t>(wrapper));
+#endif
+
   wrapper->Shutdown();
   aProxy->SetWrapper(0);
   wrapper->Release();
 }
 
 void
 a11y::ProxyEvent(ProxyAccessible*, uint32_t)
 {
--- a/b2g/app/ua-update.json.in
+++ b/b2g/app/ua-update.json.in
@@ -1,28 +1,24 @@
 #filter slashslash
 // Everything after the first // on a line will be removed by the preproccesor.
 // Send these sites a custom user-agent. Bugs should be included with an entry.
 {
   // bug 826347, msn.com
-  "msn.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
+  "msn.com": "\\(Mobile#(Android 4.4.4; Mobile",
   // bug 826353, itau.com.br
   "itau.com.br": "\\(Mobile#(Android; Mobile",
   // bug 826510, r7.com
   "r7.com": "\\(Mobile#(Android; Mobile",
   // bug 827622, bing.com
   "bing.com": "\\(Mobile#(Android; Mobile",
   // bug 827626, magazineluiza.com.br
   "magazineluiza.com.br": "\\(Mobile#(Android; Mobile",
-  // bug 827633, hao123.com
-  "hao123.com": "\\(Mobile#(Android; Mobile",
   // bug 827670, elpais.com.co
-  "elpais.com.co": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
-  // bug 827674, avianca.com
-  "avianca.com": "\\(Mobile#(Android; Mobile",
+  "elpais.com.co": "\\(Mobile#(Android 4.4.4; Mobile",
   // bug 828416, loteriasyapuestas.es
   "loteriasyapuestas.es": "\\(Mobile#(Android; Mobile",
   // bug 828418, bbva.es
   "bbva.es": "\\(Mobile#(Android; Mobile",
   // bug 828439, movistar.com.ve
   "movistar.com.ve": "\\(Mobile#(Android; Mobile",
   // bug 843132, comunio.es
   "comunio.es": "\\(Mobile#(Android; Mobile",
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="067c08fb3e5744b42b68d1f861245f7d507109bc"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="067c08fb3e5744b42b68d1f861245f7d507109bc"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "f37e8f732e0af961b43e912629c84c9e2ceda55d", 
+        "git_revision": "e6994410dcc88bfead0e44620a065220b7f12290", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "f894ddb1493cd8cdd3d3f807d784bce313e3eb9f", 
+    "revision": "0611acb452ed9c34d02ccdf24850677490b716d1", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f37e8f732e0af961b43e912629c84c9e2ceda55d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6994410dcc88bfead0e44620a065220b7f12290"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>
--- a/browser/components/migration/IEProfileMigrator.js
+++ b/browser/components/migration/IEProfileMigrator.js
@@ -7,16 +7,17 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 const kLoginsKey = "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2";
 const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main";
 
+Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
 Cu.import("resource:///modules/MSMigrationUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
                                   "resource://gre/modules/ctypes.jsm");
@@ -118,17 +119,17 @@ History.prototype = {
       handleError: function() {},
       handleCompletion: function() {
         aCallback(this._success);
       }
     });
   }
 };
 
-// IE password migrator supporting windows from XP until 7 and IE from 7 until 11
+// IE form password migrator supporting windows from XP until 7 and IE from 7 until 11
 function IE7FormPasswords () {
 }
 
 IE7FormPasswords.prototype = {
   type: MigrationUtils.resourceTypes.PASSWORDS,
 
   get exists() {
     try {
@@ -519,19 +520,22 @@ function IEProfileMigrator()
 
 IEProfileMigrator.prototype = Object.create(MigratorPrototype);
 
 IEProfileMigrator.prototype.getResources = function IE_getResources() {
   let resources = [
     MSMigrationUtils.getBookmarksMigrator()
   , new History()
   , MSMigrationUtils.getCookiesMigrator()
-  , new IE7FormPasswords()
   , new Settings()
   ];
+  // Only support the form password migrator for Windows XP to 7.
+  if (AppConstants.isPlatformAndVersionAtMost("win", "6.1")) {
+    resources.push(new IE7FormPasswords());
+  }
   return [r for each (r in resources) if (r.exists)];
 };
 
 Object.defineProperty(IEProfileMigrator.prototype, "sourceHomePageURL", {
   get: function IE_get_sourceHomePageURL() {
     let defaultStartPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
                                                       kMainKey, "Default_Page_URL");
     let startPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
--- a/browser/devtools/markupview/test/browser_markupview_html_edit_03.js
+++ b/browser/devtools/markupview/test/browser_markupview_html_edit_03.js
@@ -13,16 +13,18 @@ const TEST_URL = "data:text/html," +
   "<body>" +
   "<div id=\"keyboard\"></div>" +
   "</body>" +
   "</html>";
 const SELECTOR = "#keyboard";
 const OLD_HTML = '<div id="keyboard"></div>';
 const NEW_HTML = '<div id="keyboard">Edited</div>';
 
+requestLongerTimeout(2);
+
 add_task(function*() {
   let {inspector} = yield addTab(TEST_URL).then(openInspector);
 
   inspector.markup._frame.focus();
 
   info("Checking that pressing escape cancels edits");
   yield testEscapeCancels(inspector);
 
--- a/build/valgrind/cross-architecture.sup
+++ b/build/valgrind/cross-architecture.sup
@@ -29,16 +29,25 @@
    PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793548.)
    Memcheck:Leak
    fun:malloc
    ...
    fun:_Z12ToNewCStringRK19nsACString_internal
    fun:_ZN13CrashReporter14SetupExtraDataEP7nsIFileRK19nsACString_internal
    ...
 }
+{
+   We purposely leak the StatisticsReporter object
+   Memcheck:Leak
+   fun:malloc
+   fun:moz_xmalloc
+   fun:operator new
+   fun:_Z21XRE_CreateStatsObjectv
+   ...
+}
 
 ####################################
 #  Leaks in third party libraries  #
 ####################################
 
 {
    See bug 793535
    Memcheck:Leak
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -809,17 +809,17 @@ nsIContent::PreHandleEvent(EventChainPre
       case eImageAbort:
       case eLoadError:
       case eFormSelect:
       case eFormChange:
       case eLoad:
       case eFormReset:
       case eResize:
       case eScroll:
-      case NS_SELECT_START:
+      case eSelectStart:
         stopEvent = true;
         break;
       case eUnidentifiedEvent:
         if (aVisitor.mDOMEvent) {
           nsAutoString eventType;
           aVisitor.mDOMEvent->GetType(eventType);
           if (eventType.EqualsLiteral("abort") ||
               eventType.EqualsLiteral("error") ||
--- a/dom/base/StructuredCloneHelper.cpp
+++ b/dom/base/StructuredCloneHelper.cpp
@@ -4,38 +4,49 @@
  * 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 "StructuredCloneHelper.h"
 
 #include "ImageContainer.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/CryptoKey.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileList.h"
 #include "mozilla/dom/FileListBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/StructuredClone.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/SubtleCryptoBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "MultipartBlobImpl.h"
 #include "nsFormData.h"
 #include "nsIRemoteBlob.h"
 #include "nsQueryObject.h"
 
+#ifdef MOZ_NFC
+#include "mozilla/dom/MozNDEFRecord.h"
+#endif // MOZ_NFC
+#ifdef MOZ_WEBRTC
+#include "mozilla/dom/RTCCertificate.h"
+#include "mozilla/dom/RTCCertificateBinding.h"
+#endif
+
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 JSObject*
@@ -363,16 +374,227 @@ StructuredCloneHelper::FreeBuffer(uint64
 {
   MOZ_ASSERT(!mBuffer, "FreeBuffer() must be called without a Write().");
   MOZ_ASSERT(aBuffer);
   MOZ_ASSERT(aBufferLength);
 
   JS_ClearStructuredClone(aBuffer, aBufferLength, &gCallbacks, this, false);
 }
 
+/* static */ JSObject*
+StructuredCloneHelper::ReadFullySerializableObjects(JSContext* aCx,
+                                                    JSStructuredCloneReader* aReader,
+                                                    uint32_t aTag,
+                                                    uint32_t aIndex)
+{
+  if (aTag == SCTAG_DOM_IMAGEDATA) {
+    return ReadStructuredCloneImageData(aCx, aReader);
+  }
+
+  if (aTag == SCTAG_DOM_WEBCRYPTO_KEY) {
+    if (!NS_IsMainThread()) {
+      return nullptr;
+    }
+
+    nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+    if (!global) {
+      return nullptr;
+    }
+
+    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
+    JS::Rooted<JSObject*> result(aCx);
+    {
+      nsRefPtr<CryptoKey> key = new CryptoKey(global);
+      if (!key->ReadStructuredClone(aReader)) {
+        result = nullptr;
+      } else {
+        result = key->WrapObject(aCx, nullptr);
+      }
+    }
+    return result;
+  }
+
+  if (aTag == SCTAG_DOM_NULL_PRINCIPAL ||
+      aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
+      aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
+    if (!NS_IsMainThread()) {
+      return nullptr;
+    }
+
+    mozilla::ipc::PrincipalInfo info;
+    if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
+      info = mozilla::ipc::SystemPrincipalInfo();
+    } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
+      info = mozilla::ipc::NullPrincipalInfo();
+    } else {
+      uint32_t appId = aIndex;
+
+      uint32_t isInBrowserElement, specLength;
+      if (!JS_ReadUint32Pair(aReader, &isInBrowserElement, &specLength)) {
+        return nullptr;
+      }
+
+      nsAutoCString spec;
+      spec.SetLength(specLength);
+      if (!JS_ReadBytes(aReader, spec.BeginWriting(), specLength)) {
+        return nullptr;
+      }
+
+      info = mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement,
+                                                spec);
+    }
+
+    nsresult rv;
+    nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(info, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+      return nullptr;
+    }
+
+    JS::RootedValue result(aCx);
+    rv = nsContentUtils::WrapNative(aCx, principal, &NS_GET_IID(nsIPrincipal),
+                                    &result);
+    if (NS_FAILED(rv)) {
+      xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+      return nullptr;
+    }
+
+    return result.toObjectOrNull();
+  }
+
+#ifdef MOZ_NFC
+  if (aTag == SCTAG_DOM_NFC_NDEF) {
+    if (!NS_IsMainThread()) {
+      return nullptr;
+    }
+
+    nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+    if (!global) {
+      return nullptr;
+    }
+
+    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
+    JS::Rooted<JSObject*> result(aCx);
+    {
+      nsRefPtr<MozNDEFRecord> ndefRecord = new MozNDEFRecord(global);
+      result = ndefRecord->ReadStructuredClone(aCx, aReader) ?
+               ndefRecord->WrapObject(aCx, nullptr) : nullptr;
+    }
+    return result;
+  }
+#endif
+
+#ifdef MOZ_WEBRTC
+  if (aTag == SCTAG_DOM_RTC_CERTIFICATE) {
+    if (!NS_IsMainThread()) {
+      return nullptr;
+    }
+
+    nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+    if (!global) {
+      return nullptr;
+    }
+
+    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
+    JS::Rooted<JSObject*> result(aCx);
+    {
+      nsRefPtr<RTCCertificate> cert = new RTCCertificate(global);
+      if (!cert->ReadStructuredClone(aReader)) {
+        result = nullptr;
+      } else {
+        result = cert->WrapObject(aCx, nullptr);
+      }
+    }
+    return result;
+  }
+#endif
+
+  // Don't know what this is. Bail.
+  xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+  return nullptr;
+}
+
+/* static */ bool
+StructuredCloneHelper::WriteFullySerializableObjects(JSContext* aCx,
+                                                     JSStructuredCloneWriter* aWriter,
+                                                     JS::Handle<JSObject*> aObj)
+{
+  // See if this is a ImageData object.
+  {
+    ImageData* imageData = nullptr;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) {
+      return WriteStructuredCloneImageData(aCx, aWriter, imageData);
+    }
+  }
+
+  // Handle Key cloning
+  {
+    CryptoKey* key;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, aObj, key))) {
+      MOZ_ASSERT(NS_IsMainThread());
+      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
+             key->WriteStructuredClone(aWriter);
+    }
+  }
+
+#ifdef MOZ_WEBRTC
+  {
+    // Handle WebRTC Certificate cloning
+    RTCCertificate* cert;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCCertificate, aObj, cert))) {
+      MOZ_ASSERT(NS_IsMainThread());
+      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_RTC_CERTIFICATE, 0) &&
+             cert->WriteStructuredClone(aWriter);
+    }
+  }
+#endif
+
+  if (NS_IsMainThread() && xpc::IsReflector(aObj)) {
+    nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(aObj);
+    nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
+    if (principal) {
+      mozilla::ipc::PrincipalInfo info;
+      if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(principal, &info)))) {
+        xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+        return false;
+      }
+
+      if (info.type() == mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
+        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0);
+      }
+      if (info.type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
+        return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
+      }
+
+      MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
+      const mozilla::ipc::ContentPrincipalInfo& cInfo = info;
+      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL,
+                                cInfo.appId()) &&
+             JS_WriteUint32Pair(aWriter, cInfo.isInBrowserElement(),
+                                cInfo.spec().Length()) &&
+             JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length());
+    }
+  }
+
+#ifdef MOZ_NFC
+  {
+    MozNDEFRecord* ndefRecord;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(MozNDEFRecord, aObj, ndefRecord))) {
+      MOZ_ASSERT(NS_IsMainThread());
+      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NFC_NDEF, 0) &&
+             ndefRecord->WriteStructuredClone(aCx, aWriter);
+    }
+  }
+#endif // MOZ_NFC
+
+  // Don't know what this is
+  xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+  return false;
+}
+
 namespace {
 
 // Recursive!
 already_AddRefed<BlobImpl>
 EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl,
                                PBackgroundChild* aManager = nullptr)
 {
   MOZ_ASSERT(aBlobImpl);
@@ -726,37 +948,33 @@ StructuredCloneHelper::ReadCallback(JSCo
   if (aTag == SCTAG_DOM_BLOB) {
     return ReadBlob(aCx, aIndex, this);
   }
 
   if (aTag == SCTAG_DOM_FILELIST) {
     return ReadFileList(aCx, aReader, aIndex, this);
   }
 
-  if (aTag == SCTAG_DOM_IMAGEDATA) {
-    return ReadStructuredCloneImageData(aCx, aReader);
-  }
-
   if (aTag == SCTAG_DOM_FORMDATA) {
     return ReadFormData(aCx, aReader, aIndex, this);
   }
 
   if (aTag == SCTAG_DOM_IMAGEBITMAP) {
     MOZ_ASSERT(mContext == SameProcessSameThread ||
                mContext == SameProcessDifferentThread);
 
-     // Get the current global object.
-     // This can be null.
-     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
-     // aIndex is the index of the cloned image.
-     return ImageBitmap::ReadStructuredClone(aCx, aReader,
-                                             parent, GetImages(), aIndex);
+    // Get the current global object.
+    // This can be null.
+    nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
+    // aIndex is the index of the cloned image.
+    return ImageBitmap::ReadStructuredClone(aCx, aReader,
+                                            parent, GetImages(), aIndex);
    }
 
-  return NS_DOMReadStructuredClone(aCx, aReader, aTag, aIndex, nullptr);
+  return ReadFullySerializableObjects(aCx, aReader, aTag, aIndex);
 }
 
 bool
 StructuredCloneHelper::WriteCallback(JSContext* aCx,
                                      JSStructuredCloneWriter* aWriter,
                                      JS::Handle<JSObject*> aObj)
 {
   if (!mSupportsCloning) {
@@ -774,24 +992,16 @@ StructuredCloneHelper::WriteCallback(JSC
   // See if this is a FileList object.
   {
     FileList* fileList = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
       return WriteFileList(aWriter, fileList, this);
     }
   }
 
-  // See if this is a ImageData object.
-  {
-    ImageData* imageData = nullptr;
-    if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) {
-      return WriteStructuredCloneImageData(aCx, aWriter, imageData);
-    }
-  }
-
   // See if this is a FormData object.
   {
     nsFormData* formData = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
       return WriteFormData(aWriter, formData, this);
     }
   }
 
@@ -801,17 +1011,17 @@ StructuredCloneHelper::WriteCallback(JSC
     ImageBitmap* imageBitmap = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
       return ImageBitmap::WriteStructuredClone(aWriter,
                                                GetImages(),
                                                imageBitmap);
     }
   }
 
-  return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
+  return WriteFullySerializableObjects(aCx, aWriter, aObj);
 }
 
 bool
 StructuredCloneHelper::ReadTransferCallback(JSContext* aCx,
                                             JSStructuredCloneReader* aReader,
                                             uint32_t aTag,
                                             void* aContent,
                                             uint64_t aExtraData,
--- a/dom/base/StructuredCloneHelper.h
+++ b/dom/base/StructuredCloneHelper.h
@@ -233,16 +233,26 @@ public:
                                      JS::TransferableOwnership* aOwnership,
                                      void** aContent,
                                      uint64_t* aExtraData) override;
 
   virtual void FreeTransferCallback(uint32_t aTag,
                                     JS::TransferableOwnership aOwnership,
                                     void* aContent,
                                     uint64_t aExtraData) override;
+
+  static JSObject* ReadFullySerializableObjects(JSContext* aCx,
+                                                JSStructuredCloneReader* aReader,
+                                                uint32_t aTag,
+                                                uint32_t aIndex);
+
+  static bool  WriteFullySerializableObjects(JSContext* aCx,
+                                             JSStructuredCloneWriter* aWriter,
+                                             JS::Handle<JSObject*> aObj);
+
 protected:
   // If you receive a buffer from IPC, you can use this method to retrieve a
   // JS::Value. It can happen that you want to pre-populate the array of Blobs
   // and/or the PortIdentifiers.
   void ReadFromBuffer(nsISupports* aParent,
                       JSContext* aCx,
                       uint64_t* aBuffer,
                       size_t aBufferLength,
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -957,23 +957,23 @@ nsDOMWindowUtils::SendTouchEventCommon(c
   // get the widget to send the event to
   nsPoint offset;
   nsCOMPtr<nsIWidget> widget = GetWidget(&offset);
   if (!widget) {
     return NS_ERROR_NULL_POINTER;
   }
   EventMessage msg;
   if (aType.EqualsLiteral("touchstart")) {
-    msg = NS_TOUCH_START;
+    msg = eTouchStart;
   } else if (aType.EqualsLiteral("touchmove")) {
-    msg = NS_TOUCH_MOVE;
+    msg = eTouchMove;
   } else if (aType.EqualsLiteral("touchend")) {
-    msg = NS_TOUCH_END;
+    msg = eTouchEnd;
   } else if (aType.EqualsLiteral("touchcancel")) {
-    msg = NS_TOUCH_CANCEL;
+    msg = eTouchCancel;
   } else {
     return NS_ERROR_UNEXPECTED;
   }
   WidgetTouchEvent event(true, msg, widget);
   event.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
   event.widget = widget;
   event.time = PR_Now();
 
@@ -1291,51 +1291,52 @@ nsDOMWindowUtils::SendSimpleGestureEvent
 
   // get the widget to send the event to
   nsPoint offset;
   nsCOMPtr<nsIWidget> widget = GetWidget(&offset);
   if (!widget)
     return NS_ERROR_FAILURE;
 
   EventMessage msg;
-  if (aType.EqualsLiteral("MozSwipeGestureMayStart"))
-    msg = NS_SIMPLE_GESTURE_SWIPE_MAY_START;
-  else if (aType.EqualsLiteral("MozSwipeGestureStart"))
-    msg = NS_SIMPLE_GESTURE_SWIPE_START;
-  else if (aType.EqualsLiteral("MozSwipeGestureUpdate"))
-    msg = NS_SIMPLE_GESTURE_SWIPE_UPDATE;
-  else if (aType.EqualsLiteral("MozSwipeGestureEnd"))
-    msg = NS_SIMPLE_GESTURE_SWIPE_END;
-  else if (aType.EqualsLiteral("MozSwipeGesture"))
-    msg = NS_SIMPLE_GESTURE_SWIPE;
-  else if (aType.EqualsLiteral("MozMagnifyGestureStart"))
-    msg = NS_SIMPLE_GESTURE_MAGNIFY_START;
-  else if (aType.EqualsLiteral("MozMagnifyGestureUpdate"))
-    msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE;
-  else if (aType.EqualsLiteral("MozMagnifyGesture"))
-    msg = NS_SIMPLE_GESTURE_MAGNIFY;
-  else if (aType.EqualsLiteral("MozRotateGestureStart"))
-    msg = NS_SIMPLE_GESTURE_ROTATE_START;
-  else if (aType.EqualsLiteral("MozRotateGestureUpdate"))
-    msg = NS_SIMPLE_GESTURE_ROTATE_UPDATE;
-  else if (aType.EqualsLiteral("MozRotateGesture"))
-    msg = NS_SIMPLE_GESTURE_ROTATE;
-  else if (aType.EqualsLiteral("MozTapGesture"))
-    msg = NS_SIMPLE_GESTURE_TAP;
-  else if (aType.EqualsLiteral("MozPressTapGesture"))
-    msg = NS_SIMPLE_GESTURE_PRESSTAP;
-  else if (aType.EqualsLiteral("MozEdgeUIStarted"))
-    msg = NS_SIMPLE_GESTURE_EDGE_STARTED;
-  else if (aType.EqualsLiteral("MozEdgeUICanceled"))
-    msg = NS_SIMPLE_GESTURE_EDGE_CANCELED;
-  else if (aType.EqualsLiteral("MozEdgeUICompleted"))
-    msg = NS_SIMPLE_GESTURE_EDGE_COMPLETED;
-  else
+  if (aType.EqualsLiteral("MozSwipeGestureMayStart")) {
+    msg = eSwipeGestureMayStart;
+  } else if (aType.EqualsLiteral("MozSwipeGestureStart")) {
+    msg = eSwipeGestureStart;
+  } else if (aType.EqualsLiteral("MozSwipeGestureUpdate")) {
+    msg = eSwipeGestureUpdate;
+  } else if (aType.EqualsLiteral("MozSwipeGestureEnd")) {
+    msg = eSwipeGestureEnd;
+  } else if (aType.EqualsLiteral("MozSwipeGesture")) {
+    msg = eSwipeGesture;
+  } else if (aType.EqualsLiteral("MozMagnifyGestureStart")) {
+    msg = eMagnifyGestureStart;
+  } else if (aType.EqualsLiteral("MozMagnifyGestureUpdate")) {
+    msg = eMagnifyGestureUpdate;
+  } else if (aType.EqualsLiteral("MozMagnifyGesture")) {
+    msg = eMagnifyGesture;
+  } else if (aType.EqualsLiteral("MozRotateGestureStart")) {
+    msg = eRotateGestureStart;
+  } else if (aType.EqualsLiteral("MozRotateGestureUpdate")) {
+    msg = eRotateGestureUpdate;
+  } else if (aType.EqualsLiteral("MozRotateGesture")) {
+    msg = eRotateGesture;
+  } else if (aType.EqualsLiteral("MozTapGesture")) {
+    msg = eTapGesture;
+  } else if (aType.EqualsLiteral("MozPressTapGesture")) {
+    msg = ePressTapGesture;
+  } else if (aType.EqualsLiteral("MozEdgeUIStarted")) {
+    msg = eEdgeUIStarted;
+  } else if (aType.EqualsLiteral("MozEdgeUICanceled")) {
+    msg = eEdgeUICanceled;
+  } else if (aType.EqualsLiteral("MozEdgeUICompleted")) {
+    msg = eEdgeUICompleted;
+  } else {
     return NS_ERROR_FAILURE;
- 
+  }
+
   WidgetSimpleGestureEvent event(true, msg, widget);
   event.modifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
   event.direction = aDirection;
   event.delta = aDelta;
   event.clickCount = aClickCount;
   event.time = PR_IntervalNow();
 
   nsPresContext* presContext = GetPresContext();
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -680,17 +680,16 @@ GK_ATOM(onafterscriptexecute, "onaftersc
 GK_ATOM(onalerting, "onalerting")
 GK_ATOM(onanimationend, "onanimationend")
 GK_ATOM(onanimationiteration, "onanimationiteration")
 GK_ATOM(onanimationstart, "onanimationstart")
 GK_ATOM(onantennaavailablechange, "onantennaavailablechange")
 GK_ATOM(onAppCommand, "onAppCommand")
 GK_ATOM(onattributechanged, "onattributechanged")
 GK_ATOM(onaudioprocess, "onaudioprocess")
-GK_ATOM(onavailablechange, "onavailablechange")
 GK_ATOM(onbeforecopy, "onbeforecopy")
 GK_ATOM(onbeforecut, "onbeforecut")
 GK_ATOM(onbeforepaste, "onbeforepaste")
 GK_ATOM(onbeforeevicted, "onbeforeevicted")
 GK_ATOM(onbeforeprint, "onbeforeprint")
 GK_ATOM(onbeforescriptexecute, "onbeforescriptexecute")
 GK_ATOM(onbeforeunload, "onbeforeunload")
 GK_ATOM(onblocked, "onblocked")
@@ -881,16 +880,18 @@ GK_ATOM(onrtchange, "onrtchange")
 GK_ATOM(onscanningstatechanged, "onscanningstatechanged")
 GK_ATOM(onscostatuschanged, "onscostatuschanged")
 GK_ATOM(onscroll, "onscroll")
 GK_ATOM(onselect, "onselect")
 GK_ATOM(onselectionchange, "onselectionchange")
 GK_ATOM(onselectstart, "onselectstart")
 GK_ATOM(onsending, "onsending")
 GK_ATOM(onsent, "onsent")
+GK_ATOM(onsessionavailable, "onsessionavailable")
+GK_ATOM(onsessionconnect, "onsessionconnect")
 GK_ATOM(onset, "onset")
 GK_ATOM(onshow, "onshow")
 GK_ATOM(onshutter, "onshutter")
 GK_ATOM(onstatechange, "onstatechange")
 GK_ATOM(onstatuschanged, "onstatuschanged")
 GK_ATOM(onstkcommand, "onstkcommand")
 GK_ATOM(onstksessionend, "onstksessionend")
 GK_ATOM(onstorageareachanged, "onstorageareachanged")
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -46,33 +46,18 @@
 #include "js/SliceBudget.h"
 #include "nsIArray.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "prmem.h"
 #include "WrapperFactory.h"
 #include "nsGlobalWindow.h"
 #include "nsScriptNameSpaceManager.h"
-#include "StructuredCloneTags.h"
 #include "mozilla/AutoRestore.h"
-#include "mozilla/dom/CryptoKey.h"
 #include "mozilla/dom/ErrorEvent.h"
-#include "mozilla/dom/ImageDataBinding.h"
-#include "mozilla/dom/ImageData.h"
-#ifdef MOZ_NFC
-#include "mozilla/dom/MozNDEFRecord.h"
-#endif // MOZ_NFC
-#ifdef MOZ_WEBRTC
-#include "mozilla/dom/RTCCertificate.h"
-#include "mozilla/dom/RTCCertificateBinding.h"
-#endif
-#include "mozilla/dom/StructuredClone.h"
-#include "mozilla/dom/SubtleCryptoBinding.h"
-#include "mozilla/ipc/BackgroundUtils.h"
-#include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsAXPCNativeCallContext.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 
 #include "nsJSPrincipals.h"
 
 #ifdef XP_MACOSX
 // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
 #undef check
@@ -2480,225 +2465,16 @@ SetMemoryGCDynamicMarkSlicePrefChangedCa
 
 static void
 SetIncrementalCCPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   bool pref = Preferences::GetBool(aPrefName);
   sIncrementalCC = pref;
 }
 
-JSObject*
-NS_DOMReadStructuredClone(JSContext* cx,
-                          JSStructuredCloneReader* reader,
-                          uint32_t tag,
-                          uint32_t data,
-                          void* closure)
-{
-  if (tag == SCTAG_DOM_IMAGEDATA) {
-    return ReadStructuredCloneImageData(cx, reader);
-  }
-
-  if (tag == SCTAG_DOM_WEBCRYPTO_KEY) {
-    if (!NS_IsMainThread()) {
-      return nullptr;
-    }
-
-    nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
-    if (!global) {
-      return nullptr;
-    }
-
-    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
-    JS::Rooted<JSObject*> result(cx);
-    {
-      nsRefPtr<CryptoKey> key = new CryptoKey(global);
-      if (!key->ReadStructuredClone(reader)) {
-        result = nullptr;
-      } else {
-        result = key->WrapObject(cx, nullptr);
-      }
-    }
-    return result;
-  }
-
-  if (tag == SCTAG_DOM_NULL_PRINCIPAL ||
-      tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
-      tag == SCTAG_DOM_CONTENT_PRINCIPAL) {
-    if (!NS_IsMainThread()) {
-      return nullptr;
-    }
-
-    mozilla::ipc::PrincipalInfo info;
-    if (tag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
-      info = mozilla::ipc::SystemPrincipalInfo();
-    } else if (tag == SCTAG_DOM_NULL_PRINCIPAL) {
-      info = mozilla::ipc::NullPrincipalInfo();
-    } else {
-      uint32_t appId = data;
-
-      uint32_t isInBrowserElement, specLength;
-      if (!JS_ReadUint32Pair(reader, &isInBrowserElement, &specLength)) {
-        return nullptr;
-      }
-
-      nsAutoCString spec;
-      spec.SetLength(specLength);
-      if (!JS_ReadBytes(reader, spec.BeginWriting(), specLength)) {
-        return nullptr;
-      }
-
-      info = mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement, spec);
-    }
-
-    nsresult rv;
-    nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(info, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
-      return nullptr;
-    }
-
-    JS::RootedValue result(cx);
-    rv = nsContentUtils::WrapNative(cx, principal, &NS_GET_IID(nsIPrincipal), &result);
-    if (NS_FAILED(rv)) {
-      xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
-      return nullptr;
-    }
-
-    return result.toObjectOrNull();
-  }
-
-#ifdef MOZ_NFC
-  if (tag == SCTAG_DOM_NFC_NDEF) {
-    if (!NS_IsMainThread()) {
-      return nullptr;
-    }
-
-    nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
-    if (!global) {
-      return nullptr;
-    }
-
-    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
-    JS::Rooted<JSObject*> result(cx);
-    {
-      nsRefPtr<MozNDEFRecord> ndefRecord = new MozNDEFRecord(global);
-      result = ndefRecord->ReadStructuredClone(cx, reader) ?
-               ndefRecord->WrapObject(cx, nullptr) : nullptr;
-    }
-    return result;
-  }
-#endif
-
-#ifdef MOZ_WEBRTC
-  if (tag == SCTAG_DOM_RTC_CERTIFICATE) {
-    if (!NS_IsMainThread()) {
-      return nullptr;
-    }
-
-    nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
-    if (!global) {
-      return nullptr;
-    }
-
-    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
-    JS::Rooted<JSObject*> result(cx);
-    {
-      nsRefPtr<RTCCertificate> cert = new RTCCertificate(global);
-      if (!cert->ReadStructuredClone(reader)) {
-        result = nullptr;
-      } else {
-        result = cert->WrapObject(cx, nullptr);
-      }
-    }
-    return result;
-  }
-#endif
-
-  // Don't know what this is. Bail.
-  xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
-  return nullptr;
-}
-
-bool
-NS_DOMWriteStructuredClone(JSContext* cx,
-                           JSStructuredCloneWriter* writer,
-                           JS::Handle<JSObject*> obj,
-                           void *closure)
-{
-  // Handle ImageData cloning
-  ImageData* imageData;
-  if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, obj, imageData))) {
-    return WriteStructuredCloneImageData(cx, writer, imageData);
-  }
-
-  // Handle Key cloning
-  CryptoKey* key;
-  if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, obj, key))) {
-    MOZ_ASSERT(NS_IsMainThread(), "This object should not be exposed outside the main-thread.");
-    return JS_WriteUint32Pair(writer, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
-           key->WriteStructuredClone(writer);
-  }
-
-#ifdef MOZ_WEBRTC
-  // Handle WebRTC Certificate cloning
-  RTCCertificate* cert;
-  if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCCertificate, obj, cert))) {
-    MOZ_ASSERT(NS_IsMainThread(), "This object should not be exposed outside the main-thread.");
-    return JS_WriteUint32Pair(writer, SCTAG_DOM_RTC_CERTIFICATE, 0) &&
-           cert->WriteStructuredClone(writer);
-  }
-#endif
-
-  if (NS_IsMainThread() && xpc::IsReflector(obj)) {
-    nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);
-    nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
-    if (principal) {
-      mozilla::ipc::PrincipalInfo info;
-      if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(principal, &info)))) {
-        xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
-        return false;
-      }
-
-      if (info.type() == mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
-        return JS_WriteUint32Pair(writer, SCTAG_DOM_NULL_PRINCIPAL, 0);
-      }
-      if (info.type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
-        return JS_WriteUint32Pair(writer, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
-      }
-
-      MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
-      const mozilla::ipc::ContentPrincipalInfo& cInfo = info;
-      return JS_WriteUint32Pair(writer, SCTAG_DOM_CONTENT_PRINCIPAL, cInfo.appId()) &&
-             JS_WriteUint32Pair(writer, cInfo.isInBrowserElement(), cInfo.spec().Length()) &&
-             JS_WriteBytes(writer, cInfo.spec().get(), cInfo.spec().Length());
-    }
-  }
-
-#ifdef MOZ_NFC
-  MozNDEFRecord* ndefRecord;
-  if (NS_SUCCEEDED(UNWRAP_OBJECT(MozNDEFRecord, obj, ndefRecord))) {
-    MOZ_ASSERT(NS_IsMainThread(), "This object should not be exposed outside the main-thread.");
-    return JS_WriteUint32Pair(writer, SCTAG_DOM_NFC_NDEF, 0) &&
-           ndefRecord->WriteStructuredClone(cx, writer);
-  }
-#endif // MOZ_NFC
-
-  // Don't know what this is
-  xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
-  return false;
-}
-
-void
-NS_DOMStructuredCloneError(JSContext* cx,
-                           uint32_t errorid)
-{
-  // We don't currently support any extensions to structured cloning.
-  xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
-}
-
 static bool
 AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal,
                            const char16_t* aBegin,
                            const char16_t* aLimit,
                            size_t* aSize,
                            const uint8_t** aMemory,
                            intptr_t *aHandle)
 {
@@ -2746,27 +2522,16 @@ nsJSContext::EnsureStatics()
     MOZ_CRASH();
   }
 
   // Let's make sure that our main thread is the same as the xpcom main thread.
   MOZ_ASSERT(NS_IsMainThread());
 
   sPrevGCSliceCallback = JS::SetGCSliceCallback(sRuntime, DOMGCSliceCallback);
 
-  // Set up the structured clone callbacks.
-  static const JSStructuredCloneCallbacks cloneCallbacks = {
-    NS_DOMReadStructuredClone,
-    NS_DOMWriteStructuredClone,
-    NS_DOMStructuredCloneError,
-    nullptr,
-    nullptr,
-    nullptr
-  };
-  JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
-
   // Set up the asm.js cache callbacks
   static const JS::AsmJSCacheOps asmJSCacheOps = {
     AsmJSCacheOpenEntryForRead,
     asmjscache::CloseEntryForRead,
     AsmJSCacheOpenEntryForWrite,
     asmjscache::CloseEntryForWrite,
     asmjscache::GetBuildId
   };
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -225,19 +225,9 @@ public:
   // Bug 312003 describes why this must be "void **", but after calling argv
   // may be cast to JS::Value* and the args found at:
   //    ((JS::Value*)argv)[0], ..., ((JS::Value*)argv)[argc - 1]
   virtual nsresult GetArgs(uint32_t *argc, void **argv) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSArgArray, NS_IJSARGARRAY_IID)
 
-JSObject* NS_DOMReadStructuredClone(JSContext* cx,
-                                    JSStructuredCloneReader* reader, uint32_t tag,
-                                    uint32_t data, void* closure);
-
-bool NS_DOMWriteStructuredClone(JSContext* cx,
-                                JSStructuredCloneWriter* writer,
-                                JS::Handle<JSObject*> obj, void *closure);
-
-void NS_DOMStructuredCloneError(JSContext* cx, uint32_t errorid);
-
 #endif /* nsJSEnvironment_h */
--- a/dom/bluetooth/bluedroid/BluetoothAvrcpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothAvrcpManager.cpp
@@ -344,27 +344,32 @@ BluetoothAvrcpManager::Connect(const nsA
                                BluetoothProfileController* aController)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
   MOZ_ASSERT(aController);
 
   // AVRCP doesn't require connecting. We just set the remote address here.
   mDeviceAddress = aDeviceAddress;
+  mController = aController;
+
   SetConnected(true);
   OnConnect(EmptyString());
 }
 
 void
 BluetoothAvrcpManager::Disconnect(BluetoothProfileController* aController)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mController);
 
   mDeviceAddress.Truncate();
+
+  mController = aController;
+
   SetConnected(false);
   OnDisconnect(EmptyString());
 }
 
 void
 BluetoothAvrcpManager::OnConnect(const nsAString& aErrorStr)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.cpp
@@ -28,16 +28,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(BluetoothLeDeviceEvent, Event)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScanRecord)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothLeDeviceEvent, Event)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDevice)
   tmp->mScanRecord = nullptr;
+  mozilla::DropJSObjects(this);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothLeDeviceEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
 BluetoothLeDeviceEvent::BluetoothLeDeviceEvent(
   mozilla::dom::EventTarget* aOwner)
   : Event(aOwner, nullptr, nullptr)
@@ -124,16 +125,17 @@ BluetoothLeDeviceEvent::Rssi() const
 void
 BluetoothLeDeviceEvent::GetScanRecord(
   JSContext* cx,
   JS::MutableHandle<JSObject*> aScanRecord,
   ErrorResult& aRv)
 {
   if (!mScanRecord) {
     mScanRecord = ArrayBuffer::Create(cx,
+                                      this,
                                       mRawScanRecord.Length(),
                                       mRawScanRecord.Elements());
     if (!mScanRecord) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
     mRawScanRecord.Clear();
   }
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -732,17 +732,17 @@ Event::GetEventPopupControlState(WidgetE
     }
     break;
   case eEditorInputEventClass:
     // For this following event only allow popups if it's triggered
     // while handling user input. See
     // nsPresShell::HandleEventInternal() for details.
     if (EventStateManager::IsHandlingUserInput()) {
       switch(aEvent->mMessage) {
-      case NS_EDITOR_INPUT:
+      case eEditorInput:
         if (PopupAllowedForEvent("input")) {
           abuse = openControlled;
         }
         break;
       default:
         break;
       }
     }
@@ -794,22 +794,22 @@ Event::GetEventPopupControlState(WidgetE
       default:
         break;
       }
     }
     break;
   case eTouchEventClass:
     if (aEvent->mFlags.mIsTrusted) {
       switch (aEvent->mMessage) {
-      case NS_TOUCH_START :
+      case eTouchStart:
         if (PopupAllowedForEvent("touchstart")) {
           abuse = openControlled;
         }
         break;
-      case NS_TOUCH_END :
+      case eTouchEnd:
         if (PopupAllowedForEvent("touchend")) {
           abuse = openControlled;
         }
         break;
       default:
         break;
       }
     }
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -214,17 +214,17 @@ EVENT(emptied,
       eEmptied,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(ended,
       eEnded,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(input,
-      NS_EDITOR_INPUT,
+      eEditorInput,
       EventNameType_HTMLXUL,
       eEditorInputEventClass)
 EVENT(invalid,
       eFormInvalid,
       EventNameType_HTMLXUL,
       eBasicEventClass)
 EVENT(keydown,
       eKeyDown,
@@ -346,17 +346,17 @@ EVENT(gotpointercapture,
       ePointerGotCapture,
       EventNameType_All,
       ePointerEventClass)
 EVENT(lostpointercapture,
       ePointerLostCapture,
       EventNameType_All,
       ePointerEventClass)
 EVENT(selectstart,
-      NS_SELECT_START,
+      eSelectStart,
       EventNameType_HTMLXUL,
       eBasicEventClass)
 
 // Not supported yet; probably never because "wheel" is a better idea.
 // EVENT(mousewheel)
 EVENT(pause,
       ePause,
       EventNameType_HTML,
@@ -389,17 +389,17 @@ EVENT(seeking,
       eSeeking,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(select,
       eFormSelect,
       EventNameType_HTMLXUL,
       eBasicEventClass)
 EVENT(show,
-      NS_SHOW_EVENT,
+      eShow,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(stalled,
       eStalled,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(submit,
       eFormSubmit,
@@ -491,17 +491,17 @@ WINDOW_EVENT(hashchange,
 WINDOW_EVENT(languagechange,
              eLanguageChange,
              EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
 // XXXbz Should the onmessage attribute on <body> really not work?  If so, do we
 // need a different macro to flag things like that (IDL, but not content
 // attributes on body/frameset), or is just using EventNameType_None enough?
 WINDOW_EVENT(message,
-             NS_MESSAGE,
+             eMessage,
              EventNameType_None,
              eBasicEventClass)
 WINDOW_EVENT(offline,
              eOffline,
              EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
 WINDOW_EVENT(online,
              eOnline,
@@ -563,38 +563,38 @@ WINDOW_ONLY_EVENT(moznetworkupload,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(moznetworkdownload,
                   eNetworkDownload,
                   EventNameType_None,
                   eBasicEventClass)
 #endif // MOZ_B2G
 
 TOUCH_EVENT(touchstart,
-            NS_TOUCH_START,
+            eTouchStart,
             EventNameType_All,
             eTouchEventClass)
 TOUCH_EVENT(touchend,
-            NS_TOUCH_END,
+            eTouchEnd,
             EventNameType_All,
             eTouchEventClass)
 TOUCH_EVENT(touchmove,
-            NS_TOUCH_MOVE,
+            eTouchMove,
             EventNameType_All,
             eTouchEventClass )
 TOUCH_EVENT(touchcancel,
-            NS_TOUCH_CANCEL,
+            eTouchCancel,
             EventNameType_All,
             eTouchEventClass)
 
 DOCUMENT_ONLY_EVENT(readystatechange,
                     eReadyStateChange,
                     EventNameType_HTMLXUL,
                     eBasicEventClass)
 DOCUMENT_ONLY_EVENT(selectionchange,
-                    NS_SELECTION_CHANGE,
+                    eSelectionChange,
                     EventNameType_HTMLXUL,
                     eBasicEventClass)
 
 NON_IDL_EVENT(MozMouseHittest,
               eMouseHitTest,
               EventNameType_None,
               eMouseEventClass)
 
@@ -650,32 +650,32 @@ NON_IDL_EVENT(MozMousePixelScroll,
               eMouseScrollEventClass)
                                                 
 NON_IDL_EVENT(open,
               eOpen,
               EventNameType_None,
               eBasicEventClass)
 
 NON_IDL_EVENT(dataavailable,
-              NS_MEDIARECORDER_DATAAVAILABLE,
+              eMediaRecorderDataAvailable,
               EventNameType_None,
               eBasicEventClass)
 
 NON_IDL_EVENT(stop,
-              NS_MEDIARECORDER_STOP,
+              eMediaRecorderStop,
               EventNameType_None,
               eBasicEventClass)
 
 NON_IDL_EVENT(warning,
-              NS_MEDIARECORDER_WARNING,
+              eMediaRecorderWarning,
               EventNameType_None,
               eBasicEventClass)
 
 NON_IDL_EVENT(speakerforcedchange,
-              NS_SPEAKERMANAGER_SPEAKERFORCEDCHANGE,
+              eSpeakerForcedChange,
               EventNameType_None,
               eBasicEventClass)
 
 // Events that only have on* attributes on XUL elements
 
  // "text" event is legacy event for modifying composition string in nsEditor.
  // This shouldn't be used by web/xul apps.  "compositionupdate" should be
  // used instead.
@@ -843,104 +843,104 @@ NON_IDL_EVENT(gamepadconnected,
 NON_IDL_EVENT(gamepaddisconnected,
               eGamepadDisconnected,
               EventNameType_None,
               eBasicEventClass)
 #endif
 
 // Simple gesture events
 NON_IDL_EVENT(MozSwipeGestureMayStart,
-              NS_SIMPLE_GESTURE_SWIPE_MAY_START,
+              eSwipeGestureMayStart,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozSwipeGestureStart,
-              NS_SIMPLE_GESTURE_SWIPE_START,
+              eSwipeGestureStart,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozSwipeGestureUpdate,
-              NS_SIMPLE_GESTURE_SWIPE_UPDATE,
+              eSwipeGestureUpdate,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozSwipeGestureEnd,
-              NS_SIMPLE_GESTURE_SWIPE_END,
+              eSwipeGestureEnd,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozSwipeGesture,
-              NS_SIMPLE_GESTURE_SWIPE,
+              eSwipeGesture,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozMagnifyGestureStart,
-              NS_SIMPLE_GESTURE_MAGNIFY_START,
+              eMagnifyGestureStart,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozMagnifyGestureUpdate,
-              NS_SIMPLE_GESTURE_MAGNIFY_UPDATE,
+              eMagnifyGestureUpdate,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozMagnifyGesture,
-              NS_SIMPLE_GESTURE_MAGNIFY,
+              eMagnifyGesture,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozRotateGestureStart,
-              NS_SIMPLE_GESTURE_ROTATE_START,
+              eRotateGestureStart,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozRotateGestureUpdate,
-              NS_SIMPLE_GESTURE_ROTATE_UPDATE,
+              eRotateGestureUpdate,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozRotateGesture,
-              NS_SIMPLE_GESTURE_ROTATE,
+              eRotateGesture,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozTapGesture,
-              NS_SIMPLE_GESTURE_TAP,
+              eTapGesture,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozPressTapGesture,
-              NS_SIMPLE_GESTURE_PRESSTAP,
+              ePressTapGesture,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozEdgeUIStarted,
-              NS_SIMPLE_GESTURE_EDGE_STARTED,
+              eEdgeUIStarted,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozEdgeUICanceled,
-              NS_SIMPLE_GESTURE_EDGE_CANCELED,
+              eEdgeUICanceled,
               EventNameType_None,
               eSimpleGestureEventClass)
 NON_IDL_EVENT(MozEdgeUICompleted,
-              NS_SIMPLE_GESTURE_EDGE_COMPLETED,
+              eEdgeUICompleted,
               EventNameType_None,
               eSimpleGestureEventClass)
 
 NON_IDL_EVENT(transitionend,
-              NS_TRANSITION_END,
+              eTransitionEnd,
               EventNameType_None,
               eTransitionEventClass)
 NON_IDL_EVENT(animationstart,
               eAnimationStart,
               EventNameType_None,
               eAnimationEventClass)
 NON_IDL_EVENT(animationend,
               eAnimationEnd,
               EventNameType_None,
               eAnimationEventClass)
 NON_IDL_EVENT(animationiteration,
               eAnimationIteration,
               EventNameType_None,
               eAnimationEventClass)
 
 NON_IDL_EVENT(audioprocess,
-              NS_AUDIO_PROCESS,
+              eAudioProcess,
               EventNameType_None,
               eBasicEventClass)
 
 NON_IDL_EVENT(complete,
-              NS_AUDIO_COMPLETE,
+              eAudioComplete,
               EventNameType_None,
               eBasicEventClass)
 
 #ifdef DEFINED_FORWARDED_EVENT
 #undef DEFINED_FORWARDED_EVENT
 #undef FORWARDED_EVENT
 #endif /* DEFINED_FORWARDED_EVENT */
 
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -1148,20 +1148,20 @@ CrossProcessSafeEvent(const WidgetEvent&
     case eMouseEnterIntoWidget:
     case eMouseExitFromWidget:
       return true;
     default:
       return false;
     }
   case eTouchEventClass:
     switch (aEvent.mMessage) {
-    case NS_TOUCH_START:
-    case NS_TOUCH_MOVE:
-    case NS_TOUCH_END:
-    case NS_TOUCH_CANCEL:
+    case eTouchStart:
+    case eTouchMove:
+    case eTouchEnd:
+    case eTouchCancel:
       return true;
     default:
       return false;
     }
   case eDragEventClass:
     switch (aEvent.mMessage) {
     case eDragOver:
     case eDragExit:
@@ -1184,18 +1184,17 @@ EventStateManager::HandleCrossProcessEve
     return false;
   }
 
   // Collect the remote event targets we're going to forward this
   // event to.
   //
   // NB: the elements of |targets| must be unique, for correctness.
   nsAutoTArray<nsCOMPtr<nsIContent>, 1> targets;
-  if (aEvent->mClass != eTouchEventClass ||
-      aEvent->mMessage == NS_TOUCH_START) {
+  if (aEvent->mClass != eTouchEventClass || aEvent->mMessage == eTouchStart) {
     // If this event only has one target, and it's remote, add it to
     // the array.
     nsIFrame* frame = GetEventTarget();
     nsIContent* target = frame ? frame->GetContent() : nullptr;
     if (IsRemoteTarget(target)) {
       targets.AppendElement(target);
     }
   } else {
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -191,18 +191,16 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(IMECont
 IMEContentObserver::IMEContentObserver()
   : mESM(nullptr)
   , mSuppressNotifications(0)
   , mPreCharacterDataChangeLength(-1)
   , mIsObserving(false)
   , mIMEHasFocus(false)
   , mIsFocusEventPending(false)
   , mIsSelectionChangeEventPending(false)
-  , mSelectionChangeCausedOnlyByComposition(false)
-  , mSelectionChangeCausedOnlyBySelectionEvent(false)
   , mIsPositionChangeEventPending(false)
   , mIsFlushingPendingNotifications(false)
 {
 #ifdef DEBUG
   mTextChangeData.Test();
 #endif
   if (!sIMECOLog) {
     sIMECOLog = PR_NewLogModule("IMEContentObserver");
@@ -1019,37 +1017,24 @@ IMEContentObserver::PostTextChangeNotifi
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("IMECO: 0x%p IMEContentObserver::PostTextChangeNotification(), "
      "mTextChangeData=%s)",
      this, TextChangeDataToString(mTextChangeData).get()));
 }
 
 void
-IMEContentObserver::PostSelectionChangeNotification(
-                      bool aCausedByComposition,
-                      bool aCausedBySelectionEvent)
+IMEContentObserver::PostSelectionChangeNotification()
 {
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
-    ("IMECO: 0x%p IMEContentObserver::PostSelectionChangeNotification("
-     "aCausedByComposition=%s, aCausedBySelectionEvent=%s)",
-     this, ToChar(aCausedByComposition), ToChar(aCausedBySelectionEvent)));
+    ("IMECO: 0x%p IMEContentObserver::PostSelectionChangeNotification(), "
+     "mSelectionData={ mCausedByComposition=%s, mCausedBySelectionEvent=%s }",
+     this, ToChar(mSelectionData.mCausedByComposition),
+     ToChar(mSelectionData.mCausedBySelectionEvent)));
 
-  if (!mIsSelectionChangeEventPending) {
-    mSelectionChangeCausedOnlyByComposition = aCausedByComposition;
-  } else {
-    mSelectionChangeCausedOnlyByComposition =
-      mSelectionChangeCausedOnlyByComposition && aCausedByComposition;
-  }
-  if (!mSelectionChangeCausedOnlyBySelectionEvent) {
-    mSelectionChangeCausedOnlyBySelectionEvent = aCausedBySelectionEvent;
-  } else {
-    mSelectionChangeCausedOnlyBySelectionEvent =
-      mSelectionChangeCausedOnlyBySelectionEvent && aCausedBySelectionEvent;
-  }
   mIsSelectionChangeEventPending = true;
 }
 
 void
 IMEContentObserver::MaybeNotifyIMEOfFocusSet()
 {
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("IMECO: 0x%p IMEContentObserver::MaybeNotifyIMEOfFocusSet()", this));
@@ -1076,18 +1061,19 @@ IMEContentObserver::MaybeNotifyIMEOfSele
                       bool aCausedByComposition,
                       bool aCausedBySelectionEvent)
 {
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("IMECO: 0x%p IMEContentObserver::MaybeNotifyIMEOfSelectionChange("
      "aCausedByComposition=%s, aCausedBySelectionEvent=%s)",
      this, ToChar(aCausedByComposition), ToChar(aCausedBySelectionEvent)));
 
-  PostSelectionChangeNotification(aCausedByComposition,
-                                  aCausedBySelectionEvent);
+  mSelectionData.AssignReason(aCausedByComposition,
+                              aCausedBySelectionEvent);
+  PostSelectionChangeNotification();
   FlushMergeableNotifications();
 }
 
 void
 IMEContentObserver::MaybeNotifyIMEOfPositionChange()
 {
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("IMECO: 0x%p IMEContentObserver::MaybeNotifyIMEOfPositionChange()", this));
@@ -1099,34 +1085,34 @@ bool
 IMEContentObserver::UpdateSelectionCache()
 {
   MOZ_ASSERT(IsSafeToNotifyIME());
 
   if (!mUpdatePreference.WantSelectionChange()) {
     return false;
   }
 
-  mSelectionData.Clear();
+  mSelectionData.ClearSelectionData();
 
   // XXX Cannot we cache some information for reducing the cost to compute
   //     selection offset and writing mode?
   WidgetQueryContentEvent selection(true, eQuerySelectedText, mWidget);
   ContentEventHandler handler(GetPresContext());
   handler.OnQuerySelectedText(&selection);
   if (NS_WARN_IF(!selection.mSucceeded)) {
     return false;
   }
 
   mFocusedWidget = selection.mReply.mFocusedWidget;
   mSelectionData.mOffset = selection.mReply.mOffset;
   *mSelectionData.mString = selection.mReply.mString;
   mSelectionData.SetWritingMode(selection.GetWritingMode());
   mSelectionData.mReversed = selection.mReply.mReversed;
-  mSelectionData.mCausedByComposition = false;
-  mSelectionData.mCausedBySelectionEvent = false;
+
+  // WARNING: Don't modify the reason of selection change here.
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("IMECO: 0x%p IMEContentObserver::UpdateSelectionCache(), "
      "mSelectionData=%s",
      this, SelectionChangeDataToString(mSelectionData).get()));
 
   return mSelectionData.IsValid();
 }
@@ -1243,19 +1229,17 @@ IMEContentObserver::FlushMergeableNotifi
   // Be aware, PuppetWidget depends on the order of this. A selection change
   // notification should not be sent before a text change notification because
   // PuppetWidget shouldn't query new text content every selection change.
   if (mIsSelectionChangeEventPending) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), "
        "creating SelectionChangeEvent...", this));
     mIsSelectionChangeEventPending = false;
-    nsContentUtils::AddScriptRunner(
-      new SelectionChangeEvent(this, mSelectionChangeCausedOnlyByComposition,
-                               mSelectionChangeCausedOnlyBySelectionEvent));
+    nsContentUtils::AddScriptRunner(new SelectionChangeEvent(this));
   }
 
   if (mIsPositionChangeEventPending) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), "
        "creating PositionChangeEvent...", this));
     mIsPositionChangeEventPending = false;
     nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
@@ -1380,48 +1364,47 @@ IMEContentObserver::SelectionChangeEvent
        "due to impossible to notify IME of selection change", this));
     return NS_OK;
   }
 
   if (!IsSafeToNotifyIME()) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("IMECO: 0x%p   IMEContentObserver::SelectionChangeEvent::Run(), "
        "retrying to send NOTIFY_IME_OF_SELECTION_CHANGE...", this));
-    mIMEContentObserver->PostSelectionChangeNotification(
-                           mCausedByComposition, mCausedBySelectionEvent);
+    mIMEContentObserver->PostSelectionChangeNotification();
     return NS_OK;
   }
 
   SelectionChangeData lastSelChangeData = mIMEContentObserver->mSelectionData;
   if (NS_WARN_IF(!mIMEContentObserver->UpdateSelectionCache())) {
     MOZ_LOG(sIMECOLog, LogLevel::Error,
       ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), FAILED, "
        "due to UpdateSelectionCache() failure", this));
     return NS_OK;
   }
 
   // If the IME doesn't want selection change notifications caused by
   // composition, we should do nothing anymore.
-  if (mCausedByComposition &&
+  SelectionChangeData& newSelChangeData = mIMEContentObserver->mSelectionData;
+  if (newSelChangeData.mCausedByComposition &&
       !mIMEContentObserver->
         mUpdatePreference.WantChangesCausedByComposition()) {
     return NS_OK;
   }
 
   // The state may be changed since querying content causes flushing layout.
   if (!CanNotifyIME()) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), FAILED, "
        "due to flushing layout having changed something", this));
     return NS_OK;
   }
 
   // If the selection isn't changed actually, we shouldn't notify IME of
   // selection change.
-  SelectionChangeData& newSelChangeData = mIMEContentObserver->mSelectionData;
   if (lastSelChangeData.IsValid() &&
       lastSelChangeData.mOffset == newSelChangeData.mOffset &&
       lastSelChangeData.String() == newSelChangeData.String() &&
       lastSelChangeData.GetWritingMode() == newSelChangeData.GetWritingMode() &&
       lastSelChangeData.mReversed == newSelChangeData.mReversed) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), not "
        "notifying IME of NOTIFY_IME_OF_SELECTION_CHANGE due to not changed "
@@ -1430,18 +1413,17 @@ IMEContentObserver::SelectionChangeEvent
   }
 
   MOZ_LOG(sIMECOLog, LogLevel::Info,
     ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), "
      "sending NOTIFY_IME_OF_SELECTION_CHANGE... newSelChangeData=%s",
      this, SelectionChangeDataToString(newSelChangeData).get()));
 
   IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
-  notification.SetData(mIMEContentObserver->mSelectionData,
-                       mCausedByComposition, mCausedBySelectionEvent);
+  notification.SetData(mIMEContentObserver->mSelectionData);
   IMEStateManager::NotifyIME(notification, mIMEContentObserver->mWidget);
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), "
      "sent NOTIFY_IME_OF_SELECTION_CHANGE", this));
   return NS_OK;
 }
 
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -116,18 +116,17 @@ private:
                           nsIContent* aContent) const;
   bool IsReflowLocked() const;
   bool IsSafeToNotifyIME() const;
 
   void PostFocusSetNotification();
   void MaybeNotifyIMEOfFocusSet();
   void PostTextChangeNotification(const TextChangeDataBase& aTextChangeData);
   void MaybeNotifyIMEOfTextChange(const TextChangeDataBase& aTextChangeData);
-  void PostSelectionChangeNotification(bool aCausedByComposition,
-                                       bool aCausedBySelectionEvent);
+  void PostSelectionChangeNotification();
   void MaybeNotifyIMEOfSelectionChange(bool aCausedByComposition,
                                        bool aCausedBySelectionEvent);
   void PostPositionChangeNotification();
   void MaybeNotifyIMEOfPositionChange();
 
   void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
   void ObserveEditableNode();
   /**
@@ -218,34 +217,32 @@ private:
   FlatTextCache mEndOfAddedTextCache;
   // mStartOfRemovingTextRangeCache caches text length from the start of content
   // to the start of the last removed content only while an edit action is being
   // handled by the editor and no other mutation (e.g., adding node) occur.
   FlatTextCache mStartOfRemovingTextRangeCache;
 
   TextChangeData mTextChangeData;
 
-  // mSelectionData is the last selection data which was notified.  This is
-  // modified by UpdateSelectionCache().  Note that mCausedBy* are always
-  // false.  Do NOT refer them.
+  // mSelectionData is the last selection data which was notified.  The
+  // selection information is modified by UpdateSelectionCache().  The reason
+  // of the selection change is modified by MaybeNotifyIMEOfSelectionChange().
   SelectionChangeData mSelectionData;
 
   EventStateManager* mESM;
 
   nsIMEUpdatePreference mUpdatePreference;
   uint32_t mPreAttrChangeLength;
   uint32_t mSuppressNotifications;
   int64_t mPreCharacterDataChangeLength;
 
   bool mIsObserving;
   bool mIMEHasFocus;
   bool mIsFocusEventPending;
   bool mIsSelectionChangeEventPending;
-  bool mSelectionChangeCausedOnlyByComposition;
-  bool mSelectionChangeCausedOnlyBySelectionEvent;
   bool mIsPositionChangeEventPending;
   bool mIsFlushingPendingNotifications;
 
 
   /**
    * Helper classes to notify IME.
    */
 
@@ -291,31 +288,21 @@ private:
     {
     }
     NS_IMETHOD Run() override;
   };
 
   class SelectionChangeEvent : public AChangeEvent
   {
   public:
-    SelectionChangeEvent(IMEContentObserver* aIMEContentObserver,
-                         bool aCausedByComposition,
-                         bool aCausedBySelectionEvent)
+    explicit SelectionChangeEvent(IMEContentObserver* aIMEContentObserver)
       : AChangeEvent(eChangeEventType_Selection, aIMEContentObserver)
-      , mCausedByComposition(aCausedByComposition)
-      , mCausedBySelectionEvent(aCausedBySelectionEvent)
     {
-      aIMEContentObserver->mSelectionChangeCausedOnlyByComposition = false;
-      aIMEContentObserver->mSelectionChangeCausedOnlyBySelectionEvent = false;
     }
     NS_IMETHOD Run() override;
-
-  private:
-    bool mCausedByComposition;
-    bool mCausedBySelectionEvent;
   };
 
   class TextChangeEvent : public AChangeEvent
   {
   public:
     TextChangeEvent(IMEContentObserver* aIMEContentObserver,
                     TextChangeDataBase& aTextChangeData)
       : AChangeEvent(eChangeEventType_Text, aIMEContentObserver)
--- a/dom/events/TouchEvent.cpp
+++ b/dom/events/TouchEvent.cpp
@@ -117,18 +117,17 @@ TouchEvent::InitTouchEvent(const nsAStri
   mChangedTouches = aChangedTouches;
 }
 
 TouchList*
 TouchEvent::Touches()
 {
   if (!mTouches) {
     WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
-    if (mEvent->mMessage == NS_TOUCH_END ||
-        mEvent->mMessage == NS_TOUCH_CANCEL) {
+    if (mEvent->mMessage == eTouchEnd || mEvent->mMessage == eTouchCancel) {
       // for touchend events, remove any changed touches from the touches array
       WidgetTouchEvent::AutoTouchArray unchangedTouches;
       const WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
       for (uint32_t i = 0; i < touches.Length(); ++i) {
         if (!touches[i]->mChanged) {
           unchangedTouches.AppendElement(touches[i]);
         }
       }
@@ -145,18 +144,18 @@ TouchEvent::TargetTouches()
 {
   if (!mTargetTouches) {
     WidgetTouchEvent::AutoTouchArray targetTouches;
     WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
     const WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
     for (uint32_t i = 0; i < touches.Length(); ++i) {
       // for touchend/cancel events, don't append to the target list if this is a
       // touch that is ending
-      if ((mEvent->mMessage != NS_TOUCH_END &&
-           mEvent->mMessage != NS_TOUCH_CANCEL) || !touches[i]->mChanged) {
+      if ((mEvent->mMessage != eTouchEnd && mEvent->mMessage != eTouchCancel) ||
+          !touches[i]->mChanged) {
         if (touches[i]->mTarget == mEvent->originalTarget) {
           targetTouches.AppendElement(touches[i]);
         }
       }
     }
     mTargetTouches = new TouchList(ToSupports(this), targetTouches);
   }
   return mTargetTouches;
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -67,17 +67,17 @@ public:
       return nullptr;
     }
 
     nsRefPtr<WorkerFetchResolver> r = new WorkerFetchResolver(proxy);
     return r.forget();
   }
 
   void
-  OnResponseAvailable(InternalResponse* aResponse) override;
+  OnResponseAvailableInternal(InternalResponse* aResponse) override;
 
   void
   OnResponseEnd() override;
 
 private:
   explicit WorkerFetchResolver(PromiseWorkerProxy* aProxy)
     : mPromiseProxy(aProxy)
   {
@@ -94,17 +94,17 @@ class MainThreadFetchResolver final : pu
   nsRefPtr<Promise> mPromise;
   nsRefPtr<Response> mResponse;
 
   NS_DECL_OWNINGTHREAD
 public:
   explicit MainThreadFetchResolver(Promise* aPromise);
 
   void
-  OnResponseAvailable(InternalResponse* aResponse) override;
+  OnResponseAvailableInternal(InternalResponse* aResponse) override;
 
 private:
   ~MainThreadFetchResolver();
 };
 
 class MainThreadFetchRunnable : public nsRunnable
 {
   nsRefPtr<WorkerFetchResolver> mResolver;
@@ -232,17 +232,17 @@ FetchRequest(nsIGlobalObject* aGlobal, c
 }
 
 MainThreadFetchResolver::MainThreadFetchResolver(Promise* aPromise)
   : mPromise(aPromise)
 {
 }
 
 void
-MainThreadFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
+MainThreadFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse)
 {
   NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
   AssertIsOnMainThread();
 
   if (aResponse->Type() != ResponseType::Error) {
     nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
     mResponse = new Response(go, aResponse);
     mPromise->MaybeResolve(mResponse);
@@ -312,17 +312,17 @@ public:
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     mResolver->mPromiseProxy->CleanUp(aCx);
     return true;
   }
 };
 
 void
-WorkerFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
+WorkerFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse)
 {
   AssertIsOnMainThread();
 
   MutexAutoLock lock(mPromiseProxy->Lock());
   if (mPromiseProxy->CleanedUp()) {
     return;
   }
 
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -879,23 +879,29 @@ FetchDriver::OnDataAvailable(nsIRequest*
 }
 
 NS_IMETHODIMP
 FetchDriver::OnStopRequest(nsIRequest* aRequest,
                            nsISupports* aContext,
                            nsresult aStatusCode)
 {
   workers::AssertIsOnMainThread();
-  if (mPipeOutputStream) {
-    mPipeOutputStream->Close();
+  if (NS_FAILED(aStatusCode)) {
+    nsCOMPtr<nsIAsyncOutputStream> outputStream = do_QueryInterface(mPipeOutputStream);
+    if (outputStream) {
+      outputStream->CloseWithStatus(NS_BINDING_FAILED);
+    }
+    // We proceed as usual here, since we've already created a successful response
+    // from OnStartRequest.
+    SucceedWithResponse();
+    return aStatusCode;
   }
 
-  if (NS_FAILED(aStatusCode)) {
-    FailWithNetworkError();
-    return aStatusCode;
+  if (mPipeOutputStream) {
+    mPipeOutputStream->Close();
   }
 
   ContinueHttpFetchAfterNetworkFetch();
   return NS_OK;
 }
 
 // This is called when the channel is redirected.
 NS_IMETHODIMP
--- a/dom/fetch/FetchDriver.h
+++ b/dom/fetch/FetchDriver.h
@@ -27,24 +27,37 @@ namespace mozilla {
 namespace dom {
 
 class InternalRequest;
 class InternalResponse;
 
 class FetchDriverObserver
 {
 public:
+  FetchDriverObserver() : mGotResponseAvailable(false)
+  { }
+
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FetchDriverObserver);
-  virtual void OnResponseAvailable(InternalResponse* aResponse) = 0;
+  void OnResponseAvailable(InternalResponse* aResponse)
+  {
+    MOZ_ASSERT(!mGotResponseAvailable);
+    mGotResponseAvailable = true;
+    OnResponseAvailableInternal(aResponse);
+  }
   virtual void OnResponseEnd()
   { };
 
 protected:
   virtual ~FetchDriverObserver()
   { };
+
+  virtual void OnResponseAvailableInternal(InternalResponse* aResponse) = 0;
+
+private:
+  bool mGotResponseAvailable;
 };
 
 class FetchDriver final : public nsIStreamListener,
                           public nsIChannelEventSink,
                           public nsIInterfaceRequestor,
                           public nsIAsyncVerifyRedirectCallback,
                           public nsIThreadRetargetableStreamListener
 {
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3284,17 +3284,17 @@ HTMLInputElement::PreHandleEvent(EventCh
     // to do some special handling here.
     HTMLInputElement* textControl = nullptr;
     nsNumberControlFrame* numberControlFrame =
       do_QueryFrame(GetPrimaryFrame());
     if (numberControlFrame) {
       textControl = numberControlFrame->GetAnonTextControl();
     }
     if (textControl && aVisitor.mEvent->originalTarget == textControl) {
-      if (aVisitor.mEvent->mMessage == NS_EDITOR_INPUT) {
+      if (aVisitor.mEvent->mMessage == eEditorInput) {
         // Propogate the anon text control's new value to our HTMLInputElement:
         nsAutoString value;
         numberControlFrame->GetValueOfAnonTextControl(value);
         numberControlFrame->HandlingInputEvent(true);
         nsWeakFrame weakNumberControlFrame(numberControlFrame);
         rv = SetValueInternal(value,
                               nsTextEditorState::eSetValue_BySetUserInput |
                               nsTextEditorState::eSetValue_Notify);
@@ -4082,17 +4082,17 @@ HTMLInputElement::PostHandleEventForRang
   if (!rangeFrame && mIsDraggingRange) {
     CancelRangeThumbDrag();
     return;
   }
 
   switch (aVisitor.mEvent->mMessage)
   {
     case eMouseDown:
-    case NS_TOUCH_START: {
+    case eTouchStart: {
       if (mIsDraggingRange) {
         break;
       }
       if (nsIPresShell::GetCapturingContent()) {
         break; // don't start drag if someone else is already capturing
       }
       WidgetInputEvent* inputEvent = aVisitor.mEvent->AsInputEvent();
       if (inputEvent->IsShift() || inputEvent->IsControl() ||
@@ -4114,32 +4114,32 @@ HTMLInputElement::PostHandleEventForRang
         } else if (mIsDraggingRange) {
           CancelRangeThumbDrag();
         }
       }
       aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
     } break;
 
     case eMouseMove:
-    case NS_TOUCH_MOVE:
+    case eTouchMove:
       if (!mIsDraggingRange) {
         break;
       }
       if (nsIPresShell::GetCapturingContent() != this) {
         // Someone else grabbed capture.
         CancelRangeThumbDrag();
         break;
       }
       SetValueOfRangeForUserEvent(
         rangeFrame->GetValueAtEventPoint(aVisitor.mEvent->AsInputEvent()));
       aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
       break;
 
     case eMouseUp:
-    case NS_TOUCH_END:
+    case eTouchEnd:
       if (!mIsDraggingRange) {
         break;
       }
       // We don't check to see whether we are the capturing content here and
       // call CancelRangeThumbDrag() if that is the case. We just finish off
       // the drag and set our final value (unless someone has called
       // preventDefault() and prevents us getting here).
       FinishRangeThumbDrag(aVisitor.mEvent->AsInputEvent());
@@ -4148,17 +4148,17 @@ HTMLInputElement::PostHandleEventForRang
 
     case eKeyPress:
       if (mIsDraggingRange &&
           aVisitor.mEvent->AsKeyboardEvent()->keyCode == NS_VK_ESCAPE) {
         CancelRangeThumbDrag();
       }
       break;
 
-    case NS_TOUCH_CANCEL:
+    case eTouchCancel:
       if (mIsDraggingRange) {
         CancelRangeThumbDrag();
       }
       break;
 
     default:
       break;
   }
--- a/dom/html/HTMLMenuElement.cpp
+++ b/dom/html/HTMLMenuElement.cpp
@@ -73,17 +73,17 @@ HTMLMenuElement::SendShowEvent()
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
 
   nsCOMPtr<nsIDocument> document = GetComposedDoc();
   if (!document) {
     return NS_ERROR_FAILURE;
   }
 
-  WidgetEvent event(true, NS_SHOW_EVENT);
+  WidgetEvent event(true, eShow);
   event.mFlags.mBubbles = false;
   event.mFlags.mCancelable = false;
 
   nsCOMPtr<nsIPresShell> shell = document->GetShell();
   if (!shell) {
     return NS_ERROR_FAILURE;
   }
  
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -2398,21 +2398,23 @@ BackgroundRequestChild::HandleResponse(
 
     for (uint32_t index = 0; index < count; index++) {
       // XXX Fix this somehow...
       auto& serializedCloneInfo =
         const_cast<SerializedStructuredCloneReadInfo&>(aResponse[index]);
 
       StructuredCloneReadInfo* cloneReadInfo = cloneReadInfos.AppendElement();
 
+      // Get the files
+      nsTArray<StructuredCloneFile> files;
+      ConvertActorsToBlobs(database, serializedCloneInfo, files);
+
+      // Move relevant data into the cloneReadInfo
       *cloneReadInfo = Move(serializedCloneInfo);
-
-      ConvertActorsToBlobs(database,
-                           serializedCloneInfo,
-                           cloneReadInfo->mFiles);
+      cloneReadInfo->mFiles = Move(files);
     }
   }
 
   ResultHelper helper(mRequest, mTransaction, &cloneReadInfos);
 
   DispatchSuccessEvent(&helper);
 }
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/IDBMutableFileBinding.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/IDBObjectStoreBinding.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/ipc/nsIRemoteBlob.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsCOMPtr.h"
@@ -361,24 +362,17 @@ StructuredCloneWriteCallback(JSContext* 
         newBlobOrMutableFile =
           cloneWriteInfo->mBlobOrMutableFiles.AppendElement();
       newBlobOrMutableFile->mBlob = blob;
 
       return true;
     }
   }
 
-  // Try using the runtime callbacks
-  const JSStructuredCloneCallbacks* runtimeCallbacks =
-    js::GetContextStructuredCloneCallbacks(aCx);
-  if (runtimeCallbacks) {
-    return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr);
-  }
-
-  return false;
+  return StructuredCloneHelper::WriteFullySerializableObjects(aCx, aWriter, aObj);
 }
 
 nsresult
 GetAddInfoCallback(JSContext* aCx, void* aClosure)
 {
   static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = {
     nullptr /* read */,
     StructuredCloneWriteCallback /* write */,
@@ -894,24 +888,18 @@ CommonStructuredCloneReadCallback(JSCont
                                                     data,
                                                     &result))) {
       return nullptr;
     }
 
     return result;
   }
 
-  const JSStructuredCloneCallbacks* runtimeCallbacks =
-    js::GetContextStructuredCloneCallbacks(aCx);
-
-  if (runtimeCallbacks) {
-    return runtimeCallbacks->read(aCx, aReader, aTag, aData, nullptr);
-  }
-
-  return nullptr;
+  return StructuredCloneHelper::ReadFullySerializableObjects(aCx, aReader,
+                                                             aTag, aData);
 }
 
 // static
 void
 ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
 {
   if (aBuffer.data()) {
     aBuffer.clear();
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -101,17 +101,17 @@ class HangMonitorChild
 
   static HangMonitorChild* Get() { return sInstance; }
 
   MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
 
  private:
   void ShutdownOnThread();
 
-  static HangMonitorChild* sInstance;
+  static Atomic<HangMonitorChild*> sInstance;
 
   const nsRefPtr<ProcessHangMonitor> mHangMonitor;
   Monitor mMonitor;
 
   // Main thread-only.
   bool mSentReport;
 
   // These fields must be accessed with mMonitor held.
@@ -119,17 +119,17 @@ class HangMonitorChild
   bool mStartDebugger;
   bool mFinishedStartingDebugger;
   bool mShutdownDone;
 
   // This field is only accessed on the hang thread.
   bool mIPCOpen;
 };
 
-HangMonitorChild* HangMonitorChild::sInstance;
+Atomic<HangMonitorChild*> HangMonitorChild::sInstance;
 
 /* Parent process objects */
 
 class HangMonitorParent;
 
 class HangMonitoredProcess final
   : public nsIHangReport
 {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1998,17 +1998,17 @@ TabChild::UpdateTapState(const WidgetTou
                                 "ui.click_hold_context_menus.delay", 500);
   }
 
   if (aEvent.touches.Length() == 0) {
     return;
   }
 
   bool currentlyTrackingTouch = (mActivePointerId >= 0);
-  if (aEvent.mMessage == NS_TOUCH_START) {
+  if (aEvent.mMessage == eTouchStart) {
     if (currentlyTrackingTouch || aEvent.touches.Length() > 1) {
       // We're tracking a possible tap for another point, or we saw a
       // touchstart for a later pointer after we canceled tracking of
       // the first point.  Ignore this one.
       return;
     }
     if (aStatus == nsEventStatus_eConsumeNoDefault ||
         TouchManager::gPreventMouseEvents ||
@@ -2039,34 +2039,34 @@ TabChild::UpdateTapState(const WidgetTou
   Touch* trackedTouch = GetTouchForIdentifier(aEvent, mActivePointerId);
   if (!trackedTouch) {
     return;
   }
 
   LayoutDevicePoint currentPoint = LayoutDevicePoint(trackedTouch->mRefPoint.x, trackedTouch->mRefPoint.y);
   int64_t time = aEvent.time;
   switch (aEvent.mMessage) {
-  case NS_TOUCH_MOVE:
+  case eTouchMove:
     if (std::abs(currentPoint.x - mGestureDownPoint.x) > sDragThreshold.width ||
         std::abs(currentPoint.y - mGestureDownPoint.y) > sDragThreshold.height) {
       CancelTapTracking();
     }
     return;
 
-  case NS_TOUCH_END:
+  case eTouchEnd:
     if (!TouchManager::gPreventMouseEvents) {
       APZCCallbackHelper::DispatchSynthesizedMouseEvent(
         eMouseMove, time, currentPoint, 0, mPuppetWidget);
       APZCCallbackHelper::DispatchSynthesizedMouseEvent(
         eMouseDown, time, currentPoint, 0, mPuppetWidget);
       APZCCallbackHelper::DispatchSynthesizedMouseEvent(
         eMouseUp, time, currentPoint, 0, mPuppetWidget);
     }
     // fall through
-  case NS_TOUCH_CANCEL:
+  case eTouchCancel:
     CancelTapTracking();
     return;
 
   default:
     NS_WARNING("Unknown touch event type");
   }
 }
 
@@ -2123,17 +2123,17 @@ TabChild::RecvRealTouchEvent(const Widge
   TABC_LOG("Receiving touch event of type %d\n", aEvent.mMessage);
 
   WidgetTouchEvent localEvent(aEvent);
   localEvent.widget = mPuppetWidget;
 
   APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
       mPuppetWidget->GetDefaultScale());
 
-  if (localEvent.mMessage == NS_TOUCH_START && AsyncPanZoomEnabled()) {
+  if (localEvent.mMessage == eTouchStart && AsyncPanZoomEnabled()) {
     if (gfxPrefs::TouchActionEnabled()) {
       APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(mPuppetWidget,
           localEvent, aInputBlockId, mSetAllowedTouchBehaviorCallback);
     }
     nsCOMPtr<nsIDocument> document = GetDocument();
     APZCCallbackHelper::SendSetTargetAPZCNotification(mPuppetWidget, document,
         localEvent, aGuid, aInputBlockId);
   }
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1829,17 +1829,17 @@ bool TabParent::SendRealTouchEvent(Widge
   if (mIsDestroyed) {
     return false;
   }
 
   // PresShell::HandleEventInternal adds touches on touch end/cancel.  This
   // confuses remote content and the panning and zooming logic into thinking
   // that the added touches are part of the touchend/cancel, when actually
   // they're not.
-  if (event.mMessage == NS_TOUCH_END || event.mMessage == NS_TOUCH_CANCEL) {
+  if (event.mMessage == eTouchEnd || event.mMessage == eTouchCancel) {
     for (int i = event.touches.Length() - 1; i >= 0; i--) {
       if (!event.touches[i]->mChanged) {
         event.touches.RemoveElementAt(i);
       }
     }
   }
 
   ScrollableLayerGuid guid;
@@ -1851,17 +1851,17 @@ bool TabParent::SendRealTouchEvent(Widge
     return false;
   }
 
   LayoutDeviceIntPoint offset = GetChildProcessOffset();
   for (uint32_t i = 0; i < event.touches.Length(); i++) {
     event.touches[i]->mRefPoint += offset;
   }
 
-  return (event.mMessage == NS_TOUCH_MOVE) ?
+  return (event.mMessage == eTouchMove) ?
     PBrowserParent::SendRealTouchMoveEvent(event, guid, blockId, apzResponse) :
     PBrowserParent::SendRealTouchEvent(event, guid, blockId, apzResponse);
 }
 
 bool
 TabParent::RecvSyncMessage(const nsString& aMessage,
                            const ClonedMessageData& aData,
                            InfallibleTArray<CpowEntry>&& aCpows,
@@ -2984,18 +2984,18 @@ TabParent::InjectTouchEvent(const nsAStr
                             uint32_t* aRys,
                             float* aRotationAngles,
                             float* aForces,
                             uint32_t aCount,
                             int32_t aModifiers)
 {
   EventMessage msg;
   nsContentUtils::GetEventMessageAndAtom(aType, eTouchEventClass, &msg);
-  if (msg != NS_TOUCH_START && msg != NS_TOUCH_MOVE &&
-      msg != NS_TOUCH_END && msg != NS_TOUCH_CANCEL) {
+  if (msg != eTouchStart && msg != eTouchMove &&
+      msg != eTouchEnd && msg != eTouchCancel) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
--- a/dom/media/IdpSandbox.jsm
+++ b/dom/media/IdpSandbox.jsm
@@ -108,24 +108,24 @@ function createLocationFromURI(uri) {
   };
 }
 
 /**
  * A javascript sandbox for running an IdP.
  *
  * @param domain (string) the domain of the IdP
  * @param protocol (string?) the protocol of the IdP [default: 'default']
- * @param doc (obj) the current document
+ * @param win (obj) the current window
  * @throws if the domain or protocol aren't valid
  */
-function IdpSandbox(domain, protocol, doc) {
+function IdpSandbox(domain, protocol, win) {
   this.source = IdpSandbox.createIdpUri(domain, protocol || "default");
   this.active = null;
   this.sandbox = null;
-  this.document = doc;
+  this.window = win;
 }
 
 IdpSandbox.checkDomain = function(domain) {
   if (!domain || typeof domain !== 'string') {
     throw new Error('Invalid domain for identity provider: ' +
                     'must be a non-zero length string');
   }
 };
@@ -176,17 +176,17 @@ IdpSandbox.createIdpUri = function(domai
 
 IdpSandbox.prototype = {
   isSame: function(domain, protocol) {
     return this.source.spec === IdpSandbox.createIdpUri(domain, protocol).spec;
   },
 
   start: function() {
     if (!this.active) {
-      this.active = ResourceLoader.load(this.source, this.document)
+      this.active = ResourceLoader.load(this.source, this.window.document)
         .then(result => this._createSandbox(result));
     }
     return this.active;
   },
 
   // Provides the sandbox with some useful facilities.  Initially, this is only
   // a minimal set; it is far easier to add more as the need arises, than to
   // take them back if we discover a mistake.
@@ -213,26 +213,52 @@ IdpSandbox.prototype = {
     let registrar = this.sandbox.rtcIdentityProvider;
     if (!Cu.isXrayWrapper(registrar)) {
       throw new Error('IdP setup failed');
     }
 
     // have to use the ultimate URI, not the starting one to avoid
     // that origin stealing from the one that redirected to it
     this._populateSandbox(result.request.URI);
-    // putting a javascript version of 1.8 here seems fragile
-    Cu.evalInSandbox(result.data, this.sandbox,
-                     '1.8', result.request.URI.spec, 1);
+    try {
+      Cu.evalInSandbox(result.data, this.sandbox,
+                       'latest', result.request.URI.spec, 1);
+    } catch (e) {
+      // These can be passed straight on, because they are explicitly labelled
+      // as being IdP errors by the IdP and we drop line numbers as a result.
+      if (e.name === 'IdpError' || e.name === 'IdpLoginError') {
+        throw e;
+      }
+      this._logError(e);
+      throw new Error('Error in IdP, check console for details');
+    }
 
     if (!registrar.idp) {
       throw new Error('IdP failed to call rtcIdentityProvider.register()');
     }
     return registrar;
   },
 
+  // Capture all the details from the error and log them to the console.  This
+  // can't rethrow anything else because that could leak information about the
+  // internal workings of the IdP across origins.
+  _logError: function(e) {
+    let winID = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
+        .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+    let scriptError = Cc["@mozilla.org/scripterror;1"]
+        .createInstance(Ci.nsIScriptError);
+    scriptError.initWithWindowID(e.message, e.fileName, null,
+                                 e.lineNumber, e.columnNumber,
+                                 Ci.nsIScriptError.errorFlag,
+                                 "content javascript", winID);
+    let consoleService = Cc['@mozilla.org/consoleservice;1']
+        .getService(Ci.nsIConsoleService);
+    consoleService.logMessage(scriptError);
+  },
+
   stop: function() {
     if (this.sandbox) {
       Cu.nukeSandbox(this.sandbox);
     }
     this.sandbox = null;
     this.active = null;
   },
 
--- a/dom/media/Intervals.h
+++ b/dom/media/Intervals.h
@@ -353,33 +353,28 @@ public:
     // range. We can speed up normalization here.
     if (aInterval.RightOf(last)) {
       mIntervals.AppendElement(aInterval);
       return *this;
     }
 
     ContainerType normalized;
     ElemType current(aInterval);
-    bool inserted = false;
     IndexType i = 0;
     for (; i < mIntervals.Length(); i++) {
       ElemType& interval = mIntervals[i];
       if (current.Touches(interval)) {
         current = current.Span(interval);
       } else if (current.LeftOf(interval)) {
-        normalized.AppendElement(Move(current));
-        inserted = true;
         break;
       } else {
         normalized.AppendElement(Move(interval));
       }
     }
-    if (!inserted) {
-      normalized.AppendElement(Move(current));
-    }
+    normalized.AppendElement(Move(current));
     for (; i < mIntervals.Length(); i++) {
       normalized.AppendElement(Move(mIntervals[i]));
     }
     mIntervals.Clear();
     mIntervals.AppendElements(Move(normalized));
 
     return *this;
   }
--- a/dom/media/PeerConnectionIdp.jsm
+++ b/dom/media/PeerConnectionIdp.jsm
@@ -51,17 +51,17 @@ PeerConnectionIdp.prototype = {
     this.protocol = protocol || 'default';
     this.username = username;
     if (this._idp) {
       if (this._idp.isSame(provider, protocol)) {
         return; // noop
       }
       this._idp.stop();
     }
-    this._idp = new IdpSandbox(provider, protocol, this._win.document);
+    this._idp = new IdpSandbox(provider, protocol, this._win);
   },
 
   // start the IdP and do some error fixup
   start: function() {
     return this._idp.start()
       .catch(e => {
         throw new this._win.DOMException(e.message, 'IdpError');
       });
--- a/dom/media/eme/MediaEncryptedEvent.cpp
+++ b/dom/media/eme/MediaEncryptedEvent.cpp
@@ -23,16 +23,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MediaEncryptedEvent, Event)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInitData)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaEncryptedEvent, Event)
   tmp->mInitData = nullptr;
+  mozilla::DropJSObjects(this);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaEncryptedEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
 MediaEncryptedEvent::MediaEncryptedEvent(EventTarget* aOwner)
   : Event(aOwner, nullptr, nullptr)
 {
@@ -107,16 +108,17 @@ MediaEncryptedEvent::GetInitDataType(nsS
 
 void
 MediaEncryptedEvent::GetInitData(JSContext* cx,
                                  JS::MutableHandle<JSObject*> aData,
                                  ErrorResult& aRv)
 {
   if (mRawInitData.Length()) {
     mInitData = ArrayBuffer::Create(cx,
+                                    this,
                                     mRawInitData.Length(),
                                     mRawInitData.Elements());
     if (!mInitData) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
     mRawInitData.Clear();
   }
--- a/dom/media/eme/MediaKeyMessageEvent.cpp
+++ b/dom/media/eme/MediaKeyMessageEvent.cpp
@@ -27,16 +27,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MediaKeyMessageEvent, Event)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMessage)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaKeyMessageEvent, Event)
   tmp->mMessage = nullptr;
+  mozilla::DropJSObjects(this);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaKeyMessageEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
 MediaKeyMessageEvent::MediaKeyMessageEvent(EventTarget* aOwner)
   : Event(aOwner, nullptr, nullptr)
 {
@@ -104,16 +105,17 @@ MediaKeyMessageEvent::Constructor(const 
 
 void
 MediaKeyMessageEvent::GetMessage(JSContext* cx,
                                  JS::MutableHandle<JSObject*> aMessage,
                                  ErrorResult& aRv)
 {
   if (!mMessage) {
     mMessage = ArrayBuffer::Create(cx,
+                                   this,
                                    mRawMessage.Length(),
                                    mRawMessage.Elements());
     if (!mMessage) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
     mRawMessage.Clear();
   }
--- a/dom/media/systemservices/MediaParent.cpp
+++ b/dom/media/systemservices/MediaParent.cpp
@@ -10,18 +10,19 @@
 #include <mozilla/StaticMutex.h>
 
 #include "MediaUtils.h"
 #include "MediaEngine.h"
 #include "VideoUtils.h"
 #include "nsThreadUtils.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
 #include "nsIOutputStream.h"
-#include "nsILineInputStream.h"
 #include "nsISafeOutputStream.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/Logging.h"
 
 #undef LOG
 PRLogModuleInfo *gMediaParentLog;
 #define LOG(args) MOZ_LOG(gMediaParentLog, mozilla::LogLevel::Debug, args)
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/identity/idp-bad.js
@@ -0,0 +1,1 @@
+<This isn't valid JS>
--- a/dom/media/tests/mochitest/identity/mochitest.ini
+++ b/dom/media/tests/mochitest/identity/mochitest.ini
@@ -16,16 +16,17 @@ support-files =
   /.well-known/idp-proxy/idp-redirect-http-trick.js^headers^
   /.well-known/idp-proxy/idp-redirect-https.js
   /.well-known/idp-proxy/idp-redirect-https.js^headers^
   /.well-known/idp-proxy/idp-redirect-https-double.js
   /.well-known/idp-proxy/idp-redirect-https-double.js^headers^
   /.well-known/idp-proxy/idp-redirect-https-odd-path.js
   /.well-known/idp-proxy/idp-redirect-https-odd-path.js^headers^
   /.well-known/idp-min.js
+  /.well-known/idp-proxy/idp-bad.js
 
 [test_fingerprints.html]
 [test_getIdentityAssertion.html]
 [test_setIdentityProvider.html]
 [test_setIdentityProviderWithErrors.html]
 [test_peerConnection_peerIdentity.html]
 [test_peerConnection_asymmetricIsolation.html]
 [test_loginNeeded.html]
--- a/dom/media/tests/mochitest/identity/test_fingerprints.html
+++ b/dom/media/tests/mochitest/identity/test_fingerprints.html
@@ -7,17 +7,17 @@
 <body>
   <script class="testbody" type="application/javascript">
 'use strict';
 
 // here we call the identity provider directly
 function getIdentityAssertion(fpArray) {
   var Cu = SpecialPowers.Cu;
   var rtcid = Cu.import('resource://gre/modules/media/IdpSandbox.jsm');
-  var sandbox = new rtcid.IdpSandbox('example.com', 'idp.js', window.document);
+  var sandbox = new rtcid.IdpSandbox('example.com', 'idp.js', window);
   return sandbox.start()
     .then(idp => SpecialPowers.wrap(idp)
                    .generateAssertion(JSON.stringify({ fingerprint: fpArray }),
                                       'https://example.com'))
     .then(assertion => {
       assertion = SpecialPowers.wrap(assertion);
       var assertionString = btoa(JSON.stringify(assertion));
       sandbox.stop();
--- a/dom/media/tests/mochitest/identity/test_getIdentityAssertion.html
+++ b/dom/media/tests/mochitest/identity/test_getIdentityAssertion.html
@@ -33,16 +33,26 @@ function theTest() {
   test.chain.removeAfter('PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE');
   test.chain.append([
     function PC_LOCAL_IDENTITY_ASSERTION_FAILS_WITHOUT_PROVIDER(t) {
       return t.pcLocal._pc.getIdentityAssertion()
         .then(a => ok(false, 'should fail without provider'),
               e => ok(e, 'should fail without provider'));
     },
 
+    function PC_LOCAL_IDENTITY_ASSERTION_FAILS_WITH_BAD_PROVIDER(t) {
+      t.pcLocal._pc.setIdentityProvider('example.com', 'idp-bad.js', '');
+      return t.pcLocal._pc.getIdentityAssertion()
+        .then(a => ok(false, 'should fail with bad provider'),
+              e => {
+                is(e.name, 'IdpError', 'should fail with bad provider');
+                ok(e.message, 'should include a nice message');
+              });
+    },
+
     function PC_LOCAL_GET_TWO_ASSERTIONS(t) {
       return Promise.all([
         getAssertion(t, ''),
         getAssertion(t, '')
       ]).then(assertions => {
         is(assertions.length, 2, "Two assertions generated");
         assertions.forEach(a => checkIdentity(a, 'someone@example.com'));
       });
--- a/dom/media/tests/mochitest/identity/test_idpproxy.html
+++ b/dom/media/tests/mochitest/identity/test_idpproxy.html
@@ -21,47 +21,47 @@ function test_domain_sandbox() {
     toString : function() {
       return 'example.com/path';
     }
   };
   var domains = [ 'ex/foo', 'user@ex', 'user:pass@ex', 'ex#foo', 'ex?foo',
                   '', 12, null, diabolical, true ];
   domains.forEach(function(domain) {
     try {
-      var idp = new IdpSandbox(domain, undefined, window.document);
+      var idp = new IdpSandbox(domain, undefined, window);
       ok(false, 'IdpSandbox allowed a bad domain: ' + domain);
     } catch (e) {
       var str = (typeof domain === 'string') ? domain : typeof domain;
       ok(true, 'Evil domain "' + str + '" raises exception');
     }
   });
 }
 
 function test_protocol_sandbox() {
   var protos = [ '../evil/proto', '..%2Fevil%2Fproto',
                  '\\evil', '%5cevil', 12, true, {} ];
   protos.forEach(function(proto) {
     try {
-      var idp = new IdpSandbox('example.com', proto, window.document);
+      var idp = new IdpSandbox('example.com', proto, window);
       ok(false, 'IdpSandbox allowed a bad protocol: ' + proto);
     } catch (e) {
       var str = (typeof proto === 'string') ? proto : typeof proto;
       ok(true, 'Evil protocol "' + proto + '" raises exception');
     }
   });
 }
 
 function idpName(hash) {
   return 'idp.js' + (hash ? ('#' + hash) : '');
 }
 
 function makeSandbox(js) {
   var name = js || idpName();
   info('Creating a sandbox for the protocol: ' + name);
-  var sandbox = new IdpSandbox('example.com', name, window.document);
+  var sandbox = new IdpSandbox('example.com', name, window);
   return sandbox.start().then(idp => SpecialPowers.wrap(idp));
 }
 
 function test_generate_assertion() {
   return makeSandbox()
     .then(idp => idp.generateAssertion(dummyPayload,
                                        'https://example.net'))
     .then(response => {
@@ -121,16 +121,22 @@ function test_redirect_ok(from) {
 function test_redirect_fail(from) {
   return () => {
     return makeSandbox(from)
       .then(() => ok(false, 'Redirect to https should fail'),
             e => ok(e, 'Redirect to https should fail'));
   };
 }
 
+function test_bad_js() {
+  return makeSandbox('idp-bad.js')
+    .then(() => ok(false, 'Bad JS should not load'),
+          e => ok(e, 'Bad JS should not load'));
+}
+
 function run_all_tests() {
   [
     test_domain_sandbox,
     test_protocol_sandbox,
     test_generate_assertion,
     test_validate_assertion,
 
     // fail of the IdP fails
@@ -146,17 +152,19 @@ function run_all_tests() {
     // Two redirects is fine too
     test_redirect_ok('idp-redirect-https-double.js'),
     // A secure redirect to a path other than /.well-known/idp-proxy/* should
     // also work fine.
     test_redirect_ok('idp-redirect-https-odd-path.js'),
     // A redirect to HTTP is not-cool
     test_redirect_fail('idp-redirect-http.js'),
     // Also catch tricks like https->http->https
-    test_redirect_fail('idp-redirect-http-trick.js')
+    test_redirect_fail('idp-redirect-http-trick.js'),
+
+    test_bad_js
   ].reduce((p, test) => {
     return p.then(test)
       .catch(e => ok(false, test.name + ' failed: ' +
                      SpecialPowers.wrap(e).message + '\n' +
                      SpecialPowers.wrap(e).stack));
   }, Promise.resolve())
     .then(() => SimpleTest.finish());
 }
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -156,48 +156,43 @@ WebMDemuxer::~WebMDemuxer()
 {
   Reset();
   Cleanup();
 }
 
 nsRefPtr<WebMDemuxer::InitPromise>
 WebMDemuxer::Init()
 {
-  if (InitBufferedState() != NS_OK) {
-    return InitPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, __func__);
-  }
-  if (ReadMetadata() != NS_OK) {
+  InitBufferedState();
+
+  if (NS_FAILED(ReadMetadata())) {
     return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
   }
 
   if (!GetNumberTracks(TrackInfo::kAudioTrack) &&
       !GetNumberTracks(TrackInfo::kVideoTrack)) {
     return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
   }
 
   return InitPromise::CreateAndResolve(NS_OK, __func__);
 }
 
-nsresult
+void
 WebMDemuxer::InitBufferedState()
 {
-  if(!mBufferedState) {
-    mBufferedState = new WebMBufferedState;
-  }
-  EnsureUpToDateIndex();
-  return NS_OK;
+  MOZ_ASSERT(!mBufferedState);
+  mBufferedState = new WebMBufferedState;
 }
 
 already_AddRefed<MediaDataDemuxer>
 WebMDemuxer::Clone() const
 {
   nsRefPtr<WebMDemuxer> demuxer = new WebMDemuxer(mResource.GetResource());
-  demuxer->mInitData = mInitData;
-  if (demuxer->InitBufferedState() != NS_OK ||
-      demuxer->ReadMetadata() != NS_OK) {
+  demuxer->InitBufferedState();
+  if (NS_FAILED(demuxer->ReadMetadata())) {
     NS_WARNING("Couldn't recreate WebMDemuxer");
     return nullptr;
   }
   return demuxer.forget();
 }
 
 bool
 WebMDemuxer::HasTrackType(TrackInfo::TrackType aType) const
@@ -266,22 +261,36 @@ WebMDemuxer::Cleanup()
 nsresult
 WebMDemuxer::ReadMetadata()
 {
   nestegg_io io;
   io.read = webmdemux_read;
   io.seek = webmdemux_seek;
   io.tell = webmdemux_tell;
   io.userdata = this;
-  int64_t maxOffset = mBufferedState->GetInitEndOffset();
-  if (maxOffset == -1) {
-    maxOffset = mResource.GetLength();
+  int r = nestegg_init(&mContext, io, &webmdemux_log,
+                       IsMediaSource() ? mResource.GetLength() : -1);
+  if (r == -1) {
+    return NS_ERROR_FAILURE;
   }
-  int r = nestegg_init(&mContext, io, &webmdemux_log, maxOffset);
-  if (r == -1) {
+  {
+    // Check how much data nestegg read and force feed it to BufferedState.
+    nsRefPtr<MediaByteBuffer> buffer = mResource.MediaReadAt(0, mResource.Tell());
+    if (!buffer) {
+      return NS_ERROR_FAILURE;
+    }
+    mBufferedState->NotifyDataArrived(buffer->Elements(), buffer->Length(), 0);
+    if (mBufferedState->GetInitEndOffset() < 0) {
+      return NS_ERROR_FAILURE;
+    }
+    MOZ_ASSERT(mBufferedState->GetInitEndOffset() <= mResource.Tell());
+  }
+  mInitData = mResource.MediaReadAt(0, mBufferedState->GetInitEndOffset());
+  if (!mInitData ||
+      mInitData->Length() != size_t(mBufferedState->GetInitEndOffset())) {
     return NS_ERROR_FAILURE;
   }
 
   unsigned int ntracks = 0;
   r = nestegg_track_count(mContext, &ntracks);
   if (r == -1) {
     return NS_ERROR_FAILURE;
   }
@@ -438,32 +447,27 @@ bool
 WebMDemuxer::IsSeekable() const
 {
   return mContext && nestegg_has_cues(mContext);
 }
 
 void
 WebMDemuxer::EnsureUpToDateIndex()
 {
-  if (!mNeedReIndex) {
+  if (!mNeedReIndex || !mInitData) {
     return;
   }
-  if (mInitData && mBufferedState->GetInitEndOffset() == -1) {
-    mBufferedState->NotifyDataArrived(mInitData->Elements(), mInitData->Length(), 0);
-  }
   AutoPinned<MediaResource> resource(mResource.GetResource());
   nsTArray<MediaByteRange> byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
   if (NS_FAILED(rv) || !byteRanges.Length()) {
     return;
   }
   mBufferedState->UpdateIndex(byteRanges, resource);
-  if (!mInitData && mBufferedState->GetInitEndOffset() != -1) {
-    mInitData = mResource.MediaReadAt(0, mBufferedState->GetInitEndOffset());
-  }
+
   mNeedReIndex = false;
 
   if (!mIsMediaSource) {
     return;
   }
   mLastWebMBlockOffset = mBufferedState->GetLastBlockOffset();
   MOZ_ASSERT(mLastWebMBlockOffset <= mResource.GetLength());
 }
@@ -474,29 +478,33 @@ WebMDemuxer::NotifyDataArrived(uint32_t 
   WEBM_DEBUG("length: %ld offset: %ld", aLength, aOffset);
   mNeedReIndex = true;
 }
 
 void
 WebMDemuxer::NotifyDataRemoved()
 {
   mBufferedState->Reset();
+  if (mInitData) {
+    mBufferedState->NotifyDataArrived(mInitData->Elements(), mInitData->Length(), 0);
+  }
   mNeedReIndex = true;
 }
 
 UniquePtr<EncryptionInfo>
 WebMDemuxer::GetCrypto()
 {
   return nullptr;
 }
 
 bool
 WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSamples)
 {
   if (mIsMediaSource) {
+    // To ensure mLastWebMBlockOffset is properly up to date.
     EnsureUpToDateIndex();
   }
 
   nsRefPtr<NesteggPacketHolder> holder(NextPacket(aType));
 
   if (!holder) {
     return false;
   }
--- a/dom/media/webm/WebMDemuxer.h
+++ b/dom/media/webm/WebMDemuxer.h
@@ -125,17 +125,17 @@ public:
     return mIsMediaSource;
   }
 
 private:
   friend class WebMTrackDemuxer;
 
   ~WebMDemuxer();
   void Cleanup();
-  nsresult InitBufferedState();
+  void InitBufferedState();
   nsresult ReadMetadata();
   void NotifyDataArrived(uint32_t aLength, int64_t aOffset) override;
   void NotifyDataRemoved() override;
   void EnsureUpToDateIndex();
   media::TimeIntervals GetBuffered();
   virtual nsresult SeekInternal(const media::TimeUnit& aTarget);
 
   // Read a packet from the nestegg file. Returns nullptr if all packets for
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -2261,17 +2261,18 @@ Notification::ShowPersistentNotification
       new CheckLoadRunnable(worker, NS_ConvertUTF16toUTF8(aScope));
     if (!loadChecker->Dispatch(worker->GetJSContext())) {
       aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
       return nullptr;
     }
 
     if (NS_WARN_IF(NS_FAILED(loadChecker->Result()))) {
       if (loadChecker->Result() == NS_ERROR_NOT_AVAILABLE) {
-        aRv.ThrowTypeError(MSG_NO_ACTIVE_WORKER);
+        nsAutoString scope(aScope);
+        aRv.ThrowTypeError(MSG_NO_ACTIVE_WORKER, &scope);
       } else {
         aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
       }
       return nullptr;
     }
   }
 
 
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -230,16 +230,17 @@ nsPluginTag::nsPluginTag(nsPluginInfo* a
   : nsIInternalPluginTag(aPluginInfo->fName, aPluginInfo->fDescription,
                          aPluginInfo->fFileName, aPluginInfo->fVersion),
     mId(sNextId++),
     mContentProcessRunningCount(0),
     mHadLocalInstance(false),
     mLibrary(nullptr),
     mIsJavaPlugin(false),
     mIsFlashPlugin(false),
+    mSupportsAsyncInit(false),
     mFullPath(aPluginInfo->fFullPath),
     mLastModifiedTime(aLastModifiedTime),
     mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
     mCachedBlocklistStateValid(false),
     mIsFromExtension(fromExtension)
 {
   InitMime(aPluginInfo->fMimeTypeArray,
            aPluginInfo->fMimeDescriptionArray,
@@ -296,16 +297,17 @@ nsPluginTag::nsPluginTag(uint32_t aId,
                          bool aFromExtension)
   : nsIInternalPluginTag(aName, aDescription, aFileName, aVersion, aMimeTypes,
                          aMimeDescriptions, aExtensions),
     mId(aId),
     mContentProcessRunningCount(0),
     mLibrary(nullptr),
     mIsJavaPlugin(aIsJavaPlugin),
     mIsFlashPlugin(aIsFlashPlugin),
+    mSupportsAsyncInit(false),
     mLastModifiedTime(aLastModifiedTime),
     mNiceFileName(),
     mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
     mCachedBlocklistStateValid(false),
     mIsFromExtension(aFromExtension)
 {
 }
 
--- a/dom/presentation/Presentation.cpp
+++ b/dom/presentation/Presentation.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/AsyncEventDispatcher.h"
-#include "mozilla/dom/PresentationAvailableEvent.h"
 #include "mozilla/dom/PresentationBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIPresentationDeviceManager.h"
 #include "nsIPresentationService.h"
 #include "nsIUUIDGenerator.h"
 #include "nsServiceManagerUtils.h"
 #include "Presentation.h"
@@ -18,41 +17,44 @@
 #include "PresentationSession.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Presentation)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Presentation, DOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSession)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultRequest)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessions)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingGetSessionPromises)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Presentation, DOMEventTargetHelper)
   tmp->Shutdown();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSession)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultRequest)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessions)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingGetSessionPromises)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(Presentation, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(Presentation, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Presentation)
-  NS_INTERFACE_MAP_ENTRY(nsIPresentationListener)
+  NS_INTERFACE_MAP_ENTRY(nsIPresentationRespondingListener)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 /* static */ already_AddRefed<Presentation>
 Presentation::Create(nsPIDOMWindow* aWindow)
 {
   nsRefPtr<Presentation> presentation = new Presentation(aWindow);
   return NS_WARN_IF(!presentation->Init()) ? nullptr : presentation.forget();
 }
 
 Presentation::Presentation(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
-  , mAvailable(false)
 {
 }
 
 Presentation::~Presentation()
 {
   Shutdown();
 }
 
@@ -60,161 +62,159 @@ bool
 Presentation::Init()
 {
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return false;
   }
 
-  nsresult rv = service->RegisterListener(this);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
+  if (NS_WARN_IF(!GetOwner())) {
     return false;
   }
+  mWindowId = GetOwner()->WindowID();
 
-  nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
-    do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
-  if (NS_WARN_IF(!deviceManager)) {
-    return false;
-  }
-  deviceManager->GetDeviceAvailable(&mAvailable);
-
-  // Check if a session instance is required now. The receiver requires a
-  // session instance is ready at beginning because the web content may access
-  // it right away; whereas the sender doesn't until |startSession| succeeds.
+  // Check if a session instance is required now. A session may already be
+  // connecting before the web content gets loaded in a presenting browsing
+  // context (receiver).
   nsAutoString sessionId;
-  rv = service->GetExistentSessionIdAtLaunch(GetOwner()->WindowID(), sessionId);
+  nsresult rv = service->GetExistentSessionIdAtLaunch(mWindowId, sessionId);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
   if (!sessionId.IsEmpty()) {
-    mSession = PresentationSession::Create(GetOwner(), sessionId,
-                                           PresentationSessionState::Disconnected);
-    if (NS_WARN_IF(!mSession)) {
+    rv = NotifySessionConnect(mWindowId, sessionId);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
       return false;
     }
   }
 
+  // Register listener for incoming sessions.
+  rv = service->RegisterRespondingListener(mWindowId, this);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
   return true;
 }
 
 void Presentation::Shutdown()
 {
-  mSession = nullptr;
+  mDefaultRequest = nullptr;
+  mSessions.Clear();
+  mPendingGetSessionPromises.Clear();
 
+  // Unregister listener for incoming sessions.
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return;
   }
 
-  nsresult rv = service->UnregisterListener(this);
+  nsresult rv = service->UnregisterRespondingListener(mWindowId);
   NS_WARN_IF(NS_FAILED(rv));
 }
 
+/* virtual */ void
+Presentation::DisconnectFromOwner()
+{
+  Shutdown();
+  DOMEventTargetHelper::DisconnectFromOwner();
+}
+
 /* virtual */ JSObject*
-Presentation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+Presentation::WrapObject(JSContext* aCx,
+                         JS::Handle<JSObject*> aGivenProto)
 {
   return PresentationBinding::Wrap(aCx, this, aGivenProto);
 }
 
+void
+Presentation::SetDefaultRequest(PresentationRequest* aRequest)
+{
+  mDefaultRequest = aRequest;
+}
+
+already_AddRefed<PresentationRequest>
+Presentation::GetDefaultRequest() const
+{
+  nsRefPtr<PresentationRequest> request = mDefaultRequest;
+  return request.forget();
+}
+
 already_AddRefed<Promise>
-Presentation::StartSession(const nsAString& aUrl,
-                           const Optional<nsAString>& aId,
-                           ErrorResult& aRv)
+Presentation::GetSession(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   if (NS_WARN_IF(!global)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
-  // Get the origin.
-  nsAutoString origin;
-  nsresult rv = nsContentUtils::GetUTFOrigin(global->PrincipalOrNull(), origin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRv.Throw(rv);
-    return nullptr;
-  }
-
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  // Ensure there's something to select.
-  if (NS_WARN_IF(!mAvailable)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
-  }
-
-  // Ensure the URL is not empty.
-  if (NS_WARN_IF(aUrl.IsEmpty())) {
-    promise->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR);
-    return promise.forget();
-  }
-
-  // Generate an ID if it's not assigned.
-  nsAutoString id;
-  if (aId.WasPassed()) {
-    id = aId.Value();
+  // If there's no existing session, leave the promise pending until a
+  // connecting request arrives from the controlling browsing context (sender).
+  // http://w3c.github.io/presentation-api/#dom-presentation-getsession
+  if (!mSessions.IsEmpty()) {
+    promise->MaybeResolve(mSessions[0]);
   } else {
-    nsCOMPtr<nsIUUIDGenerator> uuidgen =
-      do_GetService("@mozilla.org/uuid-generator;1");
-    if(NS_WARN_IF(!uuidgen)) {
-      promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
-      return promise.forget();
-    }
-
-    nsID uuid;
-    uuidgen->GenerateUUIDInPlace(&uuid);
-    char buffer[NSID_LENGTH];
-    uuid.ToProvidedString(buffer);
-    CopyASCIItoUTF16(buffer, id);
-  }
-
-  nsCOMPtr<nsIPresentationService> service =
-    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
-  if(NS_WARN_IF(!service)) {
-    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
-    return promise.forget();
-  }
-
-  nsCOMPtr<nsIPresentationServiceCallback> callback =
-    new PresentationRequesterCallback(GetOwner(), aUrl, id, promise);
-  rv = service->StartSession(aUrl, id, origin, callback);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    mPendingGetSessionPromises.AppendElement(promise);
   }
 
   return promise.forget();
 }
 
-already_AddRefed<PresentationSession>
-Presentation::GetSession() const
+already_AddRefed<Promise>
+Presentation::GetSessions(ErrorResult& aRv) const
 {
-  nsRefPtr<PresentationSession> session = mSession;
-  return session.forget();
-}
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+  if (NS_WARN_IF(!global)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
 
-bool
-Presentation::CachedAvailable() const
-{
-  return mAvailable;
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  promise->MaybeResolve(mSessions);
+  return promise.forget();
 }
 
 NS_IMETHODIMP
-Presentation::NotifyAvailableChange(bool aAvailable)
+Presentation::NotifySessionConnect(uint64_t aWindowId,
+                                   const nsAString& aSessionId)
 {
-  mAvailable = aAvailable;
+  if (NS_WARN_IF(aWindowId != GetOwner()->WindowID())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsRefPtr<PresentationSession> session =
+    PresentationSession::Create(GetOwner(), aSessionId,
+                                PresentationSessionState::Disconnected);
+  if (NS_WARN_IF(!session)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  mSessions.AppendElement(session);
 
-  PresentationAvailableEventInit init;
-  init.mAvailable = mAvailable;
-  nsRefPtr<PresentationAvailableEvent> event =
-    PresentationAvailableEvent::Constructor(this,
-                                            NS_LITERAL_STRING("availablechange"),
-                                            init);
-  event->SetTrusted(true);
+  // Resolve pending |GetSession| promises if any.
+  if (!mPendingGetSessionPromises.IsEmpty()) {
+    for(uint32_t i = 0; i < mPendingGetSessionPromises.Length(); i++) {
+      mPendingGetSessionPromises[i]->MaybeResolve(session);
+    }
+    mPendingGetSessionPromises.Clear();
+  }
 
+  return DispatchSessionAvailableEvent();
+}
+
+nsresult
+Presentation::DispatchSessionAvailableEvent()
+{
   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
-    new AsyncEventDispatcher(this, event);
+    new AsyncEventDispatcher(this, NS_LITERAL_STRING("sessionavailable"), false);
   return asyncDispatcher->PostDOMEvent();
 }
--- a/dom/presentation/Presentation.h
+++ b/dom/presentation/Presentation.h
@@ -9,46 +9,62 @@
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsIPresentationListener.h"
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
+class PresentationRequest;
 class PresentationSession;
 
 class Presentation final : public DOMEventTargetHelper
-                         , public nsIPresentationListener
+                         , public nsIPresentationRespondingListener
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Presentation,
                                            DOMEventTargetHelper)
-  NS_DECL_NSIPRESENTATIONLISTENER
+  NS_DECL_NSIPRESENTATIONRESPONDINGLISTENER
 
   static already_AddRefed<Presentation> Create(nsPIDOMWindow* aWindow);
-  virtual JSObject*
-    WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  virtual void DisconnectFromOwner() override;
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL (public APIs)
-  already_AddRefed<Promise> StartSession(const nsAString& aUrl,
-                                         const Optional<nsAString>& aId,
-                                         ErrorResult& aRv);
-  already_AddRefed<PresentationSession> GetSession() const;
-  bool CachedAvailable() const;
-  IMPL_EVENT_HANDLER(availablechange);
+  void SetDefaultRequest(PresentationRequest* aRequest);
+
+  already_AddRefed<PresentationRequest> GetDefaultRequest() const;
+
+  already_AddRefed<Promise> GetSession(ErrorResult& aRv);
+
+  already_AddRefed<Promise> GetSessions(ErrorResult& aRv) const;
+
+  IMPL_EVENT_HANDLER(sessionavailable);
 
 private:
   explicit Presentation(nsPIDOMWindow* aWindow);
+
   ~Presentation();
 
   bool Init();
+
   void Shutdown();
 
-  bool mAvailable;
-  nsRefPtr<PresentationSession> mSession;
+  nsresult DispatchSessionAvailableEvent();
+
+  // Store the inner window ID for |UnregisterRespondingListener| call in
+  // |Shutdown| since the inner window may not exist at that moment.
+  uint64_t mWindowId;
+
+  nsRefPtr<PresentationRequest> mDefaultRequest;
+  nsTArray<nsRefPtr<PresentationSession>> mSessions;
+  nsTArray<nsRefPtr<Promise>> mPendingGetSessionPromises;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Presentation_h
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationAvailability.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/PresentationAvailabilityBinding.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIPresentationDeviceManager.h"
+#include "nsIPresentationService.h"
+#include "nsServiceManagerUtils.h"
+#include "PresentationAvailability.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationAvailability)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper)
+  tmp->Shutdown();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_ADDREF_INHERITED(PresentationAvailability, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(PresentationAvailability, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationAvailability)
+  NS_INTERFACE_MAP_ENTRY(nsIPresentationAvailabilityListener)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+/* static */ already_AddRefed<PresentationAvailability>
+PresentationAvailability::Create(nsPIDOMWindow* aWindow)
+{
+  nsRefPtr<PresentationAvailability> availability = new PresentationAvailability(aWindow);
+  return NS_WARN_IF(!availability->Init()) ? nullptr : availability.forget();
+}
+
+PresentationAvailability::PresentationAvailability(nsPIDOMWindow* aWindow)
+  : DOMEventTargetHelper(aWindow)
+  , mIsAvailable(false)
+{
+}
+
+PresentationAvailability::~PresentationAvailability()
+{
+  Shutdown();
+}
+
+bool
+PresentationAvailability::Init()
+{
+  nsCOMPtr<nsIPresentationService> service =
+    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    return false;
+  }
+
+  nsresult rv = service->RegisterAvailabilityListener(this);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
+    do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
+  if (NS_WARN_IF(!deviceManager)) {
+    return false;
+  }
+  deviceManager->GetDeviceAvailable(&mIsAvailable);
+
+  return true;
+}
+
+void PresentationAvailability::Shutdown()
+{
+  nsCOMPtr<nsIPresentationService> service =
+    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    return;
+  }
+
+  nsresult rv = service->UnregisterAvailabilityListener(this);
+  NS_WARN_IF(NS_FAILED(rv));
+}
+
+/* virtual */ void
+PresentationAvailability::DisconnectFromOwner()
+{
+  Shutdown();
+  DOMEventTargetHelper::DisconnectFromOwner();
+}
+
+/* virtual */ JSObject*
+PresentationAvailability::WrapObject(JSContext* aCx,
+                                     JS::Handle<JSObject*> aGivenProto)
+{
+  return PresentationAvailabilityBinding::Wrap(aCx, this, aGivenProto);
+}
+
+bool
+PresentationAvailability::Value() const
+{
+  return mIsAvailable;
+}
+
+NS_IMETHODIMP
+PresentationAvailability::NotifyAvailableChange(bool aIsAvailable)
+{
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethodWithArg<bool>(this,
+                                      &PresentationAvailability::UpdateAvailabilityAndDispatchEvent,
+                                      aIsAvailable);
+  return NS_DispatchToCurrentThread(runnable);
+}
+
+void
+PresentationAvailability::UpdateAvailabilityAndDispatchEvent(bool aIsAvailable) {
+  mIsAvailable = aIsAvailable;
+
+  NS_WARN_IF(NS_FAILED(DispatchTrustedEvent(NS_LITERAL_STRING("change"))));
+}
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationAvailability.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_PresentationAvailability_h
+#define mozilla_dom_PresentationAvailability_h
+
+#include "mozilla/DOMEventTargetHelper.h"
+#include "nsIPresentationListener.h"
+
+namespace mozilla {
+namespace dom {
+
+class PresentationAvailability final : public DOMEventTargetHelper
+                                     , public nsIPresentationAvailabilityListener
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationAvailability,
+                                           DOMEventTargetHelper)
+  NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER
+
+  static already_AddRefed<PresentationAvailability> Create(nsPIDOMWindow* aWindow);
+
+  virtual void DisconnectFromOwner() override;
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  // WebIDL (public APIs)
+  bool Value() const;
+
+  IMPL_EVENT_HANDLER(change);
+
+private:
+  explicit PresentationAvailability(nsPIDOMWindow* aWindow);
+
+  ~PresentationAvailability();
+
+  bool Init();
+
+  void Shutdown();
+
+  void UpdateAvailabilityAndDispatchEvent(bool aIsAvailable);
+
+  bool mIsAvailable;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_PresentationAvailability_h
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -6,36 +6,37 @@
 
 #include "mozilla/dom/Promise.h"
 #include "nsIDocShell.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPresentationService.h"
 #include "nsIWebProgress.h"
 #include "nsServiceManagerUtils.h"
 #include "PresentationCallbacks.h"
+#include "PresentationRequest.h"
 #include "PresentationSession.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /*
  * Implementation of PresentationRequesterCallback
  */
 
 NS_IMPL_ISUPPORTS(PresentationRequesterCallback, nsIPresentationServiceCallback)
 
-PresentationRequesterCallback::PresentationRequesterCallback(nsPIDOMWindow* aWindow,
+PresentationRequesterCallback::PresentationRequesterCallback(PresentationRequest* aRequest,
                                                              const nsAString& aUrl,
                                                              const nsAString& aSessionId,
                                                              Promise* aPromise)
-  : mWindow(aWindow)
+  : mRequest(aRequest)
   , mSessionId(aSessionId)
   , mPromise(aPromise)
 {
-  MOZ_ASSERT(mWindow);
+  MOZ_ASSERT(mRequest);
   MOZ_ASSERT(mPromise);
   MOZ_ASSERT(!mSessionId.IsEmpty());
 }
 
 PresentationRequesterCallback::~PresentationRequesterCallback()
 {
 }
 
@@ -43,24 +44,26 @@ PresentationRequesterCallback::~Presenta
 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 session state as connected.
   nsRefPtr<PresentationSession> session =
-    PresentationSession::Create(mWindow, mSessionId, PresentationSessionState::Connected);
-  if (!session) {
-    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    PresentationSession::Create(mRequest->GetOwner(), mSessionId,
+                                PresentationSessionState::Connected);
+  if (NS_WARN_IF(!session)) {
+    mPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
     return NS_OK;
   }
 
   mPromise->MaybeResolve(session);
-  return NS_OK;
+
+  return mRequest->DispatchSessionConnectEvent(session);
 }
 
 NS_IMETHODIMP
 PresentationRequesterCallback::NotifyError(nsresult aError)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mPromise->MaybeReject(aError);
--- a/dom/presentation/PresentationCallbacks.h
+++ b/dom/presentation/PresentationCallbacks.h
@@ -11,38 +11,38 @@
 #include "nsCOMPtr.h"
 #include "nsIPresentationService.h"
 #include "nsIWebProgressListener.h"
 #include "nsString.h"
 #include "nsWeakReference.h"
 
 class nsIDocShell;
 class nsIWebProgress;
-class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
+class PresentationRequest;
 class Promise;
 
 class PresentationRequesterCallback final : public nsIPresentationServiceCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPRESENTATIONSERVICECALLBACK
 
-  PresentationRequesterCallback(nsPIDOMWindow* aWindow,
+  PresentationRequesterCallback(PresentationRequest* aRequest,
                                 const nsAString& aUrl,
                                 const nsAString& aSessionId,
                                 Promise* aPromise);
 
 private:
   ~PresentationRequesterCallback();
 
-  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<PresentationRequest> mRequest;
   nsString mSessionId;
   nsRefPtr<Promise> mPromise;
 };
 
 class PresentationResponderLoadingCallback final : public nsIWebProgressListener
                                                  , public nsSupportsWeakReference
 {
 public:
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationRequest.cpp
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/PresentationRequestBinding.h"
+#include "mozilla/dom/PresentationSessionConnectEvent.h"
+#include "mozilla/dom/Promise.h"
+#include "mozIThirdPartyUtil.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIPresentationService.h"
+#include "nsIUUIDGenerator.h"
+#include "nsServiceManagerUtils.h"
+#include "PresentationAvailability.h"
+#include "PresentationCallbacks.h"
+#include "PresentationRequest.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(PresentationRequest, DOMEventTargetHelper,
+                                   mAvailability)
+
+NS_IMPL_ADDREF_INHERITED(PresentationRequest, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(PresentationRequest, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationRequest)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+/* static */ already_AddRefed<PresentationRequest>
+PresentationRequest::Constructor(const GlobalObject& aGlobal,
+                                 const nsAString& aUrl,
+                                 ErrorResult& aRv)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
+  if (!window) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  // Ensure the URL is not empty.
+  if (NS_WARN_IF(aUrl.IsEmpty())) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<PresentationRequest> request = new PresentationRequest(window, aUrl);
+  return NS_WARN_IF(!request->Init()) ? nullptr : request.forget();
+}
+
+PresentationRequest::PresentationRequest(nsPIDOMWindow* aWindow,
+                                         const nsAString& aUrl)
+  : DOMEventTargetHelper(aWindow)
+  , mUrl(aUrl)
+{
+}
+
+PresentationRequest::~PresentationRequest()
+{
+}
+
+bool
+PresentationRequest::Init()
+{
+  mAvailability = PresentationAvailability::Create(GetOwner());
+  if (NS_WARN_IF(!mAvailability)) {
+    return false;
+  }
+
+  return true;
+}
+
+/* virtual */ JSObject*
+PresentationRequest::WrapObject(JSContext* aCx,
+                                JS::Handle<JSObject*> aGivenProto)
+{
+  return PresentationRequestBinding::Wrap(aCx, this, aGivenProto);
+}
+
+already_AddRefed<Promise>
+PresentationRequest::Start(ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+  if (NS_WARN_IF(!global)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  // Get the origin.
+  nsAutoString origin;
+  nsresult rv = nsContentUtils::GetUTFOrigin(global->PrincipalOrNull(), origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  // Generate a session ID.
+  nsCOMPtr<nsIUUIDGenerator> uuidgen =
+    do_GetService("@mozilla.org/uuid-generator;1");
+  if(NS_WARN_IF(!uuidgen)) {
+    promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
+    return promise.forget();
+  }
+
+  nsID uuid;
+  uuidgen->GenerateUUIDInPlace(&uuid);
+  char buffer[NSID_LENGTH];
+  uuid.ToProvidedString(buffer);
+  nsAutoString id;
+  CopyASCIItoUTF16(buffer, id);
+
+  nsCOMPtr<nsIPresentationService> service =
+    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
+  if(NS_WARN_IF(!service)) {
+    promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
+    return promise.forget();
+  }
+
+  nsCOMPtr<nsIPresentationServiceCallback> callback =
+    new PresentationRequesterCallback(this, mUrl, id, promise);
+  rv = service->StartSession(mUrl, id, origin, callback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
+  }
+
+  return promise.forget();
+}
+
+already_AddRefed<Promise>
+PresentationRequest::GetAvailability(ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+  if (NS_WARN_IF(!global)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  promise->MaybeResolve(mAvailability);
+  return promise.forget();
+}
+
+nsresult
+PresentationRequest::DispatchSessionConnectEvent(PresentationSession* aSession)
+{
+  PresentationSessionConnectEventInit init;
+  init.mSession = aSession;
+
+  nsRefPtr<PresentationSessionConnectEvent> event =
+    PresentationSessionConnectEvent::Constructor(this,
+                                                 NS_LITERAL_STRING("sessionconnect"),
+                                                 init);
+  if (NS_WARN_IF(!event)) {
+    return NS_ERROR_FAILURE;
+  }
+  event->SetTrusted(true);
+
+  nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
+    new AsyncEventDispatcher(this, event);
+  return asyncDispatcher->PostDOMEvent();
+}
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationRequest.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_PresentationRequest_h
+#define mozilla_dom_PresentationRequest_h
+
+#include "mozilla/DOMEventTargetHelper.h"
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+class PresentationAvailability;
+class PresentationSession;
+
+class PresentationRequest final : public DOMEventTargetHelper
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationRequest,
+                                           DOMEventTargetHelper)
+
+  static already_AddRefed<PresentationRequest> Constructor(const GlobalObject& aGlobal,
+                                                           const nsAString& aUrl,
+                                                           ErrorResult& aRv);
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  // WebIDL (public APIs)
+  already_AddRefed<Promise> Start(ErrorResult& aRv);
+
+  already_AddRefed<Promise> GetAvailability(ErrorResult& aRv);
+
+  IMPL_EVENT_HANDLER(sessionconnect);
+
+  nsresult DispatchSessionConnectEvent(PresentationSession* aSession);
+
+private:
+  PresentationRequest(nsPIDOMWindow* aWindow,
+                      const nsAString& aUrl);
+
+  ~PresentationRequest();
+
+  bool Init();
+
+  nsString mUrl;
+  nsRefPtr<PresentationAvailability> mAvailability;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_PresentationRequest_h
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -103,23 +103,23 @@ PresentationDeviceRequest::Select(nsIPre
   }
   info->SetDevice(aDevice);
 
   // Establish a control channel. If we failed to do so, the callback is called
   // with an error message.
   nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
   nsresult rv = aDevice->EstablishControlChannel(mRequestUrl, mId, getter_AddRefs(ctrlChannel));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return info->ReplyError(NS_ERROR_DOM_NETWORK_ERR);
+    return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   // Initialize the session info with the control channel.
   rv = info->Init(ctrlChannel);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return info->ReplyError(NS_ERROR_DOM_NETWORK_ERR);
+    return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceRequest::Cancel()
 {
@@ -130,17 +130,17 @@ PresentationDeviceRequest::Cancel()
   }
 
   nsRefPtr<PresentationSessionInfo> info =
     static_cast<PresentationService*>(service.get())->GetSessionInfo(mId);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return info->ReplyError(NS_ERROR_DOM_PROP_ACCESS_DENIED);
+  return info->ReplyError(NS_ERROR_DOM_ABORT_ERR);
 }
 
 /*
  * Implementation of PresentationService
  */
 
 NS_IMPL_ISUPPORTS(PresentationService, nsIPresentationService, nsIObserver)
 
@@ -214,17 +214,18 @@ PresentationService::Observe(nsISupports
   return NS_ERROR_UNEXPECTED;
 }
 
 void
 PresentationService::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mListeners.Clear();
+  mAvailabilityListeners.Clear();
+  mRespondingListeners.Clear();
   mSessionInfo.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);
@@ -261,98 +262,98 @@ PresentationService::HandleSessionReques
   nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel));
   if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) {
     return rv;
   }
 
   nsAutoString url;
   rv = aRequest->GetUrl(url);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
+    ctrlChannel->Close(rv);
     return rv;
   }
 
   nsAutoString sessionId;
   rv = aRequest->GetPresentationId(sessionId);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
+    ctrlChannel->Close(rv);
     return rv;
   }
 
   nsCOMPtr<nsIPresentationDevice> device;
   rv = aRequest->GetDevice(getter_AddRefs(device));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
+    ctrlChannel->Close(rv);
     return rv;
   }
 
 #ifdef MOZ_WIDGET_GONK
   // Verify the existence of the app if necessary.
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri), url);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ctrlChannel->Close(NS_ERROR_DOM_BAD_URI);
     return rv;
   }
 
   bool isApp;
   rv = uri->SchemeIs("app", &isApp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
+    ctrlChannel->Close(rv);
     return rv;
   }
 
   if (NS_WARN_IF(isApp && !IsAppInstalled(uri))) {
     ctrlChannel->Close(NS_ERROR_DOM_NOT_FOUND_ERR);
     return NS_OK;
   }
 #endif
 
   // Create or reuse session info.
   nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(sessionId);
   if (NS_WARN_IF(info)) {
-    // TODO Update here after session resumption becomes supported.
-    ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
+    // 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 PresentationResponderInfo(url, sessionId, device);
+  info = new PresentationPresentingInfo(url, sessionId, device);
   rv = info->Init(ctrlChannel);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
+    ctrlChannel->Close(rv);
     return rv;
   }
 
   mSessionInfo.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_ABORT_ERR);
-    return info->ReplyError(NS_ERROR_NOT_AVAILABLE);
+    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));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
-    return info->ReplyError(rv);
+    ctrlChannel->Close(rv);
+    return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
   nsCOMPtr<Promise> realPromise = do_QueryInterface(promise);
-  static_cast<PresentationResponderInfo*>(info.get())->SetPromise(realPromise);
+  static_cast<PresentationPresentingInfo*>(info.get())->SetPromise(realPromise);
 
   return NS_OK;
 }
 
 void
 PresentationService::NotifyAvailableChange(bool aIsAvailable)
 {
-  nsTObserverArray<nsCOMPtr<nsIPresentationListener> >::ForwardIterator iter(mListeners);
+  nsTObserverArray<nsCOMPtr<nsIPresentationAvailabilityListener>>::ForwardIterator iter(mAvailabilityListeners);
   while (iter.HasMore()) {
-    nsCOMPtr<nsIPresentationListener> listener = iter.GetNext();
+    nsCOMPtr<nsIPresentationAvailabilityListener> listener = iter.GetNext();
     NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(aIsAvailable)));
   }
 }
 
 bool
 PresentationService::IsAppInstalled(nsIURI* aUri)
 {
   nsAutoCString prePath;
@@ -386,31 +387,31 @@ PresentationService::StartSession(const 
                                   nsIPresentationServiceCallback* aCallback)
 {
   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.
-  nsRefPtr<PresentationRequesterInfo> info =
-    new PresentationRequesterInfo(aUrl, aSessionId, aCallback);
+  nsRefPtr<PresentationSessionInfo> info =
+    new PresentationControllingInfo(aUrl, aSessionId, aCallback);
   mSessionInfo.Put(aSessionId, info);
 
   // Pop up a prompt and ask user to select a device.
   nsCOMPtr<nsIPresentationDevicePrompt> prompt =
     do_GetService(PRESENTATION_DEVICE_PROMPT_CONTRACTID);
   if (NS_WARN_IF(!prompt)) {
-    return info->ReplyError(NS_ERROR_DOM_ABORT_ERR);
+    return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
   nsCOMPtr<nsIPresentationDeviceRequest> request =
     new PresentationDeviceRequest(aUrl, aSessionId, aOrigin);
   nsresult rv = prompt->PromptDeviceSelection(request);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return info->ReplyError(NS_ERROR_DOM_ABORT_ERR);
+    return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationService::SendSessionMessage(const nsAString& aSessionId,
                                         nsIInputStream* aStream)
@@ -437,34 +438,34 @@ PresentationService::Terminate(const nsA
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return info->Close(NS_OK);
 }
 
 NS_IMETHODIMP
-PresentationService::RegisterListener(nsIPresentationListener* aListener)
+PresentationService::RegisterAvailabilityListener(nsIPresentationAvailabilityListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (NS_WARN_IF(mListeners.Contains(aListener))) {
+  if (NS_WARN_IF(mAvailabilityListeners.Contains(aListener))) {
     return NS_OK;
   }
 
-  mListeners.AppendElement(aListener);
+  mAvailabilityListeners.AppendElement(aListener);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationService::UnregisterListener(nsIPresentationListener* aListener)
+PresentationService::UnregisterAvailabilityListener(nsIPresentationAvailabilityListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mListeners.RemoveElement(aListener);
+  mAvailabilityListeners.RemoveElement(aListener);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationService::RegisterSessionListener(const nsAString& aSessionId,
                                              nsIPresentationSessionListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -497,16 +498,41 @@ PresentationService::UnregisterSessionLi
     NS_WARN_IF(NS_FAILED(info->Close(NS_OK)));
     UntrackSessionInfo(aSessionId);
     return info->SetListener(nullptr);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+PresentationService::RegisterRespondingListener(uint64_t aWindowId,
+                                                nsIPresentationRespondingListener* aListener)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aListener);
+
+  nsCOMPtr<nsIPresentationRespondingListener> listener;
+  if (mRespondingListeners.Get(aWindowId, getter_AddRefs(listener))) {
+    return (listener == aListener) ? NS_OK : NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+
+  mRespondingListeners.Put(aWindowId, aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PresentationService::UnregisterRespondingListener(uint64_t aWindowId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mRespondingListeners.Remove(aWindowId);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 PresentationService::GetExistentSessionIdAtLaunch(uint64_t aWindowId,
                                                   nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsString* sessionId = mRespondingSessionIds.Get(aWindowId);
   if (sessionId) {
     aSessionId.Assign(*sessionId);
@@ -527,17 +553,17 @@ PresentationService::NotifyReceiverReady
 
   // 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 nsAutoString(aSessionId));
     mRespondingWindowIds.Put(aSessionId, aWindowId);
   }
 
-  return static_cast<PresentationResponderInfo*>(info.get())->NotifyResponderReady();
+  return static_cast<PresentationPresentingInfo*>(info.get())->NotifyResponderReady();
 }
 
 NS_IMETHODIMP
 PresentationService::UntrackSessionInfo(const nsAString& aSessionId)
 {
   // Remove the session info.
   mSessionInfo.Remove(aSessionId);
 
--- a/dom/presentation/PresentationService.h
+++ b/dom/presentation/PresentationService.h
@@ -14,16 +14,18 @@
 #include "PresentationSessionInfo.h"
 
 class nsIPresentationSessionRequest;
 class nsIURI;
 
 namespace mozilla {
 namespace dom {
 
+class PresentationRespondingInfo;
+
 class PresentationService final : public nsIPresentationService
                                 , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIPRESENTATIONSERVICE
 
@@ -45,18 +47,26 @@ private:
   ~PresentationService();
   void HandleShutdown();
   nsresult HandleDeviceChange();
   nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest);
   void NotifyAvailableChange(bool aIsAvailable);
   bool IsAppInstalled(nsIURI* aUri);
 
   bool mIsAvailable;
+  nsTObserverArray<nsCOMPtr<nsIPresentationAvailabilityListener>> mAvailabilityListeners;
+
+  // 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;
-  nsTObserverArray<nsCOMPtr<nsIPresentationListener>> mListeners;
 
   // 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/PresentationSession.cpp
+++ b/dom/presentation/PresentationSession.cpp
@@ -83,16 +83,23 @@ PresentationSession::Shutdown()
   if (NS_WARN_IF(!service)) {
     return;
   }
 
   nsresult rv = service->UnregisterSessionListener(mId);
   NS_WARN_IF(NS_FAILED(rv));
 }
 
+/* virtual */ void
+PresentationSession::DisconnectFromOwner()
+{
+  Shutdown();
+  DOMEventTargetHelper::DisconnectFromOwner();
+}
+
 /* virtual */ JSObject*
 PresentationSession::WrapObject(JSContext* aCx,
                                 JS::Handle<JSObject*> aGivenProto)
 {
   return PresentationSessionBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
--- a/dom/presentation/PresentationSession.h
+++ b/dom/presentation/PresentationSession.h
@@ -22,37 +22,47 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationSession,
                                            DOMEventTargetHelper)
   NS_DECL_NSIPRESENTATIONSESSIONLISTENER
 
   static already_AddRefed<PresentationSession>
     Create(nsPIDOMWindow* aWindow,
            const nsAString& aId,
            PresentationSessionState aState);
-  virtual JSObject*
-    WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  virtual void DisconnectFromOwner() override;
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL (public APIs)
   void GetId(nsAString& aId) const;
+
   PresentationSessionState State() const;
+
   void Send(const nsAString& aData, ErrorResult& aRv);
+
   void Close();
 
   IMPL_EVENT_HANDLER(statechange);
   IMPL_EVENT_HANDLER(message);
 
 private:
-  explicit PresentationSession(nsPIDOMWindow* aWindow,
-                               const nsAString& aId,
-                               PresentationSessionState aState);
+  PresentationSession(nsPIDOMWindow* aWindow,
+                      const nsAString& aId,
+                      PresentationSessionState aState);
+
   ~PresentationSession();
 
   bool Init();
+
   void Shutdown();
+
   nsresult DispatchStateChangeEvent();
+
   nsresult DispatchMessageEvent(JS::Handle<JS::Value> aData);
 
   nsString mId;
   PresentationSessionState mState;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -137,16 +137,18 @@ PresentationSessionInfo::Init(nsIPresent
 {
   SetControlChannel(aControlChannel);
   return NS_OK;
 }
 
 /* virtual */ void
 PresentationSessionInfo::Shutdown(nsresult aReason)
 {
+  NS_WARN_IF(NS_FAILED(aReason));
+
   // Close the control channel if any.
   if (mControlChannel) {
     NS_WARN_IF(NS_FAILED(mControlChannel->Close(aReason)));
   }
 
   // Close the data transport channel if any.
   if (mTransport) {
     // |mIsTransportReady| will be unset once |NotifyTransportClosed| is called.
@@ -293,19 +295,19 @@ PresentationSessionInfo::NotifyTransport
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Nullify |mTransport| here so it won't try to re-close |mTransport| in
   // potential subsequent |Shutdown| calls.
   mTransport->SetCallback(nullptr);
   mTransport = nullptr;
 
-  if (!IsSessionReady()) {
+  if (NS_WARN_IF(!IsSessionReady())) {
     // It happens before the session is ready. Reply the callback.
-    return ReplyError(aReason);
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   // Unset |mIsTransportReady| here so it won't affect |IsSessionReady()| above.
   mIsTransportReady = false;
 
   Shutdown(aReason);
 
   uint16_t state = (NS_WARN_IF(NS_FAILED(aReason))) ?
@@ -336,17 +338,17 @@ PresentationSessionInfo::NotifyData(cons
   if (NS_WARN_IF(!mListener)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return mListener->NotifyMessage(mSessionId, aData);
 }
 
 /*
- * Implementation of PresentationRequesterInfo
+ * Implementation of PresentationControllingInfo
  *
  * During presentation session establishment, the sender expects the following
  * after trying to establish the control channel: (The order between step 3 and
  * 4 is not guaranteed.)
  * 1. |Init| is called to open a socket |mServerSocket| for data transport
  *    channel.
  * 2. |NotifyOpened| of |nsIPresentationControlChannelListener| is called to
  *    indicate the control channel is ready to use. Then send the offer to the
@@ -357,59 +359,59 @@ PresentationSessionInfo::NotifyData(cons
  *     called.
  * 4. |OnAnswer| of |nsIPresentationControlChannelListener| is called to
  *    indicate the receiver is ready. Close the control channel since it's no
  *    longer needed.
  * 5. Once both step 3 and 4 are done, the presentation session is ready to use.
  *    So notify the listener of CONNECTED state.
  */
 
-NS_IMPL_ISUPPORTS_INHERITED(PresentationRequesterInfo,
+NS_IMPL_ISUPPORTS_INHERITED(PresentationControllingInfo,
                             PresentationSessionInfo,
                             nsIServerSocketListener)
 
 nsresult
-PresentationRequesterInfo::Init(nsIPresentationControlChannel* aControlChannel)
+PresentationControllingInfo::Init(nsIPresentationControlChannel* aControlChannel)
 {
   PresentationSessionInfo::Init(aControlChannel);
 
   // Initialize |mServerSocket| for bootstrapping the data transport channel and
   // use |this| as the listener.
   mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
   if (NS_WARN_IF(!mServerSocket)) {
-    return ReplyError(NS_ERROR_NOT_AVAILABLE);
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   nsresult rv = mServerSocket->Init(-1, false, -1);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = mServerSocket->AsyncListen(this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 void
-PresentationRequesterInfo::Shutdown(nsresult aReason)
+PresentationControllingInfo::Shutdown(nsresult aReason)
 {
   PresentationSessionInfo::Shutdown(aReason);
 
   // Close the server socket if any.
   if (mServerSocket) {
     NS_WARN_IF(NS_FAILED(mServerSocket->Close()));
     mServerSocket = nullptr;
   }
 }
 
 nsresult
-PresentationRequesterInfo::GetAddress(nsACString& aAddress)
+PresentationControllingInfo::GetAddress(nsACString& aAddress)
 {
 #ifdef MOZ_WIDGET_GONK
   nsCOMPtr<nsINetworkManager> networkManager =
     do_GetService("@mozilla.org/network/manager;1");
   if (NS_WARN_IF(!networkManager)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -445,44 +447,44 @@ PresentationRequesterInfo::GetAddress(ns
   aAddress.Truncate();
 #endif
 
   return NS_OK;
 }
 
 // nsIPresentationControlChannelListener
 NS_IMETHODIMP
-PresentationRequesterInfo::OnOffer(nsIPresentationChannelDescription* aDescription)
+PresentationControllingInfo::OnOffer(nsIPresentationChannelDescription* aDescription)
 {
   MOZ_ASSERT(false, "Sender side should not receive offer.");
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-PresentationRequesterInfo::OnAnswer(nsIPresentationChannelDescription* aDescription)
+PresentationControllingInfo::OnAnswer(nsIPresentationChannelDescription* aDescription)
 {
   mIsResponderReady = true;
 
   // Close the control channel since it's no longer needed.
   nsresult rv = mControlChannel->Close(NS_OK);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return ReplyError(rv);
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   // Session might not be ready at this moment (waiting for the establishment of
   // the data transport channel).
   if (IsSessionReady()){
     return ReplySuccess();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationRequesterInfo::NotifyOpened()
+PresentationControllingInfo::NotifyOpened()
 {
   // Prepare and send the offer.
   int32_t port;
   nsresult rv = mServerSocket->GetPort(&port);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -493,93 +495,93 @@ PresentationRequesterInfo::NotifyOpened(
   }
 
   nsRefPtr<PresentationChannelDescription> description =
     new PresentationChannelDescription(address, static_cast<uint16_t>(port));
   return mControlChannel->SendOffer(description);
 }
 
 NS_IMETHODIMP
-PresentationRequesterInfo::NotifyClosed(nsresult aReason)
+PresentationControllingInfo::NotifyClosed(nsresult aReason)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Unset control channel here so it won't try to re-close it in potential
   // subsequent |Shutdown| calls.
   SetControlChannel(nullptr);
 
   if (NS_WARN_IF(NS_FAILED(aReason))) {
     if (mListener) {
       // The presentation session instance at receiver side may already exist.
       // Change the state to TERMINATED since it never succeeds.
       return mListener->NotifyStateChange(mSessionId,
                                           nsIPresentationSessionListener::STATE_TERMINATED);
     }
 
     // Reply error for an abnormal close.
-    return ReplyError(aReason);
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return NS_OK;
 }
 
 // nsIServerSocketListener
 NS_IMETHODIMP
-PresentationRequesterInfo::OnSocketAccepted(nsIServerSocket* aServerSocket,
+PresentationControllingInfo::OnSocketAccepted(nsIServerSocket* aServerSocket,
                                             nsISocketTransport* aTransport)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Initialize |mTransport| and use |this| as the callback.
   mTransport = do_CreateInstance(PRESENTATION_SESSION_TRANSPORT_CONTRACTID);
   if (NS_WARN_IF(!mTransport)) {
-    return ReplyError(NS_ERROR_NOT_AVAILABLE);
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   nsresult rv = mTransport->InitWithSocketTransport(aTransport, this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Enable data notification if the listener has been registered.
   if (mListener) {
     return mTransport->EnableDataNotification();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationRequesterInfo::OnStopListening(nsIServerSocket* aServerSocket,
+PresentationControllingInfo::OnStopListening(nsIServerSocket* aServerSocket,
                                            nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aStatus == NS_BINDING_ABORTED) { // The server socket was manually closed.
     return NS_OK;
   }
 
   Shutdown(aStatus);
 
-  if (!IsSessionReady()) {
+  if (NS_WARN_IF(!IsSessionReady())) {
     // It happens before the session is ready. Reply the callback.
-    return ReplyError(aStatus);
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   // It happens after the session is ready. Notify session state change.
   if (mListener) {
     return mListener->NotifyStateChange(mSessionId,
                                         nsIPresentationSessionListener::STATE_DISCONNECTED);
   }
 
   return NS_OK;
 }
 
 /*
- * Implementation of PresentationResponderInfo
+ * Implementation of PresentationPresentingInfo
  *
  * During presentation session establishment, the receiver expects the following
  * after trying to launch the app by notifying "presentation-launch-receiver":
  * (The order between step 2 and 3 is not guaranteed.)
  * 1. |Observe| of |nsIObserver| is called with "presentation-receiver-launched".
  *    Then start listen to document |STATE_TRANSFERRING| event.
  * 2. |NotifyResponderReady| is called to indicate the receiver page is ready
  *    for presentation use.
@@ -587,22 +589,22 @@ PresentationRequesterInfo::OnStopListeni
  * 4. Once both step 2 and 3 are done, establish the data transport channel and
  *    send the answer. (The control channel will be closed by the sender once it
  *    receives the answer.)
  * 5. |NotifyTransportReady| of |nsIPresentationSessionTransportCallback| is
  *    called. The presentation session is ready to use, so notify the listener
  *    of CONNECTED state.
  */
 
-NS_IMPL_ISUPPORTS_INHERITED(PresentationResponderInfo,
+NS_IMPL_ISUPPORTS_INHERITED(PresentationPresentingInfo,
                             PresentationSessionInfo,
                             nsITimerCallback)
 
 nsresult
-PresentationResponderInfo::Init(nsIPresentationControlChannel* aControlChannel)
+PresentationPresentingInfo::Init(nsIPresentationControlChannel* aControlChannel)
 {
   PresentationSessionInfo::Init(aControlChannel);
 
   // Add a timer to prevent waiting indefinitely in case the receiver page fails
   // to become ready.
   nsresult rv;
   int32_t timeout =
     Preferences::GetInt("presentation.receiver.loading.timeout", 10000);
@@ -614,31 +616,31 @@ PresentationResponderInfo::Init(nsIPrese
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 void
-PresentationResponderInfo::Shutdown(nsresult aReason)
+PresentationPresentingInfo::Shutdown(nsresult aReason)
 {
   PresentationSessionInfo::Shutdown(aReason);
 
   if (mTimer) {
     mTimer->Cancel();
   }
 
   mLoadingCallback = nullptr;
   mRequesterDescription = nullptr;
   mPromise = nullptr;
 }
 
 nsresult
-PresentationResponderInfo::InitTransportAndSendAnswer()
+PresentationPresentingInfo::InitTransportAndSendAnswer()
 {
   // Establish a data transport channel |mTransport| to the sender and use
   // |this| as the callback.
   mTransport = do_CreateInstance(PRESENTATION_SESSION_TRANSPORT_CONTRACTID);
   if (NS_WARN_IF(!mTransport)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -673,17 +675,17 @@ PresentationResponderInfo::InitTransport
   }
   nsCOMPtr<nsIPresentationChannelDescription> description =
     new PresentationChannelDescription(address, port);
 
   return mControlChannel->SendAnswer(description);
 }
 
 nsresult
-PresentationResponderInfo::UntrackFromService()
+PresentationPresentingInfo::UntrackFromService()
 {
   // Remove the OOP responding info (if it has never been used).
   if (mContentParent) {
     NS_WARN_IF(!static_cast<ContentParent*>(mContentParent.get())->SendNotifyPresentationReceiverCleanUp(mSessionId));
   }
 
   // Remove the session info (and the in-process responding info if there's any).
   nsCOMPtr<nsIPresentationService> service =
@@ -692,188 +694,188 @@ PresentationResponderInfo::UntrackFromSe
     return NS_ERROR_NOT_AVAILABLE;
   }
   static_cast<PresentationService*>(service.get())->UntrackSessionInfo(mSessionId);
 
   return NS_OK;
 }
 
 bool
-PresentationResponderInfo::IsAccessible(base::ProcessId aProcessId)
+PresentationPresentingInfo::IsAccessible(base::ProcessId aProcessId)
 {
   // Only the specific content process should access the responder info.
   return (mContentParent) ?
           aProcessId == static_cast<ContentParent*>(mContentParent.get())->OtherPid() :
           false;
 }
 
 nsresult
-PresentationResponderInfo::NotifyResponderReady()
+PresentationPresentingInfo::NotifyResponderReady()
 {
   if (mTimer) {
     mTimer->Cancel();
     mTimer = nullptr;
   }
 
   mIsResponderReady = true;
 
   // Initialize |mTransport| and send the answer to the sender if sender's
   // description is already offered.
   if (mRequesterDescription) {
     nsresult rv = InitTransportAndSendAnswer();
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return ReplyError(rv);
+      return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
     }
   }
 
   return NS_OK;
 }
 
 // nsIPresentationControlChannelListener
 NS_IMETHODIMP
-PresentationResponderInfo::OnOffer(nsIPresentationChannelDescription* aDescription)
+PresentationPresentingInfo::OnOffer(nsIPresentationChannelDescription* aDescription)
 {
   if (NS_WARN_IF(!aDescription)) {
-    return ReplyError(NS_ERROR_INVALID_ARG);
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   mRequesterDescription = aDescription;
 
   // Initialize |mTransport| and send the answer to the sender if the receiver
   // page is ready for presentation use.
   if (mIsResponderReady) {
     nsresult rv = InitTransportAndSendAnswer();
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return ReplyError(rv);
+      return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationResponderInfo::OnAnswer(nsIPresentationChannelDescription* aDescription)
+PresentationPresentingInfo::OnAnswer(nsIPresentationChannelDescription* aDescription)
 {
   MOZ_ASSERT(false, "Receiver side should not receive answer.");
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-PresentationResponderInfo::NotifyOpened()
+PresentationPresentingInfo::NotifyOpened()
 {
   // Do nothing.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationResponderInfo::NotifyClosed(nsresult aReason)
+PresentationPresentingInfo::NotifyClosed(nsresult aReason)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Unset control channel here so it won't try to re-close it in potential
   // subsequent |Shutdown| calls.
   SetControlChannel(nullptr);
 
   if (NS_WARN_IF(NS_FAILED(aReason))) {
     if (mListener) {
       // The presentation session instance at receiver side may already exist.
       // Change the state to TERMINATED since it never succeeds.
       return mListener->NotifyStateChange(mSessionId,
                                           nsIPresentationSessionListener::STATE_TERMINATED);
     }
 
     // Reply error for an abnormal close.
-    return ReplyError(aReason);
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return NS_OK;
 }
 
 // nsITimerCallback
 NS_IMETHODIMP
-PresentationResponderInfo::Notify(nsITimer* aTimer)
+PresentationPresentingInfo::Notify(nsITimer* aTimer)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_WARNING("The receiver page fails to become ready before timeout.");
 
   mTimer = nullptr;
   return ReplyError(NS_ERROR_DOM_TIMEOUT_ERR);
 }
 
 // PromiseNativeHandler
 void
-PresentationResponderInfo::ResolvedCallback(JSContext* aCx,
+PresentationPresentingInfo::ResolvedCallback(JSContext* aCx,
                                             JS::Handle<JS::Value> aValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (NS_WARN_IF(!aValue.isObject())) {
-    ReplyError(NS_ERROR_NOT_AVAILABLE);
+    ReplyError(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
   JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
   if (NS_WARN_IF(!obj)) {
-    ReplyError(NS_ERROR_NOT_AVAILABLE);
+    ReplyError(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
   // Start to listen to document state change event |STATE_TRANSFERRING|.
   HTMLIFrameElement* frame = nullptr;
   nsresult rv = UNWRAP_OBJECT(HTMLIFrameElement, obj, frame);
   if (NS_WARN_IF(!frame)) {
-    ReplyError(NS_ERROR_NOT_AVAILABLE);
+    ReplyError(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
   nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface((nsIFrameLoaderOwner*) frame);
   if (NS_WARN_IF(!owner)) {
-    ReplyError(NS_ERROR_NOT_AVAILABLE);
+    ReplyError(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
   nsCOMPtr<nsIFrameLoader> frameLoader;
   rv = owner->GetFrameLoader(getter_AddRefs(frameLoader));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    ReplyError(rv);
+    ReplyError(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
   nsRefPtr<TabParent> tabParent = TabParent::GetFrom(frameLoader);
   if (tabParent) {
     // OOP frame
     // Notify the content process that a receiver page has launched, so it can
     // start monitoring the loading progress.
     mContentParent = tabParent->Manager();
     NS_WARN_IF(!static_cast<ContentParent*>(mContentParent.get())->SendNotifyPresentationReceiverLaunched(tabParent, mSessionId));
   } else {
     // In-process frame
     nsCOMPtr<nsIDocShell> docShell;
     rv = frameLoader->GetDocShell(getter_AddRefs(docShell));
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      ReplyError(rv);
+      ReplyError(NS_ERROR_DOM_OPERATION_ERR);
       return;
     }
 
     // Keep an eye on the loading progress of the receiver page.
     mLoadingCallback = new PresentationResponderLoadingCallback(mSessionId);
     rv = mLoadingCallback->Init(docShell);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      ReplyError(rv);
+      ReplyError(NS_ERROR_DOM_OPERATION_ERR);
       return;
     }
   }
 }
 
 void
-PresentationResponderInfo::RejectedCallback(JSContext* aCx,
+PresentationPresentingInfo::RejectedCallback(JSContext* aCx,
                                             JS::Handle<JS::Value> aValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_WARNING("Launching the receiver page has been rejected.");
 
   if (mTimer) {
     mTimer->Cancel();
     mTimer = nullptr;
   }
 
-  ReplyError(NS_ERROR_DOM_ABORT_ERR);
+  ReplyError(NS_ERROR_DOM_OPERATION_ERR);
 }
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -118,61 +118,61 @@ protected:
   bool mIsTransportReady;
   nsCOMPtr<nsIPresentationServiceCallback> mCallback;
   nsCOMPtr<nsIPresentationSessionListener> mListener;
   nsCOMPtr<nsIPresentationDevice> mDevice;
   nsCOMPtr<nsIPresentationSessionTransport> mTransport;
   nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
 };
 
-// Session info with sender side behaviors.
-class PresentationRequesterInfo final : public PresentationSessionInfo
-                                      , public nsIServerSocketListener
+// Session info with controlling browsing context (sender side) behaviors.
+class PresentationControllingInfo final : public PresentationSessionInfo
+                                        , public nsIServerSocketListener
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
   NS_DECL_NSISERVERSOCKETLISTENER
 
-  PresentationRequesterInfo(const nsAString& aUrl,
-                            const nsAString& aSessionId,
-                            nsIPresentationServiceCallback* aCallback)
+  PresentationControllingInfo(const nsAString& aUrl,
+                              const nsAString& aSessionId,
+                              nsIPresentationServiceCallback* aCallback)
     : PresentationSessionInfo(aUrl, aSessionId, aCallback)
   {
     MOZ_ASSERT(mCallback);
   }
 
   nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
 
 private:
-  ~PresentationRequesterInfo()
+  ~PresentationControllingInfo()
   {
     Shutdown(NS_OK);
   }
 
   void Shutdown(nsresult aReason) override;
 
   nsresult GetAddress(nsACString& aAddress);
 
   nsCOMPtr<nsIServerSocket> mServerSocket;
 };
 
-// Session info with receiver side behaviors.
-class PresentationResponderInfo final : public PresentationSessionInfo
-                                      , public PromiseNativeHandler
-                                      , public nsITimerCallback
+// Session info with presenting browsing context (receiver side) behaviors.
+class PresentationPresentingInfo final : public PresentationSessionInfo
+                                       , public PromiseNativeHandler
+                                       , public nsITimerCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
   NS_DECL_NSITIMERCALLBACK
 
-  PresentationResponderInfo(const nsAString& aUrl,
-                            const nsAString& aSessionId,
-                            nsIPresentationDevice* aDevice)
+  PresentationPresentingInfo(const nsAString& aUrl,
+                             const nsAString& aSessionId,
+                             nsIPresentationDevice* aDevice)
     : PresentationSessionInfo(aUrl, aSessionId, nullptr)
   {
     MOZ_ASSERT(aDevice);
 
     SetDevice(aDevice);
   }
 
   nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
@@ -187,17 +187,17 @@ public:
   {
     mPromise = aPromise;
     mPromise->AppendNativeHandler(this);
   }
 
   bool IsAccessible(base::ProcessId aProcessId) override;
 
 private:
-  ~PresentationResponderInfo()
+  ~PresentationPresentingInfo()
   {
     Shutdown(NS_OK);
   }
 
   void Shutdown(nsresult aReason) override;
 
   nsresult InitTransportAndSendAnswer();
 
--- a/dom/presentation/PresentationSessionTransport.h
+++ b/dom/presentation/PresentationSessionTransport.h
@@ -25,17 +25,17 @@ class nsIInputStream;
 namespace mozilla {
 namespace dom {
 
 /*
  * App-to-App transport channel for the presentation session. It's usually
  * initialized with an |InitWithSocketTransport| call if at the presenting sender
  * side; whereas it's initialized with an |InitWithChannelDescription| if at the
  * presenting receiver side. The lifetime is managed in either
- * |PresentationRequesterInfo| (sender side) or |PresentationResponderInfo|
+ * |PresentationControllingInfo| (sender side) or |PresentationPresentingInfo|
  * (receiver side) in PresentationSessionInfo.cpp.
  *
  * TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
  * The implementation over the TCP channel is primarily used for the early stage
  * of Presentation API (without SSL) and should be migrated to DataChannel with
  * full support soon.
  */
 class PresentationSessionTransport final : public nsIPresentationSessionTransport
--- a/dom/presentation/interfaces/nsIPresentationListener.idl
+++ b/dom/presentation/interfaces/nsIPresentationListener.idl
@@ -1,16 +1,16 @@
 /* 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"
 
 [scriptable, uuid(0105f837-4279-4715-9d5b-2dc3f8b65353)]
-interface nsIPresentationListener : nsISupports
+interface nsIPresentationAvailabilityListener : nsISupports
 {
   /*
    * Called when device availability changes.
    */
   void notifyAvailableChange(in bool available);
 };
 
 [scriptable, uuid(3b9ae71f-2905-4969-9117-101627c1c2ea)]
@@ -27,8 +27,18 @@ interface nsIPresentationSessionListener
                          in unsigned short state);
 
   /*
    * Called when receive messages.
    */
   void notifyMessage(in DOMString sessionId,
                      in ACString data);
 };
+
+[scriptable, uuid(27f101d7-9ed1-429e-b4f8-43b00e8e111c)]
+interface nsIPresentationRespondingListener : nsISupports
+{
+  /*
+   * Called when an incoming session connects.
+   */
+  void notifySessionConnect(in uint64_t windowId,
+                            in DOMString sessionId);
+};
--- a/dom/presentation/interfaces/nsIPresentationService.idl
+++ b/dom/presentation/interfaces/nsIPresentationService.idl
@@ -1,16 +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 "nsISupports.idl"
 
 interface nsIInputStream;
-interface nsIPresentationListener;
+interface nsIPresentationAvailabilityListener;
+interface nsIPresentationRespondingListener;
 interface nsIPresentationSessionListener;
 
 %{C++
 #define PRESENTATION_SERVICE_CID \
   { 0x1d9bb10c, 0xc0ab, 0x4fe8, \
     { 0x9e, 0x4f, 0x40, 0x58, 0xb8, 0x51, 0x98, 0x32 } }
 #define PRESENTATION_SERVICE_CONTRACTID \
   "@mozilla.org/presentation/presentationservice;1"
@@ -27,17 +28,17 @@ interface nsIPresentationServiceCallback
   /*
    * Called when the operation fails.
    *
    * @param error: error message.
    */
   void notifyError(in nsresult error);
 };
 
-[scriptable, uuid(731af98a-2760-4e7f-bb59-c7cf6665f26f)]
+[scriptable, uuid(5a254519-45c3-4229-9b16-f58c05b78fc6)]
 interface nsIPresentationService : nsISupports
 {
   /*
    * 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.
@@ -64,27 +65,27 @@ interface nsIPresentationService : nsISu
   /*
    * Terminate the session.
    *
    * @param sessionId: An ID to identify presentation session.
    */
   void terminate(in DOMString sessionId);
 
   /*
-   * Register a listener. Must be called from the main thread.
+   * Register an availability listener. Must be called from the main thread.
    *
    * @param listener: The listener to register.
    */
-  void registerListener(in nsIPresentationListener listener);
+  void registerAvailabilityListener(in nsIPresentationAvailabilityListener listener);
 
   /*
-   * Unregister a listener. Must be called from the main thread.
+   * Unregister an availability listener. Must be called from the main thread.
    * @param listener: The listener to unregister.
    */
-  void unregisterListener(in nsIPresentationListener listener);
+  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 listener: The listener to register.
    */
   void registerSessionListener(in DOMString sessionId,
@@ -93,16 +94,31 @@ interface nsIPresentationService : nsISu
   /*
    * Unregister a session listener. Must be called from the main thread.
    *
    * @param sessionId: An ID to identify presentation session.
    */
   void unregisterSessionListener(in DOMString sessionId);
 
   /*
+   * 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 uint64_t windowId,
+                                  in nsIPresentationRespondingListener listener);
+
+  /*
+   * Unregister a responding listener. Must be called from the main thread.
+   * @param windowId: The window ID associated with the listener.
+   */
+  void unregisterRespondingListener(in uint64_t windowId);
+
+  /*
    * Check if the presentation instance has an existent session ID at launch.
    * An empty string is always returned at sender side. Whereas at receiver side
    * the associated session ID is returned if the window ID and URI are matched;
    * otherwise an empty string is returned.
    *
    * @param windowId: The inner window ID used to look up the session ID.
    */
   DOMString getExistentSessionIdAtLaunch(in uint64_t windowId);
--- a/dom/presentation/ipc/PPresentation.ipdl
+++ b/dom/presentation/ipc/PPresentation.ipdl
@@ -25,41 +25,45 @@ struct SendSessionMessageRequest
   InputStreamParams data;
 };
 
 struct TerminateRequest
 {
   nsString sessionId;
 };
 
-union PresentationRequest
+union PresentationIPCRequest
 {
   StartSessionRequest;
   SendSessionMessageRequest;
   TerminateRequest;
 };
 
 sync protocol PPresentation
 {
   manager PContent;
   manages PPresentationRequest;
 
 child:
   NotifyAvailableChange(bool aAvailable);
   NotifySessionStateChange(nsString aSessionId, uint16_t aState);
   NotifyMessage(nsString aSessionId, nsCString aData);
+  NotifySessionConnect(uint64_t aWindowId, nsString aSessionId);
 
 parent:
   __delete__();
 
-  RegisterHandler();
-  UnregisterHandler();
+  RegisterAvailabilityHandler();
+  UnregisterAvailabilityHandler();
 
   RegisterSessionHandler(nsString aSessionId);
   UnregisterSessionHandler(nsString aSessionId);
 
-  PPresentationRequest(PresentationRequest aRequest);
+  RegisterRespondingHandler(uint64_t aWindowId);
+  UnregisterRespondingHandler(uint64_t aWindowId);
+
+  PPresentationRequest(PresentationIPCRequest aRequest);
 
   NotifyReceiverReady(nsString aSessionId);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/presentation/ipc/PresentationChild.cpp
+++ b/dom/presentation/ipc/PresentationChild.cpp
@@ -39,17 +39,17 @@ void
 PresentationChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   mActorDestroyed = true;
   mService->NotifyPresentationChildDestroyed();
   mService = nullptr;
 }
 
 PPresentationRequestChild*
-PresentationChild::AllocPPresentationRequestChild(const PresentationRequest& aRequest)
+PresentationChild::AllocPPresentationRequestChild(const PresentationIPCRequest& aRequest)
 {
   NS_NOTREACHED("We should never be manually allocating PPresentationRequestChild actors");
   return nullptr;
 }
 
 bool
 PresentationChild::DeallocPPresentationRequestChild(PPresentationRequestChild* aActor)
 {
@@ -81,16 +81,26 @@ PresentationChild::RecvNotifyMessage(con
                                      const nsCString& aData)
 {
   if (mService) {
     NS_WARN_IF(NS_FAILED(mService->NotifyMessage(aSessionId, aData)));
   }
   return true;
 }
 
+bool
+PresentationChild::RecvNotifySessionConnect(const uint64_t& aWindowId,
+                                            const nsString& aSessionId)
+{
+  if (mService) {
+    NS_WARN_IF(NS_FAILED(mService->NotifySessionConnect(aWindowId, aSessionId)));
+  }
+  return true;
+}
+
 /*
  * Implementation of PresentationRequestChild
  */
 
 PresentationRequestChild::PresentationRequestChild(nsIPresentationServiceCallback* aCallback)
   : mActorDestroyed(false)
   , mCallback(aCallback)
 {
--- a/dom/presentation/ipc/PresentationChild.h
+++ b/dom/presentation/ipc/PresentationChild.h
@@ -21,32 +21,36 @@ class PresentationChild final : public P
 {
 public:
   explicit PresentationChild(PresentationIPCService* aService);
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual PPresentationRequestChild*
-  AllocPPresentationRequestChild(const PresentationRequest& aRequest) override;
+  AllocPPresentationRequestChild(const PresentationIPCRequest& aRequest) override;
 
   virtual bool
   DeallocPPresentationRequestChild(PPresentationRequestChild* aActor) override;
 
   virtual bool
   RecvNotifyAvailableChange(const bool& aAvailable) override;
 
   virtual bool
   RecvNotifySessionStateChange(const nsString& aSessionId,
                                const uint16_t& aState) override;
 
   virtual bool
   RecvNotifyMessage(const nsString& aSessionId,
                     const nsCString& aData) override;
 
+  virtual bool
+  RecvNotifySessionConnect(const uint64_t& aWindowId,
+                           const nsString& aSessionId) override;
+
 private:
   virtual ~PresentationChild();
 
   bool mActorDestroyed;
   nsRefPtr<PresentationIPCService> mService;
 };
 
 class PresentationRequestChild final : public PPresentationRequestChild
--- a/dom/presentation/ipc/PresentationIPCService.cpp
+++ b/dom/presentation/ipc/PresentationIPCService.cpp
@@ -2,23 +2,25 @@
 /* vim: set sw=2 ts=8 et ft=cpp : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PPresentation.h"
 #include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/URIUtils.h"
 #include "nsIPresentationListener.h"
 #include "PresentationCallbacks.h"
 #include "PresentationChild.h"
 #include "PresentationIPCService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::ipc;
 
 namespace {
 
 PresentationChild* sPresentationChild;
 
 } // anonymous
 
 NS_IMPL_ISUPPORTS(PresentationIPCService, nsIPresentationService)
@@ -31,17 +33,17 @@ PresentationIPCService::PresentationIPCS
   }
   sPresentationChild = new PresentationChild(this);
   NS_WARN_IF(!contentChild->SendPPresentationConstructor(sPresentationChild));
 }
 
 /* virtual */
 PresentationIPCService::~PresentationIPCService()
 {
-  mListeners.Clear();
+  mAvailabilityListeners.Clear();
   mSessionListeners.Clear();
   mRespondingSessionIds.Clear();
   mRespondingWindowIds.Clear();
   sPresentationChild = nullptr;
 }
 
 NS_IMETHODIMP
 PresentationIPCService::StartSession(const nsAString& aUrl,
@@ -73,47 +75,47 @@ PresentationIPCService::Terminate(const 
 {
   MOZ_ASSERT(!aSessionId.IsEmpty());
 
   return SendRequest(nullptr, TerminateRequest(nsAutoString(aSessionId)));
 }
 
 nsresult
 PresentationIPCService::SendRequest(nsIPresentationServiceCallback* aCallback,
-                                    const PresentationRequest& aRequest)
+                                    const PresentationIPCRequest& aRequest)
 {
   if (sPresentationChild) {
     PresentationRequestChild* actor = new PresentationRequestChild(aCallback);
     NS_WARN_IF(!sPresentationChild->SendPPresentationRequestConstructor(actor, aRequest));
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::RegisterListener(nsIPresentationListener* aListener)
+PresentationIPCService::RegisterAvailabilityListener(nsIPresentationAvailabilityListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aListener);
 
-  mListeners.AppendElement(aListener);
+  mAvailabilityListeners.AppendElement(aListener);
   if (sPresentationChild) {
-    NS_WARN_IF(!sPresentationChild->SendRegisterHandler());
+    NS_WARN_IF(!sPresentationChild->SendRegisterAvailabilityHandler());
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::UnregisterListener(nsIPresentationListener* aListener)
+PresentationIPCService::UnregisterAvailabilityListener(nsIPresentationAvailabilityListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aListener);
 
-  mListeners.RemoveElement(aListener);
+  mAvailabilityListeners.RemoveElement(aListener);
   if (sPresentationChild) {
-    NS_WARN_IF(!sPresentationChild->SendUnregisterHandler());
+    NS_WARN_IF(!sPresentationChild->SendUnregisterAvailabilityHandler());
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationIPCService::RegisterSessionListener(const nsAString& aSessionId,
                                                 nsIPresentationSessionListener* aListener)
 {
@@ -136,16 +138,41 @@ PresentationIPCService::UnregisterSessio
 
   mSessionListeners.Remove(aSessionId);
   if (sPresentationChild) {
     NS_WARN_IF(!sPresentationChild->SendUnregisterSessionHandler(nsAutoString(aSessionId)));
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PresentationIPCService::RegisterRespondingListener(uint64_t aWindowId,
+                                                   nsIPresentationRespondingListener* aListener)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mRespondingListeners.Put(aWindowId, aListener);
+  if (sPresentationChild) {
+    NS_WARN_IF(!sPresentationChild->SendRegisterRespondingHandler(aWindowId));
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PresentationIPCService::UnregisterRespondingListener(uint64_t aWindowId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mRespondingListeners.Remove(aWindowId);
+  if (sPresentationChild) {
+    NS_WARN_IF(!sPresentationChild->SendUnregisterRespondingHandler(aWindowId));
+  }
+  return NS_OK;
+}
+
 nsresult
 PresentationIPCService::NotifySessionStateChange(const nsAString& aSessionId,
                                                  uint16_t aState)
 {
   nsCOMPtr<nsIPresentationSessionListener> listener;
   if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
     return NS_OK;
   }
@@ -161,21 +188,33 @@ PresentationIPCService::NotifyMessage(co
   if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
     return NS_OK;
   }
 
   return listener->NotifyMessage(aSessionId, aData);
 }
 
 nsresult
+PresentationIPCService::NotifySessionConnect(uint64_t aWindowId,
+                                             const nsAString& aSessionId)
+{
+  nsCOMPtr<nsIPresentationRespondingListener> listener;
+  if (NS_WARN_IF(!mRespondingListeners.Get(aWindowId, getter_AddRefs(listener)))) {
+    return NS_OK;
+  }
+
+  return listener->NotifySessionConnect(aWindowId, aSessionId);
+}
+
+nsresult
 PresentationIPCService::NotifyAvailableChange(bool aAvailable)
 {
-  nsTObserverArray<nsCOMPtr<nsIPresentationListener> >::ForwardIterator iter(mListeners);
+  nsTObserverArray<nsCOMPtr<nsIPresentationAvailabilityListener>>::ForwardIterator iter(mAvailabilityListeners);
   while (iter.HasMore()) {
-    nsIPresentationListener* listener = iter.GetNext();
+    nsIPresentationAvailabilityListener* listener = iter.GetNext();
     NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(aAvailable)));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationIPCService::GetExistentSessionIdAtLaunch(uint64_t aWindowId,
--- a/dom/presentation/ipc/PresentationIPCService.h
+++ b/dom/presentation/ipc/PresentationIPCService.h
@@ -11,17 +11,17 @@
 #include "nsRefPtrHashtable.h"
 #include "nsTObserverArray.h"
 
 class nsIDocShell;
 
 namespace mozilla {
 namespace dom {
 
-class PresentationRequest;
+class PresentationIPCRequest;
 class PresentationResponderLoadingCallback;
 
 class PresentationIPCService final : public nsIPresentationService
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPRESENTATIONSERVICE
 
@@ -30,28 +30,32 @@ public:
   nsresult NotifyAvailableChange(bool aAvailable);
 
   nsresult NotifySessionStateChange(const nsAString& aSessionId,
                                     uint16_t aState);
 
   nsresult NotifyMessage(const nsAString& aSessionId,
                          const nsACString& aData);
 
+  nsresult NotifySessionConnect(uint64_t aWindowId,
+                                const nsAString& aSessionId);
+
   void NotifyPresentationChildDestroyed();
 
   nsresult MonitorResponderLoading(const nsAString& aSessionId,
                                    nsIDocShell* aDocShell);
 
 private:
   virtual ~PresentationIPCService();
   nsresult SendRequest(nsIPresentationServiceCallback* aCallback,
-                       const PresentationRequest& aRequest);
+                       const PresentationIPCRequest& aRequest);
 
-  nsTObserverArray<nsCOMPtr<nsIPresentationListener> > mListeners;
+  nsTObserverArray<nsCOMPtr<nsIPresentationAvailabilityListener> > mAvailabilityListeners;
   nsRefPtrHashtable<nsStringHashKey, nsIPresentationSessionListener> mSessionListeners;
+  nsRefPtrHashtable<nsUint64HashKey, nsIPresentationRespondingListener> mRespondingListeners;
   nsRefPtr<PresentationResponderLoadingCallback> mCallback;
 
   // Store the mapping between the window ID of the OOP page (in this process)
   // and the ID of the responding session. It's used for an OOP 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;
--- a/dom/presentation/ipc/PresentationParent.cpp
+++ b/dom/presentation/ipc/PresentationParent.cpp
@@ -11,17 +11,20 @@
 #include "PresentationService.h"
 
 using namespace mozilla::dom;
 
 /*
  * Implementation of PresentationParent
  */
 
-NS_IMPL_ISUPPORTS(PresentationParent, nsIPresentationListener, nsIPresentationSessionListener)
+NS_IMPL_ISUPPORTS(PresentationParent,
+                  nsIPresentationAvailabilityListener,
+                  nsIPresentationSessionListener,
+                  nsIPresentationRespondingListener)
 
 PresentationParent::PresentationParent()
   : mActorDestroyed(false)
 {
   MOZ_COUNT_CTOR(PresentationParent);
 }
 
 /* virtual */ PresentationParent::~PresentationParent()
@@ -42,48 +45,48 @@ PresentationParent::ActorDestroy(ActorDe
 {
   mActorDestroyed = true;
 
   for (uint32_t i = 0; i < mSessionIds.Length(); i++) {
     NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(mSessionIds[i])));
   }
   mSessionIds.Clear();
 
-  mService->UnregisterListener(this);
+  mService->UnregisterAvailabilityListener(this);
   mService = nullptr;
 }
 
 bool
 PresentationParent::RecvPPresentationRequestConstructor(
   PPresentationRequestParent* aActor,
-  const PresentationRequest& aRequest)
+  const PresentationIPCRequest& aRequest)
 {
   PresentationRequestParent* actor = static_cast<PresentationRequestParent*>(aActor);
 
   nsresult rv = NS_ERROR_FAILURE;
   switch (aRequest.type()) {
-    case PresentationRequest::TStartSessionRequest:
+    case PresentationIPCRequest::TStartSessionRequest:
       rv = actor->DoRequest(aRequest.get_StartSessionRequest());
       break;
-    case PresentationRequest::TSendSessionMessageRequest:
+    case PresentationIPCRequest::TSendSessionMessageRequest:
       rv = actor->DoRequest(aRequest.get_SendSessionMessageRequest());
       break;
-    case PresentationRequest::TTerminateRequest:
+    case PresentationIPCRequest::TTerminateRequest:
       rv = actor->DoRequest(aRequest.get_TerminateRequest());
       break;
     default:
-      MOZ_CRASH("Unknown PresentationRequest type");
+      MOZ_CRASH("Unknown PresentationIPCRequest type");
   }
 
   return NS_WARN_IF(NS_FAILED(rv)) ? false : true;
 }
 
 PPresentationRequestParent*
 PresentationParent::AllocPPresentationRequestParent(
-  const PresentationRequest& aRequest)
+  const PresentationIPCRequest& aRequest)
 {
   MOZ_ASSERT(mService);
   nsRefPtr<PresentationRequestParent> actor = new PresentationRequestParent(mService);
   return actor.forget().take();
 }
 
 bool
 PresentationParent::DeallocPPresentationRequestParent(
@@ -96,28 +99,28 @@ PresentationParent::DeallocPPresentation
 
 bool
 PresentationParent::Recv__delete__()
 {
   return true;
 }
 
 bool
-PresentationParent::RecvRegisterHandler()
+PresentationParent::RecvRegisterAvailabilityHandler()
 {
   MOZ_ASSERT(mService);
-  NS_WARN_IF(NS_FAILED(mService->RegisterListener(this)));
+  NS_WARN_IF(NS_FAILED(mService->RegisterAvailabilityListener(this)));
   return true;
 }
 
 bool
-PresentationParent::RecvUnregisterHandler()
+PresentationParent::RecvUnregisterAvailabilityHandler()
 {
   MOZ_ASSERT(mService);
-  NS_WARN_IF(NS_FAILED(mService->UnregisterListener(this)));
+  NS_WARN_IF(NS_FAILED(mService->UnregisterAvailabilityListener(this)));
   return true;
 }
 
 /* virtual */ bool
 PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId)
 {
   MOZ_ASSERT(mService);
 
@@ -137,16 +140,32 @@ PresentationParent::RecvRegisterSessionH
 PresentationParent::RecvUnregisterSessionHandler(const nsString& aSessionId)
 {
   MOZ_ASSERT(mService);
   mSessionIds.RemoveElement(aSessionId);
   NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(aSessionId)));
   return true;
 }
 
+/* virtual */ bool
+PresentationParent::RecvRegisterRespondingHandler(const uint64_t& aWindowId)
+{
+  MOZ_ASSERT(mService);
+  NS_WARN_IF(NS_FAILED(mService->RegisterRespondingListener(aWindowId, this)));
+  return true;
+}
+
+/* virtual */ bool
+PresentationParent::RecvUnregisterRespondingHandler(const uint64_t& aWindowId)
+{
+  MOZ_ASSERT(mService);
+  NS_WARN_IF(NS_FAILED(mService->UnregisterRespondingListener(aWindowId)));
+  return true;
+}
+
 NS_IMETHODIMP
 PresentationParent::NotifyAvailableChange(bool aAvailable)
 {
   if (NS_WARN_IF(mActorDestroyed || !SendNotifyAvailableChange(aAvailable))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
@@ -168,16 +187,27 @@ PresentationParent::NotifyMessage(const 
 {
   if (NS_WARN_IF(mActorDestroyed ||
                  !SendNotifyMessage(nsAutoString(aSessionId), nsAutoCString(aData)))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PresentationParent::NotifySessionConnect(uint64_t aWindowId,
+                                         const nsAString& aSessionId)
+{
+  if (NS_WARN_IF(mActorDestroyed ||
+                 !SendNotifySessionConnect(aWindowId, nsAutoString(aSessionId)))) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
 bool
 PresentationParent::RecvNotifyReceiverReady(const nsString& aSessionId)
 {
   MOZ_ASSERT(mService);
   NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(aSessionId, 0)));
   return true;
 }
 
--- a/dom/presentation/ipc/PresentationParent.h
+++ b/dom/presentation/ipc/PresentationParent.h
@@ -11,50 +11,56 @@
 #include "mozilla/dom/PPresentationRequestParent.h"
 #include "nsIPresentationListener.h"
 #include "nsIPresentationService.h"
 
 namespace mozilla {
 namespace dom {
 
 class PresentationParent final : public PPresentationParent
-                               , public nsIPresentationListener
+                               , public nsIPresentationAvailabilityListener
                                , public nsIPresentationSessionListener
+                               , public nsIPresentationRespondingListener
 {
 public:
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIPRESENTATIONLISTENER
+  NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER
   NS_DECL_NSIPRESENTATIONSESSIONLISTENER
+  NS_DECL_NSIPRESENTATIONRESPONDINGLISTENER
 
   PresentationParent();
 
   bool Init();
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual bool
   RecvPPresentationRequestConstructor(PPresentationRequestParent* aActor,
-                                      const PresentationRequest& aRequest) override;
+                                      const PresentationIPCRequest& aRequest) override;
 
   virtual PPresentationRequestParent*
-  AllocPPresentationRequestParent(const PresentationRequest& aRequest) override;
+  AllocPPresentationRequestParent(const PresentationIPCRequest& aRequest) override;
 
   virtual bool
   DeallocPPresentationRequestParent(PPresentationRequestParent* aActor) override;
 
   virtual bool Recv__delete__() override;
 
-  virtual bool RecvRegisterHandler() override;
+  virtual bool RecvRegisterAvailabilityHandler() override;
 
-  virtual bool RecvUnregisterHandler() override;
+  virtual bool RecvUnregisterAvailabilityHandler() override;
 
   virtual bool RecvRegisterSessionHandler(const nsString& aSessionId) override;
 
   virtual bool RecvUnregisterSessionHandler(const nsString& aSessionId) 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;
--- a/dom/presentation/moz.build
+++ b/dom/presentation/moz.build
@@ -9,31 +9,35 @@ DIRS += ['interfaces', 'provider']
 XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
 MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
 
 EXPORTS.mozilla.dom += [
     'ipc/PresentationChild.h',
     'ipc/PresentationIPCService.h',
     'ipc/PresentationParent.h',
     'Presentation.h',
+    'PresentationAvailability.h',
     'PresentationCallbacks.h',
     'PresentationDeviceManager.h',
+    'PresentationRequest.h',
     'PresentationService.h',
     'PresentationSession.h',
     'PresentationSessionInfo.h',
     'PresentationSessionTransport.h',
 ]
 
 UNIFIED_SOURCES += [
     'ipc/PresentationChild.cpp',
     'ipc/PresentationIPCService.cpp',
     'ipc/PresentationParent.cpp',
     'Presentation.cpp',
+    'PresentationAvailability.cpp',
     'PresentationCallbacks.cpp',
     'PresentationDeviceManager.cpp',
+    'PresentationRequest.cpp',
     'PresentationService.cpp',
     'PresentationSession.cpp',
     'PresentationSessionInfo.cpp',
     'PresentationSessionRequest.cpp',
     'PresentationSessionTransport.cpp',
 ]
 
 EXTRA_COMPONENTS += [
--- a/dom/presentation/tests/mochitest/file_presentation_non_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/file_presentation_non_receiver_oop.html
@@ -24,18 +24,28 @@ function info(msg) {
 
 function finish() {
   alert('DONE');
 }
 
 function testSessionAvailable() {
   return new Promise(function(aResolve, aReject) {
     ok(navigator.presentation, "navigator.presentation should be available in OOP pages.");
-    ok(!navigator.presentation.session, "Non-receiving OOP pages shouldn't get a predefined presentation session instance.");
-    aResolve();
+
+    navigator.presentation.getSessions().then(
+      function(aSessions) {
+        is(aSessions.length, 0, "Non-receiving OOP pages shouldn't get a predefined presentation session instance.");
+        aResolve();
+      },
+      function(aError) {
+        ok(false, "Error occurred when getting sessions: " + aError);
+        teardown();
+        aReject();
+      }
+    );
   });
 }
 
 testSessionAvailable().
 then(finish);
 
 </script>
 </body>
--- a/dom/presentation/tests/mochitest/file_presentation_receiver.html
+++ b/dom/presentation/tests/mochitest/file_presentation_receiver.html
@@ -31,20 +31,30 @@ function finish() {
 }
 
 var session;
 
 function testSessionAvailable() {
   return new Promise(function(aResolve, aReject) {
     ok(navigator.presentation, "navigator.presentation should be available.");
 
-    session = navigator.presentation.session;
-    ok(session.id, "Session ID should be set: " + session.id);
-    is(session.state, "disconnected", "Session state at receiver side should be disconnected by default.");
-    aResolve();
+    navigator.presentation.getSession().then(
+      function(aSession) {
+        session = aSession;
+
+        ok(session.id, "Session ID should be set: " + session.id);
+        is(session.state, "disconnected", "Session state at receiver side should be disconnected by default.");
+        aResolve();
+      },
+      function(aError) {
+        ok(false, "Error occurred when getting the session: " + aError);
+        teardown();
+        aReject();
+      }
+    );
   });
 }
 
 function testSessionReady() {
   return new Promise(function(aResolve, aReject) {
     session.onstatechange = function() {
       session.onstatechange = null;
       is(session.state, "connected", "Session state should become connected.");
--- a/dom/presentation/tests/mochitest/file_presentation_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/file_presentation_receiver_oop.html
@@ -31,20 +31,30 @@ function finish() {
 }
 
 var session;
 
 function testSessionAvailable() {
   return new Promise(function(aResolve, aReject) {
     ok(navigator.presentation, "navigator.presentation should be available.");
 
-    session = navigator.presentation.session;
-    ok(session.id, "Session ID should be set: " + session.id);
-    is(session.state, "disconnected", "Session state at receiver side should be disconnected by default.");
-    aResolve();
+    navigator.presentation.getSession().then(
+      function(aSession) {
+        session = aSession;
+
+        ok(session.id, "Session ID should be set: " + session.id);
+        is(session.state, "disconnected", "Session state at receiver side should be disconnected by default.");
+        aResolve();
+      },
+      function(aError) {
+        ok(false, "Error occurred when getting the session: " + aError);
+        teardown();
+        aReject();
+      }
+    );
   });
 }
 
 function testSessionReady() {
   return new Promise(function(aResolve, aReject) {
     session.onstatechange = function() {
       session.onstatechange = null;
       is(session.state, "connected", "Session state should become connected.");
--- a/dom/presentation/tests/mochitest/file_presentation_receiver_start_session_error.html
+++ b/dom/presentation/tests/mochitest/file_presentation_receiver_start_session_error.html
@@ -31,20 +31,30 @@ function finish() {
 }
 
 var session;
 
 function testSessionAvailable() {
   return new Promise(function(aResolve, aReject) {
     ok(navigator.presentation, "navigator.presentation should be available.");
 
-    session = navigator.presentation.session;
-    ok(session.id, "Session ID should be set: " + session.id);
-    is(session.state, "disconnected", "Session state at receiver side should be disconnected by default.");
-    aResolve();
+    navigator.presentation.getSession().then(
+      function(aSession) {
+        session = aSession;
+
+        ok(session.id, "Session ID should be set: " + session.id);
+        is(session.state, "disconnected", "Session state at receiver side should be disconnected by default.");
+        aResolve();
+      },
+      function(aError) {
+        ok(false, "Error occurred when getting the session: " + aError);
+        teardown();
+        aReject();
+      }
+    );
   });
 }
 
 function testUnexpectedControlChannelClose() {
   return new Promise(function(aResolve, aReject) {
     session.onstatechange = function() {
       session.onstatechange = null;
       is(session.state, "terminated", "Session state should become terminated.");
--- a/dom/presentation/tests/mochitest/mochitest.ini
+++ b/dom/presentation/tests/mochitest/mochitest.ini
@@ -10,16 +10,18 @@ support-files =
 [test_presentation_device_info.html]
 [test_presentation_device_info_permission.html]
 [test_presentation_sender_disconnect.html]
 skip-if = toolkit == 'android' # Bug 1129785
 [test_presentation_sender_start_session_error.html]
 skip-if = toolkit == 'android' # Bug 1129785
 [test_presentation_sender.html]
 skip-if = toolkit == 'android' # Bug 1129785
+[test_presentation_sender_default_request.html]
+skip-if = toolkit == 'android' # Bug 1129785
 [test_presentation_receiver_start_session_error.html]
 skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
 [test_presentation_receiver_start_session_timeout.html]
 skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
 [test_presentation_receiver.html]
 skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
 [test_presentation_receiver_oop.html]
 skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
--- a/dom/presentation/tests/mochitest/test_presentation_receiver.html
+++ b/dom/presentation/tests/mochitest/test_presentation_receiver.html
@@ -88,19 +88,28 @@ function setup() {
 
 function testIncomingSessionRequest() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) {
       gScript.removeMessageListener('receiver-launching', launchReceiverHandler);
       info("Trying to launch receiver page.");
 
       ok(navigator.presentation, "navigator.presentation should be available in in-process pages.");
-      ok(!navigator.presentation.session, "Non-receiving in-process pages shouldn't get a predefined presentation session instance.");
 
-      aResolve();
+      navigator.presentation.getSessions().then(
+        function(aSessions) {
+          is(aSessions.length, 0, "Non-receiving in-process pages shouldn't get a predefined presentation session instance.");
+          aResolve();
+        },
+        function(aError) {
+          ok(false, "Error occurred when getting sessions: " + aError);
+          teardown();
+          aReject();
+        }
+      );
     });
 
     gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl);
   });
 }
 
 function teardown() {
   gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() {
--- a/dom/presentation/tests/mochitest/test_presentation_sender.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender.html
@@ -10,26 +10,37 @@
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for B2G Presentation API at sender side</a>
 <script type="application/javascript;version=1.8">
 
 'use strict';
 
 var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
-var presentation;
+var request;
 var session;
 
 function testSetup() {
   return new Promise(function(aResolve, aReject) {
-    presentation.onavailablechange = function(aIsAvailable) {
-      presentation.onavailablechange = null;
-      ok(aIsAvailable, "Device should be available.");
-      aResolve();
-    };
+    request = new PresentationRequest("http://example.com");
+
+    request.getAvailability().then(
+      function(aAvailability) {
+        aAvailability.onchange = function() {
+          aAvailability.onchange = null;
+          ok(aAvailability.value, "Device should be available.");
+          aResolve();
+        }
+      },
+      function(aError) {
+        ok(false, "Error occurred when getting availability: " + aError);
+        teardown();
+        aReject();
+      }
+    );
 
     gScript.sendAsyncMessage('trigger-device-add');
   });
 }
 
 function testStartSession() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('device-prompt', function devicePromptHandler() {
@@ -71,23 +82,39 @@ function testStartSession() {
       gScript.sendAsyncMessage('trigger-incoming-answer');
     });
 
     gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() {
       gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler);
       info("Data notification is enabled for data transport channel.");
     });
 
-    presentation.startSession("http://example.com").then(
+    var sessionFromEvent;
+    request.onsessionconnect = function(aEvent) {
+      request.onsessionconnect = null;
+      sessionFromEvent = aEvent.session;
+      ok(sessionFromEvent, "|sessionconnect| event is fired with a session.");
+
+      if (session) {
+        is(session, sessionFromEvent, "The session from promise and the one from |sessionconnect| event should be the same.");
+        aResolve();
+      }
+    };
+
+    request.start().then(
       function(aSession) {
         session = aSession;
         ok(session, "Session should be availlable.");
         ok(session.id, "Session ID should be set.");
         is(session.state, "connected", "Session state at sender side should be connected by default.");
-        aResolve();
+
+        if (sessionFromEvent) {
+          is(session, sessionFromEvent, "The session from promise and the one from |sessionconnect| event should be the same.");
+          aResolve();
+        }
       },
       function(aError) {
         ok(false, "Error occurred when starting session: " + aError);
         teardown();
         aReject();
       }
     );
   });
@@ -144,18 +171,17 @@ function teardown() {
     gScript.destroy();
     SimpleTest.finish();
   });
 
   gScript.sendAsyncMessage('teardown');
 }
 
 function runTests() {
-  ok(navigator.presentation, "navigator.presentation should be available.");
-  presentation = navigator.presentation;
+  ok(window.PresentationRequest, "PresentationRequest should be available.");
 
   testSetup().
   then(testStartSession).
   then(testSend).
   then(testIncomingMessage).
   then(testCloseSession).
   then(teardown);
 }
new file mode 100644
--- /dev/null
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_default_request.html
@@ -0,0 +1,150 @@
+<!DOCTYPE HTML>
+<html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<head>
+  <meta charset="utf-8">
+  <title>Test default request for B2G Presentation API at sender side</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test default request for B2G Presentation API at sender side</a>
+<script type="application/javascript;version=1.8">
+
+'use strict';
+
+var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
+var session;
+
+function testSetup() {
+  return new Promise(function(aResolve, aReject) {
+    navigator.presentation.defaultRequest = new PresentationRequest("http://example.com");
+
+    navigator.presentation.defaultRequest.getAvailability().then(
+      function(aAvailability) {
+        aAvailability.onchange = function() {
+          aAvailability.onchange = null;
+          ok(aAvailability.value, "Device should be available.");
+          aResolve();
+        }
+      },
+      function(aError) {
+        ok(false, "Error occurred when getting availability: " + aError);
+        teardown();
+        aReject();
+      }
+    );
+
+    gScript.sendAsyncMessage('trigger-device-add');
+  });
+}
+
+function testStartSession() {
+  return new Promise(function(aResolve, aReject) {
+    gScript.addMessageListener('device-prompt', function devicePromptHandler() {
+      gScript.removeMessageListener('device-prompt', devicePromptHandler);
+      info("Device prompt is triggered.");
+      gScript.sendAsyncMessage('trigger-device-prompt-select');
+    });
+
+    gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
+      gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
+      info("A control channel is established.");
+      gScript.sendAsyncMessage('trigger-control-channel-open');
+    });
+
+    gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
+      gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
+      info("The control channel is opened.");
+    });
+
+    gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
+      gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
+      info("The control channel is closed. " + aReason);
+    });
+
+    gScript.addMessageListener('offer-sent', function offerSentHandler() {
+      gScript.removeMessageListener('offer-sent', offerSentHandler);
+      info("An offer is sent out.");
+      gScript.sendAsyncMessage('trigger-incoming-transport');
+    });
+
+    gScript.addMessageListener('answer-received', function answerReceivedHandler() {
+      gScript.removeMessageListener('answer-received', answerReceivedHandler);
+      info("An answer is received.");
+    });
+
+    gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
+      gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
+      info("Data transport channel is initialized.");
+      gScript.sendAsyncMessage('trigger-incoming-answer');
+    });
+
+    navigator.presentation.defaultRequest.onsessionconnect = function(aEvent) {
+      navigator.presentation.defaultRequest.onsessionconnect = null;
+      session = aEvent.session;
+      ok(session, "|sessionconnect| event is fired with a session.");
+      ok(session.id, "Session ID should be set.");
+      is(session.state, "connected", "Session state at sender side should be connected by default.");
+      aResolve();
+    };
+
+    // Simulate the UA triggers |start()| of the default request.
+    navigator.presentation.defaultRequest.start();
+  });
+}
+
+function testCloseSession() {
+  return new Promise(function(aResolve, aReject) {
+    gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
+      gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
+      info("The data transport is closed. " + aReason);
+    });
+
+    session.onstatechange = function() {
+      session.onstatechange = null;
+      is(session.state, "terminated", "Session should be terminated.");
+      aResolve();
+    };
+
+    session.close();
+  });
+}
+
+function teardown() {
+  gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() {
+    gScript.removeMessageListener('teardown-complete', teardownCompleteHandler);
+    gScript.destroy();
+    SimpleTest.finish();
+  });
+
+  gScript.sendAsyncMessage('teardown');
+}
+
+function runTests() {
+  ok(window.PresentationRequest, "PresentationRequest should be available.");
+  ok(navigator.presentation, "navigator.presentation should be available.");
+
+  testSetup().
+  then(testStartSession).
+  then(testCloseSession).
+  then(teardown);
+}
+
+SimpleTest.expectAssertions(0, 5);
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPermissions([
+  {type: 'presentation-device-manage', allow: false, context: document},
+  {type: 'presentation', allow: true, context: document},
+], function() {
+  SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
+                                      ["dom.ignore_webidl_scope_checks", true],
+                                      ["dom.presentation.test.enabled", true],
+                                      ["dom.presentation.test.stage", 0]]},
+                            runTests);
+});
+
+</script>
+</body>
+</html>
--- a/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
@@ -10,26 +10,37 @@
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for session disconnection of B2G Presentation API at sender side</a>
 <script type="application/javascript;version=1.8">
 
 'use strict';
 
 var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
-var presentation;
+var request;
 var session;
 
 function testSetup() {
   return new Promise(function(aResolve, aReject) {
-    presentation.onavailablechange = function(aIsAvailable) {
-      presentation.onavailablechange = null;
-      ok(aIsAvailable, "Device should be available.");
-      aResolve();
-    };
+    request = new PresentationRequest("http://example.com");
+
+    request.getAvailability().then(
+      function(aAvailability) {
+        aAvailability.onchange = function() {
+          aAvailability.onchange = null;
+          ok(aAvailability.value, "Device should be available.");
+          aResolve();
+        }
+      },
+      function(aError) {
+        ok(false, "Error occurred when getting availability: " + aError);
+        teardown();
+        aReject();
+      }
+    );
 
     gScript.sendAsyncMessage('trigger-device-add');
   });
 }
 
 function testStartSession() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('device-prompt', function devicePromptHandler() {
@@ -71,17 +82,17 @@ function testStartSession() {
       info("Data transport channel is initialized.");
     });
 
     gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() {
       gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler);
       info("Data notification is enabled for data transport channel.");
     });
 
-    presentation.startSession("http://example.com").then(
+    request.start().then(
       function(aSession) {
         session = aSession;
         ok(session, "Session should be availlable.");
         ok(session.id, "Session ID should be set.");
         is(session.state, "connected", "Session state at sender side should be connected by default.");
         aResolve();
       },
       function(aError) {
@@ -128,18 +139,17 @@ function teardown() {
     gScript.destroy();
     SimpleTest.finish();
   });
 
   gScript.sendAsyncMessage('teardown');
 }
 
 function runTests() {
-  ok(navigator.presentation, "navigator.presentation should be available.");
-  presentation = navigator.presentation;
+  ok(window.PresentationRequest, "PresentationRequest should be available.");
 
   testSetup().
   then(testStartSession).
   then(testSessionDisconnection).
   then(testCloseSession).
   then(teardown);
 }
 
--- a/dom/presentation/tests/mochitest/test_presentation_sender_start_session_error.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_start_session_error.html
@@ -10,60 +10,67 @@
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for startSession errors of B2G Presentation API at sender side</a>
 <script type="application/javascript;version=1.8">
 
 'use strict';
 
 var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
-var presentation;
+var request;
 
 function setup() {
   return new Promise(function(aResolve, aReject) {
-    presentation.onavailablechange = function(aIsAvailable) {
-      presentation.onavailablechange = null;
-      ok(aIsAvailable, "Device should be available.");
-      aResolve();
-    };
+    request = new PresentationRequest("http://example.com");
+
+    request.getAvailability().then(
+      function(aAvailability) {
+        aAvailability.onchange = function() {
+          aAvailability.onchange = null;
+          ok(aAvailability.value, "Device should be available.");
+          aResolve();
+        }
+      },
+      function(aError) {
+        ok(false, "Error occurred when getting availability: " + aError);
+        teardown();
+        aReject();
+      }
+    );
 
     gScript.sendAsyncMessage('trigger-device-add');
   });
 }
 
-function testStartSessionNoAvailableDevice() {
+function testCreateRequestWithEmptyURL() {
   return new Promise(function(aResolve, aReject) {
-    presentation.startSession("http://example.com").then(
-      function(aSession) {
-        ok(false, "startSession shouldn't succeed in this case.");
-        aReject();
-      },
-      function(aError) {
-        is(aError.name, "InvalidStateError", "InvalidStateError is expected when starting session.");
-        aResolve();
-      }
-    );
+    try {
+      request = new PresentationRequest("");
+    } catch (aError) {
+      is(aError.name, "SyntaxError", "SyntaxError is expected when using an empty URL.");
+      aResolve();
+    }
   });
 }
 
 function testStartSessionCancelPrompt() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('device-prompt', function devicePromptHandler() {
       gScript.removeMessageListener('device-prompt', devicePromptHandler);
       info("Device prompt is triggered.");
       gScript.sendAsyncMessage('trigger-device-prompt-cancel');
     });
 
-    presentation.startSession("http://example.com").then(
+    request.start().then(
       function(aSession) {
-        ok(false, "startSession shouldn't succeed in this case.");
+        ok(false, "|start| shouldn't succeed in this case.");
         aReject();
       },
       function(aError) {
-        is(aError.name, "NS_ERROR_DOM_PROP_ACCESS_DENIED", "NS_ERROR_DOM_PROP_ACCESS_DENIED is expected when starting session.");
+        is(aError.name, "AbortError", "AbortError is expected when the prompt is canceled.");
         aResolve();
       }
     );
   });
 }
 
 function testStartSessionUnexpectedControlChannelCloseBeforeDataTransportInit() {
   return new Promise(function(aResolve, aReject) {
@@ -90,23 +97,23 @@ function testStartSessionUnexpectedContr
     });
 
     gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
       gScript.removeMessageListener('offer-sent', offerSentHandler);
       ok(aIsValid, "A valid offer is sent out.");
       gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_FAILURE);
     });
 
-    presentation.startSession("http://example.com").then(
+    request.start().then(
       function(aSession) {
-        ok(false, "startSession shouldn't succeed in this case.");
+        ok(false, "|start| shouldn't succeed in this case.");
         aReject();
       },
       function(aError) {
-        is(aError.name, "NS_ERROR_FAILURE", "NS_ERROR_FAILURE is expected when starting session.");
+        is(aError.name, "OperationError", "OperationError is expected when a connection error happens during starting session.");
         aResolve();
       }
     );
   });
 }
 
 function testStartSessionUnexpectedControlChannelCloseBeforeDataTransportReady() {
   return new Promise(function(aResolve, aReject) {
@@ -144,23 +151,23 @@ function testStartSessionUnexpectedContr
       gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_ABORT);
     });
 
     gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
       gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
       info("The data transport is closed. " + aReason);
     });
 
-    presentation.startSession("http://example.com").then(
+    request.start().then(
       function(aSession) {
-        ok(false, "startSession shouldn't succeed in this case.");
+        ok(false, "|start| shouldn't succeed in this case.");
         aReject();
       },
       function(aError) {
-        is(aError.name, "NS_ERROR_ABORT", "NS_ERROR_ABORT is expected when starting session.");
+        is(aError.name, "OperationError", "OperationError is expected when a connection error happens during starting session.");
         aResolve();
       }
     );
   });
 }
 
 function testStartSessionUnexpectedDataTransportClose() {
   return new Promise(function(aResolve, aReject) {
@@ -198,23 +205,23 @@ function testStartSessionUnexpectedDataT
       gScript.sendAsyncMessage('trigger-data-transport-close', SpecialPowers.Cr.NS_ERROR_UNEXPECTED);
     });
 
     gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
       gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
       info("The data transport is closed. " + aReason);
     });
 
-    presentation.startSession("http://example.com").then(
+    request.start().then(
       function(aSession) {
-        ok(false, "startSession shouldn't succeed in this case.");
+        ok(false, "|start| shouldn't succeed in this case.");
         aReject();
       },
       function(aError) {
-        is(aError.name, "NS_ERROR_UNEXPECTED", "NS_ERROR_UNEXPECTED is expected when starting session.");
+        is(aError.name, "OperationError", "OperationError is expected when a connection error happens during starting session.");
         aResolve();
       }
     );
   });
 }
 
 function teardown() {
   gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() {
@@ -222,20 +229,19 @@ function teardown() {
     gScript.destroy();
     SimpleTest.finish();
   });
 
   gScript.sendAsyncMessage('teardown');
 }
 
 function runTests() {
-  ok(navigator.presentation, "navigator.presentation should be available.");
-  presentation = navigator.presentation;
+  ok(window.PresentationRequest, "PresentationRequest should be available.");
 
-  testStartSessionNoAvailableDevice().
+  testCreateRequestWithEmptyURL().
   then(setup).
   then(testStartSessionCancelPrompt).
   then(testStartSessionUnexpectedControlChannelCloseBeforeDataTransportInit).
   then(testStartSessionUnexpectedControlChannelCloseBeforeDataTransportReady).
   then(testStartSessionUnexpectedDataTransportClose).
   then(teardown);
 }
 
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -927,16 +927,26 @@ var interfaceNamesInGlobalScope =
     {name: "PopupBoxObject", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PositionSensorVRDevice", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationDeviceInfoManager",
      disabled: true,
      permission: ["presentation-device-manage"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "Presentation", disabled: true, permission: ["presentation"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "PresentationAvailability", disabled: true, permission: ["presentation"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "PresentationRequest", disabled: true, permission: ["presentation"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "PresentationSession", disabled: true, permission: ["presentation"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "PresentationSessionConnectEvent", disabled: true, permission: ["presentation"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "ProcessingInstruction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ProgressEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PropertyNodeList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -427,17 +427,17 @@ partial interface Navigator {
 };
 
 partial interface Navigator {
   [Throws, Pref="dom.inputport.enabled", CheckAnyPermissions="inputport", AvailableIn=CertifiedApps]
   readonly attribute InputPortManager inputPortManager;
 };
 
 partial interface Navigator {
-  [Throws, Pref="dom.presentation.enabled", CheckAnyPermissions="presentation", AvailableIn="PrivilegedApps"]
+  [Throws, Pref="dom.presentation.enabled", CheckAnyPermissions="presentation", AvailableIn="PrivilegedApps", SameObject]
   readonly attribute Presentation? presentation;
 };
 
 #ifdef MOZ_EME
 partial interface Navigator {
   [Pref="media.eme.apiVisible", NewObject]
   Promise<MediaKeySystemAccess>
   requestMediaKeySystemAccess(DOMString keySystem,
--- a/dom/webidl/Presentation.webidl
+++ b/dom/webidl/Presentation.webidl
@@ -3,62 +3,42 @@
  * 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/.
  */
 
 [Pref="dom.presentation.enabled",
  CheckAnyPermissions="presentation",
  AvailableIn="PrivilegedApps"]
 interface Presentation : EventTarget {
+ /*
+  * This should be used by the UA as the default presentation request for the
+  * controller. When the UA wishes to initiate a PresentationSession on the
+  * controller's behalf, it MUST start a presentation session using the default
+  * presentation request (as if the controller had called |defaultRequest.start()|).
+  *
+  * Only used by controlling browsing context (senders).
+  */
+  attribute PresentationRequest? defaultRequest;
+
   /*
-   * A requesting page use startSession() to start a new session, and the
-   * session will be returned with the promise. UA may show a prompt box with a
-   * list of available devices and ask the user to grant permission, choose a
-   * device, or cancel the operation.
-   *
-   * @url: The URL of presenting page.
-   * @sessionId: Optional. If it's not specified, a random alphanumeric value of
-   *             at least 16 characters drawn from the character [A-Za-z0-9] is
-   *             automatically generated as the id of the session.
+   * Get the first connected presentation session in a presenting browsing
+   * context.
    *
-   * The promise is resolved when the presenting page is successfully loaded and
-   * the communication channel is established, i.e., the session state is
-   * "connected".
-   *
-   * The promise may be rejected duo to one of the following reasons:
-   * - "InternalError":        Unexpected internal error occurs.
-   * - "NoDeviceAvailable":    No available device.
-   * - "PermissionDenied":     User dismiss the device prompt box.
-   * - "ControlChannelFailed": Failed to establish control channel.
-   * - "NoApplicationFound":  app:// scheme is supported on Firefox OS, but no
-   *                           corresponding application is found on remote side.
-   * - "PageLoadTimeout":      Presenting page takes too long to load.
-   * - "DataChannelFailed":    Failed to establish data channel.
+   * Only used by presenting browsing context (receivers).
    */
   [Throws]
-  Promise<PresentationSession> startSession(DOMString url,
-                                            optional DOMString sessionId);
+  Promise<PresentationSession> getSession();
 
   /*
-   * This attribute is only available on the presenting page. It should be
-   * created when loading the presenting page, and it's ready to be used after
-   * 'onload' event is dispatched.
+   * Get all connected presentation sessions in a presenting browsing context.
+   *
+   * Only used by presenting browsing context (receivers).
    */
-  [Pure]
-  readonly attribute PresentationSession? session;
-
- /*
-  * Device availability. If there is more than one device discovered by UA,
-  * the value is |true|. Otherwise, its value should be |false|.
-  *
-  * UA triggers device discovery mechanism periodically and cache the latest
-  * result in this attribute. Thus, it may be out-of-date when we're not in
-  * discovery mode, however, it is still useful to give the developers an idea
-  * that whether there are devices nearby some time ago.
-  */
-  readonly attribute boolean cachedAvailable;
+  [Throws]
+  Promise<sequence<PresentationSession>> getSessions();
 
   /*
-   * It is called when device availability changes. New value is dispatched with
-   * the event.
+   * It is called when an incoming session is connecting.
+   *
+   * Only used by presenting browsing context (receivers).
    */
-  attribute EventHandler onavailablechange;
+  attribute EventHandler onsessionavailable;
 };
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PresentationAvailability.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[Pref="dom.presentation.enabled",
+ CheckAnyPermissions="presentation",
+ AvailableIn="PrivilegedApps"]
+interface PresentationAvailability : EventTarget {
+  /*
+   * If there is at least one device discovered by UA, the value is |true|.
+   * Otherwise, its value should be |false|.
+   */
+  readonly attribute boolean value;
+
+  /*
+   * It is called when device availability changes.
+   */
+  attribute EventHandler onchange;
+};
deleted file mode 100644
--- a/dom/webidl/PresentationAvailableEvent.webidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-[Constructor(DOMString typeArg,
- optional PresentationAvailableEventInit eventInitDict),
- Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation",
- AvailableIn="PrivilegedApps"]
-interface PresentationAvailableEvent : Event
-{
-  readonly attribute boolean available;
-};
-
-dictionary PresentationAvailableEventInit : EventInit
-{
-  boolean available = false;
-};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PresentationRequest.webidl
@@ -0,0 +1,47 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[Constructor(DOMString url),
+ Pref="dom.presentation.enabled",
+ CheckAnyPermissions="presentation",
+ AvailableIn="PrivilegedApps"]
+interface PresentationRequest : EventTarget {
+  /*
+   * A requesting page use start() to start a new session, and the session will
+   * be returned with the promise. UA may show a prompt box with a list of
+   * available devices and ask the user to grant permission, choose a device, or
+   * cancel the operation.
+   *
+   * The promise is resolved when the presenting page is successfully loaded and
+   * the communication channel is established, i.e., the session state is
+   * "connected".
+   *
+   * The promise may be rejected duo to one of the following reasons:
+   * - "OperationError": Unexpected error occurs.
+   * - "NotFoundError":  No available device.
+   * - "AbortError":     User dismiss/cancel the device prompt box.
+   * - "NetworkError":   Failed to establish the control channel or data channel.
+   * - "TimeoutError":   Presenting page takes too long to load.
+   */
+  [Throws]
+  Promise<PresentationSession> start();
+
+ /*
+  * UA triggers device discovery mechanism periodically and monitor device
+  * availability.
+  *
+  * The promise may be rejected duo to one of the following reasons:
+  * - "NotSupportedError": Unable to continuously monitor the availability.
+  */
+  [Throws]
+  Promise<PresentationAvailability> getAvailability();
+
+  /*
+   * It is called when a session associated with a PresentationRequest is created.
+   * The event is fired for all sessions that are created for the controller.
+   */
+  attribute EventHandler onsessionconnect;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PresentationSessionConnectEvent.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[Constructor(DOMString type,
+             optional PresentationSessionConnectEventInit eventInitDict),
+ Pref="dom.presentation.enabled",
+ CheckAnyPermissions="presentation",
+ AvailableIn="PrivilegedApps"]
+interface PresentationSessionConnectEvent : Event
+{
+  [SameObject]
+  readonly attribute PresentationSession session;
+};
+
+dictionary PresentationSessionConnectEventInit : EventInit
+{
+  required PresentationSession session;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -366,17 +366,19 @@ WEBIDL_FILES = [
     'PhoneNumberService.webidl',
     'Plugin.webidl',
     'PluginArray.webidl',
     'PointerEvent.webidl',
     'PopupBoxObject.webidl',
     'Position.webidl',
     'PositionError.webidl',
     'Presentation.webidl',
+    'PresentationAvailability.webidl',
     'PresentationDeviceInfoManager.webidl',
+    'PresentationRequest.webidl',
     'PresentationSession.webidl',
     'ProcessingInstruction.webidl',
     'ProfileTimelineMarker.webidl',
     'Promise.webidl',
     'PromiseDebugging.webidl',
     'RadioNodeList.webidl',
     'Range.webidl',
     'Rect.webidl',
@@ -781,17 +783,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'MozSmsEvent.webidl',
     'MozStkCommandEvent.webidl',
     'MozVoicemailEvent.webidl',
     'PageTransitionEvent.webidl',
     'PerformanceEntryEvent.webidl',
     'PluginCrashedEvent.webidl',
     'PopStateEvent.webidl',
     'PopupBlockedEvent.webidl',
-    'PresentationAvailableEvent.webidl',
+    'PresentationSessionConnectEvent.webidl',
     'ProgressEvent.webidl',
     'RecordErrorEvent.webidl',
     'ScrollViewChangeEvent.webidl',
     'SelectionStateChangedEvent.webidl',
     'StyleRuleChangeEvent.webidl',
     'StyleSheetApplicableStateChangeEvent.webidl',
     'StyleSheetChangeEvent.webidl',
     'TrackEvent.webidl',
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -683,17 +683,17 @@ ServiceWorkerRegistrationMainThread::Sho
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsRefPtr<workers::ServiceWorker> worker = GetActive();
   if (!worker) {
-    aRv.ThrowTypeError(MSG_NO_ACTIVE_WORKER);
+    aRv.ThrowTypeError(MSG_NO_ACTIVE_WORKER, &mScope);
     return nullptr;
   }
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
   nsRefPtr<Promise> p =
     Notification::ShowPersistentNotification(global,
                                              mScope, aTitle, aOptions, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
--- a/dom/workers/test/serviceworkers/fetch/fetch_tests.js
+++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js
@@ -314,16 +314,33 @@ expectAsyncResult();
 fetch(new Request('body-blob', {method: 'POST', body: new Blob(new String('my body'))}))
 .then(function(res) {
   return res.text();
 }).then(function(body) {
   my_ok(body == 'my bodymy body', "the Blob body of the intercepted fetch should be visible in the SW");
   finish();
 });
 
+expectAsyncResult();
+fetch('interrupt.sjs')
+.then(function(res) {
+  my_ok(true, "interrupted fetch succeeded");
+  res.text().then(function(body) {
+    my_ok(false, "interrupted fetch shouldn't have complete body");
+    finish();
+  },
+  function() {
+    my_ok(true, "interrupted fetch shouldn't have complete body");
+    finish();
+  })
+}, function(e) {
+  my_ok(false, "interrupted fetch failed");
+  finish();
+});
+
 ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'].forEach(function(method) {
   fetchXHRWithMethod('xhr-method-test.txt', method, function(xhr) {
     my_ok(xhr.status == 200, method + " load should be successful");
     my_ok(xhr.responseText == ("intercepted " + method), method + " load should have synthesized response");
     finish();
   });
 });
 
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/interrupt.sjs
@@ -0,0 +1,20 @@
+function handleRequest(request, response) {
+  var body = "a";
+  for (var i = 0; i < 20; i++) {
+    body += body;
+  }
+
+  response.seizePower();
+  response.write("HTTP/1.1 200 OK\r\n")
+  var count = 10;
+  response.write("Content-Length: " + body.length * count + "\r\n");
+  response.write("Content-Type: text/plain; charset=utf-8\r\n");
+  response.write("Cache-Control: no-cache, must-revalidate\r\n");
+  response.write("\r\n");
+
+  for (var i = 0; i < count; i++) {
+    response.write(body);
+  }
+
+  throw Components.results.NS_BINDING_ABORTED;
+}
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -45,16 +45,17 @@ support-files =
   fetch/https/index.html
   fetch/https/register.html
   fetch/https/unregister.html
   fetch/https/https_test.js
   fetch/https/clonedresponse/index.html
   fetch/https/clonedresponse/register.html
   fetch/https/clonedresponse/unregister.html
   fetch/https/clonedresponse/https_test.js
+  fetch/interrupt.sjs
   fetch/origin/index.sjs
   fetch/origin/index-to-https.sjs
   fetch/origin/realindex.html
   fetch/origin/realindex.html^headers^
   fetch/origin/register.html
   fetch/origin/unregister.html
   fetch/origin/origin_test.js
   fetch/origin/https/index-https.sjs
--- a/editor/composer/nsEditorSpellCheck.cpp
+++ b/editor/composer/nsEditorSpellCheck.cpp
@@ -648,16 +648,19 @@ nsEditorSpellCheck::CheckCurrentDictiona
   }
 
   // If our preferred current dictionary has gone, pick another one.
   nsTArray<nsString> dictList;
   rv = mSpellChecker->GetDictionaryList(&dictList);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (dictList.Length() > 0) {
+    // Use RAII object to prevent content preferences being written during
+    // this call.
+    UpdateDictionaryHolder holder(this);
     rv = SetCurrentDictionary(dictList[0]);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
new file mode 100644
--- /dev/null
+++ b/editor/composer/test/bug1204147_subframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<textarea id="en-GB" lang="en-GB">element en-GB</textarea>
+<textarea id="en-US" lang="testing-XX">element should default to en-US</textarea>
+
+<div id="trouble-maker" contenteditable>the presence of this div triggers the faulty code path</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/editor/composer/test/bug1204147_subframe2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<textarea id="en-GB" lang="en-GB">element en-GB</textarea>
+<textarea id="en-US" lang="testing-XX">element should default to en-US</textarea>
+</body>
+</html>
--- a/editor/composer/test/chrome.ini
+++ b/editor/composer/test/chrome.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' || os == 'android'
 
 [test_async_UpdateCurrentDictionary.html]
 [test_bug338427.html]
 [test_bug434998.xul]
 [test_bug678842.html]
 [test_bug717433.html]
+[test_bug1204147.html]
 [test_bug1200533.html]
--- a/editor/composer/test/mochitest.ini
+++ b/editor/composer/test/mochitest.ini
@@ -1,14 +1,16 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 support-files =
   bug678842_subframe.html
   bug717433_subframe.html
   bug1200533_subframe.html
+  bug1204147_subframe.html
+  bug1204147_subframe2.html
   en-GB/en_GB.dic
   en-GB/en_GB.aff
   en-AU/en_AU.dic
   en-AU/en_AU.aff
   de-DE/de_DE.dic
   de-DE/de_DE.aff
 
 [test_bug348497.html]
new file mode 100644
--- /dev/null
+++ b/editor/composer/test/test_bug1204147.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1204147
+-->
+<head>
+  <title>Test for Bug 1204147</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1204147">Mozilla Bug 1204147</a>
+<p id="display"></p>
+<iframe id="content"></iframe>
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1204147 **/
+SimpleTest.waitForExplicitFinish();
+var content = document.getElementById('content');
+// Load a subframe containing an editor with using "en-GB". At first
+// load, it will set the dictionary to "en-GB". The bug was that a content preference
+// was also created. At second load, we check the dictionary for another element,
+// one that should use "en-US". With the bug corrected, we get "en-US", before
+// we got "en-GB" from the content preference.
+
+var firstLoad = true;
+var en_GB;
+var hunspell;
+
+var loadListener = function(evt) {
+  Components.utils.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm");
+
+  if (firstLoad) {
+    var dir = Components.classes["@mozilla.org/file/directory_service;1"]
+                        .getService(Components.interfaces.nsIProperties)
+                        .get("CurWorkD", Components.interfaces.nsIFile);
+    dir.append("tests");
+    dir.append("editor");
+    dir.append("composer");
+    dir.append("test");
+
+    hunspell = Components.classes["@mozilla.org/spellchecker/engine;1"]
+                         .getService(Components.interfaces.mozISpellCheckingEngine);
+
+    // Install en-GB dictionary.
+    en_GB = dir.clone();
+    en_GB.append("en-GB");
+    is(en_GB.exists(), true, "true expected (en-GB directory should exist)");
+    hunspell.addDirectory(en_GB);
+  }
+
+  var doc = evt.target.contentDocument;
+  var elem;
+  if (firstLoad) {
+    elem = doc.getElementById('en-GB');
+  } else {
+    elem = doc.getElementById('en-US');
+  }
+
+  var editor = elem.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor;
+  editor.setSpellcheckUserOverride(true);
+  var inlineSpellChecker = editor.getInlineSpellChecker(true);
+
+  onSpellCheck(elem, function () {
+    var spellchecker = inlineSpellChecker.spellChecker;
+    try {
+      var currentDictonary = spellchecker.GetCurrentDictionary();
+    } catch(e) {}
+
+    if (firstLoad) {
+      firstLoad = false;
+
+       // First time around, the element's language should be used.
+      is (currentDictonary, "en-GB", "unexpected lang " + currentDictonary + " instead of en-GB");
+
+      // Note that on second load, we load a different page, which does NOT have the trouble-causing
+      // contenteditable in it. Sadly, loading the same page with the trouble-maker in it
+      // doesn't allow the retrieval of the spell check dictionary used for the element,
+      // because the trouble-maker causes the 'global' spell check dictionary to be set to "en-GB"
+      // (since it picks the first one from the list) before we have the chance to retrieve
+      // the dictionary for the element (which happens asynchonously after the spell check has completed).
+      content.src = 'http://mochi.test:8888/tests/editor/composer/test/bug1204147_subframe2.html?firstload=false';
+    } else {
+      // Second time around, the element should default to en-US.
+      // Without the fix, the first run sets the content preference to en-GB for the whole site.
+      is (currentDictonary, "en-US", "unexpected lang " + currentDictonary + " instead of en-US");
+      content.removeEventListener('load', loadListener, false);
+
+      // Remove the fake en-GB dictionary again, since it's otherwise picked up by later tests.
+      hunspell.removeDirectory(en_GB);
+
+      // Reset the preference, so the last value we set doesn't collide with the next test.
+      SimpleTest.finish();
+    }
+  });
+}
+
+content.addEventListener('load', loadListener, false);
+
+content.src = 'http://mochi.test:8888/tests/editor/composer/test/bug1204147_subframe.html?firstload=true';
+
+</script>
+</pre>
+</body>
+</html>
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -1817,17 +1817,17 @@ public:
 
     nsCOMPtr<nsIWidget> widget = mEditor->GetWidget();
     if (!widget) {
       return NS_OK;
     }
 
     // Even if the change is caused by untrusted event, we need to dispatch
     // trusted input event since it's a fact.
-    InternalEditorInputEvent inputEvent(true, NS_EDITOR_INPUT, widget);
+    InternalEditorInputEvent inputEvent(true, eEditorInput, widget);
     inputEvent.time = static_cast<uint64_t>(PR_Now() / 1000);
     inputEvent.mIsComposing = mIsComposing;
     nsEventStatus status = nsEventStatus_eIgnore;
     nsresult rv =
       ps->HandleEventWithTarget(&inputEvent, nullptr, mTarget, &status);
     NS_ENSURE_SUCCESS(rv, NS_OK); // print the warning if error
     return NS_OK;
   }
--- a/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp
@@ -32,17 +32,26 @@ WebBrowserPersistDocumentParent::SetOnRe
 void
 WebBrowserPersistDocumentParent::ActorDestroy(ActorDestroyReason aWhy)
 {
     if (mReflection) {
         mReflection->ActorDestroy();
         mReflection = nullptr;
     }
     if (mOnReady) {
-        mOnReady->OnError(NS_ERROR_FAILURE);
+        // Bug 1202887: If this is part of a subtree destruction, then
+        // anything which could cause another actor in that subtree to
+        // be Send__delete__()ed will cause use-after-free -- such as
+        // dropping the last reference to another document's
+        // WebBrowserPersistRemoteDocument.  To avoid that, defer the
+        // callback until after the entire subtree is destroyed.
+        nsCOMPtr<nsIRunnable> errorLater = NS_NewRunnableMethodWithArg
+            <nsresult>(mOnReady, &nsIWebBrowserPersistDocumentReceiver::OnError,
+                       NS_ERROR_FAILURE);
+        NS_DispatchToCurrentThread(errorLater);
         mOnReady = nullptr;
     }
 }
 
 WebBrowserPersistDocumentParent::~WebBrowserPersistDocumentParent()
 {
     MOZ_RELEASE_ASSERT(!mReflection);
     MOZ_ASSERT(!mOnReady);
--- a/embedding/components/webbrowserpersist/WebBrowserPersistResourcesParent.cpp
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistResourcesParent.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * 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 "WebBrowserPersistResourcesParent.h"
 
+#include "nsThreadUtils.h"
+
 namespace mozilla {
 
 NS_IMPL_ISUPPORTS(WebBrowserPersistResourcesParent,
                   nsIWebBrowserPersistDocumentReceiver)
 
 WebBrowserPersistResourcesParent::WebBrowserPersistResourcesParent(
         nsIWebBrowserPersistDocument* aDocument,
         nsIWebBrowserPersistResourceVisitor* aVisitor)
@@ -24,17 +26,23 @@ WebBrowserPersistResourcesParent::WebBro
 WebBrowserPersistResourcesParent::~WebBrowserPersistResourcesParent()
 {
 }
 
 void
 WebBrowserPersistResourcesParent::ActorDestroy(ActorDestroyReason aWhy)
 {
     if (aWhy != Deletion && mVisitor) {
-        mVisitor->EndVisit(mDocument, NS_ERROR_FAILURE);
+        // See comment in WebBrowserPersistDocumentParent::ActorDestroy
+        // (or bug 1202887) for why this is deferred.
+        nsCOMPtr<nsIRunnable> errorLater = NS_NewRunnableMethodWithArgs
+            <nsCOMPtr<nsIWebBrowserPersistDocument>, nsresult>
+            (mVisitor, &nsIWebBrowserPersistResourceVisitor::EndVisit,
+             mDocument, NS_ERROR_FAILURE);
+        NS_DispatchToCurrentThread(errorLater);
     }
     mVisitor = nullptr;
 }
 
 bool
 WebBrowserPersistResourcesParent::Recv__delete__(const nsresult& aStatus)
 {
     mVisitor->EndVisit(mDocument, aStatus);
--- a/embedding/components/webbrowserpersist/WebBrowserPersistSerializeParent.cpp
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistSerializeParent.cpp
@@ -2,16 +2,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 "WebBrowserPersistSerializeParent.h"
 
 #include "nsReadableUtils.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 
 WebBrowserPersistSerializeParent::WebBrowserPersistSerializeParent(
         nsIWebBrowserPersistDocument* aDocument,
         nsIOutputStream* aStream,
         nsIWebBrowserPersistWriteCompletion* aFinish)
 : mDocument(aDocument)
@@ -69,14 +70,21 @@ WebBrowserPersistSerializeParent::Recv__
     return true;
 }
 
 void
 WebBrowserPersistSerializeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
     if (mFinish) {
         MOZ_ASSERT(aWhy != Deletion);
-        mFinish->OnFinish(mDocument, mStream, EmptyCString(), NS_ERROR_FAILURE);
+        // See comment in WebBrowserPersistDocumentParent::ActorDestroy
+        // (or bug 1202887) for why this is deferred.
+        nsCOMPtr<nsIRunnable> errorLater = NS_NewRunnableMethodWithArgs
+            <nsCOMPtr<nsIWebBrowserPersistDocument>, nsCOMPtr<nsIOutputStream>,
+             nsCString, nsresult>
+            (mFinish, &nsIWebBrowserPersistWriteCompletion::OnFinish,
+             mDocument, mStream, EmptyCString(), NS_ERROR_FAILURE);
+        NS_DispatchToCurrentThread(errorLater);
         mFinish = nullptr;
     }
 }
 
 } // namespace mozilla
--- a/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -76,38 +76,42 @@ struct nsWebBrowserPersist::WalkData
 };
 
 // Information about a DOM document
 struct nsWebBrowserPersist::DocData
 {
     nsCOMPtr<nsIURI> mBaseURI;
     nsCOMPtr<nsIWebBrowserPersistDocument> mDocument;
     nsCOMPtr<nsIURI> mFile;
-    nsCOMPtr<nsIURI> mDataPath;
-    bool mDataPathIsRelative;
-    nsCString mRelativePathToData;
     nsCString mCharset;
 };
 
 // Information about a URI
 struct nsWebBrowserPersist::URIData
 {
     bool mNeedsPersisting;
     bool mSaved;
     bool mIsSubFrame;
     bool mDataPathIsRelative;
     bool mNeedsFixup;
     nsString mFilename;
     nsString mSubFrameExt;
     nsCOMPtr<nsIURI> mFile;
     nsCOMPtr<nsIURI> mDataPath;
+    nsCOMPtr<nsIURI> mRelativeDocumentURI;
     nsCString mRelativePathToData;
     nsCString mCharset;
 
-    nsresult GetLocalURI(nsCString& aSpec);
+    nsresult GetLocalURI(nsIURI *targetBaseURI, nsCString& aSpecOut);
+};
+
+struct nsWebBrowserPersist::URIFixupData
+{
+    nsRefPtr<FlatURIMap> mFlatMap;
+    nsCOMPtr<nsIURI> mTargetBaseURI;
 };
 
 // Information about the output stream
 struct nsWebBrowserPersist::OutputData
 {
     nsCOMPtr<nsIURI> mFile;
     nsCOMPtr<nsIURI> mOriginalLocation;
     nsCOMPtr<nsIOutputStream> mStream;
@@ -638,30 +642,37 @@ nsWebBrowserPersist::SerializeNextFile()
     }
 
     mCurrentBaseURI = docData->mBaseURI;
     mCurrentCharset = docData->mCharset;
     mTargetBaseURI = docData->mFile;
 
     // Save the document, fixing it up with the new URIs as we do
 
-    if (!mFlatURIMap) {
-        nsAutoCString targetBaseSpec;
-        if (mTargetBaseURI) {
-            rv = mTargetBaseURI->GetSpec(targetBaseSpec);
-            if (NS_FAILED(rv)) {
-                SendErrorStatusChange(true, rv, nullptr, nullptr);
-                EndDownload(rv);
-                return;
-            }
+    nsAutoCString targetBaseSpec;
+    if (mTargetBaseURI) {
+        rv = mTargetBaseURI->GetSpec(targetBaseSpec);
+        if (NS_FAILED(rv)) {
+            SendErrorStatusChange(true, rv, nullptr, nullptr);
+            EndDownload(rv);
+            return;
         }
-        nsRefPtr<FlatURIMap> flatMap = new FlatURIMap(targetBaseSpec);
-        mURIMap.EnumerateRead(EnumCopyURIsToFlatMap, flatMap);
-        mFlatURIMap = flatMap.forget();
     }
+    
+    // mFlatURIMap must be rebuilt each time through SerializeNextFile, as
+    // mTargetBaseURI is used to create the relative URLs and will be different
+    // with each serialized document.
+    nsRefPtr<FlatURIMap> flatMap = new FlatURIMap(targetBaseSpec);
+    
+    URIFixupData fixupData;
+    fixupData.mFlatMap = flatMap;
+    fixupData.mTargetBaseURI = mTargetBaseURI;
+    
+    mURIMap.EnumerateRead(EnumCopyURIsToFlatMap, &fixupData);
+    mFlatURIMap = flatMap.forget();
 
     nsCOMPtr<nsIFile> localFile;
     GetLocalFileFromURI(docData->mFile, getter_AddRefs(localFile));
     if (localFile) {
         // if we're not replacing an existing file but the file
         // exists, something is wrong
         bool fileExists = false;
         rv = localFile->Exists(&fileExists);
@@ -1191,17 +1202,18 @@ nsresult nsWebBrowserPersist::GetValidUR
         *aURI = objAsURI;
         NS_ADDREF(*aURI);
         return NS_OK;
     }
 
     return NS_ERROR_FAILURE;
 }
 
-nsresult nsWebBrowserPersist::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile) const
+/* static */ nsresult
+nsWebBrowserPersist::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile)
 {
     nsresult rv;
 
     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
     if (NS_FAILED(rv))
         return rv;
 
     nsCOMPtr<nsIFile> file;
@@ -1548,16 +1560,17 @@ nsresult nsWebBrowserPersist::SaveDocume
         // 3. Store the document in a list and wait for URI persistence to finish
         // 4. After URI persistence completes save the list of documents,
         //    fixing it up as it goes out to file.
 
         mCurrentDataPathIsRelative = false;
         mCurrentDataPath = aDataPath;
         mCurrentRelativePathToData = "";
         mCurrentThingsToPersist = 0;
+        mTargetBaseURI = aFile;
 
         // Determine if the specified data path is relative to the
         // specified file, (e.g. c:\docs\htmldata is relative to
         // c:\docs\myfile.htm, but not to d:\foo\data.
 
         // Starting with the data dir work back through its parents
         // checking if one of them matches the base directory.
 
@@ -1613,36 +1626,30 @@ nsresult nsWebBrowserPersist::SaveDocume
         // filenames of saved URIs are known, the documents can be fixed up and
         // saved
 
         DocData *docData = new DocData;
         docData->mBaseURI = mCurrentBaseURI;
         docData->mCharset = mCurrentCharset;
         docData->mDocument = aDocument;
         docData->mFile = aFile;
-        docData->mRelativePathToData = mCurrentRelativePathToData;
-        docData->mDataPath = mCurrentDataPath;
-        docData->mDataPathIsRelative = mCurrentDataPathIsRelative;
         mDocList.AppendElement(docData);
 
         // Walk the DOM gathering a list of externally referenced URIs in the uri map
         nsCOMPtr<nsIWebBrowserPersistResourceVisitor> visit =
             new OnWalk(this, aFile, localDataPath);
         return aDocument->ReadResources(visit);
     }
     else
     {
         DocData *docData = new DocData;
         docData->mBaseURI = mCurrentBaseURI;
         docData->mCharset = mCurrentCharset;
         docData->mDocument = aDocument;
         docData->mFile = aFile;
-        docData->mRelativePathToData = nullptr;
-        docData->mDataPath = nullptr;
-        docData->mDataPathIsRelative = false;
         mDocList.AppendElement(docData);
 
         // Not walking DOMs, so go directly to serialization.
         SerializeNextFile();
         return NS_OK;
     }
 }
 
@@ -2508,19 +2515,20 @@ nsWebBrowserPersist::EnumCleanupUploadLi
     return PL_DHASH_NEXT;
 }
 
 /* static */ PLDHashOperator
 nsWebBrowserPersist::EnumCopyURIsToFlatMap(const nsACString &aKey,
                                           URIData *aData,
                                           void* aClosure)
 {
-    FlatURIMap* theMap = static_cast<FlatURIMap*>(aClosure);
+    URIFixupData *fixupData = static_cast<URIFixupData*>(aClosure);
+    FlatURIMap* theMap = fixupData->mFlatMap;
     nsAutoCString mapTo;
-    nsresult rv = aData->GetLocalURI(mapTo);
+    nsresult rv = aData->GetLocalURI(fixupData->mTargetBaseURI, mapTo);
     if (NS_SUCCEEDED(rv) || !mapTo.IsVoid()) {
         theMap->Add(aKey, mapTo);
     }
     return PL_DHASH_NEXT;
 }
 
 nsresult
 nsWebBrowserPersist::StoreURI(
@@ -2570,17 +2578,17 @@ nsWebBrowserPersist::StoreURI(
     {
         *aData = data;
     }
 
     return NS_OK;
 }
 
 nsresult
-nsWebBrowserPersist::URIData::GetLocalURI(nsCString& aSpecOut)
+nsWebBrowserPersist::URIData::GetLocalURI(nsIURI *targetBaseURI, nsCString& aSpecOut)
 {
     aSpecOut.SetIsVoid(true);
     if (!mNeedsFixup) {
         return NS_OK;
     }
     nsresult rv;
     nsCOMPtr<nsIURI> fileAsURI;
     if (mFile) {
@@ -2594,29 +2602,52 @@ nsWebBrowserPersist::URIData::GetLocalUR
     }
 
     // remove username/password if present
     fileAsURI->SetUserPass(EmptyCString());
 
     // reset node attribute
     // Use relative or absolute links
     if (mDataPathIsRelative) {
-        nsCOMPtr<nsIURL> url(do_QueryInterface(fileAsURI));
-        if (!url) {
-            return NS_ERROR_FAILURE;
+        bool isEqual = false;
+        if (NS_SUCCEEDED(mRelativeDocumentURI->Equals(targetBaseURI, &isEqual)) && isEqual) {
+            nsCOMPtr<nsIURL> url(do_QueryInterface(fileAsURI));
+            if (!url) {
+                return NS_ERROR_FAILURE;
+            }
+            
+            nsAutoCString filename;
+            url->GetFileName(filename);
+            
+            nsAutoCString rawPathURL(mRelativePathToData);
+            rawPathURL.Append(filename);
+            
+            nsAutoCString buf;
+            aSpecOut = NS_EscapeURL(rawPathURL, esc_FilePath, buf);
+        } else {
+            nsAutoCString rawPathURL;
+            
+            nsCOMPtr<nsIFile> dataFile;
+            rv = GetLocalFileFromURI(mFile, getter_AddRefs(dataFile));
+            NS_ENSURE_SUCCESS(rv, rv);
+            
+            nsCOMPtr<nsIFile> docFile;
+            rv = GetLocalFileFromURI(targetBaseURI, getter_AddRefs(docFile));
+            NS_ENSURE_SUCCESS(rv, rv);
+            
+            nsCOMPtr<nsIFile> parentDir;
+            rv = docFile->GetParent(getter_AddRefs(parentDir));
+            NS_ENSURE_SUCCESS(rv, rv);
+            
+            rv = dataFile->GetRelativePath(parentDir, rawPathURL);
+            NS_ENSURE_SUCCESS(rv, rv);
+            
+            nsAutoCString buf;
+            aSpecOut = NS_EscapeURL(rawPathURL, esc_FilePath, buf);
         }
-
-        nsAutoCString filename;
-        url->GetFileName(filename);
-
-        nsAutoCString rawPathURL(mRelativePathToData);
-        rawPathURL.Append(filename);
-
-        nsAutoCString buf;
-        aSpecOut = NS_EscapeURL(rawPathURL, esc_FilePath, buf);
     } else {
         fileAsURI->GetSpec(aSpecOut);
     }
     if (mIsSubFrame) {
         AppendUTF16toUTF8(mSubFrameExt, aSpecOut);
     }
 
     return NS_OK;
@@ -2790,16 +2821,17 @@ nsWebBrowserPersist::MakeAndStoreLocalFi
     data->mNeedsPersisting = aNeedsPersisting;
     data->mNeedsFixup = true;
     data->mFilename = filename;
     data->mSaved = false;
     data->mIsSubFrame = false;
     data->mDataPath = mCurrentDataPath;
     data->mDataPathIsRelative = mCurrentDataPathIsRelative;
     data->mRelativePathToData = mCurrentRelativePathToData;
+    data->mRelativeDocumentURI = mTargetBaseURI;
     data->mCharset = mCurrentCharset;
 
     if (aNeedsPersisting)
         mCurrentThingsToPersist++;
 
     mURIMap.Put(spec, data);
     if (aData)
     {
--- a/embedding/components/webbrowserpersist/nsWebBrowserPersist.h
+++ b/embedding/components/webbrowserpersist/nsWebBrowserPersist.h
@@ -74,28 +74,29 @@ private:
         const char16_t *aContentType, char16_t **aExt);
 
     struct CleanupData;
     struct DocData;
     struct OutputData;
     struct UploadData;
     struct URIData;
     struct WalkData;
+    struct URIFixupData;
 
     class OnWalk;
     class OnWrite;
     class FlatURIMap;
     friend class OnWalk;
     friend class OnWrite;
 
     nsresult SaveDocumentDeferred(mozilla::UniquePtr<WalkData>&& aData);
     void Cleanup();
     void CleanupLocalFiles();
     nsresult GetValidURIFromObject(nsISupports *aObject, nsIURI **aURI) const;
-    nsresult GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile) const;
+    static nsresult GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile);
     static nsresult AppendPathToURI(nsIURI *aURI, const nsAString & aPath);
     nsresult MakeAndStoreLocalFilenameInURIMap(
         nsIURI *aURI, bool aNeedsPersisting, URIData **aData);
     nsresult MakeOutputStream(
         nsIURI *aFile, nsIOutputStream **aOutputStream);
     nsresult MakeOutputStreamFromFile(
         nsIFile *aFile, nsIOutputStream **aOutputStream);
     nsresult MakeOutputStreamFromURI(nsIURI *aURI, nsIOutputStream  **aOutStream);
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -695,16 +695,23 @@ Layer::CalculateScissorRect(const Render
   } else {
     currentClip = aCurrentScissorRect;
   }
 
   if (!GetEffectiveClipRect()) {
     return currentClip;
   }
 
+  if (GetVisibleRegion().IsEmpty()) {
+    // When our visible region is empty, our parent may not have created the
+    // intermediate surface that we would require for correct clipping; however,
+    // this does not matter since we are invisible.
+    return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
+  }
+
   const RenderTargetIntRect clipRect =
     ViewAs<RenderTargetPixel>(*GetEffectiveClipRect(),
                               PixelCastJustification::RenderTargetIsParentLayerForRoot);
   if (clipRect.IsEmpty()) {
     // We might have a non-translation transform in the container so we can't
     // use the code path below.
     return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
   }
@@ -1185,16 +1192,22 @@ ContainerLayer::SortChildrenBy3DZOrder(n
   for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
     ContainerLayer* container = l->AsContainerLayer();
     if (container && container->GetContentFlags() & CONTENT_PRESERVE_3D) {
       toSort.AppendElement(l);
     } else {
       if (toSort.Length() > 0) {
         SortLayersBy3DZOrder(toSort);
         aArray.AppendElements(Move(toSort));
+        // XXX The move analysis gets confused here, because toSort gets moved
+        // here, and then gets used again outside of the loop. To clarify that
+        // we realize that the array is going to be empty to the move checker,
+        // we clear it again here. (This method renews toSort for the move
+        // analysis)
+        toSort.ClearAndRetainStorage();
       }
       aArray.AppendElement(l);
     }
   }
   if (toSort.Length() > 0) {
     SortLayersBy3DZOrder(toSort);
     aArray.AppendElements(Move(toSort));
   }
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -249,25 +249,25 @@ APZEventState::ProcessLongTap(const nsCO
 }
 
 void
 APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
                                  uint64_t aInputBlockId,
                                  nsEventStatus aApzResponse)
 {
-  if (aEvent.mMessage == NS_TOUCH_START && aEvent.touches.Length() > 0) {
+  if (aEvent.mMessage == eTouchStart && aEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(aEvent.touches[0]->GetTarget());
   }
 
   bool isTouchPrevented = TouchManager::gPreventMouseEvents ||
       aEvent.mFlags.mMultipleActionsPrevented;
   bool sentContentResponse = false;
   switch (aEvent.mMessage) {
-  case NS_TOUCH_START: {
+  case eTouchStart: {
     mTouchEndCancelled = false;
     if (mPendingTouchPreventedResponse) {
       // We can enter here if we get two TOUCH_STARTs in a row and didn't
       // respond to the first one. Respond to it now.
       mContentReceivedInputBlockCallback(mPendingTouchPreventedGuid,
           mPendingTouchPreventedBlockId, false);
       sentContentResponse = true;
       mPendingTouchPreventedResponse = false;
@@ -278,43 +278,43 @@ APZEventState::ProcessTouchEvent(const W
     } else {
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
       mPendingTouchPreventedBlockId = aInputBlockId;
     }
     break;
   }
 
-  case NS_TOUCH_END:
+  case eTouchEnd:
     if (isTouchPrevented) {
       mTouchEndCancelled = true;
       mEndTouchIsClick = false;
     }
     // fall through
-  case NS_TOUCH_CANCEL:
+  case eTouchCancel:
     mActiveElementManager->HandleTouchEndEvent(mEndTouchIsClick);
     // fall through
-  case NS_TOUCH_MOVE: {
+  case eTouchMove: {
     if (mPendingTouchPreventedResponse) {
       MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
     }
     sentContentResponse = SendPendingTouchPreventedResponse(isTouchPrevented);
     break;
   }
 
   default:
     NS_WARNING("Unknown touch event type");
   }
 
   if (sentContentResponse &&
         aApzResponse == nsEventStatus_eConsumeDoDefault &&
         gfxPrefs::PointerEventsEnabled()) {
     WidgetTouchEvent cancelEvent(aEvent);
-    cancelEvent.mMessage = NS_TOUCH_CANCEL;
-    cancelEvent.mFlags.mCancelable = false; // mMessage != NS_TOUCH_CANCEL;
+    cancelEvent.mMessage = eTouchCancel;
+    cancelEvent.mFlags.mCancelable = false; // mMessage != eTouchCancel;
     for (uint32_t i = 0; i < cancelEvent.touches.Length(); ++i) {
       if (mozilla::dom::Touch* touch = cancelEvent.touches[i]) {
         touch->convertToPointer = true;
       }
     }
     nsEventStatus status;
     cancelEvent.widget->DispatchEvent(&cancelEvent, status);
   }
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -9,17 +9,17 @@ namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 
 SingleTiledContentClient::SingleTiledContentClient(ClientTiledPaintedLayer* aPaintedLayer,
                                                    ClientLayerManager* aManager)
-  : TiledContentClient(aManager)
+  : TiledContentClient(aManager, "Single")
 {
   MOZ_COUNT_CTOR(SingleTiledContentClient);
 
   mTiledBuffer = new ClientSingleTiledLayerBuffer(aPaintedLayer, this, aManager);
 }
 
 void
 SingleTiledContentClient::ClearCachedResources()
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -84,17 +84,17 @@ namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 
 MultiTiledContentClient::MultiTiledContentClient(ClientTiledPaintedLayer* aPaintedLayer,
                                                  ClientLayerManager* aManager)
-  : TiledContentClient(aManager)
+  : TiledContentClient(aManager, "Multi")
 {
   MOZ_COUNT_CTOR(MultiTiledContentClient);
 
   mTiledBuffer = ClientMultiTiledLayerBuffer(aPaintedLayer, this, aManager,
                                              &mSharedFrameMetricsHelper);
   mLowPrecisionTiledBuffer = ClientMultiTiledLayerBuffer(aPaintedLayer, this, aManager,
                                                          &mSharedFrameMetricsHelper);
 
@@ -1664,17 +1664,17 @@ ClientMultiTiledLayerBuffer::Progressive
   // to the shadow layer to upload.
   return isBufferChanged;
 }
 
 void
 TiledContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
-  aStream << nsPrintfCString("TiledContentClient (0x%p)", this).get();
+  aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
 
   if (profiler_feature_active("displaylistdump")) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     Dump(aStream, pfx.get(), false);
   }
 }
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -586,18 +586,20 @@ private:
                                       nsIntRegion& aRegionToPaint,
                                       BasicTiledLayerPaintData* aPaintData,
                                       bool aIsRepeated);
 };
 
 class TiledContentClient : public CompositableClient
 {
 public:
-  TiledContentClient(ClientLayerManager* aManager)
+  TiledContentClient(ClientLayerManager* aManager,
+                     const char* aName = "")
     : CompositableClient(aManager->AsShadowForwarder())
+    , mName(aName)
   {}
 
 protected:
   ~TiledContentClient()
   {}
 
 public:
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
@@ -615,16 +617,19 @@ public:
   virtual ClientTiledLayerBuffer* GetTiledBuffer() = 0;
   virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() = 0;
 
   enum TiledBufferType {
     TILED_BUFFER,
     LOW_PRECISION_TILED_BUFFER
   };
   virtual void UpdatedBuffer(TiledBufferType aType) = 0;
+
+private:
+  const char* mName;
 };
 
 /**
  * An implementation of TiledContentClient that supports
  * multiple tiles and a low precision buffer.
  */
 class MultiTiledContentClient : public TiledContentClient
 {
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -839,17 +839,18 @@ CompositorOGL::GetShaderConfigFor(Effect
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8);
     MOZ_ASSERT_IF(source->GetTextureTarget() == LOCAL_GL_TEXTURE_RECTANGLE_ARB,
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
                   source->GetFormat() == gfx::SurfaceFormat::R5G6B5);
     config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(),
                                              source->GetFormat());
-    if (aOp == gfx::CompositionOp::OP_MULTIPLY &&
+    if ((aOp == gfx::CompositionOp::OP_MULTIPLY ||
+         aOp == gfx::CompositionOp::OP_SCREEN) &&
         !texturedEffect->mPremultiplied) {
       // We can do these blend modes just using glBlendFunc but we need the data
       // to be premultiplied first.
       config.SetPremultiply(true);
     }
     break;
   }
   }
@@ -908,17 +909,19 @@ static bool SetBlendMode(GLContext* aGL,
 
   switch (aBlendMode) {
     case gfx::CompositionOp::OP_OVER:
       MOZ_ASSERT(!aIsPremultiplied);
       srcBlend = LOCAL_GL_SRC_ALPHA;
       dstBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA;
       break;
     case gfx::CompositionOp::OP_SCREEN:
-      srcBlend = aIsPremultiplied ? LOCAL_GL_ONE : LOCAL_GL_SRC_ALPHA;
+      // If the source data was un-premultiplied we should have already
+      // asked the fragment shader to fix that.
+      srcBlend = LOCAL_GL_ONE;
       dstBlend = LOCAL_GL_ONE_MINUS_SRC_COLOR;
       break;
     case gfx::CompositionOp::OP_MULTIPLY:
       // If the source data was un-premultiplied we should have already
       // asked the fragment shader to fix that.
       srcBlend = LOCAL_GL_DST_COLOR;
       dstBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA;
       break;
--- a/gfx/src/gfxTelemetry.cpp
+++ b/gfx/src/gfxTelemetry.cpp
@@ -11,16 +11,18 @@ namespace gfx {
 const char*
 FeatureStatusToString(FeatureStatus aStatus)
 {
   switch (aStatus) {
     case FeatureStatus::Unused:
       return "unused";
     case FeatureStatus::Unavailable:
       return "unavailable";
+    case FeatureStatus::Crashed:
+      return "crashed";
     case FeatureStatus::Blocked:
       return "blocked";
     case FeatureStatus::Blacklisted:
       return "blacklisted";
     case FeatureStatus::Failed:
       return "failed";
     case FeatureStatus::Disabled:
       return "disabled";
--- a/gfx/src/gfxTelemetry.h
+++ b/gfx/src/gfxTelemetry.h
@@ -16,16 +16,19 @@ enum class FeatureStatus
 {
   // This feature has not been requested.
   Unused,
 
   // This feature is unavailable due to Safe Mode or not being included with
   // the operating system.
   Unavailable,
 
+  // This feature crashed immediately when we tried to initialize it.
+  Crashed,
+
   // This feature was blocked for reasons outside the blacklist, such as a
   // runtime test failing.
   Blocked,
 
   // This feature has been blocked by the graphics blacklist.
   Blacklisted,
 
   // This feature was attempted but failed to activate.
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -408,19 +408,21 @@ nsDeviceContext::CreateRenderingContext(
       printingSurface = mCachedPrintingSurface;
     }
 #endif
 
     RefPtr<gfx::DrawTarget> dt =
       gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(printingSurface,
                                                              gfx::IntSize(mWidth, mHeight));
 
+    // This can legitimately happen - CreateDrawTargetForSurface will fail
+    // to create a draw target if the size is too large, for instance.
     if (!dt) {
-        gfxCriticalError() << "Failed to create draw target in device context sized " << mWidth << "x" << mHeight << " and pointers " << hexa(mPrintingSurface) << " and " << hexa(printingSurface);
-        MOZ_CRASH("Cannot CreateDrawTargetForSurface");
+        gfxCriticalNote << "Failed to create draw target in device context sized " << mWidth << "x" << mHeight << " and pointers " << hexa(mPrintingSurface) << " and " << hexa(printingSurface);
+        return nullptr;
     }
 
 #ifdef XP_MACOSX
     dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
 #endif
     dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);