Bug 407963 - "[FUEL] Factor out generic toolkit aspects of FUEL into a reusable form" [p=jminta@gmail.com (Joey Minta) r=mfinkle r=gavin sr=bsmedberg a1.9=beltzner]
authorreed@reedloden.com
Sat, 12 Jan 2008 23:48:31 -0800
changeset 10227 b277871ab0cfc9f452e2e7776b5a7e89cfd5ca2f
parent 10226 9dd46f99afe205b6176192960f8859242b8e912b
child 10228 f7f38b290b5aa867be9fd2fd8a9d757e065d4e08
push idunknown
push userunknown
push dateunknown
reviewersmfinkle, gavin, bsmedberg
bugs407963
milestone1.9b3pre
Bug 407963 - "[FUEL] Factor out generic toolkit aspects of FUEL into a reusable form" [p=jminta@gmail.com (Joey Minta) r=mfinkle r=gavin sr=bsmedberg a1.9=beltzner]
browser/fuel/public/fuelIApplication.idl
browser/fuel/src/fuelApplication.js
toolkit/components/Makefile.in
toolkit/components/exthelper/Makefile.in
toolkit/components/exthelper/extApplication.js
toolkit/components/exthelper/extIApplication.idl
--- a/browser/fuel/public/fuelIApplication.idl
+++ b/browser/fuel/public/fuelIApplication.idl
@@ -1,360 +1,56 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FUEL.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mark Finkle <mfinkle@mozilla.com> (Original Author)
+ *  John Resig  <jresig@mozilla.com> (Original Author)
+ *
+ * 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
 #include "nsISupports.idl"
+#include "extIApplication.idl"
 
 interface nsIVariant;
 interface nsIURI;
 interface nsIDOMHTMLDocument;
 
-interface fuelIPreference;
 interface fuelIBookmarkFolder;
 interface fuelIBrowserTab;
 
 /**
- * Interface that gives simplified access to the console
- */
-[scriptable, uuid(ae8482e0-aa5a-11db-abbd-0800200c9a66)]
-interface fuelIConsole : nsISupports
-{
-  /**
-   * Sends a given string to the console.
-   * @param   aMsg
-   *          The text to send to the console
-   */
-  void log(in AString aMsg);
-
-  /**
-   * Opens the error console window. The console window
-   * is focused if already open.
-   */
-  void open();
-};
-
-
-/**
- * Interface holds information about an event.
- */
-[scriptable, function, uuid(05281820-ab62-11db-abbd-0800200c9a66)]
-interface fuelIEventItem : nsISupports
-{
-  /**
-   * The name of the event
-   */
-  readonly attribute AString type;
-
-  /**
-   * Can hold extra details and data associated with the event. This
-   * is optional and event specific. If the event does not send extra
-   * details, this is null.
-   */
-  readonly attribute AString data;
-
-  /**
-   * Cancels the event if it is cancelable.
-   */
-  void preventDefault();
-}; 
-
-
-/**
- * Interface used as a callback for listening to events.
- */
-[scriptable, function, uuid(2dfe3a50-ab2f-11db-abbd-0800200c9a66)]
-interface fuelIEventListener : nsISupports
-{
-  /**
-   * This method is called whenever an event occurs of the type for which 
-   * the fuelIEventListener interface was registered.
-   *
-   * @param   aEvent
-   *          The fuelIEventItem associated with the event.
-   */
-  void handleEvent(in fuelIEventItem aEvent);
-}; 
-
-
-/**
- * Interface for supporting custom events.
- */
-[scriptable, uuid(3a8ec9d0-ab19-11db-abbd-0800200c9a66)]
-interface fuelIEvents : nsISupports
-{
-  /**
-   * Adds an event listener to the list. If multiple identical event listeners
-   * are registered on the same event target with the same parameters the
-   * duplicate instances are discarded. They do not cause the EventListener
-   * to be called twice and since they are discarded they do not need to be
-   * removed with the removeListener method.
-   *
-   * @param   aEvent
-   *          The name of an event
-   * @param   aListener
-   *          The reference to a listener
-   */
-  void addListener(in AString aEvent, in fuelIEventListener aListener);
-
-  /**
-   * Removes an event listener from the list. Calling remove
-   * with arguments which do not identify any currently registered
-   * event listener has no effect.
-   * @param   aEvent
-   *          The name of an event
-   * @param   aListener
-   *          The reference to a listener
-   */
-  void removeListener(in AString aEvent, in fuelIEventListener aListener);
-}; 
-
-
-/**
- * Interface for simplified access to preferences. The interface has a
- * predefined root preference branch. The root branch is set based on the
- * context of the owner. For example, an extension's preferences have a root
- * of "extensions.<extensionid>.", while the application level preferences
- * have an empty root. All preference "aName" parameters used in this interface
- * are relative to the root branch.
- */
-[scriptable, uuid(ce697d40-aa5a-11db-abbd-0800200c9a66)]
-interface fuelIPreferenceBranch : nsISupports
-{
-  /**
-   * The name of the branch root.
-   */
-  readonly attribute AString root;
-  
-  /**
-   * Array of fuelIPreference listing all preferences in this branch.
-   */
-  readonly attribute nsIVariant all;
-  
-  /**
-   * The events object for the preferences
-   * supports: "change"
-   */
-  readonly attribute fuelIEvents events;
-  
-  /**
-   * Check to see if a preference exists.
-   * @param   aName
-   *          The name of preference
-   * @returns true if the preference exists, false if not
-   */
-  boolean has(in AString aName);
-  
-  /**
-   * Gets an object representing a preference
-   * @param   aName
-   *          The name of preference
-   * @returns a preference object, or null if the preference does not exist
-   */
-  fuelIPreference get(in AString aName);
-  
-  /**
-   * Gets the value of a preference. Returns a default value if
-   * the preference does not exist.
-   * @param   aName
-   *          The name of preference
-   * @param   aDefaultValue
-   *          The value to return if preference does not exist
-   * @returns value of the preference or the given default value if preference
-   *          does not exists.
-   */
-  nsIVariant getValue(in AString aName, in nsIVariant aDefaultValue);
-
-  /**
-   * Sets the value of a storage item with the given name.
-   * @param   aName
-   *          The name of an item
-   * @param   aValue
-   *          The value to assign to the item
-   */
-  void setValue(in AString aName, in nsIVariant aValue);
-
-  /**
-   * Resets all preferences in a branch back to their default values.
-   */
-  void reset();
-};
-
-/**
- * Interface for accessing a single preference. The data is not cached.
- * All reads access the current state of the preference.
- */
-[scriptable, uuid(2C7462E2-72C2-4473-9007-0E6AE71E23CA)]
-interface fuelIPreference : nsISupports
-{
-  /**
-   * The name of the preference.
-   */
-  readonly attribute AString name;
-  
-  /**
-   * A string representing the type of preference (String, Boolean, or Number).
-   */
-  readonly attribute AString type;
-  
-  /**
-   * Get/Set the value of the preference.
-   */
-  attribute nsIVariant value;
-  
-  /**
-   * Get the locked state of the preference. Set to a boolean value to (un)lock it.
-   */
-  attribute boolean locked;
-  
-  /**
-   * Check if a preference has been modified by the user, or not.
-   */
-  readonly attribute boolean modified;
-  
-  /**
-   * The preference branch that contains this preference.
-   */
-  readonly attribute fuelIPreferenceBranch branch;
-  
-  /**
-   * The events object for this preference.
-   * supports: "change"
-   */
-  readonly attribute fuelIEvents events;
-  
-  /**
-   * Resets a preference back to its default values.
-   */
-  void reset();
-};
-
-
-/**
- * Interface representing a simple storage system
- */
-[scriptable, uuid(0787ac44-29b9-4889-b97f-13573aec6971)]
-interface fuelISessionStorage : nsISupports
-{
-  /**
-   * The events object for the storage
-   * supports: "change"
-   */
-  readonly attribute fuelIEvents events;
-
-  /**
-   * Determines if a storage item exists with the given name.
-   * @param   aName
-   *          The name of an item
-   * @returns true if an item exists with the given name,
-   *          false otherwise.
-   */
-  boolean has(in AString aName);
-
-  /**
-   * Sets the value of a storage item with the given name.
-   * @param   aName
-   *          The name of an item
-   * @param   aValue
-   *          The value to assign to the item
-   */
-  void set(in AString aName, in nsIVariant aValue);
-
-  /**
-   * Gets the value of a storage item with the given name. Returns a
-   * default value if the item does not exist.
-   * @param   aName
-   *          The name of an item
-   * @param   aDefaultValue
-   *          The value to return if no item exists with the given name
-   * @returns value of the item or the given default value if no item
-   *          exists with the given name.
-   */
-  nsIVariant get(in AString aName, in nsIVariant aDefaultValue);
-}; 
-
-
-/**
- * Interface representing an extension
- */
-[scriptable, uuid(10cee02c-f6e0-4d61-ab27-c16572b18c46)]
-interface fuelIExtension : nsISupports
-{
-  /**
-   * The id of the extension.
-   */
-  readonly attribute AString id;
-
-  /**
-   * The name of the extension.
-   */
-  readonly attribute AString name;
-  
-  /**
-   * Check if the extension is currently enabled, or not.
-   */
-  readonly attribute boolean enabled;
-  
-  /**
-   * The version number of the extension.
-   */
-  readonly attribute AString version;
-
-  /**
-   * Indicates whether this is the extension's first run after install
-   */
-  readonly attribute boolean firstRun;
-
-  /**
-   * The preferences object for the extension. Defaults to the
-   * "extensions.<extensionid>." branch.
-   */
-  readonly attribute fuelIPreferenceBranch prefs;
-
-  /**
-   * The storage object for the extension.
-   */
-  readonly attribute fuelISessionStorage storage;
-
-  /**
-   * The events object for the extension.
-   * supports: "uninstall"
-   */
-  readonly attribute fuelIEvents events;
-}; 
-
-
-/**
- * Interface representing a list of all installed extensions
- */
-[scriptable, uuid(de281930-aa5a-11db-abbd-0800200c9a66)]
-interface fuelIExtensions : nsISupports
-{
-  /**
-   * Array of fuelIExtension listing all extensions in the application.
-   */
-  readonly attribute nsIVariant all;
-
-  /**
-   * Determines if an extension exists with the given id.
-   * @param   aId
-   *          The id of an extension
-   * @returns true if an extension exists with the given id,
-   *          false otherwise.
-   */
-  boolean has(in AString aId);
-
-  /**
-   * Gets a fuelIExtension object for an extension.
-   * @param   aId
-   *          The id of an extension
-   * @returns An extension object or null if no extension exists
-   *          with the given id.
-   */
-  fuelIExtension get(in AString aId);
-}; 
-
-
-/**
  * Interface representing a collection of annotations associated
  * with a bookmark or bookmark folder.
  */
 [scriptable, uuid(335c9292-91a1-4ca0-ad0b-07d5f63ed6cd)]
 interface fuelIAnnotations : nsISupports
 {
   /**
    * Array of the annotation names associated with the owning item
@@ -447,17 +143,17 @@ interface fuelIBookmark : nsISupports
    * The annotations object for the bookmark.
    */
   readonly attribute fuelIAnnotations annotations;
 
   /**
    * The events object for the bookmark.
    * supports: "remove", "change", "visit", "move"
    */
-  readonly attribute fuelIEvents events;
+  readonly attribute extIEvents events;
 
   /**
    * Removes the item from the parent folder. Used to
    * delete a bookmark or separator
    */
   void remove();
 }; 
 
@@ -499,17 +195,17 @@ interface fuelIBookmarkFolder : nsISuppo
    * The annotations object for the folder.
    */
   readonly attribute fuelIAnnotations annotations;
 
   /**
    * The events object for the folder.
    * supports: "add", "addchild", "remove", "removechild", "change", "move"
    */
-  readonly attribute fuelIEvents events;
+  readonly attribute extIEvents events;
 
   /**
    * Array of all bookmarks, separators and folders contained
    * in this folder.
    */
   readonly attribute nsIVariant children;
 
   /**
@@ -562,17 +258,17 @@ interface fuelIWindow : nsISupports
    *          The uri to open the browser tab to
    */
   fuelIBrowserTab open(in nsIURI aURI);
   
   /**
    * The events object for the browser window.
    * supports: "TabOpen", "TabClose", "TabMove", "TabSelect"
    */
-  readonly attribute fuelIEvents events;
+  readonly attribute extIEvents events;
 };
 
 /**
  * Interface representing a browser tab.
  */
 [scriptable, uuid(3073ceff-777c-41ce-9ace-ab37268147c1)]
 interface fuelIBrowserTab : nsISupports
 {
@@ -595,17 +291,17 @@ interface fuelIBrowserTab : nsISupports
    * The content document of the browser tab.
    */
   readonly attribute nsIDOMHTMLDocument document;
 
   /**
    * The events object for the browser tab.
    * supports: "load"
    */
-  readonly attribute fuelIEvents events;
+  readonly attribute extIEvents events;
   
   /**
    * Load a new URI into this browser tab.
    * @param   aURI
    *          The uri to load into the browser tab
    */
   void load(in nsIURI aURI);
 
@@ -628,67 +324,23 @@ interface fuelIBrowserTab : nsISupports
   void moveBefore(in fuelIBrowserTab aBefore);
   
   /**
    * Move this browser tab to the last tab within the window.
    */
   void moveToEnd();
 };
 
-
 /**
  * Interface for managing and accessing the applications systems
  */
 [scriptable, uuid(fe74cf80-aa2d-11db-abbd-0800200c9a66)]
-interface fuelIApplication : nsISupports
+interface fuelIApplication : extIApplication
 {
   /**
-   * The id of the application.
-   */
-  readonly attribute AString id;
-
-  /**
-   * The name of the application.
-   */
-  readonly attribute AString name;
-  
-  /**
-   * The version number of the application.
-   */
-  readonly attribute AString version;
-  
-  /**
-   * The console object for the application.
-   */
-  readonly attribute fuelIConsole console;
-
-  /**
-   * The extensions object for the application. Contains a list
-   * of all installed extensions.
-   */
-  readonly attribute fuelIExtensions extensions;
-
-  /**
-   * The preferences object for the application. Defaults to an empty
-   * root branch.
-   */
-  readonly attribute fuelIPreferenceBranch prefs;
-
-  /**
-   * The storage object for the application.
-   */
-  readonly attribute fuelISessionStorage storage;
-
-  /**
-   * The events object for the application.
-   * supports: "load", "ready", "quit", "unload"
-   */
-  readonly attribute fuelIEvents events;
-
-  /**
    * The root bookmarks object for the application.
    */
   readonly attribute fuelIBookmarkFolder bookmarks;
   
   /**
    * An array of browser windows within the application.
    */
   readonly attribute nsIVariant windows;
--- a/browser/fuel/src/fuelApplication.js
+++ b/browser/fuel/src/fuelApplication.js
@@ -36,530 +36,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 //=================================================
-// Shutdown - used to store cleanup functions which will
-//            be called on Application shutdown
-var gShutdown = [];
-
-//=================================================
-// Console constructor
-function Console() {
-  this._console = Components.classes["@mozilla.org/consoleservice;1"]
-    .getService(Ci.nsIConsoleService);
-}
-
-//=================================================
-// Console implementation
-Console.prototype = {
-  log : function cs_log(aMsg) {
-    this._console.logStringMessage(aMsg);
-  },
-
-  open : function cs_open() {
-    var wMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                              .getService(Ci.nsIWindowMediator);
-    var console = wMediator.getMostRecentWindow("global:console");
-    if (!console) {
-      var wWatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
-                             .getService(Ci.nsIWindowWatcher);
-      wWatch.openWindow(null, "chrome://global/content/console.xul", "_blank",
-                        "chrome,dialog=no,all", null);
-    } else {
-      // console was already open
-      console.focus();
-    }
-  },
-
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIConsole])
-};
-
-
-//=================================================
-// EventItem constructor
-function EventItem(aType, aData) {
-  this._type = aType;
-  this._data = aData;
-}
-
-//=================================================
-// EventItem implementation
-EventItem.prototype = {
-  _cancel : false,
-
-  get type() {
-    return this._type;
-  },
-
-  get data() {
-    return this._data;
-  },
-
-  preventDefault : function ei_pd() {
-    this._cancel = true;
-  },
-
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIEventItem])
-};
-
-
-//=================================================
-// Events constructor
-function Events() {
-  this._listeners = [];
-}
-
-//=================================================
-// Events implementation
-Events.prototype = {
-  addListener : function evts_al(aEvent, aListener) {
-    if (this._listeners.some(hasFilter))
-      return;
-
-    this._listeners.push({
-      event: aEvent,
-      listener: aListener
-    });
-
-    function hasFilter(element) {
-      return element.event == aEvent && element.listener == aListener;
-    }
-  },
-
-  removeListener : function evts_rl(aEvent, aListener) {
-    this._listeners = this._listeners.filter(function(element){
-      return element.event != aEvent && element.listener != aListener;
-    });
-  },
-
-  dispatch : function evts_dispatch(aEvent, aEventItem) {
-    eventItem = new EventItem(aEvent, aEventItem);
-
-    this._listeners.forEach(function(key){
-      if (key.event == aEvent) {
-        key.listener.handleEvent ?
-          key.listener.handleEvent(eventItem) :
-          key.listener(eventItem);
-      }
-    });
-
-    return !eventItem._cancel;
-  },
-
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIEvents])
-};
-
-
-//=================================================
-// PreferenceBranch constructor
-function PreferenceBranch(aBranch) {
-  if (!aBranch)
-    aBranch = "";
-
-  this._root = aBranch;
-  this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
-                          .getService(Ci.nsIPrefService);
-
-  if (aBranch)
-    this._prefs = this._prefs.getBranch(aBranch);
-
-  this._prefs.QueryInterface(Ci.nsIPrefBranch);
-  this._prefs.QueryInterface(Ci.nsIPrefBranch2);
-
-  // we want to listen to "all" changes for this branch, so pass in a blank domain
-  this._prefs.addObserver("", this, true);
-  this._events = new Events();
-
-  var self = this;
-  gShutdown.push(function() { self._shutdown(); });
-}
-
-//=================================================
-// PreferenceBranch implementation
-PreferenceBranch.prototype = {
-  // cleanup observer so we don't leak
-  _shutdown: function prefs_shutdown() {
-    this._prefs.removeObserver(this._root, this);
-
-    this._prefs = null;
-    this._events = null;
-  },
-
-  // for nsIObserver
-  observe: function prefs_observe(aSubject, aTopic, aData) {
-    if (aTopic == "nsPref:changed")
-      this._events.dispatch("change", aData);
-  },
-
-  get root() {
-    return this._root;
-  },
-
-  get all() {
-    return this.find({});
-  },
-
-  get events() {
-    return this._events;
-  },
-
-  // XXX: Disabled until we can figure out the wrapped object issues
-  // name: "name" or /name/
-  // path: "foo.bar." or "" or /fo+\.bar/
-  // type: Boolean, Number, String (getPrefType)
-  // locked: true, false (prefIsLocked)
-  // modified: true, false (prefHasUserValue)
-  find : function prefs_find(aOptions) {
-    var retVal = [];
-    var items = this._prefs.getChildList("", []);
-
-    for (var i = 0; i < items.length; i++) {
-      retVal.push(new Preference(items[i], this));
-    }
-
-    return retVal;
-  },
-
-  has : function prefs_has(aName) {
-    return (this._prefs.getPrefType(aName) != Ci.nsIPrefBranch.PREF_INVALID);
-  },
-
-  get : function prefs_get(aName) {
-    return this.has(aName) ? new Preference(aName, this) : null;
-  },
-
-  getValue : function prefs_gv(aName, aValue) {
-    var type = this._prefs.getPrefType(aName);
-
-    switch (type) {
-      case Ci.nsIPrefBranch2.PREF_STRING:
-        aValue = this._prefs.getComplexValue(aName, Ci.nsISupportsString).data;
-        break;
-      case Ci.nsIPrefBranch2.PREF_BOOL:
-        aValue = this._prefs.getBoolPref(aName);
-        break;
-      case Ci.nsIPrefBranch2.PREF_INT:
-        aValue = this._prefs.getIntPref(aName);
-        break;
-    }
-
-    return aValue;
-  },
-
-  setValue : function prefs_sv(aName, aValue) {
-    var type = aValue != null ? aValue.constructor.name : "";
-
-    switch (type) {
-      case "String":
-        var str = Components.classes["@mozilla.org/supports-string;1"]
-                            .createInstance(Ci.nsISupportsString);
-        str.data = aValue;
-        this._prefs.setComplexValue(aName, Ci.nsISupportsString, str);
-        break;
-      case "Boolean":
-        this._prefs.setBoolPref(aName, aValue);
-        break;
-      case "Number":
-        this._prefs.setIntPref(aName, aValue);
-        break;
-      default:
-        throw("Unknown preference value specified.");
-    }
-  },
-
-  reset : function prefs_reset() {
-    this._prefs.resetBranch("");
-  },
-
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIPreferenceBranch, Ci.nsISupportsWeakReference])
-};
-
-
-//=================================================
-// Preference constructor
-function Preference(aName, aBranch) {
-  this._name = aName;
-  this._branch = aBranch;
-  this._events = new Events();
-
-  var self = this;
-
-  this.branch.events.addListener("change", function(aEvent){
-    if (aEvent.data == self.name)
-      self.events.dispatch(aEvent.type, aEvent.data);
-  });
-}
-
-//=================================================
-// Preference implementation
-Preference.prototype = {
-  get name() {
-    return this._name;
-  },
-
-  get type() {
-    var value = "";
-    var type = this._prefs.getPrefType(name);
-
-    switch (type) {
-      case Ci.nsIPrefBranch2.PREF_STRING:
-        value = "String";
-        break;
-      case Ci.nsIPrefBranch2.PREF_BOOL:
-        value = "Boolean";
-        break;
-      case Ci.nsIPrefBranch2.PREF_INT:
-        value = "Number";
-        break;
-    }
-
-    return value;
-  },
-
-  get value() {
-    return this.branch.getValue(this._name, null);
-  },
-
-  set value(aValue) {
-    return this.branch.setValue(this._name, aValue);
-  },
-
-  get locked() {
-    return this.branch._prefs.prefIsLocked(this.name);
-  },
-
-  set locked(aValue) {
-    this.branch._prefs[ aValue ? "lockPref" : "unlockPref" ](this.name);
-  },
-
-  get modified() {
-    return this.branch._prefs.prefHasUserValue(this.name);
-  },
-
-  get branch() {
-    return this._branch;
-  },
-
-  get events() {
-    return this._events;
-  },
-
-  reset : function pref_reset() {
-    this.branch._prefs.clearUserPref(this.name);
-  },
-
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIPreference])
-};
-
-
-//=================================================
-// SessionStorage constructor
-function SessionStorage() {
-  this._storage = {};
-  this._events = new Events();
-}
-
-//=================================================
-// SessionStorage implementation
-SessionStorage.prototype = {
-  get events() {
-    return this._events;
-  },
-
-  has : function ss_has(aName) {
-    return this._storage.hasOwnProperty(aName);
-  },
-
-  set : function ss_set(aName, aValue) {
-    this._storage[aName] = aValue;
-    this._events.dispatch("change", aName);
-  },
-
-  get : function ss_get(aName, aDefaultValue) {
-    return this.has(aName) ? this._storage[aName] : aDefaultValue;
-  },
-
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelISessionStorage])
-};
-
-
-//=================================================
-// Extension constructor
-function Extension(aItem) {
-  this._item = aItem;
-  this._firstRun = false;
-  this._prefs = new PreferenceBranch("extensions." + this._item.id + ".");
-  this._storage = new SessionStorage();
-  this._events = new Events();
-
-  var installPref = "install-event-fired";
-  if (!this._prefs.has(installPref)) {
-    this._prefs.setValue(installPref, true);
-    this._firstRun = true;
-  }
-
-  this._enabled = false;
-  const PREFIX_ITEM_URI = "urn:mozilla:item:";
-  const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
-  var rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
-  var itemResource = rdf.GetResource(PREFIX_ITEM_URI + this._item.id);
-  if (itemResource) {
-    var extmgr = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
-    var ds = extmgr.datasource;
-    var target = ds.GetTarget(itemResource, rdf.GetResource(PREFIX_NS_EM + "isDisabled"), true);
-    if (target && target instanceof Ci.nsIRDFLiteral)
-      this._enabled = (target.Value != "true");
-  }
-
-  var os = Components.classes["@mozilla.org/observer-service;1"]
-                     .getService(Ci.nsIObserverService);
-  os.addObserver(this, "em-action-requested", false);
-
-  var self = this;
-  gShutdown.push(function(){ self._shutdown(); });
-}
-
-//=================================================
-// Extension implementation
-Extension.prototype = {
-  // cleanup observer so we don't leak
-  _shutdown: function ext_shutdown() {
-    var os = Components.classes["@mozilla.org/observer-service;1"]
-                       .getService(Ci.nsIObserverService);
-    os.removeObserver(this, "em-action-requested");
-
-    this._prefs = null;
-    this._storage = null;
-    this._events = null;
-  },
-
-  // for nsIObserver
-  observe: function ext_observe(aSubject, aTopic, aData)
-  {
-    if ((aSubject instanceof Ci.nsIUpdateItem) && (aSubject.id == this._item.id))
-    {
-      if (aData == "item-uninstalled")
-        this._events.dispatch("uninstall", this._item.id);
-      else if (aData == "item-disabled")
-        this._events.dispatch("disable", this._item.id);
-      else if (aData == "item-enabled")
-        this._events.dispatch("enable", this._item.id);
-      else if (aData == "item-cancel-action")
-        this._events.dispatch("cancel", this._item.id);
-      else if (aData == "item-upgraded")
-        this._events.dispatch("upgrade", this._item.id);
-    }
-  },
-
-  get id() {
-    return this._item.id;
-  },
-
-  get name() {
-    return this._item.name;
-  },
-
-  get enabled() {
-    return this._enabled;
-  },
-
-  get version() {
-    return this._item.version;
-  },
-
-  get firstRun() {
-    return this._firstRun;
-  },
-
-  get storage() {
-    return this._storage;
-  },
-
-  get prefs() {
-    return this._prefs;
-  },
-
-  get events() {
-    return this._events;
-  },
-
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIExtension])
-};
-
-
-//=================================================
-// Extensions constructor
-function Extensions() {
-  this._extmgr = Components.classes["@mozilla.org/extensions/manager;1"]
-                           .getService(Ci.nsIExtensionManager);
-
-  this._cache = {};
-
-  var self = this;
-  gShutdown.push(function() { self._shutdown(); });
-}
-
-//=================================================
-// Extensions implementation
-Extensions.prototype = {
-  _shutdown : function exts_shutdown() {
-    this._extmgr = null;
-    this._cache = null;
-  },
-
-  /*
-   * Helper method to check cache before creating a new extension
-   */
-  _get : function exts_get(aId) {
-    if (this._cache.hasOwnProperty(aId))
-      return this._cache[aId];
-
-    var newExt = new Extension(this._extmgr.getItemForID(aId));
-    this._cache[aId] = newExt;
-    return newExt;
-  },
-
-  get all() {
-    return this.find({});
-  },
-
-  // XXX: Disabled until we can figure out the wrapped object issues
-  // id: "some@id" or /id/
-  // name: "name" or /name/
-  // version: "1.0.1"
-  // minVersion: "1.0"
-  // maxVersion: "2.0"
-  find : function exts_find(aOptions) {
-    var retVal = [];
-    var items = this._extmgr.getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION, {});
-
-    for (var i = 0; i < items.length; i++) {
-      retVal.push(this._get(items[i].id));
-    }
-
-    return retVal;
-  },
-
-  has : function exts_has(aId) {
-    return this._extmgr.getItemForID(aId) != null;
-  },
-
-  get : function exts_get(aId) {
-    return this.has(aId) ? this._get(aId) : null;
-  },
-
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIExtensions])
-};
-
-//=================================================
 // Singleton that holds services and utilities
 var Utilities = {
   _bookmarks : null,
   get bookmarks() {
     if (!this._bookmarks) {
       this._bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
                         getService(Ci.nsINavBookmarksService);
     }
@@ -1135,163 +621,53 @@ var ApplicationFactory = {
     if (gSingleton == null) {
       gSingleton = new Application();
     }
 
     return gSingleton.QueryInterface(aIID);
   }
 };
 
+
+
 //=================================================
 // Application constructor
 function Application() {
-  this._console = null;
-  this._prefs = null;
-  this._storage = null;
-  this._events = null;
+  this.initToolkitHelpers();
   this._bookmarks = null;
-
-  this._info = Components.classes["@mozilla.org/xre/app-info;1"]
-                     .getService(Ci.nsIXULAppInfo);
-
-  var os = Components.classes["@mozilla.org/observer-service;1"]
-                     .getService(Ci.nsIObserverService);
-
-  os.addObserver(this, "final-ui-startup", false);
-  os.addObserver(this, "quit-application-requested", false);
-  os.addObserver(this, "quit-application-granted", false);
-  os.addObserver(this, "quit-application", false);
-  os.addObserver(this, "xpcom-shutdown", false);
 }
 
 //=================================================
 // Application implementation
 Application.prototype = {
   // for nsIClassInfo + XPCOMUtils
   classDescription: "Application",
   classID:          Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66"),
   contractID:       "@mozilla.org/fuel/application;1",
 
   // redefine the default factory for XPCOMUtils
   _xpcom_factory: ApplicationFactory,
 
-  // get this contractID registered for certain categories via XPCOMUtils
-  _xpcom_categories: [
-    // make Application a startup observer
-    { category: "app-startup", service: true },
-
-    // add Application as a global property for easy access
-    { category: "JavaScript global privileged property" }
-  ],
-
-  get id() {
-    return this._info.ID;
-  },
-
-  get name() {
-    return this._info.name;
-  },
-
-  get version() {
-    return this._info.version;
-  },
-
-  // for nsIObserver
-  observe: function app_observe(aSubject, aTopic, aData) {
-    if (aTopic == "app-startup") {
-      this._extensions = new Extensions();
-      this.events.dispatch("load", "application");
-    }
-    else if (aTopic == "final-ui-startup") {
-      this.events.dispatch("ready", "application");
-    }
-    else if (aTopic == "quit-application-requested") {
-      // we can stop the quit by checking the return value
-      if (this.events.dispatch("quit", "application") == false)
-        aSubject.data = true;
-    }
-    else if (aTopic == "xpcom-shutdown") {
-      this.events.dispatch("unload", "application");
-
-      // call the cleanup functions and empty the array
-      while (gShutdown.length) {
-        gShutdown.shift()();
-      }
-
-      // release our observers
-      var os = Components.classes["@mozilla.org/observer-service;1"]
-                         .getService(Ci.nsIObserverService);
-
-      os.removeObserver(this, "final-ui-startup");
-
-      os.removeObserver(this, "quit-application-requested");
-      os.removeObserver(this, "quit-application-granted");
-      os.removeObserver(this, "quit-application");
-
-      os.removeObserver(this, "xpcom-shutdown");
-
-      this._info = null;
-      this._console = null;
-      this._prefs = null;
-      this._storage = null;
-      this._events = null;
-      this._extensions = null;
-      this._bookmarks = null;
-
-      Utilities.free();
-    }
-  },
-
-  // for nsIClassInfo
-  flags : Ci.nsIClassInfo.SINGLETON,
-  implementationLanguage : Ci.nsIProgrammingLanguage.JAVASCRIPT,
+  // for nsISupports
+  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIApplication, Ci.nsIObserver, Ci.nsIClassInfo]),
 
   getInterfaces : function app_gi(aCount) {
     var interfaces = [Ci.fuelIApplication, Ci.nsIObserver, Ci.nsIClassInfo];
     aCount.value = interfaces.length;
     return interfaces;
   },
 
-  getHelperForLanguage : function app_ghfl(aCount) {
-    return null;
-  },
-
-  // for nsISupports
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIApplication, Ci.nsIObserver, Ci.nsIClassInfo]),
-
-  get console() {
-    if (this._console == null)
-        this._console = new Console();
-
-    return this._console;
-  },
-
-  get storage() {
-    if (this._storage == null)
-        this._storage = new SessionStorage();
-
-    return this._storage;
-  },
-
-  get prefs() {
-    if (this._prefs == null)
-        this._prefs = new PreferenceBranch("");
-
-    return this._prefs;
-  },
-
-  get extensions() {
-    return this._extensions;
-  },
-
-  get events() {
-    if (this._events == null)
-        this._events = new Events();
-
-    return this._events;
+  // for nsIObserver
+  observe: function app_observe(aSubject, aTopic, aData) {
+    // Call the extApplication version of this function first
+    this.__proto__.__proto__.observe(aSubject, aTopic, aData);
+    if (aTopic == "xpcom-shutdown") {
+      this._bookmarks = null;
+      Utilities.free();
+    }
   },
 
   get bookmarks() {
     if (this._bookmarks == null)
       this._bookmarks = new BookmarkFolder(null, null);
 
     return this._bookmarks;
   },
@@ -1308,10 +684,14 @@ Application.prototype = {
 
   get activeWindow() {
     return new Window(Utilities.windowMediator.getMostRecentWindow("navigator:browser"));
   }
 };
 
 //module initialization
 function NSGetModule(aCompMgr, aFileSpec) {
+  // set the proto, defined in extApplication.js
+  Application.prototype.__proto__ = extApplication.prototype;
   return XPCOMUtils.generateModule([Application]);
 }
+
+#include ../../../toolkit/components/exthelper/extApplication.js
--- a/toolkit/components/Makefile.in
+++ b/toolkit/components/Makefile.in
@@ -56,16 +56,17 @@ DIRS += \
 
 # These component dirs are built only for XUL apps
 
 ifdef MOZ_XUL_APP
 DIRS += \
 	apppicker \
 	filepicker \
 	console \
+	exthelper \
 	viewconfig \
 	typeaheadfind \
 	$(NULL)
 
 ifneq (,$(filter cocoa, $(MOZ_WIDGET_TOOLKIT)))
 TOOL_DIRS += alerts
 else
 DIRS += alerts
new file mode 100644
--- /dev/null
+++ b/toolkit/components/exthelper/Makefile.in
@@ -0,0 +1,50 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is FUEL.
+#
+# The Initial Developer of the Original Code is Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2006
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Mark Finkle <mfinkle@mozilla.com>
+#   John Resig  <jresig@mozilla.com>
+#
+# 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
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH     = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE        = exthelper
+XPIDL_MODULE  = exthelper
+
+XPIDLSRCS = extIApplication.idl
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/components/exthelper/extApplication.js
@@ -0,0 +1,692 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FUEL.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mark Finkle <mfinkle@mozilla.com> (Original Author)
+ *  John Resig  <jresig@mozilla.com> (Original Author)
+ *
+ * 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+//=================================================
+// Shutdown - used to store cleanup functions which will
+//            be called on Application shutdown
+var gShutdown = [];
+
+//=================================================
+// Console constructor
+function Console() {
+  this._console = Components.classes["@mozilla.org/consoleservice;1"]
+    .getService(Ci.nsIConsoleService);
+}
+
+//=================================================
+// Console implementation
+Console.prototype = {
+  log : function cs_log(aMsg) {
+    this._console.logStringMessage(aMsg);
+  },
+
+  open : function cs_open() {
+    var wMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+                              .getService(Ci.nsIWindowMediator);
+    var console = wMediator.getMostRecentWindow("global:console");
+    if (!console) {
+      var wWatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+                             .getService(Ci.nsIWindowWatcher);
+      wWatch.openWindow(null, "chrome://global/content/console.xul", "_blank",
+                        "chrome,dialog=no,all", null);
+    } else {
+      // console was already open
+      console.focus();
+    }
+  },
+
+  QueryInterface : XPCOMUtils.generateQI([Ci.extIConsole])
+};
+
+
+//=================================================
+// EventItem constructor
+function EventItem(aType, aData) {
+  this._type = aType;
+  this._data = aData;
+}
+
+//=================================================
+// EventItem implementation
+EventItem.prototype = {
+  _cancel : false,
+
+  get type() {
+    return this._type;
+  },
+
+  get data() {
+    return this._data;
+  },
+
+  preventDefault : function ei_pd() {
+    this._cancel = true;
+  },
+
+  QueryInterface : XPCOMUtils.generateQI([Ci.extIEventItem])
+};
+
+
+//=================================================
+// Events constructor
+function Events() {
+  this._listeners = [];
+}
+
+//=================================================
+// Events implementation
+Events.prototype = {
+  addListener : function evts_al(aEvent, aListener) {
+    if (this._listeners.some(hasFilter))
+      return;
+
+    this._listeners.push({
+      event: aEvent,
+      listener: aListener
+    });
+
+    function hasFilter(element) {
+      return element.event == aEvent && element.listener == aListener;
+    }
+  },
+
+  removeListener : function evts_rl(aEvent, aListener) {
+    this._listeners = this._listeners.filter(function(element){
+      return element.event != aEvent && element.listener != aListener;
+    });
+  },
+
+  dispatch : function evts_dispatch(aEvent, aEventItem) {
+    eventItem = new EventItem(aEvent, aEventItem);
+
+    this._listeners.forEach(function(key){
+      if (key.event == aEvent) {
+        key.listener.handleEvent ?
+          key.listener.handleEvent(eventItem) :
+          key.listener(eventItem);
+      }
+    });
+
+    return !eventItem._cancel;
+  },
+
+  QueryInterface : XPCOMUtils.generateQI([Ci.extIEvents])
+};
+
+
+//=================================================
+// PreferenceBranch constructor
+function PreferenceBranch(aBranch) {
+  if (!aBranch)
+    aBranch = "";
+
+  this._root = aBranch;
+  this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                          .getService(Ci.nsIPrefService);
+
+  if (aBranch)
+    this._prefs = this._prefs.getBranch(aBranch);
+
+  this._prefs.QueryInterface(Ci.nsIPrefBranch);
+  this._prefs.QueryInterface(Ci.nsIPrefBranch2);
+
+  // we want to listen to "all" changes for this branch, so pass in a blank domain
+  this._prefs.addObserver("", this, true);
+  this._events = new Events();
+
+  var self = this;
+  gShutdown.push(function() { self._shutdown(); });
+}
+
+//=================================================
+// PreferenceBranch implementation
+PreferenceBranch.prototype = {
+  // cleanup observer so we don't leak
+  _shutdown: function prefs_shutdown() {
+    this._prefs.removeObserver(this._root, this);
+
+    this._prefs = null;
+    this._events = null;
+  },
+
+  // for nsIObserver
+  observe: function prefs_observe(aSubject, aTopic, aData) {
+    if (aTopic == "nsPref:changed")
+      this._events.dispatch("change", aData);
+  },
+
+  get root() {
+    return this._root;
+  },
+
+  get all() {
+    return this.find({});
+  },
+
+  get events() {
+    return this._events;
+  },
+
+  // XXX: Disabled until we can figure out the wrapped object issues
+  // name: "name" or /name/
+  // path: "foo.bar." or "" or /fo+\.bar/
+  // type: Boolean, Number, String (getPrefType)
+  // locked: true, false (prefIsLocked)
+  // modified: true, false (prefHasUserValue)
+  find : function prefs_find(aOptions) {
+    var retVal = [];
+    var items = this._prefs.getChildList("", []);
+
+    for (var i = 0; i < items.length; i++) {
+      retVal.push(new Preference(items[i], this));
+    }
+
+    return retVal;
+  },
+
+  has : function prefs_has(aName) {
+    return (this._prefs.getPrefType(aName) != Ci.nsIPrefBranch.PREF_INVALID);
+  },
+
+  get : function prefs_get(aName) {
+    return this.has(aName) ? new Preference(aName, this) : null;
+  },
+
+  getValue : function prefs_gv(aName, aValue) {
+    var type = this._prefs.getPrefType(aName);
+
+    switch (type) {
+      case Ci.nsIPrefBranch2.PREF_STRING:
+        aValue = this._prefs.getComplexValue(aName, Ci.nsISupportsString).data;
+        break;
+      case Ci.nsIPrefBranch2.PREF_BOOL:
+        aValue = this._prefs.getBoolPref(aName);
+        break;
+      case Ci.nsIPrefBranch2.PREF_INT:
+        aValue = this._prefs.getIntPref(aName);
+        break;
+    }
+
+    return aValue;
+  },
+
+  setValue : function prefs_sv(aName, aValue) {
+    var type = aValue != null ? aValue.constructor.name : "";
+
+    switch (type) {
+      case "String":
+        var str = Components.classes["@mozilla.org/supports-string;1"]
+                            .createInstance(Ci.nsISupportsString);
+        str.data = aValue;
+        this._prefs.setComplexValue(aName, Ci.nsISupportsString, str);
+        break;
+      case "Boolean":
+        this._prefs.setBoolPref(aName, aValue);
+        break;
+      case "Number":
+        this._prefs.setIntPref(aName, aValue);
+        break;
+      default:
+        throw("Unknown preference value specified.");
+    }
+  },
+
+  reset : function prefs_reset() {
+    this._prefs.resetBranch("");
+  },
+
+  QueryInterface : XPCOMUtils.generateQI([Ci.extIPreferenceBranch, Ci.nsISupportsWeakReference])
+};
+
+
+//=================================================
+// Preference constructor
+function Preference(aName, aBranch) {
+  this._name = aName;
+  this._branch = aBranch;
+  this._events = new Events();
+
+  var self = this;
+
+  this.branch.events.addListener("change", function(aEvent){
+    if (aEvent.data == self.name)
+      self.events.dispatch(aEvent.type, aEvent.data);
+  });
+}
+
+//=================================================
+// Preference implementation
+Preference.prototype = {
+  get name() {
+    return this._name;
+  },
+
+  get type() {
+    var value = "";
+    var type = this._prefs.getPrefType(name);
+
+    switch (type) {
+      case Ci.nsIPrefBranch2.PREF_STRING:
+        value = "String";
+        break;
+      case Ci.nsIPrefBranch2.PREF_BOOL:
+        value = "Boolean";
+        break;
+      case Ci.nsIPrefBranch2.PREF_INT:
+        value = "Number";
+        break;
+    }
+
+    return value;
+  },
+
+  get value() {
+    return this.branch.getValue(this._name, null);
+  },
+
+  set value(aValue) {
+    return this.branch.setValue(this._name, aValue);
+  },
+
+  get locked() {
+    return this.branch._prefs.prefIsLocked(this.name);
+  },
+
+  set locked(aValue) {
+    this.branch._prefs[ aValue ? "lockPref" : "unlockPref" ](this.name);
+  },
+
+  get modified() {
+    return this.branch._prefs.prefHasUserValue(this.name);
+  },
+
+  get branch() {
+    return this._branch;
+  },
+
+  get events() {
+    return this._events;
+  },
+
+  reset : function pref_reset() {
+    this.branch._prefs.clearUserPref(this.name);
+  },
+
+  QueryInterface : XPCOMUtils.generateQI([Ci.extIPreference])
+};
+
+
+//=================================================
+// SessionStorage constructor
+function SessionStorage() {
+  this._storage = {};
+  this._events = new Events();
+}
+
+//=================================================
+// SessionStorage implementation
+SessionStorage.prototype = {
+  get events() {
+    return this._events;
+  },
+
+  has : function ss_has(aName) {
+    return this._storage.hasOwnProperty(aName);
+  },
+
+  set : function ss_set(aName, aValue) {
+    this._storage[aName] = aValue;
+    this._events.dispatch("change", aName);
+  },
+
+  get : function ss_get(aName, aDefaultValue) {
+    return this.has(aName) ? this._storage[aName] : aDefaultValue;
+  },
+
+  QueryInterface : XPCOMUtils.generateQI([Ci.extISessionStorage])
+};
+
+
+//=================================================
+// Extension constructor
+function Extension(aItem) {
+  this._item = aItem;
+  this._firstRun = false;
+  this._prefs = new PreferenceBranch("extensions." + this._item.id + ".");
+  this._storage = new SessionStorage();
+  this._events = new Events();
+
+  var installPref = "install-event-fired";
+  if (!this._prefs.has(installPref)) {
+    this._prefs.setValue(installPref, true);
+    this._firstRun = true;
+  }
+
+  this._enabled = false;
+  const PREFIX_ITEM_URI = "urn:mozilla:item:";
+  const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
+  var rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
+  var itemResource = rdf.GetResource(PREFIX_ITEM_URI + this._item.id);
+  if (itemResource) {
+    var extmgr = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
+    var ds = extmgr.datasource;
+    var target = ds.GetTarget(itemResource, rdf.GetResource(PREFIX_NS_EM + "isDisabled"), true);
+    if (target && target instanceof Ci.nsIRDFLiteral)
+      this._enabled = (target.Value != "true");
+  }
+
+  var os = Components.classes["@mozilla.org/observer-service;1"]
+                     .getService(Ci.nsIObserverService);
+  os.addObserver(this, "em-action-requested", false);
+
+  var self = this;
+  gShutdown.push(function(){ self._shutdown(); });
+}
+
+//=================================================
+// Extension implementation
+Extension.prototype = {
+  // cleanup observer so we don't leak
+  _shutdown: function ext_shutdown() {
+    var os = Components.classes["@mozilla.org/observer-service;1"]
+                       .getService(Ci.nsIObserverService);
+    os.removeObserver(this, "em-action-requested");
+
+    this._prefs = null;
+    this._storage = null;
+    this._events = null;
+  },
+
+  // for nsIObserver
+  observe: function ext_observe(aSubject, aTopic, aData)
+  {
+    if ((aSubject instanceof Ci.nsIUpdateItem) && (aSubject.id == this._item.id))
+    {
+      if (aData == "item-uninstalled")
+        this._events.dispatch("uninstall", this._item.id);
+      else if (aData == "item-disabled")
+        this._events.dispatch("disable", this._item.id);
+      else if (aData == "item-enabled")
+        this._events.dispatch("enable", this._item.id);
+      else if (aData == "item-cancel-action")
+        this._events.dispatch("cancel", this._item.id);
+      else if (aData == "item-upgraded")
+        this._events.dispatch("upgrade", this._item.id);
+    }
+  },
+
+  get id() {
+    return this._item.id;
+  },
+
+  get name() {
+    return this._item.name;
+  },
+
+  get enabled() {
+    return this._enabled;
+  },
+
+  get version() {
+    return this._item.version;
+  },
+
+  get firstRun() {
+    return this._firstRun;
+  },
+
+  get storage() {
+    return this._storage;
+  },
+
+  get prefs() {
+    return this._prefs;
+  },
+
+  get events() {
+    return this._events;
+  },
+
+  QueryInterface : XPCOMUtils.generateQI([Ci.extIExtension])
+};
+
+
+//=================================================
+// Extensions constructor
+function Extensions() {
+  this._extmgr = Components.classes["@mozilla.org/extensions/manager;1"]
+                           .getService(Ci.nsIExtensionManager);
+
+  this._cache = {};
+
+  var self = this;
+  gShutdown.push(function() { self._shutdown(); });
+}
+
+//=================================================
+// Extensions implementation
+Extensions.prototype = {
+  _shutdown : function exts_shutdown() {
+    this._extmgr = null;
+    this._cache = null;
+  },
+
+  /*
+   * Helper method to check cache before creating a new extension
+   */
+  _get : function exts_get(aId) {
+    if (this._cache.hasOwnProperty(aId))
+      return this._cache[aId];
+
+    var newExt = new Extension(this._extmgr.getItemForID(aId));
+    this._cache[aId] = newExt;
+    return newExt;
+  },
+
+  get all() {
+    return this.find({});
+  },
+
+  // XXX: Disabled until we can figure out the wrapped object issues
+  // id: "some@id" or /id/
+  // name: "name" or /name/
+  // version: "1.0.1"
+  // minVersion: "1.0"
+  // maxVersion: "2.0"
+  find : function exts_find(aOptions) {
+    var retVal = [];
+    var items = this._extmgr.getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION, {});
+
+    for (var i = 0; i < items.length; i++) {
+      retVal.push(this._get(items[i].id));
+    }
+
+    return retVal;
+  },
+
+  has : function exts_has(aId) {
+    return this._extmgr.getItemForID(aId) != null;
+  },
+
+  get : function exts_get(aId) {
+    return this.has(aId) ? this._get(aId) : null;
+  },
+
+  QueryInterface : XPCOMUtils.generateQI([Ci.extIExtensions])
+};
+
+//=================================================
+// extApplication constructor
+function extApplication() {
+}
+
+//=================================================
+// extApplication implementation
+extApplication.prototype = {
+  initToolkitHelpers: function extApp_initToolkitHelpers() {
+    this._console = null;
+    this._storage = null;
+    this._prefs = null;
+    this._extensions = null;
+    this._events = null;
+
+    this._info = Components.classes["@mozilla.org/xre/app-info;1"]
+                           .getService(Ci.nsIXULAppInfo);
+
+    var os = Components.classes["@mozilla.org/observer-service;1"]
+                       .getService(Ci.nsIObserverService);
+
+    os.addObserver(this, "final-ui-startup", false);
+    os.addObserver(this, "quit-application-requested", false);
+    os.addObserver(this, "quit-application-granted", false);
+    os.addObserver(this, "quit-application", false);
+    os.addObserver(this, "xpcom-shutdown", false);
+  },
+
+  // get this contractID registered for certain categories via XPCOMUtils
+  _xpcom_categories: [
+    // make Application a startup observer
+    { category: "app-startup", service: true },
+
+    // add Application as a global property for easy access
+    { category: "JavaScript global privileged property" }
+  ],
+
+  // for nsIClassInfo
+  flags : Ci.nsIClassInfo.SINGLETON,
+  implementationLanguage : Ci.nsIProgrammingLanguage.JAVASCRIPT,
+
+  getInterfaces : function app_gi(aCount) {
+    var interfaces = [Ci.extIApplication, Ci.nsIObserver, Ci.nsIClassInfo];
+    aCount.value = interfaces.length;
+    return interfaces;
+  },
+
+  getHelperForLanguage : function app_ghfl(aCount) {
+    return null;
+  },
+
+  // extIApplication
+  get id() {
+    return this._info.ID;
+  },
+
+  get name() {
+    return this._info.name;
+  },
+
+  get version() {
+    return this._info.version;
+  },
+
+  // for nsIObserver
+  observe: function app_observe(aSubject, aTopic, aData) {
+    if (aTopic == "app-startup") {
+      this.events.dispatch("load", "application");
+    }
+    else if (aTopic == "final-ui-startup") {
+      this.events.dispatch("ready", "application");
+    }
+    else if (aTopic == "quit-application-requested") {
+      // we can stop the quit by checking the return value
+      if (this.events.dispatch("quit", "application") == false)
+        aSubject.data = true;
+    }
+    else if (aTopic == "xpcom-shutdown") {
+      this.events.dispatch("unload", "application");
+
+      // call the cleanup functions and empty the array
+      while (gShutdown.length) {
+        gShutdown.shift()();
+      }
+
+      // release our observers
+      var os = Components.classes["@mozilla.org/observer-service;1"]
+                         .getService(Ci.nsIObserverService);
+
+      os.removeObserver(this, "final-ui-startup");
+
+      os.removeObserver(this, "quit-application-requested");
+      os.removeObserver(this, "quit-application-granted");
+      os.removeObserver(this, "quit-application");
+
+      os.removeObserver(this, "xpcom-shutdown");
+
+      this._info = null;
+      this._console = null;
+      this._prefs = null;
+      this._storage = null;
+      this._events = null;
+      this._extensions = null;
+    }
+  },
+
+  get console() {
+    if (this._console == null)
+        this._console = new Console();
+
+    return this._console;
+  },
+
+  get storage() {
+    if (this._storage == null)
+        this._storage = new SessionStorage();
+
+    return this._storage;
+  },
+
+  get prefs() {
+    if (this._prefs == null)
+        this._prefs = new PreferenceBranch("");
+
+    return this._prefs;
+  },
+
+  get extensions() {
+    if (this._extensions == null)
+      this._extensions = new Extensions();
+
+    return this._extensions;
+  },
+
+  get events() {
+    if (this._events == null)
+        this._events = new Events();
+
+    return this._events;
+  }
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/exthelper/extIApplication.idl
@@ -0,0 +1,429 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FUEL.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mark Finkle <mfinkle@mozilla.com> (Original Author)
+ *  John Resig  <jresig@mozilla.com> (Original Author)
+ *
+ * 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+interface extIPreference;
+interface extISessionStorage;
+
+
+/**
+ * Interface that gives simplified access to the console
+ */
+[scriptable, uuid(ae8482e0-aa5a-11db-abbd-0800200c9a66)]
+interface extIConsole : nsISupports
+{
+  /**
+   * Sends a given string to the console.
+   * @param   aMsg
+   *          The text to send to the console
+   */
+  void log(in AString aMsg);
+
+  /**
+   * Opens the error console window. The console window
+   * is focused if already open.
+   */
+  void open();
+};
+
+
+/**
+ * Interface holds information about an event.
+ */
+[scriptable, function, uuid(05281820-ab62-11db-abbd-0800200c9a66)]
+interface extIEventItem : nsISupports
+{
+  /**
+   * The name of the event
+   */
+  readonly attribute AString type;
+
+  /**
+   * Can hold extra details and data associated with the event. This
+   * is optional and event specific. If the event does not send extra
+   * details, this is null.
+   */
+  readonly attribute AString data;
+
+  /**
+   * Cancels the event if it is cancelable.
+   */
+  void preventDefault();
+}; 
+
+
+/**
+ * Interface used as a callback for listening to events.
+ */
+[scriptable, function, uuid(2dfe3a50-ab2f-11db-abbd-0800200c9a66)]
+interface extIEventListener : nsISupports
+{
+  /**
+   * This method is called whenever an event occurs of the type for which 
+   * the extIEventListener interface was registered.
+   *
+   * @param   aEvent
+   *          The extIEventItem associated with the event.
+   */
+  void handleEvent(in extIEventItem aEvent);
+}; 
+
+
+/**
+ * Interface for supporting custom events.
+ */
+[scriptable, uuid(3a8ec9d0-ab19-11db-abbd-0800200c9a66)]
+interface extIEvents : nsISupports
+{
+  /**
+   * Adds an event listener to the list. If multiple identical event listeners
+   * are registered on the same event target with the same parameters the
+   * duplicate instances are discarded. They do not cause the EventListener
+   * to be called twice and since they are discarded they do not need to be
+   * removed with the removeListener method.
+   *
+   * @param   aEvent
+   *          The name of an event
+   * @param   aListener
+   *          The reference to a listener
+   */
+  void addListener(in AString aEvent, in extIEventListener aListener);
+
+  /**
+   * Removes an event listener from the list. Calling remove
+   * with arguments which do not identify any currently registered
+   * event listener has no effect.
+   * @param   aEvent
+   *          The name of an event
+   * @param   aListener
+   *          The reference to a listener
+   */
+  void removeListener(in AString aEvent, in extIEventListener aListener);
+}; 
+
+
+/**
+ * Interface for simplified access to preferences. The interface has a
+ * predefined root preference branch. The root branch is set based on the
+ * context of the owner. For example, an extension's preferences have a root
+ * of "extensions.<extensionid>.", while the application level preferences
+ * have an empty root. All preference "aName" parameters used in this interface
+ * are relative to the root branch.
+ */
+[scriptable, uuid(ce697d40-aa5a-11db-abbd-0800200c9a66)]
+interface extIPreferenceBranch : nsISupports
+{
+  /**
+   * The name of the branch root.
+   */
+  readonly attribute AString root;
+  
+  /**
+   * Array of extIPreference listing all preferences in this branch.
+   */
+  readonly attribute nsIVariant all;
+  
+  /**
+   * The events object for the preferences
+   * supports: "change"
+   */
+  readonly attribute extIEvents events;
+  
+  /**
+   * Check to see if a preference exists.
+   * @param   aName
+   *          The name of preference
+   * @returns true if the preference exists, false if not
+   */
+  boolean has(in AString aName);
+  
+  /**
+   * Gets an object representing a preference
+   * @param   aName
+   *          The name of preference
+   * @returns a preference object, or null if the preference does not exist
+   */
+  extIPreference get(in AString aName);
+  
+  /**
+   * Gets the value of a preference. Returns a default value if
+   * the preference does not exist.
+   * @param   aName
+   *          The name of preference
+   * @param   aDefaultValue
+   *          The value to return if preference does not exist
+   * @returns value of the preference or the given default value if preference
+   *          does not exists.
+   */
+  nsIVariant getValue(in AString aName, in nsIVariant aDefaultValue);
+
+  /**
+   * Sets the value of a storage item with the given name.
+   * @param   aName
+   *          The name of an item
+   * @param   aValue
+   *          The value to assign to the item
+   */
+  void setValue(in AString aName, in nsIVariant aValue);
+
+  /**
+   * Resets all preferences in a branch back to their default values.
+   */
+  void reset();
+};
+
+/**
+ * Interface for accessing a single preference. The data is not cached.
+ * All reads access the current state of the preference.
+ */
+[scriptable, uuid(2C7462E2-72C2-4473-9007-0E6AE71E23CA)]
+interface extIPreference : nsISupports
+{
+  /**
+   * The name of the preference.
+   */
+  readonly attribute AString name;
+  
+  /**
+   * A string representing the type of preference (String, Boolean, or Number).
+   */
+  readonly attribute AString type;
+  
+  /**
+   * Get/Set the value of the preference.
+   */
+  attribute nsIVariant value;
+  
+  /**
+   * Get the locked state of the preference. Set to a boolean value to (un)lock it.
+   */
+  attribute boolean locked;
+  
+  /**
+   * Check if a preference has been modified by the user, or not.
+   */
+  readonly attribute boolean modified;
+  
+  /**
+   * The preference branch that contains this preference.
+   */
+  readonly attribute extIPreferenceBranch branch;
+  
+  /**
+   * The events object for this preference.
+   * supports: "change"
+   */
+  readonly attribute extIEvents events;
+  
+  /**
+   * Resets a preference back to its default values.
+   */
+  void reset();
+};
+
+
+/**
+ * Interface representing an extension
+ */
+[scriptable, uuid(10cee02c-f6e0-4d61-ab27-c16572b18c46)]
+interface extIExtension : nsISupports
+{
+  /**
+   * The id of the extension.
+   */
+  readonly attribute AString id;
+
+  /**
+   * The name of the extension.
+   */
+  readonly attribute AString name;
+  
+  /**
+   * Check if the extension is currently enabled, or not.
+   */
+  readonly attribute boolean enabled;
+  
+  /**
+   * The version number of the extension.
+   */
+  readonly attribute AString version;
+
+  /**
+   * Indicates whether this is the extension's first run after install
+   */
+  readonly attribute boolean firstRun;
+
+  /**
+   * The preferences object for the extension. Defaults to the
+   * "extensions.<extensionid>." branch.
+   */
+  readonly attribute extIPreferenceBranch prefs;
+
+  /**
+   * The storage object for the extension.
+   */
+  readonly attribute extISessionStorage storage;
+
+  /**
+   * The events object for the extension.
+   * supports: "uninstall"
+   */
+  readonly attribute extIEvents events;
+}; 
+
+
+/**
+ * Interface representing a list of all installed extensions
+ */
+[scriptable, uuid(de281930-aa5a-11db-abbd-0800200c9a66)]
+interface extIExtensions : nsISupports
+{
+  /**
+   * Array of extIExtension listing all extensions in the application.
+   */
+  readonly attribute nsIVariant all;
+
+  /**
+   * Determines if an extension exists with the given id.
+   * @param   aId
+   *          The id of an extension
+   * @returns true if an extension exists with the given id,
+   *          false otherwise.
+   */
+  boolean has(in AString aId);
+
+  /**
+   * Gets a extIExtension object for an extension.
+   * @param   aId
+   *          The id of an extension
+   * @returns An extension object or null if no extension exists
+   *          with the given id.
+   */
+  extIExtension get(in AString aId);
+}; 
+
+/**
+ * Interface representing a simple storage system
+ */
+[scriptable, uuid(0787ac44-29b9-4889-b97f-13573aec6971)]
+interface extISessionStorage : nsISupports
+{
+  /**
+   * The events object for the storage
+   * supports: "change"
+   */
+  readonly attribute extIEvents events;
+
+  /**
+   * Determines if a storage item exists with the given name.
+   * @param   aName
+   *          The name of an item
+   * @returns true if an item exists with the given name,
+   *          false otherwise.
+   */
+  boolean has(in AString aName);
+
+  /**
+   * Sets the value of a storage item with the given name.
+   * @param   aName
+   *          The name of an item
+   * @param   aValue
+   *          The value to assign to the item
+   */
+  void set(in AString aName, in nsIVariant aValue);
+
+  /**
+   * Gets the value of a storage item with the given name. Returns a
+   * default value if the item does not exist.
+   * @param   aName
+   *          The name of an item
+   * @param   aDefaultValue
+   *          The value to return if no item exists with the given name
+   * @returns value of the item or the given default value if no item
+   *          exists with the given name.
+   */
+  nsIVariant get(in AString aName, in nsIVariant aDefaultValue);
+}; 
+
+[scriptable, uuid(ba9442ee-7070-44fb-8157-c111e1fa70b6)]
+interface extIApplication : nsISupports
+{
+  /**
+   * The id of the application.
+   */
+  readonly attribute AString id;
+
+  /**
+   * The name of the application.
+   */
+  readonly attribute AString name;
+  
+  /**
+   * The version number of the application.
+   */
+  readonly attribute AString version;
+  
+  /**
+   * The console object for the application.
+   */
+  readonly attribute extIConsole console;
+
+  /**
+   * The extensions object for the application. Contains a list
+   * of all installed extensions.
+   */
+  readonly attribute extIExtensions extensions;
+
+  /**
+   * The preferences object for the application. Defaults to an empty
+   * root branch.
+   */
+  readonly attribute extIPreferenceBranch prefs;
+
+  /**
+   * The storage object for the application.
+   */
+  readonly attribute extISessionStorage storage;
+
+  /**
+   * The events object for the application.
+   * supports: "load", "ready", "quit", "unload"
+   */
+  readonly attribute extIEvents events;
+};