Merge mozilla-central to autoland. a=merge CLOSED TREE
authorGurzau Raul <rgurzau@mozilla.com>
Sat, 18 May 2019 00:46:18 +0300
changeset 533228 14f4168a80d28e5e460676491f9b9ec9c50df422
parent 533225 058d6d7a23a64ffc0051d2cc1ec6a78c3d050897 (current diff)
parent 533227 1ae707852b608ea77dc82c892f25e169cbc316b5 (diff)
child 533230 c062846ae62bc7817f35e4994cb7f2e524c017ff
push id11276
push userrgurzau@mozilla.com
push dateMon, 20 May 2019 13:11:24 +0000
treeherdermozilla-beta@847755a7c325 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
dom/localstorage/ActorsParent.cpp
--- 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.2.167
+Current extension version is: 2.2.177
 
-Taken from upstream commit: ca2fee3d
+Taken from upstream commit: 1421b2f2
--- 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.2.167';
-var pdfjsBuild = 'ca2fee3d';
+var pdfjsVersion = '2.2.177';
+var pdfjsBuild = '1421b2f2';
 
 var pdfjsSharedUtil = __w_pdfjs_require__(1);
 
 var pdfjsDisplayAPI = __w_pdfjs_require__(6);
 
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(18);
 
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(19);
@@ -1299,17 +1299,17 @@ function _fetchDocument(worker, source, 
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
     source.progressiveDone = pdfDataRangeTransport.progressiveDone;
   }
 
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.2.167',
+    apiVersion: '2.2.177',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -2542,24 +2542,24 @@ class WorkerTransport {
           break;
 
         default:
           throw new Error(`Got unknown common object type ${type}`);
       }
     }, this);
     messageHandler.on('obj', function (data) {
       if (this.destroyed) {
-        return;
+        return undefined;
       }
 
       const [id, pageIndex, type, imageData] = data;
       const pageProxy = this.pageCache[pageIndex];
 
       if (pageProxy.objs.has(id)) {
-        return;
+        return undefined;
       }
 
       switch (type) {
         case 'JpegStream':
           return new Promise((resolve, reject) => {
             const img = new Image();
 
             img.onload = function () {
@@ -2584,16 +2584,18 @@ class WorkerTransport {
             pageProxy.cleanupAfterRender = true;
           }
 
           break;
 
         default:
           throw new Error(`Got unknown object type ${type}`);
       }
+
+      return undefined;
     }, this);
     messageHandler.on('DocProgress', function (data) {
       if (this.destroyed) {
         return;
       }
 
       if (loadingTask.onProgress) {
         loadingTask.onProgress({
@@ -3093,19 +3095,19 @@ const InternalRenderTask = function Inte
       }
     }
 
   }
 
   return InternalRenderTask;
 }();
 
-const version = '2.2.167';
+const version = '2.2.177';
 exports.version = version;
-const build = 'ca2fee3d';
+const build = '1421b2f2';
 exports.build = build;
 
 /***/ }),
 /* 7 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
@@ -3623,17 +3625,17 @@ class BaseFontLoader {
     if (this.styleElement) {
       this.styleElement.remove();
       this.styleElement = null;
     }
   }
 
   async bind(font) {
     if (font.attached || font.missingFile) {
-      return;
+      return undefined;
     }
 
     font.attached = true;
 
     if (this.isFontLoadingAPISupported) {
       const nativeFontFace = font.createNativeFontFace();
 
       if (nativeFontFace) {
@@ -3647,34 +3649,36 @@ class BaseFontLoader {
           });
 
           (0, _util.warn)(`Failed to load font '${nativeFontFace.family}': '${ex}'.`);
           font.disableFontFace = true;
           throw ex;
         }
       }
 
-      return;
+      return undefined;
     }
 
     const rule = font.createFontFaceRule();
 
     if (rule) {
       this.insertRule(rule);
 
       if (this.isSyncFontLoadingSupported) {
-        return;
+        return undefined;
       }
 
       return new Promise(resolve => {
         const request = this._queueLoadingCallback(resolve);
 
         this._prepareFontLoadEvent([rule], [font], request);
       });
     }
+
+    return undefined;
   }
 
   _queueLoadingCallback(callback) {
     (0, _util.unreachable)('Abstract method `_queueLoadingCallback`.');
   }
 
   get isFontLoadingAPISupported() {
     (0, _util.unreachable)('Abstract method `isFontLoadingAPISupported`.');
@@ -5170,17 +5174,17 @@ var CanvasGraphics = function CanvasGrap
 
       if (font.isType3Font) {
         return this.showType3Text(glyphs);
       }
 
       var fontSize = current.fontSize;
 
       if (fontSize === 0) {
-        return;
+        return undefined;
       }
 
       var ctx = this.ctx;
       var fontSizeScale = current.fontSizeScale;
       var charSpacing = current.charSpacing;
       var wordSpacing = current.wordSpacing;
       var fontDirection = current.fontDirection;
       var textHScale = current.textHScale * fontDirection;
@@ -6349,17 +6353,17 @@ Object.defineProperty(exports, "__esModu
   value: true
 });
 exports.MessageHandler = MessageHandler;
 
 var _util = __w_pdfjs_require__(1);
 
 async function resolveCall(fn, args, thisArg = null) {
   if (!fn) {
-    return;
+    return undefined;
   }
 
   return fn.apply(thisArg, args);
 }
 
 function wrapReason(reason) {
   if (typeof reason !== 'object') {
     return reason;
@@ -7377,25 +7381,25 @@ exports.SimpleXMLParser = SimpleXMLParse
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.PDFDataTransportStream = void 0;
 
 var _util = __w_pdfjs_require__(1);
 
-var PDFDataTransportStream = function PDFDataTransportStreamClosure() {
-  function PDFDataTransportStream(params, pdfDataRangeTransport) {
+class PDFDataTransportStream {
+  constructor(params, pdfDataRangeTransport) {
     (0, _util.assert)(pdfDataRangeTransport);
     this._queuedChunks = [];
     this._progressiveDone = params.progressiveDone || false;
     const initialData = params.initialData;
 
     if (initialData && initialData.length > 0) {
-      let buffer = new Uint8Array(initialData).buffer;
+      const buffer = new Uint8Array(initialData).buffer;
 
       this._queuedChunks.push(buffer);
     }
 
     this._pdfDataRangeTransport = pdfDataRangeTransport;
     this._isStreamingSupported = !params.disableStream;
     this._isRangeSupported = !params.disableRange;
     this._contentLength = params.length;
@@ -7424,307 +7428,309 @@ var PDFDataTransportStream = function PD
 
     this._pdfDataRangeTransport.addProgressiveDoneListener(() => {
       this._onProgressiveDone();
     });
 
     this._pdfDataRangeTransport.transportReady();
   }
 
-  PDFDataTransportStream.prototype = {
-    _onReceiveData: function PDFDataTransportStream_onReceiveData(args) {
-      let buffer = new Uint8Array(args.chunk).buffer;
-
-      if (args.begin === undefined) {
-        if (this._fullRequestReader) {
-          this._fullRequestReader._enqueue(buffer);
-        } else {
-          this._queuedChunks.push(buffer);
-        }
+  _onReceiveData(args) {
+    const buffer = new Uint8Array(args.chunk).buffer;
+
+    if (args.begin === undefined) {
+      if (this._fullRequestReader) {
+        this._fullRequestReader._enqueue(buffer);
       } else {
-        var found = this._rangeReaders.some(function (rangeReader) {
-          if (rangeReader._begin !== args.begin) {
-            return false;
-          }
-
-          rangeReader._enqueue(buffer);
-
-          return true;
-        });
-
-        (0, _util.assert)(found);
-      }
-    },
-
-    get _progressiveDataLength() {
-      return this._fullRequestReader ? this._fullRequestReader._loaded : 0;
-    },
-
-    _onProgress: function PDFDataTransportStream_onDataProgress(evt) {
-      if (evt.total === undefined) {
-        let firstReader = this._rangeReaders[0];
-
-        if (firstReader && firstReader.onProgress) {
-          firstReader.onProgress({
-            loaded: evt.loaded
-          });
-        }
-      } else {
-        let fullReader = this._fullRequestReader;
-
-        if (fullReader && fullReader.onProgress) {
-          fullReader.onProgress({
-            loaded: evt.loaded,
-            total: evt.total
-          });
+        this._queuedChunks.push(buffer);
+      }
+    } else {
+      const found = this._rangeReaders.some(function (rangeReader) {
+        if (rangeReader._begin !== args.begin) {
+          return false;
         }
-      }
-    },
-
-    _onProgressiveDone() {
-      if (this._fullRequestReader) {
-        this._fullRequestReader.progressiveDone();
-      }
-
-      this._progressiveDone = true;
-    },
-
-    _removeRangeReader: function PDFDataTransportStream_removeRangeReader(reader) {
-      var i = this._rangeReaders.indexOf(reader);
-
-      if (i >= 0) {
-        this._rangeReaders.splice(i, 1);
-      }
-    },
-    getFullReader: function PDFDataTransportStream_getFullReader() {
-      (0, _util.assert)(!this._fullRequestReader);
-      var queuedChunks = this._queuedChunks;
-      this._queuedChunks = null;
-      return new PDFDataTransportStreamReader(this, queuedChunks, this._progressiveDone);
-    },
-    getRangeReader: function PDFDataTransportStream_getRangeReader(begin, end) {
-      if (end <= this._progressiveDataLength) {
-        return null;
-      }
-
-      var reader = new PDFDataTransportStreamRangeReader(this, begin, end);
-
-      this._pdfDataRangeTransport.requestDataRange(begin, end);
-
-      this._rangeReaders.push(reader);
-
-      return reader;
-    },
-    cancelAllRequests: function PDFDataTransportStream_cancelAllRequests(reason) {
-      if (this._fullRequestReader) {
-        this._fullRequestReader.cancel(reason);
-      }
-
-      var readers = this._rangeReaders.slice(0);
-
-      readers.forEach(function (rangeReader) {
-        rangeReader.cancel(reason);
+
+        rangeReader._enqueue(buffer);
+
+        return true;
       });
 
-      this._pdfDataRangeTransport.abort();
-    }
-  };
-
-  function PDFDataTransportStreamReader(stream, queuedChunks, progressiveDone = false) {
+      (0, _util.assert)(found);
+    }
+  }
+
+  get _progressiveDataLength() {
+    return this._fullRequestReader ? this._fullRequestReader._loaded : 0;
+  }
+
+  _onProgress(evt) {
+    if (evt.total === undefined) {
+      const firstReader = this._rangeReaders[0];
+
+      if (firstReader && firstReader.onProgress) {
+        firstReader.onProgress({
+          loaded: evt.loaded
+        });
+      }
+    } else {
+      const fullReader = this._fullRequestReader;
+
+      if (fullReader && fullReader.onProgress) {
+        fullReader.onProgress({
+          loaded: evt.loaded,
+          total: evt.total
+        });
+      }
+    }
+  }
+
+  _onProgressiveDone() {
+    if (this._fullRequestReader) {
+      this._fullRequestReader.progressiveDone();
+    }
+
+    this._progressiveDone = true;
+  }
+
+  _removeRangeReader(reader) {
+    const i = this._rangeReaders.indexOf(reader);
+
+    if (i >= 0) {
+      this._rangeReaders.splice(i, 1);
+    }
+  }
+
+  getFullReader() {
+    (0, _util.assert)(!this._fullRequestReader);
+    const queuedChunks = this._queuedChunks;
+    this._queuedChunks = null;
+    return new PDFDataTransportStreamReader(this, queuedChunks, this._progressiveDone);
+  }
+
+  getRangeReader(begin, end) {
+    if (end <= this._progressiveDataLength) {
+      return null;
+    }
+
+    const reader = new PDFDataTransportStreamRangeReader(this, begin, end);
+
+    this._pdfDataRangeTransport.requestDataRange(begin, end);
+
+    this._rangeReaders.push(reader);
+
+    return reader;
+  }
+
+  cancelAllRequests(reason) {
+    if (this._fullRequestReader) {
+      this._fullRequestReader.cancel(reason);
+    }
+
+    const readers = this._rangeReaders.slice(0);
+
+    readers.forEach(function (rangeReader) {
+      rangeReader.cancel(reason);
+    });
+
+    this._pdfDataRangeTransport.abort();
+  }
+
+}
+
+exports.PDFDataTransportStream = PDFDataTransportStream;
+
+class PDFDataTransportStreamReader {
+  constructor(stream, queuedChunks, progressiveDone = false) {
     this._stream = stream;
     this._done = progressiveDone || false;
     this._filename = null;
     this._queuedChunks = queuedChunks || [];
     this._loaded = 0;
 
     for (const chunk of this._queuedChunks) {
       this._loaded += chunk.byteLength;
     }
 
     this._requests = [];
     this._headersReady = Promise.resolve();
     stream._fullRequestReader = this;
     this.onProgress = null;
   }
 
-  PDFDataTransportStreamReader.prototype = {
-    _enqueue: function PDFDataTransportStreamReader_enqueue(chunk) {
-      if (this._done) {
-        return;
-      }
-
-      if (this._requests.length > 0) {
-        var requestCapability = this._requests.shift();
-
-        requestCapability.resolve({
-          value: chunk,
-          done: false
-        });
-      } else {
-        this._queuedChunks.push(chunk);
-      }
-
-      this._loaded += chunk.byteLength;
-    },
-
-    get headersReady() {
-      return this._headersReady;
-    },
-
-    get filename() {
-      return this._filename;
-    },
-
-    get isRangeSupported() {
-      return this._stream._isRangeSupported;
-    },
-
-    get isStreamingSupported() {
-      return this._stream._isStreamingSupported;
-    },
-
-    get contentLength() {
-      return this._stream._contentLength;
-    },
-
-    async read() {
-      if (this._queuedChunks.length > 0) {
-        var chunk = this._queuedChunks.shift();
-
-        return {
-          value: chunk,
-          done: false
-        };
-      }
-
-      if (this._done) {
-        return {
-          value: undefined,
-          done: true
-        };
-      }
-
-      var requestCapability = (0, _util.createPromiseCapability)();
-
-      this._requests.push(requestCapability);
-
-      return requestCapability.promise;
-    },
-
-    cancel: function PDFDataTransportStreamReader_cancel(reason) {
-      this._done = true;
+  _enqueue(chunk) {
+    if (this._done) {
+      return;
+    }
+
+    if (this._requests.length > 0) {
+      const requestCapability = this._requests.shift();
+
+      requestCapability.resolve({
+        value: chunk,
+        done: false
+      });
+    } else {
+      this._queuedChunks.push(chunk);
+    }
+
+    this._loaded += chunk.byteLength;
+  }
+
+  get headersReady() {
+    return this._headersReady;
+  }
+
+  get filename() {
+    return this._filename;
+  }
+
+  get isRangeSupported() {
+    return this._stream._isRangeSupported;
+  }
+
+  get isStreamingSupported() {
+    return this._stream._isStreamingSupported;
+  }
+
+  get contentLength() {
+    return this._stream._contentLength;
+  }
+
+  async read() {
+    if (this._queuedChunks.length > 0) {
+      const chunk = this._queuedChunks.shift();
+
+      return {
+        value: chunk,
+        done: false
+      };
+    }
+
+    if (this._done) {
+      return {
+        value: undefined,
+        done: true
+      };
+    }
+
+    const requestCapability = (0, _util.createPromiseCapability)();
+
+    this._requests.push(requestCapability);
+
+    return requestCapability.promise;
+  }
+
+  cancel(reason) {
+    this._done = true;
+
+    this._requests.forEach(function (requestCapability) {
+      requestCapability.resolve({
+        value: undefined,
+        done: true
+      });
+    });
+
+    this._requests = [];
+  }
+
+  progressiveDone() {
+    if (this._done) {
+      return;
+    }
+
+    this._done = true;
+  }
+
+}
+
+class PDFDataTransportStreamRangeReader {
+  constructor(stream, begin, end) {
+    this._stream = stream;
+    this._begin = begin;
+    this._end = end;
+    this._queuedChunk = null;
+    this._requests = [];
+    this._done = false;
+    this.onProgress = null;
+  }
+
+  _enqueue(chunk) {
+    if (this._done) {
+      return;
+    }
+
+    if (this._requests.length === 0) {
+      this._queuedChunk = chunk;
+    } else {
+      const requestsCapability = this._requests.shift();
+
+      requestsCapability.resolve({
+        value: chunk,
+        done: false
+      });
 
       this._requests.forEach(function (requestCapability) {
         requestCapability.resolve({
           value: undefined,
           done: true
         });
       });
 
       this._requests = [];
-    },
-
-    progressiveDone() {
-      if (this._done) {
-        return;
-      }
-
-      this._done = true;
-    }
-
-  };
-
-  function PDFDataTransportStreamRangeReader(stream, begin, end) {
-    this._stream = stream;
-    this._begin = begin;
-    this._end = end;
-    this._queuedChunk = null;
+    }
+
+    this._done = true;
+
+    this._stream._removeRangeReader(this);
+  }
+
+  get isStreamingSupported() {
+    return false;
+  }
+
+  async read() {
+    if (this._queuedChunk) {
+      const chunk = this._queuedChunk;
+      this._queuedChunk = null;
+      return {
+        value: chunk,
+        done: false
+      };
+    }
+
+    if (this._done) {
+      return {
+        value: undefined,
+        done: true
+      };
+    }
+
+    const requestCapability = (0, _util.createPromiseCapability)();
+
+    this._requests.push(requestCapability);
+
+    return requestCapability.promise;
+  }
+
+  cancel(reason) {
+    this._done = true;
+
+    this._requests.forEach(function (requestCapability) {
+      requestCapability.resolve({
+        value: undefined,
+        done: true
+      });
+    });
+
     this._requests = [];
-    this._done = false;
-    this.onProgress = null;
-  }
-
-  PDFDataTransportStreamRangeReader.prototype = {
-    _enqueue: function PDFDataTransportStreamRangeReader_enqueue(chunk) {
-      if (this._done) {
-        return;
-      }
-
-      if (this._requests.length === 0) {
-        this._queuedChunk = chunk;
-      } else {
-        var requestsCapability = this._requests.shift();
-
-        requestsCapability.resolve({
-          value: chunk,
-          done: false
-        });
-
-        this._requests.forEach(function (requestCapability) {
-          requestCapability.resolve({
-            value: undefined,
-            done: true
-          });
-        });
-
-        this._requests = [];
-      }
-
-      this._done = true;
-
-      this._stream._removeRangeReader(this);
-    },
-
-    get isStreamingSupported() {
-      return false;
-    },
-
-    async read() {
-      if (this._queuedChunk) {
-        let chunk = this._queuedChunk;
-        this._queuedChunk = null;
-        return {
-          value: chunk,
-          done: false
-        };
-      }
-
-      if (this._done) {
-        return {
-          value: undefined,
-          done: true
-        };
-      }
-
-      var requestCapability = (0, _util.createPromiseCapability)();
-
-      this._requests.push(requestCapability);
-
-      return requestCapability.promise;
-    },
-
-    cancel: function PDFDataTransportStreamRangeReader_cancel(reason) {
-      this._done = true;
-
-      this._requests.forEach(function (requestCapability) {
-        requestCapability.resolve({
-          value: undefined,
-          done: true
-        });
-      });
-
-      this._requests = [];
-
-      this._stream._removeRangeReader(this);
-    }
-  };
-  return PDFDataTransportStream;
-}();
-
-exports.PDFDataTransportStream = PDFDataTransportStream;
+
+    this._stream._removeRangeReader(this);
+  }
+
+}
 
 /***/ }),
 /* 17 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-const pdfjsVersion = '2.2.167';
-const pdfjsBuild = 'ca2fee3d';
+const pdfjsVersion = '2.2.177';
+const pdfjsBuild = '1421b2f2';
 
 const pdfjsCoreWorker = __w_pdfjs_require__(1);
 
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
@@ -373,17 +373,17 @@ var WorkerMessageHandler = {
 
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     const verbosity = (0, _util.getVerbosityLevel)();
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.2.167';
+    let workerVersion = '2.2.177';
 
     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';
@@ -3633,16 +3633,18 @@ class Catalog {
   _readDests() {
     const obj = this.catDict.get('Names');
 
     if (obj && obj.has('Dests')) {
       return new NameTree(obj.getRaw('Dests'), this.xref);
     } else if (this.catDict.has('Dests')) {
       return this.catDict.get('Dests');
     }
+
+    return undefined;
   }
 
   get pageLabels() {
     let obj = null;
 
     try {
       obj = this._readPageLabels();
     } catch (ex) {
@@ -4978,17 +4980,17 @@ var XRef = function XRefClosure() {
         if (e instanceof _core_utils.MissingDataException) {
           throw e;
         }
 
         (0, _util.info)('(while reading XRef): ' + e);
       }
 
       if (recoveryMode) {
-        return;
+        return undefined;
       }
 
       throw new _core_utils.XRefParseException();
     },
     getEntry: function XRef_getEntry(i) {
       var xrefEntry = this.entries[i];
 
       if (xrefEntry && !xrefEntry.free && xrefEntry.offset) {
@@ -12937,16 +12939,17 @@ var JpegImage = function JpegImageClosur
           scaleX: component.h / frame.maxH,
           scaleY: component.v / frame.maxV,
           blocksPerLine: component.blocksPerLine,
           blocksPerColumn: component.blocksPerColumn
         });
       }
 
       this.numComponents = this.components.length;
+      return undefined;
     },
 
     _getLinearizedBlockData(width, height, isSourcePDF = false) {
       var scaleX = this.width / width,
           scaleY = this.height / height;
       var component, componentScaleX, componentScaleY, blocksPerScanline;
       var x, y, i, j, k;
       var index;
@@ -18190,26 +18193,25 @@ class AnnotationFactory {
   static create(xref, ref, pdfManager, idFactory) {
     return pdfManager.ensure(this, '_create', [xref, ref, pdfManager, idFactory]);
   }
 
   static _create(xref, ref, pdfManager, idFactory) {
     let dict = xref.fetchIfRef(ref);
 
     if (!(0, _primitives.isDict)(dict)) {
-      return;
+      return undefined;
     }
 
     let id = (0, _primitives.isRef)(ref) ? ref.toString() : `annot_${idFactory.createObjId()}`;
     let subtype = dict.get('Subtype');
     subtype = (0, _primitives.isName)(subtype) ? subtype.name : null;
     let parameters = {
       xref,
       dict,
-      ref: (0, _primitives.isRef)(ref) ? ref : null,
       subtype,
       id,
       pdfManager
     };
 
     switch (subtype) {
       case 'Link':
         return new LinkAnnotation(parameters);
@@ -18486,17 +18488,17 @@ class Annotation {
     }
 
     this.appearance = normalAppearanceState.get(as.name);
   }
 
   loadResources(keys) {
     return this.appearance.dict.getAsync('Resources').then(resources => {
       if (!resources) {
-        return;
+        return undefined;
       }
 
       let objectLoader = new _obj.ObjectLoader(resources, keys, resources.xref);
       return objectLoader.load().then(function () {
         return resources;
       });
     });
   }
@@ -19195,16 +19197,18 @@ var QueueOptimizer = function QueueOptim
         return fnArray[i] === _util.OPS.transform;
 
       case 2:
         return fnArray[i] === _util.OPS.paintInlineImageXObject;
 
       case 3:
         return fnArray[i] === _util.OPS.restore;
     }
+
+    throw new Error(`iterateInlineImageGroup - invalid pos: ${pos}`);
   }, function foundInlineImageGroup(context, i) {
     var MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10;
     var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;
     var MAX_WIDTH = 1000;
     var IMAGE_PADDING = 1;
     var fnArray = context.fnArray,
         argsArray = context.argsArray;
     var curr = context.iCurr;
@@ -19301,16 +19305,18 @@ var QueueOptimizer = function QueueOptim
         return fnArray[i] === _util.OPS.transform;
 
       case 2:
         return fnArray[i] === _util.OPS.paintImageMaskXObject;
 
       case 3:
         return fnArray[i] === _util.OPS.restore;
     }
+
+    throw new Error(`iterateImageMaskGroup - invalid pos: ${pos}`);
   }, function foundImageMaskGroup(context, i) {
     var MIN_IMAGES_IN_MASKS_BLOCK = 10;
     var MAX_IMAGES_IN_MASKS_BLOCK = 100;
     var MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000;
     var fnArray = context.fnArray,
         argsArray = context.argsArray;
     var curr = context.iCurr;
     var iFirstSave = curr - 3;
@@ -19383,17 +19389,17 @@ var QueueOptimizer = function QueueOptim
     }
 
     return iFirstSave + 1;
   });
   addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintImageXObject, _util.OPS.restore], function (context) {
     var argsArray = context.argsArray;
     var iFirstTransform = context.iCurr - 2;
     return argsArray[iFirstTransform][1] === 0 && argsArray[iFirstTransform][2] === 0;
-  }, function (context, i) {
+  }, function iterateImageGroup(context, i) {
     var fnArray = context.fnArray,
         argsArray = context.argsArray;
     var iFirstSave = context.iCurr - 3;
     var pos = (i - iFirstSave) % 4;
 
     switch (pos) {
       case 0:
         return fnArray[i] === _util.OPS.save;
@@ -19425,16 +19431,18 @@ var QueueOptimizer = function QueueOptim
           return false;
         }
 
         return true;
 
       case 3:
         return fnArray[i] === _util.OPS.restore;
     }
+
+    throw new Error(`iterateImageGroup - invalid pos: ${pos}`);
   }, function (context, i) {
     var MIN_IMAGES_IN_BLOCK = 3;
     var MAX_IMAGES_IN_BLOCK = 1000;
     var fnArray = context.fnArray,
         argsArray = context.argsArray;
     var curr = context.iCurr;
     var iFirstSave = curr - 3;
     var iFirstTransform = curr - 2;
@@ -19457,17 +19465,17 @@ var QueueOptimizer = function QueueOptim
       positions[(q << 1) + 1] = transformArgs[5];
     }
 
     var args = [firstPIXOArg0, firstTransformArg0, firstTransformArg3, positions];
     fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageXObjectRepeat);
     argsArray.splice(iFirstSave, count * 4, args);
     return iFirstSave + 1;
   });
-  addState(InitialState, [_util.OPS.beginText, _util.OPS.setFont, _util.OPS.setTextMatrix, _util.OPS.showText, _util.OPS.endText], null, function (context, i) {
+  addState(InitialState, [_util.OPS.beginText, _util.OPS.setFont, _util.OPS.setTextMatrix, _util.OPS.showText, _util.OPS.endText], null, function iterateShowTextGroup(context, i) {
     var fnArray = context.fnArray,
         argsArray = context.argsArray;
     var iFirstSave = context.iCurr - 4;
     var pos = (i - iFirstSave) % 5;
 
     switch (pos) {
       case 0:
         return fnArray[i] === _util.OPS.beginText;
@@ -19491,16 +19499,18 @@ var QueueOptimizer = function QueueOptim
           return false;
         }
 
         return true;
 
       case 4:
         return fnArray[i] === _util.OPS.endText;
     }
+
+    throw new Error(`iterateShowTextGroup - invalid pos: ${pos}`);
   }, function (context, i) {
     var MIN_CHARS_IN_BLOCK = 3;
     var MAX_CHARS_IN_BLOCK = 1000;
     var fnArray = context.fnArray,
         argsArray = context.argsArray;
     var curr = context.iCurr;
     var iFirstBeginText = curr - 4;
     var iFirstSetFont = curr - 3;
@@ -20118,24 +20128,24 @@ var PartialEvaluator = function PartialE
       forceDisableNativeImageDecoder = false
     }) {
       var dict = image.dict;
       var w = dict.get('Width', 'W');
       var h = dict.get('Height', 'H');
 
       if (!(w && (0, _util.isNum)(w)) || !(h && (0, _util.isNum)(h))) {
         (0, _util.warn)('Image dimensions are missing, or not numbers.');
-        return;
+        return undefined;
       }
 
       var maxImageSize = this.options.maxImageSize;
 
       if (maxImageSize !== -1 && w * h > maxImageSize) {
         (0, _util.warn)('Image exceeded maximum allowed size and was removed.');
-        return;
+        return undefined;
       }
 
       var imageMask = dict.get('ImageMask', 'IM') || false;
       var imgData, args;
 
       if (imageMask) {
         var width = dict.get('Width', 'W');
         var height = dict.get('Height', 'H');
@@ -20155,34 +20165,34 @@ var PartialEvaluator = function PartialE
 
         if (cacheKey) {
           imageCache[cacheKey] = {
             fn: _util.OPS.paintImageMaskXObject,
             args
           };
         }
 
-        return;
+        return undefined;
       }
 
       var softMask = dict.get('SMask', 'SM') || false;
       var mask = dict.get('Mask') || false;
       var SMALL_IMAGE_DIMENSIONS = 200;
 
       if (isInline && !softMask && !mask && !(image instanceof _jpeg_stream.JpegStream) && w + h < SMALL_IMAGE_DIMENSIONS) {
         let imageObj = new _image.PDFImage({
           xref: this.xref,
           res: resources,
           image,
           isInline,
           pdfFunctionFactory: this.pdfFunctionFactory
         });
         imgData = imageObj.createImageData(true);
         operatorList.addOp(_util.OPS.paintInlineImageXObject, [imgData]);
-        return;
+        return undefined;
       }
 
       const nativeImageDecoderSupport = forceDisableNativeImageDecoder ? _util.NativeImageDecoding.NONE : this.options.nativeImageDecoderSupport;
       let objId = `img_${this.idFactory.createObjId()}`;
 
       if (this.parsingType3Font) {
         (0, _util.assert)(nativeImageDecoderSupport === _util.NativeImageDecoding.NONE, 'Type3 image resources should be completely decoded in the worker.');
         objId = `${this.idFactory.getDocId()}_type3res_${objId}`;
@@ -20240,38 +20250,42 @@ var PartialEvaluator = function PartialE
       }).then(imageObj => {
         var imgData = imageObj.createImageData(false);
 
         if (this.parsingType3Font) {
           return this.handler.sendWithPromise('commonobj', [objId, 'FontType3Res', imgData], [imgData.data.buffer]);
         }
 
         this.handler.send('obj', [objId, this.pageIndex, 'Image', imgData], [imgData.data.buffer]);
+        return undefined;
       }).catch(reason => {
         (0, _util.warn)('Unable to decode image: ' + reason);
 
         if (this.parsingType3Font) {
           return this.handler.sendWithPromise('commonobj', [objId, 'FontType3Res', null]);
         }
 
         this.handler.send('obj', [objId, this.pageIndex, 'Image', null]);
+        return undefined;
       });
 
       if (this.parsingType3Font) {
         await imgPromise;
       }
 
       operatorList.addOp(_util.OPS.paintImageXObject, args);
 
       if (cacheKey) {
         imageCache[cacheKey] = {
           fn: _util.OPS.paintImageXObject,
           args
         };
       }
+
+      return undefined;
     },
 
     handleSMask: function PartialEvaluator_handleSmask(smask, resources, operatorList, task, stateManager) {
       var smaskContent = smask.get('G');
       var smaskOptions = {
         subtype: smask.get('S').name,
         backdrop: smask.get('BC')
       };
@@ -21600,17 +21614,18 @@ var PartialEvaluator = function PartialE
           return;
         }
 
         throw reason;
       });
     },
 
     extractDataStructures: function PartialEvaluator_extractDataStructures(dict, baseDict, properties) {
-      var xref = this.xref;
+      let xref = this.xref,
+          cidToGidBytes;
       var toUnicode = dict.get('ToUnicode') || baseDict.get('ToUnicode');
       var toUnicodePromise = toUnicode ? this.readToUnicode(toUnicode) : Promise.resolve(undefined);
 
       if (properties.composite) {
         var cidSystemInfo = dict.get('CIDSystemInfo');
 
         if ((0, _primitives.isDict)(cidSystemInfo)) {
           properties.cidSystemInfo = {
@@ -21618,17 +21633,17 @@ var PartialEvaluator = function PartialE
             ordering: (0, _util.stringToPDFString)(cidSystemInfo.get('Ordering')),
             supplement: cidSystemInfo.get('Supplement')
           };
         }
 
         var cidToGidMap = dict.get('CIDToGIDMap');
 
         if ((0, _primitives.isStream)(cidToGidMap)) {
-          properties.cidToGidMap = this.readCidToGidMap(cidToGidMap);
+          cidToGidBytes = cidToGidMap.getBytes();
         }
       }
 
       var differences = [];
       var baseEncodingName = null;
       var encoding;
 
       if (dict.has('Encoding')) {
@@ -21693,18 +21708,23 @@ var PartialEvaluator = function PartialE
 
       properties.differences = differences;
       properties.baseEncodingName = baseEncodingName;
       properties.hasEncoding = !!baseEncodingName || differences.length > 0;
       properties.dict = dict;
       return toUnicodePromise.then(toUnicode => {
         properties.toUnicode = toUnicode;
         return this.buildToUnicode(properties);
-      }).then(function (toUnicode) {
+      }).then(toUnicode => {
         properties.toUnicode = toUnicode;
+
+        if (cidToGidBytes) {
+          properties.cidToGidMap = this.readCidToGidMap(cidToGidBytes, toUnicode);
+        }
+
         return properties;
       });
     },
 
     _buildSimpleFontToUnicode(properties) {
       (0, _util.assert)(!properties.composite, 'Must be a simple font.');
       let toUnicode = [],
           charcode,
@@ -21878,33 +21898,34 @@ var PartialEvaluator = function PartialE
             map[charCode] = String.fromCodePoint.apply(String, str);
           });
           return new _fonts.ToUnicodeMap(map);
         });
       }
 
       return Promise.resolve(null);
     },
-    readCidToGidMap: function PartialEvaluator_readCidToGidMap(cidToGidStream) {
-      var glyphsData = cidToGidStream.getBytes();
+
+    readCidToGidMap(glyphsData, toUnicode) {
       var result = [];
 
       for (var j = 0, jj = glyphsData.length; j < jj; j++) {
         var glyphID = glyphsData[j++] << 8 | glyphsData[j];
-
-        if (glyphID === 0) {
+        const code = j >> 1;
+
+        if (glyphID === 0 && !toUnicode.has(code)) {
           continue;
         }
 
-        var code = j >> 1;
         result[code] = glyphID;
       }
 
       return result;
     },
+
     extractWidths: function PartialEvaluator_extractWidths(dict, descriptor, properties) {
       var xref = this.xref;
       var glyphsWidths = [];
       var defaultWidth = 0;
       var glyphsVMetrics = [];
       var defaultVMetrics;
       var i, ii, j, jj, start, code, widths;
 
@@ -25350,17 +25371,17 @@ var Font = function FontClosure() {
         return {
           platformId: potentialTable.platformId,
           encodingId: potentialTable.encodingId,
           mappings,
           hasShortCmap
         };
       }
 
-      function sanitizeMetrics(font, header, metrics, numGlyphs) {
+      function sanitizeMetrics(font, header, metrics, numGlyphs, dupFirstEntry) {
         if (!header) {
           if (metrics) {
             metrics.data = null;
           }
 
           return;
         }
 
@@ -25388,16 +25409,22 @@ var Font = function FontClosure() {
         }
 
         var numOfSidebearings = numGlyphs - numOfMetrics;
         var numMissing = numOfSidebearings - (metrics.length - numOfMetrics * 4 >> 1);
 
         if (numMissing > 0) {
           var entries = new Uint8Array(metrics.length + numMissing * 2);
           entries.set(metrics.data);
+
+          if (dupFirstEntry) {
+            entries[metrics.length] = metrics.data[2];
+            entries[metrics.length + 1] = metrics.data[3];
+          }
+
           metrics.data = entries;
         }
       }
 
       function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart, hintsValid) {
         var glyphProfile = {
           length: 0,
           sizeOfInstructions: 0
@@ -26143,17 +26170,17 @@ var Font = function FontClosure() {
       var hintsValid = sanitizeTTPrograms(tables['fpgm'], tables['prep'], tables['cvt '], maxFunctionDefs);
 
       if (!hintsValid) {
         delete tables['fpgm'];
         delete tables['prep'];
         delete tables['cvt '];
       }
 
-      sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphsOut);
+      sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphsOut, dupFirstEntry);
 
       if (!tables['head']) {
         throw new _util.FormatError('Required "head" table is not found');
       }
 
       sanitizeHead(tables['head'], numGlyphs, isTrueType ? tables['loca'].length : 0);
       var missingGlyphs = Object.create(null);
 
@@ -44571,17 +44598,17 @@ Object.defineProperty(exports, "__esModu
   value: true
 });
 exports.MessageHandler = MessageHandler;
 
 var _util = __w_pdfjs_require__(2);
 
 async function resolveCall(fn, args, thisArg = null) {
   if (!fn) {
-    return;
+    return undefined;
   }
 
   return fn.apply(thisArg, args);
 }
 
 function wrapReason(reason) {
   if (typeof reason !== 'object') {
     return reason;
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -434,17 +434,17 @@ let PDFViewerApplication = {
       }
     } catch (reason) {
       console.error(`_readPreferences: "${reason.message}".`);
     }
   },
 
   async _parseHashParameters() {
     if (!_app_options.AppOptions.get('pdfBugEnabled')) {
-      return;
+      return undefined;
     }
 
     const waitOn = [];
     let hash = document.location.hash.substring(1);
     let hashParams = (0, _ui_utils.parseQueryString)(hash);
 
     if ('disableworker' in hashParams && hashParams['disableworker'] === 'true') {
       waitOn.push(loadFakeWorker());
@@ -775,17 +775,17 @@ let PDFViewerApplication = {
     document.title = title;
   },
 
   async close() {
     let errorWrapper = this.appConfig.errorWrapper.container;
     errorWrapper.setAttribute('hidden', 'true');
 
     if (!this.pdfLoadingTask) {
-      return;
+      return undefined;
     }
 
     let promise = this.pdfLoadingTask.destroy();
     this.pdfLoadingTask = null;
 
     if (this.pdfDocument) {
       this.pdfDocument = null;
       this.pdfThumbnailViewer.setDocument(null);
@@ -870,17 +870,17 @@ let PDFViewerApplication = {
       this.progress(loaded / total);
     };
 
     loadingTask.onUnsupportedFeature = this.fallback.bind(this);
     return loadingTask.promise.then(pdfDocument => {
       this.load(pdfDocument);
     }, exception => {
       if (loadingTask !== this.pdfLoadingTask) {
-        return;
+        return undefined;
       }
 
       let message = exception && exception.message;
       let loadingErrorMessage;
 
       if (exception instanceof _pdfjsLib.InvalidPDFException) {
         loadingErrorMessage = this.l10n.get('invalid_file_error', null, 'Invalid or corrupted PDF file.');
       } else if (exception instanceof _pdfjsLib.MissingPDFException) {
@@ -3683,16 +3683,18 @@ var isSafari6plus = /Apple/.test(navigat
 function isLeftMouseReleased(event) {
   if ('buttons' in event && isNotIEorIsIE10plus) {
     return !(event.buttons & 1);
   }
 
   if (isChrome15OrOpera15plus || isSafari6plus) {
     return event.which === 0;
   }
+
+  return false;
 }
 
 /***/ }),
 /* 8 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -4372,17 +4374,17 @@ class PasswordPrompt {
     });
   }
 
   verify() {
     let password = this.input.value;
 
     if (password && password.length > 0) {
       this.close();
-      return this.updateCallback(password);
+      this.updateCallback(password);
     }
   }
 
   setUpdateCallback(updateCallback, reason) {
     this.updateCallback = updateCallback;
     this.reason = reason;
   }
 
@@ -4728,37 +4730,37 @@ class PDFDocumentProperties {
     }
 
     for (let id in this.fields) {
       let content = this.fieldData[id];
       this.fields[id].textContent = content || content === 0 ? content : DEFAULT_FIELD_CONTENT;
     }
   }
 
-  _parseFileSize(fileSize = 0) {
+  async _parseFileSize(fileSize = 0) {
     let kb = fileSize / 1024;
 
     if (!kb) {
-      return Promise.resolve(undefined);
+      return undefined;
     } else if (kb < 1024) {
       return this.l10n.get('document_properties_kb', {
         size_kb: (+kb.toPrecision(3)).toLocaleString(),
         size_b: fileSize.toLocaleString()
       }, '{{size_kb}} KB ({{size_b}} bytes)');
     }
 
     return this.l10n.get('document_properties_mb', {
       size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(),
       size_b: fileSize.toLocaleString()
     }, '{{size_mb}} MB ({{size_b}} bytes)');
   }
 
-  _parsePageSize(pageSizeInches, pagesRotation) {
+  async _parsePageSize(pageSizeInches, pagesRotation) {
     if (!pageSizeInches) {
-      return Promise.resolve(undefined);
+      return undefined;
     }
 
     if (pagesRotation % 180 !== 0) {
       pageSizeInches = {
         width: pageSizeInches.height,
         height: pageSizeInches.width
       };
     }
@@ -4811,27 +4813,27 @@ class PDFDocumentProperties {
         height: height.toLocaleString(),
         unit,
         name,
         orientation
       }, '{{width}} × {{height}} {{unit}} (' + (name ? '{{name}}, ' : '') + '{{orientation}})');
     });
   }
 
-  _parseDate(inputDate) {
+  async _parseDate(inputDate) {
     const dateObject = _pdfjsLib.PDFDateString.toDateObject(inputDate);
 
-    if (dateObject) {
-      const dateString = dateObject.toLocaleDateString();
-      const timeString = dateObject.toLocaleTimeString();
-      return this.l10n.get('document_properties_date_string', {
-        date: dateString,
-        time: timeString
-      }, '{{date}}, {{time}}');
-    }
+    if (!dateObject) {
+      return undefined;
+    }
+
+    return this.l10n.get('document_properties_date_string', {
+      date: dateObject.toLocaleDateString(),
+      time: dateObject.toLocaleTimeString()
+    }, '{{date}}, {{time}}');
   }
 
   _parseLinearization(isLinearized) {
     return this.l10n.get('document_properties_linearized_' + (isLinearized ? 'yes' : 'no'), null, isLinearized ? 'Yes' : 'No');
   }
 
 }
 
--- a/browser/extensions/pdfjs/moz.yaml
+++ b/browser/extensions/pdfjs/moz.yaml
@@ -15,15 +15,15 @@ origin:
   description: Portable Document Format (PDF) viewer that is built with HTML5
 
   # Full URL for the package's homepage/etc
   # Usually different from repository url
   url: https://github.com/mozilla/pdf.js
 
   # Human-readable identifier for this version/release
   # Generally "version NNN", "tag SSS", "bookmark SSS"
-  release: version 2.2.167
+  release: version 2.2.177
 
   # The package's license, where possible using the mnemonic from
   # https://spdx.org/licenses/
   # Multiple licenses can be specified (as a YAML list)
   # A "LICENSE" file must exist containing the full license text
   license: Apache-2.0
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -12911,17 +12911,17 @@ nsresult nsDocShell::CharsetChangeStopDo
   return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
 }
 
 void nsDocShell::SetIsPrinting(bool aIsPrinting) {
   mIsPrintingOrPP = aIsPrinting;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview) {
+nsDocShell::InitOrReusePrintPreviewViewer(nsIWebBrowserPrint** aPrintPreview) {
   *aPrintPreview = nullptr;
 #if NS_PRINT_PREVIEW
   nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
   if (!print || !print->IsInitializedForPrintPreview()) {
     // XXX: Creating a brand new content viewer to host preview every
     // time we enter here seems overwork. We could skip ahead to where
     // we QI the mContentViewer if the current URI is either about:blank
     // or about:printpreview.
@@ -12942,16 +12942,29 @@ nsDocShell::GetPrintPreview(nsIWebBrowse
   nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
   result.forget(aPrintPreview);
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 
+NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
+#if NS_PRINT_PREVIEW
+#  ifdef DEBUG
+  nsCOMPtr<nsIDocumentViewerPrint> vp = do_QueryInterface(mContentViewer);
+  MOZ_ASSERT(vp && vp->IsInitializedForPrintPreview());
+#  endif
+  nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mContentViewer);
+  return viewer->ExitPrintPreview();
+#else
+  return NS_OK;
+#endif
+}
+
 #ifdef DEBUG
 unsigned long nsDocShell::gNumberOfDocShells = 0;
 #endif
 
 NS_IMETHODIMP
 nsDocShell::GetCanExecuteScripts(bool* aResult) {
   *aResult = mCanExecuteScripts;
   return NS_OK;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -544,21 +544,38 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
    * Allows nsDocumentViewer to tell the top-level same-type docshell that
    * one of the documents under it is printing.
    */
   [noscript, notxpcom] void setIsPrinting(in boolean aIsPrinting);
 
   /**
-   * If the current content viewer isn't initialized for print preview,
-   * it is replaced with one which is and to which an about:blank document
-   * is loaded.
+   * This method should only be called on a docShell that has been specifically
+   * created to display a print preview document.  If the current document
+   * viewer isn't initialized for print preview when this method is called, it
+   * is replaced with a new viewer with an about:blank document (with the URL
+   * about:printpreview).  The viewer is then returned, ready for the print
+   * preview document to be constructed when viewer.printPreview() is called.
+   *
+   * The same viewer will be returned on subsequent calls since various bits of
+   * code assume that, once created, the viewer is never replaced.  Note,
+   * however, that the viewer's document will be replaced with a new document
+   * each time printPreview() is called on it (which is what we do to take
+   * account of print preview settings changes).  Replacing the document
+   * viewer's document breaks the normally unchanging 1:1 relationship between
+   * a document and its viewer, but that seems to be okay.
    */
-  readonly attribute nsIWebBrowserPrint printPreview;
+  nsIWebBrowserPrint initOrReusePrintPreviewViewer();
+
+  /**
+   * Propagated to the print preview document viewer.  Must only be called on
+   * a document viewer that has been initialized for print preview.
+   */
+  void exitPrintPreview();
 
   /**
    * Whether this docshell can execute scripts based on its hierarchy.
    * The rule of thumb here is that we disable js if this docshell or any
    * of its parents disallow scripting.
    */
   [infallible] readonly attribute boolean canExecuteScripts;
 
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -7039,17 +7039,17 @@ nsresult PrepareDatastoreOp::DatabaseWor
   }
 
   rv = VerifyDatabaseInformation(connection);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (hasDataForMigration) {
-    MOZ_ASSERT(mUsage == 0);
+    MOZ_DIAGNOSTIC_ASSERT(mUsage == 0);
 
     rv = AttachArchiveDatabase(quotaManager->GetStoragePath(), connection);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     int64_t newUsage;
     rv = GetUsage(connection, mArchivedOriginScope, &newUsage);
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -1313,17 +1313,17 @@ impl BatchBuilder {
 
                                         batcher.current_batch_list().push_single_instance(
                                             key,
                                             bounding_rect,
                                             z_id,
                                             PrimitiveInstanceData::from(instance),
                                         );
                                     }
-                                    Filter::DropShadowStack(shadows) => {
+                                    Filter::DropShadows(shadows) => {
                                         // Draw an instance per shadow first, following by the content.
 
                                         // The shadows and the content get drawn as a brush image.
                                         let kind = BatchKind::Brush(
                                             BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
                                         );
 
                                         // Gets the saved render task ID of the content, which is
@@ -1426,17 +1426,17 @@ impl BatchBuilder {
                                             Filter::Contrast(..) => 1,
                                             Filter::Grayscale(..) => 2,
                                             Filter::HueRotate(..) => 3,
                                             Filter::Invert(..) => 4,
                                             Filter::Saturate(..) => 5,
                                             Filter::Sepia(..) => 6,
                                             Filter::Brightness(..) => 7,
                                             Filter::Opacity(..) => 8,
-                                            Filter::DropShadowStack(..) => 9,
+                                            Filter::DropShadows(..) => 9,
                                             Filter::ColorMatrix(..) => 10,
                                             Filter::SrgbToLinear => 11,
                                             Filter::LinearToSrgb => 12,
                                             Filter::ComponentTransfer => unreachable!(),
                                         };
 
                                         let user_data = match filter {
                                             Filter::Identity => 0x10000i32, // matches `Contrast(1)`
@@ -1450,17 +1450,17 @@ impl BatchBuilder {
                                                 (amount * 65536.0) as i32
                                             }
                                             Filter::SrgbToLinear | Filter::LinearToSrgb => 0,
                                             Filter::HueRotate(angle) => {
                                                 (0.01745329251 * angle * 65536.0) as i32
                                             }
                                             // Go through different paths
                                             Filter::Blur(..) |
-                                            Filter::DropShadowStack(..) => {
+                                            Filter::DropShadows(..) => {
                                                 unreachable!();
                                             }
                                             Filter::ColorMatrix(_) => {
                                                 picture.extra_gpu_data_handles[0].as_int(gpu_cache)
                                             }
                                             Filter::ComponentTransfer => unreachable!(),
                                         };
 
--- a/gfx/wr/webrender/src/internal_types.rs
+++ b/gfx/wr/webrender/src/internal_types.rs
@@ -45,32 +45,32 @@ pub enum Filter {
     Brightness(f32),
     Contrast(f32),
     Grayscale(f32),
     HueRotate(f32),
     Invert(f32),
     Opacity(api::PropertyBinding<f32>, f32),
     Saturate(f32),
     Sepia(f32),
-    DropShadowStack(SmallVec<[Shadow; 1]>),
+    DropShadows(SmallVec<[Shadow; 1]>),
     ColorMatrix(Box<[f32; 20]>),
     SrgbToLinear,
     LinearToSrgb,
     ComponentTransfer,
 }
 
 impl Filter {
     /// Ensure that the parameters for a filter operation
     /// are sensible.
     pub fn sanitize(&mut self) {
         match self {
             Filter::Blur(ref mut radius) => {
                 *radius = radius.min(MAX_BLUR_RADIUS);
             }
-            Filter::DropShadowStack(ref mut stack) => {
+            Filter::DropShadows(ref mut stack) => {
                 for shadow in stack {
                     shadow.blur_radius = shadow.blur_radius.min(MAX_BLUR_RADIUS);
                 }
             }
             _ => {},
         }
     }
 
@@ -80,17 +80,17 @@ impl Filter {
             Filter::Blur(..) |
             Filter::Brightness(..) |
             Filter::Contrast(..) |
             Filter::Grayscale(..) |
             Filter::HueRotate(..) |
             Filter::Invert(..) |
             Filter::Saturate(..) |
             Filter::Sepia(..) |
-            Filter::DropShadowStack(..) |
+            Filter::DropShadows(..) |
             Filter::ColorMatrix(..) |
             Filter::SrgbToLinear |
             Filter::LinearToSrgb |
             Filter::ComponentTransfer  => true,
             Filter::Opacity(_, amount) => {
                 amount > OPACITY_EPSILON
             }
         }
@@ -103,17 +103,17 @@ impl Filter {
             Filter::Brightness(amount) => amount == 1.0,
             Filter::Contrast(amount) => amount == 1.0,
             Filter::Grayscale(amount) => amount == 0.0,
             Filter::HueRotate(amount) => amount == 0.0,
             Filter::Invert(amount) => amount == 0.0,
             Filter::Opacity(_, amount) => amount >= 1.0,
             Filter::Saturate(amount) => amount == 1.0,
             Filter::Sepia(amount) => amount == 0.0,
-            Filter::DropShadowStack(ref shadows) => {
+            Filter::DropShadows(ref shadows) => {
                 for shadow in shadows {
                     if shadow.offset.x != 0.0 || shadow.offset.y != 0.0 || shadow.blur_radius != 0.0 {
                         return false;
                     }
                 }
 
                 true
             }
@@ -145,17 +145,17 @@ impl From<FilterOp> for Filter {
             FilterOp::Invert(i) => Filter::Invert(i),
             FilterOp::Opacity(binding, opacity) => Filter::Opacity(binding, opacity),
             FilterOp::Saturate(s) => Filter::Saturate(s),
             FilterOp::Sepia(s) => Filter::Sepia(s),
             FilterOp::ColorMatrix(mat) => Filter::ColorMatrix(Box::new(mat)),
             FilterOp::SrgbToLinear => Filter::SrgbToLinear,
             FilterOp::LinearToSrgb => Filter::LinearToSrgb,
             FilterOp::ComponentTransfer => Filter::ComponentTransfer,
-            FilterOp::DropShadow(shadow) => Filter::DropShadowStack(smallvec![shadow]),
+            FilterOp::DropShadow(shadow) => Filter::DropShadows(smallvec![shadow]),
         }
     }
 }
 
 /// An ID for a texture that is owned by the `texture_cache` module.
 ///
 /// This can include atlases or standalone textures allocated via the texture
 /// cache (e.g.  if an image is too large to be added to an atlas). The texture
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -2523,17 +2523,17 @@ impl PicturePrimitive {
                             frame_state.render_tasks,
                             RenderTargetKind::Color,
                             ClearMode::Transparent,
                             None,
                         );
 
                         (blur_render_task_id, picture_task_id)
                     }
-                    PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
+                    PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
                         let mut max_std_deviation = 0.0;
                         for shadow in shadows {
                             // TODO(nical) presumably we should compute the clipped rect for each shadow
                             // and compute the union of them to determine what we need to rasterize and blur?
                             max_std_deviation = f32::max(max_std_deviation, shadow.blur_radius * device_pixel_scale.0);
                         }
         
                         max_std_deviation = max_std_deviation.round();
@@ -3127,17 +3127,17 @@ impl PicturePrimitive {
             let mut surface_rect = {
                 let surface = state.current_surface_mut();
                 // Inflate the local bounding rect if required by the filter effect.
                 // This inflaction factor is to be applied to the surface itsefl.
                 // TODO: in prepare_for_render we round before multiplying with the
                 // blur sample scale. Should we do this here as well?
                 let inflation_size = match raster_config.composite_mode {
                     PictureCompositeMode::Filter(Filter::Blur(_)) => surface.inflation_factor,
-                    PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
+                    PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
                         let mut max = 0.0;
                         for shadow in shadows {
                             max = f32::max(max, shadow.blur_radius * BLUR_SAMPLE_SCALE);
                         }
                         max.ceil()
                     }
                     _ => 0.0,
                 };
@@ -3162,17 +3162,17 @@ impl PicturePrimitive {
                     raster_config.establishes_raster_root = false;
                     state.are_raster_roots_assigned = false;
                 }
             }
 
             // Drop shadows draw both a content and shadow rect, so need to expand the local
             // rect of any surfaces to be composited in parent surfaces correctly.
             match raster_config.composite_mode {
-                PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
+                PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
                     for shadow in shadows {
                         let content_rect = surface_rect;
                         let shadow_rect = surface_rect.translate(&shadow.offset);
                         surface_rect = content_rect.union(&shadow_rect);
                     }
                 }
                 _ => {}
             }
@@ -3220,17 +3220,17 @@ impl PicturePrimitive {
         //           with a ColorMatrix, which stores the color matrix here. It's
         //           probably worth tidying this code up to be a bit more consistent.
         //           Perhaps store the color matrix after the common data, even though
         //           it's not used by that shader.
 
         match raster_config.composite_mode {
             PictureCompositeMode::TileCache { .. } => {}
             PictureCompositeMode::Filter(Filter::Blur(..)) => {}
-            PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
+            PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
                 self.extra_gpu_data_handles.resize(shadows.len(), GpuCacheHandle::new());
                 for (shadow, extra_handle) in shadows.iter().zip(self.extra_gpu_data_handles.iter_mut()) {
                     if let Some(mut request) = frame_state.gpu_cache.request(extra_handle) {
                         // Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
                         //  [brush specific data]
                         //  [segment_rect, segment data]
                         let shadow_rect = self.snapped_local_rect.translate(&shadow.offset);
 
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -1876,17 +1876,17 @@ impl PrimitiveStore {
                     } else {
                         frame_state.clip_chain_stack.pop_clip();
                     }
 
                     let shadow_rect = match pic.raster_config {
                         Some(ref rc) => match rc.composite_mode {
                             // If we have a drop shadow filter, we also need to include the shadow in
                             // our local rect for the purpose of calculating the size of the picture.
-                            PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
+                            PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
                                 let mut rect = LayoutRect::zero();
                                 for shadow in shadows {
                                     rect = rect.union(&pic.snapped_local_rect.translate(&shadow.offset));
                                 }
 
                                 rect
                             }
                             _ => LayoutRect::zero(),
@@ -2166,33 +2166,33 @@ impl PrimitiveStore {
         // TODO(gw): In future, if we support specifying a flag which gets the
         //           stretch size from the segment rect in the shaders, we can
         //           remove this invalidation here completely.
         if let Some(ref raster_config) = pic.raster_config {
             // Inflate the local bounding rect if required by the filter effect.
             // This inflaction factor is to be applied to the surface itself.
             let inflation_size = match raster_config.composite_mode {
                 PictureCompositeMode::Filter(Filter::Blur(_)) => surface.inflation_factor,
-                PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
+                PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
                     let mut max = 0.0;
                     for shadow in shadows {
                         max = f32::max(max, shadow.blur_radius * BLUR_SAMPLE_SCALE);
                     }
                     max.ceil()
                 }
                 _ => 0.0,
             };
             surface_rect = surface_rect.inflate(inflation_size, inflation_size);
 
             // Layout space for the picture is picture space from the
             // perspective of its child primitives.
             let pic_local_rect = surface_rect * TypedScale::new(1.0);
             if pic.snapped_local_rect != pic_local_rect {
                 match raster_config.composite_mode {
-                    PictureCompositeMode::Filter(Filter::DropShadowStack(..)) => {
+                    PictureCompositeMode::Filter(Filter::DropShadows(..)) => {
                         for handle in &pic.extra_gpu_data_handles {
                             frame_state.gpu_cache.invalidate(handle);
                         }
                     }
                     _ => {}
                 }
                 // Invalidate any segments built for this picture, since the local
                 // rect has changed.
--- a/gfx/wr/webrender/src/prim_store/picture.rs
+++ b/gfx/wr/webrender/src/prim_store/picture.rs
@@ -33,17 +33,17 @@ pub enum PictureCompositeKey {
     Contrast(Au),
     Grayscale(Au),
     HueRotate(Au),
     Invert(Au),
     Opacity(Au),
     OpacityBinding(PropertyBindingId, Au),
     Saturate(Au),
     Sepia(Au),
-    DropShadowStack(Vec<(VectorKey, Au, ColorU)>),
+    DropShadows(Vec<(VectorKey, Au, ColorU)>),
     ColorMatrix([Au; 20]),
     SrgbToLinear,
     LinearToSrgb,
     ComponentTransfer(ItemUid),
 
     // MixBlendMode
     Multiply,
     Screen,
@@ -93,18 +93,18 @@ impl From<Option<PictureCompositeMode>> 
                     Filter::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
                     Filter::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
                     Filter::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
                     Filter::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
                     Filter::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
                     Filter::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
                     Filter::LinearToSrgb => PictureCompositeKey::LinearToSrgb,
                     Filter::Identity => PictureCompositeKey::Identity,
-                    Filter::DropShadowStack(ref shadows) => {
-                        PictureCompositeKey::DropShadowStack(
+                    Filter::DropShadows(ref shadows) => {
+                        PictureCompositeKey::DropShadows(
                             shadows.iter().map(|shadow| {
                                 (shadow.offset.into(), Au::from_f32_px(shadow.blur_radius), shadow.color.into())
                             }).collect()
                         )
                     }
                     Filter::Opacity(binding, _) => {
                         match binding {
                             PropertyBinding::Value(value) => {
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3546,17 +3546,17 @@ nsDocumentViewer::Print(nsIPrintSettings
 
 NS_IMETHODIMP
 nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings,
                                mozIDOMWindowProxy* aChildDOMWin,
                                nsIWebProgressListener* aWebProgressListener) {
 #  if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
   MOZ_ASSERT(IsInitializedForPrintPreview(),
              "For print preview nsIWebBrowserPrint must be from "
-             "docshell.printPreview!");
+             "docshell.initOrReusePrintPreviewViewer!");
 
   NS_ENSURE_ARG_POINTER(aChildDOMWin);
   nsresult rv = NS_OK;
 
   if (GetIsPrinting()) {
     nsPrintJob::CloseProgressDialog(aWebProgressListener);
     return NS_ERROR_FAILURE;
   }
--- a/layout/base/tests/chrome/printpreview_bug396024_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug396024_helper.xul
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 <![CDATA[
 var is = window.opener.wrappedJSObject.is;
 var ok = window.opener.wrappedJSObject.ok;
 var todo = window.opener.wrappedJSObject.todo;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var gWbp;
 function printpreview() {
-  gWbp = window.frames[1].docShell.printPreview;
+  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -39,17 +39,17 @@ function printpreview() {
   prefs.setBoolPref('print.show_print_progress', false);
   //XXX I would have thought this would work, instead I'm forced to use prefs service
   gWbp.globalPrintSettings.showPrintProgress = false; 
   gWbp.printPreview(gWbp.globalPrintSettings, window.frames[0], listener);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].docShell.printPreview.exitPrintPreview();
+  window.frames[1].docShell.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function run()
@@ -85,36 +85,36 @@ function run2() {
     document.getElementById("i").removeEventListener("load", arguments.callee, true);
     setTimeout(run3, 0);
   };
   document.getElementById("i").addEventListener("load", loadhandler, true);
   window.frames[0].location.reload();
 }
 
 function run3() {
-  gWbp = window.frames[1].docShell.printPreview;
+  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
   ok(gWbp.doingPrintPreview, "Should be doing print preview");
   exitprintpreview();
   setTimeout(run4, 0);
 }
 
 function run4() {
   var i = document.getElementById("i");
   i.remove();
   var loadhandler = function() {
     document.getElementById("i").removeEventListener("load", loadhandler, true);
     setTimeout(run5, 0);
   };
   i.addEventListener("load", loadhandler, true);
   document.documentElement.getBoundingClientRect();
-  document.documentElement.appendChild(i);
+  document.documentElement.prepend(i);
 }
 
 function run5() {
-  gWbp = window.frames[0].docShell.printPreview;
+  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
   ok(!gWbp.doingPrintPreview, "Should not be doing print preview anymore2");
 
   //XXX this shouldn't be necessary, see bug 405555
   printpreview();
   exitprintpreview();
   finish(); //should not have crashed after all of this
 }
 ]]></script>
--- a/layout/base/tests/chrome/printpreview_bug482976_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug482976_helper.xul
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 <![CDATA[
 var is = window.opener.wrappedJSObject.is;
 var ok = window.opener.wrappedJSObject.ok;
 var todo = window.opener.wrappedJSObject.todo;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var gWbp;
 function printpreview() {
-  gWbp = window.frames[1].docShell.printPreview;
+  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -39,17 +39,17 @@ function printpreview() {
   prefs.setBoolPref('print.show_print_progress', false);
   //XXX I would have thought this would work, instead I'm forced to use prefs service
   gWbp.globalPrintSettings.showPrintProgress = false; 
   gWbp.printPreview(gWbp.globalPrintSettings, window.frames[0], listener);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].docShell.printPreview.exitPrintPreview();
+  window.frames[1].docShell.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function run1()
--- a/layout/base/tests/chrome/printpreview_helper.xul
+++ b/layout/base/tests/chrome/printpreview_helper.xul
@@ -19,17 +19,17 @@ var ctx2;
 var counter = 0;
 
 var file = Cc["@mozilla.org/file/directory_service;1"]
              .getService(Ci.nsIProperties)
              .get("TmpD", Ci.nsIFile);
 filePath = file.path;
 
 function printpreview(hasMozPrintCallback) {
-  gWbp = window.frames[1].docShell.printPreview;
+  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -60,17 +60,17 @@ function printpreview(hasMozPrintCallbac
     is(after, 1, "Should have called afterprint listener!");
   }
   window.frames[0].removeEventListener("beforeprint", beforeprint, true);
   window.frames[0].removeEventListener("afterprint", afterprint, true);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].docShell.printPreview.exitPrintPreview();
+  window.frames[1].docShell.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function runTests()
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2419,54 +2419,90 @@ nsStyleImageLayers::nsStyleImageLayers(c
     mImageCount = std::max(mImageCount, count);
     mSizeCount = std::max(mSizeCount, count);
     mMaskModeCount = std::max(mMaskModeCount, count);
     mBlendModeCount = std::max(mBlendModeCount, count);
     mCompositeCount = std::max(mCompositeCount, count);
   }
 }
 
+static bool IsElementImage(const nsStyleImageLayers::Layer& aLayer) {
+  return aLayer.mImage.GetType() == eStyleImageType_Element;
+}
+
+static bool AnyLayerIsElementImage(const nsStyleImageLayers& aLayers) {
+  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, aLayers) {
+    if (IsElementImage(aLayers.mLayers[i])) {
+      return true;
+    }
+  }
+  return false;
+}
+
 nsChangeHint nsStyleImageLayers::CalcDifference(
-    const nsStyleImageLayers& aNewLayers,
-    nsStyleImageLayers::LayerType aType) const {
+    const nsStyleImageLayers& aNewLayers, LayerType aType) const {
+
   nsChangeHint hint = nsChangeHint(0);
 
+  // If the number of visible images changes, then it's easy-peasy.
+  if (mImageCount != aNewLayers.mImageCount) {
+    hint |= nsChangeHint_RepaintFrame;
+    if (aType == nsStyleImageLayers::LayerType::Mask ||
+        AnyLayerIsElementImage(*this) || AnyLayerIsElementImage(aNewLayers)) {
+      hint |= nsChangeHint_UpdateEffects;
+    }
+    return hint;
+  }
+
   const nsStyleImageLayers& moreLayers =
-      mImageCount > aNewLayers.mImageCount ? *this : aNewLayers;
+      mLayers.Length() > aNewLayers.mLayers.Length() ? *this : aNewLayers;
   const nsStyleImageLayers& lessLayers =
-      mImageCount > aNewLayers.mImageCount ? aNewLayers : *this;
-
-  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, moreLayers) {
-    if (i < lessLayers.mImageCount) {
+      mLayers.Length() > aNewLayers.mLayers.Length() ? aNewLayers : *this;
+
+  for (size_t i = 0; i < moreLayers.mLayers.Length(); ++i) {
+    const Layer& moreLayersLayer = moreLayers.mLayers[i];
+    if (i < moreLayers.mImageCount) {
+      // This is a visible image we're diffing, we may need to repaint.
+      const Layer& lessLayersLayer = lessLayers.mLayers[i];
       nsChangeHint layerDifference =
-          moreLayers.mLayers[i].CalcDifference(lessLayers.mLayers[i]);
+          moreLayersLayer.CalcDifference(lessLayersLayer);
+      if (layerDifference && (IsElementImage(moreLayersLayer) ||
+                              IsElementImage(lessLayersLayer))) {
+        layerDifference |= nsChangeHint_UpdateEffects |
+                           nsChangeHint_RepaintFrame;
+      }
       hint |= layerDifference;
-      if (layerDifference && ((moreLayers.mLayers[i].mImage.GetType() ==
-                               eStyleImageType_Element) ||
-                              (lessLayers.mLayers[i].mImage.GetType() ==
-                               eStyleImageType_Element))) {
-        hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
-      }
-    } else {
-      hint |= nsChangeHint_RepaintFrame;
-      if (moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) {
-        hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
-      }
+      continue;
+    }
+    if (hint) {
+      // If they're different by now, we're done.
+      return hint;
     }
-  }
-
-  if (aType == nsStyleImageLayers::LayerType::Mask &&
-      mImageCount != aNewLayers.mImageCount) {
-    hint |= nsChangeHint_UpdateEffects;
+    if (i >= lessLayers.mLayers.Length()) {
+      // The layer count differs, we know some property has changed, but if we
+      // got here we know it won't affect rendering.
+      return nsChangeHint_NeutralChange;
+    }
+
+    const Layer& lessLayersLayer = lessLayers.mLayers[i];
+    MOZ_ASSERT(moreLayersLayer.mImage.GetType() == eStyleImageType_Null);
+    MOZ_ASSERT(lessLayersLayer.mImage.GetType() == eStyleImageType_Null);
+    if (moreLayersLayer.CalcDifference(lessLayersLayer)) {
+      // We don't care about the difference returned, we know it's not visible,
+      // but if something changed, then we need to return the neutral change.
+      return nsChangeHint_NeutralChange;
+    }
   }
 
   if (hint) {
+    // If they're different by now, we're done.
     return hint;
   }
 
+  // We could have same layers and values, but different count still.
   if (mAttachmentCount != aNewLayers.mAttachmentCount ||
       mBlendModeCount != aNewLayers.mBlendModeCount ||
       mClipCount != aNewLayers.mClipCount ||
       mCompositeCount != aNewLayers.mCompositeCount ||
       mMaskModeCount != aNewLayers.mMaskModeCount ||
       mOriginCount != aNewLayers.mOriginCount ||
       mRepeatCount != aNewLayers.mRepeatCount ||
       mPositionXCount != aNewLayers.mPositionXCount ||
@@ -3304,17 +3340,18 @@ nsChangeHint nsStyleDisplay::CalcDiffere
                 mAnimationDelayCount != aNewData.mAnimationDelayCount ||
                 mAnimationNameCount != aNewData.mAnimationNameCount ||
                 mAnimationDirectionCount != aNewData.mAnimationDirectionCount ||
                 mAnimationFillModeCount != aNewData.mAnimationFillModeCount ||
                 mAnimationPlayStateCount != aNewData.mAnimationPlayStateCount ||
                 mAnimationIterationCountCount !=
                     aNewData.mAnimationIterationCountCount ||
                 mScrollSnapCoordinate != aNewData.mScrollSnapCoordinate ||
-                mWillChange != aNewData.mWillChange)) {
+                mWillChange != aNewData.mWillChange ||
+                mOverflowAnchor != aNewData.mOverflowAnchor)) {
     hint |= nsChangeHint_NeutralChange;
   }
 
   return hint;
 }
 
 // --------------------
 // nsStyleVisibility
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -175,16 +175,17 @@ support-files = file_bug1443344.css
 [test_ch_ex_no_infloops.html]
 [test_change_hint_optimizations.html]
 [test_clip-path_polygon.html]
 [test_color_rounding.html]
 [test_compute_data_with_start_struct.html]
 skip-if = toolkit == 'android'
 [test_computed_style.html]
 [test_computed_style_bfcache_display_none.html]
+[test_computed_style_difference.html]
 [test_computed_style_grid_with_pseudo.html]
 [test_computed_style_in_created_document.html]
 [test_computed_style_min_size_auto.html]
 [test_computed_style_no_flush.html]
 [test_computed_style_no_pseudo.html]
 [test_computed_style_prefs.html]
 [test_condition_text.html]
 [test_condition_text_assignment.html]
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_computed_style_difference.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<title>Test that the difference of the computed style of an element is always correctly propagated</title>
+<!--
+  There are CSS property changes that don't have an effect in computed style.
+
+  It's relatively easy to return `nsChangeHint(0)` for the case where the
+  property changes but it should have no rendering difference.
+
+  That's however incorrect, since if it's an inherited property, or a
+  descendant explicitly inherits it, we should still propagate the change
+  downwards.
+
+  This test tests that computed style diffing is correct.
+-->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="property_database.js"></script>
+<div id="outer">
+  <div id="inner"></div>
+</div>
+<script>
+// We need to skip checking for properties for which the value returned by
+// getComputedStyle depends on the parent.
+//
+// TODO(emilio): We could test a subset of these, see below.
+const kWhitelist = [
+  // Could test display values that don't force blockification of children.
+  "display",
+
+  // Could avoid testing only the ones that have percentages.
+  "transform",
+  "transform-origin",
+  "perspective-origin",
+
+  "padding-bottom",
+  "padding-left",
+  "padding-right",
+  "padding-top",
+  "padding-inline-end",
+  "padding-inline-start",
+  "padding-block-end",
+  "padding-block-start",
+
+  "margin-bottom",
+  "margin-left",
+  "margin-right",
+  "margin-top",
+  "margin-inline-end",
+  "margin-inline-start",
+  "margin-block-end",
+  "margin-block-start",
+
+  "width",
+  "height",
+  "block-size",
+  "inline-size",
+
+  "min-height",
+  "min-width",
+  "min-block-size",
+  "min-inline-size",
+];
+
+const outer = document.getElementById("outer");
+const inner = document.getElementById("inner");
+
+function testValue(prop, value) {
+  outer.style.setProperty(prop, value);
+  const computed = getComputedStyle(outer).getPropertyValue(prop);
+  assert_equals(
+    getComputedStyle(inner).getPropertyValue(prop), computed,
+    "Didn't handle the inherited change correctly?"
+  )
+}
+
+// Note that we intentionally ignore the "prerequisites" here, since that's
+// the most likely place where the diffing could potentially go wrong.
+function testProperty(prop, info) {
+  // We only care about longhands, changing shorthands is not that interesting,
+  // since we're interested of changing as little as possible, and changing
+  // them would be equivalent to changing all the longhands at the same time.
+  if (info.type !== CSS_TYPE_LONGHAND)
+    return;
+  if (kWhitelist.includes(prop))
+    return;
+
+  inner.style.setProperty(prop, "inherit");
+  for (const v of info.initial_values)
+    testValue(prop, v);
+  for (const v of info.other_values)
+    testValue(prop, v);
+  // Test again the first value so that we test changing to it, not just from
+  // it.
+  //
+  // TODO(emilio): We could test every value against every-value if we wanted,
+  // might be worth it.
+  testValue(prop, info.initial_values[0]);
+
+  inner.style.removeProperty(prop);
+}
+
+for (let prop in gCSSProperties)
+  test(() => testProperty(prop, gCSSProperties[prop]), "Diffing for " + prop);
+</script>
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -441,29 +441,25 @@ def target_tasks_pine(full_task_graph, p
 
 
 @_target_task('nightly_fennec')
 def target_tasks_nightly_fennec(full_task_graph, parameters, graph_config):
     """Select the set of tasks required for a nightly build of fennec. The
     nightly build process involves a pipeline of builds, signing,
     and, eventually, uploading the tasks to balrog."""
     def filter(task):
-        platform = task.attributes.get('build_platform')
-        if not filter_for_project(task, parameters):
-            return False
-        if platform in ('android-aarch64-nightly',
-                        'android-api-16-nightly',
-                        'android-nightly',
-                        'android-x86-nightly',
-                        'android-x86_64-nightly',
-                        ):
-            if not task.attributes.get('nightly', False):
-                return False
-            return filter_for_project(task, parameters)
-    filter
+        # XXX Starting 69, we don't ship Fennec Nightly anymore. We just want geckoview to be
+        # uploaded
+        return task.label in (
+            'beetmover-geckoview-android-aarch64-nightly/opt',
+            'beetmover-geckoview-android-api-16-nightly/opt',
+            'beetmover-geckoview-android-x86-nightly/opt',
+            'beetmover-geckoview-android-x86_64-nightly/opt',
+        )
+
     return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
 
 
 def make_desktop_nightly_filter(platforms):
     """Returns a filter that gets all nightly tasks on the given platform."""
     def filter(task, parameters):
         return all([
             filter_on_platforms(task, platforms),
--- a/toolkit/actors/PrintingChild.jsm
+++ b/toolkit/actors/PrintingChild.jsm
@@ -283,17 +283,17 @@ class PrintingChild extends ActorChild {
       // The print preview docshell will be in a different TabGroup, so
       // printPreviewInitialize must be run in a separate runnable to avoid
       // touching a different TabGroup in our own runnable.
       let printPreviewInitialize = () => {
         try {
           let listener = new PrintingListener(this.mm);
 
           this.printPreviewInitializingInfo = { changingBrowsers };
-          docShell.printPreview.printPreview(printSettings, contentWindow, listener);
+          docShell.initOrReusePrintPreviewViewer().printPreview(printSettings, contentWindow, listener);
         } catch (error) {
           // This might fail if we, for example, attempt to print a XUL document.
           // In that case, we inform the parent to bail out of print preview.
           Cu.reportError(error);
           this.printPreviewInitializingInfo = null;
           this.mm.sendAsyncMessage("Printing:Preview:Entered", { failed: true });
         }
       };
@@ -313,17 +313,17 @@ class PrintingChild extends ActorChild {
       // In that case, we inform the parent to bail out of print preview.
       Cu.reportError(error);
       this.mm.sendAsyncMessage("Printing:Preview:Entered", { failed: true });
     }
   }
 
   exitPrintPreview(glo) {
     this.printPreviewInitializingInfo = null;
-    this.docShell.printPreview.exitPrintPreview();
+    this.docShell.initOrReusePrintPreviewViewer().exitPrintPreview();
   }
 
   print(contentWindow, simplifiedMode, defaultPrinterName) {
     let printSettings = this.getPrintSettings(defaultPrinterName);
     let printCancelled = false;
 
     // If we happen to be on simplified mode, we need to set docURL in order
     // to generate header/footer content correctly, since simplified tab has
@@ -380,24 +380,24 @@ class PrintingChild extends ActorChild {
   }
 
   logKeyedTelemetry(id, key) {
     let histogram = Services.telemetry.getKeyedHistogramById(id);
     histogram.add(key);
   }
 
   updatePageCount() {
-    let numPages = this.docShell.printPreview.printPreviewNumPages;
+    let numPages = this.docShell.initOrReusePrintPreviewViewer().printPreviewNumPages;
     this.mm.sendAsyncMessage("Printing:Preview:UpdatePageCount", {
       numPages,
     });
   }
 
   navigate(navType, pageNum) {
-    this.docShell.printPreview.printPreviewNavigate(navType, pageNum);
+    this.docShell.initOrReusePrintPreviewViewer().printPreviewNavigate(navType, pageNum);
   }
 }
 
 PrintingChild.prototype.QueryInterface =
   ChromeUtils.generateQI([Ci.nsIPrintingPromptService]);
 
 function PrintingListener(global) {
   this.global = global;
--- a/toolkit/components/printing/content/printUtils.js
+++ b/toolkit/components/printing/content/printUtils.js
@@ -118,22 +118,20 @@ var PrintUtils = {
    * Starts the process of printing the contents of a window.
    *
    * @param aWindowID
    *        The outer window ID of the nsIDOMWindow to print.
    * @param aBrowser
    *        The <xul:browser> that the nsIDOMWindow for aWindowID belongs to.
    */
   printWindow(aWindowID, aBrowser) {
-    let mm = aBrowser.messageManager;
-    let defaultPrinterName = this._getDefaultPrinterName();
-    mm.sendAsyncMessage("Printing:Print", {
+    aBrowser.messageManager.sendAsyncMessage("Printing:Print", {
       windowID: aWindowID,
       simplifiedMode: this._shouldSimplify,
-      defaultPrinterName,
+      defaultPrinterName: this._getDefaultPrinterName(),
     });
   },
 
   /**
    * Initializes print preview.
    *
    * @param aListenerObj
    *        An object that defines the following functions: