merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 22 Feb 2017 14:33:38 +0100
changeset 373256 f5372cb6c3c74464c17a3d2ba2b38299f97a8f05
parent 373173 8a6084bc234ceb6036af6c660c5162cde5eaf047 (current diff)
parent 373255 3e044c903144988434e9f6322a279e2f2a6ce7ce (diff)
child 373257 a7b465111b2c5cdde91515b52c035b7c567fa7c4
child 373276 aa38899a1ec6617ab5846d0b81ae21c29793843d
child 373323 19a9a7566136a31edfb7523387fd261322b04cfc
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
merge mozilla-inbound to mozilla-central a=merge
browser/themes/linux/sync-128.png
browser/themes/osx/sync-128.png
browser/themes/windows/sync-128.png
dom/base/nsDocument.cpp
dom/ipc/CrashReporterChild.cpp
dom/ipc/CrashReporterChild.h
dom/ipc/CrashReporterParent.cpp
dom/ipc/CrashReporterParent.h
dom/ipc/PCrashReporter.ipdl
js/src/shell/js.cpp
mobile/android/app/mobile.js
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/test/unit/test_file_partial_inputstream.js
netwerk/test/unit/xpcshell.ini
toolkit/components/telemetry/Histograms.json
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -440,16 +440,17 @@ DocAccessibleParent::AddChildDoc(DocAcce
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvShutdown()
 {
+  MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(IProtocol::Id()));
   Destroy();
 
   auto mgr = static_cast<dom::TabParent*>(Manager());
   if (!mgr->IsDestroyed()) {
     if (!PDocAccessibleParent::Send__delete__(this)) {
       return IPC_FAIL_NO_REASON(mgr);
     }
   }
--- a/browser/base/content/test/general/browser_bug623893.js
+++ b/browser/base/content/test/general/browser_bug623893.js
@@ -1,45 +1,37 @@
-function test() {
-  waitForExplicitFinish();
+add_task(function* test() {
+  yield BrowserTestUtils.withNewTab("data:text/plain;charset=utf-8,1", function* (browser) {
+    BrowserTestUtils.loadURI(browser, "data:text/plain;charset=utf-8,2");
+    yield BrowserTestUtils.browserLoaded(browser);
+
+    BrowserTestUtils.loadURI(browser, "data:text/plain;charset=utf-8,3");
+    yield BrowserTestUtils.browserLoaded(browser);
+
+    yield duplicate(0, "maintained the original index");
+    yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
-  loadAndWait("data:text/plain,1", function() {
-    loadAndWait("data:text/plain,2", function() {
-      loadAndWait("data:text/plain,3", runTests);
-    });
+    yield duplicate(-1, "went back");
+    yield duplicate(1, "went forward");
+    yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+    yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  });
+});
+
+function promiseGetIndex(browser) {
+  return ContentTask.spawn(browser, null, function() {
+    let shistory = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsISHistory);
+    return shistory.index;
   });
 }
 
-function runTests() {
-  duplicate(0, "maintained the original index", function() {
-    gBrowser.removeCurrentTab();
-
-    duplicate(-1, "went back", function() {
-      duplicate(1, "went forward", function() {
-        gBrowser.removeCurrentTab();
-        gBrowser.removeCurrentTab();
-        gBrowser.addTab();
-        gBrowser.removeCurrentTab();
-        finish();
-      });
-    });
-  });
-}
-
-function duplicate(delta, msg, cb) {
-  var start = gBrowser.sessionHistory.index;
+let duplicate = Task.async(function* (delta, msg, cb) {
+  var startIndex = yield promiseGetIndex(gBrowser.selectedBrowser);
 
   duplicateTabIn(gBrowser.selectedTab, "tab", delta);
+
   let tab = gBrowser.selectedTab;
-
-  tab.addEventListener("SSTabRestored", function() {
-    is(gBrowser.sessionHistory.index, start + delta, msg);
-    executeSoon(cb);
-  }, {once: true});
-}
+  yield BrowserTestUtils.waitForEvent(tab, "SSTabRestored");
 
-function loadAndWait(url, cb) {
-  gBrowser.selectedBrowser.addEventListener("load", function() {
-    executeSoon(cb);
-  }, {capture: true, once: true});
-
-  gBrowser.loadURI(url);
-}
+  let endIndex = yield promiseGetIndex(gBrowser.selectedBrowser);
+  is(endIndex, startIndex + delta, msg);
+});
--- a/browser/locales/searchplugins/odpiralni.xml
+++ b/browser/locales/searchplugins/odpiralni.xml
@@ -2,14 +2,13 @@
    - 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/. -->
 
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Odpiralni Časi</ShortName>
 <Description>Odpiralni Časi v Sloveniji</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAEJGlDQ1BJQ0MgUHJvZmlsZQAAOBGFVd9v21QUPolvUqQWPyBYR4eKxa9VU1u5GxqtxgZJk6XtShal6dgqJOQ6N4mpGwfb6baqT3uBNwb8AUDZAw9IPCENBmJ72fbAtElThyqqSUh76MQPISbtBVXhu3ZiJ1PEXPX6yznfOec7517bRD1fabWaGVWIlquunc8klZOnFpSeTYrSs9RLA9Sr6U4tkcvNEi7BFffO6+EdigjL7ZHu/k72I796i9zRiSJPwG4VHX0Z+AxRzNRrtksUvwf7+Gm3BtzzHPDTNgQCqwKXfZwSeNHHJz1OIT8JjtAq6xWtCLwGPLzYZi+3YV8DGMiT4VVuG7oiZpGzrZJhcs/hL49xtzH/Dy6bdfTsXYNY+5yluWO4D4neK/ZUvok/17X0HPBLsF+vuUlhfwX4j/rSfAJ4H1H0qZJ9dN7nR19frRTeBt4Fe9FwpwtN+2p1MXscGLHR9SXrmMgjONd1ZxKzpBeA71b4tNhj6JGoyFNp4GHgwUp9qplfmnFW5oTdy7NamcwCI49kv6fN5IAHgD+0rbyoBc3SOjczohbyS1drbq6pQdqumllRC/0ymTtej8gpbbuVwpQfyw66dqEZyxZKxtHpJn+tZnpnEdrYBbueF9qQn93S7HQGGHnYP7w6L+YGHNtd1FJitqPAR+hERCNOFi1i1alKO6RQnjKUxL1GNjwlMsiEhcPLYTEiT9ISbN15OY/jx4SMshe9LaJRpTvHr3C/ybFYP1PZAfwfYrPsMBtnE6SwN9ib7AhLwTrBDgUKcm06FSrTfSj187xPdVQWOk5Q8vxAfSiIUc7Z7xr6zY/+hpqwSyv0I0/QMTRb7RMgBxNodTfSPqdraz/sDjzKBrv4zu2+a2t0/HHzjd2Lbcc2sG7GtsL42K+xLfxtUgI7YHqKlqHK8HbCCXgjHT1cAdMlDetv4FnQ2lLasaOl6vmB0CMmwT/IPszSueHQqv6i/qluqF+oF9TfO2qEGTumJH0qfSv9KH0nfS/9TIp0Wboi/SRdlb6RLgU5u++9nyXYe69fYRPdil1o1WufNSdTTsp75BfllPy8/LI8G7AUuV8ek6fkvfDsCfbNDP0dvRh0CrNqTbV7LfEEGDQPJQadBtfGVMWEq3QWWdufk6ZSNsjG2PQjp3ZcnOWWing6noonSInvi0/Ex+IzAreevPhe+CawpgP1/pMTMDo64G0sTCXIM+KdOnFWRfQKdJvQzV1+Bt8OokmrdtY2yhVX2a+qrykJfMq4Ml3VR4cVzTQVz+UoNne4vcKLoyS+gyKO6EHe+75Fdt0Mbe5bRIf/wjvrVmhbqBN97RD1vxrahvBOfOYzoosH9bq94uejSOQGkVM6sN/7HelL4t10t9F4gPdVzydEOx83Gv+uNxo7XyL/FtFl8z9ZAHF4bBsrEwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAnlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuMS4yIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBJbWFnZVJlYWR5PC94bXA6Q3JlYXRvclRvb2w+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOllSZXNvbHV0aW9uPjI5OTk5LzEwMDwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+Mjk5OTkvMTAwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KHvZwVAAAA0xJREFUOBE9U99PHFUYPXPv/NjdWXELLlRbgUSRXYxtgEVAcfXR/tBqGpBoaNIE+2rE+NAQH/AvUN980gf6Aib2uZhg0sqq2fWBVEtraRNghbWwdJlhdnfu/Oh3J6E3uZncOznnfOfc71PCMMTExARfWFjwFVrv5POXAs+bcuzD0WajkfB9H3HTPDRN8xZT+PyvKzevESZ8ihkfH4/AQ0NDvaZuzDu2nXMOHbzSl8XJF09C0zQ8fPAQ/95ZQywRh5k0f3cbjamVUum+JFFkBWME5pp+s7a/n3711CkxPvkR6+vL8kMiery/j3Q6jXt37/qLC4vh2t//qC2plh3Pdcd+KxbXmSxb1fV5q1ZLj72dd6/Ozmq53CBPJpP4q1TCl5/PwPM89Pf385kvZtTXR4dd68A6rurGD6DF8iMjlzzh5dIdHd7l6Wk9lXoWtmUTyIcQHuyDAzSbTdi2jYRpYmJyUm9ta/N8z3srP/LmRcY17ePKf9t474MLyvGODjh1RxIjCAJAAWWgA2RTHuqOg/b2drx75oxS3d0DU9knTLjijVRbKzLZLJeJS2AQBgjpK7fCFDqHdO8TTwhSRld3F3umpQWeEHnmus1kmliletN1IzHfJ2CkqURWJJEklgSCCMiCcuy5VknQpiKqTol+RuoENnQd5XIZSzdu4KWel0E5w5ckRKBIItryTi6mappT3d1FpVKJLqlibG1u4uu5OWxvlTF95VMYhhG9hBSQsD3yb9UOwFW1xuhn4dFOBQ/W132VMTQo8e+++RZ7/+/i6lez6O7uhitEFGRAGcmKNzY2fNuyiEAr8M4TnYjFYx9ub+8Eg7lBFovFYNA+//55dHZ1oV6vR6XKXtBUDdVqFT8tLgZkieTYHKfGuN2s1y+UN7deoBBFJpPhvZleJOLx6P2lV+lZp+e0bAvXr/8s1u/dJy6teOuPwmdRKw8PDJ82DL5CrZw4PTAgzp47y0lddmmUvk9NRbkEvywt+bdXV7VYPL7neWJspVhcU46GaTSXG9B140eawtdCMtqbzSCVOkbBgubhcTRQBJL2VoUbThZKhTtPh+loNHt6eowT7c9fCQIxRYPUT02jyio45z6p/sm5do0Z6vfLy8veEeYJlojIHKnXHv0AAAAASUVORK5CYII=</Image>
-<UpdateUrl>http://www.odpiralnicasi.com/opensearch/description.xml</UpdateUrl>
 <Url type="text/html" method="GET" template="http://www.odpiralnicasi.com/spots" resultdomain="odpiralnicasi.com">
   <Param name="q" value="{searchTerms}"/>
   <Param name="source" value="1"/>
 </Url>
 </SearchPlugin>
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -103,17 +103,16 @@ browser.jar:
   skin/classic/browser/tabbrowser/tab-stroke-end@2x.png     (tabbrowser/tab-stroke-end@2x.png)
   skin/classic/browser/tabbrowser/tab-stroke-start.png      (tabbrowser/tab-stroke-start.png)
   skin/classic/browser/tabbrowser/tab-stroke-start@2x.png   (tabbrowser/tab-stroke-start@2x.png)
   skin/classic/browser/tabbrowser/tabDragIndicator.png      (tabbrowser/tabDragIndicator.png)
 
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
-  skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
   skin/classic/browser/sync-horizontalbar.png
   skin/classic/browser/sync-horizontalbar@2x.png
   skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
   skin/classic/browser/syncProgress-horizontalbar.png
   skin/classic/browser/syncProgress-horizontalbar@2x.png
 #ifdef E10S_TESTING_ONLY
   skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
deleted file mode 100644
index 1ea34818ceb9cf9b4ea607fc62884c575e71b893..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -154,17 +154,16 @@ browser.jar:
   skin/classic/browser/tabbrowser/tab-stroke-end@2x.png                  (tabbrowser/tab-stroke-end@2x.png)
   skin/classic/browser/tabbrowser/tab-stroke-start.png                   (tabbrowser/tab-stroke-start.png)
   skin/classic/browser/tabbrowser/tab-stroke-start@2x.png                (tabbrowser/tab-stroke-start@2x.png)
   skin/classic/browser/tabbrowser/tabDragIndicator.png                   (tabbrowser/tabDragIndicator.png)
   skin/classic/browser/tabbrowser/tabDragIndicator@2x.png                (tabbrowser/tabDragIndicator@2x.png)
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
-  skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
   skin/classic/browser/sync-horizontalbar.png
   skin/classic/browser/sync-horizontalbar@2x.png
   skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
   skin/classic/browser/syncProgress-horizontalbar.png
   skin/classic/browser/syncProgress-horizontalbar@2x.png
   skin/classic/browser/Toolbar-background-noise.png         (Toolbar-background-noise.png)
   skin/classic/browser/yosemite/Toolbar.png                            (Toolbar-yosemite.png)
deleted file mode 100644
index 1ea34818ceb9cf9b4ea607fc62884c575e71b893..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -132,17 +132,16 @@ browser.jar:
 
   skin/classic/browser/tabbrowser/tab-stroke-end.png           (tabbrowser/tab-stroke-end.png)
   skin/classic/browser/tabbrowser/tab-stroke-end@2x.png        (tabbrowser/tab-stroke-end@2x.png)
   skin/classic/browser/tabbrowser/tab-stroke-start.png         (tabbrowser/tab-stroke-start.png)
   skin/classic/browser/tabbrowser/tab-stroke-start@2x.png      (tabbrowser/tab-stroke-start@2x.png)
   skin/classic/browser/tabbrowser/tabDragIndicator.png         (tabbrowser/tabDragIndicator.png)
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
-  skin/classic/browser/sync-128.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
   skin/classic/browser/sync-horizontalbar.png
   skin/classic/browser/sync-horizontalbar@2x.png
   skin/classic/browser/sync-horizontalbar-win7.png
   skin/classic/browser/sync-horizontalbar-win7@2x.png
   skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
   skin/classic/browser/syncProgress-horizontalbar.png
deleted file mode 100644
index 0647fb08044ad9005d424ecadfa68dd29e807c58..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/devtools/client/webide/test/test_telemetry.html
+++ b/devtools/client/webide/test/test_telemetry.html
@@ -161,17 +161,17 @@
         return Task.spawn(function*() {
           startConnection(win, docRuntime, type, index);
           yield waitUntilConnected(win);
         });
       }
 
       function checkResults() {
         let result = Telemetry.prototype.telemetryInfo;
-        for (let [histId, value] of Iterator(result)) {
+        for (let [histId, value] of Object.entries(result)) {
           if (histId === "DEVTOOLS_WEBIDE_IMPORT_PROJECT_BOOLEAN") {
             ok(value.length === 1 && !!value[0],
                histId + " has 1 successful entry");
           } else if (histId ===
                      "DEVTOOLS_WEBIDE_PROJECT_EDITOR_OPENED_COUNT") {
             ok(value.length === 1 && !!value[0],
                histId + " has 1 successful entry");
           } else if (histId === "DEVTOOLS_WEBIDE_OPENED_COUNT") {
--- a/devtools/server/actors/utils/css-grid-utils.js
+++ b/devtools/server/actors/utils/css-grid-utils.js
@@ -32,30 +32,39 @@ function getStringifiableFragments(fragm
  * @return {String} representation of the CSS grid fragment data.
  */
 function stringifyGridFragments(fragments) {
   return JSON.stringify(getStringifiableFragments(fragments));
 }
 
 function getStringifiableFragment(fragment) {
   return {
+    areas: getStringifiableAreas(fragment.areas),
     cols: getStringifiableDimension(fragment.cols),
     rows: getStringifiableDimension(fragment.rows)
   };
 }
 
+function getStringifiableAreas(areas) {
+  return [...areas].map(getStringifiableArea);
+}
+
 function getStringifiableDimension(dimension) {
   return {
     lines: [...dimension.lines].map(getStringifiableLine),
     tracks: [...dimension.tracks].map(getStringifiableTrack),
   };
 }
 
-function getStringifiableLine({ breadth, number, start, names }) {
-  return { breadth, number, start, names };
+function getStringifiableArea({ columnEnd, columnStart, name, rowEnd, rowStart, type }) {
+  return { columnEnd, columnStart, name, rowEnd, rowStart, type };
+}
+
+function getStringifiableLine({ breadth, names, number, start }) {
+  return { breadth, names, number, start };
 }
 
 function getStringifiableTrack({ breadth, start, state, type }) {
   return { breadth, start, state, type };
 }
 
 exports.getStringifiableFragments = getStringifiableFragments;
 exports.stringifyGridFragments = stringifyGridFragments;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2219,19 +2219,27 @@ class PropertyDefiner:
             # And the actual spec
             specs.append(specFormatter(getDataTuple(member)))
         specs.append(specTerminator)
         prefableSpecs.append("  { nullptr, nullptr }")
 
         specType = "const " + specType
         arrays = fill(
             """
+            // We deliberately use brace-elision to make Visual Studio produce better initalization code.
+            #if defined(__clang__)
+            #pragma clang diagnostic push
+            #pragma clang diagnostic ignored "-Wmissing-braces"
+            #endif
             static ${specType} ${name}_specs[] = {
             ${specs}
             };
+            #if defined(__clang__)
+            #pragma clang diagnostic pop
+            #endif
 
             ${disablers}
             // Can't be const because the pref-enabled boolean needs to be writable
             static Prefable<${specType}> ${name}[] = {
             ${prefableSpecs}
             };
 
             """,
@@ -2684,49 +2692,49 @@ class AttrDefiner(PropertyDefiner):
                     else:
                         accessor = "genericGetter"
                 elif attr.type.isPromise():
                     accessor = "GenericPromiseReturningBindingGetter"
                 else:
                     accessor = "GenericBindingGetter"
                 jitinfo = ("&%s_getterinfo" %
                            IDLToCIdentifier(attr.identifier.name))
-            return "{ { %s, %s } }" % \
+            return "%s, %s" % \
                    (accessor, jitinfo)
 
         def setter(attr):
             if (attr.readonly and
                 attr.getExtendedAttribute("PutForwards") is None and
                 attr.getExtendedAttribute("Replaceable") is None and
                 attr.getExtendedAttribute("LenientSetter") is None):
-                return "JSNATIVE_WRAPPER(nullptr)"
+                return "nullptr, nullptr"
             if self.static:
                 accessor = 'set_' + IDLToCIdentifier(attr.identifier.name)
                 jitinfo = "nullptr"
             else:
                 if attr.hasLenientThis():
                     accessor = "genericLenientSetter"
                 elif IsCrossOriginWritable(attr, self.descriptor):
                     accessor = "genericCrossOriginSetter"
                 elif self.descriptor.needsSpecialGenericOps():
                     accessor = "genericSetter"
                 else:
                     accessor = "GenericBindingSetter"
                 jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
-            return "{ { %s, %s } }" % \
+            return "%s, %s" % \
                    (accessor, jitinfo)
 
         def specData(attr):
             return (attr.identifier.name, flags(attr), getter(attr),
                     setter(attr))
 
         return self.generatePrefableArray(
             array, name,
-            lambda fields: '  { "%s", %s, { { %s, %s } } }' % fields,
-            '  JS_PS_END',
+            lambda fields: '  { "%s", %s, %s, %s }' % fields,
+            '  { nullptr, 0, nullptr, nullptr, nullptr, nullptr }',
             'JSPropertySpec',
             PropertyDefiner.getControllingCondition, specData, doIdArrays)
 
 
 class ConstDefiner(PropertyDefiner):
     """
     A class for definining constants on the interface object
     """
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1054,16 +1054,18 @@ NS_INTERFACE_MAP_END
 /**
  ** CanvasRenderingContext2D impl
  **/
 
 
 // Initialize our static variables.
 uint32_t CanvasRenderingContext2D::sNumLivingContexts = 0;
 DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
+static bool sMaxContextsInitialized = false;
+static int32_t sMaxContexts = 0;
 
 
 
 CanvasRenderingContext2D::CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend)
   : mRenderingMode(RenderingMode::OpenGLBackendMode)
   , mCompositorBackend(aCompositorBackend)
   // these are the default values from the Canvas spec
   , mWidth(0), mHeight(0)
@@ -1074,16 +1076,21 @@ CanvasRenderingContext2D::CanvasRenderin
   , mHasPendingStableStateCallback(false)
   , mDrawObserver(nullptr)
   , mIsEntireFrameInvalid(false)
   , mPredictManyRedrawCalls(false)
   , mIsCapturedFrameInvalid(false)
   , mPathTransformWillUpdate(false)
   , mInvalidateCount(0)
 {
+  if (!sMaxContextsInitialized) {
+    sMaxContexts = gfxPrefs::CanvasAzureAcceleratedLimit();
+    sMaxContextsInitialized = true;
+  }
+
   sNumLivingContexts++;
 
   mShutdownObserver = new CanvasShutdownObserver(this);
   nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
 
   // The default is to use OpenGL mode
   if (AllowOpenGLCanvas()) {
     mDrawObserver = new CanvasDrawObserver(this);
@@ -1426,41 +1433,52 @@ CanvasRenderingContext2D::DemotableConte
   // and will be in an inconsistant state.
   static std::vector<CanvasRenderingContext2D*> contexts;
   return contexts;
 }
 
 void
 CanvasRenderingContext2D::DemoteOldestContextIfNecessary()
 {
-  const size_t kMaxContexts = 64;
+  MOZ_ASSERT(sMaxContextsInitialized);
+  if (sMaxContexts <= 0) {
+    return;
+  }
 
   std::vector<CanvasRenderingContext2D*>& contexts = DemotableContexts();
-  if (contexts.size() < kMaxContexts)
+  if (contexts.size() < (size_t)sMaxContexts)
     return;
 
   CanvasRenderingContext2D* oldest = contexts.front();
   if (oldest->SwitchRenderingMode(RenderingMode::SoftwareBackendMode)) {
     RemoveDemotableContext(oldest);
   }
 }
 
 void
 CanvasRenderingContext2D::AddDemotableContext(CanvasRenderingContext2D* aContext)
 {
+  MOZ_ASSERT(sMaxContextsInitialized);
+  if (sMaxContexts <= 0)
+    return;
+
   std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(DemotableContexts().begin(), DemotableContexts().end(), aContext);
   if (iter != DemotableContexts().end())
     return;
 
   DemotableContexts().push_back(aContext);
 }
 
 void
 CanvasRenderingContext2D::RemoveDemotableContext(CanvasRenderingContext2D* aContext)
 {
+  MOZ_ASSERT(sMaxContextsInitialized);
+  if (sMaxContexts <= 0)
+    return;
+
   std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(DemotableContexts().begin(), DemotableContexts().end(), aContext);
   if (iter != DemotableContexts().end())
     DemotableContexts().erase(iter);
 }
 
 #define MIN_SKIA_GL_DIMENSION 16
 
 bool
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -2030,55 +2030,102 @@ WebGLContext::UniformNfv(const char* fun
         &gl::GLContext::fUniform4fv
     };
     const auto func = kFuncList[N-1];
 
     MakeContextCurrent();
     (gl->*func)(loc->mLoc, numElementsToUpload, elemBytes);
 }
 
+static inline void
+MatrixAxBToRowMajor(const uint8_t width, const uint8_t height,
+                    const float* __restrict srcColMajor,
+                    float* __restrict dstRowMajor)
+{
+    for (uint8_t x = 0; x < width; ++x) {
+        for (uint8_t y = 0; y < height; ++y) {
+            dstRowMajor[y * width + x] = srcColMajor[x * height + y];
+        }
+    }
+}
+
 void
 WebGLContext::UniformMatrixAxBfv(const char* funcName, uint8_t A, uint8_t B,
-                                 WebGLUniformLocation* loc, bool transpose,
+                                 WebGLUniformLocation* loc, const bool transpose,
                                  const Float32Arr& arr, GLuint elemOffset,
                                  GLuint elemCountOverride)
 {
     size_t elemCount;
     if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
                                    elemCountOverride, &elemCount))
     {
         return;
     }
     const auto elemBytes = arr.elemBytes + elemOffset;
 
-    uint32_t numElementsToUpload;
+    uint32_t numMatsToUpload;
     if (!ValidateUniformMatrixArraySetter(loc, A, B, LOCAL_GL_FLOAT, elemCount,
-                                          transpose, funcName, &numElementsToUpload))
+                                          transpose, funcName, &numMatsToUpload))
     {
         return;
     }
     MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
 
+    ////
+
+    bool uploadTranspose = transpose;
+    const float* uploadBytes = elemBytes;
+
+    UniqueBuffer temp;
+    if (!transpose && gl->WorkAroundDriverBugs() && gl->IsANGLE() &&
+        gl->IsAtLeast(gl::ContextProfile::OpenGLES, 300))
+    {
+        // ANGLE is really slow at non-GL-transposed matrices.
+        const size_t kElemsPerMat = A * B;
+
+        temp = malloc(numMatsToUpload * kElemsPerMat * sizeof(float));
+        if (!temp) {
+            ErrorOutOfMemory("%s: Failed to alloc temporary buffer for transposition.",
+                             funcName);
+            return;
+        }
+
+        auto srcItr = (const float*)elemBytes;
+        auto dstItr = (float*)temp.get();
+        const auto srcEnd = srcItr + numMatsToUpload * kElemsPerMat;
+
+        while (srcItr != srcEnd) {
+            MatrixAxBToRowMajor(A, B, srcItr, dstItr);
+            srcItr += kElemsPerMat;
+            dstItr += kElemsPerMat;
+        }
+
+        uploadBytes = (const float*)temp.get();
+        uploadTranspose = true;
+    }
+
+    ////
+
     static const decltype(&gl::GLContext::fUniformMatrix2fv) kFuncList[] = {
         &gl::GLContext::fUniformMatrix2fv,
         &gl::GLContext::fUniformMatrix2x3fv,
         &gl::GLContext::fUniformMatrix2x4fv,
 
         &gl::GLContext::fUniformMatrix3x2fv,
         &gl::GLContext::fUniformMatrix3fv,
         &gl::GLContext::fUniformMatrix3x4fv,
 
         &gl::GLContext::fUniformMatrix4x2fv,
         &gl::GLContext::fUniformMatrix4x3fv,
         &gl::GLContext::fUniformMatrix4fv
     };
     const auto func = kFuncList[3*(A-2) + (B-2)];
 
     MakeContextCurrent();
-    (gl->*func)(loc->mLoc, numElementsToUpload, transpose, elemBytes);
+    (gl->*func)(loc->mLoc, numMatsToUpload, uploadTranspose, uploadBytes);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 void
 WebGLContext::UseProgram(WebGLProgram* prog)
 {
     if (IsContextLost())
--- a/dom/file/FileBlobImpl.cpp
+++ b/dom/file/FileBlobImpl.cpp
@@ -226,23 +226,31 @@ const uint32_t sFileStreamFlags =
   nsIFileInputStream::CLOSE_ON_EOF |
   nsIFileInputStream::REOPEN_ON_REWIND |
   nsIFileInputStream::DEFER_OPEN |
   nsIFileInputStream::SHARE_DELETE;
 
 void
 FileBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
 {
-  if (mWholeFile) {
-    aRv = NS_NewLocalFileInputStream(aStream, mFile, -1, -1, sFileStreamFlags);
+  nsCOMPtr<nsIInputStream> stream;
+  aRv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1,
+                                   sFileStreamFlags);
+  if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  aRv = NS_NewPartialLocalFileInputStream(aStream, mFile, mStart, mLength,
-                                          -1, -1, sFileStreamFlags);
+  if (mWholeFile) {
+    stream.forget(aStream);
+    return;
+  }
+
+  RefPtr<SlicedInputStream> slicedInputStream =
+    new SlicedInputStream(stream, mStart, mLength);
+  slicedInputStream.forget(aStream);
 }
 
 bool
 FileBlobImpl::IsDirectory() const
 {
   bool isDirectory = false;
   if (mFile) {
     mFile->IsDirectory(&isDirectory);
deleted file mode 100644
--- a/dom/ipc/CrashReporterChild.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- 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/plugins/PluginModuleChild.h"
-#include "ContentChild.h"
-#include "CrashReporterChild.h"
-#include "nsXULAppAPI.h"
-
-using mozilla::plugins::PluginModuleChild;
-
-namespace mozilla {
-namespace dom {
-
-/*static*/
-PCrashReporterChild*
-CrashReporterChild::GetCrashReporter()
-{
-  return nullptr;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/ipc/CrashReporterChild.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- 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_dom_CrashReporterChild_h
-#define mozilla_dom_CrashReporterChild_h
-
-#include "mozilla/dom/PCrashReporterChild.h"
-
-namespace mozilla {
-namespace dom {
-
-class CrashReporterChild :
-  public PCrashReporterChild
-{
-public:
-  CrashReporterChild() {
-    MOZ_COUNT_CTOR(CrashReporterChild);
-  }
-  ~CrashReporterChild() {
-    MOZ_COUNT_DTOR(CrashReporterChild);
-  }
-
-  static PCrashReporterChild* GetCrashReporter();
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_CrashReporterChild_h
deleted file mode 100644
--- a/dom/ipc/CrashReporterParent.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/* -*- 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 "CrashReporterParent.h"
-#include "mozilla/Sprintf.h"
-#include "mozilla/dom/ContentParent.h"
-#include "mozilla/ipc/CrashReporterHost.h"
-#include "nsAutoPtr.h"
-#include "nsXULAppAPI.h"
-#include <time.h>
-
-#include "mozilla/Telemetry.h"
-
-#ifdef MOZ_CRASHREPORTER
-#include "nsExceptionHandler.h"
-#include "nsICrashService.h"
-#include "mozilla/SyncRunnable.h"
-#include "nsThreadUtils.h"
-#endif
-
-namespace mozilla {
-namespace dom {
-
-using namespace mozilla::ipc;
-
-void
-CrashReporterParent::AnnotateCrashReport(const nsCString& key,
-                                         const nsCString& data)
-{
-#ifdef MOZ_CRASHREPORTER
-  mNotes.Put(key, data);
-#endif
-}
-
-void
-CrashReporterParent::ActorDestroy(ActorDestroyReason aWhy)
-{
-  // Implement me! Bug 1005155
-}
-
-mozilla::ipc::IPCResult
-CrashReporterParent::RecvAppendAppNotes(const nsCString& data)
-{
-  mAppNotes.Append(data);
-  return IPC_OK();
-}
-
-CrashReporterParent::CrashReporterParent()
-  :
-#ifdef MOZ_CRASHREPORTER
-    mNotes(4),
-#endif
-    mStartTime(::time(nullptr))
-  , mInitialized(false)
-{
-  MOZ_COUNT_CTOR(CrashReporterParent);
-}
-
-CrashReporterParent::~CrashReporterParent()
-{
-  MOZ_COUNT_DTOR(CrashReporterParent);
-}
-
-void
-CrashReporterParent::SetChildData(const NativeThreadId& tid,
-                                  const uint32_t& processType)
-{
-  mInitialized = true;
-  mMainThread = tid;
-  mProcessType = GeckoProcessType(processType);
-}
-
-#ifdef MOZ_CRASHREPORTER
-bool
-CrashReporterParent::GenerateCrashReportForMinidump(nsIFile* minidump,
-                                                    const AnnotationTable* processNotes)
-{
-  if (!CrashReporter::GetIDFromMinidump(minidump, mChildDumpID)) {
-    return false;
-  }
-
-  bool result = GenerateChildData(processNotes);
-  FinalizeChildData();
-  return result;
-}
-
-bool
-CrashReporterParent::GenerateChildData(const AnnotationTable* processNotes)
-{
-  MOZ_ASSERT(mInitialized);
-
-  if (mChildDumpID.IsEmpty()) {
-    NS_WARNING("problem with GenerateChildData: no child dump id yet!");
-    return false;
-  }
-
-  nsAutoCString type;
-  switch (mProcessType) {
-    case GeckoProcessType_Content:
-      type = NS_LITERAL_CSTRING("content");
-      break;
-    case GeckoProcessType_Plugin:
-    case GeckoProcessType_GMPlugin:
-      type = NS_LITERAL_CSTRING("plugin");
-      break;
-    default:
-      NS_ERROR("unknown process type");
-      break;
-  }
-  mNotes.Put(NS_LITERAL_CSTRING("ProcessType"), type);
-
-  char startTime[32];
-  SprintfLiteral(startTime, "%lld", static_cast<long long>(mStartTime));
-  mNotes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime));
-
-  if (!mAppNotes.IsEmpty()) {
-    mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes);
-  }
-
-  // Append these notes to the end of the extra file based on the current
-  // dump id we obtained from CreatePairedMinidumps.
-  bool ret = CrashReporter::AppendExtraData(mChildDumpID, mNotes);
-  if (ret && processNotes) {
-    ret = CrashReporter::AppendExtraData(mChildDumpID, *processNotes);
-  }
-
-  if (!ret) {
-    NS_WARNING("problem appending child data to .extra");
-  }
-  return ret;
-}
-
-void
-CrashReporterParent::FinalizeChildData()
-{
-  MOZ_ASSERT(mInitialized);
-
-  CrashReporterHost::NotifyCrashService(mProcessType, mChildDumpID, &mNotes);
-  mNotes.Clear();
-}
-#endif
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/ipc/CrashReporterParent.h
+++ /dev/null
@@ -1,197 +0,0 @@
-/* -*- 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_dom_CrashReporterParent_h
-#define mozilla_dom_CrashReporterParent_h
-
-#include "mozilla/dom/PCrashReporterParent.h"
-#include "mozilla/dom/TabMessageUtils.h"
-#include "nsIFile.h"
-#ifdef MOZ_CRASHREPORTER
-#include "nsExceptionHandler.h"
-#include "nsDataHashtable.h"
-#endif
-
-namespace mozilla {
-namespace dom {
-
-class CrashReporterParent : public PCrashReporterParent
-{
-#ifdef MOZ_CRASHREPORTER
-  typedef CrashReporter::AnnotationTable AnnotationTable;
-#endif
-public:
-  CrashReporterParent();
-  virtual ~CrashReporterParent();
-
-#ifdef MOZ_CRASHREPORTER
-
-  /*
-   * Attempt to create a bare-bones crash report, along with extra process-
-   * specific annotations present in the given AnnotationTable. Calls
-   * GenerateChildData and FinalizeChildData.
-   *
-   * @returns true if successful, false otherwise.
-   */
-  template<class Toplevel>
-  bool
-  GenerateCrashReport(Toplevel* t, const AnnotationTable* processNotes);
-
-  /**
-   * Apply child process annotations to an existing paired mindump generated
-   * with GeneratePairedMinidump.
-   *
-   * Be careful about calling generate apis immediately after this call,
-   * see FinalizeChildData.
-   *
-   * @param processNotes (optional) - Additional notes to append. Annotations
-   *   stored in mNotes will also be applied. processNotes can be null.
-   * @returns true if successful, false otherwise.
-   */
-  bool
-  GenerateChildData(const AnnotationTable* processNotes);
-
-  /**
-   * Handles main thread finalization tasks after a report has been
-   * generated. Does the following:
-   *  - register the finished report with the crash service manager
-   *  - records telemetry related data about crashes
-   *
-   * Be careful about calling generate apis immediately after this call,
-   * if this api is called on a non-main thread it will fire off a runnable
-   * to complete its work async.
-   */
-  void
-  FinalizeChildData();
-
-  /*
-   * Attempt to generate a full paired dump complete with any child
-   * annoations, and finalizes the report. Note this call is only valid
-   * on the main thread. Calling on a background thread will fail.
-   *
-   * @returns true if successful, false otherwise.
-   */
-  template<class Toplevel>
-  bool
-  GenerateCompleteMinidump(Toplevel* t);
-
-  /**
-   * Submits a raw minidump handed in, calls GenerateChildData and
-   * FinalizeChildData. Used by content plugins and gmp.
-   *
-   * @returns true if successful, false otherwise.
-   */
-  bool
-  GenerateCrashReportForMinidump(nsIFile* minidump,
-                                 const AnnotationTable* processNotes);
-#endif // MOZ_CRASHREPORTER
-
-  /*
-   * Initialize this reporter with data from the child process.
-   */
-  void
-  SetChildData(const NativeThreadId& id, const uint32_t& processType);
-
-  /*
-   * Returns the ID of the child minidump.
-   * GeneratePairedMinidump or GenerateCrashReport must be called first.
-   */
-  const nsString& ChildDumpID() const {
-    return mChildDumpID;
-  }
-
-  /*
-   * Add an annotation to our internally tracked list of annotations.
-   * Callers must apply these notes using GenerateChildData otherwise
-   * the notes will get dropped.
-   */
-  void
-  AnnotateCrashReport(const nsCString& aKey, const nsCString& aData);
-
- protected:
-  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
-
-  virtual mozilla::ipc::IPCResult RecvAnnotateCrashReport(const nsCString& aKey,
-                                                          const nsCString& aData) override
-  {
-    AnnotateCrashReport(aKey, aData);
-    return IPC_OK();
-  }
-
-  virtual mozilla::ipc::IPCResult RecvAppendAppNotes(const nsCString& aData) override;
-
-#ifdef MOZ_CRASHREPORTER
-  void
-  NotifyCrashService();
-#endif
-
-#ifdef MOZ_CRASHREPORTER
-  AnnotationTable mNotes;
-#endif
-  nsCString mAppNotes;
-  nsString mChildDumpID;
-  // stores the child main thread id
-  NativeThreadId mMainThread;
-  time_t mStartTime;
-  // stores the child process type
-  GeckoProcessType mProcessType;
-  bool mInitialized;
-};
-
-#ifdef MOZ_CRASHREPORTER
-template<class Toplevel>
-inline bool
-CrashReporterParent::GenerateCrashReport(Toplevel* t,
-                                         const AnnotationTable* processNotes)
-{
-  nsCOMPtr<nsIFile> crashDump;
-  if (t->TakeMinidump(getter_AddRefs(crashDump), nullptr) &&
-      CrashReporter::GetIDFromMinidump(crashDump, mChildDumpID)) {
-    bool result = GenerateChildData(processNotes);
-    FinalizeChildData();
-    return result;
-  }
-  return false;
-}
-
-template<class Toplevel>
-inline bool
-CrashReporterParent::GenerateCompleteMinidump(Toplevel* t)
-{
-  mozilla::ipc::ScopedProcessHandle child;
-  if (!NS_IsMainThread()) {
-    NS_WARNING("GenerateCompleteMinidump can't be called on non-main thread.");
-    return false;
-  }
-
-#ifdef XP_MACOSX
-  child = t->Process()->GetChildTask();
-#else
-  if (!base::OpenPrivilegedProcessHandle(t->OtherPid(), &child.rwget())) {
-    NS_WARNING("Failed to open child process handle.");
-    return false;
-  }
-#endif
-  nsCOMPtr<nsIFile> childDump;
-  if (CrashReporter::CreateMinidumpsAndPair(child,
-                                            mMainThread,
-                                            NS_LITERAL_CSTRING("browser"),
-                                            nullptr, // pair with a dump of this process and thread
-                                            getter_AddRefs(childDump)) &&
-      CrashReporter::GetIDFromMinidump(childDump, mChildDumpID)) {
-    bool result = GenerateChildData(nullptr);
-    FinalizeChildData();
-    return result;
-  }
-  return false;
-}
-
-#endif
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_CrashReporterParent_h
deleted file mode 100644
--- a/dom/ipc/PCrashReporter.ipdl
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set sw=4 ts=8 et 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 protocol PContent;
-include protocol PPluginModule;
-include protocol PGMP;
-
-namespace mozilla {
-namespace dom {
-
-struct Mapping {
-  nsCString library_name;
-  nsCString file_id;
-  uintptr_t start_address;
-  size_t mapping_length;
-  size_t file_offset;
-};
-
-async protocol PCrashReporter {
-parent:
-  async AnnotateCrashReport(nsCString key, nsCString data);
-  async AppendAppNotes(nsCString data);
-  async __delete__();
-};
-
-}
-}
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -19,18 +19,16 @@ EXPORTS.mozilla.dom += [
     'ContentBridgeChild.h',
     'ContentBridgeParent.h',
     'ContentChild.h',
     'ContentParent.h',
     'ContentPrefs.h',
     'ContentProcess.h',
     'ContentProcessManager.h',
     'CPOWManagerGetter.h',
-    'CrashReporterChild.h',
-    'CrashReporterParent.h',
     'FilePickerParent.h',
     'MemoryReportRequest.h',
     'nsIContentChild.h',
     'nsIContentParent.h',
     'PermissionMessageUtils.h',
     'TabChild.h',
     'TabContext.h',
     'TabMessageUtils.h',
@@ -49,17 +47,16 @@ EXPORTS.mozilla += [
 UNIFIED_SOURCES += [
     'ColorPickerParent.cpp',
     'ContentBridgeChild.cpp',
     'ContentBridgeParent.cpp',
     'ContentParent.cpp',
     'ContentPrefs.cpp',
     'ContentProcess.cpp',
     'ContentProcessManager.cpp',
-    'CrashReporterParent.cpp',
     'DatePickerParent.cpp',
     'FilePickerParent.cpp',
     'MemoryReportRequest.cpp',
     'nsIContentChild.cpp',
     'nsIContentParent.cpp',
     'PermissionMessageUtils.cpp',
     'PreallocatedProcessManager.cpp',
     'ProcessPriorityManager.cpp',
@@ -68,36 +65,32 @@ UNIFIED_SOURCES += [
     'TabChild.cpp',
     'TabContext.cpp',
     'TabMessageUtils.cpp',
     'TabParent.cpp',
     'URLClassifierChild.cpp',
     'URLClassifierParent.cpp',
 ]
 
-# CrashReporterChild.cpp cannot be compiled in unified mode because of name clashes
-# in OS X headers.
 # ContentChild.cpp cannot be compiled in unified mode on  linux due to Time conflict
 SOURCES += [
     'ContentChild.cpp',
-    'CrashReporterChild.cpp',
     'ProcessHangMonitor.cpp',
 ]
 
 IPDL_SOURCES += [
     'DOMTypes.ipdlh',
     'MemoryReportTypes.ipdlh',
     'PBrowser.ipdl',
     'PBrowserOrId.ipdlh',
     'PColorPicker.ipdl',
     'PContent.ipdl',
     'PContentBridge.ipdl',
     'PContentPermission.ipdlh',
     'PContentPermissionRequest.ipdl',
-    'PCrashReporter.ipdl',
     'PCycleCollectWithLogs.ipdl',
     'PDatePicker.ipdl',
     'PDocumentRenderer.ipdl',
     'PFilePicker.ipdl',
     'PPluginWidget.ipdl',
     'PProcessHangMonitor.ipdl',
     'PScreenManager.ipdl',
     'PTabContext.ipdlh',
--- a/dom/media/MediaShutdownManager.cpp
+++ b/dom/media/MediaShutdownManager.cpp
@@ -69,24 +69,17 @@ MediaShutdownManager::InitStatics()
 
   sInitDone = true;
   sInstance = new MediaShutdownManager();
 
   nsresult rv = GetShutdownBarrier()->AddBlocker(
     sInstance, NS_LITERAL_STRING(__FILE__), __LINE__,
     NS_LITERAL_STRING("MediaShutdownManager shutdown"));
   if (NS_FAILED(rv)) {
-    // Leak the buffer on the heap to make sure that it lives long enough,
-    // as MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to
-    // the end of the program.
-    const size_t CAPACITY = 256;
-    auto buf = new char[CAPACITY];
-    snprintf(buf, CAPACITY, "Failed to add shutdown blocker! rv=%x", uint32_t(rv));
-    MOZ_CRASH_ANNOTATE(buf);
-    MOZ_REALLY_CRASH();
+    MOZ_CRASH_UNSAFE_PRINTF("Failed to add shutdown blocker! rv=%x", uint32_t(rv));
   }
 }
 
 void
 MediaShutdownManager::RemoveBlocker()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mIsDoingXPCOMShutDown);
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -579,19 +579,21 @@ RemoteDataDecoder::Decode(MediaRawData* 
     nsresult rv = BufferInfo::New(&bufferInfo);
     if (NS_FAILED(rv)) {
       return DecodePromise::CreateAndReject(
         MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
     }
     bufferInfo->Set(0, sample->Size(), sample->mTime, 0);
 
     mDrainStatus = DrainStatus::DRAINABLE;
-    RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
-    mJavaDecoder->Input(bytes, bufferInfo, GetCryptoInfoFromSample(sample));
-    return p;
+    return mJavaDecoder->Input(bytes, bufferInfo, GetCryptoInfoFromSample(sample))
+           ? mDecodePromise.Ensure(__func__)
+           : DecodePromise::CreateAndReject(
+               MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
+
   });
 }
 
 void
 RemoteDataDecoder::Output(MediaData* aSample)
 {
   if (!mTaskQueue->IsCurrentThreadIn()) {
     mTaskQueue->Dispatch(
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -94,25 +94,25 @@ struct NPRemoteWindow
   VisualID visualID;
   Colormap colormap;
 #endif /* XP_UNIX */
 #if defined(XP_MACOSX) || defined(XP_WIN)
   double contentsScaleFactor;
 #endif
 };
 
-// This struct is like NPAudioDeviceChangeDetails, only it uses a
-// std::wstring instead of a const wchar_t* for the defaultDevice.
-// This gives us the necessary memory-ownership semantics without
-// requiring C++ objects in npapi.h.
-struct NPAudioDeviceChangeDetailsIPC
-{
-  int32_t flow;
-  int32_t role;
-  std::wstring defaultDevice;
+// This struct is like NPAudioDeviceChangeDetails, only it uses a
+// std::wstring instead of a const wchar_t* for the defaultDevice.
+// This gives us the necessary memory-ownership semantics without
+// requiring C++ objects in npapi.h.
+struct NPAudioDeviceChangeDetailsIPC
+{
+  int32_t flow;
+  int32_t role;
+  std::wstring defaultDevice;
 };
 
 #ifdef XP_WIN
 typedef HWND NativeWindowHandle;
 #elif defined(MOZ_X11)
 typedef XID NativeWindowHandle;
 #elif defined(XP_DARWIN) || defined(ANDROID)
 typedef intptr_t NativeWindowHandle; // never actually used, will always be 0
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -27,17 +27,16 @@
 # include "nsX11ErrorHandler.h"
 # include "mozilla/X11Util.h"
 #endif
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/plugins/PluginInstanceChild.h"
 #include "mozilla/plugins/StreamNotifyChild.h"
 #include "mozilla/plugins/BrowserStreamChild.h"
 #include "mozilla/plugins/PluginStreamChild.h"
-#include "mozilla/dom/CrashReporterChild.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Unused.h"
 
 #include "nsNPAPIPlugin.h"
 
 #ifdef XP_WIN
 #include "nsWindowsDllInterceptor.h"
 #include "mozilla/widget/AudioSession.h"
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -954,17 +954,17 @@ static inline bool apply_lookup (hb_appl
 				 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */
 				 unsigned int lookupCount,
 				 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
 				 unsigned int match_length)
 {
   TRACE_APPLY (NULL);
 
   hb_buffer_t *buffer = c->buffer;
-  unsigned int end;
+  int end;
 
   /* All positions are distance from beginning of *output* buffer.
    * Adjust. */
   {
     unsigned int bl = buffer->backtrack_len ();
     end = bl + match_length;
 
     int delta = bl - buffer->idx;
@@ -993,18 +993,18 @@ static inline bool apply_lookup (hb_appl
     unsigned int new_len = buffer->backtrack_len () + buffer->lookahead_len ();
     int delta = new_len - orig_len;
 
     if (!delta)
         continue;
 
     /* Recursed lookup changed buffer len.  Adjust. */
 
-    end = int (end) + delta;
-    if (end <= match_positions[idx])
+    end += delta;
+    if (end <= int (match_positions[idx]))
     {
       /* End might end up being smaller than match_positions[idx] if the recursed
        * lookup ended up removing many items, more than we have had matched.
        * Just never rewind end back and get out of here.
        * https://bugs.chromium.org/p/chromium/issues/detail?id=659496 */
       end = match_positions[idx];
       /* There can't be any further changes. */
       break;
--- a/gfx/skia/skia/src/effects/gradients/SkClampRange.cpp
+++ b/gfx/skia/skia/src/effects/gradients/SkClampRange.cpp
@@ -28,16 +28,31 @@ static bool sk_64_smul_check(int64_t a, 
     // Since we are looking at 64x64 muls, we add 32 to the check.
     if (zeros < (32 + 34)) {
         return false;
     }
     *result = a * b;
     return true;
 }
 
+static bool sk_64_sadd_check(int64_t a, int64_t b, int64_t* result) {
+    if (a > 0) {
+        if (b > std::numeric_limits<int64_t>::max() - a) {
+            return false;
+        }
+    } else {
+        if (b < std::numeric_limits<int64_t>::min() - a) {
+            return false;
+        }
+    }
+
+    *result = a + b;
+    return true;
+}
+
 /*
  *  returns [0..count] for the number of steps (<= count) for which x0 <= edge
  *  given each step is followed by x0 += dx
  */
 static int chop(int64_t x0, SkGradFixed edge, int64_t x1, int64_t dx, int count) {
     SkASSERT(dx > 0);
     SkASSERT(count >= 0);
 
@@ -77,26 +92,25 @@ void SkClampRange::init(SkGradFixed fx0,
         this->initFor1(fx0);
         return;
     }
 
     int64_t fx = fx0;
     int64_t dx = dx0;
 
     // start with ex equal to the last computed value
-    int64_t count_times_dx;
-    if (!sk_64_smul_check(count - 1, dx, &count_times_dx)) {
+    int64_t count_times_dx, ex;
+    if (!sk_64_smul_check(count - 1, dx, &count_times_dx) ||
+        !sk_64_sadd_check(fx, count_times_dx, &ex)) {
         // we can't represent the computed end in 32.32, so just draw something (first color)
         fCount1 = fCount2 = 0;
         fCount0 = count;
         return;
     }
 
-    int64_t ex = fx + (count - 1) * dx;
-
     if ((uint64_t)(fx | ex) <= kFracMax_SkGradFixed) {
         fCount0 = fCount2 = 0;
         fCount1 = count;
         fFx1 = fx0;
         return;
     }
     if (fx <= 0 && ex <= 0) {
         fCount1 = fCount2 = 0;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -360,16 +360,17 @@ private:
   DECL_GFX_PREF(Skip, "gfx.blocklist.all",                     BlocklistAll, int32_t, 0);
 #else
   DECL_GFX_PREF(Once, "gfx.blocklist.all",                     BlocklistAll, int32_t, 0);
 #endif
   DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_calls",  CanvasAutoAccelerateMinCalls, int32_t, 4);
   DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_frames", CanvasAutoAccelerateMinFrames, int32_t, 30);
   DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_seconds", CanvasAutoAccelerateMinSeconds, float, 5.0f);
   DECL_GFX_PREF(Live, "gfx.canvas.azure.accelerated",          CanvasAzureAccelerated, bool, false);
+  DECL_GFX_PREF(Once, "gfx.canvas.azure.accelerated.limit",    CanvasAzureAcceleratedLimit, int32_t, 0);
   // 0x7fff is the maximum supported xlib surface size and is more than enough for canvases.
   DECL_GFX_PREF(Live, "gfx.canvas.max-size",                   MaxCanvasSize, int32_t, 0x7fff);
   DECL_GFX_PREF(Once, "gfx.canvas.skiagl.cache-items",         CanvasSkiaGLCacheItems, int32_t, 256);
   DECL_GFX_PREF(Once, "gfx.canvas.skiagl.cache-size",          CanvasSkiaGLCacheSize, int32_t, 96);
   DECL_GFX_PREF(Once, "gfx.canvas.skiagl.dynamic-cache",       CanvasSkiaGLDynamicCache, bool, false);
 
   DECL_GFX_PREF(Live, "gfx.color_management.enablev4",         CMSEnableV4, bool, false);
   DECL_GFX_PREF(Live, "gfx.color_management.mode",             CMSMode, int32_t,-1);
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -249,31 +249,31 @@ private:
 };
 
 NS_IMPL_ISUPPORTS(SVGLoadEventListener, nsIDOMEventListener)
 
 // Helper-class: SVGDrawingCallback
 class SVGDrawingCallback : public gfxDrawingCallback {
 public:
   SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper,
-                     const IntRect& aViewport,
+                     const IntSize& aViewportSize,
                      const IntSize& aSize,
                      uint32_t aImageFlags)
     : mSVGDocumentWrapper(aSVGDocumentWrapper)
-    , mViewport(aViewport)
+    , mViewportSize(aViewportSize)
     , mSize(aSize)
     , mImageFlags(aImageFlags)
   { }
   virtual bool operator()(gfxContext* aContext,
                           const gfxRect& aFillRect,
                           const SamplingFilter aSamplingFilter,
                           const gfxMatrix& aTransform);
 private:
   RefPtr<SVGDocumentWrapper> mSVGDocumentWrapper;
-  const IntRect              mViewport;
+  const IntSize                mViewportSize;
   const IntSize                mSize;
   uint32_t                     mImageFlags;
 };
 
 // Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator()
 bool
 SVGDrawingCallback::operator()(gfxContext* aContext,
                                const gfxRect& aFillRect,
@@ -298,26 +298,25 @@ SVGDrawingCallback::operator()(gfxContex
   aContext->Clip();
 
   gfxMatrix matrix = aTransform;
   if (!matrix.Invert()) {
     return false;
   }
   aContext->SetMatrix(
     aContext->CurrentMatrix().PreMultiply(matrix).
-                              Scale(double(mSize.width) / mViewport.width,
-                                    double(mSize.height) / mViewport.height));
+                              Scale(double(mSize.width) / mViewportSize.width,
+                                    double(mSize.height) / mViewportSize.height));
 
   nsPresContext* presContext = presShell->GetPresContext();
   MOZ_ASSERT(presContext, "pres shell w/out pres context");
 
-  nsRect svgRect(presContext->DevPixelsToAppUnits(mViewport.x),
-                 presContext->DevPixelsToAppUnits(mViewport.y),
-                 presContext->DevPixelsToAppUnits(mViewport.width),
-                 presContext->DevPixelsToAppUnits(mViewport.height));
+  nsRect svgRect(0, 0,
+                 presContext->DevPixelsToAppUnits(mViewportSize.width),
+                 presContext->DevPixelsToAppUnits(mViewportSize.height));
 
   uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
   if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) {
     renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
   }
 
   presShell->RenderDocument(svgRect, renderDocFlags,
                             NS_RGBA(0, 0, 0, 0), // transparent
@@ -928,17 +927,17 @@ VectorImage::LookupCachedSurface(const S
 void
 VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams, BackendType aBackend)
 {
   mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
   mSVGDocumentWrapper->FlushImageTransformInvalidation();
 
   RefPtr<gfxDrawingCallback> cb =
     new SVGDrawingCallback(mSVGDocumentWrapper,
-                           IntRect(IntPoint(0, 0), aParams.viewportSize),
+                           aParams.viewportSize,
                            aParams.size,
                            aParams.flags);
 
   RefPtr<gfxDrawable> svgDrawable =
     new gfxCallbackDrawable(cb, aParams.size);
 
   bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
                      // Refuse to cache animated images:
--- a/intl/locale/tests/unit/test_pluralForm.js
+++ b/intl/locale/tests/unit/test_pluralForm.js
@@ -586,30 +586,30 @@ function run_test()
     5,1,2,3,3,5,5,5,5,3,
     5,1,2,3,3,5,5,5,5,3,
     5,1,2,3,3,5,5,5,5,3,
     5,5,5,5,5,5,5,5,5,5,
     5,1,2,3,3,5,5,5,5,3,
     5,5,5,5,5,5,5,5,5,5,
   ]];
 
-  for (let [rule, expect] in Iterator(allExpect)) {
+  for (let [rule, expect] of allExpect.entries()) {
     print("\nTesting rule #" + rule);
 
     let [get, numForms] = PluralForm.makeGetter(rule);
 
     // Make sure the largest value expected matches the number of plural forms
     let maxExpect = Math.max.apply(this, expect);
     do_check_eq(maxExpect, numForms());
 
     // Make a string of numbers, e.g., 1;2;3;4;5
     let words = [];
     for (let i = 1; i <= maxExpect; i++)
       words.push(i);
     words = words.join(";");
 
     // Make sure we get the expected number
-    for (let [index, number] in Iterator(expect)) {
+    for (let [index, number] of expect.entries()) {
       print(["Plural form of ", index, " should be ", number, " (", words, ")"].join(""));
       do_check_eq(get(index, words), number);
     }
   }
 }
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -131,19 +131,17 @@ BackgroundChildImpl::ProcessingError(Res
     HANDLE_CASE(MsgValueError);
 
 #undef HANDLE_CASE
 
     default:
       MOZ_CRASH("Unknown error code!");
   }
 
-  // This is just MOZ_CRASH() un-inlined so that we can pass the result code as
-  // a string. MOZ_CRASH() only supports string literals at the moment.
-  MOZ_ReportCrash(abortMessage.get(), __FILE__, __LINE__); MOZ_REALLY_CRASH();
+  MOZ_CRASH_UNSAFE_PRINTF("%s: %s", abortMessage.get(), aReason);
 }
 
 void
 BackgroundChildImpl::ActorDestroy(ActorDestroyReason aWhy)
 {
   // May happen on any thread!
 }
 
--- a/ipc/glue/InputStreamParams.ipdlh
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -24,23 +24,16 @@ struct StringInputStreamParams
 
 struct FileInputStreamParams
 {
   uint32_t fileDescriptorIndex;
   int32_t behaviorFlags;
   int32_t ioFlags;
 };
 
-struct PartialFileInputStreamParams
-{
-  FileInputStreamParams fileStreamParams;
-  uint64_t begin;
-  uint64_t length;
-};
-
 struct TemporaryFileInputStreamParams
 {
   uint32_t fileDescriptorIndex;
   uint64_t startPos;
   uint64_t endPos;
 };
 
 struct MultiplexInputStreamParams
@@ -59,27 +52,36 @@ struct RemoteInputStreamParams
 // XXX This may only be used for same-process inter-thread communication! The
 //     value should be reinterpret_cast'd to nsIInputStream. It carries a
 //     reference.
 struct SameProcessInputStreamParams
 {
   intptr_t addRefedInputStream;
 };
 
+struct SlicedInputStreamParams
+{
+  InputStreamParams stream;
+  uint64_t start;
+  uint64_t length;
+  uint64_t curPos;
+  bool closed;
+};
+
 union InputStreamParams
 {
   StringInputStreamParams;
   FileInputStreamParams;
-  PartialFileInputStreamParams;
   TemporaryFileInputStreamParams;
   BufferedInputStreamParams;
   MIMEInputStreamParams;
   MultiplexInputStreamParams;
   RemoteInputStreamParams;
   SameProcessInputStreamParams;
+  SlicedInputStreamParams;
 };
 
 union OptionalInputStreamParams
 {
   void_t;
   InputStreamParams;
 };
 
--- a/ipc/glue/InputStreamUtils.cpp
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -17,24 +17,24 @@
 #include "nsID.h"
 #include "nsIXULRuntime.h"
 #include "nsMIMEInputStream.h"
 #include "nsMultiplexInputStream.h"
 #include "nsNetCID.h"
 #include "nsStringStream.h"
 #include "nsTemporaryFileInputStream.h"
 #include "nsXULAppAPI.h"
+#include "SlicedInputStream.h"
 
 using namespace mozilla::dom;
 
 namespace {
 
 NS_DEFINE_CID(kStringInputStreamCID, NS_STRINGINPUTSTREAM_CID);
 NS_DEFINE_CID(kFileInputStreamCID, NS_LOCALFILEINPUTSTREAM_CID);
-NS_DEFINE_CID(kPartialFileInputStreamCID, NS_PARTIALLOCALFILEINPUTSTREAM_CID);
 NS_DEFINE_CID(kBufferedInputStreamCID, NS_BUFFEREDINPUTSTREAM_CID);
 NS_DEFINE_CID(kMIMEInputStreamCID, NS_MIMEINPUTSTREAM_CID);
 NS_DEFINE_CID(kMultiplexInputStreamCID, NS_MULTIPLEXINPUTSTREAM_CID);
 
 } // namespace
 
 namespace mozilla {
 namespace ipc {
@@ -85,20 +85,16 @@ DeserializeInputStream(const InputStream
     case InputStreamParams::TStringInputStreamParams:
       serializable = do_CreateInstance(kStringInputStreamCID);
       break;
 
     case InputStreamParams::TFileInputStreamParams:
       serializable = do_CreateInstance(kFileInputStreamCID);
       break;
 
-    case InputStreamParams::TPartialFileInputStreamParams:
-      serializable = do_CreateInstance(kPartialFileInputStreamCID);
-      break;
-
     case InputStreamParams::TTemporaryFileInputStreamParams:
       serializable = new nsTemporaryFileInputStream();
       break;
 
     case InputStreamParams::TBufferedInputStreamParams:
       serializable = do_CreateInstance(kBufferedInputStreamCID);
       break;
 
@@ -143,16 +139,20 @@ DeserializeInputStream(const InputStream
 
       stream = dont_AddRef(
         reinterpret_cast<nsIInputStream*>(params.addRefedInputStream()));
       MOZ_ASSERT(stream);
 
       return stream.forget();
     }
 
+    case InputStreamParams::TSlicedInputStreamParams:
+      serializable = new SlicedInputStream();
+      break;
+
     default:
       MOZ_ASSERT(false, "Unknown params!");
       return nullptr;
   }
 
   MOZ_ASSERT(serializable);
 
   if (!serializable->Deserialize(aParams, aFileDescriptors)) {
--- a/ipc/mscom/Utils.cpp
+++ b/ipc/mscom/Utils.cpp
@@ -1,18 +1,21 @@
 /* -*- 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/. */
 
+#ifdef ACCESSIBILITY
 #include "mozilla/mscom/Registration.h"
+#include "nsTArray.h"
+#endif
+
 #include "mozilla/mscom/Utils.h"
 #include "mozilla/RefPtr.h"
-#include "nsTArray.h"
 
 #include <objbase.h>
 #include <objidl.h>
 
 namespace mozilla {
 namespace mscom {
 
 bool
@@ -41,16 +44,17 @@ IsProxy(IUnknown* aUnknown)
   HRESULT hr = aUnknown->QueryInterface(IID_IClientSecurity,
                                         (void**)getter_AddRefs(clientSecurity));
   if (SUCCEEDED(hr) || hr == RPC_E_WRONG_THREAD) {
     return true;
   }
   return false;
 }
 
+#ifdef ACCESSIBILITY
 static bool
 IsVtableIndexFromParentInterface(TYPEATTR* aTypeAttr,
                                  unsigned long aVtableIndex)
 {
   MOZ_ASSERT(aTypeAttr);
 
   // This is the number of functions declared in this interface (excluding
   // parent interfaces).
@@ -168,11 +172,12 @@ IsInterfaceEqualToOrInheritedFrom(REFIID
       }
 
       typeInfos.AppendElement(Move(nextTypeInfo));
     }
   }
 
   return false;
 }
+#endif // ifdef ACCESSIBILITY
 
 } // namespace mscom
 } // namespace mozilla
--- a/ipc/mscom/Utils.h
+++ b/ipc/mscom/Utils.h
@@ -2,27 +2,32 @@
 /* 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_mscom_Utils_h
 #define mozilla_mscom_Utils_h
 
+#ifdef ACCESSIBILITY
 #include <guiddef.h>
+#endif
 
 struct IUnknown;
 
 namespace mozilla {
 namespace mscom {
 
 bool IsCurrentThreadMTA();
 bool IsProxy(IUnknown* aUnknown);
+
+#ifdef ACCESSIBILITY
 bool IsVtableIndexFromParentInterface(REFIID aInterface,
                                       unsigned long aVtableIndex);
 bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
                                        unsigned long aVtableIndexHint);
+#endif
 
 } // namespace mscom
 } // namespace mozilla
 
 #endif // mozilla_mscom_Utils_h
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4642,30 +4642,33 @@ Parser<ParseHandler>::declarationList(Yi
             return null();
     } while (matched);
 
     return decl;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool isConst)
-{
+Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind)
+{
+    MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
+
     /*
      * Parse body-level lets without a new block object. ES6 specs
      * that an execution environment's initial lexical environment
      * is the VariableEnvironment, i.e., body-level lets are in
      * the same environment record as vars.
      *
      * However, they cannot be parsed exactly as vars, as ES6
      * requires that uninitialized lets throw ReferenceError on use.
      *
      * See 8.1.1.1.6 and the note in 13.2.1.
      */
-    Node decl = declarationList(yieldHandling, isConst ? PNK_CONST : PNK_LET);
+    Node decl = declarationList(yieldHandling,
+                                kind == DeclarationKind::Const ? PNK_CONST : PNK_LET);
     if (!decl || !matchOrInsertSemicolonAfterExpression())
         return null();
 
     return decl;
 }
 
 template <>
 bool
@@ -4862,23 +4865,17 @@ Parser<FullParseHandler>::importDeclarat
                 if (!namedImportsOrNamespaceImport(tt, importSpecSet))
                     return null();
             }
         } else {
             error(JSMSG_DECLARATION_AFTER_IMPORT);
             return null();
         }
 
-        if (!tokenStream.getToken(&tt))
-            return null();
-
-        if (tt != TOK_FROM) {
-            error(JSMSG_FROM_AFTER_IMPORT_CLAUSE);
-            return null();
-        }
+        MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_IMPORT_CLAUSE);
 
         MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
     }
 
     Node moduleSpec = stringLiteral();
     if (!moduleSpec)
         return null();
 
@@ -4944,289 +4941,564 @@ template<>
 bool
 Parser<SyntaxParseHandler>::checkExportedNamesForDeclaration(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template<>
-ParseNode*
-Parser<FullParseHandler>::exportDeclaration()
-{
+bool
+Parser<FullParseHandler>::checkExportedNameForClause(ParseNode* node)
+{
+    return checkExportedName(node->pn_atom);
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::checkExportedNameForClause(Node node)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return false;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::checkExportedNameForFunction(ParseNode* node)
+{
+    return checkExportedName(node->pn_funbox->function()->explicitName());
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::checkExportedNameForFunction(Node node)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return false;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::checkExportedNameForClass(ParseNode* node)
+{
+    const ClassNode& cls = node->as<ClassNode>();
+    MOZ_ASSERT(cls.names());
+    return checkExportedName(cls.names()->innerBinding()->pn_atom);
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::checkExportedNameForClass(Node node)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return false;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::processExport(ParseNode* node)
+{
+    return pc->sc()->asModuleContext()->builder.processExport(node);
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::processExport(Node node)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return false;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::processExportFrom(ParseNode* node)
+{
+    return pc->sc()->asModuleContext()->builder.processExportFrom(node);
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::processExportFrom(Node node)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return false;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportFrom(uint32_t begin, Node specList)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FROM));
+
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
+
+    Node moduleSpec = stringLiteral();
+    if (!moduleSpec)
+        return null();
+
+    if (!matchOrInsertSemicolonAfterNonExpression())
+        return null();
+
+    Node node = handler.newExportFromDeclaration(begin, specList, moduleSpec);
+    if (!node)
+        return null();
+
+    if (!processExportFrom(node))
+        return null();
+
+    return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportBatch(uint32_t begin)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_MUL));
+
+    Node kid = handler.newList(PNK_EXPORT_SPEC_LIST);
+    if (!kid)
+        return null();
+
+    // Handle the form |export *| by adding a special export batch
+    // specifier to the list.
+    Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
+    if (!exportSpec)
+        return null();
+
+    handler.addList(kid, exportSpec);
+
+    MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR);
+
+    return exportFrom(begin, kid);
+}
+
+template<>
+bool
+Parser<FullParseHandler>::checkLocalExportNames(ParseNode* node)
+{
+    // ES 2017 draft 15.2.3.1.
+    for (ParseNode* next = node->pn_head; next; next = next->pn_next) {
+        ParseNode* name = next->pn_left;
+        MOZ_ASSERT(name->isKind(PNK_NAME));
+
+        RootedPropertyName ident(context, name->pn_atom->asPropertyName());
+        if (!checkLocalExportName(ident, name->pn_pos.begin))
+            return false;
+    }
+
+    return true;
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::checkLocalExportNames(Node node)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return false;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportClause(uint32_t begin)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+
+    Node kid = handler.newList(PNK_EXPORT_SPEC_LIST);
+    if (!kid)
+        return null();
+
+    TokenKind tt;
+    while (true) {
+        // Handle the forms |export {}| and |export { ..., }| (where ... is non
+        // empty), by escaping the loop early if the next token is }.
+        if (!tokenStream.getToken(&tt))
+            return null();
+
+        if (tt == TOK_RC)
+            break;
+
+        if (!TokenKindIsPossibleIdentifierName(tt)) {
+            error(JSMSG_NO_BINDING_NAME);
+            return null();
+        }
+
+        Node bindingName = newName(tokenStream.currentName());
+        if (!bindingName)
+            return null();
+
+        bool foundAs;
+        if (!tokenStream.matchToken(&foundAs, TOK_AS))
+            return null();
+        if (foundAs)
+            MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME);
+
+        Node exportName = newName(tokenStream.currentName());
+        if (!exportName)
+            return null();
+
+        if (!checkExportedNameForClause(exportName))
+            return null();
+
+        Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
+        if (!exportSpec)
+            return null();
+
+        handler.addList(kid, exportSpec);
+
+        TokenKind next;
+        if (!tokenStream.getToken(&next))
+            return null();
+
+        if (next == TOK_RC)
+            break;
+
+        if (next != TOK_COMMA) {
+            error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
+            return null();
+        }
+    }
+
+    // Careful!  If |from| follows, even on a new line, it must start a
+    // FromClause:
+    //
+    //   export { x }
+    //   from "foo"; // a single ExportDeclaration
+    //
+    // But if it doesn't, we might have an ASI opportunity in Operand context:
+    //
+    //   export { x }   // ExportDeclaration, terminated by ASI
+    //   fro\u006D      // ExpressionStatement, the name "from"
+    //
+    // In that case let matchOrInsertSemicolonAfterNonExpression sort out ASI
+    // or any necessary error.
+    bool matched;
+    if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand))
+        return null();
+
+    if (matched)
+        return exportFrom(begin, kid);
+
+    if (!matchOrInsertSemicolonAfterNonExpression())
+        return null();
+
+    if (!checkLocalExportNames(kid))
+        return null();
+
+    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+    if (!node)
+        return null();
+
+    if (!processExport(node))
+        return null();
+
+    return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportVariableStatement(uint32_t begin)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR));
+
+    Node kid = declarationList(YieldIsName, PNK_VAR);
+    if (!kid)
+        return null();
+    if (!matchOrInsertSemicolonAfterExpression())
+        return null();
+    if (!checkExportedNamesForDeclaration(kid))
+        return null();
+
+    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+    if (!node)
+        return null();
+
+    if (!processExport(node))
+        return null();
+
+    return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportFunctionDeclaration(uint32_t begin,
+                                                FunctionAsyncKind asyncKind /* = SyncFunction */)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+
+    Node kid = functionStmt(YieldIsKeyword, NameRequired, asyncKind);
+    if (!kid)
+        return null();
+
+    if (!checkExportedNameForFunction(kid))
+        return null();
+
+    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+    if (!node)
+        return null();
+
+    if (!processExport(node))
+        return null();
+
+    return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportClassDeclaration(uint32_t begin)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+
+    Node kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired);
+    if (!kid)
+        return null();
+
+    if (!checkExportedNameForClass(kid))
+        return null();
+
+    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+    if (!node)
+        return null();
+
+    if (!processExport(node))
+        return null();
+
+    return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
+    MOZ_ASSERT_IF(kind == DeclarationKind::Const, tokenStream.isCurrentTokenType(TOK_CONST));
+    MOZ_ASSERT_IF(kind == DeclarationKind::Let, tokenStream.isCurrentTokenType(TOK_LET));
+
+    Node kid = lexicalDeclaration(YieldIsName, kind);
+    if (!kid)
+        return null();
+    if (!checkExportedNamesForDeclaration(kid))
+        return null();
+
+    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+    if (!node)
+        return null();
+
+    if (!processExport(node))
+        return null();
+
+    return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDefaultFunctionDeclaration(uint32_t begin,
+                                                           FunctionAsyncKind asyncKind
+                                                           /* = SyncFunction */)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+
+    Node kid = functionStmt(YieldIsKeyword, AllowDefaultName, asyncKind);
+    if (!kid)
+        return null();
+
+    Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
+    if (!node)
+        return null();
+
+    if (!processExport(node))
+        return null();
+
+    return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDefaultClassDeclaration(uint32_t begin)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+
+    Node kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
+    if (!kid)
+        return null();
+
+    Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
+    if (!node)
+        return null();
+
+    if (!processExport(node))
+        return null();
+
+    return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDefaultAssignExpr(uint32_t begin)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    RootedPropertyName name(context, context->names().starDefaultStar);
+    Node nameNode = newName(name);
+    if (!nameNode)
+        return null();
+    if (!noteDeclaredName(name, DeclarationKind::Const, pos()))
+        return null();
+
+    Node kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
+    if (!kid)
+        return null();
+    if (!matchOrInsertSemicolonAfterExpression())
+        return null();
+
+    Node node = handler.newExportDefaultDeclaration(kid, nameNode, TokenPos(begin, pos().end));
+    if (!node)
+        return null();
+
+    if (!processExport(node))
+        return null();
+
+    return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDefault(uint32_t begin)
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_DEFAULT));
+
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
+
+    if (!checkExportedName(context->names().default_))
+        return null();
+
+    switch (tt) {
+      case TOK_FUNCTION:
+        return exportDefaultFunctionDeclaration(begin);
+
+      case TOK_ASYNC: {
+        TokenKind nextSameLine = TOK_EOF;
+        if (!tokenStream.peekTokenSameLine(&nextSameLine))
+            return null();
+
+        if (nextSameLine == TOK_FUNCTION) {
+            tokenStream.consumeKnownToken(TOK_FUNCTION);
+            return exportDefaultFunctionDeclaration(begin, AsyncFunction);
+        }
+
+        tokenStream.ungetToken();
+        return exportDefaultAssignExpr(begin);
+      }
+
+      case TOK_CLASS:
+        return exportDefaultClassDeclaration(begin);
+
+      default:
+        tokenStream.ungetToken();
+        return exportDefaultAssignExpr(begin);
+    }
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDeclaration()
+{
+    if (!abortIfSyntaxParser())
+        return null();
+
     MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
 
-    Node kid;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
     switch (tt) {
-      case TOK_LC: {
-        kid = handler.newList(PNK_EXPORT_SPEC_LIST);
-        if (!kid)
-            return null();
-
-        while (true) {
-            // Handle the forms |export {}| and |export { ..., }| (where ...
-            // is non empty), by escaping the loop early if the next token
-            // is }.
-            if (!tokenStream.getToken(&tt))
-                return null();
-
-            if (tt == TOK_RC)
-                break;
-
-            if (!TokenKindIsPossibleIdentifierName(tt)) {
-                error(JSMSG_NO_BINDING_NAME);
-                return null();
-            }
-
-            Node bindingName = newName(tokenStream.currentName());
-            if (!bindingName)
-                return null();
-
-            bool foundAs;
-            if (!tokenStream.matchToken(&foundAs, TOK_AS))
-                return null();
-            if (foundAs)
-                MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME);
-
-            Node exportName = newName(tokenStream.currentName());
-            if (!exportName)
-                return null();
-
-            if (!checkExportedName(exportName->pn_atom))
-                return null();
-
-            Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
-            if (!exportSpec)
-                return null();
-
-            handler.addList(kid, exportSpec);
-
-            TokenKind next;
-            if (!tokenStream.getToken(&next))
-                return null();
-
-            if (next == TOK_RC)
-                break;
-
-            if (next != TOK_COMMA) {
-                error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
-                return null();
-            }
-        }
-
-        // Careful!  If |from| follows, even on a new line, it must start a
-        // FromClause:
-        //
-        //   export { x }
-        //   from "foo"; // a single ExportDeclaration
-        //
-        // But if it doesn't, we might have an ASI opportunity in Operand
-        // context:
-        //
-        //   export { x }   // ExportDeclaration, terminated by ASI
-        //   fro\u006D      // ExpressionStatement, the name "from"
-        //
-        // In that case let matchOrInsertSemicolonAfterNonExpression sort out
-        // ASI or any necessary error.
-        bool matched;
-        if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand))
-            return null();
-
-        if (matched) {
-            MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
-
-            Node moduleSpec = stringLiteral();
-            if (!moduleSpec)
-                return null();
-
-            if (!matchOrInsertSemicolonAfterNonExpression())
-                return null();
-
-            ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
-            if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node))
-                return null();
-
-            return node;
-        }
-
-        if (!matchOrInsertSemicolonAfterNonExpression())
-            return null();
-        break;
-      }
-
-      case TOK_MUL: {
-        kid = handler.newList(PNK_EXPORT_SPEC_LIST);
-        if (!kid)
-            return null();
-
-        // Handle the form |export *| by adding a special export batch
-        // specifier to the list.
-        Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
-        if (!exportSpec)
-            return null();
-
-        handler.addList(kid, exportSpec);
-
-        if (!tokenStream.getToken(&tt))
-            return null();
-        if (tt != TOK_FROM) {
-            error(JSMSG_FROM_AFTER_EXPORT_STAR);
-            return null();
-        }
-
-        MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
-
-        Node moduleSpec = stringLiteral();
-        if (!moduleSpec)
-            return null();
-
-        if (!matchOrInsertSemicolonAfterNonExpression())
-            return null();
-
-        ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
-        if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node))
-            return null();
-
-        return node;
-
-      }
+      case TOK_MUL:
+        return exportBatch(begin);
+
+      case TOK_LC:
+        return exportClause(begin);
+
+      case TOK_VAR:
+        return exportVariableStatement(begin);
 
       case TOK_FUNCTION:
-        kid = functionStmt(YieldIsKeyword, NameRequired);
-        if (!kid)
-            return null();
-
-        if (!checkExportedName(kid->pn_funbox->function()->explicitName()))
-            return null();
-        break;
-
-      case TOK_CLASS: {
-        kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired);
-        if (!kid)
-            return null();
-
-        const ClassNode& cls = kid->as<ClassNode>();
-        MOZ_ASSERT(cls.names());
-        if (!checkExportedName(cls.names()->innerBinding()->pn_atom))
-            return null();
-        break;
+        return exportFunctionDeclaration(begin);
+
+      case TOK_ASYNC: {
+        TokenKind nextSameLine = TOK_EOF;
+        if (!tokenStream.peekTokenSameLine(&nextSameLine))
+            return null();
+
+        if (nextSameLine == TOK_FUNCTION) {
+            tokenStream.consumeKnownToken(TOK_FUNCTION);
+            return exportFunctionDeclaration(begin, AsyncFunction);
+        }
+
+        error(JSMSG_DECLARATION_AFTER_EXPORT);
+        return null();
       }
 
-      case TOK_VAR:
-        kid = declarationList(YieldIsName, PNK_VAR);
-        if (!kid)
-            return null();
-        if (!matchOrInsertSemicolonAfterExpression())
-            return null();
-        if (!checkExportedNamesForDeclaration(kid))
-            return null();
-        break;
-
-      case TOK_DEFAULT: {
-        if (!tokenStream.getToken(&tt, TokenStream::Operand))
-            return null();
-
-        if (!checkExportedName(context->names().default_))
-            return null();
-
-        ParseNode* nameNode = nullptr;
-        switch (tt) {
-          case TOK_FUNCTION:
-            kid = functionStmt(YieldIsKeyword, AllowDefaultName);
-            if (!kid)
-                return null();
-            break;
-          case TOK_CLASS:
-            kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
-            if (!kid)
-                return null();
-            break;
-          default: {
-            if (tt == TOK_ASYNC) {
-                TokenKind nextSameLine = TOK_EOF;
-                if (!tokenStream.peekTokenSameLine(&nextSameLine))
-                    return null();
-
-                if (nextSameLine == TOK_FUNCTION) {
-                    tokenStream.consumeKnownToken(nextSameLine);
-                    kid = functionStmt(YieldIsName, AllowDefaultName, AsyncFunction);
-                    if (!kid)
-                        return null();
-                    break;
-                }
-            }
-
-            tokenStream.ungetToken();
-            RootedPropertyName name(context, context->names().starDefaultStar);
-            nameNode = newName(name);
-            if (!nameNode)
-                return null();
-            if (!noteDeclaredName(name, DeclarationKind::Const, pos()))
-                return null();
-            kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
-            if (!kid)
-                return null();
-            if (!matchOrInsertSemicolonAfterExpression())
-                return null();
-            break;
-          }
-        }
-
-        ParseNode* node = handler.newExportDefaultDeclaration(kid, nameNode,
-                                                              TokenPos(begin, pos().end));
-        if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
-            return null();
-
-        return node;
-      }
+      case TOK_CLASS:
+        return exportClassDeclaration(begin);
 
       case TOK_CONST:
-        kid = lexicalDeclaration(YieldIsName, /* isConst = */ true);
-        if (!kid)
-            return null();
-        if (!checkExportedNamesForDeclaration(kid))
-            return null();
-        break;
+        return exportLexicalDeclaration(begin, DeclarationKind::Const);
 
       case TOK_LET:
-        kid = lexicalDeclaration(YieldIsName, /* isConst = */ false);
-        if (!kid)
-            return null();
-        if (!checkExportedNamesForDeclaration(kid))
-            return null();
-        break;
+        return exportLexicalDeclaration(begin, DeclarationKind::Let);
+
+      case TOK_DEFAULT:
+        return exportDefault(begin);
 
       default:
         error(JSMSG_DECLARATION_AFTER_EXPORT);
         return null();
     }
-
-    ParseNode* node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
-    if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
-        return null();
-
-    return node;
-}
-
-template<>
-SyntaxParseHandler::Node
-Parser<SyntaxParseHandler>::exportDeclaration()
-{
-    JS_ALWAYS_FALSE(abortIfSyntaxParser());
-    return SyntaxParseHandler::NodeFailure;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked)
 {
     tokenStream.ungetToken();
     Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited,
@@ -7083,17 +7355,17 @@ Parser<ParseHandler>::statementListItem(
         if (!TokenKindIsPossibleIdentifier(tt))
             return expressionStatement(yieldHandling);
 
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
 
         if (tt == TOK_LET && nextTokenContinuesLetDeclaration(next, yieldHandling))
-            return lexicalDeclaration(yieldHandling, /* isConst = */ false);
+            return lexicalDeclaration(yieldHandling, DeclarationKind::Let);
 
         if (tt == TOK_ASYNC) {
             TokenKind nextSameLine = TOK_EOF;
             if (!tokenStream.peekTokenSameLine(&nextSameLine))
                 return null();
             if (nextSameLine == TOK_FUNCTION) {
                 tokenStream.consumeKnownToken(TOK_FUNCTION);
                 return functionStmt(yieldHandling, NameRequired, AsyncFunction);
@@ -7178,17 +7450,17 @@ Parser<ParseHandler>::statementListItem(
       case TOK_CLASS:
         return classDefinition(yieldHandling, ClassStatement, NameRequired);
 
       //   LexicalDeclaration[In, ?Yield]
       //     LetOrConst BindingList[?In, ?Yield]
       case TOK_CONST:
         // [In] is the default behavior, because for-loops specially parse
         // their heads to handle |in| in this situation.
-        return lexicalDeclaration(yieldHandling, /* isConst = */ true);
+        return lexicalDeclaration(yieldHandling, DeclarationKind::Const);
 
       // ImportDeclaration (only inside modules)
       case TOK_IMPORT:
         return importDeclaration();
 
       // ExportDeclaration (only inside modules)
       case TOK_EXPORT:
         return exportDeclaration();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1187,20 +1187,39 @@ class Parser final : public ParserBase, 
     Node ifStatement(YieldHandling yieldHandling);
     Node consequentOrAlternative(YieldHandling yieldHandling);
 
     // While on a |let| TOK_NAME token, examine |next|.  Indicate whether
     // |next|, the next token already gotten with modifier TokenStream::None,
     // continues a LexicalDeclaration.
     bool nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling);
 
-    Node lexicalDeclaration(YieldHandling yieldHandling, bool isConst);
+    Node lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
 
     Node importDeclaration();
+
+    bool processExport(Node node);
+    bool processExportFrom(Node node);
+
+    Node exportFrom(uint32_t begin, Node specList);
+    Node exportBatch(uint32_t begin);
+    bool checkLocalExportNames(Node node);
+    Node exportClause(uint32_t begin);
+    Node exportFunctionDeclaration(uint32_t begin,
+                                   FunctionAsyncKind asyncKind = SyncFunction);
+    Node exportVariableStatement(uint32_t begin);
+    Node exportClassDeclaration(uint32_t begin);
+    Node exportLexicalDeclaration(uint32_t begin, DeclarationKind kind);
+    Node exportDefaultFunctionDeclaration(uint32_t begin,
+                                          FunctionAsyncKind asyncKind = SyncFunction);
+    Node exportDefaultClassDeclaration(uint32_t begin);
+    Node exportDefaultAssignExpr(uint32_t begin);
+    Node exportDefault(uint32_t begin);
     Node exportDeclaration();
+
     Node expressionStatement(YieldHandling yieldHandling,
                              InvokedPrediction invoked = PredictUninvoked);
 
     // Declaration parsing.  The main entrypoint is Parser::declarationList,
     // with sub-functionality split out into the remaining methods.
 
     // |blockScope| may be non-null only when |kind| corresponds to a lexical
     // declaration (that is, PNK_LET or PNK_CONST).
@@ -1319,25 +1338,32 @@ class Parser final : public ParserBase, 
     Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
                                   TokenKind tt);
     Node destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, YieldHandling yieldHandling,
                                                      TokenKind tt);
 
     bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
     bool checkExportedName(JSAtom* exportName);
     bool checkExportedNamesForDeclaration(Node node);
+    bool checkExportedNameForClause(Node node);
+    bool checkExportedNameForFunction(Node node);
+    bool checkExportedNameForClass(Node node);
 
     enum ClassContext { ClassStatement, ClassExpression };
     Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
                          DefaultHandling defaultHandling);
 
     bool checkLabelOrIdentifierReference(HandlePropertyName ident,
                                          uint32_t offset,
                                          YieldHandling yieldHandling);
 
+    bool checkLocalExportName(HandlePropertyName ident, uint32_t offset) {
+        return checkLabelOrIdentifierReference(ident, offset, YieldIsName);
+    }
+
     bool checkBindingIdentifier(HandlePropertyName ident,
                                 uint32_t offset,
                                 YieldHandling yieldHandling);
 
     PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
 
     PropertyName* labelIdentifier(YieldHandling yieldHandling) {
         return labelOrIdentifierReference(yieldHandling);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -236,16 +236,20 @@ class SyntaxParseHandler
     Node newDelete(uint32_t begin, Node expr) {
         return NodeUnparenthesizedUnary;
     }
 
     Node newTypeof(uint32_t begin, Node kid) {
         return NodeUnparenthesizedUnary;
     }
 
+    Node newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) {
+        return NodeGeneric;
+    }
+
     Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) {
         return NodeUnparenthesizedUnary;
     }
 
     Node newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) {
         return NodeGeneric;
     }
 
@@ -303,16 +307,26 @@ class SyntaxParseHandler
     // Statements
 
     Node newStatementList(const TokenPos& pos) { return NodeGeneric; }
     void addStatementToList(Node list, Node stmt) {}
     void addCaseStatementToList(Node list, Node stmt) {}
     MOZ_MUST_USE bool prependInitialYield(Node stmtList, Node gen) { return true; }
     Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
 
+    Node newExportDeclaration(Node kid, const TokenPos& pos) {
+        return NodeGeneric;
+    }
+    Node newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) {
+        return NodeGeneric;
+    }
+    Node newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) {
+        return NodeGeneric;
+    }
+
     Node newSetThis(Node thisName, Node value) { return value; }
 
     Node newExprStatement(Node expr, uint32_t end) {
         return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric;
     }
 
     Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; }
     Node newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return NodeGeneric; }
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -892,28 +892,28 @@ template <> void GCMarker::traverse(js::
 
 // Object and ObjectGroup are extremely common and can contain arbitrarily
 // nested graphs, so are not trivially inlined. In this case we use a mark
 // stack to control recursion. JitCode shares none of these properties, but is
 // included for historical reasons. JSScript normally cannot recurse, but may
 // be used as a weakmap key and thereby recurse into weakmapped values.
 template <typename T>
 void
-js::GCMarker::markAndPush(StackTag tag, T* thing)
+js::GCMarker::markAndPush(T* thing)
 {
     if (!mark(thing))
         return;
-    pushTaggedPtr(tag, thing);
+    pushTaggedPtr(thing);
     markImplicitEdges(thing);
 }
 namespace js {
-template <> void GCMarker::traverse(JSObject* thing) { markAndPush(ObjectTag, thing); }
-template <> void GCMarker::traverse(ObjectGroup* thing) { markAndPush(GroupTag, thing); }
-template <> void GCMarker::traverse(jit::JitCode* thing) { markAndPush(JitCodeTag, thing); }
-template <> void GCMarker::traverse(JSScript* thing) { markAndPush(ScriptTag, thing); }
+template <> void GCMarker::traverse(JSObject* thing) { markAndPush(thing); }
+template <> void GCMarker::traverse(ObjectGroup* thing) { markAndPush(thing); }
+template <> void GCMarker::traverse(jit::JitCode* thing) { markAndPush(thing); }
+template <> void GCMarker::traverse(JSScript* thing) { markAndPush(thing); }
 } // namespace js
 
 namespace js {
 template <>
 void
 GCMarker::traverse(AccessorShape* thing) {
     MOZ_CRASH("AccessorShape must be marked as a Shape");
 }
@@ -1133,17 +1133,17 @@ js::GCMarker::eagerlyMarkChildren(JSRope
     // This function tries to scan the whole rope tree using the marking stack
     // as temporary storage. If that becomes full, the unscanned ropes are
     // added to the delayed marking list. When the function returns, the
     // marking stack is at the same depth as it was on entry. This way we avoid
     // using tags when pushing ropes to the stack as ropes never leak to other
     // users of the stack. This also assumes that a rope can only point to
     // other ropes or linear strings, it cannot refer to GC things of other
     // types.
-    ptrdiff_t savedPos = stack.position();
+    size_t savedPos = stack.position();
     JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String);
 #ifdef JS_DEBUG
     static const size_t DEEP_ROPE_THRESHOLD = 100000;
     static const size_t ROPE_CYCLE_HISTORY = 100;
     DebugOnly<size_t> ropeDepth = 0;
     JSRope* history[ROPE_CYCLE_HISTORY];
 #endif
     while (true) {
@@ -1187,26 +1187,26 @@ js::GCMarker::eagerlyMarkChildren(JSRope
         if (!left->isPermanentAtom() &&
             mark(left))
         {
             if (left->isLinear()) {
                 eagerlyMarkChildren(&left->asLinear());
             } else {
                 // When both children are ropes, set aside the right one to
                 // scan it later.
-                if (next && !stack.push(reinterpret_cast<uintptr_t>(next)))
+                if (next && !stack.pushTempRope(next))
                     delayMarkingChildren(next);
                 next = &left->asRope();
             }
         }
         if (next) {
             rope = next;
         } else if (savedPos != stack.position()) {
             MOZ_ASSERT(savedPos < stack.position());
-            rope = reinterpret_cast<JSRope*>(stack.pop());
+            rope = stack.popPtr().asTempRope();
         } else {
             break;
         }
     }
     MOZ_ASSERT(savedPos == stack.position());
 }
 
 static inline void
@@ -1639,61 +1639,51 @@ GCMarker::processMarkStackTop(SliceBudge
      * The function uses explicit goto and implements the scanning of the
      * object directly. It allows to eliminate the tail recursion and
      * significantly improve the marking performance, see bug 641025.
      */
     HeapSlot* vp;
     HeapSlot* end;
     JSObject* obj;
 
-    // Decode
-    uintptr_t addr = stack.pop();
-    uintptr_t tag = addr & StackTagMask;
-    addr &= ~StackTagMask;
-
-    // Dispatch
-    switch (tag) {
-      case ValueArrayTag: {
-        JS_STATIC_ASSERT(ValueArrayTag == 0);
-        MOZ_ASSERT(!(addr & CellMask));
-        obj = reinterpret_cast<JSObject*>(addr);
-        uintptr_t addr2 = stack.pop();
-        uintptr_t addr3 = stack.pop();
-        MOZ_ASSERT(addr2 <= addr3);
-        MOZ_ASSERT((addr3 - addr2) % sizeof(Value) == 0);
-        vp = reinterpret_cast<HeapSlot*>(addr2);
-        end = reinterpret_cast<HeapSlot*>(addr3);
+    switch (stack.peekTag()) {
+      case MarkStack::ValueArrayTag: {
+        auto array = stack.popValueArray();
+        obj = array.ptr.asValueArrayObject();
+        vp = array.start;
+        end = array.end;
         goto scan_value_array;
       }
 
-      case ObjectTag: {
-        obj = reinterpret_cast<JSObject*>(addr);
+      case MarkStack::ObjectTag: {
+        obj = stack.popPtr().as<JSObject>();
         AssertZoneIsMarking(obj);
         goto scan_obj;
       }
 
-      case GroupTag: {
-        return lazilyMarkChildren(reinterpret_cast<ObjectGroup*>(addr));
+      case MarkStack::GroupTag: {
+        auto group = stack.popPtr().as<ObjectGroup>();
+        return lazilyMarkChildren(group);
       }
 
-      case JitCodeTag: {
-        return reinterpret_cast<jit::JitCode*>(addr)->traceChildren(this);
+      case MarkStack::JitCodeTag: {
+        auto code = stack.popPtr().as<jit::JitCode>();
+        return code->traceChildren(this);
       }
 
-      case ScriptTag: {
-        return reinterpret_cast<JSScript*>(addr)->traceChildren(this);
+      case MarkStack::ScriptTag: {
+        auto script = stack.popPtr().as<JSScript>();
+        return script->traceChildren(this);
       }
 
-      case SavedValueArrayTag: {
-        MOZ_ASSERT(!(addr & CellMask));
-        JSObject* obj = reinterpret_cast<JSObject*>(addr);
-        HeapSlot* vp;
-        HeapSlot* end;
-        if (restoreValueArray(obj, (void**)&vp, (void**)&end))
-            pushValueArray(&obj->as<NativeObject>(), vp, end);
+      case MarkStack::SavedValueArrayTag: {
+        auto savedArray = stack.popSavedValueArray();
+        JSObject* obj = savedArray.ptr.asSavedValueArrayObject();
+        if (restoreValueArray(savedArray, &vp, &end))
+            pushValueArray(obj, vp, end);
         else
             repush(obj);
         return;
       }
 
       default: MOZ_CRASH("Invalid tag in mark stack");
     }
     return;
@@ -1785,106 +1775,93 @@ GCMarker::processMarkStackTop(SliceBudge
             }
         }
         MOZ_ASSERT(nslots <= nobj->numFixedSlots());
         end = vp + nslots;
         goto scan_value_array;
     }
 }
 
-struct SlotArrayLayout
-{
-    union {
-        HeapSlot* end;
-        uintptr_t kind;
-    };
-    union {
-        HeapSlot* start;
-        uintptr_t index;
-    };
-    NativeObject* obj;
-
-    static void staticAsserts() {
-        /* This should have the same layout as three mark stack items. */
-        JS_STATIC_ASSERT(sizeof(SlotArrayLayout) == 3 * sizeof(uintptr_t));
-    }
-};
-
 /*
  * During incremental GC, we return from drainMarkStack without having processed
  * the entire stack. At that point, JS code can run and reallocate slot arrays
  * that are stored on the stack. To prevent this from happening, we replace all
  * ValueArrayTag stack items with SavedValueArrayTag. In the latter, slots
  * pointers are replaced with slot indexes, and slot array end pointers are
  * replaced with the kind of index (properties vs. elements).
  */
 void
 GCMarker::saveValueRanges()
 {
-    for (uintptr_t* p = stack.tos_; p > stack.stack_; ) {
-        uintptr_t tag = *--p & StackTagMask;
-        if (tag == ValueArrayTag) {
-            *p &= ~StackTagMask;
-            p -= 2;
-            SlotArrayLayout* arr = reinterpret_cast<SlotArrayLayout*>(p);
-            NativeObject* obj = arr->obj;
+    MarkStackIter iter(stack);
+    while (!iter.done()) {
+        auto tag = iter.peekTag();
+        if (tag == MarkStack::ValueArrayTag) {
+            auto array = iter.peekValueArray();
+
+            NativeObject* obj = &array.ptr.asValueArrayObject()->as<NativeObject>();
             MOZ_ASSERT(obj->isNative());
 
+            uintptr_t index;
+            HeapSlot::Kind kind;
             HeapSlot* vp = obj->getDenseElementsAllowCopyOnWrite();
-            if (arr->end == vp + obj->getDenseInitializedLength()) {
-                MOZ_ASSERT(arr->start >= vp);
-                arr->index = arr->start - vp;
-                arr->kind = HeapSlot::Element;
+            if (array.end == vp + obj->getDenseInitializedLength()) {
+                MOZ_ASSERT(array.start >= vp);
+                index = array.start - vp;
+                kind = HeapSlot::Element;
             } else {
                 HeapSlot* vp = obj->fixedSlots();
                 unsigned nfixed = obj->numFixedSlots();
-                if (arr->start == arr->end) {
-                    arr->index = obj->slotSpan();
-                } else if (arr->start >= vp && arr->start < vp + nfixed) {
-                    MOZ_ASSERT(arr->end == vp + Min(nfixed, obj->slotSpan()));
-                    arr->index = arr->start - vp;
+                if (array.start == array.end) {
+                    index = obj->slotSpan();
+                } else if (array.start >= vp && array.start < vp + nfixed) {
+                    MOZ_ASSERT(array.end == vp + Min(nfixed, obj->slotSpan()));
+                    index = array.start - vp;
                 } else {
-                    MOZ_ASSERT(arr->start >= obj->slots_ &&
-                               arr->end == obj->slots_ + obj->slotSpan() - nfixed);
-                    arr->index = (arr->start - obj->slots_) + nfixed;
+                    MOZ_ASSERT(array.start >= obj->slots_ &&
+                               array.end == obj->slots_ + obj->slotSpan() - nfixed);
+                    index = (array.start - obj->slots_) + nfixed;
                 }
-                arr->kind = HeapSlot::Slot;
+                kind = HeapSlot::Slot;
             }
-            p[2] |= SavedValueArrayTag;
-        } else if (tag == SavedValueArrayTag) {
-            p -= 2;
+            iter.saveValueArray(obj, index, kind);
+            iter.nextArray();
+        } else if (tag == MarkStack::SavedValueArrayTag) {
+            iter.nextArray();
+        } else {
+            iter.nextPtr();
         }
     }
 }
 
 bool
-GCMarker::restoreValueArray(JSObject* objArg, void** vpp, void** endp)
+GCMarker::restoreValueArray(const MarkStack::SavedValueArray& array,
+                            HeapSlot** vpp, HeapSlot** endp)
 {
-    uintptr_t start = stack.pop();
-    HeapSlot::Kind kind = (HeapSlot::Kind) stack.pop();
-
+    JSObject* objArg = array.ptr.asSavedValueArrayObject();
     if (!objArg->isNative())
         return false;
     NativeObject* obj = &objArg->as<NativeObject>();
 
-    if (kind == HeapSlot::Element) {
+    uintptr_t start = array.index;
+    if (array.kind == HeapSlot::Element) {
         if (!obj->is<ArrayObject>())
             return false;
 
         uint32_t initlen = obj->getDenseInitializedLength();
         HeapSlot* vp = obj->getDenseElementsAllowCopyOnWrite();
         if (start < initlen) {
             *vpp = vp + start;
             *endp = vp + initlen;
         } else {
             /* The object shrunk, in which case no scanning is needed. */
             *vpp = *endp = vp;
         }
     } else {
-        MOZ_ASSERT(kind == HeapSlot::Slot);
+        MOZ_ASSERT(array.kind == HeapSlot::Slot);
         HeapSlot* vp = obj->fixedSlots();
         unsigned nfixed = obj->numFixedSlots();
         unsigned nslots = obj->slotSpan();
         if (start < nslots) {
             if (start < nfixed) {
                 *vpp = vp + start;
                 *endp = vp + Min(nfixed, nslots);
             } else {
@@ -1899,30 +1876,162 @@ GCMarker::restoreValueArray(JSObject* ob
 
     MOZ_ASSERT(*vpp <= *endp);
     return true;
 }
 
 
 /*** Mark Stack ***********************************************************************************/
 
+static_assert(sizeof(MarkStack::TaggedPtr) == sizeof(uintptr_t),
+              "A TaggedPtr should be the same size as a pointer");
+static_assert(sizeof(MarkStack::ValueArray) == sizeof(MarkStack::SavedValueArray),
+              "ValueArray and SavedValueArray should be the same size");
+static_assert((sizeof(MarkStack::ValueArray) % sizeof(uintptr_t)) == 0,
+              "ValueArray and SavedValueArray should be multiples of the pointer size");
+
+static const size_t ValueArrayWords = sizeof(MarkStack::ValueArray) / sizeof(uintptr_t);
+
+template <typename T>
+struct MapTypeToMarkStackTag {};
+template <>
+struct MapTypeToMarkStackTag<JSObject*> { static const auto value = MarkStack::ObjectTag; };
+template <>
+struct MapTypeToMarkStackTag<ObjectGroup*> { static const auto value = MarkStack::GroupTag; };
+template <>
+struct MapTypeToMarkStackTag<jit::JitCode*> { static const auto value = MarkStack::JitCodeTag; };
+template <>
+struct MapTypeToMarkStackTag<JSScript*> { static const auto value = MarkStack::ScriptTag; };
+
+static inline bool
+TagIsArrayTag(MarkStack::Tag tag)
+{
+    return tag == MarkStack::ValueArrayTag || tag == MarkStack::SavedValueArrayTag;
+}
+
+static inline void
+CheckValueArray(const MarkStack::ValueArray& array)
+{
+    MOZ_ASSERT(array.ptr.tag() == MarkStack::ValueArrayTag);
+    MOZ_ASSERT(uintptr_t(array.start) <= uintptr_t(array.end));
+    MOZ_ASSERT((uintptr_t(array.end) - uintptr_t(array.start)) % sizeof(Value) == 0);
+}
+
+static inline void
+CheckSavedValueArray(const MarkStack::SavedValueArray& array)
+{
+    MOZ_ASSERT(array.ptr.tag() == MarkStack::SavedValueArrayTag);
+    MOZ_ASSERT(array.kind == HeapSlot::Slot || array.kind == HeapSlot::Element);
+}
+
+inline
+MarkStack::TaggedPtr::TaggedPtr(Tag tag, Cell* ptr)
+  : bits(tag | uintptr_t(ptr))
+{
+    MOZ_ASSERT(tag <= LastTag);
+    MOZ_ASSERT((uintptr_t(ptr) & CellMask) == 0);
+}
+
+inline MarkStack::Tag
+MarkStack::TaggedPtr::tag() const
+{
+    auto tag = Tag(bits & TagMask);
+    MOZ_ASSERT(tag <= LastTag);
+    return tag;
+}
+
+inline Cell*
+MarkStack::TaggedPtr::ptr() const
+{
+    return reinterpret_cast<Cell*>(bits & ~TagMask);
+}
+
+template <typename T>
+inline T*
+MarkStack::TaggedPtr::as() const
+{
+    MOZ_ASSERT(tag() == MapTypeToMarkStackTag<T*>::value);
+    MOZ_ASSERT(ptr()->asTenured().getTraceKind() == MapTypeToTraceKind<T>::kind);
+    return static_cast<T*>(ptr());
+}
+
+inline JSObject*
+MarkStack::TaggedPtr::asValueArrayObject() const
+{
+    MOZ_ASSERT(tag() == ValueArrayTag);
+    MOZ_ASSERT(ptr()->asTenured().getTraceKind() == JS::TraceKind::Object);
+    return static_cast<JSObject*>(ptr());
+}
+
+inline JSObject*
+MarkStack::TaggedPtr::asSavedValueArrayObject() const
+{
+    MOZ_ASSERT(tag() == SavedValueArrayTag);
+    MOZ_ASSERT(ptr()->asTenured().getTraceKind() == JS::TraceKind::Object);
+    return static_cast<JSObject*>(ptr());
+}
+
+inline JSRope*
+MarkStack::TaggedPtr::asTempRope() const
+{
+    MOZ_ASSERT(tag() == TempRopeTag);
+    MOZ_ASSERT(ptr()->asTenured().getTraceKind() == JS::TraceKind::String);
+    return static_cast<JSRope*>(ptr());
+}
+
+inline
+MarkStack::ValueArray::ValueArray(JSObject* obj, HeapSlot* startArg, HeapSlot* endArg)
+  : end(endArg), start(startArg), ptr(ValueArrayTag, obj)
+{}
+
+inline
+MarkStack::SavedValueArray::SavedValueArray(JSObject* obj, size_t indexArg, HeapSlot::Kind kindArg)
+  : kind(kindArg), index(indexArg), ptr(SavedValueArrayTag, obj)
+{}
+
+MarkStack::MarkStack(size_t maxCapacity)
+  : stack_(nullptr)
+  , tos_(nullptr)
+  , end_(nullptr)
+  , baseCapacity_(0)
+  , maxCapacity_(maxCapacity)
+#ifdef DEBUG
+  , iteratorCount_(0)
+#endif
+{}
+
+MarkStack::~MarkStack()
+{
+    MOZ_ASSERT(iteratorCount_ == 0);
+    js_free(stack_);
+}
+
 bool
 MarkStack::init(JSGCMode gcMode)
 {
     setBaseCapacity(gcMode);
 
     MOZ_ASSERT(!stack_);
-    uintptr_t* newStack = js_pod_malloc<uintptr_t>(baseCapacity_);
+    auto newStack = js_pod_malloc<TaggedPtr>(baseCapacity_);
     if (!newStack)
         return false;
 
     setStack(newStack, 0, baseCapacity_);
     return true;
 }
 
+inline void
+MarkStack::setStack(TaggedPtr* stack, size_t tosIndex, size_t capacity)
+{
+    MOZ_ASSERT(iteratorCount_ == 0);
+    stack_ = stack;
+    tos_ = stack + tosIndex;
+    end_ = stack + capacity;
+}
+
 void
 MarkStack::setBaseCapacity(JSGCMode mode)
 {
     switch (mode) {
       case JSGC_MODE_GLOBAL:
       case JSGC_MODE_ZONE:
         baseCapacity_ = NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY;
         break;
@@ -1944,47 +2053,162 @@ MarkStack::setMaxCapacity(size_t maxCapa
     MOZ_ASSERT(isEmpty());
     maxCapacity_ = maxCapacity;
     if (baseCapacity_ > maxCapacity_)
         baseCapacity_ = maxCapacity_;
 
     reset();
 }
 
+inline bool
+MarkStack::pushTaggedPtr(Tag tag, Cell* ptr)
+{
+    if (!ensureSpace(1))
+        return false;
+
+    MOZ_ASSERT(tos_ < end_);
+    *tos_++ = TaggedPtr(tag, ptr);
+    return true;
+}
+
+template <typename T>
+inline bool
+MarkStack::push(T* ptr)
+{
+    return pushTaggedPtr(MapTypeToMarkStackTag<T*>::value, ptr);
+}
+
+inline bool
+MarkStack::pushTempRope(JSRope* rope)
+{
+    return pushTaggedPtr(TempRopeTag, rope);
+}
+
+inline bool
+MarkStack::push(JSObject* obj, HeapSlot* start, HeapSlot* end)
+{
+    return push(ValueArray(obj, start, end));
+}
+
+inline bool
+MarkStack::push(const ValueArray& array)
+{
+    CheckValueArray(array);
+
+    if (!ensureSpace(ValueArrayWords))
+        return false;
+
+    *reinterpret_cast<ValueArray*>(tos_.ref()) = array;
+    tos_ += ValueArrayWords;
+    MOZ_ASSERT(tos_ <= end_);
+    MOZ_ASSERT(peekTag() == ValueArrayTag);
+    return true;
+}
+
+inline bool
+MarkStack::push(const SavedValueArray& array)
+{
+    CheckSavedValueArray(array);
+
+    if (!ensureSpace(ValueArrayWords))
+        return false;
+
+    *reinterpret_cast<SavedValueArray*>(tos_.ref()) = array;
+    tos_ += ValueArrayWords;
+    MOZ_ASSERT(tos_ <= end_);
+    MOZ_ASSERT(peekTag() == SavedValueArrayTag);
+    return true;
+}
+
+inline const MarkStack::TaggedPtr&
+MarkStack::peekPtr() const
+{
+    MOZ_ASSERT(!isEmpty());
+    return tos_[-1];
+}
+
+inline MarkStack::Tag
+MarkStack::peekTag() const
+{
+    return peekPtr().tag();
+}
+
+inline MarkStack::TaggedPtr
+MarkStack::popPtr()
+{
+    MOZ_ASSERT(!isEmpty());
+    MOZ_ASSERT(!TagIsArrayTag(peekTag()));
+    tos_--;
+    return *tos_;
+}
+
+inline MarkStack::ValueArray
+MarkStack::popValueArray()
+{
+    MOZ_ASSERT(peekTag() == ValueArrayTag);
+    MOZ_ASSERT(position() >= ValueArrayWords);
+
+    tos_ -= ValueArrayWords;
+    const auto& array = *reinterpret_cast<ValueArray*>(tos_.ref());
+    CheckValueArray(array);
+    return array;
+}
+
+inline MarkStack::SavedValueArray
+MarkStack::popSavedValueArray()
+{
+    MOZ_ASSERT(peekTag() == SavedValueArrayTag);
+    MOZ_ASSERT(position() >= ValueArrayWords);
+
+    tos_ -= ValueArrayWords;
+    const auto& array = *reinterpret_cast<SavedValueArray*>(tos_.ref());
+    CheckSavedValueArray(array);
+    return array;
+}
+
 void
 MarkStack::reset()
 {
     if (capacity() == baseCapacity_) {
         // No size change; keep the current stack.
         setStack(stack_, 0, baseCapacity_);
         return;
     }
 
     MOZ_ASSERT(baseCapacity_ != 0);
-    uintptr_t* newStack = (uintptr_t*)js_realloc(stack_, sizeof(uintptr_t) * baseCapacity_);
+    auto newStack = js_pod_realloc<TaggedPtr>(stack_, capacity(), baseCapacity_);
     if (!newStack) {
         // If the realloc fails, just keep using the existing stack; it's
         // not ideal but better than failing.
         newStack = stack_;
         baseCapacity_ = capacity();
     }
     setStack(newStack, 0, baseCapacity_);
 }
 
+inline bool
+MarkStack::ensureSpace(size_t count)
+{
+    if ((tos_ + count) <= end_)
+        return true;
+
+    return enlarge(count);
+}
+
 bool
-MarkStack::enlarge(unsigned count)
+MarkStack::enlarge(size_t count)
 {
     size_t newCapacity = Min(maxCapacity_.ref(), capacity() * 2);
     if (newCapacity < capacity() + count)
         return false;
 
     size_t tosIndex = position();
 
     MOZ_ASSERT(newCapacity != 0);
-    uintptr_t* newStack = (uintptr_t*)js_realloc(stack_, sizeof(uintptr_t) * newCapacity);
+    auto newStack = js_pod_realloc<TaggedPtr>(stack_, capacity(), newCapacity);
     if (!newStack)
         return false;
 
     setStack(newStack, tosIndex, newCapacity);
     return true;
 }
 
 void
@@ -1996,16 +2220,98 @@ MarkStack::setGCMode(JSGCMode gcMode)
 }
 
 size_t
 MarkStack::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
     return mallocSizeOf(stack_);
 }
 
+MarkStackIter::MarkStackIter(const MarkStack& stack)
+  : stack_(stack),
+    pos_(stack.tos_)
+{
+#ifdef DEBUG
+    stack.iteratorCount_++;
+#endif
+}
+
+MarkStackIter::~MarkStackIter()
+{
+#ifdef DEBUG
+    MOZ_ASSERT(stack_.iteratorCount_);
+    stack_.iteratorCount_--;
+#endif
+}
+
+inline size_t
+MarkStackIter::position() const
+{
+    return pos_ - stack_.stack_;
+}
+
+inline bool
+MarkStackIter::done() const
+{
+    return position() == 0;
+}
+
+inline const MarkStack::TaggedPtr&
+MarkStackIter::peekPtr() const
+{
+    MOZ_ASSERT(!done());
+    return pos_[-1];
+}
+
+inline MarkStack::Tag
+MarkStackIter::peekTag() const
+{
+    return peekPtr().tag();
+}
+
+inline MarkStack::ValueArray
+MarkStackIter::peekValueArray() const
+{
+    MOZ_ASSERT(peekTag() == MarkStack::ValueArrayTag);
+    MOZ_ASSERT(position() >= ValueArrayWords);
+
+    const auto& array = *reinterpret_cast<MarkStack::ValueArray*>(pos_ - ValueArrayWords);
+    CheckValueArray(array);
+    return array;
+}
+
+inline void
+MarkStackIter::nextPtr()
+{
+    MOZ_ASSERT(!done());
+    MOZ_ASSERT(!TagIsArrayTag(peekTag()));
+    pos_--;
+}
+
+inline void
+MarkStackIter::nextArray()
+{
+    MOZ_ASSERT(TagIsArrayTag(peekTag()));
+    MOZ_ASSERT(position() >= ValueArrayWords);
+    pos_ -= ValueArrayWords;
+}
+
+void
+MarkStackIter::saveValueArray(NativeObject* obj, uintptr_t index, HeapSlot::Kind kind)
+{
+    MOZ_ASSERT(peekTag() == MarkStack::ValueArrayTag);
+    MOZ_ASSERT(peekPtr().asValueArrayObject() == obj);
+    MOZ_ASSERT(position() >= ValueArrayWords);
+
+    auto& array = *reinterpret_cast<MarkStack::SavedValueArray*>(pos_ - ValueArrayWords);
+    array = MarkStack::SavedValueArray(obj, index, kind);
+    CheckSavedValueArray(array);
+    MOZ_ASSERT(peekTag() == MarkStack::SavedValueArrayTag);
+}
+
 
 /*** GCMarker *************************************************************************************/
 
 /*
  * ExpandWeakMaps: the GC is recomputing the liveness of WeakMap entries by
  * expanding each live WeakMap into its constituent key->value edges, a table
  * of which will be consulted in a later phase whenever marking a potential
  * key.
@@ -2084,16 +2390,41 @@ GCMarker::reset()
 #ifdef DEBUG
         markLaterArenas--;
 #endif
     }
     MOZ_ASSERT(isDrained());
     MOZ_ASSERT(!markLaterArenas);
 }
 
+
+template <typename T>
+void
+GCMarker::pushTaggedPtr(T* ptr)
+{
+    checkZone(ptr);
+    if (!stack.push(ptr))
+        delayMarkingChildren(ptr);
+}
+
+void
+GCMarker::pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end)
+{
+    checkZone(obj);
+    if (!stack.push(obj, start, end))
+        delayMarkingChildren(obj);
+}
+
+void
+GCMarker::repush(JSObject* obj)
+{
+    MOZ_ASSERT(gc::TenuredCell::fromPointer(obj)->isMarked(markColor()));
+    pushTaggedPtr(obj);
+}
+
 void
 GCMarker::enterWeakMarkingMode()
 {
     MOZ_ASSERT(tag_ == TracerKindTag::Marking);
     if (linearWeakMarkingDisabled_)
         return;
 
     // During weak marking mode, we maintain a table mapping weak keys to
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -36,114 +36,179 @@ class Arena;
 } // namespace gc
 namespace jit {
 class JitCode;
 } // namespace jit
 
 static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
 static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
 
+namespace gc {
+
+class MarkStackIter;
+
 /*
  * When the native stack is low, the GC does not call js::TraceChildren to mark
  * the reachable "children" of the thing. Rather the thing is put aside and
  * js::TraceChildren is called later with more space on the C stack.
  *
  * To implement such delayed marking of the children with minimal overhead for
  * the normal case of sufficient native stack, the code adds a field per arena.
  * The field markingDelay->link links all arenas with delayed things into a
  * stack list with the pointer to stack top in GCMarker::unmarkedArenaStackTop.
  * GCMarker::delayMarkingChildren adds arenas to the stack as necessary while
  * markDelayedChildren pops the arenas from the stack until it empties.
  */
 class MarkStack
 {
-    friend class GCMarker;
+  public:
+    /*
+     * We use a common mark stack to mark GC things of different types and use
+     * the explicit tags to distinguish them when it cannot be deduced from
+     * the context of push or pop operation.
+     */
+    enum Tag {
+        ValueArrayTag,
+        ObjectTag,
+        GroupTag,
+        SavedValueArrayTag,
+        JitCodeTag,
+        ScriptTag,
+        TempRopeTag,
 
-    ActiveThreadData<uintptr_t*> stack_;
-    ActiveThreadData<uintptr_t*> tos_;
-    ActiveThreadData<uintptr_t*> end_;
+        LastTag = TempRopeTag
+    };
 
-    // The capacity we start with and reset() to.
-    ActiveThreadData<size_t> baseCapacity_;
-    ActiveThreadData<size_t> maxCapacity_;
+    static const uintptr_t TagMask = 7;
+    static_assert(TagMask >= uintptr_t(LastTag), "The tag mask must subsume the tags.");
+    static_assert(TagMask <= gc::CellMask, "The tag mask must be embeddable in a Cell*.");
+
+    class TaggedPtr
+    {
+        uintptr_t bits;
+
+        Cell* ptr() const;
 
-  public:
-    explicit MarkStack(size_t maxCapacity)
-      : stack_(nullptr),
-        tos_(nullptr),
-        end_(nullptr),
-        baseCapacity_(0),
-        maxCapacity_(maxCapacity)
-    {}
+      public:
+        TaggedPtr(Tag tag, Cell* ptr);
+        Tag tag() const;
+        template <typename T> T* as() const;
+        JSObject* asValueArrayObject() const;
+        JSObject* asSavedValueArrayObject() const;
+        JSRope* asTempRope() const;
+    };
+
+    struct ValueArray
+    {
+        ValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end);
 
-    ~MarkStack() {
-        js_free(stack_);
-    }
+        HeapSlot* end;
+        HeapSlot* start;
+        TaggedPtr ptr;
+    };
+
+    struct SavedValueArray
+    {
+        SavedValueArray(JSObject* obj, size_t index, HeapSlot::Kind kind);
+
+        uintptr_t kind;
+        uintptr_t index;
+        TaggedPtr ptr;
+    };
+
+    explicit MarkStack(size_t maxCapacity);
+    ~MarkStack();
 
     size_t capacity() { return end_ - stack_; }
 
-    ptrdiff_t position() const { return tos_ - stack_; }
+    size_t position() const {
+        auto result = tos_ - stack_;
+        MOZ_ASSERT(result >= 0);
+        return size_t(result);
+    }
 
-    void setStack(uintptr_t* stack, size_t tosIndex, size_t capacity) {
-        stack_ = stack;
-        tos_ = stack + tosIndex;
-        end_ = stack + capacity;
-    }
+    void setStack(TaggedPtr* stack, size_t tosIndex, size_t capacity);
 
     MOZ_MUST_USE bool init(JSGCMode gcMode);
 
     void setBaseCapacity(JSGCMode mode);
     size_t maxCapacity() const { return maxCapacity_; }
     void setMaxCapacity(size_t maxCapacity);
 
-    MOZ_MUST_USE bool push(uintptr_t item) {
-        if (tos_ == end_) {
-            if (!enlarge(1))
-                return false;
-        }
-        MOZ_ASSERT(tos_ < end_);
-        *tos_++ = item;
-        return true;
-    }
+    template <typename T>
+    MOZ_MUST_USE bool push(T* ptr);
+    MOZ_MUST_USE bool push(JSObject* obj, HeapSlot* start, HeapSlot* end);
+    MOZ_MUST_USE bool push(const ValueArray& array);
+    MOZ_MUST_USE bool push(const SavedValueArray& array);
 
-    MOZ_MUST_USE bool push(uintptr_t item1, uintptr_t item2, uintptr_t item3) {
-        uintptr_t* nextTos = tos_ + 3;
-        if (nextTos > end_) {
-            if (!enlarge(3))
-                return false;
-            nextTos = tos_ + 3;
-        }
-        MOZ_ASSERT(nextTos <= end_);
-        tos_[0] = item1;
-        tos_[1] = item2;
-        tos_[2] = item3;
-        tos_ = nextTos;
-        return true;
-    }
+    // GCMarker::eagerlyMarkChildren uses unused marking stack as temporary
+    // storage to hold rope pointers.
+    MOZ_MUST_USE bool pushTempRope(JSRope* ptr);
 
     bool isEmpty() const {
         return tos_ == stack_;
     }
 
-    uintptr_t pop() {
-        MOZ_ASSERT(!isEmpty());
-        return *--tos_;
-    }
+    Tag peekTag() const;
+    TaggedPtr popPtr();
+    ValueArray popValueArray();
+    SavedValueArray popSavedValueArray();
 
     void reset();
 
-    /* Grow the stack, ensuring there is space for at least count elements. */
-    MOZ_MUST_USE bool enlarge(unsigned count);
-
     void setGCMode(JSGCMode gcMode);
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+  private:
+    MOZ_MUST_USE bool ensureSpace(size_t count);
+
+    /* Grow the stack, ensuring there is space for at least count elements. */
+    MOZ_MUST_USE bool enlarge(size_t count);
+
+    const TaggedPtr& peekPtr() const;
+    MOZ_MUST_USE bool pushTaggedPtr(Tag tag, Cell* ptr);
+
+    ActiveThreadData<TaggedPtr*> stack_;
+    ActiveThreadData<TaggedPtr*> tos_;
+    ActiveThreadData<TaggedPtr*> end_;
+
+    // The capacity we start with and reset() to.
+    ActiveThreadData<size_t> baseCapacity_;
+    ActiveThreadData<size_t> maxCapacity_;
+
+#ifdef DEBUG
+    mutable size_t iteratorCount_;
+#endif
+
+    friend class MarkStackIter;
 };
 
-namespace gc {
+class MarkStackIter
+{
+    const MarkStack& stack_;
+    MarkStack::TaggedPtr* pos_;
+
+  public:
+    explicit MarkStackIter(const MarkStack& stack);
+    ~MarkStackIter();
+
+    bool done() const;
+    MarkStack::Tag peekTag() const;
+    MarkStack::ValueArray peekValueArray() const;
+    void nextPtr();
+    void nextArray();
+
+    // Mutate the current ValueArray to a SavedValueArray.
+    void saveValueArray(NativeObject* obj, uintptr_t index, HeapSlot::Kind kind);
+
+  private:
+    size_t position() const;
+    const MarkStack::TaggedPtr& peekPtr() const;
+};
 
 struct WeakKeyTableHashPolicy {
     typedef JS::GCCellPtr Lookup;
     static HashNumber hash(const Lookup& v, const mozilla::HashCodeScrambler&) {
         return mozilla::HashGeneric(v.asCell());
     }
     static bool match(const JS::GCCellPtr& k, const Lookup& l) { return k == l; }
     static bool isEmpty(const JS::GCCellPtr& v) { return !v; }
@@ -247,44 +312,22 @@ class GCMarker : public JSTracer
 
   private:
 #ifdef DEBUG
     void checkZone(void* p);
 #else
     void checkZone(void* p) {}
 #endif
 
-    /*
-     * We use a common mark stack to mark GC things of different types and use
-     * the explicit tags to distinguish them when it cannot be deduced from
-     * the context of push or pop operation.
-     */
-    enum StackTag {
-        ValueArrayTag,
-        ObjectTag,
-        GroupTag,
-        SavedValueArrayTag,
-        JitCodeTag,
-        ScriptTag,
-        LastTag = JitCodeTag
-    };
-
-    static const uintptr_t StackTagMask = 7;
-    static_assert(StackTagMask >= uintptr_t(LastTag), "The tag mask must subsume the tags.");
-    static_assert(StackTagMask <= gc::CellMask, "The tag mask must be embeddable in a Cell*.");
-
     // Push an object onto the stack for later tracing and assert that it has
     // already been marked.
-    void repush(JSObject* obj) {
-        MOZ_ASSERT(gc::TenuredCell::fromPointer(obj)->isMarked(markColor()));
-        pushTaggedPtr(ObjectTag, obj);
-    }
+    inline void repush(JSObject* obj);
 
     template <typename T> void markAndTraceChildren(T* thing);
-    template <typename T> void markAndPush(StackTag tag, T* thing);
+    template <typename T> void markAndPush(T* thing);
     template <typename T> void markAndScan(T* thing);
     template <typename T> void markImplicitEdgesHelper(T oldThing);
     template <typename T> void markImplicitEdges(T* oldThing);
     void eagerlyMarkChildren(JSLinearString* str);
     void eagerlyMarkChildren(JSRope* rope);
     void eagerlyMarkChildren(JSString* str);
     void eagerlyMarkChildren(LazyScript *thing);
     void eagerlyMarkChildren(Shape* shape);
@@ -295,50 +338,32 @@ class GCMarker : public JSTracer
     template <typename T>
     void dispatchToTraceChildren(T* thing);
 
     // Mark the given GC thing, but do not trace its children. Return true
     // if the thing became marked.
     template <typename T>
     MOZ_MUST_USE bool mark(T* thing);
 
-    void pushTaggedPtr(StackTag tag, void* ptr) {
-        checkZone(ptr);
-        uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
-        MOZ_ASSERT(!(addr & StackTagMask));
-        if (!stack.push(addr | uintptr_t(tag)))
-            delayMarkingChildren(ptr);
-    }
-
-    void pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end) {
-        checkZone(obj);
+    template <typename T>
+    inline void pushTaggedPtr(T* ptr);
 
-        MOZ_ASSERT(start <= end);
-        uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | GCMarker::ValueArrayTag;
-        uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
-        uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
-
-        /*
-         * Push in the reverse order so obj will be on top. If we cannot push
-         * the array, we trigger delay marking for the whole object.
-         */
-        if (!stack.push(endAddr, startAddr, tagged))
-            delayMarkingChildren(obj);
-    }
+    inline void pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end);
 
     bool isMarkStackEmpty() {
         return stack.isEmpty();
     }
 
-    MOZ_MUST_USE bool restoreValueArray(JSObject* obj, void** vpp, void** endp);
+    MOZ_MUST_USE bool restoreValueArray(const gc::MarkStack::SavedValueArray& array,
+                                        HeapSlot** vpp, HeapSlot** endp);
     void saveValueRanges();
     inline void processMarkStackTop(SliceBudget& budget);
 
     /* The mark stack. Pointers in this stack are "gray" in the GC sense. */
-    MarkStack stack;
+    gc::MarkStack stack;
 
     /* The color is only applied to objects and functions. */
     ActiveThreadData<uint32_t> color;
 
     /* Pointer to the top of the stack of arenas we are delaying marking on. */
     ActiveThreadData<js::gc::Arena*> unmarkedArenaStackTop;
 
     /*
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -838,33 +838,27 @@ DeallocateMappedContent(void* p, size_t 
     size_t alignedLength = length + (uintptr_t(p) % allocGranularity);
     UnmapPages(reinterpret_cast<void*>(map), alignedLength);
 }
 
 #else
 #error "Memory mapping functions are not defined for your OS."
 #endif
 
-#ifdef XP_WIN
-static char sCrashReason[256];
-#endif
-
 void
 ProtectPages(void* p, size_t size)
 {
     MOZ_ASSERT(size % pageSize == 0);
     MOZ_RELEASE_ASSERT(size > 0);
     MOZ_RELEASE_ASSERT(p);
 #if defined(XP_WIN)
     DWORD oldProtect;
     if (!VirtualProtect(p, size, PAGE_NOACCESS, &oldProtect)) {
-        SprintfLiteral(sCrashReason,
-            "MOZ_CRASH(VirtualProtect(PAGE_NOACCESS) failed! Error code: %u)", GetLastError());
-        MOZ_CRASH_ANNOTATE(sCrashReason);
-        MOZ_REALLY_CRASH();
+        MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_NOACCESS) failed! Error code: %u",
+                                GetLastError());
     }
     MOZ_ASSERT(oldProtect == PAGE_READWRITE);
 #else  // assume Unix
     if (mprotect(p, size, PROT_NONE))
         MOZ_CRASH("mprotect(PROT_NONE) failed");
 #endif
 }
 
@@ -872,20 +866,18 @@ void
 MakePagesReadOnly(void* p, size_t size)
 {
     MOZ_ASSERT(size % pageSize == 0);
     MOZ_RELEASE_ASSERT(size > 0);
     MOZ_RELEASE_ASSERT(p);
 #if defined(XP_WIN)
     DWORD oldProtect;
     if (!VirtualProtect(p, size, PAGE_READONLY, &oldProtect)) {
-        SprintfLiteral(sCrashReason,
-            "MOZ_CRASH(VirtualProtect(PAGE_READONLY) failed! Error code: %u)", GetLastError());
-        MOZ_CRASH_ANNOTATE(sCrashReason);
-        MOZ_REALLY_CRASH();
+        MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READONLY) failed! Error code: %u",
+                                GetLastError());
     }
     MOZ_ASSERT(oldProtect == PAGE_READWRITE);
 #else  // assume Unix
     if (mprotect(p, size, PROT_READ))
         MOZ_CRASH("mprotect(PROT_READ) failed");
 #endif
 }
 
@@ -893,20 +885,18 @@ void
 UnprotectPages(void* p, size_t size)
 {
     MOZ_ASSERT(size % pageSize == 0);
     MOZ_RELEASE_ASSERT(size > 0);
     MOZ_RELEASE_ASSERT(p);
 #if defined(XP_WIN)
     DWORD oldProtect;
     if (!VirtualProtect(p, size, PAGE_READWRITE, &oldProtect)) {
-        SprintfLiteral(sCrashReason,
-            "MOZ_CRASH(VirtualProtect(PAGE_READWRITE) failed! Error code: %u)", GetLastError());
-        MOZ_CRASH_ANNOTATE(sCrashReason);
-        MOZ_REALLY_CRASH();
+        MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READWRITE) failed! Error code: %u",
+                                GetLastError());
     }
     MOZ_ASSERT(oldProtect == PAGE_NOACCESS || oldProtect == PAGE_READONLY);
 #else  // assume Unix
     if (mprotect(p, size, PROT_READ | PROT_WRITE))
         MOZ_CRASH("mprotect(PROT_READ | PROT_WRITE) failed");
 #endif
 }
 
--- a/js/src/jit-test/tests/modules/export-declaration.js
+++ b/js/src/jit-test/tests/modules/export-declaration.js
@@ -398,20 +398,62 @@ assertThrowsInstanceOf(function() {
 assertThrowsInstanceOf(function() {
     parseAsModule("export {} from");
 }, SyntaxError);
 
 assertThrowsInstanceOf(function() {
     parseAsModule("export {,} from 'a'");
 }, SyntaxError);
 
-parseAsModule("export { true as a } from 'b'");
+program([
+    exportDeclaration(
+        null,
+        [
+            exportSpecifier(
+                ident("true"),
+                ident("true")
+            ),
+        ],
+        lit("b"),
+        false
+    )
+]).assert(parseAsModule("export { true } from 'b'"));
+
+program([
+    exportDeclaration(
+        null,
+        [
+            exportSpecifier(
+                ident("true"),
+                ident("name")
+            ),
+        ],
+        lit("b"),
+        false
+    )
+]).assert(parseAsModule("export { true as name } from 'b'"));
+
+assertThrowsInstanceOf(function() {
+    parseAsModule("export { true }");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function() {
+    parseAsModule("export { true as name }");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function() {
+    parseAsModule("export { static }");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function() {
+    parseAsModule("export { static as name }");
+}, SyntaxError);
 
 assertThrowsInstanceOf(function () {
-    parseAsModule("export { a } from 'b' f();");
+    parseAsModule("export { name } from 'b' f();");
 }, SyntaxError);
 
 assertThrowsInstanceOf(function () {
     parseAsModule("export *");
 }, SyntaxError);
 
 assertThrowsInstanceOf(function () {
     parseAsModule("export * from 'b' f();");
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3172,19 +3172,20 @@ Crash(JSContext* cx, unsigned argc, Valu
     if (args.length() == 0)
         MOZ_CRASH("forced crash");
     RootedString message(cx, JS::ToString(cx, args[0]));
     if (!message)
         return false;
     char* utf8chars = JS_EncodeStringToUTF8(cx, message);
     if (!utf8chars)
         return false;
+#ifndef DEBUG
     MOZ_ReportCrash(utf8chars, __FILE__, __LINE__);
-    MOZ_CRASH_ANNOTATE("MOZ_CRASH(dynamic)");
-    MOZ_REALLY_CRASH();
+#endif
+    MOZ_CRASH_UNSAFE_OOL(utf8chars);
 }
 
 static bool
 GetSLX(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedScript script(cx);
 
--- a/js/src/tests/ecma_2017/AsyncFunctions/syntax-modules.js
+++ b/js/src/tests/ecma_2017/AsyncFunctions/syntax-modules.js
@@ -11,15 +11,21 @@ if (typeof parseModule === "function") {
     assertThrows(() => parseModule("await 5;"), SyntaxError);
     assertThrows(() => parseModule("function f() { await 5; }"), SyntaxError);
     assertThrows(() => parseModule("() => { await 5; }"), SyntaxError);
     assertThrows(() => parseModule("export var await;"), SyntaxError);
     assertThrows(() => parseModule("await => 1;"), SyntaxError);
     assertThrows(() => parseModule("async function f() { function g() { await 3; } }"), SyntaxError);
 
     if (typeof Reflect !== "undefined" && Reflect.parse) {
+        Reflect.parse("export async function f() {}", { target: "module" });
+        assertThrows(() => Reflect.parse("export async function() {}", { target: "module" }), SyntaxError);
+
+        Reflect.parse("export default async function() {}", { target: "module" });
+        Reflect.parse("export default async function f() {}", { target: "module" });
+
         assertThrows(() => Reflect.parse("export default async function() { yield; }", { target: "module" }), SyntaxError);
         assertThrows(() => Reflect.parse("export default async function() { yield = 1; }", { target: "module" }), SyntaxError);
     }
 }
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2603,26 +2603,21 @@ TypeZone::addPendingRecompile(JSContext*
     // When one script is inlined into another the caller listens to state
     // changes on the callee's script, so trigger these to force recompilation
     // of any such callers.
     if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyGroup())
         ObjectStateChange(cx, script->functionNonDelazifying()->group(), false);
 }
 
 #ifdef JS_CRASH_DIAGNOSTICS
-static char sCrashReason[256];
-
-MOZ_NORETURN MOZ_COLD MOZ_NEVER_INLINE void
+void
 js::ReportMagicWordFailure(uintptr_t actual, uintptr_t expected)
 {
-    SprintfLiteral(sCrashReason,
-                   "MOZ_CRASH(Got 0x%" PRIxPTR " expected magic word 0x%" PRIxPTR ")",
-                   actual, expected);
-    MOZ_CRASH_ANNOTATE(sCrashReason);
-    MOZ_REALLY_CRASH();
+    MOZ_CRASH_UNSAFE_PRINTF("Got 0x%" PRIxPTR " expected magic word 0x%" PRIxPTR,
+                            actual, expected);
 }
 #endif
 
 void
 js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force)
 {
 #ifdef DEBUG
     gc::AutoSuppressGC suppressGC(cx);
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -539,17 +539,17 @@ class TypeSet
 static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4;
 #else
 static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4c5c6d7d8;
 #endif
 static const uintptr_t TypeConstraintMagic = BaseTypeInferenceMagic + 1;
 static const uintptr_t ConstraintTypeSetMagic = BaseTypeInferenceMagic + 2;
 
 #ifdef JS_CRASH_DIAGNOSTICS
-extern MOZ_NORETURN MOZ_COLD MOZ_NEVER_INLINE void
+extern void
 ReportMagicWordFailure(uintptr_t actual, uintptr_t expected);
 #endif
 
 /*
  * A constraint which listens to additions to a type set and propagates those
  * changes to other type sets.
  */
 class TypeConstraint
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1727,16 +1727,44 @@ public:
    */
   static nsIFrame* GetClosestLayer(nsIFrame* aFrame);
 
   /**
    * Gets the graphics sampling filter for the frame
    */
   static SamplingFilter GetSamplingFilterForFrame(nsIFrame* aFrame);
 
+  static inline void InitDashPattern(StrokeOptions& aStrokeOptions,
+                                     uint8_t aBorderStyle) {
+    if (aBorderStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
+      static Float dot[] = { 1.f, 1.f };
+      aStrokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dot);
+      aStrokeOptions.mDashPattern = dot;
+    } else if (aBorderStyle == NS_STYLE_BORDER_STYLE_DASHED) {
+      static Float dash[] = { 5.f, 5.f };
+      aStrokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash);
+      aStrokeOptions.mDashPattern = dash;
+    } else {
+      aStrokeOptions.mDashLength = 0;
+      aStrokeOptions.mDashPattern = nullptr;
+    }
+  }
+
+  /**
+   * Convert an nsRect to a gfxRect.
+   */
+  static gfxRect RectToGfxRect(const nsRect& aRect,
+                               int32_t aAppUnitsPerDevPixel);
+
+  static gfxPoint PointToGfxPoint(const nsPoint& aPoint,
+                                  int32_t aAppUnitsPerPixel) {
+    return gfxPoint(gfxFloat(aPoint.x) / aAppUnitsPerPixel,
+                    gfxFloat(aPoint.y) / aAppUnitsPerPixel);
+  }
+
   /* N.B. The only difference between variants of the Draw*Image
    * functions below is the type of the aImage argument.
    */
 
   /**
    * Draw a background image.  The image's dimensions are as specified in aDest;
    * the image itself is not consulted to determine a size.
    * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
@@ -1798,44 +1826,16 @@ public:
                               const SamplingFilter aSamplingFilter,
                               const nsRect&       aDest,
                               const nsRect&       aFill,
                               const nsPoint&      aAnchor,
                               const nsRect&       aDirty,
                               uint32_t            aImageFlags,
                               float               aOpacity = 1.0);
 
-  static inline void InitDashPattern(StrokeOptions& aStrokeOptions,
-                                     uint8_t aBorderStyle) {
-    if (aBorderStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
-      static Float dot[] = { 1.f, 1.f };
-      aStrokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dot);
-      aStrokeOptions.mDashPattern = dot;
-    } else if (aBorderStyle == NS_STYLE_BORDER_STYLE_DASHED) {
-      static Float dash[] = { 5.f, 5.f };
-      aStrokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash);
-      aStrokeOptions.mDashPattern = dash;
-    } else {
-      aStrokeOptions.mDashLength = 0;
-      aStrokeOptions.mDashPattern = nullptr;
-    }
-  }
-
-  /**
-   * Convert an nsRect to a gfxRect.
-   */
-  static gfxRect RectToGfxRect(const nsRect& aRect,
-                               int32_t aAppUnitsPerDevPixel);
-
-  static gfxPoint PointToGfxPoint(const nsPoint& aPoint,
-                                  int32_t aAppUnitsPerPixel) {
-    return gfxPoint(gfxFloat(aPoint.x) / aAppUnitsPerPixel,
-                    gfxFloat(aPoint.y) / aAppUnitsPerPixel);
-  }
-
   /**
    * Draw a whole image without scaling or tiling.
    *
    *   @param aRenderingContext Where to draw the image, set up with an
    *                            appropriate scale and transform for drawing in
    *                            app units.
    *   @param aImage            The image.
    *   @param aDest             The top-left where the image should be drawn.
--- a/media/webrtc/signaling/fuzztest/moz.build
+++ b/media/webrtc/signaling/fuzztest/moz.build
@@ -16,16 +16,17 @@ if CONFIG['OS_TARGET'] == 'Linux' or CON
     LOCAL_INCLUDES += [
         '../..',
         '/media/mtransport',
         '/media/webrtc/signaling/src/common/browser_logging',
     ]
 
     USE_LIBS += [
         '/media/webrtc/trunk/testing/gtest_gtest/gtest',
+        'mozglue',
         'nspr',
     ]
 
     SOURCES = [
       '/media/webrtc/signaling/src/sdp/SdpAttribute.cpp',
       '/media/webrtc/signaling/src/sdp/SdpHelper.cpp',
       '/media/webrtc/signaling/src/sdp/SdpMediaSection.cpp',
       '/media/webrtc/signaling/src/sdp/sipcc/cpr_string.c',
--- a/memory/mozjemalloc/jemalloc.c
+++ b/memory/mozjemalloc/jemalloc.c
@@ -1543,55 +1543,38 @@ wrtmessage(const char *p1, const char *p
 	if (_write(STDERR_FILENO, p4, (unsigned int) strlen(p4)) < 0)
 		return;
 }
 
 MOZ_JEMALLOC_API
 void	(*_malloc_message)(const char *p1, const char *p2, const char *p3,
 	    const char *p4) = wrtmessage;
 
-#ifdef MALLOC_DEBUG
-#  define assert(e) do {						\
-	if (!(e)) {							\
-		char line_buf[UMAX2S_BUFSIZE];				\
-		_malloc_message(__FILE__, ":", umax2s(__LINE__, 10,	\
-		    line_buf), ": Failed assertion: ");			\
-		_malloc_message("\"", #e, "\"\n", "");			\
-		abort();						\
-	}								\
-} while (0)
-#else
-#define assert(e)
-#endif
-
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TaggedAnonymousMemory.h"
 // Note: MozTaggedAnonymousMmap() could call an LD_PRELOADed mmap
 // instead of the one defined here; use only MozTagAnonymousMemory().
 
+#ifdef MALLOC_DEBUG
+#  define assert(e) MOZ_ASSERT(e)
+#else
+#  define assert(e)
+#endif
+
 #ifdef MOZ_MEMORY_ANDROID
 // Android's pthread.h does not declare pthread_atfork() until SDK 21.
 extern MOZ_EXPORT
 int pthread_atfork(void (*)(void), void (*)(void), void(*)(void));
 #endif
 
-/* RELEASE_ASSERT calls jemalloc_crash() instead of calling MOZ_CRASH()
- * directly because we want crashing to add a frame to the stack.  This makes
- * it easier to find the failing assertion in crash stacks. */
-MOZ_NEVER_INLINE static void
-jemalloc_crash()
-{
-	MOZ_CRASH();
-}
-
 #if defined(MOZ_JEMALLOC_HARD_ASSERTS)
 #  define RELEASE_ASSERT(assertion) do {	\
 	if (!(assertion)) {			\
-		jemalloc_crash();		\
+		MOZ_CRASH_UNSAFE_OOL(#assertion);	\
 	}					\
 } while (0)
 #else
 #  define RELEASE_ASSERT(assertion) assert(assertion)
 #endif
 
 /******************************************************************************/
 /*
--- a/memory/mozjemalloc/moz.build
+++ b/memory/mozjemalloc/moz.build
@@ -23,21 +23,16 @@ if not CONFIG['MOZ_JEMALLOC4']:
 # helps us catch memory errors.
 if CONFIG['MOZ_UPDATE_CHANNEL'] not in ('release', 'esr'):
     DEFINES['MOZ_JEMALLOC_HARD_ASSERTS'] = True
 
 DEFINES['abort'] = 'moz_abort'
 
 DEFINES['MOZ_JEMALLOC_IMPL'] = True
 
-#XXX: PGO on Linux causes problems here
-# See bug 419470
-if CONFIG['OS_TARGET'] == 'Linux':
-    NO_PGO = True
-
 LOCAL_INCLUDES += [
     '/memory/build',
 ]
 
 if CONFIG['GNU_CC']:
     CFLAGS += ['-Wno-unused'] # too many annoying warnings from mfbt/ headers
 
 if CONFIG['_MSC_VER']:
--- a/memory/replace/logalloc/replay/moz.build
+++ b/memory/replace/logalloc/replay/moz.build
@@ -16,9 +16,12 @@ LOCAL_INCLUDES += [
     '..',
 ]
 
 # Link replace-malloc and the default allocator.
 USE_LIBS += [
     'memory',
 ]
 
+# The memory library defines this, so it's needed here too.
+DEFINES['IMPL_MFBT'] = True
+
 DISABLE_STL_WRAPPING = True
--- a/mfbt/Assertions.cpp
+++ b/mfbt/Assertions.cpp
@@ -1,17 +1,66 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/Types.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+
+#include <stdarg.h>
+
+MOZ_BEGIN_EXTERN_C
 
 /*
  * The crash reason is defined as a global variable here rather than in the
  * crash reporter itself to make it available to all code, even libraries like
  * JS that don't link with the crash reporter directly. This value will only
  * be consumed if the crash reporter is used by the target application.
  */
+MFBT_DATA const char* gMozCrashReason = nullptr;
 
-MOZ_BEGIN_EXTERN_C
-MOZ_EXPORT const char* gMozCrashReason = nullptr;
+#ifndef DEBUG
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
+MOZ_CrashOOL(int aLine, const char* aReason)
+#else
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
+MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason)
+#endif
+{
+#ifdef DEBUG
+  MOZ_ReportCrash(aReason, aFilename, aLine);
+#endif
+  MOZ_CRASH_ANNOTATE(aReason);
+  MOZ_REALLY_CRASH(aLine);
+}
+
+static char sPrintfCrashReason[sPrintfCrashReasonSize] = {};
+static mozilla::Atomic<bool> sCrashing(false);
+
+#ifndef DEBUG
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void
+MOZ_CrashPrintf(int aLine, const char* aFormat, ...)
+#else
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void
+MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...)
+#endif
+{
+  if (!sCrashing.compareExchange(false, true)) {
+    // In the unlikely event of a race condition, skip
+    // setting the crash reason and just crash safely.
+    MOZ_REALLY_CRASH(aLine);
+  }
+  va_list aArgs;
+  va_start(aArgs, aFormat);
+  int ret = vsnprintf(sPrintfCrashReason, sPrintfCrashReasonSize,
+                      aFormat, aArgs);
+  va_end(aArgs);
+  MOZ_RELEASE_ASSERT(ret >= 0 && size_t(ret) < sPrintfCrashReasonSize,
+    "Could not write the explanation string to the supplied buffer!");
+#ifdef DEBUG
+  MOZ_ReportCrash(sPrintfCrashReason, aFilename, aLine);
+#endif
+  MOZ_CRASH_ANNOTATE(sPrintfCrashReason);
+  MOZ_REALLY_CRASH(aLine);
+}
+
 MOZ_END_EXTERN_C
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -209,32 +209,32 @@ MOZ_ReportCrash(const char* aStr, const 
     */
 
 static MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void MOZ_NoReturn(int aLine)
 {
   *((volatile int*) NULL) = aLine;
   TerminateProcess(GetCurrentProcess(), 3);
 }
 
-#  define MOZ_REALLY_CRASH() \
+#  define MOZ_REALLY_CRASH(line) \
      do { \
        __debugbreak(); \
-       MOZ_NoReturn(__LINE__); \
+       MOZ_NoReturn(line); \
      } while (0)
 #else
 #  ifdef __cplusplus
-#    define MOZ_REALLY_CRASH() \
+#    define MOZ_REALLY_CRASH(line) \
        do { \
-         *((volatile int*) NULL) = __LINE__; \
+         *((volatile int*) NULL) = line; \
          ::abort(); \
        } while (0)
 #  else
-#    define MOZ_REALLY_CRASH() \
+#    define MOZ_REALLY_CRASH(line) \
        do { \
-         *((volatile int*) NULL) = __LINE__; \
+         *((volatile int*) NULL) = line; \
          abort(); \
        } while (0)
 #  endif
 #endif
 
 /*
  * MOZ_CRASH([explanation-string]) crashes the program, plain and simple, in a
  * Breakpad-compatible way, in both debug and release builds.
@@ -255,27 +255,79 @@ static MOZ_COLD MOZ_NORETURN MOZ_NEVER_I
  * print anything; this is because we want MOZ_CRASH to be 100% safe in release
  * builds, and it's hard to print to stderr safely when memory might have been
  * corrupted.
  */
 #ifndef DEBUG
 #  define MOZ_CRASH(...) \
      do { \
        MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
-       MOZ_REALLY_CRASH(); \
+       MOZ_REALLY_CRASH(__LINE__); \
      } while (0)
 #else
 #  define MOZ_CRASH(...) \
      do { \
        MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \
        MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
-       MOZ_REALLY_CRASH(); \
+       MOZ_REALLY_CRASH(__LINE__); \
      } while (0)
 #endif
 
+/*
+ * MOZ_CRASH_UNSAFE_OOL(explanation-string) can be used if the explanation
+ * string cannot be a string literal (but no other processing needs to be done
+ * on it). A regular MOZ_CRASH() is preferred wherever possible, as passing
+ * arbitrary strings from a potentially compromised process is not without risk.
+ * If the string being passed is the result of a printf-style function,
+ * consider using MOZ_CRASH_UNSAFE_PRINTF instead.
+ */
+#ifndef DEBUG
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
+MOZ_CrashOOL(int aLine, const char* aReason);
+#  define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__LINE__, reason)
+#else
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
+MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason);
+#  define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__FILE__, __LINE__, reason)
+#endif
+
+static const size_t sPrintfMaxArgs = 4;
+static const size_t sPrintfCrashReasonSize = 1024;
+
+#ifndef DEBUG
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void
+MOZ_CrashPrintf(int aLine, const char* aFormat, ...);
+#  define MOZ_CALL_CRASH_PRINTF(format, ...) \
+     MOZ_CrashPrintf(__LINE__, format, __VA_ARGS__)
+#else
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void
+MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...);
+#  define MOZ_CALL_CRASH_PRINTF(format, ...) \
+     MOZ_CrashPrintf(__FILE__, __LINE__, format, __VA_ARGS__)
+#endif
+
+/*
+ * MOZ_CRASH_UNSAFE_PRINTF(format, arg1 [, args]) can be used when more
+ * information is desired than a string literal can supply. The caller provides
+ * a printf-style format string, which must be a string literal and between
+ * 1 and 4 additional arguments. A regular MOZ_CRASH() is preferred wherever
+ * possible, as passing arbitrary strings to printf from a potentially
+ * compromised process is not without risk.
+ */
+#define MOZ_CRASH_UNSAFE_PRINTF(format, ...) \
+   do { \
+     MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+     static_assert( \
+       MOZ_PASTE_PREFIX_AND_ARG_COUNT(, __VA_ARGS__) <= sPrintfMaxArgs, \
+       "Only up to 4 additional arguments are allowed!"); \
+     static_assert(sizeof(format) <= sPrintfCrashReasonSize, \
+       "The supplied format string is too long!"); \
+     MOZ_CALL_CRASH_PRINTF("" format, __VA_ARGS__); \
+   } while (0)
+
 MOZ_END_EXTERN_C
 
 /*
  * MOZ_ASSERT(expr [, explanation-string]) asserts that |expr| must be truthy in
  * debug builds.  If it is, execution continues.  Otherwise, an error message
  * including the expression and the explanation-string (if provided) is printed,
  * an attempt is made to invoke any existing debugger, and execution halts.
  * MOZ_ASSERT is fatal: no recovery is possible.  Do not assert a condition
@@ -361,27 +413,27 @@ struct AssertionConditionType
 
 /* First the single-argument form. */
 #define MOZ_ASSERT_HELPER1(expr) \
   do { \
     MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
     if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \
       MOZ_REPORT_ASSERTION_FAILURE(#expr, __FILE__, __LINE__); \
       MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ")"); \
-      MOZ_REALLY_CRASH(); \
+      MOZ_REALLY_CRASH(__LINE__); \
     } \
   } while (0)
 /* Now the two-argument form. */
 #define MOZ_ASSERT_HELPER2(expr, explain) \
   do { \
     MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
     if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \
       MOZ_REPORT_ASSERTION_FAILURE(#expr " (" explain ")", __FILE__, __LINE__); \
       MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ") (" explain ")"); \
-      MOZ_REALLY_CRASH(); \
+      MOZ_REALLY_CRASH(__LINE__); \
     } \
   } while (0)
 
 #define MOZ_RELEASE_ASSERT_GLUE(a, b) a b
 #define MOZ_RELEASE_ASSERT(...) \
   MOZ_RELEASE_ASSERT_GLUE( \
     MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \
     (__VA_ARGS__))
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -772,16 +772,17 @@ pref("browser.contentHandlers.types.3.ty
 // 01187654321 can be found with 87654321
 pref("dom.phonenumber.substringmatching.BR", 8);
 pref("dom.phonenumber.substringmatching.CO", 10);
 pref("dom.phonenumber.substringmatching.VE", 7);
 
 // Enable hardware-accelerated Skia canvas
 pref("gfx.canvas.azure.backends", "skia");
 pref("gfx.canvas.azure.accelerated", true);
+pref("gfx.canvas.azure.accelerated.limit", 64);
 
 // See ua-update.json.in for the packaged UA override list
 pref("general.useragent.updates.enabled", true);
 pref("general.useragent.updates.url", "https://dynamicua.cdn.mozilla.net/0/%APP_ID%");
 pref("general.useragent.updates.interval", 604800); // 1 week
 pref("general.useragent.updates.retry", 86400); // 1 day
 
 // When true, phone number linkification is enabled.
--- a/mobile/android/base/java/org/mozilla/gecko/AccountsHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/AccountsHelper.java
@@ -84,16 +84,20 @@ public class AccountsHelper implements B
             Log.e(LOGTAG, "Profile is not allowed to modify accounts!  Ignoring event: " + event);
             if (callback != null) {
                 callback.sendError("Profile is not allowed to modify accounts!");
             }
             return;
         }
 
         if ("Accounts:CreateFirefoxAccountFromJSON".equals(event)) {
+            // As we are about to create a new account, let's ensure our in-memory accounts cache
+            // is empty so that there are no undesired side-effects.
+            AndroidFxAccount.invalidateCaches();
+
             AndroidFxAccount fxAccount = null;
             try {
                 final GeckoBundle json = message.getBundle("json");
                 final String email = json.getString("email");
                 final String uid = json.getString("uid");
                 final boolean verified = json.getBoolean("verified", false);
                 final byte[] unwrapkB = Utils.hex2Byte(json.getString("unwrapBKey"));
                 final byte[] sessionToken = Utils.hex2Byte(json.getString("sessionToken"));
@@ -145,16 +149,20 @@ public class AccountsHelper implements B
                     return;
                 }
             }
             if (callback != null) {
                 callback.sendSuccess(fxAccount != null);
             }
 
         } else if ("Accounts:UpdateFirefoxAccountFromJSON".equals(event)) {
+            // We might be significantly changing state of the account; let's ensure our in-memory
+            // accounts cache is empty so that there are no undesired side-effects.
+            AndroidFxAccount.invalidateCaches();
+
             final Account account = FirefoxAccounts.getFirefoxAccount(mContext);
             if (account == null) {
                 if (callback != null) {
                     callback.sendError("Could not update Firefox Account since none exists");
                 }
                 return;
             }
 
--- a/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
@@ -172,39 +172,35 @@ public final class CodecProxy {
         }
         try {
             Sample sample = processInput(bytes, info, cryptoInfo);
             if (sample == null) {
                 return false;
             }
             mRemote.queueInput(sample);
             sample.dispose();
-        } catch (RemoteException e) {
-            e.printStackTrace();
+        } catch (RemoteException | IOException e) {
             Log.e(LOGTAG, "fail to input sample: size=" + info.size +
                     ", pts=" + info.presentationTimeUs +
-                    ", flags=" + Integer.toHexString(info.flags));
+                    ", flags=" + Integer.toHexString(info.flags) +
+                    ", e=" + e);
             return false;
         }
 
         return true;
     }
 
-    private Sample processInput(ByteBuffer bytes, BufferInfo info, CryptoInfo cryptoInfo) throws RemoteException {
+    private Sample processInput(ByteBuffer bytes, BufferInfo info, CryptoInfo cryptoInfo)
+            throws RemoteException, IOException {
         if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
             mCallbacks.setEndOfInput(true);
             return Sample.EOS;
         } else {
             mCallbacks.setEndOfInput(false);
-            try {
-                return mRemote.dequeueInput(info.size).set(bytes, info, cryptoInfo);
-            } catch (IOException e) {
-                e.printStackTrace();
-                return null;
-            }
+            return mRemote.dequeueInput(info.size).set(bytes, info, cryptoInfo);
         }
     }
 
     @WrapForJNI
     public synchronized boolean flush() {
         if (mRemote == null) {
             Log.e(LOGTAG, "cannot flush an ended codec");
             return false;
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/receivers/FxAccountDeletedService.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/receivers/FxAccountDeletedService.java
@@ -33,16 +33,21 @@ public class FxAccountDeletedService ext
   public static final String LOG_TAG = FxAccountDeletedService.class.getSimpleName();
 
   public FxAccountDeletedService() {
     super(LOG_TAG);
   }
 
   @Override
   protected void onHandleIntent(final Intent intent) {
+    // We have an in-memory accounts cache which we use for a variety of tasks; it needs to be cleared.
+    // It should be fine to invalidate it before doing anything else, as the tasks below do not rely
+    // on this data.
+    AndroidFxAccount.invalidateCaches();
+
     // Intent can, in theory, be null. Bug 1025937.
     if (intent == null) {
       Logger.debug(LOG_TAG, "Short-circuiting on null intent.");
       return;
     }
 
     final Context context = this;
 
--- a/netwerk/base/nsFileStreams.cpp
+++ b/netwerk/base/nsFileStreams.cpp
@@ -248,19 +248,16 @@ nsresult
 nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                                uint32_t aCount, uint32_t* aResult)
 {
     // ReadSegments is not implemented because it would be inefficient when
     // the writer does not consume all data.  If you want to call ReadSegments,
     // wrap a BufferedInputStream around the file stream.  That will call
     // Read().
 
-    // If this is ever implemented you might need to modify
-    // nsPartialFileInputStream::ReadSegments
-
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
 nsFileStreamBase::IsNonBlocking(bool *aNonBlocking)
 {
     *aNonBlocking = false;
     return NS_OK;
@@ -683,234 +680,16 @@ nsFileInputStream::Deserialize(const Inp
 
 Maybe<uint64_t>
 nsFileInputStream::ExpectedSerializedLength()
 {
     return Nothing();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// nsPartialFileInputStream
-
-NS_IMPL_ADDREF_INHERITED(nsPartialFileInputStream, nsFileStreamBase)
-NS_IMPL_RELEASE_INHERITED(nsPartialFileInputStream, nsFileStreamBase)
-
-NS_IMPL_CLASSINFO(nsPartialFileInputStream, nullptr, nsIClassInfo::THREADSAFE,
-                  NS_PARTIALLOCALFILEINPUTSTREAM_CID)
-
-// Don't forward to nsFileInputStream as we don't want to QI to
-// nsIFileInputStream
-NS_INTERFACE_MAP_BEGIN(nsPartialFileInputStream)
-    NS_INTERFACE_MAP_ENTRY(nsIInputStream)
-    NS_INTERFACE_MAP_ENTRY(nsIPartialFileInputStream)
-    NS_INTERFACE_MAP_ENTRY(nsILineInputStream)
-    NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
-    NS_IMPL_QUERY_CLASSINFO(nsPartialFileInputStream)
-NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase)
-
-NS_IMPL_CI_INTERFACE_GETTER(nsPartialFileInputStream,
-                            nsIInputStream,
-                            nsIPartialFileInputStream,
-                            nsISeekableStream,
-                            nsILineInputStream)
-
-nsresult
-nsPartialFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID,
-                                 void **aResult)
-{
-    NS_ENSURE_NO_AGGREGATION(aOuter);
-
-    nsPartialFileInputStream* stream = new nsPartialFileInputStream();
-
-    NS_ADDREF(stream);
-    nsresult rv = stream->QueryInterface(aIID, aResult);
-    NS_RELEASE(stream);
-    return rv;
-}
-
-NS_IMETHODIMP
-nsPartialFileInputStream::Init(nsIFile* aFile, uint64_t aStart,
-                               uint64_t aLength, int32_t aIOFlags,
-                               int32_t aPerm, int32_t aBehaviorFlags)
-{
-    mStart = aStart;
-    mLength = aLength;
-    mPosition = 0;
-
-    nsresult rv = nsFileInputStream::Init(aFile, aIOFlags, aPerm,
-                                          aBehaviorFlags);
-
-    // aFile is a partial file, it must exist.
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mDeferredSeek = true;
-
-    return rv;
-}
-
-NS_IMETHODIMP
-nsPartialFileInputStream::Tell(int64_t *aResult)
-{
-    int64_t tell = 0;
-
-    nsresult rv = DoPendingSeek();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = nsFileInputStream::Tell(&tell);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-
-    *aResult = tell - mStart;
-    return rv;
-}
-
-NS_IMETHODIMP
-nsPartialFileInputStream::Available(uint64_t* aResult)
-{
-    uint64_t available = 0;
-
-    nsresult rv = DoPendingSeek();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = nsFileInputStream::Available(&available);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    *aResult = TruncateSize(available);
-    return rv;
-}
-
-NS_IMETHODIMP
-nsPartialFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
-{
-    nsresult rv = DoPendingSeek();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    uint32_t readsize = (uint32_t) TruncateSize(aCount);
-    if (readsize == 0 && mBehaviorFlags & CLOSE_ON_EOF) {
-        Close();
-        *aResult = 0;
-        return NS_OK;
-    }
-
-    rv = nsFileInputStream::Read(aBuf, readsize, aResult);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mPosition += readsize;
-    return rv;
-}
-
-NS_IMETHODIMP
-nsPartialFileInputStream::Seek(int32_t aWhence, int64_t aOffset)
-{
-    nsresult rv = DoPendingSeek();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    int64_t offset;
-    switch (aWhence) {
-        case NS_SEEK_SET:
-            offset = mStart + aOffset;
-            break;
-        case NS_SEEK_CUR:
-            offset = mStart + mPosition + aOffset;
-            break;
-        case NS_SEEK_END:
-            offset = mStart + mLength + aOffset;
-            break;
-        default:
-            return NS_ERROR_ILLEGAL_VALUE;
-    }
-
-    if (offset < (int64_t)mStart || offset > (int64_t)(mStart + mLength)) {
-        return NS_ERROR_INVALID_ARG;
-    }
-
-    rv = nsFileInputStream::Seek(NS_SEEK_SET, offset);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mPosition = offset - mStart;
-    return rv;
-}
-
-void
-nsPartialFileInputStream::Serialize(InputStreamParams& aParams,
-                                    FileDescriptorArray& aFileDescriptors)
-{
-    // Serialize the base class first.
-    InputStreamParams fileParams;
-    nsFileInputStream::Serialize(fileParams, aFileDescriptors);
-
-    PartialFileInputStreamParams params;
-
-    params.fileStreamParams() = fileParams.get_FileInputStreamParams();
-    params.begin() = mStart;
-    params.length() = mLength;
-
-    aParams = params;
-}
-
-bool
-nsPartialFileInputStream::Deserialize(
-                                    const InputStreamParams& aParams,
-                                    const FileDescriptorArray& aFileDescriptors)
-{
-    NS_ASSERTION(!mFD, "Already have a file descriptor?!");
-    NS_ASSERTION(!mStart, "Already have a start?!");
-    NS_ASSERTION(!mLength, "Already have a length?!");
-    NS_ASSERTION(!mPosition, "Already have a position?!");
-
-    if (aParams.type() != InputStreamParams::TPartialFileInputStreamParams) {
-        NS_WARNING("Received unknown parameters from the other process!");
-        return false;
-    }
-
-    const PartialFileInputStreamParams& params =
-        aParams.get_PartialFileInputStreamParams();
-
-    // Deserialize the base class first.
-    InputStreamParams fileParams(params.fileStreamParams());
-    if (!nsFileInputStream::Deserialize(fileParams, aFileDescriptors)) {
-        NS_WARNING("Base class deserialize failed!");
-        return false;
-    }
-
-    NS_ASSERTION(mFD, "Must have a file descriptor now!");
-
-    mStart = params.begin();
-    mLength = params.length();
-    mPosition = 0;
-
-    if (!mStart) {
-      return true;
-    }
-
-    // XXX This is so broken. Main thread IO alert.
-    return NS_SUCCEEDED(nsFileInputStream::Seek(NS_SEEK_SET, mStart));
-}
-
-Maybe<uint64_t>
-nsPartialFileInputStream::ExpectedSerializedLength()
-{
-    return Some(mLength);
-}
-
-
-nsresult
-nsPartialFileInputStream::DoPendingSeek()
-{
-    if (!mDeferredSeek) {
-       return NS_OK;
-    }
-
-    mDeferredSeek = false;
-
-    // This is the first time to open the file, don't clear mLinebuffer.
-    // mLineBuffer might be already initialized by ReadLine().
-    return nsFileInputStream::SeekInternal(NS_SEEK_SET, mStart, false);
-}
-////////////////////////////////////////////////////////////////////////////////
 // nsFileOutputStream
 
 NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream,
                             nsFileStreamBase,
                             nsIOutputStream,
                             nsIFileOutputStream)
 
 nsresult
--- a/netwerk/base/nsFileStreams.h
+++ b/netwerk/base/nsFileStreams.h
@@ -170,57 +170,16 @@ protected:
      * Internal, called to open a file.  Parameters are the same as their
      * Init() analogues.
      */
     nsresult Open(nsIFile* file, int32_t ioFlags, int32_t perm);
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
-class nsPartialFileInputStream : public nsFileInputStream,
-                                 public nsIPartialFileInputStream
-{
-public:
-    using nsFileInputStream::Init;
-    using nsFileInputStream::Read;
-    NS_DECL_ISUPPORTS_INHERITED
-    NS_DECL_NSIPARTIALFILEINPUTSTREAM
-    NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
-
-    nsPartialFileInputStream()
-      : mStart(0), mLength(0), mPosition(0), mDeferredSeek(false)
-    { }
-
-    NS_IMETHOD Tell(int64_t *aResult) override;
-    NS_IMETHOD Available(uint64_t *aResult) override;
-    NS_IMETHOD Read(char* aBuf, uint32_t aCount, uint32_t* aResult) override;
-    NS_IMETHOD Seek(int32_t aWhence, int64_t aOffset) override;
-
-    static nsresult
-    Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
-
-protected:
-    ~nsPartialFileInputStream()
-    { }
-
-    inline nsresult DoPendingSeek();
-
-private:
-    uint64_t TruncateSize(uint64_t aSize) {
-          return std::min<uint64_t>(mLength - mPosition, aSize);
-    }
-
-    uint64_t mStart;
-    uint64_t mLength;
-    uint64_t mPosition;
-    bool mDeferredSeek;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
 class nsFileOutputStream : public nsFileStreamBase,
                            public nsIFileOutputStream
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIFILEOUTPUTSTREAM
     NS_FORWARD_NSIOUTPUTSTREAM(nsFileStreamBase::)
 
--- a/netwerk/base/nsIFileStreams.idl
+++ b/netwerk/base/nsIFileStreams.idl
@@ -133,47 +133,16 @@ interface nsIFileOutputStream : nsIOutpu
      *       happen when this flag is not set would happen during the
      *       first write, and if the file is to be created, then it will not
      *       appear on the disk until the first write.
      */
     const long DEFER_OPEN = 1<<0;
 };
 
 /**
- * An input stream that allows you to read from a slice of a file.
- */
-[scriptable, uuid(3ce03a2f-97f7-4375-b6bb-1788a60cad3b)]
-interface nsIPartialFileInputStream : nsISupports
-{
-    /**
-     * Initialize with a file and new start/end positions. Both start and
-     * start+length must be smaller than the size of the file. Not doing so
-     * will lead to undefined behavior.
-     * You must initialize the stream, and only initialize it once, before it
-     * can be used.
-     * 
-     * @param file          file to read from
-     * @param start         start offset of slice to read. Must be smaller
-     *                      than the size of the file.
-     * @param length        length of slice to read. Must be small enough that
-     *                      start+length is smaller than the size of the file.
-     * @param ioFlags       file open flags listed in prio.h (see
-     *                      PR_Open documentation) or -1 to open the
-     *                      file in default mode (PR_RDONLY).
-     * @param perm          file mode bits listed in prio.h or -1 to
-     *                      use the default value (0)
-     * @param behaviorFlags flags specifying various behaviors of the class
-     *        (see enumerations in nsIFileInputStream)
-     */
-    void init(in nsIFile file, in unsigned long long start,
-              in unsigned long long length,
-              in long ioFlags, in long perm, in long behaviorFlags);
-};
-
-/**
  * A stream that allows you to read from a file or stream to a file.
  */
 [scriptable, uuid(82cf605a-8393-4550-83ab-43cd5578e006)]
 interface nsIFileStream : nsISupports
 {
     /**
      * @param file          file to read from or stream to
      * @param ioFlags       file open flags listed in prio.h (see
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -984,36 +984,16 @@ NS_ExtractCharsetFromContentType(const n
     return util->ExtractCharsetFromContentType(rawContentType,
                                                contentCharset,
                                                charsetStart,
                                                charsetEnd,
                                                hadCharset);
 }
 
 nsresult
-NS_NewPartialLocalFileInputStream(nsIInputStream **result,
-                                  nsIFile         *file,
-                                  uint64_t         offset,
-                                  uint64_t         length,
-                                  int32_t          ioFlags       /* = -1 */,
-                                  int32_t          perm          /* = -1 */,
-                                  int32_t          behaviorFlags /* = 0 */)
-{
-    nsresult rv;
-    nsCOMPtr<nsIPartialFileInputStream> in =
-        do_CreateInstance(NS_PARTIALLOCALFILEINPUTSTREAM_CONTRACTID, &rv);
-    if (NS_SUCCEEDED(rv)) {
-        rv = in->Init(file, offset, length, ioFlags, perm, behaviorFlags);
-        if (NS_SUCCEEDED(rv))
-            rv = CallQueryInterface(in, result);
-    }
-    return rv;
-}
-
-nsresult
 NS_NewAtomicFileOutputStream(nsIOutputStream **result,
                                 nsIFile       *file,
                                 int32_t        ioFlags       /* = -1 */,
                                 int32_t        perm          /* = -1 */,
                                 int32_t        behaviorFlags /* = 0 */)
 {
     nsresult rv;
     nsCOMPtr<nsIFileOutputStream> out =
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -474,24 +474,16 @@ nsresult NS_ExtractCharsetFromContentTyp
                                           int32_t          *charsetEnd);
 
 nsresult NS_NewLocalFileInputStream(nsIInputStream **result,
                                     nsIFile         *file,
                                     int32_t          ioFlags       = -1,
                                     int32_t          perm          = -1,
                                     int32_t          behaviorFlags = 0);
 
-nsresult NS_NewPartialLocalFileInputStream(nsIInputStream **result,
-                                           nsIFile         *file,
-                                           uint64_t         offset,
-                                           uint64_t         length,
-                                           int32_t          ioFlags       = -1,
-                                           int32_t          perm          = -1,
-                                           int32_t          behaviorFlags = 0);
-
 nsresult NS_NewLocalFileOutputStream(nsIOutputStream **result,
                                      nsIFile          *file,
                                      int32_t           ioFlags       = -1,
                                      int32_t           perm          = -1,
                                      int32_t           behaviorFlags = 0);
 
 // returns a file output stream which can be QI'ed to nsISafeOutputStream.
 nsresult NS_NewAtomicFileOutputStream(nsIOutputStream **result,
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -386,26 +386,16 @@
 #define NS_LOCALFILEOUTPUTSTREAM_CID                 \
 { /* c272fee0-c7e9-11d3-8cda-0060b0fc14a3 */         \
     0xc272fee0,                                      \
     0xc7e9,                                          \
     0x11d3,                                          \
     {0x8c, 0xda, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
 }
 
-#define NS_PARTIALLOCALFILEINPUTSTREAM_CONTRACTID \
-    "@mozilla.org/network/partial-file-input-stream;1"
-#define NS_PARTIALLOCALFILEINPUTSTREAM_CID           \
-{ /* 8738afd6-162a-418d-a99b-75b3a6b10a56 */         \
-    0x8738afd6,                                      \
-    0x162a,                                          \
-    0x418d,                                          \
-    {0xa9, 0x9b, 0x75, 0xb3, 0xa6, 0xb1, 0x0a, 0x56} \
-}
-
 #define NS_BUFFEREDINPUTSTREAM_CONTRACTID \
     "@mozilla.org/network/buffered-input-stream;1"
 #define NS_BUFFEREDINPUTSTREAM_CID                   \
 { /* 9226888e-da08-11d3-8cda-0060b0fc14a3 */         \
     0x9226888e,                                      \
     0xda08,                                          \
     0x11d3,                                          \
     {0x8c, 0xda, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -751,17 +751,16 @@ NS_DEFINE_NAMED_CID(NS_BACKGROUNDFILESAV
 NS_DEFINE_NAMED_CID(NS_BACKGROUNDFILESAVERSTREAMLISTENER_CID);
 NS_DEFINE_NAMED_CID(NS_SYNCSTREAMLISTENER_CID);
 NS_DEFINE_NAMED_CID(NS_REQUESTOBSERVERPROXY_CID);
 NS_DEFINE_NAMED_CID(NS_SIMPLESTREAMLISTENER_CID);
 NS_DEFINE_NAMED_CID(NS_STREAMLISTENERTEE_CID);
 NS_DEFINE_NAMED_CID(NS_LOADGROUP_CID);
 NS_DEFINE_NAMED_CID(NS_LOCALFILEINPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_LOCALFILEOUTPUTSTREAM_CID);
-NS_DEFINE_NAMED_CID(NS_PARTIALLOCALFILEINPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_ATOMICLOCALFILEOUTPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_SAFELOCALFILEOUTPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_LOCALFILESTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_INCREMENTALDOWNLOAD_CID);
 NS_DEFINE_NAMED_CID(NS_STDURLPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_NOAUTHURLPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_AUTHURLPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_STANDARDURL_CID);
@@ -904,17 +903,16 @@ static const mozilla::Module::CIDEntry k
       mozilla::net::BackgroundFileSaverStreamListenerConstructor },
     { &kNS_SYNCSTREAMLISTENER_CID, false, nullptr, nsSyncStreamListenerConstructor },
     { &kNS_REQUESTOBSERVERPROXY_CID, false, nullptr, nsRequestObserverProxyConstructor },
     { &kNS_SIMPLESTREAMLISTENER_CID, false, nullptr, nsSimpleStreamListenerConstructor },
     { &kNS_STREAMLISTENERTEE_CID, false, nullptr, nsStreamListenerTeeConstructor },
     { &kNS_LOADGROUP_CID, false, nullptr, nsLoadGroupConstructor },
     { &kNS_LOCALFILEINPUTSTREAM_CID, false, nullptr, nsFileInputStream::Create },
     { &kNS_LOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsFileOutputStream::Create },
-    { &kNS_PARTIALLOCALFILEINPUTSTREAM_CID, false, nullptr, nsPartialFileInputStream::Create },
     { &kNS_ATOMICLOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsAtomicFileOutputStreamConstructor },
     { &kNS_SAFELOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsSafeFileOutputStreamConstructor },
     { &kNS_LOCALFILESTREAM_CID, false, nullptr, nsFileStreamConstructor },
     { &kNS_INCREMENTALDOWNLOAD_CID, false, nullptr, net_NewIncrementalDownload },
     { &kNS_STDURLPARSER_CID, false, nullptr, nsStdURLParserConstructor },
     { &kNS_NOAUTHURLPARSER_CID, false, nullptr, nsNoAuthURLParserConstructor },
     { &kNS_AUTHURLPARSER_CID, false, nullptr, nsAuthURLParserConstructor },
     { &kNS_STANDARDURL_CID, false, nullptr, nsStandardURLConstructor },
@@ -1059,17 +1057,16 @@ static const mozilla::Module::ContractID
     { NS_BACKGROUNDFILESAVERSTREAMLISTENER_CONTRACTID, &kNS_BACKGROUNDFILESAVERSTREAMLISTENER_CID },
     { NS_SYNCSTREAMLISTENER_CONTRACTID, &kNS_SYNCSTREAMLISTENER_CID },
     { NS_REQUESTOBSERVERPROXY_CONTRACTID, &kNS_REQUESTOBSERVERPROXY_CID },
     { NS_SIMPLESTREAMLISTENER_CONTRACTID, &kNS_SIMPLESTREAMLISTENER_CID },
     { NS_STREAMLISTENERTEE_CONTRACTID, &kNS_STREAMLISTENERTEE_CID },
     { NS_LOADGROUP_CONTRACTID, &kNS_LOADGROUP_CID },
     { NS_LOCALFILEINPUTSTREAM_CONTRACTID, &kNS_LOCALFILEINPUTSTREAM_CID },
     { NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_LOCALFILEOUTPUTSTREAM_CID },
-    { NS_PARTIALLOCALFILEINPUTSTREAM_CONTRACTID, &kNS_PARTIALLOCALFILEINPUTSTREAM_CID },
     { NS_ATOMICLOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_ATOMICLOCALFILEOUTPUTSTREAM_CID },
     { NS_SAFELOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_SAFELOCALFILEOUTPUTSTREAM_CID },
     { NS_LOCALFILESTREAM_CONTRACTID, &kNS_LOCALFILESTREAM_CID },
     { NS_INCREMENTALDOWNLOAD_CONTRACTID, &kNS_INCREMENTALDOWNLOAD_CID },
     { NS_STDURLPARSER_CONTRACTID, &kNS_STDURLPARSER_CID },
     { NS_NOAUTHURLPARSER_CONTRACTID, &kNS_NOAUTHURLPARSER_CID },
     { NS_AUTHURLPARSER_CONTRACTID, &kNS_AUTHURLPARSER_CID },
     { NS_STANDARDURL_CONTRACTID, &kNS_STANDARDURL_CID },
--- a/netwerk/cache/nsDeleteDir.cpp
+++ b/netwerk/cache/nsDeleteDir.cpp
@@ -75,20 +75,20 @@ nsDeleteDir::Shutdown(bool finishDeletin
 
     if (!finishDeleting)
       gInstance->mStopDeleting = true;
 
     // remove all pending timers
     for (int32_t i = gInstance->mTimers.Count(); i > 0; i--) {
       nsCOMPtr<nsITimer> timer = gInstance->mTimers[i-1];
       gInstance->mTimers.RemoveObjectAt(i-1);
-      timer->Cancel();
 
       nsCOMArray<nsIFile> *arg;
       timer->GetClosure((reinterpret_cast<void**>(&arg)));
+      timer->Cancel();
 
       if (finishDeleting)
         dirsToRemove.AppendObjects(*arg);
 
       // delete argument passed to the timer
       delete arg;
     }
 
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -144,18 +144,17 @@ GetRequestingPrincipal(const FTPChannelC
 
 // Bug 1289001 - If GetValidatedOriginAttributes returns an error string, that
 // usually leads to a content crash with very little info about the cause.
 // We prefer to crash on the parent, so we get the reason in the crash report.
 static MOZ_COLD
 void CrashWithReason(const char * reason)
 {
 #ifndef RELEASE_OR_BETA
-  MOZ_CRASH_ANNOTATE(reason);
-  MOZ_REALLY_CRASH();
+  MOZ_CRASH_UNSAFE_OOL(reason);
 #endif
 }
 
 const char*
 NeckoParent::GetValidatedOriginAttributes(const SerializedLoadContext& aSerialized,
                                           PContentParent* aContent,
                                           nsIPrincipal* aRequestingPrincipal,
                                           OriginAttributes& aAttrs)
--- a/netwerk/protocol/http/ASpdySession.cpp
+++ b/netwerk/protocol/http/ASpdySession.cpp
@@ -27,31 +27,32 @@ namespace net {
 ASpdySession::ASpdySession()
 {
 }
 
 ASpdySession::~ASpdySession() = default;
 
 ASpdySession *
 ASpdySession::NewSpdySession(uint32_t version,
-                             nsISocketTransport *aTransport)
+                             nsISocketTransport *aTransport,
+                             bool attemptingEarlyData)
 {
   // This is a necko only interface, so we can enforce version
   // requests as a precondition
   MOZ_ASSERT(version == HTTP_VERSION_2,
              "Unsupported spdy version");
 
   // Don't do a runtime check of IsSpdyV?Enabled() here because pref value
   // may have changed since starting negotiation. The selected protocol comes
   // from a list provided in the SERVER HELLO filtered by our acceptable
   // versions, so there is no risk of the server ignoring our prefs.
 
   Telemetry::Accumulate(Telemetry::SPDY_VERSION2, version);
 
-  return new Http2Session(aTransport, version);
+  return new Http2Session(aTransport, version, attemptingEarlyData);
 }
 
 SpdyInformation::SpdyInformation()
 {
   // highest index of enabled protocols is the
   // most preferred for ALPN negotiaton
   Version[0] = HTTP_VERSION_2;
   VersionString[0] = NS_LITERAL_CSTRING("h2");
--- a/netwerk/protocol/http/ASpdySession.h
+++ b/netwerk/protocol/http/ASpdySession.h
@@ -23,18 +23,19 @@ public:
 
   virtual bool AddStream(nsAHttpTransaction *, int32_t,
                          bool, nsIInterfaceRequestor *) = 0;
   virtual bool CanReuse() = 0;
   virtual bool RoomForMoreStreams() = 0;
   virtual PRIntervalTime IdleTime() = 0;
   virtual uint32_t ReadTimeoutTick(PRIntervalTime now) = 0;
   virtual void DontReuse() = 0;
+  virtual uint32_t SpdyVersion() = 0;
 
-  static ASpdySession *NewSpdySession(uint32_t version, nsISocketTransport *);
+  static ASpdySession *NewSpdySession(uint32_t version, nsISocketTransport *, bool);
 
   // MaybeReTunnel() is called by the connection manager when it cannot
   // dispatch a tunneled transaction. That might be because the tunnels it
   // expects to see are dead (and we may or may not be able to make more),
   // or it might just need to wait longer for one of them to become free.
   //
   // return true if the session takes back ownership of the transaction from
   // the connection manager.
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -59,17 +59,17 @@ const uint8_t Http2Session::kMagicHello[
 };
 
 #define RETURN_SESSION_ERROR(o,x)  \
 do {                             \
   (o)->mGoAwayReason = (x);      \
   return NS_ERROR_ILLEGAL_VALUE; \
   } while (0)
 
-Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t version)
+Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t version, bool attemptingEarlyData)
   : mSocketTransport(aSocketTransport)
   , mSegmentReader(nullptr)
   , mSegmentWriter(nullptr)
   , mNextStreamID(3) // 1 is reserved for Updgrade handshakes
   , mLastPushedID(0)
   , mConcurrentHighWater(0)
   , mDownstreamState(BUFFERING_OPENING_SETTINGS)
   , mInputFrameBufferSize(kDefaultBufferSize)
@@ -107,16 +107,17 @@ Http2Session::Http2Session(nsISocketTran
   , mOutputQueueUsed(0)
   , mOutputQueueSent(0)
   , mLastReadEpoch(PR_IntervalNow())
   , mPingSentEpoch(0)
   , mPreviousUsed(false)
   , mWaitingForSettingsAck(false)
   , mGoAwayOnPush(false)
   , mUseH2Deps(false)
+  , mAttemptingEarlyData(attemptingEarlyData)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
   static uint64_t sSerial;
   mSerial = ++sSerial;
 
   LOG3(("Http2Session::Http2Session %p serial=0x%" PRIX64 "\n", this, mSerial));
 
@@ -496,16 +497,22 @@ Http2Session::SetWriteCallbacks()
 {
   if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed))
     mConnection->ResumeSend();
 }
 
 void
 Http2Session::RealignOutputQueue()
 {
+  if (mAttemptingEarlyData) {
+    // We can't realign right now, because we may need what's in there if early
+    // data fails.
+    return;
+  }
+
   mOutputQueueUsed -= mOutputQueueSent;
   memmove(mOutputQueueBuffer.get(),
           mOutputQueueBuffer.get() + mOutputQueueSent,
           mOutputQueueUsed);
   mOutputQueueSent = 0;
 }
 
 void
@@ -513,34 +520,46 @@ Http2Session::FlushOutputQueue()
 {
   if (!mSegmentReader || !mOutputQueueUsed)
     return;
 
   nsresult rv;
   uint32_t countRead;
   uint32_t avail = mOutputQueueUsed - mOutputQueueSent;
 
+  if (!avail && mAttemptingEarlyData) {
+    // This is kind of a hack, but there are cases where we'll have already
+    // written the data we want whlie doing early data, but we get called again
+    // with a reader, and we need to avoid calling the reader when there's
+    // nothing for it to read.
+    return;
+  }
+
   rv = mSegmentReader->
     OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
                   &countRead);
   LOG3(("Http2Session::FlushOutputQueue %p sz=%d rv=%" PRIx32 " actual=%d",
         this, avail, static_cast<uint32_t>(rv), countRead));
 
   // Dont worry about errors on write, we will pick this up as a read error too
   if (NS_FAILED(rv))
     return;
 
+  mOutputQueueSent += countRead;
+
+  if (mAttemptingEarlyData) {
+    return;
+  }
+
   if (countRead == avail) {
     mOutputQueueUsed = 0;
     mOutputQueueSent = 0;
     return;
   }
 
-  mOutputQueueSent += countRead;
-
   // If the output queue is close to filling up and we have sent out a good
   // chunk of data from the beginning then realign it.
 
   if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
       ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
     RealignOutputQueue();
   }
 }
@@ -550,16 +569,22 @@ Http2Session::DontReuse()
 {
   LOG3(("Http2Session::DontReuse %p\n", this));
   mShouldGoAway = true;
   if (!mStreamTransactionHash.Count())
     Close(NS_OK);
 }
 
 uint32_t
+Http2Session::SpdyVersion()
+{
+  return HTTP_VERSION_2;
+}
+
+uint32_t
 Http2Session::GetWriteQueueSize()
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
   return mReadyForWrite.GetSize();
 }
 
 void
@@ -2339,25 +2364,61 @@ Http2Session::ReadSegmentsAgain(nsAHttpS
   LOG3(("Http2Session::ReadSegments %p", this));
 
   Http2Stream *stream = static_cast<Http2Stream *>(mReadyForWrite.PopFront());
   if (!stream) {
     LOG3(("Http2Session %p could not identify a stream to write; suspending.",
           this));
     FlushOutputQueue();
     SetWriteCallbacks();
-    return NS_BASE_STREAM_WOULD_BLOCK;
+    if (mAttemptingEarlyData) {
+      // We can still try to send our preamble as early-data
+      *countRead = mOutputQueueUsed - mOutputQueueSent;
+    }
+    return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
+  }
+
+  uint32_t earlyDataUsed = 0;
+  if (mAttemptingEarlyData) {
+    if (!stream->Do0RTT()) {
+      LOG3(("Http2Session %p will not get early data from Http2Stream %p 0x%X",
+            this, stream, stream->StreamID()));
+      FlushOutputQueue();
+      SetWriteCallbacks();
+      // We can still send our preamble
+      *countRead = mOutputQueueUsed - mOutputQueueSent;
+      return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
+    }
+
+    if (!m0RTTStreams.Contains(stream->StreamID())) {
+      m0RTTStreams.AppendElement(stream->StreamID());
+    }
+
+    // Need to adjust this to only take as much as we can fit in with the
+    // preamble/settings/priority stuff
+    count -= (mOutputQueueUsed - mOutputQueueSent);
+
+    // Keep track of this to add it into countRead later, as
+    // stream->ReadSegments will likely change the value of mOutputQueueUsed.
+    earlyDataUsed = mOutputQueueUsed - mOutputQueueSent;
   }
 
   LOG3(("Http2Session %p will write from Http2Stream %p 0x%X "
         "block-input=%d block-output=%d\n", this, stream, stream->StreamID(),
         stream->RequestBlockedOnRead(), stream->BlockedOnRwin()));
 
   rv = stream->ReadSegments(this, count, countRead);
 
+  if (earlyDataUsed) {
+    // Do this here because countRead could get reset somewhere down the rabbit
+    // hole of stream->ReadSegments, and we want to make sure we return the
+    // proper value to our caller.
+    *countRead += earlyDataUsed;
+  }
+
   // Not every permutation of stream->ReadSegents produces data (and therefore
   // tries to flush the output queue) - SENDING_FIN_STREAM can be an example
   // of that. But we might still have old data buffered that would be good
   // to flush.
   FlushOutputQueue();
 
   // Allow new server reads - that might be data or control information
   // (e.g. window updates or http replies) that are responses to these writes
@@ -2899,16 +2960,68 @@ nsresult
 Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
                             uint32_t count, uint32_t *countWritten)
 {
   bool again = false;
   return WriteSegmentsAgain(writer, count, countWritten, &again);
 }
 
 nsresult
+Http2Session::Finish0RTT(bool aRestart, bool aAlpnChanged)
+{
+  MOZ_ASSERT(mAttemptingEarlyData);
+  LOG3(("Http2Session::Finish0RTT %p aRestart=%d aAlpnChanged=%d", this,
+        aRestart, aAlpnChanged));
+
+  for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
+    // Instead of passing (aRestart, aAlpnChanged) here, we use aAlpnChanged for
+    // both arguments because as long as the alpn token stayed the same, we can
+    // just reuse what we have in our buffer to send instead of having to have
+    // the transaction rewind and read it all over again. We only need to rewind
+    // the transaction if we're switching to a new protocol, because our buffer
+    // won't get used in that case.
+    Http2Stream *stream = mStreamIDHash.Get(m0RTTStreams[i]);
+    if (stream) {
+      stream->Finish0RTT(aAlpnChanged, aAlpnChanged);
+    }
+  }
+
+  if (aRestart) {
+    // 0RTT failed
+    if (aAlpnChanged) {
+      // This is a slightly more involved case - we need to get all our streams/
+      // transactions back in the queue so they can restart as http/1
+
+      // These must be set this way to ensure we gracefully restart all streams
+      mGoAwayID = 0;
+      mCleanShutdown = true;
+
+      // Close takes care of the rest of our work for us. The reason code here
+      // doesn't matter, as we aren't actually going to send a GOAWAY frame, but
+      // we use NS_ERROR_NET_RESET as it's closest to the truth.
+      Close(NS_ERROR_NET_RESET);
+    } else {
+      // This is the easy case - early data failed, but we're speaking h2, so
+      // we just need to rewind to the beginning of the preamble and try again.
+      mOutputQueueSent = 0;
+    }
+  } else {
+    // 0RTT succeeded
+    // Make sure we look for any incoming data in repsonse to our early data.
+    ResumeRecv();
+  }
+
+  mAttemptingEarlyData = false;
+  m0RTTStreams.Clear();
+  RealignOutputQueue();
+
+  return NS_OK;
+}
+
+nsresult
 Http2Session::ProcessConnectedPush(Http2Stream *pushConnectedStream,
                                    nsAHttpSegmentWriter * writer,
                                    uint32_t count, uint32_t *countWritten)
 {
   LOG3(("Http2Session::ProcessConnectedPush %p 0x%X\n",
         this, pushConnectedStream->StreamID()));
   mSegmentWriter = writer;
   nsresult rv = pushConnectedStream->WriteSegments(this, count, countWritten);
@@ -3106,17 +3219,19 @@ Http2Session::Close(nsresult aReason)
     goAwayReason = mGoAwayReason;
   } else if (NS_SUCCEEDED(aReason)) {
     goAwayReason = NO_HTTP_ERROR;
   } else if (aReason == NS_ERROR_ILLEGAL_VALUE) {
     goAwayReason = PROTOCOL_ERROR;
   } else {
     goAwayReason = INTERNAL_ERROR;
   }
-  GenerateGoAway(goAwayReason);
+  if (!mAttemptingEarlyData) {
+    GenerateGoAway(goAwayReason);
+  }
   mConnection = nullptr;
   mSegmentReader = nullptr;
   mSegmentWriter = nullptr;
 }
 
 nsHttpConnectionInfo *
 Http2Session::ConnectionInfo()
 {
@@ -3208,17 +3323,17 @@ Http2Session::OnReadSegment(const char *
   FlushOutputQueue();
 
   return NS_OK;
 }
 
 nsresult
 Http2Session::CommitToSegmentSize(uint32_t count, bool forceCommitment)
 {
-  if (mOutputQueueUsed)
+  if (mOutputQueueUsed && !mAttemptingEarlyData)
     FlushOutputQueue();
 
   // would there be enough room to buffer this if needed?
   if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
     return NS_OK;
 
   // if we are using part of our buffers already, try again later unless
   // forceCommitment is set.
@@ -3528,22 +3643,28 @@ Http2Session::ALPNCallback(nsISupports *
     }
   }
   return false;
 }
 
 nsresult
 Http2Session::ConfirmTLSProfile()
 {
-  if (mTLSProfileConfirmed)
+  if (mTLSProfileConfirmed) {
     return NS_OK;
+  }
 
   LOG3(("Http2Session::ConfirmTLSProfile %p mConnection=%p\n",
         this, mConnection.get()));
 
+  if (mAttemptingEarlyData) {
+    LOG3(("Http2Session::ConfirmTLSProfile %p temporarily passing due to early data\n", this));
+    return NS_OK;
+  }
+
   if (!gHttpHandler->EnforceHttp2TlsProfile()) {
     LOG3(("Http2Session::ConfirmTLSProfile %p passed due to configuration bypass\n", this));
     mTLSProfileConfirmed = true;
     return NS_OK;
   }
 
   if (!mConnection)
     return NS_ERROR_FAILURE;
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -38,22 +38,23 @@ class Http2Session final : public ASpdyS
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSAHTTPTRANSACTION
   NS_DECL_NSAHTTPCONNECTION(mConnection)
   NS_DECL_NSAHTTPSEGMENTREADER
   NS_DECL_NSAHTTPSEGMENTWRITER
 
- Http2Session(nsISocketTransport *, uint32_t version);
+ Http2Session(nsISocketTransport *, uint32_t version, bool attemptingEarlyData);
 
   bool AddStream(nsAHttpTransaction *, int32_t,
                  bool, nsIInterfaceRequestor *) override;
   bool CanReuse() override { return !mShouldGoAway && !mClosed; }
   bool RoomForMoreStreams() override;
+  uint32_t SpdyVersion() override;
 
   // When the connection is active this is called up to once every 1 second
   // return the interval (in seconds) that the connection next wants to
   // have this invoked. It might happen sooner depending on the needs of
   // other connections.
   uint32_t  ReadTimeoutTick(PRIntervalTime now) override;
 
   // Idle time represents time since "goodput".. e.g. a data or header frame
@@ -230,16 +231,18 @@ public:
 
   void SendPing() override;
   bool MaybeReTunnel(nsAHttpTransaction *) override;
   bool UseH2Deps() { return mUseH2Deps; }
 
   // overload of nsAHttpTransaction
   nsresult ReadSegmentsAgain(nsAHttpSegmentReader *, uint32_t, uint32_t *, bool *) override final;
   nsresult WriteSegmentsAgain(nsAHttpSegmentWriter *, uint32_t , uint32_t *, bool *) override final;
+  bool Do0RTT() override final { return true; }
+  nsresult Finish0RTT(bool aRestart, bool aAlpnChanged) override final;
 
 private:
 
   // These internal states do not correspond to the states of the HTTP/2 specification
   enum internalStateType {
     BUFFERING_OPENING_SETTINGS,
     BUFFERING_FRAME_HEADER,
     BUFFERING_CONTROL_FRAME,
@@ -490,16 +493,20 @@ private:
   // receive a PUSH_PROMISE, but we have to wait for the SETTINGS ACK before
   // we can actually tell the other end to go away. These help us keep track
   // of that state so we can behave appropriately.
   bool mWaitingForSettingsAck;
   bool mGoAwayOnPush;
 
   bool mUseH2Deps;
 
+  bool mAttemptingEarlyData;
+  // The ID(s) of the stream(s) that we are getting 0RTT data from.
+  nsTArray<uint32_t> m0RTTStreams;
+
 private:
 /// connect tunnels
   void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
   void CreateTunnel(nsHttpTransaction *, nsHttpConnectionInfo *, nsIInterfaceRequestor *);
   void RegisterTunnel(Http2Stream *);
   void UnRegisterTunnel(Http2Stream *);
   uint32_t FindTunnelCount(nsHttpConnectionInfo *);
   nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash;
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -65,16 +65,17 @@ Http2Stream::Http2Stream(nsAHttpTransact
   , mTxInlineFrameUsed(0)
   , mTxStreamFrameSize(0)
   , mRequestBodyLenRemaining(0)
   , mLocalUnacked(0)
   , mBlockedOnRwin(false)
   , mTotalSent(0)
   , mTotalRead(0)
   , mPushSource(nullptr)
+  , mAttempting0RTT(false)
   , mIsTunnel(false)
   , mPlainTextTunnel(false)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
   LOG3(("Http2Stream::Http2Stream %p", this));
 
   mServerReceiveWindow = session->GetServerInitialStreamWindow();
@@ -921,17 +922,19 @@ Http2Stream::TransmitFrame(const char *b
                "inconsistent stream commitment count");
 
     Http2Session::LogIO(mSession, this, "Writing from Transaction Buffer",
                          buf, transmittedCount);
 
     *countUsed += mTxStreamFrameSize;
   }
 
-  mSession->FlushOutputQueue();
+  if (!mAttempting0RTT) {
+    mSession->FlushOutputQueue();
+  }
 
   // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
   UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
 
   mTxInlineFrameUsed = 0;
   mTxStreamFrameSize = 0;
 
   return NS_OK;
@@ -1464,10 +1467,31 @@ void
 Http2Stream::MapStreamToHttpConnection()
 {
   RefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
   MOZ_ASSERT(qiTrans);
   qiTrans->MapStreamToHttpConnection(mSocketTransport,
                                      mTransaction->ConnectionInfo());
 }
 
+// -----------------------------------------------------------------------------
+// mirror nsAHttpTransaction
+// -----------------------------------------------------------------------------
+
+bool
+Http2Stream::Do0RTT()
+{
+  MOZ_ASSERT(mTransaction);
+  mAttempting0RTT = true;
+  return mTransaction->Do0RTT();
+}
+
+nsresult
+Http2Stream::Finish0RTT(bool aRestart, bool aAlpnChanged)
+{
+  MOZ_ASSERT(mTransaction);
+  mAttempting0RTT = false;
+  return mTransaction->Finish0RTT(aRestart, aAlpnChanged);
+}
+
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/Http2Stream.h
+++ b/netwerk/protocol/http/Http2Stream.h
@@ -149,16 +149,20 @@ public:
 
   static nsresult MakeOriginURL(const nsACString &origin,
                                 RefPtr<nsStandardURL> &url);
 
   static nsresult MakeOriginURL(const nsACString &scheme,
                                 const nsACString &origin,
                                 RefPtr<nsStandardURL> &url);
 
+  // Mirrors nsAHttpTransaction
+  bool Do0RTT();
+  nsresult Finish0RTT(bool aRestart, bool aAlpnIgnored);
+
 protected:
   static void CreatePushHashKey(const nsCString &scheme,
                                 const nsCString &hostHeader,
                                 uint64_t serial,
                                 const nsCSubstring &pathInfo,
                                 nsCString &outOrigin,
                                 nsCString &outKey);
 
@@ -323,16 +327,18 @@ private:
 
   // For Http2Push
   Http2PushedStream *mPushSource;
 
   // Used to store stream data when the transaction channel cannot keep up
   // and flow control has not yet kicked in.
   SimpleBuffer mSimpleBuffer;
 
+  bool mAttempting0RTT;
+
 /// connect tunnels
 public:
   bool IsTunnel() { return mIsTunnel; }
 private:
   void ClearTransactionsBlockedOnTunnel();
   void MapStreamToPlainText();
   void MapStreamToHttpConnection();
 
--- a/netwerk/protocol/http/nsAHttpTransaction.h
+++ b/netwerk/protocol/http/nsAHttpTransaction.h
@@ -211,18 +211,21 @@ public:
     }
     // This function will be called when a tls handshake has been finished and
     // we know whether early-data that was sent has been accepted or not, e.g.
     // do we need to restart a transaction. This will be called only if Do0RTT
     // returns true.
     // If aRestart parameter is true we need to restart the transaction,
     // otherwise the erly-data has been accepted and we can continue the
     // transaction.
+    // If aAlpnChanged is true (and we were assuming http/2), we'll need to take
+    // the transactions out of the session, rewind them all, and start them back
+    // over as http/1 transactions
     // The function will return success or failure of the transaction restart.
-    virtual nsresult Finish0RTT(bool aRestart) {
+    virtual nsresult Finish0RTT(bool aRestart, bool aAlpnChanged) {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpTransaction, NS_AHTTPTRANSACTION_IID)
 
 #define NS_DECL_NSAHTTPTRANSACTION \
     void SetConnection(nsAHttpConnection *) override; \
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -82,16 +82,17 @@ nsHttpConnection::nsHttpConnection()
     , mTransactionCaps(0)
     , mResponseTimeoutEnabled(false)
     , mTCPKeepaliveConfig(kTCPKeepaliveDisabled)
     , mForceSendPending(false)
     , m0RTTChecked(false)
     , mWaitingFor0RTTResponse(false)
     , mContentBytesWritten0RTT(0)
     , mEarlyDataNegotiated(false)
+    , mDid0RTTSpdy(false)
 {
     LOG(("Creating nsHttpConnection @%p\n", this));
 
     // the default timeout is for when this connection has not yet processed a
     // transaction
     static const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
     mIdleTimeout =
         (k5Sec < gHttpHandler->IdleTimeout()) ? k5Sec : gHttpHandler->IdleTimeout();
@@ -153,26 +154,123 @@ nsHttpConnection::Init(nsHttpConnectionI
     mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(callbacks, false);
 
     mSocketTransport->SetEventSink(this, nullptr);
     mSocketTransport->SetSecurityCallbacks(this);
 
     return NS_OK;
 }
 
+nsresult
+nsHttpConnection::TryTakeSubTransactions(nsTArray<RefPtr<nsAHttpTransaction> > &list)
+{
+    nsresult rv = mTransaction->TakeSubTransactions(list);
+
+    if (rv == NS_ERROR_ALREADY_OPENED) {
+        // Has the interface for TakeSubTransactions() changed?
+        LOG(("TakeSubTransactions somehow called after "
+             "nsAHttpTransaction began processing\n"));
+        MOZ_ASSERT(false,
+                   "TakeSubTransactions somehow called after "
+                   "nsAHttpTransaction began processing");
+        mTransaction->Close(NS_ERROR_ABORT);
+        return rv;
+    }
+
+    if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
+        // Has the interface for TakeSubTransactions() changed?
+        LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()"));
+        MOZ_ASSERT(false,
+                   "unexpected result from "
+                   "nsAHttpTransaction::TakeSubTransactions()");
+        mTransaction->Close(NS_ERROR_ABORT);
+        return rv;
+    }
+
+    return rv;
+}
+
+nsresult
+nsHttpConnection::MoveTransactionsToSpdy(nsresult status, nsTArray<RefPtr<nsAHttpTransaction> > &list)
+{
+    if (NS_FAILED(status)) { // includes NS_ERROR_NOT_IMPLEMENTED
+        MOZ_ASSERT(list.IsEmpty(), "sub transaction list not empty");
+
+        // This is ok - treat mTransaction as a single real request.
+        // Wrap the old http transaction into the new spdy session
+        // as the first stream.
+        LOG(("nsHttpConnection::MoveTransactionsToSpdy moves single transaction %p "
+             "into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
+        nsresult rv = AddTransaction(mTransaction, mPriority);
+        if (NS_FAILED(rv)) {
+            return rv;
+        }
+    } else {
+        int32_t count = list.Length();
+
+        LOG(("nsHttpConnection::MoveTransactionsToSpdy moving transaction list len=%d "
+             "into SpdySession %p\n", count, mSpdySession.get()));
+
+        if (!count) {
+            mTransaction->Close(NS_ERROR_ABORT);
+            return NS_ERROR_ABORT;
+        }
+
+        for (int32_t index = 0; index < count; ++index) {
+            nsresult rv = AddTransaction(list[index], mPriority);
+            if (NS_FAILED(rv)) {
+                return rv;
+            }
+        }
+    }
+
+    return NS_OK;
+}
+
+void
+nsHttpConnection::Start0RTTSpdy(uint8_t spdyVersion)
+{
+    LOG(("nsHttpConnection::Start0RTTSpdy [this=%p]", this));
+    mDid0RTTSpdy = true;
+    mUsingSpdyVersion = spdyVersion;
+    mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport,
+                                                true);
+
+    nsTArray<RefPtr<nsAHttpTransaction> > list;
+    nsresult rv = TryTakeSubTransactions(list);
+    if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
+        LOG(("nsHttpConnection::Start0RTTSpdy [this=%p] failed taking "
+             "subtransactions rv=%" PRIx32 , this, static_cast<uint32_t>(rv)));
+        return;
+    }
+
+    rv = MoveTransactionsToSpdy(rv, list);
+    if (NS_FAILED(rv)) {
+        LOG(("nsHttpConnection::Start0RTTSpdy [this=%p] failed moving "
+             "transactions rv=%" PRIx32 , this, static_cast<uint32_t>(rv)));
+        return;
+    }
+
+    mTransaction = mSpdySession;
+}
+
 void
 nsHttpConnection::StartSpdy(uint8_t spdyVersion)
 {
-    LOG(("nsHttpConnection::StartSpdy [this=%p]\n", this));
+    LOG(("nsHttpConnection::StartSpdy [this=%p, mDid0RTTSpdy=%d]\n", this, mDid0RTTSpdy));
 
-    MOZ_ASSERT(!mSpdySession);
+    MOZ_ASSERT(!mSpdySession || mDid0RTTSpdy);
 
     mUsingSpdyVersion = spdyVersion;
     mEverUsedSpdy = true;
-    mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport);
+
+    if (!mDid0RTTSpdy) {
+        mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport,
+                                                    false);
+    }
 
     if (!mReportedSpdy) {
         mReportedSpdy = true;
         gHttpHandler->ConnMgr()->ReportSpdyConnection(this, true);
     }
 
     // Setting the connection as reused allows some transactions that fail
     // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code
@@ -180,37 +278,23 @@ nsHttpConnection::StartSpdy(uint8_t spdy
     // a server goaway was generated).
     mIsReused = true;
 
     // If mTransaction is a pipeline object it might represent
     // several requests. If so, we need to unpack that and
     // pack them all into a new spdy session.
 
     nsTArray<RefPtr<nsAHttpTransaction> > list;
-    nsresult rv = mTransaction->TakeSubTransactions(list);
+    nsresult rv = NS_OK;
+    if (!mDid0RTTSpdy) {
+        rv = TryTakeSubTransactions(list);
 
-    if (rv == NS_ERROR_ALREADY_OPENED) {
-        // Has the interface for TakeSubTransactions() changed?
-        LOG(("TakeSubTransactions somehow called after "
-             "nsAHttpTransaction began processing\n"));
-        MOZ_ASSERT(false,
-                   "TakeSubTransactions somehow called after "
-                   "nsAHttpTransaction began processing");
-        mTransaction->Close(NS_ERROR_ABORT);
-        return;
-    }
-
-    if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
-        // Has the interface for TakeSubTransactions() changed?
-        LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()"));
-        MOZ_ASSERT(false,
-                   "unexpected result from "
-                   "nsAHttpTransaction::TakeSubTransactions()");
-        mTransaction->Close(NS_ERROR_ABORT);
-        return;
+        if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
+            return;
+        }
     }
 
     if (NeedSpdyTunnel()) {
         LOG3(("nsHttpConnection::StartSpdy %p Connecting To a HTTP/2 "
               "Proxy and Need Connect", this));
         MOZ_ASSERT(mProxyConnectStream);
 
         mProxyConnectStream = nullptr;
@@ -222,45 +306,21 @@ nsHttpConnection::StartSpdy(uint8_t spdy
     if (spdyProxy) {
         RefPtr<nsHttpConnectionInfo> wildCardProxyCi;
         mConnInfo->CreateWildCard(getter_AddRefs(wildCardProxyCi));
         gHttpHandler->ConnMgr()->MoveToWildCardConnEntry(mConnInfo,
                                                          wildCardProxyCi, this);
         mConnInfo = wildCardProxyCi;
     }
 
-    if (NS_FAILED(rv)) { // includes NS_ERROR_NOT_IMPLEMENTED
-        MOZ_ASSERT(list.IsEmpty(), "sub transaction list not empty");
-
-        // This is ok - treat mTransaction as a single real request.
-        // Wrap the old http transaction into the new spdy session
-        // as the first stream.
-        LOG(("nsHttpConnection::StartSpdy moves single transaction %p "
-             "into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
-        rv = AddTransaction(mTransaction, mPriority);
+    if (!mDid0RTTSpdy) {
+        rv = MoveTransactionsToSpdy(rv, list);
         if (NS_FAILED(rv)) {
             return;
         }
-    } else {
-        int32_t count = list.Length();
-
-        LOG(("nsHttpConnection::StartSpdy moving transaction list len=%d "
-             "into SpdySession %p\n", count, mSpdySession.get()));
-
-        if (!count) {
-            mTransaction->Close(NS_ERROR_ABORT);
-            return;
-        }
-
-        for (int32_t index = 0; index < count; ++index) {
-            rv = AddTransaction(list[index], mPriority);
-            if (NS_FAILED(rv)) {
-                return;
-            }
-        }
     }
 
     // Disable TCP Keepalives - use SPDY ping instead.
     rv = DisableTCPKeepalives();
     if (NS_FAILED(rv)) {
         LOG(("nsHttpConnection::StartSpdy [%p] DisableTCPKeepalives failed "
              "rv[0x%" PRIx32 "]", this, static_cast<uint32_t>(rv)));
     }
@@ -316,57 +376,63 @@ nsHttpConnection::EnsureNPNComplete(nsre
     rv = ssl->GetNegotiatedNPN(negotiatedNPN);
     if (!m0RTTChecked && (rv == NS_ERROR_NOT_CONNECTED) &&
         !mConnInfo->UsingProxy()) {
         // There is no ALPN info (yet!). We need to consider doing 0RTT. We
         // will do so if there is ALPN information from a previous session
         // (AlpnEarlySelection), we are using HTTP/1, and the request data can
         // be safely retried.
         m0RTTChecked = true;
-        nsAutoCString earlyNegotiatedNPN;
-        nsresult rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN);
+        nsresult rvEarlyAlpn = ssl->GetAlpnEarlySelection(mEarlyNegotiatedALPN);
         if (NS_FAILED(rvEarlyAlpn)) {
             // if ssl->DriveHandshake() has never been called the value
             // for AlpnEarlySelection is still not set. So call it here and
             // check again.
             LOG(("nsHttpConnection::EnsureNPNComplete %p - "
                  "early selected alpn not available, we will try one more time.",
                  this));
             // Let's do DriveHandshake again.
             rv = ssl->DriveHandshake();
             if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
                 goto npnComplete;
             }
 
             // Check NegotiatedNPN first.
             rv = ssl->GetNegotiatedNPN(negotiatedNPN);
             if (rv == NS_ERROR_NOT_CONNECTED) {
-                rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN);
+                rvEarlyAlpn = ssl->GetAlpnEarlySelection(mEarlyNegotiatedALPN);
             }
         }
 
         if (NS_FAILED(rvEarlyAlpn)) {
             LOG(("nsHttpConnection::EnsureNPNComplete %p - "
                  "early selected alpn not available", this));
             mEarlyDataNegotiated = false;
         } else {
             LOG(("nsHttpConnection::EnsureNPNComplete %p -"
-                 "early selected alpn: %s", this, earlyNegotiatedNPN.get()));
+                 "early selected alpn: %s", this, mEarlyNegotiatedALPN.get()));
             uint32_t infoIndex;
             const SpdyInformation *info = gHttpHandler->SpdyInfo();
-            // We are doing 0RTT only with Http/1 right now!
-            if (NS_FAILED(info->GetNPNIndex(earlyNegotiatedNPN, &infoIndex))) {
+            if (NS_FAILED(info->GetNPNIndex(mEarlyNegotiatedALPN, &infoIndex))) {
+                // This is the HTTP/1 case.
                 // Check if early-data is allowed for this transaction.
                 if (mTransaction->Do0RTT()) {
                     LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - We "
-                         "can do 0RTT!", this));
+                         "can do 0RTT (http/1)!", this));
                     mWaitingFor0RTTResponse = true;
                 }
-                mEarlyDataNegotiated = true;
+            } else {
+                // We have h2, we can at least 0-RTT the preamble and opening
+                // SETTINGS, etc, and maybe some of the first request
+                LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - Starting "
+                     "0RTT for h2!", this));
+                mWaitingFor0RTTResponse = true;
+                Start0RTTSpdy(info->Version[infoIndex]);
             }
+            mEarlyDataNegotiated = true;
         }
     }
 
     if (rv == NS_ERROR_NOT_CONNECTED) {
         if (mWaitingFor0RTTResponse) {
             aOut0RTTWriteHandshakeValue = mTransaction->ReadSegments(this,
                 nsIOService::gDefaultSegmentSize, &aOut0RTTBytesWritten);
             if (NS_FAILED(aOut0RTTWriteHandshakeValue) &&
@@ -386,75 +452,97 @@ nsHttpConnection::EnsureNPNComplete(nsre
         return false;
     }
 
     if (NS_SUCCEEDED(rv)) {
         LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'%s\n",
              this, mConnInfo->HashKey().get(), negotiatedNPN.get(),
              mTLSFilter ? " [Double Tunnel]" : ""));
 
-        bool ealyDataAccepted = false;
+        bool earlyDataAccepted = false;
         if (mWaitingFor0RTTResponse) {
             // Check if early data has been accepted.
-            rv = ssl->GetEarlyDataAccepted(&ealyDataAccepted);
+            rv = ssl->GetEarlyDataAccepted(&earlyDataAccepted);
             LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - early data "
-                 "that was sent during 0RTT %s been accepted.",
-                 this, ealyDataAccepted ? "has" : "has not"));
+                 "that was sent during 0RTT %s been accepted [rv=%" PRIx32 "].",
+                 this, earlyDataAccepted ? "has" : "has not", static_cast<uint32_t>(rv)));
 
             if (NS_FAILED(rv) ||
-                NS_FAILED(mTransaction->Finish0RTT(!ealyDataAccepted))) {
+                NS_FAILED(mTransaction->Finish0RTT(!earlyDataAccepted, negotiatedNPN != mEarlyNegotiatedALPN))) {
+                LOG(("nsHttpConection::EnsureNPNComplete [this=%p] closing transaction %p", this, mTransaction.get()));
                 mTransaction->Close(NS_ERROR_NET_RESET);
                 goto npnComplete;
             }
         }
 
         int16_t tlsVersion;
         ssl->GetSSLVersionUsed(&tlsVersion);
         // Send the 0RTT telemetry only for tls1.3
         if (tlsVersion > nsISSLSocketControl::TLS_VERSION_1_2) {
             Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_NEGOTIATED,
                 (!mEarlyDataNegotiated) ? TLS_EARLY_DATA_NOT_AVAILABLE
                     : ((mWaitingFor0RTTResponse) ? TLS_EARLY_DATA_AVAILABLE_AND_USED
                                                  : TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED));
             if (mWaitingFor0RTTResponse) {
                 Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_ACCEPTED,
-                                      ealyDataAccepted);
+                                      earlyDataAccepted);
             }
-            if (ealyDataAccepted) {
+            if (earlyDataAccepted) {
                 Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_BYTES_WRITTEN,
                                       mContentBytesWritten0RTT);
             }
         }
         mWaitingFor0RTTResponse = false;
 
-        if (!ealyDataAccepted) {
+        if (!earlyDataAccepted) {
+            LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] early data not accepted", this));
             uint32_t infoIndex;
             const SpdyInformation *info = gHttpHandler->SpdyInfo();
             if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) {
                 StartSpdy(info->Version[infoIndex]);
             }
         } else {
           LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %" PRId64 " bytes "
                "has been sent during 0RTT.", this, mContentBytesWritten0RTT));
           mContentBytesWritten = mContentBytesWritten0RTT;
+          if (mSpdySession) {
+              // We had already started 0RTT-spdy, now we need to fully set up
+              // spdy, since we know we're sticking with it.
+              LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - finishing "
+                   "StartSpdy for 0rtt spdy session %p", this, mSpdySession.get()));
+              StartSpdy(mSpdySession->SpdyVersion());
+          }
         }
 
         Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy());
     }
 
 npnComplete:
-    LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true"));
+    LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] setting complete to true", this));
     mNPNComplete = true;
     if (mWaitingFor0RTTResponse) {
+        // Didn't get 0RTT OK, back out of the "attempting 0RTT" state
         mWaitingFor0RTTResponse = false;
-        if (NS_FAILED(mTransaction->Finish0RTT(true))) {
+        LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] 0rtt failed", this));
+        if (NS_FAILED(mTransaction->Finish0RTT(true, negotiatedNPN != mEarlyNegotiatedALPN))) {
             mTransaction->Close(NS_ERROR_NET_RESET);
         }
         mContentBytesWritten0RTT = 0;
     }
+
+    if (mDid0RTTSpdy && negotiatedNPN != mEarlyNegotiatedALPN) {
+        // Reset the work done by Start0RTTSpdy
+        LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] resetting Start0RTTSpdy", this));
+        mUsingSpdyVersion = 0;
+        mTransaction = nullptr;
+        mSpdySession = nullptr;
+        // We have to reset this here, just in case we end up starting spdy again,
+        // so it can actually do everything it needs to do.
+        mDid0RTTSpdy = false;
+    }
     return true;
 }
 
 void
 nsHttpConnection::OnTunnelNudged(TLSFilterTransaction *trans)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     LOG(("nsHttpConnection::OnTunnelNudged %p\n", this));
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -243,16 +243,23 @@ private:
     // Makes certain the SSL handshake is complete and NPN negotiation
     // has had a chance to happen
     bool     EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue,
                                uint32_t &aOut0RTTBytesWritten);
     void     SetupSSL();
 
     // Start the Spdy transaction handler when NPN indicates spdy/*
     void     StartSpdy(uint8_t versionLevel);
+    // Like the above, but do the bare minimum to do 0RTT data, so we can back
+    // it out, if necessary
+    void     Start0RTTSpdy(uint8_t versionLevel);
+
+    // Helpers for Start*Spdy
+    nsresult TryTakeSubTransactions(nsTArray<RefPtr<nsAHttpTransaction> > &list);
+    nsresult MoveTransactionsToSpdy(nsresult status, nsTArray<RefPtr<nsAHttpTransaction> > &list);
 
     // Directly Add a transaction to an active connection for SPDY
     nsresult AddTransaction(nsAHttpTransaction *, int32_t);
 
     // Used to set TCP keepalives for fast detection of dead connections during
     // an initial period, and slower detection for long-lived connections.
     nsresult StartShortLivedTCPKeepalives();
     nsresult StartLongLivedTCPKeepalives();
@@ -365,14 +372,16 @@ private:
     bool                            mWaitingFor0RTTResponse; // We have are
                                                              // sending 0RTT
                                                              // data and we
                                                              // are waiting
                                                              // for the end of
                                                              // the handsake.
     int64_t                        mContentBytesWritten0RTT;
     bool                           mEarlyDataNegotiated; //Only used for telemetry
+    nsCString                      mEarlyNegotiatedALPN;
+    bool                           mDid0RTTSpdy;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpConnection_h__
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -2383,17 +2383,17 @@ nsHttpTransaction::Do0RTT()
    if (mRequestHead->IsSafeMethod() &&
        !mConnection->IsProxyConnectInProgress()) {
      m0RTTInProgress = true;
    }
    return m0RTTInProgress;
 }
 
 nsresult
-nsHttpTransaction::Finish0RTT(bool aRestart)
+nsHttpTransaction::Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */)
 {
     MOZ_ASSERT(m0RTTInProgress);
     m0RTTInProgress = false;
     if (aRestart) {
         // Reset request headers to be sent again.
         nsCOMPtr<nsISeekableStream> seekable =
             do_QueryInterface(mRequestStream);
         if (seekable) {
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -163,17 +163,17 @@ public:
     mozilla::TimeStamp GetConnectEnd();
     mozilla::TimeStamp GetRequestStart();
     mozilla::TimeStamp GetResponseStart();
     mozilla::TimeStamp GetResponseEnd();
 
     int64_t GetTransferSize() { return mTransferSize; }
 
     bool Do0RTT() override;
-    nsresult Finish0RTT(bool aRestart) override;
+    nsresult Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */) override;
 private:
     friend class DeleteHttpTransaction;
     virtual ~nsHttpTransaction();
 
     nsresult Restart();
     nsresult RestartInProgress();
     char    *LocateHttpStart(char *buf, uint32_t len,
                              bool aAllowPartialMatch);
--- a/netwerk/test/httpserver/test/head_utils.js
+++ b/netwerk/test/httpserver/test/head_utils.js
@@ -72,17 +72,17 @@ function fileContents(file)
  * without the trailing line separator.
  *
  * @param data : string
  *   a string consisting of lines of data separated by CRLFs
  * @returns Iterator
  *   an Iterator which returns each line from data in turn; note that this
  *   includes a final empty line if data ended with a CRLF
  */
-function LineIterator(data)
+function* LineIterator(data)
 {
   var start = 0, index = 0;
   do
   {
     index = data.indexOf("\r\n");
     if (index >= 0)
       yield data.substring(0, index);
     else
@@ -102,17 +102,17 @@ function LineIterator(data)
  * @param expectedLines : [string]
  *   an array of the expected lines of text
  * @throws string
  *   an error message if iter doesn't agree with expectedLines
  */
 function expectLines(iter, expectedLines)
 {
   var index = 0;
-  for (var line in iter)
+  for (var line of iter)
   {
     if (expectedLines.length == index)
       throw "Error: got more than " + expectedLines.length + " expected lines!";
 
     var expected = expectedLines[index++];
     if (expected !== line)
       throw "Error on line " + index + "!\n" +
             "  actual: '" + line + "',\n" +
@@ -150,19 +150,19 @@ function writeDetails(request, response)
  * point the body of the response will be returned next from the iterator.
  *
  * @param iter : Iterator
  *   an iterator over the CRLF-delimited lines in an HTTP response, currently
  *   just after the Request-Line
  */
 function skipHeaders(iter)
 {
-  var line = iter.next();
+  var line = iter.next().value;
   while (line !== "")
-    line = iter.next();
+    line = iter.next().value;
 }
 
 /**
  * Checks that the exception e (which may be an XPConnect-created exception
  * object or a raw nsresult number) is the given nsresult.
  *
  * @param e : Exception or nsresult
  *   the actual exception
--- a/netwerk/test/httpserver/test/test_host.js
+++ b/netwerk/test/httpserver/test/test_host.js
@@ -262,17 +262,17 @@ function checkPrimariesThrow(id)
 /**
  * Utility function to check for a 400 response.
  */
 function check400(data)
 {
   var iter = LineIterator(data);
 
   // Status-Line
-  var firstLine = iter.next();
+  var { value: firstLine } = iter.next();
   do_check_eq(firstLine.substring(0, HTTP_400_LEADER_LENGTH), HTTP_400_LEADER);
 }
 
 
 /***************
  * BEGIN TESTS *
  ***************/
 
@@ -291,17 +291,17 @@ function http10Request(request, response
 }
 data = "GET /http/1.0-request HTTP/1.0\r\n" +
        "\r\n";
 function check10(data)
 {
   var iter = LineIterator(data);
 
   // Status-Line
-  do_check_eq(iter.next(), "HTTP/1.0 200 TEST PASSED");
+  do_check_eq(iter.next().value, "HTTP/1.0 200 TEST PASSED");
 
   skipHeaders(iter);
 
   // Okay, next line must be the data we expected to be written
   var body =
     [
      "Method:  GET",
      "Path:    /http/1.0-request",
@@ -390,17 +390,17 @@ function http11goodHost(request, respons
 data = "GET /http/1.1-good-host HTTP/1.1\r\n" +
        "Host: localhost:4444\r\n" +
        "\r\n";
 function check11goodHost(data)
 {
   var iter = LineIterator(data);
 
   // Status-Line
-  do_check_eq(iter.next(), "HTTP/1.1 200 TEST PASSED");
+  do_check_eq(iter.next().value, "HTTP/1.1 200 TEST PASSED");
 
   skipHeaders(iter);
 
   // Okay, next line must be the data we expected to be written
   var body =
     [
      "Method:  GET",
      "Path:    /http/1.1-good-host",
@@ -427,17 +427,17 @@ function http11ipHost(request, response)
 data = "GET /http/1.1-ip-host HTTP/1.1\r\n" +
        "Host: 127.0.0.1:4444\r\n" +
        "\r\n";
 function check11ipHost(data)
 {
   var iter = LineIterator(data);
 
   // Status-Line
-  do_check_eq(iter.next(), "HTTP/1.1 200 TEST PASSED");
+  do_check_eq(iter.next().value, "HTTP/1.1 200 TEST PASSED");
 
   skipHeaders(iter);
 
   // Okay, next line must be the data we expected to be written
   var body =
     [
      "Method:  GET",
      "Path:    /http/1.1-ip-host",
@@ -512,17 +512,17 @@ tests.push(test);
 data = "GET /http/1.0-request HTTP/1.0\r\n" +
        "Host: not-localhost:4444\r\n" +
        "\r\n";
 function check10ip(data)
 {
   var iter = LineIterator(data);
 
   // Status-Line
-  do_check_eq(iter.next(), "HTTP/1.0 200 TEST PASSED");
+  do_check_eq(iter.next().value, "HTTP/1.0 200 TEST PASSED");
 
   skipHeaders(iter);
 
   // Okay, next line must be the data we expected to be written
   var body =
     [
      "Method:  GET",
      "Path:    /http/1.0-request",
@@ -549,17 +549,17 @@ function http11goodHostWackyPort(request
 data = "GET /http/1.1-good-host-wacky-port HTTP/1.1\r\n" +
        "Host: localhost\r\n" +
        "\r\n";
 function check11goodHostWackyPort(data)
 {
   var iter = LineIterator(data);
 
   // Status-Line
-  do_check_eq(iter.next(), "HTTP/1.1 200 TEST PASSED");
+  do_check_eq(iter.next().value, "HTTP/1.1 200 TEST PASSED");
 
   skipHeaders(iter);
 
   // Okay, next line must be the data we expected to be written
   var body =
     [
      "Method:  GET",
      "Path:    /http/1.1-good-host-wacky-port",
--- a/netwerk/test/httpserver/test/test_request_line_split_in_two_packets.js
+++ b/netwerk/test/httpserver/test/test_request_line_split_in_two_packets.js
@@ -60,17 +60,17 @@ for (var i = 0; i < str.length; i += 163
 function checkVeryLongRequestLine(data)
 {
   var iter = LineIterator(data);
 
   print("data length: " + data.length);
   print("iter object: " + iter);
 
   // Status-Line
-  do_check_eq(iter.next(), "HTTP/1.1 200 TEST PASSED");
+  do_check_eq(iter.next().value, "HTTP/1.1 200 TEST PASSED");
 
   skipHeaders(iter);
 
   // Okay, next line must be the data we expected to be written
   var body =
     [
      "Method:  GET",
      "Path:    /very-long-request-line",
@@ -107,17 +107,17 @@ for (var i = 0; i < str.length; i += 100
 function checkLotsOfLeadingBlankLines(data)
 {
   var iter = LineIterator(data);
 
   // Status-Line
   print("data length: " + data.length);
   print("iter object: " + iter);
 
-  do_check_eq(iter.next(), "HTTP/1.1 200 TEST PASSED");
+  do_check_eq(iter.next().value, "HTTP/1.1 200 TEST PASSED");
 
   skipHeaders(iter);
 
   // Okay, next line must be the data we expected to be written
   var body =
     [
      "Method:  GET",
      "Path:    /lots-of-leading-blank-lines",
--- a/netwerk/test/httpserver/test/test_seizepower.js
+++ b/netwerk/test/httpserver/test/test_seizepower.js
@@ -152,17 +152,17 @@ function checkRawData(data)
 {
   do_check_eq(data, "Raw data!");
 }
 
 var data1 = "GET /called-too-late HTTP/1.0\r\n" +
        "\r\n";
 function checkTooLate(data)
 {
-  do_check_eq(LineIterator(data).next(), "too-late passed");
+  do_check_eq(LineIterator(data).next().value, "too-late passed");
 }
 
 var data2 = "GET /exceptions HTTP/1.0\r\n" +
        "\r\n";
 function checkExceptions(data)
 {
   do_check_eq("exceptions test passed", data);
 }
@@ -173,10 +173,10 @@ function checkAsyncSeizure(data)
 {
   do_check_eq(data, "async seizure passed");
 }
 
 var data4 = "GET /seize-after-async HTTP/1.0\r\n" +
        "\r\n";
 function checkSeizeAfterAsync(data)
 {
-  do_check_eq(LineIterator(data).next(), "HTTP/1.0 200 async seizure pass");
+  do_check_eq(LineIterator(data).next().value, "HTTP/1.0 200 async seizure pass");
 }
--- a/netwerk/test/unit/test_backgroundfilesaver.js
+++ b/netwerk/test/unit/test_backgroundfilesaver.js
@@ -257,17 +257,17 @@ add_task(function test_setup()
   // Wait 10 minutes, that is half of the external xpcshell timeout.
   do_timeout(10 * 60 * 1000, function() {
     if (gStillRunning) {
       do_throw("Test timed out.");
     }
   })
 });
 
-add_task(function test_normal()
+add_task(function* test_normal()
 {
   // This test demonstrates the most basic use case.
   let destFile = getTempFile(TEST_FILE_NAME_1);
 
   // Create the object implementing the output stream.
   let saver = new BackgroundFileSaverOutputStream();
 
   // Set up callbacks for completion and target file name change.
@@ -291,17 +291,17 @@ add_task(function test_normal()
   // Only after we receive the completion notification, we can also be sure that
   // we've received the target file name change notification before it.
   do_check_true(receivedOnTargetChange);
 
   // Clean up.
   destFile.remove(false);
 });
 
-add_task(function test_combinations()
+add_task(function* test_combinations()
 {
   let initialFile = getTempFile(TEST_FILE_NAME_1);
   let renamedFile = getTempFile(TEST_FILE_NAME_2);
 
   // Keep track of the current file.
   let currentFile = null;
   function onTargetChange(aTarget) {
     currentFile = null;
@@ -398,17 +398,17 @@ add_task(function test_combinations()
         renamedFile.remove(false);
       }
     }
 
     do_print("Test case completed in " + (Date.now() - startTime) + " ms.");
   }
 });
 
-add_task(function test_setTarget_after_close_stream()
+add_task(function* test_setTarget_after_close_stream()
 {
   // This test checks the case where we close the output stream before we call
   // the setTarget method.  All the data should be buffered and written anyway.
   let destFile = getTempFile(TEST_FILE_NAME_1);
 
   // Test the case where the file does not already exists first, then the case
   // where the file already exists.
   for (let i = 0; i < 2; i++) {
@@ -432,17 +432,17 @@ add_task(function test_setTarget_after_c
     do_check_eq(EXPECTED_HASHES[TEST_DATA_SHORT.length],
                 toHex(saver.sha256Hash));
   }
 
   // Clean up.
   destFile.remove(false);
 });
 
-add_task(function test_setTarget_fast()
+add_task(function* test_setTarget_fast()
 {
   // This test checks a fast rename of the target file.
   let destFile1 = getTempFile(TEST_FILE_NAME_1);
   let destFile2 = getTempFile(TEST_FILE_NAME_2);
   let saver = new BackgroundFileSaverOutputStream();
   let completionPromise = promiseSaverComplete(saver);
 
   // Set the initial name after the stream is closed, then rename immediately.
@@ -455,17 +455,17 @@ add_task(function test_setTarget_fast()
   yield completionPromise;
 
   // Verify results and clean up.
   do_check_false(destFile1.exists());
   yield promiseVerifyContents(destFile2, TEST_DATA_SHORT);
   destFile2.remove(false);
 });
 
-add_task(function test_setTarget_multiple()
+add_task(function* test_setTarget_multiple()
 {
   // This test checks multiple renames of the target file.
   let destFile = getTempFile(TEST_FILE_NAME_1);
   let saver = new BackgroundFileSaverOutputStream();
   let completionPromise = promiseSaverComplete(saver);
 
   // Rename both before and after the stream is closed.
   saver.setTarget(getTempFile(TEST_FILE_NAME_2), false);
@@ -480,17 +480,17 @@ add_task(function test_setTarget_multipl
 
   // Verify results and clean up.
   do_check_false(getTempFile(TEST_FILE_NAME_2).exists());
   do_check_false(getTempFile(TEST_FILE_NAME_3).exists());
   yield promiseVerifyContents(destFile, TEST_DATA_SHORT);
   destFile.remove(false);
 });
 
-add_task(function test_enableAppend()
+add_task(function* test_enableAppend()
 {
   // This test checks append mode with hashing disabled.
   let destFile = getTempFile(TEST_FILE_NAME_1);
 
   // Test the case where the file does not already exists first, then the case
   // where the file already exists.
   for (let i = 0; i < 2; i++) {
     let saver = new BackgroundFileSaverOutputStream();
@@ -508,17 +508,17 @@ add_task(function test_enableAppend()
                                    : TEST_DATA_LONG + TEST_DATA_LONG);
     yield promiseVerifyContents(destFile, expectedContents);
   }
 
   // Clean up.
   destFile.remove(false);
 });
 
-add_task(function test_enableAppend_setTarget_fast()
+add_task(function* test_enableAppend_setTarget_fast()
 {
   // This test checks a fast rename of the target file in append mode.
   let destFile1 = getTempFile(TEST_FILE_NAME_1);
   let destFile2 = getTempFile(TEST_FILE_NAME_2);
 
   // Test the case where the file does not already exists first, then the case
   // where the file already exists.
   for (let i = 0; i < 2; i++) {
@@ -545,17 +545,17 @@ add_task(function test_enableAppend_setT
                                    : TEST_DATA_SHORT + TEST_DATA_SHORT);
     yield promiseVerifyContents(secondFile, expectedContents);
   }
 
   // Clean up.
   destFile1.remove(false);
 });
 
-add_task(function test_enableAppend_hash()
+add_task(function* test_enableAppend_hash()
 {
   // This test checks append mode, also verifying that the computed hash
   // includes the contents of the existing data.
   let destFile = getTempFile(TEST_FILE_NAME_1);
 
   // Test the case where the file does not already exists first, then the case
   // where the file already exists.
   for (let i = 0; i < 2; i++) {
@@ -577,30 +577,30 @@ add_task(function test_enableAppend_hash
     do_check_eq(EXPECTED_HASHES[expectedContents.length],
                 toHex(saver.sha256Hash));
   }
 
   // Clean up.
   destFile.remove(false);
 });
 
-add_task(function test_finish_only()
+add_task(function* test_finish_only()
 {
   // This test checks creating the object and doing nothing.
   let destFile = getTempFile(TEST_FILE_NAME_1);
   let saver = new BackgroundFileSaverOutputStream();
   function onTargetChange(aTarget) {
     do_throw("Should not receive the onTargetChange notification.");
   }
   let completionPromise = promiseSaverComplete(saver, onTargetChange);
   saver.finish(Cr.NS_OK);
   yield completionPromise;
 });
 
-add_task(function test_empty()
+add_task(function* test_empty()
 {
   // This test checks we still create an empty file when no data is fed.
   let destFile = getTempFile(TEST_FILE_NAME_1);
 
   let saver = new BackgroundFileSaverOutputStream();
   let completionPromise = promiseSaverComplete(saver);
 
   saver.setTarget(destFile, false);
@@ -612,17 +612,17 @@ add_task(function test_empty()
   // Verify results.
   do_check_true(destFile.exists());
   do_check_eq(destFile.fileSize, 0);
 
   // Clean up.
   destFile.remove(false);
 });
 
-add_task(function test_empty_hash()
+add_task(function* test_empty_hash()
 {
   // This test checks the hash of an empty file, both in normal and append mode.
   let destFile = getTempFile(TEST_FILE_NAME_1);
 
   // Test normal mode first, then append mode.
   for (let i = 0; i < 2; i++) {
     let saver = new BackgroundFileSaverOutputStream();
     if (i == 1) {
@@ -641,17 +641,17 @@ add_task(function test_empty_hash()
     do_check_eq(destFile.fileSize, 0);
     do_check_eq(EXPECTED_HASHES[0], toHex(saver.sha256Hash));
   }
 
   // Clean up.
   destFile.remove(false);
 });
 
-add_task(function test_invalid_hash()
+add_task(function* test_invalid_hash()
 {
   let saver = new BackgroundFileSaverStreamListener();
   let completionPromise = promiseSaverComplete(saver);
   // We shouldn't be able to get the hash if hashing hasn't been enabled
   try {
     let hash = saver.sha256Hash;
     do_throw("Shouldn't be able to get hash if hashing not enabled");
   } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { }
@@ -671,17 +671,17 @@ add_task(function test_invalid_hash()
   // Wait for completion so that the worker thread finishes dealing with the
   // target file. We expect it to fail.
   try {
     yield completionPromise;
     do_throw("completionPromise should throw");
   } catch (ex if ex.result == Cr.NS_ERROR_FAILURE) { }
 });
 
-add_task(function test_signature()
+add_task(function* test_signature()
 {
   // Check that we get a signature if the saver is finished.
   let destFile = getTempFile(TEST_FILE_NAME_1);
 
   let saver = new BackgroundFileSaverOutputStream();
   let completionPromise = promiseSaverComplete(saver);
 
   try {
@@ -699,17 +699,17 @@ add_task(function test_signature()
 
   // signatureInfo is an empty nsIArray
   do_check_eq(0, saver.signatureInfo.length);
 
   // Clean up.
   destFile.remove(false);
 });
 
-add_task(function test_signature_not_enabled()
+add_task(function* test_signature_not_enabled()
 {
   // Check that we get a signature if the saver is finished on Windows.
   let destFile = getTempFile(TEST_FILE_NAME_1);
 
   let saver = new BackgroundFileSaverOutputStream();
   let completionPromise = promiseSaverComplete(saver);
   saver.setTarget(destFile, false);
   yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
--- a/netwerk/test/unit/test_bug826063.js
+++ b/netwerk/test/unit/test_bug826063.js
@@ -20,17 +20,17 @@ function LoadContext(usePrivateBrowsing)
   this.usePrivateBrowsing = usePrivateBrowsing;
 }
 LoadContext.prototype = {
   originAttributes: {},
   QueryInterface: XPCOMUtils.generateQI([Ci.nsILoadContext, Ci.nsIInterfaceRequestor]),
   getInterface: XPCOMUtils.generateQI([Ci.nsILoadContext])
 };
 
-function getChannels() {
+function* getChannels() {
   for (let u of URIs) {
     yield NetUtil.newChannel({
       uri: u,
       loadUsingSystemPrincipal: true
     });
   }
 }
 
--- a/netwerk/test/unit/test_cache_jar.js
+++ b/netwerk/test/unit/test_cache_jar.js
@@ -42,85 +42,80 @@ var thirdTests = [
   [0, false, 0, 0], [0, true, 0, 0], [1, false, 0, 1], [1, true, 0, 1],
   [0, false, 1, 0], [0, true, 1, 0], [1, false, 1, 0], [1, true, 1, 0]
 ];
 var fourthTests = [
   [0, false, 0, 0], [0, true, 0, 0], [1, false, 0, 0], [1, true, 0, 0],
   [0, false, 1, 1], [0, true, 1, 0], [1, false, 1, 0], [1, true, 1, 0]
 ];
 
-function run_all_tests() {
+async function run_all_tests() {
   for (let test of firstTests) {
     handlers_called = 0;
-    var chan = makeChan(URL, test[0], test[1], test[2]);
-    chan.asyncOpen2(new ChannelListener(doneFirstLoad, test[3]));
-    yield undefined;
+    await test_channel(...test);
   }
 
   // We can't easily cause webapp data to be cleared from the child process, so skip
   // the rest of these tests.
   let procType = Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime).processType;
   if (procType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT)
     return;
 
   let attrs_inBrowser = JSON.stringify({ appId:1, inIsolatedMozBrowser:true });
   let attrs_notInBrowser = JSON.stringify({ appId:1 });
 
   Services.obs.notifyObservers(null, "clear-origin-attributes-data", attrs_inBrowser);
 
   for (let test of secondTests) {
     handlers_called = 0;
-    var chan = makeChan(URL, test[0], test[1], test[2]);
-    chan.asyncOpen2(new ChannelListener(doneFirstLoad, test[3]));
-    yield undefined;
+    await test_channel(...test);
   }
 
   Services.obs.notifyObservers(null, "clear-origin-attributes-data", attrs_notInBrowser);
   Services.obs.notifyObservers(null, "clear-origin-attributes-data", attrs_inBrowser);
 
   for (let test of thirdTests) {
     handlers_called = 0;
-    var chan = makeChan(URL, test[0], test[1], test[2]);
-    chan.asyncOpen2(new ChannelListener(doneFirstLoad, test[3]));
-    yield undefined;
+    await test_channel(...test);
   }
 
   let attrs_userContextId = JSON.stringify({ userContextId: 1 });
   Services.obs.notifyObservers(null, "clear-origin-attributes-data", attrs_userContextId);
 
   for (let test of fourthTests) {
     handlers_called = 0;
-    var chan = makeChan(URL, test[0], test[1], test[2]);
-    chan.asyncOpen2(new ChannelListener(doneFirstLoad, test[3]));
-    yield undefined;
+    await test_channel(...test);
   }
 }
 
-var gTests;
 function run_test() {
   do_get_profile();
   if (!newCacheBackEndUsed()) {
     do_check_true(true, "This test checks only cache2 specific behavior.");
     return;
   }
   do_test_pending();
   httpserv = new HttpServer();
   httpserv.registerPathHandler("/cached", cached_handler);
   httpserv.start(-1);
-  gTests = run_all_tests();
-  gTests.next();
+  run_all_tests().then(() => {
+    do_test_finished();
+  });
 }
 
-function doneFirstLoad(req, buffer, expected) {
+function test_channel(appId, inIsolatedMozBrowser, userContextId, expected) {
+  return new Promise(resolve => {
+    var chan = makeChan(URL, appId, inIsolatedMozBrowser, userContextId);
+    chan.asyncOpen2(new ChannelListener(doneFirstLoad.bind(null, resolve), expected));
+  });
+}
+
+function doneFirstLoad(resolve, req, buffer, expected) {
   // Load it again, make sure it hits the cache
   var oa = req.loadInfo.originAttributes;
   var chan = makeChan(URL, oa.appId, oa.isInIsolatedMozBrowserElement, oa.userContextId);
-  chan.asyncOpen2(new ChannelListener(doneSecondLoad, expected));
+  chan.asyncOpen2(new ChannelListener(doneSecondLoad.bind(null, resolve), expected));
 }
 
-function doneSecondLoad(req, buffer, expected) {
+function doneSecondLoad(resolve, req, buffer, expected) {
   do_check_eq(handlers_called, expected);
-  try {
-    gTests.next();
-  } catch (x) {
-    do_test_finished();
-  }
+  resolve();
 }
deleted file mode 100644
--- a/netwerk/test/unit/test_file_partial_inputstream.js
+++ /dev/null
@@ -1,515 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// Test nsIPartialFileInputStream
-// NOTE! These tests often use do_check_true(a == b) rather than
-//       do_check_eq(a, b) to avoid outputting characters which confuse
-//       the console
-
-"use strict";
-
-var CC = Components.Constructor;
-const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
-                             "nsIBinaryInputStream",
-                             "setInputStream");
-const PR_RDONLY = 0x1;  // see prio.h
-
-// We need the profile directory so the test harness will clean up our test
-// files.
-do_get_profile();
-
-var binary_test_file_name = "data/image.png";
-var text_test_file_name = "test_file_partial_inputstream.js";
-// This is a global variable since if it's passed as an argument stack traces
-// become unreadable.
-var test_file_data;
-
-function run_test()
-{
-  // Binary tests
-  let binaryFile = do_get_file(binary_test_file_name);
-  let size = binaryFile.fileSize;
-  // Want to make sure we're working with a large enough file
-  dump("**** binary file size is: " + size + " ****\n");
-  do_check_true(size > 65536);
-
-  let binaryStream = new BinaryInputStream(new_file_input_stream(binaryFile));
-  test_file_data = "";
-  let avail = 0;
-  while ((avail = binaryStream.available()) > 0) {
-    test_file_data += binaryStream.readBytes(avail);
-  }
-  do_check_eq(test_file_data.length, size);
-  binaryStream.close();
-
-  test_binary_portion(0, 10);
-  test_binary_portion(0, 20000);
-  test_binary_portion(0, size);
-  test_binary_portion(20000, 10);
-  test_binary_portion(20000, 20000);
-  test_binary_portion(20000, size-20000);
-  test_binary_portion(size-10, 10);
-  test_binary_portion(size-20000, 20000);
-  test_binary_portion(0, 0);
-  test_binary_portion(20000, 0);
-  test_binary_portion(size-1, 1);
-
-
-  // Text-file tests
-  let textFile = do_get_file(binary_test_file_name);
-  size = textFile.fileSize;
-  // Want to make sure we're working with a large enough file
-  dump("**** text file size is: " + size + " ****\n");
-  do_check_true(size > 7000);
-
-  let textStream = new BinaryInputStream(new_file_input_stream(textFile));
-  test_file_data = "";
-  while ((avail = textStream.available()) > 0)
-    test_file_data += textStream.readBytes(avail);
-  do_check_eq(test_file_data.length, size);
-  textStream.close();
-
-  test_text_portion(0, 100);
-  test_text_portion(0, size);
-  test_text_portion(5000, 1000);
-  test_text_portion(size-10, 10);
-  test_text_portion(size-5000, 5000);
-  test_text_portion(10, 0);
-  test_text_portion(size-1, 1);
-
-  // Test auto-closing files
-  // Test behavior when *not* autoclosing
-  let tempFile = create_temp_file("01234567890123456789");
-  let tempInputStream = new_partial_file_input_stream(tempFile, 5, 10);
-  tempInputStream.QueryInterface(Ci.nsILineInputStream);
-  do_check_eq(read_line_stream(tempInputStream)[1], "5678901234");
-  try {
-    // This fails on some platforms
-    tempFile.remove(false);
-  }
-  catch (ex) {
-  }
-  tempInputStream.QueryInterface(Ci.nsISeekableStream);
-  tempInputStream.seek(SET, 1);
-  do_check_eq(read_line_stream(tempInputStream)[1], "678901234");
-
-  // Test removing the file when autoclosing
-  tempFile = create_temp_file("01234567890123456789");
-  tempInputStream = new_partial_file_input_stream(tempFile, 5, 10,
-                                                  Ci.nsIFileInputStream.CLOSE_ON_EOF |
-                                                  Ci.nsIFileInputStream.REOPEN_ON_REWIND);
-  tempInputStream.QueryInterface(Ci.nsILineInputStream);
-  do_check_eq(read_line_stream(tempInputStream)[1], "5678901234");
-  tempFile.remove(false);
-  tempInputStream.QueryInterface(Ci.nsISeekableStream);
-  try {
-    // The seek should reopen the file, which should fail.
-    tempInputStream.seek(SET, 1);
-    do_check_true(false);
-  }
-  catch (ex) {
-  }
-
-  // Test editing the file when autoclosing
-  tempFile = create_temp_file("01234567890123456789");
-  tempInputStream = new_partial_file_input_stream(tempFile, 5, 10,
-                                                  Ci.nsIFileInputStream.CLOSE_ON_EOF |
-                                                  Ci.nsIFileInputStream.REOPEN_ON_REWIND);
-  tempInputStream.QueryInterface(Ci.nsILineInputStream);
-  do_check_eq(read_line_stream(tempInputStream)[1], "5678901234");
-  let ostream = Cc["@mozilla.org/network/file-output-stream;1"].
-                createInstance(Ci.nsIFileOutputStream);
-  ostream.init(tempFile, 0x02 | 0x08 | 0x20, // write, create, truncate
-               0o666, 0);
-  let newData = "abcdefghijklmnopqrstuvwxyz";
-  ostream.write(newData, newData.length);
-  ostream.close();
-  tempInputStream.QueryInterface(Ci.nsISeekableStream);
-  tempInputStream.seek(SET, 1);
-  do_check_eq(read_line_stream(tempInputStream)[1], newData.substr(6,9));
-
-  // Test auto-delete and auto-close together
-  tempFile = create_temp_file("01234567890123456789");
-  tempInputStream = new_partial_file_input_stream(tempFile, 5, 10,
-                                                  Ci.nsIFileInputStream.CLOSE_ON_EOF |
-                                                  Ci.nsIFileInputStream.DELETE_ON_CLOSE);
-  tempInputStream.QueryInterface(Ci.nsILineInputStream);
-  do_check_eq(read_line_stream(tempInputStream)[1], "5678901234");
-  do_check_false(tempFile.exists());
-}
-
-function test_binary_portion(start, length) {
-  let subFile = create_temp_file(test_file_data.substr(start, length));
-
-  let streamTests = [
-    test_4k_read,
-    test_max_read,
-    test_seek,
-    test_seek_then_read,
-  ];
-
-  for (var test of streamTests) {
-    let fileStream = new_file_input_stream(subFile);
-    let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name),
-                                                      start, length);
-    test(fileStream, partialStream, length);
-    fileStream.close();
-    partialStream.close();
-  }
-}
-
-function test_4k_read(fileStreamA, fileStreamB) {
-  fileStreamA.QueryInterface(Ci.nsISeekableStream);
-  fileStreamB.QueryInterface(Ci.nsISeekableStream);
-  let streamA = new BinaryInputStream(fileStreamA);
-  let streamB = new BinaryInputStream(fileStreamB);
-
-  while(1) {
-    do_check_eq(fileStreamA.tell(), fileStreamB.tell());
-
-    let availA = streamA.available();
-    let availB = streamB.available();
-    do_check_eq(availA, availB);
-    if (availA == 0)
-      return;
-
-    let readSize = availA > 4096 ? 4096 : availA;
-
-    do_check_true(streamA.readBytes(readSize) ==
-                  streamB.readBytes(readSize));
-  }
-}
-
-function test_max_read(fileStreamA, fileStreamB) {
-  fileStreamA.QueryInterface(Ci.nsISeekableStream);
-  fileStreamB.QueryInterface(Ci.nsISeekableStream);
-  let streamA = new BinaryInputStream(fileStreamA);
-  let streamB = new BinaryInputStream(fileStreamB);
-
-  while(1) {
-    do_check_eq(fileStreamA.tell(), fileStreamB.tell());
-
-    let availA = streamA.available();
-    let availB = streamB.available();
-    do_check_eq(availA, availB);
-    if (availA == 0)
-      return;
-
-    do_check_true(streamA.readBytes(availA) ==
-                  streamB.readBytes(availB));
-  }
-}
-
-const SET = Ci.nsISeekableStream.NS_SEEK_SET;
-const CUR = Ci.nsISeekableStream.NS_SEEK_CUR;
-const END = Ci.nsISeekableStream.NS_SEEK_END;
-function test_seek(dummy, partialFileStream, size) {
-  // We can't test the "real" filestream here as our existing file streams
-  // are very broken and allows searching past the end of the file.
-
-  partialFileStream.QueryInterface(Ci.nsISeekableStream);
-
-  var tests = [
-    [SET, 0],
-    [SET, 5],
-    [SET, 1000],
-    [SET, size-10],
-    [SET, size-5],
-    [SET, size-1],
-    [SET, size],
-    [SET, size+10],
-    [SET, 0],
-    [CUR, 5],
-    [CUR, -5],
-    [SET, 5000],
-    [CUR, -100],
-    [CUR, 200],
-    [CUR, -5000],
-    [CUR, 5000],
-    [CUR, size * 2],
-    [SET, 1],
-    [CUR, -1],
-    [CUR, -1],
-    [CUR, -1],
-    [CUR, -1],
-    [CUR, -1],
-    [SET, size-1],
-    [CUR, 1],
-    [CUR, 1],
-    [CUR, 1],
-    [CUR, 1],
-    [CUR, 1],
-    [END, 0],
-    [END, -1],
-    [END, -5],
-    [END, -1000],
-    [END, -size+10],
-    [END, -size+5],
-    [END, -size+1],
-    [END, -size],
-    [END, -size-10],
-    [END, 10],
-    [CUR, 10],
-    [CUR, 10],
-    [CUR, 100],
-    [CUR, 1000],
-    [END, -1000],
-    [CUR, 100],
-    [CUR, 900],
-    [CUR, 100],
-    [CUR, 100],
-  ];
-
-  let pos = 0;
-  for (var test of tests) {
-    let didThrow = false;
-    try {
-      partialFileStream.seek(test[0], test[1]);
-    }
-    catch (ex) {
-      didThrow = true;
-    }
-
-    let newPos = test[0] == SET ? test[1] :
-                 test[0] == CUR ? pos + test[1] :
-                 size + test[1];
-    if (newPos > size || newPos < 0) {
-      do_check_true(didThrow);
-    }
-    else {
-      do_check_false(didThrow);
-      pos = newPos;
-    }
-
-    do_check_eq(partialFileStream.tell(), pos);
-    do_check_eq(partialFileStream.available(), size - pos);
-  }
-}
-
-function test_seek_then_read(fileStreamA, fileStreamB, size) {
-  // For now we only test seeking inside the file since our existing file
-  // streams behave very strange when seeking to past the end of the file.
-  if (size < 20000) {
-    return;
-  }
-
-  fileStreamA.QueryInterface(Ci.nsISeekableStream);
-  fileStreamB.QueryInterface(Ci.nsISeekableStream);
-  let streamA = new BinaryInputStream(fileStreamA);
-  let streamB = new BinaryInputStream(fileStreamB);
-
-  let read = {};
-
-  var tests = [
-    [SET, 0],
-    [read, 1000],
-    [read, 1000],
-    [SET, 5],
-    [read, 1000],
-    [read, 5000],
-    [CUR, 100],
-    [read, 1000],
-    [read, 5000],
-    [CUR, -100],
-    [read, 1000],
-    [CUR, -100],
-    [read, 5000],
-    [END, -10],
-    [read, 10],
-    [END, -100],
-    [read, 101],
-    [CUR, -100],
-    [read, 10],
-    [SET, 0],
-    [read, 20000],
-    [read, 1],
-    [read, 100],
-  ];
-
-  for (var test of tests) {
-    if (test[0] === read) {
-  
-      let didThrowA = false;
-      let didThrowB = false;
-
-      let bytesA, bytesB;
-      try {
-        bytesA = streamA.readBytes(test[1]);
-      }
-      catch (ex) {
-        didThrowA = true;
-      }
-      try {
-        bytesB = streamB.readBytes(test[1]);
-      }
-      catch (ex) {
-        didThrowB = true;
-      }
-  
-      do_check_eq(didThrowA, didThrowB);
-      do_check_true(bytesA == bytesB);
-    }
-    else {
-      fileStreamA.seek(test[0], test[1]);
-      fileStreamB.seek(test[0], test[1]);
-    }
-    do_check_eq(fileStreamA.tell(), fileStreamB.tell());
-    do_check_eq(fileStreamA.available(), fileStreamB.available());
-  }
-}
-
-function test_text_portion(start, length) {
-  let subFile = create_temp_file(test_file_data.substr(start, length));
-
-  let streamTests = [
-    test_readline,
-    test_seek_then_readline,
-  ];
-
-  for (var test of streamTests) {
-    let fileStream = new_file_input_stream(subFile)
-                     .QueryInterface(Ci.nsILineInputStream);
-    let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name),
-                                                      start, length)
-                        .QueryInterface(Ci.nsILineInputStream);
-    test(fileStream, partialStream, length);
-    fileStream.close();
-    partialStream.close();
-  }
-}
-
-function test_readline(fileStreamA, fileStreamB)
-{
-  let moreA = true, moreB;
-  while(moreA) {
-    let lineA, lineB;
-    [moreA, lineA] = read_line_stream(fileStreamA);
-    [moreB, lineB] = read_line_stream(fileStreamB);
-    do_check_eq(moreA, moreB);
-    do_check_true(lineA.value == lineB.value);
-  }
-}
-
-function test_seek_then_readline(fileStreamA, fileStreamB, size) {
-  // For now we only test seeking inside the file since our existing file
-  // streams behave very strange when seeking to past the end of the file.
-  if (size < 100) {
-    return;
-  }
-
-  fileStreamA.QueryInterface(Ci.nsISeekableStream);
-  fileStreamB.QueryInterface(Ci.nsISeekableStream);
-
-  let read = {};
-
-  var tests = [
-    [SET, 0],
-    [read, 5],
-    [read, 5],
-    [SET, 5],
-    [read, 5],
-    [read, 15],
-    [CUR, 100],
-    [read, 5],
-    [read, 15],
-    [CUR, -100],
-    [read, 5],
-    [CUR, -100],
-    [read, 25],
-    [END, -10],
-    [read, 1],
-    [END, -50],
-    [read, 30],
-    [read, 1],
-    [read, 1],
-    [CUR, -100],
-    [read, 1],
-    [SET, 0],
-    [read, 10000],
-    [read, 1],
-    [read, 1],
-    [SET, 0],
-    [read, 1],
-  ];
-
-  for (var test of tests) {
-    if (test[0] === read) {
-
-      for (let i = 0; i < test[1]; ++i) {
-        let didThrowA = false;
-        let didThrowB = false;
-
-        let lineA, lineB, moreA, moreB;
-        try {
-          [moreA, lineA] = read_line_stream(fileStreamA);
-        }
-        catch (ex) {
-          didThrowA = true;
-        }
-        try {
-          [moreB, lineB] = read_line_stream(fileStreamB);
-        }
-        catch (ex) {
-          didThrowB = true;
-        }
-
-        do_check_eq(didThrowA, didThrowB);
-        do_check_eq(moreA, moreB);
-        do_check_true(lineA == lineB);
-        do_check_eq(fileStreamA.tell(), fileStreamB.tell());
-        do_check_eq(fileStreamA.available(), fileStreamB.available());
-        if (!moreA)
-          break;
-      }
-    }
-    else {
-      if (!(test[0] == CUR && (test[1] > fileStreamA.available() ||
-                               test[1] < -fileStreamA.tell()))) {
-        fileStreamA.seek(test[0], test[1]);
-        fileStreamB.seek(test[0], test[1]);
-        do_check_eq(fileStreamA.tell(), fileStreamB.tell());
-        do_check_eq(fileStreamA.available(), fileStreamB.available());
-      }
-    }
-  }
-}
-
-function read_line_stream(stream) {
-  let line = {};
-  let more = stream.readLine(line);
-  return [more, line.value];
-}
-
-function new_file_input_stream(file) {
-  var stream =
-      Cc["@mozilla.org/network/file-input-stream;1"]
-      .createInstance(Ci.nsIFileInputStream);
-  stream.init(file, PR_RDONLY, 0, 0);
-  return stream.QueryInterface(Ci.nsIInputStream);
-}
-
-function new_partial_file_input_stream(file, start, length, flags) {
-  var stream =
-      Cc["@mozilla.org/network/partial-file-input-stream;1"]
-      .createInstance(Ci.nsIPartialFileInputStream);
-  stream.init(file, start, length, PR_RDONLY, 0, flags || 0);
-  return stream.QueryInterface(Ci.nsIInputStream);
-}
-
-function create_temp_file(data) {
-  let file = Cc["@mozilla.org/file/directory_service;1"].
-             getService(Ci.nsIProperties).
-             get("ProfD", Ci.nsIFile);
-  file.append("fileinputstream-test-file.tmp");
-  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
-
-  let ostream = Cc["@mozilla.org/network/file-output-stream;1"].
-                createInstance(Ci.nsIFileOutputStream);
-  ostream.init(file, 0x02 | 0x08 | 0x20, // write, create, truncate
-               0o666, 0);
-  do_check_eq(ostream.write(data, data.length), data.length);
-  ostream.close();
-
-  return file;
-}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -203,17 +203,16 @@ skip-if = bits != 32
 [test_fallback_no-cache-entry_canceled.js]
 [test_fallback_no-cache-entry_passing.js]
 [test_fallback_redirect-to-different-origin_canceled.js]
 [test_fallback_redirect-to-different-origin_passing.js]
 [test_fallback_request-error_canceled.js]
 [test_fallback_request-error_passing.js]
 [test_fallback_response-error_canceled.js]
 [test_fallback_response-error_passing.js]
-[test_file_partial_inputstream.js]
 [test_file_protocol.js]
 [test_filestreams.js]
 [test_freshconnection.js]
 [test_gre_resources.js]
 [test_gzipped_206.js]
 [test_head.js]
 [test_header_Accept-Language.js]
 [test_header_Accept-Language_case.js]
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1688,18 +1688,16 @@ GetNSSProfilePath(nsAutoCString& aProfil
     return rv;
   }
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
           ("NSS profile at '%s'\n", aProfilePath.get()));
   return NS_OK;
 }
 
-static char sCrashReasonBuffer[1024];
-
 nsresult
 nsNSSComponent::InitializeNSS()
 {
   // Can be called both during init and profile change.
   // Needs mutex protection.
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent::InitializeNSS\n"));
 
@@ -1768,24 +1766,19 @@ nsNSSComponent::InitializeNSS()
   }
   // If we haven't succeeded in initializing the DB in our profile
   // directory or we don't have a profile at all, or the "security.nocertdb"
   // pref has been set to "true", attempt to initialize with no DB.
   if (nocertdb || init_rv != SECSuccess) {
     init_rv = NSS_NoDB_Init(nullptr);
     if (init_rv != SECSuccess) {
       PRErrorCode savedPRErrorCode3 = PR_GetError();
-      nsPrintfCString message("NSS initialization failed PRErrorCodes %d %d %d",
+      MOZ_CRASH_UNSAFE_PRINTF("NSS initialization failed PRErrorCodes %d %d %d",
                               savedPRErrorCode1, savedPRErrorCode2,
                               savedPRErrorCode3);
-      mozilla::PodArrayZero(sCrashReasonBuffer);
-      strncpy(sCrashReasonBuffer, message.get(),
-              sizeof(sCrashReasonBuffer) - 1);
-      MOZ_CRASH_ANNOTATE(sCrashReasonBuffer);
-      MOZ_REALLY_CRASH();
     }
   }
   if (init_rv != SECSuccess) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("could not initialize NSS - panicking\n"));
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // ensure we have an initial value for the content signer root
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1012,27 +1012,27 @@
   "GEOLOCATION_WATCHPOSITION_VISIBLE": {
     "alert_emails": ["michelangelo@mozilla.com"],
     "expires_in_version": "55",
     "kind": "boolean",
     "bug_numbers": [1255198],
     "description": "This metric is recorded every time a navigator.geolocation.watchPosition() request gets allowed/fulfilled. A false value is recorded if the owner is not visible according to document.isVisible."
   },
   "GPU_PROCESS_LAUNCH_TIME_MS_2" : {
-    "alert_emails": ["george@mozilla.com", "danderson@mozilla.com"],
+    "alert_emails": ["danderson@mozilla.com"],
     "expires_in_version": "never",
     "bug_numbers": [1297790, 1317796],
     "kind": "exponential",
     "high": 64000,
     "n_buckets": 100,
     "releaseChannelCollection": "opt-out",
     "description": "GPU process launch time in milliseconds"
   },
   "GPU_PROCESS_INITIALIZATION_TIME_MS" : {
-    "alert_emails": ["george@mozilla.com", "danderson@mozilla.com"],
+    "alert_emails": ["danderson@mozilla.com"],
     "expires_in_version": "never",
     "bug_numbers": [1324095],
     "kind": "exponential",
     "high": 64000,
     "n_buckets": 100,
     "releaseChannelCollection": "opt-out",
     "description": "GPU process initialization (excluding XPCOM and fork time) time in milliseconds"
   },
--- a/toolkit/components/telemetry/ProcessedStack.h
+++ b/toolkit/components/telemetry/ProcessedStack.h
@@ -3,16 +3,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/. */
 
 #ifndef ProcessedStack_h__
 #define ProcessedStack_h__
 
 #include <string>
 #include <vector>
+#include "nsString.h"
 
 namespace mozilla {
 namespace Telemetry {
 
 // This class represents a stack trace and the modules referenced in that trace.
 // It is designed to be easy to read and write to disk or network and doesn't
 // include any logic on how to collect or read the information it stores.
 class ProcessedStack
@@ -28,17 +29,18 @@ public:
     uintptr_t mOffset;
     // The index to pass to GetModule to get the module this program counter
     // was in.
     uint16_t mModIndex;
   };
   struct Module
   {
     // The file name, /foo/bar/libxul.so for example.
-    std::string mName;
+    // It can contain unicode characters.
+    nsString mName;
     std::string mBreakpadId;
 
     bool operator==(const Module& other) const;
   };
 
   const Frame &GetFrame(unsigned aIndex) const;
   void AddFrame(const Frame& aFrame);
   const Module &GetModule(unsigned aIndex) const;
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -1590,30 +1590,24 @@ CreateJSStackObject(JSContext *cx, const
     if (!JS_DefineElement(cx, moduleArray, moduleIndex, moduleInfoArray,
                           JSPROP_ENUMERATE)) {
       return nullptr;
     }
 
     unsigned index = 0;
 
     // Module name
-    JS::Rooted<JSString*> str(cx, JS_NewStringCopyZ(cx, module.mName.c_str()));
-    if (!str) {
-      return nullptr;
-    }
-    if (!JS_DefineElement(cx, moduleInfoArray, index++, str, JSPROP_ENUMERATE)) {
+    JS::Rooted<JSString*> str(cx, JS_NewUCStringCopyZ(cx, module.mName.get()));
+    if (!str || !JS_DefineElement(cx, moduleInfoArray, index++, str, JSPROP_ENUMERATE)) {
       return nullptr;
     }
 
     // Module breakpad identifier
     JS::Rooted<JSString*> id(cx, JS_NewStringCopyZ(cx, module.mBreakpadId.c_str()));
-    if (!id) {
-      return nullptr;
-    }
-    if (!JS_DefineElement(cx, moduleInfoArray, index++, id, JSPROP_ENUMERATE)) {
+    if (!id || !JS_DefineElement(cx, moduleInfoArray, index++, id, JSPROP_ENUMERATE)) {
       return nullptr;
     }
   }
 
   JS::Rooted<JSObject*> reportArray(cx, JS_NewArrayObject(cx, 0));
   if (!reportArray) {
     return nullptr;
   }
@@ -1895,17 +1889,17 @@ ReadStack(const char *aFileName, Telemet
 
     std::string moduleName;
     getline(file, moduleName);
     if (file.fail() || moduleName[0] == ' ') {
       return;
     }
 
     Telemetry::ProcessedStack::Module module = {
-      moduleName,
+      NS_ConvertUTF8toUTF16(moduleName.c_str()),
       breakpadId
     };
     stack.AddModule(module);
   }
 
   size_t numFrames;
   file >> numFrames;
   if (file.fail()) {
@@ -3219,24 +3213,24 @@ GetStackAndModules(const std::vector<uin
   for (auto & rawFrame : rawStack) {
     mozilla::Telemetry::ProcessedStack::Frame frame = { rawFrame.mPC, rawFrame.mModIndex };
     Ret.AddFrame(frame);
   }
 
 #ifdef MOZ_GECKO_PROFILER
   for (unsigned i = 0, n = rawModules.GetSize(); i != n; ++i) {
     const SharedLibrary &info = rawModules.GetEntry(i);
-    std::string basename = info.GetNativeDebugName();
+    nsString basename = info.GetDebugName();
 #if defined(XP_MACOSX) || defined(XP_LINUX)
     // We want to use just the basename as the libname, but the
     // current profiler addon needs the full path name, so we compute the
     // basename in here.
-    size_t pos = basename.rfind('/');
-    if (pos != std::string::npos) {
-      basename = basename.substr(pos + 1);
+    int32_t pos = basename.RFindChar('/');
+    if (pos != kNotFound) {
+      basename.Cut(0, pos + 1);
     }
 #endif
     mozilla::Telemetry::ProcessedStack::Module module = {
       basename,
       info.GetBreakpadId()
     };
     Ret.AddModule(module);
   }
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/TelemetryHistogram.cpp
@@ -1311,16 +1311,19 @@ internal_RemoteAccumulate(mozilla::Telem
 {
   if (XRE_IsParentProcess()) {
     return false;
   }
   const HistogramInfo& th = gHistograms[aId];
   KeyedHistogram* keyed
      = internal_GetKeyedHistogramById(nsDependentCString(th.id()));
   MOZ_ASSERT(keyed);
+  // Assert on empty keys as well, we should be filtering them out in
+  // the outer API.
+  MOZ_ASSERT(!aKey.IsEmpty());
   if (!keyed->IsRecordingEnabled()) {
     return false;
   }
   TelemetryIPCAccumulator::AccumulateChildKeyedHistogram(aId, aKey, aSample);
   return true;
 }
 
 void internal_Accumulate(mozilla::Telemetry::HistogramID aHistogram, uint32_t aSample)
@@ -1738,16 +1741,22 @@ internal_JSKeyedHistogram_Add(JSContext 
   }
 
   nsAutoJSString key;
   if (!args[0].isString() || !key.init(cx, args[0])) {
     LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Not a string"));
     return true;
   }
 
+  if (key.IsEmpty()) {
+    LogToBrowserConsole(nsIScriptError::errorFlag,
+      NS_LITERAL_STRING("Empty histogram keys are not allowed."));
+    return true;
+  }
+
   const uint32_t type = keyed->GetHistogramType();
 
   // If we don't have an argument for the count histogram, assume an increment of 1.
   // Otherwise, make sure to run some sanity checks on the argument.
   int32_t value = 1;
   if ((type != base::CountHistogram::COUNT_HISTOGRAM) || (args.length() == 2)) {
     if (args.length() < 2) {
       LogToBrowserConsole(nsIScriptError::errorFlag,
@@ -2131,16 +2140,22 @@ void
 TelemetryHistogram::Accumulate(mozilla::Telemetry::HistogramID aID,
                                const nsCString& aKey, uint32_t aSample)
 {
   if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
     MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
     return;
   }
 
+  if (aKey.IsEmpty()) {
+    LogToBrowserConsole(nsIScriptError::errorFlag,
+      NS_LITERAL_STRING("Empty histogram keys are not allowed."));
+    return;
+  }
+
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   internal_Accumulate(aID, aKey, aSample);
 }
 
 void
 TelemetryHistogram::Accumulate(const char* name, uint32_t sample)
 {
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
@@ -2154,16 +2169,22 @@ TelemetryHistogram::Accumulate(const cha
   }
   internal_Accumulate(id, sample);
 }
 
 void
 TelemetryHistogram::Accumulate(const char* name,
                                const nsCString& key, uint32_t sample)
 {
+  if (key.IsEmpty()) {
+    LogToBrowserConsole(nsIScriptError::errorFlag,
+      NS_LITERAL_STRING("Empty histogram keys are not allowed."));
+    return;
+  }
+
   StaticMutexAutoLock locker(gTelemetryHistogramMutex);
   if (!internal_CanRecordBase()) {
     return;
   }
   mozilla::Telemetry::HistogramID id;
   nsresult rv = internal_GetHistogramEnumId(name, &id);
   if (NS_SUCCEEDED(rv)) {
     internal_Accumulate(id, key, sample);
--- a/toolkit/components/telemetry/docs/collection/histograms.rst
+++ b/toolkit/components/telemetry/docs/collection/histograms.rst
@@ -84,17 +84,21 @@ Flag histograms will ignore any changes 
 *Deprecated* (please use uint :doc:`scalars`).
 
 This histogram type is used when you want to record a count of something. It only stores a single value and defaults to `0`.
 
 Keyed Histograms
 ----------------
 
 Keyed histograms are collections of one of the histogram types above, indexed by a string key. This is for example useful when you want to break down certain counts by a name, like how often searches happen with which search engine.
-Note that when you need to record for a small set of known keys, using separate plain histograms is more efficient.
+
+Note that:
+
+- keys can't be empty strings;
+- when you need to record for a small set of known keys, using separate plain histograms is more efficient.
 
 .. warning::
 
     Keyed histograms are currently not supported in the `histogram change detector <https://alerts.telemetry.mozilla.org/index.html>`_.
 
 Declaring a Histogram
 =====================
 
@@ -198,17 +202,21 @@ A Telemetry probe is the code that measu
 .. code-block:: js
 
   let histogram = Services.telemetry.getHistogramById("PLACES_AUTOCOMPLETE_1ST_RESULT_TIME_MS");
   histogram.add(measuredDuration);
 
   let keyed = Services.telemetry.getKeyedHistogramById("TAG_SEEN_COUNTS");
   keyed.add("blink");
 
-Note that ``nsITelemetry.getHistogramById()`` will throw an ``NS_ERROR_ILLEGAL_VALUE`` JavaScript exception if it is called with an invalid histogram ID. The ``add()`` function will not throw if it fails, instead it prints an error in the browser console.
+Note that:
+
+- ``nsITelemetry.getHistogramById()`` will throw an ``NS_ERROR_ILLEGAL_VALUE`` JavaScript exception if it is called with an invalid histogram ID;
+- the ``add()`` function will not throw on failure, but log an error to the browser console;
+- for keyed histograms, calling ``add()`` with an empty key will be ignored and log an error.
 
 For histograms measuring time, `TelemetryStopwatch <https://mxr.mozilla.org/mozilla-central/source/toolkit/components/telemetry/TelemetryStopwatch.jsm>`_ can be used to avoid working with Dates manually:
 
 .. code-block:: js
 
   TelemetryStopwatch.start("SEARCH_SERVICE_INIT_MS");
   TelemetryStopwatch.finish("SEARCH_SERVICE_INIT_MS");
 
--- a/toolkit/components/telemetry/docs/data/main-ping.rst
+++ b/toolkit/components/telemetry/docs/data/main-ping.rst
@@ -296,16 +296,18 @@ This is similar to :ref:`chromeHangs`, b
 the parent process are reported. This data is only available on Windows, either
 in Firefox Nightly or in builds using ``--enable-profiling`` switch.
 
 Limits for captured stacks are the same as for chromeHangs (see below). Furthermore:
 
 * the key length is limited to 50 characters,
 * keys are restricted to alpha-numeric characters and `-`.
 
+The module names can contain unicode characters.
+
 Structure:
 
 .. code-block:: js
 
     "capturedStacks" : {
       "memoryMap": [
         ["wgdi32.pdb", "08A541B5942242BDB4AEABD8C87E4CFF2"],
         ["igd10iumd32.pdb", "D36DEBF2E78149B5BE1856B772F1C3991"],
@@ -331,16 +333,18 @@ chromeHangs
 -----------
 Contains the statistics about the hangs happening exclusively on the main thread of the parent process. Precise C++ stacks are reported. This is only available on Nightly Release on Windows, when building using "--enable-profiling" switch.
 
 Some limits are applied:
 
 * Reported chrome hang stacks are limited in depth to 50 entries.
 * The maximum number of reported stacks is 50.
 
+The module names can contain unicode characters.
+
 Structure:
 
 .. code-block:: js
 
     "chromeHangs" : {
       "memoryMap" : [
         ["wgdi32.pdb", "08A541B5942242BDB4AEABD8C87E4CFF2"],
         ["igd10iumd32.pdb", "D36DEBF2E78149B5BE1856B772F1C3991"],
@@ -556,17 +560,19 @@ Structure:
         stats, // Number of stat operations
       ],
       "{profile}": [ ... ],
       ...
     }
 
 lateWrites
 ----------
-This sections reports writes to the file system that happen during shutdown. The reported data contains the stack and the loaded libraries at the time the writes happened.
+This sections reports writes to the file system that happen during shutdown. The reported data contains the stack and the file names of the loaded libraries at the time the writes happened.
+
+The file names of the loaded libraries can contain unicode characters.
 
 Structure:
 
 .. code-block:: js
 
     "lateWrites" : {
       "memoryMap" : [
         ["wgdi32.pdb", "08A541B5942242BDB4AEABD8C87E4CFF2"],
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
@@ -482,16 +482,25 @@ add_task(function* test_keyed_histogram(
   let threw = false;
   try {
     Telemetry.getKeyedHistogramById("test::unknown histogram", "never", Telemetry.HISTOGRAM_BOOLEAN);
   } catch (e) {
     // This should throw as it is an unknown ID
     threw = true;
   }
   Assert.ok(threw, "getKeyedHistogramById should have thrown");
+
+  // Check that empty keys are not allowed.
+  const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT";
+  let h = Telemetry.getKeyedHistogramById(KEYED_ID);
+
+  // Try to add to an empty key and make sure nothing happens.
+  h.add("");
+  Assert.ok(!("" in h.snapshot()),
+            "Keyed histogram should not allow empty keys.");
 });
 
 add_task(function* test_keyed_boolean_histogram() {
   const KEYED_ID = "TELEMETRY_TEST_KEYED_BOOLEAN";
   let KEYS = numberRange(0, 2).map(i => "key" + (i + 1));
   KEYS.push("漢語");
   let histogramBase = {
     "min": 1,
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryLateWrites.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryLateWrites.js
@@ -15,40 +15,49 @@ const STACK_SUFFIX1 = "stack1.txt";
 const STACK_SUFFIX2 = "stack2.txt";
 const STACK_BOGUS_SUFFIX = "bogus.txt";
 const LATE_WRITE_PREFIX = "Telemetry.LateWriteFinal-";
 
 // The names and IDs don't matter, but the format of the IDs does.
 const LOADED_MODULES = {
   "4759A7E6993548C89CAF716A67EC242D00": "libtest.so",
   "F77AF15BB8D6419FA875954B4A3506CA00": "libxul.so",
-  "1E2F7FB590424E8F93D60BB88D66B8C500": "libc.so"
+  "1E2F7FB590424E8F93D60BB88D66B8C500": "libc.so",
+  "E4D6D70CC09A63EF8B88D532F867858800": "libmodμles.so",
 };
 const N_MODULES = Object.keys(LOADED_MODULES).length;
 
 // Format of individual items is [index, offset-in-library].
 const STACK1 = [
   [ 0, 0 ],
   [ 1, 1 ],
-  [ 2, 2 ]
+  [ 2, 2 ],
+  [ 3, 3 ],
 ];
 const STACK2 = [
   [ 0, 0 ],
   [ 1, 5 ],
   [ 2, 10 ],
+  [ 3, 15 ],
 ];
 // XXX The only error checking is for a zero-sized stack.
 const STACK_BOGUS = [];
 
 function write_string_to_file(file, contents) {
   let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]
                 .createInstance(Ci.nsIFileOutputStream);
   ostream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
-	       RW_OWNER, ostream.DEFER_OPEN);
-  ostream.write(contents, contents.length);
+               RW_OWNER, ostream.DEFER_OPEN);
+
+  var bos = Cc["@mozilla.org/binaryoutputstream;1"]
+            .createInstance(Ci.nsIBinaryOutputStream);
+  bos.setOutputStream(ostream);
+
+  let utf8 = new TextEncoder("utf-8").encode(contents);
+  bos.writeByteArray(utf8, utf8.length);
   ostream.QueryInterface(Ci.nsISafeOutputStream).finish();
   ostream.close();
 }
 
 function construct_file(suffix) {
   let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile);
   let file = profileDirectory.clone();
   file.append(LATE_WRITE_PREFIX + suffix);
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -6,25 +6,24 @@
 
 #include "nsExceptionHandler.h"
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryService.h"
 #include "nsDataHashtable.h"
 #include "mozilla/ArrayUtils.h"
-#include "mozilla/dom/CrashReporterChild.h"
-#include "mozilla/ipc/CrashReporterClient.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Printf.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/ipc/CrashReporterClient.h"
 
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "jsfriendapi.h"
 
 #if defined(XP_WIN32)
 #ifdef WIN32_LEAN_AND_MEAN
 #undef WIN32_LEAN_AND_MEAN
@@ -106,18 +105,16 @@ using google_breakpad::ClientInfo;
 using google_breakpad::MinidumpDescriptor;
 #endif
 #if defined(MOZ_WIDGET_ANDROID)
 using google_breakpad::auto_wasteful_vector;
 using google_breakpad::FileID;
 using google_breakpad::PageAllocator;
 #endif
 using namespace mozilla;
-using mozilla::dom::CrashReporterChild;
-using mozilla::dom::PCrashReporterChild;
 using mozilla::ipc::CrashReporterClient;
 
 namespace CrashReporter {
 
 #ifdef XP_WIN32
 typedef wchar_t XP_CHAR;
 typedef std::wstring xpstring;
 #define XP_TEXT(x) L##x
@@ -2254,38 +2251,16 @@ static void
 EnqueueDelayedNote(DelayedNote* aNote)
 {
   if (!gDelayedAnnotations) {
     gDelayedAnnotations = new nsTArray<nsAutoPtr<DelayedNote> >();
   }
   gDelayedAnnotations->AppendElement(aNote);
 }
 
-class CrashReporterHelperRunnable : public Runnable {
-public:
-  explicit CrashReporterHelperRunnable(const nsACString& aKey,
-                                       const nsACString& aData)
-    : mKey(aKey)
-    , mData(aData)
-    , mAppendAppNotes(false)
-    {}
-  explicit CrashReporterHelperRunnable(const nsACString& aData)
-    : mKey()
-    , mData(aData)
-    , mAppendAppNotes(true)
-    {}
-
-  NS_IMETHOD Run() override;
-
-private:
-  nsCString mKey;
-  nsCString mData;
-  bool mAppendAppNotes;
-};
-
 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
 {
   if (!GetEnabled())
     return NS_ERROR_NOT_INITIALIZED;
 
   nsCString escapedData;
   nsresult rv = EscapeAnnotation(key, data, escapedData);
   if (NS_FAILED(rv))
@@ -2293,33 +2268,20 @@ nsresult AnnotateCrashReport(const nsACS
 
   if (!XRE_IsParentProcess()) {
     // The newer CrashReporterClient can be used from any thread.
     if (RefPtr<CrashReporterClient> client = CrashReporterClient::GetSingleton()) {
       client->AnnotateCrashReport(nsCString(key), escapedData);
       return NS_OK;
     }
 
-    // Otherwise, we have to handle this on the main thread since we will go
-    // through IPDL.
-    if (!NS_IsMainThread()) {
-      // Child process needs to handle this in the main thread:
-      nsCOMPtr<nsIRunnable> r = new CrashReporterHelperRunnable(key, data);
-      NS_DispatchToMainThread(r);
-      return NS_OK;
-    }
-
-    MOZ_ASSERT(NS_IsMainThread());
-    PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
-    if (!reporter) {
-      EnqueueDelayedNote(new DelayedNote(key, data));
-      return NS_OK;
-    }
-    if (!reporter->SendAnnotateCrashReport(nsCString(key), escapedData))
-      return NS_ERROR_FAILURE;
+    // EnqueueDelayedNote() can only be called on the main thread.
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+    EnqueueDelayedNote(new DelayedNote(key, data));
     return NS_OK;
   }
 
   MutexAutoLock lock(*crashReporterAPILock);
 
   crashReporterAPIData_Hash->Put(key, escapedData);
 
   // now rebuild the file contents
@@ -2378,59 +2340,29 @@ nsresult AppendAppNotesToCrashReport(con
     if (NS_FAILED(rv))
       return rv;
 
     if (RefPtr<CrashReporterClient> client = CrashReporterClient::GetSingleton()) {
       client->AppendAppNotes(escapedData);
       return NS_OK;
     }
 
-    if (!NS_IsMainThread()) {
-      // Child process needs to handle this in the main thread:
-      nsCOMPtr<nsIRunnable> r = new CrashReporterHelperRunnable(data);
-      NS_DispatchToMainThread(r);
-      return NS_OK;
-    }
-
-    MOZ_ASSERT(NS_IsMainThread());
-    PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
-    if (!reporter) {
-      EnqueueDelayedNote(new DelayedNote(data));
-      return NS_OK;
-    }
-
-    if (!reporter->SendAppendAppNotes(escapedData))
-      return NS_ERROR_FAILURE;
+    // EnqueueDelayedNote can only be called on the main thread.
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+    EnqueueDelayedNote(new DelayedNote(data));
     return NS_OK;
   }
 
   MutexAutoLock lock(*notesFieldLock);
 
   notesField->Append(data);
   return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField);
 }
 
-nsresult CrashReporterHelperRunnable::Run()
-{
-  // We expect this to be in the child process' main thread.  If it isn't,
-  // something is happening we didn't design for.
-  MOZ_ASSERT(!XRE_IsParentProcess());
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // Don't just leave the assert, paranoid about infinite recursion
-  if (NS_IsMainThread()) {
-    if (mAppendAppNotes) {
-      return AppendAppNotesToCrashReport(mData);
-    } else {
-      return AnnotateCrashReport(mKey, mData);
-    }
-  }
-  return NS_ERROR_FAILURE;
-}
-
 // Returns true if found, false if not found.
 bool GetAnnotation(const nsACString& key, nsACString& data)
 {
   if (!gExceptionHandler)
     return false;
 
   nsAutoCString entry;
   if (!crashReporterAPIData_Hash->Get(key, &entry))
--- a/toolkit/locales/en-US/chrome/global/videocontrols.dtd
+++ b/toolkit/locales/en-US/chrome/global/videocontrols.dtd
@@ -37,13 +37,13 @@ the 5 minute mark in a 6 hour long video
 "6:00:00", result string would be "5:00 of 6:00:00 elapsed".
 -->
 <!ENTITY scrubberScale.nameFormat "#1 of #2 elapsed">
 
 <!-- LOCALIZATION NOTE (positionAndDuration.nameFormat): the #1 string is the current
 media position, and the #2 string is the total duration. For example, when at
 the 5 minute mark in a 6 hour long video, #1 would be "5:00" and #2 would be
 "6:00:00", result string would be "5:00 / 6:00:00".
-Note that #2 is not always avaiable. For example, when at the 5 minute mark in an
-unknown duration video, #1 would be "5:00" and string which is surrounded by <span>
-would be deleted, result string would be "5:00".
+Note that #2 is not always available. For example, when at the 5 minute mark in an
+unknown duration video, #1 would be "5:00" and the string which is surrounded by
+<span> would be deleted, result string would be "5:00".
 -->
 <!ENTITY positionAndDuration.nameFormat "#1<span> / #2</span>">
--- a/toolkit/themes/shared/extensions/extensions.inc.css
+++ b/toolkit/themes/shared/extensions/extensions.inc.css
@@ -255,16 +255,18 @@ button.warning {
   border-color: #c1c1c1;
   background-color: #fbfbfb;
   padding-right: 10px;
   padding-left: 10px;
 }
 
 #header-utils-btn:not([disabled="true"]):active:hover,
 #header-utils-btn[open="true"] {
+  padding-bottom: 0px;
+  padding-top: 0px;
   background-color: #dadada;
 }
 
 .header-button {
   -moz-appearance: none;
   border: 1px solid;
   border-radius: 2px;
 }
--- a/xpcom/build/LateWriteChecks.cpp
+++ b/xpcom/build/LateWriteChecks.cpp
@@ -163,17 +163,17 @@ LateWriteObserver::Observe(IOInterposeOb
 
   SHA1Stream sha1Stream(stream);
 
   size_t numModules = stack.GetNumModules();
   sha1Stream.Printf("%u\n", (unsigned)numModules);
   for (size_t i = 0; i < numModules; ++i) {
     Telemetry::ProcessedStack::Module module = stack.GetModule(i);
     sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(),
-                      module.mName.c_str());
+                      NS_ConvertUTF16toUTF8(module.mName).get());
   }
 
   size_t numFrames = stack.GetStackSize();
   sha1Stream.Printf("%u\n", (unsigned)numFrames);
   for (size_t i = 0; i < numFrames; ++i) {
     const Telemetry::ProcessedStack::Frame& frame = stack.GetFrame(i);
     // NOTE: We write the offsets, while the atos tool expects a value with
     // the virtual address added. For example, running otool -l on the the firefox
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -624,23 +624,18 @@ RegisterStaticAtoms(const nsStaticAtom* 
     if (atom) {
       // Disallow creating a dynamic atom, and then later, while the
       // dynamic atom is still alive, registering that same atom as a
       // static atom.  It causes subtle bugs, and we're programming in
       // C++ here, not Smalltalk.
       if (!atom->IsStaticAtom()) {
         nsAutoCString name;
         atom->ToUTF8String(name);
-
-        static char sCrashReason[1024];
-        SprintfLiteral(sCrashReason,
-                       "static atom registration for %s should be pushed back",
-                       name.get());
-        MOZ_CRASH_ANNOTATE(sCrashReason);
-        MOZ_REALLY_CRASH();
+        MOZ_CRASH_UNSAFE_PRINTF(
+          "Static atom registration for %s should be pushed back", name.get());
       }
     } else {
       atom = new StaticAtom(stringBuffer, stringLen, hash);
       he->mAtom = atom;
     }
     *atomp = atom;
 
     if (!gStaticAtomTableSealed) {
--- a/xpcom/ds/nsTArray.cpp
+++ b/xpcom/ds/nsTArray.cpp
@@ -4,33 +4,26 @@
  * 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 <string.h>
 #include "nsTArray.h"
 #include "nsXPCOM.h"
 #include "nsDebug.h"
 #include "mozilla/CheckedInt.h"
+#include "mozilla/IntegerPrintfMacros.h"
 
 nsTArrayHeader nsTArrayHeader::sEmptyHdr = { 0, 0, 0 };
 
 bool
 IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity, size_t aElemSize)
 {
   using mozilla::CheckedUint32;
   return ((CheckedUint32(aCapacity) * aElemSize) * 2).isValid();
 }
 
 MOZ_NORETURN MOZ_COLD void
 InvalidArrayIndex_CRASH(size_t aIndex, size_t aLength)
 {
-  const size_t CAPACITY = 512;
-  // Leak the buffer on the heap to make sure that it lives long enough, as
-  // MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to the end of
-  // the program.
-  auto* buffer = new char[CAPACITY];
-  snprintf(buffer, CAPACITY,
-           "ElementAt(aIndex = %llu, aLength = %llu)",
-           (long long unsigned) aIndex,
-           (long long unsigned) aLength);
-  MOZ_CRASH_ANNOTATE(buffer);
-  MOZ_REALLY_CRASH();
+  MOZ_CRASH_UNSAFE_PRINTF(
+    "ElementAt(aIndex = %" PRIu64 ", aLength = %" PRIu64 ")",
+    static_cast<uint64_t>(aIndex), static_cast<uint64_t>(aLength));
 }
--- a/xpcom/io/SlicedInputStream.cpp
+++ b/xpcom/io/SlicedInputStream.cpp
@@ -1,46 +1,106 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SlicedInputStream.h"
+#include "mozilla/ipc/InputStreamUtils.h"
 #include "nsISeekableStream.h"
 #include "nsStreamUtils.h"
 
-NS_IMPL_ISUPPORTS(SlicedInputStream, nsIInputStream,
-                  nsICloneableInputStream, nsIAsyncInputStream)
+using namespace mozilla::ipc;
+
+NS_IMPL_ADDREF(SlicedInputStream);
+NS_IMPL_RELEASE(SlicedInputStream);
+
+NS_INTERFACE_MAP_BEGIN(SlicedInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
+                                     mWeakCloneableInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
+                                     mWeakIPCSerializableInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
+                                     mWeakSeekableInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+NS_INTERFACE_MAP_END
 
 SlicedInputStream::SlicedInputStream(nsIInputStream* aInputStream,
                                      uint64_t aStart, uint64_t aLength)
-  : mInputStream(aInputStream)
+  : mWeakCloneableInputStream(nullptr)
+  , mWeakIPCSerializableInputStream(nullptr)
+  , mWeakSeekableInputStream(nullptr)
   , mStart(aStart)
   , mLength(aLength)
   , mCurPos(0)
   , mClosed(false)
 {
   MOZ_ASSERT(aInputStream);
+  SetSourceStream(aInputStream);
 }
 
+SlicedInputStream::SlicedInputStream()
+  : mWeakCloneableInputStream(nullptr)
+  , mWeakIPCSerializableInputStream(nullptr)
+  , mWeakSeekableInputStream(nullptr)
+  , mStart(0)
+  , mLength(0)
+  , mCurPos(0)
+  , mClosed(false)
+{}
+
 SlicedInputStream::~SlicedInputStream()
 {}
 
+void
+SlicedInputStream::SetSourceStream(nsIInputStream* aInputStream)
+{
+  MOZ_ASSERT(!mInputStream);
+  MOZ_ASSERT(aInputStream);
+
+  mInputStream = aInputStream;
+
+  nsCOMPtr<nsICloneableInputStream> cloneableStream =
+    do_QueryInterface(aInputStream);
+  if (cloneableStream && SameCOMIdentity(aInputStream, cloneableStream)) {
+    mWeakCloneableInputStream = cloneableStream;
+  }
+
+  nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
+    do_QueryInterface(aInputStream);
+  if (serializableStream &&
+      SameCOMIdentity(aInputStream, serializableStream)) {
+    mWeakIPCSerializableInputStream = serializableStream;
+  }
+
+  nsCOMPtr<nsISeekableStream> seekableStream =
+    do_QueryInterface(aInputStream);
+  if (seekableStream && SameCOMIdentity(aInputStream, seekableStream)) {
+    mWeakSeekableInputStream = seekableStream;
+  }
+}
+
 NS_IMETHODIMP
 SlicedInputStream::Close()
 {
+  NS_ENSURE_STATE(mInputStream);
+
   mClosed = true;
   return NS_OK;
 }
 
 // nsIInputStream interface
 
 NS_IMETHODIMP
 SlicedInputStream::Available(uint64_t* aLength)
 {
+  NS_ENSURE_STATE(mInputStream);
+
   if (mClosed) {
     return NS_BASE_STREAM_CLOSED;
   }
 
   nsresult rv = mInputStream->Available(aLength);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -56,33 +116,20 @@ SlicedInputStream::Available(uint64_t* a
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SlicedInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
 {
-  return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aReadCount);
-}
-
-NS_IMETHODIMP
-SlicedInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
-                                uint32_t aCount, uint32_t *aResult)
-{
-  uint32_t result;
-
-  if (!aResult) {
-    aResult = &result;
-  }
-
-  *aResult = 0;
+  *aReadCount = 0;
 
   if (mClosed) {
-    return NS_BASE_STREAM_CLOSED;
+    return NS_OK;
   }
 
   if (mCurPos < mStart) {
     nsCOMPtr<nsISeekableStream> seekableStream =
       do_QueryInterface(mInputStream);
     if (seekableStream) {
       nsresult rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET,
                                          mStart);
@@ -106,104 +153,229 @@ SlicedInputStream::ReadSegments(nsWriteS
     }
   }
 
   // Let's reduce aCount in case it's too big.
   if (mCurPos + aCount > mStart + mLength) {
     aCount = mStart + mLength - mCurPos;
   }
 
-  char buf[4096];
-  while (mCurPos < mStart + mLength && *aResult < aCount) {
-    uint32_t bytesRead;
-    uint64_t bufCount = XPCOM_MIN(aCount - *aResult, (uint32_t)sizeof(buf));
-    nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead);
-    if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) {
-      return rv;
-    }
-
-    mCurPos += bytesRead;
-
-    uint32_t bytesWritten = 0;
-    while (bytesWritten < bytesRead) {
-      uint32_t writerCount = 0;
-      rv = aWriter(this, aClosure, buf + bytesWritten, *aResult,
-                   bytesRead - bytesWritten, &writerCount);
-      if (NS_FAILED(rv) || writerCount == 0) {
-	return NS_OK;
-      }
-
-      MOZ_ASSERT(writerCount <= bytesRead - bytesWritten);
-      bytesWritten += writerCount;
-      *aResult += writerCount;
-    }
+  nsresult rv = mInputStream->Read(aBuffer, aCount, aReadCount);
+  if (NS_WARN_IF(NS_FAILED(rv)) || *aReadCount == 0) {
+    return rv;
   }
 
+  mCurPos += *aReadCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+SlicedInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+                                uint32_t aCount, uint32_t *aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 SlicedInputStream::IsNonBlocking(bool* aNonBlocking)
 {
+  NS_ENSURE_STATE(mInputStream);
   return mInputStream->IsNonBlocking(aNonBlocking);
 }
 
 // nsICloneableInputStream interface
 
 NS_IMETHODIMP
 SlicedInputStream::GetCloneable(bool* aCloneable)
 {
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakCloneableInputStream);
+
   *aCloneable = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SlicedInputStream::Clone(nsIInputStream** aResult)
 {
-  nsCOMPtr<nsIInputStream> clonedStream;
-  nsCOMPtr<nsIInputStream> replacementStream;
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakCloneableInputStream);
 
-  nsresult rv = NS_CloneInputStream(mInputStream, getter_AddRefs(clonedStream),
-                                    getter_AddRefs(replacementStream));
+  nsCOMPtr<nsIInputStream> clonedStream;
+  nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  if (replacementStream) {
-    mInputStream = replacementStream.forget();
-  }
-
   nsCOMPtr<nsIInputStream> sis =
     new SlicedInputStream(clonedStream, mStart, mLength);
 
   sis.forget(aResult);
   return NS_OK;
 }
 
 // nsIAsyncInputStream interface
 
 NS_IMETHODIMP
 SlicedInputStream::CloseWithStatus(nsresult aStatus)
 {
+  NS_ENSURE_STATE(mInputStream);
+
   nsCOMPtr<nsIAsyncInputStream> asyncStream =
     do_QueryInterface(mInputStream);
   if (!asyncStream) {
     return NS_ERROR_FAILURE;
   }
 
   return asyncStream->CloseWithStatus(aStatus);
 }
 
 NS_IMETHODIMP
 SlicedInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
                              uint32_t aFlags,
                              uint32_t aRequestedCount,
                              nsIEventTarget* aEventTarget)
 {
+  NS_ENSURE_STATE(mInputStream);
+
   nsCOMPtr<nsIAsyncInputStream> asyncStream =
     do_QueryInterface(mInputStream);
   if (!asyncStream) {
     return NS_ERROR_FAILURE;
   }
 
   return asyncStream->AsyncWait(aCallback, aFlags, aRequestedCount,
                                 aEventTarget);
 }
+
+// nsIIPCSerializableInputStream
+
+void
+SlicedInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
+                             FileDescriptorArray& aFileDescriptors)
+{
+  MOZ_ASSERT(mInputStream);
+  MOZ_ASSERT(mWeakIPCSerializableInputStream);
+
+  SlicedInputStreamParams params;
+  SerializeInputStream(mInputStream, params.stream(), aFileDescriptors);
+  params.start() = mStart;
+  params.length() = mLength;
+  params.curPos() = mCurPos;
+  params.closed() = mClosed;
+
+  aParams = params;
+}
+
+bool
+SlicedInputStream::Deserialize(const mozilla::ipc::InputStreamParams& aParams,
+                               const FileDescriptorArray& aFileDescriptors)
+{
+  MOZ_ASSERT(!mInputStream);
+  MOZ_ASSERT(!mWeakIPCSerializableInputStream);
+
+  if (aParams.type() !=
+      InputStreamParams::TSlicedInputStreamParams) {
+    NS_ERROR("Received unknown parameters from the other process!");
+    return false;
+  }
+
+  const SlicedInputStreamParams& params =
+    aParams.get_SlicedInputStreamParams();
+
+  nsCOMPtr<nsIInputStream> stream =
+    DeserializeInputStream(params.stream(), aFileDescriptors);
+  if (!stream) {
+    NS_WARNING("Deserialize failed!");
+    return false;
+  }
+
+  SetSourceStream(stream);
+
+  mStart = params.start();
+  mLength = params.length();
+  mCurPos = params.curPos();
+  mClosed = params.closed();
+
+  return true;
+}
+
+mozilla::Maybe<uint64_t>
+SlicedInputStream::ExpectedSerializedLength()
+{
+  if (!mInputStream || !mWeakIPCSerializableInputStream) {
+    return mozilla::Nothing();
+  }
+
+  return mWeakIPCSerializableInputStream->ExpectedSerializedLength();
+}
+
+// nsISeekableStream
+
+NS_IMETHODIMP
+SlicedInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+
+  int64_t offset;
+  switch (aWhence) {
+    case NS_SEEK_SET:
+      offset = mStart + aOffset;
+      break;
+    case NS_SEEK_CUR:
+      offset = mStart + mCurPos + aOffset;
+      break;
+    case NS_SEEK_END:
+      offset = mStart + mLength + aOffset;
+      break;
+   default:
+     return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  if (offset < (int64_t)mStart || offset > (int64_t)(mStart + mLength)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsresult rv = mWeakSeekableInputStream->Seek(NS_SEEK_SET, offset);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mCurPos = offset - mStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SlicedInputStream::Tell(int64_t *aResult)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+
+  int64_t tell = 0;
+
+  nsresult rv = mWeakSeekableInputStream->Tell(&tell);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (tell < (int64_t)mStart) {
+    *aResult = 0;
+    return NS_OK;
+  }
+
+  *aResult = tell - mStart;
+  if (*aResult > (int64_t)mLength) {
+    *aResult = mLength;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SlicedInputStream::SetEOF()
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+
+  mClosed = true;
+  return mWeakSeekableInputStream->SetEOF();
+}
--- a/xpcom/io/SlicedInputStream.h
+++ b/xpcom/io/SlicedInputStream.h
@@ -5,46 +5,64 @@
 
 #ifndef SlicedInputStream_h
 #define SlicedInputStream_h
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nsIAsyncInputStream.h"
 #include "nsICloneableInputStream.h"
+#include "nsIIPCSerializableInputStream.h"
+#include "nsISeekableStream.h"
 
 // A wrapper for a slice of an underlying input stream.
 
 class SlicedInputStream final : public nsIAsyncInputStream
                               , public nsICloneableInputStream
+                              , public nsIIPCSerializableInputStream
+                              , public nsISeekableStream
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
+  NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
-  NS_DECL_NSIASYNCINPUTSTREAM
+  NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+  NS_DECL_NSISEEKABLESTREAM
 
   // Create an input stream whose data comes from a slice of aInputStream.  The
   // slice begins at aStart bytes beyond aInputStream's current position, and
   // extends for a maximum of aLength bytes.  If aInputStream contains fewer
   // than aStart bytes, reading from SlicedInputStream returns no data.  If
   // aInputStream contains more than aStart bytes, but fewer than aStart +
   // aLength bytes, reading from SlicedInputStream returns as many bytes as can
   // be consumed from aInputStream after reading aLength bytes.
   //
   // aInputStream should not be read from after constructing a
   // SlicedInputStream wrapper around it.
 
   SlicedInputStream(nsIInputStream* aInputStream,
                     uint64_t aStart, uint64_t aLength);
 
+  // This CTOR is meant to be used just for IPC.
+  SlicedInputStream();
+
 private:
   ~SlicedInputStream();
 
+  void
+  SetSourceStream(nsIInputStream* aInputStream);
+
   nsCOMPtr<nsIInputStream> mInputStream;
+
+  // Raw pointers because these are just QI of mInputStream.
+  nsICloneableInputStream* mWeakCloneableInputStream;
+  nsIIPCSerializableInputStream* mWeakIPCSerializableInputStream;
+  nsISeekableStream* mWeakSeekableInputStream;
+
   uint64_t mStart;
   uint64_t mLength;
   uint64_t mCurPos;
 
   bool mClosed;
 };
 
 #endif // SlicedInputStream_h