Do not override nsExtensionManager, just "redirect" topics.
authorNils Maier <MaierMan@web.de>
Thu, 09 Sep 2010 20:39:39 +0200
changeset 1345 680a2ceef4928416bc8f55c77dc3d7d90d4c18ad
parent 1344 2cbd2bcf31d9cbfa24d64e9bf4a4ae70229af9ce
child 1346 d5922183c9bfd0a64269b0d5ed1dc818195b96f6
child 1349 492a6ae3d4a6e2f6eb766b1b3e1f7d00e7d4082c
push id864
push userjose@glaxstar.com
push dateThu, 09 Sep 2010 21:51:37 +0000
Do not override nsExtensionManager, just "redirect" topics.
client/components/nsPersonasExtensionManager.js
--- a/client/components/nsPersonasExtensionManager.js
+++ b/client/components/nsPersonasExtensionManager.js
@@ -15,16 +15,17 @@
  *
  * The Initial Developer of the Original Code is Mozilla.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jose E. Bolanos <jose@appcoast.com>
  *   Myk Melez <myk@mozilla.org>
+ *   Nils Maier <maierman@web.de>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -53,23 +54,38 @@ const PREF_DSS_SKIN_TO_SELECT         = 
 const PREF_LWTHEME_TO_SELECT          = "extensions.lwThemeToSelect";
 const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
 const PREF_FORCE_SKINNING             = "lightweightThemes.forceSkinning";
 const URI_EXTENSION_MANAGER           = "chrome://mozapps/content/extensions/extensions.xul";
 const FEATURES_EXTENSION_MANAGER      = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable";
 const PREFIX_NS_EM                    = "http://www.mozilla.org/2004/em-rdf#";
 const PREFIX_ITEM_URI                 = "urn:mozilla:item:";
 
-// ID of the original Extension Manager, "@mozilla.org/extensions/manager;1"
-var EXTENSIONS_MANAGER_ID = "{8A115FAA-7DCB-4e8f-979B-5F53472F51CF}";
-var gOldHandler = null;
-var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
-var gOS = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+const DEFAULT_THEME = "classic/1.0";
 
-const DEFAULT_THEME = "classic/1.0";
+// If defineLazyServiceGetter is not present we won't load anyway, as this is
+// 3.6+ only
+if ('defineLazyServiceGetter' in XPCOMUtils) {
+  XPCOMUtils.defineLazyServiceGetter(
+    this, "gRDF",
+    "@mozilla.org/rdf/rdf-service;1", "nsIRDFService"
+    );
+  XPCOMUtils.defineLazyServiceGetter(
+    this, "gExtMan",
+    "@mozilla.org/extensions/manager;1", "nsIExtensionManager"
+    );
+  XPCOMUtils.defineLazyServiceGetter(
+    this, "gIoServ",
+    "@mozilla.org/network/io-service;1", "nsIIOService"
+    );
+  XPCOMUtils.defineLazyServiceGetter(
+    this, "gAppStartup",
+    "@mozilla.org/toolkit/app-startup;1", "nsIAppStartup"
+    );
+}
 
 //
 // Utility Functions
 //
 
 function stringData(literalOrResource) {
   if (literalOrResource instanceof Ci.nsIRDFLiteral)
     return literalOrResource.Value;
@@ -80,57 +96,30 @@ function stringData(literalOrResource) {
 
 function intData(literal) {
   if (literal instanceof Ci.nsIRDFInt)
     return literal.Value;
   return undefined;
 }
 
 function getURLSpecFromFile(file) {
-  var ioServ = Cc["@mozilla.org/network/io-service;1"].
-               getService(Ci.nsIIOService);
-  var fph = ioServ.getProtocolHandler("file")
-                  .QueryInterface(Ci.nsIFileProtocolHandler);
-  return fph.getURLSpecFromFile(file);
-}
-
-// Given two instances, copy in all properties from "base"
-// and create forwarding methods for all functions and getters.
-// NOTE: Settable properties are not copied.
-function inheritCurrentInterface(self, base) {
-  function defineGetter(prop) {
-    self.__defineGetter__(prop, function() base[prop]);
-  }
-
-  for(let prop in base) {
-    if(typeof self[prop] === 'undefined')
-      if(typeof base[prop] === 'function') {
-        (function(prop) {
-          self[prop] = function() {
-            return base[prop].apply(base,arguments);
-          };
-        })(prop);
-      }
-      else
-        defineGetter(prop);
-  }
+  return gIoServ.newFileURI(file).spec;
 }
 
 function restartApp() {
   // Notify all windows that an application quit has been requested.
   var cancelQuit =
     Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
   Observers.notify("quit-application-requested", cancelQuit, "restart");
 
   // Something aborted the quit process.
   if (cancelQuit.data)
     return;
 
-  Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup).
-    quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
+  gAppStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
 }
 
 /**
  * Overriden Extension Manager object. Handles the lightweight theme observer
  * notifications to allow personas to be applied over compatible themes.
  */
 function PersonasExtensionManager() {
   Cu.import("resource://personas/modules/Observers.js");
@@ -139,41 +128,25 @@ function PersonasExtensionManager() {
   // Add observers for the lightweight theme topics to override their behavior,
   // and for the xpcom-shutdown topic to remove them afterwards.
   Observers.add("xpcom-shutdown", this);
   Observers.add("lightweight-theme-preview-requested", this);
   Observers.add("lightweight-theme-change-requested", this);
 }
 
 PersonasExtensionManager.prototype = {
-  classDescription: "Personas Plus Extension Manager",
-  contractID: "@mozilla.org/extensions/manager;1",
-  classID: Components.ID("{DF74077B-EB16-47B0-8C37-12D577A7F1AE}"),
-
-  QueryInterface: function(aIID) {
-    // Retrieve the old nsExtensionsManager anew and then QI to copy the
-    // properties of the requested interface only.
-    delete gOldHandler;
-    gOldHandler =
-      Components.classesByID[EXTENSIONS_MANAGER_ID].
-        getService(Ci.nsIExtensionManager);
-    gOldHandler.QueryInterface(aIID);
+  classDescription: "Personas Plus Extension Manager Integrator",
+  contractID: "@mozilla.org/extensions/personas-manager;1",
+  classID: Components.ID("{21372722-3631-41c3-8946-950382a3c523}"),
 
-    // Remove the original nsExtensionManager listeners for the
-    // lightweight theme topics. This might fail when the given aIID has not
-    // these topics registered, but can be safely ignored.
-    try {
-      gOS.removeObserver(gOldHandler, "lightweight-theme-preview-requested");
-      gOS.removeObserver(gOldHandler, "lightweight-theme-change-requested");
-    }
-    catch (e) {}
-
-    inheritCurrentInterface(this, gOldHandler);
-    return this;
-  },
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  _xpcom_categories: [
+    {category: "profile-after-change"},
+    {category: "app-startup", service: true}
+  ],
 
   /* Whether the current theme is compatible with Personas */
   _currentThemeSkinnable : true,
 
   /* Personas string bundle */
   _strings : null,
   get strings() {
     if (!this.StringBundle)
@@ -187,16 +160,32 @@ PersonasExtensionManager.prototype = {
    * Overriden method from the original nsExtensionManager.
    * Handles different observer notifications, and delegates the rest to the
    * original nsExtensionManager object.
    */
   observe: function(subject, topic, data) {
     let forceSkinning;
 
     switch (topic) {
+      case "app-startup":
+        // Unregister the nsExtensionManager profile-after-change.
+        // PersonasExtensionManager will register it's own and call
+        // nsExtensionManager from there after running some own code.
+        // We need to use app-startup here to be called before the
+        // profile-after-change notification.
+        try {
+          let catMan = Cc["@mozilla.org/categorymanager;1"].
+                       getService(Ci.nsICategoryManager);
+          catMan.deleteCategoryEntry("profile-after-change", "Extension Manager", false);
+        }
+        catch (ex) {
+          // Do nothing here.
+        }
+      break;
+
       case "lightweight-theme-preview-requested":
         forceSkinning = Preferences.get(PREF_FORCE_SKINNING, false);
 
         // Cancel if a custom theme with no support for personas is set.
         if (!this._currentThemeSkinnable && !forceSkinning) {
           let cancel = subject.QueryInterface(Ci.nsISupportsPRBool);
           cancel.data = true;
         }
@@ -241,16 +230,30 @@ PersonasExtensionManager.prototype = {
       case "xpcom-shutdown":
         // Remove the observers
         Observers.remove("xpcom-shutdown", this);
         Observers.remove("lightweight-theme-preview-requested", this);
         Observers.remove("lightweight-theme-change-requested", this);
         break;
 
       case "profile-after-change":
+        // Remove the original nsExtensionManager listeners for the
+        // lightweight theme topics. This might fail when the given aIID has not
+        // these topics registered, but can be safely ignored.
+        try {
+          // Cannot use Observers here
+          let os = Cc["@mozilla.org/observer-service;1"].
+                   getService(Ci.nsIObserverService);
+          os.removeObserver(gExtMan, "lightweight-theme-preview-requested");
+          os.removeObserver(gExtMan, "lightweight-theme-change-requested");
+        }
+        catch (ex) {
+          // Do nothing here.
+        }
+
         try {
           if (Preferences.get(PREF_DSS_SWITCHPENDING)) {
             var toSelect = Preferences.get(PREF_DSS_SKIN_TO_SELECT);
             Preferences.set(PREF_GENERAL_SKINS_SELECTEDSKIN, toSelect);
             Preferences.reset(PREF_DSS_SWITCHPENDING);
             Preferences.reset(PREF_DSS_SKIN_TO_SELECT);
           }
 
@@ -265,29 +268,26 @@ PersonasExtensionManager.prototype = {
               } catch (e) {}
             }
             else {
               LightweightThemeManager.currentTheme = null;
             }
             Preferences.reset(PREF_LWTHEME_TO_SELECT);
           }
         }
-        catch (e) {}
+        catch (e) {
+          // Do nothing here.
+        }
 
         // Let the original nsExtensionManager perform actions
         // during "profile-after-change".
-        gOldHandler.observe(subject, topic, data);
+        gExtMan.observe(subject, topic, data);
         // Load current theme properties, e.g. "skinnable" property.
         this._loadThemeProperties();
         break;
-
-      default:
-        // Let the original nsExtensionManager handle the event.
-        gOldHandler.observe(subject, topic, data);
-        break;
     }
   },
 
   /**
    * Shows a notification in the browser informing to the user to restart it so
    * the persona can be applied.
    */
   _showRestartNotification : function() {
@@ -330,17 +330,17 @@ PersonasExtensionManager.prototype = {
     // when the theme has the "skinnable" property set to true in its install.rdf.
     let currentTheme = Preferences.get(PREF_GENERAL_SKINS_SELECTEDSKIN);
 
     if (currentTheme == DEFAULT_THEME)
       this._currentThemeSkinnable = true;
     else {
       // Find the current theme and load its install.rdf to read its
       // "skinnable" property.
-      let themes = this.getItemList(Ci.nsIUpdateItem.TYPE_THEME, { });
+      let themes = gExtMan.getItemList(Ci.nsIUpdateItem.TYPE_THEME, { });
       for (let i = 0; i < themes.length; i++) {
         let theme = themes[i];
 
         let internalName = this._getItemProperty(theme.id, "internalName");
         if (internalName == currentTheme) {
           this._loadThemeSkinnableProperty(theme.id);
           break;
         }
@@ -366,17 +366,17 @@ PersonasExtensionManager.prototype = {
             let personas = {};
             Cu.import("resource://personas/modules/service.js", personas);
             personas.PersonaService.changeToDefaultPersona();
           } catch (e) {}
         }
       }
     };
 
-    let location = this.getInstallLocation(aThemeId);
+    let location = gExtMan.getInstallLocation(aThemeId);
     let file = location.getItemFile(aThemeId, "install.rdf");
     this._loadDatasource(file, onInstallRDFLoaded);
   },
 
   /**
    * Loads an RDF datasource file.
    * @param aDatasourceFile The file path of the datasource.
    * @param aLoadCallback Callback used to notify the caller when the datasource
@@ -408,17 +408,17 @@ PersonasExtensionManager.prototype = {
   /**
    * Gets a property of an item (theme) from extensions.rdf
    * @param aItemId The id of the item.
    * @param aPropertyName The name of the property to get.
    * @return The value of the property, or undefined if not found.
    */
   _getItemProperty : function(aItemId, aPropertyName) {
     return this._getDatasourceProperty(
-      this.datasource,
+      gExtMan.datasource,
       PREFIX_ITEM_URI + aItemId,
       PREFIX_NS_EM + aPropertyName);
   },
 
   /**
    * Gets a property of the given extension install.rdf datasource.
    * @param aDatasource The install.rdf datasource.
    * @param aPropertyName The name of the property to get.