Bug 1453838 - Update pdf.js to version 2.0.491. r=bdahl, r=yury
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 12 Apr 2018 19:29:03 -0400
changeset 466861 676c6c0096afa3e77d32ace4cfd4b21f65b53d15
parent 466860 79dd1948f4bd3c731ab60aa59571bd9db10f09f1
child 466862 fde411a83e7f419be5c48b2a07e52c4c567059f5
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbdahl, yury
bugs1453838
milestone61.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 1453838 - Update pdf.js to version 2.0.491. r=bdahl, r=yury
browser/base/content/test/static/browser_parsable_css.js
browser/extensions/pdfjs/README.mozilla
browser/extensions/pdfjs/content/PdfStreamConverter.jsm
browser/extensions/pdfjs/content/build/pdf.js
browser/extensions/pdfjs/content/build/pdf.worker.js
browser/extensions/pdfjs/content/web/viewer.css
--- a/browser/base/content/test/static/browser_parsable_css.js
+++ b/browser/base/content/test/static/browser_parsable_css.js
@@ -20,17 +20,17 @@ let whitelist = [
    {sourceName: /devtools-client-shared\/components\/reps\/reps.css/i,
    isFromDevTools: true},
   // PDFjs is futureproofing its pseudoselectors, and those rules are dropped.
   {sourceName: /web\/viewer\.css$/i,
    errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i,
    isFromDevTools: false},
   // PDFjs rules needed for compat with other UAs.
   {sourceName: /web\/viewer\.css$/i,
-   errorMessage: /Unknown property.*appearance/i,
+   errorMessage: /Unknown property.*(appearance|user-select)/i,
    isFromDevTools: false},
   // Highlighter CSS uses a UA-only pseudo-class, see bug 985597.
   {sourceName: /highlighters\.css$/i,
    errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i,
    isFromDevTools: true},
   // Responsive Design Mode CSS uses a UA-only pseudo-class, see Bug 1241714.
   {sourceName: /responsive-ua\.css$/i,
    errorMessage: /Unknown pseudo-class.*moz-dropdown-list/i,
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 2.0.480
+Current extension version is: 2.0.491
 
-Taken from upstream commit: a7a034d8
+Taken from upstream commit: 2dc4af52
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -14,16 +14,17 @@
  */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["PdfStreamConverter"];
 
 const PDFJS_EVENT_ID = "pdf.js.message";
 const PREF_PREFIX = "pdfjs";
+const PDF_VIEWER_ORIGIN = "resource://pdf.js";
 const PDF_VIEWER_WEB_PAGE = "resource://pdf.js/web/viewer.html";
 const MAX_NUMBER_OF_PREFS = 50;
 const MAX_STRING_PREF_LENGTH = 128;
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "NetUtil",
@@ -76,21 +77,25 @@ function log(aMsg) {
   if (!getBoolPref(PREF_PREFIX + ".pdfBugEnabled", false)) {
     return;
   }
   var msg = "PdfStreamConverter.js: " + (aMsg.join ? aMsg.join("") : aMsg);
   Services.console.logStringMessage(msg);
   dump(msg + "\n");
 }
 
-function getDOMWindow(aChannel) {
+function getDOMWindow(aChannel, aPrincipal) {
   var requestor = aChannel.notificationCallbacks ?
                   aChannel.notificationCallbacks :
                   aChannel.loadGroup.notificationCallbacks;
   var win = requestor.getInterface(Ci.nsIDOMWindow);
+  // Ensure the window wasn't navigated to something that is not PDF.js.
+  if (!win.document.nodePrincipal.equals(aPrincipal)) {
+    return null;
+  }
   return win;
 }
 
 function getLocalizedStrings(path) {
   var stringBundle =
     Services.strings.createBundle("chrome://pdf.js/locale/" + path);
 
   var map = {};
@@ -590,31 +595,31 @@ class RangedChromeActions extends Chrome
       data = this.dataListener.readData();
 
       this.dataListener.onprogress = (loaded, total) => {
         this.domWindow.postMessage({
           pdfjsLoadAction: "progressiveRead",
           loaded,
           total,
           chunk: this.dataListener.readData(),
-        }, "*");
+        }, PDF_VIEWER_ORIGIN);
       };
       this.dataListener.oncomplete = () => {
         this.dataListener = null;
       };
     }
 
     this.domWindow.postMessage({
       pdfjsLoadAction: "supportsRangedLoading",
       rangeEnabled: this.rangeEnabled,
       streamingEnabled: this.streamingEnabled,
       pdfUrl: this.pdfUrl,
       length: this.contentLength,
       data,
-    }, "*");
+    }, PDF_VIEWER_ORIGIN);
 
     return true;
   }
 
   requestDataRange(args) {
     if (!this.rangeEnabled) {
       return;
     }
@@ -626,23 +631,23 @@ class RangedChromeActions extends Chrome
     // errors from chrome code for non-range requests, so this doesn't
     // seem high-pri
     this.networkManager.requestRange(begin, end, {
       onDone: function RangedChromeActions_onDone(aArgs) {
         domWindow.postMessage({
           pdfjsLoadAction: "range",
           begin: aArgs.begin,
           chunk: aArgs.chunk,
-        }, "*");
+        }, PDF_VIEWER_ORIGIN);
       },
       onProgress: function RangedChromeActions_onProgress(evt) {
         domWindow.postMessage({
           pdfjsLoadAction: "rangeProgress",
           loaded: evt.loaded,
-        }, "*");
+        }, PDF_VIEWER_ORIGIN);
       },
     });
   }
 
   abortLoading() {
     this.networkManager.abortAllRequests();
     if (this.originalRequest) {
       this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
@@ -668,25 +673,25 @@ class StandardChromeActions extends Chro
       return false;
     }
 
     this.dataListener.onprogress = (loaded, total) => {
       this.domWindow.postMessage({
         pdfjsLoadAction: "progress",
         loaded,
         total,
-      }, "*");
+      }, PDF_VIEWER_ORIGIN);
     };
 
     this.dataListener.oncomplete = (data, errorCode) => {
       this.domWindow.postMessage({
         pdfjsLoadAction: "complete",
         data,
         errorCode,
-      }, "*");
+      }, PDF_VIEWER_ORIGIN);
 
       this.dataListener = null;
       this.originalRequest = null;
     };
 
     return true;
   }
 
@@ -934,30 +939,34 @@ PdfStreamConverter.prototype = {
       onStartRequest(request, context) {
         listener.onStartRequest(aRequest, aContext);
       },
       onDataAvailable(request, context, inputStream, offset, count) {
         listener.onDataAvailable(aRequest, aContext, inputStream,
                                  offset, count);
       },
       onStopRequest(request, context, statusCode) {
-        // We get the DOM window here instead of before the request since it
-        // may have changed during a redirect.
-        var domWindow = getDOMWindow(channel);
+        var domWindow = getDOMWindow(channel, resourcePrincipal);
+        if (!Components.isSuccessCode(statusCode) || !domWindow) {
+          // The request may have been aborted and the document may have been
+          // replaced with something that is not PDF.js, abort attaching.
+          listener.onStopRequest(aRequest, context, statusCode);
+          return;
+        }
         var actions;
         if (rangeRequest || streamRequest) {
           actions = new RangedChromeActions(
             domWindow, contentDispositionFilename, aRequest,
             rangeRequest, streamRequest, dataListener);
         } else {
           actions = new StandardChromeActions(
             domWindow, contentDispositionFilename, aRequest, dataListener);
         }
         var requestListener = new RequestListener(actions);
-        domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
+        domWindow.document.addEventListener(PDFJS_EVENT_ID, function(event) {
           requestListener.receive(event);
         }, false, true);
         if (actions.supportsIntegratedFind()) {
           var findEventManager = new FindEventManager(domWindow);
           findEventManager.bind();
         }
         listener.onStopRequest(aRequest, aContext, statusCode);
 
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -1645,18 +1645,18 @@ exports.GlobalWorkerOptions = GlobalWork
 
 /***/ }),
 /* 5 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.480';
-var pdfjsBuild = 'a7a034d8';
+var pdfjsVersion = '2.0.491';
+var pdfjsBuild = '2dc4af52';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayAPI = __w_pdfjs_require__(9);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(17);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(18);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(19);
 let pdfjsDisplayWorkerOptions = __w_pdfjs_require__(4);
 let pdfjsDisplayAPICompatibility = __w_pdfjs_require__(3);
@@ -4924,17 +4924,17 @@ function _fetchDocument(worker, source, 
     return Promise.reject(new Error('Worker was destroyed'));
   }
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.0.480',
+    apiVersion: '2.0.491',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -6247,18 +6247,18 @@ var InternalRenderTask = function Intern
         }
       }
     }
   };
   return InternalRenderTask;
 }();
 var version, build;
 {
-  exports.version = version = '2.0.480';
-  exports.build = build = 'a7a034d8';
+  exports.version = version = '2.0.491';
+  exports.build = build = '2dc4af52';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -2753,19 +2753,19 @@ var ColorSpace = function ColorSpaceClos
           }
         }
       }
     },
     usesZeroToOneRange: true
   };
   ColorSpace.parse = function (cs, xref, res, pdfFunctionFactory) {
     let IR = ColorSpace.parseToIR(cs, xref, res, pdfFunctionFactory);
-    return ColorSpace.fromIR(IR, pdfFunctionFactory);
-  };
-  ColorSpace.fromIR = function (IR, pdfFunctionFactory) {
+    return ColorSpace.fromIR(IR);
+  };
+  ColorSpace.fromIR = function (IR) {
     var name = Array.isArray(IR) ? IR[0] : IR;
     var whitePoint, blackPoint, gamma;
     switch (name) {
       case 'DeviceGrayCS':
         return this.singletons.gray;
       case 'DeviceRgbCS':
         return this.singletons.rgb;
       case 'DeviceCmykCS':
@@ -2779,29 +2779,29 @@ var ColorSpace = function ColorSpaceClos
         whitePoint = IR[1];
         blackPoint = IR[2];
         gamma = IR[3];
         var matrix = IR[4];
         return new CalRGBCS(whitePoint, blackPoint, gamma, matrix);
       case 'PatternCS':
         var basePatternCS = IR[1];
         if (basePatternCS) {
-          basePatternCS = ColorSpace.fromIR(basePatternCS, pdfFunctionFactory);
+          basePatternCS = ColorSpace.fromIR(basePatternCS);
         }
         return new PatternCS(basePatternCS);
       case 'IndexedCS':
         var baseIndexedCS = IR[1];
         var hiVal = IR[2];
         var lookup = IR[3];
-        return new IndexedCS(ColorSpace.fromIR(baseIndexedCS, pdfFunctionFactory), hiVal, lookup);
+        return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup);
       case 'AlternateCS':
         var numComps = IR[1];
         var alt = IR[2];
-        var tintFnIR = IR[3];
-        return new AlternateCS(numComps, ColorSpace.fromIR(alt, pdfFunctionFactory), pdfFunctionFactory.createFromIR(tintFnIR));
+        var tintFn = IR[3];
+        return new AlternateCS(numComps, ColorSpace.fromIR(alt), tintFn);
       case 'LabCS':
         whitePoint = IR[1];
         blackPoint = IR[2];
         var range = IR[3];
         return new LabCS(whitePoint, blackPoint, range);
       default:
         throw new _util.FormatError(`Unknown colorspace name: ${name}`);
     }
@@ -2900,18 +2900,18 @@ var ColorSpace = function ColorSpaceClos
             lookup = lookup.getBytes();
           }
           return ['IndexedCS', baseIndexedCS, hiVal, lookup];
         case 'Separation':
         case 'DeviceN':
           var name = xref.fetchIfRef(cs[1]);
           numComps = Array.isArray(name) ? name.length : 1;
           alt = ColorSpace.parseToIR(cs[2], xref, res, pdfFunctionFactory);
-          let tintFnIR = pdfFunctionFactory.createIR(xref.fetchIfRef(cs[3]));
-          return ['AlternateCS', numComps, alt, tintFnIR];
+          let tintFn = pdfFunctionFactory.create(xref.fetchIfRef(cs[3]));
+          return ['AlternateCS', numComps, alt, tintFn];
         case 'Lab':
           params = xref.fetchIfRef(cs[1]);
           whitePoint = params.getArray('WhitePoint');
           blackPoint = params.getArray('BlackPoint');
           var range = params.getArray('Range');
           return ['LabCS', whitePoint, blackPoint, range];
         default:
           throw new _util.FormatError(`unimplemented color space object "${mode}"`);
@@ -20086,30 +20086,32 @@ class PDFFunctionFactory {
   }
   createFromArray(fnObj) {
     return PDFFunction.parseArray({
       xref: this.xref,
       isEvalSupported: this.isEvalSupported,
       fnObj
     });
   }
-  createFromIR(IR) {
-    return PDFFunction.fromIR({
-      xref: this.xref,
-      isEvalSupported: this.isEvalSupported,
-      IR
-    });
-  }
-  createIR(fn) {
-    return PDFFunction.getIR({
-      xref: this.xref,
-      isEvalSupported: this.isEvalSupported,
-      fn
-    });
-  }
+}
+function toNumberArray(arr) {
+  if (!Array.isArray(arr)) {
+    return null;
+  }
+  const length = arr.length;
+  for (let i = 0; i < length; i++) {
+    if (typeof arr[i] !== 'number') {
+      const result = new Array(length);
+      for (let i = 0; i < length; i++) {
+        result[i] = +arr[i];
+      }
+      return result;
+    }
+  }
+  return arr;
 }
 var PDFFunction = function PDFFunctionClosure() {
   const CONSTRUCT_SAMPLED = 0;
   const CONSTRUCT_INTERPOLATED = 2;
   const CONSTRUCT_STICHED = 3;
   const CONSTRUCT_POSTSCRIPT = 4;
   return {
     getSampleArray(size, outputSize, bps, stream) {
@@ -20224,41 +20226,41 @@ var PDFFunction = function PDFFunctionCl
         var out = [];
         var index = 0;
         for (var i = 0; i < inputLength; i += 2) {
           out[index] = [arr[i], arr[i + 1]];
           ++index;
         }
         return out;
       }
-      var domain = dict.getArray('Domain');
-      var range = dict.getArray('Range');
+      var domain = toNumberArray(dict.getArray('Domain'));
+      var range = toNumberArray(dict.getArray('Range'));
       if (!domain || !range) {
         throw new _util.FormatError('No domain or range');
       }
       var inputSize = domain.length / 2;
       var outputSize = range.length / 2;
       domain = toMultiArray(domain);
       range = toMultiArray(range);
-      var size = dict.get('Size');
+      var size = toNumberArray(dict.get('Size'));
       var bps = dict.get('BitsPerSample');
       var order = dict.get('Order') || 1;
       if (order !== 1) {
         (0, _util.info)('No support for cubic spline interpolation: ' + order);
       }
-      var encode = dict.getArray('Encode');
+      var encode = toNumberArray(dict.getArray('Encode'));
       if (!encode) {
         encode = [];
         for (var i = 0; i < inputSize; ++i) {
-          encode.push(0);
-          encode.push(size[i] - 1);
-        }
-      }
-      encode = toMultiArray(encode);
-      var decode = dict.getArray('Decode');
+          encode.push([0, size[i] - 1]);
+        }
+      } else {
+        encode = toMultiArray(encode);
+      }
+      var decode = toNumberArray(dict.getArray('Decode'));
       if (!decode) {
         decode = range;
       } else {
         decode = toMultiArray(decode);
       }
       var samples = this.getSampleArray(size, outputSize, bps, fn);
       return [CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size, outputSize, Math.pow(2, bps) - 1, range];
     },
@@ -20314,22 +20316,19 @@ var PDFFunction = function PDFFunctionCl
             rj += samples[cubeVertex[i] + j] * cubeN[i];
           }
           rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
           dest[destOffset + j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);
         }
       };
     },
     constructInterpolated({ xref, isEvalSupported, fn, dict }) {
-      var c0 = dict.getArray('C0') || [0];
-      var c1 = dict.getArray('C1') || [1];
+      var c0 = toNumberArray(dict.getArray('C0')) || [0];
+      var c1 = toNumberArray(dict.getArray('C1')) || [1];
       var n = dict.get('N');
-      if (!Array.isArray(c0) || !Array.isArray(c1)) {
-        throw new _util.FormatError('Illegal dictionary for interpolated function');
-      }
       var length = c0.length;
       var diff = [];
       for (var i = 0; i < length; ++i) {
         diff.push(c1[i] - c0[i]);
       }
       return [CONSTRUCT_INTERPOLATED, c0, diff, n];
     },
     constructInterpolatedFromIR({ xref, isEvalSupported, IR }) {
@@ -20340,51 +20339,43 @@ var PDFFunction = function PDFFunctionCl
       return function constructInterpolatedFromIRResult(src, srcOffset, dest, destOffset) {
         var x = n === 1 ? src[srcOffset] : Math.pow(src[srcOffset], n);
         for (var j = 0; j < length; ++j) {
           dest[destOffset + j] = c0[j] + x * diff[j];
         }
       };
     },
     constructStiched({ xref, isEvalSupported, fn, dict }) {
-      var domain = dict.getArray('Domain');
+      var domain = toNumberArray(dict.getArray('Domain'));
       if (!domain) {
         throw new _util.FormatError('No domain');
       }
       var inputSize = domain.length / 2;
       if (inputSize !== 1) {
         throw new _util.FormatError('Bad domain for stiched function');
       }
       var fnRefs = dict.get('Functions');
       var fns = [];
       for (var i = 0, ii = fnRefs.length; i < ii; ++i) {
-        fns.push(this.getIR({
+        fns.push(this.parse({
           xref,
           isEvalSupported,
           fn: xref.fetchIfRef(fnRefs[i])
         }));
       }
-      var bounds = dict.getArray('Bounds');
-      var encode = dict.getArray('Encode');
+      var bounds = toNumberArray(dict.getArray('Bounds'));
+      var encode = toNumberArray(dict.getArray('Encode'));
       return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
     },
     constructStichedFromIR({ xref, isEvalSupported, IR }) {
       var domain = IR[1];
       var bounds = IR[2];
       var encode = IR[3];
-      var fnsIR = IR[4];
-      var fns = [];
+      var fns = IR[4];
       var tmpBuf = new Float32Array(1);
-      for (var i = 0, ii = fnsIR.length; i < ii; i++) {
-        fns.push(this.fromIR({
-          xref,
-          isEvalSupported,
-          IR: fnsIR[i]
-        }));
-      }
       return function constructStichedFromIRResult(src, srcOffset, dest, destOffset) {
         var clip = function constructStichedFromIRClip(v, min, max) {
           if (v > max) {
             v = max;
           } else if (v < min) {
             v = min;
           }
           return v;
@@ -20405,18 +20396,18 @@ var PDFFunction = function PDFFunctionCl
         }
         var rmin = encode[2 * i];
         var rmax = encode[2 * i + 1];
         tmpBuf[0] = dmin === dmax ? rmin : rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
         fns[i](tmpBuf, 0, dest, destOffset);
       };
     },
     constructPostScript({ xref, isEvalSupported, fn, dict }) {
-      var domain = dict.getArray('Domain');
-      var range = dict.getArray('Range');
+      var domain = toNumberArray(dict.getArray('Domain'));
+      var range = toNumberArray(dict.getArray('Range'));
       if (!domain) {
         throw new _util.FormatError('No domain.');
       }
       if (!range) {
         throw new _util.FormatError('No range.');
       }
       var lexer = new _ps_parser.PostScriptLexer(fn);
       var parser = new _ps_parser.PostScriptParser(lexer);
@@ -21107,18 +21098,18 @@ exports.PostScriptCompiler = PostScriptC
 
 /***/ }),
 /* 19 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.480';
-var pdfjsBuild = 'a7a034d8';
+var pdfjsVersion = '2.0.491';
+var pdfjsBuild = '2dc4af52';
 var pdfjsCoreWorker = __w_pdfjs_require__(20);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 20 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -21309,17 +21300,17 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.0.480';
+    let workerVersion = '2.0.491';
     if (apiVersion !== null && apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
     var handler = new _util.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
@@ -35126,17 +35117,20 @@ var FontRendererFactory = function FontR
   }
   function parseCff(data, start, end, seacAnalysisEnabled) {
     var properties = {};
     var parser = new _cff_parser.CFFParser(new _stream.Stream(data, start, end - start), properties, seacAnalysisEnabled);
     var cff = parser.parse();
     return {
       glyphs: cff.charStrings.objects,
       subrs: cff.topDict.privateDict && cff.topDict.privateDict.subrsIndex && cff.topDict.privateDict.subrsIndex.objects,
-      gsubrs: cff.globalSubrIndex && cff.globalSubrIndex.objects
+      gsubrs: cff.globalSubrIndex && cff.globalSubrIndex.objects,
+      isCFFCIDFont: cff.isCIDFont,
+      fdSelect: cff.fdSelect,
+      fdArray: cff.fdArray
     };
   }
   function parseGlyfTable(glyf, loca, isGlyphLocationsLong) {
     var itemSize, itemDecode;
     if (isGlyphLocationsLong) {
       itemSize = 4;
       itemDecode = function fontItemDecodeLong(data, offset) {
         return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];
@@ -35330,17 +35324,17 @@ var FontRendererFactory = function FontR
           } else {
             quadraticCurveTo(contour[j].x, contour[j].y, (contour[j].x + contour[j + 1].x) / 2, (contour[j].y + contour[j + 1].y) / 2);
           }
         }
         startPoint = endPoint + 1;
       }
     }
   }
-  function compileCharString(code, cmds, font) {
+  function compileCharString(code, cmds, font, glyphId) {
     var stack = [];
     var x = 0,
         y = 0;
     var stems = 0;
     function moveTo(x, y) {
       cmds.push({
         cmd: 'moveTo',
         args: [x, y]
@@ -35414,18 +35408,37 @@ var FontRendererFactory = function FontR
               xb = xa + stack.shift();
               yb = ya + stack.shift();
               x = xb + stack.shift();
               y = yb + stack.shift();
               bezierCurveTo(xa, ya, xb, yb, x, y);
             }
             break;
           case 10:
-            n = stack.pop() + font.subrsBias;
-            subrCode = font.subrs[n];
+            n = stack.pop();
+            subrCode = null;
+            if (font.isCFFCIDFont) {
+              let fdIndex = font.fdSelect.getFDIndex(glyphId);
+              if (fdIndex >= 0 && fdIndex < font.fdArray.length) {
+                let fontDict = font.fdArray[fdIndex],
+                    subrs;
+                if (fontDict.privateDict && fontDict.privateDict.subrsIndex) {
+                  subrs = fontDict.privateDict.subrsIndex.objects;
+                }
+                if (subrs) {
+                  let numSubrs = subrs.length;
+                  n += numSubrs < 1240 ? 107 : numSubrs < 33900 ? 1131 : 32768;
+                  subrCode = subrs[n];
+                }
+              } else {
+                (0, _util.warn)('Invalid fd index for glyph index.');
+              }
+            } else {
+              subrCode = font.subrs[n + font.subrsBias];
+            }
             if (subrCode) {
               parse(subrCode);
             }
             break;
           case 11:
             return;
           case 12:
             v = code[i++];
@@ -35505,20 +35518,20 @@ var FontRendererFactory = function FontR
               y = stack.pop();
               x = stack.pop();
               cmds.push({ cmd: 'save' });
               cmds.push({
                 cmd: 'translate',
                 args: [x, y]
               });
               var cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[_encodings.StandardEncoding[achar]]));
-              compileCharString(font.glyphs[cmap.glyphId], cmds, font);
+              compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);
               cmds.push({ cmd: 'restore' });
               cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[_encodings.StandardEncoding[bchar]]));
-              compileCharString(font.glyphs[cmap.glyphId], cmds, font);
+              compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);
             }
             return;
           case 18:
             stems += stack.length >> 1;
             stackClean = true;
             break;
           case 19:
             stems += stack.length >> 1;
@@ -35683,39 +35696,49 @@ var FontRendererFactory = function FontR
     this.compiledCharCodeToGlyphId = Object.create(null);
     this.fontMatrix = fontMatrix;
   }
   CompiledFont.prototype = {
     getPathJs(unicode) {
       var cmap = lookupCmap(this.cmap, unicode);
       var fn = this.compiledGlyphs[cmap.glyphId];
       if (!fn) {
-        fn = this.compileGlyph(this.glyphs[cmap.glyphId]);
+        fn = this.compileGlyph(this.glyphs[cmap.glyphId], cmap.glyphId);
         this.compiledGlyphs[cmap.glyphId] = fn;
       }
       if (this.compiledCharCodeToGlyphId[cmap.charCode] === undefined) {
         this.compiledCharCodeToGlyphId[cmap.charCode] = cmap.glyphId;
       }
       return fn;
     },
-    compileGlyph(code) {
+    compileGlyph(code, glyphId) {
       if (!code || code.length === 0 || code[0] === 14) {
         return noop;
       }
+      let fontMatrix = this.fontMatrix;
+      if (this.isCFFCIDFont) {
+        let fdIndex = this.fdSelect.getFDIndex(glyphId);
+        if (fdIndex >= 0 && fdIndex < this.fdArray.length) {
+          let fontDict = this.fdArray[fdIndex];
+          fontMatrix = fontDict.getByName('FontMatrix') || _util.FONT_IDENTITY_MATRIX;
+        } else {
+          (0, _util.warn)('Invalid fd index for glyph index.');
+        }
+      }
       var cmds = [];
       cmds.push({ cmd: 'save' });
       cmds.push({
         cmd: 'transform',
-        args: this.fontMatrix.slice()
+        args: fontMatrix.slice()
       });
       cmds.push({
         cmd: 'scale',
         args: ['size', '-size']
       });
-      this.compileGlyphImpl(code, cmds);
+      this.compileGlyphImpl(code, cmds, glyphId);
       cmds.push({ cmd: 'restore' });
       return cmds;
     },
     compileGlyphImpl() {
       (0, _util.unreachable)('Children classes should implement this.');
     },
     hasBuiltPath(unicode) {
       var cmap = lookupCmap(this.cmap, unicode);
@@ -35738,20 +35761,23 @@ var FontRendererFactory = function FontR
     CompiledFont.call(this, fontMatrix);
     this.glyphs = cffInfo.glyphs;
     this.gsubrs = cffInfo.gsubrs || [];
     this.subrs = cffInfo.subrs || [];
     this.cmap = cmap;
     this.glyphNameMap = glyphNameMap || (0, _glyphlist.getGlyphsUnicode)();
     this.gsubrsBias = this.gsubrs.length < 1240 ? 107 : this.gsubrs.length < 33900 ? 1131 : 32768;
     this.subrsBias = this.subrs.length < 1240 ? 107 : this.subrs.length < 33900 ? 1131 : 32768;
+    this.isCFFCIDFont = cffInfo.isCFFCIDFont;
+    this.fdSelect = cffInfo.fdSelect;
+    this.fdArray = cffInfo.fdArray;
   }
   _util.Util.inherit(Type2Compiled, CompiledFont, {
-    compileGlyphImpl(code, cmds) {
-      compileCharString(code, cmds, this);
+    compileGlyphImpl(code, cmds, glyphId) {
+      compileCharString(code, cmds, this, glyphId);
     }
   });
   return {
     create: function FontRendererFactory_create(font, seacAnalysisEnabled) {
       var data = new Uint8Array(font.data);
       var cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm;
       var numTables = getUshort(data, 4);
       for (var i = 0, p = 12; i < numTables; i++, p += 16) {
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -24,17 +24,16 @@
   line-height: 1.0;
 }
 
 .textLayer > div {
   color: transparent;
   position: absolute;
   white-space: pre;
   cursor: text;
-  -moz-transform-origin: 0% 0%;
   transform-origin: 0% 0%;
 }
 
 .textLayer .highlight {
   margin: -1px;
   padding: 1px;
 
   background-color: rgb(180, 0, 170);
@@ -52,29 +51,31 @@
 .textLayer .highlight.middle {
   border-radius: 0px;
 }
 
 .textLayer .highlight.selected {
   background-color: rgb(0, 100, 0);
 }
 
+.textLayer ::-moz-selection { background: rgb(0,0,255); }
+
 .textLayer ::selection { background: rgb(0,0,255); }
-.textLayer ::-moz-selection { background: rgb(0,0,255); }
 
 .textLayer .endOfContent {
   display: block;
   position: absolute;
   left: 0px;
   top: 100%;
   right: 0px;
   bottom: 0px;
   z-index: -1;
   cursor: default;
   -moz-user-select: none;
+       user-select: none;
 }
 
 .textLayer .endOfContent.active {
   top: 0px;
 }
 
 
 .annotationLayer section {
@@ -204,17 +205,17 @@
    * when the element has focus and revert this when it loses focus.
    */
   width: 115%;
 }
 
 .annotationLayer .buttonWidgetAnnotation.checkBox input,
 .annotationLayer .buttonWidgetAnnotation.radioButton input {
   -moz-appearance: none;
-  appearance: none;
+       appearance: none;
   padding: 0;
 }
 
 .annotationLayer .popupWrapper {
   position: absolute;
   width: 20em;
 }
 
@@ -360,27 +361,29 @@ select {
   top: 0px;
   border-top: 2px solid transparent;
   background-color: #000;
   width: 100%;
   height: 100%;
   overflow: hidden;
   cursor: none;
   -moz-user-select: none;
+       user-select: none;
 }
 
 #viewerContainer.pdfPresentationMode:fullscreen {
   top: 0px;
   border-top: 2px solid transparent;
   background-color: #000;
   width: 100%;
   height: 100%;
   overflow: hidden;
   cursor: none;
   -moz-user-select: none;
+       user-select: none;
 }
 
 .pdfPresentationMode:-moz-full-screen a:not(.internalLink) {
   display: none;
 }
 
 .pdfPresentationMode:fullscreen a:not(.internalLink) {
   display: none;
@@ -434,16 +437,17 @@ html[dir='rtl'] #sidebarContainer {
   top: 36px;
 }
 
 #outerContainer.sidebarResizing #sidebarContainer {
   /* Improve responsiveness and avoid visual glitches when the sidebar is resized. */
   transition-duration: 0s;
   /* Prevent e.g. the thumbnails being selected when the sidebar is resized. */
   -moz-user-select: none;
+       user-select: none;
 }
 
 #outerContainer.sidebarMoving #sidebarContainer,
 #outerContainer.sidebarOpen #sidebarContainer {
   visibility: visible;
 }
 html[dir='ltr'] #outerContainer.sidebarOpen #sidebarContainer {
   left: 0px;
@@ -659,24 +663,22 @@ html[dir='ltr'] .findbar {
   left: 68px;
 }
 html[dir='rtl'] .findbar {
   right: 68px;
 }
 
 .findbar label {
   -moz-user-select: none;
+       user-select: none;
 }
 
 #findInput {
   width: 200px;
 }
-#findInput::-moz-placeholder {
-  font-style: italic;
-}
 #findInput::placeholder {
   font-style: italic;
 }
 #findInput[data-status="pending"] {
   background-image: url(images/loading-small.png);
   background-repeat: no-repeat;
   background-position: right;
 }
@@ -928,16 +930,17 @@ html[dir='rtl'] .splitToolbarButtonSepar
   min-width: 16px;
   padding: 2px 6px 0;
   border: 1px solid transparent;
   border-radius: 2px;
   color: hsla(0,0%,100%,.8);
   font-size: 12px;
   line-height: 14px;
   -moz-user-select: none;
+       user-select: none;
   /* Opera does not support user-select, use <... unselectable="on"> instead */
   cursor: default;
   transition-property: background-color, border-color, box-shadow;
   transition-duration: 150ms;
   transition-timing-function: ease;
 }
 
 html[dir='ltr'] .toolbarButton,
@@ -1359,16 +1362,17 @@ html[dir='rtl'] .verticalToolbarSeparato
   margin: 4px 2px 4px 0;
   border: 1px solid transparent;
   border-radius: 2px;
   color: hsl(0,0%,85%);
   font-size: 12px;
   line-height: 14px;
   text-align: left;
   -moz-user-select: none;
+       user-select: none;
   cursor: default;
 }
 
 #thumbnailView {
   position: absolute;
   width: calc(100% - 60px);
   top: 0;
   bottom: 0;
@@ -1447,16 +1451,17 @@ a:focus > .thumbnail > .thumbnailSelecti
 #outlineView,
 #attachmentsView {
   position: absolute;
   width: calc(100% - 8px);
   top: 0;
   bottom: 0;
   overflow: auto;
   -moz-user-select: none;
+       user-select: none;
 }
 
 #outlineView {
   padding: 4px 4px 0;
 }
 #attachmentsView {
   padding: 3px 4px 0;
 }
@@ -1480,16 +1485,17 @@ html[dir='rtl'] .outlineItem > .outlineI
                                   of the container. */
   height: auto;
   margin-bottom: 1px;
   border-radius: 2px;
   color: hsla(0,0%,100%,.8);
   font-size: 13px;
   line-height: 15px;
   -moz-user-select: none;
+       user-select: none;
   white-space: normal;
 }
 
 .attachmentsItem > button {
   border: 0 none;
   background: none;
   cursor: pointer;
   width: 100%;
@@ -1574,18 +1580,18 @@ html[dir='rtl'] .outlineItemToggler::bef
   color: hsla(0,0%,100%,.8);
   font-style: italic;
   cursor: default;
 }
 
 /* TODO: file FF bug to support ::-moz-selection:window-inactive
    so we can override the opaque grey background when the window is inactive;
    see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
+::-moz-selection { background: rgba(0,0,255,0.3); }
 ::selection { background: rgba(0,0,255,0.3); }
-::-moz-selection { background: rgba(0,0,255,0.3); }
 
 #errorWrapper {
   background: none repeat scroll 0 0 #FF5555;
   color: white;
   left: 0;
   position: absolute;
   right: 0;
   z-index: 1000;