Merge inbound to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Sat, 18 May 2019 00:42:21 +0300
changeset 536294 1ae707852b608ea77dc82c892f25e169cbc316b5
parent 536293 7c540586aedbc69e75649ab34fbaaceee912bebd (current diff)
parent 536274 159848724347565e9f92d8049d2a847e754faed6 (diff)
child 536295 14f4168a80d28e5e460676491f9b9ec9c50df422
child 536296 6e4c58629a7cf3448d2d82a8287aef23ade8f6b5
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
1ae707852b60 / 68.0a1 / 20190517214313 / files
nightly linux64
1ae707852b60 / 68.0a1 / 20190517214313 / files
nightly mac
1ae707852b60 / 68.0a1 / 20190517214313 / files
nightly win32
1ae707852b60 / 68.0a1 / 20190517214313 / files
nightly win64
1ae707852b60 / 68.0a1 / 20190517214313 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/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
@@ -7037,17 +7037,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/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: