bug 372069. add FUEL to trunk. r=gavin
authormark.finkle@gmail.com
Mon, 23 Apr 2007 13:27:46 -0700
changeset 738 454242ccdff6
parent 737 08b777b6bcf1
child 739 d90652534ac8
push id1
push userbsmedberg@mozilla.com
push date2008-03-20 16:49 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin
bugs372069
milestone1.9a4pre
bug 372069. add FUEL to trunk. r=gavin
browser/Makefile.in
browser/fuel/Makefile.in
browser/fuel/public/Makefile.in
browser/fuel/public/fuelIApplication.idl
browser/fuel/src/Makefile.in
browser/fuel/src/fuelApplication.js
browser/fuel/test/Makefile.in
browser/fuel/test/test_Application.html
browser/fuel/test/test_ApplicationPrefs.html
browser/fuel/test/test_ApplicationStorage.html
browser/fuel/test/test_Extensions.html
--- a/browser/Makefile.in
+++ b/browser/Makefile.in
@@ -37,17 +37,17 @@
 
 DEPTH		= ..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(topsrcdir)/config/config.mk
 
-DIRS = base components locales themes app
+DIRS = base components locales themes app fuel
 
 include $(topsrcdir)/config/rules.mk
 
 ifeq ($(OS_ARCH),WINNT)
 ifdef MOZ_INSTALLER
 DIRS += installer/windows
 
 ifdef MOZ_BRANDING_DIRECTORY
--- a/browser/fuel/Makefile.in
+++ b/browser/fuel/Makefile.in
@@ -8,17 +8,17 @@
 #
 # 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.
+# 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
@@ -30,18 +30,23 @@
 # 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@
+DEPTH     = ../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS = public src
 
-include $(topsrcdir)/config/rules.mk
\ No newline at end of file
+ifdef MOZ_MOCHITEST
+DIRS += test
+endif
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/browser/fuel/public/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        = fuel
+XPIDL_MODULE  = fuel
+
+XPIDLSRCS = fuelIApplication.idl
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/fuel/public/fuelIApplication.idl
@@ -0,0 +1,390 @@
+#include "nsISupports.idl"
+#include "nsIVariant.idl"
+
+interface fuelIPreference;
+
+/**
+ * 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.
+   */
+  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(bbaf6210-aafe-11db-abbd-0800200c9a66)]
+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 AString 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.
+   */
+  AString get(in AString aName, in AString aDefaultValue);
+}; 
+
+
+  /**
+   * Interface representing an extension
+   */
+[scriptable, uuid(ea563b60-aa5a-11db-abbd-0800200c9a66)]
+interface fuelIExtension : nsISupports
+{
+  /**
+   * The id of the extension.
+   */
+  readonly attribute AString id;
+
+  /**
+   * The name of the extension.
+   */
+  readonly attribute AString name;
+  
+  /**
+   * 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 for managing and accessing the applications systems
+ */
+[scriptable, uuid(fe74cf80-aa2d-11db-abbd-0800200c9a66)]
+interface fuelIApplication : 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 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;
+}; 
new file mode 100644
--- /dev/null
+++ b/browser/fuel/src/Makefile.in
@@ -0,0 +1,49 @@
+# ***** 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 = fuel
+
+EXTRA_PP_COMPONENTS = fuelApplication.js
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/fuel/src/fuelApplication.js
@@ -0,0 +1,685 @@
+/* ***** 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 ***** */
+ 
+const nsISupports = Components.interfaces.nsISupports;
+const nsIClassInfo = Components.interfaces.nsIClassInfo;
+const nsIObserver = Components.interfaces.nsIObserver;
+const fuelIApplication = Components.interfaces.fuelIApplication;
+
+
+//=================================================
+// 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(Components.interfaces.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(Components.interfaces.nsIWindowMediator);
+    var console = wMediator.getMostRecentWindow("global:console");
+    if (!console) {
+      var wWatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+                             .getService(Components.interfaces.nsIWindowWatcher);
+      wWatch.openWindow(null, "chrome://global/content/console.xul", "_blank",
+                        "chrome,dialog=no,all", cmdLine);
+    } else {
+      // console was already open
+      console.focus();
+    }
+  }
+};
+
+
+//=================================================
+// 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;
+  }
+};
+
+
+//=================================================
+// 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;
+  }
+};
+
+
+//=================================================
+// Preferences constants
+const nsIPrefService = Components.interfaces.nsIPrefService;
+const nsIPrefBranch = Components.interfaces.nsIPrefBranch;
+const nsIPrefBranch2 = Components.interfaces.nsIPrefBranch2;
+const nsISupportsString = Components.interfaces.nsISupportsString;
+
+//=================================================
+// PreferenceBranch constructor
+function PreferenceBranch(aBranch) {
+  if (!aBranch)
+    aBranch = "";
+  
+  this._root = aBranch;
+  this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                          .getService(nsIPrefService);
+
+  if (aBranch)
+    this._prefs = this._prefs.getBranch(aBranch);
+    
+  this._prefs.QueryInterface(nsIPrefBranch);
+  this._prefs.QueryInterface(nsIPrefBranch2);
+  
+  this._prefs.addObserver(this._root, this, false);
+  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);
+  },
+  
+  // 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) != 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 nsIPrefBranch2.PREF_STRING:
+        aValue = this._prefs.getComplexValue(aName, nsISupportsString).data;
+        break;
+      case nsIPrefBranch2.PREF_BOOL:
+        aValue = this._prefs.getBoolPref(aName);
+        break;
+      case 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(nsISupportsString);
+        str.data = aValue;
+        this._prefs.setComplexValue(aName, 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("");
+  }
+};
+
+
+//=================================================
+// 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 nsIPrefBranch2.PREF_STRING:
+        value = "String";
+        break;
+      case nsIPrefBranch2.PREF_BOOL:
+        value = "Boolean";
+        break;
+      case 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);
+  }
+};
+
+
+//=================================================
+// 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;
+  }
+};
+
+
+//=================================================
+// Extension constants
+const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
+
+//=================================================
+// 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;
+  }
+
+  var os = Components.classes["@mozilla.org/observer-service;1"]
+                     .getService(Components.interfaces.nsIObserverService);
+  os.addObserver(this, "em-action-requested", false);
+  
+  var self = this;
+  gShutdown.push(function(){ self._shutdown(); });
+}
+
+//=================================================
+// Extensions implementation
+Extension.prototype = {
+  // cleanup observer so we don't leak
+  _shutdown: function ext_shutdown() {
+    var os = Components.classes["@mozilla.org/observer-service;1"]
+                       .getService(Components.interfaces.nsIObserverService);
+    os.removeObserver(this, "em-action-requested");
+  },
+  
+  // for nsIObserver  
+  observe: function ext_observe(aSubject, aTopic, aData)
+  {
+    if ((aData == "item-uninstalled") &&
+        (aSubject instanceof nsIUpdateItem) &&
+        (aSubject.id == this._item.id))
+    {
+      this._events.dispatch("uninstall", this._item.id);
+    }
+  },
+
+  get id() {
+    return this._item.id;
+  },
+  
+  get name() {
+    return this._item.name;
+  },
+  
+  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;
+  }
+};
+
+
+//=================================================
+// Extensions constructor
+function Extensions() {
+  this._extmgr = Components.classes["@mozilla.org/extensions/manager;1"]
+                           .getService(Components.interfaces.nsIExtensionManager);
+}
+
+//=================================================
+// Extensions implementation
+Extensions.prototype = {
+  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(nsIUpdateItem.TYPE_EXTENSION, {});
+    
+    for (var i = 0; i < items.length; i++) {
+      retVal.push(new Extension(items[i]));
+    }
+
+    return retVal;
+  },
+  
+  has : function exts_has(aId) {
+    // getItemForID never returns null for a non-existent id, so we
+    // check the type of the returned update item, which should be
+    // greater than 1 for a valid extension.
+    return !!(this._extmgr.getItemForID(aId).type);
+  },
+  
+  get : function exts_get(aId) {
+    return this.has(aId) ? new Extension(this._extmgr.getItemForID(aId)) : null;
+  }
+};
+
+
+const CLASS_ID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66");
+const CLASS_NAME = "Application wrapper";
+const CONTRACT_ID = "@mozilla.org/application;1";
+
+//=================================================
+// Application constructor
+function Application() {
+  this._console = new Console();
+  this._prefs = new PreferenceBranch("");
+  this._storage = new SessionStorage();
+  this._events = new Events();
+  
+  this._info = Components.classes["@mozilla.org/xre/app-info;1"]
+                     .getService(Components.interfaces.nsIXULAppInfo);
+    
+  var os = Components.classes["@mozilla.org/observer-service;1"]
+                     .getService(Components.interfaces.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 = {
+  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(Components.interfaces.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");
+    }
+  },
+
+  // for nsIClassInfo
+  classDescription : "Application",
+  classID : CLASS_ID,
+  contractID : CONTRACT_ID,
+  flags : nsIClassInfo.SINGLETON,
+  implementationLanguage : Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
+
+  getInterfaces : function app_gi(aCount) {
+    var interfaces = [fuelIApplication, nsIObserver, nsIClassInfo];
+    aCount.value = interfaces.length;
+    return interfaces;
+  },
+
+  getHelperForLanguage : function app_ghfl(aCount) {
+    return null;
+  },
+  
+  // for nsISupports
+  QueryInterface: function app_qi(aIID) {
+    // add any other interfaces you support here
+    if (aIID.equals(fuelIApplication) ||
+        aIID.equals(nsIObserver) ||
+        aIID.equals(nsIClassInfo) ||
+        aIID.equals(nsISupports))
+    {
+      return this;
+    }
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+  
+  get console() {
+    return this._console;
+  },
+  
+  get storage() {
+    return this._storage;
+  },
+  
+  get prefs() {
+    return this._prefs;
+  },
+  
+  get extensions() {
+    return this._extensions;
+  },
+
+  get events() {
+    return this._events;
+  }
+}
+
+//=================================================
+// Factory - Treat Application as a singleton
+var ApplicationFactory = {
+  singleton: null,
+  
+  createInstance: function af_ci(aOuter, aIID)
+  {
+    if (aOuter != null)
+      throw Components.results.NS_ERROR_NO_AGGREGATION;
+      
+    if (this.singleton == null)
+      this.singleton = new Application();
+
+    return this.singleton.QueryInterface(aIID);
+  }
+};
+
+//=================================================
+// Module
+var ApplicationModule = {
+  registerSelf: function am_rs(aCompMgr, aFileSpec, aLocation, aType)
+  {
+    aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+    aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
+    
+    // make the Update Service a startup observer
+    var categoryManager = Components.classes["@mozilla.org/categorymanager;1"]
+                                    .getService(Components.interfaces.nsICategoryManager);
+    categoryManager.addCategoryEntry("app-startup", CLASS_NAME, "service," + CONTRACT_ID, true, true);
+
+    // add Application as a global property for easy access                                     
+    categoryManager.addCategoryEntry("JavaScript global property", "Application", CONTRACT_ID, true, true);
+  },
+
+  unregisterSelf: function am_us(aCompMgr, aLocation, aType)
+  {
+    aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+    aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);        
+  },
+  
+  getClassObject: function am_gco(aCompMgr, aCID, aIID)
+  {
+    if (!aIID.equals(Components.interfaces.nsIFactory))
+      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+
+    if (aCID.equals(CLASS_ID))
+      return ApplicationFactory;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  canUnload: function am_cu(aCompMgr) { return true; }
+};
+
+//module initialization
+function NSGetModule(aCompMgr, aFileSpec) { return ApplicationModule; }
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/Makefile.in
@@ -0,0 +1,54 @@
+# ***** 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@
+relativesrcdir = browser/fuel/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES =	test_Application.html \
+		test_ApplicationPrefs.html \
+		test_ApplicationStorage.html \
+		test_Extensions.html \
+		$(NULL)
+
+libs::	$(_TEST_FILES)
+	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_Application.html
@@ -0,0 +1,29 @@
+<html>
+<head>
+  <title>Testing Application.js</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+test_Application();
+
+function test_Application() {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+  ok(Application, "Check global access to Application");
+  
+  // I'd test these against a specific value, but that is bound to flucuate
+  ok(Application.id, "Check to see if an ID exists for the Application");
+  ok(Application.name, "Check to see if a name exists for the Application");
+  ok(Application.version, "Check to see if a version exists for the Application");
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_ApplicationPrefs.html
@@ -0,0 +1,180 @@
+<html>
+<head>
+  <title>Testing Application.js</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// The various properties that we'll be testing
+var test = {
+	missing: "fuel.fuel-test-missing",
+	dummy: "fuel.fuel-test",
+	string: "browser.active_color",
+	integer: "permissions.default.image",
+	boolean: "browser.blink_allowed"
+};
+
+test_Preferences();
+
+function test_Preferences() {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+  // test getting non-existing values
+  var itemValue = Application.prefs.getValue(test.missing, "default");
+  is(itemValue, "default", "Check 'Application.prefs.getValue' for non-existing item");
+  
+  is(Application.prefs.get(test.missing), null, "Check 'Application.prefs.get' for non-existing item");
+  
+  // test setting and getting a value
+  Application.prefs.setValue(test.dummy, "dummy");
+  itemValue = Application.prefs.getValue(test.dummy, "default");
+  is(itemValue, "dummy", "Check 'Application.prefs.getValue' for existing item");
+
+  // test for overwriting an existing value
+  Application.prefs.setValue(test.dummy, "smarty");
+  itemValue = Application.prefs.getValue(test.dummy, "default");
+  is(itemValue, "smarty", "Check 'Application.prefs.getValue' for overwritten item");
+  
+  // test setting and getting a value
+  Application.prefs.get(test.dummy).value = "dummy2";
+  itemValue = Application.prefs.get(test.dummy).value;
+  is(itemValue, "dummy2", "Check 'Application.prefs.get().value' for existing item");
+
+  // test resetting a pref [since there is no default value, the pref should disappear]
+  Application.prefs.get(test.dummy).reset();
+  var itemValue = Application.prefs.getValue(test.dummy, "default");
+  is(itemValue, "default", "Check 'Application.prefs.getValue' for reset pref");
+  
+  // test to see if a non-existant property exists
+  ok(!Application.prefs.has(test.dummy), "Check non-existant property for existance");
+  
+  // PREF: string browser.active_color == #EE0000
+  
+  // test to see if an existing string property exists
+  ok(Application.prefs.has(test.string), "Check existing string property for existance");
+  
+  // test accessing a non-existant string property
+  var val = Application.prefs.getValue(test.dummy, "default");
+  is(val, "default", "Check non-existant string property for expected value");
+  
+  // test accessing an existing string property
+  var val = Application.prefs.getValue(test.string, "default");
+  is(val, "#EE0000", "Check existing string property for expected value");
+  
+  // test manipulating an existing string property
+  Application.prefs.setValue(test.string, "#EF0000");
+  var val = Application.prefs.getValue(test.string, "default");
+  is(val, "#EF0000", "Set existing string property");
+  
+  // test resetting an existing string property
+  Application.prefs.get(test.string).reset();
+  var val = Application.prefs.getValue(test.string, "default");
+  is(val, "#EE0000", "Reset existing string property");
+  
+  // PREF: integer permissions.default.image == 1
+  
+  // test to see if an existing integer property exists
+  ok(Application.prefs.has(test.integer), "Check existing integer property for existance");
+  
+  // test accessing a non-existant integer property
+  var val = Application.prefs.getValue(test.dummy, 0);
+  is(val, 0, "Check non-existant integer property for expected value");
+  
+  // test accessing an existing integer property
+  var val = Application.prefs.getValue(test.integer, 0);
+  is(val, 1, "Check existing integer property for expected value");
+
+  // test manipulating an existing integer property
+  Application.prefs.setValue(test.integer, 0);
+  var val = Application.prefs.getValue(test.integer, 1);
+  is(val, 0, "Set existing integer property");
+  
+  // test resetting an existing integer property
+  Application.prefs.get(test.integer).reset();
+  var val = Application.prefs.getValue(test.integer, 0);
+  is(val, 1, "Reset existing integer property");
+
+  // PREF: boolean browser.blink_allowed == true
+  
+  // test to see if an existing boolean property exists
+  ok(Application.prefs.has(test.boolean), "Check existing boolean property for existance");
+  
+  // test accessing a non-existant boolean property
+  var val = Application.prefs.getValue(test.dummy, true);
+  ok(val, "Check non-existant boolean property for expected value");
+  
+  // test accessing an existing boolean property
+  var val = Application.prefs.getValue(test.boolean, false);
+  ok(val, "Check existing boolean property for expected value");
+  
+  // test manipulating an existing boolean property
+  Application.prefs.setValue(test.boolean, false);
+  var val = Application.prefs.getValue(test.boolean, true);
+  ok(!val, "Set existing boolean property");
+  
+  // test resetting an existing boolean property
+  Application.prefs.get(test.boolean).reset();
+  var val = Application.prefs.getValue(test.boolean, false);
+  ok(val, "Reset existing string property for expected value");
+  
+  // test getting all preferences
+  var allPrefs = Application.prefs.all;
+  ok(allPrefs.length >= 800, "Check 'Application.prefs.all' for the right number of preferences");
+  is(allPrefs[0].name, "capability.policy.default.Window.parent.get", "Check 'Application.prefs.all' for the right starting preference");
+
+  // test the value of the preference root
+  is(Application.prefs.root, "", "Check the Application preference root");
+  
+  // test for user changed preferences
+  ok(Application.prefs.get("browser.shell.checkDefaultBrowser").modified, "A single preference is marked as modified.");
+  ok(!Application.prefs.get(test.string).modified, "A single preference is marked as not modified.");
+  
+  // test for a locked preference
+  var pref = Application.prefs.get(test.string);
+  ok(!pref.locked, "A single preference should not be locked.");
+  
+  pref.locked = true;
+  ok(pref.locked, "A single preference should be locked.");
+  
+  try {
+    prev.value = "test value";
+    
+    ok(false, "A locked preference should not be able to be modified.");
+  } catch(e){
+    ok(true, "A locked preference should not be able to be modified.");
+  }
+  
+  pref.locked = false;
+  ok(!pref.locked, "A single preference should not be locked.");
+  
+  // check for change event when setting a value
+  SimpleTest.waitForExplicitFinish();
+  Application.prefs.events.addListener("change", onPrefChange);
+  Application.prefs.setValue("fuel.fuel-test", "change event");
+}
+
+function onPrefChange(evt) {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  is(evt.data, test.dummy, "Check 'Application.prefs.set' fired a change event");
+  Application.prefs.events.removeListener("change", onPrefChange);
+
+  Application.prefs.get("fuel.fuel-test").events.addListener("change", onPrefChange2);
+  Application.prefs.setValue("fuel.fuel-test", "change event2");
+}
+
+function onPrefChange2(evt) {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  is(evt.data, test.dummy, "Check 'Application.prefs.set' fired a change event for a single preference");
+  Application.prefs.events.removeListener("change", onPrefChange2);
+  
+  SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_ApplicationStorage.html
@@ -0,0 +1,52 @@
+<html>
+<head>
+  <title>Testing Application.js</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+test_Storage();
+
+function test_Storage() {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+  // test for existence of values
+  var hasItem = Application.storage.has("fuel-test-missing");
+  is(hasItem, false, "Check 'Application.storage.has' for non-existing item");
+  Application.storage.set("fuel-test", "dummy");
+  hasItem = Application.storage.has("fuel-test");
+  is(hasItem, true, "Check 'Application.storage.has' for existing item");
+
+  // test getting non-existing and existing values
+  var itemValue = Application.storage.get("fuel-test-missing", "default");
+  is(itemValue, "default", "Check 'Application.storage.get' for non-existing item");
+  itemValue = Application.storage.get("fuel-test", "default");
+  is(itemValue, "dummy", "Check 'Application.storage.get' for existing item");
+
+  // test for overwriting an existing value
+  Application.storage.set("fuel-test", "smarty");
+  itemValue = Application.storage.get("fuel-test", "default");
+  is(itemValue, "smarty", "Check 'Application.storage.get' for overwritten item");
+
+  // check for change event when setting a value
+  SimpleTest.waitForExplicitFinish();
+  Application.storage.events.addListener("change", onStorageChange);
+  Application.storage.set("fuel-test", "change event");
+}
+
+function onStorageChange(evt) {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  is(evt.data, "fuel-test", "Check 'Application.storage.set' fired a change event");
+  Application.storage.events.removeListener("change", onStorageChange);
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_Extensions.html
@@ -0,0 +1,51 @@
+<html>
+<head>
+  <title>Testing Application.js</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// The various pieces that we'll be testing
+var test = {
+	dummyid: "fuel-dummy-extension@mozilla.org",
+	dummyname: "Dummy Extension",
+	inspectorid: "inspector@mozilla.org",
+	inspectorname: "DOM Inspector"
+};
+
+test_Extensions();
+
+function test_Extensions() {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+  // test to see if a non-existant extension exists
+  ok(!Application.extensions.has(test.dummyid), "Check non-existant extension for existance");
+
+  // test to see if an extension exists
+  ok(Application.extensions.has(test.inspectorid), "Check extension for existance");
+
+  var inspector = Application.extensions.get(test.inspectorid);
+  is(inspector.id, test.inspectorid, "Check 'Extension.id' for known extension");
+  is(inspector.name, test.inspectorname, "Check 'Extension.name' for known extension");
+  // The known version number changes too frequently to hardcode in
+  ok(inspector.version, "Check 'Extension.version' for known extension");
+  ok(inspector.firstRun, "Check 'Extension.firstRun' for known extension");
+  
+  // test to see if extension find works
+  is(Application.extensions.all.length, 1, "Check a find for all extensions");
+
+  // test the value of the preference root
+  is(Application.extensions.all[0].prefs.root, "extensions.inspector@mozilla.org.", "Check an extension preference root");
+  
+  // Reset the install event preference, so that we can test it again later
+  inspector.prefs.get("install-event-fired").reset();
+}
+</script>
+</pre>
+</body>
+</html>