Bug 1315421 - Update pdf.js to version 1.6.304. r=bdahl
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 04 Nov 2016 22:50:14 -0400
changeset 351316 fc8665420489fe577191a430798cf494f8b762af
parent 351315 41d172f46ee99f38dbbd5d874f06fbce81f64c44
child 351317 87ae1185df39806f84a0b6069eddcc24febf2bd2
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbdahl
bugs1315421
milestone52.0a1
Bug 1315421 - Update pdf.js to version 1.6.304. r=bdahl
browser/extensions/pdfjs/README.mozilla
browser/extensions/pdfjs/content/PdfJs.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.css
browser/extensions/pdfjs/content/web/viewer.html
browser/extensions/pdfjs/content/web/viewer.js
browser/locales/en-US/pdfviewer/viewer.properties
--- 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.6.274
+Current extension version is: 1.6.304
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -88,17 +88,18 @@ function initializeDefaultPreferences() 
   "disableRange": false,
   "disableStream": false,
   "disableAutoFetch": false,
   "disableFontFace": false,
   "disableTextLayer": false,
   "useOnlyCssZoom": false,
   "externalLinkTarget": 0,
   "enhanceTextSelection": false,
-  "renderInteractiveForms": false
+  "renderInteractiveForms": false,
+  "disablePageLabels": false
 }
 
 
   var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.');
   var defaultValue;
   for (var key in DEFAULT_PREFERENCES) {
     defaultValue = DEFAULT_PREFERENCES[key];
     switch (typeof defaultValue) {
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -46,17 +46,18 @@ var DEFAULT_PREFERENCES =
   "disableRange": false,
   "disableStream": false,
   "disableAutoFetch": false,
   "disableFontFace": false,
   "disableTextLayer": false,
   "useOnlyCssZoom": false,
   "externalLinkTarget": 0,
   "enhanceTextSelection": false,
-  "renderInteractiveForms": false
+  "renderInteractiveForms": false,
+  "disablePageLabels": false
 }
 
 
 var PdfjsChromeUtils = {
   // For security purposes when running remote, we restrict preferences
   // content can access.
   _allowedPrefNames: Object.keys(DEFAULT_PREFERENCES),
   _ppmm: null,
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -19,18 +19,18 @@
   } else if (typeof exports !== 'undefined') {
     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.6.274';
-  var pdfjsBuild = '1c3fb17';
+  var pdfjsVersion = '1.6.304';
+  var pdfjsBuild = 'b4100ba';
   var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
   var pdfjsLibs = {};
   (function pdfjsWrapper() {
     (function (root, factory) {
       factory(root.pdfjsSharedUtil = {});
     }(this, function (exports) {
       var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
       var FONT_IDENTITY_MATRIX = [
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -19,18 +19,18 @@
   } else if (typeof exports !== 'undefined') {
     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.6.274';
-  var pdfjsBuild = '1c3fb17';
+  var pdfjsVersion = '1.6.304';
+  var pdfjsBuild = 'b4100ba';
   var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
   var pdfjsLibs = {};
   (function pdfjsWrapper() {
     (function (root, factory) {
       factory(root.pdfjsCoreArithmeticDecoder = {});
     }(this, function (exports) {
       /* This class implements the QM Coder decoding as defined in
        *   JPEG 2000 Part I Final Committee Draft Version 1.0
@@ -5974,19 +5974,19 @@
                 return value;
               } else if (value >= 32 && value <= 246) {
                 return value - 139;
               } else if (value >= 247 && value <= 250) {
                 return (value - 247) * 256 + dict[pos++] + 108;
               } else if (value >= 251 && value <= 254) {
                 return -((value - 251) * 256) - dict[pos++] - 108;
               } else {
-                error('255 is not a valid DICT command');
-              }
-              return -1;
+                warn('CFFParser_parseDict: "' + value + '" is a reserved command.');
+                return NaN;
+              }
             }
             function parseFloatOperand() {
               var str = '';
               var eof = 15;
               var lookup = [
                 '0',
                 '1',
                 '2',
@@ -6615,29 +6615,32 @@
           this.values = Object.create(null);
         }
         CFFDict.prototype = {
           // value should always be an array
           setByKey: function CFFDict_setByKey(key, value) {
             if (!(key in this.keyToNameMap)) {
               return false;
             }
+            var valueLength = value.length;
             // ignore empty values
-            if (value.length === 0) {
+            if (valueLength === 0) {
               return true;
             }
+            // Ignore invalid values (fixes bug1068432.pdf and bug1308536.pdf).
+            for (var i = 0; i < valueLength; i++) {
+              if (isNaN(value[i])) {
+                warn('Invalid CFFDict value: "' + value + '" for key "' + key + '".');
+                return true;
+              }
+            }
             var type = this.types[key];
             // remove the array wrapping these types of values
             if (type === 'num' || type === 'sid' || type === 'offset') {
               value = value[0];
-              // Ignore invalid values (fixes bug 1068432).
-              if (isNaN(value)) {
-                warn('Invalid CFFDict value: ' + value + ', for key: ' + key + '.');
-                return true;
-              }
             }
             this.values[key] = value;
             return true;
           },
           setByName: function CFFDict_setByName(name, value) {
             if (!(name in this.nameToKeyMap)) {
               error('Invalid dictionary name "' + name + '"');
             }
@@ -44586,16 +44589,17 @@
               var color = outlineDict.getArray('C'), rgbColor = blackColor;
               // We only need to parse the color when it's valid, and non-default.
               if (isArray(color) && color.length === 3 && (color[0] !== 0 || color[1] !== 0 || color[2] !== 0)) {
                 rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0);
               }
               var outlineItem = {
                 dest: data.dest,
                 url: data.url,
+                unsafeUrl: data.unsafeUrl,
                 newWindow: data.newWindow,
                 title: stringToPDFString(title),
                 color: rgbColor,
                 count: outlineDict.get('Count'),
                 bold: !!(flags & 2),
                 italic: !!(flags & 1),
                 items: []
               };
@@ -44696,34 +44700,34 @@
           readPageLabels: function Catalog_readPageLabels() {
             var obj = this.catDict.getRaw('PageLabels');
             if (!obj) {
               return null;
             }
             var pageLabels = new Array(this.numPages);
             var style = null;
             var prefix = '';
-            var start = 1;
             var numberTree = new NumberTree(obj, this.xref);
             var nums = numberTree.getAll();
             var currentLabel = '', currentIndex = 1;
             for (var i = 0, ii = this.numPages; i < ii; i++) {
               if (i in nums) {
                 var labelDict = nums[i];
                 assert(isDict(labelDict), 'The PageLabel is not a dictionary.');
                 var type = labelDict.get('Type');
                 assert(!type || isName(type, 'PageLabel'), 'Invalid type in PageLabel dictionary.');
                 var s = labelDict.get('S');
                 assert(!s || isName(s), 'Invalid style in PageLabel dictionary.');
                 style = s ? s.name : null;
-                prefix = labelDict.get('P') || '';
-                assert(isString(prefix), 'Invalid prefix in PageLabel dictionary.');
-                start = labelDict.get('St') || 1;
-                assert(isInt(start), 'Invalid start in PageLabel dictionary.');
-                currentIndex = start;
+                var p = labelDict.get('P');
+                assert(!p || isString(p), 'Invalid prefix in PageLabel dictionary.');
+                prefix = p ? stringToPDFString(p) : '';
+                var st = labelDict.get('St');
+                assert(!st || isInt(st) && st >= 1, 'Invalid start in PageLabel dictionary.');
+                currentIndex = st || 1;
               }
               switch (style) {
               case 'D':
                 currentLabel = currentIndex;
                 break;
               case 'R':
               case 'r':
                 currentLabel = Util.toRoman(currentIndex, style === 'r');
@@ -45027,16 +45031,20 @@
                 url = addDefaultProtocolToUrl(url);
               }
               // TODO: pdf spec mentions urls can be relative to a Base
               // entry in the dictionary.
               break;
             case 'GoTo':
               dest = action.get('D');
               break;
+            case 'Launch':
+            // We neither want, nor can, support arbitrary 'Launch' actions.
+            // However, in practice they are mostly used for linking to other PDF
+            // files, which we thus attempt to support (utilizing `docBaseUrl`).
             case 'GoToR':
               var urlDict = action.get('F');
               if (isDict(urlDict)) {
                 // We assume that we found a FileSpec dictionary
                 // and fetch the URL without checking any further.
                 url = urlDict.get('F') || null;
               } else if (isString(urlDict)) {
                 url = urlDict;
@@ -50739,66 +50747,71 @@
         return AnnotationBorderStyle;
       }();
       var WidgetAnnotation = function WidgetAnnotationClosure() {
         function WidgetAnnotation(params) {
           Annotation.call(this, params);
           var dict = params.dict;
           var data = this.data;
           data.annotationType = AnnotationType.WIDGET;
+          data.fieldName = this._constructFieldName(dict);
           data.fieldValue = Util.getInheritableProperty(dict, 'V', /* getArray = */
           true);
           data.alternativeText = stringToPDFString(dict.get('TU') || '');
           data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';
           var fieldType = Util.getInheritableProperty(dict, 'FT');
           data.fieldType = isName(fieldType) ? fieldType.name : null;
           this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty;
           data.fieldFlags = Util.getInheritableProperty(dict, 'Ff');
           if (!isInt(data.fieldFlags) || data.fieldFlags < 0) {
             data.fieldFlags = 0;
           }
           data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
           // Hide signatures because we cannot validate them.
           if (data.fieldType === 'Sig') {
             this.setFlags(AnnotationFlag.HIDDEN);
           }
-          // Building the full field name by collecting the field and
-          // its ancestors 'T' data and joining them using '.'.
-          var fieldName = [];
-          var namedItem = dict;
-          var ref = params.ref;
-          while (namedItem) {
-            var parent = namedItem.get('Parent');
-            var parentRef = namedItem.getRaw('Parent');
-            var name = namedItem.get('T');
-            if (name) {
-              fieldName.unshift(stringToPDFString(name));
-            } else if (parent && ref) {
-              // The field name is absent, that means more than one field
-              // with the same name may exist. Replacing the empty name
-              // with the '`' plus index in the parent's 'Kids' array.
-              // This is not in the PDF spec but necessary to id the
-              // the input controls.
-              var kids = parent.get('Kids');
-              var j, jj;
-              for (j = 0, jj = kids.length; j < jj; j++) {
-                var kidRef = kids[j];
-                if (kidRef.num === ref.num && kidRef.gen === ref.gen) {
-                  break;
-                }
-              }
-              fieldName.unshift('`' + j);
-            }
-            namedItem = parent;
-            ref = parentRef;
-          }
-          data.fullName = fieldName.join('.');
         }
         Util.inherit(WidgetAnnotation, Annotation, {
           /**
+           * Construct the (fully qualified) field name from the (partial) field
+           * names of the field and its ancestors.
+           *
+           * @private
+           * @memberof WidgetAnnotation
+           * @param {Dict} dict - Complete widget annotation dictionary
+           * @return {string}
+           */
+          _constructFieldName: function WidgetAnnotation_constructFieldName(dict) {
+            // Both the `Parent` and `T` fields are optional. While at least one of
+            // them should be provided, bad PDF generators may fail to do so.
+            if (!dict.has('T') && !dict.has('Parent')) {
+              warn('Unknown field name, falling back to empty field name.');
+              return '';
+            }
+            // If no parent exists, the partial and fully qualified names are equal.
+            if (!dict.has('Parent')) {
+              return stringToPDFString(dict.get('T'));
+            }
+            // Form the fully qualified field name by appending the partial name to
+            // the parent's fully qualified name, separated by a period.
+            var fieldName = [];
+            if (dict.has('T')) {
+              fieldName.unshift(stringToPDFString(dict.get('T')));
+            }
+            var loopDict = dict;
+            while (loopDict.has('Parent')) {
+              loopDict = loopDict.get('Parent');
+              if (loopDict.has('T')) {
+                fieldName.unshift(stringToPDFString(loopDict.get('T')));
+              }
+            }
+            return fieldName.join('.');
+          },
+          /**
            * Check if a provided field flag is set.
            *
            * @public
            * @memberof WidgetAnnotation
            * @param {number} flag - Hexadecimal representation for an annotation
            *                        field characteristic
            * @return {boolean}
            * @see {@link shared/util.js}
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -2064,12 +2064,12 @@ html[dir='rtl'] #documentPropertiesOverl
     right: 156px;
   }
   .toolbarButtonSpacer {
     width: 0;
   }
 }
 
 @media all and (max-width: 510px) {
-  #scaleSelectContainer, #pageNumberLabel {
+  #scaleSelectContainer {
     display: none;
   }
 }
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -160,18 +160,17 @@ See https://github.com/adobe-type-tools/
                   <button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="13" data-l10n-id="previous">
                     <span data-l10n-id="previous_label">Previous</span>
                   </button>
                   <div class="splitToolbarButtonSeparator"></div>
                   <button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="14" data-l10n-id="next">
                     <span data-l10n-id="next_label">Next</span>
                   </button>
                 </div>
-                <label id="pageNumberLabel" class="toolbarLabel" for="pageNumber" data-l10n-id="page_label">Page: </label>
-                <input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="15">
+                <input type="number" id="pageNumber" class="toolbarField pageNumber" title="Page" value="1" size="4" min="1" tabindex="15" data-l10n-id="page">
                 <span id="numPages" class="toolbarLabel"></span>
               </div>
               <div id="toolbarViewerRight">
                 <button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="31" data-l10n-id="presentation_mode">
                   <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
                 </button>
 
                 <button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="32" data-l10n-id="open_file">
@@ -202,17 +201,17 @@ See https://github.com/adobe-type-tools/
                       <span data-l10n-id="zoom_out_label">Zoom Out</span>
                     </button>
                     <div class="splitToolbarButtonSeparator"></div>
                     <button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="22" data-l10n-id="zoom_in">
                       <span data-l10n-id="zoom_in_label">Zoom In</span>
                      </button>
                   </div>
                   <span id="scaleSelectContainer" class="dropdownToolbarButton">
-                     <select id="scaleSelect" title="Zoom" tabindex="23" data-l10n-id="zoom">
+                    <select id="scaleSelect" title="Zoom" tabindex="23" data-l10n-id="zoom">
                       <option id="pageAutoOption" title="" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
                       <option id="pageActualOption" title="" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
                       <option id="pageFitOption" title="" value="page-fit" data-l10n-id="page_scale_fit">Fit Page</option>
                       <option id="pageWidthOption" title="" value="page-width" data-l10n-id="page_scale_width">Full Width</option>
                       <option id="customScaleOption" title="" value="custom" hidden="true"></option>
                       <option title="" value="0.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 50 }'>50%</option>
                       <option title="" value="0.75" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 75 }'>75%</option>
                       <option title="" value="1" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 100 }'>100%</option>
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -504,17 +504,18 @@ var pdfjsWebLibs;
         "disableRange": false,
         "disableStream": false,
         "disableAutoFetch": false,
         "disableFontFace": false,
         "disableTextLayer": false,
         "useOnlyCssZoom": false,
         "externalLinkTarget": 0,
         "enhanceTextSelection": false,
-        "renderInteractiveForms": false
+        "renderInteractiveForms": false,
+        "disablePageLabels": false
       });
       function cloneObj(obj) {
         var result = {};
         for (var i in obj) {
           if (Object.prototype.hasOwnProperty.call(obj, i)) {
             result[i] = obj[i];
           }
         }
@@ -3156,16 +3157,17 @@ var pdfjsWebLibs;
           var container = options.container;
           var id = options.id;
           var defaultViewport = options.defaultViewport;
           var linkService = options.linkService;
           var renderingQueue = options.renderingQueue;
           var disableCanvasToImageConversion = options.disableCanvasToImageConversion || false;
           this.id = id;
           this.renderingId = 'thumbnail' + id;
+          this.pageLabel = null;
           this.pdfPage = null;
           this.rotation = 0;
           this.viewport = defaultViewport;
           this.pdfPageRotate = defaultViewport.rotation;
           this.linkService = linkService;
           this.renderingQueue = renderingQueue;
           this.renderTask = null;
           this.renderingState = RenderingStates.INITIAL;
@@ -3179,16 +3181,17 @@ var pdfjsWebLibs;
           this.scale = this.canvasWidth / this.pageWidth;
           var anchor = document.createElement('a');
           anchor.href = linkService.getAnchorUrl('#page=' + id);
           anchor.title = mozL10n.get('thumb_page_title', { page: id }, 'Page {{page}}');
           anchor.onclick = function stopNavigation() {
             linkService.page = id;
             return false;
           };
+          this.anchor = anchor;
           var div = document.createElement('div');
           div.id = 'thumbnailContainer' + id;
           div.className = 'thumbnail';
           this.div = div;
           if (id === 1) {
             // Highlight the thumbnail of the first page when no page number is
             // specified (or exists in cache) when the document is loaded.
             div.classList.add('selected');
@@ -3285,17 +3288,17 @@ var pdfjsWebLibs;
             if (!this.canvas) {
               return;
             }
             if (this.renderingState !== RenderingStates.FINISHED) {
               return;
             }
             var id = this.renderingId;
             var className = 'thumbnailImage';
-            var ariaLabel = mozL10n.get('thumb_page_canvas', { page: this.id }, 'Thumbnail of Page {{page}}');
+            var ariaLabel = mozL10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}');
             if (this.disableCanvasToImageConversion) {
               this.canvas.id = id;
               this.canvas.className = className;
               this.canvas.setAttribute('aria-label', ariaLabel);
               this.div.setAttribute('data-loaded', true);
               this.ring.appendChild(this.canvas);
               return;
             }
@@ -3404,16 +3407,35 @@ var pdfjsWebLibs;
             reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, reducedWidth, reducedHeight);
             while (reducedWidth > 2 * canvas.width) {
               reducedImageCtx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, reducedWidth >> 1, reducedHeight >> 1);
               reducedWidth >>= 1;
               reducedHeight >>= 1;
             }
             ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, canvas.width, canvas.height);
             this._convertCanvasToImage();
+          },
+          get pageId() {
+            return this.pageLabel !== null ? this.pageLabel : this.id;
+          },
+          /**
+           * @param {string|null} label
+           */
+          setPageLabel: function PDFThumbnailView_setPageLabel(label) {
+            this.pageLabel = typeof label === 'string' ? label : null;
+            this.anchor.title = mozL10n.get('thumb_page_title', { page: this.pageId }, 'Page {{page}}');
+            if (this.renderingState !== RenderingStates.FINISHED) {
+              return;
+            }
+            var ariaLabel = mozL10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}');
+            if (this.image) {
+              this.image.setAttribute('aria-label', ariaLabel);
+            } else if (this.disableCanvasToImageConversion && this.canvas) {
+              this.canvas.setAttribute('aria-label', ariaLabel);
+            }
           }
         };
         return PDFThumbnailView;
       }();
       PDFThumbnailView.tempImageCache = null;
       exports.PDFThumbnailView = PDFThumbnailView;
     }));
     (function (root, factory) {
@@ -4577,16 +4599,17 @@ var pdfjsWebLibs;
           var defaultViewport = options.defaultViewport;
           var renderingQueue = options.renderingQueue;
           var textLayerFactory = options.textLayerFactory;
           var annotationLayerFactory = options.annotationLayerFactory;
           var enhanceTextSelection = options.enhanceTextSelection || false;
           var renderInteractiveForms = options.renderInteractiveForms || false;
           this.id = id;
           this.renderingId = 'page' + id;
+          this.pageLabel = null;
           this.rotation = 0;
           this.scale = scale || DEFAULT_SCALE;
           this.viewport = defaultViewport;
           this.pdfPageRotate = defaultViewport.rotation;
           this.hasRestrictedScaling = false;
           this.enhanceTextSelection = enhanceTextSelection;
           this.renderInteractiveForms = renderInteractiveForms;
           this.eventBus = options.eventBus || domEvents.getGlobalEventBus();
@@ -4975,16 +4998,27 @@ var pdfjsWebLibs;
               }
               this.annotationLayer.render(this.viewport, 'display');
             }
             div.setAttribute('data-loaded', true);
             if (self.onBeforeDraw) {
               self.onBeforeDraw();
             }
             return promise;
+          },
+          /**
+           * @param {string|null} label
+           */
+          setPageLabel: function PDFView_setPageLabel(label) {
+            this.pageLabel = typeof label === 'string' ? label : null;
+            if (this.pageLabel !== null) {
+              this.div.setAttribute('data-page-label', this.pageLabel);
+            } else {
+              this.div.removeAttribute('data-page-label');
+            }
           }
         };
         return PDFPageView;
       }();
       exports.PDFPageView = PDFPageView;
     }));
     (function (root, factory) {
       factory(root.pdfjsWebPDFThumbnailViewer = {}, root.pdfjsWebUIUtils, root.pdfjsWebPDFThumbnailView);
@@ -5075,16 +5109,17 @@ var pdfjsWebLibs;
             }
             PDFThumbnailView.tempImageCache = null;
           },
           /**
            * @private
            */
           _resetView: function PDFThumbnailViewer_resetView() {
             this.thumbnails = [];
+            this._pageLabels = null;
             this._pagesRotation = 0;
             this._pagesRequests = [];
             // Remove the thumbnails from the DOM.
             this.container.textContent = '';
           },
           setDocument: function PDFThumbnailViewer_setDocument(pdfDocument) {
             if (this.pdfDocument) {
               this._cancelRendering();
@@ -5116,16 +5151,38 @@ var pdfjsWebLibs;
           _cancelRendering: function PDFThumbnailViewer_cancelRendering() {
             for (var i = 0, ii = this.thumbnails.length; i < ii; i++) {
               if (this.thumbnails[i]) {
                 this.thumbnails[i].cancelRendering();
               }
             }
           },
           /**
+           * @param {Array|null} labels
+           */
+          setPageLabels: function PDFThumbnailViewer_setPageLabels(labels) {
+            if (!this.pdfDocument) {
+              return;
+            }
+            if (!labels) {
+              this._pageLabels = null;
+            } else if (!(labels instanceof Array && this.pdfDocument.numPages === labels.length)) {
+              this._pageLabels = null;
+              console.error('PDFThumbnailViewer_setPageLabels: Invalid page labels.');
+            } else {
+              this._pageLabels = labels;
+            }
+            // Update all the `PDFThumbnailView` instances.
+            for (var i = 0, ii = this.thumbnails.length; i < ii; i++) {
+              var thumbnailView = this.thumbnails[i];
+              var label = this._pageLabels && this._pageLabels[i];
+              thumbnailView.setPageLabel(label);
+            }
+          },
+          /**
            * @param {PDFThumbnailView} thumbView
            * @returns {PDFPage}
            * @private
            */
           _ensurePdfPageLoaded: function PDFThumbnailViewer_ensurePdfPageLoaded(thumbView) {
             if (thumbView.pdfPage) {
               return Promise.resolve(thumbView.pdfPage);
             }
@@ -5731,26 +5788,48 @@ var pdfjsWebLibs;
               return;
             }
             if (!(0 < val && val <= this.pagesCount)) {
               console.error('PDFViewer_setCurrentPageNumber: "' + val + '" is out of bounds.');
               return;
             }
             var arg = {
               source: this,
-              pageNumber: val
+              pageNumber: val,
+              pageLabel: this._pageLabels && this._pageLabels[val - 1]
             };
             this._currentPageNumber = val;
             this.eventBus.dispatch('pagechanging', arg);
             this.eventBus.dispatch('pagechange', arg);
             if (resetCurrentPageView) {
               this._resetCurrentPageView();
             }
           },
           /**
+           * @returns {string|null} Returns the current page label,
+           *                        or `null` if no page labels exist.
+           */
+          get currentPageLabel() {
+            return this._pageLabels && this._pageLabels[this._currentPageNumber - 1];
+          },
+          /**
+           * @param {string} val - The page label.
+           */
+          set currentPageLabel(val) {
+            var pageNumber = val | 0;
+            // Fallback page number.
+            if (this._pageLabels) {
+              var i = this._pageLabels.indexOf(val);
+              if (i >= 0) {
+                pageNumber = i + 1;
+              }
+            }
+            this.currentPageNumber = pageNumber;
+          },
+          /**
            * @returns {number}
            */
           get currentScale() {
             return this._currentScale !== UNKNOWN_SCALE ? this._currentScale : DEFAULT_SCALE;
           },
           /**
            * @param {number} val - Scale of the pages in percents.
            */
@@ -5910,21 +5989,44 @@ var pdfjsWebLibs;
               if (this.defaultRenderingQueue) {
                 this.update();
               }
               if (this.findController) {
                 this.findController.resolveFirstPage();
               }
             }.bind(this));
           },
+          /**
+           * @param {Array|null} labels
+           */
+          setPageLabels: function PDFViewer_setPageLabels(labels) {
+            if (!this.pdfDocument) {
+              return;
+            }
+            if (!labels) {
+              this._pageLabels = null;
+            } else if (!(labels instanceof Array && this.pdfDocument.numPages === labels.length)) {
+              this._pageLabels = null;
+              console.error('PDFViewer_setPageLabels: Invalid page labels.');
+            } else {
+              this._pageLabels = labels;
+            }
+            // Update all the `PDFPageView` instances.
+            for (var i = 0, ii = this._pages.length; i < ii; i++) {
+              var pageView = this._pages[i];
+              var label = this._pageLabels && this._pageLabels[i];
+              pageView.setPageLabel(label);
+            }
+          },
           _resetView: function () {
             this._pages = [];
             this._currentPageNumber = 1;
             this._currentScale = UNKNOWN_SCALE;
             this._currentScaleValue = null;
+            this._pageLabels = null;
             this._buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE);
             this._location = null;
             this._pagesRotation = 0;
             this._pagesRequests = [];
             this._pageViewsReady = false;
             // Remove the pages from the DOM.
             this.viewer.textContent = '';
           },
@@ -6442,20 +6544,22 @@ var pdfjsWebLibs;
         eventBus: null,
         pageRotation: 0,
         isInitialViewSet: false,
         animationStartedPromise: null,
         preferenceSidebarViewOnLoad: SidebarView.NONE,
         preferencePdfBugEnabled: false,
         preferenceShowPreviousViewOnLoad: true,
         preferenceDefaultZoomValue: '',
+        preferenceDisablePageLabels: false,
         isViewerEmbedded: window.parent !== window,
         url: '',
         baseUrl: '',
         externalServices: DefaultExernalServices,
+        hasPageLabels: false,
         // called once when the document is loaded
         initialize: function pdfViewInitialize(appConfig) {
           configure(pdfjsLib.PDFJS);
           this.appConfig = appConfig;
           var eventBus = appConfig.eventBus || getGlobalEventBus();
           this.eventBus = eventBus;
           this.bindEvents();
           var pdfRenderingQueue = new PDFRenderingQueue();
@@ -6617,16 +6721,19 @@ var pdfjsWebLibs;
               }
               PDFJS.externalLinkTarget = value;
             }),
             Preferences.get('renderInteractiveForms').then(function resolved(value) {
               // TODO: Like the `enhanceTextSelection` preference, move the
               //       initialization and fetching of `Preferences` to occur
               //       before the various viewer components are initialized.
               self.pdfViewer.renderInteractiveForms = value;
+            }),
+            Preferences.get('disablePageLabels').then(function resolved(value) {
+              self.preferenceDisablePageLabels = value;
             })
           ]).catch(function (reason) {
           });
           return initializedPromise.then(function () {
             if (self.isViewerEmbedded && !PDFJS.isExternalLinkTargetSet()) {
               // Prevent external links from "replacing" the viewer,
               // when it's embedded in e.g. an iframe or an object.
               PDFJS.externalLinkTarget = PDFJS.LinkTarget.TOP;
@@ -6760,16 +6867,17 @@ var pdfjsWebLibs;
           if (this.pdfDocument) {
             this.pdfDocument = null;
             this.pdfThumbnailViewer.setDocument(null);
             this.pdfViewer.setDocument(null);
             this.pdfLinkService.setDocument(null, null);
           }
           this.store = null;
           this.isInitialViewSet = false;
+          this.hasPageLabels = false;
           this.pdfSidebar.reset();
           this.pdfOutlineViewer.reset();
           this.pdfAttachmentViewer.reset();
           this.findController.reset();
           this.findBar.reset();
           if (typeof PDFBug !== 'undefined') {
             PDFBug.cleanup();
           }
@@ -6965,17 +7073,18 @@ var pdfjsWebLibs;
           this.pdfLinkService.setDocument(pdfDocument, baseDocumentUrl);
           var pdfViewer = this.pdfViewer;
           pdfViewer.currentScale = scale;
           pdfViewer.setDocument(pdfDocument);
           var firstPagePromise = pdfViewer.firstPagePromise;
           var pagesPromise = pdfViewer.pagesPromise;
           var onePageRendered = pdfViewer.onePageRendered;
           this.pageRotation = 0;
-          this.pdfThumbnailViewer.setDocument(pdfDocument);
+          var pdfThumbnailViewer = this.pdfThumbnailViewer;
+          pdfThumbnailViewer.setDocument(pdfDocument);
           firstPagePromise.then(function (pdfPage) {
             downloadedPromise.then(function () {
               self.eventBus.dispatch('documentload', { source: self });
             });
             self.loadingBar.setWidth(self.appConfig.viewerContainer);
             if (!pdfjsLib.PDFJS.disableHistory && !self.isViewerEmbedded) {
               // The browsing history is only enabled when the viewer is standalone,
               // i.e. not when it is embedded in a web page.
@@ -7030,16 +7139,37 @@ var pdfjsWebLibs;
                 return;
               }
               self.initialDestination = initialParams.destination;
               self.initialBookmark = initialParams.bookmark;
               self.pdfViewer.currentScaleValue = self.pdfViewer.currentScaleValue;
               self.setInitialView(initialParams.hash);
             });
           });
+          pdfDocument.getPageLabels().then(function (labels) {
+            if (!labels || self.preferenceDisablePageLabels) {
+              return;
+            }
+            var i = 0, numLabels = labels.length;
+            if (numLabels !== self.pagesCount) {
+              console.error('The number of Page Labels does not match ' + 'the number of pages in the document.');
+              return;
+            }
+            // Ignore page labels that correspond to standard page numbering.
+            while (i < numLabels && labels[i] === (i + 1).toString()) {
+              i++;
+            }
+            if (i === numLabels) {
+              return;
+            }
+            pdfViewer.setPageLabels(labels);
+            pdfThumbnailViewer.setPageLabels(labels);
+            self.hasPageLabels = true;
+            self._updateUIToolbar({ resetNumPages: true });
+          });
           pagesPromise.then(function () {
             if (self.supportsPrinting) {
               pdfDocument.getJavaScript().then(function (javaScript) {
                 if (javaScript.length) {
                   console.warn('Warning: JavaScript is not supported');
                   self.fallback(pdfjsLib.UNSUPPORTED_FEATURES.javaScript);
                 }
                 // Hack to support auto printing.
@@ -7232,16 +7362,17 @@ var pdfjsWebLibs;
           if (!this.pdfPresentationMode) {
             return;
           }
           this.pdfPresentationMode.request();
         },
         /**
          * @typedef UpdateUIToolbarParameters
          * @property {number} pageNumber
+         * @property {string} pageLabel
          * @property {string} scaleValue
          * @property {number} scale
          * @property {boolean} resetNumPages
          */
         /**
          * @param {Object} UpdateUIToolbarParameters
          * @private
          */
@@ -7266,20 +7397,33 @@ var pdfjsWebLibs;
           }
           var pageNumber = params.pageNumber || this.pdfViewer.currentPageNumber;
           var scaleValue = (params.scaleValue || params.scale || this.pdfViewer.currentScaleValue || DEFAULT_SCALE_VALUE).toString();
           var scale = params.scale || this.pdfViewer.currentScale;
           var resetNumPages = params.resetNumPages || false;
           var toolbarConfig = this.appConfig.toolbar;
           var pagesCount = this.pagesCount;
           if (resetNumPages) {
-            toolbarConfig.numPages.textContent = mozL10n.get('page_of', { pageCount: pagesCount }, 'of {{pageCount}}');
+            if (this.hasPageLabels) {
+              toolbarConfig.pageNumber.type = 'text';
+            } else {
+              toolbarConfig.pageNumber.type = 'number';
+              toolbarConfig.numPages.textContent = mozL10n.get('of_pages', { pagesCount: pagesCount }, 'of {{pagesCount}}');
+            }
             toolbarConfig.pageNumber.max = pagesCount;
           }
-          toolbarConfig.pageNumber.value = pageNumber;
+          if (this.hasPageLabels) {
+            toolbarConfig.pageNumber.value = params.pageLabel || this.pdfViewer.currentPageLabel;
+            toolbarConfig.numPages.textContent = mozL10n.get('page_of_pages', {
+              pageNumber: pageNumber,
+              pagesCount: pagesCount
+            }, '({{pageNumber}} of {{pagesCount}})');
+          } else {
+            toolbarConfig.pageNumber.value = pageNumber;
+          }
           toolbarConfig.previous.disabled = pageNumber <= 1;
           toolbarConfig.next.disabled = pageNumber >= pagesCount;
           toolbarConfig.firstPage.disabled = pageNumber <= 1;
           toolbarConfig.lastPage.disabled = pageNumber >= pagesCount;
           toolbarConfig.zoomOut.disabled = scale <= MIN_SCALE;
           toolbarConfig.zoomIn.disabled = scale >= MAX_SCALE;
           selectScaleOption(scaleValue, scale);
         },
@@ -7428,20 +7572,21 @@ var pdfjsWebLibs;
         });
         appConfig.toolbar.zoomOut.addEventListener('click', function () {
           PDFViewerApplication.zoomOut();
         });
         appConfig.toolbar.pageNumber.addEventListener('click', function () {
           this.select();
         });
         appConfig.toolbar.pageNumber.addEventListener('change', function () {
-          PDFViewerApplication.page = this.value | 0;
+          var pdfViewer = PDFViewerApplication.pdfViewer;
+          pdfViewer.currentPageLabel = this.value;
           // Ensure that the page number input displays the correct value, even if the
           // value entered by the user was invalid (e.g. a floating point number).
-          if (this.value !== PDFViewerApplication.page.toString()) {
+          if (this.value !== pdfViewer.currentPageNumber.toString() && this.value !== pdfViewer.currentPageLabel) {
             PDFViewerApplication._updateUIToolbar({});
           }
         });
         appConfig.toolbar.scaleSelect.addEventListener('change', function () {
           if (this.value === 'custom') {
             return;
           }
           PDFViewerApplication.pdfViewer.currentScaleValue = this.value;
@@ -7724,32 +7869,35 @@ var pdfjsWebLibs;
         });
         if (!PDFViewerApplication.initialized) {
           return;
         }
         PDFViewerApplication.pdfViewer.update();
       }
       function webViewerPageChanging(e) {
         var page = e.pageNumber;
-        PDFViewerApplication._updateUIToolbar({ pageNumber: page });
+        PDFViewerApplication._updateUIToolbar({
+          pageNumber: page,
+          pageLabel: e.pageLabel
+        });
         if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) {
           PDFViewerApplication.pdfThumbnailViewer.scrollThumbnailIntoView(page);
         }
         // we need to update stats
         if (pdfjsLib.PDFJS.pdfBug && Stats.enabled) {
           var pageView = PDFViewerApplication.pdfViewer.getPageView(page - 1);
           if (pageView.stats) {
             Stats.add(page, pageView.stats);
           }
         }
       }
       var zoomDisabled = false, zoomDisabledTimeout;
       function handleMouseWheel(evt) {
         var pdfViewer = PDFViewerApplication.pdfViewer;
-        if (pdfViewer.isInPresentationMode) {
+        if (!pdfViewer || pdfViewer.isInPresentationMode) {
           return;
         }
         if (evt.ctrlKey || evt.metaKey) {
           var support = PDFViewerApplication.supportedMouseWheelZoomModifierKeys;
           if (evt.ctrlKey && !support.ctrlKey || evt.metaKey && !support.metaKey) {
             return;
           }
           // Only zoom the pages, not the entire viewer.
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -13,22 +13,25 @@
 # limitations under the License.
 
 # Main toolbar buttons (tooltips and alt text for images)
 previous.title=Previous Page
 previous_label=Previous
 next.title=Next Page
 next_label=Next
 
-# LOCALIZATION NOTE (page_label, page_of):
-# These strings are concatenated to form the "Page: X of Y" string.
-# Do not translate "{{pageCount}}", it will be substituted with a number
-# representing the total number of pages.
-page_label=Page:
-page_of=of {{pageCount}}
+# LOCALIZATION NOTE (page.title): The tooltip for the pageNumber input.
+page.title=Page
+# LOCALIZATION NOTE (of_pages): "{{pagesCount}}" will be replaced by a number
+# representing the total number of pages in the document.
+of_pages=of {{pagesCount}}
+# LOCALIZATION NOTE (page_of_pages): "{{pageNumber}}" and "{{pagesCount}}"
+# will be replaced by a number representing the currently visible page,
+# respectively a number representing the total number of pages in the document.
+page_of_pages=({{pageNumber}} of {{pagesCount}})
 
 zoom_out.title=Zoom Out
 zoom_out_label=Zoom Out
 zoom_in.title=Zoom In
 zoom_in_label=Zoom In
 zoom.title=Zoom
 presentation_mode.title=Switch to Presentation Mode
 presentation_mode_label=Presentation Mode