Bug 1303228 - Update pdf.js to version 1.5.464. r=bdahl
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 15 Sep 2016 21:46:34 -0400
changeset 357661 46ce3df4161f8dfbc0a319546a8ba1cad2b2ae2d
parent 357637 3b871b737c616c89a4b46398b800974b455c0960
child 357662 23dbff2ec6145f5d555c1c31b2a3bffe2a54f09d
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbdahl
bugs1303228
milestone51.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
Bug 1303228 - Update pdf.js to version 1.5.464. r=bdahl
browser/extensions/pdfjs/README.mozilla
browser/extensions/pdfjs/content/PdfJs.jsm
browser/extensions/pdfjs/content/PdfStreamConverter.jsm
browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
browser/extensions/pdfjs/content/build/pdf.js
browser/extensions/pdfjs/content/build/pdf.worker.js
browser/extensions/pdfjs/content/web/viewer.js
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.5.437
+Current extension version is: 1.5.464
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -87,16 +87,17 @@ function initializeDefaultPreferences() 
   "pdfBugEnabled": false,
   "disableRange": false,
   "disableStream": false,
   "disableAutoFetch": false,
   "disableFontFace": false,
   "disableTextLayer": false,
   "useOnlyCssZoom": false,
   "externalLinkTarget": 0,
+  "enhanceTextSelection": false,
   "renderInteractiveForms": false
 }
 
 
   var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.');
   var defaultValue;
   for (var key in DEFAULT_PREFERENCES) {
     defaultValue = DEFAULT_PREFERENCES[key];
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -942,16 +942,18 @@ PdfStreamConverter.prototype = {
     // Change the content type so we don't get stuck in a loop.
     aRequest.setProperty('contentType', aRequest.contentType);
     aRequest.contentType = 'text/html';
     if (isHttpRequest) {
       // We trust PDF viewer, using no CSP
       aRequest.setResponseHeader('Content-Security-Policy', '', false);
       aRequest.setResponseHeader('Content-Security-Policy-Report-Only', '',
                                  false);
+      // The viewer does not need to handle HTTP Refresh header.
+      aRequest.setResponseHeader('Refresh', '', false);
     }
 
     PdfJsTelemetry.onViewerIsUsed();
     PdfJsTelemetry.onDocumentSize(aRequest.contentLength);
 
     // Creating storage for PDF data
     var contentLength = aRequest.contentLength;
     this.dataListener = new PdfDataListener(contentLength);
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -45,16 +45,17 @@ var DEFAULT_PREFERENCES =
   "pdfBugEnabled": false,
   "disableRange": false,
   "disableStream": false,
   "disableAutoFetch": false,
   "disableFontFace": false,
   "disableTextLayer": false,
   "useOnlyCssZoom": false,
   "externalLinkTarget": 0,
+  "enhanceTextSelection": false,
   "renderInteractiveForms": false
 }
 
 
 var PdfjsChromeUtils = {
   // For security purposes when running remote, we restrict preferences
   // content can access.
   _allowedPrefNames: Object.keys(DEFAULT_PREFERENCES),
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -23,18 +23,18 @@ define('pdfjs-dist/build/pdf', ['exports
     factory(exports);
   } else {
 factory((root.pdfjsDistBuildPdf = {}));
   }
 }(this, function (exports) {
   // Use strict in our context only - users might not want it
   'use strict';
 
-var pdfjsVersion = '1.5.437';
-var pdfjsBuild = 'ca61ccc';
+var pdfjsVersion = '1.5.464';
+var pdfjsBuild = '834a7ff';
 
   var pdfjsFilePath =
     typeof document !== 'undefined' && document.currentScript ?
       document.currentScript.src : null;
 
   var pdfjsLibs = {};
 
   (function pdfjsWrapper() {
@@ -2316,51 +2316,60 @@ var WidgetAnnotationElement = (function 
 })();
 
 /**
  * @class
  * @alias TextWidgetAnnotationElement
  */
 var TextWidgetAnnotationElement = (
     function TextWidgetAnnotationElementClosure() {
+  var TEXT_ALIGNMENT = ['left', 'center', 'right'];
+
   function TextWidgetAnnotationElement(parameters) {
     WidgetAnnotationElement.call(this, parameters);
   }
 
   Util.inherit(TextWidgetAnnotationElement, WidgetAnnotationElement, {
     /**
      * Render the text widget annotation's HTML element in the empty container.
      *
      * @public
      * @memberof TextWidgetAnnotationElement
      * @returns {HTMLSectionElement}
      */
     render: function TextWidgetAnnotationElement_render() {
       this.container.className = 'textWidgetAnnotation';
 
+      var element = null;
       if (this.renderInteractiveForms) {
-        var input = document.createElement('input');
-        input.type = 'text';
-        input.value = this.data.fieldValue;
-
-        this.container.appendChild(input);
+        element = document.createElement('input');
+        element.type = 'text';
+        element.value = this.data.fieldValue;
+
+        if (this.data.maxLen !== null) {
+          element.maxLength = this.data.maxLen;
+        }
       } else {
-        var content = document.createElement('div');
-        content.textContent = this.data.fieldValue;
-        var textAlignment = this.data.textAlignment;
-        content.style.textAlign = ['left', 'center', 'right'][textAlignment];
-        content.style.verticalAlign = 'middle';
-        content.style.display = 'table-cell';
-
-        var font = (this.data.fontRefName ?
-          this.page.commonObjs.getData(this.data.fontRefName) : null);
-        this._setTextStyle(content, font);
-
-        this.container.appendChild(content);
-      }
+        element = document.createElement('div');
+        element.textContent = this.data.fieldValue;
+        element.style.verticalAlign = 'middle';
+        element.style.display = 'table-cell';
+
+        var font = null;
+        if (this.data.fontRefName) {
+          font = this.page.commonObjs.getData(this.data.fontRefName);
+        }
+        this._setTextStyle(element, font);
+      }
+
+      if (this.data.textAlignment !== null) {
+        element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];
+      }
+
+      this.container.appendChild(element);
       return this.container;
     },
 
     /**
      * Apply text styles to the text in the element.
      *
      * @private
      * @param {HTMLDivElement} element
@@ -2884,24 +2893,30 @@ var renderTextLayer = (function renderTe
   var MAX_TEXT_DIVS_TO_RENDER = 100000;
 
   var NonWhitespaceRegexp = /\S/;
 
   function isAllWhitespace(str) {
     return !NonWhitespaceRegexp.test(str);
   }
 
+  // Text layers may contain many thousand div's, and using `styleBuf` avoids
+  // creating many intermediate strings when building their 'style' properties.
+  var styleBuf = ['left: ', 0, 'px; top: ', 0, 'px; font-size: ', 0,
+                  'px; font-family: ', '', ';'];
+
   function appendText(task, geom, styles) {
     // Initialize all used properties to keep the caches monomorphic.
     var textDiv = document.createElement('div');
     var textDivProperties = {
+      style: null,
       angle: 0,
       canvasWidth: 0,
       isWhitespace: false,
-      originalTransform: '',
+      originalTransform: null,
       paddingBottom: 0,
       paddingLeft: 0,
       paddingRight: 0,
       paddingTop: 0,
       scale: 1,
     };
 
     task._textDivs.push(textDiv);
@@ -2929,20 +2944,22 @@ var renderTextLayer = (function renderTe
     var top;
     if (angle === 0) {
       left = tx[4];
       top = tx[5] - fontAscent;
     } else {
       left = tx[4] + (fontAscent * Math.sin(angle));
       top = tx[5] - (fontAscent * Math.cos(angle));
     }
-    textDiv.style.left = left + 'px';
-    textDiv.style.top = top + 'px';
-    textDiv.style.fontSize = fontHeight + 'px';
-    textDiv.style.fontFamily = style.fontFamily;
+    styleBuf[1] = left;
+    styleBuf[3] = top;
+    styleBuf[5] = fontHeight;
+    styleBuf[7] = style.fontFamily;
+    textDivProperties.style = styleBuf.join('');
+    textDiv.setAttribute('style', textDivProperties.style);
 
     textDiv.textContent = geom.str;
     // |fontName| is only used by the Font Inspector. This test will succeed
     // when e.g. the Font Inspector is off but the Stepper is on, but it's
     // not worth the effort to do a more accurate test. We only use `dataset`
     // here to make the font name available for the debugger.
     if (getDefaultSetting('pdfBug')) {
       textDiv.dataset.fontName = geom.fontName;
@@ -3340,17 +3357,16 @@ var renderTextLayer = (function renderTe
     this._textDivs = textDivs || [];
     this._textDivProperties = new WeakMap();
     this._renderingDone = false;
     this._canceled = false;
     this._capability = createPromiseCapability();
     this._renderTimer = null;
     this._bounds = [];
     this._enhanceTextSelection = !!enhanceTextSelection;
-    this._expanded = false;
   }
   TextLayerRenderTask.prototype = {
     get promise() {
       return this._capability.promise;
     },
 
     cancel: function TextLayer_cancel() {
       this._canceled = true;
@@ -3378,59 +3394,66 @@ var renderTextLayer = (function renderTe
         }, timeout);
       }
     },
 
     expandTextDivs: function TextLayer_expandTextDivs(expandDivs) {
       if (!this._enhanceTextSelection || !this._renderingDone) {
         return;
       }
-      if (!this._expanded) {
+      if (this._bounds !== null) {
         expand(this);
-        this._expanded = true;
-        this._bounds.length = 0;
+        this._bounds = null;
       }
 
       for (var i = 0, ii = this._textDivs.length; i < ii; i++) {
         var div = this._textDivs[i];
         var divProperties = this._textDivProperties.get(div);
 
+        if (divProperties.isWhitespace) {
+          continue;
+        }
         if (expandDivs) {
-          var transform = '';
+          var transform = '', padding = '';
 
           if (divProperties.scale !== 1) {
             transform = 'scaleX(' + divProperties.scale + ')';
           }
           if (divProperties.angle !== 0) {
             transform = 'rotate(' + divProperties.angle + 'deg) ' + transform;
           }
           if (divProperties.paddingLeft !== 0) {
-            div.style.paddingLeft =
-              (divProperties.paddingLeft / divProperties.scale) + 'px';
+            padding += ' padding-left: ' +
+              (divProperties.paddingLeft / divProperties.scale) + 'px;';
             transform += ' translateX(' +
               (-divProperties.paddingLeft / divProperties.scale) + 'px)';
           }
           if (divProperties.paddingTop !== 0) {
-            div.style.paddingTop = divProperties.paddingTop + 'px';
+            padding += ' padding-top: ' + divProperties.paddingTop + 'px;';
             transform += ' translateY(' + (-divProperties.paddingTop) + 'px)';
           }
           if (divProperties.paddingRight !== 0) {
-            div.style.paddingRight =
-              (divProperties.paddingRight / divProperties.scale) + 'px';
+            padding += ' padding-right: ' +
+              (divProperties.paddingRight / divProperties.scale) + 'px;';
           }
           if (divProperties.paddingBottom !== 0) {
-            div.style.paddingBottom = divProperties.paddingBottom + 'px';
+            padding += ' padding-bottom: ' +
+              divProperties.paddingBottom + 'px;';
+          }
+
+          if (padding !== '') {
+            div.setAttribute('style', divProperties.style + padding);
           }
           if (transform !== '') {
             CustomStyle.setProp('transform', div, transform);
           }
         } else {
           div.style.padding = 0;
           CustomStyle.setProp('transform', div,
-                              divProperties.originalTransform);
+                              divProperties.originalTransform || '');
         }
       }
     },
   };
 
   /**
    * Starts rendering of the text layer.
    *
@@ -4945,19 +4968,16 @@ var CanvasGraphics = (function CanvasGra
       var property = properties[i];
       if (sourceCtx[property] !== undefined) {
         destCtx[property] = sourceCtx[property];
       }
     }
     if (sourceCtx.setLineDash !== undefined) {
       destCtx.setLineDash(sourceCtx.getLineDash());
       destCtx.lineDashOffset =  sourceCtx.lineDashOffset;
-    } else if (sourceCtx.mozDashOffset !== undefined) {
-      destCtx.mozDash = sourceCtx.mozDash;
-      destCtx.mozDashOffset = sourceCtx.mozDashOffset;
     }
   }
 
   function composeSMaskBackdrop(bytes, r0, g0, b0) {
     var length = bytes.length;
     for (var i = 3; i < length; i += 4) {
       var alpha = bytes[i];
       if (alpha === 0) {
@@ -5203,19 +5223,16 @@ var CanvasGraphics = (function CanvasGra
     setMiterLimit: function CanvasGraphics_setMiterLimit(limit) {
       this.ctx.miterLimit = limit;
     },
     setDash: function CanvasGraphics_setDash(dashArray, dashPhase) {
       var ctx = this.ctx;
       if (ctx.setLineDash !== undefined) {
         ctx.setLineDash(dashArray);
         ctx.lineDashOffset = dashPhase;
-      } else {
-        ctx.mozDash = dashArray;
-        ctx.mozDashOffset = dashPhase;
       }
     },
     setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {
       // Maybe if we one day fully support color spaces this will be important
       // for now we can ignore.
       // TODO set rendering intent?
     },
     setFlatness: function CanvasGraphics_setFlatness(flatness) {
@@ -6648,16 +6665,17 @@ var UnexpectedResponseException = shared
 var UnknownErrorException = sharedUtil.UnknownErrorException;
 var Util = sharedUtil.Util;
 var createPromiseCapability = sharedUtil.createPromiseCapability;
 var error = sharedUtil.error;
 var deprecated = sharedUtil.deprecated;
 var getVerbosityLevel = sharedUtil.getVerbosityLevel;
 var info = sharedUtil.info;
 var isInt = sharedUtil.isInt;
+var isArray = sharedUtil.isArray;
 var isArrayBuffer = sharedUtil.isArrayBuffer;
 var isSameOrigin = sharedUtil.isSameOrigin;
 var loadJpegStream = sharedUtil.loadJpegStream;
 var stringToBytes = sharedUtil.stringToBytes;
 var globalScope = sharedUtil.globalScope;
 var warn = sharedUtil.warn;
 var FontFaceObject = displayFontLoader.FontFaceObject;
 var FontLoader = displayFontLoader.FontLoader;
@@ -7641,16 +7659,94 @@ var PDFWorker = (function PDFWorkerClosu
           callback(window.pdfjsDistBuildPdfWorker.WorkerMessageHandler);
         });
       };
       loader(fakeWorkerFilesLoadedCapability.resolve);
     }
     return fakeWorkerFilesLoadedCapability.promise;
   }
 
+  function FakeWorkerPort(defer) {
+    this._listeners = [];
+    this._defer = defer;
+    this._deferred = Promise.resolve(undefined);
+  }
+  FakeWorkerPort.prototype = {
+    postMessage: function (obj, transfers) {
+      function cloneValue(value) {
+        // Trying to perform a structured clone close to the spec, including
+        // transfers.
+        if (typeof value !== 'object' || value === null) {
+          return value;
+        }
+        if (cloned.has(value)) { // already cloned the object
+          return cloned.get(value);
+        }
+        var result;
+        var buffer;
+        if ((buffer = value.buffer) && isArrayBuffer(buffer)) {
+          // We found object with ArrayBuffer (typed array).
+          var transferable = transfers && transfers.indexOf(buffer) >= 0;
+          if (value === buffer) {
+            // Special case when we are faking typed arrays in compatibility.js.
+            result = value;
+          } else if (transferable) {
+            result = new value.constructor(buffer, value.byteOffset,
+                                           value.byteLength);
+          } else {
+            result = new value.constructor(value);
+          }
+          cloned.set(value, result);
+          return result;
+        }
+        result = isArray(value) ? [] : {};
+        cloned.set(value, result); // adding to cache now for cyclic references
+        // Cloning all value and object properties, however ignoring properties
+        // defined via getter.
+        for (var i in value) {
+          var desc, p = value;
+          while (!(desc = Object.getOwnPropertyDescriptor(p, i))) {
+            p = Object.getPrototypeOf(p);
+          }
+          if (typeof desc.value === 'undefined' ||
+              typeof desc.value === 'function') {
+            continue;
+          }
+          result[i] = cloneValue(desc.value);
+        }
+        return result;
+      }
+
+      if (!this._defer) {
+        this._listeners.forEach(function (listener) {
+          listener.call(this, {data: obj});
+        }, this);
+        return;
+      }
+
+      var cloned = new WeakMap();
+      var e = {data: cloneValue(obj)};
+      this._deferred.then(function () {
+        this._listeners.forEach(function (listener) {
+          listener.call(this, e);
+        }, this);
+      }.bind(this));
+    },
+    addEventListener: function (name, listener) {
+      this._listeners.push(listener);
+    },
+    removeEventListener: function (name, listener) {
+      var i = this._listeners.indexOf(listener);
+      this._listeners.splice(i, 1);
+    },
+    terminate: function () {
+      this._listeners = [];
+    }
+  };
+
   function createCDNWrapper(url) {
     // We will rely on blob URL's property to specify origin.
     // We want this function to fail in case if createObjectURL or Blob do not
     // exist or fail for some reason -- our Worker creation will fail anyway.
     var wrapper = 'importScripts(\'' + url + '\');';
     return URL.createObjectURL(new Blob([wrapper]));
   }
 
@@ -7800,34 +7896,21 @@ var PDFWorker = (function PDFWorkerClosu
       }
 
       setupFakeWorkerGlobal().then(function (WorkerMessageHandler) {
         if (this.destroyed) {
           this._readyCapability.reject(new Error('Worker was destroyed'));
           return;
         }
 
-        // If we don't use a worker, just post/sendMessage to the main thread.
-        var port = {
-          _listeners: [],
-          postMessage: function (obj) {
-            var e = {data: obj};
-            this._listeners.forEach(function (listener) {
-              listener.call(this, e);
-            }, this);
-          },
-          addEventListener: function (name, listener) {
-            this._listeners.push(listener);
-          },
-          removeEventListener: function (name, listener) {
-            var i = this._listeners.indexOf(listener);
-            this._listeners.splice(i, 1);
-          },
-          terminate: function () {}
-        };
+        // We cannot turn on proper fake port simulation (this includes
+        // structured cloning) when typed arrays are not supported. Relying
+        // on a chance that messages will be sent in proper order.
+        var isTypedArraysPresent = Uint8Array !== Float32Array;
+        var port = new FakeWorkerPort(isTypedArraysPresent);
         this._port = port;
 
         // All fake workers use the same port, making id unique.
         var id = 'fake' + (nextFakeWorkerId++);
 
         // If the main thread is our worker, setup the handling for the
         // messages -- the main thread sends to it self.
         var workerHandler = new MessageHandler(id + '_worker', id, port);
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -23,18 +23,18 @@ define('pdfjs-dist/build/pdf.worker', ['
     factory(exports);
   } else {
 factory((root.pdfjsDistBuildPdfWorker = {}));
   }
 }(this, function (exports) {
   // Use strict in our context only - users might not want it
   'use strict';
 
-var pdfjsVersion = '1.5.437';
-var pdfjsBuild = 'ca61ccc';
+var pdfjsVersion = '1.5.464';
+var pdfjsBuild = '834a7ff';
 
   var pdfjsFilePath =
     typeof document !== 'undefined' && document.currentScript ?
       document.currentScript.src : null;
 
   var pdfjsLibs = {};
 
   (function pdfjsWrapper() {
@@ -11223,20 +11223,21 @@ exports.JpegImage = JpegImage;
 (function (root, factory) {
   {
     factory((root.pdfjsCoreJpx = {}), root.pdfjsSharedUtil,
       root.pdfjsCoreArithmeticDecoder);
   }
 }(this, function (exports, sharedUtil, coreArithmeticDecoder) {
 
 var info = sharedUtil.info;
+var warn = sharedUtil.warn;
+var error = sharedUtil.error;
 var log2 = sharedUtil.log2;
 var readUint16 = sharedUtil.readUint16;
 var readUint32 = sharedUtil.readUint32;
-var warn = sharedUtil.warn;
 var ArithmeticDecoder = coreArithmeticDecoder.ArithmeticDecoder;
 
 var JpxImage = (function JpxImageClosure() {
   // Table E.1
   var SubbandsGainLog2 = {
     'LL': 0,
     'LH': 1,
     'HL': 1,
@@ -11268,17 +11269,17 @@ var JpxImage = (function JpxImageClosure
                  readUint32(data, position + 4);
           position += 8;
           headerSize += 8;
         }
         if (lbox === 0) {
           lbox = length - position + headerSize;
         }
         if (lbox < headerSize) {
-          throw new Error('JPX Error: Invalid box field size');
+          error('JPX Error: Invalid box field size');
         }
         var dataLength = lbox - headerSize;
         var jumpDataLength = true;
         switch (tbox) {
           case 0x6A703268: // 'jp2h'
             jumpDataLength = false; // parsing child boxes
             break;
           case 0x636F6C72: // 'colr'
@@ -11346,22 +11347,22 @@ var JpxImage = (function JpxImageClosure
           this.width = Xsiz - XOsiz;
           this.height = Ysiz - YOsiz;
           this.componentsCount = Csiz;
           // Results are always returned as Uint8Arrays
           this.bitsPerComponent = 8;
           return;
         }
       }
-      throw new Error('JPX Error: No size marker found in JPX stream');
+      error('JPX Error: No size marker found in JPX stream');
     },
     parseCodestream: function JpxImage_parseCodestream(data, start, end) {
       var context = {};
+      var doNotRecover = false;
       try {
-        var doNotRecover = false;
         var position = start;
         while (position + 1 < end) {
           var code = readUint16(data, position);
           position += 2;
 
           var length = 0, j, sqcd, spqcds, spqcdSize, scalarExpounded, tile;
           switch (code) {
             case 0xFF4F: // Start of codestream (SOC)
@@ -11414,17 +11415,17 @@ var JpxImage = (function JpxImageClosure
                   spqcdSize = 16;
                   scalarExpounded = false;
                   break;
                 case 2:
                   spqcdSize = 16;
                   scalarExpounded = true;
                   break;
                 default:
-                  throw new Error('JPX Error: Invalid SQcd value ' + sqcd);
+                  throw new Error('Invalid SQcd value ' + sqcd);
               }
               qcd.noQuantization = (spqcdSize === 8);
               qcd.scalarExpounded = scalarExpounded;
               qcd.guardBits = sqcd >> 5;
               spqcds = [];
               while (j < length + position) {
                 var spqcd = {};
                 if (spqcdSize === 8) {
@@ -11466,17 +11467,17 @@ var JpxImage = (function JpxImageClosure
                   spqcdSize = 16;
                   scalarExpounded = false;
                   break;
                 case 2:
                   spqcdSize = 16;
                   scalarExpounded = true;
                   break;
                 default:
-                  throw new Error('JPX Error: Invalid SQcd value ' + sqcd);
+                  throw new Error('Invalid SQcd value ' + sqcd);
               }
               qcc.noQuantization = (spqcdSize === 8);
               qcc.scalarExpounded = scalarExpounded;
               qcc.guardBits = sqcd >> 5;
               spqcds = [];
               while (j < (length + position)) {
                 spqcd = {};
                 if (spqcdSize === 8) {
@@ -11544,17 +11545,17 @@ var JpxImage = (function JpxImageClosure
               if (cod.verticalyStripe) {
                 unsupported.push('verticalyStripe');
               }
               if (cod.predictableTermination) {
                 unsupported.push('predictableTermination');
               }
               if (unsupported.length > 0) {
                 doNotRecover = true;
-                throw new Error('JPX Error: Unsupported COD options (' +
+                throw new Error('Unsupported COD options (' +
                                 unsupported.join(', ') + ')');
               }
               if (context.mainHeader) {
                 context.COD = cod;
               } else {
                 context.currentTile.COD = cod;
                 context.currentTile.COC = [];
               }
@@ -11592,29 +11593,28 @@ var JpxImage = (function JpxImageClosure
             case 0xFF55: // Tile-part lengths, main header (TLM)
             case 0xFF57: // Packet length, main header (PLM)
             case 0xFF58: // Packet length, tile-part header (PLT)
             case 0xFF64: // Comment (COM)
               length = readUint16(data, position);
               // skipping content
               break;
             case 0xFF53: // Coding style component (COC)
-              throw new Error('JPX Error: Codestream code 0xFF53 (COC) is ' +
+              throw new Error('Codestream code 0xFF53 (COC) is ' +
                               'not implemented');
             default:
-              throw new Error('JPX Error: Unknown codestream code: ' +
-                              code.toString(16));
+              throw new Error('Unknown codestream code: ' + code.toString(16));
           }
           position += length;
         }
       } catch (e) {
         if (doNotRecover || this.failOnCorruptedImage) {
-          throw e;
-        } else {
-          warn('Trying to recover from ' + e.message);
+          error('JPX Error: ' + e.message);
+        } else {
+          warn('JPX: Trying to recover from: ' + e.message);
         }
       }
       this.tiles = transformComponents(context);
       this.width = context.SIZ.Xsiz - context.SIZ.XOsiz;
       this.height = context.SIZ.Ysiz - context.SIZ.YOsiz;
       this.componentsCount = context.SIZ.Csiz;
     }
   };
@@ -11854,17 +11854,17 @@ var JpxImage = (function JpxImageClosure
               return packet;
             }
             k = 0;
           }
           i = 0;
         }
         r = 0;
       }
-      throw new Error('JPX Error: Out of packets');
+      error('JPX Error: Out of packets');
     };
   }
   function ResolutionLayerComponentPositionIterator(context) {
     var siz = context.SIZ;
     var tileIndex = context.currentTile.index;
     var tile = context.tiles[tileIndex];
     var layersCount = tile.codingStyleDefaultParameters.layersCount;
     var componentsCount = siz.Csiz;
@@ -11894,17 +11894,17 @@ var JpxImage = (function JpxImageClosure
               return packet;
             }
             k = 0;
           }
           i = 0;
         }
         l = 0;
       }
-      throw new Error('JPX Error: Out of packets');
+      error('JPX Error: Out of packets');
     };
   }
   function ResolutionPositionComponentLayerIterator(context) {
     var siz = context.SIZ;
     var tileIndex = context.currentTile.index;
     var tile = context.tiles[tileIndex];
     var layersCount = tile.codingStyleDefaultParameters.layersCount;
     var componentsCount = siz.Csiz;
@@ -11953,17 +11953,17 @@ var JpxImage = (function JpxImageClosure
               return packet;
             }
             l = 0;
           }
           c = 0;
         }
         p = 0;
       }
-      throw new Error('JPX Error: Out of packets');
+      error('JPX Error: Out of packets');
     };
   }
   function PositionComponentResolutionLayerIterator(context) {
     var siz = context.SIZ;
     var tileIndex = context.currentTile.index;
     var tile = context.tiles[tileIndex];
     var layersCount = tile.codingStyleDefaultParameters.layersCount;
     var componentsCount = siz.Csiz;
@@ -12000,17 +12000,17 @@ var JpxImage = (function JpxImageClosure
               l = 0;
             }
             r = 0;
           }
           c = 0;
         }
         px = 0;
       }
-      throw new Error('JPX Error: Out of packets');
+      error('JPX Error: Out of packets');
     };
   }
   function ComponentPositionResolutionLayerIterator(context) {
     var siz = context.SIZ;
     var tileIndex = context.currentTile.index;
     var tile = context.tiles[tileIndex];
     var layersCount = tile.codingStyleDefaultParameters.layersCount;
     var componentsCount = siz.Csiz;
@@ -12046,17 +12046,17 @@ var JpxImage = (function JpxImageClosure
               l = 0;
             }
             r = 0;
           }
           px = 0;
         }
         py = 0;
       }
-      throw new Error('JPX Error: Out of packets');
+      error('JPX Error: Out of packets');
     };
   }
   function getPrecinctIndexIfExist(
     pxIndex, pyIndex, sizeInImageScale, precinctIterationSizes, resolution) {
     var posX = pxIndex * precinctIterationSizes.minWidth;
     var posY = pyIndex * precinctIterationSizes.minHeight;
     if (posX % sizeInImageScale.width !== 0 ||
         posY % sizeInImageScale.height !== 0) {
@@ -12226,18 +12226,17 @@ var JpxImage = (function JpxImageClosure
         tile.packetsIterator =
           new PositionComponentResolutionLayerIterator(context);
         break;
       case 4:
         tile.packetsIterator =
           new ComponentPositionResolutionLayerIterator(context);
         break;
       default:
-        throw new Error('JPX Error: Unsupported progression order ' +
-                        progressionOrder);
+        error('JPX Error: Unsupported progression order ' + progressionOrder);
     }
   }
   function parseTilePackets(context, data, offset, dataLength) {
     var position = 0;
     var buffer, bufferSize = 0, skipNextBit = false;
     function readBits(count) {
       while (bufferSize < count) {
         var b = data[offset + position];
@@ -13164,17 +13163,17 @@ var JpxImage = (function JpxImageClosure
       checkSegmentationSymbol: function BitModel_checkSegmentationSymbol() {
         var decoder = this.decoder;
         var contexts = this.contexts;
         var symbol = (decoder.readBit(contexts, UNIFORM_CONTEXT) << 3) |
                      (decoder.readBit(contexts, UNIFORM_CONTEXT) << 2) |
                      (decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
                       decoder.readBit(contexts, UNIFORM_CONTEXT);
         if (symbol !== 0xA) {
-          throw new Error('JPX Error: Invalid segmentation symbol');
+          error('JPX Error: Invalid segmentation symbol');
         }
       }
     };
 
     return BitModel;
   })();
 
   // Section F, Discrete wavelet transformation
@@ -27470,22 +27469,27 @@ var Font = (function FontClosure() {
       }
       this.loadedName = fontName.split('-')[0];
       this.loading = false;
       this.fontType = getFontType(type, subtype);
       return;
     }
 
     // Some fonts might use wrong font types for Type1C or CIDFontType0C
-    if (subtype === 'Type1C' && (type !== 'Type1' && type !== 'MMType1')) {
-      // Some TrueType fonts by mistake claim Type1C
-      if (isTrueTypeFile(file)) {
-        subtype = 'TrueType';
-      } else {
-        type = 'Type1';
+    if (subtype === 'Type1C') {
+      if (type !== 'Type1' && type !== 'MMType1') {
+        // Some TrueType fonts by mistake claim Type1C
+        if (isTrueTypeFile(file)) {
+          subtype = 'TrueType';
+        } else {
+          type = 'Type1';
+        }
+      } else if (isOpenTypeFile(file)) {
+        // Sometimes the type/subtype can be a complete lie (see issue7598.pdf).
+        type = subtype = 'OpenType';
       }
     }
     if (subtype === 'CIDFontType0C' && type !== 'CIDFontType0') {
       type = 'CIDFontType0';
     }
     if (subtype === 'OpenType') {
       type = 'OpenType';
     }
@@ -29153,17 +29157,20 @@ var Font = (function FontClosure() {
             glyphId = cidToGidMap[cid];
           }
 
           if (glyphId >= 0 && glyphId < numGlyphs &&
               hasGlyph(glyphId, charCode, cid)) {
             charCodeToGlyphId[charCode] = glyphId;
           }
         });
-        if (dupFirstEntry) {
+        if (dupFirstEntry && (isCidToGidMapEmpty || !charCodeToGlyphId[0])) {
+          // We don't duplicate the first entry in the `charCodeToGlyphId` map
+          // if the font has a `CIDToGIDMap` which has already mapped the first
+          // entry to a non-zero `glyphId` (fixes issue7544.pdf).
           charCodeToGlyphId[0] = numGlyphs - 1;
         }
       } else {
         // Most of the following logic in this code branch is based on the
         // 9.6.6.4 of the PDF spec.
         var hasEncoding =
           properties.differences.length > 0 || !!properties.baseEncodingName;
         var cmapTable =
@@ -39812,18 +39819,29 @@ var WidgetAnnotation = (function WidgetA
 
   return WidgetAnnotation;
 })();
 
 var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
   function TextWidgetAnnotation(params) {
     WidgetAnnotation.call(this, params);
 
-    this.data.textAlignment = Util.getInheritableProperty(params.dict, 'Q');
-    this.data.maxLen = Util.getInheritableProperty(params.dict, 'MaxLen');
+    // Determine the alignment of text in the field.
+    var alignment = Util.getInheritableProperty(params.dict, 'Q');
+    if (!isInt(alignment) || alignment < 0 || alignment > 2) {
+      alignment = null;
+    }
+    this.data.textAlignment = alignment;
+
+    // Determine the maximum length of text in the field.
+    var maximumLength = Util.getInheritableProperty(params.dict, 'MaxLen');
+    if (!isInt(maximumLength) || maximumLength < 0) {
+      maximumLength = null;
+    }
+    this.data.maxLen = maximumLength;
   }
 
   Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
     getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator,
                                                                    task) {
       if (this.appearance) {
         return Annotation.prototype.getOperatorList.call(this, evaluator, task);
       }
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -947,16 +947,17 @@ exports.PDFRenderingQueue = PDFRendering
   "pdfBugEnabled": false,
   "disableRange": false,
   "disableStream": false,
   "disableAutoFetch": false,
   "disableFontFace": false,
   "disableTextLayer": false,
   "useOnlyCssZoom": false,
   "externalLinkTarget": 0,
+  "enhanceTextSelection": false,
   "renderInteractiveForms": false
 }
 
   );
 
 function cloneObj(obj) {
   var result = {};
   for (var i in obj) {
@@ -3828,16 +3829,19 @@ exports.PDFThumbnailView = PDFThumbnailV
 var SCROLLBAR_PADDING = uiUtils.SCROLLBAR_PADDING;
 var mozL10n = uiUtils.mozL10n;
 
 /**
  * @typedef {Object} SecondaryToolbarOptions
  * @property {HTMLDivElement} toolbar - Container for the secondary toolbar.
  * @property {HTMLButtonElement} toggleButton - Button to toggle the visibility
  *   of the secondary toolbar.
+ * @property {HTMLDivElement} toolbarButtonContainer - Container where all the
+ *   toolbar buttons are placed. The maximum height of the toolbar is controlled
+ *   dynamically by adjusting the 'max-height' CSS property of this DOM element.
  * @property {HTMLButtonElement} presentationModeButton - Button for entering
  *   presentation mode.
  * @property {HTMLButtonElement} openFileButton - Button to open a file.
  * @property {HTMLButtonElement} printButton - Button to print the document.
  * @property {HTMLButtonElement} downloadButton - Button to download the
  *   document.
  * @property {HTMLLinkElement} viewBookmarkButton - Button to obtain a bookmark
  *   link to the current location in the document.
@@ -3857,21 +3861,23 @@ var mozL10n = uiUtils.mozL10n;
 
 /**
  * @class
  */
 var SecondaryToolbar = (function SecondaryToolbarClosure() {
   /**
    * @constructs SecondaryToolbar
    * @param {SecondaryToolbarOptions} options
+   * @param {HTMLDivElement} mainContainer
    * @param {EventBus} eventBus
    */
-  function SecondaryToolbar(options, eventBus) {
+  function SecondaryToolbar(options, mainContainer, eventBus) {
     this.toolbar = options.toolbar;
     this.toggleButton = options.toggleButton;
+    this.toolbarButtonContainer = options.toolbarButtonContainer;
     this.buttons = [
       { element: options.presentationModeButton, eventName: 'presentationmode',
         close: true },
       { element: options.openFileButton, eventName: 'openfile', close: true },
       { element: options.printButton, eventName: 'print', close: true },
       { element: options.downloadButton, eventName: 'download', close: true },
       { element: options.viewBookmarkButton, eventName: null, close: true },
       { element: options.firstPageButton, eventName: 'firstpage', close: true },
@@ -3881,26 +3887,29 @@ var SecondaryToolbar = (function Seconda
       { element: options.pageRotateCcwButton, eventName: 'rotateccw',
         close: false },
       { element: options.toggleHandToolButton, eventName: 'togglehandtool',
         close: true },
       { element: options.documentPropertiesButton,
         eventName: 'documentproperties', close: true }
     ];
 
+    this.mainContainer = mainContainer;
     this.eventBus = eventBus;
 
     this.opened = false;
+    this.containerHeight = null;
     this.previousContainerHeight = null;
-    this.newContainerHeight = null;
-    this.buttonContainer = this.toolbar.firstElementChild;
 
     // Bind the event listeners for click and hand tool actions.
     this._bindClickListeners();
     this._bindHandToolListener(options.toggleHandToolButton);
+
+    // Bind the event listener for adjusting the 'max-height' of the toolbar.
+    this.eventBus.on('resize', this._setMaxHeight.bind(this));
   }
 
   SecondaryToolbar.prototype = {
     /**
      * @return {boolean}
      */
     get isOpen() {
       return this.opened;
@@ -3913,17 +3922,17 @@ var SecondaryToolbar = (function Seconda
       // All items within the secondary toolbar.
       for (var button in this.buttons) {
         var element = this.buttons[button].element;
         var eventName = this.buttons[button].eventName;
         var close = this.buttons[button].close;
 
         element.addEventListener('click', function (eventName, close) {
           if (eventName !== null) {
-            this.eventBus.dispatch(eventName);
+            this.eventBus.dispatch(eventName, { source: this, });
           }
           if (close) {
             this.close();
           }
         }.bind(this, eventName, close));
       }
     },
 
@@ -3949,16 +3958,18 @@ var SecondaryToolbar = (function Seconda
       }.bind(this));
     },
 
     open: function SecondaryToolbar_open() {
       if (this.opened) {
         return;
       }
       this.opened = true;
+      this._setMaxHeight();
+
       this.toggleButton.classList.add('toggled');
       this.toolbar.classList.remove('hidden');
     },
 
     close: function SecondaryToolbar_close() {
       if (!this.opened) {
         return;
       }
@@ -3970,28 +3981,32 @@ var SecondaryToolbar = (function Seconda
     toggle: function SecondaryToolbar_toggle() {
       if (this.opened) {
         this.close();
       } else {
         this.open();
       }
     },
 
-    setMaxHeight: function SecondaryToolbar_setMaxHeight(container) {
-      if (!container || !this.buttonContainer) {
+    /**
+     * @private
+     */
+    _setMaxHeight: function SecondaryToolbar_setMaxHeight() {
+      if (!this.opened) {
+        return; // Only adjust the 'max-height' if the toolbar is visible.
+      }
+      this.containerHeight = this.mainContainer.clientHeight;
+
+      if (this.containerHeight === this.previousContainerHeight) {
         return;
       }
-      this.newContainerHeight = container.clientHeight;
-      if (this.previousContainerHeight === this.newContainerHeight) {
-        return;
-      }
-      var maxHeight = this.newContainerHeight - SCROLLBAR_PADDING;
-      this.buttonContainer.setAttribute('style',
-        'max-height: ' + maxHeight + 'px;');
-      this.previousContainerHeight = this.newContainerHeight;
+      this.toolbarButtonContainer.setAttribute('style',
+        'max-height: ' + (this.containerHeight - SCROLLBAR_PADDING) + 'px;');
+
+      this.previousContainerHeight = this.containerHeight;
     }
   };
 
   return SecondaryToolbar;
 })();
 
 exports.SecondaryToolbar = SecondaryToolbar;
 }));
@@ -7150,17 +7165,16 @@ var getGlobalEventBus = domEventsLib.get
 
 var DEFAULT_SCALE_DELTA = 1.1;
 var MIN_SCALE = 0.25;
 var MAX_SCALE = 10.0;
 var SCALE_SELECT_CONTAINER_PADDING = 8;
 var SCALE_SELECT_PADDING = 22;
 var PAGE_NUMBER_LOADING_INDICATOR = 'visiblePageIsLoading';
 var DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000;
-var ENHANCE_TEXT_SELECTION = false;
 
 function configure(PDFJS) {
   PDFJS.imageResourcesPath = './images/';
   PDFJS.workerSrc = '../build/pdf.worker.js';
   PDFJS.cMapUrl = '../web/cmaps/';
   PDFJS.cMapPacked = true;
 }
 
@@ -7252,17 +7266,17 @@ var PDFViewerApplication = {
     var viewer = appConfig.viewerContainer;
     this.pdfViewer = new PDFViewer({
       container: container,
       viewer: viewer,
       eventBus: eventBus,
       renderingQueue: pdfRenderingQueue,
       linkService: pdfLinkService,
       downloadManager: downloadManager,
-      enhanceTextSelection: ENHANCE_TEXT_SELECTION,
+      enhanceTextSelection: false,
     });
     pdfRenderingQueue.setViewer(this.pdfViewer);
     pdfLinkService.setViewer(this.pdfViewer);
 
     var thumbnailContainer = appConfig.sidebar.thumbnailView;
     this.pdfThumbnailViewer = new PDFThumbnailViewer({
       container: thumbnailContainer,
       renderingQueue: pdfRenderingQueue,
@@ -7311,17 +7325,17 @@ var PDFViewerApplication = {
       container: container,
       eventBus: this.eventBus,
     });
 
     this.pdfDocumentProperties =
       new PDFDocumentProperties(appConfig.documentProperties);
 
     this.secondaryToolbar =
-      new SecondaryToolbar(appConfig.secondaryToolbar, eventBus);
+      new SecondaryToolbar(appConfig.secondaryToolbar, container, eventBus);
 
     if (this.supportsFullscreen) {
       this.pdfPresentationMode = new PDFPresentationMode({
         container: container,
         viewer: viewer,
         pdfViewer: this.pdfViewer,
         eventBus: this.eventBus,
         contextMenuItems: appConfig.fullscreen
@@ -7364,16 +7378,28 @@ var PDFViewerApplication = {
         self.preferencePdfBugEnabled = value;
       }),
       Preferences.get('showPreviousViewOnLoad').then(function resolved(value) {
         self.preferenceShowPreviousViewOnLoad = value;
       }),
       Preferences.get('defaultZoomValue').then(function resolved(value) {
         self.preferenceDefaultZoomValue = value;
       }),
+      Preferences.get('enhanceTextSelection').then(function resolved(value) {
+        // TODO: Move the initialization and fetching of `Preferences` to occur
+        //       before the various viewer components are initialized.
+        //
+        // This was attempted in: https://github.com/mozilla/pdf.js/pull/7586,
+        // but it had to be backed out since it violated implicit assumptions
+        // about some viewer components being synchronously available.
+        //
+        // NOTE: This hack works since the `enhanceTextSelection` option is not
+        //       needed until `PDFViewer.setDocument` has been called.
+        self.pdfViewer.enhanceTextSelection = value;
+      }),
       Preferences.get('disableTextLayer').then(function resolved(value) {
         if (PDFJS.disableTextLayer === true) {
           return;
         }
         PDFJS.disableTextLayer = value;
       }),
       Preferences.get('disableRange').then(function resolved(value) {
         if (PDFJS.disableRange === true) {
@@ -8634,20 +8660,16 @@ function webViewerResize() {
     } else if (!currentScaleValue) {
       // Normally this shouldn't happen, but if the scale wasn't initialized
       // we set it to the default value in order to prevent any issues.
       // (E.g. the document being rendered with the wrong scale on load.)
       PDFViewerApplication.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
     }
     PDFViewerApplication.pdfViewer.update();
   }
-
-  // Set the 'max-height' CSS property of the secondary toolbar.
-  var mainContainer = PDFViewerApplication.appConfig.mainContainer;
-  PDFViewerApplication.secondaryToolbar.setMaxHeight(mainContainer);
 }
 
 window.addEventListener('hashchange', function webViewerHashchange(evt) {
   var hash = document.location.hash.substring(1);
   PDFViewerApplication.eventBus.dispatch('hashchange', {hash: hash});
 });
 
 function webViewerHashchange(e) {
@@ -8684,20 +8706,16 @@ function webViewerLocalized() {
       var select = PDFViewerApplication.appConfig.toolbar.scaleSelect;
       select.setAttribute('style', 'min-width: inherit;');
       var width = select.clientWidth + SCALE_SELECT_CONTAINER_PADDING;
       select.setAttribute('style', 'min-width: ' +
                                    (width + SCALE_SELECT_PADDING) + 'px;');
       container.setAttribute('style', 'min-width: ' + width + 'px; ' +
                                       'max-width: ' + width + 'px;');
     }
-
-    // Set the 'max-height' CSS property of the secondary toolbar.
-    var mainContainer = PDFViewerApplication.appConfig.mainContainer;
-    PDFViewerApplication.secondaryToolbar.setMaxHeight(mainContainer);
   });
 }
 
 function webViewerPresentationMode() {
   PDFViewerApplication.requestPresentationMode();
 }
 function webViewerOpenFile() {
   var openFileInputName = PDFViewerApplication.appConfig.openFileInputName;
@@ -9412,16 +9430,18 @@ function getViewerConfiguration() {
       print: document.getElementById('print'),
       presentationModeButton: document.getElementById('presentationMode'),
       download: document.getElementById('download'),
       viewBookmark: document.getElementById('viewBookmark'),
     },
     secondaryToolbar: {
       toolbar: document.getElementById('secondaryToolbar'),
       toggleButton: document.getElementById('secondaryToolbarToggle'),
+      toolbarButtonContainer:
+        document.getElementById('secondaryToolbarButtonContainer'),
       presentationModeButton:
         document.getElementById('secondaryPresentationMode'),
       openFileButton: document.getElementById('secondaryOpenFile'),
       printButton: document.getElementById('secondaryPrint'),
       downloadButton: document.getElementById('secondaryDownload'),
       viewBookmarkButton: document.getElementById('secondaryViewBookmark'),
       firstPageButton: document.getElementById('firstPage'),
       lastPageButton: document.getElementById('lastPage'),