From 6acdf1791cdb8775024db1d7e6b87181fc9cbf83 Mon Sep 17 00:00:00 2001
From: Dmitry Kataev <dmitryskey@hotmail.com>
Date: Wed, 9 May 2012 21:29:50 +0400
Subject: [PATCH 1/8] Two or more operations can be combined together like qqBT

---
 src/evaluator.js | 44 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 33 insertions(+), 11 deletions(-)

diff --git a/src/evaluator.js b/src/evaluator.js
index 23c9d1f65..ae443fa81 100644
--- a/src/evaluator.js
+++ b/src/evaluator.js
@@ -112,14 +112,33 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
   };
 
   function splitCombinedOperations(operations) {
-    // Two operations can be combined together, trying to find which two
+    // Two or more operations can be combined together, trying to find which
     // operations were concatenated.
-    for (var i = operations.length - 1; i > 0; i--) {
-      var op1 = operations.substring(0, i), op2 = operations.substring(i);
-      if (op1 in OP_MAP && op2 in OP_MAP)
-        return [op1, op2]; // operations found
+    var result = [];
+    var opIndex = 0;
+
+    if (!operations) {
+      return null;
     }
-    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;
+      }
+    }
+
+    return result;
   }
 
   PartialEvaluator.prototype = {
@@ -267,14 +286,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
       var patterns = resources.get('Pattern') || new Dict();
       var parser = new Parser(new Lexer(stream), false, xref);
       var res = resources;
-      var hasNextObj = false, nextObj;
+      var hasNextObj = false, nextObjs;
       var args = [], obj;
       var TILING_PATTERN = 1, SHADING_PATTERN = 2;
 
       while (true) {
         if (hasNextObj) {
-          obj = nextObj;
-          hasNextObj = false;
+          obj = nextObjs.pop();
+          hasNextObj = (nextObjs.length > 0);
         } else {
           obj = parser.getObj();
           if (isEOF(obj))
@@ -290,9 +309,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
             if (cmds) {
               cmd = cmds[0];
               fn = OP_MAP[cmd];
-              // feeding other command on the next interation
+              // feeding other command on the next iteration
               hasNextObj = true;
-              nextObj = Cmd.get(cmds[1]);
+              nextObjs = [];
+              for (var idx = 1; idx < cmds.length; idx++) {
+                 nextObjs.push(Cmd.get(cmds[idx]));
+              }
             }
           }
           assertWellFormed(fn, 'Unknown command "' + cmd + '"');

From 324b867183f3c9b6cff15129644570b1d7047209 Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Thu, 10 May 2012 16:11:27 -0500
Subject: [PATCH 2/8] Adds evaluator tests

---
 test/unit/evaluator_spec.js | 83 +++++++++++++++++++++++++++++++++++++
 test/unit/unit_test.html    |  1 +
 2 files changed, 84 insertions(+)
 create mode 100644 test/unit/evaluator_spec.js

diff --git a/test/unit/evaluator_spec.js b/test/unit/evaluator_spec.js
new file mode 100644
index 000000000..4ee0768a7
--- /dev/null
+++ b/test/unit/evaluator_spec.js
@@ -0,0 +1,83 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+'use strict';
+
+describe('evaluator', function() {
+  function XrefMock(queue) {
+    this.queue = queue;
+  }
+  XrefMock.prototype = {
+    fetchIfRef: function() {
+      return this.queue.shift();
+    }
+  };
+  function HandlerMock() {
+    this.inputs = [];
+  }
+  HandlerMock.prototype = {
+    send: function(name, data) {
+      this.inputs({name: name, data: data});
+    }
+  };
+  function ResourcesMock() { }
+  ResourcesMock.prototype = {
+    get: function(name) {
+      return this[name];
+    }
+  };
+
+  describe('splitCombinedOperations', function() {
+    it('should reject unknown operations', function() {
+      var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+                                           'prefix');
+      var stream = new StringStream('qTT');
+      var thrown = false;
+      try {
+        evaluator.getOperatorList(stream, new ResourcesMock(), []);
+      } catch (e) {
+        thrown = e;
+      }
+      expect(thrown).toNotEqual(false);
+    });
+
+    it('should handle one operations', function() {
+      var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+                                           'prefix');
+      var stream = new StringStream('Q');
+      var result = evaluator.getOperatorList(stream, new ResourcesMock(), []);
+
+      expect(!!result.fnArray && !!result.argsArray).toEqual(true);
+      expect(result.fnArray.length).toEqual(1);
+      expect(result.fnArray[0]).toEqual('restore');
+    });
+
+    it('should handle two glued operations', function() {
+      var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+                                           'prefix');
+      var resources = new ResourcesMock();
+      resources.Res1 = {};
+      var stream = new StringStream('/Res1 DoQ');
+      var result = evaluator.getOperatorList(stream, resources, []);
+
+      expect(!!result.fnArray && !!result.argsArray).toEqual(true);
+      expect(result.fnArray.length).toEqual(2);
+      expect(result.fnArray[0]).toEqual('paintXObject');
+      expect(result.fnArray[1]).toEqual('restore');
+    });
+
+    it('should handle tree glued operations', function() {
+      var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+                                           'prefix');
+      var stream = new StringStream('qqq');
+      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('save');
+      expect(result.fnArray[1]).toEqual('save');
+      expect(result.fnArray[2]).toEqual('save');
+    });
+  });
+});
+
diff --git a/test/unit/unit_test.html b/test/unit/unit_test.html
index cdd0c297f..ca0a1aed1 100644
--- a/test/unit/unit_test.html
+++ b/test/unit/unit_test.html
@@ -39,6 +39,7 @@
   <script type="text/javascript" src="font_spec.js"></script>
   <script type="text/javascript" src="function_spec.js"></script>
   <script type="text/javascript" src="crypto_spec.js"></script>
+  <script type="text/javascript" src="evaluator_spec.js"></script>
   <script type="text/javascript" src="stream_spec.js"></script>
   <script type="text/javascript" src="api_spec.js"></script>
   <script type="text/javascript" src="metadata_spec.js"></script>

From 6323c8e084c8e263953dc2d5684859ee6f01b843 Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Fri, 11 May 2012 14:39:11 -0500
Subject: [PATCH 3/8] Loading extension resources via stringbundle

---
 extensions/chrome/.gitignore                  |  1 +
 extensions/firefox/.gitignore                 |  2 +
 extensions/firefox/chrome.manifest            |  3 +
 .../firefox/components/PdfStreamConverter.js  | 39 +++++++++++++
 make.js                                       | 55 +++++++++++--------
 web/viewer.js                                 | 11 ++--
 6 files changed, 84 insertions(+), 27 deletions(-)
 create mode 100644 extensions/chrome/.gitignore
 create mode 100644 extensions/firefox/chrome.manifest

diff --git a/extensions/chrome/.gitignore b/extensions/chrome/.gitignore
new file mode 100644
index 000000000..3eb92306c
--- /dev/null
+++ b/extensions/chrome/.gitignore
@@ -0,0 +1 @@
+content/
diff --git a/extensions/firefox/.gitignore b/extensions/firefox/.gitignore
index 08a23850c..6eec9a7f2 100644
--- a/extensions/firefox/.gitignore
+++ b/extensions/firefox/.gitignore
@@ -1,2 +1,4 @@
 content/
 metadata.inc
+chrome.manifest.inc
+locale/
diff --git a/extensions/firefox/chrome.manifest b/extensions/firefox/chrome.manifest
new file mode 100644
index 000000000..97b76306b
--- /dev/null
+++ b/extensions/firefox/chrome.manifest
@@ -0,0 +1,3 @@
+# Additional resources for pdf.js
+
+# PDFJS_SUPPORTED_LOCALES
diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js
index 49fd134ae..d84f19e58 100644
--- a/extensions/firefox/components/PdfStreamConverter.js
+++ b/extensions/firefox/components/PdfStreamConverter.js
@@ -4,6 +4,10 @@
 'use strict';
 
 var EXPORTED_SYMBOLS = ['PdfStreamConverter'];
+var DEFAULT_LOCALE = 'en-US';
+
+var IS_MOZCENTRAL = false; // PDFJS_SUPPORTED_LOCALES
+var SUPPORTED_LOCALES = [DEFAULT_LOCALE]; // PDFJS_SUPPORTED_LOCALES
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
@@ -59,6 +63,28 @@ function getDOMWindow(aChannel) {
   return win;
 }
 
+function getLocalizedStrings(path) {
+  var stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].
+      getService(Ci.nsIStringBundleService).
+      createBundle("chrome://pdfviewer/locale/" + path);
+
+  var map = {};
+  var enumerator = stringBundle.getSimpleEnumeration();
+  while (enumerator.hasMoreElements()) {
+    var string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
+    var key = string.key, property = 'textContent';
+    var i = key.lastIndexOf('.');
+    if (i >= 0) {
+      property = key.substring(i + 1);
+      key = key.substring(0, i);
+    }
+    if (!(key in map))
+      map[key] = {};
+    map[key][property] = string.value;
+  }
+  return map;
+}
+
 // All the priviledged actions.
 function ChromeActions() {
   this.inPrivateBrowswing = privateBrowsing.privateBrowsingEnabled;
@@ -113,6 +139,19 @@ ChromeActions.prototype = {
   getLocale: function() {
     return getStringPref('general.useragent.locale', 'en-US');
   },
+  getStrings: function(data) {
+    try {
+      // Lazy initialization of localizedStrings
+      if (!('localizedStrings' in this))
+        this.localizedStrings = getLocalizedStrings('viewer.properties');
+
+      var result = this.localizedStrings[data];
+      return JSON.stringify(result || null)
+    } catch (e) {
+      log('Unable to retrive localized strings: ' + e);
+      return 'null';
+    }
+  },
   pdfBugEnabled: function() {
     return getBoolPref(EXT_PREFIX + '.pdfBugEnabled', false);
   }
diff --git a/make.js b/make.js
index 0a39e0b07..34dc27c9b 100755
--- a/make.js
+++ b/make.js
@@ -6,6 +6,7 @@ var ROOT_DIR = __dirname + '/', // absolute path to project's root
     BUILD_TARGET = BUILD_DIR + 'pdf.js',
     FIREFOX_BUILD_DIR = BUILD_DIR + '/firefox/',
     EXTENSION_SRC_DIR = 'extensions/',
+    LOCALE_SRC_DIR = 'l10n/',
     GH_PAGES_DIR = BUILD_DIR + 'gh-pages/',
     REPO = 'git@github.com:mozilla/pdf.js.git',
     PYTHON_BIN = 'python2.7';
@@ -67,8 +68,9 @@ target.web = function() {
 // Creates localized resources for the viewer and extension.
 //
 target.locale = function() {
-  var L10N_PATH = 'l10n';
   var METADATA_OUTPUT = 'extensions/firefox/metadata.inc';
+  var CHROME_MANIFEST_OUTPUT = 'extensions/firefox/chrome.manifest.inc';
+  var EXTENSION_LOCALE_OUTPUT = 'extensions/firefox/locale';
   var VIEWER_OUTPUT = 'web/locale.properties';
   var DEFAULT_LOCALE = 'en-US';
 
@@ -76,13 +78,17 @@ target.locale = function() {
   echo();
   echo('### Building localization files');
 
-  var subfolders = ls(L10N_PATH);
+  rm('-rf', EXTENSION_LOCALE_OUTPUT);
+  mkdir('-p', EXTENSION_LOCALE_OUTPUT);
+
+  var subfolders = ls(LOCALE_SRC_DIR);
   subfolders.sort();
   var metadataContent = '';
+  var chromeManifestContent = '';
   var viewerOutput = '';
   for (var i = 0; i < subfolders.length; i++) {
     var locale = subfolders[i];
-    var path = L10N_PATH + '/' + locale;
+    var path = LOCALE_SRC_DIR + locale;
     if (!test('-d', path))
       continue;
 
@@ -91,12 +97,13 @@ target.locale = function() {
       continue;
     }
 
+    mkdir('-p', EXTENSION_LOCALE_OUTPUT + '/' + locale);
+    chromeManifestContent += 'locale  pdfviewer  ' + locale + '  locale/' + locale + '/\n';
+
     if (test('-f', path + '/viewer.properties')) {
       var properties = cat(path + '/viewer.properties');
-      if (locale == DEFAULT_LOCALE)
-        viewerOutput = '[*]\n' + properties + '\n' + viewerOutput;
-      else
-        viewerOutput = viewerOutput + '[' + locale + ']\n' + properties + '\n';
+      viewerOutput += '[' + locale + ']\n' + properties + '\n';
+      cp(path + '/viewer.properties', EXTENSION_LOCALE_OUTPUT + '/' + locale);
     }
 
     if (test('-f', path + '/metadata.inc')) {
@@ -106,6 +113,7 @@ target.locale = function() {
   }
   viewerOutput.to(VIEWER_OUTPUT);
   metadataContent.to(METADATA_OUTPUT);
+  chromeManifestContent.to(CHROME_MANIFEST_OUTPUT);
 };
 
 //
@@ -227,7 +235,7 @@ var EXTENSION_WEB_FILES =
        'web/viewer.css',
        'web/viewer.js',
        'web/viewer.html',
-       'external/webL10n/l10n.js',
+       'extensions/firefox/tools/l10n.js',
        'web/viewer-production.html'],
     EXTENSION_BASE_VERSION = 'f0f0418a9c6637981fe1182b9212c2d592774c7d',
     EXTENSION_VERSION_PREFIX = '0.3.',
@@ -277,21 +285,23 @@ target.firefox = function() {
          '*.rdf',
          '*.svg',
          '*.png',
+         '*.manifest',
          'components',
+         'locale',
          '../../LICENSE'],
       FIREFOX_EXTENSION_FILES =
         ['bootstrap.js',
          'install.rdf',
+         'chrome.manifest',
          'icon.png',
          'icon64.png',
          'components',
          'content',
+         'locale',
          'LICENSE'],
       FIREFOX_EXTENSION_NAME = 'pdf.js.xpi',
       FIREFOX_AMO_EXTENSION_NAME = 'pdf.js.amo.xpi';
 
-  var LOCALE_CONTENT = cat('web/locale.properties');
-
   target.production();
   target.buildnumber();
   cd(ROOT_DIR);
@@ -310,7 +320,6 @@ target.firefox = function() {
   // Copy a standalone version of pdf.js inside the content directory
   cp(BUILD_TARGET, FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR);
   cp('-R', EXTENSION_WEB_FILES, FIREFOX_BUILD_CONTENT_DIR + '/web');
-  cp('web/locale.properties', FIREFOX_BUILD_CONTENT_DIR + '/web');
   rm(FIREFOX_BUILD_CONTENT_DIR + '/web/viewer-production.html');
 
   // Copy over the firefox extension snippet so we can inline pdf.js in it
@@ -319,7 +328,6 @@ target.firefox = function() {
   // Modify the viewer so it does all the extension-only stuff.
   cd(FIREFOX_BUILD_CONTENT_DIR + '/web');
   sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUNDLE.*\n/, cat(ROOT_DIR + BUILD_TARGET), 'viewer-snippet-firefox-extension.html');
-  sed('-i', /.*PDFJSSCRIPT_LOCALE_DATA.*\n/, LOCALE_CONTENT, 'viewer-snippet-firefox-extension.html');
   sed('-i', /.*PDFJSSCRIPT_REMOVE_CORE.*\n/g, '', 'viewer.html');
   sed('-i', /.*PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION.*\n/g, '', 'viewer.html');
   sed('-i', /.*PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION.*\n/, cat('viewer-snippet-firefox-extension.html'), 'viewer.html');
@@ -328,7 +336,6 @@ target.firefox = function() {
   // We don't need pdf.js anymore since its inlined
   rm('-Rf', FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR);
   rm(FIREFOX_BUILD_CONTENT_DIR + '/web/viewer-snippet-firefox-extension.html');
-  rm(FIREFOX_BUILD_CONTENT_DIR + '/web/locale.properties');
   // Remove '.DS_Store' and other hidden files
   find(FIREFOX_BUILD_DIR).forEach(function(file) {
     if (file.match(/^\./))
@@ -342,6 +349,8 @@ target.firefox = function() {
   // Update localized metadata
   var localizedMetadata = cat(EXTENSION_SRC_DIR + '/firefox/metadata.inc');
   sed('-i', /.*PDFJS_LOCALIZED_METADATA.*\n/, localizedMetadata, FIREFOX_BUILD_DIR + '/install.rdf');
+  var chromeManifest = cat(EXTENSION_SRC_DIR + '/firefox/chrome.manifest.inc');
+  sed('-i', /.*PDFJS_SUPPORTED_LOCALES.*\n/, chromeManifest, FIREFOX_BUILD_DIR + '/chrome.manifest');
 
   // Create the xpi
   cd(FIREFOX_BUILD_DIR);
@@ -365,25 +374,27 @@ target.mozcentral = function() {
   echo();
   echo('### Building mozilla-central extension');
 
-  var MOZCENTRAL_DIR = BUILD_DIR + '/mozcentral',
-      MOZCENTRAL_CONTENT_DIR = MOZCENTRAL_DIR + '/content/',
-      MOZCENTRAL_L10N_DIR = MOZCENTRAL_DIR + '/l10n/',
+  var MOZCENTRAL_DIR = BUILD_DIR + 'mozcentral/',
+      MOZCENTRAL_EXTENSION_DIR = MOZCENTRAL_DIR + 'browser/app/profile/extensions/uriloader@pdf.js/',
+      MOZCENTRAL_CONTENT_DIR = MOZCENTRAL_EXTENSION_DIR + 'content/',
+      MOZCENTRAL_L10N_DIR = MOZCENTRAL_DIR + 'browser/locales/en-US/pdfviewer/',
       FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + '/firefox/content/',
       FIREFOX_EXTENSION_FILES_TO_COPY =
         ['*.js',
          '*.svg',
          '*.png',
+         '*.manifest',
          'install.rdf.in',
          'README.mozilla',
          'components',
          '../../LICENSE'],
       DEFAULT_LOCALE_FILES =
-        ['l10n/en-US/viewer.properties',
-         'l10n/en-US/metadata.inc'],
+        [LOCALE_SRC_DIR + 'en-US/viewer.properties'],
       FIREFOX_MC_EXTENSION_FILES =
         ['bootstrap.js',
          'icon.png',
          'icon64.png',
+         'chrome.manifest',
          'components',
          'content',
          'LICENSE'];
@@ -401,7 +412,7 @@ target.mozcentral = function() {
 
   // Copy extension files
   cd('extensions/firefox');
-  cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR + MOZCENTRAL_DIR);
+  cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR + MOZCENTRAL_EXTENSION_DIR);
   cd(ROOT_DIR);
 
   // Copy a standalone version of pdf.js inside the content directory
@@ -433,11 +444,11 @@ target.mozcentral = function() {
   cp(DEFAULT_LOCALE_FILES, MOZCENTRAL_L10N_DIR);
 
   // Update the build version number
-  sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, MOZCENTRAL_DIR + '/install.rdf.in');
-  sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, MOZCENTRAL_DIR + '/README.mozilla');
+  sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, MOZCENTRAL_EXTENSION_DIR + 'install.rdf.in');
+  sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, MOZCENTRAL_EXTENSION_DIR + 'README.mozilla');
 
   // List all files for mozilla-central
-  cd(MOZCENTRAL_DIR);
+  cd(MOZCENTRAL_EXTENSION_DIR);
   var extensionFiles = '';
   find(FIREFOX_MC_EXTENSION_FILES).forEach(function(file){
     if (test('-f', file))
diff --git a/web/viewer.js b/web/viewer.js
index dcbfcf14e..dd267065a 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -1362,11 +1362,12 @@ window.addEventListener('load', function webViewerLoad(evt) {
   if ('disableWorker' in hashParams)
     PDFJS.disableWorker = (hashParams['disableWorker'] === 'true');
 
-  var locale = !PDFJS.isFirefoxExtension ? navigator.language :
-    FirefoxCom.request('getLocale', null);
-  if ('locale' in hashParams)
-    locale = hashParams['locale'];
-  mozL10n.language.code = locale;
+  if (!PDFJS.isFirefoxExtension) {
+    var locale = navigator.language;
+    if ('locale' in hashParams)
+      locale = hashParams['locale'];
+    mozL10n.language.code = locale;
+  }
 
   if ('disableTextLayer' in hashParams)
     PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true');

From 56b3a7086e94775ff0cd3d03135d78965e0da066 Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Fri, 11 May 2012 14:42:20 -0500
Subject: [PATCH 4/8] Remove unused code

---
 extensions/firefox/components/PdfStreamConverter.js | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js
index d84f19e58..898058004 100644
--- a/extensions/firefox/components/PdfStreamConverter.js
+++ b/extensions/firefox/components/PdfStreamConverter.js
@@ -4,10 +4,6 @@
 'use strict';
 
 var EXPORTED_SYMBOLS = ['PdfStreamConverter'];
-var DEFAULT_LOCALE = 'en-US';
-
-var IS_MOZCENTRAL = false; // PDFJS_SUPPORTED_LOCALES
-var SUPPORTED_LOCALES = [DEFAULT_LOCALE]; // PDFJS_SUPPORTED_LOCALES
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;

From 5ca12d549e5d9d0ea1162e33067d5da7bf2389f9 Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Fri, 11 May 2012 15:14:50 -0500
Subject: [PATCH 5/8] Missing file

---
 extensions/firefox/tools/l10n.js | 113 +++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)
 create mode 100644 extensions/firefox/tools/l10n.js

diff --git a/extensions/firefox/tools/l10n.js b/extensions/firefox/tools/l10n.js
new file mode 100644
index 000000000..ad3b4ab95
--- /dev/null
+++ b/extensions/firefox/tools/l10n.js
@@ -0,0 +1,113 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+'use strict';
+
+// Small subset of the webL10n API by Fabien Cazenave for pdf.js extension.
+(function(window) {
+  var gLanguage = '';
+
+  // fetch an l10n objects
+  function getL10nData(key) {
+    var response = FirefoxCom.request('getStrings', key);
+    var data = JSON.parse(response);
+    if (!data)
+      console.warn('[l10n] #' + key + ' missing for [' + gLanguage + ']');
+    return data;
+  }
+
+  // replace {{arguments}} with their values
+  function substArguments(text, args) {
+    if (!args)
+      return text;
+
+    return text.replace(/\{\{\s*(\w+)\s*\}\}/g, function(all, name) {
+      return name in args ? args[name] : '{{' + name + '}}';
+    });
+  }
+
+  // translate a string
+  function translateString(key, args, fallback) {
+    var data = getL10nData(key);
+    if (!data && fallback)
+      data = {textContent: fallback};
+    if (!data)
+      return '{{' + key + '}}';
+    return substArguments(data.textContent, args);
+  }
+
+  // translate an HTML element
+  function translateElement(element) {
+    if (!element || !element.dataset)
+      return;
+
+    // get the related l10n object
+    var data = getL10nData(element.dataset.l10nId);
+    if (!data)
+      return;
+
+    // get arguments (if any)
+    // TODO: more flexible parser?
+    var args;
+    if (element.dataset.l10nArgs) try {
+      args = JSON.parse(element.dataset.l10nArgs);
+    } catch (e) {
+      console.warn('[l10n] could not parse arguments for #' + key + '');
+    }
+
+    // translate element
+    // TODO: security check?
+    for (var k in data)
+      element[k] = substArguments(data[k], args);
+  }
+
+
+  // translate an HTML subtree
+  function translateFragment(element) {
+    element = element || document.querySelector('html');
+
+    // check all translatable children (= w/ a `data-l10n-id' attribute)
+    var children = element.querySelectorAll('*[data-l10n-id]');
+    var elementCount = children.length;
+    for (var i = 0; i < elementCount; i++)
+      translateElement(children[i]);
+
+    // translate element itself if necessary
+    if (element.dataset.l10nId)
+      translateElement(element);
+  }
+
+  window.addEventListener('DOMContentLoaded', function() {
+    gLanguage = FirefoxCom.request('getLocale', null);
+
+    translateFragment();
+
+    // fire a 'localized' DOM event
+    var evtObject = document.createEvent('Event');
+    evtObject.initEvent('localized', false, false);
+    evtObject.language = gLanguage;
+    window.dispatchEvent(evtObject);
+  });
+
+  // Public API
+  document.mozL10n = {
+    // get a localized string
+    get: translateString,
+
+    // get|set the document language and direction
+    get language() {
+      return {
+        // get|set the document language (ISO-639-1)
+        get code() { return gLanguage; },
+
+        // get the direction (ltr|rtl) of the current language
+        get direction() {
+          // http://www.w3.org/International/questions/qa-scripts
+          // Arabic, Hebrew, Farsi, Pashto, Urdu
+          var rtlList = ['ar', 'he', 'fa', 'ps', 'ur'];
+          return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr';
+        }
+      };
+    }
+  };
+})(this);
\ No newline at end of file

From 7d648cfa130203392b34d0c93aefddde433ae855 Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Fri, 11 May 2012 15:18:46 -0500
Subject: [PATCH 6/8] Lint errors

---
 extensions/firefox/components/PdfStreamConverter.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js
index 898058004..6ed755554 100644
--- a/extensions/firefox/components/PdfStreamConverter.js
+++ b/extensions/firefox/components/PdfStreamConverter.js
@@ -60,9 +60,9 @@ function getDOMWindow(aChannel) {
 }
 
 function getLocalizedStrings(path) {
-  var stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].
+  var stringBundle = Cc['@mozilla.org/intl/stringbundle;1'].
       getService(Ci.nsIStringBundleService).
-      createBundle("chrome://pdfviewer/locale/" + path);
+      createBundle('chrome://pdfviewer/locale/' + path);
 
   var map = {};
   var enumerator = stringBundle.getSimpleEnumeration();
@@ -142,7 +142,7 @@ ChromeActions.prototype = {
         this.localizedStrings = getLocalizedStrings('viewer.properties');
 
       var result = this.localizedStrings[data];
-      return JSON.stringify(result || null)
+      return JSON.stringify(result || null);
     } catch (e) {
       log('Unable to retrive localized strings: ' + e);
       return 'null';

From d9f995f9caa149aa3e8334092e02e3a04aad61ce Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Fri, 11 May 2012 17:35:43 -0500
Subject: [PATCH 7/8] Change pdfviewer->pdf.js for chrome://;new line

---
 extensions/firefox/components/PdfStreamConverter.js | 2 +-
 extensions/firefox/tools/l10n.js                    | 3 ++-
 make.js                                             | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js
index 6ed755554..4a6cd9eca 100644
--- a/extensions/firefox/components/PdfStreamConverter.js
+++ b/extensions/firefox/components/PdfStreamConverter.js
@@ -62,7 +62,7 @@ function getDOMWindow(aChannel) {
 function getLocalizedStrings(path) {
   var stringBundle = Cc['@mozilla.org/intl/stringbundle;1'].
       getService(Ci.nsIStringBundleService).
-      createBundle('chrome://pdfviewer/locale/' + path);
+      createBundle('chrome://pdf.js/locale/' + path);
 
   var map = {};
   var enumerator = stringBundle.getSimpleEnumeration();
diff --git a/extensions/firefox/tools/l10n.js b/extensions/firefox/tools/l10n.js
index ad3b4ab95..b16636e31 100644
--- a/extensions/firefox/tools/l10n.js
+++ b/extensions/firefox/tools/l10n.js
@@ -110,4 +110,5 @@
       };
     }
   };
-})(this);
\ No newline at end of file
+})(this);
+
diff --git a/make.js b/make.js
index 34dc27c9b..90ccfb347 100755
--- a/make.js
+++ b/make.js
@@ -98,7 +98,7 @@ target.locale = function() {
     }
 
     mkdir('-p', EXTENSION_LOCALE_OUTPUT + '/' + locale);
-    chromeManifestContent += 'locale  pdfviewer  ' + locale + '  locale/' + locale + '/\n';
+    chromeManifestContent += 'locale  pdf.js  ' + locale + '  locale/' + locale + '/\n';
 
     if (test('-f', path + '/viewer.properties')) {
       var properties = cat(path + '/viewer.properties');

From 085723a7b2daf548a148d64d26a024c6b7834ef0 Mon Sep 17 00:00:00 2001
From: Yury Delendik <ydelendik@mozilla.com>
Date: Fri, 11 May 2012 18:12:48 -0500
Subject: [PATCH 8/8] make the locale stuff a precondition for make extension

---
 make.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/make.js b/make.js
index 90ccfb347..88f7c69a0 100755
--- a/make.js
+++ b/make.js
@@ -250,6 +250,7 @@ target.extension = function() {
   echo();
   echo('### Building extensions');
 
+  target.locale();
   target.production();
   target.firefox();
   target.chrome();
@@ -302,6 +303,7 @@ target.firefox = function() {
       FIREFOX_EXTENSION_NAME = 'pdf.js.xpi',
       FIREFOX_AMO_EXTENSION_NAME = 'pdf.js.amo.xpi';
 
+  target.locale();
   target.production();
   target.buildnumber();
   cd(ROOT_DIR);