]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/bootstrap.js
Import 1.0 supporting Firefox up to 14.*
[dactyl.git] / common / bootstrap.js
index ccf100761e2f56b331f9dfa664ec72b6098aa6ac..d81411ea2877bbb8558e89731af3a158efa59f30 100755 (executable)
@@ -9,10 +9,7 @@
 const NAME = "bootstrap";
 const global = this;
 
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
+var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 
 function module(uri) {
     let obj = {};
@@ -29,24 +26,20 @@ const resourceProto = Services.io.getProtocolHandler("resource")
 const categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
 const manager = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
 
-const BOOTSTRAP_JSM = "resource://dactyl/bootstrap.jsm";
-
+const DISABLE_ACR        = "resource://dactyl-content/disable-acr.jsm";
+const BOOTSTRAP_JSM      = "resource://dactyl/bootstrap.jsm";
 const BOOTSTRAP_CONTRACT = "@dactyl.googlecode.com/base/bootstrap";
-JSMLoader = JSMLoader || BOOTSTRAP_CONTRACT in Cc && Cc[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader;
-
-var JSMLoader = BOOTSTRAP_CONTRACT in Components.classes &&
-    Components.classes[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader;
 
-// Temporary migration code.
-if (!JSMLoader && "@mozilla.org/fuel/application;1" in Components.classes)
-    JSMLoader = Components.classes["@mozilla.org/fuel/application;1"]
-                          .getService(Components.interfaces.extIApplication)
-                          .storage.get("dactyl.JSMLoader", null);
+var JSMLoader = BOOTSTRAP_CONTRACT in Cc && Cc[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader;
+var name = "dactyl";
 
 function reportError(e) {
-    dump("\ndactyl: bootstrap: " + e + "\n" + (e.stack || Error().stack) + "\n");
+    dump("\n" + name + ": bootstrap: " + e + "\n" + (e.stack || Error().stack) + "\n");
     Cu.reportError(e);
 }
+function debug(msg) {
+    dump(name + ": " + msg + "\n");
+}
 
 function httpGet(url) {
     let xmlhttp = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
@@ -65,6 +58,16 @@ let components = {};
 let resources = [];
 let getURI = null;
 
+function updateLoader() {
+    try {
+        JSMLoader.loader = Cc["@dactyl.googlecode.com/extra/utils"].getService(Ci.dactylIUtils);
+    }
+    catch (e) {};
+}
+
+/**
+ * Performs necessary migrations after a version change.
+ */
 function updateVersion() {
     try {
         function isDev(ver) /^hg|pre$/.test(ver);
@@ -77,6 +80,11 @@ function updateVersion() {
 
         localPrefs.set("lastVersion", addon.version);
 
+        // We're switching from a nightly version to a stable or
+        // semi-stable version or vice versa.
+        //
+        // Disable automatic updates when switching to nightlies,
+        // restore the default action when switching to stable.
         if (!config.lastVersion || isDev(config.lastVersion) != isDev(addon.version))
             addon.applyBackgroundUpdates = AddonManager[isDev(addon.version) ? "AUTOUPDATE_DISABLE" : "AUTOUPDATE_DEFAULT"];
     }
@@ -86,19 +94,24 @@ function updateVersion() {
 }
 
 function startup(data, reason) {
-    dump("dactyl: bootstrap: startup " + reasonToString(reason) + "\n");
+    debug("bootstrap: startup " + reasonToString(reason));
     basePath = data.installPath;
 
     if (!initialized) {
         initialized = true;
 
-        dump("dactyl: bootstrap: init" + " " + data.id + "\n");
+        debug("bootstrap: init" + " " + data.id);
 
         addonData = data;
         addon = data;
+        name = data.id.replace(/@.*/, "");
         AddonManager.getAddonByID(addon.id, function (a) {
             addon = a;
+
+            updateLoader();
             updateVersion();
+            if (typeof require !== "undefined")
+                require(global, "main");
         });
 
         if (basePath.isDirectory())
@@ -109,7 +122,8 @@ function startup(data, reason) {
             };
         else
             getURI = function getURI(path)
-                Services.io.newURI("jar:" + Services.io.newFileURI(basePath).spec + "!/" + path, null, null);
+                Services.io.newURI("jar:" + Services.io.newFileURI(basePath).spec.replace(/!/g, "%21") + "!" +
+                                   "/" + path, null, null);
 
         try {
             init();
@@ -120,6 +134,14 @@ function startup(data, reason) {
     }
 }
 
+/**
+ * An XPCOM class factory proxy. Loads the JavaScript module at *url*
+ * when an instance is to be created and calls its NSGetFactory method
+ * to obtain the actual factory.
+ *
+ * @param {string} url The URL of the module housing the real factory.
+ * @param {string} classID The CID of the class this factory represents.
+ */
 function FactoryProxy(url, classID) {
     this.url = url;
     this.classID = Components.ID(classID);
@@ -127,12 +149,12 @@ function FactoryProxy(url, classID) {
 FactoryProxy.prototype = {
     QueryInterface: XPCOMUtils.generateQI(Ci.nsIFactory),
     register: function () {
-        dump("dactyl: bootstrap: register: " + this.classID + " " + this.contractID + "\n");
+        debug("bootstrap: register: " + this.classID + " " + this.contractID);
 
         JSMLoader.registerFactory(this);
     },
     get module() {
-        dump("dactyl: bootstrap: create module: " + this.contractID + "\n");
+        debug("bootstrap: create module: " + this.contractID);
 
         Object.defineProperty(this, "module", { value: {}, enumerable: true });
         JSMLoader.load(this.url, this.module);
@@ -145,7 +167,7 @@ FactoryProxy.prototype = {
 }
 
 function init() {
-    dump("dactyl: bootstrap: init\n");
+    debug("bootstrap: init");
 
     let manifestURI = getURI("chrome.manifest");
     let manifest = httpGet(manifestURI.spec)
@@ -173,8 +195,6 @@ function init() {
             break;
 
         case "resource":
-            var hardSuffix = /^[^\/]*/.exec(fields[2])[0];
-
             resources.push(fields[1], fields[1] + suffix);
             resourceProto.setSubstitution(fields[1], getURI(fields[2]));
             resourceProto.setSubstitution(fields[1] + suffix, getURI(fields[2]));
@@ -183,52 +203,62 @@ function init() {
 
     // Flush the cache if necessary, just to be paranoid
     let pref = "extensions.dactyl.cacheFlushCheck";
-    let val  = addon.version + "-" + hardSuffix;
+    let val  = addon.version;
     if (!Services.prefs.prefHasUserValue(pref) || Services.prefs.getCharPref(pref) != val) {
+        var cacheFlush = true;
         Services.obs.notifyObservers(null, "startupcache-invalidate", "");
         Services.prefs.setCharPref(pref, val);
     }
 
     try {
-        module("resource://dactyl-content/disable-acr.jsm").init(addon.id);
+        module(DISABLE_ACR).init(addon.id);
     }
     catch (e) {
         reportError(e);
     }
 
     if (JSMLoader) {
+        // Temporary hacks until platforms and dactyl releases that don't
+        // support Cu.unload are phased out.
         if (Cu.unload) {
+            // Upgrading from dactyl release without Cu.unload support.
             Cu.unload(BOOTSTRAP_JSM);
             for (let [name] in Iterator(JSMLoader.globals))
                 Cu.unload(~name.indexOf(":") ? name : "resource://dactyl" + JSMLoader.suffix + "/" + name);
         }
-        else if (JSMLoader.bump != 5) // Temporary hack
+        else if (JSMLoader.bump != 6) {
+            // We're in a version without Cu.unload support and the
+            // JSMLoader interface has changed. Bump off the old one.
             Services.scriptloader.loadSubScript("resource://dactyl" + suffix + "/bootstrap.jsm",
                 Cu.import(BOOTSTRAP_JSM, global));
+        }
     }
 
-    if (!JSMLoader || JSMLoader.bump !== 5 || Cu.unload)
+    if (!JSMLoader || JSMLoader.bump !== 6 || Cu.unload)
         Cu.import(BOOTSTRAP_JSM, global);
 
+    JSMLoader.name = name;
     JSMLoader.bootstrap = this;
 
     JSMLoader.load(BOOTSTRAP_JSM, global);
 
     JSMLoader.init(suffix);
+    JSMLoader.cacheFlush = cacheFlush;
     JSMLoader.load("base.jsm", global);
 
-    if (!(BOOTSTRAP_CONTRACT in Cc))
-        manager.registerFactory(Components.ID("{f541c8b0-fe26-4621-a30b-e77d21721fb5}"),
-                                String("{f541c8b0-fe26-4621-a30b-e77d21721fb5}"),
-                                BOOTSTRAP_CONTRACT, {
-            QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]),
-            instance: {
-                QueryInterface: XPCOMUtils.generateQI([]),
-                contractID: BOOTSTRAP_CONTRACT,
-                wrappedJSObject: {}
-            },
-            createInstance: function () this.instance
-        });
+    if (!(BOOTSTRAP_CONTRACT in Cc)) {
+        // Use Sandbox to prevent closures over this scope
+        let sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].getService());
+        let factory = Cu.evalInSandbox("({ createInstance: function () this })", sandbox);
+
+        factory.classID         = Components.ID("{f541c8b0-fe26-4621-a30b-e77d21721fb5}");
+        factory.contractID      = BOOTSTRAP_CONTRACT;
+        factory.QueryInterface  = XPCOMUtils.generateQI([Ci.nsIFactory]);
+        factory.wrappedJSObject = factory;
+
+        manager.registerFactory(factory.classID, String(factory.classID),
+                                BOOTSTRAP_CONTRACT, factory);
+    }
 
     Cc[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader = !Cu.unload && JSMLoader;
 
@@ -237,14 +267,19 @@ function init() {
 
     Services.obs.notifyObservers(null, "dactyl-rehash", null);
     updateVersion();
-    require(global, "overlay");
+
+    updateLoader();
+    if (addon !== addonData)
+        require(global, "main");
 }
 
 function shutdown(data, reason) {
-    dump("dactyl: bootstrap: shutdown " + reasonToString(reason) + "\n");
+    debug("bootstrap: shutdown " + reasonToString(reason));
     if (reason != APP_SHUTDOWN) {
         try {
-            module("resource://dactyl-content/disable-acr.jsm").cleanup();
+            module(DISABLE_ACR).cleanup();
+            if (Cu.unload)
+                Cu.unload(DISABLE_ACR);
         }
         catch (e) {
             reportError(e);
@@ -264,6 +299,18 @@ function shutdown(data, reason) {
     }
 }
 
+function uninstall(data, reason) {
+    debug("bootstrap: uninstall " + reasonToString(reason));
+    if (reason == ADDON_UNINSTALL) {
+        Services.prefs.deleteBranch("extensions.dactyl.");
+
+        if (BOOTSTRAP_CONTRACT in Cc) {
+            let service = Cc[BOOTSTRAP_CONTRACT].getService().wrappedJSObject;
+            manager.unregisterFactory(service.classID, service);
+        }
+    }
+}
+
 function reasonToString(reason) {
     for each (let name in ["disable", "downgrade", "enable",
                            "install", "shutdown", "startup",
@@ -273,7 +320,6 @@ function reasonToString(reason) {
             return name;
 }
 
-function install(data, reason) { dump("dactyl: bootstrap: install " + reasonToString(reason) + "\n"); }
-function uninstall(data, reason) { dump("dactyl: bootstrap: uninstall " + reasonToString(reason) + "\n"); }
+function install(data, reason) { debug("bootstrap: install " + reasonToString(reason)); }
 
 // vim: set fdm=marker sw=4 ts=4 et: