bootstrap.js
author Dave Townsend <dtownsend@oxymoronical.com>
Fri Jul 09 12:41:11 2010 -0700 (18 months ago)
changeset 0 f8b0b8c6f940
permissions -rw-r--r--
Initial import of demo extension
     1 Components.utils.import("resource://gre/modules/AddonManager.jsm");
     2 Components.utils.import("resource://gre/modules/Services.jsm");
     3 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
     4 
     5 const Cc = Components.classes;
     6 const Ci = Components.interfaces;
     7 
     8 const NS_XHTML = "http://www.w3.org/1999/xhtml";
     9 
    10 var gDBStarted = false;
    11 var gRootURI = null;
    12 
    13 XPCOMUtils.defineLazyGetter(this, "ScriptDB", function() {
    14   try {
    15     var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
    16                  getService(Ci.mozIJSSubScriptLoader);
    17     var scope = {};
    18     loader.loadSubScript(gRootURI.spec + "includes/scriptdb.js", scope);
    19     gDBStarted = true;
    20     return scope.ScriptDatabase;
    21   }
    22   catch (e) {
    23     LOGE("Exception loading database", e);
    24   }
    25 });
    26 
    27 function LOG(aStr) {
    28   dump("SLMNKY: " + aStr + "\n");
    29 }
    30 
    31 function LOGE(aStr, aException) {
    32   dump("SLMNKY: " + aStr + ": " + aException + "\n");
    33   if ("stack" in aException)
    34     dump(aException.stack);
    35 }
    36 
    37 function getManagerStyles() {
    38   return "#category-scripts > .category-icon,\
    39           .addon[type=user-script] .icon,\
    40           #detail-view[type=user-script] #detail-icon {\
    41             list-style-image: url(" + gRootURI.spec + "images/script.png)\
    42           }";
    43 }
    44 
    45 function ExtendedStringBundle(aBase) {
    46   this.basebundle = aBase;
    47   this.strings = {};
    48 }
    49 
    50 ExtendedStringBundle.prototype = {
    51   strings: null,
    52   basebundle: null,
    53 
    54   GetStringFromName: function(aName) {
    55     if (aName in this.strings)
    56       return this.strings[aName];
    57     return this.basebundle.GetStringFromName(aName);
    58   },
    59 
    60   formatStringFromName: function(aName, aArgs, aLength) {
    61     return this.basebundle.formatStringFromName(aName, aArgs, aLength);
    62   }
    63 };
    64 
    65 var WindowObserver = {
    66   addToAddonsManager: function(aWindow) {
    67     var window = aWindow.wrappedJSObject;
    68 
    69     try {
    70       var bundle = new ExtendedStringBundle(window.gStrings.ext);
    71       bundle.strings["header-user-script"] = "User Scripts";
    72       window.gStrings.ext = bundle;
    73 
    74       var plugins = window.document.getElementById("category-plugins");
    75       var scripts = window.document.createElement("richlistitem");
    76       scripts.setAttribute("id", "category-scripts");
    77       scripts.setAttribute("value", "addons://list/user-script");
    78       scripts.setAttribute("class", "category");
    79       scripts.setAttribute("name", "User Scripts");
    80       plugins.parentNode.insertBefore(scripts, plugins);
    81 
    82       var styles = window.document.createElementNS(NS_XHTML, "style");
    83       styles.setAttribute("id", "script-styles");
    84       styles.setAttribute("type", "text/css");
    85       styles.appendChild(window.document.createTextNode(getManagerStyles()));
    86       window.document.documentElement.appendChild(styles);
    87     }
    88     catch (e) {
    89       LOGE("Exception in injectIntoAddonsManager", e);
    90     }
    91   },
    92 
    93   findAllAddonsManagers: function() {
    94     var managers = [];
    95     var windows = Services.wm.getEnumerator("navigator:browser");
    96     while (windows.hasMoreElements()) {
    97       var window = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
    98       window.gBrowser.browsers.forEach(function(aBrowser) {
    99         if (aBrowser.currentURI.spec == "about:addons")
   100           managers.push(aBrowser.contentWindow);
   101       });
   102     }
   103     return managers;
   104   },
   105 
   106   addToAddonsManagers: function() {
   107     var managers = this.findAllAddonsManagers();
   108     managers.forEach(function(aWindow) {
   109       this.addToAddonsManager(aWindow);
   110     }, this);
   111   },
   112 
   113   removeFromAddonsManagers: function() {
   114     var managers = this.findAllAddonsManagers();
   115     managers.forEach(function(aWindow) {
   116       var window = aWindow.wrappedJSObject;
   117       var scripts = window.document.getElementById("category-scripts");
   118       scripts.parentNode.removeChild(scripts);
   119       var styles = window.document.getElementById("script-styles");
   120       styles.parentNode.removeChild(styles);
   121       window.gStrings.ext = window.gStrings.ext.basebundle;
   122     });
   123   },
   124 
   125   installPrompt: function(aWindow) {
   126     var window = aWindow.wrappedJSObject;
   127 
   128     window.addEventListener("load", function() {
   129       if (!Services.prompt.confirm(window, "Install Script?", "Do you want to install this user script?"))
   130         return;
   131 
   132       ScriptDB.installScript(window.document.body.textContent,
   133                              aWindow.document.documentURIObject);
   134     }, false);
   135   },
   136 
   137   injectScripts: function(aWindow, aScripts) {
   138     var safeWin = new XPCNativeWrapper(aWindow.wrappedJSObject);
   139 
   140     aScripts.forEach(function(aScript) {
   141       LOG("Injecting " + aScript.name);
   142       var sandbox = new Components.utils.Sandbox(safeWin);
   143       sandbox.window = safeWin;
   144       sandbox.document = sandbox.window.document;
   145       sandbox.unsafeWindow = aWindow.wrappedJSObject;
   146       sandbox.__proto__ = safeWin;
   147 
   148       var code = "(function(){" + aScript.script + "})()";
   149 
   150       try {
   151         Components.utils.evalInSandbox(code, sandbox, "1.8");
   152       }
   153       catch(e) {
   154         LOGE("Exception in injected script", e);
   155       }
   156     });
   157   },
   158 
   159   observe: function(aSubject, aTopic, aData) {
   160     try {
   161       var window = aSubject;
   162       var uri = window.document.documentURIObject;
   163       switch (aTopic) {
   164       case "content-document-global-created":
   165         LOG("Content window loaded: " + uri.spec);
   166         var scripts = ScriptDB.getScriptsForURI(uri);
   167         if (scripts.length > 0) {
   168           window.addEventListener("DOMContentLoaded", function() {
   169             WindowObserver.injectScripts(window, scripts);
   170           }, false);
   171         }
   172         if (uri.spec.substring(uri.spec.length - 8) == ".user.js" &&
   173             !ScriptDB.hasScript(uri))
   174           this.installPrompt(window);
   175         break;
   176       case "chrome-document-global-created":
   177         LOG("Chrome window loaded: " + uri.spec);
   178         if (uri.spec == "about:addons") {
   179           window.addEventListener("load", function() {
   180             WindowObserver.addToAddonsManager(window);
   181           }, false);
   182         }
   183         break;
   184       }
   185     }
   186     catch (e) {
   187       LOGE("Exception in observe", e);
   188     }
   189   }
   190 };
   191 
   192 var AddonProvider = {
   193   getAddonByID: function(aId, aCallback) {
   194     aCallback(ScriptDB.getScript(aId));
   195   },
   196 
   197   getAddonsByTypes: function(aTypes, aCallback) {
   198     if (aTypes && aTypes.indexOf("user-script") < 0)
   199       aCallback([]);
   200     else
   201       aCallback(ScriptDB.getAllScripts());
   202   }
   203 };
   204 
   205 function startup(aParams) {
   206   LOG("startup");
   207 
   208   try {
   209     gRootURI = Services.io.newFileURI(aParams.installPath);
   210     Services.obs.addObserver(WindowObserver, "content-document-global-created", false);
   211     Services.obs.addObserver(WindowObserver, "chrome-document-global-created", false);
   212     AddonManagerPrivate.registerProvider(AddonProvider);
   213 
   214     WindowObserver.addToAddonsManagers();
   215   }
   216   catch (e) {
   217     LOGE("Exception during startup", e);
   218   }
   219 }
   220 
   221 function shutdown() {
   222   LOG("shutdown");
   223 
   224   try {
   225     WindowObserver.removeFromAddonsManagers();
   226 
   227     AddonManagerPrivate.unregisterProvider(AddonProvider);
   228     Services.obs.removeObserver(WindowObserver, "content-document-global-created");
   229     Services.obs.removeObserver(WindowObserver, "chrome-document-global-created");
   230 
   231     if (gDBStarted)
   232       ScriptDB.shutdown();
   233   }
   234   catch (e) {
   235     LOGE("Exception during shutdown", e);
   236   }
   237 }