From da94701c7ba78e0d65e2849b1630859e3dd1a017 Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Mon, 4 Jun 2012 09:38:22 -0700
Subject: [PATCH 1/3] Addresses review feedback from mozilla central. See
 bugzilla bug 752676.

---
 .../firefox/components/PdfStreamConverter.js  |  34 +++--
 extensions/firefox/content/PdfJs.jsm          | 131 ++++++++++--------
 test/mozcentral/browser_pdfjs_main.js         |   9 +-
 test/mozcentral/browser_pdfjs_savedialog.js   |  17 +--
 4 files changed, 97 insertions(+), 94 deletions(-)

diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js
index dd8f2c190..9e5dcbe61 100644
--- a/extensions/firefox/components/PdfStreamConverter.js
+++ b/extensions/firefox/components/PdfStreamConverter.js
@@ -9,6 +9,7 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
+// True only if this is the version of pdf.js that is included with firefox.
 const MOZ_CENTRAL = PDFJSSCRIPT_MOZ_CENTRAL;
 const PDFJS_EVENT_ID = 'pdf.js.message';
 const PDF_CONTENT_TYPE = 'application/pdf';
@@ -21,11 +22,14 @@ Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/NetUtil.jsm');
 
+
 let appInfo = Cc['@mozilla.org/xre/app-info;1']
                   .getService(Ci.nsIXULAppInfo);
 let privateBrowsing, inPrivateBrowsing;
-let mimeService = Cc['@mozilla.org/mime;1']
-                    .getService(Ci.nsIMIMEService);
+let Svc = {};
+XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
+                                   '@mozilla.org/mime;1',
+                                   'nsIMIMEService');
 
 if (appInfo.ID === FIREFOX_ID) {
   privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
@@ -75,15 +79,15 @@ function getDOMWindow(aChannel) {
 
 function isEnabled() {
   if (MOZ_CENTRAL) {
-    var enabled = getBoolPref(PREF_PREFIX + '.enabled', false);
-    if (!enabled)
+    var disabled = getBoolPref(PREF_PREFIX + '.disabled', false);
+    if (disabled)
       return false;
     // To also be considered enabled the "Preview in Firefox" option must be
     // selected in the Application preferences.
-    var handlerInfo = mimeService.
-                        getFromTypeAndExtension('application/pdf', 'pdf');
-    return handlerInfo && (handlerInfo.alwaysAskBeforeHandling == false &&
-           handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally);
+    var handlerInfo = Svc.mime
+                         .getFromTypeAndExtension('application/pdf', 'pdf');
+    return handlerInfo.alwaysAskBeforeHandling == false &&
+           handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally;
   }
   // Always returns true for the extension since enabling/disabling is handled
   // by the add-on manager.
@@ -111,9 +115,10 @@ function getLocalizedStrings(path) {
   }
   return map;
 }
-function getLocalizedString(strings, id) {
+function getLocalizedString(strings, id, property) {
+  property = property || 'textContent';
   if (id in strings)
-    return strings[id]['textContent'];
+    return strings[id][property];
   return id;
 }
 
@@ -124,9 +129,8 @@ function ChromeActions(domWindow) {
 
 ChromeActions.prototype = {
   download: function(data) {
-    let mimeService = Cc['@mozilla.org/mime;1'].getService(Ci.nsIMIMEService);
-    var handlerInfo = mimeService.
-                        getFromTypeAndExtension('application/pdf', 'pdf');
+    var handlerInfo = Svc.mime
+                         .getFromTypeAndExtension('application/pdf', 'pdf');
     var uri = NetUtil.newURI(data);
 
     var extHelperAppSvc =
@@ -200,10 +204,10 @@ ChromeActions.prototype = {
     var win = Services.wm.getMostRecentWindow('navigator:browser');
     var browser = win.gBrowser.getBrowserForDocument(domWindow.top.document);
     var notificationBox = win.gBrowser.getNotificationBox(browser);
-
     var buttons = [{
       label: getLocalizedString(strings, 'open_with_different_viewer'),
-      accessKey: null,
+      accessKey: getLocalizedString(strings, 'open_with_different_viewer',
+                                    'accessKey'),
       callback: function() {
         self.download(url);
       }
diff --git a/extensions/firefox/content/PdfJs.jsm b/extensions/firefox/content/PdfJs.jsm
index cd208c7fe..9d112f5b2 100644
--- a/extensions/firefox/content/PdfJs.jsm
+++ b/extensions/firefox/content/PdfJs.jsm
@@ -7,72 +7,82 @@ const Cm = Components.manager;
 const Cu = Components.utils;
 
 const PREF_PREFIX = 'pdfjs';
-const PREF_ENABLED = PREF_PREFIX + '.enabled';
+const PREF_DISABLED = PREF_PREFIX + '.disabled';
 const PREF_FIRST_RUN = PREF_PREFIX + '.firstRun';
-const PREF_PREVIOUS_ACTION = PREF_PREFIX + '.previousAction';
-const PREF_PREVIOUS_ASK = PREF_PREFIX + '.previousAsk';
-const PDFJS_HANDLER_CHANGED = 'pdfjs:handlerChanged';
+const PREF_PREVIOUS_ACTION = PREF_PREFIX + '.previousHandler.preferredAction';
+const PREF_PREVIOUS_ASK = PREF_PREFIX + '.previousHandler.alwaysAskBeforeHandling';
+const TOPIC_PDFJS_HANDLER_CHANGED = 'pdfjs:handlerChanged';
 
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://pdf.js.components/PdfStreamConverter.js');
 
-let mimeService = Cc["@mozilla.org/mime;1"]
-                    .getService(Ci.nsIMIMEService);
+let Svc = {};
+XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
+                                   '@mozilla.org/mime;1',
+                                   'nsIMIMEService');
 
-function getBoolPref(pref, def) {
+function getBoolPref(aPref, aDefaultValue) {
   try {
-    return Services.prefs.getBoolPref(pref);
+    return Services.prefs.getBoolPref(aPref);
   } catch (ex) {
-    return def;
+    return aDefaultValue;
   }
 }
 
-// Register/unregister a class as a component.
+// Register/unregister a constructor as a component.
 let Factory = {
-  registrar: null,
-  aClass: null,
-  register: function(aClass) {
-    if (this.aClass) {
-      return;
-    }
-    this.registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
-    this.aClass = aClass;
-    var proto = aClass.prototype;
-    this.registrar.registerFactory(proto.classID, proto.classDescription,
-      proto.contractID, this);
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]),
+  _targetConstructor: null,
+
+  register: function register(targetConstructor) {
+    this._targetConstructor = targetConstructor;
+    var proto = targetConstructor.prototype;
+    var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
+    registrar.registerFactory(proto.classID, proto.classDescription,
+                              proto.contractID, this);
   },
-  unregister: function() {
-    if (!this.aClass) {
-      return;
-    }
-    var proto = this.aClass.prototype;
-    this.registrar.unregisterFactory(proto.classID, this);
-    this.aClass = null;
+
+  unregister: function unregister() {
+    var proto = this._targetConstructor.prototype;
+    var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
+    registrar.unregisterFactory(proto.classID, this);
+    this._targetConstructor = null;
   },
-  // nsIFactory::createInstance
-  createInstance: function(outer, iid) {
-    if (outer !== null)
+
+  // nsIFactory
+  createInstance: function createInstance(aOuter, iid) {
+    if (aOuter !== null)
       throw Cr.NS_ERROR_NO_AGGREGATION;
-    return (new (this.aClass)).QueryInterface(iid);
+    return (new (this._targetConstructor)).QueryInterface(iid);
+  },
+
+  // nsIFactory
+  lockFactory: function lockFactory(lock) { 
+    // No longer used as of gecko 1.7.
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   }
 };
 
 let PdfJs = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
   _registered: false,
-  init: function() {
+
+  init: function init() {
     // On first run make pdf.js the default handler.
-    if (getBoolPref(PREF_ENABLED, false) && getBoolPref(PREF_FIRST_RUN, false)) {
+    if (!getBoolPref(PREF_DISABLED, true) && getBoolPref(PREF_FIRST_RUN, false)) {
       Services.prefs.setBoolPref(PREF_FIRST_RUN, false);
 
-      let handlerService = Cc['@mozilla.org/uriloader/handler-service;1'].
-                            getService(Ci.nsIHandlerService);
-      let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf');
-
+      let handlerInfo = Svc.mime.getFromTypeAndExtension('application/pdf', 'pdf');
       // Store the previous settings of preferredAction and
-      // alwaysAskBeforeHandling in case we need to fall back to it.
+      // alwaysAskBeforeHandling in case we need to revert them in a hotfix that
+      // would turn pdf.js off.
       Services.prefs.setIntPref(PREF_PREVIOUS_ACTION, handlerInfo.preferredAction);
       Services.prefs.setBoolPref(PREF_PREVIOUS_ASK, handlerInfo.alwaysAskBeforeHandling);
 
+      let handlerService = Cc['@mozilla.org/uriloader/handler-service;1'].
+                           getService(Ci.nsIHandlerService);
+
       // Change and save mime handler settings.
       handlerInfo.alwaysAskBeforeHandling = false;
       handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
@@ -80,42 +90,49 @@ let PdfJs = {
     }
 
     if (this.enabled)
-      this._register();
+      this._ensureRegistered();
     else
-      this._unregister();
+      this._ensureUnregistered();
 
     // Listen for when pdf.js is completely disabled or a different pdf handler
     // is chosen.
-    Services.prefs.addObserver(PREF_ENABLED, this, false);
-    Services.obs.addObserver(this, PDFJS_HANDLER_CHANGED, false);
+    Services.prefs.addObserver(PREF_DISABLED, this, false);
+    Services.obs.addObserver(this, TOPIC_PDFJS_HANDLER_CHANGED, false);
   },
-  observe: function(subject, topic, data) {
-    if (topic != 'nsPref:changed' && topic != PDFJS_HANDLER_CHANGED)
-      return;
 
+  // nsIObserver
+  observe: function observe(aSubject, aTopic, aData) {
     if (this.enabled)
-      this._register();
+      this._ensureRegistered();
     else
-      this._unregister();
+      this._ensureUnregistered();
   },
-  // pdf.js is only enabled if we're both selected as the pdf viewer and if the 
-  // global switch enabling it is true.
+  
+  /**
+   * pdf.js is only enabled if it is both selected as the pdf viewer and if the 
+   * global switch enabling it is true.
+   * @return {boolean} Wether or not it's enabled.
+   */
   get enabled() {
-    var handlerInfo = mimeService.
-                        getFromTypeAndExtension('application/pdf', 'pdf');
+    var disabled = getBoolPref(PREF_DISABLED, true);
+    if (disabled)
+      return false;
 
-    var selectedAsHandler = handlerInfo && (handlerInfo.alwaysAskBeforeHandling == false &&
-           handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally);
-    return getBoolPref(PREF_ENABLED, false) && selectedAsHandler;
+    var handlerInfo = Svc.mime.
+                        getFromTypeAndExtension('application/pdf', 'pdf');
+    return handlerInfo.alwaysAskBeforeHandling == false &&
+           handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally;
   },
-  _register: function() {
+
+  _ensureRegistered: function _ensureRegistered() {
     if (this._registered)
       return;
 
     Factory.register(PdfStreamConverter);
     this._registered = true;
   },
-  _unregister: function() {
+
+  _ensureUnregistered: function _ensureUnregistered() {
     if (!this._registered)
       return;
 
diff --git a/test/mozcentral/browser_pdfjs_main.js b/test/mozcentral/browser_pdfjs_main.js
index f4bb26eff..e3d7c8bab 100644
--- a/test/mozcentral/browser_pdfjs_main.js
+++ b/test/mozcentral/browser_pdfjs_main.js
@@ -1,6 +1,5 @@
 /* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const RELATIVE_DIR = "browser/extensions/pdfjs/test/";
 const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR;
@@ -8,8 +7,6 @@ const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR;
 function test() {
   var tab;
 
-  const Cc = Components.classes;
-  const Ci = Components.interfaces;
   let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService);
   let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
   let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf');
@@ -35,9 +32,7 @@ function test() {
 
     // Runs tests after all 'load' event handlers have fired off
     setTimeout(function() {
-      runTests(document, window, function() {
-        finish();
-      });
+      runTests(document, window, finish);
     }, 0);
   }, true);
 }
diff --git a/test/mozcentral/browser_pdfjs_savedialog.js b/test/mozcentral/browser_pdfjs_savedialog.js
index 3a7148ed5..a6564e591 100644
--- a/test/mozcentral/browser_pdfjs_savedialog.js
+++ b/test/mozcentral/browser_pdfjs_savedialog.js
@@ -1,17 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const RELATIVE_DIR = "browser/extensions/pdfjs/test/";
 const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR;
 
 function test() {
-  const Cc = Components.classes;
-  const Ci = Components.interfaces;
-  var tab;
-
   var oldAction = changeMimeHandler(Ci.nsIHandlerInfo.useSystemDefault, true);
-
+  var tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf");
   //
   // Test: "Open with" dialog comes up when pdf.js is not selected as the default
   // handler.
@@ -23,14 +18,9 @@ function test() {
     changeMimeHandler(oldAction[0], oldAction[1]);
     gBrowser.removeTab(tab);
   });
-
-  tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf");
-  var newTabBrowser = gBrowser.getBrowserForTab(tab);
 }
 
 function changeMimeHandler(preferredAction, alwaysAskBeforeHandling) {
-  const Cc = Components.classes;
-  const Ci = Components.interfaces;
   let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService);
   let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
   let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf');
@@ -44,7 +34,6 @@ function changeMimeHandler(preferredAction, alwaysAskBeforeHandling) {
   Services.obs.notifyObservers(null, 'pdfjs:handlerChanged', null);
 
   // Refresh data
-  mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
   handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf');
 
   //
@@ -57,8 +46,6 @@ function changeMimeHandler(preferredAction, alwaysAskBeforeHandling) {
 }
 
 function addWindowListener(aURL, aCallback) {
-  const Cc = Components.classes;
-  const Ci = Components.interfaces;
   Services.wm.addListener({
     onOpenWindow: function(aXULWindow) {
       info("window opened, waiting for focus");

From 72e6dbebaa5a794651feffa2727a13bd4961ef8c Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Mon, 4 Jun 2012 09:45:09 -0700
Subject: [PATCH 2/3] Update factory in bootstrap.js.

---
 extensions/firefox/bootstrap.js | 53 +++++++++++++++++----------------
 1 file changed, 28 insertions(+), 25 deletions(-)

diff --git a/extensions/firefox/bootstrap.js b/extensions/firefox/bootstrap.js
index 5bf66d444..a5f4bd282 100644
--- a/extensions/firefox/bootstrap.js
+++ b/extensions/firefox/bootstrap.js
@@ -11,6 +11,7 @@ let Ci = Components.interfaces;
 let Cm = Components.manager;
 let Cu = Components.utils;
 
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 
 function getBoolPref(pref, def) {
@@ -34,35 +35,37 @@ function log(str) {
   dump(str + '\n');
 }
 
-// Register/unregister a class as a component.
+// Register/unregister a constructor as a component.
 let Factory = {
-  registrar: null,
-  aClass: null,
-  register: function(aClass) {
-    if (this.aClass) {
-      log('Cannot register more than one class');
-      return;
-    }
-    this.registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
-    this.aClass = aClass;
-    var proto = aClass.prototype;
-    this.registrar.registerFactory(proto.classID, proto.classDescription,
-      proto.contractID, this);
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]),
+  _targetConstructor: null,
+
+  register: function register(targetConstructor) {
+    this._targetConstructor = targetConstructor;
+    var proto = targetConstructor.prototype;
+    var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
+    registrar.registerFactory(proto.classID, proto.classDescription,
+                              proto.contractID, this);
   },
-  unregister: function() {
-    if (!this.aClass) {
-      log('Class was never registered.');
-      return;
-    }
-    var proto = this.aClass.prototype;
-    this.registrar.unregisterFactory(proto.classID, this);
-    this.aClass = null;
+
+  unregister: function unregister() {
+    var proto = this._targetConstructor.prototype;
+    var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
+    registrar.unregisterFactory(proto.classID, this);
+    this._targetConstructor = null;
   },
-  // nsIFactory::createInstance
-  createInstance: function(outer, iid) {
-    if (outer !== null)
+
+  // nsIFactory
+  createInstance: function createInstance(aOuter, iid) {
+    if (aOuter !== null)
       throw Cr.NS_ERROR_NO_AGGREGATION;
-    return (new (this.aClass)).QueryInterface(iid);
+    return (new (this._targetConstructor)).QueryInterface(iid);
+  },
+
+  // nsIFactory
+  lockFactory: function lockFactory(lock) { 
+    // No longer used as of gecko 1.7.
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   }
 };
 

From 4f314fff397a7036f176bda77bb3739ae97cdb99 Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Mon, 4 Jun 2012 15:39:50 -0700
Subject: [PATCH 3/3] Remove space at end of line.

---
 extensions/firefox/bootstrap.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/extensions/firefox/bootstrap.js b/extensions/firefox/bootstrap.js
index a5f4bd282..85ade961b 100644
--- a/extensions/firefox/bootstrap.js
+++ b/extensions/firefox/bootstrap.js
@@ -63,7 +63,7 @@ let Factory = {
   },
 
   // nsIFactory
-  lockFactory: function lockFactory(lock) { 
+  lockFactory: function lockFactory(lock) {
     // No longer used as of gecko 1.7.
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   }