Merge inbound to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Tue, 19 Jun 2018 13:02:07 +0300
changeset 808416 257c191e7903523a1132e04460a0b2460d950809
parent 808415 4432fb67de2d1e2658a36fed800979c1968c1f94 (current diff)
parent 808324 81bb56f69788224c30d62dc42e60004ecb5fc860 (diff)
child 808417 c735e33ff04b2a6d72c0101d8676f33cc7a98ec7
child 808425 5f0ae9a4f7b561eca3e4ce4d2f6f868321e5bb60
child 808427 347a2d5512cfa192947adc9785b2afd42feced0c
child 808431 8c0a88de5c9da2691572c42fea3cd8f59d67d5d6
child 808435 53192258129c669cf48854ab506c677d3fae49b9
child 808439 4cd5c52af14b780ac1a02700a34bbac4642d2749
child 808440 bd58b35db4e4e8c63bca096f0b4a529f297c2f5f
child 808441 1d96d1bea37a40d1b92b86f899d1a7ae02ee268b
child 808463 522266b62da16a2621cea92028f6ed0dc4d4b721
child 808468 2535f662672a31d529e73e9d47b339c942efb234
child 808469 eb40379a385330de162c3fde600a1b00b868df3f
child 808470 aa1b3e50d1367d8fb8dd52433c147e141467d849
child 808471 0627c5001cebc3a3a4f5e81f8a05f01a1cc25261
child 808472 f11fb3788ded4442df4b315c0e426e6b5687fd83
child 808473 862f21ed5da8eeeccb4decc8446ec05cb6d86e87
child 808481 ec2b4c61c36f737abb7d9c434ed045f50ae36ed9
child 808532 fa4b9c6f483cf3dcfd9a418cde753873a4cf0664
child 808533 458d02502617b210824848109ba099b40812170f
child 808556 d5d1f362d05e171a23f63d0d485155f6dd5502e2
child 808558 4649a65ac9a9800bbe5593da0f75f49fc49acfd7
child 808567 d1d36d462a35b2023f4f9e181749fae1da2391bc
child 808665 9bf2eca8280a0e82d9bf28ff43279bdead9d166f
child 808666 7ee46ae2ae223960dd742db6c81589c7dab5e22e
child 808728 5a8456910c16274995de7fda3f687656dca93636
child 808769 17ae04695c70a127aaab44226b7874e1c6b1ba2b
child 808771 f5a4853e6bdbff55e0bac19ce953ced45bed5800
child 808783 228357b98888236fcb5615d878e84b67e0b147ed
child 808810 8efee17758cdfd4927f630c383ec281db5a6a9ef
child 808822 cbdf9b7127ed40b4aeada3c1f0c94e70d14fc99d
child 808828 85b10f77c1163e12dba1b25864ac2222a5fa8e32
child 808832 6ffee8a23daf204c76620975ddbecd302fe29fe1
child 808835 96b32afc914da6f56e902c94f22a2bbcf3d976b1
child 808864 92d66c1c8150ce9f124880a0e61110463f1baa10
child 808969 e3bd6c891bb241d72bda880a76e119ab7f74dcf2
child 809078 d1cf346decb0af4752b57b2e6ce7826861c3f98a
child 809171 d16fcf1fad85f058853a5831c65ef49e31240e18
child 809200 0dcebb0e12663c001f7a39144b14674b77e2cdc0
child 809202 d9649f545b0a11a71704f7fd21bae81a639e97f1
child 809217 fa00c1d01279a902614c1e4dbd4f4864dcff9a11
child 809223 6905a1d1a953cd4401c0e108ef6ce60285a460df
child 809224 1fba64c3ceac856013ca22acb996a045de35eced
child 809246 1a46577b55e4c104ef5806654a3c6d7aa8ef8ba1
child 809264 46744ea6f9d5f6901a79b54176898a754964961d
child 809272 39d34165dfaf769acc30a11910c327dc8de99254
child 809306 b321c7572ca0ef37caab8d9e846279657e28f64d
child 809326 796beaf513225ac030de58d842a6070bb3a60d26
child 809328 c307dc3de4ebf567dacd36517682b432142a8727
child 809682 20ac7f78400a057622402b0d3afe1d26598a6425
child 809683 1fe5e9a801be5cba0e1188cf8e81a5f8e0de6127
child 809737 ae72c573333d84dc101fd4a5151a35f6a45ba42c
child 810222 e517100e722cad944ac6b16df6a481925c8ca40b
child 810229 14a4a6621f521444cad5bcada06acc16eb8b15dd
child 810382 b8546b1091498b2a09aa888e592340be88a461c8
child 810646 2c5bed3142772ee3d26e38581cba0523aa9119d2
child 810776 f4de291526eed3bcfbade01afede0b9e4282d9c2
child 810778 8eda56898c152665e0f7b086e60d1daa8acf439c
child 810949 c83df013a3514557b93f6f3339343087e7cfea40
child 811379 4e8b4b6ea19f995b8d29629ece8fdaec8232470b
child 811544 147172203a36f66e197090ad40b0cc8da53ed212
child 811580 b72bb4f25974e9e4a630289dfdc776b60b19c4b5
child 813757 126964c89ace795dd370765efa90b3654e66482a
push id113374
push userbmo:ntim.bugs@gmail.com
push dateTue, 19 Jun 2018 12:53:33 +0000
reviewersmerge
milestone62.0a1
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