merge mozilla-central to autoland. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sat, 07 Oct 2017 10:52:29 +0200
changeset 676437 591f6b2788a91ee51338ae5f7c775dc88cebf303
parent 676436 638ce81da2e381ae8c4764a585705f23327329c2 (current diff)
parent 676397 6b0491f83229a80462793c74825f79b1ec1d9cda (diff)
child 676438 05315cefb232192e4a73417bdfdf3e12b0cfeee6
push id83480
push userbmo:emilio@crisal.io
push dateSat, 07 Oct 2017 12:44:35 +0000
reviewersmerge, merge
milestone58.0a1
merge mozilla-central to autoland. r=merge a=merge
browser/locales/central-changesets.json
docshell/base/nsIContentViewerFile.idl
dom/base/FragmentOrElement.cpp
dom/file/TemporaryBlobImpl.cpp
dom/file/TemporaryBlobImpl.h
dom/interfaces/html/moz.build
dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
dom/media/EncodedBufferCache.cpp
dom/media/EncodedBufferCache.h
gfx/angle/README.mozilla
gfx/angle/src/common/BitSetIterator.h
gfx/angle/src/common/BitSetIterator_unittest.cpp
gfx/angle/src/common/third_party/numerics/base/logging.h
gfx/angle/src/common/third_party/numerics/base/numerics/OWNERS
gfx/angle/src/common/third_party/numerics/base/numerics/safe_conversions_impl.h
gfx/angle/src/common/third_party/numerics/base/numerics/safe_numerics_unittest.cc
gfx/angle/src/compiler/translator/InitializeParseContext.cpp
gfx/angle/src/compiler/translator/InitializeParseContext.h
gfx/angle/src/compiler/translator/Intermediate.cpp
gfx/angle/src/compiler/translator/Intermediate.h
gfx/angle/src/compiler/translator/VariableInfo.cpp
gfx/angle/src/compiler/translator/VariableInfo.h
gfx/angle/src/compiler/translator/intermOut.cpp
gfx/angle/src/libANGLE/renderer/d3d/WorkaroundsD3D.h
gfx/angle/src/libANGLE/renderer/null/FenceSyncNULL.cpp
gfx/angle/src/libANGLE/signal_utils.cpp
gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp
gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp
gfx/angle/src/tests/WebGL-CTS-known-failures.txt
gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp
gfx/angle/src/third_party/murmurhash/LICENSE
gfx/angle/src/third_party/murmurhash/MurmurHash3.cpp
gfx/angle/src/third_party/murmurhash/MurmurHash3.h
layout/generic/nsFrameUtil.cpp
layout/generic/nsIFrameUtil.h
layout/printing/nsPrintEngine.cpp
layout/tools/layout-debug/src/nsILayoutRegressionTester.idl
layout/tools/layout-debug/src/nsRegressionTester.cpp
layout/tools/layout-debug/src/nsRegressionTester.h
layout/tools/tests/regression_tests.html
layout/tools/tests/regression_tests.js
modules/libpref/init/all.js
netwerk/base/nsTemporaryFileInputStream.cpp
netwerk/base/nsTemporaryFileInputStream.h
testing/talos/talos/pageloader/chrome/memory.js
testing/talos/talos/startup_test/sessionrestore/addon/sessionrestore-signed.xpi
testing/talos/talos/startup_test/tresize/addon/tresize-signed.xpi
testing/talos/talos/talos-powers/talos-powers-signed.xpi
testing/talos/talos/tests/cpstartup/cpstartup-signed.xpi
testing/talos/talos/tests/devtools/addon/devtools-signed.xpi
testing/talos/talos/tests/tabpaint/tabpaint-signed.xpi
testing/talos/talos/tests/tabswitch/tabswitch-signed.xpi
testing/talos/talos/tests/tart/addon/tart-signed.xpi
testing/web-platform/meta/service-workers/service-worker/resource-timing.https.html.ini
third_party/speedometer/resources/todomvc/architecture-examples/angular/dist/inline.28efc571905d42a672b0.bundle.js
third_party/speedometer/resources/todomvc/architecture-examples/angular/dist/main.c82799edffc751aa6590.bundle.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/assets/todomvc.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/assets/vendor.css
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/assets/vendor.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/crossdomain.xml
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/index.html
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/robots.txt
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/.bowerrc
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/.editorconfig
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/.ember-cli
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/.gitignore
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/.jshintrc
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/.travis.yml
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/.watchmanconfig
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/README.md
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/app.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/components/todo-item.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/components/todo-list.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/controllers/active.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/controllers/application.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/controllers/completed.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/helpers/gt.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/helpers/pluralize.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/index.html
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/resolver.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/router.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/routes/application.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/services/memory.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/services/repo.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/templates/active.hbs
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/templates/application.hbs
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/templates/completed.hbs
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/templates/components/todo-item.hbs
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/templates/components/todo-list.hbs
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/app/templates/index.hbs
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/bower.json
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/config/environment.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/dist/assets/todomvc-7601ffca6150ed633f0f333fe76bfaf2.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/dist/assets/vendor-7b5c98520910afa58d74e05ec86cd873.css
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/dist/assets/vendor-820919567eb7bd4d9fac358a90a5aac4.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/dist/crossdomain.xml
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/dist/index.html
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/dist/robots.txt
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/ember-cli-build.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/package-lock.json
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/package.json
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/public/crossdomain.xml
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/public/robots.txt
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/testem.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/tests/helpers/destroy-app.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/tests/helpers/module-for-acceptance.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/tests/helpers/resolver.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/tests/helpers/start-app.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/tests/index.html
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/tests/test-helper.js
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/vendor/base.css
third_party/speedometer/resources/todomvc/architecture-examples/emberjs/source/vendor/index.css
third_party/speedometer/resources/todomvc/architecture-examples/react-redux/dist/static/css/main.f04a7319.css
third_party/speedometer/resources/todomvc/architecture-examples/react-redux/dist/static/css/main.f04a7319.css.map
third_party/speedometer/resources/todomvc/architecture-examples/react-redux/dist/static/js/main.69cd9655.js
third_party/speedometer/resources/todomvc/architecture-examples/react-redux/dist/static/js/main.69cd9655.js.map
third_party/speedometer/resources/todomvc/architecture-examples/vuejs-cli/dist/static/css/app.dca4292ab322c1e7d51534e72981032e.css
third_party/speedometer/resources/todomvc/architecture-examples/vuejs-cli/dist/static/css/app.dca4292ab322c1e7d51534e72981032e.css.map
third_party/speedometer/resources/todomvc/architecture-examples/vuejs-cli/dist/static/js/app.d6ed8935438f44e9843a.js
third_party/speedometer/resources/todomvc/architecture-examples/vuejs-cli/dist/static/js/app.d6ed8935438f44e9843a.js.map
third_party/speedometer/resources/todomvc/architecture-examples/vuejs-cli/dist/static/js/manifest.a307c7f9a5da5dc72bb5.js
third_party/speedometer/resources/todomvc/architecture-examples/vuejs-cli/dist/static/js/manifest.a307c7f9a5da5dc72bb5.js.map
third_party/speedometer/resources/todomvc/architecture-examples/vuejs-cli/dist/static/js/vendor.2e4e9e528664fa796d3a.js
third_party/speedometer/resources/todomvc/architecture-examples/vuejs-cli/dist/static/js/vendor.2e4e9e528664fa796d3a.js.map
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/bundle.app.71bb1f671e4e65604d05.js
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/bundle.app.71bb1f671e4e65604d05.js.map
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/bundle.manifest.2102040c9a6e04cc046e.js
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/bundle.manifest.2102040c9a6e04cc046e.js.map
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/bundle.vendor.b16cc08e016d07886f5f.js
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/bundle.vendor.b16cc08e016d07886f5f.js.map
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/styles.app.71bb1f671e4e65604d05.css
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/styles.app.71bb1f671e4e65604d05.css.map
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/styles.vendor.b16cc08e016d07886f5f.css
third_party/speedometer/resources/todomvc/vanilla-examples/es2015-babel-webpack/dist/styles.vendor.b16cc08e016d07886f5f.css.map
third_party/speedometer/resources/todomvc/vanilla-examples/es2015/node_modules/todomvc-app-css/package.json
third_party/speedometer/resources/todomvc/vanilla-examples/es2015/node_modules/todomvc-app-css/readme.md
third_party/speedometer/resources/todomvc/vanilla-examples/es2015/node_modules/todomvc-common/package.json
third_party/speedometer/resources/todomvc/vanilla-examples/es2015/node_modules/todomvc-common/readme.md
xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
--- a/.cron.yml
+++ b/.cron.yml
@@ -97,12 +97,18 @@ jobs:
           by-project:
               mozilla-central: [{hour: 10, minute: 0}]
               # No default
 
     - name: periodic-update
       job:
           type: decision-task
           treeherder-symbol: Nfile
-          target-tasks-method: nightly_file_update
+          target-tasks-method: file_update
       run-on-projects:
           - mozilla-central
-      when: []  # don't run for now due to unforeseen issues
+      when:
+          by-project:
+              # No default branch
+              mozilla-central:
+                  # Buildbot start time is 10:02am UTC, until we are able to
+                  # disable buildbot scheduling, use +12h
+                  - {hour: 22, minute: 0}
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -29,17 +29,17 @@ namespace a11y {
 
 bool
 ProxyAccessible::GetCOMInterface(void** aOutAccessible) const
 {
   if (!aOutAccessible) {
     return false;
   }
 
-  if (!mCOMProxy) {
+  if (!mCOMProxy && mSafeToRecurse) {
     // See if we can lazily obtain a COM proxy
     AccessibleWrap* wrap = WrapperFor(this);
     bool isDefunct = false;
     ProxyAccessible* thisPtr = const_cast<ProxyAccessible*>(this);
     // NB: Don't pass CHILDID_SELF here, use the absolute MSAA ID. Otherwise
     // GetIAccessibleFor will recurse into this function and we will just
     // overflow the stack.
     VARIANT realId = {VT_I4};
--- a/accessible/ipc/win/ProxyAccessible.h
+++ b/accessible/ipc/win/ProxyAccessible.h
@@ -22,36 +22,47 @@ namespace mozilla {
 namespace a11y {
 
 class ProxyAccessible : public ProxyAccessibleBase<ProxyAccessible>
 {
 public:
   ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
                   DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces)
     : ProxyAccessibleBase(aID, aParent, aDoc, aRole, aInterfaces)
+    , mSafeToRecurse(true)
   {
     MOZ_COUNT_CTOR(ProxyAccessible);
   }
 
   ~ProxyAccessible()
   {
     MOZ_COUNT_DTOR(ProxyAccessible);
   }
 
 #include "mozilla/a11y/ProxyAccessibleShared.h"
 
   bool GetCOMInterface(void** aOutAccessible) const;
   void SetCOMInterface(const RefPtr<IAccessible>& aIAccessible)
-  { mCOMProxy = aIAccessible; }
+  {
+    if (aIAccessible) {
+      mCOMProxy = aIAccessible;
+    } else {
+      // If we were supposed to be receiving an interface (hence the call to
+      // this function), but the interface turns out to be null, then we're
+      // broken for some reason.
+      mSafeToRecurse = false;
+    }
+  }
 
 protected:
   explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc)
     : ProxyAccessibleBase(aThisAsDoc)
   { MOZ_COUNT_CTOR(ProxyAccessible); }
 
 private:
   RefPtr<IAccessible> mCOMProxy;
+  bool                mSafeToRecurse;
 };
 
 }
 }
 
 #endif
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -318,16 +318,17 @@ toolbarpaletteitem {
 %endif
 
 %endif
 
 #main-window[inFullscreen][inDOMFullscreen] #navigator-toolbox,
 #main-window[inFullscreen][inDOMFullscreen] #fullscr-toggler,
 #main-window[inFullscreen][inDOMFullscreen] #sidebar-box,
 #main-window[inFullscreen][inDOMFullscreen] #sidebar-splitter,
+#main-window[inFullscreen][inDOMFullscreen] #developer-toolbar,
 #main-window[inFullscreen]:not([OSXLionFullscreen]) toolbar:not([fullscreentoolbar=true]),
 #main-window[inFullscreen] #global-notificationbox,
 #main-window[inFullscreen] #high-priority-global-notificationbox {
   visibility: collapse;
 }
 
 #navigator-toolbox[fullscreenShouldAnimate] {
   transition: 1.5s margin-top ease-out;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -567,20 +567,16 @@
   </popupset>
   <box id="appMenu-viewCache" hidden="true"/>
 
 #ifdef CAN_DRAW_IN_TITLEBAR
 <vbox id="titlebar">
   <hbox id="titlebar-content">
     <spacer id="titlebar-spacer" flex="1"/>
     <hbox id="titlebar-buttonbox-container">
-#ifdef XP_WIN
-      <button class="accessibility-indicator" tooltiptext="&accessibilityIndicator.tooltip;" aria-live="polite"/>
-      <hbox class="private-browsing-indicator"/>
-#endif
       <hbox id="titlebar-buttonbox">
         <toolbarbutton class="titlebar-button" id="titlebar-min" oncommand="window.minimize();"/>
         <toolbarbutton class="titlebar-button" id="titlebar-max" oncommand="onTitlebarMaxClick();"/>
         <toolbarbutton class="titlebar-button" id="titlebar-close" command="cmd_closeWindow"/>
       </hbox>
     </hbox>
 #ifdef XP_MACOSX
     <!-- OS X does not natively support RTL for its titlebar items, so we prevent this secondary
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5394,20 +5394,16 @@
       <property name="markupDocumentViewer"
                 onget="return this.mCurrentBrowser.markupDocumentViewer;"
                 readonly="true"/>
 
       <property name="contentViewerEdit"
                 onget="return this.mCurrentBrowser.contentViewerEdit;"
                 readonly="true"/>
 
-      <property name="contentViewerFile"
-                onget="return this.mCurrentBrowser.contentViewerFile;"
-                readonly="true"/>
-
       <property name="contentDocument"
                 onget="return this.mCurrentBrowser.contentDocument;"
                 readonly="true"/>
 
       <property name="contentDocumentAsCPOW"
                 onget="return this.mCurrentBrowser.contentDocumentAsCPOW;"
                 readonly="true"/>
 
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.9.607
+Current extension version is: 1.9.630
 
-Taken from upstream commit: b3f84112
+Taken from upstream commit: ec469673
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -701,19 +701,16 @@ var Util = function UtilClosure() {
     if (orderedY[0] === rect1[1] && orderedY[1] === rect2[1] || orderedY[0] === rect2[1] && orderedY[1] === rect1[1]) {
       result[1] = orderedY[1];
       result[3] = orderedY[2];
     } else {
       return false;
     }
     return result;
   };
-  Util.sign = function Util_sign(num) {
-    return num < 0 ? -1 : 1;
-  };
   var ROMAN_NUMBER_MAP = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
   Util.toRoman = function Util_toRoman(number, lowerCase) {
     assert(Number.isInteger(number) && number > 0, 'The number should be a positive integer.');
     var pos,
         romanBuf = [];
     while (number >= 1000) {
       number -= 1000;
       romanBuf.push('M');
@@ -1988,17 +1985,17 @@ function getDocument(src, pdfDataRangeTr
     });
   }).catch(task._capability.reject);
   return task;
 }
 function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
   if (worker.destroyed) {
     return Promise.reject(new Error('Worker was destroyed'));
   }
-  let apiVersion = '1.9.607';
+  let apiVersion = '1.9.630';
   source.disableAutoFetch = (0, _dom_utils.getDefaultSetting)('disableAutoFetch');
   source.disableStream = (0, _dom_utils.getDefaultSetting)('disableStream');
   source.chunkedViewerLoading = !!pdfDataRangeTransport;
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
@@ -3309,18 +3306,18 @@ var _UnsupportedManager = function Unsup
       for (var i = 0, ii = listeners.length; i < ii; i++) {
         listeners[i](featureId);
       }
     }
   };
 }();
 var version, build;
 {
-  exports.version = version = '1.9.607';
-  exports.build = build = 'b3f84112';
+  exports.version = version = '1.9.630';
+  exports.build = build = 'ec469673';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamClass = setPDFNetworkStreamClass;
@@ -5049,18 +5046,18 @@ exports.SVGGraphics = SVGGraphics;
 
 /***/ }),
 /* 9 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.9.607';
-var pdfjsBuild = 'b3f84112';
+var pdfjsVersion = '1.9.630';
+var pdfjsBuild = 'ec469673';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(13);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(7);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(6);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(8);
 ;
@@ -8148,17 +8145,17 @@ if (isReadableStreamSupported) {
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.PDFJS = exports.isWorker = exports.globalScope = undefined;
+exports.PDFJS = exports.globalScope = undefined;
 
 var _api = __w_pdfjs_require__(3);
 
 var _dom_utils = __w_pdfjs_require__(1);
 
 var _util = __w_pdfjs_require__(0);
 
 var _annotation_layer = __w_pdfjs_require__(6);
@@ -8170,24 +8167,23 @@ var _global_scope2 = _interopRequireDefa
 var _metadata = __w_pdfjs_require__(5);
 
 var _text_layer = __w_pdfjs_require__(7);
 
 var _svg = __w_pdfjs_require__(8);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-var isWorker = typeof window === 'undefined';
 if (!_global_scope2.default.PDFJS) {
   _global_scope2.default.PDFJS = {};
 }
 var PDFJS = _global_scope2.default.PDFJS;
 {
-  PDFJS.version = '1.9.607';
-  PDFJS.build = 'b3f84112';
+  PDFJS.version = '1.9.630';
+  PDFJS.build = 'ec469673';
 }
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
   (0, _util.setVerbosityLevel)(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
   get() {
@@ -8255,17 +8251,16 @@ PDFJS.addLinkAttributes = _dom_utils.add
 PDFJS.getFilenameFromUrl = _dom_utils.getFilenameFromUrl;
 PDFJS.isExternalLinkTargetSet = _dom_utils.isExternalLinkTargetSet;
 PDFJS.AnnotationLayer = _annotation_layer.AnnotationLayer;
 PDFJS.renderTextLayer = _text_layer.renderTextLayer;
 PDFJS.Metadata = _metadata.Metadata;
 PDFJS.SVGGraphics = _svg.SVGGraphics;
 PDFJS.UnsupportedManager = _api._UnsupportedManager;
 exports.globalScope = _global_scope2.default;
-exports.isWorker = isWorker;
 exports.PDFJS = PDFJS;
 
 /***/ }),
 /* 14 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -701,19 +701,16 @@ var Util = function UtilClosure() {
     if (orderedY[0] === rect1[1] && orderedY[1] === rect2[1] || orderedY[0] === rect2[1] && orderedY[1] === rect1[1]) {
       result[1] = orderedY[1];
       result[3] = orderedY[2];
     } else {
       return false;
     }
     return result;
   };
-  Util.sign = function Util_sign(num) {
-    return num < 0 ? -1 : 1;
-  };
   var ROMAN_NUMBER_MAP = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
   Util.toRoman = function Util_toRoman(number, lowerCase) {
     assert(Number.isInteger(number) && number > 0, 'The number should be a positive integer.');
     var pos,
         romanBuf = [];
     while (number >= 1000) {
       number -= 1000;
       romanBuf.push('M');
@@ -23821,18 +23818,18 @@ exports.PostScriptCompiler = PostScriptC
 
 /***/ }),
 /* 17 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.9.607';
-var pdfjsBuild = 'b3f84112';
+var pdfjsVersion = '1.9.630';
+var pdfjsBuild = 'ec469673';
 var pdfjsCoreWorker = __w_pdfjs_require__(18);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 18 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -24017,17 +24014,17 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '1.9.607';
+    let workerVersion = '1.9.630';
     if (apiVersion !== null && apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
     var handler = new _util.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -4436,17 +4436,17 @@ class PDFHistory {
     let state = window.history.state;
     this.initialized = true;
     this.initialBookmark = null;
     this.initialRotation = null;
     this._popStateInProgress = false;
     this._blockHashChange = 0;
     this._currentHash = getCurrentHash();
     this._numPositionUpdates = 0;
-    this._currentUid = this._uid = 0;
+    this._uid = this._maxUid = 0;
     this._destination = null;
     this._position = null;
     if (!this._isValidState(state) || resetHistory) {
       let { hash, page, rotation } = parseCurrentHash(this.linkService);
       if (!hash || reInitialized || resetHistory) {
         this._pushOrReplaceState(null, true);
         return;
       }
@@ -4521,34 +4521,35 @@ class PDFHistory {
       window.history.back();
     }
   }
   forward() {
     if (!this.initialized || this._popStateInProgress) {
       return;
     }
     let state = window.history.state;
-    if (this._isValidState(state) && state.uid < this._uid - 1) {
+    if (this._isValidState(state) && state.uid < this._maxUid) {
       window.history.forward();
     }
   }
   get popStateInProgress() {
     return this.initialized && (this._popStateInProgress || this._blockHashChange > 0);
   }
   _pushOrReplaceState(destination, forceReplace = false) {
     let shouldReplace = forceReplace || !this._destination;
     let newState = {
       fingerprint: this.fingerprint,
-      uid: shouldReplace ? this._currentUid : this._uid,
+      uid: shouldReplace ? this._uid : this._uid + 1,
       destination
     };
     this._updateInternalState(destination, newState.uid);
     if (shouldReplace) {
       window.history.replaceState(newState, '');
     } else {
+      this._maxUid = this._uid;
       window.history.pushState(newState, '');
     }
   }
   _tryPushCurrentPosition(temporary = false) {
     if (!this._position) {
       return;
     }
     let position = this._position;
@@ -4598,18 +4599,17 @@ class PDFHistory {
     if (this._updateViewareaTimeout) {
       clearTimeout(this._updateViewareaTimeout);
       this._updateViewareaTimeout = null;
     }
     if (removeTemporary && destination && destination.temporary) {
       delete destination.temporary;
     }
     this._destination = destination;
-    this._currentUid = uid;
-    this._uid = this._currentUid + 1;
+    this._uid = uid;
     this._numPositionUpdates = 0;
   }
   _updateViewarea({ location }) {
     if (this._updateViewareaTimeout) {
       clearTimeout(this._updateViewareaTimeout);
       this._updateViewareaTimeout = null;
     }
     this._position = {
@@ -4633,17 +4633,17 @@ class PDFHistory {
       }, UPDATE_VIEWAREA_TIMEOUT);
     }
   }
   _popState({ state }) {
     let newHash = getCurrentHash(),
         hashChanged = this._currentHash !== newHash;
     this._currentHash = newHash;
     if (!state || false) {
-      this._currentUid = this._uid;
+      this._uid++;
       let { hash, page, rotation } = parseCurrentHash(this.linkService);
       this._pushOrReplaceState({
         hash,
         page,
         rotation
       }, true);
       return;
     }
deleted file mode 100644
--- a/browser/locales/central-changesets.json
+++ /dev/null
@@ -1,997 +0,0 @@
-{
-    "ach": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "af": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "an": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ar": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "as": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ast": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "az": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "be": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "bg": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "bn-BD": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "bn-IN": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "br": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "bs": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ca": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "cak": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "cs": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "cy": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "da": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "de": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "dsb": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "el": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "en-GB": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "en-ZA": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "eo": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "es-AR": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "es-CL": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "es-ES": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "es-MX": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "et": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "eu": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "fa": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ff": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "fi": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "fr": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "fy-NL": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ga-IE": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "gd": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "gl": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "gn": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "gu-IN": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "he": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "hi-IN": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "hr": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "hsb": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "hu": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "hy-AM": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ia": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "id": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "is": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "it": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ja": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ja-JP-mac": {
-        "platforms": [
-            "macosx64"
-        ], 
-        "revision": "default"
-    }, 
-    "ka": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "kab": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "kk": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "km": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "kn": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ko": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "lij": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "lo": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "lt": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ltg": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "lv": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "mai": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "mk": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ml": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "mr": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ms": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "my": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "nb-NO": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ne-NP": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "nl": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "nn-NO": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "or": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "pa-IN": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "pl": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "pt-BR": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "pt-PT": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "rm": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ro": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ru": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "si": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "sk": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "sl": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "son": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "sq": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "sr": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "sv-SE": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ta": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "te": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "th": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "tl": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "tr": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "uk": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "ur": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "uz": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "vi": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "xh": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "zh-CN": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }, 
-    "zh-TW": {
-        "platforms": [
-            "linux", 
-            "linux64", 
-            "macosx64", 
-            "win32", 
-            "win64"
-        ], 
-        "revision": "default"
-    }
-}
--- a/browser/themes/shared/places/folder.svg
+++ b/browser/themes/shared/places/folder.svg
@@ -1,10 +1,9 @@
 <!-- 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/. -->
 <svg width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
-  <path fill-opacity=".15" d="M5,1H1C0.4,1,0,1.4,0,2v12.1C0,14.6,0.4,15,0.9,15h14.2c0.5,0,0.9-0.4,0.9-0.9V3.9C16,3.4,15.6,3,15.1,3H7 L6.2,1.9C6.2,1.9,5.6,1,5,1L5,1z"/>
-  <path d="M4.9,2C5,2.1,5.2,2.3,5.4,2.5l0.8,1.1L6.5,4H7h7.5C14.8,4,15,4.2,15,4.5v9c0,0.3-0.2,0.5-0.5,0.5h-13 C1.2,14,1,13.8,1,13.5v-11C1,2.2,1.2,2,1.5,2H4.9 M5,1H1C0.4,1,0,1.4,0,2v12.1C0,14.6,0.4,15,0.9,15h14.2c0.5,0,0.9-0.4,0.9-0.9V3.9 C16,3.4,15.6,3,15.1,3H7L6.2,1.9C6.2,1.9,5.6,1,5,1L5,1z"/>
-  <path fill-opacity=".15" d="M14,5H2C0.9,5,0,5.9,0,7v7c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1V7C16,5.9,15.1,5,14,5L14,5z"/>
-  <path fill-opacity=".15" d="M16,13H0v1c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1V13z"/>
-  <path d="M14,6c0.6,0,1,0.4,1,1v6.5c0,0.3-0.2,0.5-0.5,0.5h-13C1.2,14,1,13.8,1,13.5V7c0-0.6,0.4-1,1-1H14 M14,5H2 C0.9,5,0,5.9,0,7v7c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1V7C16,5.9,15.1,5,14,5L14,5z"/>
+  <path d="M14.5 3H6.914a.5.5 0 0 1-.354-.146L5.146 1.439A1.491 1.491 0 0 0 4.086 1H1.5A1.5 1.5 0 0 0 0 2.5v11A1.5 1.5 0 0 0 1.5 15h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 3zm.5 10.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V6h14zM1 5V2.5a.5.5 0 0 1 .5-.5h2.586a.5.5 0 0 1 .354.146l1.414 1.415A1.491 1.491 0 0 0 6.914 4H14.5a.5.5 0 0 1 .5.5V5z"/>
+  <path d="M15 13.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V6h14z" fill-opacity=".2"/>
+  <path d="M1 5V2.5a.5.5 0 0 1 .5-.5h2.586a.5.5 0 0 1 .354.146l1.414 1.415A1.491 1.491 0 0 0 6.914 4H14.5a.5.5 0 0 1 .5.5V5z" fill-opacity=".1"/>
+  <path d="M15 13.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V13h14z" fill-opacity=".05"/>
 </svg>
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1071,46 +1071,43 @@ notification[value="translation"] {
 #main-window:-moz-any([customize-entering],[customize-exiting]) label {
   transform: perspective(0.01px);
 }
 
 /* End customization mode */
 
 /* Private browsing and accessibility indicators */
 
+:root[sizemode="normal"][chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar > .private-browsing-indicator,
+:root[sizemode="normal"][chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar > .accessibility-indicator,
+:root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar > .private-browsing-indicator,
+:root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar > .accessibility-indicator {
+  margin-top: calc(-1 * var(--space-above-tabbar));
+}
+
+/* Compensate for 4px extra margin on top of the tabs toolbar on Windows 7. */
 @media (-moz-os-version: windows-win7) {
-  /* Making sure that indicators take up all available vertical space. */
-  :root[tabsintitlebar]:not([inFullscreen]) .private-browsing-indicator,
-  :root[tabsintitlebar]:not([inFullscreen]) .accessibility-indicator {
-    height: var(--tab-min-height);
-  }
-
-  :root[tabsintitlebar][sizemode="normal"]:not([inFullscreen]) .private-browsing-indicator,
-  :root[tabsintitlebar][sizemode="normal"]:not([inFullscreen]) .accessibility-indicator {
-    height: calc(var(--tab-min-height) + 4px);
+  :root[sizemode="normal"][chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar > .private-browsing-indicator,
+  :root[sizemode="normal"][chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar > .accessibility-indicator,
+  :root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar > .private-browsing-indicator,
+  :root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar > .accessibility-indicator {
+    margin-top: calc(-1 * (var(--space-above-tabbar) + 4px));
   }
 }
 
-:root:-moz-any([tabsintitlebar], [inFullscreen]):not([privatebrowsingmode=temporary]) .accessibility-indicator,
-:root:-moz-any([tabsintitlebar], [inFullscreen]) .private-browsing-indicator {
+:root:not([privatebrowsingmode=temporary]) .accessibility-indicator,
+.private-browsing-indicator {
   margin-inline-end: 12px;
 }
 
 :root:not([accessibilitymode]) .private-browsing-indicator,
 .accessibility-indicator {
   margin-inline-start: 12px;
 }
 
-:root[accessibilitymode][tabsintitlebar]:not([inFullscreen]) > #tab-view-deck > #browser-panel > #navigator-toolbox > #TabsToolbar > .accessibility-indicator,
-:root[privatebrowsingmode=temporary][tabsintitlebar]:not([inFullscreen]) > #tab-view-deck > #browser-panel > #navigator-toolbox > #TabsToolbar > .private-browsing-indicator,
-:root[accessibilitymode]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .accessibility-indicator,
-:root[privatebrowsingmode=temporary]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator {
-  display: none;
-}
-
 /* End private browsing and accessibility indicators */
 
 %include ../shared/UITour.inc.css
 
 #UITourTooltipButtons {
   /**
    * Override the --arrowpanel-padding so the background extends
    * to the sides and bottom of the panel.
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -1245,19 +1245,19 @@ Inspector.prototype = {
     menu.append(new MenuItem({
       id: "node-menu-expand",
       label: INSPECTOR_L10N.getStr("inspectorExpandNode.label"),
       disabled: !isNodeWithChildren,
       click: () => this.expandNode(),
     }));
     menu.append(new MenuItem({
       id: "node-menu-collapse",
-      label: INSPECTOR_L10N.getStr("inspectorCollapseNode.label"),
+      label: INSPECTOR_L10N.getStr("inspectorCollapseAll.label"),
       disabled: !isNodeWithChildren || !markupContainer.expanded,
-      click: () => this.collapseNode(),
+      click: () => this.collapseAll(),
     }));
 
     menu.append(new MenuItem({
       type: "separator",
     }));
 
     menu.append(new MenuItem({
       id: "node-menu-scrollnodeintoview",
@@ -1976,18 +1976,18 @@ Inspector.prototype = {
     let container = this.markup.getContainer(this.selection.nodeFront);
     container.removeAttribute(this.nodeMenuTriggerInfo.name);
   },
 
   expandNode: function () {
     this.markup.expandAll(this.selection.nodeFront);
   },
 
-  collapseNode: function () {
-    this.markup.collapseNode(this.selection.nodeFront);
+  collapseAll: function () {
+    this.markup.collapseAll(this.selection.nodeFront);
   },
 
   /**
    * This method is here for the benefit of the node-menu-link-follow menu item
    * in the inspector contextual-menu.
    */
   onFollowLink: function () {
     let type = this.contextMenuTarget.dataset.type;
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -258,20 +258,20 @@ inspectorShowDOMProperties.label=Show DO
 # this item opens the split Console.
 inspectorUseInConsole.label=Use in Console
 
 # LOCALIZATION NOTE (inspectorExpandNode.label): This is the label
 # shown in the inspector contextual-menu for recursively expanding
 # mark-up elements
 inspectorExpandNode.label=Expand All
 
-# LOCALIZATION NOTE (inspectorCollapseNode.label): This is the label
+# LOCALIZATION NOTE (inspectorCollapseAll.label): This is the label
 # shown in the inspector contextual-menu for recursively collapsing
 # mark-up elements
-inspectorCollapseNode.label=Collapse
+inspectorCollapseAll.label=Collapse All
 
 # LOCALIZATION NOTE (inspectorScreenshotNode.label): This is the label
 # shown in the inspector contextual-menu for the item that lets users take
 # a screenshot of the currently selected node.
 inspectorScreenshotNode.label=Screenshot Node
 
 # LOCALIZATION NOTE (inspectorDuplicateNode.label): This is the label
 # shown in the inspector contextual-menu for the item that lets users
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -1,17 +1,25 @@
 /* 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/. */
 
 :root {
+  --markup-hidden-attr-name-color: #CA60AC;
+  --markup-hidden-attr-value-color: #5C6D87;
+  --markup-hidden-punctuation-color: #909090;
+  --markup-hidden-tag-color: #97A4B3;
   --markup-outline: var(--theme-splitter-color);
 }
 
 .theme-dark:root {
+  --markup-hidden-attr-name-color: #CC8EC8;
+  --markup-hidden-attr-value-color: #9893A3;
+  --markup-hidden-punctuation-color: #909090;
+  --markup-hidden-tag-color: #AFB5BF;
   --markup-outline: var(--theme-selection-background);
 }
 
 * {
   padding: 0;
   margin: 0;
 }
 
@@ -327,16 +335,32 @@ ul.children + .tag-line::before {
   width: .8em;
   height: .8em;
   margin-top: .3em;
   left: 1px;
   position: absolute;
   z-index: 1;
 }
 
+.not-displayed {
+  color: var(--markup-hidden-punctuation-color);
+}
+
+.not-displayed .attr-name {
+  color: var(--markup-hidden-attr-name-color);
+}
+
+.not-displayed .attr-value {
+  color: var(--markup-hidden-attr-value-color);
+}
+
+.not-displayed .tag {
+  color: var(--markup-hidden-tag-color);
+}
+
 /* Firebug Theme */
 
 .theme-firebug .theme-fg-color3 {
   color: var(--theme-graphs-full-blue);
   font-weight: normal;
 }
 
 .theme-firebug .open,
@@ -348,16 +372,22 @@ ul.children + .tag-line::before {
 .theme-firebug .attr-value.theme-fg-color6 {
   color: var(--theme-highlight-red);
 }
 
 .theme-firebug .markupview-events {
   font-size: var(--theme-toolbar-font-size);
 }
 
+/* In case a node isn't displayed in the page, we fade the syntax highlighting */
+.theme-firebug .not-displayed .open,
+.theme-firebug .not-displayed .close {
+  opacity: .5;
+}
+
 /* Selected nodes in the tree should have light selected text.
    theme-selected doesn't work in this case since the text is a
    sibling of the class, not a child. */
 .theme-selected ~ .editor,
 .theme-selected ~ .editor .theme-fg-color1,
 .theme-selected ~ .editor .theme-fg-color2,
 .theme-selected ~ .editor .theme-fg-color3,
 .theme-selected ~ .editor .theme-fg-color4,
@@ -378,22 +408,16 @@ ul.children + .tag-line::before {
 .doctype {
   font-style: italic;
 }
 
 .theme-firebug .doctype {
   color: #787878;
 }
 
-/* In case a node isn't displayed in the page, we fade the syntax highlighting */
-.not-displayed .open,
-.not-displayed .close {
-  opacity: .5;
-}
-
 /* Events */
 .markupview-events {
   font-size: 8px;
   font-weight: bold;
   line-height: 10px;
   border-radius: 3px;
   padding: 0px 2px;
   margin-inline-start: 5px;
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -805,26 +805,26 @@ WebConsoleActor.prototype =
       let type = types.shift();
       switch (type) {
         case "ConsoleAPI": {
           if (!this.consoleAPIListener) {
             break;
           }
 
           // See `window` definition. It isn't always a DOM Window.
-          let requestStartTime = this.window && this.window.performance ?
-            this.window.performance.timing.requestStart : 0;
+          let winStartTime = this.window && this.window.performance ?
+            this.window.performance.timing.navigationStart : 0;
 
           let cache = this.consoleAPIListener
                       .getCachedMessages(!this.parentActor.isRootActor);
           cache.forEach((cachedMessage) => {
             // Filter out messages that came from a ServiceWorker but happened
             // before the page was requested.
             if (cachedMessage.innerID === "ServiceWorker" &&
-                requestStartTime > cachedMessage.timeStamp) {
+                winStartTime > cachedMessage.timeStamp) {
               return;
             }
 
             let message = this.prepareConsoleMessageForRemote(cachedMessage);
             message._type = type;
             messages.push(message);
           });
           break;
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -36,17 +36,16 @@ DIRS += [
 ]
 
 XPIDL_SOURCES += [
     'nsCDefaultURIFixup.idl',
     'nsIClipboardCommands.idl',
     'nsIContentViewer.idl',
     'nsIContentViewerContainer.idl',
     'nsIContentViewerEdit.idl',
-    'nsIContentViewerFile.idl',
     'nsIContextMenuListener.idl',
     'nsIContextMenuListener2.idl',
     'nsIDocCharset.idl',
     'nsIDocShell.idl',
     'nsIDocShellLoadInfo.idl',
     'nsIDocShellTreeItem.idl',
     'nsIDocShellTreeOwner.idl',
     'nsIDocumentLoaderFactory.idl',
deleted file mode 100644
--- a/docshell/base/nsIContentViewerFile.idl
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; 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 "nsISupports.idl"
-
-interface nsIDOMWindow;
-interface nsIPrintSettings;
-interface nsIWebProgressListener;
-
-%{ C++
-#include <stdio.h>
-%}
-
-[ptr] native FILE(FILE);
-
-/**
- * The nsIDocShellFile    
- */
-
-[scriptable, uuid(564a3276-6228-401e-9b5c-d82cb382a60f)]
-interface nsIContentViewerFile : nsISupports
-{
-  readonly attribute boolean printable;
-
-  [noscript] void print(in boolean aSilent,
-                        in FILE    aDebugFile, 
-                        in nsIPrintSettings aPrintSettings);
-};
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/CustomElementRegistry.h"
 
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/CustomElementRegistryBinding.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "mozilla/dom/WebComponentsBinding.h"
 #include "mozilla/dom/DocGroup.h"
 #include "nsHTMLTags.h"
 #include "jsapi.h"
 
 namespace mozilla {
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -42,25 +42,23 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOM
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   tmp->Disconnect();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 already_AddRefed<DOMIntersectionObserver>
 DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
                                      mozilla::dom::IntersectionCallback& aCb,
                                      mozilla::ErrorResult& aRv)
 {
@@ -76,17 +74,20 @@ DOMIntersectionObserver::Constructor(con
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
   RefPtr<DOMIntersectionObserver> observer =
     new DOMIntersectionObserver(window.forget(), aCb);
 
-  observer->mRoot = aOptions.mRoot;
+  if (aOptions.mRoot) {
+    observer->mRoot = aOptions.mRoot;
+    observer->mRoot->RegisterIntersectionObserver(observer);
+  }
 
   if (!observer->SetRootMargin(aOptions.mRootMargin)) {
     aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
       NS_LITERAL_CSTRING("rootMargin must be specified in pixels or percent."));
     return nullptr;
   }
 
   if (aOptions.mThreshold.IsDoubleSequence()) {
@@ -170,18 +171,23 @@ DOMIntersectionObserver::Unobserve(Eleme
     return;
   }
 
   mObservationTargets.RemoveElement(&aTarget);
   aTarget.UnregisterIntersectionObserver(this);
 }
 
 void
-DOMIntersectionObserver::UnlinkTarget(Element& aTarget)
+DOMIntersectionObserver::UnlinkElement(Element& aTarget)
 {
+  if (mRoot && mRoot == &aTarget) {
+    mRoot = nullptr;
+    Disconnect();
+    return;
+  }
   mObservationTargets.RemoveElement(&aTarget);
   if (mObservationTargets.Length() == 0) {
     Disconnect();
   }
 }
 
 void
 DOMIntersectionObserver::Connect()
--- a/dom/base/DOMIntersectionObserver.h
+++ b/dom/base/DOMIntersectionObserver.h
@@ -111,17 +111,21 @@ class DOMIntersectionObserver final : pu
 {
   virtual ~DOMIntersectionObserver() {
     Disconnect();
   }
 
 public:
   DOMIntersectionObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner,
                           mozilla::dom::IntersectionCallback& aCb)
-  : mOwner(aOwner), mDocument(mOwner->GetExtantDoc()), mCallback(&aCb), mConnected(false)
+  : mOwner(aOwner),
+    mDocument(mOwner->GetExtantDoc()),
+    mCallback(&aCb),
+    mRoot(nullptr),
+    mConnected(false)
   {
   }
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMIntersectionObserver)
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_INTERSECTION_OBSERVER_IID)
 
   static already_AddRefed<DOMIntersectionObserver>
   Constructor(const mozilla::dom::GlobalObject& aGlobal,
@@ -147,17 +151,17 @@ public:
     return mRoot;
   }
 
   void GetRootMargin(mozilla::dom::DOMString& aRetVal);
   void GetThresholds(nsTArray<double>& aRetVal);
   void Observe(Element& aTarget);
   void Unobserve(Element& aTarget);
 
-  void UnlinkTarget(Element& aTarget);
+  void UnlinkElement(Element& aTarget);
   void Disconnect();
 
   void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal);
 
   mozilla::dom::IntersectionCallback* IntersectionCallback() { return mCallback; }
 
   bool SetRootMargin(const nsAString& aString);
 
@@ -171,21 +175,22 @@ protected:
                                       const Maybe<nsRect>& aRootRect,
                                       const nsRect& aTargetRect,
                                       const Maybe<nsRect>& aIntersectionRect,
                                       double aIntersectionRatio);
 
   nsCOMPtr<nsPIDOMWindowInner>                    mOwner;
   RefPtr<nsIDocument>                             mDocument;
   RefPtr<mozilla::dom::IntersectionCallback>      mCallback;
-  RefPtr<Element>                                 mRoot;
+  // Raw pointer which is explicitly cleared by UnlinkElement().
+  Element*                                        mRoot;
   nsCSSRect                                       mRootMargin;
   nsTArray<double>                                mThresholds;
 
-  // Holds raw pointers which are explicitly cleared by UnlinkTarget().
+  // Holds raw pointers which are explicitly cleared by UnlinkElement().
   nsTArray<Element*>                              mObservationTargets;
 
   nsTArray<RefPtr<DOMIntersectionObserverEntry>>  mQueuedEntries;
   bool                                            mConnected;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(DOMIntersectionObserver, NS_DOM_INTERSECTION_OBSERVER_IID)
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -4189,52 +4189,88 @@ Element::ClearDataset()
 {
   nsDOMSlots *slots = GetExistingDOMSlots();
 
   MOZ_ASSERT(slots && slots->mDataset,
              "Slots should exist and dataset should not be null.");
   slots->mDataset = nullptr;
 }
 
-nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>*
-Element::RegisteredIntersectionObservers()
-{
-  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
-  return &slots->mRegisteredIntersectionObservers;
-}
-
 enum nsPreviousIntersectionThreshold {
   eUninitialized = -2,
   eNonIntersecting = -1
 };
 
 void
 Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver)
 {
-  RegisteredIntersectionObservers()->LookupForAdd(aObserver).OrInsert([]() {
-    // Value can be:
+  IntersectionObserverList* observers =
+    static_cast<IntersectionObserverList*>(
+      GetProperty(nsGkAtoms::intersectionobserverlist)
+    );
+
+  if (!observers) {
+    observers = new IntersectionObserverList();
+    observers->Put(aObserver, eUninitialized);
+    SetProperty(nsGkAtoms::intersectionobserverlist, observers,
+                nsINode::DeleteProperty<IntersectionObserverList>);
+    return;
+  }
+
+  observers->LookupForAdd(aObserver).OrInsert([]() {
+    // If element is being observed, value can be:
     //   -2:   Makes sure next calculated threshold always differs, leading to a
     //         notification task being scheduled.
     //   -1:   Non-intersecting.
     //   >= 0: Intersecting, valid index of aObserver->mThresholds.
     return eUninitialized;
   });
 }
 
 void
 Element::UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver)
 {
-  RegisteredIntersectionObservers()->Remove(aObserver);
+  IntersectionObserverList* observers =
+    static_cast<IntersectionObserverList*>(
+      GetProperty(nsGkAtoms::intersectionobserverlist)
+    );
+  if (observers) {
+    observers->Remove(aObserver);
+  }
+}
+
+void
+Element::UnlinkIntersectionObservers()
+{
+  IntersectionObserverList* observers =
+    static_cast<IntersectionObserverList*>(
+      GetProperty(nsGkAtoms::intersectionobserverlist)
+    );
+  if (!observers) {
+    return;
+  }
+  for (auto iter = observers->Iter(); !iter.Done(); iter.Next()) {
+    DOMIntersectionObserver* observer = iter.Key();
+    observer->UnlinkElement(*this);
+  }
+  observers->Clear();
 }
 
 bool
 Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t aThreshold)
 {
+  IntersectionObserverList* observers =
+    static_cast<IntersectionObserverList*>(
+      GetProperty(nsGkAtoms::intersectionobserverlist)
+    );
+  if (!observers) {
+    return false;
+  }
   bool updated = false;
-  if (auto entry = RegisteredIntersectionObservers()->Lookup(aObserver)) {
+  if (auto entry = observers->Lookup(aObserver)) {
     updated = entry.Data() != aThreshold;
     entry.Data() = aThreshold;
   }
   return updated;
 }
 
 void
 Element::ClearServoData(nsIDocument* aDoc) {
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -68,16 +68,18 @@ namespace dom {
   struct AnimationFilter;
   struct ScrollIntoViewOptions;
   struct ScrollToOptions;
   class DOMIntersectionObserver;
   class DOMMatrixReadOnly;
   class ElementOrCSSPseudoElement;
   class UnrestrictedDoubleOrKeyframeAnimationOptions;
   enum class CallerType : uint32_t;
+  typedef nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>
+    IntersectionObserverList;
 } // namespace dom
 } // namespace mozilla
 
 
 already_AddRefed<nsContentList>
 NS_GetContentList(nsINode* aRootNode,
                   int32_t  aMatchNameSpaceId,
                   const nsAString& aTagname);
@@ -1431,16 +1433,17 @@ public:
   // Getter, to be called from bindings.
   already_AddRefed<nsDOMStringMap> Dataset();
   // Callback for destructor of dataset to ensure to null out our weak pointer
   // to it.
   void ClearDataset();
 
   void RegisterIntersectionObserver(DOMIntersectionObserver* aObserver);
   void UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver);
+  void UnlinkIntersectionObservers();
   bool UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t threshold);
 
 protected:
   /*
    * Named-bools for use with SetAttrAndNotify to make call sites easier to
    * read.
    */
   static const bool kFireMutationEvent           = true;
@@ -1716,19 +1719,16 @@ protected:
    * the value of xlink:show, converted to a suitably equivalent named target
    * (e.g. _blank).
    */
   virtual void GetLinkTarget(nsAString& aTarget);
 
   nsDOMTokenList* GetTokenList(nsIAtom* aAtom,
                                const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr);
 
-  nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>*
-    RegisteredIntersectionObservers();
-
 private:
   /**
    * Hook for implementing GetClasses.  This is guaranteed to only be
    * called if the NODE_MAY_HAVE_CLASS flag is set.
    */
   const nsAttrValue* DoGetClasses() const;
 
   /**
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -788,24 +788,16 @@ FragmentOrElement::nsDOMSlots::Traverse(
     if (mExtendedSlots->mCustomElementData->mCustomElementDefinition) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
         "mExtendedSlots->mCustomElementData->mCustomElementDefinition");
       cb.NoteNativeChild(mExtendedSlots->mCustomElementData->mCustomElementDefinition,
         NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition));
     }
   }
 
-  for (auto iter = mExtendedSlots->mRegisteredIntersectionObservers.Iter();
-       !iter.Done(); iter.Next()) {
-    DOMIntersectionObserver* observer = iter.Key();
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
-                                       "mExtendedSlots->mRegisteredIntersectionObservers[i]");
-    cb.NoteXPCOMChild(observer);
-  }
-
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mFrameLoaderOrOpener");
   cb.NoteXPCOMChild(mExtendedSlots->mFrameLoaderOrOpener);
 }
 
 void
 FragmentOrElement::nsDOMSlots::Unlink()
 {
   mStyle = nullptr;
@@ -828,17 +820,16 @@ FragmentOrElement::nsDOMSlots::Unlink()
   MOZ_ASSERT(!(mExtendedSlots->mXBLBinding));
   mExtendedSlots->mXBLInsertionParent = nullptr;
   if (mExtendedSlots->mCustomElementData) {
     if (mExtendedSlots->mCustomElementData->mCustomElementDefinition) {
       mExtendedSlots->mCustomElementData->mCustomElementDefinition = nullptr;
     }
     mExtendedSlots->mCustomElementData = nullptr;
   }
-  mExtendedSlots->mRegisteredIntersectionObservers.Clear();
   nsCOMPtr<nsIFrameLoader> frameLoader =
     do_QueryInterface(mExtendedSlots->mFrameLoaderOrOpener);
   if (frameLoader) {
     static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
   }
   mExtendedSlots->mFrameLoaderOrOpener = nullptr;
 }
 
@@ -1535,16 +1526,21 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentO
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
   nsINode::Unlink(tmp);
 
   // The XBL binding is removed by RemoveFromBindingManagerRunnable
   // which is dispatched in UnbindFromTree.
 
   if (tmp->HasProperties()) {
+    if (tmp->IsElement()) {
+      Element* elem = tmp->AsElement();
+      elem->UnlinkIntersectionObservers();
+    }
+
     if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
       nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
       for (uint32_t i = 0; props[i]; ++i) {
         tmp->DeleteProperty(*props[i]);
       }
       if (tmp->MayHaveAnimations()) {
         nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
         for (uint32_t i = 0; effectProps[i]; ++i) {
@@ -1589,24 +1585,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Fr
   nsIDocument* doc = tmp->OwnerDoc();
   doc->BindingManager()->RemovedFromDocument(tmp, doc,
                                              nsBindingManager::eDoNotRunDtor);
 
   // Unlink any DOM slots of interest.
   {
     nsDOMSlots *slots = tmp->GetExistingDOMSlots();
     if (slots) {
-      if (slots->mExtendedSlots && tmp->IsElement()) {
-        Element* elem = tmp->AsElement();
-        for (auto iter = slots->mExtendedSlots->mRegisteredIntersectionObservers.Iter();
-             !iter.Done(); iter.Next()) {
-          DOMIntersectionObserver* observer = iter.Key();
-          observer->UnlinkTarget(*elem);
-        }
-      }
       slots->Unlink();
     }
   }
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
 
@@ -2117,16 +2105,29 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 #ifdef DEBUG
   nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
   for (uint32_t i = 0; effectProps[i]; ++i) {
     MOZ_ASSERT_IF(tmp->GetProperty(effectProps[i]), tmp->MayHaveAnimations());
   }
 #endif
 
   if (tmp->HasProperties()) {
+    if (tmp->IsElement()) {
+      Element* elem = tmp->AsElement();
+      IntersectionObserverList* observers =
+        static_cast<IntersectionObserverList*>(
+          elem->GetProperty(nsGkAtoms::intersectionobserverlist)
+        );
+      if (observers) {
+        for (auto iter = observers->Iter(); !iter.Done(); iter.Next()) {
+          DOMIntersectionObserver* observer = iter.Key();
+          cb.NoteXPCOMChild(observer);
+        }
+      }
+    }
     if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
       nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
       for (uint32_t i = 0; props[i]; ++i) {
         nsISupports* property =
           static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
         cb.NoteXPCOMChild(property);
       }
       if (tmp->MayHaveAnimations()) {
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -35,17 +35,16 @@ class nsICSSDeclaration;
 class nsIDocument;
 class nsDOMStringMap;
 class nsIURI;
 
 namespace mozilla {
 class DeclarationBlock;
 namespace dom {
 struct CustomElementData;
-class DOMIntersectionObserver;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 /**
  * A class that implements nsIWeakReference
  */
 
@@ -309,22 +308,16 @@ public:
     nsCOMPtr<nsIContent> mXBLInsertionParent;
 
     /**
      * Web components custom element data.
      */
     RefPtr<CustomElementData> mCustomElementData;
 
     /**
-     * Registered Intersection Observers on the element.
-     */
-    nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>
-      mRegisteredIntersectionObservers;
-
-    /**
      * For XUL to hold either frameloader or opener.
      */
     nsCOMPtr<nsISupports> mFrameLoaderOrOpener;
 
   };
 
   class nsDOMSlots : public nsINode::nsSlots
   {
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/1405771.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<script></script>
+<!-- a -->
+<script>
+window.onload=function(){
+  let s=window.getSelection();
+  let r=document.createRange();
+  r.selectNode(document.getElementById('b'));
+  s.addRange(r);
+  try{window.getSelection().modify('extend','forward','word')}catch(e){}
+  let o=document.getElementById('a');
+  o.parentNode.replaceChild(document.createElement('col'), o);
+}
+</script>
+>
+<template id='a' contenteditable='true'></template>
+<header id='b'></header>
+<!-- a -->
+</html>
\ No newline at end of file
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -222,9 +222,10 @@ skip-if(stylo&&isDebugBuild&&winWidget) 
 load xhr_empty_datauri.html
 load xhr_html_nullresponse.html
 load 1383478.html
 load 1383780.html
 pref(clipboard.autocopy,true) load 1385272-1.html
 load 1393806.html
 load 1400701.html
 load 1403377.html
+load 1405771.html
 load 1406109-1.html
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -262,17 +262,16 @@ nsIStringBundleService *nsContentUtils::
 nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
 nsIContentPolicy *nsContentUtils::sContentPolicyService;
 bool nsContentUtils::sTriedToGetContentPolicy = false;
 nsILineBreaker *nsContentUtils::sLineBreaker;
 nsIWordBreaker *nsContentUtils::sWordBreaker;
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr;
 uint32_t nsContentUtils::sScriptBlockerCount = 0;
 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
-uint32_t nsContentUtils::sMicroTaskLevel = 0;
 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners = nullptr;
 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
 
 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
 bool nsContentUtils::sAllowXULXBL_for_file = false;
 
 nsString* nsContentUtils::sShiftText = nullptr;
@@ -5761,61 +5760,16 @@ nsContentUtils::RunInMetastableState(alr
 
 /* static */
 nsISerialEventTarget*
 nsContentUtils::GetStableStateEventTarget()
 {
   return sStableStateEventTarget;
 }
 
-void
-nsContentUtils::EnterMicroTask()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ++sMicroTaskLevel;
-}
-
-void
-nsContentUtils::LeaveMicroTask()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (--sMicroTaskLevel == 0) {
-    PerformMainThreadMicroTaskCheckpoint();
-  }
-}
-
-bool
-nsContentUtils::IsInMicroTask()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return sMicroTaskLevel != 0;
-}
-
-uint32_t
-nsContentUtils::MicroTaskLevel()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return sMicroTaskLevel;
-}
-
-void
-nsContentUtils::SetMicroTaskLevel(uint32_t aLevel)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  sMicroTaskLevel = aLevel;
-}
-
-void
-nsContentUtils::PerformMainThreadMicroTaskCheckpoint()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsDOMMutationObserver::HandleMutations();
-}
-
 /*
  * Helper function for nsContentUtils::ProcessViewportInfo.
  *
  * Handles a single key=value pair. If it corresponds to a valid viewport
  * attribute, add it to the document header data. No validation is done on the
  * value itself (this is done at display time).
  */
 static void ProcessViewportToken(nsIDocument *aDocument,
@@ -10459,19 +10413,21 @@ nsContentUtils::GetLoadingPrincipalForXU
   MOZ_ASSERT(aLoadingNode);
   MOZ_ASSERT(aLoadingPrincipal);
 
   bool result = false;
   nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadingNode->NodePrincipal();
   nsAutoString loadingStr;
   aLoadingNode->GetAttr(kNameSpaceID_None, nsGkAtoms::loadingprincipal,
                         loadingStr);
-  if (loadingStr.IsEmpty()) {
-    // Fall back to mContent's principal (SystemPrincipal) if 'loadingprincipal'
-    // isn't specified.
+
+  // Fall back to mContent's principal if 'loadingprincipal' isn't specified,
+  // or if the doc isn't loaded by System Principal.
+  if (loadingStr.IsEmpty() ||
+      !aLoadingNode->OwnerDoc()->NodePrincipal()->GetIsSystemPrincipal()) {
     loadingPrincipal.forget(aLoadingPrincipal);
     return result;
   }
 
   nsCOMPtr<nsISupports> serializedPrincipal;
   NS_DeserializeObject(NS_ConvertUTF16toUTF8(loadingStr),
                        getter_AddRefs(serializedPrincipal));
   loadingPrincipal = do_QueryInterface(serializedPrincipal);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1943,27 +1943,16 @@ public:
    * once the event loop has reached a "stable state". Runnables dispatched to
    * this event target must not cause any queued events to be processed (i.e.
    * must not spin the event loop).
    *
    * See RunInStableState for more information about stable states
    */
   static nsISerialEventTarget* GetStableStateEventTarget();
 
-  // Call EnterMicroTask when you're entering JS execution.
-  // Usually the best way to do this is to use nsAutoMicroTask.
-  static void EnterMicroTask();
-  static void LeaveMicroTask();
-
-  static bool IsInMicroTask();
-  static uint32_t MicroTaskLevel();
-  static void SetMicroTaskLevel(uint32_t aLevel);
-
-  static void PerformMainThreadMicroTaskCheckpoint();
-
   /* Process viewport META data. This gives us information for the scale
    * and zoom of a page on mobile devices. We stick the information in
    * the document header and use it later on after rendering.
    *
    * See Bug #436083
    */
   static nsresult ProcessViewportInfo(nsIDocument *aDocument,
                                       const nsAString &viewportInfo);
@@ -3326,17 +3315,17 @@ private:
   static nsILineBreaker* sLineBreaker;
   static nsIWordBreaker* sWordBreaker;
 
   static nsIBidiKeyboard* sBidiKeyboard;
 
   static bool sInitialized;
   static uint32_t sScriptBlockerCount;
   static uint32_t sDOMNodeRemovedSuppressCount;
-  static uint32_t sMicroTaskLevel;
+
   // Not an nsCOMArray because removing elements from those is slower
   static AutoTArray<nsCOMPtr<nsIRunnable>, 8>* sBlockedScriptRunners;
   static uint32_t sRunnersCountAtFirstBlocker;
   static uint32_t sScriptBlockerCountWhereRunnersPrevented;
 
   static nsIInterfaceRequestor* sSameOriginChecker;
 
   static bool sIsHandlingKeyBoardEvent;
@@ -3488,29 +3477,16 @@ public:
   nsAutoScriptBlockerSuppressNodeRemoved() {
     ++nsContentUtils::sDOMNodeRemovedSuppressCount;
   }
   ~nsAutoScriptBlockerSuppressNodeRemoved() {
     --nsContentUtils::sDOMNodeRemovedSuppressCount;
   }
 };
 
-class MOZ_STACK_CLASS nsAutoMicroTask
-{
-public:
-  nsAutoMicroTask()
-  {
-    nsContentUtils::EnterMicroTask();
-  }
-  ~nsAutoMicroTask()
-  {
-    nsContentUtils::LeaveMicroTask();
-  }
-};
-
 namespace mozilla {
 namespace dom {
 
 class TreeOrderComparator {
 public:
   bool Equals(nsINode* aElem1, nsINode* aElem2) const {
     return aElem1 == aElem2;
   }
--- a/dom/base/nsDOMMutationObserver.h
+++ b/dom/base/nsDOMMutationObserver.h
@@ -21,16 +21,17 @@
 #include "nsClassHashtable.h"
 #include "nsNodeUtils.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsWrapperCache.h"
 #include "mozilla/dom/MutationObserverBinding.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/Animation.h"
 #include "nsIAnimationObserver.h"
+#include "nsGlobalWindow.h"
 
 class nsDOMMutationObserver;
 using mozilla::dom::MutationObservingInfo;
 
 class nsDOMMutationRecord final : public nsISupports,
                                   public nsWrapperCache
 {
   virtual ~nsDOMMutationRecord() {}
@@ -604,21 +605,17 @@ protected:
   void ScheduleForRun();
   void RescheduleForRun();
 
   nsDOMMutationRecord* CurrentRecord(nsIAtom* aType);
   bool HasCurrentRecord(const nsAString& aType);
 
   bool Suppressed()
   {
-    if (mOwner) {
-      nsCOMPtr<nsIDocument> d = mOwner->GetExtantDoc();
-      return d && d->IsInSyncOperation();
-    }
-    return false;
+    return mOwner && nsGlobalWindow::Cast(mOwner)->IsInSyncOperation();
   }
 
   static void HandleMutationsInternal();
 
   static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver,
                                            uint32_t aMutationLevel);
 
   nsCOMPtr<nsPIDOMWindowInner>                       mOwner;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -45,17 +45,16 @@
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TouchEvents.h"
 
 #include "nsViewManager.h"
 
-#include "nsIDOMHTMLCanvasElement.h"
 #include "nsLayoutUtils.h"
 #include "nsComputedDOMStyle.h"
 #include "nsIPresShell.h"
 #include "nsCSSProps.h"
 #include "nsTArrayHelpers.h"
 #include "nsIDocShell.h"
 #include "nsIContentViewer.h"
 #include "mozilla/StyleAnimationValue.h"
@@ -101,16 +100,17 @@
 #include "nsIContentIterator.h"
 #include "nsIDOMStyleSheet.h"
 #include "nsIStyleSheetService.h"
 #include "nsContentPermissionHelper.h"
 #include "nsCSSPseudoElements.h"            // for CSSPseudoElementType
 #include "nsNetUtil.h"
 #include "nsDocument.h"
 #include "HTMLImageElement.h"
+#include "HTMLCanvasElement.h"
 #include "mozilla/css/ImageLoader.h"
 #include "mozilla/layers/APZCTreeManager.h" // for layers::ZoomToRectBehavior
 #include "mozilla/dom/Promise.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/PreloadedStyleSheet.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
@@ -1619,46 +1619,48 @@ nsDOMWindowUtils::GetTranslationNodes(ns
     }
   }
 
   *aRetVal = list.forget().take();
   return NS_OK;
 }
 
 static already_AddRefed<DataSourceSurface>
-CanvasToDataSourceSurface(nsIDOMHTMLCanvasElement* aCanvas)
+CanvasToDataSourceSurface(HTMLCanvasElement* aCanvas)
 {
-  nsCOMPtr<nsINode> node = do_QueryInterface(aCanvas);
-  if (!node) {
-    return nullptr;
-  }
-
-  MOZ_ASSERT(node->IsElement(),
-             "An nsINode that implements nsIDOMHTMLCanvasElement should "
-             "be an element.");
+  MOZ_ASSERT(aCanvas);
   nsLayoutUtils::SurfaceFromElementResult result =
-    nsLayoutUtils::SurfaceFromElement(node->AsElement());
+    nsLayoutUtils::SurfaceFromElement(aCanvas);
 
   MOZ_ASSERT(result.GetSourceSurface());
   return result.GetSourceSurface()->GetDataSurface();
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::CompareCanvases(nsIDOMHTMLCanvasElement *aCanvas1,
-                                  nsIDOMHTMLCanvasElement *aCanvas2,
+nsDOMWindowUtils::CompareCanvases(nsISupports *aCanvas1,
+                                  nsISupports *aCanvas2,
                                   uint32_t* aMaxDifference,
                                   uint32_t* retVal)
 {
   if (aCanvas1 == nullptr ||
       aCanvas2 == nullptr ||
       retVal == nullptr)
     return NS_ERROR_FAILURE;
 
-  RefPtr<DataSourceSurface> img1 = CanvasToDataSourceSurface(aCanvas1);
-  RefPtr<DataSourceSurface> img2 = CanvasToDataSourceSurface(aCanvas2);
+  nsCOMPtr<nsIContent> contentCanvas1 = do_QueryInterface(aCanvas1);
+  nsCOMPtr<nsIContent> contentCanvas2 = do_QueryInterface(aCanvas2);
+  auto canvas1 = HTMLCanvasElement::FromContentOrNull(contentCanvas1);
+  auto canvas2 = HTMLCanvasElement::FromContentOrNull(contentCanvas2);
+
+  if (!canvas1 || !canvas2) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<DataSourceSurface> img1 = CanvasToDataSourceSurface(canvas1);
+  RefPtr<DataSourceSurface> img2 = CanvasToDataSourceSurface(canvas2);
 
   DataSourceSurface::ScopedMap map1(img1, DataSourceSurface::READ);
   DataSourceSurface::ScopedMap map2(img2, DataSourceSurface::READ);
 
   if (img1 == nullptr || img2 == nullptr ||
       !map1.IsMapped() || !map2.IsMapped() ||
       img1->GetSize() != img2->GetSize() ||
       map1.GetStride() != map2.GetStride()) {
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -13470,18 +13470,22 @@ MarkDocumentTreeToBeInSyncOperation(nsID
     documents->AppendObject(aDoc);
     aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
   }
   return true;
 }
 
 nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
 {
-  mMicroTaskLevel = nsContentUtils::MicroTaskLevel();
-  nsContentUtils::SetMicroTaskLevel(0);
+  mMicroTaskLevel = 0;
+  CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+  if (ccjs) {
+    mMicroTaskLevel = ccjs->MicroTaskLevel();
+    ccjs->SetMicroTaskLevel(0);
+  }
   if (aDoc) {
     if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
       if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop()) {
         nsCOMPtr<nsIDocument> doc = top->GetExtantDoc();
         MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
       }
     }
   }
@@ -13490,17 +13494,20 @@ nsAutoSyncOperation::nsAutoSyncOperation
 nsAutoSyncOperation::~nsAutoSyncOperation()
 {
   for (int32_t i = 0; i < mDocuments.Count(); ++i) {
     if (nsCOMPtr<nsPIDOMWindowInner> window = mDocuments[i]->GetInnerWindow()) {
       window->TimeoutManager().EndSyncOperation();
     }
     mDocuments[i]->SetIsInSyncOperation(false);
   }
-  nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel);
+  CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+  if (ccjs) {
+    ccjs->SetMicroTaskLevel(mMicroTaskLevel);
+  }
 }
 
 gfxUserFontSet*
 nsIDocument::GetUserFontSet(bool aFlushUserFontSet)
 {
   // We want to initialize the user font set lazily the first time the
   // user asks for it, rather than building it too early and forcing
   // rule cascade creation.  Thus we try to enforce the invariant that
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -577,16 +577,17 @@ GK_ATOM(insertafter, "insertafter")
 GK_ATOM(insertbefore, "insertbefore")
 GK_ATOM(install, "install")
 GK_ATOM(instanceOf, "instanceOf")
 GK_ATOM(int32, "int32")
 GK_ATOM(int64, "int64")
 GK_ATOM(integer, "integer")
 GK_ATOM(integrity, "integrity")
 GK_ATOM(intersection, "intersection")
+GK_ATOM(intersectionobserverlist, "intersectionobserverlist")
 GK_ATOM(is, "is")
 GK_ATOM(iscontainer, "iscontainer")
 GK_ATOM(isempty, "isempty")
 GK_ATOM(ismap, "ismap")
 GK_ATOM(itemid, "itemid")
 GK_ATOM(itemprop, "itemprop")
 GK_ATOM(itemref, "itemref")
 GK_ATOM(itemscope, "itemscope")
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1308,16 +1308,21 @@ public:
 
   already_AddRefed<nsWindowRoot> GetWindowRootOuter();
   already_AddRefed<nsWindowRoot> GetWindowRoot(mozilla::ErrorResult& aError);
 
   mozilla::dom::Performance* GetPerformance();
 
   void UpdateTopInnerWindow();
 
+  virtual bool IsInSyncOperation() override
+  {
+    return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
+  }
+
 protected:
   // Web IDL helpers
 
   // Redefine the property called aPropName on this window object to be a value
   // property with the value aValue, much like we would do for a [Replaceable]
   // property in IDL.
   void RedefineProperty(JSContext* aCx, const char* aPropName,
                         JS::Handle<JS::Value> aValue,
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -69,16 +69,18 @@ public:
 
   void UnregisterHostObjectURI(const nsACString& aURI);
 
   // Any CC class inheriting nsIGlobalObject should call these 2 methods if it
   // exposes the URL API.
   void UnlinkHostObjectURIs();
   void TraverseHostObjectURIs(nsCycleCollectionTraversalCallback &aCb);
 
+  virtual bool IsInSyncOperation() { return false; }
+
 protected:
   virtual ~nsIGlobalObject();
 
   void
   StartDying()
   {
     mIsDying = true;
   }
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -20,17 +20,17 @@
 #include "nsCOMPtr.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsPIDOMWindow.h"
 #include "GeckoProfiler.h"
 #include "nsJSPrincipals.h"
 #include "xpcpublic.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
-
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Date.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 using namespace mozilla::dom;
 
 bool
@@ -147,17 +147,18 @@ nsJSUtils::ExecutionContext::ExecutionCo
   , mEncodeBytecode(false)
 #ifdef DEBUG
   , mWantsReturnValue(false)
   , mExpectScopeChain(false)
 #endif
 {
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(nsContentUtils::IsInMicroTask());
+  MOZ_ASSERT(CycleCollectedJSContext::Get() &&
+             CycleCollectedJSContext::Get()->MicroTaskLevel());
   MOZ_ASSERT(mRetValue.isUndefined());
 
   MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aGlobal) == aGlobal);
   if (MOZ_UNLIKELY(!xpc::Scriptability::Get(aGlobal).Allowed())) {
     mSkip = true;
     mRv = NS_OK;
   }
 }
@@ -385,17 +386,18 @@ nsJSUtils::CompileModule(JSContext* aCx,
   MOZ_ASSERT_IF(aCompileOptions.versionSet,
                 aCompileOptions.version != JSVERSION_UNKNOWN);
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   MOZ_ASSERT(aSrcBuf.get());
   MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
              aEvaluationGlobal);
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == aEvaluationGlobal);
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(nsContentUtils::IsInMicroTask());
+  MOZ_ASSERT(CycleCollectedJSContext::Get() &&
+             CycleCollectedJSContext::Get()->MicroTaskLevel());
 
   NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK);
 
   if (!JS::CompileModule(aCx, aCompileOptions, aSrcBuf, aModule)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
@@ -403,17 +405,18 @@ nsJSUtils::CompileModule(JSContext* aCx,
 
 nsresult
 nsJSUtils::ModuleInstantiate(JSContext* aCx, JS::Handle<JSObject*> aModule)
 {
   AUTO_PROFILER_LABEL("nsJSUtils::ModuleInstantiate", JS);
 
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(nsContentUtils::IsInMicroTask());
+  MOZ_ASSERT(CycleCollectedJSContext::Get() &&
+             CycleCollectedJSContext::Get()->MicroTaskLevel());
 
   NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
 
   if (!JS::ModuleInstantiate(aCx, aModule)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
@@ -421,17 +424,18 @@ nsJSUtils::ModuleInstantiate(JSContext* 
 
 nsresult
 nsJSUtils::ModuleEvaluate(JSContext* aCx, JS::Handle<JSObject*> aModule)
 {
   AUTO_PROFILER_LABEL("nsJSUtils::ModuleEvaluate", JS);
 
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(nsContentUtils::IsInMicroTask());
+  MOZ_ASSERT(CycleCollectedJSContext::Get() &&
+             CycleCollectedJSContext::Get()->MicroTaskLevel());
 
   NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
 
   if (!JS::ModuleEvaluate(aCx, aModule)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -288,43 +288,36 @@ nsNodeUtils::LastRelease(nsINode* aNode)
   nsINode::nsSlots* slots = aNode->GetExistingSlots();
   if (slots) {
     if (!slots->mMutationObservers.IsEmpty()) {
       NS_OBSERVER_AUTO_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers,
                                               nsIMutationObserver, 1,
                                               NodeWillBeDestroyed, (aNode));
     }
 
-    if (aNode->IsElement()) {
-      Element* elem = aNode->AsElement();
-      FragmentOrElement::nsDOMSlots* domSlots =
-        static_cast<FragmentOrElement::nsDOMSlots*>(slots);
-      if (domSlots->mExtendedSlots) {
-        for (auto iter = domSlots->mExtendedSlots->mRegisteredIntersectionObservers.Iter();
-             !iter.Done(); iter.Next()) {
-          DOMIntersectionObserver* observer = iter.Key();
-          observer->UnlinkTarget(*elem);
-        }
-      }
-    }
-
     delete slots;
     aNode->mSlots = nullptr;
   }
 
   // Kill properties first since that may run external code, so we want to
   // be in as complete state as possible at that time.
   if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
     // Delete all properties before tearing down the document. Some of the
     // properties are bound to nsINode objects and the destructor functions of
     // the properties may want to use the owner document of the nsINode.
     static_cast<nsIDocument*>(aNode)->DeleteAllProperties();
   }
   else {
     if (aNode->HasProperties()) {
+      if (aNode->IsElement()) {
+        Element* elem = aNode->AsElement();
+        elem->UnlinkIntersectionObservers();
+        elem->DeleteProperty(nsGkAtoms::intersectionobserverlist);
+      }
+
       // Strong reference to the document so that deleting properties can't
       // delete the document.
       nsCOMPtr<nsIDocument> document = aNode->OwnerDoc();
       document->DeleteAllPropertiesFor(aNode);
     }
 
     // I wonder whether it's faster to do the HasFlag check first....
     if (aNode->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) &&
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -942,16 +942,33 @@ nsRange::IntersectsNode(nsINode& aNode, 
 
   // Step 2.
   if (disconnected) {
     result = false;
   }
   return result;
 }
 
+void
+nsRange::NotifySelectionListenersAfterRangeSet()
+{
+  if (mSelection) {
+    // Our internal code should not move focus with using this instance while
+    // it's calling Selection::NotifySelectionListeners() which may move focus
+    // or calls selection listeners.  So, let's set mCalledByJS to false here
+    // since non-*JS() methods don't set it to false.
+    AutoCalledByJSRestore calledByJSRestorer(*this);
+    mCalledByJS = false;
+    // Be aware, this range may be modified or stop being a range for selection
+    // after this call.  Additionally, the selection instance may have gone.
+    RefPtr<Selection> selection = mSelection;
+    selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
+  }
+}
+
 /******************************************************
  * Private helper routines
  ******************************************************/
 
 // It's important that all setting of the range start/end points
 // go through this function, which will do all the right voodoo
 // for content notification of range ownership.
 // Calling DoSetRange with either parent argument null will collapse
@@ -1025,27 +1042,23 @@ nsRange::DoSetRange(const RawRangeBounda
   }
 
   // This needs to be the last thing this function does, other than notifying
   // selection listeners. See comment in ParentChainChanged.
   mRoot = aRoot;
 
   // Notify any selection listeners. This has to occur last because otherwise the world
   // could be observed by a selection listener while the range was in an invalid state.
+  // So we run it off of a script runner to ensure it runs after the mutation observers
+  // have finished running.
   if (mSelection) {
-    // Our internal code should not move focus with using this instance while
-    // it's calling Selection::NotifySelectionListeners() which may move focus
-    // or calls selection listeners.  So, let's set mCalledByJS to false here
-    // since non-*JS() methods don't set it to false.
-    AutoCalledByJSRestore calledByJSRestorer(*this);
-    mCalledByJS = false;
-    // Be aware, this range may be modified or stop being a range for selection
-    // after this call.  Additionally, the selection instance may have gone.
-    RefPtr<Selection> selection = mSelection;
-    selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
+    nsContentUtils::AddScriptRunner(NewRunnableMethod(
+                                    "NotifySelectionListenersAfterRangeSet",
+                                    this,
+                                    &nsRange::NotifySelectionListenersAfterRangeSet));
   }
 }
 
 static int32_t
 IndexOf(nsINode* aChild)
 {
   nsINode* parent = aChild->GetParentNode();
 
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -422,16 +422,21 @@ public:
    * Otherwise, |this| will be modified so that it ends before the first
    * -moz-user-select:none node and additional ranges may also be created.
    * If all nodes in the range are -moz-user-select:none then aOutRanges
    * will be empty.
    * @param aOutRanges the resulting set of ranges
    */
   void ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges);
 
+  /**
+   * Notify the selection listeners after a range has been modified.
+   */
+  void NotifySelectionListenersAfterRangeSet();
+
   typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
 protected:
 
   void RegisterCommonAncestor(nsINode* aNode);
   void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking);
   nsINode* IsValidBoundary(nsINode* aNode) const
   {
     return ComputeRootNode(aNode, mMaySpanAnonymousSubtrees);
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -619,16 +619,17 @@ skip-if = toolkit == 'android'
 [test_bug1307730.html]
 [test_bug1308069.html]
 [test_bug1314032.html]
 [test_bug1318303.html]
 [test_bug1375050.html]
 [test_bug1381710.html]
 [test_bug1384658.html]
 skip-if = toolkit == 'android'
+[test_bug1399603.html]
 [test_bug1399605.html]
 [test_caretPositionFromPoint.html]
 [test_change_policy.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_copyimage.html]
 subsuite = clipboard
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1399603.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1399603</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1399603">Mozilla Bug 1399603</a>
+<p id="display"></p>
+<div id="content">
+  <div id="root">
+    <div id="target"></div>
+  </div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  function waitForNotification(f) {
+    requestAnimationFrame(function() {
+      setTimeout(function() { setTimeout(f); });
+    });
+  }
+
+  function forceGC() {
+    SpecialPowers.gc();
+    SpecialPowers.forceShrinkingGC();
+    SpecialPowers.forceCC();
+    SpecialPowers.gc();
+    SpecialPowers.forceShrinkingGC();
+    SpecialPowers.forceCC();
+  }
+
+  let content = document.getElementById('content');
+  let root = document.getElementById('root');
+  let target = document.getElementById('target');
+  let entries = [];
+  let observer = new IntersectionObserver(function(changes) {
+    entries = entries.concat(changes);
+  }, { root: root });
+  observer.observe(target);
+
+  waitForNotification(function () {
+    is(entries.length, 1, "Initial notification.");
+    root.removeChild(target);
+    content.removeChild(root);
+    root = null;
+    forceGC();
+    waitForNotification(function () {
+      is(entries.length, 1, "No new notifications.");
+      SimpleTest.finish();
+    });
+  });
+
+  SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<div id="log">
+</div>
+</body>
+</html>
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -1,15 +1,16 @@
 /* -*- 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/dom/CallbackObject.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "jsfriendapi.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIXPConnect.h"
 #include "nsIScriptContext.h"
 #include "nsPIDOMWindow.h"
 #include "nsJSUtils.h"
 #include "xpcprivate.h"
@@ -135,17 +136,20 @@ CallbackObject::CallSetup::CallSetup(Cal
                                      bool aIsJSImplementedWebIDL)
   : mCx(nullptr)
   , mCompartment(aCompartment)
   , mErrorResult(aRv)
   , mExceptionHandling(aExceptionHandling)
   , mIsMainThread(NS_IsMainThread())
 {
   if (mIsMainThread) {
-    nsContentUtils::EnterMicroTask();
+    CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+    if (ccjs) {
+      ccjs->EnterMicroTask();
+    }
   }
 
   // Compute the caller's subject principal (if necessary) early, before we
   // do anything that might perturb the relevant state.
   nsIPrincipal* webIDLCallerPrincipal = nullptr;
   if (aIsJSImplementedWebIDL) {
     webIDLCallerPrincipal = nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
   }
@@ -344,17 +348,20 @@ CallbackObject::CallSetup::~CallSetup()
   }
 
   mAutoIncumbentScript.reset();
   mAutoEntryScript.reset();
 
   // It is important that this is the last thing we do, after leaving the
   // compartment and undoing all our entry/incumbent script changes
   if (mIsMainThread) {
-    nsContentUtils::LeaveMicroTask();
+    CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+    if (ccjs) {
+      ccjs->LeaveMicroTask();
+    }
   }
 }
 
 already_AddRefed<nsISupports>
 CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
                                           const nsIID& aIID) const
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -971,17 +971,17 @@ public:
   CanvasFilterChainObserver(nsTArray<nsStyleFilter>& aFilters,
                             Element* aCanvasElement,
                             CanvasRenderingContext2D* aContext)
     : nsSVGFilterChainObserver(aFilters, aCanvasElement)
     , mContext(aContext)
   {
   }
 
-  virtual void DoUpdate() override
+  virtual void OnRenderingChange() override
   {
     if (!mContext) {
       MOZ_CRASH("GFX: This should never be called without a context");
     }
     // Refresh the cached FilterDescription in mContext->CurrentState().filter.
     // If this filter is not at the top of the state stack, we'll refresh the
     // wrong filter, but that's ok, because we'll refresh the right filter
     // when we pop the state stack in CanvasRenderingContext2D::Restore().
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1814,17 +1814,17 @@ WebGLContext::UpdateContextLossStatus()
         const auto kEventName = NS_LITERAL_STRING("webglcontextlost");
         const bool kCanBubble = true;
         const bool kIsCancelable = true;
         bool useDefaultHandler;
 
         if (mCanvasElement) {
             nsContentUtils::DispatchTrustedEvent(
                 mCanvasElement->OwnerDoc(),
-                static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
+                static_cast<nsIContent*>(mCanvasElement),
                 kEventName,
                 kCanBubble,
                 kIsCancelable,
                 &useDefaultHandler);
         } else {
             // OffscreenCanvas case
             RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
             event->InitEvent(kEventName, kCanBubble, kIsCancelable);
@@ -1882,17 +1882,17 @@ WebGLContext::UpdateContextLossStatus()
         }
 
         // Revival!
         mContextStatus = ContextNotLost;
 
         if (mCanvasElement) {
             nsContentUtils::DispatchTrustedEvent(
                 mCanvasElement->OwnerDoc(),
-                static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
+                static_cast<nsIContent*>(mCanvasElement),
                 NS_LITERAL_STRING("webglcontextrestored"),
                 true,
                 true);
         } else {
             RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
             event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"), true, true);
             event->SetTrusted(true);
             bool unused;
--- a/dom/credentialmanagement/CredentialsContainer.cpp
+++ b/dom/credentialmanagement/CredentialsContainer.cpp
@@ -33,22 +33,20 @@ CredentialsContainer::WrapObject(JSConte
 {
   return CredentialsContainerBinding::Wrap(aCx, this, aGivenProto);
 }
 
 already_AddRefed<Promise>
 CredentialsContainer::Get(const CredentialRequestOptions& aOptions)
 {
   RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
-  MOZ_ASSERT(mgr);
   return mgr->GetAssertion(mParent, aOptions.mPublicKey);
 }
 
 already_AddRefed<Promise>
 CredentialsContainer::Create(const CredentialCreationOptions& aOptions)
 {
   RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
-  MOZ_ASSERT(mgr);
   return mgr->MakeCredential(mParent, aOptions.mPublicKey);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -1095,29 +1095,35 @@ EventListenerManager::HandleEventSubType
   if ((aListener->mListenerType == Listener::eJSEventListener) &&
       aListener->mHandlerIsString) {
     result = CompileEventHandlerInternal(aListener, nullptr, nullptr);
     aListener = nullptr;
   }
 
   if (NS_SUCCEEDED(result)) {
     if (mIsMainThreadELM) {
-      nsContentUtils::EnterMicroTask();
+      CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+      if (ccjs) {
+        ccjs->EnterMicroTask();
+      }
     }
     // nsIDOMEvent::currentTarget is set in EventDispatcher.
     if (listenerHolder.HasWebIDLCallback()) {
       ErrorResult rv;
       listenerHolder.GetWebIDLCallback()->
         HandleEvent(aCurrentTarget, *(aDOMEvent->InternalDOMEvent()), rv);
       result = rv.StealNSResult();
     } else {
       result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
     }
     if (mIsMainThreadELM) {
-      nsContentUtils::LeaveMicroTask();
+      CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+      if (ccjs) {
+        ccjs->LeaveMicroTask();
+      }
     }
   }
 
   return result;
 }
 
 EventMessage
 EventListenerManager::GetLegacyEventMessage(EventMessage aEventMessage) const
--- a/dom/file/Blob.cpp
+++ b/dom/file/Blob.cpp
@@ -6,17 +6,16 @@
 
 #include "Blob.h"
 #include "File.h"
 #include "MemoryBlobImpl.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "MultipartBlobImpl.h"
 #include "nsIInputStream.h"
 #include "nsPIDOMWindow.h"
-#include "TemporaryBlobImpl.h"
 #include "StreamBlobImpl.h"
 #include "StringBlobImpl.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Blob)
 
@@ -89,27 +88,16 @@ Blob::CreateMemoryBlob(nsISupports* aPar
                        uint64_t aLength, const nsAString& aContentType)
 {
   RefPtr<Blob> blob = Blob::Create(aParent,
     new MemoryBlobImpl(aMemoryBuffer, aLength, aContentType));
   MOZ_ASSERT(!blob->mImpl->IsFile());
   return blob.forget();
 }
 
-/* static */ already_AddRefed<Blob>
-Blob::CreateTemporaryBlob(nsISupports* aParent, PRFileDesc* aFD,
-                          uint64_t aStartPos, uint64_t aLength,
-                          const nsAString& aContentType)
-{
-  RefPtr<Blob> blob = Blob::Create(aParent,
-    new TemporaryBlobImpl(aFD, aStartPos, aLength, aContentType));
-  MOZ_ASSERT(!blob->mImpl->IsFile());
-  return blob.forget();
-}
-
 Blob::Blob(nsISupports* aParent, BlobImpl* aImpl)
   : mImpl(aImpl)
   , mParent(aParent)
 {
   MOZ_ASSERT(mImpl);
 
 #ifdef DEBUG
   {
--- a/dom/file/Blob.h
+++ b/dom/file/Blob.h
@@ -54,21 +54,16 @@ public:
 
   // The returned Blob takes ownership of aMemoryBuffer. aMemoryBuffer will be
   // freed by free so it must be allocated by malloc or something
   // compatible with it.
   static already_AddRefed<Blob>
   CreateMemoryBlob(nsISupports* aParent, void* aMemoryBuffer, uint64_t aLength,
                    const nsAString& aContentType);
 
-  static already_AddRefed<Blob>
-  CreateTemporaryBlob(nsISupports* aParent, PRFileDesc* aFD,
-                      uint64_t aStartPos, uint64_t aLength,
-                      const nsAString& aContentType);
-
   BlobImpl* Impl() const
   {
     return mImpl;
   }
 
   bool IsFile() const;
 
   const nsTArray<RefPtr<BlobImpl>>* GetSubBlobImpls() const;
--- a/dom/file/FileBlobImpl.cpp
+++ b/dom/file/FileBlobImpl.cpp
@@ -249,17 +249,17 @@ FileBlobImpl::CreateInputStream(nsIInput
   }
 
   if (mWholeFile) {
     stream.forget(aStream);
     return;
   }
 
   RefPtr<SlicedInputStream> slicedInputStream =
-    new SlicedInputStream(stream, mStart, mLength);
+    new SlicedInputStream(stream.forget(), mStart, mLength);
   slicedInputStream.forget(aStream);
 }
 
 bool
 FileBlobImpl::IsDirectory() const
 {
   bool isDirectory = false;
   if (mFile) {
--- a/dom/file/FileBlobImpl.h
+++ b/dom/file/FileBlobImpl.h
@@ -68,17 +68,16 @@ public:
   void SetFileId(int64_t aFileId)
   {
     mFileId = aFileId;
   }
 
 protected:
   virtual ~FileBlobImpl() = default;
 
-private:
   // Create slice
   FileBlobImpl(const FileBlobImpl* aOther, uint64_t aStart,
                uint64_t aLength, const nsAString& aContentType);
 
   virtual already_AddRefed<BlobImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType, ErrorResult& aRv) override;
 
--- a/dom/file/MutableBlobStorage.cpp
+++ b/dom/file/MutableBlobStorage.cpp
@@ -2,16 +2,19 @@
 /* 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 "MutableBlobStorage.h"
 #include "MemoryBlobImpl.h"
 #include "mozilla/CheckedInt.h"
+#include "mozilla/dom/ipc/TemporaryIPCBlobChild.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TaskQueue.h"
 #include "File.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsNetCID.h"
 #include "nsProxyRelease.h"
 #include "WorkerPrivate.h"
 
@@ -69,88 +72,16 @@ private:
   }
 
   RefPtr<MutableBlobStorage> mBlobStorage;
   RefPtr<MutableBlobStorageCallback> mCallback;
   RefPtr<Blob> mBlob;
   nsresult mRv;
 };
 
-// This runnable goes back to the main-thread and informs the BlobStorage about
-// the temporary file.
-class FileCreatedRunnable final : public Runnable
-{
-public:
-  FileCreatedRunnable(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD)
-    : Runnable("dom::FileCreatedRunnable")
-    , mBlobStorage(aBlobStorage)
-    , mFD(aFD)
-  {
-    MOZ_ASSERT(aBlobStorage);
-    MOZ_ASSERT(aFD);
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    mBlobStorage->TemporaryFileCreated(mFD);
-    mFD = nullptr;
-    return NS_OK;
-  }
-
-private:
-  ~FileCreatedRunnable()
-  {
-    // If something when wrong, we still have to close the FileDescriptor.
-    if (mFD) {
-      PR_Close(mFD);
-    }
-  }
-
-  RefPtr<MutableBlobStorage> mBlobStorage;
-  PRFileDesc* mFD;
-};
-
-// This runnable creates the temporary file. When done, FileCreatedRunnable is
-// dispatched back to the main-thread.
-class CreateTemporaryFileRunnable final : public Runnable
-{
-public:
-  explicit CreateTemporaryFileRunnable(MutableBlobStorage* aBlobStorage)
-    : Runnable("dom::CreateTemporaryFileRunnable")
-    , mBlobStorage(aBlobStorage)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(XRE_IsParentProcess());
-    MOZ_ASSERT(aBlobStorage);
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(XRE_IsParentProcess());
-    MOZ_ASSERT(mBlobStorage);
-
-    PRFileDesc* tempFD = nullptr;
-    nsresult rv = NS_OpenAnonymousTemporaryFile(&tempFD);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return NS_OK;
-    }
-
-    // The ownership of the tempFD is moved to the FileCreatedRunnable.
-    return mBlobStorage->EventTarget()->Dispatch(
-      new FileCreatedRunnable(mBlobStorage, tempFD), NS_DISPATCH_NORMAL);
-  }
-
-private:
-  RefPtr<MutableBlobStorage> mBlobStorage;
-};
-
 // Simple runnable to propagate the error to the BlobStorage.
 class ErrorPropagationRunnable final : public Runnable
 {
 public:
   ErrorPropagationRunnable(MutableBlobStorage* aBlobStorage, nsresult aRv)
     : Runnable("dom::ErrorPropagationRunnable")
     , mBlobStorage(aBlobStorage)
     , mRv(aRv)
@@ -276,18 +207,21 @@ private:
   }
 
   PRFileDesc* mFD;
 };
 
 // This runnable is dispatched to the main-thread from the IO thread and its
 // task is to create the blob and inform the callback.
 class CreateBlobRunnable final : public Runnable
+                               , public TemporaryIPCBlobChildCallback
 {
 public:
+  NS_DECL_ISUPPORTS_INHERITED
+
   CreateBlobRunnable(MutableBlobStorage* aBlobStorage,
                      already_AddRefed<nsISupports> aParent,
                      const nsACString& aContentType,
                      already_AddRefed<MutableBlobStorageCallback> aCallback)
     : Runnable("dom::CreateBlobRunnable")
     , mBlobStorage(aBlobStorage)
     , mParent(aParent)
     , mContentType(aContentType)
@@ -297,21 +231,37 @@ public:
     MOZ_ASSERT(aBlobStorage);
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mBlobStorage);
-    mBlobStorage->CreateBlobAndRespond(mParent.forget(), mContentType,
-                                       mCallback.forget());
+    mBlobStorage->AskForBlob(this, mContentType);
     return NS_OK;
   }
 
+  void
+  OperationSucceeded(BlobImpl* aBlobImpl) override
+  {
+    nsCOMPtr<nsISupports> parent(Move(mParent));
+    RefPtr<MutableBlobStorageCallback> callback(Move(mCallback));
+
+    RefPtr<Blob> blob = Blob::Create(parent, aBlobImpl);
+    callback->BlobStoreCompleted(mBlobStorage, blob, NS_OK);
+  }
+
+  void
+  OperationFailed(nsresult aRv) override
+  {
+    RefPtr<MutableBlobStorageCallback> callback(Move(mCallback));
+    callback->BlobStoreCompleted(mBlobStorage, nullptr, aRv);
+  }
+
 private:
   ~CreateBlobRunnable()
   {
     MOZ_ASSERT(mBlobStorage);
     // If something when wrong, we still have to release data in the correct
     // thread.
     NS_ProxyRelease(
       "CreateBlobRunnable::mParent",
@@ -322,41 +272,49 @@ private:
   }
 
   RefPtr<MutableBlobStorage> mBlobStorage;
   nsCOMPtr<nsISupports> mParent;
   nsCString mContentType;
   RefPtr<MutableBlobStorageCallback> mCallback;
 };
 
+NS_IMPL_ISUPPORTS_INHERITED0(CreateBlobRunnable, Runnable)
+
 // This task is used to know when the writing is completed. From the IO thread
 // it dispatches a CreateBlobRunnable to the main-thread.
 class LastRunnable final : public Runnable
 {
 public:
   LastRunnable(MutableBlobStorage* aBlobStorage,
+               PRFileDesc* aFD,
                nsISupports* aParent,
                const nsACString& aContentType,
                MutableBlobStorageCallback* aCallback)
     : Runnable("dom::LastRunnable")
     , mBlobStorage(aBlobStorage)
+    , mFD(aFD)
     , mParent(aParent)
     , mContentType(aContentType)
     , mCallback(aCallback)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mBlobStorage);
     MOZ_ASSERT(aCallback);
+    MOZ_ASSERT(aFD);
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(mBlobStorage);
+
+    PR_Close(mFD);
+    mFD = nullptr;
+
     RefPtr<Runnable> runnable =
       new CreateBlobRunnable(mBlobStorage, mParent.forget(),
                              mContentType, mCallback.forget());
     return mBlobStorage->EventTarget()->Dispatch(runnable, NS_DISPATCH_NORMAL);
   }
 
 private:
   ~LastRunnable()
@@ -368,54 +326,67 @@ private:
       "LastRunnable::mParent",
       mBlobStorage->EventTarget(), mParent.forget());
     NS_ProxyRelease(
       "LastRunnable::mCallback",
       mBlobStorage->EventTarget(), mCallback.forget());
   }
 
   RefPtr<MutableBlobStorage> mBlobStorage;
+  PRFileDesc* mFD;
   nsCOMPtr<nsISupports> mParent;
   nsCString mContentType;
   RefPtr<MutableBlobStorageCallback> mCallback;
 };
 
 } // anonymous namespace
 
 MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
-                                       nsIEventTarget* aEventTarget)
+                                       nsIEventTarget* aEventTarget,
+                                       uint32_t aMaxMemory)
   : mData(nullptr)
   , mDataLen(0)
   , mDataBufferLen(0)
   , mStorageState(aType == eOnlyInMemory ? eKeepInMemory : eInMemory)
   , mFD(nullptr)
   , mErrorResult(NS_OK)
   , mEventTarget(aEventTarget)
+  , mMaxMemory(aMaxMemory)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mEventTarget) {
     mEventTarget = GetMainThreadEventTarget();
   }
 
+  if (aMaxMemory == 0 && aType == eCouldBeInTemporaryFile) {
+    mMaxMemory = Preferences::GetUint("dom.blob.memoryToTemporaryFile",
+                                      BLOB_MEMORY_TEMPORARY_FILE);
+  }
+
   MOZ_ASSERT(mEventTarget);
 }
 
 MutableBlobStorage::~MutableBlobStorage()
 {
   free(mData);
 
   if (mFD) {
     RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
     DispatchToIOThread(runnable.forget());
   }
 
   if (mTaskQueue) {
     mTaskQueue->BeginShutdown();
   }
+
+  if (mActor) {
+    NS_ProxyRelease("MutableBlobStorage::mActor",
+                    EventTarget(), mActor.forget());
+  }
 }
 
 void
 MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
                                      const nsACString& aContentType,
                                      MutableBlobStorageCallback* aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -425,28 +396,34 @@ MutableBlobStorage::GetBlobWhenReady(nsI
   MOZ_ASSERT(mStorageState != eClosed);
   StorageState previousState = mStorageState;
   mStorageState = eClosed;
 
   if (previousState == eInTemporaryFile) {
     MOZ_ASSERT(mFD);
 
     if (NS_FAILED(mErrorResult)) {
+      MOZ_ASSERT(!mActor);
+
       RefPtr<Runnable> runnable =
         new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult);
       EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
       return;
     }
 
+    MOZ_ASSERT(mActor);
+
     // We want to wait until all the WriteRunnable are completed. The way we do
     // this is to go to the I/O thread and then we come back: the runnables are
     // executed in order and this LastRunnable will be... the last one.
+    // This Runnable will also close the FD on the I/O thread.
     RefPtr<Runnable> runnable =
-      new LastRunnable(this, aParent, aContentType, aCallback);
+      new LastRunnable(this, mFD, aParent, aContentType, aCallback);
     DispatchToIOThread(runnable.forget());
+    mFD = nullptr;
     return;
   }
 
   // If we are waiting for the temporary file, it's better to wait...
   if (previousState == eWaitingForTemporaryFile) {
     mPendingParent = aParent;
     mPendingContentType = aContentType;
     mPendingCallback = aCallback;
@@ -485,20 +462,17 @@ MutableBlobStorage::Append(const void* a
 
   if (!aLength) {
     return NS_OK;
   }
 
   // If eInMemory is the current Storage state, we could maybe migrate to
   // a temporary file.
   if (mStorageState == eInMemory && ShouldBeTemporaryStorage(aLength)) {
-    nsresult rv = MaybeCreateTemporaryFile();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+    MaybeCreateTemporaryFile();
   }
 
   // If we are already in the temporaryFile mode, we have to dispatch a
   // runnable.
   if (mStorageState == eInTemporaryFile) {
     MOZ_ASSERT(mFD);
 
     RefPtr<WriteRunnable> runnable =
@@ -565,55 +539,83 @@ MutableBlobStorage::ShouldBeTemporarySto
 
   CheckedUint32 bufferSize = mDataLen;
   bufferSize += aSize;
 
   if (!bufferSize.isValid()) {
     return false;
   }
 
-  return bufferSize.value() >= Preferences::GetUint("dom.blob.memoryToTemporaryFile",
-                                                    BLOB_MEMORY_TEMPORARY_FILE);
+  return bufferSize.value() >= mMaxMemory;
 }
 
-nsresult
+void
 MutableBlobStorage::MaybeCreateTemporaryFile()
 {
-  if (XRE_IsParentProcess()) {
-    RefPtr<Runnable> runnable = new CreateTemporaryFileRunnable(this);
-    DispatchToIOThread(runnable.forget());
+  mStorageState = eWaitingForTemporaryFile;
+
+  mozilla::ipc::PBackgroundChild* actor =
+    mozilla::ipc::BackgroundChild::GetForCurrentThread();
+  if (actor) {
+    ActorCreated(actor);
   } else {
-    RefPtr<MutableBlobStorage> self(this);
-    ContentChild::GetSingleton()->
-      AsyncOpenAnonymousTemporaryFile([self](PRFileDesc* prfile) {
-        if (prfile) {
-          // The ownership of the prfile is moved to the FileCreatedRunnable.
-          self->EventTarget()->Dispatch(
-            new FileCreatedRunnable(self, prfile), NS_DISPATCH_NORMAL);
-        }
-      });
+    mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this);
+  }
+}
+
+void
+MutableBlobStorage::ActorFailed()
+{
+  MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+}
+
+void
+MutableBlobStorage::ActorCreated(PBackgroundChild* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
+             mStorageState == eClosed);
+  MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
+
+  // If the object has been already closed and we don't need to execute a
+  // callback, we have nothing else to do.
+  if (mStorageState == eClosed && !mPendingCallback) {
+    return;
   }
 
-  mStorageState = eWaitingForTemporaryFile;
-  return NS_OK;
+  mActor = new TemporaryIPCBlobChild(this);
+  aActor->SendPTemporaryIPCBlobConstructor(mActor);
+
+  // We need manually to increase the reference for this actor because the
+  // IPC allocator method is not triggered. The Release() is called by IPDL
+  // when the actor is deleted.
+  mActor.get()->AddRef();
+
+  // The actor will call us when the FileDescriptor is received.
 }
 
 void
 MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
              mStorageState == eClosed);
   MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(aFD);
 
   // If the object has been already closed and we don't need to execute a
   // callback, we need just to close the file descriptor in the correct thread.
   if (mStorageState == eClosed && !mPendingCallback) {
     RefPtr<Runnable> runnable = new CloseFileRunnable(aFD);
     DispatchToIOThread(runnable.forget());
+
+    // Let's inform the parent that we have nothing else to do.
+    mActor->SendOperationDone(false, EmptyCString());
+    mActor = nullptr;
     return;
   }
 
   // If we still receiving data, we can proceed in temporary-file mode.
   if (mStorageState == eWaitingForTemporaryFile) {
     mStorageState = eInTemporaryFile;
   }
 
@@ -633,51 +635,48 @@ MutableBlobStorage::TemporaryFileCreated
   // were already waiting for a temporary file-descriptor. Finally we are here,
   // AdoptBuffer runnable is going to write the current buffer into this file.
   // After that, there is nothing else to write, and we dispatch LastRunnable
   // which ends up calling mPendingCallback via CreateBlobRunnable.
   if (mStorageState == eClosed) {
     MOZ_ASSERT(mPendingCallback);
 
     RefPtr<Runnable> runnable =
-      new LastRunnable(this, mPendingParent, mPendingContentType,
+      new LastRunnable(this, mFD, mPendingParent, mPendingContentType,
                        mPendingCallback);
     DispatchToIOThread(runnable.forget());
+    mFD = nullptr;
 
     mPendingParent = nullptr;
     mPendingCallback = nullptr;
   }
 }
 
 void
-MutableBlobStorage::CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
-                                         const nsACString& aContentType,
-                                         already_AddRefed<MutableBlobStorageCallback> aCallback)
+MutableBlobStorage::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
+                               const nsACString& aContentType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mStorageState == eClosed);
-  MOZ_ASSERT(mFD);
-
-  nsCOMPtr<nsISupports> parent(aParent);
-  RefPtr<MutableBlobStorageCallback> callback(aCallback);
+  MOZ_ASSERT(!mFD);
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(aCallback);
 
-  RefPtr<Blob> blob =
-    File::CreateTemporaryBlob(parent, mFD, 0, mDataLen,
-                              NS_ConvertUTF8toUTF16(aContentType));
-  callback->BlobStoreCompleted(this, blob, NS_OK);
-
-  // ownership of this FD is moved to the BlobImpl.
-  mFD = nullptr;
+  mActor->AskForBlob(aCallback, aContentType);
+  mActor = nullptr;
 }
 
 void
 MutableBlobStorage::ErrorPropagated(nsresult aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mErrorResult = aRv;
+
+  mActor->SendOperationDone(false, EmptyCString());
+  mActor = nullptr;
 }
 
 void
 MutableBlobStorage::DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable)
 {
   if (!mTaskQueue) {
     nsCOMPtr<nsIEventTarget> target
       = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
@@ -685,10 +684,19 @@ MutableBlobStorage::DispatchToIOThread(a
 
     mTaskQueue = new TaskQueue(target.forget());
   }
 
   nsCOMPtr<nsIRunnable> runnable(aRunnable);
   mTaskQueue->Dispatch(runnable.forget());
 }
 
+size_t
+MutableBlobStorage::SizeOfCurrentMemoryBuffer() const
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return mStorageState < eInTemporaryFile ? mDataLen : 0;
+}
+
+NS_IMPL_ISUPPORTS(MutableBlobStorage, nsIIPCBackgroundChildCreateCallback)
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/file/MutableBlobStorage.h
+++ b/dom/file/MutableBlobStorage.h
@@ -3,85 +3,94 @@
 /* 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_MutableBlobStorage_h
 #define mozilla_dom_MutableBlobStorage_h
 
 #include "mozilla/RefPtr.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
 #include "prio.h"
 
 class nsIEventTarget;
+class nsIRunnable;
 
 namespace mozilla {
 
 class TaskQueue;
 
 namespace dom {
 
 class Blob;
 class BlobImpl;
 class MutableBlobStorage;
+class TemporaryIPCBlobChild;
+class TemporaryIPCBlobChildCallback;
 
 class MutableBlobStorageCallback
 {
 public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
                                   Blob* aBlob,
                                   nsresult aRv) = 0;
 };
 
 // This class is main-thread only.
-class MutableBlobStorage final
+class MutableBlobStorage final : public nsIIPCBackgroundChildCreateCallback
 {
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MutableBlobStorage);
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+  NS_DECL_THREADSAFE_ISUPPORTS
 
   enum MutableBlobStorageType
   {
     eOnlyInMemory,
     eCouldBeInTemporaryFile,
   };
 
   explicit MutableBlobStorage(MutableBlobStorageType aType,
-                              nsIEventTarget* aEventTarget = nullptr);
+                              nsIEventTarget* aEventTarget = nullptr,
+                              uint32_t aMaxMemory = 0);
 
   nsresult Append(const void* aData, uint32_t aLength);
 
   // This method can be called just once.
   // The callback will be called when the Blob is ready.
   void GetBlobWhenReady(nsISupports* aParent,
                         const nsACString& aContentType,
                         MutableBlobStorageCallback* aCallback);
 
   void TemporaryFileCreated(PRFileDesc* aFD);
 
-  void  CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
-                             const nsACString& aContentType,
-                             already_AddRefed<MutableBlobStorageCallback> aCallback);
+  void AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
+                  const nsACString& aContentType);
 
   void ErrorPropagated(nsresult aRv);
 
   nsIEventTarget* EventTarget()
   {
     MOZ_ASSERT(mEventTarget);
     return mEventTarget;
   }
 
+  // Returns the heap size in bytes of our internal buffers.
+  // Note that this intentionally ignores the data in the temp file.
+  size_t SizeOfCurrentMemoryBuffer() const;
+
 private:
   ~MutableBlobStorage();
 
   bool ExpandBufferSize(uint64_t aSize);
 
   bool ShouldBeTemporaryStorage(uint64_t aSize) const;
 
-  nsresult MaybeCreateTemporaryFile();
+  void MaybeCreateTemporaryFile();
 
   void DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable);
 
   // All these variables are touched on the main thread only.
 
   void* mData;
   uint64_t mDataLen;
   uint64_t mDataBufferLen;
@@ -101,14 +110,22 @@ private:
   nsresult mErrorResult;
 
   RefPtr<TaskQueue> mTaskQueue;
   nsCOMPtr<nsIEventTarget> mEventTarget;
 
   nsCOMPtr<nsISupports> mPendingParent;
   nsCString mPendingContentType;
   RefPtr<MutableBlobStorageCallback> mPendingCallback;
+
+  RefPtr<TemporaryIPCBlobChild> mActor;
+
+  // This value is used when we go from eInMemory to eWaitingForTemporaryFile
+  // and eventually eInTemporaryFile. If the size of the buffer is >=
+  // mMaxMemory, the creation of the temporary file will start.
+  // It's not used if mStorageState is eKeepInMemory.
+  uint32_t mMaxMemory;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MutableBlobStorage_h
--- a/dom/file/StreamBlobImpl.cpp
+++ b/dom/file/StreamBlobImpl.cpp
@@ -44,27 +44,16 @@ StreamBlobImpl::StreamBlobImpl(nsIInputS
   : BaseBlobImpl(aContentType, aLength)
   , mInputStream(aInputStream)
   , mIsDirectory(false)
   , mFileId(-1)
 {
   mImmutable = true;
 }
 
-StreamBlobImpl::StreamBlobImpl(StreamBlobImpl* aOther,
-                               const nsAString& aContentType,
-                               uint64_t aStart, uint64_t aLength)
-  : BaseBlobImpl(aContentType, aOther->mStart + aStart, aLength)
-  , mInputStream(new SlicedInputStream(aOther->mInputStream, aStart, aLength))
-  , mIsDirectory(false)
-  , mFileId(-1)
-{
-  mImmutable = true;
-}
-
 StreamBlobImpl::StreamBlobImpl(nsIInputStream* aInputStream,
                                const nsAString& aName,
                                const nsAString& aContentType,
                                int64_t aLastModifiedDate,
                                uint64_t aLength)
   : BaseBlobImpl(aName, aContentType, aLength, aLastModifiedDate)
   , mInputStream(aInputStream)
   , mIsDirectory(false)
@@ -101,32 +90,39 @@ already_AddRefed<BlobImpl>
 StreamBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
                             const nsAString& aContentType, ErrorResult& aRv)
 {
   if (!aLength) {
     RefPtr<BlobImpl> impl = new EmptyBlobImpl(aContentType);
     return impl.forget();
   }
 
+  nsCOMPtr<nsIInputStream> clonedStream;
+
   nsCOMPtr<nsICloneableInputStreamWithRange> stream =
     do_QueryInterface(mInputStream);
   if (stream) {
-    nsCOMPtr<nsIInputStream> clonedStream;
     aRv = stream->CloneWithRange(aStart, aLength, getter_AddRefs(clonedStream));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
+  } else {
+    CreateInputStream(getter_AddRefs(clonedStream), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
 
-    RefPtr<BlobImpl> impl =
-      new StreamBlobImpl(clonedStream, aContentType, aLength);
-    return impl.forget();
+    clonedStream =
+      new SlicedInputStream(clonedStream.forget(), aStart, aLength);
   }
 
-  RefPtr<BlobImpl> impl;
-    impl = new StreamBlobImpl(this, aContentType, aStart, aLength);
+  MOZ_ASSERT(clonedStream);
+
+  RefPtr<BlobImpl> impl =
+    new StreamBlobImpl(clonedStream, aContentType, aLength);
   return impl.forget();
 }
 
 void
 StreamBlobImpl::MaybeRegisterMemoryReporter()
 {
   // We report only stringInputStream.
   nsCOMPtr<nsIStringInputStream> stringInputStream =
--- a/dom/file/StreamBlobImpl.h
+++ b/dom/file/StreamBlobImpl.h
@@ -85,21 +85,16 @@ private:
                  uint64_t aLength);
 
   StreamBlobImpl(nsIInputStream* aInputStream,
                  const nsAString& aName,
                  const nsAString& aContentType,
                  int64_t aLastModifiedDate,
                  uint64_t aLength);
 
-  StreamBlobImpl(StreamBlobImpl* aOther,
-                 const nsAString& aContentType,
-                 uint64_t aStart,
-                 uint64_t aLength);
-
   ~StreamBlobImpl();
 
   void MaybeRegisterMemoryReporter();
 
   nsCOMPtr<nsIInputStream> mInputStream;
 
   nsString mFullPath;
   bool mIsDirectory;
deleted file mode 100644
--- a/dom/file/TemporaryBlobImpl.cpp
+++ /dev/null
@@ -1,58 +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 "TemporaryBlobImpl.h"
-
-namespace mozilla {
-namespace dom {
-
-NS_IMPL_ISUPPORTS_INHERITED0(TemporaryBlobImpl, BlobImpl)
-
-TemporaryBlobImpl::TemporaryBlobImpl(PRFileDesc* aFD, uint64_t aStartPos,
-                                     uint64_t aLength,
-                                     const nsAString& aContentType)
-  : BaseBlobImpl(aContentType, aLength)
-  , mStartPos(aStartPos)
-{
-  mFileDescOwner = new nsTemporaryFileInputStream::FileDescOwner(aFD);
-}
-
-TemporaryBlobImpl::TemporaryBlobImpl(const TemporaryBlobImpl* aOther,
-                                     uint64_t aStart, uint64_t aLength,
-                                     const nsAString& aContentType)
-  : BaseBlobImpl(aContentType, aLength)
-  , mStartPos(aStart)
-  , mFileDescOwner(aOther->mFileDescOwner)
-{}
-
-already_AddRefed<BlobImpl>
-TemporaryBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
-                               const nsAString& aContentType,
-                               ErrorResult& aRv)
-{
-  if (aStart + aLength > mLength) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
-  }
-
-  RefPtr<BlobImpl> impl =
-    new TemporaryBlobImpl(this, aStart + mStartPos,
-                              aLength, aContentType);
-  return impl.forget();
-}
-
-void
-TemporaryBlobImpl::CreateInputStream(nsIInputStream** aStream,
-                                     ErrorResult& aRv)
-{
-  nsCOMPtr<nsIInputStream> stream =
-    new nsTemporaryFileInputStream(mFileDescOwner, mStartPos,
-                                   mStartPos + mLength);
-  stream.forget(aStream);
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/file/TemporaryBlobImpl.h
+++ /dev/null
@@ -1,45 +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_TemporaryBlobImpl_h
-#define mozilla_dom_TemporaryBlobImpl_h
-
-#include "BaseBlobImpl.h"
-#include "nsTemporaryFileInputStream.h"
-
-namespace mozilla {
-namespace dom {
-
-class TemporaryBlobImpl final : public BaseBlobImpl
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  TemporaryBlobImpl(PRFileDesc* aFD, uint64_t aStartPos,
-                   uint64_t aLength, const nsAString& aContentType);
-
-  virtual void CreateInputStream(nsIInputStream** aStream,
-                                 ErrorResult& aRv) override;
-
-  virtual already_AddRefed<BlobImpl>
-  CreateSlice(uint64_t aStart, uint64_t aLength,
-              const nsAString& aContentType, ErrorResult& aRv) override;
-
-private:
-  TemporaryBlobImpl(const TemporaryBlobImpl* aOther,
-                    uint64_t aStart, uint64_t aLength,
-                    const nsAString& aContentType);
-
-  ~TemporaryBlobImpl() = default;
-
-  uint64_t mStartPos;
-  RefPtr<nsTemporaryFileInputStream::FileDescOwner> mFileDescOwner;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_TemporaryBlobImpl_h
new file mode 100644
--- /dev/null
+++ b/dom/file/TemporaryFileBlobImpl.cpp
@@ -0,0 +1,142 @@
+/* -*- 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 "TemporaryFileBlobImpl.h"
+
+#include "IPCBlobInputStreamThread.h"
+#include "nsFileStreams.h"
+#include "nsIFile.h"
+#include "nsIFileStreams.h"
+#include "nsNetUtil.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+// Here the flags needed in order to keep the temporary file opened.
+// 1. REOPEN_ON_REWIND -> otherwise the stream is not serializable more than
+//                        once.
+// 2. no DEFER_OPEN -> the file must be kept open on windows in order to be
+//                     deleted when used.
+// 3. no CLOSE_ON_EOF -> the file will be closed by the DTOR. No needs. Also
+//                       because the inputStream will not be read directly.
+// 4. no SHARE_DELETE -> We don't want to allow this file to be deleted.
+const uint32_t sTemporaryFileStreamFlags =
+  nsIFileInputStream::REOPEN_ON_REWIND;
+
+class TemporaryFileInputStream final : public nsFileInputStream
+{
+public:
+  static nsresult
+  Create(nsIFile* aFile, nsIInputStream** aInputStream)
+  {
+    MOZ_ASSERT(aFile);
+    MOZ_ASSERT(aInputStream);
+    MOZ_ASSERT(XRE_IsParentProcess());
+
+    RefPtr<TemporaryFileInputStream> stream =
+      new TemporaryFileInputStream(aFile);
+
+    nsresult rv = stream->Init(aFile, -1, -1, sTemporaryFileStreamFlags);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    stream.forget(aInputStream);
+    return NS_OK;
+  }
+
+  void
+  Serialize(InputStreamParams& aParams,
+            FileDescriptorArray& aFileDescriptors) override
+  {
+    MOZ_CRASH("This inputStream cannot be serialized.");
+  }
+
+  bool
+  Deserialize(const InputStreamParams& aParams,
+              const FileDescriptorArray& aFileDescriptors) override
+  {
+    MOZ_CRASH("This inputStream cannot be deserialized.");
+    return false;
+  }
+
+private:
+  explicit TemporaryFileInputStream(nsIFile* aFile)
+    : mFile(aFile)
+  {
+    MOZ_ASSERT(XRE_IsParentProcess());
+  }
+
+  ~TemporaryFileInputStream()
+  {
+    // Let's delete the file on the IPCBlob Thread.
+    RefPtr<IPCBlobInputStreamThread> thread =
+      IPCBlobInputStreamThread::GetOrCreate();
+    if (NS_WARN_IF(!thread)) {
+      return;
+    }
+
+    nsCOMPtr<nsIFile> file = Move(mFile);
+    thread->Dispatch(NS_NewRunnableFunction(
+      "TemporaryFileInputStream::Runnable",
+      [file]() {
+        file->Remove(false);
+      }
+    ));
+  }
+
+  nsCOMPtr<nsIFile> mFile;
+};
+
+} // anonymous
+
+TemporaryFileBlobImpl::TemporaryFileBlobImpl(nsIFile* aFile,
+                                             const nsAString& aContentType)
+  : FileBlobImpl(aFile, EmptyString(), aContentType)
+#ifdef DEBUG
+  , mInputStreamCreated(false)
+#endif
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+}
+
+TemporaryFileBlobImpl::~TemporaryFileBlobImpl()
+{
+  MOZ_ASSERT(mInputStreamCreated);
+}
+
+already_AddRefed<BlobImpl>
+TemporaryFileBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
+                                   const nsAString& aContentType,
+                                   ErrorResult& aRv)
+{
+  MOZ_CRASH("This BlobImpl is not meant to be sliced!");
+  return nullptr;
+}
+
+void
+TemporaryFileBlobImpl::CreateInputStream(nsIInputStream** aStream, ErrorResult& aRv)
+{
+#ifdef DEBUG
+  MOZ_ASSERT(!mInputStreamCreated);
+  // CreateInputStream can be called only once.
+  mInputStreamCreated = true;
+#endif
+
+  aRv = TemporaryFileInputStream::Create(mFile, aStream);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/file/TemporaryFileBlobImpl.h
@@ -0,0 +1,46 @@
+/* -*- 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_TemporaryFileBlobImpl_h
+#define mozilla_dom_TemporaryFileBlobImpl_h
+
+#include "FileBlobImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+// This class is meant to be used by TemporaryIPCBlobParent only.
+// Don't use it for anything else, please!
+// Note that CreateInputStream() _must_ be called, and called just once!
+
+// This class is a BlobImpl because it needs to be sent via IPC using
+// IPCBlobUtils.
+class TemporaryFileBlobImpl final : public FileBlobImpl
+{
+#ifdef DEBUG
+  bool mInputStreamCreated;
+#endif
+
+public:
+  explicit TemporaryFileBlobImpl(nsIFile* aFile, const nsAString& aContentType);
+
+  // Overrides
+  virtual void CreateInputStream(nsIInputStream** aInputStream,
+                                 ErrorResult& aRv) override;
+
+protected:
+  virtual ~TemporaryFileBlobImpl();
+
+private:
+  virtual already_AddRefed<BlobImpl>
+  CreateSlice(uint64_t aStart, uint64_t aLength,
+              const nsAString& aContentType, ErrorResult& aRv) override;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TemporaryFileBlobImpl_h
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -376,40 +376,43 @@ IPCBlobInputStream::AsyncWait(nsIInputSt
   // Stream is closed.
   default:
     MOZ_ASSERT(mState == eClosed);
     return NS_BASE_STREAM_CLOSED;
   }
 }
 
 void
-IPCBlobInputStream::StreamReady(nsIInputStream* aInputStream)
+IPCBlobInputStream::StreamReady(already_AddRefed<nsIInputStream> aInputStream)
 {
+  nsCOMPtr<nsIInputStream> inputStream = Move(aInputStream);
+
   // We have been closed in the meantime.
   if (mState == eClosed) {
-    if (aInputStream) {
-      aInputStream->Close();
+    if (inputStream) {
+      inputStream->Close();
     }
     return;
   }
 
-  // If aInputStream is null, it means that the serialization went wrong or the
+  // If inputStream is null, it means that the serialization went wrong or the
   // stream is not available anymore. We keep the state as pending just to block
   // any additional operation.
 
-  if (!aInputStream) {
+  if (!inputStream) {
     return;
   }
 
   // Now it's the right time to apply a slice if needed.
   if (mStart > 0 || mLength < mActor->Size()) {
-    aInputStream = new SlicedInputStream(aInputStream, mStart, mLength);
+    inputStream =
+      new SlicedInputStream(inputStream.forget(), mStart, mLength);
   }
 
-  mRemoteStream = aInputStream;
+  mRemoteStream = inputStream;
 
   MOZ_ASSERT(mState == ePending);
   mState = eRunning;
 
   nsCOMPtr<nsIFileMetadataCallback> fileMetadataCallback;
   fileMetadataCallback.swap(mFileMetadataCallback);
 
   nsCOMPtr<nsIEventTarget> fileMetadataCallbackEventTarget;
@@ -470,17 +473,18 @@ IPCBlobInputStream::InitWithExistingRang
   mLength = aLength;
 
   // In the child, we slice in StreamReady() when we set mState to eRunning.
   // But in the parent, we start out eRunning, so it's necessary to slice the
   // stream as soon as we have the information during the initialization phase
   // because the stream is immediately consumable.
   if (mState == eRunning && mRemoteStream && XRE_IsParentProcess() &&
       (mStart > 0 || mLength < mActor->Size())) {
-    mRemoteStream = new SlicedInputStream(mRemoteStream, mStart, mLength);
+    mRemoteStream =
+      new SlicedInputStream(mRemoteStream.forget(), mStart, mLength);
   }
 }
 
 // nsIInputStreamCallback
 
 NS_IMETHODIMP
 IPCBlobInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
 {
--- a/dom/file/ipc/IPCBlobInputStream.h
+++ b/dom/file/ipc/IPCBlobInputStream.h
@@ -33,17 +33,17 @@ public:
   NS_DECL_NSICLONEABLEINPUTSTREAMWITHRANGE
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSIFILEMETADATA
   NS_DECL_NSIASYNCFILEMETADATA
 
   explicit IPCBlobInputStream(IPCBlobInputStreamChild* aActor);
 
   void
-  StreamReady(nsIInputStream* aInputStream);
+  StreamReady(already_AddRefed<nsIInputStream> aInputStream);
 
 private:
   ~IPCBlobInputStream();
 
   nsresult
   MaybeExecuteInputStreamCallback(nsIInputStreamCallback* aCallback,
                                   nsIEventTarget* aEventTarget);
 
--- a/dom/file/ipc/IPCBlobInputStreamChild.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamChild.cpp
@@ -66,29 +66,29 @@ private:
 };
 
 // When the stream has been received from the parent, we inform the
 // IPCBlobInputStream.
 class StreamReadyRunnable final : public CancelableRunnable
 {
 public:
   StreamReadyRunnable(IPCBlobInputStream* aDestinationStream,
-                      nsIInputStream* aCreatedStream)
+                      already_AddRefed<nsIInputStream> aCreatedStream)
     : CancelableRunnable("dom::StreamReadyRunnable")
     , mDestinationStream(aDestinationStream)
-    , mCreatedStream(aCreatedStream)
+    , mCreatedStream(Move(aCreatedStream))
   {
     MOZ_ASSERT(mDestinationStream);
     // mCreatedStream can be null.
   }
 
   NS_IMETHOD
   Run() override
   {
-    mDestinationStream->StreamReady(mCreatedStream);
+    mDestinationStream->StreamReady(mCreatedStream.forget());
     return NS_OK;
   }
 
 private:
   RefPtr<IPCBlobInputStream> mDestinationStream;
   nsCOMPtr<nsIInputStream> mCreatedStream;
 };
 
@@ -312,17 +312,17 @@ IPCBlobInputStreamChild::RecvStreamReady
 
     pendingStream = mPendingOperations[0].mStream;
     eventTarget = mPendingOperations[0].mEventTarget;
 
     mPendingOperations.RemoveElementAt(0);
   }
 
   RefPtr<StreamReadyRunnable> runnable =
-    new StreamReadyRunnable(pendingStream, stream);
+    new StreamReadyRunnable(pendingStream, stream.forget());
 
   // If IPCBlobInputStream::AsyncWait() has been executed without passing an
   // event target, we run the callback synchronous because any thread could be
   // result to be the wrong one. See more in nsIAsyncInputStream::asyncWait
   // documentation.
   if (eventTarget) {
     eventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
   } else {
--- a/dom/file/ipc/IPCBlobInputStreamStorage.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamStorage.cpp
@@ -167,17 +167,18 @@ IPCBlobInputStreamStorage::GetStream(con
       return;
     }
 
     data->mInputStream = replacementStream;
   }
 
   // Now it's the right time to apply a slice if needed.
   if (aStart > 0 || aLength < size) {
-    clonedStream = new SlicedInputStream(clonedStream, aStart, aLength);
+    clonedStream =
+      new SlicedInputStream(clonedStream.forget(), aStart, aLength);
   }
 
   clonedStream.forget(aInputStream);
 }
 
 void
 IPCBlobInputStreamStorage::StoreCallback(const nsID& aID,
                                          IPCBlobInputStreamParentCallback* aCallback)
--- a/dom/file/ipc/IPCBlobInputStreamThread.h
+++ b/dom/file/ipc/IPCBlobInputStreamThread.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_IPCBlobInputStreamThread_h
 #define mozilla_dom_IPCBlobInputStreamThread_h
 
 #include "nsIObserverService.h"
 #include "nsIEventTarget.h"
+#include "nsIObserver.h"
 
 class nsIThread;
 
 namespace mozilla {
 namespace dom {
 
 class IPCBlobInputStreamChild;
 
--- a/dom/file/ipc/IPCBlobUtils.h
+++ b/dom/file/ipc/IPCBlobUtils.h
@@ -28,17 +28,16 @@
  * properties (size, type, name if it's a file) and the nsIInputStream.
  *
  * Before talking about the nsIInputStream it's important to say that we have
  * different kinds of Blobs, based on the different kinds of sources. A non
  * exaustive list is:
  * - a memory buffer: MemoryBlobImpl
  * - a string: StringBlobImpl
  * - a real OS file: FileBlobImpl
- * - a temporary OS file: TemporaryBlobImpl
  * - a generic nsIInputStream: StreamBlobImpl
  * - an empty blob: EmptyBlobImpl
  * - more blobs combined together: MultipartBlobImpl
  * Each one of these implementations has a custom ::CreateInputStream method.
  * So, basically, each one has a different kind of nsIInputStream (nsFileStream,
  * nsIStringInputStream, SlicedInputStream, and so on).
  *
  * Another important point to keep in mind is that a Blob can be created on the
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/PTemporaryIPCBlob.ipdl
@@ -0,0 +1,41 @@
+/* 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 PBackground;
+include protocol PChildToParentStream;
+include protocol PFileDescriptorSet;
+include protocol PIPCBlobInputStream;
+include protocol PParentToChildStream;
+
+include IPCBlob;
+
+namespace mozilla {
+namespace ipc {
+
+union IPCBlobOrError
+{
+  IPCBlob;
+  nsresult;
+};
+
+protocol PTemporaryIPCBlob
+{
+  manager PBackground;
+
+  // When this actor is created on the child side, the parent will send
+  // immediatelly back a FileDescriptor or a __delete__ in case of error.
+  // When the FileDescriptor is received, the child has to call
+  // OperationDone(). When OperationDone() is received on the parent side, the
+  // parent actor will send a __delete__.
+
+child:
+  async FileDesc(FileDescriptor aFD);
+  async __delete__(IPCBlobOrError aBlobOrError);
+
+parent:
+  async OperationDone(bool aSuccess, nsCString aContentType);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/TemporaryIPCBlobChild.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TemporaryIPCBlobChild.h"
+#include "mozilla/dom/MutableBlobStorage.h"
+#include <private/pprio.h>
+
+namespace mozilla {
+namespace dom {
+
+TemporaryIPCBlobChild::TemporaryIPCBlobChild(MutableBlobStorage* aStorage)
+  : mMutableBlobStorage(aStorage)
+  , mActive(true)
+{
+  MOZ_ASSERT(aStorage);
+}
+
+TemporaryIPCBlobChild::~TemporaryIPCBlobChild()
+{}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobChild::RecvFileDesc(const FileDescriptor& aFD)
+{
+  MOZ_ASSERT(mActive);
+
+  auto rawFD = aFD.ClonePlatformHandle();
+  PRFileDesc* prfile = PR_ImportFile(PROsfd(rawFD.release()));
+
+  mMutableBlobStorage->TemporaryFileCreated(prfile);
+  mMutableBlobStorage = nullptr;
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobChild::Recv__delete__(const IPCBlobOrError& aData)
+{
+  mActive = false;
+  mMutableBlobStorage = nullptr;
+
+  if (aData.type() == IPCBlobOrError::TIPCBlob) {
+    // This must be always deserialized.
+    RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aData.get_IPCBlob());
+    MOZ_ASSERT(blobImpl);
+
+    if (mCallback) {
+      mCallback->OperationSucceeded(blobImpl);
+    }
+  } else if(mCallback) {
+    MOZ_ASSERT(aData.type() == IPCBlobOrError::Tnsresult);
+    mCallback->OperationFailed(aData.get_nsresult());
+  }
+
+  mCallback = nullptr;
+
+  return IPC_OK();
+}
+
+void
+TemporaryIPCBlobChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mActive = false;
+  mMutableBlobStorage = nullptr;
+
+  if (mCallback) {
+    mCallback->OperationFailed(NS_ERROR_FAILURE);
+    mCallback = nullptr;
+  }
+}
+
+void
+TemporaryIPCBlobChild::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
+                                  const nsACString& aContentType)
+{
+  MOZ_ASSERT(aCallback);
+  MOZ_ASSERT(!mCallback);
+
+  if (!mActive) {
+    aCallback->OperationFailed(NS_ERROR_FAILURE);
+    return;
+  }
+
+  mCallback = aCallback;
+  SendOperationDone(true, nsCString(aContentType));
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/TemporaryIPCBlobChild.h
@@ -0,0 +1,58 @@
+/* -*- 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_ipc_TemporaryIPCBlobChild_h
+#define mozilla_dom_ipc_TemporaryIPCBlobChild_h
+
+#include "mozilla/ipc/PTemporaryIPCBlob.h"
+#include "mozilla/ipc/PTemporaryIPCBlobChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class MutableBlobStorage;
+
+class TemporaryIPCBlobChildCallback
+{
+public:
+  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+  virtual void OperationSucceeded(BlobImpl* aBlobImpl) = 0;
+  virtual void OperationFailed(nsresult aRv) = 0;
+};
+
+class TemporaryIPCBlobChild final : public mozilla::ipc::PTemporaryIPCBlobChild
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(TemporaryIPCBlobChild)
+
+  explicit TemporaryIPCBlobChild(MutableBlobStorage* aMutableBlobStorage);
+
+  void
+  AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
+             const nsACString& aContentType);
+
+private:
+  ~TemporaryIPCBlobChild();
+
+  mozilla::ipc::IPCResult
+  RecvFileDesc(const FileDescriptor& aFD) override;
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const IPCBlobOrError& aBlobOrError) override;
+
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  RefPtr<MutableBlobStorage> mMutableBlobStorage;
+  RefPtr<TemporaryIPCBlobChildCallback> mCallback;
+  bool mActive;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_TemporaryIPCBlobChild_h
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/TemporaryIPCBlobParent.cpp
@@ -0,0 +1,105 @@
+/* -*- 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 "TemporaryIPCBlobParent.h"
+
+#include "mozilla/dom/FileBlobImpl.h"
+#include "nsAnonymousTemporaryFile.h"
+#include "nsIFileStreams.h"
+#include "TemporaryFileBlobImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+TemporaryIPCBlobParent::TemporaryIPCBlobParent()
+  : mActive(true)
+{}
+
+TemporaryIPCBlobParent::~TemporaryIPCBlobParent()
+{
+  // If we still have mFile, let's remove it.
+  if (mFile) {
+    mFile->Remove(false);
+  }
+}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobParent::CreateAndShareFile()
+{
+  MOZ_ASSERT(mActive);
+  MOZ_ASSERT(!mFile);
+
+  nsresult rv = NS_OpenAnonymousTemporaryNsIFile(getter_AddRefs(mFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return SendDeleteError(rv);
+  }
+
+  PRFileDesc* fd;
+  rv = mFile->OpenNSPRFileDesc(PR_RDWR, PR_IRWXU, &fd);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return SendDeleteError(rv);
+  }
+
+  FileDescriptor fdd =
+    FileDescriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd)));
+
+  // The FileDescriptor object owns a duplicate of the file handle; we
+  // must close the original (and clean up the NSPR descriptor).
+  PR_Close(fd);
+
+  Unused << SendFileDesc(fdd);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobParent::RecvOperationDone(const bool& aSuccess,
+                                          const nsCString& aContentType)
+{
+  MOZ_ASSERT(mActive);
+  mActive = false;
+
+  if (!aSuccess) {
+    // Nothing to do.
+    Unused << Send__delete__(this, NS_ERROR_FAILURE);
+    return IPC_OK();
+  }
+
+  // Let's create the BlobImpl.
+  nsCOMPtr<nsIFile> file = Move(mFile);
+
+  RefPtr<TemporaryFileBlobImpl> blobImpl =
+    new TemporaryFileBlobImpl(file, NS_ConvertUTF8toUTF16(aContentType));
+
+  IPCBlob ipcBlob;
+  nsresult rv = IPCBlobUtils::Serialize(blobImpl, Manager(), ipcBlob);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    Unused << Send__delete__(this, NS_ERROR_FAILURE);
+    return IPC_OK();
+  }
+
+  Unused << Send__delete__(this, ipcBlob);
+  return IPC_OK();
+}
+
+void
+TemporaryIPCBlobParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mActive = false;
+}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobParent::SendDeleteError(nsresult aRv)
+{
+  MOZ_ASSERT(mActive);
+  mActive = false;
+
+  Unused << Send__delete__(this, aRv);
+  return IPC_OK();
+}
+
+} // dom namespace
+} // mozilla namespace
+
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/TemporaryIPCBlobParent.h
@@ -0,0 +1,46 @@
+/* -*- 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_ipc_TemporaryIPCBlobParent_h
+#define mozilla_dom_ipc_TemporaryIPCBlobParent_h
+
+#include "mozilla/ipc/PTemporaryIPCBlob.h"
+#include "mozilla/ipc/PTemporaryIPCBlobParent.h"
+
+class nsIFile;
+
+namespace mozilla {
+namespace dom {
+
+class TemporaryIPCBlobParent final : public mozilla::ipc::PTemporaryIPCBlobParent
+{
+public:
+  explicit TemporaryIPCBlobParent();
+
+  mozilla::ipc::IPCResult
+  CreateAndShareFile();
+
+private:
+  ~TemporaryIPCBlobParent();
+
+  mozilla::ipc::IPCResult
+  RecvOperationDone(const bool& aSuccess,
+                    const nsCString& aContentType) override;
+
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  mozilla::ipc::IPCResult
+  SendDeleteError(nsresult aRv);
+
+  nsCOMPtr<nsIFile> mFile;
+  bool mActive;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_TemporaryIPCBlobParent_h
--- a/dom/file/ipc/moz.build
+++ b/dom/file/ipc/moz.build
@@ -6,38 +6,43 @@
 
 EXPORTS.mozilla.dom.ipc += [
     'IPCBlobInputStream.h',
     'IPCBlobInputStreamChild.h',
     'IPCBlobInputStreamParent.h',
     'IPCBlobInputStreamStorage.h',
     'PendingIPCBlobChild.h',
     'PendingIPCBlobParent.h',
+    'TemporaryIPCBlobChild.h',
+    'TemporaryIPCBlobParent.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'IPCBlobUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'IPCBlobInputStream.cpp',
     'IPCBlobInputStreamChild.cpp',
     'IPCBlobInputStreamParent.cpp',
     'IPCBlobInputStreamStorage.cpp',
     'IPCBlobInputStreamThread.cpp',
     'IPCBlobUtils.cpp',
     'PendingIPCBlobChild.cpp',
     'PendingIPCBlobParent.cpp',
+    'TemporaryIPCBlobChild.cpp',
+    'TemporaryIPCBlobParent.cpp',
 ]
 
 IPDL_SOURCES += [
     'BlobTypes.ipdlh',
     'IPCBlob.ipdlh',
     'PIPCBlobInputStream.ipdl',
     'PPendingIPCBlob.ipdl',
+    'PTemporaryIPCBlob.ipdl',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/file',
     '/dom/ipc',
     '/dom/workers',
     '/xpcom/build',
 ]
--- a/dom/file/ipc/tests/browser.ini
+++ b/dom/file/ipc/tests/browser.ini
@@ -1,5 +1,7 @@
 [DEFAULT]
 support-files =
   empty.html
 
 [browser_ipcBlob.js]
+[browser_ipcBlob_temporary.js]
+support-files = temporary.sjs
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/tests/browser_ipcBlob_temporary.js
@@ -0,0 +1,114 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
+
+requestLongerTimeout(3);
+
+
+const BASE_URI = "http://mochi.test:8888/browser/dom/file/ipc/tests/empty.html";
+
+add_task(async function test() {
+  await SpecialPowers.pushPrefEnv({ "set" : [
+    ["dom.blob.memoryToTemporaryFile", 1 ],
+    ["dom.ipc.processCount", 4],
+  ]});
+
+  let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
+  let browser1 = gBrowser.getBrowserForTab(tab1);
+
+  let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
+  let browser2 = gBrowser.getBrowserForTab(tab2);
+
+  await ContentTask.spawn(browser2, null, function() {
+    content.window.testPromise = new content.window.Promise(resolve => {
+      let bc = new content.window.BroadcastChannel('foobar');
+      bc.onmessage = e => {
+        function realTest() {
+          return new content.window.Promise(resolve => {
+            let count = 10;
+            for (let i = 0; i < count; ++i) {
+              info("FileReader at the same time: " + i);
+              let fr = new content.window.FileReader();
+              fr.readAsText(e.data);
+              fr.onerror = () => {
+                ok(false, "Something wrong happened.");
+              }
+
+              fr.onloadend = () => {
+                is (fr.result.length, e.data.size, "FileReader worked fine.");
+                if (!--count) {
+                  resolve(true);
+                }
+              }
+            }
+          });
+        }
+
+        let promises = [];
+        for (let i = 0; i < 5; ++i) {
+          promises.push(realTest());
+        }
+
+        Promise.all(promises).then(() => {
+          resolve(true);
+        });
+      };
+    });
+  });
+
+  let status = await ContentTask.spawn(browser1, null, function() {
+    let p = new content.window.Promise(resolve => {
+      let xhr = new content.window.XMLHttpRequest();
+      xhr.open('GET', 'temporary.sjs', true);
+      xhr.responseType = 'blob';
+      xhr.onload = () => {
+        resolve(xhr.response);
+      }
+      xhr.send();
+    });
+
+    return p.then(blob => {
+      function realTest() {
+        return new content.window.Promise(resolve => {
+          info("Let's broadcast the blob...");
+          let bc = new content.window.BroadcastChannel('foobar');
+          bc.postMessage(blob);
+
+          info("Here the test...");
+          let count = 10;
+          for (let i = 0; i < count; ++i) {
+            info("FileReader at the same time: " + i);
+            let fr = new content.window.FileReader();
+            fr.readAsText(blob);
+            fr.onerror = () => {
+              ok(false, "Something wrong happened.");
+            }
+
+            fr.onloadend = () => {
+              is (fr.result.length, blob.size, "FileReader worked fine.");
+              if (!--count) {
+                resolve(true);
+              }
+            }
+          }
+        });
+      }
+
+      let promises = [];
+      for (let i = 0; i < 5; ++i) {
+        promises.push(realTest());
+      }
+
+      return Promise.all(promises);
+    });
+  });
+
+  ok(status, "All good for tab1!");
+
+  status = await ContentTask.spawn(browser2, null, function() {
+    return content.window.testPromise;
+  });
+
+  ok(status, "All good for tab2!");
+
+  await BrowserTestUtils.removeTab(tab1);
+  await BrowserTestUtils.removeTab(tab2);
+});
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/tests/temporary.sjs
@@ -0,0 +1,7 @@
+function handleRequest(request, response)
+{
+  response.setHeader("Content-Type", "text/plain", false);
+
+  var data = new Array(1024*64).join("1234567890ABCDEF");
+  response.bodyOutputStream.write(data, data.length);
+}
--- a/dom/file/moz.build
+++ b/dom/file/moz.build
@@ -52,20 +52,21 @@ UNIFIED_SOURCES += [
     'MemoryBlobImpl.cpp',
     'MultipartBlobImpl.cpp',
     'MutableBlobStorage.cpp',
     'MutableBlobStreamListener.cpp',
     'nsHostObjectProtocolHandler.cpp',
     'nsHostObjectURI.cpp',
     'StreamBlobImpl.cpp',
     'StringBlobImpl.cpp',
-    'TemporaryBlobImpl.cpp',
+    'TemporaryFileBlobImpl.cpp',
 ]
 
 LOCAL_INCLUDES += [
+    '/dom/file/ipc',
     '/dom/workers',
 ]
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -398,19 +398,17 @@ HTMLCanvasElement::~HTMLCanvasElement()
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
                                    mCurrentContext, mPrintCallback,
                                    mPrintState, mOriginalCanvas,
                                    mOffscreenCanvas)
 
-NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement,
-                                             nsGenericHTMLElement,
-                                             nsIDOMHTMLCanvasElement)
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLCanvasElement, nsGenericHTMLElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement)
 
 /* virtual */ JSObject*
 HTMLCanvasElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLCanvasElementBinding::Wrap(aCx, this, aGivenProto);
 }
@@ -454,20 +452,16 @@ HTMLCanvasElement::GetWidthHeight()
 
   MOZ_ASSERT(size.width >= 0 && size.height >= 0,
              "we should've required <canvas> width/height attrs to be "
              "unsigned (non-negative) values");
 
   return size;
 }
 
-NS_IMPL_UINT_ATTR_DEFAULT_VALUE(HTMLCanvasElement, Width, width, DEFAULT_CANVAS_WIDTH)
-NS_IMPL_UINT_ATTR_DEFAULT_VALUE(HTMLCanvasElement, Height, height, DEFAULT_CANVAS_HEIGHT)
-NS_IMPL_BOOL_ATTR(HTMLCanvasElement, MozOpaque, moz_opaque)
-
 nsresult
 HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue,
                                 const nsAttrValue* aOldValue, bool aNotify)
 {
   AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
 
   return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
--- a/dom/html/HTMLCanvasElement.h
+++ b/dom/html/HTMLCanvasElement.h
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(mozilla_dom_HTMLCanvasElement_h)
 #define mozilla_dom_HTMLCanvasElement_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/WeakPtr.h"
 #include "nsIDOMEventListener.h"
-#include "nsIDOMHTMLCanvasElement.h"
 #include "nsIObserver.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsError.h"
 
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/CanvasRenderingContextHelper.h"
@@ -111,17 +110,16 @@ public:
 
 protected:
   virtual ~FrameCaptureListener() {}
 
   bool mFrameCaptureRequested;
 };
 
 class HTMLCanvasElement final : public nsGenericHTMLElement,
-                                public nsIDOMHTMLCanvasElement,
                                 public CanvasRenderingContextHelper
 {
   enum {
     DEFAULT_CANVAS_WIDTH = 300,
     DEFAULT_CANVAS_HEIGHT = 150
   };
 
   typedef layers::AsyncCanvasRenderer AsyncCanvasRenderer;
@@ -133,19 +131,16 @@ class HTMLCanvasElement final : public n
 public:
   explicit HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLCanvasElement, canvas)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
-  // nsIDOMHTMLCanvasElement
-  NS_DECL_NSIDOMHTMLCANVASELEMENT
-
   // CC
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLCanvasElement,
                                            nsGenericHTMLElement)
 
   // WebIDL
   uint32_t Height()
   {
     return GetUnsignedIntAttr(nsGkAtoms::height, DEFAULT_CANVAS_HEIGHT);
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -29,17 +29,16 @@ native nscolor(nscolor);
 [ptr] native gfxContext(gfxContext);
 typedef unsigned long long nsViewID;
 
 interface nsIArray;
 interface nsICycleCollectorListener;
 interface nsIDOMNode;
 interface nsIDOMNodeList;
 interface nsIDOMElement;
-interface nsIDOMHTMLCanvasElement;
 interface nsIDOMEvent;
 interface nsIPreloadedStyleSheet;
 interface nsITransferable;
 interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIFile;
 interface nsIDOMClientRect;
 interface nsIURI;
@@ -923,18 +922,18 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * Compare the two canvases, returning the number of differing pixels and
    * the maximum difference in a channel.  This will throw an error if
    * the dimensions of the two canvases are different.
    *
    * This method requires chrome privileges.
    */
-  uint32_t compareCanvases(in nsIDOMHTMLCanvasElement aCanvas1,
-                           in nsIDOMHTMLCanvasElement aCanvas2,
+  uint32_t compareCanvases(in nsISupports aCanvas1,
+                           in nsISupports aCanvas2,
                            out unsigned long aMaxDifference);
 
   /**
    * Returns true if a MozAfterPaint event has been queued but not yet
    * fired.
    */
   readonly attribute boolean isMozAfterPaintPending;
 
--- a/dom/interfaces/html/moz.build
+++ b/dom/interfaces/html/moz.build
@@ -4,17 +4,16 @@
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM")
 
 XPIDL_SOURCES += [
     'nsIDOMHTMLBaseElement.idl',
-    'nsIDOMHTMLCanvasElement.idl',
     'nsIDOMHTMLCollection.idl',
     'nsIDOMHTMLDocument.idl',
     'nsIDOMHTMLElement.idl',
     'nsIDOMHTMLFormElement.idl',
     'nsIDOMHTMLHtmlElement.idl',
     'nsIDOMHTMLImageElement.idl',
     'nsIDOMHTMLInputElement.idl',
     'nsIDOMHTMLLinkElement.idl',
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIDOMHTMLElement.idl"
-
-/**
- * The nsIDOMHTMLCanvasElement interface is the interface to a HTML
- * <canvas> element.
- *
- * For more information on this interface, please see
- * http://www.whatwg.org/specs/web-apps/current-work/#graphics
- *
- * @status UNDER_DEVELOPMENT
- */
-
-interface nsIDOMBlob;
-interface nsIVariant;
-interface nsIInputStreamCallback;
-
-[uuid(4e8f1316-b601-471d-8f44-3c650d91ee9b)]
-interface nsIDOMHTMLCanvasElement : nsISupports
-{
-  attribute unsigned long width;
-  attribute unsigned long height;
-  attribute boolean mozOpaque;
-};
-
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -582,23 +582,16 @@ ContentChild::RecvSetXPCOMProcessAttribu
   }
 
   mLookAndFeelCache = Move(aLookAndFeelIntCache);
   mFontFamilies = Move(aFontFamilyList);
   gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates());
   InitXPCOM(aXPCOMInit, aInitialData);
   InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
 
-#ifdef NS_PRINTING
-  // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
-  // PrintingParent, is always available for printing initiated from the parent.
-  // Create nsPrintingProxy instance later than the SystemGroup initialization.
-  RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
-#endif
-
   return IPC_OK();
 }
 
 bool
 ContentChild::Init(MessageLoop* aIOLoop,
                    base::ProcessId aParentPid,
                    IPC::Channel* aChannel,
                    uint64_t aChildID,
@@ -676,16 +669,22 @@ ContentChild::Init(MessageLoop* aIOLoop,
 
 #ifdef MOZ_CRASHREPORTER
   CrashReporterClient::InitSingleton(this);
 #endif
 
   mID = aChildID;
   mIsForBrowser = aIsForBrowser;
 
+#ifdef NS_PRINTING
+  // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
+  // PrintingParent, is always available for printing initiated from the parent.
+  RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
+#endif
+
   SetProcessName(NS_LITERAL_STRING("Web Content"));
 
 #ifdef NIGHTLY_BUILD
   // NOTE: We have to register the annotator on the main thread, as annotators
   // only affect a single thread.
   SystemGroup::Dispatch(TaskCategory::Other,
                         NS_NewRunnableFunction("RegisterPendingInputEventHangAnnotator", [] {
                           HangMonitor::RegisterAnnotator(
@@ -3031,21 +3030,26 @@ ContentChild::RecvShutdown()
 
   // Start a timer that will insure we quickly exit after a reasonable
   // period of time. Prevents shutdown hangs after our connection to the
   // parent closes.
   StartForceKillTimer();
 
 #if defined(MOZ_CRASHREPORTER)
   CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"),
-                                     NS_LITERAL_CSTRING("SendFinishShutdown"));
-#endif
+                                     NS_LITERAL_CSTRING("SendFinishShutdown (sending)"));
+  bool sent = SendFinishShutdown();
+  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"),
+                                     sent ? NS_LITERAL_CSTRING("SendFinishShutdown (sent)")
+                                          : NS_LITERAL_CSTRING("SendFinishShutdown (failed)"));
+#else
   // Ignore errors here. If this fails, the parent will kill us after a
   // timeout.
   Unused << SendFinishShutdown();
+#endif
   return IPC_OK();
 }
 
 PBrowserOrId
 ContentChild::GetBrowserOrId(TabChild* aTabChild)
 {
   if (!aTabChild ||
     this == aTabChild->Manager()) {
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -968,19 +968,21 @@ TabParent::RecvPDocAccessibleConstructor
       return IPC_FAIL_NO_REASON(this);
     }
 
     doc->SetTopLevel();
     a11y::DocManager::RemoteDocAdded(doc);
 #ifdef XP_WIN
     a11y::WrapperFor(doc)->SetID(aMsaaID);
     MOZ_ASSERT(!aDocCOMProxy.IsNull());
+#ifdef NIGHTLY_BUILD
     if (aDocCOMProxy.IsNull()) {
       return IPC_FAIL(this, "Constructing a top-level PDocAccessible with null COM proxy");
     }
+#endif
 
     RefPtr<IAccessible> proxy(aDocCOMProxy.Get());
     doc->SetCOMInterface(proxy);
     doc->MaybeInitWindowEmulation();
     doc->SendParentCOMProxy();
 #endif
   }
 #endif
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -39,16 +39,17 @@
 #include "nsIScriptChannel.h"
 #include "nsIDocument.h"
 #include "nsILoadInfo.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsSandboxFlags.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsILoadInfo.h"
 #include "nsContentSecurityManager.h"
 
 #include "mozilla/ipc/URIUtils.h"
 
 using mozilla::dom::AutoEntryScript;
 
@@ -229,17 +230,17 @@ nsresult nsJSThunk::EvaluateScript(nsICh
 
     nsAutoCString script(mScript);
     // Unescape the script
     NS_UnescapeURL(script);
 
 
     // New script entry point required, due to the "Create a script" step of
     // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
-    nsAutoMicroTask mt;
+    mozilla::nsAutoMicroTask mt;
     AutoEntryScript aes(innerGlobal, "javascript: URI", true);
     JSContext* cx = aes.cx();
     JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());
     NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
 
     //-- Don't execute unless the script principal subsumes the
     //   principal of the context.
     nsIPrincipal* objectPrincipal = nsContentUtils::ObjectPrincipal(globalJSObject);
deleted file mode 100644
--- a/dom/media/EncodedBufferCache.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "EncodedBufferCache.h"
-#include "prio.h"
-#include "nsAnonymousTemporaryFile.h"
-#include "mozilla/Monitor.h"
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/File.h"
-#include "nsThreadUtils.h"
-#include "nsXULAppAPI.h"
-
-namespace mozilla {
-
-void
-EncodedBufferCache::AppendBuffer(nsTArray<uint8_t> & aBuf)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-
-  MutexAutoLock lock(mMutex);
-  mDataSize += aBuf.Length();
-
-  mEncodedBuffers.AppendElement()->SwapElements(aBuf);
-
-  if (!mTempFileEnabled && mDataSize > mMaxMemoryStorage) {
-    nsresult rv;
-    PRFileDesc* tempFD = nullptr;
-    {
-      // Release the mMutex because of the sync dispatch to the main thread.
-      MutexAutoUnlock unlock(mMutex);
-      if (XRE_IsParentProcess()) {
-        // In case we are in the parent process, do a synchronous I/O here to open a
-        // temporary file.
-        rv = NS_OpenAnonymousTemporaryFile(&tempFD);
-      } else {
-        // In case we are in the child process, we don't have access to open a file
-        // directly due to sandbox restrictions, so we need to ask the parent process
-        // to do that for us.  In order to initiate the IPC, we need to first go to
-        // the main thread.  This is done by dispatching a runnable to the main thread.
-        // From there, we start an asynchronous IPC, and we block the current thread
-        // using a monitor while this async work is in progress.  When we receive the
-        // resulting file descriptor from the parent process, we notify the monitor
-        // and unblock the current thread and continue.
-        typedef dom::ContentChild::AnonymousTemporaryFileCallback
-          AnonymousTemporaryFileCallback;
-        bool done = false;
-        Monitor monitor("EncodeBufferCache::AppendBuffer");
-        RefPtr<dom::ContentChild> cc = dom::ContentChild::GetSingleton();
-        nsCOMPtr<nsIRunnable> runnable =
-          NewRunnableMethod<AnonymousTemporaryFileCallback>(
-            "dom::ContentChild::AsyncOpenAnonymousTemporaryFile",
-            cc,
-            &dom::ContentChild::AsyncOpenAnonymousTemporaryFile,
-            [&](PRFileDesc* aFile) {
-              rv = aFile ? NS_OK : NS_ERROR_FAILURE;
-              tempFD = aFile;
-              MonitorAutoLock lock(monitor);
-              done = true;
-              lock.Notify();
-            });
-        MonitorAutoLock lock(monitor);
-        rv = NS_DispatchToMainThread(runnable);
-        if (NS_SUCCEEDED(rv)) {
-          while (!done) {
-            lock.Wait();
-          }
-        }
-      }
-    }
-    if (!NS_FAILED(rv)) {
-      // Check the mDataSize again since we release the mMutex before.
-      if (mDataSize > mMaxMemoryStorage) {
-        mFD = tempFD;
-        mTempFileEnabled = true;
-      } else {
-        // Close the tempFD because the data had been taken during the
-        // MutexAutoUnlock.
-        PR_Close(tempFD);
-      }
-    }
-  }
-
-  if (mTempFileEnabled) {
-    // has created temporary file, write buffer in it
-    for (uint32_t i = 0; i < mEncodedBuffers.Length(); i++) {
-      int32_t amount = PR_Write(mFD, mEncodedBuffers.ElementAt(i).Elements(), mEncodedBuffers.ElementAt(i).Length());
-      if (amount < 0 || size_t(amount) < mEncodedBuffers.ElementAt(i).Length()) {
-        NS_WARNING("Failed to write media cache block!");
-      }
-    }
-    mEncodedBuffers.Clear();
-  }
-
-}
-
-already_AddRefed<dom::Blob>
-EncodedBufferCache::ExtractBlob(nsISupports* aParent,
-                                const nsAString &aContentType)
-{
-  MutexAutoLock lock(mMutex);
-  RefPtr<dom::Blob> blob;
-  if (mTempFileEnabled) {
-    // generate new temporary file to write
-    blob = dom::Blob::CreateTemporaryBlob(aParent, mFD, 0, mDataSize,
-                                          aContentType);
-    // fallback to memory blob
-    mTempFileEnabled = false;
-    mDataSize = 0;
-    mFD = nullptr;
-  } else {
-    void* blobData = malloc(mDataSize);
-    NS_ASSERTION(blobData, "out of memory!!");
-
-    if (blobData) {
-      for (uint32_t i = 0, offset = 0; i < mEncodedBuffers.Length(); i++) {
-        memcpy((uint8_t*)blobData + offset, mEncodedBuffers.ElementAt(i).Elements(),
-               mEncodedBuffers.ElementAt(i).Length());
-        offset += mEncodedBuffers.ElementAt(i).Length();
-      }
-      blob = dom::Blob::CreateMemoryBlob(aParent, blobData, mDataSize,
-                                         aContentType);
-      mEncodedBuffers.Clear();
-    } else
-      return nullptr;
-  }
-  mDataSize = 0;
-  return blob.forget();
-}
-
-size_t
-EncodedBufferCache::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
-{
-  MutexAutoLock lock(mMutex);
-  return mEncodedBuffers.ShallowSizeOfExcludingThis(aMallocSizeOf);
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/EncodedBufferCache.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef EncodedBufferCache_h_
-#define EncodedBufferCache_h_
-
-#include "nsCOMPtr.h"
-#include "nsTArray.h"
-#include "mozilla/Mutex.h"
-
-struct PRFileDesc;
-
-namespace mozilla {
-
-namespace dom {
-class Blob;
-} // namespace dom
-
-/**
- * Data is moved into a temporary file when it grows beyond
- * the maximal size passed in the Init function.
- * The AppendBuffer and ExtractBlob methods are thread-safe and can be called on
- * different threads at the same time.
- */
-class EncodedBufferCache
-{
-public:
-  explicit EncodedBufferCache(uint32_t aMaxMemoryStorage)
-  : mFD(nullptr),
-    mMutex("EncodedBufferCache.Data.Mutex"),
-    mDataSize(0),
-    mMaxMemoryStorage(aMaxMemoryStorage),
-    mTempFileEnabled(false) { }
-  ~EncodedBufferCache()
-  {
-  }
-  // Append buffers in cache, check if the queue is too large then switch to write buffer to file system
-  // aBuf will append to mEncodedBuffers or temporary File, aBuf also be cleared
-  void AppendBuffer(nsTArray<uint8_t> & aBuf);
-  // Read all buffer from memory or file System, also Remove the temporary file or clean the buffers in memory.
-  already_AddRefed<dom::Blob> ExtractBlob(nsISupports* aParent, const nsAString &aContentType);
-  // Returns the heap size in bytes of our internal buffers.
-  // Note that this intentionally ignores the data in the temp file.
-  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
-
-private:
-  //array for storing the encoded data.
-  nsTArray<nsTArray<uint8_t> > mEncodedBuffers;
-  // File handle for the temporary file
-  PRFileDesc* mFD;
-  // Used to protect the mEncodedBuffer for avoiding AppendBuffer/Consume on different thread at the same time.
-  Mutex mMutex;
-  // the current buffer size can be read
-  uint64_t mDataSize;
-  // The maximal buffer allowed in memory
-  uint32_t mMaxMemoryStorage;
-  // indicate the buffer is stored on temporary file or not
-  bool mTempFileEnabled;
-};
-
-} // namespace mozilla
-
-#endif
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -4,26 +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 "MediaRecorder.h"
 
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "DOMMediaStream.h"
-#include "EncodedBufferCache.h"
 #include "GeckoProfiler.h"
 #include "MediaDecoder.h"
 #include "MediaEncoder.h"
 #include "MediaStreamGraphImpl.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/AudioStreamTrack.h"
 #include "mozilla/dom/BlobEvent.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaRecorderErrorEvent.h"
+#include "mozilla/dom/MutableBlobStorage.h"
 #include "mozilla/dom/VideoStreamTrack.h"
 #include "mozilla/media/MediaUtils.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TaskQueue.h"
 #include "nsAutoPtr.h"
 #include "nsCharSeparatedTokenizer.h"
@@ -196,43 +196,100 @@ NS_IMPL_RELEASE_INHERITED(MediaRecorder,
 class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
                               public DOMMediaStream::TrackListener
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Session)
 
   // Main thread task.
   // Create a blob event and send back to client.
   class PushBlobRunnable : public Runnable
+                         , public MutableBlobStorageCallback
   {
   public:
-    explicit PushBlobRunnable(Session* aSession)
+    NS_DECL_ISUPPORTS_INHERITED
+
+    // aDestroyRunnable can be null. If it's not, it will be dispatched after
+    // the PushBlobRunnable::Run().
+    PushBlobRunnable(Session* aSession, Runnable* aDestroyRunnable)
       : Runnable("dom::MediaRecorder::Session::PushBlobRunnable")
       , mSession(aSession)
+      , mDestroyRunnable(aDestroyRunnable)
     { }
 
     NS_IMETHOD Run() override
     {
       LOG(LogLevel::Debug, ("Session.PushBlobRunnable s=(%p)", mSession.get()));
       MOZ_ASSERT(NS_IsMainThread());
 
+      mSession->GetBlobWhenReady(this);
+      return NS_OK;
+    }
+
+    void
+    BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob,
+                       nsresult aRv) override
+    {
       RefPtr<MediaRecorder> recorder = mSession->mRecorder;
       if (!recorder) {
-        return NS_OK;
+        return;
+      }
+
+      if (NS_FAILED(aRv)) {
+        recorder->NotifyError(aRv);
+        return;
+      }
+
+      nsresult rv = recorder->CreateAndDispatchBlobEvent(aBlob);
+      if (NS_FAILED(rv)) {
+        recorder->NotifyError(aRv);
       }
 
-      nsresult rv = recorder->CreateAndDispatchBlobEvent(mSession->GetEncodedData());
-      if (NS_FAILED(rv)) {
-        recorder->NotifyError(rv);
+      if (mDestroyRunnable &&
+          NS_FAILED(NS_DispatchToMainThread(mDestroyRunnable.forget()))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
+      }
+    }
+
+  private:
+    ~PushBlobRunnable() = default;
+
+    RefPtr<Session> mSession;
+
+    // The generation of the blob is async. In order to avoid dispatching the
+    // DestroyRunnable before pushing the blob event, we store the runnable
+    // here.
+    RefPtr<Runnable> mDestroyRunnable;
+  };
+
+  class StoreEncodedBufferRunnable final : public Runnable
+  {
+    RefPtr<Session> mSession;
+    nsTArray<nsTArray<uint8_t>> mBuffer;
+
+  public:
+    StoreEncodedBufferRunnable(Session* aSession,
+                               nsTArray<nsTArray<uint8_t>>&& aBuffer)
+      : Runnable("StoreEncodedBufferRunnable")
+      , mSession(aSession)
+      , mBuffer(Move(aBuffer))
+    {}
+
+    NS_IMETHOD
+    Run() override
+    {
+      mSession->MaybeCreateMutableBlobStorage();
+      for (uint32_t i = 0; i < mBuffer.Length(); i++) {
+        if (!mBuffer[i].IsEmpty()) {
+          mSession->mMutableBlobStorage->Append(mBuffer[i].Elements(),
+                                                mBuffer[i].Length());
+        }
       }
 
       return NS_OK;
     }
-
-  private:
-    RefPtr<Session> mSession;
   };
 
   // Notify encoder error, run in main thread task. (Bug 1095381)
   class EncoderErrorNotifierRunnable : public Runnable
   {
   public:
     explicit EncoderErrorNotifierRunnable(Session* aSession)
       : Runnable("dom::MediaRecorder::Session::EncoderErrorNotifierRunnable")
@@ -428,19 +485,18 @@ public:
     : mRecorder(aRecorder)
     , mTimeSlice(aTimeSlice)
     , mStopIssued(false)
     , mIsStartEventFired(false)
     , mNeedSessionEndTask(true)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    uint32_t maxMem = Preferences::GetUint("media.recorder.max_memory",
-                                           MAX_ALLOW_MEMORY_BUFFER);
-    mEncodedBufferCache = new EncodedBufferCache(maxMem);
+    mMaxMemory = Preferences::GetUint("media.recorder.max_memory",
+                                      MAX_ALLOW_MEMORY_BUFFER);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
 
   void PrincipalChanged(MediaStreamTrack* aTrack) override
   {
     NS_ASSERTION(mMediaStreamTracks.Contains(aTrack),
                  "Principal changed for unrecorded track");
     if (!MediaStreamTracksPrincipalSubsumes()) {
@@ -546,37 +602,53 @@ public:
     return NS_OK;
   }
 
   nsresult RequestData()
   {
     LOG(LogLevel::Debug, ("Session.RequestData"));
     MOZ_ASSERT(NS_IsMainThread());
 
-    if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+    if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, nullptr)))) {
       MOZ_ASSERT(false, "RequestData NS_DispatchToMainThread failed");
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
-  already_AddRefed<nsIDOMBlob> GetEncodedData()
+  void
+  MaybeCreateMutableBlobStorage()
+  {
+    if (!mMutableBlobStorage) {
+      mMutableBlobStorage =
+        new MutableBlobStorage(MutableBlobStorage::eCouldBeInTemporaryFile,
+                               nullptr, mMaxMemory);
+    }
+  }
+
+  void
+  GetBlobWhenReady(MutableBlobStorageCallback* aCallback)
   {
     MOZ_ASSERT(NS_IsMainThread());
-    return mEncodedBufferCache->ExtractBlob(mRecorder->GetParentObject(),
-                                            mMimeType);
+
+    MaybeCreateMutableBlobStorage();
+    mMutableBlobStorage->GetBlobWhenReady(mRecorder->GetParentObject(),
+                                          NS_ConvertUTF16toUTF8(mMimeType),
+                                          aCallback);
+    mMutableBlobStorage = nullptr;
   }
 
   RefPtr<SizeOfPromise>
   SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
   {
     MOZ_ASSERT(NS_IsMainThread());
-    size_t encodedBufferSize =
-      mEncodedBufferCache->SizeOfExcludingThis(aMallocSizeOf);
+    size_t encodedBufferSize = mMutableBlobStorage
+                                 ? mMutableBlobStorage->SizeOfCurrentMemoryBuffer()
+                                 : 0;
 
     if (!mEncoder) {
       return SizeOfPromise::CreateAndResolve(encodedBufferSize, __func__);
     }
 
     auto& encoder = mEncoder;
     return InvokeAsync(mEncoderThread, __func__,
       [encoder, encodedBufferSize, aMallocSizeOf]() {
@@ -588,21 +660,21 @@ public:
 private:
   // Only DestroyRunnable is allowed to delete Session object on main thread.
   virtual ~Session()
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mShutdownPromise);
     LOG(LogLevel::Debug, ("Session.~Session (%p)", this));
   }
-  // Pull encoded media data from MediaEncoder and put into EncodedBufferCache.
+  // Pull encoded media data from MediaEncoder and put into MutableBlobStorage.
   // Destroy this session object in the end of this function.
   // If the bool aForceFlush is true, we will force to dispatch a
   // PushBlobRunnable to main thread.
-  void Extract(bool aForceFlush)
+  void Extract(bool aForceFlush, Runnable* aDestroyRunnable)
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
     LOG(LogLevel::Debug, ("Session.Extract %p", this));
 
     AUTO_PROFILER_LABEL("MediaRecorder::Session::Extract", OTHER);
 
     // Pull encoded media data from MediaEncoder
@@ -610,36 +682,37 @@ private:
     nsresult rv = mEncoder->GetEncodedData(&encodedBuf);
     if (NS_FAILED(rv)) {
       MOZ_RELEASE_ASSERT(encodedBuf.IsEmpty());
       // Even if we failed to encode more data, it might be time to push a blob
       // with already encoded data.
     }
 
     // Append pulled data into cache buffer.
-    for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
-      if (!encodedBuf[i].IsEmpty()) {
-        mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
-      }
-    }
+    NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
+                                                           Move(encodedBuf)));
 
     // Whether push encoded data back to onDataAvailable automatically or we
     // need a flush.
     bool pushBlob = aForceFlush;
     if (!pushBlob &&
         mTimeSlice > 0 &&
         (TimeStamp::Now()-mLastBlobTimeStamp).ToMilliseconds() > mTimeSlice) {
       pushBlob = true;
     }
     if (pushBlob) {
-      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, aDestroyRunnable)))) {
         MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
       } else {
         mLastBlobTimeStamp = TimeStamp::Now();
       }
+    } else if (aDestroyRunnable) {
+      if (NS_FAILED(NS_DispatchToMainThread(aDestroyRunnable))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
+      }
     }
   }
 
   void MediaStreamReady(DOMMediaStream* aStream) {
     MOZ_RELEASE_ASSERT(aStream);
 
     if (mStopIssued) {
       return;
@@ -889,83 +962,82 @@ private:
     if (NS_FAILED(rv)) {
       mRecorder->ForceInactive();
       NS_DispatchToMainThread(
         NewRunnableMethod<nsresult>("dom::MediaRecorder::NotifyError",
                                     mRecorder,
                                     &MediaRecorder::NotifyError,
                                     rv));
     }
+
+    RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
+
     if (rv != NS_ERROR_DOM_SECURITY_ERR) {
       // Don't push a blob if there was a security error.
-      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, destroyRunnable)))) {
         MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
       }
+    } else {
+      if (NS_FAILED(NS_DispatchToMainThread(destroyRunnable))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
+      }
     }
-    if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
-      MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
-    }
+
     mNeedSessionEndTask = false;
   }
 
   void MediaEncoderInitialized()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
     // Pull encoded metadata from MediaEncoder
     nsTArray<nsTArray<uint8_t> > encodedBuf;
     nsresult rv = mEncoder->GetEncodedMetadata(&encodedBuf, mMimeType);
     if (NS_FAILED(rv)) {
       MOZ_ASSERT(false);
       return;
     }
 
     // Append pulled data into cache buffer.
-    for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
-      if (!encodedBuf[i].IsEmpty()) {
-        mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
-      }
-    }
+    NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
+                                                           Move(encodedBuf)));
   }
 
   void MediaEncoderDataAvailable()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
     if (!mIsStartEventFired) {
       NS_DispatchToMainThread(
         new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
       mIsStartEventFired = true;
     }
 
-    Extract(false);
+    Extract(false, nullptr);
   }
 
   void MediaEncoderError()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
     NS_DispatchToMainThread(
       NewRunnableMethod<nsresult>(
         "dom::MediaRecorder::Session::DoSessionEndTask",
         this, &Session::DoSessionEndTask, NS_ERROR_FAILURE));
   }
 
   void MediaEncoderShutdown()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
     MOZ_ASSERT(mEncoder->IsShutdown());
 
-    // Forces the last blob even if it's not time for it yet.
-    Extract(true);
+    // For the stop event. Let's the creation of the blob to dispatch this runnable.
+    RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
 
-    // For the stop event.
-    if (NS_FAILED(NS_DispatchToMainThread(
-                  new DestroyRunnable(this)))) {
-      MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
-    }
+    // Forces the last blob even if it's not time for it yet.
+    Extract(true, destroyRunnable);
 
     // Clean up.
     mEncoderListener->Forget();
     DebugOnly<bool> unregistered =
       mEncoder->UnregisterListener(mEncoderListener);
     MOZ_ASSERT(unregistered);
   }
 
@@ -1060,36 +1132,40 @@ private:
   RefPtr<TaskQueue> mEncoderThread;
   // MediaEncoder pipeline.
   RefPtr<MediaEncoder> mEncoder;
   // Listener through which MediaEncoder signals us.
   RefPtr<EncoderListener> mEncoderListener;
   // Set in Shutdown() and resolved when shutdown is complete.
   RefPtr<ShutdownPromise> mShutdownPromise;
   // A buffer to cache encoded media data.
-  nsAutoPtr<EncodedBufferCache> mEncodedBufferCache;
+  RefPtr<MutableBlobStorage> mMutableBlobStorage;
+  // Max memory to use for the MutableBlobStorage.
+  uint64_t mMaxMemory;
   // Current session mimeType
   nsString mMimeType;
   // Timestamp of the last fired dataavailable event.
   TimeStamp mLastBlobTimeStamp;
-  // The interval of passing encoded data from EncodedBufferCache to onDataAvailable
-  // handler. "mTimeSlice < 0" means Session object does not push encoded data to
-  // onDataAvailable, instead, it passive wait the client side pull encoded data
-  // by calling requestData API.
+  // The interval of passing encoded data from MutableBlobStorage to
+  // onDataAvailable handler. "mTimeSlice < 0" means Session object does not
+  // push encoded data to onDataAvailable, instead, it passive wait the client
+  // side pull encoded data by calling requestData API.
   const int32_t mTimeSlice;
   // Indicate this session's stop has been called.
   bool mStopIssued;
   // Indicate the session had fire start event. Encoding thread only.
   bool mIsStartEventFired;
   // False if the InitEncoder called successfully, ensure the
   // ExtractRunnable/DestroyRunnable will end the session.
   // Main thread only.
   bool mNeedSessionEndTask;
 };
 
+NS_IMPL_ISUPPORTS_INHERITED0(MediaRecorder::Session::PushBlobRunnable, Runnable)
+
 MediaRecorder::~MediaRecorder()
 {
   LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
   UnRegisterActivityObserver();
 }
 
 MediaRecorder::MediaRecorder(DOMMediaStream& aSourceMediaStream,
                              nsPIDOMWindowInner* aOwnerWindow)
@@ -1443,26 +1519,24 @@ MediaRecorder::IsTypeSupported(const nsA
       return false;
     }
   }
 
   return true;
 }
 
 nsresult
-MediaRecorder::CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob)
+MediaRecorder::CreateAndDispatchBlobEvent(Blob* aBlob)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
 
   BlobEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
-
-  nsCOMPtr<nsIDOMBlob> blob = aBlob;
-  init.mData = static_cast<Blob*>(blob.get());
+  init.mData = aBlob;
 
   RefPtr<BlobEvent> event =
     BlobEvent::Constructor(this,
                            NS_LITERAL_STRING("dataavailable"),
                            init);
   event->SetTrusted(true);
   bool dummy;
   return DispatchEvent(event, &dummy);
--- a/dom/media/MediaRecorder.h
+++ b/dom/media/MediaRecorder.h
@@ -21,26 +21,27 @@ class DOMMediaStream;
 class ErrorResult;
 struct MediaRecorderOptions;
 class MediaStream;
 class GlobalObject;
 
 namespace dom {
 
 class AudioNode;
+class Blob;
 class DOMException;
 
 /**
  * Implementation of https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html
  * The MediaRecorder accepts a mediaStream as input source passed from UA. When recorder starts,
  * a MediaEncoder will be created and accept the mediaStream as input source.
- * Encoder will get the raw data by track data changes, encode it by selected MIME Type, then store the encoded in EncodedBufferCache object.
+ * Encoder will get the raw data by track data changes, encode it by selected MIME Type, then store the encoded in a MutableBlobStorage object.
  * The encoded data will be extracted on every timeslice passed from Start function call or by RequestData function.
  * Thread model:
- * When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in EncodedBufferCache object.
+ * When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in MutableBlobStorage object.
  * Also extract the encoded data and create blobs on every timeslice passed from start function or RequestData function called by UA.
  */
 
 class MediaRecorder final : public DOMEventTargetHelper,
                             public nsIDocumentActivity
 {
 public:
   class Session;
@@ -67,17 +68,17 @@ public:
   // If timeSlice isn't provided, UA should call the RequestData to obtain the Blob data, also set the mTimeSlice to zero.
   void Start(const Optional<int32_t>& timeSlice, ErrorResult & aResult);
   // Stop the recording activiy. Including stop the Media Encoder thread, un-hook the mediaStreamListener to encoder.
   void Stop(ErrorResult& aResult);
   // Pause a recording.
   void Pause(ErrorResult& aResult);
   // Resume a paused recording.
   void Resume(ErrorResult& aResult);
-  // Extract encoded data Blob from EncodedBufferCache.
+  // Extract encoded data Blob from MutableBlobStorage.
   void RequestData(ErrorResult& aResult);
   // Return the The DOMMediaStream passed from UA.
   DOMMediaStream* Stream() const { return mDOMStream; }
   // The current state of the MediaRecorder object.
   RecordingState State() const { return mState; }
   // Return the current encoding MIME type selected by the MediaEncoder.
   void GetMimeType(nsString &aMimeType);
 
@@ -116,17 +117,17 @@ public:
   uint32_t GetAudioBitrate() { return mAudioBitsPerSecond; }
   uint32_t GetVideoBitrate() { return mVideoBitsPerSecond; }
   uint32_t GetBitrate() { return mBitsPerSecond; }
 protected:
   virtual ~MediaRecorder();
 
   MediaRecorder& operator = (const MediaRecorder& x) = delete;
   // Create dataavailable event with Blob data and it runs in main thread
-  nsresult CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob);
+  nsresult CreateAndDispatchBlobEvent(Blob* aBlob);
   // Creating a simple event to notify UA simple event.
   void DispatchSimpleEvent(const nsAString & aStr);
   // Creating a error event with message.
   void NotifyError(nsresult aRv);
   // Set encoded MIME type.
   void SetMimeType(const nsString &aMimeType);
   void SetOptions(const MediaRecorderOptions& aInitDict);
 
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -99,17 +99,16 @@ EXPORTS += [
     'AudioStream.h',
     'Benchmark.h',
     'BufferMediaResource.h',
     'ChannelMediaDecoder.h',
     'CubebUtils.h',
     'DecoderDoctorDiagnostics.h',
     'DecoderTraits.h',
     'DOMMediaStream.h',
-    'EncodedBufferCache.h',
     'FileBlockCache.h',
     'FrameStatistics.h',
     'Intervals.h',
     'Latency.h',
     'MediaCache.h',
     'MediaContainerType.h',
     'MediaData.h',
     'MediaDataDemuxer.h',
@@ -214,17 +213,16 @@ UNIFIED_SOURCES += [
     'Benchmark.cpp',
     'CanvasCaptureMediaStream.cpp',
     'ChannelMediaDecoder.cpp',
     'ChannelMediaResource.cpp',
     'CloneableWithRangeMediaResource.cpp',
     'CubebUtils.cpp',
     'DecoderDoctorDiagnostics.cpp',
     'DOMMediaStream.cpp',
-    'EncodedBufferCache.cpp',
     'FileBlockCache.cpp',
     'FileMediaResource.cpp',
     'GetUserMediaRequest.cpp',
     'GraphDriver.cpp',
     'Latency.cpp',
     'MediaCache.cpp',
     'MediaContainerType.cpp',
     'MediaData.cpp',
--- a/dom/performance/PerformanceResourceTiming.cpp
+++ b/dom/performance/PerformanceResourceTiming.cpp
@@ -66,18 +66,32 @@ PerformanceResourceTiming::SetProperties
 
 PerformanceResourceTiming::~PerformanceResourceTiming()
 {
 }
 
 DOMHighResTimeStamp
 PerformanceResourceTiming::StartTime() const
 {
-  DOMHighResTimeStamp startTime = mTiming->RedirectStartHighRes();
-  return startTime ? startTime : mTiming->FetchStartHighRes();
+  // Force the start time to be the earliest of:
+  //  - RedirectStart
+  //  - WorkerStart
+  //  - AsyncOpen
+  // Ignore zero values.  The RedirectStart and WorkerStart values
+  // can come from earlier redirected channels prior to the AsyncOpen
+  // time being recorded.
+  DOMHighResTimeStamp redirect = mTiming->RedirectStartHighRes();
+  redirect = redirect ? redirect : DBL_MAX;
+
+  DOMHighResTimeStamp worker = mTiming->WorkerStartHighRes();
+  worker = worker ? worker : DBL_MAX;
+
+  DOMHighResTimeStamp asyncOpen = mTiming->AsyncOpenHighRes();
+
+  return std::min(asyncOpen, std::min(redirect, worker));
 }
 
 JSObject*
 PerformanceResourceTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceResourceTimingBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/performance/PerformanceResourceTiming.h
+++ b/dom/performance/PerformanceResourceTiming.h
@@ -58,16 +58,22 @@ public:
     aNextHopProtocol = mNextHopProtocol;
   }
 
   void SetNextHopProtocol(const nsAString& aNextHopProtocol)
   {
     mNextHopProtocol = aNextHopProtocol;
   }
 
+  DOMHighResTimeStamp WorkerStart() const {
+    return mTiming && mTiming->TimingAllowed()
+        ? mTiming->WorkerStartHighRes()
+        : 0;
+  }
+
   DOMHighResTimeStamp FetchStart() const {
     return mTiming
         ? mTiming->FetchStartHighRes()
         : 0;
   }
 
   DOMHighResTimeStamp RedirectStart() const {
     // We have to check if all the redirect URIs had the same origin (since
--- a/dom/performance/PerformanceTiming.cpp
+++ b/dom/performance/PerformanceTiming.cpp
@@ -66,55 +66,64 @@ PerformanceTiming::PerformanceTiming(Per
 
 // Copy the timing info from the channel so we don't need to keep the channel
 // alive just to get the timestamps.
 void
 PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
 {
   if (aChannel) {
     aChannel->GetAsyncOpen(&mAsyncOpen);
+    aChannel->GetDispatchFetchEventStart(&mWorkerStart);
     aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
     aChannel->GetRedirectCount(&mRedirectCount);
     aChannel->GetRedirectStart(&mRedirectStart);
     aChannel->GetRedirectEnd(&mRedirectEnd);
     aChannel->GetDomainLookupStart(&mDomainLookupStart);
     aChannel->GetDomainLookupEnd(&mDomainLookupEnd);
     aChannel->GetConnectStart(&mConnectStart);
     aChannel->GetSecureConnectionStart(&mSecureConnectionStart);
     aChannel->GetConnectEnd(&mConnectEnd);
     aChannel->GetRequestStart(&mRequestStart);
     aChannel->GetResponseStart(&mResponseStart);
     aChannel->GetCacheReadStart(&mCacheReadStart);
     aChannel->GetResponseEnd(&mResponseEnd);
     aChannel->GetCacheReadEnd(&mCacheReadEnd);
 
-    // the performance timing api essentially requires that the event timestamps
-    // are >= asyncOpen().. but in truth the browser engages in a number of
-    // speculative activities that sometimes mean connections and lookups begin
-    // earlier. Workaround that here by just using asyncOpen as the minimum
-    // timestamp for dns and connection info.
+    // The performance timing api essentially requires that the event timestamps
+    // have a strict relation with each other. The truth, however, is the browser
+    // engages in a number of speculative activities that sometimes mean connections
+    // and lookups begin at different times. Workaround that here by clamping
+    // these values to what we expect FetchStart to be.  This means the later of
+    // AsyncOpen or WorkerStart times.
     if (!mAsyncOpen.IsNull()) {
-      if (!mDomainLookupStart.IsNull() && mDomainLookupStart < mAsyncOpen) {
-        mDomainLookupStart = mAsyncOpen;
+      // We want to clamp to the expected FetchStart value.  This is later of
+      // the AsyncOpen and WorkerStart values.
+      const TimeStamp* clampTime = &mAsyncOpen;
+      if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
+        clampTime = &mWorkerStart;
       }
 
-      if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < mAsyncOpen) {
-        mDomainLookupEnd = mAsyncOpen;
+      if (!mDomainLookupStart.IsNull() && mDomainLookupStart < *clampTime) {
+        mDomainLookupStart = *clampTime;
+      }
+
+      if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < *clampTime) {
+        mDomainLookupEnd = *clampTime;
       }
 
-      if (!mConnectStart.IsNull() && mConnectStart < mAsyncOpen) {
-        mConnectStart = mAsyncOpen;
+      if (!mConnectStart.IsNull() && mConnectStart < *clampTime) {
+        mConnectStart = *clampTime;
       }
 
-      if (!mSecureConnectionStart.IsNull() && mSecureConnectionStart < mAsyncOpen) {
-        mSecureConnectionStart = mAsyncOpen;
+      if (!mSecureConnectionStart.IsNull() && mSecureConnectionStart < *clampTime) {
+        mSecureConnectionStart = *clampTime;
       }
 
-      if (!mConnectEnd.IsNull() && mConnectEnd < mAsyncOpen) {
-        mConnectEnd = mAsyncOpen;
+      if (!mConnectEnd.IsNull() && mConnectEnd < *clampTime) {
+        mConnectEnd = *clampTime;
       }
     }
   }
 }
 
 PerformanceTiming::~PerformanceTiming()
 {
 }
@@ -124,19 +133,23 @@ PerformanceTiming::FetchStartHighRes()
 {
   if (!mFetchStart) {
     if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return mZeroTime;
     }
     MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be "
         "valid if the performance timing is enabled");
-    mFetchStart = (!mAsyncOpen.IsNull())
-        ? TimeStampToDOMHighRes(mAsyncOpen)
-        : 0.0;
+    if (!mAsyncOpen.IsNull()) {
+      if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
+        mFetchStart = TimeStampToDOMHighRes(mWorkerStart);
+      } else {
+        mFetchStart = TimeStampToDOMHighRes(mAsyncOpen);
+      }
+    }
   }
   return mFetchStart;
 }
 
 DOMTimeMilliSec
 PerformanceTiming::FetchStart()
 {
   return static_cast<int64_t>(FetchStartHighRes());
@@ -173,17 +186,17 @@ PerformanceTiming::CheckAllowedOrigin(ns
 }
 
 bool
 PerformanceTiming::TimingAllowed() const
 {
   return mTimingAllowed;
 }
 
-uint16_t
+uint8_t
 PerformanceTiming::GetRedirectCount() const
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return 0;
   }
   if (!mAllRedirectsSameOrigin) {
     return 0;
@@ -200,16 +213,36 @@ PerformanceTiming::ShouldReportCrossOrig
   }
 
   // If the redirect count is 0, or if one of the cross-origin
   // redirects doesn't have the proper Timing-Allow-Origin header,
   // then RedirectStart and RedirectEnd will be set to zero
   return (mRedirectCount != 0) && mReportCrossOriginRedirect;
 }
 
+DOMHighResTimeStamp
+PerformanceTiming::AsyncOpenHighRes()
+{
+  if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
+      nsContentUtils::ShouldResistFingerprinting() || mAsyncOpen.IsNull()) {
+    return mZeroTime;
+  }
+  return TimeStampToDOMHighRes(mAsyncOpen);
+}
+
+DOMHighResTimeStamp
+PerformanceTiming::WorkerStartHighRes()
+{
+  if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
+      nsContentUtils::ShouldResistFingerprinting() || mWorkerStart.IsNull()) {
+    return mZeroTime;
+  }
+  return TimeStampToDOMHighRes(mWorkerStart);
+}
+
 /**
  * RedirectStartHighRes() is used by both the navigation timing and the
  * resource timing. Since, navigation timing and resource timing check and
  * interpret cross-domain redirects in a different manner,
  * RedirectStartHighRes() will make no checks for cross-domain redirect.
  * It's up to the consumers of this method (PerformanceTiming::RedirectStart()
  * and PerformanceResourceTiming::RedirectStart() to make such verifications.
  *
--- a/dom/performance/PerformanceTiming.h
+++ b/dom/performance/PerformanceTiming.h
@@ -136,33 +136,38 @@ public:
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     return GetDOMTiming()->GetUnloadEventEnd();
   }
 
-  uint16_t GetRedirectCount() const;
+  uint8_t GetRedirectCount() const;
 
   // Checks if the resource is either same origin as the page that started
   // the load, or if the response contains the Timing-Allow-Origin header
   // with a value of * or matching the domain of the loading Principal
   bool CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, nsITimedChannel* aChannel);
 
   // Cached result of CheckAllowedOrigin. If false, security sensitive
   // attributes of the resourceTiming object will be set to 0
   bool TimingAllowed() const;
 
   // If this is false the values of redirectStart/End will be 0
   // This is false if no redirects occured, or if any of the responses failed
   // the timing-allow-origin check in HttpBaseChannel::TimingAllowCheck
   bool ShouldReportCrossOriginRedirect() const;
 
+  // The last channel's AsyncOpen time.  This may occur before the FetchStart
+  // in some cases.
+  DOMHighResTimeStamp AsyncOpenHighRes();
+
   // High resolution (used by resource timing)
+  DOMHighResTimeStamp WorkerStartHighRes();
   DOMHighResTimeStamp FetchStartHighRes();
   DOMHighResTimeStamp RedirectStartHighRes();
   DOMHighResTimeStamp RedirectEndHighRes();
   DOMHighResTimeStamp DomainLookupStartHighRes();
   DOMHighResTimeStamp DomainLookupEndHighRes();
   DOMHighResTimeStamp ConnectStartHighRes();
   DOMHighResTimeStamp SecureConnectionStartHighRes();
   DOMHighResTimeStamp ConnectEndHighRes();
@@ -268,29 +273,30 @@ private:
 
   // This is an offset that will be added to each timing ([ms] resolution).
   // There are only 2 possible values: (1) logicaly equal to navigationStart
   // TimeStamp (results are absolute timstamps - wallclock); (2) "0" (results
   // are relative to the navigation start).
   DOMHighResTimeStamp mZeroTime;
 
   TimeStamp mAsyncOpen;
+  TimeStamp mWorkerStart;
   TimeStamp mRedirectStart;
   TimeStamp mRedirectEnd;
   TimeStamp mDomainLookupStart;
   TimeStamp mDomainLookupEnd;
   TimeStamp mConnectStart;
   TimeStamp mSecureConnectionStart;
   TimeStamp mConnectEnd;
   TimeStamp mRequestStart;
   TimeStamp mResponseStart;
   TimeStamp mCacheReadStart;
   TimeStamp mResponseEnd;
   TimeStamp mCacheReadEnd;
-  uint16_t mRedirectCount;
+  uint8_t mRedirectCount;
   bool mTimingAllowed;
   bool mAllRedirectsSameOrigin;
   bool mInitialized;
 
   // If the resourceTiming object should have non-zero redirectStart and
   // redirectEnd attributes. It is false if there were no redirects, or if
   // any of the responses didn't pass the timing-allow-check
   bool mReportCrossOriginRedirect;
--- a/dom/smil/nsSMILTimeValueSpec.h
+++ b/dom/smil/nsSMILTimeValueSpec.h
@@ -33,16 +33,17 @@ class EventListenerManager;
 //
 // For an overview of how this class is related to other SMIL time classes see
 // the documentation in nsSMILTimeValue.h
 
 class nsSMILTimeValueSpec
 {
 public:
   typedef mozilla::dom::Element Element;
+  typedef mozilla::dom::IDTracker IDTracker;
 
   nsSMILTimeValueSpec(nsSMILTimedElement& aOwner, bool aIsBegin);
   ~nsSMILTimeValueSpec();
 
   nsresult SetSpec(const nsAString& aStringSpec, Element* aContextNode);
   void     ResolveReferences(nsIContent* aContextNode);
   bool     IsEventBased() const;
 
@@ -81,20 +82,32 @@ protected:
   nsSMILTimedElement*           mOwner;
   bool                          mIsBegin; // Indicates if *we* are a begin spec,
                                           // not to be confused with
                                           // mParams.mSyncBegin which indicates
                                           // if we're synced with the begin of
                                           // the target.
   nsSMILTimeValueSpecParams     mParams;
 
-  class TimeReferenceElement : public mozilla::dom::IDTracker
+  /**
+   * If our nsSMILTimeValueSpec exists for a 'begin' or 'end' attribute with a
+   * value that specifies a time that is relative to the animation of some
+   * other element, it will create an instance of this class to reference and
+   * track that other element.  For example, if the nsSMILTimeValueSpec is for
+   * end='a.end+2s', an instance of this class will be created to track the
+   * element associated with the element ID "a".  This class will notify the
+   * nsSMILTimeValueSpec if the element that that ID identifies changes to a
+   * different element (or none).
+   */
+  class TimeReferenceTracker final : public IDTracker
   {
   public:
-    explicit TimeReferenceElement(nsSMILTimeValueSpec* aOwner) : mSpec(aOwner) { }
+    explicit TimeReferenceTracker(nsSMILTimeValueSpec* aOwner)
+      : mSpec(aOwner)
+    {}
     void ResetWithElement(Element* aTo) {
       RefPtr<Element> from = get();
       Unlink();
       ElementChanged(from, aTo);
     }
 
   protected:
     virtual void ElementChanged(Element* aFrom, Element* aTo) override
@@ -102,17 +115,17 @@ protected:
       IDTracker::ElementChanged(aFrom, aTo);
       mSpec->UpdateReferencedElement(aFrom, aTo);
     }
     virtual bool IsPersistent() override { return true; }
   private:
     nsSMILTimeValueSpec* mSpec;
   };
 
-  TimeReferenceElement mReferencedElement;
+  TimeReferenceTracker mReferencedElement;
 
   class EventListener final : public nsIDOMEventListener
   {
     ~EventListener() {}
   public:
     explicit EventListener(nsSMILTimeValueSpec* aOwner) : mSpec(aOwner) { }
     void Disconnect()
     {
--- a/dom/svg/SVGAnimationElement.h
+++ b/dom/svg/SVGAnimationElement.h
@@ -94,36 +94,45 @@ public:
 
  protected:
   // nsSVGElement overrides
 
   void UpdateHrefTarget(nsIContent* aNodeForContext,
                         const nsAString& aHrefStr);
   void AnimationTargetChanged();
 
-  class TargetReference : public mozilla::dom::IDTracker {
+  /**
+   * Helper that provides a reference to the element with the ID that is
+   * referenced by the animation element's 'href' attribute, if any, and that
+   * will notify the animation element if the element that that ID identifies
+   * changes to a different element (or none).  (If the 'href' attribute is not
+   * specified, then the animation target is the parent element and this helper
+   * is not used.)
+   */
+  class HrefTargetTracker final : public IDTracker {
   public:
-    explicit TargetReference(SVGAnimationElement* aAnimationElement) :
-      mAnimationElement(aAnimationElement) {}
+    explicit HrefTargetTracker(SVGAnimationElement* aAnimationElement)
+      : mAnimationElement(aAnimationElement)
+    {}
   protected:
     // We need to be notified when target changes, in order to request a
     // sample (which will clear animation effects from old target and apply
     // them to the new target) and update any event registrations.
     virtual void ElementChanged(Element* aFrom, Element* aTo) override {
       IDTracker::ElementChanged(aFrom, aTo);
       mAnimationElement->AnimationTargetChanged();
     }
 
     // We need to override IsPersistent to get persistent tracking (beyond the
     // first time the target changes)
     virtual bool IsPersistent() override { return true; }
   private:
     SVGAnimationElement* const mAnimationElement;
   };
 
-  TargetReference      mHrefTarget;
+  HrefTargetTracker    mHrefTarget;
   nsSMILTimedElement   mTimedElement;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SVGAnimationElement_h
--- a/dom/svg/SVGMPathElement.cpp
+++ b/dom/svg/SVGMPathElement.cpp
@@ -36,33 +36,33 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(SVGMPathE
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGMPathElement,
                                                 SVGMPathElementBase)
   tmp->UnlinkHrefTarget(false);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGMPathElement,
                                                   SVGMPathElementBase)
-  tmp->mHrefTarget.Traverse(&cb);
+  tmp->mPathTracker.Traverse(&cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGMPathElement,
                                              SVGMPathElementBase,
                                              nsIDOMNode,
                                              nsIDOMElement,
                                              nsIDOMSVGElement,
                                              nsIMutationObserver)
 
 // Constructor
 SVGMPathElement::SVGMPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
-  : SVGMPathElementBase(aNodeInfo),
-    mHrefTarget(this)
+  : SVGMPathElementBase(aNodeInfo)
+  , mPathTracker(this)
 {
 }
 
 SVGMPathElement::~SVGMPathElement()
 {
   UnlinkHrefTarget(false);
 }
 
@@ -83,17 +83,17 @@ SVGMPathElement::Href()
 // nsIContent methods
 
 nsresult
 SVGMPathElement::BindToTree(nsIDocument* aDocument,
                             nsIContent* aParent,
                             nsIContent* aBindingParent,
                             bool aCompileEventHandlers)
 {
-  MOZ_ASSERT(!mHrefTarget.get(),
+  MOZ_ASSERT(!mPathTracker.get(),
              "Shouldn't have href-target yet (or it should've been cleared)");
   nsresult rv = SVGMPathElementBase::BindToTree(aDocument, aParent,
                                                 aBindingParent,
                                                 aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv,rv);
 
   if (aDocument) {
     const nsAttrValue* hrefAttrValue =
@@ -200,23 +200,23 @@ SVGMPathElement::AttributeChanged(nsIDoc
 //----------------------------------------------------------------------
 // Public helper methods
 
 SVGPathElement*
 SVGMPathElement::GetReferencedPath()
 {
   if (!HasAttr(kNameSpaceID_XLink, nsGkAtoms::href) &&
       !HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
-    MOZ_ASSERT(!mHrefTarget.get(),
+    MOZ_ASSERT(!mPathTracker.get(),
                "We shouldn't have a href target "
                "if we don't have an xlink:href or href attribute");
     return nullptr;
   }
 
-  nsIContent* genericTarget = mHrefTarget.get();
+  nsIContent* genericTarget = mPathTracker.get();
   if (genericTarget && genericTarget->IsSVGElement(nsGkAtoms::path)) {
     return static_cast<SVGPathElement*>(genericTarget);
   }
   return nullptr;
 }
 
 //----------------------------------------------------------------------
 // Protected helper methods
@@ -226,47 +226,47 @@ SVGMPathElement::UpdateHrefTarget(nsICon
                                   const nsAString& aHrefStr)
 {
   nsCOMPtr<nsIURI> targetURI;
   nsCOMPtr<nsIURI> baseURI = GetBaseURI();
   nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI),
                                             aHrefStr, OwnerDoc(), baseURI);
 
   // Stop observing old target (if any)
-  if (mHrefTarget.get()) {
-    mHrefTarget.get()->RemoveMutationObserver(this);
+  if (mPathTracker.get()) {
+    mPathTracker.get()->RemoveMutationObserver(this);
   }
 
   if (aParent) {
     // Pass in |aParent| instead of |this| -- first argument is only used
     // for a call to GetComposedDoc(), and |this| might not have a current
     // document yet (if our caller is BindToTree).
-    mHrefTarget.Reset(aParent, targetURI);
+    mPathTracker.Reset(aParent, targetURI);
   } else {
     // if we don't have a parent, then there's no animateMotion element
     // depending on our target, so there's no point tracking it right now.
-    mHrefTarget.Unlink();
+    mPathTracker.Unlink();
   }
 
   // Start observing new target (if any)
-  if (mHrefTarget.get()) {
-    mHrefTarget.get()->AddMutationObserver(this);
+  if (mPathTracker.get()) {
+    mPathTracker.get()->AddMutationObserver(this);
   }
 
   NotifyParentOfMpathChange(aParent);
 }
 
 void
 SVGMPathElement::UnlinkHrefTarget(bool aNotifyParent)
 {
   // Stop observing old target (if any)
-  if (mHrefTarget.get()) {
-    mHrefTarget.get()->RemoveMutationObserver(this);
+  if (mPathTracker.get()) {
+    mPathTracker.get()->RemoveMutationObserver(this);
   }
-  mHrefTarget.Unlink();
+  mPathTracker.Unlink();
 
   if (aNotifyParent) {
     NotifyParentOfMpathChange(GetParent());
   }
 }
 
 void
 SVGMPathElement::NotifyParentOfMpathChange(nsIContent* aParent)
--- a/dom/svg/SVGMPathElement.h
+++ b/dom/svg/SVGMPathElement.h
@@ -61,20 +61,28 @@ public:
   // element, this method returns a pointer to that element. Otherwise,
   // this returns nullptr.
   SVGPathElement* GetReferencedPath();
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> Href();
 
 protected:
-  class PathReference : public mozilla::dom::IDTracker {
+  /**
+   * Helper that provides a reference to the 'path' element with the ID that is
+   * referenced by the 'mpath' element's 'href' attribute, and that will
+   * invalidate the parent of the 'mpath' and update mutation observers to the
+   * new path element if the element that that ID identifies changes to a
+   * different element (or none).
+   */
+  class PathElementTracker final : public IDTracker {
   public:
-    explicit PathReference(SVGMPathElement* aMpathElement) :
-      mMpathElement(aMpathElement) {}
+    explicit PathElementTracker(SVGMPathElement* aMpathElement)
+      : mMpathElement(aMpathElement)
+    {}
   protected:
     // We need to be notified when target changes, in order to request a sample
     // (which will clear animation effects that used the old target-path
     // and recompute the animation effects using the new target-path).
     virtual void ElementChanged(Element* aFrom, Element* aTo) override {
       IDTracker::ElementChanged(aFrom, aTo);
       if (aFrom) {
         aFrom->RemoveMutationObserver(mMpathElement);
@@ -96,15 +104,15 @@ protected:
 
   void UpdateHrefTarget(nsIContent* aParent, const nsAString& aHrefStr);
   void UnlinkHrefTarget(bool aNotifyParent);
   void NotifyParentOfMpathChange(nsIContent* aParent);
 
   enum { HREF, XLINK_HREF };
   nsSVGString        mStringAttributes[2];
   static StringInfo  sStringInfo[2];
-  PathReference      mHrefTarget;
+  PathElementTracker mPathTracker;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SVGMPathElement_h
--- a/dom/svg/SVGUseElement.cpp
+++ b/dom/svg/SVGUseElement.cpp
@@ -57,28 +57,29 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
                                                 SVGUseElementBase)
   nsAutoScriptBlocker scriptBlocker;
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginal)
   tmp->UnlinkSource();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGUseElement,
                                                   SVGUseElementBase)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginal)
-  tmp->mSource.Traverse(&cb);
+  tmp->mReferencedElementTracker.Traverse(&cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGUseElement,
                                              SVGUseElementBase,
                                              nsIMutationObserver)
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGUseElement::SVGUseElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
-  : SVGUseElementBase(aNodeInfo), mSource(this)
+  : SVGUseElementBase(aNodeInfo)
+  , mReferencedElementTracker(this)
 {
 }
 
 SVGUseElement::~SVGUseElement()
 {
   UnlinkSource();
 }
 
@@ -205,22 +206,22 @@ SVGUseElement::NodeWillBeDestroyed(const
   UnlinkSource();
 }
 
 //----------------------------------------------------------------------
 
 already_AddRefed<nsIContent>
 SVGUseElement::CreateAnonymousContent()
 {
-  if (mSource.get()) {
-    mSource.get()->RemoveMutationObserver(this);
+  if (mReferencedElementTracker.get()) {
+    mReferencedElementTracker.get()->RemoveMutationObserver(this);
   }
 
   LookupHref();
-  nsIContent* targetContent = mSource.get();
+  nsIContent* targetContent = mReferencedElementTracker.get();
   if (!targetContent)
     return nullptr;
 
   // make sure target is valid type for <use>
   // QIable nsSVGGraphicsElement would eliminate enumerating all elements
   if (!targetContent->IsAnyOfSVGElements(nsGkAtoms::svg,
                                          nsGkAtoms::symbol,
                                          nsGkAtoms::g,
@@ -295,17 +296,17 @@ SVGUseElement::CreateAnonymousContent()
 #endif // DEBUG
 
   return newcontent.forget();
 }
 
 nsIURI*
 SVGUseElement::GetSourceDocURI()
 {
-  nsIContent* targetContent = mSource.get();
+  nsIContent* targetContent = mReferencedElementTracker.get();
   if (!targetContent)
     return nullptr;
 
   return targetContent->OwnerDoc()->GetDocumentURI();
 }
 
 bool
 SVGUseElement::OurWidthAndHeightAreUsed() const
@@ -373,17 +374,17 @@ SVGUseElement::LookupHref()
     mOriginal ? mOriginal->GetBaseURI() : GetBaseURI();
   nsCOMPtr<nsIURI> baseURI = nsContentUtils::IsLocalRefURL(href)
     ? SVGObserverUtils::GetBaseURLForLocalRef(this, originURI)
     : originURI;
 
   nsCOMPtr<nsIURI> targetURI;
   nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
                                             GetComposedDoc(), baseURI);
-  mSource.Reset(this, targetURI);
+  mReferencedElementTracker.Reset(this, targetURI);
 }
 
 void
 SVGUseElement::TriggerReclone()
 {
   nsIDocument *doc = GetComposedDoc();
   if (!doc)
     return;
@@ -391,20 +392,20 @@ SVGUseElement::TriggerReclone()
   if (!presShell)
     return;
   presShell->PostRecreateFramesFor(this);
 }
 
 void
 SVGUseElement::UnlinkSource()
 {
-  if (mSource.get()) {
-    mSource.get()->RemoveMutationObserver(this);
+  if (mReferencedElementTracker.get()) {
+    mReferencedElementTracker.get()->RemoveMutationObserver(this);
   }
-  mSource.Unlink();
+  mReferencedElementTracker.Unlink();
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 /* virtual */ gfxMatrix
 SVGUseElement::PrependLocalTransformsTo(
   const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const
--- a/dom/svg/SVGUseElement.h
+++ b/dom/svg/SVGUseElement.h
@@ -76,29 +76,37 @@ public:
   already_AddRefed<SVGAnimatedLength> Y();
   already_AddRefed<SVGAnimatedLength> Width();
   already_AddRefed<SVGAnimatedLength> Height();
 
   nsIURI* GetSourceDocURI();
   URLExtraData* GetContentURLData() const { return mContentURLData; }
 
 protected:
-  class SourceReference : public mozilla::dom::IDTracker {
+  /**
+   * Helper that provides a reference to the element with the ID that is
+   * referenced by the 'use' element's 'href' attribute, and that will update
+   * the 'use' element if the element that that ID identifies changes to a
+   * different element (or none).
+   */
+  class ElementTracker final : public IDTracker {
   public:
-    explicit SourceReference(SVGUseElement* aContainer) : mContainer(aContainer) {}
+    explicit ElementTracker(SVGUseElement* aOwningUseElement)
+      : mOwningUseElement(aOwningUseElement)
+    {}
   protected:
     virtual void ElementChanged(Element* aFrom, Element* aTo) override {
       IDTracker::ElementChanged(aFrom, aTo);
       if (aFrom) {
-        aFrom->RemoveMutationObserver(mContainer);
+        aFrom->RemoveMutationObserver(mOwningUseElement);
       }
-      mContainer->TriggerReclone();
+      mOwningUseElement->TriggerReclone();
     }
   private:
-    SVGUseElement* mContainer;
+    SVGUseElement* mOwningUseElement;
   };
 
   nsSVGUseFrame* GetFrame() const;
 
   virtual LengthAttributesInfo GetLengthInfo() override;
   virtual StringAttributesInfo GetStringInfo() override;
 
   /**
@@ -117,16 +125,16 @@ protected:
   static LengthInfo sLengthInfo[4];
 
   enum { HREF, XLINK_HREF };
   nsSVGString mStringAttributes[2];
   static StringInfo sStringInfo[2];
 
   nsCOMPtr<nsIContent> mOriginal; // if we've been cloned, our "real" copy
   nsCOMPtr<nsIContent> mClone;    // cloned tree
-  SourceReference      mSource;   // observed element
+  ElementTracker       mReferencedElementTracker;
   RefPtr<URLExtraData> mContentURLData; // URL data for its anonymous content
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SVGUseElement_h
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -242,24 +242,18 @@ U2F::Register(const nsAString& aAppId,
               const Sequence<RegisterRequest>& aRegisterRequests,
               const Sequence<RegisteredKey>& aRegisteredKeys,
               U2FRegisterCallback& aCallback,
               const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
               ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
-  MOZ_ASSERT(mgr);
-  if (!mgr || mRegisterCallback.isSome()) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
+  Cancel();
 
-  Cancel();
   MOZ_ASSERT(mRegisterCallback.isNothing());
   mRegisterCallback = Some(nsMainThreadPtrHandle<U2FRegisterCallback>(
                         new nsMainThreadPtrHolder<U2FRegisterCallback>(
                             "U2F::Register::callback", &aCallback)));
 
   uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
 
   // Evaluate the AppID
@@ -302,16 +296,17 @@ U2F::Register(const nsAString& aAppId,
 
   // Build the exclusion list, if any
   nsTArray<WebAuthnScopedCredentialDescriptor> excludeList;
   RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
                                        excludeList);
 
   auto& localReqHolder = mPromiseHolder;
   auto& localCb = mRegisterCallback;
+  RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
   RefPtr<U2FPromise> p = mgr->Register(mParent, cAppId,
                                        NS_ConvertUTF16toUTF8(clientDataJSON),
                                        adjustedTimeoutMillis, excludeList);
   p->Then(mEventTarget, "dom::U2F::Register::Promise::Resolve",
           [&localCb, &localReqHolder](nsString aResponse) {
               MOZ_LOG(gU2FLog, LogLevel::Debug,
                       ("dom::U2F::Register::Promise::Resolve, response was %s",
                         NS_ConvertUTF16toUTF8(aResponse).get()));
@@ -339,24 +334,18 @@ U2F::Sign(const nsAString& aAppId,
           const nsAString& aChallenge,
           const Sequence<RegisteredKey>& aRegisteredKeys,
           U2FSignCallback& aCallback,
           const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
           ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
-  MOZ_ASSERT(mgr);
-  if (!mgr || mSignCallback.isSome()) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
+  Cancel();
 
-  Cancel();
   MOZ_ASSERT(mSignCallback.isNothing());
   mSignCallback = Some(nsMainThreadPtrHandle<U2FSignCallback>(
                     new nsMainThreadPtrHolder<U2FSignCallback>(
                         "U2F::Sign::callback", &aCallback)));
 
   uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
 
   // Evaluate the AppID
@@ -384,16 +373,17 @@ U2F::Sign(const nsAString& aAppId,
   }
 
   // Build the key list, if any
   nsTArray<WebAuthnScopedCredentialDescriptor> permittedList;
   RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
                                        permittedList);
   auto& localReqHolder = mPromiseHolder;
   auto& localCb = mSignCallback;
+  RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
   RefPtr<U2FPromise> p = mgr->Sign(mParent, cAppId,
                                    NS_ConvertUTF16toUTF8(clientDataJSON),
                                    adjustedTimeoutMillis, permittedList);
   p->Then(mEventTarget, "dom::U2F::Sign::Promise::Resolve",
           [&localCb, &localReqHolder](nsString aResponse) {
               MOZ_LOG(gU2FLog, LogLevel::Debug,
                       ("dom::U2F::Sign::Promise::Resolve, response was %s",
                         NS_ConvertUTF16toUTF8(aResponse).get()));
--- a/dom/webidl/PerformanceResourceTiming.webidl
+++ b/dom/webidl/PerformanceResourceTiming.webidl
@@ -1,30 +1,26 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
- * http://w3c-test.org/webperf/specs/ResourceTiming/#performanceresourcetiming
+ * https://w3c.github.io/resource-timing/#performanceresourcetiming
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface PerformanceResourceTiming : PerformanceEntry
 {
-  // A string with the name of that element that initiated the load.
-  // If the initiator is a CSS resource, the initiatorType attribute must return
-  // the string "css".
-  // If the initiator is an XMLHttpRequest object, the initiatorType attribute
-  // must return the string "xmlhttprequest".
   readonly attribute DOMString initiatorType;
   readonly attribute DOMString nextHopProtocol;
 
+  readonly attribute DOMHighResTimeStamp workerStart;
   readonly attribute DOMHighResTimeStamp redirectStart;
   readonly attribute DOMHighResTimeStamp redirectEnd;
   readonly attribute DOMHighResTimeStamp fetchStart;
   readonly attribute DOMHighResTimeStamp domainLookupStart;
   readonly attribute DOMHighResTimeStamp domainLookupEnd;
   readonly attribute DOMHighResTimeStamp connectStart;
   readonly attribute DOMHighResTimeStamp connectEnd;
   readonly attribute DOMHighResTimeStamp secureConnectionStart;
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -16,16 +16,17 @@
 #include "nsIPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamTransportService.h"
 #include "nsISupportsPriority.h"
 #include "nsITimer.h"
 #include "nsIURI.h"
+#include "nsIXULRuntime.h"
 #include "nsPIDOMWindow.h"
 
 #include <algorithm>
 #include "BackgroundChild.h"
 #include "GeckoProfiler.h"
 #include "jsfriendapi.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/ArrayUtils.h"
@@ -316,16 +317,25 @@ LoadContextOptions(const char* aPrefName
                 .setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
                 .setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
 #ifdef FUZZING
                 .setFuzzing(GetWorkerPref<bool>(NS_LITERAL_CSTRING("fuzzing.enabled")))
 #endif
                 .setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
                 .setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")));
 
+  nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
+  if (xr) {
+    bool safeMode = false;
+    xr->GetInSafeMode(&safeMode);
+    if (safeMode) {
+      contextOptions.disableOptionsForSafeMode();
+    }
+  }
+
   RuntimeService::SetDefaultContextOptions(contextOptions);
 
   if (rts) {
     rts->UpdateAllWorkerContextOptions();
   }
 }
 
 #ifdef JS_GC_ZEAL
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -3893,16 +3893,42 @@ EditorBase::GetEndNodeAndOffset(Selectio
 
   NS_ENSURE_TRUE(range->IsPositioned(), NS_ERROR_FAILURE);
 
   NS_IF_ADDREF(*aEndContainer = range->GetEndContainer());
   *aEndOffset = range->EndOffset();
   return NS_OK;
 }
 
+nsresult
+EditorBase::GetEndChildNode(Selection* aSelection,
+                            nsIContent** aEndNode)
+{
+  MOZ_ASSERT(aSelection);
+  MOZ_ASSERT(aEndNode);
+
+  *aEndNode = nullptr;
+
+  if (NS_WARN_IF(!aSelection->RangeCount())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  const nsRange* range = aSelection->GetRangeAt(0);
+  if (NS_WARN_IF(!range)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (NS_WARN_IF(!range->IsPositioned())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  NS_IF_ADDREF(*aEndNode = range->GetChildAtEndOffset());
+  return NS_OK;
+}
+
 /**
  * IsPreformatted() checks the style info for the node for the preformatted
  * text style.
  */
 nsresult
 EditorBase::IsPreformatted(nsIDOMNode* aNode,
                            bool* aResult)
 {
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -893,16 +893,20 @@ public:
                                         nsINode** aStartContainer,
                                         int32_t* aStartOffset);
   static nsresult GetEndNodeAndOffset(Selection* aSelection,
                                       nsIDOMNode** outEndNode,
                                       int32_t* outEndOffset);
   static nsresult GetEndNodeAndOffset(Selection* aSelection,
                                       nsINode** aEndContainer,
                                       int32_t* aEndOffset);
+
+  static nsresult GetEndChildNode(Selection* aSelection,
+                                  nsIContent** aEndNode);
+
 #if DEBUG_JOE
   static void DumpNode(nsIDOMNode* aNode, int32_t indent = 0);
 #endif
   Selection* GetSelection(SelectionType aSelectionType =
                                           SelectionType::eNormal)
   {
     nsISelectionController* sc = GetSelectionController();
     if (!sc) {
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -4721,29 +4721,32 @@ HTMLEditRules::WillAlign(Selection& aSel
     }
   }
   if (emptyDiv) {
     nsCOMPtr<nsINode> parent =
       aSelection.GetRangeAt(0) ? aSelection.GetRangeAt(0)->GetStartContainer()
                                : nullptr;
     NS_ENSURE_STATE(parent);
     int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
+    nsIContent* child = aSelection.GetRangeAt(0)->GetChildAtStartOffset();
 
     rv = SplitAsNeeded(*nsGkAtoms::div, parent, offset);
     NS_ENSURE_SUCCESS(rv, rv);
     // Consume a trailing br, if any.  This is to keep an alignment from
     // creating extra lines, if possible.
     nsCOMPtr<nsIContent> brContent =
       htmlEditor->GetNextHTMLNode(parent, offset);
     if (brContent && TextEditUtils::IsBreak(brContent)) {
       // Making use of html structure... if next node after where we are
       // putting our div is not a block, then the br we found is in same block
       // we are, so it's safe to consume it.
-      nsCOMPtr<nsIContent> sibling = htmlEditor->GetNextHTMLSibling(parent,
-                                                                    offset);
+      nsCOMPtr<nsIContent> sibling;
+      if (child) {
+        sibling = htmlEditor->GetNextHTMLSibling(child);
+      }
       if (sibling && !IsBlockNode(*sibling)) {
         rv = htmlEditor->DeleteNode(brContent);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
     nsCOMPtr<Element> div = htmlEditor->CreateNode(nsGkAtoms::div, parent,
                                                    offset);
     NS_ENSURE_STATE(div);
@@ -6377,18 +6380,20 @@ HTMLEditRules::ReturnInHeader(Selection&
   bool isEmpty;
   rv = IsEmptyBlock(aHeader, &isEmpty, MozBRCounts::no);
   NS_ENSURE_SUCCESS(rv, rv);
   if (isEmpty) {
     rv = htmlEditor->DeleteNode(&aHeader);
     NS_ENSURE_SUCCESS(rv, rv);
     // Layout tells the caret to blink in a weird place if we don't place a
     // break after the header.
-    nsCOMPtr<nsIContent> sibling =
-      htmlEditor->GetNextHTMLSibling(headerParent, offset + 1);
+    nsCOMPtr<nsIContent> sibling;
+    if (aHeader.GetNextSibling()) {
+      sibling = htmlEditor->GetNextHTMLSibling(aHeader.GetNextSibling());
+    }
     if (!sibling || !sibling->IsHTMLElement(nsGkAtoms::br)) {
       ClearCachedStyles();
       htmlEditor->mTypeInState->ClearAllProps();
 
       // Create a paragraph
       nsIAtom& paraAtom = DefaultParagraphSeparator();
       // We want a wrapper element even if we separate with <br>
       nsCOMPtr<Element> pNode =
@@ -7489,17 +7494,21 @@ HTMLEditRules::CheckInterlinePosition(Se
     node = nullptr;
   }
   if (node && IsBlockNode(*node)) {
     aSelection.SetInterlinePosition(true);
     return;
   }
 
   // Are we before a block?  If so try set caret to prior content
-  node = htmlEditor->GetNextHTMLSibling(selNode, selOffset);
+  if (child) {
+    node = htmlEditor->GetNextHTMLSibling(child);
+  } else {
+    node = nullptr;
+  }
   if (node && IsBlockNode(*node)) {
     aSelection.SetInterlinePosition(false);
   }
 }
 
 nsresult
 HTMLEditRules::AdjustSelection(Selection* aSelection,
                                nsIEditor::EDirection aAction)
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3850,50 +3850,16 @@ HTMLEditor::GetNextHTMLSibling(nsIDOMNod
   nsCOMPtr<nsINode> node = do_QueryInterface(inNode);
   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
 
   *outNode = do_QueryInterface(GetNextHTMLSibling(node));
   return NS_OK;
 }
 
 /**
- * GetNextHTMLSibling() returns the next editable sibling, if there is
- * one within the parent.  just like above routine but takes a parent/offset
- * instead of a node.
- */
-nsIContent*
-HTMLEditor::GetNextHTMLSibling(nsINode* aParent,
-                               int32_t aOffset)
-{
-  MOZ_ASSERT(aParent);
-
-  nsIContent* node = aParent->GetChildAt(aOffset + 1);
-  if (!node || IsEditable(node)) {
-    return node;
-  }
-
-  return GetNextHTMLSibling(node);
-}
-
-nsresult
-HTMLEditor::GetNextHTMLSibling(nsIDOMNode* inParent,
-                               int32_t inOffset,
-                               nsCOMPtr<nsIDOMNode>* outNode)
-{
-  NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
-  *outNode = nullptr;
-
-  nsCOMPtr<nsINode> parent = do_QueryInterface(inParent);
-  NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
-
-  *outNode = do_QueryInterface(GetNextHTMLSibling(parent, inOffset));
-  return NS_OK;
-}
-
-/**
  * GetPriorHTMLNode() returns the previous editable leaf node, if there is
  * one within the <body>.
  */
 nsIContent*
 HTMLEditor::GetPriorHTMLNode(nsINode* aNode,
                              bool aNoBlockCrossing)
 {
   MOZ_ASSERT(aNode);
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -769,19 +769,16 @@ protected:
 
   nsIContent* GetPriorHTMLSibling(nsINode* aNode);
   nsresult GetPriorHTMLSibling(nsIDOMNode*inNode,
                                nsCOMPtr<nsIDOMNode>* outNode);
 
   nsIContent* GetNextHTMLSibling(nsINode* aNode);
   nsresult GetNextHTMLSibling(nsIDOMNode* inNode,
                               nsCOMPtr<nsIDOMNode>* outNode);
-  nsIContent* GetNextHTMLSibling(nsINode* aParent, int32_t aOffset);
-  nsresult GetNextHTMLSibling(nsIDOMNode* inParent, int32_t inOffset,
-                              nsCOMPtr<nsIDOMNode>* outNode);
 
   nsIContent* GetPriorHTMLNode(nsINode* aNode, bool aNoBlockCrossing = false);
   nsresult GetPriorHTMLNode(nsIDOMNode* inNode, nsCOMPtr<nsIDOMNode>* outNode,
                             bool bNoBlockCrossing = false);
   nsIContent* GetPriorHTMLNode(nsINode* aParent, int32_t aOffset,
                                bool aNoBlockCrossing = false);
   nsresult GetPriorHTMLNode(nsIDOMNode* inParent, int32_t inOffset,
                             nsCOMPtr<nsIDOMNode>* outNode,
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -1634,22 +1634,24 @@ TextEditor::SelectEntireDocument(Selecti
     return aSelection->Collapse(rootElement, 0);
   }
 
   SelectionBatcher selectionBatcher(aSelection);
   nsresult rv = EditorBase::SelectEntireDocument(aSelection);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Don't select the trailing BR node if we have one
-  int32_t selOffset;
-  nsCOMPtr<nsINode> selNode;
-  rv = GetEndNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsINode* childNode = selNode->GetChildAt(selOffset - 1);
+  nsCOMPtr<nsIContent> childNode;
+  rv = GetEndChildNode(aSelection, getter_AddRefs(childNode));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (childNode) {
+    childNode = childNode->GetPreviousSibling();
+  }
 
   if (childNode && TextEditUtils::IsMozBR(childNode)) {
     int32_t parentOffset;
     nsINode* parentNode = GetNodeLocation(childNode, &parentOffset);
 
     return aSelection->Extend(parentNode, parentOffset);
   }
 
--- a/editor/txtsvc/nsTextServicesDocument.cpp
+++ b/editor/txtsvc/nsTextServicesDocument.cpp
@@ -2384,30 +2384,20 @@ nsTextServicesDocument::GetCollapsedSele
   nsIContent* saveNode;
   if (parent->HasChildren()) {
     // XXX: We need to make sure that all of parent's
     //      children are in the text block.
 
     // If the parent has children, position the iterator
     // on the child that is to the left of the offset.
 
-    uint32_t childIndex = offset;
-
-    if (childIndex > 0) {
-      uint32_t numChildren = parent->GetChildCount();
-      NS_ASSERTION(childIndex <= numChildren, "Invalid selection offset!");
-
-      if (childIndex > numChildren) {
-        childIndex = numChildren;
-      }
-
-      childIndex -= 1;
+    nsIContent* content = range->GetChildAtStartOffset();
+    if (content && parent->GetFirstChild() != content) {
+      content = content->GetPreviousSibling();
     }
-
-    nsIContent* content = parent->GetChildAt(childIndex);
     NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
 
     rv = iter->PositionAt(content);
     NS_ENSURE_SUCCESS(rv, rv);
 
     saveNode = content;
   } else {
     // The parent has no children, so position the iterator
--- a/gfx/2d/DrawTargetCapture.h
+++ b/gfx/2d/DrawTargetCapture.h
@@ -98,16 +98,18 @@ public:
                          const Matrix& aMaskTransform,
                          const IntRect& aBounds,
                          bool aCopyBackground) override;
   virtual void PopLayer() override;
 
 
   virtual void SetTransform(const Matrix &aTransform) override;
 
+  virtual bool SupportsRegionClipping() const override { return mRefDT->SupportsRegionClipping(); }
+
   virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                                   const IntSize &aSize,
                                                                   int32_t aStride,
                                                                   SurfaceFormat aFormat) const override
   {
     return mRefDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
   }
   virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override
--- a/gfx/layers/SyncObject.h
+++ b/gfx/layers/SyncObject.h
@@ -36,17 +36,17 @@ public:
   virtual SyncHandle GetSyncHandle() = 0;
 
   virtual bool Synchronize() = 0;
 
 protected:
   SyncObjectHost() { }
 };
 
-class SyncObjectClient : public RefCounted<SyncObjectClient>
+class SyncObjectClient : public external::AtomicRefCounted<SyncObjectClient>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SyncObjectClient)
   virtual ~SyncObjectClient() { }
 
   static already_AddRefed<SyncObjectClient> CreateSyncObjectClient(SyncHandle aHandle
 #ifdef XP_WIN
                                                                      , ID3D11Device* aDevice = nullptr
--- a/gfx/layers/ipc/CompositorManagerChild.cpp
+++ b/gfx/layers/ipc/CompositorManagerChild.cpp
@@ -31,17 +31,17 @@ CompositorManagerChild::IsInitialized(ui
          sInstance->mProcessToken == aProcessToken;
 }
 
 /* static */ void
 CompositorManagerChild::InitSameProcess(uint32_t aNamespace,
                                         uint64_t aProcessToken)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (NS_WARN_IF(IsInitialized(base::GetCurrentProcId()))) {
+  if (NS_WARN_IF(IsInitialized(aProcessToken))) {
     MOZ_ASSERT_UNREACHABLE("Already initialized same process");
     return;
   }
 
   RefPtr<CompositorManagerParent> parent =
     CompositorManagerParent::CreateSameProcess();
   RefPtr<CompositorManagerChild> child =
     new CompositorManagerChild(parent, aProcessToken, aNamespace);
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -67,17 +67,17 @@ WebRenderBridgeChild::AddWebRenderParent
 void
 WebRenderBridgeChild::AddWebRenderParentCommands(const nsTArray<WebRenderParentCommand>& aCommands)
 {
   MOZ_ASSERT(mIsInTransaction);
   mParentCommands.AppendElements(aCommands);
 }
 
 bool
-WebRenderBridgeChild::BeginTransaction(const gfx::IntSize& aSize)
+WebRenderBridgeChild::BeginTransaction()
 {
   MOZ_ASSERT(!mDestroyed);
 
   UpdateFwdTransactionId();
   mIsInTransaction = true;
   mReadLockSequenceNumber = 0;
   mReadLocks.AppendElement();
   return true;
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -62,17 +62,17 @@ class WebRenderBridgeChild final : publi
 
 public:
   explicit WebRenderBridgeChild(const wr::PipelineId& aPipelineId);
 
   void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd);
   void AddWebRenderParentCommands(const nsTArray<WebRenderParentCommand>& aCommands);
 
   void UpdateResources(wr::IpcResourceUpdateQueue& aResources);
-  bool BeginTransaction(const  gfx::IntSize& aSize);
+  bool BeginTransaction();
   void EndTransaction(const wr::LayoutSize& aContentSize,
                       wr::BuiltDisplayList& dl,
                       wr::IpcResourceUpdateQueue& aResources,
                       const gfx::IntSize& aSize,
                       bool aIsSync, uint64_t aTransactionId,
                       const WebRenderScrollData& aScrollData,
                       const mozilla::TimeStamp& aTxnStartTime);
   void ProcessWebRenderParentCommands();
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -539,22 +539,20 @@ WebRenderBridgeParent::RecvSetDisplayLis
   AutoClearReadLocks clearLocks(mReadLocks);
 
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvDPEnd without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
 
   uint32_t wrEpoch = GetNextWrEpoch();
 
+  mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
+  ProcessWebRenderParentCommands(aCommands);
 
   wr::ResourceUpdateQueue resources;
-
-  mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
-  ProcessWebRenderParentCommands(aCommands, resources);
-
   if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, resources)) {
     return IPC_FAIL(this, "Failed to deserialize resource updates");
   }
 
   // If id namespaces do not match, it means the command is obsolete, probably
   // because the tab just moved to a new window.
   // In that case do not send the commands to webrender.
   if (mIdNamespace == aIdNamespace) {
@@ -625,25 +623,22 @@ WebRenderBridgeParent::RecvSetFocusTarge
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvParentCommands(nsTArray<WebRenderParentCommand>&& aCommands)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
-  wr::ResourceUpdateQueue resources;
-  ProcessWebRenderParentCommands(aCommands, resources);
-  mApi->UpdateResources(resources);
+  ProcessWebRenderParentCommands(aCommands);
   return IPC_OK();
 }
 
 void
-WebRenderBridgeParent::ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands,
-                                                      wr::ResourceUpdateQueue& aResources)
+WebRenderBridgeParent::ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands)
 {
   for (InfallibleTArray<WebRenderParentCommand>::index_type i = 0; i < aCommands.Length(); ++i) {
     const WebRenderParentCommand& cmd = aCommands[i];
     switch (cmd.type()) {
       case WebRenderParentCommand::TOpUpdateAsyncImagePipeline: {
         const OpUpdateAsyncImagePipeline& op = cmd.get_OpUpdateAsyncImagePipeline();
         mAsyncImageManager->UpdateAsyncImagePipeline(op.pipelineId(),
                                                       op.scBounds(),
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -196,18 +196,17 @@ private:
   bool UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
                        const nsTArray<ipc::Shmem>& aSmallShmems,
                        const nsTArray<ipc::Shmem>& aLargeShmems,
                        wr::ResourceUpdateQueue& aUpdates);
   bool AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey,
                         wr::ResourceUpdateQueue& aResources);
 
   uint64_t GetLayersId() const;
-  void ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands,
-                                      wr::ResourceUpdateQueue& aResources);
+  void ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands);
 
   void ClearResources();
   uint64_t GetChildLayerObserverEpoch() const { return mChildLayerObserverEpoch; }
   bool ShouldParentObserveEpoch();
   mozilla::ipc::IPCResult HandleShutdown();
 
   void AdvanceAnimations();
   void SampleAnimations(nsTArray<wr::WrOpacityProperty>& aOpacityArray,
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -236,22 +236,22 @@ WebRenderLayerManager::EndTransactionWit
 
   if (gfxPrefs::LayersDump()) {
     this->Dump();
   }
 
   // Since we don't do repeat transactions right now, just set the time
   mAnimationReadyTime = TimeStamp::Now();
 
-  LayoutDeviceIntSize size = mWidget->GetClientSize();
-  if (!WrBridge()->BeginTransaction(size.ToUnknownSize())) {
+  if (!WrBridge()->BeginTransaction()) {
     return;
   }
   DiscardCompositorAnimations();
 
+  LayoutDeviceIntSize size = mWidget->GetClientSize();
   wr::LayoutSize contentSize { (float)size.width, (float)size.height };
   wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize, mLastDisplayListSize);
   wr::IpcResourceUpdateQueue resourceUpdates(WrBridge()->GetShmemAllocator());
 
   mWebRenderCommandBuilder.BuildWebRenderCommands(builder,
                                                   resourceUpdates,
                                                   aDisplayList,
                                                   aDisplayListBuilder,
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -749,25 +749,16 @@ DisplayListBuilder::PushStickyFrame(cons
 
 void
 DisplayListBuilder::PopStickyFrame()
 {
   WRDL_LOG("PopSticky\n", mWrState);
   wr_dp_pop_clip(mWrState);
 }
 
-void
-DisplayListBuilder::PushBuiltDisplayList(BuiltDisplayList &dl)
-{
-  WRDL_LOG("PushBuiltDisplayList\n", mWrState);
-  wr_dp_push_built_display_list(mWrState,
-                                dl.dl_desc,
-                                &dl.dl.inner);
-}
-
 bool
 DisplayListBuilder::IsScrollLayerDefined(layers::FrameMetrics::ViewID aScrollId) const
 {
   return mScrollParents.find(aScrollId) != mScrollParents.end();
 }
 
 void
 DisplayListBuilder::DefineScrollLayer(const layers::FrameMetrics::ViewID& aScrollId,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -231,18 +231,16 @@ public:
   wr::WrStickyId DefineStickyFrame(const wr::LayoutRect& aContentRect,
                                    const wr::StickySideConstraint* aTop,
                                    const wr::StickySideConstraint* aRight,
                                    const wr::StickySideConstraint* aBottom,
                                    const wr::StickySideConstraint* aLeft);
   void PushStickyFrame(const wr::WrStickyId& aStickyId);
   void PopStickyFrame();
 
-  void PushBuiltDisplayList(wr::BuiltDisplayList &dl);
-
   bool IsScrollLayerDefined(layers::FrameMetrics::ViewID aScrollId) const;
   void DefineScrollLayer(const layers::FrameMetrics::ViewID& aScrollId,
                          const wr::LayoutRect& aContentRect, // TODO: We should work with strongly typed rects
                          const wr::LayoutRect& aClipRect);
   void PushScrollLayer(const layers::FrameMetrics::ViewID& aScrollId);
   void PopScrollLayer();
 
   void PushClipAndScrollInfo(const layers::FrameMetrics::ViewID& aScrollId,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1734,29 +1734,16 @@ pub unsafe extern "C" fn wr_api_finalize
                                                                 LayoutSize::zero()));
     let (_, size, dl) = frame_builder.dl_builder.finalize();
     *content_size = LayoutSize::new(size.width, size.height);
     let (data, descriptor) = dl.into_data();
     *dl_data = WrVecU8::from_vec(data);
     *dl_descriptor = descriptor;
 }
 
-#[no_mangle]
-pub extern "C" fn wr_dp_push_built_display_list(state: &mut WrState,
-                                                dl_descriptor: BuiltDisplayListDescriptor,
-                                                dl_data: &mut WrVecU8) {
-    let dl_vec = mem::replace(dl_data, WrVecU8::from_vec(Vec::new())).to_vec();
-
-    let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor);
-
-    state.frame_builder.dl_builder.push_nested_display_list(&dl);
-    let (data, _) = dl.into_data();
-    mem::replace(dl_data, WrVecU8::from_vec(data));
-}
-
 pub type VecU8 = Vec<u8>;
 pub type ArcVecU8 = Arc<VecU8>;
 
 #[no_mangle]
 pub extern "C" fn wr_add_ref_arc(arc: &ArcVecU8) -> *const VecU8 {
     Arc::into_raw(arc.clone())
 }
 
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -962,22 +962,16 @@ void wr_dp_push_box_shadow(WrState *aSta
                            ColorF aColor,
                            float aBlurRadius,
                            float aSpreadRadius,
                            float aBorderRadius,
                            BoxShadowClipMode aClipMode)
 WR_FUNC;
 
 WR_INLINE
-void wr_dp_push_built_display_list(WrState *aState,
-                                   BuiltDisplayListDescriptor aDlDescriptor,
-                                   WrVecU8 *aDlData)
-WR_FUNC;
-
-WR_INLINE
 void wr_dp_push_clip(WrState *aState,
                      uint64_t aClipId)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_clip_and_scroll_info(WrState *aState,
                                      uint64_t aScrollId,
                                      const uint64_t *aClipId)
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -57,42 +57,42 @@ public:
     : nsSVGRenderingObserver()
     , mDocWrapper(aDocWrapper)
     , mVectorImage(aVectorImage)
     , mHonoringInvalidations(true)
   {
     MOZ_ASSERT(mDocWrapper, "Need a non-null SVG document wrapper");
     MOZ_ASSERT(mVectorImage, "Need a non-null VectorImage");
 
-    StartListening();
+    StartObserving();
     Element* elem = GetTarget();
     MOZ_ASSERT(elem, "no root SVG node for us to observe");
 
     SVGObserverUtils::AddRenderingObserver(elem, this);
     mInObserverList = true;
   }
 
 
   void ResumeHonoringInvalidations()
   {
     mHonoringInvalidations = true;
   }
 
 protected:
   virtual ~SVGRootRenderingObserver()
   {
-    StopListening();
+    StopObserving();
   }
 
   virtual Element* GetTarget() override
   {
     return mDocWrapper->GetRootSVGElem();
   }
 
-  virtual void DoUpdate() override
+  virtual void OnRenderingChange() override
   {
     Element* elem = GetTarget();
     MOZ_ASSERT(elem, "missing root SVG node");
 
     if (mHonoringInvalidations && !mDocWrapper->ShouldIgnoreInvalidation()) {
       nsIFrame* frame = elem->GetPrimaryFrame();
       if (!frame || frame->PresContext()->PresShell()->IsDestroying()) {
         // We're being destroyed. Bail out.
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
 #include "mozilla/dom/ipc/PendingIPCBlobChild.h"
+#include "mozilla/dom/ipc/TemporaryIPCBlobChild.h"
 #include "mozilla/dom/quota/PQuotaChild.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/GamepadEventChannelChild.h"
 #include "mozilla/dom/GamepadTestChannelChild.h"
 #include "mozilla/dom/LocalStorage.h"
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabGroup.h"
@@ -233,16 +234,31 @@ BackgroundChildImpl::AllocPPendingIPCBlo
 
 bool
 BackgroundChildImpl::DeallocPPendingIPCBlobChild(PPendingIPCBlobChild* aActor)
 {
   delete aActor;
   return true;
 }
 
+PTemporaryIPCBlobChild*
+BackgroundChildImpl::AllocPTemporaryIPCBlobChild()
+{
+  MOZ_CRASH("This is not supposed to be called.");
+  return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPTemporaryIPCBlobChild(PTemporaryIPCBlobChild* aActor)
+{
+  RefPtr<mozilla::dom::TemporaryIPCBlobChild> actor =
+    dont_AddRef(static_cast<mozilla::dom::TemporaryIPCBlobChild*>(aActor));
+  return true;
+}
+
 PIPCBlobInputStreamChild*
 BackgroundChildImpl::AllocPIPCBlobInputStreamChild(const nsID& aID,
                                                    const uint64_t& aSize)
 {
   // IPCBlobInputStreamChild is refcounted. Here it's created and in
   // DeallocPIPCBlobInputStreamChild is released.
 
   RefPtr<mozilla::dom::IPCBlobInputStreamChild> actor =
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -84,16 +84,22 @@ protected:
 
   virtual PIPCBlobInputStreamChild*
   AllocPIPCBlobInputStreamChild(const nsID& aID,
                                 const uint64_t& aSize) override;
 
   virtual bool
   DeallocPIPCBlobInputStreamChild(PIPCBlobInputStreamChild* aActor) override;
 
+  virtual PTemporaryIPCBlobChild*
+  AllocPTemporaryIPCBlobChild() override;
+
+  virtual bool
+  DeallocPTemporaryIPCBlobChild(PTemporaryIPCBlobChild* aActor) override;
+
   virtual PFileDescriptorSetChild*
   AllocPFileDescriptorSetChild(const FileDescriptor& aFileDescriptor)
                                override;
 
   virtual bool
   DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) override;
 
   virtual PCamerasChild*
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/dom/PGamepadTestChannelParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
 #include "mozilla/dom/ipc/PendingIPCBlobParent.h"
+#include "mozilla/dom/ipc/TemporaryIPCBlobParent.h"
 #include "mozilla/dom/quota/ActorsParent.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/ipc/PChildToParentStreamParent.h"
@@ -313,16 +314,37 @@ BackgroundParentImpl::DeallocPPendingIPC
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
+PTemporaryIPCBlobParent*
+BackgroundParentImpl::AllocPTemporaryIPCBlobParent()
+{
+  return new mozilla::dom::TemporaryIPCBlobParent();
+}
+
+mozilla::ipc::IPCResult
+BackgroundParentImpl::RecvPTemporaryIPCBlobConstructor(PTemporaryIPCBlobParent* aActor)
+{
+  mozilla::dom::TemporaryIPCBlobParent* actor =
+    static_cast<mozilla::dom::TemporaryIPCBlobParent*>(aActor);
+  return actor->CreateAndShareFile();
+}
+
+bool
+BackgroundParentImpl::DeallocPTemporaryIPCBlobParent(PTemporaryIPCBlobParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
 PIPCBlobInputStreamParent*
 BackgroundParentImpl::AllocPIPCBlobInputStreamParent(const nsID& aID,
                                                      const uint64_t& aSize)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::IPCBlobInputStreamParent::Create(aID, aSize, this);
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -94,16 +94,25 @@ protected:
   virtual mozilla::ipc::IPCResult
   RecvPIPCBlobInputStreamConstructor(PIPCBlobInputStreamParent* aActor,
                                      const nsID& aID,
                                      const uint64_t& aSize) override;
 
   virtual bool
   DeallocPIPCBlobInputStreamParent(PIPCBlobInputStreamParent* aActor) override;
 
+  virtual PTemporaryIPCBlobParent*
+  AllocPTemporaryIPCBlobParent() override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvPTemporaryIPCBlobConstructor(PTemporaryIPCBlobParent* actor) override;
+
+  virtual bool
+  DeallocPTemporaryIPCBlobParent(PTemporaryIPCBlobParent* aActor) override;
+
   virtual PFileDescriptorSetParent*
   AllocPFileDescriptorSetParent(const FileDescriptor& aFileDescriptor)
                                 override;
 
   virtual bool
   DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
                                   override;
 
--- a/ipc/glue/InputStreamParams.ipdlh
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -23,23 +23,16 @@ struct StringInputStreamParams
 
 struct FileInputStreamParams
 {
   uint32_t fileDescriptorIndex;
   int32_t behaviorFlags;
   int32_t ioFlags;
 };
 
-struct TemporaryFileInputStreamParams
-{
-  uint32_t fileDescriptorIndex;
-  uint64_t startPos;
-  uint64_t endPos;
-};
-
 struct MultiplexInputStreamParams
 {
   InputStreamParams[] streams;
   uint32_t currentStream;
   nsresult status;
   bool startedReadingCurrent;
 };
 
@@ -58,17 +51,16 @@ struct IPCBlobInputStreamParams
   uint64_t start;
   uint64_t length;
 };
 
 union InputStreamParams
 {
   StringInputStreamParams;
   FileInputStreamParams;
-  TemporaryFileInputStreamParams;
   BufferedInputStreamParams;
   MIMEInputStreamParams;
   MultiplexInputStreamParams;
   SlicedInputStreamParams;
   IPCBlobInputStreamParams;
 };
 
 union OptionalInputStreamParams
--- a/ipc/glue/InputStreamUtils.cpp
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -15,17 +15,16 @@
 #include "nsComponentManagerUtils.h"
 #include "nsDebug.h"
 #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);
@@ -80,20 +79,16 @@ InputStreamHelper::DeserializeInputStrea
     case InputStreamParams::TStringInputStreamParams:
       serializable = do_CreateInstance(kStringInputStreamCID);
       break;
 
     case InputStreamParams::TFileInputStreamParams:
       serializable = do_CreateInstance(kFileInputStreamCID);
       break;
 
-    case InputStreamParams::TTemporaryFileInputStreamParams:
-      serializable = new nsTemporaryFileInputStream();
-      break;
-
     case InputStreamParams::TBufferedInputStreamParams:
       serializable = do_CreateInstance(kBufferedInputStreamCID);
       break;
 
     case InputStreamParams::TMIMEInputStreamParams:
       serializable = do_CreateInstance(kMIMEInputStreamCID);
       break;
 
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -13,16 +13,17 @@ include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PFileSystemRequest;
 include protocol PGamepadEventChannel;
 include protocol PGamepadTestChannel;
 include protocol PHttpBackgroundChannel;
 include protocol PIPCBlobInputStream;
 include protocol PPendingIPCBlob;
+include protocol PTemporaryIPCBlob;
 include protocol PMessagePort;
 include protocol PCameras;
 include protocol PQuota;
 include protocol PChildToParentStream;
 include protocol PParentToChildStream;
 include protocol PServiceWorkerManager;
 include protocol PWebAuthnTransaction;
 include protocol PUDPSocket;
@@ -62,16 +63,17 @@ sync protocol PBackground
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PFileSystemRequest;
   manages PGamepadEventChannel;
   manages PGamepadTestChannel;
   manages PHttpBackgroundChannel;
   manages PIPCBlobInputStream;
   manages PPendingIPCBlob;
+  manages PTemporaryIPCBlob;
   manages PMessagePort;
   manages PCameras;
   manages PQuota;
   manages PChildToParentStream;
   manages PParentToChildStream;
   manages PServiceWorkerManager;
   manages PWebAuthnTransaction;
   manages PUDPSocket;
@@ -127,16 +129,18 @@ parent:
   async PGamepadEventChannel();
 
   async PGamepadTestChannel();
 
   async PHttpBackgroundChannel(uint64_t channelId);
 
   async PWebAuthnTransaction();
 
+  async PTemporaryIPCBlob();
+
 child:
   async PCache();
   async PCacheStreamControl();
 
   async PParentToChildStream();
 
   async PPendingIPCBlob(IPCBlob blob);
 
--- a/js/public/Debug.h
+++ b/js/public/Debug.h
@@ -285,47 +285,16 @@ GetDebuggerMallocSizeOf(JSContext* cx);
 JS_PUBLIC_API(bool)
 FireOnGarbageCollectionHookRequired(JSContext* cx);
 
 // For each Debugger that observed a debuggee involved in the given GC event,
 // call its `onGarbageCollection` hook.
 JS_PUBLIC_API(bool)
 FireOnGarbageCollectionHook(JSContext* cx, GarbageCollectionEvent::Ptr&& data);
 
-
-
-// Handlers for observing Promises
-// -------------------------------
-//
-// The Debugger wants to observe behavior of promises, which are implemented by
-// Gecko with webidl and which SpiderMonkey knows nothing about. On the other
-// hand, Gecko knows nothing about which (if any) debuggers are observing a
-// promise's global. The compromise is that Gecko is responsible for calling
-// these handlers at the appropriate times, and SpiderMonkey will handle
-// notifying any Debugger instances that are observing the given promise's
-// global.
-
-// Notify any Debugger instances observing this promise's global that a new
-// promise was allocated.
-JS_PUBLIC_API(void)
-onNewPromise(JSContext* cx, HandleObject promise);
-
-// Notify any Debugger instances observing this promise's global that the
-// promise has settled (ie, it has either been fulfilled or rejected). Note that
-// this is *not* equivalent to the promise resolution (ie, the promise's fate
-// getting locked in) because you can resolve a promise with another pending
-// promise, in which case neither promise has settled yet.
-//
-// It is Gecko's responsibility to ensure that this is never called on the same
-// promise more than once (because a promise can only make the transition from
-// unsettled to settled once).
-JS_PUBLIC_API(void)
-onPromiseSettled(JSContext* cx, HandleObject promise);
-
-
 
 // Return true if the given value is a Debugger object, false otherwise.
 JS_PUBLIC_API(bool)
 IsDebugger(JSObject& obj);
 
 // Append each of the debuggee global objects observed by the Debugger object
 // |dbgObj| to |vector|. Returns true on success, false on failure.
 JS_PUBLIC_API(bool)
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -14,19 +14,21 @@
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsiter.h"
 
 #include "gc/Heap.h"
 #include "js/Debug.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
+#include "vm/Debugger.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/Debugger-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 static double
 MillisecondsSinceStartup()
 {
     auto now = mozilla::TimeStamp::Now();
@@ -1045,18 +1047,22 @@ RejectMaybeWrappedPromise(JSContext *cx,
 // ES2016, 25.4.1.8.
 static MOZ_MUST_USE bool
 TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseState state,
                         HandleValue valueOrReason)
 {
     RootedObject reactions(cx, &reactionsVal.toObject());
     RootedObject reaction(cx);
 
-    if (reactions->is<PromiseReactionRecord>() || IsWrapper(reactions))
+    if (reactions->is<PromiseReactionRecord>() ||
+        IsWrapper(reactions) ||
+        JS_IsDeadWrapper(reactions))
+    {
         return EnqueuePromiseReactionJob(cx, reactions, valueOrReason, state);
+    }
 
     RootedNativeObject reactionsList(cx, &reactions->as<NativeObject>());
     size_t reactionsCount = reactionsList->getDenseInitializedLength();
     MOZ_ASSERT(reactionsCount > 1, "Reactions list should be created lazily");
 
     RootedValue reactionVal(cx);
     for (size_t i = 0; i < reactionsCount; i++) {
         reactionVal = reactionsList->getDenseElement(i);
@@ -1493,17 +1499,17 @@ CreatePromiseObjectInternal(JSContext* c
     if (ShouldCaptureDebugInfo(cx)) {
         PromiseDebugInfo* debugInfo = PromiseDebugInfo::create(cx, promise);
         if (!debugInfo)
             return nullptr;
     }
 
     // Let the Debugger know about this Promise.
     if (informDebugger)
-        JS::dbg::onNewPromise(cx, promise);
+        Debugger::onNewPromise(cx, promise);
 
     return promise;
 }
 
 // ES2016, 25.4.3.1.
 static bool
 PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1666,17 +1672,17 @@ PromiseObject::create(JSContext* cx, Han
         args[0].set(exceptionVal);
 
         RootedValue calleeOrRval(cx, ObjectValue(*rejectFn));
         if (!Call(cx, calleeOrRval, UndefinedHandleValue, args, &calleeOrRval))
             return nullptr;
     }
 
     // Let the Debugger know about this Promise.
-    JS::dbg::onNewPromise(cx, promise);
+    Debugger::onNewPromise(cx, promise);
 
     // Step 11.
     return promise;
 }
 
 // ES2016, 25.4.3.1. skipping creation of resolution functions and executor
 // function invocation.
 /* static */ PromiseObject*
@@ -3380,25 +3386,25 @@ PromiseObject::reject(JSContext* cx, Han
 /* static */ void
 PromiseObject::onSettled(JSContext* cx, Handle<PromiseObject*> promise)
 {
     PromiseDebugInfo::setResolutionInfo(cx, promise);
 
     if (promise->state() == JS::PromiseState::Rejected && promise->isUnhandled())
         cx->runtime()->addUnhandledRejectedPromise(cx, promise);
 
-    JS::dbg::onPromiseSettled(cx, promise);
+    Debugger::onPromiseSettled(cx, promise);
 }
 
 OffThreadPromiseTask::OffThreadPromiseTask(JSContext* cx, Handle<PromiseObject*> promise)
   : runtime_(cx->runtime()),
     promise_(cx, promise),
     registered_(false)
 {
-    MOZ_ASSERT(runtime_ == promise->zone()->runtimeFromActiveCooperatingThread());
+    MOZ_ASSERT(runtime_ == promise_->zone()->runtimeFromActiveCooperatingThread());
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());
 }
 
 OffThreadPromiseTask::~OffThreadPromiseTask()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
 
@@ -3447,17 +3453,17 @@ OffThreadPromiseTask::run(JSContext* cx,
         if (!resolve(cx, promise_))
             cx->clearPendingException();
     }
 
     js_delete(this);
 }
 
 void
-OffThreadPromiseTask::dispatchResolve()
+OffThreadPromiseTask::dispatchResolveAndDestroy()
 {
     MOZ_ASSERT(registered_);
 
     OffThreadPromiseRuntimeState& state = runtime_->offThreadPromiseState.ref();
     MOZ_ASSERT(state.initialized());
     MOZ_ASSERT((LockGuard<Mutex>(state.mutex_), state.live_.has(this)));
 
     // If the dispatch succeeds, then we are guaranteed that run() will be
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -182,24 +182,28 @@ class OffThreadPromiseTask : public JS::
     // To be called by OffThreadPromiseTask and implemented by the derived class.
     virtual bool resolve(JSContext* cx, Handle<PromiseObject*> promise) = 0;
 
     // JS::Dispatchable implementation. Ends with 'delete this'.
     void run(JSContext* cx, MaybeShuttingDown maybeShuttingDown) override final;
 
   public:
     ~OffThreadPromiseTask() override;
+
+    // Initializing an OffThreadPromiseTask informs the runtime that it must
+    // wait on shutdown for this task to rejoin the active JSContext by calling
+    // dispatchResolveAndDestroy().
     bool init(JSContext* cx);
 
     // An initialized OffThreadPromiseTask can be dispatched to an active
     // JSContext of its Promise's JSRuntime from any thread. Normally, this will
     // lead to resolve() being called on JSContext thread, given the Promise.
     // However, if shutdown interrupts, resolve() may not be called, though the
     // OffThreadPromiseTask will be destroyed on a JSContext thread.
-    void dispatchResolve();
+    void dispatchResolveAndDestroy();
 };
 
 using OffThreadPromiseTaskSet = HashSet<OffThreadPromiseTask*,
                                         DefaultHasher<OffThreadPromiseTask*>,
                                         SystemAllocPolicy>;
 
 using DispatchableVector = Vector<JS::Dispatchable*, 0, SystemAllocPolicy>;
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -36,16 +36,17 @@
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
+#include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/ProxyObject.h"
 #include "vm/SavedStacks.h"
 #include "vm/Stack.h"
 #include "vm/StringBuffer.h"
 #include "vm/TraceLogging.h"
 #include "wasm/AsmJS.h"
@@ -54,16 +55,17 @@
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTextToBinary.h"
 #include "wasm/WasmTypes.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
+#include "vm/Debugger-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::Move;
 
@@ -1938,23 +1940,23 @@ SettlePromiseNow(JSContext* cx, unsigned
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "settlePromiseNow", 1))
         return false;
     if (!args[0].isObject() || !args[0].toObject().is<PromiseObject>()) {
         JS_ReportErrorASCII(cx, "first argument must be a Promise object");
         return false;
     }
 
-    RootedNativeObject promise(cx, &args[0].toObject().as<NativeObject>());
+    Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
     int32_t flags = promise->getFixedSlot(PromiseSlot_Flags).toInt32();
     promise->setFixedSlot(PromiseSlot_Flags,
                           Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED));
     promise->setFixedSlot(PromiseSlot_ReactionsOrResult, UndefinedValue());
 
-    JS::dbg::onPromiseSettled(cx, promise);
+    Debugger::onPromiseSettled(cx, promise);
     return true;
 }
 
 static bool
 GetWaitForAllPromise(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "getWaitForAllPromise", 1))
--- a/js/src/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -11,161 +11,149 @@
 using namespace js;
 
 using mozilla::RoundUpPow2;
 using mozilla::tl::BitSize;
 
 namespace js {
 namespace detail {
 
-BumpChunk*
-BumpChunk::new_(size_t chunkSize)
+/* static */
+UniquePtr<BumpChunk>
+BumpChunk::newWithCapacity(size_t size)
 {
-    MOZ_ASSERT(RoundUpPow2(chunkSize) == chunkSize);
-    void* mem = js_malloc(chunkSize);
+    MOZ_ASSERT(RoundUpPow2(size) == size);
+    MOZ_ASSERT(size >= sizeof(BumpChunk));
+    void* mem = js_malloc(size);
     if (!mem)
         return nullptr;
-    BumpChunk* result = new (mem) BumpChunk(chunkSize - sizeof(BumpChunk));
 
-    // We assume that the alignment of sAlign is less than that of
-    // the underlying memory allocator -- creating a new BumpChunk should
-    // always satisfy the sAlign alignment constraint.
-    MOZ_ASSERT(AlignPtr(result->bump) == result->bump);
-    return result;
-}
+    UniquePtr<BumpChunk> result(new (mem) BumpChunk(size));
 
-void
-BumpChunk::delete_(BumpChunk* chunk)
-{
-#ifdef DEBUG
-    // Part of the chunk may have been marked as poisoned/noaccess.  Undo that
-    // before writing the 0xcd bytes.
-    size_t size = sizeof(*chunk) + chunk->bumpSpaceSize;
-    MOZ_MAKE_MEM_UNDEFINED(chunk, size);
-    memset(chunk, 0xcd, size);
-#endif
-    js_free(chunk);
+    // We assume that the alignment of LIFO_ALLOC_ALIGN is less than that of the
+    // underlying memory allocator -- creating a new BumpChunk should always
+    // satisfy the LIFO_ALLOC_ALIGN alignment constraint.
+    MOZ_ASSERT(AlignPtr(result->begin()) == result->begin());
+    return result;
 }
 
 bool
 BumpChunk::canAlloc(size_t n)
 {
-    char* aligned = AlignPtr(bump);
-    char* bumped = aligned + n;
-    return bumped <= limit && bumped > headerBase();
+    uint8_t* aligned = AlignPtr(bump_);
+    uint8_t* newBump = aligned + n;
+    // bump_ <= newBump, is necessary to catch overflow.
+    return bump_ <= newBump && newBump <= capacity_;
 }
 
 } // namespace detail
 } // namespace js
 
 void
 LifoAlloc::freeAll()
 {
-    while (first) {
-        BumpChunk* victim = first;
-        first = first->next();
-        decrementCurSize(victim->computedSizeOfIncludingThis());
-        BumpChunk::delete_(victim);
+    while (!chunks_.empty()) {
+        BumpChunk bc = mozilla::Move(chunks_.popFirst());
+        decrementCurSize(bc->computedSizeOfIncludingThis());
     }
-    first = latest = last = nullptr;
+    while (!unused_.empty()) {
+        BumpChunk bc = mozilla::Move(unused_.popFirst());
+        decrementCurSize(bc->computedSizeOfIncludingThis());
+    }
 
     // Nb: maintaining curSize_ correctly isn't easy.  Fortunately, this is an
     // excellent sanity check.
     MOZ_ASSERT(curSize_ == 0);
 }
 
-LifoAlloc::BumpChunk*
-LifoAlloc::getOrCreateChunk(size_t n)
+LifoAlloc::BumpChunk
+LifoAlloc::newChunkWithCapacity(size_t n)
 {
-    if (first) {
-        // Look for existing, unused BumpChunks to satisfy the request.
-        while (latest->next()) {
-            latest = latest->next();
-            latest->resetBump();    // This was an unused BumpChunk on the chain.
-            if (latest->canAlloc(n))
-                return latest;
-        }
-    }
+    MOZ_ASSERT(fallibleScope_, "[OOM] Cannot allocate a new chunk in an infallible scope.");
 
-    size_t defaultChunkFreeSpace = defaultChunkSize_ - sizeof(BumpChunk);
+    // Compute the size which should be requested in order to be able to fit |n|
+    // bytes in the newly allocated chunk, or default the |defaultChunkSize_|.
+    size_t defaultChunkFreeSpace = defaultChunkSize_ - detail::BumpChunk::reservedSpace;
     size_t chunkSize;
     if (n > defaultChunkFreeSpace) {
-        size_t allocSizeWithHeader = n + sizeof(BumpChunk);
+        MOZ_ASSERT(defaultChunkFreeSpace < defaultChunkSize_);
+        size_t allocSizeWithCanaries = n + (defaultChunkSize_ - defaultChunkFreeSpace);
 
         // Guard for overflow.
-        if (allocSizeWithHeader < n ||
-            (allocSizeWithHeader & (size_t(1) << (BitSize<size_t>::value - 1)))) {
+        if (allocSizeWithCanaries < n ||
+            (allocSizeWithCanaries & (size_t(1) << (BitSize<size_t>::value - 1))))
+        {
             return nullptr;
         }
 
-        chunkSize = RoundUpPow2(allocSizeWithHeader);
+        chunkSize = RoundUpPow2(allocSizeWithCanaries);
     } else {
         chunkSize = defaultChunkSize_;
     }
 
-    // If we get here, we couldn't find an existing BumpChunk to fill the request.
-    MOZ_ASSERT(fallibleScope_, "[OOM] Cannot allocate a new chunk in an infallible scope.");
-    BumpChunk* newChunk = BumpChunk::new_(chunkSize);
-    if (!newChunk)
+    // Create a new BumpChunk, and allocate space for it.
+    BumpChunk result = detail::BumpChunk::newWithCapacity(chunkSize);
+    if (!result)
         return nullptr;
-    if (!first) {
-        latest = first = last = newChunk;
-    } else {
-        MOZ_ASSERT(latest && !latest->next());
-        latest->setNext(newChunk);
-        latest = last = newChunk;
+    MOZ_ASSERT(result->computedSizeOfIncludingThis() == chunkSize);
+    return result;
+}
+
+bool
+LifoAlloc::getOrCreateChunk(size_t n)
+{
+    // Look for existing unused BumpChunks to satisfy the request, and pick the
+    // first one which is large enough, and move it into the list of used
+    // chunks.
+    if (!unused_.empty()) {
+        if (unused_.begin()->canAlloc(n)) {
+            chunks_.append(mozilla::Move(unused_.popFirst()));
+            return true;
+        }
+
+        BumpChunkList::Iterator e(unused_.end());
+        for (BumpChunkList::Iterator i(unused_.begin()); i->next() != e.get(); ++i) {
+            detail::BumpChunk* elem = i->next();
+            MOZ_ASSERT(elem->empty());
+            if (elem->canAlloc(n)) {
+                BumpChunkList temp = mozilla::Move(unused_.splitAfter(i.get()));
+                chunks_.append(mozilla::Move(temp.popFirst()));
+                unused_.appendAll(mozilla::Move(temp));
+                return true;
+            }
+        }
     }
 
-    size_t computedChunkSize = newChunk->computedSizeOfIncludingThis();
-    MOZ_ASSERT(computedChunkSize == chunkSize);
-    incrementCurSize(computedChunkSize);
-
-    return newChunk;
+    // Allocate a new BumpChunk with enough space for the next allocation.
+    BumpChunk newChunk = newChunkWithCapacity(n);
+    if (!newChunk)
+        return false;
+    size_t size = newChunk->computedSizeOfIncludingThis();
+    chunks_.append(mozilla::Move(newChunk));
+    incrementCurSize(size);
+    return true;
 }
 
 void
 LifoAlloc::transferFrom(LifoAlloc* other)
 {
     MOZ_ASSERT(!markCount);
     MOZ_ASSERT(!other->markCount);
 
-    if (!other->first)
-        return;
-
     incrementCurSize(other->curSize_);
-    if (other->isEmpty())
-        appendUnused(other->first, other->last);
-    else
-        appendUsed(other->first, other->latest, other->last);
-    other->first = other->last = other->latest = nullptr;
+    appendUnused(mozilla::Move(other->unused_));
+    appendUsed(mozilla::Move(other->chunks_));
     other->curSize_ = 0;
 }
 
 void
 LifoAlloc::transferUnusedFrom(LifoAlloc* other)
 {
     MOZ_ASSERT(!markCount);
-    MOZ_ASSERT(latest == first);
 
-    if (other->markCount || !other->first)
-        return;
-
-    // Transfer all chunks *after* |latest|.
+    size_t size = 0;
+    for (detail::BumpChunk& bc : other->unused_)
+        size += bc.computedSizeOfIncludingThis();
 
-    if (other->latest->next()) {
-        if (other->latest == other->first) {
-            // We're transferring everything except the first chunk.
-            size_t delta = other->curSize_ - other->first->computedSizeOfIncludingThis();
-            other->decrementCurSize(delta);
-            incrementCurSize(delta);
-        } else {
-            for (BumpChunk* chunk = other->latest->next(); chunk; chunk = chunk->next()) {
-                size_t size = chunk->computedSizeOfIncludingThis();
-                incrementCurSize(size);
-                other->decrementCurSize(size);
-            }
-        }
-
-        appendUnused(other->latest->next(), other->last);
-        other->latest->setNext(nullptr);
-        other->last = other->latest;
-    }
+    appendUnused(mozilla::Move(other->unused_));
+    incrementCurSize(size);
+    other->decrementCurSize(size);
 }
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -6,232 +6,500 @@
 
 #ifndef ds_LifoAlloc_h
 #define ds_LifoAlloc_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryChecking.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TemplateLib.h"
 #include "mozilla/TypeTraits.h"
 
 // This data structure supports stacky LIFO allocation (mark/release and
 // LifoAllocScope). It does not maintain one contiguous segment; instead, it
 // maintains a bunch of linked memory segments. In order to prevent malloc/free
 // thrashing, unused segments are deallocated when garbage collection occurs.
 
 #include "jsutil.h"
 
+#include "js/UniquePtr.h"
+
 namespace js {
 
 namespace detail {
 
+template <typename T>
+class SingleLinkedList;
+
+template <typename T>
+class SingleLinkedListElement
+{
+    friend class SingleLinkedList<T>;
+    js::UniquePtr<T> next_;
+
+  public:
+    SingleLinkedListElement()
+      : next_(nullptr)
+    {}
+    ~SingleLinkedListElement() {
+        MOZ_ASSERT(!next_);
+    }
+
+    T* next() const { return next_.get(); }
+};
+
+// Single linked list which is using UniquePtr to hold the next pointers.
+// UniquePtr are used to ensure that none of the elements are used
+// silmutaneously in 2 different list.
+template <typename T>
+class SingleLinkedList
+{
+  private:
+    // First element of the list which owns the next element, and ensure that
+    // that this list is the only owner of the element.
+    UniquePtr<T> head_;
+
+    // Weak pointer to the last element of the list.
+    T* last_;
+
+    void assertInvariants() {
+        MOZ_ASSERT(bool(head_) == bool(last_));
+        MOZ_ASSERT_IF(last_, !last_->next_);
+    }
+
+  public:
+    SingleLinkedList()
+      : head_(nullptr), last_(nullptr)
+    {
+        assertInvariants();
+    }
+
+    SingleLinkedList(SingleLinkedList&& other)
+      : head_(mozilla::Move(other.head_)), last_(other.last_)
+    {
+        other.last_ = nullptr;
+        assertInvariants();
+        other.assertInvariants();
+    }
+
+    ~SingleLinkedList() {
+        MOZ_ASSERT(!head_);
+        MOZ_ASSERT(!last_);
+    }
+
+    // Move the elements of the |other| list in the current one, and implicitly
+    // remove all the elements of the current list.
+    SingleLinkedList& operator=(SingleLinkedList&& other) {
+        head_ = mozilla::Move(other.head_);
+        last_ = other.last_;
+        other.last_ = nullptr;
+        assertInvariants();
+        other.assertInvariants();
+        return *this;
+    }
+
+    bool empty() const { return !last_; }
+
+    // Iterates over the elements of the list, this is used to avoid raw
+    // manipulation of pointers as much as possible.
+    class Iterator {
+        T* current_;
+
+      public:
+        explicit Iterator(T* current) : current_(current) {}
+
+        T& operator *() const { return *current_; }
+        T* operator ->() const { return current_; }
+        T* get() const { return current_; }
+
+        const Iterator& operator++() {
+            current_ = current_->next();
+            return *this;
+        }
+
+        bool operator!=(Iterator& other) const {
+            return current_ != other.current_;
+        }
+        bool operator==(Iterator& other) const {
+            return current_ == other.current_;
+        }
+    };
+
+    Iterator begin() const { return Iterator(head_.get()); }
+    Iterator end() const { return Iterator(nullptr); }
+    Iterator last() const { return Iterator(last_); }
+
+    // Split the list in 2 single linked lists after the element given as
+    // argument.  The front of the list remains in the current list, while the
+    // back goes in the newly create linked list.
+    //
+    // This is used for example to extract one element from a list. (see
+    // LifoAlloc::getOrCreateChunk)
+    SingleLinkedList splitAfter(T* newLast) {
+        MOZ_ASSERT(newLast);
+        SingleLinkedList result;
+        if (newLast->next_) {
+            result.head_ = mozilla::Move(newLast->next_);
+            result.last_ = last_;
+            last_ = newLast;
+        }
+        assertInvariants();
+        result.assertInvariants();
+        return result;
+    }
+
+    void pushFront(UniquePtr<T>&& elem) {
+        if (!last_)
+            last_ = elem.get();
+        elem->next_ = mozilla::Move(head_);
+        head_ = mozilla::Move(elem);
+        assertInvariants();
+    }
+
+    void append(UniquePtr<T>&& elem) {
+        if (last_) {
+            last_->next_ = mozilla::Move(elem);
+            last_ = last_->next_.get();
+        } else {
+            head_ = mozilla::Move(elem);
+            last_ = head_.get();
+        }
+        assertInvariants();
+    }
+    void appendAll(SingleLinkedList&& list) {
+        if (list.empty())
+            return;
+        if (last_)
+            last_->next_ = mozilla::Move(list.head_);
+        else
+            head_ = mozilla::Move(list.head_);
+        last_ = list.last_;
+        list.last_ = nullptr;
+        assertInvariants();
+        list.assertInvariants();
+    }
+    UniquePtr<T> popFirst() {
+        MOZ_ASSERT(head_);
+        UniquePtr<T> result = mozilla::Move(head_);
+        head_ = mozilla::Move(result->next_);
+        if (!head_)
+            last_ = nullptr;
+        assertInvariants();
+        return result;
+    }
+};
+
 static const size_t LIFO_ALLOC_ALIGN = 8;
 
 MOZ_ALWAYS_INLINE
-char*
-AlignPtr(void* orig)
-{
+uint8_t*
+AlignPtr(uint8_t* orig) {
     static_assert(mozilla::tl::FloorLog2<LIFO_ALLOC_ALIGN>::value ==
                   mozilla::tl::CeilingLog2<LIFO_ALLOC_ALIGN>::value,
                   "LIFO_ALLOC_ALIGN must be a power of two");
 
-    char* result = (char*) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & (~LIFO_ALLOC_ALIGN + 1));
+    uint8_t* result = (uint8_t*) AlignBytes(uintptr_t(orig), LIFO_ALLOC_ALIGN);
     MOZ_ASSERT(uintptr_t(result) % LIFO_ALLOC_ALIGN == 0);
     return result;
 }
 
-// Header for a chunk of memory wrangled by the LifoAlloc.
-class BumpChunk
+// A Chunk represent a single memory allocation made with the system
+// allocator. As the owner of the memory, it is responsible for the allocation
+// and the deallocation.
+//
+// This structure is only move-able, but not copyable.
+class BumpChunk : public SingleLinkedListElement<BumpChunk>
 {
-    char*       bump;          // start of the available data
-    char*       limit;         // end of the data
-    BumpChunk*  next_;         // the next BumpChunk
-    size_t      bumpSpaceSize;  // size of the data area
+  private:
+    // Pointer to the last byte allocated in this chunk.
+    uint8_t* bump_;
+    // Pointer to the last byte available in this chunk.
+    const uint8_t* capacity_;
+    // Magic number used to check against poisoned values.
+    const uintptr_t magic_;
 
-    char* headerBase() { return reinterpret_cast<char*>(this); }
-    char* bumpBase() const { return limit - bumpSpaceSize; }
+    // Byte used for poisoning unused memory after releasing memory.
+    static constexpr int undefinedChunkMemory = 0xcd;
+    static constexpr uintptr_t magicNumber =
+        sizeof(uintptr_t) == 4 ? uintptr_t(0x4c69666f) : uintptr_t(0x4c69666f42756d70);
 
-    explicit BumpChunk(size_t bumpSpaceSize)
-      : bump(reinterpret_cast<char*>(this) + sizeof(BumpChunk)),
-        limit(bump + bumpSpaceSize),
-        next_(nullptr), bumpSpaceSize(bumpSpaceSize)
-    {
-        MOZ_ASSERT(bump == AlignPtr(bump));
+    void assertInvariants() {
+        MOZ_DIAGNOSTIC_ASSERT(magic_ == magicNumber);
+        MOZ_ASSERT(begin() <= end());
+        MOZ_ASSERT(end() <= capacity_);
     }
 
-    void setBump(void* ptr) {
-        MOZ_ASSERT(bumpBase() <= ptr);
-        MOZ_ASSERT(ptr <= limit);
+    BumpChunk& operator=(const BumpChunk&) = delete;
+    BumpChunk(const BumpChunk&) = delete;
+
+    explicit BumpChunk(uintptr_t capacity)
+      : bump_(begin()),
+        capacity_(base() + capacity),
+        magic_(magicNumber)
+    {
+        // We cannot bake this value inside the BumpChunk class, because
+        // sizeof(BumpChunk) can only be computed after the closing brace of the
+        // BumpChunk class, or within one of its methods. As a work-around, the
+        // reservedSpace value is baked in, and we check that it indeed matches
+        // with the space taken by the data of the BumpChunk class, and the
+        // alignment of a pointer.
+        MOZ_ASSERT(BumpChunk::reservedSpace == AlignBytes(sizeof(BumpChunk), LIFO_ALLOC_ALIGN),
+                   "Checked that the baked-in value correspond to computed value");
+
+        assertInvariants();
+    }
+
+    // Cast |this| into a uint8_t* pointer.
+    //
+    // Warning: Are you sure you do not want to use begin() instead?
+    const uint8_t* base() const { return reinterpret_cast<const uint8_t*>(this); }
+    uint8_t* base() { return reinterpret_cast<uint8_t*>(this); }
+
+    // Update the bump pointer to any value contained in this chunk, which is
+    // above the private fields of this chunk.
+    //
+    // The memory is poisoned and flagged as no-access when the bump pointer is
+    // moving backward, such as when memory is released, or when a Mark is used
+    // to unwind previous allocations.
+    //
+    // The memory is flagged as undefined when the bump pointer is moving
+    // forward.
+    void setBump(uint8_t* newBump) {
+        assertInvariants();
+        MOZ_ASSERT(begin() <= newBump);
+        MOZ_ASSERT(newBump <= capacity_);
 #if defined(DEBUG) || defined(MOZ_HAVE_MEM_CHECKS)
-        char* prevBump = bump;
+        uint8_t* prev = bump_;
 #endif
-        bump = static_cast<char*>(ptr);
+        bump_ = newBump;
 #ifdef DEBUG
-        MOZ_ASSERT(contains(prevBump));
-
         // Clobber the now-free space.
-        if (prevBump > bump)
-            memset(bump, 0xcd, prevBump - bump);
+        if (prev > bump_)
+            memset(bump_, undefinedChunkMemory, prev - bump_);
 #endif
-
+#if defined(MOZ_HAVE_MEM_CHECKS)
         // Poison/Unpoison memory that we just free'd/allocated.
-#if defined(MOZ_HAVE_MEM_CHECKS)
-        if (prevBump > bump)
-            MOZ_MAKE_MEM_NOACCESS(bump, prevBump - bump);
-        else if (bump > prevBump)
-            MOZ_MAKE_MEM_UNDEFINED(prevBump, bump - prevBump);
+        if (prev > bump_)
+            MOZ_MAKE_MEM_NOACCESS(bump_, prev - bump_);
+        else if (bump_ > prev)
+            MOZ_MAKE_MEM_UNDEFINED(prev, bump_ - prev);
 #endif
     }
 
   public:
-    BumpChunk* next() const { return next_; }
-    void setNext(BumpChunk* succ) { next_ = succ; }
+    ~BumpChunk() {
+        release();
+    }
 
-    size_t used() const { return bump - bumpBase(); }
+    // Space reserved for the BumpChunk internal data, and the alignment of the
+    // first allocation content.  This can be used to ensure there is enough
+    // space for the next allocation. (see LifoAlloc::newChunkWithCapacity)
+    static constexpr size_t reservedSpace = 4 * sizeof(uintptr_t);
+
+    // Returns true if this chunk contains no allocated content.
+    bool empty() const { return end() == begin(); }
 
-    void* start() const { return bumpBase(); }
-    void* end() const { return limit; }
+    // Returns the size in bytes of the number of allocated space. This includes
+    // the size consumed by the alignment of the allocations.
+    size_t used() const { return end() - begin(); }
 
-    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
+    // These are used for manipulating a chunk as if it was a vector of bytes,
+    // and used for iterating over the content of the buffer (see
+    // LifoAlloc::Enum)
+    const uint8_t* begin() const { return base() + reservedSpace; }
+    uint8_t* begin() { return base() + reservedSpace; }
+    uint8_t* end() const { return bump_; }
+
+    // This function is the only way to allocate and construct a chunk. It
+    // returns a UniquePtr to the newly allocated chunk.  The size given as
+    // argument includes the space needed for the header of the chunk.
+    static UniquePtr<BumpChunk> newWithCapacity(size_t size);
+
+    // Report allocation.
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this);
     }
 
-    size_t computedSizeOfIncludingThis() {
-        return limit - headerBase();
+    // Report allocation size.
+    size_t computedSizeOfIncludingThis() const {
+        return capacity_ - base();
     }
 
-    void resetBump() {
-        setBump(headerBase() + sizeof(BumpChunk));
+    // Opaque type used to carry a pointer to the current location of the bump_
+    // pointer, within a BumpChunk.
+    class Mark
+    {
+        // Chunk which owns the pointer.
+        BumpChunk* chunk_;
+        // Recorded of the bump_ pointer of the BumpChunk.
+        uint8_t* bump_;
+
+        friend class BumpChunk;
+        explicit Mark(BumpChunk* chunk, uint8_t* bump)
+          : chunk_(chunk), bump_(bump)
+        {}
+
+      public:
+        Mark() : chunk_(nullptr), bump_(nullptr) {}
+
+        BumpChunk* markedChunk() const { return chunk_; }
+    };
+
+    // Return a mark to be able to unwind future allocations with the release
+    // function. (see LifoAllocScope)
+    Mark mark() {
+        return Mark(this, end());
     }
 
-    void* mark() const { return bump; }
-
-    void release(void* mark) {
-        MOZ_ASSERT(contains(mark));
-        MOZ_ASSERT(mark <= bump);
-        setBump(mark);
+    // Check if a pointer is part of the allocated data of this chunk.
+    bool contains(void* ptr) const {
+        // Note: We cannot check "ptr < end()" because the mark have a 0-size
+        // length.
+        return begin() <= ptr && ptr <= end();
     }
 
-    bool contains(void* mark) const {
-        return bumpBase() <= mark && mark <= limit;
+    // Check if a mark is part of the allocated data of this chunk.
+    bool contains(Mark m) const {
+        MOZ_ASSERT(m.chunk_ == this);
+        return contains(m.bump_);
     }
 
+    // Release the memory allocated in this chunk. This function does not call
+    // any of the destructors.
+    void release() {
+        setBump(begin());
+    }
+
+    // Release the memory allocated in this chunk since the corresponding mark
+    // got created. This function does not call any of the destructors.
+    void release(Mark m) {
+        MOZ_RELEASE_ASSERT(contains(m));
+        setBump(m.bump_);
+    }
+
+    // Returns true, if the unused space is large enough for an allocation of
+    // |n| bytes.
     bool canAlloc(size_t n);
 
-    size_t unused() {
-        return limit - AlignPtr(bump);
+    // Space remaining in the current chunk.
+    size_t unused() const {
+        uint8_t* aligned = AlignPtr(end());
+        if (aligned < capacity_)
+            return capacity_ - aligned;
+        return 0;
     }
 
-    // Try to perform an allocation of size |n|, return null if not possible.
+    // Try to perform an allocation of size |n|, returns nullptr if not possible.
     MOZ_ALWAYS_INLINE
     void* tryAlloc(size_t n) {
-        char* aligned = AlignPtr(bump);
-        char* newBump = aligned + n;
+        uint8_t* aligned = AlignPtr(end());
+        uint8_t* newBump = aligned + n;
 
-        if (newBump > limit)
+        if (newBump > capacity_)
             return nullptr;
 
         // Check for overflow.
-        if (MOZ_UNLIKELY(newBump < bump))
+        if (MOZ_UNLIKELY(newBump < bump_))
             return nullptr;
 
         MOZ_ASSERT(canAlloc(n)); // Ensure consistency between "can" and "try".
         setBump(newBump);
         return aligned;
     }
-
-    static BumpChunk* new_(size_t chunkSize);
-    static void delete_(BumpChunk* chunk);
 };
 
 } // namespace detail
 
 // LIFO bump allocator: used for phase-oriented and fast LIFO allocations.
 //
-// Note: |latest| is not necessary "last". We leave BumpChunks latent in the
-// chain after they've been released to avoid thrashing before a GC.
+// Note: We leave BumpChunks latent in the set of unused chunks after they've
+// been released to avoid thrashing before a GC.
 class LifoAlloc
 {
-    typedef detail::BumpChunk BumpChunk;
+    using BumpChunk = js::UniquePtr<detail::BumpChunk>;
+    using BumpChunkList = detail::SingleLinkedList<detail::BumpChunk>;
 
-    BumpChunk*  first;
-    BumpChunk*  latest;
-    BumpChunk*  last;
+    // List of chunks containing allocated data. In the common case, the last
+    // chunk of this list is always used to perform the allocations. When the
+    // allocation cannot be performed, we move a Chunk from the unused set to
+    // the list of used chunks.
+    BumpChunkList chunks_;
+
+    // Set of unused chunks, which can be reused for future allocations.
+    BumpChunkList unused_;
+
     size_t      markCount;
     size_t      defaultChunkSize_;
     size_t      curSize_;
     size_t      peakSize_;
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     bool        fallibleScope_;
 #endif
 
     void operator=(const LifoAlloc&) = delete;
     LifoAlloc(const LifoAlloc&) = delete;
 
-    // Return a BumpChunk that can perform an allocation of at least size |n|
-    // and add it to the chain appropriately.
-    //
-    // Side effect: if retval is non-null, |first| and |latest| are initialized
-    // appropriately.
-    BumpChunk* getOrCreateChunk(size_t n);
+    // Return a BumpChunk that can perform an allocation of at least size |n|.
+    BumpChunk newChunkWithCapacity(size_t n);
+
+    // Reuse or allocate a BumpChunk that can perform an allocation of at least
+    // size |n|, if successful it is placed at the end the list of |chunks_|.
+    MOZ_MUST_USE bool getOrCreateChunk(size_t n);
 
     void reset(size_t defaultChunkSize) {
         MOZ_ASSERT(mozilla::RoundUpPow2(defaultChunkSize) == defaultChunkSize);
-        first = latest = last = nullptr;
+        while (!chunks_.empty())
+            chunks_.popFirst();
+        while (!unused_.empty())
+            unused_.popFirst();
         defaultChunkSize_ = defaultChunkSize;
         markCount = 0;
         curSize_ = 0;
     }
 
     // Append unused chunks to the end of this LifoAlloc.
-    void appendUnused(BumpChunk* start, BumpChunk* end) {
-        MOZ_ASSERT(start && end);
-        if (last)
-            last->setNext(start);
-        else
-            first = latest = start;
-        last = end;
+    void appendUnused(BumpChunkList&& otherUnused) {
+#ifdef DEBUG
+        for (detail::BumpChunk& bc: otherUnused)
+            MOZ_ASSERT(bc.empty());
+#endif
+        unused_.appendAll(mozilla::Move(otherUnused));
     }
 
     // Append used chunks to the end of this LifoAlloc. We act as if all the
     // chunks in |this| are used, even if they're not, so memory may be wasted.
-    void appendUsed(BumpChunk* otherFirst, BumpChunk* otherLatest, BumpChunk* otherLast) {
-        MOZ_ASSERT(otherFirst && otherLatest && otherLast);
-        if (last)
-            last->setNext(otherFirst);
-        else
-            first = otherFirst;
-        latest = otherLatest;
-        last = otherLast;
+    void appendUsed(BumpChunkList&& otherChunks) {
+        chunks_.appendAll(mozilla::Move(otherChunks));
     }
 
+    // Track the amount of space allocated in used and unused chunks.
     void incrementCurSize(size_t size) {
         curSize_ += size;
         if (curSize_ > peakSize_)
             peakSize_ = curSize_;
     }
     void decrementCurSize(size_t size) {
         MOZ_ASSERT(curSize_ >= size);
         curSize_ -= size;
     }
 
     MOZ_ALWAYS_INLINE
     void* allocImpl(size_t n) {
         void* result;
-        if (latest && (result = latest->tryAlloc(n)))
+        if (!chunks_.empty() && (result = chunks_.last()->tryAlloc(n)))
             return result;
 
         if (!getOrCreateChunk(n))
             return nullptr;
 
         // Since we just created a large enough chunk, this can't fail.
-        result = latest->tryAlloc(n);
+        result = chunks_.last()->tryAlloc(n);
         MOZ_ASSERT(result);
         return result;
     }
 
   public:
     explicit LifoAlloc(size_t defaultChunkSize)
       : peakSize_(0)
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
@@ -239,23 +507,29 @@ class LifoAlloc
 #endif
     {
         reset(defaultChunkSize);
     }
 
     // Steal allocated chunks from |other|.
     void steal(LifoAlloc* other) {
         MOZ_ASSERT(!other->markCount);
-        MOZ_ASSERT(!latest);
+        MOZ_ASSERT(chunks_.empty());
 
         // Copy everything from |other| to |this| except for |peakSize_|, which
         // requires some care.
-        size_t oldPeakSize = peakSize_;
-        mozilla::PodAssign(this, other);
-        peakSize_ = Max(oldPeakSize, curSize_);
+        chunks_ = mozilla::Move(other->chunks_);
+        unused_ = mozilla::Move(other->unused_);
+        markCount = other->markCount;
+        defaultChunkSize_ = other->defaultChunkSize_;
+        curSize_ = other->curSize_;
+        peakSize_ = Max(peakSize_, other->peakSize_);
+#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+        fallibleScope_ = other->fallibleScope_;
+#endif
 
         other->reset(defaultChunkSize_);
     }
 
     // Append all chunks from |other|. They are removed from |other|.
     void transferFrom(LifoAlloc* other);
 
     // Append unused chunks from |other|. They are removed from |other|.
@@ -296,26 +570,34 @@ class LifoAlloc
 
     // Ensures that enough space exists to satisfy N bytes worth of
     // allocation requests, not necessarily contiguous. Note that this does
     // not guarantee a successful single allocation of N bytes.
     MOZ_ALWAYS_INLINE
     MOZ_MUST_USE bool ensureUnusedApproximate(size_t n) {
         AutoFallibleScope fallibleAllocator(this);
         size_t total = 0;
-        for (BumpChunk* chunk = latest; chunk; chunk = chunk->next()) {
-            total += chunk->unused();
+        if (!chunks_.empty()) {
+            total += chunks_.last()->unused();
             if (total >= n)
                 return true;
         }
-        BumpChunk* latestBefore = latest;
-        if (!getOrCreateChunk(n))
+
+        for (detail::BumpChunk& bc : unused_) {
+            total += bc.unused();
+            if (total >= n)
+                return true;
+        }
+
+        BumpChunk newChunk = newChunkWithCapacity(n);
+        if (!newChunk)
             return false;
-        if (latestBefore)
-            latest = latestBefore;
+        size_t size = newChunk->computedSizeOfIncludingThis();
+        unused_.pushFront(mozilla::Move(newChunk));
+        incrementCurSize(size);
         return true;
     }
 
     MOZ_ALWAYS_INLINE
     void setAsInfallibleByDefault() {
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
         fallibleScope_ = false;
 #endif
@@ -357,86 +639,90 @@ class LifoAlloc
     template <typename T>
     T* newArrayUninitialized(size_t count) {
         size_t bytes;
         if (MOZ_UNLIKELY(!CalculateAllocSize<T>(count, &bytes)))
             return nullptr;
         return static_cast<T*>(alloc(bytes));
     }
 
-    class Mark {
-        BumpChunk* chunk;
-        void* markInChunk;
-        friend class LifoAlloc;
-        Mark(BumpChunk* chunk, void* markInChunk) : chunk(chunk), markInChunk(markInChunk) {}
-      public:
-        Mark() : chunk(nullptr), markInChunk(nullptr) {}
-    };
+    using Mark = detail::BumpChunk::Mark;
 
     Mark mark() {
         markCount++;
-        return latest ? Mark(latest, latest->mark()) : Mark();
+        if (chunks_.empty())
+            return Mark();
+        return chunks_.last()->mark();
     }
 
     void release(Mark mark) {
         markCount--;
-        if (!mark.chunk) {
-            latest = first;
-            if (latest)
-                latest->resetBump();
-        } else {
-            latest = mark.chunk;
-            latest->release(mark.markInChunk);
-        }
+
+        // Move the blocks which are after the mark to the set of unused chunks.
+        BumpChunkList released;
+        if (!mark.markedChunk())
+            released = mozilla::Move(chunks_);
+        else
+            released = mozilla::Move(chunks_.splitAfter(mark.markedChunk()));
+
+        // Release the content of all the blocks which are after the marks.
+        for (detail::BumpChunk& bc : released)
+            bc.release();
+        unused_.appendAll(mozilla::Move(released));
+
+        // Release everything which follows the mark in the last chunk.
+        if (!chunks_.empty())
+            chunks_.last()->release(mark);
     }
 
     void releaseAll() {
         MOZ_ASSERT(!markCount);
-        latest = first;
-        if (latest)
-            latest->resetBump();
+        for (detail::BumpChunk& bc : chunks_)
+            bc.release();
+        unused_.appendAll(mozilla::Move(chunks_));
     }
 
     // Get the total "used" (occupied bytes) count for the arena chunks.
     size_t used() const {
         size_t accum = 0;
-        for (BumpChunk* chunk = first; chunk; chunk = chunk->next()) {
-            accum += chunk->used();
-            if (chunk == latest)
-                break;
-        }
+        for (const detail::BumpChunk& chunk : chunks_)
+            accum += chunk.used();
         return accum;
     }
 
     // Return true if the LifoAlloc does not currently contain any allocations.
     bool isEmpty() const {
-        return !latest || !latest->used();
+        return chunks_.empty() || !chunks_.last()->empty();
     }
 
     // Return the number of bytes remaining to allocate in the current chunk.
     // e.g. How many bytes we can allocate before needing a new block.
     size_t availableInCurrentChunk() const {
-        if (!latest)