Merge inbound to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Tue, 19 Jun 2018 13:02:07 +0300
changeset 422933 257c191e7903523a1132e04460a0b2460d950809
parent 422895 4432fb67de2d1e2658a36fed800979c1968c1f94 (current diff)
parent 422932 81bb56f69788224c30d62dc42e60004ecb5fc860 (diff)
child 422941 d1d36d462a35b2023f4f9e181749fae1da2391bc
child 422966 c735e33ff04b2a6d72c0101d8676f33cc7a98ec7
push id34158
push userrgurzau@mozilla.com
push dateTue, 19 Jun 2018 10:02:37 +0000
treeherdermozilla-central@257c191e7903 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
257c191e7903 / 62.0a1 / 20180619102337 / files
nightly linux64
257c191e7903 / 62.0a1 / 20180619102337 / files
nightly mac
257c191e7903 / 62.0a1 / 20180619102337 / files
nightly win32
257c191e7903 / 62.0a1 / 20180619102337 / files
nightly win64
257c191e7903 / 62.0a1 / 20180619102337 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/accessible/base/TextUpdater.cpp
+++ b/accessible/base/TextUpdater.cpp
@@ -41,17 +41,17 @@ TextUpdater::DoUpdate(const nsAString& a
                       uint32_t aSkipStart)
 {
   Accessible* parent = mTextLeaf->Parent();
   if (!parent)
     return;
 
   mHyperText = parent->AsHyperText();
   if (!mHyperText) {
-    NS_ERROR("Text leaf parent is not hypertext!");
+    MOZ_ASSERT_UNREACHABLE("Text leaf parent is not hypertext!");
     return;
   }
 
   // Get the text leaf accessible offset and invalidate cached offsets after it.
   mTextOffset = mHyperText->GetChildOffset(mTextLeaf, true);
   NS_ASSERTION(mTextOffset != -1,
                "Text leaf hasn't offset within hyper text!");
 
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -352,17 +352,17 @@ Accessible::VisibilityState() const
     // Offscreen state for background tab content and invisible for not selected
     // deck panel.
     nsIFrame* parentFrame = curFrame->GetParent();
     nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
     if (deckFrame && deckFrame->GetSelectedBox() != curFrame) {
       if (deckFrame->GetContent()->IsXULElement(nsGkAtoms::tabpanels))
         return states::OFFSCREEN;
 
-      NS_NOTREACHED("Children of not selected deck panel are not accessible.");
+      MOZ_ASSERT_UNREACHABLE("Children of not selected deck panel are not accessible.");
       return states::INVISIBLE;
     }
 
     // If contained by scrollable frame then check that at least 12 pixels
     // around the object is visible, otherwise the object is offscreen.
     nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
     if (scrollableFrame) {
       nsRect scrollPortRect = scrollableFrame->GetScrollPortRect();
@@ -1952,18 +1952,18 @@ Accessible::AppendTextTo(nsAString& aTex
   nsIFrame *frame = GetFrame();
   if (!frame) {
     if (mContent->IsElement() && mContent->AsElement()->IsDisplayContents()) {
       aText += kEmbeddedObjectChar;
     }
     return;
   }
 
-  NS_ASSERTION(mParent,
-               "Called on accessible unbound from tree. Result can be wrong.");
+  MOZ_ASSERT(mParent,
+             "Called on accessible unbound from tree. Result can be wrong.");
 
   if (frame->IsBrFrame()) {
     aText += kForcedNewLineChar;
   } else if (mParent && nsAccUtils::MustPrune(mParent)) {
     // Expose the embedded object accessible as imaginary embedded object
     // character if its parent hypertext accessible doesn't expose children to
     // AT.
     aText += kImaginaryEmbeddedObjectChar;
--- 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: 2.0.581
+Current extension version is: 2.0.602
 
-Taken from upstream commit: 790e2124
+Taken from upstream commit: 3b07147d
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.581';
-var pdfjsBuild = '790e2124';
+var pdfjsVersion = '2.0.602';
+var pdfjsBuild = '3b07147d';
 var pdfjsSharedUtil = __w_pdfjs_require__(1);
 var pdfjsDisplayAPI = __w_pdfjs_require__(6);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(18);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(19);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(7);
 var pdfjsDisplaySVG = __w_pdfjs_require__(20);
 let pdfjsDisplayWorkerOptions = __w_pdfjs_require__(12);
 let pdfjsDisplayAPICompatibility = __w_pdfjs_require__(9);
@@ -4223,17 +4223,17 @@ function _fetchDocument(worker, source, 
     return Promise.reject(new Error('Worker was destroyed'));
   }
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.0.581',
+    apiVersion: '2.0.602',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -5124,16 +5124,18 @@ var WorkerTransport = function WorkerTra
                 registerFont(font, url) {
                   _global_scope2.default['FontInspector'].fontAdded(font, url);
                 }
               };
             }
             var font = new _font_loader.FontFaceObject(exportedData, {
               isEvalSupported: params.isEvalSupported,
               disableFontFace: params.disableFontFace,
+              ignoreErrors: params.ignoreErrors,
+              onUnsupportedFeature: this._onUnsupportedFeature.bind(this),
               fontRegistry
             });
             var fontReady = fontObjs => {
               this.commonObjs.resolve(id, font);
             };
             this.fontLoader.bind([font], fontReady);
             break;
           case 'FontPath':
@@ -5207,25 +5209,17 @@ var WorkerTransport = function WorkerTra
         }
         if (intentState.operatorList) {
           intentState.operatorList.lastChunk = true;
           for (var i = 0; i < intentState.renderTasks.length; i++) {
             intentState.renderTasks[i].operatorListChanged();
           }
         }
       }, this);
-      messageHandler.on('UnsupportedFeature', function (data) {
-        if (this.destroyed) {
-          return;
-        }
-        let loadingTask = this.loadingTask;
-        if (loadingTask.onUnsupportedFeature) {
-          loadingTask.onUnsupportedFeature(data.featureId);
-        }
-      }, this);
+      messageHandler.on('UnsupportedFeature', this._onUnsupportedFeature, this);
       messageHandler.on('JpegDecode', function (data) {
         if (this.destroyed) {
           return Promise.reject(new Error('Worker was destroyed'));
         }
         if (typeof document === 'undefined') {
           return Promise.reject(new Error('"document" is not defined.'));
         }
         var imageUrl = data[0];
@@ -5273,16 +5267,25 @@ var WorkerTransport = function WorkerTra
       }, this);
       messageHandler.on('FetchBuiltInCMap', function (data) {
         if (this.destroyed) {
           return Promise.reject(new Error('Worker was destroyed'));
         }
         return this.CMapReaderFactory.fetch({ name: data.name });
       }, this);
     },
+    _onUnsupportedFeature({ featureId }) {
+      if (this.destroyed) {
+        return;
+      }
+      let loadingTask = this.loadingTask;
+      if (loadingTask.onUnsupportedFeature) {
+        loadingTask.onUnsupportedFeature(featureId);
+      }
+    },
     getData: function WorkerTransport_getData() {
       return this.messageHandler.sendWithPromise('GetData', null);
     },
     getPage(pageNumber) {
       if (!Number.isInteger(pageNumber) || pageNumber <= 0 || pageNumber > this.numPages) {
         return Promise.reject(new Error('Invalid page request'));
       }
       var pageIndex = pageNumber - 1;
@@ -5531,44 +5534,48 @@ var InternalRenderTask = function Intern
       if (this.task.onContinue) {
         this.task.onContinue(this._scheduleNextBound);
       } else {
         this._scheduleNext();
       }
     },
     _scheduleNext: function InternalRenderTask__scheduleNext() {
       if (this.useRequestAnimationFrame && typeof window !== 'undefined') {
-        window.requestAnimationFrame(this._nextBound);
+        window.requestAnimationFrame(() => {
+          this._nextBound().catch(this.callback);
+        });
       } else {
         Promise.resolve().then(this._nextBound).catch(this.callback);
       }
     },
     _next: function InternalRenderTask__next() {
-      if (this.cancelled) {
-        return;
-      }
-      this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper);
-      if (this.operatorListIdx === this.operatorList.argsArray.length) {
-        this.running = false;
-        if (this.operatorList.lastChunk) {
-          this.gfx.endDrawing();
-          if (this._canvas) {
-            canvasInRendering.delete(this._canvas);
+      return new Promise(() => {
+        if (this.cancelled) {
+          return;
+        }
+        this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper);
+        if (this.operatorListIdx === this.operatorList.argsArray.length) {
+          this.running = false;
+          if (this.operatorList.lastChunk) {
+            this.gfx.endDrawing();
+            if (this._canvas) {
+              canvasInRendering.delete(this._canvas);
+            }
+            this.callback();
           }
-          this.callback();
-        }
-      }
+        }
+      });
     }
   };
   return InternalRenderTask;
 }();
 var version, build;
 {
-  exports.version = version = '2.0.581';
-  exports.build = build = '790e2124';
+  exports.version = version = '2.0.602';
+  exports.build = build = '3b07147d';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
@@ -5935,23 +5942,25 @@ FontLoader.prototype = {
 ;
 ;
 var IsEvalSupportedCached = {
   get value() {
     return (0, _util.shadow)(this, 'value', (0, _util.isEvalSupported)());
   }
 };
 var FontFaceObject = function FontFaceObjectClosure() {
-  function FontFaceObject(translatedData, { isEvalSupported = true, disableFontFace = false, fontRegistry = null }) {
+  function FontFaceObject(translatedData, { isEvalSupported = true, disableFontFace = false, ignoreErrors = false, onUnsupportedFeature = null, fontRegistry = null }) {
     this.compiledGlyphs = Object.create(null);
     for (var i in translatedData) {
       this[i] = translatedData[i];
     }
     this.isEvalSupported = isEvalSupported !== false;
     this.disableFontFace = disableFontFace === true;
+    this.ignoreErrors = ignoreErrors === true;
+    this._onUnsupportedFeature = onUnsupportedFeature;
     this.fontRegistry = fontRegistry;
   }
   FontFaceObject.prototype = {
     createNativeFontFace: function FontFaceObject_createNativeFontFace() {
       throw new Error('Not implemented: createNativeFontFace');
     },
     createFontFaceRule: function FontFaceObject_createFontFaceRule() {
       if (!this.data || this.disableFontFace) {
@@ -5961,46 +5970,56 @@ var FontFaceObject = function FontFaceOb
       var fontName = this.loadedName;
       var url = 'url(data:' + this.mimetype + ';base64,' + btoa(data) + ');';
       var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}';
       if (this.fontRegistry) {
         this.fontRegistry.registerFont(this, url);
       }
       return rule;
     },
-    getPathGenerator: function FontFaceObject_getPathGenerator(objs, character) {
-      if (!(character in this.compiledGlyphs)) {
-        var cmds = objs.get(this.loadedName + '_path_' + character);
-        var current, i, len;
-        if (this.isEvalSupported && IsEvalSupportedCached.value) {
-          var args,
-              js = '';
-          for (i = 0, len = cmds.length; i < len; i++) {
-            current = cmds[i];
-            if (current.args !== undefined) {
-              args = current.args.join(',');
-            } else {
-              args = '';
-            }
-            js += 'c.' + current.cmd + '(' + args + ');\n';
+    getPathGenerator(objs, character) {
+      if (this.compiledGlyphs[character] !== undefined) {
+        return this.compiledGlyphs[character];
+      }
+      let cmds, current;
+      try {
+        cmds = objs.get(this.loadedName + '_path_' + character);
+      } catch (ex) {
+        if (!this.ignoreErrors) {
+          throw ex;
+        }
+        if (this._onUnsupportedFeature) {
+          this._onUnsupportedFeature({ featureId: _util.UNSUPPORTED_FEATURES.font });
+        }
+        (0, _util.warn)(`getPathGenerator - ignoring character: "${ex}".`);
+        return this.compiledGlyphs[character] = function (c, size) {};
+      }
+      if (this.isEvalSupported && IsEvalSupportedCached.value) {
+        let args,
+            js = '';
+        for (let i = 0, ii = cmds.length; i < ii; i++) {
+          current = cmds[i];
+          if (current.args !== undefined) {
+            args = current.args.join(',');
+          } else {
+            args = '';
           }
-          this.compiledGlyphs[character] = new Function('c', 'size', js);
-        } else {
-          this.compiledGlyphs[character] = function (c, size) {
-            for (i = 0, len = cmds.length; i < len; i++) {
-              current = cmds[i];
-              if (current.cmd === 'scale') {
-                current.args = [size, -size];
-              }
-              c[current.cmd].apply(c, current.args);
-            }
-          };
-        }
-      }
-      return this.compiledGlyphs[character];
+          js += 'c.' + current.cmd + '(' + args + ');\n';
+        }
+        return this.compiledGlyphs[character] = new Function('c', 'size', js);
+      }
+      return this.compiledGlyphs[character] = function (c, size) {
+        for (let i = 0, ii = cmds.length; i < ii; i++) {
+          current = cmds[i];
+          if (current.cmd === 'scale') {
+            current.args = [size, -size];
+          }
+          c[current.cmd].apply(c, current.args);
+        }
+      };
     }
   };
   return FontFaceObject;
 }();
 exports.FontFaceObject = FontFaceObject;
 exports.FontLoader = FontLoader;
 
 /***/ }),
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.581';
-var pdfjsBuild = '790e2124';
+var pdfjsVersion = '2.0.602';
+var pdfjsBuild = '3b07147d';
 var pdfjsCoreWorker = __w_pdfjs_require__(1);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -322,18 +322,18 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.0.581';
-    if (apiVersion !== null && apiVersion !== workerVersion) {
+    let workerVersion = '2.0.602';
+    if (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 _message_handler.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
     function ensureNotTerminated() {
@@ -4909,39 +4909,41 @@ var ChunkedStream = function ChunkedStre
     },
     getInt32: function ChunkedStream_getInt32() {
       var b0 = this.getByte();
       var b1 = this.getByte();
       var b2 = this.getByte();
       var b3 = this.getByte();
       return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
     },
-    getBytes: function ChunkedStream_getBytes(length) {
+    getBytes(length, forceClamped = false) {
       var bytes = this.bytes;
       var pos = this.pos;
       var strEnd = this.end;
       if (!length) {
         this.ensureRange(pos, strEnd);
-        return bytes.subarray(pos, strEnd);
+        let subarray = bytes.subarray(pos, strEnd);
+        return forceClamped ? new Uint8ClampedArray(subarray) : subarray;
       }
       var end = pos + length;
       if (end > strEnd) {
         end = strEnd;
       }
       this.ensureRange(pos, end);
       this.pos = end;
-      return bytes.subarray(pos, end);
+      let subarray = bytes.subarray(pos, end);
+      return forceClamped ? new Uint8ClampedArray(subarray) : subarray;
     },
     peekByte: function ChunkedStream_peekByte() {
       var peekedByte = this.getByte();
       this.pos--;
       return peekedByte;
     },
-    peekBytes: function ChunkedStream_peekBytes(length) {
-      var bytes = this.getBytes(length);
+    peekBytes(length, forceClamped = false) {
+      var bytes = this.getBytes(length, forceClamped);
       this.pos -= bytes.length;
       return bytes;
     },
     getByteRange: function ChunkedStream_getBytes(begin, end) {
       this.ensureRange(begin, end);
       return this.bytes.subarray(begin, end);
     },
     skip: function ChunkedStream_skip(n) {
@@ -5834,17 +5836,17 @@ var Catalog = function CatalogClosure() 
       var root = { items: [] };
       var queue = [{
         obj,
         parent: root
       }];
       var processed = new _primitives.RefSet();
       processed.put(obj);
       var xref = this.xref,
-          blackColor = new Uint8Array(3);
+          blackColor = new Uint8ClampedArray(3);
       while (queue.length > 0) {
         var i = queue.shift();
         var outlineDict = xref.fetchIfRef(i.obj);
         if (outlineDict === null) {
           continue;
         }
         if (!outlineDict.has('Title')) {
           throw new _util.FormatError('Invalid outline item');
@@ -7554,19 +7556,16 @@ var _jbig2_stream = __w_pdfjs_require__(
 var _jpeg_stream = __w_pdfjs_require__(19);
 
 var _jpx_stream = __w_pdfjs_require__(21);
 
 const MAX_LENGTH_TO_CACHE = 1000;
 const MAX_ADLER32_LENGTH = 5552;
 function computeAdler32(bytes) {
   let bytesLength = bytes.length;
-  if (bytesLength >= MAX_ADLER32_LENGTH) {
-    throw new Error('computeAdler32: The input is too large.');
-  }
   let a = 1,
       b = 0;
   for (let i = 0; i < bytesLength; ++i) {
     a += bytes[i] & 0xFF;
     b += a;
   }
   return b % 65521 << 16 | a % 65521;
 }
@@ -8568,37 +8567,39 @@ var Stream = function StreamClosure() {
     },
     getInt32: function Stream_getInt32() {
       var b0 = this.getByte();
       var b1 = this.getByte();
       var b2 = this.getByte();
       var b3 = this.getByte();
       return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
     },
-    getBytes: function Stream_getBytes(length) {
+    getBytes(length, forceClamped = false) {
       var bytes = this.bytes;
       var pos = this.pos;
       var strEnd = this.end;
       if (!length) {
-        return bytes.subarray(pos, strEnd);
+        let subarray = bytes.subarray(pos, strEnd);
+        return forceClamped ? new Uint8ClampedArray(subarray) : subarray;
       }
       var end = pos + length;
       if (end > strEnd) {
         end = strEnd;
       }
       this.pos = end;
-      return bytes.subarray(pos, end);
+      let subarray = bytes.subarray(pos, end);
+      return forceClamped ? new Uint8ClampedArray(subarray) : subarray;
     },
     peekByte: function Stream_peekByte() {
       var peekedByte = this.getByte();
       this.pos--;
       return peekedByte;
     },
-    peekBytes: function Stream_peekBytes(length) {
-      var bytes = this.getBytes(length);
+    peekBytes(length, forceClamped = false) {
+      var bytes = this.getBytes(length, forceClamped);
       this.pos -= bytes.length;
       return bytes;
     },
     skip: function Stream_skip(n) {
       if (!n) {
         n = 1;
       }
       this.pos += n;
@@ -8677,17 +8678,17 @@ var DecodeStream = function DecodeStream
     },
     getInt32: function DecodeStream_getInt32() {
       var b0 = this.getByte();
       var b1 = this.getByte();
       var b2 = this.getByte();
       var b3 = this.getByte();
       return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
     },
-    getBytes: function DecodeStream_getBytes(length) {
+    getBytes(length, forceClamped = false) {
       var end,
           pos = this.pos;
       if (length) {
         this.ensureBuffer(pos + length);
         end = pos + length;
         while (!this.eof && this.bufferLength < end) {
           this.readBlock();
         }
@@ -8697,25 +8698,26 @@ var DecodeStream = function DecodeStream
         }
       } else {
         while (!this.eof) {
           this.readBlock();
         }
         end = this.bufferLength;
       }
       this.pos = end;
-      return this.buffer.subarray(pos, end);
+      let subarray = this.buffer.subarray(pos, end);
+      return forceClamped && !(subarray instanceof Uint8ClampedArray) ? new Uint8ClampedArray(subarray) : subarray;
     },
     peekByte: function DecodeStream_peekByte() {
       var peekedByte = this.getByte();
       this.pos--;
       return peekedByte;
     },
-    peekBytes: function DecodeStream_peekBytes(length) {
-      var bytes = this.getBytes(length);
+    peekBytes(length, forceClamped = false) {
+      var bytes = this.getBytes(length, forceClamped);
       this.pos -= bytes.length;
       return bytes;
     },
     makeSubStream: function DecodeStream_makeSubStream(start, length, dict) {
       var end = start + length;
       while (this.bufferLength <= end && !this.eof) {
         this.readBlock();
       }
@@ -16706,17 +16708,17 @@ Object.defineProperty(exports, "__esModu
 });
 exports.ColorSpace = undefined;
 
 var _util = __w_pdfjs_require__(2);
 
 var _primitives = __w_pdfjs_require__(11);
 
 var ColorSpace = function ColorSpaceClosure() {
-  function resizeRgbImage(src, bpc, w1, h1, w2, h2, alpha01, dest) {
+  function resizeRgbImage(src, dest, w1, h1, w2, h2, alpha01) {
     var COMPONENTS = 3;
     alpha01 = alpha01 !== 1 ? 0 : alpha01;
     var xRatio = w1 / w2;
     var yRatio = h1 / h2;
     var i,
         j,
         py,
         newIndex = 0,
@@ -16736,48 +16738,48 @@ var ColorSpace = function ColorSpaceClos
         newIndex += alpha01;
       }
     }
   }
   function ColorSpace() {
     (0, _util.unreachable)('should not call ColorSpace constructor');
   }
   ColorSpace.prototype = {
-    getRgb: function ColorSpace_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
+    getRgb(src, srcOffset) {
+      let rgb = new Uint8ClampedArray(3);
       this.getRgbItem(src, srcOffset, rgb, 0);
       return rgb;
     },
-    getRgbItem: function ColorSpace_getRgbItem(src, srcOffset, dest, destOffset) {
+    getRgbItem(src, srcOffset, dest, destOffset) {
       (0, _util.unreachable)('Should not call ColorSpace.getRgbItem');
     },
-    getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
+    getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
       (0, _util.unreachable)('Should not call ColorSpace.getRgbBuffer');
     },
-    getOutputLength: function ColorSpace_getOutputLength(inputLength, alpha01) {
+    getOutputLength(inputLength, alpha01) {
       (0, _util.unreachable)('Should not call ColorSpace.getOutputLength');
     },
-    isPassthrough: function ColorSpace_isPassthrough(bits) {
-      return false;
-    },
-    fillRgb: function ColorSpace_fillRgb(dest, originalWidth, originalHeight, width, height, actualHeight, bpc, comps, alpha01) {
+    isPassthrough(bits) {
+      return false;
+    },
+    fillRgb(dest, originalWidth, originalHeight, width, height, actualHeight, bpc, comps, alpha01) {
       var count = originalWidth * originalHeight;
       var rgbBuf = null;
       var numComponentColors = 1 << bpc;
       var needsResizing = originalHeight !== height || originalWidth !== width;
       var i, ii;
       if (this.isPassthrough(bpc)) {
         rgbBuf = comps;
       } else if (this.numComps === 1 && count > numComponentColors && this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') {
         var allColors = bpc <= 8 ? new Uint8Array(numComponentColors) : new Uint16Array(numComponentColors);
         var key;
         for (i = 0; i < numComponentColors; i++) {
           allColors[i] = i;
         }
-        var colorMap = new Uint8Array(numComponentColors * 3);
+        var colorMap = new Uint8ClampedArray(numComponentColors * 3);
         this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc, 0);
         var destPos, rgbPos;
         if (!needsResizing) {
           destPos = 0;
           for (i = 0; i < count; ++i) {
             key = comps[i] * 3;
             dest[destPos++] = colorMap[key];
             dest[destPos++] = colorMap[key + 1];
@@ -16793,23 +16795,23 @@ var ColorSpace = function ColorSpaceClos
             rgbBuf[rgbPos++] = colorMap[key + 1];
             rgbBuf[rgbPos++] = colorMap[key + 2];
           }
         }
       } else {
         if (!needsResizing) {
           this.getRgbBuffer(comps, 0, width * actualHeight, dest, 0, bpc, alpha01);
         } else {
-          rgbBuf = new Uint8Array(count * 3);
+          rgbBuf = new Uint8ClampedArray(count * 3);
           this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc, 0);
         }
       }
       if (rgbBuf) {
         if (needsResizing) {
-          resizeRgbImage(rgbBuf, bpc, originalWidth, originalHeight, width, height, alpha01, dest);
+          resizeRgbImage(rgbBuf, dest, originalWidth, originalHeight, width, height, alpha01);
         } else {
           rgbPos = 0;
           destPos = 0;
           for (i = 0, ii = width * actualHeight; i < ii; i++) {
             dest[destPos++] = rgbBuf[rgbPos++];
             dest[destPos++] = rgbBuf[rgbPos++];
             dest[destPos++] = rgbBuf[rgbPos++];
             destPos += alpha01;
@@ -16977,17 +16979,17 @@ var ColorSpace = function ColorSpaceClos
           var range = params.getArray('Range');
           return ['LabCS', whitePoint, blackPoint, range];
         default:
           throw new _util.FormatError(`unimplemented color space object "${mode}"`);
       }
     }
     throw new _util.FormatError(`unrecognized color space object: "${cs}"`);
   };
-  ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) {
+  ColorSpace.isDefaultDecode = function (decode, n) {
     if (!Array.isArray(decode)) {
       return true;
     }
     if (n * 2 !== decode.length) {
       (0, _util.warn)('The decode map is not the correct length');
       return true;
     }
     for (var i = 0, ii = decode.length; i < ii; i += 2) {
@@ -17019,30 +17021,30 @@ var AlternateCS = function AlternateCSCl
       this.defaultColor[i] = 1;
     }
     this.base = base;
     this.tintFn = tintFn;
     this.tmpBuf = new Float32Array(base.numComps);
   }
   AlternateCS.prototype = {
     getRgb: ColorSpace.prototype.getRgb,
-    getRgbItem: function AlternateCS_getRgbItem(src, srcOffset, dest, destOffset) {
+    getRgbItem(src, srcOffset, dest, destOffset) {
       var tmpBuf = this.tmpBuf;
       this.tintFn(src, srcOffset, tmpBuf, 0);
       this.base.getRgbItem(tmpBuf, 0, dest, destOffset);
     },
-    getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
+    getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
       var tintFn = this.tintFn;
       var base = this.base;
       var scale = 1 / ((1 << bits) - 1);
       var baseNumComps = base.numComps;
       var usesZeroToOneRange = base.usesZeroToOneRange;
       var isPassthrough = (base.isPassthrough(8) || !usesZeroToOneRange) && alpha01 === 0;
       var pos = isPassthrough ? destOffset : 0;
-      var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count);
+      let baseBuf = isPassthrough ? dest : new Uint8ClampedArray(baseNumComps * count);
       var numComps = this.numComps;
       var scaled = new Float32Array(numComps);
       var tinted = new Float32Array(baseNumComps);
       var i, j;
       for (i = 0; i < count; i++) {
         for (j = 0; j < numComps; j++) {
           scaled[j] = src[srcOffset++] * scale;
         }
@@ -17055,22 +17057,22 @@ var AlternateCS = function AlternateCSCl
           base.getRgbItem(tinted, 0, baseBuf, pos);
           pos += baseNumComps;
         }
       }
       if (!isPassthrough) {
         base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01);
       }
     },
-    getOutputLength: function AlternateCS_getOutputLength(inputLength, alpha01) {
+    getOutputLength(inputLength, alpha01) {
       return this.base.getOutputLength(inputLength * this.base.numComps / this.numComps, alpha01);
     },
     isPassthrough: ColorSpace.prototype.isPassthrough,
     fillRgb: ColorSpace.prototype.fillRgb,
-    isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) {
+    isDefaultDecode(decodeMap) {
       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
     },
     usesZeroToOneRange: true
   };
   return AlternateCS;
 }();
 var PatternCS = function PatternCSClosure() {
   function PatternCS(baseCS) {
@@ -17101,164 +17103,157 @@ var IndexedCS = function IndexedCSClosur
     } else if (lookup instanceof Uint8Array) {
       this.lookup = lookup;
     } else {
       throw new _util.FormatError(`Unrecognized lookup table: ${lookup}`);
     }
   }
   IndexedCS.prototype = {
     getRgb: ColorSpace.prototype.getRgb,
-    getRgbItem: function IndexedCS_getRgbItem(src, srcOffset, dest, destOffset) {
+    getRgbItem(src, srcOffset, dest, destOffset) {
       var numComps = this.base.numComps;
       var start = src[srcOffset] * numComps;
       this.base.getRgbBuffer(this.lookup, start, 1, dest, destOffset, 8, 0);
     },
-    getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
+    getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
       var base = this.base;
       var numComps = base.numComps;
       var outputDelta = base.getOutputLength(numComps, alpha01);
       var lookup = this.lookup;
       for (var i = 0; i < count; ++i) {
         var lookupPos = src[srcOffset++] * numComps;
         base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8, alpha01);
         destOffset += outputDelta;
       }
     },
-    getOutputLength: function IndexedCS_getOutputLength(inputLength, alpha01) {
+    getOutputLength(inputLength, alpha01) {
       return this.base.getOutputLength(inputLength * this.base.numComps, alpha01);
     },
     isPassthrough: ColorSpace.prototype.isPassthrough,
     fillRgb: ColorSpace.prototype.fillRgb,
-    isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) {
+    isDefaultDecode(decodeMap) {
       return true;
     },
     usesZeroToOneRange: true
   };
   return IndexedCS;
 }();
 var DeviceGrayCS = function DeviceGrayCSClosure() {
   function DeviceGrayCS() {
     this.name = 'DeviceGray';
     this.numComps = 1;
     this.defaultColor = new Float32Array(this.numComps);
   }
   DeviceGrayCS.prototype = {
     getRgb: ColorSpace.prototype.getRgb,
-    getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset, dest, destOffset) {
-      var c = src[srcOffset] * 255 | 0;
-      c = c < 0 ? 0 : c > 255 ? 255 : c;
+    getRgbItem(src, srcOffset, dest, destOffset) {
+      let c = src[srcOffset] * 255;
       dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c;
     },
-    getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
+    getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
       var scale = 255 / ((1 << bits) - 1);
       var j = srcOffset,
           q = destOffset;
       for (var i = 0; i < count; ++i) {
-        var c = scale * src[j++] | 0;
+        let c = scale * src[j++];
         dest[q++] = c;
         dest[q++] = c;
         dest[q++] = c;
         q += alpha01;
       }
     },
-    getOutputLength: function DeviceGrayCS_getOutputLength(inputLength, alpha01) {
+    getOutputLength(inputLength, alpha01) {
       return inputLength * (3 + alpha01);
     },
     isPassthrough: ColorSpace.prototype.isPassthrough,
     fillRgb: ColorSpace.prototype.fillRgb,
-    isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) {
+    isDefaultDecode(decodeMap) {
       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
     },
     usesZeroToOneRange: true
   };
   return DeviceGrayCS;
 }();
 var DeviceRgbCS = function DeviceRgbCSClosure() {
   function DeviceRgbCS() {
     this.name = 'DeviceRGB';
     this.numComps = 3;
     this.defaultColor = new Float32Array(this.numComps);
   }
   DeviceRgbCS.prototype = {
     getRgb: ColorSpace.prototype.getRgb,
-    getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset, dest, destOffset) {
-      var r = src[srcOffset] * 255 | 0;
-      var g = src[srcOffset + 1] * 255 | 0;
-      var b = src[srcOffset + 2] * 255 | 0;
-      dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r;
-      dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g;
-      dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b;
-    },
-    getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
+    getRgbItem(src, srcOffset, dest, destOffset) {
+      dest[destOffset] = src[srcOffset] * 255;
+      dest[destOffset + 1] = src[srcOffset + 1] * 255;
+      dest[destOffset + 2] = src[srcOffset + 2] * 255;
+    },
+    getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
       if (bits === 8 && alpha01 === 0) {
         dest.set(src.subarray(srcOffset, srcOffset + count * 3), destOffset);
         return;
       }
       var scale = 255 / ((1 << bits) - 1);
       var j = srcOffset,
           q = destOffset;
       for (var i = 0; i < count; ++i) {
-        dest[q++] = scale * src[j++] | 0;
-        dest[q++] = scale * src[j++] | 0;
-        dest[q++] = scale * src[j++] | 0;
+        dest[q++] = scale * src[j++];
+        dest[q++] = scale * src[j++];
+        dest[q++] = scale * src[j++];
         q += alpha01;
       }
     },
-    getOutputLength: function DeviceRgbCS_getOutputLength(inputLength, alpha01) {
+    getOutputLength(inputLength, alpha01) {
       return inputLength * (3 + alpha01) / 3 | 0;
     },
-    isPassthrough: function DeviceRgbCS_isPassthrough(bits) {
+    isPassthrough(bits) {
       return bits === 8;
     },
     fillRgb: ColorSpace.prototype.fillRgb,
-    isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) {
+    isDefaultDecode(decodeMap) {
       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
     },
     usesZeroToOneRange: true
   };
   return DeviceRgbCS;
 }();
 var DeviceCmykCS = function DeviceCmykCSClosure() {
   function convertToRgb(src, srcOffset, srcScale, dest, destOffset) {
-    var c = src[srcOffset + 0] * srcScale;
+    var c = src[srcOffset] * srcScale;
     var m = src[srcOffset + 1] * srcScale;
     var y = src[srcOffset + 2] * srcScale;
     var k = src[srcOffset + 3] * srcScale;
-    var r = c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k + -285.2331026137004) + m * (1.7149763477362134 * m - 5.6096736904047315 * y + -17.873870861415444 * k - 5.497006427196366) + y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) + k * (-21.86122147463605 * k - 189.48180835922747) + 255 | 0;
-    var g = c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k + -79.2970844816548) + m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) + k * (-20.737325471181034 * k - 187.80453709719578) + 255 | 0;
-    var b = c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k + -14.183576799673286) + m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) + y * (0.03296041114873217 * y + 115.60384449646641 * k + -193.58209356861505) + k * (-22.33816807309886 * k - 180.12613974708367) + 255 | 0;
-    dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r;
-    dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
-    dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
+    dest[destOffset] = 255 + c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k + -285.2331026137004) + m * (1.7149763477362134 * m - 5.6096736904047315 * y + -17.873870861415444 * k - 5.497006427196366) + y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) + k * (-21.86122147463605 * k - 189.48180835922747);
+    dest[destOffset + 1] = 255 + c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k + -79.2970844816548) + m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) + k * (-20.737325471181034 * k - 187.80453709719578);
+    dest[destOffset + 2] = 255 + c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k + -14.183576799673286) + m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) + y * (0.03296041114873217 * y + 115.60384449646641 * k + -193.58209356861505) + k * (-22.33816807309886 * k - 180.12613974708367);
   }
   function DeviceCmykCS() {
     this.name = 'DeviceCMYK';
     this.numComps = 4;
     this.defaultColor = new Float32Array(this.numComps);
     this.defaultColor[3] = 1;
   }
   DeviceCmykCS.prototype = {
     getRgb: ColorSpace.prototype.getRgb,
-    getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset, dest, destOffset) {
+    getRgbItem(src, srcOffset, dest, destOffset) {
       convertToRgb(src, srcOffset, 1, dest, destOffset);
     },
-    getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
+    getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
       var scale = 1 / ((1 << bits) - 1);
       for (var i = 0; i < count; i++) {
         convertToRgb(src, srcOffset, scale, dest, destOffset);
         srcOffset += 4;
         destOffset += 3 + alpha01;
       }
     },
-    getOutputLength: function DeviceCmykCS_getOutputLength(inputLength, alpha01) {
+    getOutputLength(inputLength, alpha01) {
       return inputLength / 4 * (3 + alpha01) | 0;
     },
     isPassthrough: ColorSpace.prototype.isPassthrough,
     fillRgb: ColorSpace.prototype.fillRgb,
-    isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) {
+    isDefaultDecode(decodeMap) {
       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
     },
     usesZeroToOneRange: true
   };
   return DeviceCmykCS;
 }();
 var CalGrayCS = function CalGrayCSClosure() {
   function CalGrayCS(whitePoint, blackPoint, gamma) {
@@ -17291,40 +17286,40 @@ var CalGrayCS = function CalGrayCSClosur
       (0, _util.info)('Invalid Gamma: ' + this.G + ' for ' + this.name + ', falling back to default');
       this.G = 1;
     }
   }
   function convertToRgb(cs, src, srcOffset, dest, destOffset, scale) {
     var A = src[srcOffset] * scale;
     var AG = Math.pow(A, cs.G);
     var L = cs.YW * AG;
-    var val = Math.max(295.8 * Math.pow(L, 0.333333333333333333) - 40.8, 0) | 0;
+    let val = Math.max(295.8 * Math.pow(L, 0.333333333333333333) - 40.8, 0);
     dest[destOffset] = val;
     dest[destOffset + 1] = val;
     dest[destOffset + 2] = val;
   }
   CalGrayCS.prototype = {
     getRgb: ColorSpace.prototype.getRgb,
-    getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset, dest, destOffset) {
+    getRgbItem(src, srcOffset, dest, destOffset) {
       convertToRgb(this, src, srcOffset, dest, destOffset, 1);
     },
-    getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
+    getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
       var scale = 1 / ((1 << bits) - 1);
       for (var i = 0; i < count; ++i) {
         convertToRgb(this, src, srcOffset, dest, destOffset, scale);
         srcOffset += 1;
         destOffset += 3 + alpha01;
       }
     },
-    getOutputLength: function CalGrayCS_getOutputLength(inputLength, alpha01) {
+    getOutputLength(inputLength, alpha01) {
       return inputLength * (3 + alpha01);
     },
     isPassthrough: ColorSpace.prototype.isPassthrough,
     fillRgb: ColorSpace.prototype.fillRgb,
-    isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) {
+    isDefaultDecode(decodeMap) {
       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
     },
     usesZeroToOneRange: true
   };
   return CalGrayCS;
 }();
 var CalRGBCS = function CalRGBCSClosure() {
   var BRADFORD_SCALE_MATRIX = new Float32Array([0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296]);
@@ -17479,42 +17474,39 @@ var CalRGBCS = function CalRGBCSClosure(
     var XYZ_Flat = tempConvertMatrix2;
     normalizeWhitePointToFlat(cs.whitePoint, XYZ, XYZ_Flat);
     var XYZ_Black = tempConvertMatrix1;
     compensateBlackPoint(cs.blackPoint, XYZ_Flat, XYZ_Black);
     var XYZ_D65 = tempConvertMatrix2;
     normalizeWhitePointToD65(FLAT_WHITEPOINT_MATRIX, XYZ_Black, XYZ_D65);
     var SRGB = tempConvertMatrix1;
     matrixProduct(SRGB_D65_XYZ_TO_RGB_MATRIX, XYZ_D65, SRGB);
-    var sR = sRGBTransferFunction(SRGB[0]);
-    var sG = sRGBTransferFunction(SRGB[1]);
-    var sB = sRGBTransferFunction(SRGB[2]);
-    dest[destOffset] = Math.round(sR * 255);
-    dest[destOffset + 1] = Math.round(sG * 255);
-    dest[destOffset + 2] = Math.round(sB * 255);
+    dest[destOffset] = sRGBTransferFunction(SRGB[0]) * 255;
+    dest[destOffset + 1] = sRGBTransferFunction(SRGB[1]) * 255;
+    dest[destOffset + 2] = sRGBTransferFunction(SRGB[2]) * 255;
   }
   CalRGBCS.prototype = {
     getRgb: ColorSpace.prototype.getRgb,
-    getRgbItem: function CalRGBCS_getRgbItem(src, srcOffset, dest, destOffset) {
+    getRgbItem(src, srcOffset, dest, destOffset) {
       convertToRgb(this, src, srcOffset, dest, destOffset, 1);
     },
-    getRgbBuffer: function CalRGBCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
+    getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
       var scale = 1 / ((1 << bits) - 1);
       for (var i = 0; i < count; ++i) {
         convertToRgb(this, src, srcOffset, dest, destOffset, scale);
         srcOffset += 3;
         destOffset += 3 + alpha01;
       }
     },
-    getOutputLength: function CalRGBCS_getOutputLength(inputLength, alpha01) {
+    getOutputLength(inputLength, alpha01) {
       return inputLength * (3 + alpha01) / 3 | 0;
     },
     isPassthrough: ColorSpace.prototype.isPassthrough,
     fillRgb: ColorSpace.prototype.fillRgb,
-    isDefaultDecode: function CalRGBCS_isDefaultDecode(decodeMap) {
+    isDefaultDecode(decodeMap) {
       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
     },
     usesZeroToOneRange: true
   };
   return CalRGBCS;
 }();
 var LabCS = function LabCSClosure() {
   function LabCS(whitePoint, blackPoint, range) {
@@ -17585,39 +17577,39 @@ var LabCS = function LabCSClosure() {
       r = X * 3.1339 + Y * -1.6170 + Z * -0.4906;
       g = X * -0.9785 + Y * 1.9160 + Z * 0.0333;
       b = X * 0.0720 + Y * -0.2290 + Z * 1.4057;
     } else {
       r = X * 3.2406 + Y * -1.5372 + Z * -0.4986;
       g = X * -0.9689 + Y * 1.8758 + Z * 0.0415;
       b = X * 0.0557 + Y * -0.2040 + Z * 1.0570;
     }
-    dest[destOffset] = r <= 0 ? 0 : r >= 1 ? 255 : Math.sqrt(r) * 255 | 0;
-    dest[destOffset + 1] = g <= 0 ? 0 : g >= 1 ? 255 : Math.sqrt(g) * 255 | 0;
-    dest[destOffset + 2] = b <= 0 ? 0 : b >= 1 ? 255 : Math.sqrt(b) * 255 | 0;
+    dest[destOffset] = Math.sqrt(r) * 255;
+    dest[destOffset + 1] = Math.sqrt(g) * 255;
+    dest[destOffset + 2] = Math.sqrt(b) * 255;
   }
   LabCS.prototype = {
     getRgb: ColorSpace.prototype.getRgb,
-    getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) {
+    getRgbItem(src, srcOffset, dest, destOffset) {
       convertToRgb(this, src, srcOffset, false, dest, destOffset);
     },
-    getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
+    getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {
       var maxVal = (1 << bits) - 1;
       for (var i = 0; i < count; i++) {
         convertToRgb(this, src, srcOffset, maxVal, dest, destOffset);
         srcOffset += 3;
         destOffset += 3 + alpha01;
       }
     },
-    getOutputLength: function LabCS_getOutputLength(inputLength, alpha01) {
+    getOutputLength(inputLength, alpha01) {
       return inputLength * (3 + alpha01) / 3 | 0;
     },
     isPassthrough: ColorSpace.prototype.isPassthrough,
     fillRgb: ColorSpace.prototype.fillRgb,
-    isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) {
+    isDefaultDecode(decodeMap) {
       return true;
     },
     usesZeroToOneRange: false
   };
   return LabCS;
 }();
 exports.ColorSpace = ColorSpace;
 
@@ -17778,17 +17770,17 @@ class Annotation {
   setRectangle(rectangle) {
     if (Array.isArray(rectangle) && rectangle.length === 4) {
       this.rectangle = _util.Util.normalizeRect(rectangle);
     } else {
       this.rectangle = [0, 0, 0, 0];
     }
   }
   setColor(color) {
-    let rgbColor = new Uint8Array(3);
+    let rgbColor = new Uint8ClampedArray(3);
     if (!Array.isArray(color)) {
       this.color = rgbColor;
       return;
     }
     switch (color.length) {
       case 0:
         this.color = null;
         break;
@@ -19150,17 +19142,17 @@ var PartialEvaluator = function PartialE
         return Promise.resolve();
       }
       var imageMask = dict.get('ImageMask', 'IM') || false;
       var imgData, args;
       if (imageMask) {
         var width = dict.get('Width', 'W');
         var height = dict.get('Height', 'H');
         var bitStrideLength = width + 7 >> 3;
-        var imgArray = image.getBytes(bitStrideLength * height);
+        var imgArray = image.getBytes(bitStrideLength * height, true);
         var decode = dict.getArray('Decode', 'D');
         imgData = _image.PDFImage.createMask({
           imgArray,
           width,
           height,
           imageIsFromDecodeStream: image instanceof _stream.DecodeStream,
           inverseDecode: !!decode && decode[0] > 0
         });
@@ -19830,17 +19822,17 @@ var PartialEvaluator = function PartialE
           next(deferred);
           return;
         }
         closePendingRestoreOPS();
         resolve();
       }).catch(reason => {
         if (this.options.ignoreErrors) {
           this.handler.send('UnsupportedFeature', { featureId: _util.UNSUPPORTED_FEATURES.unknown });
-          (0, _util.warn)('getOperatorList - ignoring errors during task: ' + task.name);
+          (0, _util.warn)(`getOperatorList - ignoring errors during "${task.name}" ` + `task: "${reason}".`);
           closePendingRestoreOPS();
           return;
         }
         throw reason;
       });
     },
     getTextContent({ stream, task, resources, stateManager = null, normalizeWhitespace = false, combineTextItems = false, sink, seenStyles = Object.create(null) }) {
       resources = resources || _primitives.Dict.empty;
@@ -20303,17 +20295,17 @@ var PartialEvaluator = function PartialE
         flushTextContentItem();
         enqueueChunk();
         resolve();
       }).catch(reason => {
         if (reason instanceof _util.AbortException) {
           return;
         }
         if (this.options.ignoreErrors) {
-          (0, _util.warn)('getTextContent - ignoring errors during task: ' + task.name);
+          (0, _util.warn)(`getTextContent - ignoring errors during "${task.name}" ` + `task: "${reason}".`);
           flushTextContentItem();
           enqueueChunk();
           return;
         }
         throw reason;
       });
     },
     extractDataStructures: function PartialEvaluator_extractDataStructures(dict, baseDict, properties) {
@@ -40438,20 +40430,20 @@ var PDFImage = function PDFImageClosure(
   PDFImage.createMask = function ({ imgArray, width, height, imageIsFromDecodeStream, inverseDecode }) {
     var computedLength = (width + 7 >> 3) * height;
     var actualLength = imgArray.byteLength;
     var haveFullData = computedLength === actualLength;
     var data, i;
     if (imageIsFromDecodeStream && (!inverseDecode || haveFullData)) {
       data = imgArray;
     } else if (!inverseDecode) {
-      data = new Uint8Array(actualLength);
+      data = new Uint8ClampedArray(actualLength);
       data.set(imgArray);
     } else {
-      data = new Uint8Array(computedLength);
+      data = new Uint8ClampedArray(computedLength);
       data.set(imgArray);
       for (i = actualLength; i < computedLength; i++) {
         data[i] = 0xff;
       }
     }
     if (inverseDecode) {
       for (i = 0; i < actualLength; i++) {
         data[i] ^= 0xFF;
@@ -40556,36 +40548,36 @@ var PDFImage = function PDFImageClosure(
     },
     fillOpacity(rgbaBuf, width, height, actualHeight, image) {
       var smask = this.smask;
       var mask = this.mask;
       var alphaBuf, sw, sh, i, ii, j;
       if (smask) {
         sw = smask.width;
         sh = smask.height;
-        alphaBuf = new Uint8Array(sw * sh);
+        alphaBuf = new Uint8ClampedArray(sw * sh);
         smask.fillGrayBuffer(alphaBuf);
         if (sw !== width || sh !== height) {
           alphaBuf = resizeImageMask(alphaBuf, smask.bpc, sw, sh, width, height);
         }
       } else if (mask) {
         if (mask instanceof PDFImage) {
           sw = mask.width;
           sh = mask.height;
-          alphaBuf = new Uint8Array(sw * sh);
+          alphaBuf = new Uint8ClampedArray(sw * sh);
           mask.numComps = 1;
           mask.fillGrayBuffer(alphaBuf);
           for (i = 0, ii = sw * sh; i < ii; ++i) {
             alphaBuf[i] = 255 - alphaBuf[i];
           }
           if (sw !== width || sh !== height) {
             alphaBuf = resizeImageMask(alphaBuf, mask.bpc, sw, sh, width, height);
           }
         } else if (Array.isArray(mask)) {
-          alphaBuf = new Uint8Array(width * height);
+          alphaBuf = new Uint8ClampedArray(width * height);
           var numComps = this.numComps;
           for (i = 0, ii = width * height; i < ii; ++i) {
             var opacity = 0;
             var imageOffset = i * numComps;
             for (j = 0; j < numComps; ++j) {
               var color = image[imageOffset + j];
               var maskOffset = j * 2;
               if (color < mask[maskOffset] || color > mask[maskOffset + 1]) {
@@ -40614,40 +40606,38 @@ var PDFImage = function PDFImageClosure(
       if (!matte) {
         return;
       }
       var matteRgb = this.colorSpace.getRgb(matte, 0);
       var matteR = matteRgb[0];
       var matteG = matteRgb[1];
       var matteB = matteRgb[2];
       var length = width * height * 4;
-      var r, g, b;
       for (var i = 0; i < length; i += 4) {
         var alpha = buffer[i + 3];
         if (alpha === 0) {
           buffer[i] = 255;
           buffer[i + 1] = 255;
           buffer[i + 2] = 255;
           continue;
         }
         var k = 255 / alpha;
-        r = (buffer[i] - matteR) * k + matteR;
-        g = (buffer[i + 1] - matteG) * k + matteG;
-        b = (buffer[i + 2] - matteB) * k + matteB;
-        buffer[i] = r <= 0 ? 0 : r >= 255 ? 255 : r | 0;
-        buffer[i + 1] = g <= 0 ? 0 : g >= 255 ? 255 : g | 0;
-        buffer[i + 2] = b <= 0 ? 0 : b >= 255 ? 255 : b | 0;
+        buffer[i] = (buffer[i] - matteR) * k + matteR;
+        buffer[i + 1] = (buffer[i + 1] - matteG) * k + matteG;
+        buffer[i + 2] = (buffer[i + 2] - matteB) * k + matteB;
       }
     },
     createImageData(forceRGBA = false) {
       var drawWidth = this.drawWidth;
       var drawHeight = this.drawHeight;
       var imgData = {
         width: drawWidth,
-        height: drawHeight
+        height: drawHeight,
+        kind: 0,
+        data: null
       };
       var numComps = this.numComps;
       var originalWidth = this.width;
       var originalHeight = this.height;
       var bpc = this.bpc;
       var rowBytes = originalWidth * numComps * bpc + 7 >> 3;
       var imgArray;
       if (!forceRGBA) {
@@ -40658,22 +40648,22 @@ var PDFImage = function PDFImageClosure(
           kind = _util.ImageKind.RGB_24BPP;
         }
         if (kind && !this.smask && !this.mask && drawWidth === originalWidth && drawHeight === originalHeight) {
           imgData.kind = kind;
           imgArray = this.getImageBytes(originalHeight * rowBytes);
           if (this.image instanceof _stream.DecodeStream) {
             imgData.data = imgArray;
           } else {
-            var newArray = new Uint8Array(imgArray.length);
+            var newArray = new Uint8ClampedArray(imgArray.length);
             newArray.set(imgArray);
             imgData.data = newArray;
           }
           if (this.needsDecode) {
-            (0, _util.assert)(kind === _util.ImageKind.GRAYSCALE_1BPP);
+            (0, _util.assert)(kind === _util.ImageKind.GRAYSCALE_1BPP, 'PDFImage.createImageData: The image must be grayscale.');
             var buffer = imgData.data;
             for (var i = 0, ii = buffer.length; i < ii; i++) {
               buffer[i] ^= 0xff;
             }
           }
           return imgData;
         }
         if (this.image instanceof _jpeg_stream.JpegStream && !this.smask && !this.mask) {
@@ -40690,22 +40680,22 @@ var PDFImage = function PDFImageClosure(
         }
       }
       imgArray = this.getImageBytes(originalHeight * rowBytes);
       var actualHeight = 0 | imgArray.length / rowBytes * drawHeight / originalHeight;
       var comps = this.getComponents(imgArray);
       var alpha01, maybeUndoPreblend;
       if (!forceRGBA && !this.smask && !this.mask) {
         imgData.kind = _util.ImageKind.RGB_24BPP;
-        imgData.data = new Uint8Array(drawWidth * drawHeight * 3);
+        imgData.data = new Uint8ClampedArray(drawWidth * drawHeight * 3);
         alpha01 = 0;
         maybeUndoPreblend = false;
       } else {
         imgData.kind = _util.ImageKind.RGBA_32BPP;
-        imgData.data = new Uint8Array(drawWidth * drawHeight * 4);
+        imgData.data = new Uint8ClampedArray(drawWidth * drawHeight * 4);
         alpha01 = 1;
         maybeUndoPreblend = true;
         this.fillOpacity(imgData.data, drawWidth, drawHeight, actualHeight, comps);
       }
       if (this.needsDecode) {
         this.decodeBuffer(comps);
       }
       this.colorSpace.fillRgb(imgData.data, originalWidth, originalHeight, drawWidth, drawHeight, actualHeight, bpc, comps, alpha01);
@@ -40740,25 +40730,25 @@ var PDFImage = function PDFImageClosure(
         return;
       }
       if (this.needsDecode) {
         this.decodeBuffer(comps);
       }
       length = width * height;
       var scale = 255 / ((1 << bpc) - 1);
       for (i = 0; i < length; ++i) {
-        buffer[i] = scale * comps[i] | 0;
+        buffer[i] = scale * comps[i];
       }
     },
     getImageBytes(length, drawWidth, drawHeight, forceRGB = false) {
       this.image.reset();
       this.image.drawWidth = drawWidth || this.width;
       this.image.drawHeight = drawHeight || this.height;
       this.image.forceRGB = !!forceRGB;
-      return this.image.getBytes(length);
+      return this.image.getBytes(length, true);
     }
   };
   return PDFImage;
 }();
 exports.PDFImage = PDFImage;
 
 /***/ }),
 /* 45 */
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -2205,20 +2205,20 @@ html[dir='rtl'] #documentPropertiesOverl
 }
 
 @media all and (max-width: 840px) {
   #sidebarContent {
     background-color: hsla(0,0%,0%,.7);
   }
 
   html[dir='ltr'] #outerContainer.sidebarOpen #viewerContainer {
-    left: 0px;
+    left: 0px !important;
   }
   html[dir='rtl'] #outerContainer.sidebarOpen #viewerContainer {
-    right: 0px;
+    right: 0px !important;
   }
 
   #outerContainer .hiddenLargeView,
   #outerContainer .hiddenMediumView {
     display: inherit;
   }
   #outerContainer .visibleLargeView,
   #outerContainer .visibleMediumView {
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -693,41 +693,41 @@ let PDFViewerApplication = {
         PDFViewerApplication.open(url, { range: transport });
         if (length) {
           PDFViewerApplication.pdfDocumentProperties.setFileSize(length);
         }
       },
       onOpenWithData(data) {
         PDFViewerApplication.open(data);
       },
-      onOpenWithURL(url, length, originalURL) {
+      onOpenWithURL(url, length, originalUrl) {
         let file = url,
             args = null;
         if (length !== undefined) {
           args = { length };
         }
-        if (originalURL !== undefined) {
+        if (originalUrl !== undefined) {
           file = {
-            file: url,
-            originalURL
+            url,
+            originalUrl
           };
         }
         PDFViewerApplication.open(file, args);
       },
       onError(err) {
         PDFViewerApplication.l10n.get('loading_error', null, 'An error occurred while loading the PDF.').then(msg => {
           PDFViewerApplication.error(msg, err);
         });
       },
       onProgress(loaded, total) {
         PDFViewerApplication.progress(loaded / total);
       }
     });
   },
-  setTitleUsingUrl(url) {
+  setTitleUsingUrl(url = '') {
     this.url = url;
     this.baseUrl = url.split('#')[0];
     let title = (0, _ui_utils.getPDFFileNameFromURL)(url, '');
     if (!title) {
       try {
         title = decodeURIComponent((0, _pdfjsLib.getFilenameFromUrl)(url)) || url;
       } catch (ex) {
         title = url;
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -4005,45 +4005,41 @@ namespace {
 
 // This runnable is used to write a deprecation message from a worker to the
 // console running on the main-thread.
 class DeprecationWarningRunnable final : public WorkerProxyToMainThreadRunnable
 {
   nsIDocument::DeprecatedOperations mOperation;
 
 public:
-  DeprecationWarningRunnable(WorkerPrivate* aWorkerPrivate,
-                             nsIDocument::DeprecatedOperations aOperation)
-    : WorkerProxyToMainThreadRunnable(aWorkerPrivate)
-    , mOperation(aOperation)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-  }
+  explicit DeprecationWarningRunnable(nsIDocument::DeprecatedOperations aOperation)
+    : mOperation(aOperation)
+  {}
 
 private:
   void
-  RunOnMainThread() override
+  RunOnMainThread(WorkerPrivate* aWorkerPrivate) override
   {
     MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aWorkerPrivate);
 
     // Walk up to our containing page
-    WorkerPrivate* wp = mWorkerPrivate;
+    WorkerPrivate* wp = aWorkerPrivate;
     while (wp->GetParent()) {
       wp = wp->GetParent();
     }
 
     nsPIDOMWindowInner* window = wp->GetWindow();
     if (window && window->GetExtantDoc()) {
       window->GetExtantDoc()->WarnOnceAbout(mOperation);
     }
   }
 
   void
-  RunBackOnWorkerThreadForCleanup() override
+  RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override
   {}
 };
 
 } // anonymous namespace
 
 void
 DeprecationWarning(JSContext* aCx, JSObject* aObject,
                    nsIDocument::DeprecatedOperations aOperation)
@@ -4071,18 +4067,18 @@ DeprecationWarning(const GlobalObject& a
   }
 
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aGlobal.Context());
   if (!workerPrivate) {
     return;
   }
 
   RefPtr<DeprecationWarningRunnable> runnable =
-    new DeprecationWarningRunnable(workerPrivate, aOperation);
-  runnable->Dispatch();
+    new DeprecationWarningRunnable(aOperation);
+  runnable->Dispatch(workerPrivate);
 }
 
 namespace binding_detail {
 JSObject*
 UnprivilegedJunkScopeOrWorkerGlobal()
 {
   if (NS_IsMainThread()) {
     return xpc::UnprivilegedJunkScope();
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRef.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Swizzle.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/ScopeExit.h"
 #include "ImageBitmapColorUtils.h"
 #include "ImageBitmapUtils.h"
 #include "ImageUtils.h"
@@ -1214,17 +1215,16 @@ AsyncFulfillImageBitmapPromise(Promise* 
   } else {
     RefPtr<FulfillImageBitmapPromiseWorkerTask> task =
       new FulfillImageBitmapPromiseWorkerTask(aPromise, aImageBitmap);
     task->Dispatch(); // Actually, to the current worker-thread.
   }
 }
 
 class CreateImageBitmapFromBlobRunnable;
-class CreateImageBitmapFromBlobHolder;
 
 class CreateImageBitmapFromBlob final : public CancelableRunnable
                                       , public imgIContainerCallback
 {
   friend class CreateImageBitmapFromBlobRunnable;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -1244,17 +1244,17 @@ public:
     nsresult rv = StartDecodeAndCropBlob();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       DecodeAndCropBlobCompletedMainThread(nullptr, rv);
     }
 
     return NS_OK;
   }
 
-  // Called by the WorkerHolder.
+  // Called by the WorkerRef.
   void WorkerShuttingDown();
 
 private:
   CreateImageBitmapFromBlob(Promise* aPromise,
                             nsIGlobalObject* aGlobal,
                             already_AddRefed<nsIInputStream> aInputStream,
                             const nsACString& aMimeType,
                             const Maybe<IntRect>& aCropRect,
@@ -1296,17 +1296,17 @@ private:
 
   // This is called on the main-thread only.
   nsresult DecodeAndCropBlob();
 
   Mutex mMutex;
 
   // The access to this object is protected by mutex but is always nullified on
   // the owning thread.
-  UniquePtr<CreateImageBitmapFromBlobHolder> mWorkerHolder;
+  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
 
   // Touched only on the owning thread.
   RefPtr<Promise> mPromise;
 
   // Touched only on the owning thread.
   nsCOMPtr<nsIGlobalObject> mGlobalObject;
 
   nsCOMPtr<nsIInputStream> mInputStream;
@@ -1343,49 +1343,16 @@ public:
   }
 
 private:
   RefPtr<CreateImageBitmapFromBlob> mTask;
   RefPtr<layers::Image> mImage;
   nsresult mStatus;
 };
 
-// This class keeps the worker alive and it informs CreateImageBitmapFromBlob
-// when it goes away.
-class CreateImageBitmapFromBlobHolder final : public WorkerHolder
-{
-public:
-  CreateImageBitmapFromBlobHolder(WorkerPrivate* aWorkerPrivate,
-                                  CreateImageBitmapFromBlob* aTask)
-    : WorkerHolder("CreateImageBitmapFromBlobHolder")
-    , mWorkerPrivate(aWorkerPrivate)
-    , mTask(aTask)
-    , mNotified(false)
-  {}
-
-  bool Notify(WorkerStatus aStatus) override
-  {
-    if (!mNotified) {
-      mNotified = true;
-      mTask->WorkerShuttingDown();
-    }
-    return true;
-  }
-
-  WorkerPrivate* GetWorkerPrivate() const
-  {
-    return mWorkerPrivate;
-  }
-
-private:
-  WorkerPrivate* mWorkerPrivate;
-  RefPtr<CreateImageBitmapFromBlob> mTask;
-  bool mNotified;
-};
-
 static void
 AsyncCreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal,
                                Blob& aBlob, const Maybe<IntRect>& aCropRect)
 {
   // Let's identify the main-thread event target.
   nsCOMPtr<nsIEventTarget> mainThreadEventTarget;
   if (NS_IsMainThread()) {
      mainThreadEventTarget = aGlobal->EventTargetFor(TaskCategory::Other);
@@ -2199,29 +2166,31 @@ CreateImageBitmapFromBlob::Create(Promis
     new CreateImageBitmapFromBlob(aPromise, aGlobal, stream.forget(), mimeType,
                                   aCropRect, aMainThreadEventTarget);
 
   // Nothing to do for the main-thread.
   if (NS_IsMainThread()) {
     return task.forget();
   }
 
-  // Let's use a WorkerHolder to keep the worker alive if this is not the
+  // Let's use a WorkerRef to keep the worker alive if this is not the
   // main-thread.
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
-  UniquePtr<CreateImageBitmapFromBlobHolder> holder(
-    new CreateImageBitmapFromBlobHolder(workerPrivate, task));
-
-  if (!holder->HoldWorker(workerPrivate, Terminating)) {
+  RefPtr<StrongWorkerRef> workerRef =
+    StrongWorkerRef::Create(workerPrivate, "CreateImageBitmapFromBlob",
+                            [task]() {
+      task->WorkerShuttingDown();
+    });
+  if (NS_WARN_IF(!workerRef)) {
     return nullptr;
   }
 
-  task->mWorkerHolder = std::move(holder);
+  task->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
   return task.forget();
 }
 
 nsresult
 CreateImageBitmapFromBlob::StartDecodeAndCropBlob()
 {
   MOZ_ASSERT(IsCurrentThread());
 
@@ -2334,23 +2303,23 @@ void
 CreateImageBitmapFromBlob::DecodeAndCropBlobCompletedMainThread(layers::Image* aImage,
                                                                 nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!IsCurrentThread()) {
     MutexAutoLock lock(mMutex);
 
-    if (!mWorkerHolder) {
+    if (!mWorkerRef) {
       // The worker is already gone.
       return;
     }
 
     RefPtr<CreateImageBitmapFromBlobRunnable> r =
-      new CreateImageBitmapFromBlobRunnable(mWorkerHolder->GetWorkerPrivate(),
+      new CreateImageBitmapFromBlobRunnable(mWorkerRef->Private(),
                                             this, aImage, aStatus);
     r->Dispatch();
     return;
   }
 
   DecodeAndCropBlobCompletedOwningThread(aImage, aStatus);
 }
 
@@ -2363,17 +2332,17 @@ CreateImageBitmapFromBlob::DecodeAndCrop
   if (!mPromise) {
     // The worker is going to be released soon. No needs to continue.
     return;
   }
 
   // Let's release what has to be released on the owning thread.
   auto raii = MakeScopeExit([&] {
     // Doing this we also release the worker.
-    mWorkerHolder = nullptr;
+    mWorkerRef = nullptr;
 
     mPromise = nullptr;
     mGlobalObject = nullptr;
   });
 
   if (NS_WARN_IF(NS_FAILED(aStatus))) {
     mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
@@ -2404,15 +2373,15 @@ CreateImageBitmapFromBlob::DecodeAndCrop
 void
 CreateImageBitmapFromBlob::WorkerShuttingDown()
 {
   MOZ_ASSERT(IsCurrentThread());
 
   MutexAutoLock lock(mMutex);
 
   // Let's release all the non-thread-safe objects now.
-  mWorkerHolder = nullptr;
+  mWorkerRef = nullptr;
   mPromise = nullptr;
   mGlobalObject = nullptr;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -662,88 +662,91 @@ private:
   RefPtr<ConsoleCallData> mCallData;
 };
 
 class ConsoleWorkerRunnable : public WorkerProxyToMainThreadRunnable
                             , public ConsoleRunnable
 {
 public:
   explicit ConsoleWorkerRunnable(Console* aConsole)
-    : WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
-    , mConsole(aConsole)
+    : mConsole(aConsole)
   {}
 
   ~ConsoleWorkerRunnable() override = default;
 
   bool
   Dispatch(JSContext* aCx)
   {
-    mWorkerPrivate->AssertIsOnWorkerThread();
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
 
     if (NS_WARN_IF(!PreDispatch(aCx))) {
-      RunBackOnWorkerThreadForCleanup();
+      RunBackOnWorkerThreadForCleanup(workerPrivate);
       return false;
     }
 
-    if (NS_WARN_IF(!WorkerProxyToMainThreadRunnable::Dispatch())) {
+    if (NS_WARN_IF(!WorkerProxyToMainThreadRunnable::Dispatch(workerPrivate))) {
       // RunBackOnWorkerThreadForCleanup() will be called by
       // WorkerProxyToMainThreadRunnable::Dispatch().
       return false;
     }
 
     return true;
   }
 
 protected:
   void
-  RunOnMainThread() override
+  RunOnMainThread(WorkerPrivate* aWorkerPrivate) override
   {
+    MOZ_ASSERT(aWorkerPrivate);
     AssertIsOnMainThread();
 
     // Walk up to our containing page
-    WorkerPrivate* wp = mWorkerPrivate;
+    WorkerPrivate* wp = aWorkerPrivate;
     while (wp->GetParent()) {
       wp = wp->GetParent();
     }
 
     nsPIDOMWindowInner* window = wp->GetWindow();
     if (!window) {
-      RunWindowless();
+      RunWindowless(aWorkerPrivate);
     } else {
-      RunWithWindow(window);
+      RunWithWindow(aWorkerPrivate, window);
     }
   }
 
   void
-  RunWithWindow(nsPIDOMWindowInner* aWindow)
+  RunWithWindow(WorkerPrivate* aWorkerPrivate, nsPIDOMWindowInner* aWindow)
   {
+    MOZ_ASSERT(aWorkerPrivate);
     AssertIsOnMainThread();
 
     AutoJSAPI jsapi;
     MOZ_ASSERT(aWindow);
 
     RefPtr<nsGlobalWindowInner> win = nsGlobalWindowInner::Cast(aWindow);
     if (NS_WARN_IF(!jsapi.Init(win))) {
       return;
     }
 
     nsPIDOMWindowOuter* outerWindow = aWindow->GetOuterWindow();
     if (NS_WARN_IF(!outerWindow)) {
       return;
     }
 
-    RunConsole(jsapi.cx(), outerWindow, aWindow);
+    RunConsole(jsapi.cx(), aWorkerPrivate, outerWindow, aWindow);
   }
 
   void
-  RunWindowless()
+  RunWindowless(WorkerPrivate* aWorkerPrivate)
   {
+    MOZ_ASSERT(aWorkerPrivate);
     AssertIsOnMainThread();
 
-    WorkerPrivate* wp = mWorkerPrivate;
+    WorkerPrivate* wp = aWorkerPrivate;
     while (wp->GetParent()) {
       wp = wp->GetParent();
     }
 
     MOZ_ASSERT(!wp->GetWindow());
 
     AutoJSAPI jsapi;
     jsapi.Init();
@@ -756,34 +759,36 @@ protected:
     }
 
     // The GetOrCreateSandbox call returns a proxy to the actual sandbox object.
     // We don't need a proxy here.
     global = js::UncheckedUnwrap(global);
 
     JSAutoRealm ar(cx, global);
 
-    RunConsole(cx, nullptr, nullptr);
+    RunConsole(cx, aWorkerPrivate, nullptr, nullptr);
   }
 
   void
-  RunBackOnWorkerThreadForCleanup() override
+  RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override
   {
-    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
     ReleaseData();
     mConsole = nullptr;
   }
 
   // This method is called in the owning thread of the Console object.
   virtual bool
   PreDispatch(JSContext* aCx) = 0;
 
   // This method is called in the main-thread.
   virtual void
-  RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
+  RunConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+             nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) = 0;
 
   // This method is called in the owning thread of the Console object.
   virtual void
   ReleaseData() = 0;
 
   // This must be released on the worker thread.
   RefPtr<Console> mConsole;
@@ -797,17 +802,16 @@ class ConsoleCallDataWorkerRunnable fina
 {
 public:
   ConsoleCallDataWorkerRunnable(Console* aConsole,
                                 ConsoleCallData* aCallData)
     : ConsoleWorkerRunnable(aConsole)
     , mCallData(aCallData)
   {
     MOZ_ASSERT(aCallData);
-    mWorkerPrivate->AssertIsOnWorkerThread();
     mCallData->AssertIsOnOwningThread();
 
     // Marking this CallData as in use.
     mCallData->mStatus = ConsoleCallData::eInUse;
   }
 
 private:
   ~ConsoleCallDataWorkerRunnable() override
@@ -817,41 +821,43 @@ private:
 
   bool
   PreDispatch(JSContext* aCx) override
   {
     return StoreConsoleData(aCx, mCallData);
   }
 
   void
-  RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
+  RunConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+             nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) override
   {
+    MOZ_ASSERT(aWorkerPrivate);
     AssertIsOnMainThread();
 
     // The windows have to run in parallel.
     MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
 
     if (aOuterWindow) {
       mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
     } else {
       ConsoleStackEntry frame;
       if (mCallData->mTopStackFrame) {
         frame = *mCallData->mTopStackFrame;
       }
 
       nsString id = frame.mFilename;
       nsString innerID;
-      if (mWorkerPrivate->IsSharedWorker()) {
+      if (aWorkerPrivate->IsSharedWorker()) {
         innerID = NS_LITERAL_STRING("SharedWorker");
-      } else if (mWorkerPrivate->IsServiceWorker()) {
+      } else if (aWorkerPrivate->IsServiceWorker()) {
         innerID = NS_LITERAL_STRING("ServiceWorker");
         // Use scope as ID so the webconsole can decide if the message should
         // show up per tab
-        CopyASCIItoUTF16(mWorkerPrivate->ServiceWorkerScope(), id);
+        CopyASCIItoUTF16(aWorkerPrivate->ServiceWorkerScope(), id);
       } else {
         innerID = NS_LITERAL_STRING("Worker");
       }
 
       mCallData->SetIDs(id, innerID);
     }
 
     // Now we could have the correct window (if we are not window-less).
@@ -956,17 +962,18 @@ public:
 private:
   bool
   PreDispatch(JSContext* aCx) override
   {
     return StoreProfileData(aCx, mArguments);
   }
 
   void
-  RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
+  RunConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+             nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) override
   {
     AssertIsOnMainThread();
 
     // Now we could have the correct window (if we are not window-less).
     mClonedData.mParent = aInnerWindow;
 
     ProcessProfileData(aCx, mConsole, mName, mAction);
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -14,17 +14,17 @@
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/CryptoBuffer.h"
 #include "mozilla/dom/CryptoKey.h"
 #include "mozilla/dom/KeyAlgorithmProxy.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/dom/WebCryptoTask.h"
 #include "mozilla/dom/WebCryptoThreadPool.h"
-#include "mozilla/dom/WorkerHolder.h"
+#include "mozilla/dom/WorkerRef.h"
 #include "mozilla/dom/WorkerPrivate.h"
 
 // Template taken from security/nss/lib/util/templates.c
 // This (or SGN_EncodeDigestInfo) would ideally be exported
 // by NSS and until that happens we have to keep our own copy.
 const SEC_ASN1Template SGN_DigestInfoTemplate[] = {
     { SEC_ASN1_SEQUENCE,
       0, NULL, sizeof(SGNDigestInfo) },
@@ -130,57 +130,16 @@ public:
   {
     JS_ClearPendingException(mCx);
   }
 
 private:
   JSContext* mCx;
 };
 
-class WebCryptoTask::InternalWorkerHolder final : public WorkerHolder
-{
-  InternalWorkerHolder()
-    : WorkerHolder("WebCryptoTask::InternalWorkerHolder")
-  { }
-
-  ~InternalWorkerHolder()
-  {
-    NS_ASSERT_OWNINGTHREAD(InternalWorkerHolder);
-    // Nothing to do here since the parent destructor releases the
-    // worker automatically.
-  }
-
-public:
-  static already_AddRefed<InternalWorkerHolder>
-  Create()
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-    RefPtr<InternalWorkerHolder> ref = new InternalWorkerHolder();
-    if (NS_WARN_IF(!ref->HoldWorker(workerPrivate, Canceling))) {
-      return nullptr;
-    }
-    return ref.forget();
-  }
-
-  virtual bool
-  Notify(WorkerStatus aStatus) override
-  {
-    NS_ASSERT_OWNINGTHREAD(InternalWorkerHolder);
-    // Do nothing here.  Since WebCryptoTask dispatches back to
-    // the worker thread using nsThread::Dispatch() instead of
-    // WorkerRunnable it will always be able to execute its
-    // runnables.
-    return true;
-  }
-
-  NS_INLINE_DECL_REFCOUNTING(WebCryptoTask::InternalWorkerHolder)
-};
-
 template<class OOS>
 static nsresult
 GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm, nsString& aName)
 {
   ClearException ce(aCx);
 
   if (aAlgorithm.IsString()) {
     // If string, then treat as algorithm name
@@ -380,21 +339,25 @@ WebCryptoTask::DispatchWithPromise(Promi
   // Store calling thread
   mOriginalEventTarget = GetCurrentThreadSerialEventTarget();
 
   // If we are running on a worker thread we must hold the worker
   // alive while we work on the thread pool.  Otherwise the worker
   // private may get torn down before we dispatch back to complete
   // the transaction.
   if (!NS_IsMainThread()) {
-    mWorkerHolder = InternalWorkerHolder::Create();
-    // If we can't register a holder then the worker is already
-    // shutting down.  Don't start new work.
-    if (!mWorkerHolder) {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+
+    RefPtr<StrongWorkerRef> workerRef =
+      StrongWorkerRef::Create(workerPrivate, "WebCryptoTask");
+    if (NS_WARN_IF(!workerRef)) {
       mEarlyRv = NS_BINDING_ABORTED;
+    } else {
+      mWorkerRef = new ThreadSafeWorkerRef(workerRef);
     }
   }
   MAYBE_EARLY_FAIL(mEarlyRv);
 
   // dispatch to thread pool
   mEarlyRv = WebCryptoThreadPool::Dispatch(this);
   MAYBE_EARLY_FAIL(mEarlyRv)
 }
@@ -411,17 +374,17 @@ WebCryptoTask::Run()
     return NS_OK;
   }
 
   // We're now back on the calling thread.
   CallCallback(mRv);
 
   // Stop holding the worker thread alive now that the async work has
   // been completed.
-  mWorkerHolder = nullptr;
+  mWorkerRef = nullptr;
 
   return NS_OK;
 }
 
 nsresult
 WebCryptoTask::Cancel()
 {
   MOZ_ASSERT(IsOnOriginalThread());
@@ -435,17 +398,17 @@ WebCryptoTask::FailWithError(nsresult aR
   MOZ_ASSERT(IsOnOriginalThread());
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
 
   // Blindly convert nsresult to DOMException
   // Individual tasks must ensure they pass the right values
   mResultPromise->MaybeReject(aRv);
   // Manually release mResultPromise while we're on the main thread
   mResultPromise = nullptr;
-  mWorkerHolder = nullptr;
+  mWorkerRef = nullptr;
   Cleanup();
 }
 
 nsresult
 WebCryptoTask::CalculateResult()
 {
   MOZ_ASSERT(!IsOnOriginalThread());
 
@@ -3670,19 +3633,12 @@ WebCryptoTask::WebCryptoTask()
   : CancelableRunnable("WebCryptoTask")
   , mEarlyRv(NS_OK)
   , mEarlyComplete(false)
   , mOriginalEventTarget(nullptr)
   , mRv(NS_ERROR_NOT_INITIALIZED)
 {
 }
 
-WebCryptoTask::~WebCryptoTask()
-{
-  if (mWorkerHolder) {
-    NS_ProxyRelease(
-      "WebCryptoTask::mWorkerHolder",
-      mOriginalEventTarget, mWorkerHolder.forget());
-  }
-}
+WebCryptoTask::~WebCryptoTask() = default;
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/crypto/WebCryptoTask.h
+++ b/dom/crypto/WebCryptoTask.h
@@ -12,16 +12,18 @@
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/SubtleCryptoBinding.h"
 #include "nsIGlobalObject.h"
 
 namespace mozilla {
 namespace dom {
 
+class ThreadSafeWorkerRef;
+
 typedef ArrayBufferViewOrArrayBuffer CryptoOperationData;
 typedef ArrayBufferViewOrArrayBuffer KeyData;
 
 /*
 
 The execution of a WebCryptoTask happens in several phases
 
 1. Constructor
@@ -182,20 +184,18 @@ protected:
   nsresult CalculateResult();
 
   void CallCallback(nsresult rv);
 
 private:
   NS_IMETHOD Run() final;
   nsresult Cancel() final;
 
-  class InternalWorkerHolder;
-
   nsCOMPtr<nsISerialEventTarget> mOriginalEventTarget;
-  RefPtr<InternalWorkerHolder> mWorkerHolder;
+  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
   nsresult mRv;
 };
 
 // XXX This class is declared here (unlike others) to enable reuse by WebRTC.
 class GenerateAsymmetricKeyTask : public WebCryptoTask
 {
 public:
   GenerateAsymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -10,19 +10,20 @@
 #include "IDBDatabase.h"
 #include "IDBEvents.h"
 #include "IDBObjectStore.h"
 #include "IDBRequest.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMStringList.h"
-#include "mozilla/dom/WorkerHolder.h"
+#include "mozilla/dom/WorkerRef.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ScopeExit.h"
 #include "nsAutoPtr.h"
 #include "nsPIDOMWindow.h"
 #include "nsQueryObject.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTHashtable.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
 
@@ -30,50 +31,16 @@
 #include "ActorsChild.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::ipc;
 
-class IDBTransaction::WorkerHolder final : public mozilla::dom::WorkerHolder
-{
-  WorkerPrivate* mWorkerPrivate;
-
-  // The IDBTransaction owns this object so we only need a weak reference back
-  // to it.
-  IDBTransaction* mTransaction;
-
-public:
-  WorkerHolder(WorkerPrivate* aWorkerPrivate, IDBTransaction* aTransaction)
-    : mozilla::dom::WorkerHolder("IDBTransaction::WorkerHolder")
-    , mWorkerPrivate(aWorkerPrivate)
-    , mTransaction(aTransaction)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    MOZ_ASSERT(aTransaction);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-    aTransaction->AssertIsOnOwningThread();
-
-    MOZ_COUNT_CTOR(IDBTransaction::WorkerHolder);
-  }
-
-  ~WorkerHolder()
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-
-    MOZ_COUNT_DTOR(IDBTransaction::WorkerHolder);
-  }
-
-private:
-  virtual bool
-  Notify(WorkerStatus aStatus) override;
-};
-
 IDBTransaction::IDBTransaction(IDBDatabase* aDatabase,
                                const nsTArray<nsString>& aObjectStoreNames,
                                Mode aMode)
   : IDBWrapperCache(aDatabase)
   , mDatabase(aDatabase)
   , mObjectStoreNames(aObjectStoreNames)
   , mLoggingSerialNumber(0)
   , mNextObjectStoreId(0)
@@ -226,28 +193,36 @@ IDBTransaction::Create(JSContext* aCx, I
   transaction->SetScriptOwner(aDatabase->GetScriptOwner());
 
   if (!NS_IsMainThread()) {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
 
     workerPrivate->AssertIsOnWorkerThread();
 
-    nsAutoPtr<WorkerHolder> workerHolder(
-      new WorkerHolder(workerPrivate, transaction));
-    if (NS_WARN_IF(!workerHolder->HoldWorker(workerPrivate, Canceling))) {
+    RefPtr<StrongWorkerRef> workerRef =
+      StrongWorkerRef::Create(workerPrivate, "IDBTransaction",
+                              [transaction]() {
+        transaction->AssertIsOnOwningThread();
+        if (!transaction->IsCommittingOrDone()) {
+          IDB_REPORT_INTERNAL_ERR();
+          transaction->AbortInternal(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
+                                     nullptr);
+        }
+      });
+    if (NS_WARN_IF(!workerRef)) {
       // Silence the destructor assertion if we never made this object live.
 #ifdef DEBUG
       MOZ_ASSERT(!transaction->mSentCommitOrAbort);
       transaction->mSentCommitOrAbort = true;
 #endif
       return nullptr;
     }
 
-    transaction->mWorkerHolder = std::move(workerHolder);
+    transaction->mWorkerRef = std::move(workerRef);
   }
 
   nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
   nsContentUtils::AddPendingIDBTransaction(runnable.forget());
 
   transaction->mCreating = true;
 
   aDatabase->RegisterTransaction(transaction);
@@ -815,18 +790,20 @@ IDBTransaction::FireCompleteOrAbortEvent
   MOZ_ASSERT(!mFiredCompleteOrAbort);
 
   mReadyState = DONE;
 
 #ifdef DEBUG
   mFiredCompleteOrAbort = true;
 #endif
 
-  // Make sure we drop the WorkerHolder when this function completes.
-  nsAutoPtr<WorkerHolder> workerHolder = std::move(mWorkerHolder);
+  // Make sure we drop the WorkerRef when this function completes.
+  auto scopeExit = MakeScopeExit([&] {
+    mWorkerRef = nullptr;
+  });
 
   RefPtr<Event> event;
   if (NS_SUCCEEDED(aResult)) {
     event = CreateGenericEvent(this,
                                nsDependentString(kCompleteEventType),
                                eDoesNotBubble,
                                eNotCancelable);
     MOZ_ASSERT(event);
@@ -1065,32 +1042,10 @@ IDBTransaction::Run()
     mReadyState = DONE;
 
     SendCommit();
   }
 
   return NS_OK;
 }
 
-bool
-IDBTransaction::
-WorkerHolder::Notify(WorkerStatus aStatus)
-{
-  MOZ_ASSERT(mWorkerPrivate);
-  mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(aStatus > Running);
-
-  if (mTransaction && aStatus > Terminating) {
-    mTransaction->AssertIsOnOwningThread();
-
-    RefPtr<IDBTransaction> transaction = std::move(mTransaction);
-
-    if (!transaction->IsCommittingOrDone()) {
-      IDB_REPORT_INTERNAL_ERR();
-      transaction->AbortInternal(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, nullptr);
-    }
-  }
-
-  return true;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -26,16 +26,17 @@ class EventChainPreVisitor;
 namespace dom {
 
 class DOMException;
 class DOMStringList;
 class IDBDatabase;
 class IDBObjectStore;
 class IDBOpenDBRequest;
 class IDBRequest;
+class StrongWorkerRef;
 
 namespace indexedDB {
 class BackgroundCursorChild;
 class BackgroundRequestChild;
 class BackgroundTransactionChild;
 class BackgroundVersionChangeTransactionChild;
 class IndexMetadata;
 class ObjectStoreSpec;
@@ -75,17 +76,17 @@ public:
   };
 
 private:
   RefPtr<IDBDatabase> mDatabase;
   RefPtr<DOMException> mError;
   nsTArray<nsString> mObjectStoreNames;
   nsTArray<RefPtr<IDBObjectStore>> mObjectStores;
   nsTArray<RefPtr<IDBObjectStore>> mDeletedObjectStores;
-  nsAutoPtr<WorkerHolder> mWorkerHolder;
+  RefPtr<StrongWorkerRef> mWorkerRef;
 
   // Tagged with mMode. If mMode is VERSION_CHANGE then mBackgroundActor will be
   // a BackgroundVersionChangeTransactionChild. Otherwise it will be a
   // BackgroundTransactionChild.
   union {
     indexedDB::BackgroundTransactionChild* mNormalBackgroundActor;
     indexedDB::BackgroundVersionChangeTransactionChild* mVersionChangeBackgroundActor;
   } mBackgroundActor;
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -27,25 +27,27 @@ using namespace mozilla;
 using namespace mozilla::gl;
 using namespace mozilla::java::sdk;
 using media::TimeUnit;
 
 namespace mozilla {
 
 mozilla::LazyLogModule sAndroidDecoderModuleLog("AndroidDecoderModule");
 
-const char*
+const nsCString
 TranslateMimeType(const nsACString& aMimeType)
 {
   if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP8)) {
-    return "video/x-vnd.on2.vp8";
+    static NS_NAMED_LITERAL_CSTRING(vp8, "video/x-vnd.on2.vp8");
+    return vp8;
   } else if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP9)) {
-    return "video/x-vnd.on2.vp9";
+    static NS_NAMED_LITERAL_CSTRING(vp9, "video/x-vnd.on2.vp9");
+    return vp9;
   }
-  return PromiseFlatCString(aMimeType).get();
+  return nsCString(aMimeType);
 }
 
 static bool
 GetFeatureStatus(int32_t aFeature)
 {
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
   nsCString discardFailureId;
@@ -106,17 +108,17 @@ AndroidDecoderModule::SupportsMimeType(
   // Prefer the gecko decoder for Theora.
   // Not all android devices support Theora even when they say they do.
   if (TheoraDecoder::IsTheora(aMimeType)) {
     LOG("Rejecting video of type %s", aMimeType.Data());
     return false;
   }
 
   return java::HardwareCodecCapabilityUtils::FindDecoderCodecInfoForMimeType(
-    nsCString(TranslateMimeType(aMimeType)));
+    TranslateMimeType(aMimeType));
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
 {
   // Temporary - forces use of VPXDecoder when alpha is present.
   // Bug 1263836 will handle alpha scenario once implemented. It will shift
   // the check for alpha to PDMFactory but not itself remove the need for a
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -26,14 +26,14 @@ public:
 
 private:
   virtual ~AndroidDecoderModule() { }
   RefPtr<MediaDrmCDMProxy> mProxy;
 };
 
 extern LazyLogModule sAndroidDecoderModuleLog;
 
-const char*
+const nsCString
 TranslateMimeType(const nsACString& aMimeType);
 
 } // namespace mozilla
 
 #endif
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -53,16 +53,17 @@
 #include "mozilla/dom/ServiceWorkerEvents.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "SharedWorker.h"
 #include "WorkerDebugger.h"
 #include "WorkerDebuggerManager.h"
 #include "WorkerError.h"
 #include "WorkerEventTarget.h"
 #include "WorkerNavigator.h"
+#include "WorkerRef.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 #include "WorkerThread.h"
 
 #include "nsThreadManager.h"
 
 #ifdef XP_WIN
 #undef PostMessage
@@ -963,26 +964,16 @@ PRThreadFromThread(nsIThread* aThread)
 
   PRThread* result;
   MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
   MOZ_ASSERT(result);
 
   return result;
 }
 
-class SimpleWorkerHolder final : public WorkerHolder
-{
-public:
-  SimpleWorkerHolder()
-    : WorkerHolder("SimpleWorkerHolder")
-  {}
-
-  virtual bool Notify(WorkerStatus aStatus) override { return true; }
-};
-
 // A runnable to cancel the worker from the parent thread when self.close() is
 // called. This runnable is executed on the parent process in order to cancel
 // the current runnable. It uses a normal WorkerRunnable in order to be sure
 // that all the pending WorkerRunnables are executed before this.
 class CancelingOnParentRunnable final : public WorkerRunnable
 {
 public:
   explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
@@ -2790,28 +2781,29 @@ WorkerPrivate::~WorkerPrivate()
 already_AddRefed<WorkerPrivate>
 WorkerPrivate::Constructor(JSContext* aCx,
                            const nsAString& aScriptURL,
                            bool aIsChromeWorker, WorkerType aWorkerType,
                            const nsAString& aWorkerName,
                            const nsACString& aServiceWorkerScope,
                            WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
 {
-  // If this is a sub-worker, we need to keep the parent worker alive until this
-  // one is registered.
-  UniquePtr<SimpleWorkerHolder> holder;
-
   WorkerPrivate* parent = NS_IsMainThread() ?
                           nullptr :
                           GetCurrentThreadWorkerPrivate();
+
+  // If this is a sub-worker, we need to keep the parent worker alive until this
+  // one is registered.
+  RefPtr<StrongWorkerRef> workerRef;
   if (parent) {
     parent->AssertIsOnWorkerThread();
 
-    holder.reset(new SimpleWorkerHolder());
-    if (!holder->HoldWorker(parent, Canceling)) {
+    workerRef =
+      StrongWorkerRef::Create(parent, "WorkerPrivate::Constructor");
+    if (NS_WARN_IF(!workerRef)) {
       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
       return nullptr;
     }
   } else {
     AssertIsOnMainThread();
   }
 
   Maybe<WorkerLoadInfo> stackLoadInfo;
--- a/dom/workers/WorkerRef.h
+++ b/dom/workers/WorkerRef.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_WorkerRef_h
 #define mozilla_dom_workers_WorkerRef_h
 
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/dom/WorkerHolder.h"
 #include "mozilla/UniquePtr.h"
+#include <functional>
 
 namespace mozilla {
 namespace dom {
 
 /*
  * If you want to play with a DOM Worker, you must know that it can go away
  * at any time if nothing prevents its shutting down. This documentation helps
  * to understand how to play with DOM Workers correctly.
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -645,52 +645,52 @@ WorkerSameThreadRunnable::PostDispatch(W
   if (aDispatchResult) {
     DebugOnly<bool> willIncrement = aWorkerPrivate->ModifyBusyCountFromWorker(true);
     // Should never fail since if this thread is still running, so should the
     // parent and it should be able to process a control runnable.
     MOZ_ASSERT(willIncrement);
   }
 }
 
-WorkerProxyToMainThreadRunnable::WorkerProxyToMainThreadRunnable(
-  WorkerPrivate* aWorkerPrivate)
+WorkerProxyToMainThreadRunnable::WorkerProxyToMainThreadRunnable()
   : mozilla::Runnable("dom::WorkerProxyToMainThreadRunnable")
-  , mWorkerPrivate(aWorkerPrivate)
-{
-  MOZ_ASSERT(mWorkerPrivate);
-  mWorkerPrivate->AssertIsOnWorkerThread();
-}
-
-WorkerProxyToMainThreadRunnable::~WorkerProxyToMainThreadRunnable()
 {}
 
+WorkerProxyToMainThreadRunnable::~WorkerProxyToMainThreadRunnable() = default;
+
 bool
-WorkerProxyToMainThreadRunnable::Dispatch()
+WorkerProxyToMainThreadRunnable::Dispatch(WorkerPrivate* aWorkerPrivate)
 {
-  mWorkerPrivate->AssertIsOnWorkerThread();
+  MOZ_ASSERT(aWorkerPrivate);
+  aWorkerPrivate->AssertIsOnWorkerThread();
 
-  if (NS_WARN_IF(!HoldWorker())) {
-    RunBackOnWorkerThreadForCleanup();
+  RefPtr<StrongWorkerRef> workerRef =
+    StrongWorkerRef::Create(aWorkerPrivate, "WorkerProxyToMainThreadRunnable");
+  if (NS_WARN_IF(!workerRef)) {
+    RunBackOnWorkerThreadForCleanup(aWorkerPrivate);
     return false;
   }
 
-  if (NS_WARN_IF(NS_FAILED(mWorkerPrivate->DispatchToMainThread(this)))) {
+  MOZ_ASSERT(!mWorkerRef);
+  mWorkerRef = new ThreadSafeWorkerRef(workerRef);
+
+  if (NS_WARN_IF(NS_FAILED(aWorkerPrivate->DispatchToMainThread(this)))) {
     ReleaseWorker();
-    RunBackOnWorkerThreadForCleanup();
+    RunBackOnWorkerThreadForCleanup(aWorkerPrivate);
     return false;
   }
 
   return true;
 }
 
 NS_IMETHODIMP
 WorkerProxyToMainThreadRunnable::Run()
 {
   AssertIsOnMainThread();
-  RunOnMainThread();
+  RunOnMainThread(mWorkerRef->Private());
   PostDispatchOnMainThread();
   return NS_OK;
 }
 
 void
 WorkerProxyToMainThreadRunnable::PostDispatchOnMainThread()
 {
   class ReleaseRunnable final : public MainThreadWorkerControlRunnable
@@ -717,68 +717,36 @@ WorkerProxyToMainThreadRunnable::PostDis
 
     virtual bool
     WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
     {
       MOZ_ASSERT(aWorkerPrivate);
       aWorkerPrivate->AssertIsOnWorkerThread();
 
       if (mRunnable) {
-        mRunnable->RunBackOnWorkerThreadForCleanup();
+        mRunnable->RunBackOnWorkerThreadForCleanup(aWorkerPrivate);
 
         // Let's release the worker thread.
         mRunnable->ReleaseWorker();
         mRunnable = nullptr;
       }
 
       return true;
     }
 
   private:
     ~ReleaseRunnable()
     {}
   };
 
   RefPtr<WorkerControlRunnable> runnable =
-    new ReleaseRunnable(mWorkerPrivate, this);
+    new ReleaseRunnable(mWorkerRef->Private(), this);
   Unused << NS_WARN_IF(!runnable->Dispatch());
 }
 
-bool
-WorkerProxyToMainThreadRunnable::HoldWorker()
-{
-  mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(!mWorkerHolder);
-
-  class SimpleWorkerHolder final : public WorkerHolder
-  {
-  public:
-    SimpleWorkerHolder()
-      : WorkerHolder("WorkerProxyToMainThreadRunnable::SimpleWorkerHolder")
-    {}
-
-    bool Notify(WorkerStatus aStatus) override
-    {
-      // We don't care about the notification. We just want to keep the
-      // mWorkerPrivate alive.
-      return true;
-    }
-  };
-
-  UniquePtr<WorkerHolder> workerHolder(new SimpleWorkerHolder());
-  if (NS_WARN_IF(!workerHolder->HoldWorker(mWorkerPrivate, Canceling))) {
-    return false;
-  }
-
-  mWorkerHolder = std::move(workerHolder);
-  return true;
-}
-
 void
 WorkerProxyToMainThreadRunnable::ReleaseWorker()
 {
-  mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(mWorkerHolder);
-  mWorkerHolder = nullptr;
+  mWorkerRef = nullptr;
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/workers/WorkerRunnable.h
+++ b/dom/workers/WorkerRunnable.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_workerrunnable_h__
 #define mozilla_dom_workers_workerrunnable_h__
 
 #include "mozilla/dom/WorkerCommon.h"
-#include "mozilla/dom/WorkerHolder.h"
+#include "mozilla/dom/WorkerRef.h"
 
 #include "nsICancelableRunnable.h"
 
 #include "mozilla/Atomics.h"
 #include "nsISupportsImpl.h"
 #include "nsThreadUtils.h" /* nsRunnable */
 
 struct JSContext;
@@ -426,40 +426,38 @@ private:
 // says, only to release resources, no JS has to be executed, no timers, or
 // other things. The reason of such limitations is that, in order to execute
 // this method in any condition (also when the worker is shutting down), a
 // Control Runnable is used, and, this could generate a reordering of existing
 // runnables.
 class WorkerProxyToMainThreadRunnable : public Runnable
 {
 protected:
-  explicit WorkerProxyToMainThreadRunnable(WorkerPrivate* aWorkerPrivate);
+  WorkerProxyToMainThreadRunnable();
 
   virtual ~WorkerProxyToMainThreadRunnable();
 
   // First this method is called on the main-thread.
-  virtual void RunOnMainThread() = 0;
+  virtual void RunOnMainThread(WorkerPrivate* aWorkerPrivate) = 0;
 
   // After this second method is called on the worker-thread.
-  virtual void RunBackOnWorkerThreadForCleanup() = 0;
+  virtual void
+  RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) = 0;
 
 public:
-  bool Dispatch();
+  bool Dispatch(WorkerPrivate* aWorkerPrivate);
 
 private:
   NS_IMETHOD Run() override;
 
   void PostDispatchOnMainThread();
 
-  bool HoldWorker();
   void ReleaseWorker();
 
-protected:
-  WorkerPrivate* mWorkerPrivate;
-  UniquePtr<WorkerHolder> mWorkerHolder;
+  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
 };
 
 // This runnable is used to stop a sync loop and it's meant to be used on the
 // main-thread only. As sync loops keep the busy count incremented as long as
 // they run this runnable does not modify the busy count
 // in any way.
 class MainThreadStopSyncLoopRunnable : public WorkerSyncRunnable
 {
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FormData.h"
 #include "mozilla/dom/ProgressEvent.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/UnionConversions.h"
 #include "mozilla/dom/URLSearchParams.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRef.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/Telemetry.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 
 #include "XMLHttpRequestUpload.h"
@@ -1522,33 +1523,37 @@ SendRunnable::RunOnMainThread(ErrorResul
           !mProxy->AddRemoveEventListeners(true, true)) {
         MOZ_ASSERT(false, "This should never fail!");
       }
     }
   }
 }
 
 XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate)
-: WorkerHolder("XMLHttpRequestWorker"), mWorkerPrivate(aWorkerPrivate),
-  mResponseType(XMLHttpRequestResponseType::Text), mTimeout(0),
-  mRooted(false), mBackgroundRequest(false), mWithCredentials(false),
-  mCanceled(false), mMozAnon(false), mMozSystem(false)
+  : mWorkerPrivate(aWorkerPrivate)
+  , mResponseType(XMLHttpRequestResponseType::Text)
+  , mTimeout(0)
+  , mBackgroundRequest(false)
+  , mWithCredentials(false)
+  , mCanceled(false)
+  , mMozAnon(false)
+  , mMozSystem(false)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   mozilla::HoldJSObjects(this);
 }
 
 XMLHttpRequestWorker::~XMLHttpRequestWorker()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   ReleaseProxy(XHRIsGoingAway);
 
-  MOZ_ASSERT(!mRooted);
+  MOZ_ASSERT(!mWorkerRef);
 
   mozilla::DropJSObjects(this);
 }
 
 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget)
 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestWorker)
@@ -1633,28 +1638,35 @@ XMLHttpRequestWorker::ReleaseProxy(Relea
   }
 }
 
 void
 XMLHttpRequestWorker::MaybePin(ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
-  if (mRooted) {
+  if (mWorkerRef) {
     return;
   }
 
-  if (!HoldWorker(mWorkerPrivate, Canceling)) {
+  RefPtr<XMLHttpRequestWorker> self = this;
+  mWorkerRef =
+    StrongWorkerRef::Create(mWorkerPrivate, "XMLHttpRequestWorker",
+                            [self]() {
+      if (!self->mCanceled) {
+        self->mCanceled = true;
+        self->ReleaseProxy(WorkerIsGoingAway);
+      }
+    });
+  if (NS_WARN_IF(!mWorkerRef)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   NS_ADDREF_THIS();
-
-  mRooted = true;
 }
 
 void
 XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(mProxy);
 
@@ -1755,21 +1767,18 @@ XMLHttpRequestWorker::DispatchPrematureA
   aTarget->DispatchEvent(*event);
 }
 
 void
 XMLHttpRequestWorker::Unpin()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
-  MOZ_ASSERT(mRooted, "Mismatched calls to Unpin!");
-
-  ReleaseWorker();
-
-  mRooted = false;
+  MOZ_ASSERT(mWorkerRef, "Mismatched calls to Unpin!");
+  mWorkerRef = nullptr;
 
   NS_RELEASE_THIS();
 }
 
 void
 XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable,
                                    ErrorResult& aRv)
 {
@@ -1809,17 +1818,17 @@ XMLHttpRequestWorker::SendInternal(SendR
   aRunnable->SetHaveUploadListeners(hasUploadListeners);
 
   mStateData.mFlagSend = true;
 
   aRunnable->Dispatch(Terminating, aRv);
   if (aRv.Failed()) {
     // Dispatch() may have spun the event loop and we may have already unrooted.
     // If so we don't want autoUnpin to try again.
-    if (!mRooted) {
+    if (!mWorkerRef) {
       autoUnpin.Clear();
     }
     return;
   }
 
   if (!isSyncXHR)  {
     autoUnpin.Clear();
     MOZ_ASSERT(!autoSyncLoop);
@@ -1836,29 +1845,16 @@ XMLHttpRequestWorker::SendInternal(SendR
   // that this autoSyncLoop->Run() can never fail, since the StopSyncLoop call
   // for it will come from ProxyCompleteRunnable and that always passes true for
   // the second arg.
   if (!succeeded && !aRv.Failed()) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
 }
 
-bool
-XMLHttpRequestWorker::Notify(WorkerStatus aStatus)
-{
-  mWorkerPrivate->AssertIsOnWorkerThread();
-
-  if (aStatus >= Canceling && !mCanceled) {
-    mCanceled = true;
-    ReleaseProxy(WorkerIsGoingAway);
-  }
-
-  return true;
-}
-
 void
 XMLHttpRequestWorker::Open(const nsACString& aMethod,
                            const nsAString& aUrl, bool aAsync,
                            const Optional<nsAString>& aUser,
                            const Optional<nsAString>& aPassword,
                            ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
--- a/dom/xhr/XMLHttpRequestWorker.h
+++ b/dom/xhr/XMLHttpRequestWorker.h
@@ -5,28 +5,27 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_XMLHttpRequestWorker_h
 #define mozilla_dom_XMLHttpRequestWorker_h
 
 #include "XMLHttpRequest.h"
 #include "XMLHttpRequestString.h"
 #include "mozilla/dom/TypedArray.h"
-#include "mozilla/dom/WorkerHolder.h"
 
 namespace mozilla {
 namespace dom {
 
 class Proxy;
+class DOMString;
 class SendRunnable;
-class DOMString;
+class StrongWorkerRef;
 class WorkerPrivate;
 
-class XMLHttpRequestWorker final : public XMLHttpRequest,
-                                   public WorkerHolder
+class XMLHttpRequestWorker final : public XMLHttpRequest
 {
 public:
   struct StateData
   {
     XMLHttpRequestStringSnapshot mResponseText;
     nsString mResponseURL;
     uint32_t mStatus;
     nsCString mStatusText;
@@ -44,23 +43,23 @@ public:
     { }
 
     void trace(JSTracer* trc);
   };
 
 private:
   RefPtr<XMLHttpRequestUpload> mUpload;
   WorkerPrivate* mWorkerPrivate;
+  RefPtr<StrongWorkerRef> mWorkerRef;
   RefPtr<Proxy> mProxy;
   XMLHttpRequestResponseType mResponseType;
   StateData mStateData;
 
   uint32_t mTimeout;
 
-  bool mRooted;
   bool mBackgroundRequest;
   bool mWithCredentials;
   bool mCanceled;
 
   bool mMozAnon;
   bool mMozSystem;
 
 public:
@@ -72,19 +71,16 @@ public:
   static already_AddRefed<XMLHttpRequest>
   Construct(const GlobalObject& aGlobal,
             const MozXMLHttpRequestParameters& aParams,
             ErrorResult& aRv);
 
   void
   Unpin();
 
-  bool
-  Notify(WorkerStatus aStatus) override;
-
   virtual uint16_t
   ReadyState() const override
   {
     return mStateData.mReadyState;
   }
 
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl,
@@ -261,17 +257,17 @@ public:
   virtual bool MozSystem() const override
   {
     return mMozSystem;
   }
 
   bool
   SendInProgress() const
   {
-    return mRooted;
+    return !!mWorkerRef;
   }
 
 private:
   explicit XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate);
   ~XMLHttpRequestWorker();
 
   enum ReleaseType { Default, XHRIsGoingAway, WorkerIsGoingAway };
 
--- a/gfx/src/nsColor.cpp
+++ b/gfx/src/nsColor.cpp
@@ -211,24 +211,16 @@ bool NS_ColorNameToRGB(const nsAString& 
     if (aResult) {
       *aResult = kColors[id];
     }
     return true;
   }
   return false;
 }
 
-// Returns kColorNames, an array of all possible color names, and sets
-// *aSizeArray to the size of that array. Do NOT call free() on this array.
-const char * const * NS_AllColorNames(size_t *aSizeArray)
-{
-  *aSizeArray = ArrayLength(kColorNames);
-  return kColorNames;
-}
-
 // Macro to blend two colors
 //
 // equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255
 #define MOZ_BLEND(target, bg, fg, fgalpha)       \
   FAST_DIVIDE_BY_255(target, (bg)*(255-fgalpha) + (fg)*(fgalpha))
 
 nscolor
 NS_ComposeColors(nscolor aBG, nscolor aFG)
--- a/gfx/src/nsColor.h
+++ b/gfx/src/nsColor.h
@@ -95,20 +95,16 @@ bool NS_LooseHexToRGB(const nsString& aB
 
 // There is no function to translate a color to a hex string, because
 // the hex-string syntax does not support transparency.
 
 // Translate a color name to a color. Return true if it parses ok,
 // otherwise return false.
 bool NS_ColorNameToRGB(const nsAString& aBuf, nscolor* aResult);
 
-// Returns an array of all possible color names, and sets
-// *aSizeArray to the size of that array. Do NOT call |free()| on this array.
-const char * const * NS_AllColorNames(size_t *aSizeArray);
-
 // function to convert from HSL color space to RGB color space
 // the float parameters are all expected to be in the range 0-1
 nscolor NS_HSL2RGB(float h, float s, float l);
 
 // Return a color name for the given nscolor.  If there is no color
 // name for it, returns null.  If there are multiple possible color
 // names for the given color, the first one in nsColorNameList.h
 // (which is generally the first one in alphabetical order) will be
--- a/ipc/glue/IPCStreamSource.cpp
+++ b/ipc/glue/IPCStreamSource.cpp
@@ -9,18 +9,16 @@
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "nsIAsyncInputStream.h"
 #include "nsICancelableRunnable.h"
 #include "nsIRunnable.h"
 #include "nsISerialEventTarget.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 
-using mozilla::dom::WorkerPrivate;
-using mozilla::dom::WorkerStatus;
 using mozilla::wr::ByteBuffer;
 
 namespace mozilla {
 namespace ipc {
 
 class IPCStreamSource::Callback final : public nsIInputStreamCallback
                                       , public nsIRunnable
                                       , public nsICancelableRunnable
@@ -38,17 +36,17 @@ public:
   {
     // any thread
     if (mOwningEventTarget->IsOnCurrentThread()) {
       return Run();
     }
 
     // If this fails, then it means the owning thread is a Worker that has
     // been shutdown.  Its ok to lose the event in this case because the
-    // IPCStreamChild listens for this event through the WorkerHolder.
+    // IPCStreamChild listens for this event through the WorkerRef.
     nsresult rv = mOwningEventTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch stream readable event to owning thread");
     }
 
     return NS_OK;
   }
 
@@ -62,17 +60,17 @@ public:
     return NS_OK;
   }
 
   nsresult
   Cancel() override
   {
     // Cancel() gets called when the Worker thread is being shutdown.  We have
     // nothing to do here because IPCStreamChild handles this case via
-    // the WorkerHolder.
+    // the WorkerRef.
     return NS_OK;
   }
 
   void
   ClearSource()
   {
     MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
     MOZ_ASSERT(mSource);
@@ -98,99 +96,86 @@ private:
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(IPCStreamSource::Callback, nsIInputStreamCallback,
                                              nsIRunnable,
                                              nsICancelableRunnable);
 
 IPCStreamSource::IPCStreamSource(nsIAsyncInputStream* aInputStream)
-  : WorkerHolder("IPCStreamSource")
-  , mStream(aInputStream)
-  , mWorkerPrivate(nullptr)
+  : mStream(aInputStream)
   , mState(ePending)
 {
   MOZ_ASSERT(aInputStream);
 }
 
 IPCStreamSource::~IPCStreamSource()
 {
   NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
   MOZ_ASSERT(mState == eClosed);
   MOZ_ASSERT(!mCallback);
-  MOZ_ASSERT(!mWorkerPrivate);
+  MOZ_ASSERT(!mWorkerRef);
 }
 
 bool
 IPCStreamSource::Initialize()
 {
   bool nonBlocking = false;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mStream->IsNonBlocking(&nonBlocking)));
   // IPCStreamChild reads in the current thread, so it is only supported on
   // non-blocking, async channels
   if (!nonBlocking) {
     return false;
   }
 
   // A source can be used on any thread, but we only support IPCStream on
   // main thread, Workers and PBackground thread right now.  This is due
   // to the requirement  that the thread be guaranteed to live long enough to
-  // receive messages. We can enforce this guarantee with a WorkerHolder on
+  // receive messages. We can enforce this guarantee with a StrongWorkerRef on
   // worker threads, but not other threads. Main-thread and PBackground thread
   // do not need anything special in order to be kept alive.
-  WorkerPrivate* workerPrivate = nullptr;
   if (!NS_IsMainThread()) {
-    workerPrivate = mozilla::dom::GetCurrentThreadWorkerPrivate();
+    mozilla::dom::WorkerPrivate* workerPrivate =
+      mozilla::dom::GetCurrentThreadWorkerPrivate();
     if (workerPrivate) {
-      bool result = HoldWorker(workerPrivate, WorkerStatus::Canceling);
-      if (!result) {
+      RefPtr<mozilla::dom::StrongWorkerRef> workerRef =
+        mozilla::dom::StrongWorkerRef::Create(workerPrivate, "IPCStreamSource");
+      if (NS_WARN_IF(!workerRef)) {
         return false;
       }
 
-      mWorkerPrivate = workerPrivate;
+      mWorkerRef = std::move(workerRef);
     } else {
       AssertIsOnBackgroundThread();
     }
   }
 
   return true;
 }
 
 void
 IPCStreamSource::ActorConstructed()
 {
   MOZ_ASSERT(mState == ePending);
   mState = eActorConstructed;
 }
 
-bool
-IPCStreamSource::Notify(WorkerStatus aStatus)
-{
-  NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
-
-  // Keep the worker thread alive until the stream is finished.
-  return true;
-}
-
 void
 IPCStreamSource::ActorDestroyed()
 {
   NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
 
   mState = eClosed;
 
   if (mCallback) {
     mCallback->ClearSource();
     mCallback = nullptr;
   }
 
-  if (mWorkerPrivate) {
-    ReleaseWorker();
-    mWorkerPrivate = nullptr;
-  }
+  mWorkerRef = nullptr;
 }
 
 void
 IPCStreamSource::Start()
 {
   NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
   DoRead();
 }
--- a/ipc/glue/IPCStreamSource.h
+++ b/ipc/glue/IPCStreamSource.h
@@ -3,18 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ipc_IPCStreamSource_h
 #define mozilla_ipc_IPCStreamSource_h
 
 #include "mozilla/AlreadyAddRefed.h"
-#include "mozilla/dom/WorkerHolder.h"
-#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRef.h"
 
 class nsIAsyncInputStream;
 
 namespace mozilla {
 
 namespace dom {
 class nsIContentChild;
 class nsIContentParent;
@@ -51,17 +50,17 @@ class PParentToChildStreamParent;
 //  2) If the actor is never sent to the other-side, then this code must
 //     call IPCStreamSource{Child,Parent}::StartDestroy() to avoid memory leaks.
 //  3) The IPCStreamSource actor can only be used on threads that can be
 //     guaranteed to stay alive as long as the actor is alive.  Right now
 //     this limits IPCStream to the main thread and Worker threads.
 //
 // In general you should probably use the AutoIPCStreamSource RAII class
 // defined in InputStreamUtils.h instead of using IPCStreamSource directly.
-class IPCStreamSource : public dom::WorkerHolder
+class IPCStreamSource
 {
 public:
   // Create a IPCStreamSource using a PContent IPC manager on the
   // main thread.  This can return nullptr if the provided stream is
   // blocking.
   static PChildToParentStreamChild*
   Create(nsIAsyncInputStream* aInputStream, dom::nsIContentChild* aManager);
 
@@ -118,33 +117,26 @@ protected:
   SendData(const wr::ByteBuffer& aBuffer) = 0;
 
   void
   ActorConstructed();
 
 private:
   class Callback;
 
-  // WorkerHolder methods
-  virtual bool
-  Notify(dom::WorkerStatus aStatus) override;
-
   void DoRead();
 
   void Wait();
 
   void OnStreamReady(Callback* aCallback);
 
   nsCOMPtr<nsIAsyncInputStream> mStream;
   RefPtr<Callback> mCallback;
 
-  // Raw pointer because this IPCStreamSource keeps the worker alive using a
-  // WorkerHolder. The worker is kept alive when the actor is created and,
-  // released when the actor is destroyed.
-  dom::WorkerPrivate* mWorkerPrivate;
+  RefPtr<dom::StrongWorkerRef> mWorkerRef;
 
 #ifdef DEBUG
 protected:
 #endif
 
   enum {
    ePending,
    eActorConstructed,
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2461,20 +2461,22 @@ ReadGeckoProfilingStack(JSContext* cx, u
             frameKind = NewStringCopyZ<CanGC>(cx, inlineFrame.kind);
             if (!frameKind)
                 return false;
 
             if (!JS_DefineProperty(cx, inlineFrameInfo, "kind", frameKind, propAttrs))
                 return false;
 
             size_t length = strlen(inlineFrame.label.get());
-            auto label = reinterpret_cast<Latin1Char*>(inlineFrame.label.release());
+            auto* label = reinterpret_cast<Latin1Char*>(inlineFrame.label.release());
             frameLabel = NewString<CanGC>(cx, label, length);
-            if (!frameLabel)
+            if (!frameLabel) {
+                js_free(label);
                 return false;
+            }
 
             if (!JS_DefineProperty(cx, inlineFrameInfo, "label", frameLabel, propAttrs))
                 return false;
 
             idx = INT_TO_JSID(inlineFrameNo);
             if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0))
                 return false;
 
@@ -2987,19 +2989,24 @@ class CloneBufferObject : public NativeO
         size_t size = data->Size();
         UniqueChars buffer(static_cast<char*>(js_malloc(size)));
         if (!buffer) {
             ReportOutOfMemory(cx);
             return false;
         }
         auto iter = data->Start();
         data->ReadBytes(iter, buffer.get(), size);
-        JSObject* arrayBuffer = JS_NewArrayBufferWithContents(cx, size, buffer.release());
-        if (!arrayBuffer)
+
+        auto* rawBuffer = buffer.release();
+        JSObject* arrayBuffer = JS_NewArrayBufferWithContents(cx, size, rawBuffer);
+        if (!arrayBuffer) {
+            js_free(rawBuffer);
             return false;
+        }
+
         args.rval().setObject(*arrayBuffer);
         return true;
     }
 
     static bool
     getCloneBufferAsArrayBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
         CallArgs args = CallArgsFromVp(argc, vp);
         return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args);
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -1816,17 +1816,17 @@ RegExpParser<CharT>::ParseDisjunction()
                     return ReportError(JSMSG_INVALID_IDENTITY_ESCAPE);
                 builder->AddCharacter(Next());
                 Advance(2);
                 break;
             }
             break;
           case '{': {
             if (unicode_)
-                return ReportError(JSMSG_RAW_BRACE_IN_REGEP);
+                return ReportError(JSMSG_RAW_BRACE_IN_REGEXP);
             int dummy;
             if (ParseIntervalQuantifier(&dummy, &dummy))
                 return ReportError(JSMSG_NOTHING_TO_REPEAT);
             MOZ_FALLTHROUGH;
           }
           default:
             if (unicode_) {
                 char16_t lead, trail;
@@ -1834,19 +1834,19 @@ RegExpParser<CharT>::ParseDisjunction()
                     builder->AddAtom(SurrogatePairAtom(alloc, lead, trail, ignore_case_));
                 } else {
                     widechar c = current();
                     if (unicode::IsLeadSurrogate(c))
                         builder->AddAtom(LeadSurrogateAtom(alloc, c));
                     else if (unicode::IsTrailSurrogate(c))
                         builder->AddAtom(TrailSurrogateAtom(alloc, c));
                     else if (c == ']')
-                        return ReportError(JSMSG_RAW_BRACKET_IN_REGEP);
+                        return ReportError(JSMSG_RAW_BRACKET_IN_REGEXP);
                     else if (c == '}')
-                        return ReportError(JSMSG_RAW_BRACE_IN_REGEP);
+                        return ReportError(JSMSG_RAW_BRACE_IN_REGEXP);
                     else
                         builder->AddCharacter(c);
                     Advance();
                 }
                 break;
             }
             builder->AddCharacter(current());
             Advance();
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -745,19 +745,20 @@ PostWriteElementBarrier(JSRuntime* rt, J
 
 template void
 PostWriteElementBarrier<IndexInBounds::Yes>(JSRuntime* rt, JSObject* obj, int32_t index);
 
 template void
 PostWriteElementBarrier<IndexInBounds::Maybe>(JSRuntime* rt, JSObject* obj, int32_t index);
 
 void
-PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj)
+PostGlobalWriteBarrier(JSRuntime* rt, GlobalObject* obj)
 {
-    MOZ_ASSERT(obj->is<GlobalObject>());
+    MOZ_ASSERT(obj->JSObject::is<GlobalObject>());
+
     if (!obj->realm()->globalWriteBarriered) {
         PostWriteBarrier(rt, obj);
         obj->realm()->globalWriteBarriered = 1;
     }
 }
 
 int32_t
 GetIndexFromString(JSString* str)
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -746,17 +746,17 @@ MOZ_MUST_USE bool
 GetIntrinsicValue(JSContext* cx, HandlePropertyName name, MutableHandleValue rval);
 
 MOZ_MUST_USE bool
 CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHandleValue rval);
 
 void GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp);
 
 void PostWriteBarrier(JSRuntime* rt, js::gc::Cell* cell);
-void PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj);
+void PostGlobalWriteBarrier(JSRuntime* rt, GlobalObject* obj);
 
 enum class IndexInBounds { Yes, Maybe };
 
 template <IndexInBounds InBounds>
 void PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index);
 
 // If |str| is an index in the range [0, INT32_MAX], return it. If the string
 // is not an index in this range, return -1.
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -513,18 +513,18 @@ MSG_DEF(JSMSG_INVALID_DECIMAL_ESCAPE, 0,
 MSG_DEF(JSMSG_INVALID_GROUP,           0, JSEXN_SYNTAXERR, "invalid regexp group")
 MSG_DEF(JSMSG_INVALID_IDENTITY_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid identity escape in regular expression")
 MSG_DEF(JSMSG_INVALID_UNICODE_ESCAPE,  0, JSEXN_SYNTAXERR, "invalid unicode escape in regular expression")
 MSG_DEF(JSMSG_MISSING_PAREN,           0, JSEXN_SYNTAXERR, "unterminated parenthetical")
 MSG_DEF(JSMSG_NEWREGEXP_FLAGGED,       0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another")
 MSG_DEF(JSMSG_NOTHING_TO_REPEAT,       0, JSEXN_SYNTAXERR, "nothing to repeat")
 MSG_DEF(JSMSG_NUMBERS_OUT_OF_ORDER,    0, JSEXN_SYNTAXERR, "numbers out of order in {} quantifier.")
 MSG_DEF(JSMSG_RANGE_WITH_CLASS_ESCAPE, 0, JSEXN_SYNTAXERR, "character class escape cannot be used in class range in regular expression")
-MSG_DEF(JSMSG_RAW_BRACE_IN_REGEP,      0, JSEXN_SYNTAXERR, "raw brace is not allowed in regular expression with unicode flag")
-MSG_DEF(JSMSG_RAW_BRACKET_IN_REGEP,    0, JSEXN_SYNTAXERR, "raw bracket is not allowed in regular expression with unicode flag")
+MSG_DEF(JSMSG_RAW_BRACE_IN_REGEXP,     0, JSEXN_SYNTAXERR, "raw brace is not allowed in regular expression with unicode flag")
+MSG_DEF(JSMSG_RAW_BRACKET_IN_REGEXP,   0, JSEXN_SYNTAXERR, "raw bracket is not allowed in regular expression with unicode flag")
 MSG_DEF(JSMSG_TOO_MANY_PARENS,         0, JSEXN_INTERNALERR, "too many parentheses in regular expression")
 MSG_DEF(JSMSG_UNICODE_OVERFLOW,        1, JSEXN_SYNTAXERR, "Unicode codepoint must not be greater than 0x10FFFF in {0}")
 MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN,   0, JSEXN_SYNTAXERR, "unmatched ) in regular expression")
 MSG_DEF(JSMSG_UNTERM_CLASS,            0, JSEXN_SYNTAXERR, "unterminated character class")
 
 // Self-hosting
 MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR,    0, JSEXN_ERR, "internal error getting the default locale")
 MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1, JSEXN_ERR, "No such property on self-hosted object: {0}")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1861,34 +1861,35 @@ JS_NewGlobalObject(JSContext* cx, const 
     CHECK_REQUEST(cx);
 
     return GlobalObject::new_(cx, Valueify(clasp), principals, hookOption, options);
 }
 
 JS_PUBLIC_API(void)
 JS_GlobalObjectTraceHook(JSTracer* trc, JSObject* global)
 {
-    MOZ_ASSERT(global->is<GlobalObject>());
+    GlobalObject* globalObj = &global->as<GlobalObject>();
+    Realm* globalRealm = globalObj->realm();
 
     // Off thread parsing and compilation tasks create a dummy global which is
     // then merged back into the host realm. Since it used to be a global, it
     // will still have this trace hook, but it does not have a meaning relative
     // to its new realm. We can safely skip it.
     //
     // Similarly, if we GC when creating the global, we may not have set that
     // global's realm's global pointer yet. In this case, the realm will not yet
     // contain anything that needs to be traced.
-    if (!global->isOwnGlobal(trc))
+    if (globalRealm->unsafeUnbarrieredMaybeGlobal() != globalObj)
         return;
 
     // Trace the realm for any GC things that should only stick around if we
     // know the global is live.
-    global->realm()->traceGlobal(trc);
-
-    if (JSTraceOp trace = global->realm()->creationOptions().getTrace())
+    globalRealm->traceGlobal(trc);
+
+    if (JSTraceOp trace = globalRealm->creationOptions().getTrace())
         trace(trc, global);
 }
 
 JS_PUBLIC_API(void)
 JS_FireOnNewGlobalObject(JSContext* cx, JS::HandleObject global)
 {
     // This hook is infallible, because we don't really want arbitrary script
     // to be able to throw errors during delicate global creation routines.
@@ -4382,17 +4383,18 @@ JS_PUBLIC_API(bool)
 JS_BufferIsCompilableUnit(JSContext* cx, HandleObject obj, const char* utf8, size_t length)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     cx->clearPendingException();
 
-    char16_t* chars = JS::UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(utf8, length), &length).get();
+    UniquePtr<char16_t> chars
+        { JS::UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(utf8, length), &length).get() };
     if (!chars)
         return true;
 
     // Return true on any out-of-memory error or non-EOF-related syntax error, so our
     // caller doesn't try to collect more buffered source.
     bool result = true;
 
     CompileOptions options(cx);
@@ -4401,34 +4403,33 @@ JS_BufferIsCompilableUnit(JSContext* cx,
         return false;
 
     RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
                                                                                  mozilla::Nothing()));
     if (!sourceObject)
         return false;
 
     frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
-                                                                  options, chars, length,
+                                                                  options, chars.get(), length,
                                                                   /* foldConstants = */ true,
                                                                   usedNames, nullptr, nullptr,
                                                                   sourceObject,
                                                                   frontend::ParseGoal::Script);
     JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
     if (!parser.checkOptions() || !parser.parse()) {
         // We ran into an error. If it was because we ran out of source, we
         // return false so our caller knows to try to collect more buffered
         // source.
         if (parser.isUnexpectedEOF())
             result = false;
 
         cx->clearPendingException();
     }
     JS::SetWarningReporter(cx, older);
 
-    js_free(chars);
     return result;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_GetGlobalFromScript(JSScript* script)
 {
     MOZ_ASSERT(!script->isCachedEval());
     return &script->global();
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -413,18 +413,20 @@ js::AssertSameCompartment(JSObject* objA
 {
     MOZ_ASSERT(objA->compartment() == objB->compartment());
 }
 #endif
 
 JS_FRIEND_API(void)
 js::NotifyAnimationActivity(JSObject* obj)
 {
+    MOZ_ASSERT(obj->is<GlobalObject>());
+
     int64_t timeNow = PRMJ_Now();
-    obj->realm()->lastAnimationTime = timeNow;
+    obj->as<GlobalObject>().realm()->lastAnimationTime = timeNow;
     obj->runtimeFromMainThread()->lastAnimationTime = timeNow;
 }
 
 JS_FRIEND_API(uint32_t)
 js::GetObjectSlotSpan(JSObject* obj)
 {
     return obj->as<NativeObject>().slotSpan();
 }
@@ -1547,17 +1549,17 @@ js::EnableAccessValidation(JSContext* cx
 {
     cx->enableAccessValidation = enabled;
 }
 
 JS_FRIEND_API(void)
 js::SetRealmValidAccessPtr(JSContext* cx, JS::HandleObject global, bool* accessp)
 {
     MOZ_ASSERT(global->is<GlobalObject>());
-    global->realm()->setValidAccessPtr(accessp);
+    global->as<GlobalObject>().realm()->setValidAccessPtr(accessp);
 }
 
 JS_FRIEND_API(bool)
 js::SystemZoneAvailable(JSContext* cx)
 {
     return true;
 }
 
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -2182,20 +2182,19 @@ DecompileAtPCForStackDump(JSContext* cx,
 
     if (!ed.decompilePC(offsetAndDefIndex))
         return false;
 
     char* result;
     if (!ed.getOutput(&result))
         return false;
 
-    if (!sp->put(result))
-        return false;
-
-    return true;
+    bool ok = sp->put(result);
+    js_free(result);
+    return ok;
 }
 #endif /* DEBUG */
 
 static bool
 FindStartPC(JSContext* cx, const FrameIter& iter, int spindex, int skipStackHits, const Value& v,
             jsbytecode** valuepc, uint8_t* defIndex)
 {
     jsbytecode* current = *valuepc;
--- a/js/src/vm/Compartment.cpp
+++ b/js/src/vm/Compartment.cpp
@@ -126,24 +126,32 @@ CopyStringPure(JSContext* cx, JSString* 
                : NewStringCopyNDontDeflate<CanGC>(cx, chars.twoByteRange().begin().get(), len);
     }
 
     if (str->hasLatin1Chars()) {
         ScopedJSFreePtr<Latin1Char> copiedChars;
         if (!str->asRope().copyLatin1CharsZ(cx, copiedChars))
             return nullptr;
 
-        return NewString<CanGC>(cx, copiedChars.forget(), len);
+        auto* rawCopiedChars = copiedChars.forget();
+        auto* result = NewString<CanGC>(cx, rawCopiedChars, len);
+        if (!result)
+            js_free(rawCopiedChars);
+        return result;
     }
 
     ScopedJSFreePtr<char16_t> copiedChars;
     if (!str->asRope().copyTwoByteCharsZ(cx, copiedChars))
         return nullptr;
 
-    return NewStringDontDeflate<CanGC>(cx, copiedChars.forget(), len);
+    auto* rawCopiedChars = copiedChars.forget();
+    auto* result = NewStringDontDeflate<CanGC>(cx, rawCopiedChars, len);
+    if (!result)
+        js_free(rawCopiedChars);
+    return result;
 }
 
 bool
 Compartment::wrap(JSContext* cx, MutableHandleString strp)
 {
     MOZ_ASSERT(cx->compartment() == this);
 
     /* If the string is already in this compartment, we are done. */
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -3324,17 +3324,17 @@ js::GetThisValueForDebuggerMaybeOptimize
 
 bool
 js::CheckLexicalNameConflict(JSContext* cx, Handle<LexicalEnvironmentObject*> lexicalEnv,
                              HandleObject varObj, HandlePropertyName name)
 {
     const char* redeclKind = nullptr;
     RootedId id(cx, NameToId(name));
     RootedShape shape(cx);
-    if (varObj->is<GlobalObject>() && varObj->realm()->isInVarNames(name)) {
+    if (varObj->is<GlobalObject>() && varObj->as<GlobalObject>().realm()->isInVarNames(name)) {
         // ES 15.1.11 step 5.a
         redeclKind = "var";
     } else if ((shape = lexicalEnv->lookup(cx, name))) {
         // ES 15.1.11 step 5.b
         redeclKind = shape->writable() ? "let" : "const";
     } else if (varObj->isNative() && (shape = varObj->as<NativeObject>().lookup(cx, name))) {
         // Faster path for ES 15.1.11 step 5.c-d when the shape can be found
         // without going through a resolve hook.
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1847,17 +1847,17 @@ GlobalHelperThreadState::mergeParseTaskR
     // After we call LeaveParseTaskZone() it's not safe to GC until we have
     // finished merging the contents of the parse task's realm into the
     // destination realm.
     JS::AutoAssertNoGC nogc(cx);
 
     LeaveParseTaskZone(cx->runtime(), parseTask);
 
     // Move the parsed script and all its contents into the desired realm.
-    gc::MergeRealms(parseTask->parseGlobal->realm(), dest);
+    gc::MergeRealms(parseTask->parseGlobal->as<GlobalObject>().realm(), dest);
 }
 
 void
 HelperThread::destroy()
 {
     if (thread.isSome()) {
         {
             AutoLockHelperThreadState lock;
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -434,17 +434,17 @@ DefVarOperation(JSContext* cx, HandleObj
 
     /* Steps 8c, 8d. */
     if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) {
         if (!DefineDataProperty(cx, varobj, dn, UndefinedHandleValue, attrs))
             return false;
     }
 
     if (varobj->is<GlobalObject>()) {
-        if (!varobj->realm()->addToVarNames(cx, dn))
+        if (!varobj->as<GlobalObject>().realm()->addToVarNames(cx, dn))
             return false;
     }
 
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
 NegOperation(JSContext* cx, HandleValue val, MutableHandleValue res)
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -4515,17 +4515,20 @@ js::DefFunOperation(JSContext* cx, Handl
                      ? JSPROP_ENUMERATE
                      : JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
     /* Steps 5d, 5f. */
     if (!prop || pobj != parent) {
         if (!DefineDataProperty(cx, parent, name, rval, attrs))
             return false;
 
-        return parent->is<GlobalObject>() ? parent->realm()->addToVarNames(cx, name) : true;
+        if (parent->is<GlobalObject>())
+            return parent->as<GlobalObject>().realm()->addToVarNames(cx, name);
+
+        return true;
     }
 
     /*
      * Step 5e.
      *
      * A DebugEnvironmentProxy is okay here, and sometimes necessary. If
      * Debugger.Frame.prototype.eval defines a function with the same name as an
      * extant variable in the frame, the DebugEnvironmentProxy takes care of storing
@@ -4541,17 +4544,17 @@ js::DefFunOperation(JSContext* cx, Handl
         } else {
             MOZ_ASSERT(shape->isDataDescriptor());
             MOZ_ASSERT(shape->writable());
             MOZ_ASSERT(shape->enumerable());
         }
 
         // Careful: the presence of a shape, even one appearing to derive from
         // a variable declaration, doesn't mean it's in [[VarNames]].
-        if (!parent->realm()->addToVarNames(cx, name))
+        if (!parent->as<GlobalObject>().realm()->addToVarNames(cx, name))
             return false;
     }
 
     /*
      * Non-global properties, and global properties which we aren't simply
      * redefining, must be set.  First, this preserves their attributes.
      * Second, this will produce warnings and/or errors as necessary if the
      * specified Call object property is not writable (const).
@@ -4765,17 +4768,17 @@ js::DeleteNameOperation(JSContext* cx, H
         return false;
 
     bool status = result.ok();
     res.setBoolean(status);
 
     if (status) {
         // Deleting a name from the global object removes it from [[VarNames]].
         if (pobj == scope && scope->is<GlobalObject>())
-            scope->realm()->removeFromVarNames(name);
+            scope->as<GlobalObject>().realm()->removeFromVarNames(name);
     }
 
     return true;
 }
 
 bool
 js::ImplicitThisOperation(JSContext* cx, HandleObject scopeObj, HandlePropertyName name,
                           MutableHandleValue res)
--- a/js/src/vm/JSONParser.cpp
+++ b/js/src/vm/JSONParser.cpp
@@ -721,18 +721,20 @@ JSONParser<CharT>::parse(MutableHandleVa
                 if (!freeElements.empty()) {
                     elements = freeElements.popCopy();
                     elements->clear();
                 } else {
                     elements = cx->new_<ElementVector>(cx);
                     if (!elements)
                         return false;
                 }
-                if (!stack.append(elements))
+                if (!stack.append(elements)) {
+                    js_delete(elements);
                     return false;
+                }
 
                 token = advance();
                 if (token == ArrayClose) {
                     if (!finishArray(&value, *elements))
                         return false;
                     break;
                 }
                 goto JSONValueSwitch;
@@ -743,18 +745,20 @@ JSONParser<CharT>::parse(MutableHandleVa
                 if (!freeProperties.empty()) {
                     properties = freeProperties.popCopy();
                     properties->clear();
                 } else {
                     properties = cx->new_<PropertyVector>(cx);
                     if (!properties)
                         return false;
                 }
-                if (!stack.append(properties))
+                if (!stack.append(properties)) {
+                    js_delete(properties);
                     return false;
+                }
 
                 token = advanceAfterObjectOpen();
                 if (token == ObjectClose) {
                     if (!finishObject(&value, *properties))
                         return false;
                     break;
                 }
                 goto JSONMember;
--- a/js/src/vm/JSObject-inl.h
+++ b/js/src/vm/JSObject-inl.h
@@ -405,28 +405,16 @@ JSObject::nonCCWGlobal() const
      * The global is read-barriered so that it is kept live by access through
      * the Realm. When accessed through a JSObject, however, the global will be
      * already kept live by the black JSObject's group pointer, so does not
      * need to be read-barriered.
      */
     return *realm()->unsafeUnbarrieredMaybeGlobal();
 }
 
-inline js::GlobalObject*
-JSObject::globalForTracing(JSTracer*) const
-{
-    return realm()->unsafeUnbarrieredMaybeGlobal();
-}
-
-inline bool
-JSObject::isOwnGlobal(JSTracer* trc) const
-{
-    return globalForTracing(trc) == this;
-}
-
 inline bool
 JSObject::hasAllFlags(js::BaseShape::Flag flags) const
 {
     MOZ_ASSERT(flags);
     if (js::Shape* shape = maybeShape())
         return shape->hasAllObjectFlags(flags);
     return false;
 }
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -431,30 +431,16 @@ class JSObject : public js::gc::Cell
 
     // Deprecated: call nonCCWGlobal or NativeObject::global() instead!
     inline js::GlobalObject& deprecatedGlobal() const;
 
     // Cross-compartment wrappers are not associated with a single realm/global,
     // so this method asserts the object is not a CCW.
     inline js::GlobalObject& nonCCWGlobal() const;
 
-    // In some rare cases the global object's compartment's global may not be
-    // the same global object. For this reason, we need to take extra care when
-    // tracing.
-    //
-    // These cases are:
-    //  1) The off-thread parsing task uses a dummy global since it cannot
-    //     share with the actual global being used concurrently on the active
-    //     thread.
-    //  2) A GC may occur when creating the GlobalObject, in which case the
-    //     compartment global pointer may not yet be set. In this case there is
-    //     nothing interesting to trace in the compartment.
-    inline bool isOwnGlobal(JSTracer*) const;
-    inline js::GlobalObject* globalForTracing(JSTracer*) const;
-
     /*
      * ES5 meta-object properties and operations.
      */
 
   public:
     // Indicates whether a non-proxy is extensible.  Don't call on proxies!
     // This method really shouldn't exist -- but there are a few internal
     // places that want it (JITs and the like), and it'd be a pain to mark them
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1470,16 +1470,17 @@ class NativeObject : public ShapedObject
 
     void updateShapeAfterMovingGC();
     void sweepDictionaryListPointer();
     void updateDictionaryListPointerAfterMinorGC(NativeObject* old);
 
     // Native objects are never wrappers, so a native object always has a realm
     // and global.
     inline js::GlobalObject& global() const;
+    JS::Realm* realm() const { return JSObject::realm(); }
 
     /* JIT Accessors */
     static size_t offsetOfElements() { return offsetof(NativeObject, elements_); }
     static size_t offsetOfFixedElements() {
         return sizeof(NativeObject) + sizeof(ObjectElements);
     }
 
     static size_t getFixedSlotOffset(size_t slot) {
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3552,17 +3552,17 @@ PreliminaryObjectArray::sweep()
         if (*ptr && IsAboutToBeFinalizedUnbarriered(ptr)) {
             // Before we clear this reference, change the object's group to the
             // Object.prototype group. This is done to ensure JSObject::finalize
             // sees a NativeObject Class even if we change the current group's
             // Class to one of the unboxed object classes in the meantime. If
             // the realm's global is dead, we don't do anything as the group's
             // Class is not going to change in that case.
             JSObject* obj = *ptr;
-            GlobalObject* global = obj->realm()->unsafeUnbarrieredMaybeGlobal();
+            GlobalObject* global = obj->as<NativeObject>().realm()->unsafeUnbarrieredMaybeGlobal();
             if (global && !obj->isSingleton()) {
                 JSObject* objectProto = global->maybeGetPrototype(JSProto_Object);
                 obj->setGroup(objectProto->groupRaw());
                 MOZ_ASSERT(obj->is<NativeObject>());
                 MOZ_ASSERT(obj->getClass() == objectProto->getClass());
                 MOZ_ASSERT(!obj->getClass()->hasFinalize());
             }
 
--- a/mfbt/SegmentedVector.h
+++ b/mfbt/SegmentedVector.h
@@ -15,19 +15,19 @@
 //
 // - In the case where you know the final size in advance and so can set the
 //   capacity appropriately, using SegmentedVector still avoids the need for
 //   large allocations (which can trigger OOMs).
 
 #ifndef mozilla_SegmentedVector_h
 #define mozilla_SegmentedVector_h
 
-#include "mozilla/Alignment.h"
 #include "mozilla/AllocPolicy.h"
 #include "mozilla/Array.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/TypeTraits.h"
 
 #include <new>  // for placement new
 
 namespace mozilla {
@@ -52,28 +52,39 @@ template<typename T,
          size_t IdealSegmentSize = 4096,
          typename AllocPolicy = MallocAllocPolicy>
 class SegmentedVector : private AllocPolicy
 {
   template<size_t SegmentCapacity>
   struct SegmentImpl
     : public mozilla::LinkedListElement<SegmentImpl<SegmentCapacity>>
   {
+  private:
+    uint32_t mLength;
+    alignas(T) MOZ_INIT_OUTSIDE_CTOR unsigned char mData[sizeof(T) * SegmentCapacity];
+
+    // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a
+    // -Werror compile error) to reinterpret_cast<> |mData| to |T*|, even
+    // through |void*|.  Placing the latter cast in these separate functions
+    // breaks the chain such that affected GCC versions no longer warn/error.
+    void* RawData() { return mData; }
+
+  public:
     SegmentImpl() : mLength(0) {}
 
     ~SegmentImpl()
     {
       for (uint32_t i = 0; i < mLength; i++) {
         (*this)[i].~T();
       }
     }
 
     uint32_t Length() const { return mLength; }
 
-    T* Elems() { return reinterpret_cast<T*>(&mStorage.mBuf); }
+    T* Elems() { return reinterpret_cast<T*>(RawData()); }
 
     T& operator[](size_t aIndex)
     {
       MOZ_ASSERT(aIndex < mLength);
       return Elems()[aIndex];
     }
 
     const T& operator[](size_t aIndex) const
@@ -93,28 +104,16 @@ class SegmentedVector : private AllocPol
     }
 
     void PopLast()
     {
       MOZ_ASSERT(mLength > 0);
       (*this)[mLength - 1].~T();
       mLength--;
     }
-
-    uint32_t mLength;
-
-    // The union ensures that the elements are appropriately aligned.
-    union Storage
-    {
-      char mBuf[sizeof(T) * SegmentCapacity];
-      mozilla::AlignedElem<MOZ_ALIGNOF(T)> mAlign;
-    } mStorage;
-
-    static_assert(MOZ_ALIGNOF(T) == MOZ_ALIGNOF(Storage),
-                  "SegmentedVector provides incorrect alignment");
   };
 
   // See how many we elements we can fit in a segment of IdealSegmentSize. If
   // IdealSegmentSize is too small, it'll be just one. The +1 is because
   // kSingleElementSegmentSize already accounts for one element.
   static const size_t kSingleElementSegmentSize = sizeof(SegmentImpl<1>);
   static const size_t kSegmentCapacity =
     kSingleElementSegmentSize <= IdealSegmentSize
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
@@ -259,17 +259,18 @@ public final class GeckoRuntimeSettings 
         "browser.display.use_document_fonts", true);
     /* package */ Pref<Integer> mCookieBehavior = new Pref<Integer>(
         "network.cookie.cookieBehavior", COOKIE_ACCEPT_ALL);
     /* package */ Pref<Integer> mCookieLifetime = new Pref<Integer>(
         "network.cookie.lifetimePolicy", COOKIE_LIFETIME_NORMAL);
     /* package */ Pref<Integer> mCookieLifetimeDays = new Pref<Integer>(
         "network.cookie.lifetime.days", 90);
     /* package */ Pref<String> mTrackingProtection = new Pref<String>(
-        "urlclassifier.trackingTable", "");
+        "urlclassifier.trackingTable",
+        TrackingProtection.buildPrefValue(TrackingProtectionDelegate.CATEGORY_ALL));
 
     /* package */ boolean mNativeCrashReporting;
     /* package */ boolean mJavaCrashReporting;
     /* package */ boolean mDebugPause;
 
     private final Pref<?>[] mPrefs = new Pref<?>[] {
         mCookieBehavior, mCookieLifetime, mCookieLifetimeDays, mJavaScript,
         mRemoteDebugging, mTrackingProtection, mWebFonts
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -1517,43 +1517,41 @@ NS_NewBufferedInputStream(nsIInputStream
     }
     return rv;
 }
 
 namespace {
 
 #define BUFFER_SIZE 8192
 
-class BufferWriter final : public Runnable
-                         , public nsIInputStreamCallback
+class BufferWriter final : public nsIInputStreamCallback
 {
 public:
-    NS_DECL_ISUPPORTS_INHERITED
+    NS_DECL_THREADSAFE_ISUPPORTS
 
     BufferWriter(nsIInputStream* aInputStream,
                  void* aBuffer, int64_t aCount)
-        : Runnable("BufferWriterRunnable")
-        , mMonitor("BufferWriter.mMonitor")
+        : mMonitor("BufferWriter.mMonitor")
         , mInputStream(aInputStream)
         , mBuffer(aBuffer)
         , mCount(aCount)
         , mWrittenData(0)
         , mBufferType(aBuffer ? eExternal : eInternal)
-        , mAsyncResult(NS_OK)
         , mBufferSize(0)
-        , mCompleted(false)
     {
         MOZ_ASSERT(aInputStream);
         MOZ_ASSERT(aCount == -1 || aCount > 0);
         MOZ_ASSERT_IF(mBuffer, aCount > 0);
     }
 
     nsresult
     Write()
     {
+        NS_ASSERT_OWNINGTHREAD(BufferWriter);
+
         // Let's make the inputStream buffered if it's not.
         if (!NS_InputStreamIsBuffered(mInputStream)) {
             nsCOMPtr<nsIInputStream> bufferedStream;
             nsresult rv =
                 NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
                                           mInputStream.forget(), BUFFER_SIZE);
             NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1570,26 +1568,26 @@ public:
         mInputStream = nullptr;
 
         return WriteAsync();
     }
 
     uint64_t
     WrittenData() const
     {
+        NS_ASSERT_OWNINGTHREAD(BufferWriter);
         return mWrittenData;
     }
 
     void*
     StealBuffer()
     {
+        NS_ASSERT_OWNINGTHREAD(BufferWriter);
         MOZ_ASSERT(mBufferType == eInternal);
 
-        MonitorAutoLock lock(mMonitor);
-
         void* buffer = mBuffer;
 
         mBuffer = nullptr;
         mBufferSize = 0;
 
         return buffer;
     }
 
@@ -1603,16 +1601,18 @@ private:
         if (mTaskQueue) {
             mTaskQueue->BeginShutdown();
         }
     }
 
     nsresult
     WriteSync()
     {
+        NS_ASSERT_OWNINGTHREAD(BufferWriter);
+
         uint64_t length = (uint64_t)mCount;
 
         if (mCount == -1) {
             nsresult rv = mInputStream->Available(&length);
             NS_ENSURE_SUCCESS(rv, rv);
 
             if (length == 0) {
                 // nothing to read.
@@ -1635,135 +1635,115 @@ private:
 
         mWrittenData = writtenData;
         return NS_OK;
     }
 
     nsresult
     WriteAsync()
     {
+        NS_ASSERT_OWNINGTHREAD(BufferWriter);
+
         if (mCount > 0 && mBufferType == eInternal) {
             mBuffer = malloc(mCount);
             if (NS_WARN_IF(!mBuffer)) {
                 return NS_ERROR_OUT_OF_MEMORY;
             }
         }
 
-        nsCOMPtr<nsIEventTarget> target =
-            do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-        if (!target) {
-            return NS_ERROR_FAILURE;
-        }
-
-        mTaskQueue = new TaskQueue(target.forget());
-
-        MonitorAutoLock lock(mMonitor);
-
-        nsCOMPtr<nsIRunnable> runnable = this;
-        nsresult rv = mTaskQueue->Dispatch(runnable.forget());
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        lock.Wait();
-
-        mCompleted = true;
-        return mAsyncResult;
-    }
-
-    // This method runs on the I/O Thread when the owning thread is blocked by
-    // the mMonitor. It is called multiple times until mCount is greater than 0
-    // or until there is something to read in the stream.
-    NS_IMETHOD
-    Run() override
-    {
-        MOZ_ASSERT(mAsyncInputStream);
-        MOZ_ASSERT(!mInputStream);
-
-        MonitorAutoLock lock(mMonitor);
-
         while (true) {
-            if (mCompleted) {
-                return NS_OK;
-            }
-
-            if (mCount == 0) {
-                OperationCompleted(lock, NS_OK);
-                return NS_OK;
-            }
-
             if (mCount == -1 && !MaybeExpandBufferSize()) {
-                OperationCompleted(lock, NS_ERROR_OUT_OF_MEMORY);
-                return NS_OK;
+                return NS_ERROR_OUT_OF_MEMORY;
             }
 
             uint64_t offset = mWrittenData;
             uint64_t length = mCount == -1 ? BUFFER_SIZE : mCount;
 
-            // Let's try to read it directly.
+            // Let's try to read data directly.
             uint32_t writtenData;
             nsresult rv = mAsyncInputStream->ReadSegments(NS_CopySegmentToBuffer,
                                                          static_cast<char*>(mBuffer) + offset,
                                                          length, &writtenData);
 
-            // Operation completed.
+            // Operation completed. Nothing more to read.
             if (NS_SUCCEEDED(rv) && writtenData == 0) {
-                OperationCompleted(lock, NS_OK);
                 return NS_OK;
             }
 
             // If we succeeded, let's try to read again.
             if (NS_SUCCEEDED(rv)) {
                 mWrittenData += writtenData;
                 if (mCount != -1) {
                     MOZ_ASSERT(mCount >= writtenData);
                     mCount -= writtenData;
                 }
 
                 continue;
             }
 
             // Async wait...
             if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+                rv = MaybeCreateTaskQueue();
+                if (NS_WARN_IF(NS_FAILED(rv))) {
+                    return rv;
+                }
+
+                MonitorAutoLock lock(mMonitor);
+
                 rv = mAsyncInputStream->AsyncWait(this, 0, length, mTaskQueue);
                 if (NS_WARN_IF(NS_FAILED(rv))) {
-                    OperationCompleted(lock, rv);
+                    return rv;
                 }
-                return NS_OK;
+
+                lock.Wait();
+                continue;
             }
 
-            // Error.
-            OperationCompleted(lock, rv);
-            return NS_OK;
+            // Otherwise, let's propagate the error.
+            return rv;
         }
 
         MOZ_ASSERT_UNREACHABLE("We should not be here");
         return NS_ERROR_FAILURE;
     }
 
+    nsresult
+    MaybeCreateTaskQueue()
+    {
+        NS_ASSERT_OWNINGTHREAD(BufferWriter);
+
+        if (!mTaskQueue) {
+            nsCOMPtr<nsIEventTarget> target =
+                do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+            if (!target) {
+                return NS_ERROR_FAILURE;
+            }
+
+            mTaskQueue = new TaskQueue(target.forget());
+        }
+
+        return NS_OK;
+    }
+
     NS_IMETHOD
     OnInputStreamReady(nsIAsyncInputStream* aStream) override
     {
-        MOZ_ASSERT(aStream == mAsyncInputStream);
-        // The stream is ready, let's read it again.
-        return Run();
-    }
-
-    // This function is called from the I/O thread and it will unblock the
-    // owning thread.
-    void
-    OperationCompleted(MonitorAutoLock& aLock, nsresult aRv)
-    {
-        mAsyncResult = aRv;
-
-        // This will unlock the owning thread.
-        aLock.Notify();
+        MOZ_ASSERT(!NS_IsMainThread());
+
+        // We have something to read. Let's unlock the main-thread.
+        MonitorAutoLock lock(mMonitor);
+        lock.Notify();
+        return NS_OK;
     }
 
     bool
     MaybeExpandBufferSize()
     {
+        NS_ASSERT_OWNINGTHREAD(BufferWriter);
+
         MOZ_ASSERT(mCount == -1);
 
         if (mBufferSize >= mWrittenData + BUFFER_SIZE) {
             // The buffer is big enough.
             return true;
         }
 
         CheckedUint32 bufferSize =
@@ -1783,16 +1763,18 @@ private:
             return false;
         }
 
         mBuffer = buffer;
         mBufferSize = bufferSize.value();
         return true;
     }
 
+    // All the members of this class are touched on the owning thread only. The
+    // monitor is only used to communicate when there is more data to read.
     Monitor mMonitor;
 
     nsCOMPtr<nsIInputStream> mInputStream;
     nsCOMPtr<nsIAsyncInputStream> mAsyncInputStream;
 
     RefPtr<TaskQueue> mTaskQueue;
 
     void* mBuffer;
@@ -1804,22 +1786,20 @@ private:
         // in the DTOR if not stolen. The buffer can be reallocated.
         eInternal,
 
         // The buffer is not owned by this object and it cannot be reallocated.
         eExternal,
     } mBufferType;
 
     // The following set if needed for the async read.
-    nsresult mAsyncResult;
     uint64_t mBufferSize;
-    Atomic<bool> mCompleted;
 };
 
-NS_IMPL_ISUPPORTS_INHERITED(BufferWriter, Runnable, nsIInputStreamCallback)
+NS_IMPL_ISUPPORTS(BufferWriter, nsIInputStreamCallback)
 
 } // anonymous namespace
 
 nsresult
 NS_ReadInputStreamToBuffer(nsIInputStream* aInputStream,
                            void** aDest,
                            int64_t aCount,
                            uint64_t* aWritten)
--- a/servo/components/style/media_queries/media_list.rs
+++ b/servo/components/style/media_queries/media_list.rs
@@ -1,19 +1,19 @@
 /* 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/. */
 
 //! A media query list:
 //!
 //! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list
 
+use context::QuirksMode;
 use cssparser::{Delimiter, Parser};
 use cssparser::{ParserInput, Token};
-use context::QuirksMode;
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 use parser::{ParserContext, ParserErrorContext};
 use super::{Device, MediaQuery, Qualifier};
 
 /// A type that encapsulates a media query list.
 #[css(comma)]
 #[derive(Clone, Debug, MallocSizeOf, ToCss)]
 pub struct MediaList {
--- a/servo/components/style/media_queries/media_query.rs
+++ b/servo/components/style/media_queries/media_query.rs
@@ -8,22 +8,21 @@
 
 use Atom;
 use cssparser::Parser;
 use parser::ParserContext;
 use selectors::parser::SelectorParseErrorKind;
 use std::fmt::{self, Write};
 use str::string_as_ascii_lowercase;
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use super::Expression;
 use values::CustomIdent;
 
-use super::Expression;
-
 /// <https://drafts.csswg.org/mediaqueries/#mq-prefix>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Parse, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
 pub enum Qualifier {
     /// Hide a media query from legacy UAs:
     /// <https://drafts.csswg.org/mediaqueries/#mq-only>
     Only,
     /// Negate a media query:
     /// <https://drafts.csswg.org/mediaqueries/#mq-not>
     Not,
 }
--- a/servo/components/style/values/specified/resolution.rs
+++ b/servo/components/style/values/specified/resolution.rs
@@ -7,17 +7,17 @@
 //! https://drafts.csswg.org/css-values/#resolution
 
 use cssparser::{Parser, Token};
 use parser::{Parse, ParserContext};
 use style_traits::{ParseError, StyleParseErrorKind};
 use values::CSSFloat;
 
 /// A specified resolution.
-#[derive(Clone, Debug, PartialEq, MallocSizeOf, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
 pub enum Resolution {
     /// Dots per inch.
     #[css(dimension)]
     Dpi(CSSFloat),
     /// An alias unit for dots per pixel.
     #[css(dimension)]
     X(CSSFloat),
     /// Dots per pixel.
--- a/xpcom/io/nsLocalFileWin.cpp
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -2946,16 +2946,17 @@ nsLocalFile::IsExecutable(bool* aResult)
       "plg",         // Developer Studio Build Log
       "prf",         // windows system file
       "prg",
       "pst",
       "reg",
       "scf",         // Windows explorer command
       "scr",
       "sct",
+      "settingcontent-ms",
       "shb",
       "shs",
       "url",
       "vb",
       "vbe",
       "vbs",
       "vsd",
       "vsmacros",    // Visual Studio .NET Binary-based Macro Project