From 84b4f53ed60432b85cb1d63f085160d4302925ea Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Fri, 18 May 2012 11:14:09 -0500
Subject: [PATCH 01/10] Adjusts MacRoman switch heuristics threshold

---
 src/fonts.js                 | 6 +++---
 test/pdfs/issue1709.pdf.link | 1 +
 test/test_manifest.json      | 8 ++++++++
 3 files changed, 12 insertions(+), 3 deletions(-)
 create mode 100644 test/pdfs/issue1709.pdf.link

diff --git a/src/fonts.js b/src/fonts.js
index 22037e724..9e4189462 100644
--- a/src/fonts.js
+++ b/src/fonts.js
@@ -1976,9 +1976,9 @@ var Font = (function FontClosure() {
             this.isSymbolicFont = false;
         }
 
-        // heuristics: if removed more than 2 glyphs encoding WinAnsiEncoding
-        // does not set properly
-        if (glyphsRemoved > 2) {
+        // heuristics: if removed more than 10 glyphs encoding WinAnsiEncoding
+        // does not set properly (broken PDFs have about 100 removed glyphs)
+        if (glyphsRemoved > 10) {
           warn('Switching TrueType encoding to MacRomanEncoding for ' +
                this.name + ' font');
           encoding = Encodings.MacRomanEncoding;
diff --git a/test/pdfs/issue1709.pdf.link b/test/pdfs/issue1709.pdf.link
new file mode 100644
index 000000000..ca5121b2d
--- /dev/null
+++ b/test/pdfs/issue1709.pdf.link
@@ -0,0 +1 @@
+http://www.mft-online.de/files/medizinerreport_2012.pdf
diff --git a/test/test_manifest.json b/test/test_manifest.json
index 5ec9e850f..3caaf3bd4 100644
--- a/test/test_manifest.json
+++ b/test/test_manifest.json
@@ -402,6 +402,14 @@
       "link": true,
       "type": "eq"
     },
+    {  "id": "issue1709",
+      "file": "pdfs/issue1709.pdf",
+      "md5": "84497bd23b7c82d03d2681a1cb1d9ed0",
+      "rounds": 1,
+      "pageLimit": 10,
+      "link": true,
+      "type": "eq"
+    },
     {  "id": "issue1015",
       "file": "pdfs/issue1015.pdf",
       "md5": "b61503d1b445742b665212866afb60e2",

From ed02be835222091a62797b6d5c69286ed8c97fb2 Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Fri, 18 May 2012 16:51:55 -0500
Subject: [PATCH 02/10] Removes the CID data from the CFF font

---
 src/fonts.js | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/fonts.js b/src/fonts.js
index 22037e724..d235881df 100644
--- a/src/fonts.js
+++ b/src/fonts.js
@@ -3477,7 +3477,7 @@ var CFFFont = (function CFFFontClosure() {
     this.properties = properties;
 
     var parser = new CFFParser(file, properties);
-    var cff = parser.parse();
+    var cff = parser.parse(true);
     var compiler = new CFFCompiler(cff);
     this.readExtra(cff);
     try {
@@ -3568,7 +3568,7 @@ var CFFParser = (function CFFParserClosure() {
     this.properties = properties;
   }
   CFFParser.prototype = {
-    parse: function CFFParser_parse() {
+    parse: function CFFParser_parse(normalizeCIDData) {
       var properties = this.properties;
       var cff = new CFF();
       this.cff = cff;
@@ -3623,6 +3623,21 @@ var CFFParser = (function CFFParserClosure() {
       cff.charset = charset;
       cff.encoding = encoding;
 
+      if (!cff.isCIDFont || !normalizeCIDData)
+        return cff;
+
+      // DirectWrite does not like CID fonts data. Trying to convert/flatten
+      // the font data and remove CID properties.
+      if (cff.fdArray.length !== 1)
+        error('Unable to normalize CID font in CFF data');
+
+      var fontDict = cff.fdArray[0];
+      fontDict.setByKey(17, topDict.getByName('CharStrings'));
+      cff.topDict = fontDict;
+      cff.isCIDFont = false;
+      delete cff.fdArray;
+      delete cff.fdSelect;
+
       return cff;
     },
     parseHeader: function CFFParser_parseHeader() {

From ec6c185cf5b5f8ac3af3f9e2f45f3bf40b32e22f Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Sun, 20 May 2012 13:44:03 -0500
Subject: [PATCH 03/10] Allow parsing of the "glued" commands

---
 src/evaluator.js            | 64 ++++++-------------------------------
 src/parser.js               | 11 +++++--
 test/unit/evaluator_spec.js | 30 +++++++++++++++++
 3 files changed, 48 insertions(+), 57 deletions(-)

diff --git a/src/evaluator.js b/src/evaluator.js
index ae443fa81..2c07db88c 100644
--- a/src/evaluator.js
+++ b/src/evaluator.js
@@ -108,38 +108,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 
     // Compatibility
     BX: 'beginCompat',
-    EX: 'endCompat'
-  };
-
-  function splitCombinedOperations(operations) {
-    // Two or more operations can be combined together, trying to find which
-    // operations were concatenated.
-    var result = [];
-    var opIndex = 0;
-
-    if (!operations) {
-      return null;
-    }
-
-    while (opIndex < operations.length) {
-      var currentOp = '';
-      for (var op in OP_MAP) {
-        if (op == operations.substr(opIndex, op.length) &&
-            op.length > currentOp.length) {
-          currentOp = op;
-        }
-      }
-
-      if (currentOp.length > 0) {
-        result.push(operations.substr(opIndex, currentOp.length));
-        opIndex += currentOp.length;
-      } else {
-        return null;
-      }
-    }
+    EX: 'endCompat',
 
-    return result;
-  }
+    // (reserved partial commands for the lexer)
+    BM: null,
+    BD: null
+  };
 
   PartialEvaluator.prototype = {
     getOperatorList: function PartialEvaluator_getOperatorList(stream,
@@ -284,39 +258,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
       resources = resources || new Dict();
       var xobjs = resources.get('XObject') || new Dict();
       var patterns = resources.get('Pattern') || new Dict();
-      var parser = new Parser(new Lexer(stream), false, xref);
+      var parser = new Parser(new Lexer(stream, OP_MAP), false, xref);
       var res = resources;
-      var hasNextObj = false, nextObjs;
       var args = [], obj;
       var TILING_PATTERN = 1, SHADING_PATTERN = 2;
 
       while (true) {
-        if (hasNextObj) {
-          obj = nextObjs.pop();
-          hasNextObj = (nextObjs.length > 0);
-        } else {
-          obj = parser.getObj();
-          if (isEOF(obj))
-            break;
-        }
+        obj = parser.getObj();
+        if (isEOF(obj))
+          break;
 
         if (isCmd(obj)) {
           var cmd = obj.cmd;
           var fn = OP_MAP[cmd];
-          if (!fn) {
-            // invalid content command, trying to recover
-            var cmds = splitCombinedOperations(cmd);
-            if (cmds) {
-              cmd = cmds[0];
-              fn = OP_MAP[cmd];
-              // feeding other command on the next iteration
-              hasNextObj = true;
-              nextObjs = [];
-              for (var idx = 1; idx < cmds.length; idx++) {
-                 nextObjs.push(Cmd.get(cmds[idx]));
-              }
-            }
-          }
           assertWellFormed(fn, 'Unknown command "' + cmd + '"');
           // TODO figure out how to type-check vararg functions
 
diff --git a/src/parser.js b/src/parser.js
index 2855018a6..3a2218f0d 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -264,8 +264,9 @@ var Parser = (function ParserClosure() {
 })();
 
 var Lexer = (function LexerClosure() {
-  function Lexer(stream) {
+  function Lexer(stream, knownCommands) {
     this.stream = stream;
+    this.knownCommands = knownCommands;
   }
 
   Lexer.isSpace = function Lexer_isSpace(ch) {
@@ -529,12 +530,18 @@ var Lexer = (function LexerClosure() {
 
       // command
       var str = ch;
+      var knownCommands = this.knownCommands;
+      var knownCommandFound = knownCommands && (str in knownCommands);
       while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) {
+        // stop if known command is found and next character does not make
+        // the str a command
+        if (knownCommandFound && !((str + ch) in knownCommands))
+          break;
         stream.skip();
         if (str.length == 128)
           error('Command token too long: ' + str.length);
-
         str += ch;
+        knownCommandFound = knownCommands && (str in knownCommands);
       }
       if (str == 'true')
         return true;
diff --git a/test/unit/evaluator_spec.js b/test/unit/evaluator_spec.js
index 4ee0768a7..286b8158a 100644
--- a/test/unit/evaluator_spec.js
+++ b/test/unit/evaluator_spec.js
@@ -78,6 +78,36 @@ describe('evaluator', function() {
       expect(result.fnArray[1]).toEqual('save');
       expect(result.fnArray[2]).toEqual('save');
     });
+
+    it('should handle three glued operations #2', function() {
+      var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+                                           'prefix');
+      var resources = new ResourcesMock();
+      resources.Res1 = {};
+      var stream = new StringStream('B*BBMC');
+      var result = evaluator.getOperatorList(stream, resources, []);
+
+      expect(!!result.fnArray && !!result.argsArray).toEqual(true);
+      expect(result.fnArray.length).toEqual(3);
+      expect(result.fnArray[0]).toEqual('eoFillStroke');
+      expect(result.fnArray[1]).toEqual('fillStroke');
+      expect(result.fnArray[2]).toEqual('beginMarkedContent');
+    });
+
+    it('should handle glued operations and operands', function() {
+      var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+                                           'prefix');
+      var stream = new StringStream('q5 Ts');
+      var result = evaluator.getOperatorList(stream, new ResourcesMock(), []);
+
+      expect(!!result.fnArray && !!result.argsArray).toEqual(true);
+      expect(result.fnArray.length).toEqual(2);
+      expect(result.fnArray[0]).toEqual('save');
+      expect(result.fnArray[1]).toEqual('setTextRise');
+      expect(result.argsArray.length).toEqual(2);
+      expect(result.argsArray[1].length).toEqual(1);
+      expect(result.argsArray[1][0]).toEqual(5);
+    });
   });
 });
 

From 43f1946c7a57b42cf1306c5857859cec9209edbc Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Sun, 20 May 2012 14:05:23 -0500
Subject: [PATCH 04/10] Add prefixes for literals

---
 src/evaluator.js            | 10 +++++++++-
 test/unit/evaluator_spec.js | 18 ++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/src/evaluator.js b/src/evaluator.js
index 2c07db88c..1dab7de0b 100644
--- a/src/evaluator.js
+++ b/src/evaluator.js
@@ -112,7 +112,15 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 
     // (reserved partial commands for the lexer)
     BM: null,
-    BD: null
+    BD: null,
+    'true': null,
+    fa: null,
+    fal: null,
+    fals: null,
+    'false': null,
+    nu: null,
+    nul: null,
+    'null': null
   };
 
   PartialEvaluator.prototype = {
diff --git a/test/unit/evaluator_spec.js b/test/unit/evaluator_spec.js
index 286b8158a..e31a525ac 100644
--- a/test/unit/evaluator_spec.js
+++ b/test/unit/evaluator_spec.js
@@ -108,6 +108,24 @@ describe('evaluator', function() {
       expect(result.argsArray[1].length).toEqual(1);
       expect(result.argsArray[1][0]).toEqual(5);
     });
+
+    it('should handle glued operations and literals', function() {
+      var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+                                           'prefix');
+      var stream = new StringStream('trueifalserinulli');
+      var result = evaluator.getOperatorList(stream, new ResourcesMock(), []);
+
+      expect(!!result.fnArray && !!result.argsArray).toEqual(true);
+      expect(result.fnArray.length).toEqual(3);
+      expect(result.fnArray[0]).toEqual('setFlatness');
+      expect(result.fnArray[1]).toEqual('setRenderingIntent');
+      expect(result.fnArray[2]).toEqual('setFlatness');
+      expect(result.argsArray.length).toEqual(3);
+      expect(result.argsArray[0].length).toEqual(1);
+      expect(result.argsArray[0][0]).toEqual(true);
+      expect(result.argsArray[1].length).toEqual(1);
+      expect(result.argsArray[1][0]).toEqual(false);
+    });
   });
 });
 

From 874357aac1762f111192c212452ba43bc9d0ee70 Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Mon, 21 May 2012 15:23:49 -0500
Subject: [PATCH 05/10] Comment for knownCommands

---
 src/parser.js | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/parser.js b/src/parser.js
index 3a2218f0d..6c9b4e67f 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -266,6 +266,13 @@ var Parser = (function ParserClosure() {
 var Lexer = (function LexerClosure() {
   function Lexer(stream, knownCommands) {
     this.stream = stream;
+    // The PDFs might have "glued" commands with other commands, operands or
+    // literals, e.g. "q1". The knownCommands is a dictionary of the valid
+    // commands and their prefixes. The prefixes are built the following way:
+    // if there a command that is a prefix of the other valid command or
+    // literal (e.g. 'f' and 'false') the following prefixes must be included,
+    // 'fa', 'fal', 'fals'. The prefixes are not needed, if the command has no
+    // other commands or literals as a prefix. The knowCommands is optional.
     this.knownCommands = knownCommands;
   }
 

From 6971aec9f33558c8459733330b05fcc2f555ffd0 Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Mon, 21 May 2012 22:15:09 -0500
Subject: [PATCH 06/10] Adjust heuristic to properly handle unicode characters

---
 src/evaluator.js             | 5 +++--
 test/pdfs/issue1721.pdf.link | 1 +
 test/test_manifest.json      | 8 ++++++++
 3 files changed, 12 insertions(+), 2 deletions(-)
 create mode 100644 test/pdfs/issue1721.pdf.link

diff --git a/src/evaluator.js b/src/evaluator.js
index 1dab7de0b..1a8db1473 100644
--- a/src/evaluator.js
+++ b/src/evaluator.js
@@ -611,8 +611,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
             }
           } else if (octet == 0x3E) {
             if (token.length) {
-              // XXX guessing chars size by checking number size in the CMap
-              if (token.length <= 2 && properties.composite)
+              // Heuristic: guessing chars size by checking numbers sizes
+              // in the CMap entries.
+              if (token.length == 2 && properties.composite)
                 properties.wideChars = false;
 
               if (token.length <= 4) {
diff --git a/test/pdfs/issue1721.pdf.link b/test/pdfs/issue1721.pdf.link
new file mode 100644
index 000000000..5a36166d8
--- /dev/null
+++ b/test/pdfs/issue1721.pdf.link
@@ -0,0 +1 @@
+http://www.lezarts.org/07oforhom/Faitsdivers/Hadopi/Le%20Rapport%20Hadopi,%20Intox.pdf
diff --git a/test/test_manifest.json b/test/test_manifest.json
index 4706b68c4..be30d6c81 100644
--- a/test/test_manifest.json
+++ b/test/test_manifest.json
@@ -550,6 +550,14 @@
       "link": false,
       "type": "eq"
     },
+    {  "id": "issue1721",
+      "file": "pdfs/issue1721.pdf",
+      "md5": "b47177f9e5197a76ec498733ecab60e6",
+      "rounds": 1,
+      "pageLimit": 2,
+      "link": true,
+      "type": "eq"
+    },
     {  "id": "issue1243",
       "file": "pdfs/issue1243.pdf",
       "md5": "130c849b83513d5ac5e03c6421fc7489",

From 424f52205c67262ebfc7fe05c3e94d0673d10bcf Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Tue, 22 May 2012 18:56:12 -0400
Subject: [PATCH 07/10] Fixed moz-central manifest; bundling Mochitests

---
 extensions/firefox/chrome-mozcentral.manifest |   8 ++
 make.js                                       |  10 +-
 test/mozcentral/Makefile.in                   |  21 ++++
 test/mozcentral/browser_pdfjs_main.js         | 102 ++++++++++++++++++
 test/mozcentral/file_pdfjs_test.pdf           | Bin 0 -> 14234 bytes
 test/mozcentral/head.js                       |  21 ++++
 6 files changed, 161 insertions(+), 1 deletion(-)
 create mode 100644 extensions/firefox/chrome-mozcentral.manifest
 create mode 100644 test/mozcentral/Makefile.in
 create mode 100644 test/mozcentral/browser_pdfjs_main.js
 create mode 100644 test/mozcentral/file_pdfjs_test.pdf
 create mode 100644 test/mozcentral/head.js

diff --git a/extensions/firefox/chrome-mozcentral.manifest b/extensions/firefox/chrome-mozcentral.manifest
new file mode 100644
index 000000000..8348f377e
--- /dev/null
+++ b/extensions/firefox/chrome-mozcentral.manifest
@@ -0,0 +1,8 @@
+resource pdf.js content/
+component {6457a96b-2d68-439a-bcfa-44465fbcdbb1} components/PdfStreamConverter.js
+contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {6457a96b-2d68-439a-bcfa-44465fbcdbb1}
+
+# Additional resources for pdf.js
+
+# PDFJS_SUPPORTED_LOCALES
+
diff --git a/make.js b/make.js
index 88f7c69a0..05c9cceff 100755
--- a/make.js
+++ b/make.js
@@ -377,9 +377,10 @@ target.mozcentral = function() {
   echo('### Building mozilla-central extension');
 
   var MOZCENTRAL_DIR = BUILD_DIR + 'mozcentral/',
-      MOZCENTRAL_EXTENSION_DIR = MOZCENTRAL_DIR + 'browser/app/profile/extensions/uriloader@pdf.js/',
+      MOZCENTRAL_EXTENSION_DIR = MOZCENTRAL_DIR + 'browser/extensions/pdfjs/',
       MOZCENTRAL_CONTENT_DIR = MOZCENTRAL_EXTENSION_DIR + 'content/',
       MOZCENTRAL_L10N_DIR = MOZCENTRAL_DIR + 'browser/locales/en-US/pdfviewer/',
+      MOZCENTRAL_TEST_DIR = MOZCENTRAL_EXTENSION_DIR + 'test/',
       FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + '/firefox/content/',
       FIREFOX_EXTENSION_FILES_TO_COPY =
         ['*.js',
@@ -415,6 +416,8 @@ target.mozcentral = function() {
   // Copy extension files
   cd('extensions/firefox');
   cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR + MOZCENTRAL_EXTENSION_DIR);
+  mv('-f', ROOT_DIR + MOZCENTRAL_EXTENSION_DIR + '/chrome-mozcentral.manifest',
+           ROOT_DIR + MOZCENTRAL_EXTENSION_DIR + '/chrome.manifest')
   cd(ROOT_DIR);
 
   // Copy a standalone version of pdf.js inside the content directory
@@ -457,6 +460,11 @@ target.mozcentral = function() {
       extensionFiles += file+'\n';
   });
   extensionFiles.to('extension-files');
+  cd(ROOT_DIR);
+
+  // Copy test files
+  mkdir('-p', MOZCENTRAL_TEST_DIR);
+  cp('-Rf', 'test/mozcentral/*', MOZCENTRAL_TEST_DIR);
 };
 
 //
diff --git a/test/mozcentral/Makefile.in b/test/mozcentral/Makefile.in
new file mode 100644
index 000000000..8c9face17
--- /dev/null
+++ b/test/mozcentral/Makefile.in
@@ -0,0 +1,21 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH     = ../../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+relativesrcdir  = browser/extensions/pdfjs/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_BROWSER_TEST_FILES = \
+  head.js \
+  browser_pdfjs_main.js \
+  file_pdfjs_test.pdf \
+  $(NULL)
+
+libs:: $(_BROWSER_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) 
diff --git a/test/mozcentral/browser_pdfjs_main.js b/test/mozcentral/browser_pdfjs_main.js
new file mode 100644
index 000000000..d3f5dd646
--- /dev/null
+++ b/test/mozcentral/browser_pdfjs_main.js
@@ -0,0 +1,102 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const RELATIVE_DIR = "browser/extensions/pdfjs/test/";
+const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR;
+
+function test() {
+  waitForExplicitFinish();
+
+  var tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf");
+  var newTabBrowser = gBrowser.getBrowserForTab(tab);
+  newTabBrowser.addEventListener("pagechange", function onPageChange() {
+    newTabBrowser.removeEventListener("pagechange", onPageChange, true);
+
+    var document = newTabBrowser.contentDocument,
+        window = newTabBrowser.contentWindow;
+
+    //
+    // Overall sanity tests
+    //
+    ok(document.querySelector('div#viewer'), "document content has viewer UI");
+    ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object");
+
+    //
+    // Sidebar: open
+    //
+    var sidebar = document.querySelector('button#sidebarToggle'),
+        outerContainer = document.querySelector('div#outerContainer');
+
+    sidebar.click();
+    ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click');
+
+    // Thumbnails are created asynchronously - wait for them
+    waitForElement(document, 'canvas#thumbnail2', function(error) {
+      if (error)
+        finish();
+
+      //
+      // Page change from thumbnail click
+      //
+      var pageNumber = document.querySelector('input#pageNumber');
+      is(parseInt(pageNumber.value), 1, 'initial page is 1');
+
+      var thumbnail = document.querySelector('canvas#thumbnail2');
+      ok(thumbnail, 'thumbnail2 is available');
+      if (thumbnail) {
+        thumbnail.click();
+        is(parseInt(pageNumber.value), 2, 'clicking on thumbnail changes page');
+      }
+
+      //
+      // Sidebar: close
+      //
+      sidebar.click();
+      ok(!outerContainer.classList.contains('sidebarOpen'), 'sidebar closes on click');
+
+      //
+      // Page change from prev/next buttons
+      //
+      var prevPage = document.querySelector('button#previous'),
+          nextPage = document.querySelector('button#next');
+
+      nextPage.click();
+      is(parseInt(pageNumber.value), 2, 'page increases after clicking on next');
+
+      prevPage.click();
+      is(parseInt(pageNumber.value), 1, 'page decreases after clicking on previous');
+
+      //
+      // Bookmark button
+      //
+      var viewBookmark = document.querySelector('a#viewBookmark');
+      viewBookmark.click();
+      ok(viewBookmark.href.length > 0, 'viewBookmark button has href');
+
+      //
+      // Zoom in/out
+      //
+      var zoomOut = document.querySelector('button.zoomOut'),
+          zoomIn = document.querySelector('button.zoomIn');
+
+      // Zoom in
+      var oldWidth = document.querySelector('canvas#page1').width;
+      zoomIn.click();
+      var newWidth = document.querySelector('canvas#page1').width;
+      ok(oldWidth < newWidth, 'zooming in increases page width (old: '+oldWidth+', new: '+newWidth+')');
+
+      // Zoom out
+      var oldWidth = document.querySelector('canvas#page1').width;
+      zoomOut.click();
+      var newWidth = document.querySelector('canvas#page1').width;
+      ok(oldWidth > newWidth, 'zooming out decreases page width (old: '+oldWidth+', new: '+newWidth+')');
+
+      finish();
+    });
+  }, true, true);
+
+  registerCleanupFunction(function() {
+    gBrowser.removeTab(tab);
+  });
+}
diff --git a/test/mozcentral/file_pdfjs_test.pdf b/test/mozcentral/file_pdfjs_test.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..ea5ebe39538ca03715041d802ad769005141f200
GIT binary patch
literal 14234
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

literal 0
HcmV?d00001

diff --git a/test/mozcentral/head.js b/test/mozcentral/head.js
new file mode 100644
index 000000000..6ee421b2f
--- /dev/null
+++ b/test/mozcentral/head.js
@@ -0,0 +1,21 @@
+// Waits for element 'el' to exist in the DOM of 'doc' before executing 'callback'
+// Useful when elements are created asynchronously, e.g. after a Web Worker task
+function waitForElement(doc, el, callback) {
+  var time = 0,
+      interval = 10,
+      timeout = 5000;
+
+  var checkEl = setInterval(function() {
+    if (doc.querySelector(el)) {
+      clearInterval(checkEl);
+      if (callback) callback();
+    }
+
+    time += interval;
+    if (time > timeout) {
+      ok(false, 'waitForElement timed out on element: '+el);
+      clearInterval(checkEl);
+      if (callback) callback(true);
+    }
+  }, interval);
+}

From ea60f2a0880ba015e4af0c6cf04b3e0aee7429da Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Tue, 22 May 2012 19:20:24 -0400
Subject: [PATCH 08/10] addr reviewer comments

---
 extensions/firefox/chrome-mozcentral.manifest | 5 -----
 make.js                                       | 3 +--
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/extensions/firefox/chrome-mozcentral.manifest b/extensions/firefox/chrome-mozcentral.manifest
index 8348f377e..2618c476b 100644
--- a/extensions/firefox/chrome-mozcentral.manifest
+++ b/extensions/firefox/chrome-mozcentral.manifest
@@ -1,8 +1,3 @@
 resource pdf.js content/
 component {6457a96b-2d68-439a-bcfa-44465fbcdbb1} components/PdfStreamConverter.js
 contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {6457a96b-2d68-439a-bcfa-44465fbcdbb1}
-
-# Additional resources for pdf.js
-
-# PDFJS_SUPPORTED_LOCALES
-
diff --git a/make.js b/make.js
index 05c9cceff..7862b1dc5 100755
--- a/make.js
+++ b/make.js
@@ -394,8 +394,7 @@ target.mozcentral = function() {
       DEFAULT_LOCALE_FILES =
         [LOCALE_SRC_DIR + 'en-US/viewer.properties'],
       FIREFOX_MC_EXTENSION_FILES =
-        ['bootstrap.js',
-         'icon.png',
+        ['icon.png',
          'icon64.png',
          'chrome.manifest',
          'components',

From d5bfd71c02999d53e5e35db236806023949d9d0f Mon Sep 17 00:00:00 2001
From: Sam <Sam@babelzilla.org>
Date: Tue, 22 May 2012 19:33:39 -0500
Subject: [PATCH 09/10] Czech locale

---
 l10n/cs/metadata.inc      |  8 +++++++
 l10n/cs/viewer.properties | 45 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)
 create mode 100644 l10n/cs/metadata.inc
 create mode 100644 l10n/cs/viewer.properties

diff --git a/l10n/cs/metadata.inc b/l10n/cs/metadata.inc
new file mode 100644
index 000000000..ed5c2a1c7
--- /dev/null
+++ b/l10n/cs/metadata.inc
@@ -0,0 +1,8 @@
+    <em:localized>
+      <Description>
+        <em:locale>cs</em:locale>
+        <em:name>PDF Viewer</em:name>
+        <em:description>Používá HTML5 pro zobrazení PDF souborů přímo ve Firefoxu.</em:description>
+      </Description>
+    </em:localized>
+
diff --git a/l10n/cs/viewer.properties b/l10n/cs/viewer.properties
new file mode 100644
index 000000000..96b596b16
--- /dev/null
+++ b/l10n/cs/viewer.properties
@@ -0,0 +1,45 @@
+bookmark.title=Aktuální zobrazení(zkopírovat nebo otevřít v novém okně)
+previous.title=Předchozí stránka
+next.title=Další stránka
+print.title=Tisk
+download.title=Stáhnout
+zoom_out.title=Zmenšit
+zoom_in.title=Zvětšit
+error_more_info=Více informací
+error_less_info=Méně informací
+error_close=Zavřít
+error_build=PDF.JS Build: {{build}}
+error_message=Zpráva:{{message}}
+error_stack=Stack:{{stack}}
+error_file=Soubor:{{file}}
+error_line=Řádek:{{line}}
+page_scale_width=Šířka stránky
+page_scale_fit=Stránka
+page_scale_auto=Automatické přibližení
+page_scale_actual=Skutečná velikost
+toggle_slider.title=Přepnout posuvník
+thumbs.title=Zobrazit náhledy
+outline.title=Zobrazit osnovu dokumentu
+loading=Načítám... {{percent}}%
+loading_error_indicator=Chyba
+loading_error=Došlo k chybě při načítání PDF.
+rendering_error=Došlo k chybě při vykreslování stránky.
+page_label=Stránka:
+page_of=z{{pageCount}}
+no_outline=Žádné osnovy k dispozici
+open_file.title=Otevřít soubor
+text_annotation_type=[{{type}}Anotace]
+toggle_slider_label=Přepnout posuvník
+thumbs_label=Náhledy
+outline_label=Přehled dokumentu
+bookmark_label=Aktuální zobrazení
+previous_label=Předchozí
+next_label=Další
+print_label=Tisk
+download_label=Stáhnout
+zoom_out_label=Zmenšit
+zoom_in_label=Přiblížit
+zoom.title=Zvětšit
+thumb_page_title=Stránka{{page}}
+thumb_page_canvas=Náhled stránky {{page}}
+request_password=PDF je chráněn heslem:

From 4bee4c6ee862eebdebd880fceb86b206067a17d9 Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Wed, 23 May 2012 15:57:14 -0700
Subject: [PATCH 10/10] Use different id's for moz central and extension.

---
 extensions/firefox/chrome-mozcentral.manifest |  4 +--
 .../firefox/components/PdfStreamConverter.js  | 12 ++++-----
 extensions/firefox/install.rdf.in             | 27 -------------------
 make.js                                       | 20 +++++++++-----
 4 files changed, 21 insertions(+), 42 deletions(-)
 delete mode 100644 extensions/firefox/install.rdf.in

diff --git a/extensions/firefox/chrome-mozcentral.manifest b/extensions/firefox/chrome-mozcentral.manifest
index 2618c476b..a2a6757c3 100644
--- a/extensions/firefox/chrome-mozcentral.manifest
+++ b/extensions/firefox/chrome-mozcentral.manifest
@@ -1,3 +1,3 @@
 resource pdf.js content/
-component {6457a96b-2d68-439a-bcfa-44465fbcdbb1} components/PdfStreamConverter.js
-contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {6457a96b-2d68-439a-bcfa-44465fbcdbb1}
+component {d0c5195d-e798-49d4-b1d3-9324328b2291} components/PdfStreamConverter.js
+contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {d0c5195d-e798-49d4-b1d3-9324328b2291}
diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js
index 1bcccbe22..2c984e86c 100644
--- a/extensions/firefox/components/PdfStreamConverter.js
+++ b/extensions/firefox/components/PdfStreamConverter.js
@@ -11,7 +11,7 @@ const Cr = Components.results;
 const Cu = Components.utils;
 const PDFJS_EVENT_ID = 'pdf.js.message';
 const PDF_CONTENT_TYPE = 'application/pdf';
-const EXT_PREFIX = 'extensions.uriloader@pdf.js';
+const PREF_PREFIX = 'PDFJSSCRIPT_PREF_PREFIX';
 const MAX_DATABASE_LENGTH = 4096;
 const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
 const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
@@ -57,7 +57,7 @@ function getStringPref(pref, def) {
 }
 
 function log(aMsg) {
-  if (!getBoolPref(EXT_PREFIX + '.pdfBugEnabled', false))
+  if (!getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false))
     return;
   let msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
   Services.console.logStringMessage(msg);
@@ -136,12 +136,12 @@ ChromeActions.prototype = {
     // Protect against something sending tons of data to setDatabase.
     if (data.length > MAX_DATABASE_LENGTH)
       return;
-    setStringPref(EXT_PREFIX + '.database', data);
+    setStringPref(PREF_PREFIX + '.database', data);
   },
   getDatabase: function() {
     if (inPrivateBrowsing)
       return '{}';
-    return getStringPref(EXT_PREFIX + '.database', '{}');
+    return getStringPref(PREF_PREFIX + '.database', '{}');
   },
   getLocale: function() {
     return getStringPref('general.useragent.locale', 'en-US');
@@ -160,7 +160,7 @@ ChromeActions.prototype = {
     }
   },
   pdfBugEnabled: function() {
-    return getBoolPref(EXT_PREFIX + '.pdfBugEnabled', false);
+    return getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false);
   }
 };
 
@@ -190,7 +190,7 @@ function PdfStreamConverter() {
 PdfStreamConverter.prototype = {
 
   // properties required for XPCOM registration:
-  classID: Components.ID('{6457a96b-2d68-439a-bcfa-44465fbcdbb1}'),
+  classID: Components.ID('{PDFJSSCRIPT_STREAM_CONVERTER_ID}'),
   classDescription: 'pdf.js Component',
   contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*',
 
diff --git a/extensions/firefox/install.rdf.in b/extensions/firefox/install.rdf.in
deleted file mode 100644
index 084d6dc2d..000000000
--- a/extensions/firefox/install.rdf.in
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0"?>
-
-#filter substitution
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>uriloader@pdf.js</em:id>
-    <!-- PDFJS_LOCALIZED_METADATA -->
-    <em:name>PDF Viewer</em:name>
-    <em:version>PDFJSSCRIPT_VERSION</em:version>
-    <em:targetApplication>
-      <Description>
-       <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-       <em:minVersion>@FIREFOX_VERSION@</em:minVersion>
-       <em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
-     </Description>
-    </em:targetApplication>
-    <em:strictCompatibility>true</em:strictCompatibility>
-    <em:bootstrap>true</em:bootstrap>
-    <em:creator>Mozilla</em:creator>
-    <em:description>Uses HTML5 to display PDF files directly in Firefox.</em:description>
-    <em:homepageURL>https://support.mozilla.org/kb/Opening%20PDF%20files%20within%20Firefox</em:homepageURL>
-    <em:type>2</em:type>
-  </Description>
-</RDF>
diff --git a/make.js b/make.js
index 7862b1dc5..e5202b296 100755
--- a/make.js
+++ b/make.js
@@ -9,7 +9,11 @@ var ROOT_DIR = __dirname + '/', // absolute path to project's root
     LOCALE_SRC_DIR = 'l10n/',
     GH_PAGES_DIR = BUILD_DIR + 'gh-pages/',
     REPO = 'git@github.com:mozilla/pdf.js.git',
-    PYTHON_BIN = 'python2.7';
+    PYTHON_BIN = 'python2.7',
+    MOZCENTRAL_PREF_PREFIX = 'pdfjs',
+    FIREFOX_PREF_PREFIX = 'extensions.uriloader@pdf.js',
+    MOZCENTRAL_STREAM_CONVERTER_ID = 'd0c5195d-e798-49d4-b1d3-9324328b2291',
+    FIREFOX_STREAM_CONVERTER_ID = '6457a96b-2d68-439a-bcfa-44465fbcdbb1';
 
 //
 // make all
@@ -348,6 +352,9 @@ target.firefox = function() {
   sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/install.rdf');
   sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/update.rdf');
 
+  sed('-i', /PDFJSSCRIPT_STREAM_CONVERTER_ID/, FIREFOX_STREAM_CONVERTER_ID, FIREFOX_BUILD_DIR + 'components/PdfStreamConverter.js');
+  sed('-i', /PDFJSSCRIPT_PREF_PREFIX/, FIREFOX_PREF_PREFIX, FIREFOX_BUILD_DIR + 'components/PdfStreamConverter.js');
+
   // Update localized metadata
   var localizedMetadata = cat(EXTENSION_SRC_DIR + '/firefox/metadata.inc');
   sed('-i', /.*PDFJS_LOCALIZED_METADATA.*\n/, localizedMetadata, FIREFOX_BUILD_DIR + '/install.rdf');
@@ -383,20 +390,17 @@ target.mozcentral = function() {
       MOZCENTRAL_TEST_DIR = MOZCENTRAL_EXTENSION_DIR + 'test/',
       FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + '/firefox/content/',
       FIREFOX_EXTENSION_FILES_TO_COPY =
-        ['*.js',
+        ['components/*.js',
          '*.svg',
          '*.png',
          '*.manifest',
-         'install.rdf.in',
          'README.mozilla',
          'components',
          '../../LICENSE'],
       DEFAULT_LOCALE_FILES =
         [LOCALE_SRC_DIR + 'en-US/viewer.properties'],
       FIREFOX_MC_EXTENSION_FILES =
-        ['icon.png',
-         'icon64.png',
-         'chrome.manifest',
+        ['chrome.manifest',
          'components',
          'content',
          'LICENSE'];
@@ -448,9 +452,11 @@ target.mozcentral = function() {
   cp(DEFAULT_LOCALE_FILES, MOZCENTRAL_L10N_DIR);
 
   // Update the build version number
-  sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, MOZCENTRAL_EXTENSION_DIR + 'install.rdf.in');
   sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, MOZCENTRAL_EXTENSION_DIR + 'README.mozilla');
 
+  sed('-i', /PDFJSSCRIPT_STREAM_CONVERTER_ID/, MOZCENTRAL_STREAM_CONVERTER_ID, MOZCENTRAL_EXTENSION_DIR + 'components/PdfStreamConverter.js');
+  sed('-i', /PDFJSSCRIPT_PREF_PREFIX/, MOZCENTRAL_PREF_PREFIX, MOZCENTRAL_EXTENSION_DIR + 'components/PdfStreamConverter.js');
+
   // List all files for mozilla-central
   cd(MOZCENTRAL_EXTENSION_DIR);
   var extensionFiles = '';