Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 14 Mar 2017 17:18:59 -0700
changeset 347607 8dd496fd015a2b6e99573070279d9d1593836ea9
parent 347582 cef93bf5a0a15ab7a2dc96d0cee90a599a4eae76 (current diff)
parent 347606 8afe7f683eaa6e449c2399e994e78932f20d5e0a (diff)
child 347624 041b82938c1f0fec764208acf000214a9ca2adb2
child 347699 f25c2d0458548e17f44ee8ae0bb041a0ed7cbe45
push id31500
push userkwierso@gmail.com
push dateWed, 15 Mar 2017 00:19:11 +0000
treeherdermozilla-central@8dd496fd015a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly mac
8dd496fd015a / 55.0a1 / 20170315030215 / files
nightly win32
8dd496fd015a / 55.0a1 / 20170315030215 / files
nightly win64
8dd496fd015a / 55.0a1 / 20170315030215 / files
nightly linux32
nightly linux64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge MozReview-Commit-ID: KWDF34vWlBx
ipc/glue/PSendStream.ipdl
ipc/glue/SendStream.h
ipc/glue/SendStreamAlloc.h
ipc/glue/SendStreamChild.cpp
ipc/glue/SendStreamParent.cpp
layout/generic/crashtests/crashtests.list
layout/tables/crashtests/crashtests.list
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -150,19 +150,23 @@ XPCOMUtils.defineLazyGetter(this, "PageM
 });
 
 XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
   try {
     // Hide all notifications while the URL is being edited and the address bar
     // has focus, including the virtual focus in the results popup.
+    // We also have to hide notifications explicitly when the window is
+    // minimized because of the effects of the "noautohide" attribute on Linux.
+    // This can be removed once bug 545265 and bug 1320361 are fixed.
     let shouldSuppress = () => {
-      return gURLBar.getAttribute("pageproxystate") != "valid" &&
-             gURLBar.focused;
+      return window.windowState == window.STATE_MINIMIZED ||
+             (gURLBar.getAttribute("pageproxystate") != "valid" &&
+             gURLBar.focused);
     };
     return new tmp.PopupNotifications(gBrowser,
                                       document.getElementById("notification-popup"),
                                       document.getElementById("notification-popup-box"),
                                       { shouldSuppress });
   } catch (ex) {
     Cu.reportError(ex);
     return null;
@@ -1495,16 +1499,25 @@ var gBrowserInit = {
     gBrowserThumbnails.init();
 
     gMenuButtonBadgeManager.init();
 
     gMenuButtonUpdateBadge.init();
 
     gExtensionsNotifications.init();
 
+    let wasMinimized = window.windowState == window.STATE_MINIMIZED;
+    window.addEventListener("sizemodechange", () => {
+      let isMinimized = window.windowState == window.STATE_MINIMIZED;
+      if (wasMinimized != isMinimized) {
+        wasMinimized = isMinimized;
+        UpdatePopupNotificationsVisibility();
+      }
+    });
+
     window.addEventListener("mousemove", MousePosTracker);
     window.addEventListener("dragover", MousePosTracker);
 
     gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
     gNavToolbox.addEventListener("customizationchange", CustomizationHandler);
     gNavToolbox.addEventListener("customizationending", CustomizationHandler);
 
     // End startup crash tracking after a delay to catch crashes while restoring
--- a/browser/config/mozconfigs/macosx64/debug-static-analysis
+++ b/browser/config/mozconfigs/macosx64/debug-static-analysis
@@ -1,32 +1,12 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
-if test `uname -s` = Darwin; then
-  # The toolchain installed on our OSX 10.7 build machines is too old to support
-  # MachO LC_DATA_IN_CODE load command, which newer LLVM generates, so we need to
-  # use a newer toolchain that we build.
-  #
-  # Unfortunately setting $PATH is not enough, because the build system hardcodes
-  # the default values for some of the build tools, which we also need to
-  # override below.  The default value for host ar and host ranlib is also
-  # hardcoded so we need to override those separately.
-  CCTOOLS_DIR="$topsrcdir/cctools/bin"
-  export PATH="$CCTOOLS_DIR:$PATH"
-  export AR="$CCTOOLS_DIR/ar"
-  export HOST_AR="$CCTOOLS_DIR/ar"
-  export RANLIB="$CCTOOLS_DIR/ranlib"
-  export HOST_RANLIB="$CCTOOLS_DIR/ranlib"
-  export LIPO="$CCTOOLS_DIR/lipo"
-  export OTOOL="$CCTOOLS_DIR/otool"
-  export STRIP="$CCTOOLS_DIR/strip"
-fi
-
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-debug
 ac_add_options --enable-dmd
 
 ac_add_options --enable-clang-plugin
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -4,16 +4,17 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <!DOCTYPE html>
 
 <html xmlns="http://www.w3.org/1999/xhtml" dir="">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 
   <link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
+  <link rel="stylesheet" href="chrome://devtools/content/shared/widgets/color-widget.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/inspector.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/rules.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/computed.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/fonts.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/boxmodel.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/layout.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/animationinspector.css"/>
   <link rel="stylesheet" href="resource://devtools/client/shared/components/sidebar-toggle.css"/>
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -103,17 +103,17 @@ TextPropertyEditor.prototype = {
    * Create the property editor's DOM.
    */
   _create: function () {
     this.element = this.doc.createElementNS(HTML_NS, "li");
     this.element.classList.add("ruleview-property");
     this.element._textPropertyEditor = this;
 
     this.container = createChild(this.element, "div", {
-      class: "ruleview-propertycontainer"
+      class: "ruleview-propertycontainer inline-tooltip-container"
     });
 
     // The enable checkbox will disable or enable the rule.
     this.enable = createChild(this.container, "div", {
       class: "ruleview-enableproperty theme-checkbox",
       tabindex: "-1"
     });
 
--- a/devtools/client/shared/widgets/ColorWidget.js
+++ b/devtools/client/shared/widgets/ColorWidget.js
@@ -35,112 +35,28 @@ const XHTML_NS = "http://www.w3.org/1999
  * visible will allow the color widget to correctly initialize its various parts.
  *
  * Fires the following events:
  * - changed : When the user changes the current color
  */
 function ColorWidget(parentEl, rgb) {
   EventEmitter.decorate(this);
 
-  this.element = parentEl.ownerDocument.createElementNS(XHTML_NS, "div");
   this.parentEl = parentEl;
 
-  this.element.className = "colorwidget-container";
-  this.element.innerHTML = `
-    <div class="colorwidget-top">
-      <div class="colorwidget-fill"></div>
-      <div class="colorwidget-top-inner">
-        <div class="colorwidget-color colorwidget-box">
-          <div class="colorwidget-sat">
-            <div class="colorwidget-val">
-              <div class="colorwidget-dragger"></div>
-            </div>
-          </div>
-        </div>
-        <div class="colorwidget-hue colorwidget-box">
-          <div class="colorwidget-slider colorwidget-slider-control"></div>
-        </div>
-      </div>
-    </div>
-    <div class="colorwidget-alpha colorwidget-checker colorwidget-box">
-      <div class="colorwidget-alpha-inner">
-        <div class="colorwidget-alpha-handle colorwidget-slider-control"></div>
-      </div>
-    </div>
-    <div class="colorwidget-value">
-      <select class="colorwidget-select">
-        <option value="hex">Hex</option>
-        <option value="rgba">RGBA</option>
-        <option value="hsla">HSLA</option>
-      </select>
-      <div class="colorwidget-hex">
-        <input class="colorwidget-hex-input"/>
-      </div>
-      <div class="colorwidget-rgba colorwidget-hidden">
-        <input class="colorwidget-rgba-r" data-id="r" />
-        <input class="colorwidget-rgba-g" data-id="g" />
-        <input class="colorwidget-rgba-b" data-id="b" />
-        <input class="colorwidget-rgba-a" data-id="a" />
-      </div>
-      <div class="colorwidget-hsla colorwidget-hidden">
-        <input class="colorwidget-hsla-h" data-id="h" />
-        <input class="colorwidget-hsla-s" data-id="s" />
-        <input class="colorwidget-hsla-l" data-id="l" />
-        <input class="colorwidget-hsla-a" data-id="a" />
-      </div>
-    </div>
-  `;
-
+  this.onAlphaSliderMove = this.onAlphaSliderMove.bind(this);
+  this.onElementClick = this.onElementClick.bind(this);
+  this.onDraggerMove = this.onDraggerMove.bind(this);
+  this.onHexInputChange = this.onHexInputChange.bind(this);
+  this.onHslaInputChange = this.onHslaInputChange.bind(this);
+  this.onRgbaInputChange = this.onRgbaInputChange.bind(this);
   this.onSelectValueChange = this.onSelectValueChange.bind(this);
-  this.onHexInputChange = this.onHexInputChange.bind(this);
-  this.onRgbaInputChange = this.onRgbaInputChange.bind(this);
-  this.onHslaInputChange = this.onHslaInputChange.bind(this);
-
-  this.onElementClick = this.onElementClick.bind(this);
-  this.element.addEventListener("click", this.onElementClick);
-
-  this.parentEl.appendChild(this.element);
-
-  this.slider = this.element.querySelector(".colorwidget-hue");
-  this.slideHelper = this.element.querySelector(".colorwidget-slider");
-  ColorWidget.draggable(this.slider, this.onSliderMove.bind(this));
-
-  this.dragger = this.element.querySelector(".colorwidget-color");
-  this.dragHelper = this.element.querySelector(".colorwidget-dragger");
-  ColorWidget.draggable(this.dragger, this.onDraggerMove.bind(this));
-
-  this.alphaSlider = this.element.querySelector(".colorwidget-alpha");
-  this.alphaSliderInner = this.element.querySelector(".colorwidget-alpha-inner");
-  this.alphaSliderHelper = this.element.querySelector(".colorwidget-alpha-handle");
-  ColorWidget.draggable(this.alphaSliderInner, this.onAlphaSliderMove.bind(this));
+  this.onSliderMove = this.onSliderMove.bind(this);
 
-  this.colorSelect = this.element.querySelector(".colorwidget-select");
-  this.colorSelect.addEventListener("change", this.onSelectValueChange);
-
-  this.hexValue = this.element.querySelector(".colorwidget-hex");
-  this.hexValueInput = this.element.querySelector(".colorwidget-hex-input");
-  this.hexValueInput.addEventListener("input", this.onHexInputChange);
-
-  this.rgbaValue = this.element.querySelector(".colorwidget-rgba");
-  this.rgbaValueInputs = {
-    r: this.element.querySelector(".colorwidget-rgba-r"),
-    g: this.element.querySelector(".colorwidget-rgba-g"),
-    b: this.element.querySelector(".colorwidget-rgba-b"),
-    a: this.element.querySelector(".colorwidget-rgba-a"),
-  };
-  this.rgbaValue.addEventListener("input", this.onRgbaInputChange);
-
-  this.hslaValue = this.element.querySelector(".colorwidget-hsla");
-  this.hslaValueInputs = {
-    h: this.element.querySelector(".colorwidget-hsla-h"),
-    s: this.element.querySelector(".colorwidget-hsla-s"),
-    l: this.element.querySelector(".colorwidget-hsla-l"),
-    a: this.element.querySelector(".colorwidget-hsla-a"),
-  };
-  this.hslaValue.addEventListener("input", this.onHslaInputChange);
+  this.initializeColorWidget();
 
   if (rgb) {
     this.rgb = rgb;
     this.updateUI();
   }
 }
 
 module.exports.ColorWidget = ColorWidget;
@@ -286,17 +202,111 @@ ColorWidget.prototype = {
   },
 
   get rgbCssString() {
     let rgb = this.rgb;
     return "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " +
       rgb[3] + ")";
   },
 
+  initializeColorWidget: function () {
+    this.parentEl.innerHTML = "";
+    this.element = this.parentEl.ownerDocument.createElementNS(XHTML_NS, "div");
+
+    this.element.className = "colorwidget-container";
+    this.element.innerHTML = `
+      <div class="colorwidget-top">
+        <div class="colorwidget-fill"></div>
+        <div class="colorwidget-top-inner">
+          <div class="colorwidget-color colorwidget-box">
+            <div class="colorwidget-sat">
+              <div class="colorwidget-val">
+                <div class="colorwidget-dragger"></div>
+              </div>
+            </div>
+          </div>
+          <div class="colorwidget-hue colorwidget-box">
+            <div class="colorwidget-slider colorwidget-slider-control"></div>
+          </div>
+        </div>
+      </div>
+      <div class="colorwidget-alpha colorwidget-checker colorwidget-box">
+        <div class="colorwidget-alpha-inner">
+          <div class="colorwidget-alpha-handle colorwidget-slider-control"></div>
+        </div>
+      </div>
+      <div class="colorwidget-value">
+        <select class="colorwidget-select">
+          <option value="hex">Hex</option>
+          <option value="rgba">RGBA</option>
+          <option value="hsla">HSLA</option>
+        </select>
+        <div class="colorwidget-hex">
+          <input class="colorwidget-hex-input"/>
+        </div>
+        <div class="colorwidget-rgba colorwidget-hidden">
+          <input class="colorwidget-rgba-r" data-id="r" />
+          <input class="colorwidget-rgba-g" data-id="g" />
+          <input class="colorwidget-rgba-b" data-id="b" />
+          <input class="colorwidget-rgba-a" data-id="a" />
+        </div>
+        <div class="colorwidget-hsla colorwidget-hidden">
+          <input class="colorwidget-hsla-h" data-id="h" />
+          <input class="colorwidget-hsla-s" data-id="s" />
+          <input class="colorwidget-hsla-l" data-id="l" />
+          <input class="colorwidget-hsla-a" data-id="a" />
+        </div>
+      </div>
+    `;
+
+    this.element.addEventListener("click", this.onElementClick);
+
+    this.parentEl.appendChild(this.element);
+
+    this.slider = this.element.querySelector(".colorwidget-hue");
+    this.slideHelper = this.element.querySelector(".colorwidget-slider");
+    ColorWidget.draggable(this.slider, this.onSliderMove);
+
+    this.dragger = this.element.querySelector(".colorwidget-color");
+    this.dragHelper = this.element.querySelector(".colorwidget-dragger");
+    ColorWidget.draggable(this.dragger, this.onDraggerMove);
+
+    this.alphaSlider = this.element.querySelector(".colorwidget-alpha");
+    this.alphaSliderInner = this.element.querySelector(".colorwidget-alpha-inner");
+    this.alphaSliderHelper = this.element.querySelector(".colorwidget-alpha-handle");
+    ColorWidget.draggable(this.alphaSliderInner, this.onAlphaSliderMove);
+
+    this.colorSelect = this.element.querySelector(".colorwidget-select");
+    this.colorSelect.addEventListener("change", this.onSelectValueChange);
+
+    this.hexValue = this.element.querySelector(".colorwidget-hex");
+    this.hexValueInput = this.element.querySelector(".colorwidget-hex-input");
+    this.hexValueInput.addEventListener("input", this.onHexInputChange);
+
+    this.rgbaValue = this.element.querySelector(".colorwidget-rgba");
+    this.rgbaValueInputs = {
+      r: this.element.querySelector(".colorwidget-rgba-r"),
+      g: this.element.querySelector(".colorwidget-rgba-g"),
+      b: this.element.querySelector(".colorwidget-rgba-b"),
+      a: this.element.querySelector(".colorwidget-rgba-a"),
+    };
+    this.rgbaValue.addEventListener("input", this.onRgbaInputChange);
+
+    this.hslaValue = this.element.querySelector(".colorwidget-hsla");
+    this.hslaValueInputs = {
+      h: this.element.querySelector(".colorwidget-hsla-h"),
+      s: this.element.querySelector(".colorwidget-hsla-s"),
+      l: this.element.querySelector(".colorwidget-hsla-l"),
+      a: this.element.querySelector(".colorwidget-hsla-a"),
+    };
+    this.hslaValue.addEventListener("input", this.onHslaInputChange);
+  },
+
   show: function () {
+    this.initializeColorWidget();
     this.element.classList.add("colorwidget-show");
 
     this.slideHeight = this.slider.offsetHeight;
     this.dragWidth = this.dragger.offsetWidth;
     this.dragHeight = this.dragger.offsetHeight;
     this.dragHelperHeight = this.dragHelper.offsetHeight;
     this.slideHelperHeight = this.slideHelper.offsetHeight;
     this.alphaSliderWidth = this.alphaSliderInner.offsetWidth;
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/InlineTooltip.js
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const EventEmitter = require("devtools/shared/event-emitter");
+
+/**
+ * The InlineTooltip can display widgets for the CSS Rules view in an
+ * inline container.
+ *
+ * @param {Document} doc
+ *        The toolbox document to attach the InlineTooltip container.
+ */
+function InlineTooltip(doc) {
+  EventEmitter.decorate(this);
+
+  this.doc = doc;
+
+  this.panel = this.doc.createElement("div");
+
+  this.topWindow = this._getTopWindow();
+}
+
+InlineTooltip.prototype = {
+  /**
+   * Show the tooltip. It might be wise to append some content first if you
+   * don't want the tooltip to be empty.
+   *
+   * @param {Node} anchor
+   *        Which node below which the tooltip should be shown.
+   */
+  show(anchor) {
+    anchor.parentNode.insertBefore(this.panel, anchor.nextSibling);
+
+    this.emit("shown");
+  },
+
+  /**
+   * Hide the current tooltip.
+   */
+  hide() {
+    if (!this.isVisible()) {
+      return;
+    }
+
+    this.panel.parentNode.remove(this.panel);
+
+    this.emit("hidden");
+  },
+
+  /**
+   * Check if the tooltip is currently displayed.
+   *
+   * @return {Boolean} true if the tooltip is visible
+   */
+  isVisible() {
+    return typeof this.panel.parentNode !== "undefined" && this.panel.parentNode !== null;
+  },
+
+  /**
+   * Clears the HTML content of the tooltip panel
+   */
+  clear() {
+    this.panel.innerHTML = "";
+  },
+
+  /**
+   * Set the content of this tooltip. Will first clear the tooltip and then
+   * append the new content element.
+   *
+   * @param {DOMNode} content
+   *        A node that can be appended in the tooltip
+   */
+  setContent(content) {
+    this.clear();
+
+    this.panel.appendChild(content);
+  },
+
+  get content() {
+    return this.panel.firstChild;
+  },
+
+  _getTopWindow: function () {
+    return this.doc.defaultView;
+  },
+
+  destroy() {
+    this.hide();
+    this.doc = null;
+    this.panel = null;
+  },
+};
+
+module.exports = InlineTooltip;
--- a/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
@@ -2,38 +2,52 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+const InlineTooltip = require("devtools/client/shared/widgets/tooltip/InlineTooltip");
+
+const INLINE_TOOLTIP_CLASS = "inline-tooltip-container";
 
 /**
  * Base class for all (color, gradient, ...)-swatch based value editors inside
  * tooltips
  *
  * @param {Document} document
  *        The document to attach the SwatchBasedEditorTooltip. This is either the toolbox
  *        document if the tooltip is a popup tooltip or the panel's document if it is an
  *        inline editor.
+ * @param {String} stylesheet
+ *        The stylesheet to be used for the HTMLTooltip.
+ * @param {Boolean} useInline
+ *        A boolean flag representing whether or not the InlineTooltip should be used.
  */
-function SwatchBasedEditorTooltip(document, stylesheet) {
+function SwatchBasedEditorTooltip(document, stylesheet, useInline) {
   EventEmitter.decorate(this);
+
+  this.useInline = useInline;
+
   // Creating a tooltip instance
-  // This one will consume outside clicks as it makes more sense to let the user
-  // close the tooltip by clicking out
-  // It will also close on <escape> and <enter>
-  this.tooltip = new HTMLTooltip(document, {
-    type: "arrow",
-    consumeOutsideClicks: true,
-    useXulWrapper: true,
-    stylesheet
-  });
+  if (useInline) {
+    this.tooltip = new InlineTooltip(document);
+  } else {
+    // This one will consume outside clicks as it makes more sense to let the user
+    // close the tooltip by clicking out
+    // It will also close on <escape> and <enter>
+    this.tooltip = new HTMLTooltip(document, {
+      type: "arrow",
+      consumeOutsideClicks: true,
+      useXulWrapper: true,
+      stylesheet
+    });
+  }
 
   // By default, swatch-based editor tooltips revert value change on <esc> and
   // commit value change on <enter>
   this.shortcuts = new KeyShortcuts({
     window: this.tooltip.topWindow
   });
   this.shortcuts.on("Escape", (name, event) => {
     if (!this.tooltip.isVisible()) {
@@ -68,19 +82,23 @@ function SwatchBasedEditorTooltip(docume
 SwatchBasedEditorTooltip.prototype = {
   /**
    * Show the editor tooltip for the currently active swatch.
    *
    * @return {Promise} a promise that resolves once the editor tooltip is displayed, or
    *         immediately if there is no currently active swatch.
    */
   show: function () {
-    if (this.activeSwatch) {
+    let tooltipAnchor = this.useInline ?
+      this.activeSwatch.closest(`.${INLINE_TOOLTIP_CLASS}`) :
+      this.activeSwatch;
+
+    if (tooltipAnchor) {
       let onShown = this.tooltip.once("shown");
-      this.tooltip.show(this.activeSwatch, "topcenter bottomleft");
+      this.tooltip.show(tooltipAnchor, "topcenter bottomleft");
 
       // When the tooltip is closed by clicking outside the panel we want to
       // commit any changes.
       this.tooltip.once("hidden", () => {
         if (!this._reverted && !this.eyedropperOpen) {
           this.commit();
         }
         this._reverted = false;
--- a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const {Task} = require("devtools/shared/task");
 const {colorUtils} = require("devtools/shared/css/color");
+const {ColorWidget} = require("devtools/client/shared/widgets/ColorWidget");
 const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
 const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip");
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 const Heritage = require("sdk/core/heritage");
 
 const colorWidgetPref = "devtools.inspector.colorWidget.enabled";
@@ -33,19 +34,20 @@ const XHTML_NS = "http://www.w3.org/1999
  *        The inspector panel, needed for the eyedropper.
  * @param {Function} supportsCssColor4ColorFunction
  *        A function for checking the supporting of css-color-4 color function.
  */
 function SwatchColorPickerTooltip(document,
                                   inspector,
                                   {supportsCssColor4ColorFunction}) {
   let stylesheet = NEW_COLOR_WIDGET ?
-    "chrome://devtools/content/shared/widgets/color-widget.css" :
+    null :
     "chrome://devtools/content/shared/widgets/spectrum.css";
-  SwatchBasedEditorTooltip.call(this, document, stylesheet);
+  let tooltipDocument = NEW_COLOR_WIDGET ? inspector.panelDoc : document;
+  SwatchBasedEditorTooltip.call(this, tooltipDocument, stylesheet, NEW_COLOR_WIDGET);
 
   this.inspector = inspector;
 
   // Creating a spectrum instance. this.spectrum will always be a promise that
   // resolves to the spectrum instance
   this.spectrum = this.setColorPickerContent([0, 0, 0, 1]);
   this._onSpectrumColorChange = this._onSpectrumColorChange.bind(this);
   this._openEyeDropper = this._openEyeDropper.bind(this);
@@ -57,54 +59,58 @@ SwatchColorPickerTooltip.prototype = Her
    * Fill the tooltip with a new instance of the spectrum color picker widget
    * initialized with the given color, and return the instance of spectrum
    */
   setColorPickerContent: function (color) {
     let { doc } = this.tooltip;
 
     let container = doc.createElementNS(XHTML_NS, "div");
     container.id = "spectrum-tooltip";
-    let spectrumNode = doc.createElementNS(XHTML_NS, "div");
-    spectrumNode.id = "spectrum";
-    container.appendChild(spectrumNode);
+
+    let widget;
+    let node = doc.createElementNS(XHTML_NS, "div");
+
+    if (NEW_COLOR_WIDGET) {
+      node.id = "colorwidget";
+      container.appendChild(node);
+      widget = new ColorWidget(node, color);
+    } else {
+      node.id = "spectrum";
+      container.appendChild(node);
+      widget = new Spectrum(node, color);
+    }
+
     let eyedropper = doc.createElementNS(XHTML_NS, "button");
     eyedropper.id = "eyedropper-button";
     eyedropper.className = "devtools-button";
     /* pointerEvents for eyedropper has to be set auto to display tooltip when
      * eyedropper is disabled in non-HTML documents.
      */
     eyedropper.style.pointerEvents = "auto";
     container.appendChild(eyedropper);
 
-    let spectrum;
-    if (NEW_COLOR_WIDGET) {
-      this.tooltip.setContent(container, { width: 218, height: 271 });
-      const {ColorWidget} = require("devtools/client/shared/widgets/ColorWidget");
-      spectrum = new ColorWidget(spectrumNode, color);
-    } else {
-      this.tooltip.setContent(container, { width: 218, height: 224 });
-      spectrum = new Spectrum(spectrumNode, color);
-    }
+    this.tooltip.setContent(container, { width: 218, height: 224 });
 
-    // Wait for the tooltip to be shown before calling spectrum.show
+    // Wait for the tooltip to be shown before calling widget.show
     // as it expect to be visible in order to compute DOM element sizes.
     this.tooltip.once("shown", () => {
-      spectrum.show();
+      widget.show();
     });
 
-    return spectrum;
+    return widget;
   },
 
   /**
    * Overriding the SwatchBasedEditorTooltip.show function to set spectrum's
    * color.
    */
   show: Task.async(function* () {
     // Call then parent class' show function
     yield SwatchBasedEditorTooltip.prototype.show.call(this);
+
     // Then set spectrum's color and listen to color changes to preview them
     if (this.activeSwatch) {
       this.currentSwatchColor = this.activeSwatch.nextSibling;
       this._originalColor = this.currentSwatchColor.textContent;
       let color = this.activeSwatch.style.backgroundColor;
       this.spectrum.off("changed", this._onSpectrumColorChange);
       this.spectrum.rgb = this._colorToRgba(color);
       this.spectrum.on("changed", this._onSpectrumColorChange);
--- a/devtools/client/shared/widgets/tooltip/moz.build
+++ b/devtools/client/shared/widgets/tooltip/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'CssDocsTooltip.js',
     'EventTooltipHelper.js',
     'HTMLTooltip.js',
     'ImageTooltipHelper.js',
+    'InlineTooltip.js',
     'SwatchBasedEditorTooltip.js',
     'SwatchColorPickerTooltip.js',
     'SwatchCubicBezierTooltip.js',
     'SwatchFilterTooltip.js',
     'Tooltip.js',
     'TooltipToggle.js',
     'VariableContentHelper.js',
 )
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -1,25 +1,28 @@
 /* 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/. */
 
 /* CSS Variables specific to this panel that aren't defined by the themes */
 .theme-light {
   --rule-highlight-background-color: #ffee99;
+  --rule-overridden-item-border-color: var(--theme-content-color3);
 }
 
 .theme-dark {
   --rule-highlight-background-color: #594724;
+  --rule-overridden-item-border-color: var(--theme-content-color1);
 }
 
 .theme-firebug {
   --rule-highlight-background-color: #ffee99;
   --rule-property-name: darkgreen;
   --rule-property-value: darkblue;
+  --rule-overridden-item-border-color: var(--theme-content-color2);
 }
 
 /* Rule View Tabpanel */
 
 #sidebar-panel-ruleview {
   margin: 0;
   display: flex;
   flex-direction: column;
@@ -423,29 +426,29 @@
 }
 
 .ruleview-overridden-item::before {
   position: absolute;
   left: -15px;
   top: 0px;
   content: '';
   display: block;
-  border-left: 1px solid var(--theme-highlight-gray);
-  height: 0.7em;
-  border-bottom: 1px solid var(--theme-highlight-gray);
+  border-left: 0.5px solid var(--rule-overridden-item-border-color);
+  height: 0.8em;
+  border-bottom: 0.5px solid var(--rule-overridden-item-border-color);
   width: 10px;
 }
 
 .ruleview-overridden-item::after {
   position: absolute;
   left: -15px;
   bottom: -7px;
   content: '';
   display: block;
-  border-left: 1px solid var(--theme-highlight-gray);
+  border-left: 0.5px solid var(--rule-overridden-item-border-color);
   height: 100%;
 }
 
 .ruleview-overridden-item:last-child:after {
   display: none;
 }
 
 .ruleview-grid,
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -983,23 +983,16 @@ WriteFormData(JSStructuredCloneWriter* a
         }
 
         closure->mHolder->BlobImpls().AppendElement(blobImpl);
         return true;
       }
 
       if (aValue.IsDirectory()) {
         Directory* directory = aValue.GetAsDirectory();
-
-        if (closure->mHolder->CloneScope() !=
-              StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread &&
-            !directory->ClonableToDifferentThreadOrProcess()) {
-          return false;
-        }
-
         return WriteDirectory(closure->mWriter, directory);
       }
 
       size_t charSize = sizeof(nsString::char_type);
       if (!JS_WriteUint32Pair(closure->mWriter, 0,
                               aValue.GetAsUSVString().Length()) ||
           !JS_WriteBytes(closure->mWriter, aValue.GetAsUSVString().get(),
                          aValue.GetAsUSVString().Length() * charSize)) {
@@ -1109,21 +1102,16 @@ StructuredCloneHolder::CustomWriteHandle
       return WriteBlob(aWriter, blob, this);
     }
   }
 
   // See if this is a Directory object.
   {
     Directory* directory = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory, aObj, directory))) {
-      if (mStructuredCloneScope != StructuredCloneScope::SameProcessSameThread &&
-          !directory->ClonableToDifferentThreadOrProcess()) {
-        return false;
-      }
-
       return WriteDirectory(aWriter, directory);
     }
   }
 
   // See if this is a FileList object.
   {
     FileList* fileList = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
--- a/dom/cache/AutoUtils.cpp
+++ b/dom/cache/AutoUtils.cpp
@@ -33,17 +33,17 @@ enum CleanupAction
   Forget,
   Delete
 };
 
 void
 CleanupChild(CacheReadStream& aReadStream, CleanupAction aAction)
 {
   // fds cleaned up by mStreamCleanupList
-  // PSendStream actors cleaned up by mStreamCleanupList
+  // PChildToParentStream actors cleaned up by mStreamCleanupList
 }
 
 void
 CleanupChild(CacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
 {
   if (aReadStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
     return;
   }
--- a/dom/cache/CacheOpParent.cpp
+++ b/dom/cache/CacheOpParent.cpp
@@ -7,25 +7,23 @@
 #include "mozilla/dom/cache/CacheOpParent.h"
 
 #include "mozilla/Unused.h"
 #include "mozilla/dom/cache/AutoUtils.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
-#include "mozilla/ipc/SendStream.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::ipc::FileDescriptorSetParent;
 using mozilla::ipc::PBackgroundParent;
-using mozilla::ipc::SendStreamParent;
 
 CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager, CacheId aCacheId,
                              const CacheOpArgs& aOpArgs)
   : mIpcManager(aIpcManager)
   , mCacheId(aCacheId)
   , mNamespace(INVALID_NAMESPACE)
   , mOpArgs(aOpArgs)
 {
@@ -215,16 +213,16 @@ CacheOpParent::DeserializeCacheStream(co
   // Option 1: One of our own ReadStreams was passed back to us with a stream
   //           control actor.
   stream = ReadStream::Create(readStream);
   if (stream) {
     return stream.forget();
   }
 
   // Option 2: A stream was serialized using normal methods or passed
-  //           as a PSendStream actor.  Use the standard method for
+  //           as a PChildToParentStream actor.  Use the standard method for
   //           extracting the resulting stream.
   return DeserializeIPCStream(readStream.stream());
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/CacheStreamControlParent.cpp
+++ b/dom/cache/CacheStreamControlParent.cpp
@@ -55,18 +55,21 @@ CacheStreamControlParent::SerializeContr
 void
 CacheStreamControlParent::SerializeStream(CacheReadStream* aReadStreamOut,
                                           nsIInputStream* aStream,
                                           nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
   MOZ_DIAGNOSTIC_ASSERT(aStream);
+
   UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream(aReadStreamOut->stream()));
-  autoStream->Serialize(aStream, Manager());
+  bool ok = autoStream->Serialize(aStream, Manager());
+  MOZ_DIAGNOSTIC_ASSERT(ok);
+
   aStreamCleanupList.AppendElement(Move(autoStream));
 }
 
 void
 CacheStreamControlParent::NoteClosedAfterForget(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   RecvNoteClosed(aId);
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -1,15 +1,15 @@
 /* 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 protocol PCache;
 include protocol PCacheStreamControl;
-include protocol PSendStream;
+include protocol PChildToParentStream;
 include IPCStream;
 include ChannelInfo;
 include PBackgroundSharedTypes;
 
 using HeadersGuardEnum from "mozilla/dom/FetchIPCTypes.h";
 using ReferrerPolicy from "mozilla/dom/FetchIPCTypes.h";
 using RequestCredentials from "mozilla/dom/FetchIPCTypes.h";
 using RequestMode from "mozilla/dom/FetchIPCTypes.h";
--- a/dom/cache/PCache.ipdl
+++ b/dom/cache/PCache.ipdl
@@ -2,17 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackground;
 include protocol PBlob; // FIXME: bug 792908
 include protocol PCacheOp;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
-include protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 
 include CacheTypes;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 protocol PCache
--- a/dom/cache/PCacheOp.ipdl
+++ b/dom/cache/PCacheOp.ipdl
@@ -1,17 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
-include protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 
 include CacheTypes;
 
 using mozilla::ErrorResult from "ipc/ErrorIPCUtils.h";
 
 namespace mozilla {
 namespace dom {
 namespace cache {
--- a/dom/cache/PCacheStorage.ipdl
+++ b/dom/cache/PCacheStorage.ipdl
@@ -3,17 +3,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackground;
 include protocol PBlob; // FIXME: bug 792908
 include protocol PCache;
 include protocol PCacheOp;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
-include protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 
 include CacheTypes;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 protocol PCacheStorage
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -12,17 +12,16 @@
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PFileDescriptorSetChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
-#include "mozilla/ipc/SendStream.h"
 #include "nsCOMPtr.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "nsQueryObject.h"
 #include "nsPromiseFlatString.h"
 #include "nsStreamUtils.h"
 #include "nsString.h"
--- a/dom/cache/TypeUtils.h
+++ b/dom/cache/TypeUtils.h
@@ -15,17 +15,16 @@
 class nsIGlobalObject;
 class nsIAsyncInputStream;
 class nsIInputStream;
 
 namespace mozilla {
 
 namespace ipc {
 class PBackgroundChild;
-class SendStreamChild;
 class AutoIPCStream;
 }
 
 namespace dom {
 
 struct CacheQueryOptions;
 class InternalRequest;
 class InternalResponse;
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -75,19 +75,19 @@ InternalResponse::FromIPC(const IPCInter
   return response.forget();
 }
 
 InternalResponse::~InternalResponse()
 {
 }
 
 template void
-InternalResponse::ToIPC<PContentParent>
+InternalResponse::ToIPC<nsIContentParent>
   (IPCInternalResponse* aIPCResponse,
-   PContentParent* aManager,
+   nsIContentParent* aManager,
    UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
 template void
 InternalResponse::ToIPC<nsIContentChild>
   (IPCInternalResponse* aIPCResponse,
    nsIContentChild* aManager,
    UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
 template void
 InternalResponse::ToIPC<mozilla::ipc::PBackgroundParent>
@@ -122,17 +122,18 @@ InternalResponse::ToIPC(IPCInternalRespo
   }
 
   nsCOMPtr<nsIInputStream> body;
   int64_t bodySize;
   GetUnfilteredBody(getter_AddRefs(body), &bodySize);
 
   if (body) {
     aAutoStream.reset(new mozilla::ipc::AutoIPCStream(aIPCResponse->body()));
-    aAutoStream->Serialize(body, aManager);
+    bool ok = aAutoStream->Serialize(body, aManager);
+    MOZ_DIAGNOSTIC_ASSERT(ok);
   } else {
     aIPCResponse->body() = void_t();
   }
 
   aIPCResponse->bodySize() = bodySize;
 }
 
 already_AddRefed<InternalResponse>
--- a/dom/file/ipc/PBlob.ipdl
+++ b/dom/file/ipc/PBlob.ipdl
@@ -2,17 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackground;
 include protocol PBlobStream;
 include protocol PContent;
 include protocol PContentBridge;
 include protocol PFileDescriptorSet;
-include protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 
 include BlobTypes;
 include DOMTypes;
 include InputStreamParams;
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -234,23 +234,10 @@ Directory::GetFileSystem(ErrorResult& aR
     fs->Init(mParent);
 
     mFileSystem = fs;
   }
 
   return mFileSystem;
 }
 
-
-bool
-Directory::ClonableToDifferentThreadOrProcess() const
-{
-  // If we don't have a fileSystem we are going to create a OSFileSystem that is
-  // clonable everywhere.
-  if (!mFileSystem) {
-    return true;
-  }
-
-  return mFileSystem->ClonableToDifferentThreadOrProcess();
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/Directory.h
+++ b/dom/filesystem/Directory.h
@@ -91,19 +91,16 @@ public:
    * picker operation.
    */
   void
   SetContentFilters(const nsAString& aFilters);
 
   FileSystemBase*
   GetFileSystem(ErrorResult& aRv);
 
-  bool
-  ClonableToDifferentThreadOrProcess() const;
-
   nsIFile*
   GetInternalNsIFile() const
   {
     return mFile;
   }
 
 private:
   Directory(nsISupports* aParent,
--- a/dom/filesystem/FileSystemBase.h
+++ b/dom/filesystem/FileSystemBase.h
@@ -67,28 +67,16 @@ public:
   IsSafeFile(nsIFile* aFile) const;
 
   virtual bool
   IsSafeDirectory(Directory* aDir) const;
 
   bool
   GetRealPath(BlobImpl* aFile, nsIFile** aPath) const;
 
-  // IPC initialization
-  // See how these 2 methods are used in FileSystemTaskChildBase.
-
-  virtual bool
-  NeedToGoToMainThread() const { return false; }
-
-  virtual nsresult
-  MainThreadWork() { return NS_ERROR_FAILURE; }
-
-  virtual bool
-  ClonableToDifferentThreadOrProcess() const { return false; }
-
   // CC methods
   virtual void Unlink() {}
   virtual void Traverse(nsCycleCollectionTraversalCallback &cb) {}
 
   void
   AssertIsOnOwningThread() const;
 
 protected:
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -251,22 +251,16 @@ FileSystemTaskParentBase::~FileSystemTas
 }
 
 void
 FileSystemTaskParentBase::Start()
 {
   AssertIsOnBackgroundThread();
   mFileSystem->AssertIsOnOwningThread();
 
-  if (NeedToGoToMainThread()) {
-    DebugOnly<nsresult> rv = NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
-    return;
-  }
-
   DebugOnly<nsresult> rv = DispatchToIOThread(this);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "DispatchToIOThread failed");
 }
 
 void
 FileSystemTaskParentBase::HandleResult()
 {
   AssertIsOnBackgroundThread();
@@ -300,62 +294,22 @@ FileSystemTaskParentBase::GetRequestResu
 }
 
 void
 FileSystemTaskParentBase::SetError(const nsresult& aErrorValue)
 {
   mErrorValue = FileSystemErrorFromNsError(aErrorValue);
 }
 
-bool
-FileSystemTaskParentBase::NeedToGoToMainThread() const
-{
-  return mFileSystem->NeedToGoToMainThread();
-}
-
-nsresult
-FileSystemTaskParentBase::MainThreadWork()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return mFileSystem->MainThreadWork();
-}
-
 NS_IMETHODIMP
 FileSystemTaskParentBase::Run()
 {
-  // This method can run in 3 different threads. Here why:
-  // 1. if we are on the main-thread it's because the task must do something
-  //    here. If no errors are returned we go the step 2.
-  // 2. We can be here directly if the task doesn't have nothing to do on the
-  //    main-thread. We are are on the I/O thread and we call IOWork().
-  // 3. Both step 1 (in case of error) and step 2 end up here where return the
-  //    value back to the PBackground thread.
-  if (NS_IsMainThread()) {
-    MOZ_ASSERT(NeedToGoToMainThread());
-
-    nsresult rv = MainThreadWork();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      SetError(rv);
-
-      // Something when wrong. Let's go to the Background thread directly
-      // skipping the I/O thread step.
-      rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
-    // Next step must happen on the I/O thread.
-    rv = DispatchToIOThread(this);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    return NS_OK;
-  }
+  // This method can run in 2 different threads. Here why:
+  // 1. We are are on the I/O thread and we call IOWork().
+  // 2. After step 1, it returns back to the PBackground thread.
 
   // Run I/O thread tasks
   if (!IsOnBackgroundThread()) {
     nsresult rv = IOWork();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       SetError(rv);
     }
 
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -220,27 +220,16 @@ public:
   /*
    * After finishing the task operation, handle the task result.
    * If it is an IPC task, send back the IPC result. It runs on the PBackground
    * thread.
    */
   void
   HandleResult();
 
-  // If this task must do something on the main-thread before IOWork(), it must
-  // overwrite this method. Otherwise it returns true if the FileSystem must be
-  // initialized on the main-thread. It's called from the Background thread.
-  virtual bool
-  NeedToGoToMainThread() const;
-
-  // This method is called only if NeedToGoToMainThread() returns true.
-  // Of course, it runs on the main-thread.
-  virtual nsresult
-  MainThreadWork();
-
   bool
   HasError() const { return NS_FAILED(mErrorValue); }
 
   NS_IMETHOD
   Run() override;
 
 private:
   /*
--- a/dom/filesystem/OSFileSystem.h
+++ b/dom/filesystem/OSFileSystem.h
@@ -41,19 +41,16 @@ public:
   IsSafeFile(nsIFile* aFile) const override;
 
   virtual bool
   IsSafeDirectory(Directory* aDir) const override;
 
   virtual void
   SerializeDOMPath(nsAString& aOutput) const override;
 
-  virtual bool
-  ClonableToDifferentThreadOrProcess() const override { return true; }
-
   // CC methods
   virtual void Unlink() override;
   virtual void Traverse(nsCycleCollectionTraversalCallback &cb) override;
 
 private:
   virtual ~OSFileSystem() {}
 
   nsCOMPtr<nsISupports> mParent;
--- a/dom/flyweb/PFlyWebPublishedServer.ipdl
+++ b/dom/flyweb/PFlyWebPublishedServer.ipdl
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 protocol PContent;
-include protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 include protocol PFileDescriptorSet;
 include protocol PTransportProvider;
 include FetchTypes;
 include ChannelInfo;
 include PBackgroundSharedTypes;
 
 namespace mozilla {
 namespace dom {
--- a/dom/ipc/ContentBridgeChild.cpp
+++ b/dom/ipc/ContentBridgeChild.cpp
@@ -85,20 +85,20 @@ ContentBridgeChild::SendPBrowserConstruc
 }
 
 PFileDescriptorSetChild*
 ContentBridgeChild::SendPFileDescriptorSetConstructor(const FileDescriptor& aFD)
 {
   return PContentBridgeChild::SendPFileDescriptorSetConstructor(aFD);
 }
 
-PSendStreamChild*
-ContentBridgeChild::SendPSendStreamConstructor(PSendStreamChild* aActor)
+PChildToParentStreamChild*
+ContentBridgeChild::SendPChildToParentStreamConstructor(PChildToParentStreamChild* aActor)
 {
-  return PContentBridgeChild::SendPSendStreamConstructor(aActor);
+  return PContentBridgeChild::SendPChildToParentStreamConstructor(aActor);
 }
 
 // This implementation is identical to ContentChild::GetCPOWManager but we can't
 // move it to nsIContentChild because it calls ManagedPJavaScriptChild() which
 // only exists in PContentChild and PContentBridgeChild.
 jsipc::CPOWManager*
 ContentBridgeChild::GetCPOWManager()
 {
@@ -163,26 +163,38 @@ ContentBridgeChild::AllocPBlobChild(cons
 }
 
 bool
 ContentBridgeChild::DeallocPBlobChild(PBlobChild* aActor)
 {
   return nsIContentChild::DeallocPBlobChild(aActor);
 }
 
-PSendStreamChild*
-ContentBridgeChild::AllocPSendStreamChild()
+PChildToParentStreamChild*
+ContentBridgeChild::AllocPChildToParentStreamChild()
 {
-  return nsIContentChild::AllocPSendStreamChild();
+  return nsIContentChild::AllocPChildToParentStreamChild();
 }
 
 bool
-ContentBridgeChild::DeallocPSendStreamChild(PSendStreamChild* aActor)
+ContentBridgeChild::DeallocPChildToParentStreamChild(PChildToParentStreamChild* aActor)
+{
+  return nsIContentChild::DeallocPChildToParentStreamChild(aActor);
+}
+
+PParentToChildStreamChild*
+ContentBridgeChild::AllocPParentToChildStreamChild()
 {
-  return nsIContentChild::DeallocPSendStreamChild(aActor);
+  return nsIContentChild::AllocPParentToChildStreamChild();
+}
+
+bool
+ContentBridgeChild::DeallocPParentToChildStreamChild(PParentToChildStreamChild* aActor)
+{
+  return nsIContentChild::DeallocPParentToChildStreamChild(aActor);
 }
 
 PFileDescriptorSetChild*
 ContentBridgeChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
 {
   return nsIContentChild::AllocPFileDescriptorSetChild(aFD);
 }
 
--- a/dom/ipc/ContentBridgeChild.h
+++ b/dom/ipc/ContentBridgeChild.h
@@ -43,18 +43,18 @@ public:
                                        const IPCTabContext& aContext,
                                        const uint32_t& aChromeFlags,
                                        const ContentParentId& aCpID,
                                        const bool& aIsForBrowser) override;
 
   virtual mozilla::ipc::PFileDescriptorSetChild*
   SendPFileDescriptorSetConstructor(const mozilla::ipc::FileDescriptor&) override;
 
-  virtual mozilla::ipc::PSendStreamChild*
-  SendPSendStreamConstructor(mozilla::ipc::PSendStreamChild*) override;
+  virtual mozilla::ipc::PChildToParentStreamChild*
+  SendPChildToParentStreamConstructor(mozilla::ipc::PChildToParentStreamChild*) override;
 
   FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeChild)
 
 protected:
   virtual ~ContentBridgeChild();
 
   virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                             const IPCTabContext& aContext,
@@ -70,20 +70,26 @@ protected:
                                                           const bool& aIsForBrowser) override;
 
   virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() override;
   virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) override;
 
   virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams) override;
   virtual bool DeallocPBlobChild(PBlobChild*) override;
 
-  virtual mozilla::ipc::PSendStreamChild* AllocPSendStreamChild() override;
+  virtual mozilla::ipc::PChildToParentStreamChild*
+  AllocPChildToParentStreamChild() override;
 
   virtual bool
-  DeallocPSendStreamChild(mozilla::ipc::PSendStreamChild* aActor) override;
+  DeallocPChildToParentStreamChild(mozilla::ipc::PChildToParentStreamChild* aActor) override;
+
+  virtual PParentToChildStreamChild* AllocPParentToChildStreamChild() override;
+
+  virtual bool
+  DeallocPParentToChildStreamChild(PParentToChildStreamChild* aActor) override;
 
   virtual PFileDescriptorSetChild*
   AllocPFileDescriptorSetChild(const mozilla::ipc::FileDescriptor& aFD) override;
 
   virtual bool
   DeallocPFileDescriptorSetChild(mozilla::ipc::PFileDescriptorSetChild* aActor) override;
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeChild);
--- a/dom/ipc/ContentBridgeParent.cpp
+++ b/dom/ipc/ContentBridgeParent.cpp
@@ -105,16 +105,22 @@ ContentBridgeParent::SendPBrowserConstru
   return PContentBridgeParent::SendPBrowserConstructor(aActor,
                                                        aTabId,
                                                        aContext,
                                                        aChromeFlags,
                                                        aCpID,
                                                        aIsForBrowser);
 }
 
+PParentToChildStreamParent*
+ContentBridgeParent::SendPParentToChildStreamConstructor(PParentToChildStreamParent* aActor)
+{
+  return PContentBridgeParent::SendPParentToChildStreamConstructor(aActor);
+}
+
 PBlobParent*
 ContentBridgeParent::AllocPBlobParent(const BlobConstructorParams& aParams)
 {
   return nsIContentParent::AllocPBlobParent(aParams);
 }
 
 bool
 ContentBridgeParent::DeallocPBlobParent(PBlobParent* aActor)
@@ -182,33 +188,51 @@ ContentBridgeParent::Observe(nsISupports
 {
   if (!strcmp(aTopic, "content-child-shutdown")) {
     Close();
   }
   return NS_OK;
 }
 
 PFileDescriptorSetParent*
+ContentBridgeParent::SendPFileDescriptorSetConstructor(const FileDescriptor& aFD)
+{
+  return PContentBridgeParent::SendPFileDescriptorSetConstructor(aFD);
+}
+
+PFileDescriptorSetParent*
 ContentBridgeParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD)
 {
   return nsIContentParent::AllocPFileDescriptorSetParent(aFD);
 }
 
 bool
 ContentBridgeParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
 {
   return nsIContentParent::DeallocPFileDescriptorSetParent(aActor);
 }
 
-PSendStreamParent*
-ContentBridgeParent::AllocPSendStreamParent()
+PChildToParentStreamParent*
+ContentBridgeParent::AllocPChildToParentStreamParent()
 {
-  return nsIContentParent::AllocPSendStreamParent();
+  return nsIContentParent::AllocPChildToParentStreamParent();
 }
 
 bool
-ContentBridgeParent::DeallocPSendStreamParent(PSendStreamParent* aActor)
+ContentBridgeParent::DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor)
+{
+  return nsIContentParent::DeallocPChildToParentStreamParent(aActor);
+}
+
+PParentToChildStreamParent*
+ContentBridgeParent::AllocPParentToChildStreamParent()
 {
-  return nsIContentParent::DeallocPSendStreamParent(aActor);
+  return nsIContentParent::AllocPParentToChildStreamParent();
+}
+
+bool
+ContentBridgeParent::DeallocPParentToChildStreamParent(PParentToChildStreamParent* aActor)
+{
+  return nsIContentParent::DeallocPParentToChildStreamParent(aActor);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentBridgeParent.h
+++ b/dom/ipc/ContentBridgeParent.h
@@ -40,16 +40,19 @@ public:
   virtual PBrowserParent*
   SendPBrowserConstructor(PBrowserParent* aActor,
                           const TabId& aTabId,
                           const IPCTabContext& aContext,
                           const uint32_t& aChromeFlags,
                           const ContentParentId& aCpID,
                           const bool& aIsForBrowser) override;
 
+  virtual PFileDescriptorSetParent*
+  SendPFileDescriptorSetConstructor(const FileDescriptor&) override;
+
   FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeParent)
 
   jsipc::CPOWManager* GetCPOWManager() override;
 
   virtual ContentParentId ChildID() const override
   {
     return mChildID;
   }
@@ -58,16 +61,19 @@ public:
     return mIsForBrowser;
   }
   virtual int32_t Pid() const override
   {
     // XXX: do we need this for ContentBridgeParent?
     return -1;
   }
 
+  virtual mozilla::ipc::PParentToChildStreamParent*
+  SendPParentToChildStreamConstructor(mozilla::ipc::PParentToChildStreamParent*) override;
+
 protected:
   virtual ~ContentBridgeParent();
 
   void SetChildID(ContentParentId aId)
   {
     mChildID = aId;
   }
 
@@ -109,19 +115,26 @@ protected:
 
   virtual bool DeallocPBrowserParent(PBrowserParent*) override;
 
   virtual PBlobParent*
   AllocPBlobParent(const BlobConstructorParams& aParams) override;
 
   virtual bool DeallocPBlobParent(PBlobParent*) override;
 
-  virtual PSendStreamParent* AllocPSendStreamParent() override;
+  virtual PChildToParentStreamParent* AllocPChildToParentStreamParent() override;
+
+  virtual bool
+  DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor) override;
 
-  virtual bool DeallocPSendStreamParent(PSendStreamParent* aActor) override;
+  virtual mozilla::ipc::PParentToChildStreamParent*
+  AllocPParentToChildStreamParent() override;
+
+  virtual bool
+  DeallocPParentToChildStreamParent(mozilla::ipc::PParentToChildStreamParent* aActor) override;
 
   virtual PFileDescriptorSetParent*
   AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor&) override;
 
   virtual bool
   DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) override;
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeParent);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -44,17 +44,17 @@
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/psm/PSMContentListener.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/ProcessChild.h"
-#include "mozilla/ipc/PSendStreamChild.h"
+#include "mozilla/ipc/PChildToParentStreamChild.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/ContentProcessController.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/net/NeckoChild.h"
@@ -1708,36 +1708,48 @@ ContentChild::AllocPPrintingChild()
 }
 
 bool
 ContentChild::DeallocPPrintingChild(PPrintingChild* printing)
 {
   return true;
 }
 
-PSendStreamChild*
-ContentChild::SendPSendStreamConstructor(PSendStreamChild* aActor)
+PChildToParentStreamChild*
+ContentChild::SendPChildToParentStreamConstructor(PChildToParentStreamChild* aActor)
 {
   if (IsShuttingDown()) {
     return nullptr;
   }
 
-  return PContentChild::SendPSendStreamConstructor(aActor);
+  return PContentChild::SendPChildToParentStreamConstructor(aActor);
 }
 
-PSendStreamChild*
-ContentChild::AllocPSendStreamChild()
+PChildToParentStreamChild*
+ContentChild::AllocPChildToParentStreamChild()
 {
-  return nsIContentChild::AllocPSendStreamChild();
+  return nsIContentChild::AllocPChildToParentStreamChild();
 }
 
 bool
-ContentChild::DeallocPSendStreamChild(PSendStreamChild* aActor)
+ContentChild::DeallocPChildToParentStreamChild(PChildToParentStreamChild* aActor)
+{
+  return nsIContentChild::DeallocPChildToParentStreamChild(aActor);
+}
+
+PParentToChildStreamChild*
+ContentChild::AllocPParentToChildStreamChild()
 {
-  return nsIContentChild::DeallocPSendStreamChild(aActor);
+  return nsIContentChild::AllocPParentToChildStreamChild();
+}
+
+bool
+ContentChild::DeallocPParentToChildStreamChild(PParentToChildStreamChild* aActor)
+{
+  return nsIContentChild::DeallocPParentToChildStreamChild(aActor);
 }
 
 PScreenManagerChild*
 ContentChild::AllocPScreenManagerChild(uint32_t* aNumberOfScreens,
                                        float* aSystemDefaultScale,
                                        bool* aSuccess)
 {
   // The ContentParent should never attempt to allocate the
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -228,21 +228,24 @@ public:
   virtual PNeckoChild* AllocPNeckoChild() override;
 
   virtual bool DeallocPNeckoChild(PNeckoChild*) override;
 
   virtual PPrintingChild* AllocPPrintingChild() override;
 
   virtual bool DeallocPPrintingChild(PPrintingChild*) override;
 
-  virtual PSendStreamChild*
-  SendPSendStreamConstructor(PSendStreamChild*) override;
+  virtual PChildToParentStreamChild*
+  SendPChildToParentStreamConstructor(PChildToParentStreamChild*) override;
 
-  virtual PSendStreamChild* AllocPSendStreamChild() override;
-  virtual bool DeallocPSendStreamChild(PSendStreamChild*) override;
+  virtual PChildToParentStreamChild* AllocPChildToParentStreamChild() override;
+  virtual bool DeallocPChildToParentStreamChild(PChildToParentStreamChild*) override;
+
+  virtual PParentToChildStreamChild* AllocPParentToChildStreamChild() override;
+  virtual bool DeallocPParentToChildStreamChild(PParentToChildStreamChild*) override;
 
   virtual PScreenManagerChild*
   AllocPScreenManagerChild(uint32_t* aNumberOfScreens,
                            float* aSystemDefaultScale,
                            bool* aSuccess) override;
 
   virtual bool DeallocPScreenManagerChild(PScreenManagerChild*) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -64,17 +64,17 @@
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/embedding/printingui/PrintingParent.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
-#include "mozilla/ipc/PSendStreamParent.h"
+#include "mozilla/ipc/PChildToParentStreamParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/PAPZParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layout/RenderFrameParent.h"
@@ -3171,26 +3171,44 @@ ContentParent::GetPrintingParent()
 {
   MOZ_ASSERT(mPrintingParent);
 
   RefPtr<embedding::PrintingParent> printingParent = mPrintingParent;
   return printingParent.forget();
 }
 #endif
 
-PSendStreamParent*
-ContentParent::AllocPSendStreamParent()
-{
-  return nsIContentParent::AllocPSendStreamParent();
+PChildToParentStreamParent*
+ContentParent::AllocPChildToParentStreamParent()
+{
+  return nsIContentParent::AllocPChildToParentStreamParent();
 }
 
 bool
-ContentParent::DeallocPSendStreamParent(PSendStreamParent* aActor)
-{
-  return nsIContentParent::DeallocPSendStreamParent(aActor);
+ContentParent::DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor)
+{
+  return nsIContentParent::DeallocPChildToParentStreamParent(aActor);
+}
+
+PParentToChildStreamParent*
+ContentParent::SendPParentToChildStreamConstructor(PParentToChildStreamParent* aActor)
+{
+  return PContentParent::SendPParentToChildStreamConstructor(aActor);
+}
+
+PParentToChildStreamParent*
+ContentParent::AllocPParentToChildStreamParent()
+{
+  return nsIContentParent::AllocPParentToChildStreamParent();
+}
+
+bool
+ContentParent::DeallocPParentToChildStreamParent(PParentToChildStreamParent* aActor)
+{
+  return nsIContentParent::DeallocPParentToChildStreamParent(aActor);
 }
 
 PScreenManagerParent*
 ContentParent::AllocPScreenManagerParent(uint32_t* aNumberOfScreens,
                                          float* aSystemDefaultScale,
                                          bool* aSuccess)
 {
   return new ScreenManagerParent(aNumberOfScreens, aSystemDefaultScale, aSuccess);
@@ -3916,17 +3934,20 @@ ContentParent::RecvKeywordToURI(const ns
 
   if (NS_FAILED(fixup->KeywordToURI(aKeyword, getter_AddRefs(postData),
                                     getter_AddRefs(info)))) {
     return IPC_OK();
   }
   info->GetKeywordProviderName(*aProviderName);
 
   AutoIPCStream autoStream;
-  autoStream.Serialize(postData, this);
+  if (NS_WARN_IF(!autoStream.Serialize(postData, this))) {
+    NS_ENSURE_SUCCESS(NS_ERROR_FAILURE, IPC_FAIL_NO_REASON(this));
+  }
+
   *aPostData = autoStream.TakeOptionalValue();
 
   nsCOMPtr<nsIURI> uri;
   info->GetPreferredURI(getter_AddRefs(uri));
   SerializeURI(uri, *aURI);
   return IPC_OK();
 }
 
@@ -4106,16 +4127,22 @@ ContentParent::RecvKeygenProvideContent(
   }
 
   formProcessor->ProvideContent(NS_LITERAL_STRING("SELECT"), *aContent,
                                 *aAttribute);
   return IPC_OK();
 }
 
 PFileDescriptorSetParent*
+ContentParent::SendPFileDescriptorSetConstructor(const FileDescriptor& aFD)
+{
+  return PContentParent::SendPFileDescriptorSetConstructor(aFD);
+}
+
+PFileDescriptorSetParent*
 ContentParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD)
 {
   return nsIContentParent::AllocPFileDescriptorSetParent(aFD);
 }
 
 bool
 ContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
 {
@@ -5132,15 +5159,19 @@ ContentParent::RecvFileCreationRequest(c
     }
 
     return IPC_OK();
   }
 
   MOZ_ASSERT(blobImpl);
 
   BlobParent* blobParent = BlobParent::GetOrCreate(this, blobImpl);
+  if (NS_WARN_IF(!blobParent)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
   if (!SendFileCreationResponse(aID,
                                 FileCreationSuccessResult(blobParent, nullptr))) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   return IPC_OK();
 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -420,18 +420,29 @@ public:
 
 #if defined(NS_PRINTING)
   /**
    * @return the PrintingParent for this ContentParent.
    */
   already_AddRefed<embedding::PrintingParent> GetPrintingParent();
 #endif
 
-  virtual PSendStreamParent* AllocPSendStreamParent() override;
-  virtual bool DeallocPSendStreamParent(PSendStreamParent* aActor) override;
+  virtual PChildToParentStreamParent* AllocPChildToParentStreamParent() override;
+  virtual bool
+  DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor) override;
+
+  virtual PParentToChildStreamParent*
+  SendPParentToChildStreamConstructor(PParentToChildStreamParent*) override;
+
+  virtual PFileDescriptorSetParent*
+  SendPFileDescriptorSetConstructor(const FileDescriptor&) override;
+
+  virtual PParentToChildStreamParent* AllocPParentToChildStreamParent() override;
+  virtual bool
+  DeallocPParentToChildStreamParent(PParentToChildStreamParent* aActor) override;
 
   virtual PScreenManagerParent*
   AllocPScreenManagerParent(uint32_t* aNumberOfScreens,
                             float* aSystemDefaultScale,
                             bool* aSuccess) override;
 
   virtual bool
   DeallocPScreenManagerParent(PScreenManagerParent* aActor) override;
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 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 protocol PBlob;
-include protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 include IPCStream;
 include ProtocolTypes;
 
 using struct mozilla::void_t
   from "ipc/IPCMessageUtils.h";
 
 using struct mozilla::SerializedStructuredCloneBuffer
   from "ipc/IPCMessageUtils.h";
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -21,17 +21,18 @@ include protocol PProcessHangMonitor;
 include protocol PImageBridge;
 include protocol PMedia;
 include protocol PNecko;
 include protocol PGMPContent;
 include protocol PGMPService;
 include protocol PPluginModule;
 include protocol PGMP;
 include protocol PPrinting;
-include protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 include protocol POfflineCacheUpdate;
 include protocol PRenderFrame;
 include protocol PScreenManager;
 include protocol PSpeechSynthesis;
 include protocol PStorage;
 include protocol PTestShell;
 include protocol PJavaScript;
 include protocol PRemoteSpellcheckEngine;
@@ -281,17 +282,18 @@ nested(upto inside_cpow) sync protocol P
     manages PFileDescriptorSet;
     manages PHal;
     manages PHandlerService;
     manages PHeapSnapshotTempFileHelper;
     manages PMedia;
     manages PNecko;
     manages POfflineCacheUpdate;
     manages PPrinting;
-    manages PSendStream;
+    manages PChildToParentStream;
+    manages PParentToChildStream;
     manages PScreenManager;
     manages PSpeechSynthesis;
     manages PStorage;
     manages PTestShell;
     manages PJavaScript;
     manages PRemoteSpellcheckEngine;
     manages PWebBrowserPersistDocument;
     manages PWebrtcGlobal;
@@ -582,16 +584,18 @@ child:
      * Sending an activate message moves focus to the child.
      */
     async Activate(PBrowser aTab);
 
     async Deactivate(PBrowser aTab);
 
     async ParentActivated(PBrowser aTab, bool aActivated);
 
+    async PParentToChildStream();
+
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId)
         returns (ContentParentId cpId, bool isForBrowser, TabId tabId);
     sync BridgeToChildProcess(ContentParentId cpId)
@@ -696,17 +700,17 @@ parent:
     nested(inside_cpow) async PHal();
 
     async PHeapSnapshotTempFileHelper();
 
     async PNecko();
 
     async PPrinting();
 
-    async PSendStream();
+    async PChildToParentStream();
 
     nested(inside_sync) sync PScreenManager()
         returns (uint32_t numberOfScreens,
                  float systemDefaultScale,
                  bool success);
 
     async PSpeechSynthesis();
 
--- a/dom/ipc/PContentBridge.ipdl
+++ b/dom/ipc/PContentBridge.ipdl
@@ -4,17 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBlob;
 include protocol PBrowser;
 include protocol PContent;
 include protocol PJavaScript;
 include protocol PFileDescriptorSet;
-include protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 
 include DOMTypes;
 include JavaScriptTypes;
 include PTabContext;
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
 using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
@@ -33,26 +34,30 @@ namespace dom {
  * share PBrowsers and send messages to each other.
  */
 nested(upto inside_cpow) sync protocol PContentBridge
 {
     manages PBlob;
     manages PBrowser;
     manages PFileDescriptorSet;
     manages PJavaScript;
-    manages PSendStream;
+    manages PChildToParentStream;
+    manages PParentToChildStream;
+
+child:
+    async PParentToChildStream();
 
 parent:
     sync SyncMessage(nsString aMessage, ClonedMessageData aData,
                      CpowEntry[] aCpows, Principal aPrincipal)
       returns (StructuredCloneData[] retval);
 
     async PJavaScript();
 
-    async PSendStream();
+    async PChildToParentStream();
 
 both:
     // Both the parent and the child can construct the PBrowser.
     // See the comment in PContent::PBrowser().
     async PBrowser(TabId tabId, IPCTabContext context, uint32_t chromeFlags,
                    ContentParentId cpId, bool isForBrowser);
 
     async PBlob(BlobConstructorParams params);
--- a/dom/ipc/nsIContentChild.cpp
+++ b/dom/ipc/nsIContentChild.cpp
@@ -10,17 +10,21 @@
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
-#include "mozilla/ipc/SendStream.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
+#include "mozilla/ipc/IPCStreamDestination.h"
+#include "mozilla/ipc/IPCStreamSource.h"
+#include "mozilla/ipc/PChildToParentStreamChild.h"
+#include "mozilla/ipc/PParentToChildStreamChild.h"
 
 #include "nsPrintfCString.h"
 #include "xpcpublic.h"
 
 using namespace mozilla::ipc;
 using namespace mozilla::jsipc;
 
 namespace mozilla {
@@ -130,24 +134,37 @@ nsIContentChild::GetOrCreateActorForBlob
   MOZ_ASSERT(aImpl);
 
   BlobChild* actor = BlobChild::GetOrCreate(this, aImpl);
   NS_ENSURE_TRUE(actor, nullptr);
 
   return actor;
 }
 
-PSendStreamChild*
-nsIContentChild::AllocPSendStreamChild()
+PChildToParentStreamChild*
+nsIContentChild::AllocPChildToParentStreamChild()
 {
-  MOZ_CRASH("PSendStreamChild actors should be manually constructed!");
+  MOZ_CRASH("PChildToParentStreamChild actors should be manually constructed!");
 }
 
 bool
-nsIContentChild::DeallocPSendStreamChild(PSendStreamChild* aActor)
+nsIContentChild::DeallocPChildToParentStreamChild(PChildToParentStreamChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PParentToChildStreamChild*
+nsIContentChild::AllocPParentToChildStreamChild()
+{
+  return mozilla::ipc::AllocPParentToChildStreamChild();
+}
+
+bool
+nsIContentChild::DeallocPParentToChildStreamChild(PParentToChildStreamChild* aActor)
 {
   delete aActor;
   return true;
 }
 
 PFileDescriptorSetChild*
 nsIContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
 {
--- a/dom/ipc/nsIContentChild.h
+++ b/dom/ipc/nsIContentChild.h
@@ -24,17 +24,18 @@ class nsString;
 namespace IPC {
 class Principal;
 } // namespace IPC
 
 namespace mozilla {
 namespace ipc {
 class FileDescriptor;
 class PFileDescriptorSetChild;
-class PSendStreamChild;
+class PChildToParentStreamChild;
+class PParentToChildStreamChild;
 class Shmem;
 } // namespace ipc
 
 namespace jsipc {
 class PJavaScriptChild;
 class CpowEntry;
 } // namespace jsipc
 
@@ -69,18 +70,18 @@ public:
                           const IPCTabContext& aContext,
                           const uint32_t& aChromeFlags,
                           const ContentParentId& aCpID,
                           const bool& aIsForBrowser) = 0;
 
   virtual mozilla::ipc::PFileDescriptorSetChild*
   SendPFileDescriptorSetConstructor(const mozilla::ipc::FileDescriptor&) = 0;
 
-  virtual mozilla::ipc::PSendStreamChild*
-  SendPSendStreamConstructor(mozilla::ipc::PSendStreamChild*) = 0;
+  virtual mozilla::ipc::PChildToParentStreamChild*
+  SendPChildToParentStreamConstructor(mozilla::ipc::PChildToParentStreamChild*) = 0;
 
 protected:
   virtual jsipc::PJavaScriptChild* AllocPJavaScriptChild();
   virtual bool DeallocPJavaScriptChild(jsipc::PJavaScriptChild*);
 
   virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                             const IPCTabContext& aContext,
                                             const uint32_t& aChromeFlags,
@@ -94,19 +95,25 @@ protected:
                                                           const uint32_t& aChromeFlags,
                                                           const ContentParentId& aCpID,
                                                           const bool& aIsForBrowse);
 
   virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams);
 
   virtual bool DeallocPBlobChild(PBlobChild* aActor);
 
-  virtual mozilla::ipc::PSendStreamChild* AllocPSendStreamChild();
+  virtual mozilla::ipc::PChildToParentStreamChild* AllocPChildToParentStreamChild();
+
+  virtual bool
+  DeallocPChildToParentStreamChild(mozilla::ipc::PChildToParentStreamChild* aActor);
 
-  virtual bool DeallocPSendStreamChild(mozilla::ipc::PSendStreamChild* aActor);
+  virtual mozilla::ipc::PParentToChildStreamChild* AllocPParentToChildStreamChild();
+
+  virtual bool
+  DeallocPParentToChildStreamChild(mozilla::ipc::PParentToChildStreamChild* aActor);
 
   virtual mozilla::ipc::PFileDescriptorSetChild*
   AllocPFileDescriptorSetChild(const mozilla::ipc::FileDescriptor& aFD);
 
   virtual bool
   DeallocPFileDescriptorSetChild(mozilla::ipc::PFileDescriptorSetChild* aActor);
 
   virtual mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMsg,
--- a/dom/ipc/nsIContentParent.cpp
+++ b/dom/ipc/nsIContentParent.cpp
@@ -13,17 +13,19 @@
 #include "mozilla/dom/PTabContext.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
-#include "mozilla/ipc/SendStreamAlloc.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
+#include "mozilla/ipc/IPCStreamDestination.h"
+#include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/Unused.h"
 
 #include "nsFrameMessageManager.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsPrintfCString.h"
 #include "xpcpublic.h"
 
 using namespace mozilla::jsipc;
@@ -255,24 +257,37 @@ nsIContentParent::AllocPFileDescriptorSe
 
 bool
 nsIContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
 {
   delete static_cast<FileDescriptorSetParent*>(aActor);
   return true;
 }
 
-PSendStreamParent*
-nsIContentParent::AllocPSendStreamParent()
+PChildToParentStreamParent*
+nsIContentParent::AllocPChildToParentStreamParent()
 {
-  return mozilla::ipc::AllocPSendStreamParent();
+  return mozilla::ipc::AllocPChildToParentStreamParent();
 }
 
 bool
-nsIContentParent::DeallocPSendStreamParent(PSendStreamParent* aActor)
+nsIContentParent::DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PParentToChildStreamParent*
+nsIContentParent::AllocPParentToChildStreamParent()
+{
+  MOZ_CRASH("PParentToChildStreamChild actors should be manually constructed!");
+}
+
+bool
+nsIContentParent::DeallocPParentToChildStreamParent(PParentToChildStreamParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 mozilla::ipc::IPCResult
 nsIContentParent::RecvAsyncMessage(const nsString& aMsg,
                                    InfallibleTArray<CpowEntry>&& aCpows,
--- a/dom/ipc/nsIContentParent.h
+++ b/dom/ipc/nsIContentParent.h
@@ -5,16 +5,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_nsIContentParent_h
 #define mozilla_dom_nsIContentParent_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/PChildToParentStreamParent.h"
+#include "mozilla/ipc/PParentToChildStreamParent.h"
 
 #include "nsFrameMessageManager.h"
 #include "nsISupports.h"
 #include "mozilla/dom/CPOWManagerGetter.h"
 
 #define NS_ICONTENTPARENT_IID                                   \
   { 0xeeec9ebf, 0x8ecf, 0x4e38,                                 \
     { 0x81, 0xda, 0xb7, 0x34, 0x13, 0x7e, 0xac, 0xf3 } }
@@ -27,17 +29,18 @@ namespace mozilla {
 
 namespace jsipc {
 class PJavaScriptParent;
 class CpowEntry;
 } // namespace jsipc
 
 namespace ipc {
 class PFileDescriptorSetParent;
-class PSendStreamParent;
+class PChildToParentStreamParent;
+class PParentToChildStreamParent;
 }
 
 namespace dom {
 
 class Blob;
 class BlobConstructorParams;
 class BlobImpl;
 class BlobParent;
@@ -70,28 +73,34 @@ public:
   MOZ_MUST_USE virtual PBrowserParent*
   SendPBrowserConstructor(PBrowserParent* actor,
                           const TabId& aTabId,
                           const IPCTabContext& context,
                           const uint32_t& chromeFlags,
                           const ContentParentId& aCpId,
                           const bool& aIsForBrowser) = 0;
 
+  virtual mozilla::ipc::PFileDescriptorSetParent*
+  SendPFileDescriptorSetConstructor(const mozilla::ipc::FileDescriptor&) = 0;
+
   virtual bool IsContentParent() const { return false; }
 
   ContentParent* AsContentParent();
 
   virtual bool IsContentBridgeParent() const { return false; }
 
   ContentBridgeParent* AsContentBridgeParent();
 
   nsFrameMessageManager* GetMessageManager() const { return mMessageManager; }
 
   virtual int32_t Pid() const = 0;
 
+  virtual mozilla::ipc::PParentToChildStreamParent*
+  SendPParentToChildStreamConstructor(mozilla::ipc::PParentToChildStreamParent*) = 0;
+
 protected: // methods
   bool CanOpenBrowser(const IPCTabContext& aContext);
 
 protected: // IPDL methods
   virtual mozilla::jsipc::PJavaScriptParent* AllocPJavaScriptParent();
   virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*);
 
   virtual PBrowserParent* AllocPBrowserParent(const TabId& aTabId,
@@ -106,19 +115,25 @@ protected: // IPDL methods
   virtual bool DeallocPBlobParent(PBlobParent* aActor);
 
   virtual mozilla::ipc::PFileDescriptorSetParent*
   AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor& aFD);
 
   virtual bool
   DeallocPFileDescriptorSetParent(mozilla::ipc::PFileDescriptorSetParent* aActor);
 
-  virtual mozilla::ipc::PSendStreamParent* AllocPSendStreamParent();
+  virtual mozilla::ipc::PChildToParentStreamParent* AllocPChildToParentStreamParent();
+
+  virtual bool
+  DeallocPChildToParentStreamParent(mozilla::ipc::PChildToParentStreamParent* aActor);
 
-  virtual bool DeallocPSendStreamParent(mozilla::ipc::PSendStreamParent* aActor);
+  virtual mozilla::ipc::PParentToChildStreamParent* AllocPParentToChildStreamParent();
+
+  virtual bool
+  DeallocPParentToChildStreamParent(mozilla::ipc::PParentToChildStreamParent* aActor);
 
   virtual mozilla::ipc::IPCResult RecvSyncMessage(const nsString& aMsg,
                                                   const ClonedMessageData& aData,
                                                   InfallibleTArray<jsipc::CpowEntry>&& aCpows,
                                                   const IPC::Principal& aPrincipal,
                                                   nsTArray<ipc::StructuredCloneData>* aRetvals);
   virtual mozilla::ipc::IPCResult RecvRpcMessage(const nsString& aMsg,
                                                  const ClonedMessageData& aData,
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -694,17 +694,18 @@ D3D11DXVA2Manager::Init(layers::KnowsCom
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   if (layers::ImageBridgeChild::GetSingleton() || !aKnowsCompositor) {
     // There's no proper KnowsCompositor for ImageBridge currently (and it
     // implements the interface), so just use that if it's available.
     mTextureClientAllocator = new D3D11RecycleAllocator(
       layers::ImageBridgeChild::GetSingleton().get(), mDevice);
 
-    if (ImageBridgeChild::GetSingleton() && gfxPrefs::PDMWMFUseSyncTexture()) {
+    if (ImageBridgeChild::GetSingleton() && gfxPrefs::PDMWMFUseSyncTexture() &&
+        mDevice != DeviceManagerDx::Get()->GetCompositorDevice()) {
       // We use a syncobject to avoid the cost of the mutex lock when compositing,
       // and because it allows color conversion ocurring directly from this texture
       // DXVA does not seem to accept IDXGIKeyedMutex textures as input.
       mSyncObject =
         layers::SyncObject::CreateSyncObject(layers::ImageBridgeChild::GetSingleton()->
                                                GetTextureFactoryIdentifier().mSyncHandle,
                                              mDevice);
     }
@@ -897,17 +898,17 @@ D3D11DXVA2Manager::CopyToImage(IMFSample
   HRESULT hr;
   RefPtr<ID3D11Texture2D> texture = image->GetTexture();
 
   texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
 
   if (mutex) {
     hr = mutex->AcquireSync(0, 2000);
     NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-  } else {
+  } else if (mDevice != DeviceManagerDx::Get()->GetCompositorDevice()) {
     NS_ENSURE_TRUE(mSyncObject, E_FAIL);
   }
 
   if (client && client->GetFormat() == SurfaceFormat::NV12) {
     // Our video frame is stored in a non-sharable ID3D11Texture2D. We need
     // to create a copy of that frame as a sharable resource, save its share
     // handle, and put that handle into the rendering pipeline.
 
@@ -934,20 +935,20 @@ D3D11DXVA2Manager::CopyToImage(IMFSample
 
     RefPtr<IMFSample> sample;
     hr = CreateOutputSample(sample, texture);
     NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
     hr = mTransform->Output(&sample);
   }
 
-  if (!mutex) {
+  if (!mutex && mDevice != DeviceManagerDx::Get()->GetCompositorDevice()) {
     client->SyncWithObject(mSyncObject);
     mSyncObject->FinalizeFrame();
-  } else {
+  } else if (mutex) {
     mutex->ReleaseSync(0);
   }
 
   image.forget(aOutImage);
 
   return S_OK;
 }
 
--- a/dom/network/PUDPSocket.ipdl
+++ b/dom/network/PUDPSocket.ipdl
@@ -4,17 +4,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PNecko;
 include protocol PBackground;
 include protocol PBlob; //FIXME: bug #792908
 include protocol PFileDescriptorSet; // FIXME: bug #792908
-include protocol PSendStream; //FIXME: bug #792908
+include protocol PChildToParentStream; //FIXME: bug #792908
+include protocol PParentToChildStream; //FIXME: bug #792908
 
 include IPCStream;
 
 include "mozilla/net/NeckoMessageUtils.h";
 include "mozilla/net/DNS.h";
 include "prio.h";
 
 using mozilla::net::NetAddr from "mozilla/net/DNS.h";
--- a/dom/tests/mochitest/chrome/test_bug1224790-1.xul
+++ b/dom/tests/mochitest/chrome/test_bug1224790-1.xul
@@ -25,26 +25,39 @@ https://bugzilla.mozilla.org/show_bug.cg
   function startTest() {
     window.openDialog('file_bug1224790-1_modal.xul', '', 'modal');
   }
 
   function modalClosed() {
     SimpleTest.waitForFocus(gotFocus);
   }
 
+  var timer = null;
   function gotFocus() {
     var button = document.getElementById('button');
     synthesizeMouseAtCenter(button, { type: 'mousemove' }, window);
-    // The bug is not reproducible with synthesizeMouseAtCenter.
-    // Need to emulate native mouse event.
-    synthesizeNativeOSXClick(button.boxObject.screenX + button.boxObject.width / 2,
-                             button.boxObject.screenY + button.boxObject.height / 2);
+    function click() {
+      // The bug is not reproducible with synthesizeMouseAtCenter.
+      // Need to emulate native mouse event.
+      synthesizeNativeOSXClick(button.boxObject.screenX + button.boxObject.width / 2,
+                               button.boxObject.screenY + button.boxObject.height / 2);
+    }
+    click();
+    // On debug build, it's possible that the click event handler is not
+    // triggered by the first click in case the click is dispatched too early
+    // before Firefox gets ready for input.
+    // Click the button again after 1 sec when we don't get click event.
+    timer = setTimeout(click, 1000);
   }
 
   function onClick() {
+    if (timer) {
+      // Avoid clicking unrelated thing.
+      clearTimeout(timer);
+    }
     ok(true, "Click event should be fired");
     SimpleTest.finish();
   }
 
   SimpleTest.waitForExplicitFinish();
   SimpleTest.waitForFocus(startTest);
   ]]>
   </script>
--- a/dom/tests/mochitest/chrome/test_bug1224790-2.xul
+++ b/dom/tests/mochitest/chrome/test_bug1224790-2.xul
@@ -26,26 +26,39 @@ https://bugzilla.mozilla.org/show_bug.cg
   function startTest() {
     window.openDialog('file_bug1224790-2_modal.xul', '', 'modal');
   }
 
   function nonModalClosed() {
     SimpleTest.waitForFocus(gotFocus);
   }
 
+  var timer = null;
   function gotFocus() {
     var button = document.getElementById('button');
     synthesizeMouseAtCenter(button, { type: 'mousemove' }, window);
-    // The bug is not reproducible with synthesizeMouseAtCenter.
-    // Need to emulate native mouse event.
-    synthesizeNativeOSXClick(button.boxObject.screenX + button.boxObject.width / 2,
-                             button.boxObject.screenY + button.boxObject.height / 2);
+    function click() {
+      // The bug is not reproducible with synthesizeMouseAtCenter.
+      // Need to emulate native mouse event.
+      synthesizeNativeOSXClick(button.boxObject.screenX + button.boxObject.width / 2,
+                               button.boxObject.screenY + button.boxObject.height / 2);
+    }
+    click();
+    // On debug build, it's possible that the click event handler is not
+    // triggered by the first click in case the click is dispatched too early
+    // before Firefox gets ready for input.
+    // Click the button again after 1 sec when we don't get click event.
+    timer = setTimeout(click, 1000);
   }
 
   function onClick() {
+    if (timer) {
+      // Avoid clicking unrelated thing.
+      clearTimeout(timer);
+    }
     ok(true, "Click event should be fired");
     SimpleTest.finish();
   }
 
   SimpleTest.waitForExplicitFinish();
   SimpleTest.waitForFocus(startTest);
   ]]>
   </script>
--- a/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
+++ b/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
@@ -2,17 +2,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
 include protocol PWebBrowserPersistResources;
 include protocol PWebBrowserPersistSerialize;
 include protocol PFileDescriptorSet;
-include protocol PSendStream;
+include protocol PChildToParentStream; //FIXME: bug #792908
+include protocol PParentToChildStream; //FIXME: bug #792908
 
 include IPCStream;
 
 namespace mozilla {
 
 // nsIWebBrowserPersistDocument has attributes which can be read
 // synchronously.  To avoid using sync IPC for them, the actor sends
 // this structure from the child to the parent before the parent actor
--- a/gfx/layers/D3D11ShareHandleImage.cpp
+++ b/gfx/layers/D3D11ShareHandleImage.cpp
@@ -6,24 +6,27 @@
 #include "WMF.h"
 #include "D3D11ShareHandleImage.h"
 #include "gfxImageSurface.h"
 #include "gfxWindowsPlatform.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/TextureD3D11.h"
 #include "mozilla/layers/CompositableClient.h"
 #include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
 #include "d3d11.h"
 #include "gfxPrefs.h"
 #include "DXVA2Manager.h"
 #include <memory>
 
 namespace mozilla {
 namespace layers {
 
+using namespace gfx;
+
 D3D11ShareHandleImage::D3D11ShareHandleImage(const gfx::IntSize& aSize,
                                              const gfx::IntRect& aRect)
  : Image(nullptr, ImageFormat::D3D11_SHARE_HANDLE_TEXTURE),
    mSize(aSize),
    mPictureRect(aRect)
 {
 }
 
@@ -188,17 +191,18 @@ D3D11RecycleAllocator::Allocate(gfx::Sur
                                             mDevice, mSurfaceAllocator->GetTextureForwarder());
 }
 
 already_AddRefed<TextureClient>
 D3D11RecycleAllocator::CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
                                              const gfx::IntSize& aSize)
 {
   TextureAllocationFlags allocFlags = TextureAllocationFlags::ALLOC_DEFAULT;
-  if (gfxPrefs::PDMWMFUseSyncTexture()) {
+  if (gfxPrefs::PDMWMFUseSyncTexture() || mDevice == DeviceManagerDx::Get()->GetCompositorDevice()) {
+    // If our device is the compositor device, we don't need any synchronization in practice.
     allocFlags = TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION;
   }
   RefPtr<TextureClient> textureClient =
     CreateOrRecycle(aFormat,
                     aSize,
                     BackendSelector::Content,
                     layers::TextureFlags::DEFAULT,
                     allocFlags);
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -22,18 +22,20 @@
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/quota/PQuotaChild.h"
 #include "mozilla/dom/GamepadEventChannelChild.h"
 #include "mozilla/dom/GamepadTestChannelChild.h"
 #include "mozilla/dom/MessagePortChild.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
-#include "mozilla/ipc/PSendStreamChild.h"
+#include "mozilla/ipc/PChildToParentStreamChild.h"
+#include "mozilla/ipc/PParentToChildStreamChild.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "mozilla/net/PUDPSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "nsID.h"
 #include "nsTraceRefcnt.h"
 
 namespace {
 
@@ -398,24 +400,37 @@ bool
 BackgroundChildImpl::DeallocPMessagePortChild(PMessagePortChild* aActor)
 {
   RefPtr<dom::MessagePortChild> child =
     dont_AddRef(static_cast<dom::MessagePortChild*>(aActor));
   MOZ_ASSERT(child);
   return true;
 }
 
-PSendStreamChild*
-BackgroundChildImpl::AllocPSendStreamChild()
+PChildToParentStreamChild*
+BackgroundChildImpl::AllocPChildToParentStreamChild()
 {
-  MOZ_CRASH("PSendStreamChild actors should be manually constructed!");
+  MOZ_CRASH("PChildToParentStreamChild actors should be manually constructed!");
 }
 
 bool
-BackgroundChildImpl::DeallocPSendStreamChild(PSendStreamChild* aActor)
+BackgroundChildImpl::DeallocPChildToParentStreamChild(PChildToParentStreamChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PParentToChildStreamChild*
+BackgroundChildImpl::AllocPParentToChildStreamChild()
+{
+  return mozilla::ipc::AllocPParentToChildStreamChild();
+}
+
+bool
+BackgroundChildImpl::DeallocPParentToChildStreamChild(PParentToChildStreamChild* aActor)
 {
   delete aActor;
   return true;
 }
 
 PAsmJSCacheEntryChild*
 BackgroundChildImpl::AllocPAsmJSCacheEntryChild(
                                const dom::asmjscache::OpenMode& aOpenMode,
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -135,21 +135,27 @@ protected:
 
   virtual PMessagePortChild*
   AllocPMessagePortChild(const nsID& aUUID, const nsID& aDestinationUUID,
                          const uint32_t& aSequenceID) override;
 
   virtual bool
   DeallocPMessagePortChild(PMessagePortChild* aActor) override;
 
-  virtual PSendStreamChild*
-  AllocPSendStreamChild() override;
+  virtual PChildToParentStreamChild*
+  AllocPChildToParentStreamChild() override;
 
   virtual bool
-  DeallocPSendStreamChild(PSendStreamChild* aActor) override;
+  DeallocPChildToParentStreamChild(PChildToParentStreamChild* aActor) override;
+
+  virtual PParentToChildStreamChild*
+  AllocPParentToChildStreamChild() override;
+
+  virtual bool
+  DeallocPParentToChildStreamChild(PParentToChildStreamChild* aActor) override;
 
   virtual PAsmJSCacheEntryChild*
   AllocPAsmJSCacheEntryChild(const dom::asmjscache::OpenMode& aOpenMode,
                              const dom::asmjscache::WriteParams& aWriteParams,
                              const PrincipalInfo& aPrincipalInfo) override;
 
   virtual bool
   DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor) override;
--- a/ipc/glue/BackgroundImpl.cpp
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -49,17 +49,17 @@
 #ifdef RELEASE_OR_BETA
 #define THREADSAFETY_ASSERT MOZ_ASSERT
 #else
 #define THREADSAFETY_ASSERT MOZ_RELEASE_ASSERT
 #endif
 
 #define CRASH_IN_CHILD_PROCESS(_msg)                                           \
   do {                                                                         \
-    if (XRE_IsParentProcess()) {                                                     \
+    if (XRE_IsParentProcess()) {                                               \
       MOZ_ASSERT(false, _msg);                                                 \
     } else {                                                                   \
       MOZ_CRASH(_msg);                                                         \
     }                                                                          \
   }                                                                            \
   while (0)
 
 using namespace mozilla;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -26,20 +26,21 @@
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/quota/ActorsParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
-#include "mozilla/ipc/PSendStreamParent.h"
-#include "mozilla/ipc/SendStreamAlloc.h"
+#include "mozilla/ipc/PChildToParentStreamParent.h"
+#include "mozilla/ipc/PParentToChildStreamParent.h"
 #include "mozilla/layout/VsyncParent.h"
 #include "mozilla/dom/network/UDPSocketParent.h"
 #include "mozilla/Preferences.h"
 #include "nsNetUtil.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsProxyRelease.h"
 #include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
@@ -297,24 +298,39 @@ BackgroundParentImpl::DeallocPFileDescri
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<FileDescriptorSetParent*>(aActor);
   return true;
 }
 
-PSendStreamParent*
-BackgroundParentImpl::AllocPSendStreamParent()
+PChildToParentStreamParent*
+BackgroundParentImpl::AllocPChildToParentStreamParent()
 {
-  return mozilla::ipc::AllocPSendStreamParent();
+  return mozilla::ipc::AllocPChildToParentStreamParent();
 }
 
 bool
-BackgroundParentImpl::DeallocPSendStreamParent(PSendStreamParent* aActor)
+BackgroundParentImpl::DeallocPChildToParentStreamParent(
+                                             PChildToParentStreamParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PParentToChildStreamParent*
+BackgroundParentImpl::AllocPParentToChildStreamParent()
+{
+  MOZ_CRASH("PParentToChildStreamParent actors should be manually constructed!");
+}
+
+bool
+BackgroundParentImpl::DeallocPParentToChildStreamParent(
+                                             PParentToChildStreamParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 BackgroundParentImpl::PVsyncParent*
 BackgroundParentImpl::AllocPVsyncParent()
 {
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -96,21 +96,27 @@ protected:
   RecvPBroadcastChannelConstructor(PBroadcastChannelParent* actor,
                                    const PrincipalInfo& aPrincipalInfo,
                                    const nsCString& origin,
                                    const nsString& channel) override;
 
   virtual bool
   DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
 
-  virtual PSendStreamParent*
-  AllocPSendStreamParent() override;
+  virtual PChildToParentStreamParent*
+  AllocPChildToParentStreamParent() override;
 
   virtual bool
-  DeallocPSendStreamParent(PSendStreamParent* aActor) override;
+  DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor) override;
+
+  virtual PParentToChildStreamParent*
+  AllocPParentToChildStreamParent() override;
+
+  virtual bool
+  DeallocPParentToChildStreamParent(PParentToChildStreamParent* aActor) override;
 
   virtual PServiceWorkerManagerParent*
   AllocPServiceWorkerManagerParent() override;
 
   virtual bool
   DeallocPServiceWorkerManagerParent(PServiceWorkerManagerParent* aActor) override;
 
   virtual PCamerasParent*
--- a/ipc/glue/IPCStream.ipdlh
+++ b/ipc/glue/IPCStream.ipdlh
@@ -1,13 +1,14 @@
 /* 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 protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 include BlobTypes;
 include InputStreamParams;
 
 namespace mozilla {
 namespace ipc {
 
 // Do not use this directly.  See IPCStream below.
 struct InputStreamParamsWithFds
@@ -17,17 +18,18 @@ struct InputStreamParamsWithFds
 };
 
 // Use IPCStream or OptionalIPCStream in your ipdl to represent serialized
 // nsIInputStreams.  Then use AutoIPCStream from IPCStreamUtils.h to perform
 // the serialization.
 union IPCStream
 {
   InputStreamParamsWithFds;
-  PSendStream;
+  PChildToParentStream;
+  PParentToChildStream;
 };
 
 union OptionalIPCStream
 {
   IPCStream;
   void_t;
 };
 
rename from ipc/glue/SendStreamAlloc.h
rename to ipc/glue/IPCStreamAlloc.h
--- a/ipc/glue/SendStreamAlloc.h
+++ b/ipc/glue/IPCStreamAlloc.h
@@ -1,21 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_ipc_SendStreamAlloc_h
-#define mozilla_ipc_SendStreamAlloc_h
+#ifndef mozilla_ipc_IPCStreamAlloc_h
+#define mozilla_ipc_IPCStreamAlloc_h
 
 namespace mozilla {
 namespace ipc {
 
-class PSendStreamParent;
+class PChildToParentStreamParent;
+class PParentToChildStreamChild;
 
-PSendStreamParent*
-AllocPSendStreamParent();
+PChildToParentStreamParent*
+AllocPChildToParentStreamParent();
+
+PParentToChildStreamChild*
+AllocPParentToChildStreamChild();
 
 } // ipc namespace
 } // mozilla namespace
 
-#endif // mozilla_ipc_SendStreamAlloc_h
+#endif // mozilla_ipc_IPCStreamAlloc_h
rename from ipc/glue/SendStreamChild.cpp
rename to ipc/glue/IPCStreamChild.cpp
--- a/ipc/glue/SendStreamChild.cpp
+++ b/ipc/glue/IPCStreamChild.cpp
@@ -1,429 +1,212 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/ipc/SendStream.h"
+#include "IPCStreamDestination.h"
+#include "IPCStreamSource.h"
 
 #include "mozilla/Unused.h"
 #include "mozilla/dom/nsIContentChild.h"
-#include "mozilla/dom/WorkerPrivate.h"
-#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 #include "mozilla/ipc/PBackgroundChild.h"
-#include "nsIAsyncInputStream.h"
-#include "nsICancelableRunnable.h"
-#include "nsIRunnable.h"
-#include "nsIThread.h"
-#include "nsStreamUtils.h"
+#include "mozilla/ipc/PChildToParentStreamChild.h"
+#include "mozilla/ipc/PParentToChildStreamChild.h"
 
 namespace mozilla {
 namespace ipc {
 
-using mozilla::dom::nsIContentChild;
-using mozilla::dom::workers::Canceling;
-using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
-using mozilla::dom::workers::Status;
-using mozilla::dom::workers::WorkerHolder;
-using mozilla::dom::workers::WorkerPrivate;
+// Child to Parent implementation
+// ----------------------------------------------------------------------------
 
 namespace {
 
-class SendStreamChildImpl final : public SendStreamChild
-                                , public WorkerHolder
+class IPCStreamSourceChild final : public PChildToParentStreamChild
+                                 , public IPCStreamSource
 {
 public:
-  explicit SendStreamChildImpl(nsIAsyncInputStream* aStream);
-  ~SendStreamChildImpl();
-
-  void Start() override;
-  void StartDestroy() override;
-
-  bool
-  AddAsWorkerHolder(dom::workers::WorkerPrivate* aWorkerPrivate);
-
-private:
-  class Callback;
-
-  // PSendStreamChild methods
-  virtual void
-  ActorDestroy(ActorDestroyReason aReason) override;
-
-  virtual mozilla::ipc::IPCResult
-  RecvRequestClose(const nsresult& aRv) override;
-
-  // WorkerHolder methods
-  virtual bool
-  Notify(Status aStatus) override;
-
-  void DoRead();
+  static IPCStreamSourceChild*
+  Create(nsIAsyncInputStream* aInputStream)
+  {
+    MOZ_ASSERT(aInputStream);
 
-  void Wait();
-
-  void OnStreamReady(Callback* aCallback);
-
-  void OnEnd(nsresult aRv);
-
-  nsCOMPtr<nsIAsyncInputStream> mStream;
-  RefPtr<Callback> mCallback;
-  WorkerPrivate* mWorkerPrivate;
-  bool mClosed;
+    IPCStreamSourceChild* source = new IPCStreamSourceChild(aInputStream);
+    if (!source->Initialize()) {
+      delete source;
+      return nullptr;
+    }
 
-  NS_DECL_OWNINGTHREAD
-};
-
-class SendStreamChildImpl::Callback final : public nsIInputStreamCallback
-                                          , public nsIRunnable
-                                          , public nsICancelableRunnable
-{
-public:
-  explicit Callback(SendStreamChildImpl* aActor)
-    : mActor(aActor)
-    , mOwningThread(NS_GetCurrentThread())
-  {
-    MOZ_ASSERT(mActor);
+    return source;
   }
 
-  NS_IMETHOD
-  OnInputStreamReady(nsIAsyncInputStream* aStream) override
-  {
-    // any thread
-    if (mOwningThread == NS_GetCurrentThread()) {
-      return Run();
-    }
+  // PChildToParentStreamChild methods
 
-    // If this fails, then it means the owning thread is a Worker that has
-    // been shutdown.  Its ok to lose the event in this case because the
-    // SendStreamChild listens for this event through the WorkerHolder.
-    nsresult rv = mOwningThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to dispatch stream readable event to owning thread");
-    }
-
-    return NS_OK;
+  void
+  ActorDestroy(ActorDestroyReason aReason) override
+  {
+    ActorDestroyed();
   }
 
-  NS_IMETHOD
-  Run() override
+  IPCResult
+  RecvRequestClose(const nsresult& aRv) override
   {
-    MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
-    if (mActor) {
-      mActor->OnStreamReady(this);
-    }
-    return NS_OK;
-  }
-
-  nsresult
-  Cancel() override
-  {
-    // Cancel() gets called when the Worker thread is being shutdown.  We have
-    // nothing to do here because SendStreamChild handles this case via
-    // the WorkerHolder.
-    return NS_OK;
+    OnEnd(aRv);
+    return IPC_OK();
   }
 
   void
-  ClearActor()
+  Close(nsresult aRv) override
   {
-    MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
-    MOZ_ASSERT(mActor);
-    mActor = nullptr;
+    MOZ_ASSERT(IPCStreamSource::mState == IPCStreamSource::eClosed);
+    Unused << SendClose(aRv);
+  }
+
+  void
+  SendData(const nsCString& aBuffer) override
+  {
+    Unused << SendBuffer(aBuffer);
   }
 
 private:
-  ~Callback()
-  {
-    // called on any thread
-
-    // ClearActor() should be called before the Callback is destroyed
-    MOZ_ASSERT(!mActor);
-  }
-
-  SendStreamChildImpl* mActor;
-  nsCOMPtr<nsIThread> mOwningThread;
-
-  NS_DECL_THREADSAFE_ISUPPORTS
+  explicit IPCStreamSourceChild(nsIAsyncInputStream* aInputStream)
+    :IPCStreamSource(aInputStream)
+  {}
 };
 
-NS_IMPL_ISUPPORTS(SendStreamChildImpl::Callback, nsIInputStreamCallback,
-                                                 nsIRunnable,
-                                                 nsICancelableRunnable);
-
-SendStreamChildImpl::SendStreamChildImpl(nsIAsyncInputStream* aStream)
-  : mStream(aStream)
-  , mWorkerPrivate(nullptr)
-  , mClosed(false)
-{
-  MOZ_ASSERT(mStream);
-}
-
-SendStreamChildImpl::~SendStreamChildImpl()
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(mClosed);
-  MOZ_ASSERT(!mCallback);
-  MOZ_ASSERT(!mWorkerPrivate);
-}
-
-void
-SendStreamChildImpl::Start()
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerPrivate);
-  DoRead();
-}
-
-void
-SendStreamChildImpl::StartDestroy()
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  OnEnd(NS_ERROR_ABORT);
-}
-
-bool
-SendStreamChildImpl::AddAsWorkerHolder(WorkerPrivate* aWorkerPrivate)
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(aWorkerPrivate);
-  bool result = HoldWorker(aWorkerPrivate, Canceling);
-  if (result) {
-    mWorkerPrivate = aWorkerPrivate;
-  }
-  return result;
-}
-
-void
-SendStreamChildImpl::ActorDestroy(ActorDestroyReason aReason)
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-
-  // If the parent side runs into a problem it will ask the child to
-  // close the connection via RequestClose().  Therefore OnEnd() should
-  // always run before the actor is destroyed.
-  MOZ_ASSERT(mClosed);
-
-  if (mCallback) {
-    mCallback->ClearActor();
-    mCallback = nullptr;
-  }
-
-  if (mWorkerPrivate) {
-    ReleaseWorker();
-    mWorkerPrivate = nullptr;
-  }
-}
-
-mozilla::ipc::IPCResult
-SendStreamChildImpl::RecvRequestClose(const nsresult& aRv)
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  OnEnd(aRv);
-  return IPC_OK();
-}
-
-bool
-SendStreamChildImpl::Notify(Status aStatus)
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-
-  // Keep the worker thread alive until the stream is finished.
-  return true;
-}
-
-void
-SendStreamChildImpl::DoRead()
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(!mClosed);
-  MOZ_ASSERT(!mCallback);
-
-  // The input stream (likely a pipe) probably uses a segment size of
-  // 4kb.  If there is data already buffered it would be nice to aggregate
-  // multiple segments into a single IPC call.  Conversely, don't send too
-  // too large of a buffer in a single call to avoid spiking memory.
-  static const uint64_t kMaxBytesPerMessage = 32 * 1024;
-  static_assert(kMaxBytesPerMessage <= static_cast<uint64_t>(UINT32_MAX),
-                "kMaxBytesPerMessage must cleanly cast to uint32_t");
-
-  while (true) {
-    // It should not be possible to transition to closed state without
-    // this loop terminating via a return.
-    MOZ_ASSERT(!mClosed);
-
-    // Use non-auto here as we're unlikely to hit stack storage with the
-    // sizes we are sending.  Also, it would be nice to avoid another copy
-    // to the IPC layer which we avoid if we use COW strings.  Unfortunately
-    // IPC does not seem to support passing dependent storage types.
-    nsCString buffer;
-
-    uint64_t available = 0;
-    nsresult rv = mStream->Available(&available);
-    if (NS_FAILED(rv)) {
-      OnEnd(rv);
-      return;
-    }
-
-    if (available == 0) {
-      Wait();
-      return;
-    }
-
-    uint32_t expectedBytes =
-      static_cast<uint32_t>(std::min(available, kMaxBytesPerMessage));
-
-    buffer.SetLength(expectedBytes);
-
-    uint32_t bytesRead = 0;
-    rv = mStream->Read(buffer.BeginWriting(), buffer.Length(), &bytesRead);
-    MOZ_ASSERT_IF(NS_FAILED(rv), bytesRead == 0);
-    buffer.SetLength(bytesRead);
-
-    // If we read any data from the stream, send it across.
-    if (!buffer.IsEmpty()) {
-      Unused << SendBuffer(buffer);
-    }
-
-    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
-      Wait();
-      return;
-    }
-
-    // Any other error or zero-byte read indicates end-of-stream
-    if (NS_FAILED(rv) || buffer.IsEmpty()) {
-      OnEnd(rv);
-      return;
-    }
-  }
-}
-
-void
-SendStreamChildImpl::Wait()
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(!mClosed);
-  MOZ_ASSERT(!mCallback);
-
-  // Set mCallback immediately instead of waiting for success.  Its possible
-  // AsyncWait() will callback synchronously.
-  mCallback = new Callback(this);
-  nsresult rv = mStream->AsyncWait(mCallback, 0, 0, nullptr);
-  if (NS_FAILED(rv)) {
-    OnEnd(rv);
-    return;
-  }
-}
-
-void
-SendStreamChildImpl::OnStreamReady(Callback* aCallback)
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(mCallback);
-  MOZ_ASSERT(aCallback == mCallback);
-  mCallback->ClearActor();
-  mCallback = nullptr;
-  DoRead();
-}
-
-void
-SendStreamChildImpl::OnEnd(nsresult aRv)
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(aRv != NS_BASE_STREAM_WOULD_BLOCK);
-
-  if (mClosed) {
-    return;
-  }
-
-  mClosed = true;
-
-  mStream->CloseWithStatus(aRv);
-
-  if (aRv == NS_BASE_STREAM_CLOSED) {
-    aRv = NS_OK;
-  }
-
-  // This will trigger an ActorDestroy() from the parent side
-  Unused << SendClose(aRv);
-}
-
-bool
-IsBlocking(nsIAsyncInputStream* aInputStream)
-{
-  bool nonBlocking = false;
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aInputStream->IsNonBlocking(&nonBlocking)));
-  return !nonBlocking;
-}
-
 } // anonymous namespace
 
-// static
-SendStreamChild*
-SendStreamChild::Create(nsIAsyncInputStream* aInputStream,
-                        nsIContentChild* aManager)
+/* static */ PChildToParentStreamChild*
+IPCStreamSource::Create(nsIAsyncInputStream* aInputStream,
+                        dom::nsIContentChild* aManager)
 {
   MOZ_ASSERT(aInputStream);
   MOZ_ASSERT(aManager);
 
   // PContent can only be used on the main thread
   MOZ_ASSERT(NS_IsMainThread());
 
-  // SendStreamChild reads in the current thread, so it is only supported
-  // on non-blocking, async channels
-  if (NS_WARN_IF(IsBlocking(aInputStream))) {
+  IPCStreamSourceChild* source = IPCStreamSourceChild::Create(aInputStream);
+  if (!source) {
     return nullptr;
   }
 
-  SendStreamChild* actor = new SendStreamChildImpl(aInputStream);
-  aManager->SendPSendStreamConstructor(actor);
+  if (!aManager->SendPChildToParentStreamConstructor(source)) {
+    return nullptr;
+  }
 
-  return actor;
+  source->ActorConstructed();
+  return source;
 }
 
-// static
-SendStreamChild*
-SendStreamChild::Create(nsIAsyncInputStream* aInputStream,
+/* static */ PChildToParentStreamChild*
+IPCStreamSource::Create(nsIAsyncInputStream* aInputStream,
                         PBackgroundChild* aManager)
 {
   MOZ_ASSERT(aInputStream);
   MOZ_ASSERT(aManager);
 
-  // PBackground can be used on any thread, but we only support SendStream on
-  // main thread and Worker threads right now.  This is due to the requirement
-  // that the thread be guaranteed to live long enough to receive messages
-  // sent from parent to child.  We can enforce this guarantee with a feature
-  // on worker threads, but not other threads.
-  WorkerPrivate* workerPrivate = nullptr;
-  if (!NS_IsMainThread()) {
-    workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
+  IPCStreamSourceChild* source = IPCStreamSourceChild::Create(aInputStream);
+  if (!source) {
+    return nullptr;
   }
 
-  // SendStreamChild reads in the current thread, so it is only supported
-  // on non-blocking, async channels
-  if (NS_WARN_IF(IsBlocking(aInputStream))) {
+  if (!aManager->SendPChildToParentStreamConstructor(source)) {
     return nullptr;
   }
 
-  SendStreamChildImpl* actor = new SendStreamChildImpl(aInputStream);
+  source->ActorConstructed();
+  return source;
+}
+
+/* static */ IPCStreamSource*
+IPCStreamSource::Cast(PChildToParentStreamChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+  return static_cast<IPCStreamSourceChild*>(aActor);
+}
+
+// Parent to Child implementation
+// ----------------------------------------------------------------------------
+
+namespace {
 
-  if (workerPrivate && !actor->AddAsWorkerHolder(workerPrivate)) {
-    delete actor;
-    return nullptr;
+class IPCStreamDestinationChild final : public PParentToChildStreamChild
+                                      , public IPCStreamDestination
+{
+public:
+  nsresult Initialize()
+  {
+    return IPCStreamDestination::Initialize();
+  }
+
+  ~IPCStreamDestinationChild()
+  {}
+
+private:
+  // PParentToChildStreamChild methods
+
+  void
+  ActorDestroy(ActorDestroyReason aReason) override
+  {
+    ActorDestroyed();
   }
 
-  aManager->SendPSendStreamConstructor(actor);
+  IPCResult
+  RecvBuffer(const nsCString& aBuffer) override
+  {
+    BufferReceived(aBuffer);
+    return IPC_OK();
+  }
+
+  IPCResult
+  RecvClose(const nsresult& aRv) override
+  {
+    CloseReceived(aRv);
+    return IPC_OK();
+  }
+
+  // IPCStreamDestination methods
+
+  void
+  RequestClose(nsresult aRv) override
+  {
+    Unused << SendRequestClose(aRv);
+  }
+
+  void
+  TerminateDestination() override
+  {
+    Unused << Send__delete__(this);
+  }
+};
+
+} // anonymous namespace
+
+PParentToChildStreamChild*
+AllocPParentToChildStreamChild()
+{
+  IPCStreamDestinationChild* actor = new IPCStreamDestinationChild();
+
+  if (NS_WARN_IF(NS_FAILED(actor->Initialize()))) {
+    delete actor;
+    actor = nullptr;
+  }
+
   return actor;
 }
 
-SendStreamChild::~SendStreamChild()
-{
-}
-
 void
-DeallocPSendStreamChild(PSendStreamChild* aActor)
+DeallocPParentToChildStreamChild(PParentToChildStreamChild* aActor)
 {
   delete aActor;
 }
 
+/* static */ IPCStreamDestination*
+IPCStreamDestination::Cast(PParentToChildStreamChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+  return static_cast<IPCStreamDestinationChild*>(aActor);
+}
+
 } // namespace ipc
 } // namespace mozilla
rename from ipc/glue/SendStreamParent.cpp
rename to ipc/glue/IPCStreamDestination.cpp
--- a/ipc/glue/SendStreamParent.cpp
+++ b/ipc/glue/IPCStreamDestination.cpp
@@ -1,136 +1,90 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/ipc/SendStream.h"
-
-#include "mozilla/Unused.h"
+#include "IPCStreamDestination.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIPipe.h"
 
 namespace mozilla {
 namespace ipc {
 
-namespace {
-
-class SendStreamParentImpl final : public SendStreamParent
+IPCStreamDestination::IPCStreamDestination()
 {
-public:
-  SendStreamParentImpl(nsIAsyncInputStream* aReader,
-                        nsIAsyncOutputStream* aWriter);
-  ~SendStreamParentImpl();
-
-private:
-  // PSendStreamParentImpl methods
-  virtual void
-  ActorDestroy(ActorDestroyReason aReason) override;
+}
 
-  // SendStreamparent methods
-  already_AddRefed<nsIInputStream>
-  TakeReader() override;
-
-  virtual mozilla::ipc::IPCResult
-  RecvBuffer(const nsCString& aBuffer) override;
-
-  virtual mozilla::ipc::IPCResult
-  RecvClose(const nsresult& aRv) override;
-
-  nsCOMPtr<nsIAsyncInputStream> mReader;
-  nsCOMPtr<nsIAsyncOutputStream> mWriter;
-
-  NS_DECL_OWNINGTHREAD
-};
-
-SendStreamParentImpl::~SendStreamParentImpl()
+IPCStreamDestination::~IPCStreamDestination()
 {
 }
 
+nsresult
+IPCStreamDestination::Initialize()
+{
+  MOZ_ASSERT(!mReader);
+  MOZ_ASSERT(!mWriter);
+
+  // use async versions for both reader and writer even though we are
+  // opening the writer as an infinite stream.  We want to be able to
+  // use CloseWithStatus() to communicate errors through the pipe.
+
+  // Use an "infinite" pipe because we cannot apply back-pressure through
+  // the async IPC layer at the moment.  Blocking the IPC worker thread
+  // is not desirable, either.
+  nsresult rv = NS_NewPipe2(getter_AddRefs(mReader),
+                            getter_AddRefs(mWriter),
+                            true, true,   // non-blocking
+                            0,            // segment size
+                            UINT32_MAX);  // "infinite" pipe
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 already_AddRefed<nsIInputStream>
-SendStreamParentImpl::TakeReader()
+IPCStreamDestination::TakeReader()
 {
   MOZ_ASSERT(mReader);
   return mReader.forget();
 }
 
 void
-SendStreamParentImpl::ActorDestroy(ActorDestroyReason aReason)
+IPCStreamDestination::ActorDestroyed()
 {
+  MOZ_ASSERT(mWriter);
+
   // If we were gracefully closed we should have gotten RecvClose().  In
   // that case, the writer will already be closed and this will have no
   // effect.  This just aborts the writer in the case where the child process
   // crashes.
   mWriter->CloseWithStatus(NS_ERROR_ABORT);
 }
 
-mozilla::ipc::IPCResult
-SendStreamParentImpl::RecvBuffer(const nsCString& aBuffer)
+void
+IPCStreamDestination::BufferReceived(const nsCString& aBuffer)
 {
+  MOZ_ASSERT(mWriter);
+
   uint32_t numWritten = 0;
 
   // This should only fail if we hit an OOM condition.
   nsresult rv = mWriter->Write(aBuffer.get(), aBuffer.Length(), &numWritten);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    Unused << SendRequestClose(rv);
+    RequestClose(rv);
   }
-
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-SendStreamParentImpl::RecvClose(const nsresult& aRv)
-{
-  mWriter->CloseWithStatus(aRv);
-  Unused << Send__delete__(this);
-  return IPC_OK();
-}
-
-SendStreamParentImpl::SendStreamParentImpl(nsIAsyncInputStream* aReader,
-                                             nsIAsyncOutputStream* aWriter)
-  : mReader(aReader)
-  , mWriter(aWriter)
-{
-  MOZ_ASSERT(mReader);
-  MOZ_ASSERT(mWriter);
-}
-
-} // anonymous namespace
-
-SendStreamParent::~SendStreamParent()
-{
-}
-
-PSendStreamParent*
-AllocPSendStreamParent()
-{
-  // use async versions for both reader and writer even though we are
-  // opening the writer as an infinite stream.  We want to be able to
-  // use CloseWithStatus() to communicate errors through the pipe.
-  nsCOMPtr<nsIAsyncInputStream> reader;
-  nsCOMPtr<nsIAsyncOutputStream> writer;
-
-  // Use an "infinite" pipe because we cannot apply back-pressure through
-  // the async IPC layer at the moment.  Blocking the IPC worker thread
-  // is not desirable, either.
-  nsresult rv = NS_NewPipe2(getter_AddRefs(reader),
-                            getter_AddRefs(writer),
-                            true, true,   // non-blocking
-                            0,            // segment size
-                            UINT32_MAX);  // "infinite" pipe
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-
-  return new SendStreamParentImpl(reader, writer);
 }
 
 void
-DeallocPSendStreamParent(PSendStreamParent* aActor)
+IPCStreamDestination::CloseReceived(nsresult aRv)
 {
-  delete aActor;
+  MOZ_ASSERT(mWriter);
+  mWriter->CloseWithStatus(aRv);
+  TerminateDestination();
 }
 
 } // namespace ipc
 } // namespace mozilla
rename from ipc/glue/SendStream.h
rename to ipc/glue/IPCStreamDestination.h
--- a/ipc/glue/SendStream.h
+++ b/ipc/glue/IPCStreamDestination.h
@@ -1,101 +1,69 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_ipc_SendStream_h
-#define mozilla_ipc_SendStream_h
+#ifndef mozilla_ipc_IPCStreamDestination_h
+#define mozilla_ipc_IPCStreamDestination_h
 
 #include "mozilla/AlreadyAddRefed.h"
-#include "mozilla/ipc/PSendStreamChild.h"
-#include "mozilla/ipc/PSendStreamParent.h"
 
 class nsIInputStream;
 class nsIAsyncInputStream;
+class nsIAsyncOutputStream;
 
 namespace mozilla {
-
-namespace dom {
-class nsIContentChild;
-} // dom namespace
-
 namespace ipc {
 
-class PBackgroundChild;
+class PChildToParentStreamParent;
+class PParentToChildStreamChild;
 
-// The SendStream IPC actor is designed to push an nsIInputStream from child to
-// parent incrementally.  This is mainly needed for streams such as nsPipe that
-// may not yet have all their data available when the stream must be sent across
-// an IPC boundary.  While many streams are handled by SerializeInputStream(),
-// these streams cannot be serialized and must be sent using this actor.
-//
-// The SendStream actor only supports sending data from child to parent.
-//
-// The SendStream actor only support async, non-blocking streams because they
-// must be read inline on the main thread and Worker threads.
-//
-// In general, the creation and handling of the SendStream actor cannot be
-// abstracted away behind SerializeInputStream() because the actor must be
-// carefully managed.  Specifically:
-//
-//  1) The data flow must be explicitly initiated by calling
-//     SendStreamChild::Start() after the actor has been sent to the parent.
-//  2) If the actor is never sent to the parent, then the child code must
-//     call SendStreamChild::StartDestroy() to avoid memory leaks.
-//  3) The SendStreamChild actor can only be used on threads that can be
-//     guaranteed to stay alive as long as the actor is alive.  Right now
-//     this limits SendStream to the main thread and Worker threads.
-//
-// In general you should probably use the AutoIPCStreamChild RAII class
-// defined in InputStreamUtils.h instead of using SendStreamChild directly.
-class SendStreamChild : public PSendStreamChild
+// On the destination side, you must simply call TakeReader() upon receiving a
+// reference to the IPCStream{Child,Parent} actor.  You do not need to maintain
+// a reference to the actor itself.
+class IPCStreamDestination
 {
 public:
-  // Create a SendStreamChild using a PContent IPC manager on the
-  // main thread.  This can return nullptr if the provided stream is
-  // blocking.
-  static SendStreamChild*
-  Create(nsIAsyncInputStream* aInputStream, dom::nsIContentChild* aManager);
-
-  // Create a SendStreamChild using a PBackground IPC manager on the
-  // main thread or a Worker thread.  This can return nullptr if the provided
-  // stream is blocking or if the Worker thread is already shutting down.
-  static SendStreamChild*
-  Create(nsIAsyncInputStream* aInputStream, PBackgroundChild* aManager);
+  static IPCStreamDestination*
+  Cast(PChildToParentStreamParent* aActor);
 
-  // Start reading data from the nsIAsyncInputStream used to create the actor.
-  // This must be called after the actor is passed to the parent.  If you
-  // use AutoIPCStream this is handled automatically.
-  virtual void
-  Start() = 0;
+  static IPCStreamDestination*
+  Cast(PParentToChildStreamChild* aActor);
 
-  // Start cleaning up the actor.  This must be called if the actor is never
-  // sent to the parent.  If you use AutoIPCStream this is handled
-  // automatically.
-  virtual void
-  StartDestroy() = 0;
+  virtual already_AddRefed<nsIInputStream>
+  TakeReader();
 
 protected:
-  virtual
-  ~SendStreamChild() = 0;
-};
+  IPCStreamDestination();
+  virtual ~IPCStreamDestination();
+
+  nsresult Initialize();
+
+  // The implementation of the actor should call these methods.
+
+  void
+  ActorDestroyed();
+
+  void
+  BufferReceived(const nsCString& aBuffer);
 
-// On the parent side, you must simply call TakeReader() upon receiving a
-// reference to the SendStreamParent actor.  You do not need to maintain a
-// reference to the actor itself.
-class SendStreamParent : public PSendStreamParent
-{
-public:
-  virtual already_AddRefed<nsIInputStream>
-  TakeReader() = 0;
+  void
+  CloseReceived(nsresult aRv);
+
+  // These methods will be implemented by the actor.
 
-protected:
-  virtual
-  ~SendStreamParent() = 0;
+  virtual void
+  RequestClose(nsresult aRv) = 0;
+
+  virtual void
+  TerminateDestination() = 0;
+
+  nsCOMPtr<nsIAsyncInputStream> mReader;
+  nsCOMPtr<nsIAsyncOutputStream> mWriter;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
-#endif // mozilla_ipc_SendStream_h
+#endif // mozilla_ipc_IPCStreamDestination_h
copy from ipc/glue/SendStreamParent.cpp
copy to ipc/glue/IPCStreamParent.cpp
--- a/ipc/glue/SendStreamParent.cpp
+++ b/ipc/glue/IPCStreamParent.cpp
@@ -1,136 +1,212 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/ipc/SendStream.h"
-
+#include "IPCStreamDestination.h"
+#include "mozilla/dom/nsIContentParent.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+#include "mozilla/ipc/PChildToParentStreamParent.h"
+#include "mozilla/ipc/PParentToChildStreamParent.h"
 #include "mozilla/Unused.h"
-#include "nsIAsyncInputStream.h"
-#include "nsIAsyncOutputStream.h"
-#include "nsIPipe.h"
 
 namespace mozilla {
 namespace ipc {
 
+// Child to Parent implementation
+// ----------------------------------------------------------------------------
+
 namespace {
 
-class SendStreamParentImpl final : public SendStreamParent
+class IPCStreamSourceParent final : public PParentToChildStreamParent
+                                  , public IPCStreamSource
 {
 public:
-  SendStreamParentImpl(nsIAsyncInputStream* aReader,
-                        nsIAsyncOutputStream* aWriter);
-  ~SendStreamParentImpl();
+  static IPCStreamSourceParent*
+  Create(nsIAsyncInputStream* aInputStream)
+  {
+    MOZ_ASSERT(aInputStream);
+
+    IPCStreamSourceParent* source = new IPCStreamSourceParent(aInputStream);
+    if (!source->Initialize()) {
+      delete source;
+      return nullptr;
+    }
+
+    return source;
+  }
+
+  // PParentToChildStreamParent methods
+
+  void
+  ActorDestroy(ActorDestroyReason aReason) override
+  {
+    ActorDestroyed();
+  }
+
+  IPCResult
+  RecvRequestClose(const nsresult& aRv) override
+  {
+    OnEnd(aRv);
+    return IPC_OK();
+  }
+
+  void
+  Close(nsresult aRv) override
+  {
+    MOZ_ASSERT(IPCStreamSource::mState == IPCStreamSource::eClosed);
+    Unused << SendClose(aRv);
+  }
+
+  void
+  SendData(const nsCString& aBuffer) override
+  {
+    Unused << SendBuffer(aBuffer);
+  }
 
 private:
-  // PSendStreamParentImpl methods
-  virtual void
-  ActorDestroy(ActorDestroyReason aReason) override;
-
-  // SendStreamparent methods
-  already_AddRefed<nsIInputStream>
-  TakeReader() override;
-
-  virtual mozilla::ipc::IPCResult
-  RecvBuffer(const nsCString& aBuffer) override;
-
-  virtual mozilla::ipc::IPCResult
-  RecvClose(const nsresult& aRv) override;
-
-  nsCOMPtr<nsIAsyncInputStream> mReader;
-  nsCOMPtr<nsIAsyncOutputStream> mWriter;
-
-  NS_DECL_OWNINGTHREAD
+  explicit IPCStreamSourceParent(nsIAsyncInputStream* aInputStream)
+    :IPCStreamSource(aInputStream)
+  {}
 };
 
-SendStreamParentImpl::~SendStreamParentImpl()
+} // anonymous namespace
+
+/* static */ PParentToChildStreamParent*
+IPCStreamSource::Create(nsIAsyncInputStream* aInputStream,
+                        dom::nsIContentParent* aManager)
 {
+  MOZ_ASSERT(aInputStream);
+  MOZ_ASSERT(aManager);
+
+  // PContent can only be used on the main thread
+  MOZ_ASSERT(NS_IsMainThread());
+
+  IPCStreamSourceParent* source = IPCStreamSourceParent::Create(aInputStream);
+  if (!source) {
+    return nullptr;
+  }
+
+  if (!aManager->SendPParentToChildStreamConstructor(source)) {
+    // no delete here, the manager will delete the actor for us.
+    return nullptr;
+  }
+
+  source->ActorConstructed();
+  return source;
+}
+
+/* static */ PParentToChildStreamParent*
+IPCStreamSource::Create(nsIAsyncInputStream* aInputStream,
+                        PBackgroundParent* aManager)
+{
+  MOZ_ASSERT(aInputStream);
+  MOZ_ASSERT(aManager);
+
+  IPCStreamSourceParent* source = IPCStreamSourceParent::Create(aInputStream);
+  if (!source) {
+    return nullptr;
+  }
+
+  if (!aManager->SendPParentToChildStreamConstructor(source)) {
+    // no delete here, the manager will delete the actor for us.
+    return nullptr;
+  }
+
+  source->ActorConstructed();
+  return source;
+}
+
+/* static */ IPCStreamSource*
+IPCStreamSource::Cast(PParentToChildStreamParent* aActor)
+{
+  MOZ_ASSERT(aActor);
+  return static_cast<IPCStreamSourceParent*>(aActor);
 }
 
-already_AddRefed<nsIInputStream>
-SendStreamParentImpl::TakeReader()
+// Child to Parent implementation
+// ----------------------------------------------------------------------------
+
+namespace {
+
+class IPCStreamDestinationParent final : public PChildToParentStreamParent
+                                       , public IPCStreamDestination
 {
-  MOZ_ASSERT(mReader);
-  return mReader.forget();
+public:
+  nsresult Initialize()
+  {
+    return IPCStreamDestination::Initialize();
+  }
+
+  ~IPCStreamDestinationParent()
+  {}
+
+private:
+  // PChildToParentStreamParent methods
+
+  void
+  ActorDestroy(ActorDestroyReason aReason) override
+  {
+    ActorDestroyed();
+  }
+
+  IPCResult
+  RecvBuffer(const nsCString& aBuffer) override
+  {
+    BufferReceived(aBuffer);
+    return IPC_OK();
+  }
+
+  IPCResult
+  RecvClose(const nsresult& aRv) override
+  {
+    CloseReceived(aRv);
+    return IPC_OK();
+  }
+
+  // IPCStreamDestination methods
+
+  void
+  RequestClose(nsresult aRv) override
+  {
+    Unused << SendRequestClose(aRv);
+  }
+
+  void
+  TerminateDestination() override
+  {
+    Unused << Send__delete__(this);
+  }
+};
+
+} // anonymous namespace
+
+PChildToParentStreamParent*
+AllocPChildToParentStreamParent()
+{
+  IPCStreamDestinationParent* actor = new IPCStreamDestinationParent();
+
+  if (NS_WARN_IF(NS_FAILED(actor->Initialize()))) {
+    delete actor;
+    actor = nullptr;
+  }
+
+  return actor;
 }
 
 void
-SendStreamParentImpl::ActorDestroy(ActorDestroyReason aReason)
-{
-  // If we were gracefully closed we should have gotten RecvClose().  In
-  // that case, the writer will already be closed and this will have no
-  // effect.  This just aborts the writer in the case where the child process
-  // crashes.
-  mWriter->CloseWithStatus(NS_ERROR_ABORT);
-}
-
-mozilla::ipc::IPCResult
-SendStreamParentImpl::RecvBuffer(const nsCString& aBuffer)
-{
-  uint32_t numWritten = 0;
-
-  // This should only fail if we hit an OOM condition.
-  nsresult rv = mWriter->Write(aBuffer.get(), aBuffer.Length(), &numWritten);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    Unused << SendRequestClose(rv);
-  }
-
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-SendStreamParentImpl::RecvClose(const nsresult& aRv)
-{
-  mWriter->CloseWithStatus(aRv);
-  Unused << Send__delete__(this);
-  return IPC_OK();
-}
-
-SendStreamParentImpl::SendStreamParentImpl(nsIAsyncInputStream* aReader,
-                                             nsIAsyncOutputStream* aWriter)
-  : mReader(aReader)
-  , mWriter(aWriter)
-{
-  MOZ_ASSERT(mReader);
-  MOZ_ASSERT(mWriter);
-}
-
-} // anonymous namespace
-
-SendStreamParent::~SendStreamParent()
-{
-}
-
-PSendStreamParent*
-AllocPSendStreamParent()
-{
-  // use async versions for both reader and writer even though we are
-  // opening the writer as an infinite stream.  We want to be able to
-  // use CloseWithStatus() to communicate errors through the pipe.
-  nsCOMPtr<nsIAsyncInputStream> reader;
-  nsCOMPtr<nsIAsyncOutputStream> writer;
-
-  // Use an "infinite" pipe because we cannot apply back-pressure through
-  // the async IPC layer at the moment.  Blocking the IPC worker thread
-  // is not desirable, either.
-  nsresult rv = NS_NewPipe2(getter_AddRefs(reader),
-                            getter_AddRefs(writer),
-                            true, true,   // non-blocking
-                            0,            // segment size
-                            UINT32_MAX);  // "infinite" pipe
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-
-  return new SendStreamParentImpl(reader, writer);
-}
-
-void
-DeallocPSendStreamParent(PSendStreamParent* aActor)
+DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor)
 {
   delete aActor;
 }
 
+/* static */ IPCStreamDestination*
+IPCStreamDestination::Cast(PChildToParentStreamParent* aActor)
+{
+  MOZ_ASSERT(aActor);
+  return static_cast<IPCStreamDestinationParent*>(aActor);
+}
+
 } // namespace ipc
 } // namespace mozilla
copy from ipc/glue/SendStreamChild.cpp
copy to ipc/glue/IPCStreamSource.cpp
--- a/ipc/glue/SendStreamChild.cpp
+++ b/ipc/glue/IPCStreamSource.cpp
@@ -1,259 +1,226 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/ipc/SendStream.h"
-
-#include "mozilla/Unused.h"
-#include "mozilla/dom/nsIContentChild.h"
-#include "mozilla/dom/WorkerPrivate.h"
-#include "mozilla/dom/workers/bindings/WorkerHolder.h"
-#include "mozilla/ipc/PBackgroundChild.h"
+#include "IPCStreamSource.h"
 #include "nsIAsyncInputStream.h"
 #include "nsICancelableRunnable.h"
 #include "nsIRunnable.h"
 #include "nsIThread.h"
 #include "nsStreamUtils.h"
 
-namespace mozilla {
-namespace ipc {
-
-using mozilla::dom::nsIContentChild;
 using mozilla::dom::workers::Canceling;
 using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
 using mozilla::dom::workers::Status;
-using mozilla::dom::workers::WorkerHolder;
 using mozilla::dom::workers::WorkerPrivate;
 
-namespace {
+namespace mozilla {
+namespace ipc {
 
-class SendStreamChildImpl final : public SendStreamChild
-                                , public WorkerHolder
+class IPCStreamSource::Callback final : public nsIInputStreamCallback
+                                      , public nsIRunnable
+                                      , public nsICancelableRunnable
 {
 public:
-  explicit SendStreamChildImpl(nsIAsyncInputStream* aStream);
-  ~SendStreamChildImpl();
-
-  void Start() override;
-  void StartDestroy() override;
-
-  bool
-  AddAsWorkerHolder(dom::workers::WorkerPrivate* aWorkerPrivate);
-
-private:
-  class Callback;
-
-  // PSendStreamChild methods
-  virtual void
-  ActorDestroy(ActorDestroyReason aReason) override;
-
-  virtual mozilla::ipc::IPCResult
-  RecvRequestClose(const nsresult& aRv) override;
-
-  // WorkerHolder methods
-  virtual bool
-  Notify(Status aStatus) override;
-
-  void DoRead();
-
-  void Wait();
-
-  void OnStreamReady(Callback* aCallback);
-
-  void OnEnd(nsresult aRv);
-
-  nsCOMPtr<nsIAsyncInputStream> mStream;
-  RefPtr<Callback> mCallback;
-  WorkerPrivate* mWorkerPrivate;
-  bool mClosed;
-
-  NS_DECL_OWNINGTHREAD
-};
-
-class SendStreamChildImpl::Callback final : public nsIInputStreamCallback
-                                          , public nsIRunnable
-                                          , public nsICancelableRunnable
-{
-public:
-  explicit Callback(SendStreamChildImpl* aActor)
-    : mActor(aActor)
+  explicit Callback(IPCStreamSource* aSource)
+    : mSource(aSource)
     , mOwningThread(NS_GetCurrentThread())
   {
-    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(mSource);
   }
 
   NS_IMETHOD
   OnInputStreamReady(nsIAsyncInputStream* aStream) override
   {
     // any thread
     if (mOwningThread == NS_GetCurrentThread()) {
       return Run();
     }
 
     // If this fails, then it means the owning thread is a Worker that has
     // been shutdown.  Its ok to lose the event in this case because the
-    // SendStreamChild listens for this event through the WorkerHolder.
+    // IPCStreamChild listens for this event through the WorkerHolder.
     nsresult rv = mOwningThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch stream readable event to owning thread");
     }
 
     return NS_OK;
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
-    if (mActor) {
-      mActor->OnStreamReady(this);
+    if (mSource) {
+      mSource->OnStreamReady(this);
     }
     return NS_OK;
   }
 
   nsresult
   Cancel() override
   {
     // Cancel() gets called when the Worker thread is being shutdown.  We have
-    // nothing to do here because SendStreamChild handles this case via
+    // nothing to do here because IPCStreamChild handles this case via
     // the WorkerHolder.
     return NS_OK;
   }
 
   void
-  ClearActor()
+  ClearSource()
   {
     MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
-    MOZ_ASSERT(mActor);
-    mActor = nullptr;
+    MOZ_ASSERT(mSource);
+    mSource = nullptr;
   }
 
 private:
   ~Callback()
   {
     // called on any thread
 
-    // ClearActor() should be called before the Callback is destroyed
-    MOZ_ASSERT(!mActor);
+    // ClearSource() should be called before the Callback is destroyed
+    MOZ_ASSERT(!mSource);
   }
 
-  SendStreamChildImpl* mActor;
+  // This is a raw pointer because the source keeps alive the callback and,
+  // before beeing destroyed, it nullifies this pointer (this happens when
+  // ActorDestroyed() is called).
+  IPCStreamSource* mSource;
+
   nsCOMPtr<nsIThread> mOwningThread;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
-NS_IMPL_ISUPPORTS(SendStreamChildImpl::Callback, nsIInputStreamCallback,
-                                                 nsIRunnable,
-                                                 nsICancelableRunnable);
+NS_IMPL_ISUPPORTS(IPCStreamSource::Callback, nsIInputStreamCallback,
+                                             nsIRunnable,
+                                             nsICancelableRunnable);
 
-SendStreamChildImpl::SendStreamChildImpl(nsIAsyncInputStream* aStream)
-  : mStream(aStream)
+IPCStreamSource::IPCStreamSource(nsIAsyncInputStream* aInputStream)
+  : mStream(aInputStream)
   , mWorkerPrivate(nullptr)
-  , mClosed(false)
+  , mState(ePending)
 {
-  MOZ_ASSERT(mStream);
+  MOZ_ASSERT(aInputStream);
 }
 
-SendStreamChildImpl::~SendStreamChildImpl()
+IPCStreamSource::~IPCStreamSource()
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(mClosed);
+  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
+  MOZ_ASSERT(mState == eClosed);
   MOZ_ASSERT(!mCallback);
   MOZ_ASSERT(!mWorkerPrivate);
 }
 
-void
-SendStreamChildImpl::Start()
+bool
+IPCStreamSource::Initialize()
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerPrivate);
-  DoRead();
+  bool nonBlocking = false;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mStream->IsNonBlocking(&nonBlocking)));
+  // IPCStreamChild reads in the current thread, so it is only supported on
+  // non-blocking, async channels
+  if (!nonBlocking) {
+    return false;
+  }
+
+  // A source can be used on any thread, but we only support IPCStream on
+  // main thread and Worker threads right now.  This is due to the requirement
+  // that the thread be guaranteed to live long enough to receive messages.
+  // We can enforce this guarantee with a feature on worker threads, but not
+  // other threads.
+  WorkerPrivate* workerPrivate = nullptr;
+  if (!NS_IsMainThread()) {
+    workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_RELEASE_ASSERT(workerPrivate);
+
+    bool result = HoldWorker(workerPrivate, Canceling);
+    if (!result) {
+      return false;
+    }
+
+    mWorkerPrivate = workerPrivate;
+  }
+
+  return true;
 }
 
 void
-SendStreamChildImpl::StartDestroy()
+IPCStreamSource::ActorConstructed()
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  OnEnd(NS_ERROR_ABORT);
+  MOZ_ASSERT(mState == ePending);
+  mState = eActorConstructed;
 }
 
 bool
-SendStreamChildImpl::AddAsWorkerHolder(WorkerPrivate* aWorkerPrivate)
+IPCStreamSource::Notify(Status aStatus)
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(aWorkerPrivate);
-  bool result = HoldWorker(aWorkerPrivate, Canceling);
-  if (result) {
-    mWorkerPrivate = aWorkerPrivate;
-  }
-  return result;
+  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
+
+  // Keep the worker thread alive until the stream is finished.
+  return true;
 }
 
 void
-SendStreamChildImpl::ActorDestroy(ActorDestroyReason aReason)
+IPCStreamSource::ActorDestroyed()
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
 
-  // If the parent side runs into a problem it will ask the child to
-  // close the connection via RequestClose().  Therefore OnEnd() should
-  // always run before the actor is destroyed.
-  MOZ_ASSERT(mClosed);
+  mState = eClosed;
 
   if (mCallback) {
-    mCallback->ClearActor();
+    mCallback->ClearSource();
     mCallback = nullptr;
   }
 
   if (mWorkerPrivate) {
     ReleaseWorker();
     mWorkerPrivate = nullptr;
   }
 }
 
-mozilla::ipc::IPCResult
-SendStreamChildImpl::RecvRequestClose(const nsresult& aRv)
+void
+IPCStreamSource::Start()
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  OnEnd(aRv);
-  return IPC_OK();
-}
-
-bool
-SendStreamChildImpl::Notify(Status aStatus)
-{
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-
-  // Keep the worker thread alive until the stream is finished.
-  return true;
+  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
+  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerPrivate);
+  DoRead();
 }
 
 void
-SendStreamChildImpl::DoRead()
+IPCStreamSource::StartDestroy()
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(!mClosed);
+  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
+  OnEnd(NS_ERROR_ABORT);
+}
+
+void
+IPCStreamSource::DoRead()
+{
+  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
+  MOZ_ASSERT(mState == eActorConstructed);
   MOZ_ASSERT(!mCallback);
 
   // The input stream (likely a pipe) probably uses a segment size of
   // 4kb.  If there is data already buffered it would be nice to aggregate
   // multiple segments into a single IPC call.  Conversely, don't send too
   // too large of a buffer in a single call to avoid spiking memory.
   static const uint64_t kMaxBytesPerMessage = 32 * 1024;
   static_assert(kMaxBytesPerMessage <= static_cast<uint64_t>(UINT32_MAX),
                 "kMaxBytesPerMessage must cleanly cast to uint32_t");
 
   while (true) {
     // It should not be possible to transition to closed state without
     // this loop terminating via a return.
-    MOZ_ASSERT(!mClosed);
+    MOZ_ASSERT(mState == eActorConstructed);
 
     // Use non-auto here as we're unlikely to hit stack storage with the
     // sizes we are sending.  Also, it would be nice to avoid another copy
     // to the IPC layer which we avoid if we use COW strings.  Unfortunately
     // IPC does not seem to support passing dependent storage types.
     nsCString buffer;
 
     uint64_t available = 0;
@@ -275,155 +242,76 @@ SendStreamChildImpl::DoRead()
 
     uint32_t bytesRead = 0;
     rv = mStream->Read(buffer.BeginWriting(), buffer.Length(), &bytesRead);
     MOZ_ASSERT_IF(NS_FAILED(rv), bytesRead == 0);
     buffer.SetLength(bytesRead);
 
     // If we read any data from the stream, send it across.
     if (!buffer.IsEmpty()) {
-      Unused << SendBuffer(buffer);
+      SendData(buffer);
     }
 
     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
       Wait();
       return;
     }
 
     // Any other error or zero-byte read indicates end-of-stream
     if (NS_FAILED(rv) || buffer.IsEmpty()) {
       OnEnd(rv);
       return;
     }
   }
 }
 
 void
-SendStreamChildImpl::Wait()
+IPCStreamSource::Wait()
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
-  MOZ_ASSERT(!mClosed);
+  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
+  MOZ_ASSERT(mState == eActorConstructed);
   MOZ_ASSERT(!mCallback);
 
   // Set mCallback immediately instead of waiting for success.  Its possible
   // AsyncWait() will callback synchronously.
   mCallback = new Callback(this);
   nsresult rv = mStream->AsyncWait(mCallback, 0, 0, nullptr);
   if (NS_FAILED(rv)) {
     OnEnd(rv);
     return;
   }
 }
 
 void
-SendStreamChildImpl::OnStreamReady(Callback* aCallback)
+IPCStreamSource::OnStreamReady(Callback* aCallback)
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
   MOZ_ASSERT(mCallback);
   MOZ_ASSERT(aCallback == mCallback);
-  mCallback->ClearActor();
+  mCallback->ClearSource();
   mCallback = nullptr;
   DoRead();
 }
 
 void
-SendStreamChildImpl::OnEnd(nsresult aRv)
+IPCStreamSource::OnEnd(nsresult aRv)
 {
-  NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
   MOZ_ASSERT(aRv != NS_BASE_STREAM_WOULD_BLOCK);
 
-  if (mClosed) {
+  if (mState == eClosed) {
     return;
   }
 
-  mClosed = true;
+  mState = eClosed;
 
   mStream->CloseWithStatus(aRv);
 
   if (aRv == NS_BASE_STREAM_CLOSED) {
     aRv = NS_OK;
   }
 
-  // This will trigger an ActorDestroy() from the parent side
-  Unused << SendClose(aRv);
-}
-
-bool
-IsBlocking(nsIAsyncInputStream* aInputStream)
-{
-  bool nonBlocking = false;
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aInputStream->IsNonBlocking(&nonBlocking)));
-  return !nonBlocking;
-}
-
-} // anonymous namespace
-
-// static
-SendStreamChild*
-SendStreamChild::Create(nsIAsyncInputStream* aInputStream,
-                        nsIContentChild* aManager)
-{
-  MOZ_ASSERT(aInputStream);
-  MOZ_ASSERT(aManager);
-
-  // PContent can only be used on the main thread
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // SendStreamChild reads in the current thread, so it is only supported
-  // on non-blocking, async channels
-  if (NS_WARN_IF(IsBlocking(aInputStream))) {
-    return nullptr;
-  }
-
-  SendStreamChild* actor = new SendStreamChildImpl(aInputStream);
-  aManager->SendPSendStreamConstructor(actor);
-
-  return actor;
-}
-
-// static
-SendStreamChild*
-SendStreamChild::Create(nsIAsyncInputStream* aInputStream,
-                        PBackgroundChild* aManager)
-{
-  MOZ_ASSERT(aInputStream);
-  MOZ_ASSERT(aManager);
-
-  // PBackground can be used on any thread, but we only support SendStream on
-  // main thread and Worker threads right now.  This is due to the requirement
-  // that the thread be guaranteed to live long enough to receive messages
-  // sent from parent to child.  We can enforce this guarantee with a feature
-  // on worker threads, but not other threads.
-  WorkerPrivate* workerPrivate = nullptr;
-  if (!NS_IsMainThread()) {
-    workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-  }
-
-  // SendStreamChild reads in the current thread, so it is only supported
-  // on non-blocking, async channels
-  if (NS_WARN_IF(IsBlocking(aInputStream))) {
-    return nullptr;
-  }
-
-  SendStreamChildImpl* actor = new SendStreamChildImpl(aInputStream);
-
-  if (workerPrivate && !actor->AddAsWorkerHolder(workerPrivate)) {
-    delete actor;
-    return nullptr;
-  }
-
-  aManager->SendPSendStreamConstructor(actor);
-  return actor;
-}
-
-SendStreamChild::~SendStreamChild()
-{
-}
-
-void
-DeallocPSendStreamChild(PSendStreamChild* aActor)
-{
-  delete aActor;
+  // This will trigger an ActorDestroy() from the other side
+  Close(aRv);
 }
 
 } // namespace ipc
 } // namespace mozilla
copy from ipc/glue/SendStream.h
copy to ipc/glue/IPCStreamSource.h
--- a/ipc/glue/SendStream.h
+++ b/ipc/glue/IPCStreamSource.h
@@ -1,101 +1,155 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_ipc_SendStream_h
-#define mozilla_ipc_SendStream_h
+#ifndef mozilla_ipc_IPCStreamSource_h
+#define mozilla_ipc_IPCStreamSource_h
 
 #include "mozilla/AlreadyAddRefed.h"
-#include "mozilla/ipc/PSendStreamChild.h"
-#include "mozilla/ipc/PSendStreamParent.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 
-class nsIInputStream;
 class nsIAsyncInputStream;
 
 namespace mozilla {
 
 namespace dom {
 class nsIContentChild;
+class nsIContentParent;
 } // dom namespace
 
 namespace ipc {
 
 class PBackgroundChild;
+class PBackgroundParent;
 
-// The SendStream IPC actor is designed to push an nsIInputStream from child to
-// parent incrementally.  This is mainly needed for streams such as nsPipe that
-// may not yet have all their data available when the stream must be sent across
-// an IPC boundary.  While many streams are handled by SerializeInputStream(),
-// these streams cannot be serialized and must be sent using this actor.
+// The IPCStream IPC actor is designed to push an nsIInputStream from child to
+// parent or parent to child incrementally.  This is mainly needed for streams
+// such as nsPipe that may not yet have all their data available when the
+// stream must be sent across an IPC boundary.  While many streams are handled
+// by SerializeInputStream(), these streams cannot be serialized and must be
+// sent using this actor.
 //
-// The SendStream actor only supports sending data from child to parent.
-//
-// The SendStream actor only support async, non-blocking streams because they
+// The IPCStream actor only support async, non-blocking streams because they
 // must be read inline on the main thread and Worker threads.
 //
-// In general, the creation and handling of the SendStream actor cannot be
+// In general, the creation and handling of the IPCStream actor cannot be
 // abstracted away behind SerializeInputStream() because the actor must be
 // carefully managed.  Specifically:
 //
 //  1) The data flow must be explicitly initiated by calling
-//     SendStreamChild::Start() after the actor has been sent to the parent.
-//  2) If the actor is never sent to the parent, then the child code must
-//     call SendStreamChild::StartDestroy() to avoid memory leaks.
-//  3) The SendStreamChild actor can only be used on threads that can be
+//     IPCStreamSource{Child,Parent}::Start() after the actor has been sent to
+//     the other-side actor.
+//  2) If the actor is never sent to the other-side, then this code must
+//     call IPCStreamSource{Child,Parent}::StartDestroy() to avoid memory leaks.
+//  3) The IPCStreamSource actor can only be used on threads that can be
 //     guaranteed to stay alive as long as the actor is alive.  Right now
-//     this limits SendStream to the main thread and Worker threads.
+//     this limits IPCStream to the main thread and Worker threads.
 //
-// In general you should probably use the AutoIPCStreamChild RAII class
-// defined in InputStreamUtils.h instead of using SendStreamChild directly.
-class SendStreamChild : public PSendStreamChild
+// In general you should probably use the AutoIPCStreamSource RAII class
+// defined in InputStreamUtils.h instead of using IPCStreamSource directly.
+class IPCStreamSource : public dom::workers::WorkerHolder
 {
 public:
-  // Create a SendStreamChild using a PContent IPC manager on the
+  // Create a IPCStreamSource using a PContent IPC manager on the
   // main thread.  This can return nullptr if the provided stream is
   // blocking.
-  static SendStreamChild*
+  static PChildToParentStreamChild*
   Create(nsIAsyncInputStream* aInputStream, dom::nsIContentChild* aManager);
 
-  // Create a SendStreamChild using a PBackground IPC manager on the
+  // Create a IPCStreamSource using a PBackground IPC manager on the
   // main thread or a Worker thread.  This can return nullptr if the provided
   // stream is blocking or if the Worker thread is already shutting down.
-  static SendStreamChild*
+  static PChildToParentStreamChild*
   Create(nsIAsyncInputStream* aInputStream, PBackgroundChild* aManager);
 
+  // Create a IPCStreamSource using a PContent IPC manager on the
+  // main thread.  This can return nullptr if the provided stream is
+  // blocking.
+  static PParentToChildStreamParent*
+  Create(nsIAsyncInputStream* aInputStream, dom::nsIContentParent* aManager);
+
+  // Create a IPCStreamSource using a PBackground IPC manager on the
+  // main thread or a Worker thread.  This can return nullptr if the provided
+  // stream is blocking or if the Worker thread is already shutting down.
+  static PParentToChildStreamParent*
+  Create(nsIAsyncInputStream* aInputStream, PBackgroundParent* aManager);
+
+  static IPCStreamSource*
+  Cast(PChildToParentStreamChild* aActor);
+
+  static IPCStreamSource*
+  Cast(PParentToChildStreamParent* aActor);
+
   // Start reading data from the nsIAsyncInputStream used to create the actor.
   // This must be called after the actor is passed to the parent.  If you
   // use AutoIPCStream this is handled automatically.
-  virtual void
-  Start() = 0;
+  void
+  Start();
 
   // Start cleaning up the actor.  This must be called if the actor is never
-  // sent to the parent.  If you use AutoIPCStream this is handled
+  // sent to the other side.  If you use AutoIPCStream this is handled
   // automatically.
-  virtual void
-  StartDestroy() = 0;
+  void
+  StartDestroy();
 
 protected:
-  virtual
-  ~SendStreamChild() = 0;
-};
+  IPCStreamSource(nsIAsyncInputStream* aInputStream);
+  virtual ~IPCStreamSource();
+
+  bool Initialize();
+
+  void ActorDestroyed();
+
+  void OnEnd(nsresult aRv);
+
+  virtual void
+  Close(nsresult aRv) = 0;
+
+  virtual void
+  SendData(const nsCString& aBuffer) = 0;
+
+  void
+  ActorConstructed();
+
+private:
+  class Callback;
+
+  // WorkerHolder methods
+  virtual bool
+  Notify(dom::workers::Status aStatus) override;
 
-// On the parent side, you must simply call TakeReader() upon receiving a
-// reference to the SendStreamParent actor.  You do not need to maintain a
-// reference to the actor itself.
-class SendStreamParent : public PSendStreamParent
-{
-public:
-  virtual already_AddRefed<nsIInputStream>
-  TakeReader() = 0;
+  void DoRead();
+
+  void Wait();
+
+  void OnStreamReady(Callback* aCallback);
+
+  nsCOMPtr<nsIAsyncInputStream> mStream;
+  RefPtr<Callback> mCallback;
 
+  // Raw pointer because this IPCStreamSource keeps the worker alive using a
+  // WorkerHolder. The worker is kept alive when the actor is created and,
+  // released when the actor is destroyed.
+  dom::workers::WorkerPrivate* mWorkerPrivate;
+
+#ifdef DEBUG
 protected:
-  virtual
-  ~SendStreamParent() = 0;
+#endif
+
+  enum {
+   ePending,
+   eActorConstructed,
+   eClosed
+  } mState;
+
+private:
+  NS_DECL_OWNINGTHREAD
 };
 
 } // namespace ipc
 } // namespace mozilla
 
-#endif // mozilla_ipc_SendStream_h
+#endif // mozilla_ipc_IPCStreamSource_h
--- a/ipc/glue/IPCStreamUtils.cpp
+++ b/ipc/glue/IPCStreamUtils.cpp
@@ -10,96 +10,103 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/PContentParent.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/IPCStreamDestination.h"
+#include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundParent.h"
-#include "mozilla/ipc/SendStream.h"
 #include "mozilla/Unused.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIPipe.h"
 #include "nsStreamUtils.h"
 
 namespace mozilla {
 namespace ipc {
 
 namespace {
 
+void
+AssertValidValueToTake(const IPCStream& aVal)
+{
+  MOZ_ASSERT(aVal.type() == IPCStream::TPChildToParentStreamChild ||
+             aVal.type() == IPCStream::TPParentToChildStreamParent ||
+             aVal.type() == IPCStream::TInputStreamParamsWithFds);
+}
+
+void
+AssertValidValueToTake(const OptionalIPCStream& aVal)
+{
+  MOZ_ASSERT(aVal.type() == OptionalIPCStream::Tvoid_t ||
+             aVal.type() == OptionalIPCStream::TIPCStream);
+  if (aVal.type() == OptionalIPCStream::TIPCStream) {
+    AssertValidValueToTake(aVal.get_IPCStream());
+  }
+}
+
 // These serialization and cleanup functions could be externally exposed.  For
 // now, though, keep them private to encourage use of the safer RAII
 // AutoIPCStream class.
 
 template<typename M>
-void
-SerializeInputStreamWithFdsChild(nsIInputStream* aStream,
+bool
+SerializeInputStreamWithFdsChild(nsIIPCSerializableInputStream* aStream,
                                  IPCStream& aValue,
                                  M* aManager)
 {
-  MOZ_ASSERT(aStream);
+  MOZ_RELEASE_ASSERT(aStream);
   MOZ_ASSERT(aManager);
 
-  // First attempt simple stream serialization
-  nsCOMPtr<nsIIPCSerializableInputStream> serializable =
-    do_QueryInterface(aStream);
-  if (!serializable) {
-    MOZ_CRASH("Input stream is not serializable!");
-  }
-
   aValue = InputStreamParamsWithFds();
   InputStreamParamsWithFds& streamWithFds =
     aValue.get_InputStreamParamsWithFds();
 
   AutoTArray<FileDescriptor, 4> fds;
-  serializable->Serialize(streamWithFds.stream(), fds);
+  aStream->Serialize(streamWithFds.stream(), fds);
 
   if (streamWithFds.stream().type() == InputStreamParams::T__None) {
     MOZ_CRASH("Serialize failed!");
   }
 
   if (fds.IsEmpty()) {
     streamWithFds.optionalFds() = void_t();
   } else {
     PFileDescriptorSetChild* fdSet =
       aManager->SendPFileDescriptorSetConstructor(fds[0]);
     for (uint32_t i = 1; i < fds.Length(); ++i) {
       Unused << fdSet->SendAddFileDescriptor(fds[i]);
     }
 
     streamWithFds.optionalFds() = fdSet;
   }
+
+  return true;
 }
 
 template<typename M>
-void
-SerializeInputStreamWithFdsParent(nsIInputStream* aStream,
+bool
+SerializeInputStreamWithFdsParent(nsIIPCSerializableInputStream* aStream,
                                   IPCStream& aValue,
                                   M* aManager)
 {
-  MOZ_ASSERT(aStream);
+  MOZ_RELEASE_ASSERT(aStream);
   MOZ_ASSERT(aManager);
 
-  // First attempt simple stream serialization
-  nsCOMPtr<nsIIPCSerializableInputStream> serializable =
-    do_QueryInterface(aStream);
-  if (!serializable) {
-    MOZ_CRASH("Input stream is not serializable!");
-  }
-
   aValue = InputStreamParamsWithFds();
   InputStreamParamsWithFds& streamWithFds =
     aValue.get_InputStreamParamsWithFds();
 
   AutoTArray<FileDescriptor, 4> fds;
-  serializable->Serialize(streamWithFds.stream(), fds);
+  aStream->Serialize(streamWithFds.stream(), fds);
 
   if (streamWithFds.stream().type() == InputStreamParams::T__None) {
     MOZ_CRASH("Serialize failed!");
   }
 
   streamWithFds.optionalFds() = void_t();
   if (!fds.IsEmpty()) {
     PFileDescriptorSetParent* fdSet =
@@ -111,94 +118,127 @@ SerializeInputStreamWithFdsParent(nsIInp
         break;
       }
     }
 
     if (fdSet) {
       streamWithFds.optionalFds() = fdSet;
     }
   }
+
+  return true;
 }
 
 template<typename M>
-void
-SerializeInputStreamWithFdsParent(nsIInputStream* aStream,
-                                  OptionalIPCStream& aValue,
-                                  M* aManager)
-{
-  if (!aStream) {
-    aValue = void_t();
-    return;
-  }
-
-  aValue = IPCStream();
-  SerializeInputStreamWithFdsParent(aStream, aValue.get_IPCStream(), aManager);
-}
-
-template<typename M>
-void
+bool
 SerializeInputStream(nsIInputStream* aStream, IPCStream& aValue, M* aManager)
 {
   MOZ_ASSERT(aStream);
   MOZ_ASSERT(aManager);
 
+  // As a fallback, attempt to stream the data across using a IPCStream
+  // actor. For blocking streams, create a nonblocking pipe instead,
+  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
+  if (!asyncStream) {
+    const uint32_t kBufferSize = 32768; // matches IPCStream buffer size.
+    nsCOMPtr<nsIAsyncOutputStream> sink;
+    nsresult rv = NS_NewPipe2(getter_AddRefs(asyncStream),
+                              getter_AddRefs(sink),
+                              true,
+                              false,
+                              kBufferSize,
+                              UINT32_MAX);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    nsCOMPtr<nsIEventTarget> target =
+      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+
+    rv = NS_AsyncCopy(aStream, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS,
+                      kBufferSize);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+  }
+
+  MOZ_ASSERT(asyncStream);
+  aValue = IPCStreamSource::Create(asyncStream, aManager);
+  return true;
+}
+
+template<typename M>
+bool
+SerializeInputStreamChild(nsIInputStream* aStream, M* aManager,
+                          IPCStream* aValue,
+                          OptionalIPCStream* aOptionalValue)
+{
+  MOZ_ASSERT(aStream);
+  MOZ_ASSERT(aManager);
+  MOZ_ASSERT(aValue || aOptionalValue);
+
   // If a stream is known to be larger than 1MB, prefer sending it in chunks.
   const uint64_t kTooLargeStream = 1024 * 1024;
 
-  // First attempt simple stream serialization
+  nsCOMPtr<nsIIPCSerializableInputStream> serializable =
+    do_QueryInterface(aStream);
+
+  // ExpectedSerializedLength() returns the length of the stream if serialized.
+  // This is useful to decide if we want to continue using the serialization
+  // directly, or if it's better to use IPCStream.
+  uint64_t expectedLength =
+    serializable ? serializable->ExpectedSerializedLength().valueOr(0) : 0;
+  if (serializable && expectedLength < kTooLargeStream) {
+    if (aValue) {
+      return SerializeInputStreamWithFdsChild(serializable, *aValue, aManager);
+    }
+
+    return SerializeInputStreamWithFdsChild(serializable, *aOptionalValue,
+                                            aManager);
+  }
+
+  if (aValue) {
+    return SerializeInputStream(aStream, *aValue, aManager);
+  }
+
+  return SerializeInputStream(aStream, *aOptionalValue, aManager);
+}
+
+template<typename M>
+bool
+SerializeInputStreamParent(nsIInputStream* aStream, M* aManager,
+                           IPCStream* aValue,
+                           OptionalIPCStream* aOptionalValue)
+{
+  MOZ_ASSERT(aStream);
+  MOZ_ASSERT(aManager);
+  MOZ_ASSERT(aValue || aOptionalValue);
+
+  // If a stream is known to be larger than 1MB, prefer sending it in chunks.
+  const uint64_t kTooLargeStream = 1024 * 1024;
+
   nsCOMPtr<nsIIPCSerializableInputStream> serializable =
     do_QueryInterface(aStream);
   uint64_t expectedLength =
     serializable ? serializable->ExpectedSerializedLength().valueOr(0) : 0;
+
   if (serializable && expectedLength < kTooLargeStream) {
-    SerializeInputStreamWithFdsChild(aStream, aValue, aManager);
-    return;
+    if (aValue) {
+      return SerializeInputStreamWithFdsParent(serializable, *aValue, aManager);
+    }
+
+    return SerializeInputStreamWithFdsParent(serializable, *aOptionalValue,
+                                             aManager);
   }
 
-  // As a fallback, attempt to stream the data across using a SendStream
-  // actor. For blocking streams, create a nonblocking pipe instead,
-  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
-  if (!asyncStream) {
-    const uint32_t kBufferSize = 32768; // matches SendStream buffer size.
-    nsCOMPtr<nsIAsyncOutputStream> sink;
-    DebugOnly<nsresult> rv = NS_NewPipe2(getter_AddRefs(asyncStream),
-                                         getter_AddRefs(sink),
-                                         true,
-                                         false,
-                                         kBufferSize,
-                                         UINT32_MAX);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-    nsCOMPtr<nsIEventTarget> target =
-        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-
-    rv = NS_AsyncCopy(aStream, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS, kBufferSize);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
+  if (aValue) {
+    return SerializeInputStream(aStream, *aValue, aManager);
   }
 
-  MOZ_ASSERT(asyncStream);
-  aValue = SendStreamChild::Create(asyncStream, aManager);
-
-  if (!aValue.get_PSendStreamChild()) {
-    MOZ_CRASH("SendStream creation failed!");
-  }
-}
-
-template<typename M>
-void
-SerializeInputStream(nsIInputStream* aStream, OptionalIPCStream& aValue,
-                     M* aManager)
-{
-  if (!aStream) {
-    aValue = void_t();
-    return;
-  }
-
-  aValue = IPCStream();
-  SerializeInputStream(aStream, aValue.get_IPCStream(), aManager);
+  return SerializeInputStream(aStream, *aOptionalValue, aManager);
 }
 
 void
 CleanupIPCStream(IPCStream& aValue, bool aConsumedByIPC)
 {
   if (aValue.type() == IPCStream::T__None) {
     return;
   }
@@ -244,54 +284,86 @@ CleanupIPCStream(IPCStream& aValue, bool
       if (!aConsumedByIPC) {
         Unused << fdSetActor->Send__delete__(fdSetActor);
       }
     }
 
     return;
   }
 
-  MOZ_ASSERT(aValue.type() == IPCStream::TPSendStreamChild);
+  IPCStreamSource* source = nullptr;
+  if (aValue.type() == IPCStream::TPChildToParentStreamChild) {
+    source = IPCStreamSource::Cast(aValue.get_PChildToParentStreamChild());
+  } else {
+    MOZ_ASSERT(aValue.type() == IPCStream::TPParentToChildStreamParent);
+    source = IPCStreamSource::Cast(aValue.get_PParentToChildStreamParent());
+  }
 
-  auto sendStream =
-    static_cast<SendStreamChild*>(aValue.get_PSendStreamChild());
+  MOZ_ASSERT(source);
 
   if (!aConsumedByIPC) {
-    sendStream->StartDestroy();
+    source->StartDestroy();
     return;
   }
 
-  // If the SendStream was taken to be sent to the parent, then we need to
-  // start it before forgetting about it.
-  sendStream->Start();
+  // If the source stream was taken to be sent to the other side, then we need
+  // to start it before forgetting about it.
+  source->Start();
 }
 
 void
 CleanupIPCStream(OptionalIPCStream& aValue, bool aConsumedByIPC)
 {
   if (aValue.type() == OptionalIPCStream::Tvoid_t) {
     return;
   }
 
   CleanupIPCStream(aValue.get_IPCStream(), aConsumedByIPC);
 }
 
+// Returns false if the serialization should not proceed. This means that the
+// inputStream is null.
+bool
+NormalizeOptionalValue(nsIInputStream* aStream,
+                       IPCStream* aValue,
+                       OptionalIPCStream* aOptionalValue)
+{
+  if (aValue) {
+    // if aStream is null, we will crash when serializing.
+    return true;
+  }
+
+  if (!aStream) {
+    *aOptionalValue = void_t();
+    return false;
+  }
+
+  *aOptionalValue = IPCStream();
+  return true;
+}
+
 } // anonymous namespace
 
 already_AddRefed<nsIInputStream>
 DeserializeIPCStream(const IPCStream& aValue)
 {
-  if (aValue.type() == IPCStream::TPSendStreamParent) {
+  if (aValue.type() == IPCStream::TPChildToParentStreamParent) {
     auto sendStream =
-      static_cast<SendStreamParent*>(aValue.get_PSendStreamParent());
+      IPCStreamDestination::Cast(aValue.get_PChildToParentStreamParent());
     return sendStream->TakeReader();
   }
 
-  // Note, we explicitly do not support deserializing the PSendStream actor on
-  // the child side.  It can only be sent from child to parent.
+  if (aValue.type() == IPCStream::TPParentToChildStreamChild) {
+    auto sendStream =
+      IPCStreamDestination::Cast(aValue.get_PParentToChildStreamChild());
+    return sendStream->TakeReader();
+  }
+
+  // Note, we explicitly do not support deserializing the PChildToParentStream actor on
+  // the child side nor the PParentToChildStream actor on the parent side.
   MOZ_ASSERT(aValue.type() == IPCStream::TInputStreamParamsWithFds);
 
   const InputStreamParamsWithFds& streamWithFds =
     aValue.get_InputStreamParamsWithFds();
 
   AutoTArray<FileDescriptor, 4> fds;
   if (streamWithFds.optionalFds().type() ==
       OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
@@ -328,37 +400,16 @@ DeserializeIPCStream(const OptionalIPCSt
 {
   if (aValue.type() == OptionalIPCStream::Tvoid_t) {
     return nullptr;
   }
 
   return DeserializeIPCStream(aValue.get_IPCStream());
 }
 
-namespace {
-
-void
-AssertValidValueToTake(const IPCStream& aVal)
-{
-  MOZ_ASSERT(aVal.type() == IPCStream::TPSendStreamChild ||
-             aVal.type() == IPCStream::TInputStreamParamsWithFds);
-}
-
-void
-AssertValidValueToTake(const OptionalIPCStream& aVal)
-{
-  MOZ_ASSERT(aVal.type() == OptionalIPCStream::Tvoid_t ||
-             aVal.type() == OptionalIPCStream::TIPCStream);
-  if (aVal.type() == OptionalIPCStream::TIPCStream) {
-    AssertValidValueToTake(aVal.get_IPCStream());
-  }
-}
-
-} // anonymous namespace
-
 AutoIPCStream::AutoIPCStream()
   : mInlineValue(void_t())
   , mValue(nullptr)
   , mOptionalValue(&mInlineValue)
   , mTaken(false)
 {
 }
 
@@ -384,86 +435,123 @@ AutoIPCStream::~AutoIPCStream()
   MOZ_ASSERT(mValue || mOptionalValue);
   if (mValue && IsSet()) {
     CleanupIPCStream(*mValue, mTaken);
   } else {
     CleanupIPCStream(*mOptionalValue, mTaken);
   }
 }
 
-void
+bool
 AutoIPCStream::Serialize(nsIInputStream* aStream, dom::nsIContentChild* aManager)
 {
   MOZ_ASSERT(aStream || !mValue);
   MOZ_ASSERT(aManager);
   MOZ_ASSERT(mValue || mOptionalValue);
   MOZ_ASSERT(!mTaken);
   MOZ_ASSERT(!IsSet());
 
+  // If NormalizeOptionalValue returns false, we don't have to proceed.
+  if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) {
+    return true;
+  }
+
+  if (!SerializeInputStreamChild(aStream, aManager, mValue, mOptionalValue)) {
+    MOZ_CRASH("IPCStream creation failed!");
+  }
+
   if (mValue) {
-    SerializeInputStream(aStream, *mValue, aManager);
     AssertValidValueToTake(*mValue);
   } else {
-    SerializeInputStream(aStream, *mOptionalValue, aManager);
     AssertValidValueToTake(*mOptionalValue);
   }
+
+  return true;
 }
 
-void
+bool
 AutoIPCStream::Serialize(nsIInputStream* aStream, PBackgroundChild* aManager)
 {
   MOZ_ASSERT(aStream || !mValue);
   MOZ_ASSERT(aManager);
   MOZ_ASSERT(mValue || mOptionalValue);
   MOZ_ASSERT(!mTaken);
   MOZ_ASSERT(!IsSet());
 
+  // If NormalizeOptionalValue returns false, we don't have to proceed.
+  if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) {
+    return true;
+  }
+
+  if (!SerializeInputStreamChild(aStream, aManager, mValue, mOptionalValue)) {
+    MOZ_CRASH("IPCStream creation failed!");
+  }
+
   if (mValue) {
-    SerializeInputStream(aStream, *mValue, aManager);
     AssertValidValueToTake(*mValue);
   } else {
-    SerializeInputStream(aStream, *mOptionalValue, aManager);
     AssertValidValueToTake(*mOptionalValue);
   }
+
+  return true;
 }
 
-void
-AutoIPCStream::Serialize(nsIInputStream* aStream, dom::PContentParent* aManager)
+bool
+AutoIPCStream::Serialize(nsIInputStream* aStream,
+                         dom::nsIContentParent* aManager)
 {
   MOZ_ASSERT(aStream || !mValue);
   MOZ_ASSERT(aManager);
   MOZ_ASSERT(mValue || mOptionalValue);
   MOZ_ASSERT(!mTaken);
   MOZ_ASSERT(!IsSet());
 
+  // If NormalizeOptionalValue returns false, we don't have to proceed.
+  if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) {
+    return true;
+  }
+
+  if (!SerializeInputStreamParent(aStream, aManager, mValue, mOptionalValue)) {
+    return false;
+  }
+
   if (mValue) {
-    SerializeInputStreamWithFdsParent(aStream, *mValue, aManager);
     AssertValidValueToTake(*mValue);
   } else {
-    SerializeInputStreamWithFdsParent(aStream, *mOptionalValue, aManager);
     AssertValidValueToTake(*mOptionalValue);
   }
+
+  return true;
 }
 
-void
+bool
 AutoIPCStream::Serialize(nsIInputStream* aStream, PBackgroundParent* aManager)
 {
   MOZ_ASSERT(aStream || !mValue);
   MOZ_ASSERT(aManager);
   MOZ_ASSERT(mValue || mOptionalValue);
   MOZ_ASSERT(!mTaken);
   MOZ_ASSERT(!IsSet());
 
+  // If NormalizeOptionalValue returns false, we don't have to proceed.
+  if (!NormalizeOptionalValue(aStream, mValue, mOptionalValue)) {
+    return true;
+  }
+
+  if (!SerializeInputStreamParent(aStream, aManager, mValue, mOptionalValue)) {
+    return false;
+  }
+
   if (mValue) {
-    SerializeInputStreamWithFdsParent(aStream, *mValue, aManager);
     AssertValidValueToTake(*mValue);
   } else {
-    SerializeInputStreamWithFdsParent(aStream, *mOptionalValue, aManager);
     AssertValidValueToTake(*mOptionalValue);
   }
+
+  return true;
 }
 
 bool
 AutoIPCStream::IsSet() const
 {
   MOZ_ASSERT(mValue || mOptionalValue);
   if (mValue) {
     return mValue->type() != IPCStream::T__None;
--- a/ipc/glue/IPCStreamUtils.h
+++ b/ipc/glue/IPCStreamUtils.h
@@ -9,34 +9,34 @@
 
 #include "mozilla/ipc/IPCStream.h"
 #include "nsIInputStream.h"
 
 namespace mozilla {
 
 namespace dom {
 class nsIContentChild;
-class PContentParent;
+class nsIContentParent;
 }
 
 namespace ipc {
 
 class PBackgroundChild;
 class PBackgroundParent;
 
 // Deserialize an IPCStream received from an actor call.  These methods
 // work in both the child and parent.
 already_AddRefed<nsIInputStream>
 DeserializeIPCStream(const IPCStream& aValue);
 
 already_AddRefed<nsIInputStream>
 DeserializeIPCStream(const OptionalIPCStream& aValue);
 
 // RAII helper class that serializes an nsIInputStream into an IPCStream struct.
-// Any file descriptor or PSendStream actors are automatically managed
+// Any file descriptor or PChildToParentStream actors are automatically managed
 // correctly.
 //
 // Here is a simple example:
 //
 //  // in ipdl file
 //  Protocol PMyStuff
 //  {
 //  parent:
@@ -55,17 +55,17 @@ DeserializeIPCStream(const OptionalIPCSt
 //
 //  // in parent c++ code
 //  bool
 //  MyStuffParent::RecvDoStuff(const IPCStream& aIPCStream) {
 //    nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aIPCStream);
 //    // Do something with stream...
 //
 //    // You can also serialize streams from parent-to-child as long as
-//    // they don't require PSendStream actor support.
+//    // they don't require PChildToParentStream actor support.
 //    AutoIPCStream anotherStream;
 //    anotherStream.Serialize(mFileStream, Manager());
 //    SendStuffDone(anotherStream.TakeValue());
 //  }
 //
 // The AutoIPCStream RAII class may also be used if your stream is embedded
 // in a more complex IPDL structure.  In this case you attach the AutoIPCStream
 // to the embedded IPCStream and call TakeValue() after you pass the structure.
@@ -96,24 +96,27 @@ DeserializeIPCStream(const OptionalIPCSt
 //
 //  // in parent c++ code
 //  bool
 //  MyStuffParent::RecvDoStuff(const Stuff& aStuff) {
 //    nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStuff.stream());
 //    /* do something with the nsIInputStream */
 //  }
 //
+// Note: This example is about child-to-parent inputStream, but AutoIPCStream
+// works also parent-to-child.
+//
 // The AutoIPCStream class also supports OptionalIPCStream values.  As long as
 // you did not initialize the object with a non-optional IPCStream, you can call
 // TakeOptionalValue() instead.
 //
 // The AutoIPCStream class can also be used to serialize nsIInputStream objects
 // on the parent side to send to the child.  Currently, however, this only
-// works for directly serializable stream types.  The PSendStream actor mechanism
-// is not supported in this direction yet.
+// works for directly serializable stream types.  The PChildToParentStream actor
+// mechanism is not supported in this direction yet.
 //
 // Like SerializeInputStream(), the AutoIPCStream will crash if
 // serialization cannot be completed.
 //
 // NOTE: This is not a MOZ_STACK_CLASS so that it can be more easily integrated
 //       with complex ipdl structures.  For example, you may want to create an
 //       array of RAII AutoIPCStream objects or build your own wrapping
 //       RAII object to handle other actors that need to be cleaned up.
@@ -141,45 +144,43 @@ public:
   // or TakeOptionalValue can be used.
   explicit AutoIPCStream(OptionalIPCStream& aTarget);
 
   ~AutoIPCStream();
 
   // Serialize the input stream or create a SendStream actor using the PContent
   // manager.  If neither of these succeed, then crash.  This should only be
   // used on the main thread.
-  void
+  bool
   Serialize(nsIInputStream* aStream, dom::nsIContentChild* aManager);
 
   // Serialize the input stream or create a SendStream actor using the
   // PBackground manager.  If neither of these succeed, then crash.  This can
   // be called on the main thread or Worker threads.
-  void
+  bool
   Serialize(nsIInputStream* aStream, PBackgroundChild* aManager);
 
-  // Serialize the input stream.  A PSendStream cannot be used when going
-  // from parent-to-child.
-  void
-  Serialize(nsIInputStream* aStream, dom::PContentParent* aManager);
+  // Serialize the input stream.
+  MOZ_MUST_USE bool
+  Serialize(nsIInputStream* aStream, dom::nsIContentParent* aManager);
 
-  // Serialize the input stream.  A PSendStream cannot be used when going
-  // from parent-to-child.
-  void
+  // Serialize the input stream.
+  MOZ_MUST_USE bool
   Serialize(nsIInputStream* aStream, PBackgroundParent* aManager);
 
   // Get the IPCStream as a non-optional value.  This will
   // assert if a stream has not been serialized or if it has already been taken.
   // This should only be called if the value is being, or has already been, sent
-  // to the parent
+  // to the other side.
   IPCStream&
   TakeValue();
 
   // Get the OptionalIPCStream value.  This will assert if
   // the value has already been taken.  This should only be called if the value
-  // is being, or has already been, sent to the parent
+  // is being, or has already been, sent to the other side.
   OptionalIPCStream&
   TakeOptionalValue();
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_IPCStreamUtils_h
--- a/ipc/glue/MessageLink.cpp
+++ b/ipc/glue/MessageLink.cpp
@@ -13,16 +13,17 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDebug.h"
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 #include "nsISupportsImpl.h"
+#include "nsPrintfCString.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla;
 using namespace std;
 
 // We rely on invariants about the lifetime of the transport:
 //
 //  - outlives this MessageChannel
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -13,17 +13,18 @@ include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PFileSystemRequest;
 include protocol PGamepadEventChannel;
 include protocol PGamepadTestChannel;
 include protocol PMessagePort;
 include protocol PCameras;
 include protocol PQuota;
-include protocol PSendStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
 include protocol PServiceWorkerManager;
 include protocol PUDPSocket;
 include protocol PVsync;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
 include PFileSystemParams;
@@ -56,17 +57,18 @@ sync protocol PBackground
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PFileSystemRequest;
   manages PGamepadEventChannel;
   manages PGamepadTestChannel;
   manages PMessagePort;
   manages PCameras;
   manages PQuota;
-  manages PSendStream;
+  manages PChildToParentStream;
+  manages PParentToChildStream;
   manages PServiceWorkerManager;
   manages PUDPSocket;
   manages PVsync;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   async PBackgroundTest(nsCString testArg);
 
@@ -87,17 +89,17 @@ parent:
   async PServiceWorkerManager();
 
   async ShutdownServiceWorkerRegistrar();
 
   async PCacheStorage(Namespace aNamespace, PrincipalInfo aPrincipalInfo);
 
   async PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
 
-  async PSendStream();
+  async PChildToParentStream();
 
   async MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
 
   async PAsmJSCacheEntry(OpenMode openMode,
                          WriteParams write,
                          PrincipalInfo principalInfo);
 
   async PQuota();
@@ -107,16 +109,18 @@ parent:
   async PGamepadEventChannel();
 
   async PGamepadTestChannel();
 
 child:
   async PCache();
   async PCacheStreamControl();
 
+  async PParentToChildStream();
+
 both:
   async PBlob(BlobConstructorParams params);
 
   async PFileDescriptorSet(FileDescriptor fd);
 };
 
 } // namespace ipc
 } // namespace mozilla
rename from ipc/glue/PSendStream.ipdl
rename to ipc/glue/PChildToParentStream.ipdl
--- a/ipc/glue/PSendStream.ipdl
+++ b/ipc/glue/PChildToParentStream.ipdl
@@ -4,17 +4,19 @@
 
 include protocol PBackground;
 include protocol PContent;
 include protocol PContentBridge;
 
 namespace mozilla {
 namespace ipc {
 
-protocol PSendStream
+// This is protocol is the opposite of PParentToChildStream. Please keep these
+// protocols in sync.
+protocol PChildToParentStream
 {
   manager PBackground or PContent or PContentBridge;
 
 parent:
   async Buffer(nsCString aBuffer);
   async Close(nsresult aRv);
 
 child:
copy from ipc/glue/PSendStream.ipdl
copy to ipc/glue/PParentToChildStream.ipdl
--- a/ipc/glue/PSendStream.ipdl
+++ b/ipc/glue/PParentToChildStream.ipdl
@@ -4,25 +4,27 @@
 
 include protocol PBackground;
 include protocol PContent;
 include protocol PContentBridge;
 
 namespace mozilla {
 namespace ipc {
 
-protocol PSendStream
+// This is protocol is the opposite of PChildToParentStream. Please keep these
+// protocols in sync.
+protocol PParentToChildStream
 {
   manager PBackground or PContent or PContentBridge;
 
-parent:
+child:
   async Buffer(nsCString aBuffer);
   async Close(nsresult aRv);
 
-child:
+parent:
   // The parent side has hit an error condition and has requested the child
   // actor issue a Close() message.  The close must be initiated by the child
   // to avoid racing with an in-flight Buffer() message.
   async RequestClose(nsresult aRv);
 
   // Stream is always destroyed from the parent side.  This occurs if the
   // parent encounters an error while writing to its pipe or if the child
   // signals the stream should close by SendClose().
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -22,25 +22,26 @@ EXPORTS.mozilla.ipc += [
     'CrossProcessSemaphore.h',
     'FileDescriptor.h',
     'FileDescriptorSetChild.h',
     'FileDescriptorSetParent.h',
     'FileDescriptorUtils.h',
     'GeckoChildProcessHost.h',
     'InputStreamUtils.h',
     'IOThreadChild.h',
+    'IPCStreamAlloc.h',
+    'IPCStreamDestination.h',
+    'IPCStreamSource.h',
     'IPCStreamUtils.h',
     'MessageChannel.h',
     'MessageLink.h',
     'Neutering.h',
     'ProcessChild.h',
     'ProtocolUtils.h',
     'ScopedXREEmbed.h',
-    'SendStream.h',
-    'SendStreamAlloc.h',
     'SharedMemory.h',
     'SharedMemoryBasic.h',
     'Shmem.h',
     'TaskFactory.h',
     'Transport.h',
     'URIUtils.h',
     'WindowsMessageLoop.h',
 ]
@@ -138,25 +139,27 @@ UNIFIED_SOURCES += [
     'BrowserProcessSubThread.cpp',
     'CrashReporterClient.cpp',
     'CrashReporterHost.cpp',
     'CrashReporterMetadataShmem.cpp',
     'FileDescriptor.cpp',
     'FileDescriptorUtils.cpp',
     'InputStreamUtils.cpp',
     'IPCMessageUtils.cpp',
+    'IPCStreamChild.cpp',
+    'IPCStreamDestination.cpp',
+    'IPCStreamParent.cpp',
+    'IPCStreamSource.cpp',
     'IPCStreamUtils.cpp',
     'MessageChannel.cpp',
     'MessageLink.cpp',
     'MessagePump.cpp',
     'ProcessChild.cpp',
     'ProtocolUtils.cpp',
     'ScopedXREEmbed.cpp',
-    'SendStreamChild.cpp',
-    'SendStreamParent.cpp',
     'SharedMemory.cpp',
     'Shmem.cpp',
     'StringUtil.cpp',
 ]
 
 # GeckoChildProcessHost.cpp cannot be built in unified mode because it uses plarena.h.
 # URIUtils.cpp cannot be built in unified mode because of name clashes on strdup.
 SOURCES += [
@@ -184,19 +187,20 @@ LOCAL_INCLUDES += [
 ]
 
 IPDL_SOURCES = [
     'InputStreamParams.ipdlh',
     'IPCStream.ipdlh',
     'PBackground.ipdl',
     'PBackgroundSharedTypes.ipdlh',
     'PBackgroundTest.ipdl',
+    'PChildToParentStream.ipdl',
     'PFileDescriptorSet.ipdl',
+    'PParentToChildStream.ipdl',
     'ProtocolTypes.ipdlh',
-    'PSendStream.ipdl',
     'URIParams.ipdlh',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/ipc',
     '/toolkit/crashreporter',
     '/toolkit/xre',
     '/xpcom/threads',
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1270,17 +1270,20 @@ NewExternalString(JSContext* cx, unsigne
     if (args.length() != 1 || !args[0].isString()) {
         JS_ReportErrorASCII(cx, "newExternalString takes exactly one string argument.");
         return false;
     }
 
     RootedString str(cx, args[0].toString());
     size_t len = str->length();
 
-    UniqueTwoByteChars buf(js_pod_malloc<char16_t>(len));
+    UniqueTwoByteChars buf(cx->pod_malloc<char16_t>(len));
+    if (!buf)
+        return false;
+
     if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len), str))
         return false;
 
     JSString* res = JS_NewExternalString(cx, buf.get(), len, &ExternalStringFinalizer);
     if (!res)
         return false;
 
     mozilla::Unused << buf.release();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1344265.js
@@ -0,0 +1,5 @@
+// |jit-test| allow-unhandlable-oom; allow-oom
+if (!('oomAfterAllocations' in this))
+    quit();
+oomAfterAllocations(1);
+newExternalString("a");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1345707.js
@@ -0,0 +1,7 @@
+for (var i=0; i<10; i++) {
+    var o = {};
+    if (i & 1)
+	Object.preventExtensions(o);
+    o[0] = i;
+    assertEq(0 in o, !(i & 1));
+}
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -762,17 +762,17 @@ BaselineCacheIRCompiler::emitStoreSlotSh
         masm.storeValue(val, slot);
     } else {
         masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch2.ref());
         BaseIndex slot(scratch2.ref(), scratch1, TimesOne);
         EmitPreBarrier(masm, slot, MIRType::Value);
         masm.storeValue(val, slot);
     }
 
-    BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
+    emitPostBarrierSlot(obj, val, scratch1);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreFixedSlot()
 {
     return emitStoreSlotShared(true);
 }
@@ -874,17 +874,17 @@ BaselineCacheIRCompiler::emitAddAndStore
     } else {
         MOZ_ASSERT(op == CacheOp::AddAndStoreDynamicSlot ||
                    op == CacheOp::AllocateAndStoreDynamicSlot);
         masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch2);
         BaseIndex slot(scratch2, scratch1, TimesOne);
         masm.storeValue(val, slot);
     }
 
-    BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
+    emitPostBarrierSlot(obj, val, scratch1);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitAddAndStoreFixedSlot()
 {
     return emitAddAndStoreSlotShared(CacheOp::AddAndStoreFixedSlot);
 }
@@ -930,17 +930,17 @@ BaselineCacheIRCompiler::emitStoreUnboxe
     // Note that the storeUnboxedProperty call here is infallible, as the
     // IR emitter is responsible for guarding on |val|'s type.
     EmitICUnboxedPreBarrier(masm, fieldAddr, fieldType);
     masm.storeUnboxedProperty(fieldAddr, fieldType,
                               ConstantOrRegister(TypedOrValueRegister(val)),
                               /* failure = */ nullptr);
 
     if (UnboxedTypeNeedsPostBarrier(fieldType))
-        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
+        emitPostBarrierSlot(obj, val, scratch);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreTypedObjectReferenceProperty()
 {
     ObjOperandId objId = reader.objOperandId();
     Address offsetAddr = stubAddress(reader.stubOffset());
@@ -967,17 +967,17 @@ BaselineCacheIRCompiler::emitStoreTypedO
     // Compute the address being written to.
     LoadTypedThingData(masm, layout, obj, scratch1);
     masm.addPtr(offsetAddr, scratch1);
     Address dest(scratch1, 0);
 
     emitStoreTypedObjectReferenceProp(val, type, dest, scratch2);
 
     if (type != ReferenceTypeDescr::TYPE_STRING)
-        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
+        emitPostBarrierSlot(obj, val, scratch1);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreTypedObjectScalarProperty()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address offsetAddr = stubAddress(reader.stubOffset());
@@ -1070,17 +1070,17 @@ BaselineCacheIRCompiler::emitStoreDenseE
         return false;
 
     // Perform the store. Reload obj->elements because callTypeUpdateIC
     // used the scratch register.
     masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
     EmitPreBarrier(masm, element, MIRType::Value);
     masm.storeValue(val, element);
 
-    BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
+    emitPostBarrierElement(obj, val, scratch, index);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreDenseElementHole()
 {
     ObjOperandId objId = reader.objOperandId();
     Int32OperandId indexId = reader.int32OperandId();
@@ -1207,17 +1207,17 @@ BaselineCacheIRCompiler::emitStoreDenseE
         masm.bind(&inBounds);
     }
 
     EmitPreBarrier(masm, element, MIRType::Value);
 
     masm.bind(&doStore);
     masm.storeValue(val, element);
 
-    BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
+    emitPostBarrierElement(obj, val, scratch, index);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreTypedElement()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Register index = allocator.useRegister(masm, reader.int32OperandId());
@@ -1305,17 +1305,17 @@ BaselineCacheIRCompiler::emitStoreUnboxe
     // IR emitter is responsible for guarding on |val|'s type.
     BaseIndex element(scratch, index, ScaleFromElemWidth(UnboxedTypeSize(elementType)));
     EmitICUnboxedPreBarrier(masm, element, elementType);
     masm.storeUnboxedProperty(element, elementType,
                               ConstantOrRegister(TypedOrValueRegister(val)),
                               /* failure = */ nullptr);
 
     if (UnboxedTypeNeedsPostBarrier(elementType))
-        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
+        emitPostBarrierSlot(obj, val, scratch);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreUnboxedArrayElementHole()
 {
     ObjOperandId objId = reader.objOperandId();
     Int32OperandId indexId = reader.int32OperandId();
@@ -1382,17 +1382,17 @@ BaselineCacheIRCompiler::emitStoreUnboxe
     // Note that the storeUnboxedProperty call here is infallible, as the
     // IR emitter is responsible for guarding on |val|'s type.
     masm.bind(&doStore);
     masm.storeUnboxedProperty(element, elementType,
                               ConstantOrRegister(TypedOrValueRegister(val)),
                               /* failure = */ nullptr);
 
     if (UnboxedTypeNeedsPostBarrier(elementType))
-        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
+        emitPostBarrierSlot(obj, val, scratch);
     return true;
 }
 
 typedef bool (*CallNativeSetterFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
 static const VMFunction CallNativeSetterInfo =
     FunctionInfo<CallNativeSetterFn>(CallNativeSetter, "CallNativeSetter");
 
 bool
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2464,19 +2464,22 @@ SetPropIRGenerator::tryAttachSetDenseEle
 
     JSOp op = JSOp(*pc_);
     MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
 
     if (op == JSOP_INITHIDDENELEM)
         return false;
 
     NativeObject* nobj = &obj->as<NativeObject>();
-    if (nobj->getElementsHeader()->isFrozen())
+    if (!nobj->nonProxyIsExtensible())
         return false;
 
+    MOZ_ASSERT(!nobj->getElementsHeader()->isFrozen(),
+               "Extensible objects should not have frozen elements");
+
     uint32_t initLength = nobj->getDenseInitializedLength();
 
     // Optimize if we're adding an element at initLength or writing to a hole.
     // Don't handle the adding case if the current accesss is in bounds, to
     // ensure we always call noteArrayWriteHole.
     bool isAdd = index == initLength;
     bool isHoleInBounds = index < initLength && !nobj->containsDenseElement(index);
     if (!isAdd && !isHoleInBounds)
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -2108,16 +2108,64 @@ CacheIRCompiler::emitStoreTypedObjectRef
       case ReferenceTypeDescr::TYPE_STRING:
         EmitPreBarrier(masm, dest, MIRType::String);
         masm.unboxString(val, scratch);
         masm.storePtr(scratch, dest);
         break;
     }
 }
 
+void
+CacheIRCompiler::emitPostBarrierShared(Register obj, const ConstantOrRegister& val,
+                                       Register scratch, Register maybeIndex)
+{
+    if (!cx_->nursery().exists())
+        return;
+
+    if (val.constant()) {
+        MOZ_ASSERT_IF(val.value().isObject(), !IsInsideNursery(&val.value().toObject()));
+        return;
+    }
+
+    TypedOrValueRegister reg = val.reg();
+    if (reg.hasTyped() && reg.type() != MIRType::Object)
+        return;
+
+    Label skipBarrier;
+    if (reg.hasValue()) {
+        masm.branchValueIsNurseryObject(Assembler::NotEqual, reg.valueReg(), scratch,
+                                        &skipBarrier);
+    } else {
+        masm.branchPtrInNurseryChunk(Assembler::NotEqual, reg.typedReg().gpr(), scratch,
+                                     &skipBarrier);
+    }
+    masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, &skipBarrier);
+
+    // Call one of these, depending on maybeIndex:
+    //
+    //   void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
+    //   void PostWriteElementBarrier(JSRuntime* rt, JSObject* obj,
+    //                                int32_t index);
+    LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+    masm.PushRegsInMask(save);
+    masm.setupUnalignedABICall(scratch);
+    masm.movePtr(ImmPtr(cx_->runtime()), scratch);
+    masm.passABIArg(scratch);
+    masm.passABIArg(obj);
+    if (maybeIndex != InvalidReg) {
+        masm.passABIArg(maybeIndex);
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*,
+                                             (PostWriteElementBarrier<IndexInBounds::Yes>)));
+    } else {
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
+    }
+    masm.PopRegsInMask(save);
+
+    masm.bind(&skipBarrier);
+}
 
 bool
 CacheIRCompiler::emitWrapResult()
 {
     AutoOutputRegister output(*this);
     AutoScratchRegister scratch(allocator, masm);
 
     FailurePath* failure;
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -577,16 +577,37 @@ class MOZ_RAII CacheIRCompiler
 
     void emitLoadTypedObjectResultShared(const Address& fieldAddr, Register scratch,
                                          TypedThingLayout layout, uint32_t typeDescr,
                                          const AutoOutputRegister& output);
 
     void emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTypeDescr::Type type,
                                            const Address& dest, Register scratch);
 
+  private:
+    void emitPostBarrierShared(Register obj, const ConstantOrRegister& val, Register scratch,
+                               Register maybeIndex);
+
+    void emitPostBarrierShared(Register obj, ValueOperand val, Register scratch,
+                               Register maybeIndex) {
+        emitPostBarrierShared(obj, ConstantOrRegister(val), scratch, maybeIndex);
+    }
+
+  protected:
+    template <typename T>
+    void emitPostBarrierSlot(Register obj, const T& val, Register scratch) {
+        emitPostBarrierShared(obj, val, scratch, InvalidReg);
+    }
+
+    template <typename T>
+    void emitPostBarrierElement(Register obj, const T& val, Register scratch, Register index) {
+        MOZ_ASSERT(index != InvalidReg);
+        emitPostBarrierShared(obj, val, scratch, index);
+    }
+
 #define DEFINE_SHARED_OP(op) MOZ_MUST_USE bool emit##op();
     CACHE_IR_SHARED_OPS(DEFINE_SHARED_OP)
 #undef DEFINE_SHARED_OP
 };
 
 // Ensures the IC's output register is available for writing.
 class MOZ_RAII AutoOutputRegister
 {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3851,17 +3851,17 @@ CodeGenerator::visitOutOfLineCallPostWri
     }
 
     Register runtimereg = regs.takeAny();
     masm.setupUnalignedABICall(runtimereg);
     masm.mov(ImmPtr(GetJitContext()->runtime), runtimereg);
     masm.passABIArg(runtimereg);
     masm.passABIArg(objreg);
     masm.passABIArg(indexreg);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteElementBarrier));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, (PostWriteElementBarrier<IndexInBounds::Maybe>)));
 
     restoreLiveVolatile(ool->lir());
 
     masm.jump(ool->rejoin());
 }
 
 void
 CodeGenerator::visitPostWriteElementBarrierO(LPostWriteElementBarrierO* lir)
@@ -10301,28 +10301,28 @@ CodeGenerator::addGetPropertyCache(LInst
     addIC(ins, allocateIC(cache));
 }
 
 void
 CodeGenerator::addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
                                    Register temp, FloatRegister tempDouble,
                                    FloatRegister tempF32, const ConstantOrRegister& id,
                                    const ConstantOrRegister& value,
-                                   bool strict, bool needsTypeBarrier, bool guardHoles,
-                                   jsbytecode* profilerLeavePc)
+                                   bool strict, bool needsPostBarrier, bool needsTypeBarrier,
+                                   bool guardHoles, jsbytecode* profilerLeavePc)
 {
     CacheKind kind = CacheKind::SetElem;
     if (id.constant() && id.value().isString()) {
         JSString* idString = id.value().toString();
         uint32_t dummy;
         if (idString->isAtom() && !idString->asAtom().isIndex(&dummy))
             kind = CacheKind::SetProp;
     }
     IonSetPropertyIC cache(kind, liveRegs, objReg, temp, tempDouble, tempF32,
-                           id, value, strict, needsTypeBarrier, guardHoles);
+                           id, value, strict, needsPostBarrier, needsTypeBarrier, guardHoles);
     addIC(ins, allocateIC(cache));
 }
 
 ConstantOrRegister
 CodeGenerator::toConstantOrRegister(LInstruction* lir, size_t n, MIRType type)
 {
     if (type == MIRType::Value)
         return TypedOrValueRegister(ToValue(lir, n));
@@ -10462,18 +10462,19 @@ CodeGenerator::visitSetPropertyCache(LSe
     FloatRegister tempF32 = ToTempFloatRegisterOrInvalid(ins->tempFloat32());
 
     ConstantOrRegister id =
         toConstantOrRegister(ins, LSetPropertyCache::Id, ins->mir()->idval()->type());
     ConstantOrRegister value =
         toConstantOrRegister(ins, LSetPropertyCache::Value, ins->mir()->value()->type());
 
     addSetPropertyCache(ins, liveRegs, objReg, temp, tempDouble, tempF32,
-                        id, value, ins->mir()->strict(), ins->mir()->needsTypeBarrier(),
-                        ins->mir()->guardHoles(), ins->mir()->profilerLeavePc());
+                        id, value, ins->mir()->strict(), ins->mir()->needsPostBarrier(),
+                        ins->mir()->needsTypeBarrier(), ins->mir()->guardHoles(),
+                        ins->mir()->profilerLeavePc());
 }
 
 typedef bool (*ThrowFn)(JSContext*, HandleValue);
 static const VMFunction ThrowInfoCodeGen = FunctionInfo<ThrowFn>(js::Throw, "Throw");
 
 void
 CodeGenerator::visitThrow(LThrow* lir)
 {
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -459,18 +459,18 @@ class CodeGenerator final : public CodeG
     void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
                              TypedOrValueRegister value, const ConstantOrRegister& id,
                              TypedOrValueRegister output, Register maybeTemp, bool monitoredResult,
                              bool allowDoubleResult, jsbytecode* profilerLeavePc);
     void addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
                              Register temp, FloatRegister tempDouble,
                              FloatRegister tempF32, const ConstantOrRegister& id,
                              const ConstantOrRegister& value,
-                             bool strict, bool needsTypeBarrier, bool guardHoles,
-                             jsbytecode* profilerLeavePc);
+                             bool strict, bool needsPostBarrier, bool needsTypeBarrier,
+                             bool guardHoles, jsbytecode* profilerLeavePc);
 
     MOZ_MUST_USE bool generateBranchV(const ValueOperand& value, Label* ifTrue, Label* ifFalse,
                                       FloatRegister fr);
 
     void emitLambdaInit(Register resultReg, Register envChainReg,
                         const LambdaFunctionInfo& info);
 
     void emitFilterArgumentsOrEval(LInstruction* lir, Register string, Register temp1,
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7226,19 +7226,19 @@ IonBuilder::setStaticName(JSObject* stat
 
     // If the property has a known type, we may be able to optimize typed stores by not
     // storing the type tag.
     MIRType slotType = MIRType::None;
     MIRType knownType = property.knownMIRType(constraints());
     if (knownType != MIRType::Value)
         slotType = knownType;
 
-    bool needsBarrier = property.needsBarrier(constraints());
+    bool needsPreBarrier = property.needsBarrier(constraints());
     return storeSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
-                     value, needsBarrier, slotType);
+                     value, needsPreBarrier, slotType);
 }
 
 JSObject*
 IonBuilder::testGlobalLexicalBinding(PropertyName* name)
 {
     MOZ_ASSERT(JSOp(*pc) == JSOP_BINDGNAME ||
                JSOp(*pc) == JSOP_GETGNAME ||
                JSOp(*pc) == JSOP_SETGNAME ||
@@ -9017,27 +9017,21 @@ IonBuilder::initOrSetElemTryCache(bool* 
     bool guardHoles;
     MOZ_TRY_VAR(guardHoles, ElementAccessHasExtraIndexedProperty(this, object));
 
     // Make sure the object being written to doesn't have copy on write elements.
     const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr;
     bool checkNative = !clasp || !clasp->isNative();
     object = addMaybeCopyElementsForWrite(object, checkNative);
 
-    if (NeedsPostBarrier(value)) {
-        if (indexIsInt32)
-            current->add(MPostWriteElementBarrier::New(alloc(), object, value, index));
-        else
-            current->add(MPostWriteBarrier::New(alloc(), object, value));
-    }
-
     // Emit SetPropertyCache.
     bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
     MSetPropertyCache* ins =
-        MSetPropertyCache::New(alloc(), object, index, value, strict, barrier, guardHoles);
+        MSetPropertyCache::New(alloc(), object, index, value, strict, NeedsPostBarrier(value),
+                               barrier, guardHoles);
     current->add(ins);
 
     if (IsPropertyInitOp(JSOp(*pc)))
         current->push(objectArg);
     else
         current->push(value);
 
     MOZ_TRY(resumeAfter(ins));
@@ -11280,21 +11274,16 @@ IonBuilder::jsop_setprop(PropertyName* n
     if (!forceInlineCaches()) {
         // Try to emit stores to unboxed objects.
         trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed);
         MOZ_TRY(setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes));
         if (emitted)
             return Ok();
     }
 
-    // Add post barrier if needed. The instructions above manage any post
-    // barriers they need directly.
-    if (NeedsPostBarrier(value))
-        current->add(MPostWriteBarrier::New(alloc(), obj, value));
-
     if (!forceInlineCaches()) {
         // Try to emit store from definite slots.
         trackOptimizationAttempt(TrackedStrategy::SetProp_DefiniteSlot);
         MOZ_TRY(setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes));
         if (emitted)
             return Ok();
 
         // Try to emit a monomorphic/polymorphic store based on baseline caches.
@@ -11569,16 +11558,19 @@ IonBuilder::setPropTryDefiniteSlot(bool*
         HeapTypeSetKey property = key->property(NameToId(name));
         if (property.nonWritable(constraints())) {
             trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
             return Ok();
         }
         writeBarrier |= property.needsBarrier(constraints());
     }
 
+    if (NeedsPostBarrier(value))
+        current->add(MPostWriteBarrier::New(alloc(), obj, value));
+
     MInstruction* store;
     if (slot < nfixed) {
         store = MStoreFixedSlot::New(alloc(), obj, slot, value);
         if (writeBarrier)
             store->toStoreFixedSlot()->setNeedsBarrier();
     } else {
         MInstruction* slots = MSlots::New(alloc(), obj);
         current->add(slots);
@@ -11718,18 +11710,21 @@ IonBuilder::setPropTryInlineAccess(bool*
             // Monomorphic store to a native object.
             spew("Inlining monomorphic native SETPROP");
 
             obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
 
             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
             MOZ_ASSERT(shape);
 
-            bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
-            MOZ_TRY(storeSlot(obj, shape, value, needsBarrier));
+            if (NeedsPostBarrier(value))
+                current->add(MPostWriteBarrier::New(alloc(), obj, value));
+
+            bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
+            MOZ_TRY(storeSlot(obj, shape, value, needsPreBarrier));
 
             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
             *emitted = true;
             return Ok();
         }
 
         if (receivers[0].shape) {
             // Monomorphic store to an unboxed object expando.
@@ -11741,33 +11736,39 @@ IonBuilder::setPropTryInlineAccess(bool*
             MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
             current->add(expando);
 
             expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
 
             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
             MOZ_ASSERT(shape);
 
-            bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
-            MOZ_TRY(storeSlot(expando, shape, value, needsBarrier));
+            if (NeedsPostBarrier(value))
+                current->add(MPostWriteBarrier::New(alloc(), obj, value));
+
+            bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
+            MOZ_TRY(storeSlot(expando, shape, value, needsPreBarrier));
 
             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
             *emitted = true;
             return Ok();
         }
 
         // Monomorphic store to an unboxed object.
         spew("Inlining monomorphic unboxed SETPROP");
 
         ObjectGroup* group = receivers[0].group;
         if (!objTypes->hasType(TypeSet::ObjectType(group)))
             return Ok();
 
         obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
 
+        if (NeedsPostBarrier(value))
+            current->add(MPostWriteBarrier::New(alloc(), obj, value));
+
         const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
         storeUnboxedProperty(obj, property->offset, property->type, value);
 
         current->push(value);
 
         trackOptimizationOutcome(TrackedOutcome::Monomorphic);
         *emitted = true;
         return Ok();
@@ -11776,24 +11777,30 @@ IonBuilder::setPropTryInlineAccess(bool*
     MOZ_ASSERT(receivers.length() > 1);
     spew("Inlining polymorphic SETPROP");
 
     if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
         obj = addGuardReceiverPolymorphic(obj, receivers);
         if (!obj)
             return abort(AbortReason::Alloc);
 
-        bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
-        MOZ_TRY(storeSlot(obj, propShape, value, needsBarrier));
+        if (NeedsPostBarrier(value))
+            current->add(MPostWriteBarrier::New(alloc(), obj, value));
+
+        bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
+        MOZ_TRY(storeSlot(obj, propShape, value, needsPreBarrier));
 
         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
         *emitted = true;
         return Ok();
     }
 
+    if (NeedsPostBarrier(value))
+        current->add(MPostWriteBarrier::New(alloc(), obj, value));
+
     MSetPropertyPolymorphic* ins = MSetPropertyPolymorphic::New(alloc(), obj, value, name);
     current->add(ins);
     current->push(value);
 
     for (size_t i = 0; i < receivers.length(); i++) {
         Shape* propShape = nullptr;
         if (receivers[i].shape) {
             propShape = receivers[i].shape->searchLinear(NameToId(name));
@@ -11818,17 +11825,18 @@ IonBuilder::setPropTryCache(bool* emitte
                             PropertyName* name, MDefinition* value,
                             bool barrier, TemporaryTypeSet* objTypes)
 {
     MOZ_ASSERT(*emitted == false);
 
     bool strict = IsStrictSetPC(pc);
 
     MConstant* id = constant(StringValue(name));
-    MSetPropertyCache* ins = MSetPropertyCache::New(alloc(), obj, id, value, strict, barrier,
+    MSetPropertyCache* ins = MSetPropertyCache::New(alloc(), obj, id, value, strict,
+                                                    NeedsPostBarrier(value), barrier,
                                                     /* guardHoles = */ false);
     current->add(ins);
     current->push(value);
 
     MOZ_TRY(resumeAfter(ins));
 
     trackOptimizationSuccess();
     *emitted = true;
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -128,16 +128,20 @@ class MOZ_RAII IonCacheIRCompiler : publ
         return ptr;
     }
 
     void prepareVMCall(MacroAssembler& masm);
     MOZ_MUST_USE bool callVM(MacroAssembler& masm, const VMFunction& fun);
 
     MOZ_MUST_USE bool emitAddAndStoreSlotShared(CacheOp op);
 
+    bool needsPostBarrier() const {
+        return ic_->asSetPropertyIC()->needsPostBarrier();
+    }
+
     void pushStubCodePointer() {
         stubJitCodeOffset_.emplace(masm.PushWithPatch(ImmPtr((void*)-1)));
     }
 
 #define DEFINE_OP(op) MOZ_MUST_USE bool emit##op();
     CACHE_IR_OPS(DEFINE_OP)
 #undef DEFINE_OP
 };
@@ -1019,27 +1023,33 @@ EmitCheckPropertyTypes(MacroAssembler& m
 
 bool
 IonCacheIRCompiler::emitStoreFixedSlot()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     int32_t offset = int32StubField(reader.stubOffset());
     ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
 
+    Maybe<AutoScratchRegister> scratch;
+    if (needsPostBarrier())
+        scratch.emplace(allocator, masm);
+
     if (typeCheckInfo_->isSet()) {
         FailurePath* failure;
         if (!addFailurePath(&failure))
             return false;
 
         EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label());
     }
 
     Address slot(obj, offset);
     EmitPreBarrier(masm, slot, MIRType::Value);
     masm.storeConstantOrRegister(val, slot);
+    if (needsPostBarrier())
+        emitPostBarrierSlot(obj, val, scratch.ref());
     return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreDynamicSlot()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     int32_t offset = int32StubField(reader.stubOffset());
@@ -1053,16 +1063,18 @@ IonCacheIRCompiler::emitStoreDynamicSlot
 
         EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label());
     }
 
     masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
     Address slot(scratch, offset);
     EmitPreBarrier(masm, slot, MIRType::Value);
     masm.storeConstantOrRegister(val, slot);
+    if (needsPostBarrier())
+        emitPostBarrierSlot(obj, val, scratch);
     return true;
 }
 
 bool
 IonCacheIRCompiler::emitAddAndStoreSlotShared(CacheOp op)
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     int32_t offset = int32StubField(reader.stubOffset());
@@ -1141,16 +1153,19 @@ IonCacheIRCompiler::emitAddAndStoreSlotS
     } else {
         MOZ_ASSERT(op == CacheOp::AddAndStoreDynamicSlot ||
                    op == CacheOp::AllocateAndStoreDynamicSlot);
         masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch1);
         Address slot(scratch1, offset);
         masm.storeConstantOrRegister(val, slot);
     }
 
+    if (needsPostBarrier())
+        emitPostBarrierSlot(obj, val, scratch1);
+
     return true;
 }
 
 bool
 IonCacheIRCompiler::emitAddAndStoreFixedSlot()
 {
     return emitAddAndStoreSlotShared(CacheOp::AddAndStoreFixedSlot);
 }
@@ -1170,28 +1185,34 @@ IonCacheIRCompiler::emitAllocateAndStore
 bool
 IonCacheIRCompiler::emitStoreUnboxedProperty()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     JSValueType fieldType = reader.valueType();
     int32_t offset = int32StubField(reader.stubOffset());
     ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
 
+    Maybe<AutoScratchRegister> scratch;
+    if (needsPostBarrier() && UnboxedTypeNeedsPostBarrier(fieldType))
+        scratch.emplace(allocator, masm);
+
     if (fieldType == JSVAL_TYPE_OBJECT && typeCheckInfo_->isSet()) {
         FailurePath* failure;
         if (!addFailurePath(&failure))
             return false;
         EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label());
     }
 
     // Note that the storeUnboxedProperty call here is infallible, as the
     // IR emitter is responsible for guarding on |val|'s type.
     Address fieldAddr(obj, offset);
     EmitICUnboxedPreBarrier(masm, fieldAddr, fieldType);
     masm.storeUnboxedProperty(fieldAddr, fieldType, val, /* failure = */ nullptr);
+    if (needsPostBarrier() && UnboxedTypeNeedsPostBarrier(fieldType))
+        emitPostBarrierSlot(obj, val, scratch.ref());
     return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreTypedObjectReferenceProperty()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     int32_t offset = int32StubField(reader.stubOffset());
@@ -1213,16 +1234,19 @@ IonCacheIRCompiler::emitStoreTypedObject
                                *liveRegs_, failure->label());
     }
 
     // Compute the address being written to.
     LoadTypedThingData(masm, layout, obj, scratch1);
     Address dest(scratch1, offset);
 
     emitStoreTypedObjectReferenceProp(val, type, dest, scratch2);
+
+    if (needsPostBarrier() && type != ReferenceTypeDescr::TYPE_STRING)
+        emitPostBarrierSlot(obj, val, scratch1);
     return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreTypedObjectScalarProperty()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     int32_t offset = int32StubField(reader.stubOffset());
@@ -1267,16 +1291,18 @@ IonCacheIRCompiler::emitStoreDenseElemen
     masm.branch32(Assembler::BelowOrEqual, initLength, index, failure->label());
 
     // Hole check.
     BaseObjectElementIndex element(scratch, index);
     masm.branchTestMagic(Assembler::Equal, element, failure->label());
 
     EmitPreBarrier(masm, element, MIRType::Value);
     EmitIonStoreDenseElement(masm, val, scratch, element);
+    if (needsPostBarrier())
+        emitPostBarrierElement(obj, val, scratch, index);
     return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreDenseElementHole()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Register index = allocator.useRegister(masm, reader.int32OperandId());
@@ -1351,16 +1377,18 @@ IonCacheIRCompiler::emitStoreDenseElemen
     masm.jump(&doStore);
 
     masm.bind(&inBounds);
 
     EmitPreBarrier(masm, element, MIRType::Value);
 
     masm.bind(&doStore);
     EmitIonStoreDenseElement(masm, val, scratch, element);
+    if (needsPostBarrier())
+        emitPostBarrierElement(obj, val, scratch, index);
     return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreTypedElement()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Register index = allocator.useRegister(masm, reader.int32OperandId());
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -209,47 +209,50 @@ class IonSetPropertyIC : public IonIC
 
     Register object_;
     Register temp_;
     FloatRegister maybeTempDouble_;
     FloatRegister maybeTempFloat32_;
     ConstantOrRegister id_;
     ConstantOrRegister rhs_;
     bool strict_ : 1;
+    bool needsPostBarrier_ : 1;
     bool needsTypeBarrier_ : 1;
     bool guardHoles_ : 1;
 
   public:
     IonSetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs, Register object, Register temp,
                      FloatRegister maybeTempDouble, FloatRegister maybeTempFloat32,
                      const ConstantOrRegister& id, const ConstantOrRegister& rhs, bool strict,
-                     bool needsTypeBarrier, bool guardHoles)
+                     bool needsPostBarrier, bool needsTypeBarrier, bool guardHoles)
       : IonIC(kind),
         liveRegs_(liveRegs),
         object_(object),
         temp_(temp),
         maybeTempDouble_(maybeTempDouble),
         maybeTempFloat32_(maybeTempFloat32),
         id_(id),
         rhs_(rhs),
         strict_(strict),
+        needsPostBarrier_(needsPostBarrier),
         needsTypeBarrier_(needsTypeBarrier),
         guardHoles_(guardHoles)
     { }
 
     LiveRegisterSet liveRegs() const { return liveRegs_; }
     Register object() const { return object_; }
     ConstantOrRegister id() const { return id_; }
     ConstantOrRegister rhs() const { return rhs_; }
 
     Register temp() const { return temp_; }
     FloatRegister maybeTempDouble() const { return maybeTempDouble_; }
     FloatRegister maybeTempFloat32() const { return maybeTempFloat32_; }
 
     bool strict() const { return strict_; }
+    bool needsPostBarrier() const { return needsPostBarrier_; }
     bool needsTypeBarrier() const { return needsTypeBarrier_; }
     bool guardHoles() const { return guardHoles_; }
 
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonSetPropertyIC* ic,
                                     HandleObject obj, HandleValue idVal, HandleValue rhs);
 };
 
 } // namespace jit
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11830,33 +11830,38 @@ class MCallSetProperty
     }
 };
 
 class MSetPropertyCache
   : public MTernaryInstruction,
     public Mix3Policy<SingleObjectPolicy, CacheIdPolicy<1>, NoFloatPolicy<2>>::Data
 {
     bool strict_ : 1;
+    bool needsPostBarrier_ : 1;
     bool needsTypeBarrier_ : 1;
     bool guardHoles_ : 1;
 
     MSetPropertyCache(MDefinition* obj, MDefinition* id, MDefinition* value, bool strict,
-                      bool typeBarrier, bool guardHoles)
+                      bool needsPostBarrier, bool typeBarrier, bool guardHoles)
       : MTernaryInstruction(obj, id, value),
         strict_(strict),
+        needsPostBarrier_(needsPostBarrier),
         needsTypeBarrier_(typeBarrier),
         guardHoles_(guardHoles)
     {
     }
 
   public:
     INSTRUCTION_HEADER(SetPropertyCache)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, object), (1, idval), (2, value))
 
+    bool needsPostBarrier() const {
+        return needsPostBarrier_;
+    }
     bool needsTypeBarrier() const {
         return needsTypeBarrier_;
     }
 
     bool guardHoles() const {
         return guardHoles_;
     }
 
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -608,44 +608,16 @@ ICStubCompiler::pushStubPayload(MacroAss
 
 void
 ICStubCompiler::PushStubPayload(MacroAssembler& masm, Register scratch)
 {
     pushStubPayload(masm, scratch);
     masm.adjustFrame(sizeof(intptr_t));
 }
 
-void
-BaselineEmitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val,
-                                 Register scratch, LiveGeneralRegisterSet saveRegs,
-                                 JSContext* cx)
-{
-    if (!cx->nursery().exists())
-        return;
-
-    Label skipBarrier;
-    masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, &skipBarrier);
-    masm.branchValueIsNurseryObject(Assembler::NotEqual, val, scratch, &skipBarrier);
-
-    // void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
-#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
-    saveRegs.add(ICTailCallReg);
-#endif
-    saveRegs.set() = GeneralRegisterSet::Intersect(saveRegs.set(), GeneralRegisterSet::Volatile());
-    masm.PushRegsInMask(saveRegs);
-    masm.setupUnalignedABICall(scratch);
-    masm.movePtr(ImmPtr(cx->runtime()), scratch);
-    masm.passABIArg(scratch);
-    masm.passABIArg(obj);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
-    masm.PopRegsInMask(saveRegs);
-
-    masm.bind(&skipBarrier);
-}
-
 SharedStubInfo::SharedStubInfo(JSContext* cx, void* payload, ICEntry* icEntry)
   : maybeFrame_(nullptr),
     outerScript_(cx),
     innerScript_(cx),
     icEntry_(icEntry)
 {
     if (payload) {
         maybeFrame_ = (BaselineFrame*) payload;
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1135,20 +1135,16 @@ class ICStubCompiler
         }
         return outerScript->zone()->jitZone()->optimizedStubSpace();
     }
     ICStubSpace* getStubSpace(JSScript* outerScript) {
         return StubSpaceForStub(ICStub::NonCacheIRStubMakesGCCalls(kind), outerScript, engine_);
     }
 };
 
-void BaselineEmitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val,
-                                      Register scratch, LiveGeneralRegisterSet saveRegs,
-                                      JSContext* cx);
-
 class SharedStubInfo
 {
     BaselineFrame* maybeFrame_;
     RootedScript outerScript_;
     RootedScript innerScript_;
     ICEntry* icEntry_;
 
   public:
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -655,42 +655,65 @@ GetDynamicName(JSContext* cx, JSObject* 
     }
 
     vp->setUndefined();
 }
 
 void
 PostWriteBarrier(JSRuntime* rt, JSObject* obj)
 {
+    JS::AutoCheckCannotGC nogc;
     MOZ_ASSERT(!IsInsideNursery(obj));
-    obj->zone()->group()->storeBuffer().putWholeCell(obj);
+    rt->gc.storeBuffer().putWholeCell(obj);
 }
 
 static const size_t MAX_WHOLE_CELL_BUFFER_SIZE = 4096;
 
+template <IndexInBounds InBounds>
 void
 PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index)
 {
+    JS::AutoCheckCannotGC nogc;
+
     MOZ_ASSERT(!IsInsideNursery(obj));
-    if (obj->is<NativeObject>() &&
-        !obj->as<NativeObject>().isInWholeCellBuffer() &&
-        uint32_t(index) < obj->as<NativeObject>().getDenseInitializedLength() &&
-        (obj->as<NativeObject>().getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
+
+    if (InBounds == IndexInBounds::Yes) {
+        MOZ_ASSERT(uint32_t(index) < obj->as<NativeObject>().getDenseInitializedLength());
+    } else {
+        if (MOZ_UNLIKELY(!obj->is<NativeObject>()) ||
+            uint32_t(index) >= obj->as<NativeObject>().getDenseInitializedLength())
+        {
+            rt->gc.storeBuffer().putWholeCell(obj);
+            return;
+        }
+    }
+
+    NativeObject* nobj = &obj->as<NativeObject>();
+    if (nobj->isInWholeCellBuffer())
+        return;
+
+    if (nobj->getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
 #ifdef JS_GC_ZEAL
          || rt->hasZealMode(gc::ZealMode::ElementsBarrier)
 #endif
-        ))
+        )
     {
-        obj->zone()->group()->storeBuffer().putSlot(&obj->as<NativeObject>(), HeapSlot::Element, index, 1);
+        rt->gc.storeBuffer().putSlot(nobj, HeapSlot::Element, index, 1);
         return;
     }
 
-    obj->zone()->group()->storeBuffer().putWholeCell(obj);
+    rt->gc.storeBuffer().putWholeCell(obj);
 }
 
+template void
+PostWriteElementBarrier<IndexInBounds::Yes>(JSRuntime* rt, JSObject* obj, int32_t index);
+
+template void
+PostWriteElementBarrier<IndexInBounds::Maybe>(JSRuntime* rt, JSObject* obj, int32_t index);
+
 void
 PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj)
 {
     MOZ_ASSERT(obj->is<GlobalObject>());
     if (!obj->compartment()->globalWriteBarriered) {
         PostWriteBarrier(rt, obj);
         obj->compartment()->globalWriteBarriered = 1;
     }
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -669,18 +669,22 @@ MOZ_MUST_USE bool
 GetIntrinsicValue(JSContext* cx, HandlePropertyName name, MutableHandleValue rval);
 
 MOZ_MUST_USE bool
 CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHandleValue rval);
 
 void GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp);
 
 void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
+void PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj);
+
+enum class IndexInBounds { Yes, Maybe };
+
+template <IndexInBounds InBounds>
 void PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index);
-void PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj);
 
 // If |str| is an index in the range [0, INT32_MAX], return it. If the string
 // is not an index in this range, return -1.
 int32_t GetIndexFromString(JSString* str);
 
 JSObject* WrapObjectPure(JSContext* cx, JSObject* obj);
 
 MOZ_MUST_USE bool
--- a/js/xpconnect/shell/moz.build
+++ b/js/xpconnect/shell/moz.build
@@ -45,11 +45,14 @@ if CONFIG['OS_ARCH'] == 'WINNT':
             'winmm.dll',
             'user32.dll',
         ]
 
     DELAYLOAD_DLLS += [
         'xul.dll',
     ]
 
+if CONFIG['OS_TARGET'] == 'Android':
+    LDFLAGS += ['-pie']
+
 CFLAGS += CONFIG['TK_CFLAGS']
 CXXFLAGS += CONFIG['TK_CFLAGS']
 OS_LIBS += CONFIG['TK_LIBS']
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/489647-1.html
@@ -0,0 +1,13 @@
+<html class="reftest-wait"><head><title> Bug 489647 -  New 1.9.0.9 topcrash [@nsTextFrame::ClearTextRun()]</title></head>
+<body>
+<div id="a" style="white-space: pre;">
+m</div>
+<script>
+function doe() {
+  document.getElementById('a').childNodes[0].splitText(1);
+  document.documentElement.className = "";
+}
+setTimeout(doe, 100);
+</script>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/883514-1.html
@@ -0,0 +1,18 @@
+<html>
+<body>
+  <blockquote>
+    <ul style="float: right;-moz-column-count: 7723">
+      <li>
+        <table></table>
+        <button><table></table></button>
+        <form style="float: left">
+          <button>
+            <ol><li></ol>
+          </button>
+        </form>
+      </li>
+      <li>
+        <div></div>
+        <meter style="float: right;">
+
+
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/883514-2.html
@@ -0,0 +1,15 @@
+<html>
+<body>
+  <ul style="width: 600px; -moz-column-count: 13">
+    <li>
+      <table></table>
+      <button></button>
+      <form style="float: left">
+        <button>
+          <ol><li></li></ol>
+        </button>
+      </form>
+    </li>
+    <li>
+      <div></div>
+      <img style="float: left">
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -346,16 +346,17 @@ load 478131-1.html
 load 478170-1.html
 load 478185-1.html
 asserts-if(!Android,0-1) load 479938-1.html # Bug 575011
 load 480345-1.html
 load 481921.html
 load 489462-1.html
 load 489477.html
 asserts-if(stylo,7) load 489480-1.xhtml # bug 1330260
+load 489647-1.html
 load 493111-1.html
 load 493118-1.html
 load 493649.html
 load 494283-1.xhtml
 load 494283-2.html
 load 494300-1.xul
 load 494332-1.html
 load 495875-1.html
@@ -541,16 +542,18 @@ load 851396-1.html
 load 854263-1.html
 load 862185.html
 load 862947-1.html
 load 863935.html
 load 866547-1.html
 needs-focus pref(accessibility.browsewithcaret,true) load 868906.html
 asserts(0-5) load 876074-1.html # bug 876749
 load 876155.html
+load 883514-1.html
+load 883514-2.html
 load 885009-1.html
 load 893496-1.html
 load 893523.html
 asserts(0-3) load 898871.html # bug 479160 - mostly OSX, sometimes Windows
 asserts(0-3) load 914501.html # bug 1144852 - all platforms
 load 914891.html
 load 915475.xhtml
 load 927558.html
new file mode 100644
--- /dev/null
+++ b/layout/tables/crashtests/980223.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>#980223 testcase</title>
+</head>
+<body>
+<table>
+  <tbody>
+  	<tr>
+  		<td style="position:relative;">Test table cell</td>
+  	</tr>
+  </tbody>
+</table>
+
+<script>
+document.body.offsetHeight;
+document.querySelector('tbody').innerHTML = '';
+document.body.offsetHeight;
+</script>
+
+</body>
+</html>
--- a/layout/tables/crashtests/crashtests.list
+++ b/layout/tables/crashtests/crashtests.list
@@ -146,14 +146,15 @@ load 696640-2.html
 load 705996-1.html
 load 705996-2.html
 load 707622-1.html
 load 710098-1.html
 load 711864-1.html
 asserts-if(gtkWidget&&browserIsRemote,5) load 759249-1.html # Bug 1195474
 load 759249-2.html
 load 814713.html
+load 980223.html
 load 1027611-1.html
 load 1031934.html
 load 1183896.html
 load 1223282.html
 load 1223232.html
 load 1243623-1.html
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -98,18 +98,18 @@ class AndroidEmulatorCommands(MachComman
     """
        Run the Android emulator with one of the AVDs used in the Mozilla
        automated test environment. If necessary, the AVD is fetched from
        the tooltool server and installed.
     """
     @Command('android-emulator', category='devenv',
         conditions=[],
         description='Run the Android emulator with an AVD from test automation.')
-    @CommandArgument('--version', metavar='VERSION', choices=['4.3', '6.0', 'x86'],
-        help='Specify Android version to run in emulator. One of "4.3", "6.0", or "x86".',
+    @CommandArgument('--version', metavar='VERSION', choices=['4.3', '6.0', 'x86', 'x86-6.0'],
+        help='Specify Android version to run in emulator. One of "4.3", "6.0", "x86", or "x86-6.0".',
         default='4.3')
     @CommandArgument('--wait', action='store_true',
         help='Wait for emulator to be closed.')
     @CommandArgument('--force-update', action='store_true',
         help='Update AVD definition even when AVD is already installed.')
     @CommandArgument('--verbose', action='store_true',
         help='Log informative status messages.')
     def emulator(self, version, wait=False, force_update=False, verbose=False):
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -638,19 +638,20 @@ nsProtocolProxyService::PrefsChanged(nsI
                           mProxyOverTLS);
     }
 
     if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
         proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
                          mFailedProxyTimeout);
 
     if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
-        nsCString no_proxies;
-        proxy_GetStringPref(prefBranch, PROXY_PREF("no_proxies_on"), no_proxies);
-        LoadHostFilters(no_proxies.get());
+        rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
+                                     getter_Copies(tempString));
+        if (NS_SUCCEEDED(rv))
+            LoadHostFilters(tempString.get());
     }
 
     // We're done if not using something that could give us a PAC URL
     // (PAC, WPAD or System)
     if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
         mProxyConfig != PROXYCONFIG_SYSTEM)
         return;
 
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -3,17 +3,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 protocol PHttpChannel;
 include protocol PFTPChannel;
 include protocol PRtspChannel;
-include protocol PSendStream;
+include protocol PChildToParentStream;
 include BlobTypes;
 include URIParams;
 include IPCStream;
 include PBackgroundSharedTypes;
 
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -17,17 +17,18 @@ include protocol PTCPSocket;
 include protocol PTCPServerSocket;
 include protocol PUDPSocket;
 include protocol PDNSRequest;
 include protocol PChannelDiverter;
 include protocol PBlob; //FIXME: bug #792908
 include protocol PFileDescriptorSet;
 include protocol PDataChannel;
 include protocol PTransportProvider;
-include protocol PSendStream;
+include protocol PChildToParentStream; //FIXME: bug #792908
+include protocol PParentToChildStream; //FIXME: bug #792908
 
 include protocol PRtspController;
 include protocol PRtspChannel;
 include URIParams;
 include NeckoChannelParams;
 include PBrowserOrId;
 include protocol PAltDataOutputStream;
 
@@ -140,16 +141,16 @@ child:
   async PredOnPredictDNS(URIParams uri);
 
   async SpeculativeConnectRequest();
 
   async PTransportProvider();
 
 both:
   // Actually we need PTCPSocket() for parent. But ipdl disallows us having different
-  // signatures on parent and child. So when constructing the parent side object, we just 
+  // signatures on parent and child. So when constructing the parent side object, we just
   // leave host/port unused.
   async PTCPSocket(nsString host, uint16_t port);
 };
 
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -435,17 +435,17 @@ HttpChannelParent::DoAsyncOpen(  const U
   if (stream) {
     // FIXME: The fast path of using the existing stream currently only applies to streams
     //   that have had their entire contents serialized from the child at this point.
     //   Once bug 1294446 and bug 1294450 are fixed it is worth revisiting this heuristic.
     nsCOMPtr<nsIIPCSerializableInputStream> completeStream = do_QueryInterface(stream);
     if (!completeStream) {
       delayAsyncOpen = true;
 
-      // buffer size matches PSendStream transfer size.
+      // buffer size matches PChildToParentStream transfer size.
       const uint32_t kBufferSize = 32768;
 
       nsCOMPtr<nsIStorageStream> storageStream;
       nsresult rv = NS_NewStorageStream(kBufferSize, UINT32_MAX,
                                         getter_AddRefs(storageStream));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return SendFailedAsyncOpen(rv);
       }
--- a/netwerk/protocol/websocket/PWebSocket.ipdl
+++ b/netwerk/protocol/websocket/PWebSocket.ipdl
@@ -9,17 +9,18 @@ include protocol PNecko;
 include protocol PBrowser;
 include protocol PTransportProvider;
 include IPCStream;
 include URIParams;
 include NeckoChannelParams;
 
 include protocol PBlob; //FIXME: bug #792908
 include protocol PFileDescriptorSet; //FIXME: bug #792908
-include protocol PSendStream; //FIXME: bug #792908
+include protocol PChildToParentStream; //FIXME: bug #792908
+include protocol PParentToChildStream; //FIXME: bug #792908
 
 using class IPC::SerializedLoadContext from "SerializedLoadContext.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace net {
 
 union OptionalTransportProvider
new file mode 100644
--- /dev/null
+++ b/testing/config/tooltool-manifests/androidx86_6_0/mach-emulator.manifest
@@ -0,0 +1,10 @@
+[
+{
+"size": 416677519,
+"visibility": "public",
+"digest": "11a1be4d1f1085b1aa516c4f37b4a5d1e81e13bccd1243ebf8bf8431fd3738830675ea3719888ac43931b3b73508040090fd2ff6dd98f99db2678a7cfd6b18a4",
+"algorithm": "sha512",
+"filename": "AVDs-x86-android-6.0-build-2017-03-10.tar.gz",
+"unpack": true
+}
+]
--- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
@@ -281,50 +281,38 @@ class DeviceManagerADB(DeviceManager):
             except:
                 self._logger.warning(traceback.format_exc())
                 self._logger.warning("zip/unzip failure: falling back to normal push")
                 self._useZip = False
                 self.pushDir(localDir, remoteDir, retryLimit=retryLimit, timeout=timeout)
         else:
             localDir = os.path.normpath(localDir)
             remoteDir = os.path.normpath(remoteDir)
-            copyRequired = False
-            if self._adb_version >= '1.0.36' and \
-               os.path.isdir(localDir) and self.dirExists(remoteDir):
-                # See do_sync_push in
-                # https://android.googlesource.com/platform/system/core/+/master/adb/file_sync_client.cpp
-                # Work around change in behavior in adb 1.0.36 where if
-                # the remote destination directory exists, adb push will
-                # copy the source directory *into* the destination
-                # directory otherwise it will copy the source directory
-                # *onto* the destination directory.
-                #
-                # If the destination directory does exist, push to its
-                # parent directory.  If the source and destination leaf
-                # directory names are different, copy the source directory
-                # to a temporary directory with the same leaf name as the
-                # destination so that when we push to the parent, the
-                # source is copied onto the destination directory.
-                localName = os.path.basename(localDir)
-                remoteName = os.path.basename(remoteDir)
-                if localName != remoteName:
-                    copyRequired = True
-                    tempParent = tempfile.mkdtemp()
-                    newLocal = os.path.join(tempParent, remoteName)
-                    dir_util.copy_tree(localDir, newLocal)
-                    localDir = newLocal
+            tempParent = tempfile.mkdtemp()
+            remoteName = os.path.basename(remoteDir)
+            newLocal = os.path.join(tempParent, remoteName)
+            dir_util.copy_tree(localDir, newLocal)
+            # See do_sync_push in
+            # https://android.googlesource.com/platform/system/core/+/master/adb/file_sync_client.cpp
+            # Work around change in behavior in adb 1.0.36 where if
+            # the remote destination directory exists, adb push will
+            # copy the source directory *into* the destination
+            # directory otherwise it will copy the source directory
+            # *onto* the destination directory.
+            if self._adb_version >= '1.0.36':
                 remoteDir = '/'.join(remoteDir.rstrip('/').split('/')[:-1])
             try:
-                self._checkCmd(["push", localDir, remoteDir],
-                               retryLimit=retryLimit, timeout=timeout)
+                if self._checkCmd(["push", newLocal, remoteDir],
+                                  retryLimit=retryLimit, timeout=timeout):
+                    raise DMError("failed to push %s (copy of %s) to %s" %
+                                  (newLocal, localDir, remoteDir))
             except:
                 raise
             finally:
-                if copyRequired:
-                    mozfile.remove(tempParent)
+                mozfile.remove(tempParent)
 
     def dirExists(self, remotePath):
         self._detectLsModifier()
         data = self._runCmd(["shell", "ls", self._lsModifier, remotePath + '/'],
                             timeout=self.short_timeout).output
 
         if len(data) == 1:
             res = data[0]
--- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
@@ -66,17 +66,25 @@ AVD_DICT = {
                     'init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket'],
                    5554),
     'x86': AvdInfo('Android 4.2 x86',
                    'mozemulator-x86',
                    'testing/config/tooltool-manifests/androidx86/mach-emulator.manifest',
                    ['-debug',
                     'init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket',
                     '-qemu', '-m', '1024', '-enable-kvm'],
-                   5554)
+                   5554),
+    'x86-6.0': AvdInfo('Android 6.0 x86',
+                       'mozemulator-x86-6.0',
+                       'testing/config/tooltool-manifests/androidx86_6_0/mach-emulator.manifest',
+                       ['-debug',
+                        'init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket',
+                        '-ranchu',
+                        '-qemu', '-m', '2048'],
+                       5554)
 }
 
 
 def verify_android_device(build_obj, install=False, xre=False, debugger=False, verbose=False):
     """
        Determine if any Android device is connected via adb.
        If no device is found, prompt to start an emulator.
        If a device is found or an emulator started and 'install' is