diff --git a/extensions/chromium/preferences_schema.json b/extensions/chromium/preferences_schema.json
index baa3300c4..9bff7d805 100644
--- a/extensions/chromium/preferences_schema.json
+++ b/extensions/chromium/preferences_schema.json
@@ -89,6 +89,10 @@
       ],
       "default": 0
     },
+    "disablePageLabels": {
+      "type": "boolean",
+      "default": false
+    },
     "disableTelemetry": {
       "title": "Disable telemetry",
       "type": "boolean",
diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties
index 90c09aa99..2775ae19b 100644
--- a/l10n/en-US/viewer.properties
+++ b/l10n/en-US/viewer.properties
@@ -20,9 +20,13 @@ next_label=Next
 
 # LOCALIZATION NOTE (page.title): The tooltip for the pageNumber input.
 page.title=Page
-# LOCALIZATION NOTE (page_of): "{{pageCount}}" will be replaced by a number
+# LOCALIZATION NOTE (of_pages): "{{pagesCount}}" will be replaced by a number
 # representing the total number of pages in the document.
-page_of=of {{pageCount}}
+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
diff --git a/l10n/sv-SE/viewer.properties b/l10n/sv-SE/viewer.properties
index 514c32d4b..9ada2c333 100644
--- a/l10n/sv-SE/viewer.properties
+++ b/l10n/sv-SE/viewer.properties
@@ -20,9 +20,13 @@ next_label=Nästa
 
 # LOCALIZATION NOTE (page.title): The tooltip for the pageNumber input.
 page.title=Sida
-# LOCALIZATION NOTE (page_of): "{{pageCount}}" will be replaced by a number
+# LOCALIZATION NOTE (of_pages): "{{pagesCount}}" will be replaced by a number
 # representing the total number of pages in the document.
-page_of=av {{pageCount}}
+of_pages=av {{pagesCount}}
+# LOCALIZATION NOTE (page_of_pages): "{{pageNumber}}" and "{{pagesCount}}"
+# will be replaced by a number representing the currently active visible page,
+# respectively a number representing the total number of pages in the document.
+page_of_pages=({{pageNumber}} av {{pagesCount}})
 
 zoom_out.title=Zooma ut
 zoom_out_label=Zooma ut
diff --git a/web/app.js b/web/app.js
index 73cc29911..a2fbfb884 100644
--- a/web/app.js
+++ b/web/app.js
@@ -178,10 +178,12 @@ var PDFViewerApplication = {
   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) {
@@ -380,6 +382,9 @@ var PDFViewerApplication = {
         //       before the various viewer components are initialized.
         self.pdfViewer.renderInteractiveForms = value;
       }),
+      Preferences.get('disablePageLabels').then(function resolved(value) {
+        self.preferenceDisablePageLabels = value;
+      }),
       // TODO move more preferences and other async stuff here
     ]).catch(function (reason) { });
 
@@ -567,6 +572,7 @@ var PDFViewerApplication = {
     }
     this.store = null;
     this.isInitialViewSet = false;
+    this.hasPageLabels = false;
 
     this.pdfSidebar.reset();
     this.pdfOutlineViewer.reset();
@@ -879,7 +885,8 @@ var PDFViewerApplication = {
 
     this.pageRotation = 0;
 
-    this.pdfThumbnailViewer.setDocument(pdfDocument);
+    var pdfThumbnailViewer = this.pdfThumbnailViewer;
+    pdfThumbnailViewer.setDocument(pdfDocument);
 
     firstPagePromise.then(function(pdfPage) {
       downloadedPromise.then(function () {
@@ -959,6 +966,33 @@ var PDFViewerApplication = {
       });
     });
 
+    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) {
@@ -1186,6 +1220,7 @@ var PDFViewerApplication = {
   /**
    * @typedef UpdateUIToolbarParameters
    * @property {number} pageNumber
+   * @property {string} pageLabel
    * @property {string} scaleValue
    * @property {number} scale
    * @property {boolean} resetNumPages
@@ -1226,11 +1261,25 @@ var PDFViewerApplication = {
     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);
@@ -1495,11 +1544,13 @@ function webViewerInitialized() {
   });
 
   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({});
     }
   });
@@ -1930,6 +1981,7 @@ function webViewerPageChanging(e) {
 
   PDFViewerApplication._updateUIToolbar({
     pageNumber: page,
+    pageLabel: e.pageLabel,
   });
 
   if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) {
diff --git a/web/default_preferences.json b/web/default_preferences.json
index 242c30b12..15371f6b3 100644
--- a/web/default_preferences.json
+++ b/web/default_preferences.json
@@ -13,5 +13,6 @@
   "useOnlyCssZoom": false,
   "externalLinkTarget": 0,
   "enhanceTextSelection": false,
-  "renderInteractiveForms": false
+  "renderInteractiveForms": false,
+  "disablePageLabels": false
 }
diff --git a/web/pdf_thumbnail_viewer.js b/web/pdf_thumbnail_viewer.js
index aa4181195..8a9dcc663 100644
--- a/web/pdf_thumbnail_viewer.js
+++ b/web/pdf_thumbnail_viewer.js
@@ -133,6 +133,7 @@ var PDFThumbnailViewer = (function PDFThumbnailViewerClosure() {
      */
     _resetView: function PDFThumbnailViewer_resetView() {
       this.thumbnails = [];
+      this._pageLabels = null;
       this._pagesRotation = 0;
       this._pagesRequests = [];
 
@@ -179,6 +180,24 @@ var PDFThumbnailViewer = (function PDFThumbnailViewerClosure() {
       }
     },
 
+    /**
+     * @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;
+      }
+    },
+
     /**
      * @param {PDFThumbnailView} thumbView
      * @returns {PDFPage}
diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js
index 16c3198b7..5f7b97c13 100644
--- a/web/pdf_viewer.js
+++ b/web/pdf_viewer.js
@@ -211,6 +211,7 @@ var PDFViewer = (function pdfViewer() {
       var arg = {
         source: this,
         pageNumber: val,
+        pageLabel: this._pageLabels && this._pageLabels[val - 1],
       };
       this._currentPageNumber = val;
       this.eventBus.dispatch('pagechanging', arg);
@@ -221,6 +222,28 @@ var PDFViewer = (function pdfViewer() {
       }
     },
 
+    /**
+     * @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}
      */
@@ -414,11 +437,30 @@ var PDFViewer = (function pdfViewer() {
       }.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;
+      }
+    },
+
     _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;