b=380168. r=gavin, r=dietrich. Add FUEL 0.2 (bookmarks and browsers) to tree.
authormark.finkle@gmail.com
Wed, 27 Jun 2007 05:58:29 -0700
changeset 2849 f22210c7aaf7fe4c45664a8822c4ccc6959e834c
parent 2848 8188d0ab768ad770ad511a3ae15fe65d6481c36a
child 2850 94fd99199ca3bf7275ec6f286da8a89aaf5a5ea0
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin, dietrich
bugs380168
milestone1.9a6pre
b=380168. r=gavin, r=dietrich. Add FUEL 0.2 (bookmarks and browsers) to tree.
browser/fuel/public/fuelIApplication.idl
browser/fuel/src/fuelApplication.js
browser/fuel/test/Makefile.in
browser/fuel/test/test_Application.xul
browser/fuel/test/test_ApplicationPrefs.xul
browser/fuel/test/test_ApplicationStorage.xul
browser/fuel/test/test_Bookmarks.xul
browser/fuel/test/test_Browser.xul
browser/fuel/test/test_ContentA.html
browser/fuel/test/test_ContentB.html
browser/fuel/test/test_Extensions.xul
--- a/browser/fuel/public/fuelIApplication.idl
+++ b/browser/fuel/public/fuelIApplication.idl
@@ -1,12 +1,17 @@
 #include "nsISupports.idl"
-#include "nsIVariant.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
 {
   /**
@@ -258,19 +263,19 @@ interface fuelISessionStorage : nsISuppo
    *          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
-   */
+/**
+ * Interface representing an extension
+ */
 [scriptable, uuid(ea563b60-aa5a-11db-abbd-0800200c9a66)]
 interface fuelIExtension : nsISupports
 {
   /**
    * The id of the extension.
    */
   readonly attribute AString id;
 
@@ -335,16 +340,301 @@ interface fuelIExtensions : nsISupports
    * @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
+   */
+  readonly attribute nsIVariant names;
+
+  /**
+   * Determines if an annotation exists with the given name.
+   * @param   aName
+   *          The name of the annotation
+   * @returns true if an annotation exists with the given name,
+   *          false otherwise.
+   */
+  boolean has(in AString aName);
+
+  /**
+   * Gets the value of an annotation with the given name.
+   * @param   aName
+   *          The name of the annotation
+   * @returns A variant containing the value of the annotation. Supports
+   *          string, boolean and number.
+   */
+  nsIVariant get(in AString aName);
+
+  /**
+   * Sets the value of an annotation with the given name.
+   * @param   aName
+   *          The name of the annotation
+   * @param   aValue
+   *          The new value of the annotation. Supports string, boolean
+   *          and number
+   * @param   aExpiration
+   *          The expiration policy for the annotation.
+   *          See nsIAnnotationService.
+   */
+  void set(in AString aName, in nsIVariant aValue, in PRInt32 aExpiration);
+
+  /**
+   * Removes the named annotation from the owner item.
+   * @param   aName
+   *          The name of annotation.
+   */
+  void remove(in AString aName);
+};
+
+
+/**
+ * Interface representing a bookmark item.
+ */
+[scriptable, uuid(808585b6-7568-4b26-8c62-545221bf2b8c)]
+interface fuelIBookmark : nsISupports
+{
+  /**
+   * The id of the bookmark.
+   */
+  readonly attribute long long id;
+
+  /**
+   * The title of the bookmark.
+   */
+  attribute AString title;
+
+  /**
+   * The uri of the bookmark.
+   */
+  attribute nsIURI uri;
+
+  /**
+   * The description of the bookmark.
+   */
+  attribute AString description;
+
+  /**
+   * The keyword associated with the bookmark.
+   */
+  attribute AString keyword;
+
+  /**
+   * The type of the bookmark.
+   * values: "bookmark", "separator"
+   */
+  readonly attribute AString type;
+
+  /**
+   * The parent folder of the bookmark.
+   */
+  attribute fuelIBookmarkFolder parent;
+
+  /**
+   * 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;
+
+  /**
+   * Removes the item from the parent folder. Used to
+   * delete a bookmark or separator
+   */
+  void remove();
+}; 
+
+
+/**
+ * Interface representing a bookmark folder. Folders
+ * can hold bookmarks, separators and other folders.
+ */
+[scriptable, uuid(9f42fe20-52de-4a55-8632-a459c7716aa0)]
+interface fuelIBookmarkFolder : nsISupports
+{
+  /**
+   * The id of the folder.
+   */
+  readonly attribute long long id;
+
+  /**
+   * The title of the folder.
+   */
+  attribute AString title;
+
+  /**
+   * The description of the folder.
+   */
+  attribute AString description;
+
+  /**
+   * The type of the folder.
+   * values: "folder"
+   */
+  readonly attribute AString type;
+
+  /**
+   * The parent folder of the folder.
+   */
+  attribute fuelIBookmarkFolder parent;
+
+  /**
+   * 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;
+
+  /**
+   * Array of all bookmarks, separators and folders contained
+   * in this folder.
+   */
+  readonly attribute nsIVariant children;
+
+  /**
+   * Adds a new child bookmark to this folder.
+   * @param   aTitle
+   *          The title of bookmark.
+   * @param   aURI
+   *          The uri of bookmark.
+   */
+  fuelIBookmark addBookmark(in AString aTitle, in nsIURI aURI);
+
+  /**
+   * Adds a new child separator to this folder.
+   */
+  fuelIBookmark addSeparator();
+
+  /**
+   * Adds a new child folder to this folder.
+   * @param   aTitle
+   *          The title of folder.
+   */
+  fuelIBookmarkFolder addFolder(in AString aTitle);
+
+  /**
+   * Removes the folder from the parent folder.
+   */
+  void remove();
+};
+
+
+/**
+ * Interface representing a browser window.
+ */
+[scriptable, uuid(207edb28-eb5e-424e-a862-b0e97C8de866)]
+interface fuelIWindow : nsISupports
+{
+  /**
+   * A collection of browser tabs within the browser window.
+   */
+  readonly attribute nsIVariant tabs;
+  
+  /**
+   * The currently-active tab within the browser window.
+   */
+  readonly attribute fuelIBrowserTab activeTab;
+  
+  /**
+   * Open a new browser tab, pointing to the specified URI.
+   * @param   aURI
+   *          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;
+};
+
+/**
+ * Interface representing a browser tab.
+ */
+[scriptable, uuid(3073ceff-777c-41ce-9ace-ab37268147c1)]
+interface fuelIBrowserTab : nsISupports
+{
+  /**
+   * The current uri of this tab.
+   */
+  readonly attribute nsIURI uri;
+  
+  /**
+   * The current index of this tab in the browser window.
+   */
+  readonly attribute PRInt32 index;
+
+  /**
+   * The browser window that is holding the tab.
+   */
+  readonly attribute fuelIWindow window;
+  
+  /**
+   * The content document of the browser tab.
+   */
+  readonly attribute nsIDOMHTMLDocument document;
+
+  /**
+   * The events object for the browser tab.
+   * supports: "load"
+   */
+  readonly attribute fuelIEvents events;
+  
+  /**
+   * Load a new URI into this browser tab.
+   * @param   aURI
+   *          The uri to load into the browser tab
+   */
+  void load(in nsIURI aURI);
+
+  /**
+   * Give focus to this browser tab, and bring it to the front.
+   */
+  void focus();
+  
+  /**
+   * Close the browser tab. This may not actually close the tab
+   * as script may abort the close operation.
+   */
+  void close();
+  
+  /**
+   * Moves this browser tab before another browser tab within the window.
+   * @param   aBefore
+   *          The tab before which the target tab will be moved
+   */
+  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
 {
   /**
    * The id of the application.
    */
@@ -382,9 +672,24 @@ interface fuelIApplication : nsISupports
    */
   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;
+  
+  /**
+   * The currently active browser window.
+   */
+  readonly attribute fuelIWindow activeWindow;
 }; 
--- a/browser/fuel/src/fuelApplication.js
+++ b/browser/fuel/src/fuelApplication.js
@@ -29,49 +29,46 @@
  * 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;
 
+const Ci = Components.interfaces;
+const Cc = Components.classes;
 
 //=================================================
 // 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);
+    .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(Components.interfaces.nsIWindowMediator);
+                              .getService(Ci.nsIWindowMediator);
     var console = wMediator.getMostRecentWindow("global:console");
     if (!console) {
       var wWatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
-                             .getService(Components.interfaces.nsIWindowWatcher);
+                             .getService(Ci.nsIWindowWatcher);
       wWatch.openWindow(null, "chrome://global/content/console.xul", "_blank",
                         "chrome,dialog=no,all", cmdLine);
     } else {
       // console was already open
       console.focus();
     }
   }
 };
@@ -144,37 +141,30 @@ Events.prototype = {
     });
     
     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);
+                          .getService(Ci.nsIPrefService);
 
   if (aBranch)
     this._prefs = this._prefs.getBranch(aBranch);
     
-  this._prefs.QueryInterface(nsIPrefBranch);
-  this._prefs.QueryInterface(nsIPrefBranch2);
+  this._prefs.QueryInterface(Ci.nsIPrefBranch);
+  this._prefs.QueryInterface(Ci.nsIPrefBranch2);
   
   this._prefs.addObserver(this._root, this, false);
   this._events = new Events();
   
   var self = this;
   gShutdown.push(function() { self._shutdown(); });
 }
 
@@ -220,50 +210,50 @@ PreferenceBranch.prototype = {
     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);
+    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 nsIPrefBranch2.PREF_STRING:
-        aValue = this._prefs.getComplexValue(aName, nsISupportsString).data;
+      case Ci.nsIPrefBranch2.PREF_STRING:
+        aValue = this._prefs.getComplexValue(aName, Ci.nsISupportsString).data;
         break;
-      case nsIPrefBranch2.PREF_BOOL:
+      case Ci.nsIPrefBranch2.PREF_BOOL:
         aValue = this._prefs.getBoolPref(aName);
         break;
-      case nsIPrefBranch2.PREF_INT:
+      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(nsISupportsString);
+                            .createInstance(Ci.nsISupportsString);
         str.data = aValue;
-        this._prefs.setComplexValue(aName, nsISupportsString, str);
+        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:
@@ -299,23 +289,23 @@ Preference.prototype = {
     return this._name;
   },
   
   get type() {
     var value = "";
     var type = this._prefs.getPrefType(name);
     
     switch (type) {
-      case nsIPrefBranch2.PREF_STRING:
+      case Ci.nsIPrefBranch2.PREF_STRING:
         value = "String";
         break;
-      case nsIPrefBranch2.PREF_BOOL:
+      case Ci.nsIPrefBranch2.PREF_BOOL:
         value = "Boolean";
         break;
-      case nsIPrefBranch2.PREF_INT:
+      case Ci.nsIPrefBranch2.PREF_INT:
         value = "Number";
         break;
     }
     
     return value;
   },
   
   get value() {
@@ -377,61 +367,57 @@ SessionStorage.prototype = {
   
   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);
+                     .getService(Ci.nsIObserverService);
   os.addObserver(this, "em-action-requested", false);
   
   var self = this;
   gShutdown.push(function(){ self._shutdown(); });
 }
 
 //=================================================
-// Extensions implementation
+// 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(Components.interfaces.nsIObserverService);
+                       .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 ((aData == "item-uninstalled") &&
-        (aSubject instanceof nsIUpdateItem) &&
+        (aSubject instanceof Ci.nsIUpdateItem) &&
         (aSubject.id == this._item.id))
     {
       this._events.dispatch("uninstall", this._item.id);
     }
   },
 
   get id() {
     return this._item.id;
@@ -462,17 +448,17 @@ Extension.prototype = {
   }
 };
 
 
 //=================================================
 // Extensions constructor
 function Extensions() {
   this._extmgr = Components.classes["@mozilla.org/extensions/manager;1"]
-                           .getService(Components.interfaces.nsIExtensionManager);
+                           .getService(Ci.nsIExtensionManager);
                              
   var self = this;
   gShutdown.push(function() { self._shutdown(); });
 }
 
 //=================================================
 // Extensions implementation
 Extensions.prototype = {
@@ -487,17 +473,17 @@ Extensions.prototype = {
   // 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, {});
+    var items = this._extmgr.getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION, {});
     
     for (var i = 0; i < items.length; i++) {
       retVal.push(new Extension(items[i]));
     }
 
     return retVal;
   },
   
@@ -508,34 +494,635 @@ Extensions.prototype = {
     return !!(this._extmgr.getItemForID(aId).type);
   },
   
   get : function exts_get(aId) {
     return this.has(aId) ? new Extension(this._extmgr.getItemForID(aId)) : null;
   }
 };
 
+//=================================================
+// 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);
+    }
+    return this._bookmarks;
+  },
+
+  _livemarks : null,
+  get livemarks() {
+    if (!this._livemarks) {
+      this._livemarks = Cc["@mozilla.org/browser/livemark-service;2"].
+                        getService(Ci.nsILivemarkService);
+    }
+    return this._livemarks;
+  },
+
+  _annotations : null,
+  get annotations() {
+    if (!this._annotations) {
+      this._annotations = Cc["@mozilla.org/browser/annotation-service;1"].
+                          getService(Ci.nsIAnnotationService);
+    }
+    return this._annotations;
+  },
+  
+  _history : null,
+  get history() {
+    if (!this._history) {
+      this._history = Cc["@mozilla.org/browser/nav-history-service;1"].
+                      getService(Ci.nsINavHistoryService);
+    }
+    return this._history;
+  },
+  
+  _windowMediator : null,
+  get windowMediator() {
+    if (!this._windowMediator) {
+      this._windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
+                             getService(Ci.nsIWindowMediator);
+    }
+    return this._windowMediator;
+  },
+  
+  makeURI : function(aSpec) {
+    if (!aSpec)
+      return null;
+    var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+    return ios.newURI(aSpec, null, null);
+  },
+  
+  free : function() {
+    this._bookmarks = null;
+    this._livemarks = null;
+    this._annotations = null;
+    this._history = null;
+    this._windowMediator = null;
+  }
+};
+
+
+//=================================================
+// Window implementation
+function Window(aWindow) {
+  this._window = aWindow;
+  this._tabbrowser = aWindow.getBrowser();
+  this._events = new Events();
+  this._cleanup = {};
+  
+  this._watch("TabOpen");
+  this._watch("TabMove");
+  this._watch("TabClose");
+  this._watch("TabSelect");
+                                 
+  var self = this;
+  gShutdown.push(function() { self._shutdown(); });
+}
+
+Window.prototype = {
+  get events() {
+    return this._events;
+  },
+
+  /*
+   * Helper used to setup event handlers on the XBL element. Note that the events
+   * are actually dispatched to tabs, so we capture them.
+   */
+  _watch : function(aType) {
+    var self = this;
+    this._tabbrowser.addEventListener(aType, 
+      this._cleanup[aType] = function(e){ self._event(e); },
+      true);
+  },
+  
+  /*
+   * Helper event callback used to redirect events made on the XBL element
+   */
+  _event : function(aEvent) {
+    this._events.dispatch(aEvent.type, "");
+  },
+  
+  get tabs() {
+    var tabs = [];
+    var browsers = this._tabbrowser.browsers;
+    
+    for (var i=0; i<browsers.length; i++)
+      tabs.push(new BrowserTab(this._window, browsers[i]));
+    
+    return tabs;
+  },
+  
+  get activeTab() {
+    return new BrowserTab(this._window, this._tabbrowser.selectedBrowser);
+  },
+  
+  open : function(aURI) {
+    return new BrowserTab(this._window, this._tabbrowser.addTab(aURI.spec).linkedBrowser);
+  },
+  
+  _shutdown : function() {
+    for (var type in this._cleanup)
+      this._tabbrowser.removeEventListener(type, this._cleanup[type]);
+    this._cleanup = null;
+
+    this._window = null;
+    this._tabbrowser = null;
+    this._events = null;
+  }
+};
+
+
+//=================================================
+// BrowserTab implementation
+function BrowserTab(aWindow, aBrowser) {
+  this._window = aWindow;
+  this._tabbrowser = aWindow.getBrowser();
+  this._browser = aBrowser;
+  this._events = new Events();
+  this._cleanup = {};
+  
+  this._watch("load");
+                                 
+  var self = this;
+  gShutdown.push(function() { self._shutdown(); });
+}
+
+BrowserTab.prototype = {
+  get uri() {
+    return this._browser.currentURI;
+  },
+  
+  get index() {
+    var tabs = this._tabbrowser.mTabs;
+    for (var i=0; i<tabs.length; i++) {
+      if (tabs[i].linkedBrowser == this._browser)
+        return i;
+    }
+    return -1;
+  },
+
+  get events() {
+    return this._events;
+  },
+  
+  get window() {
+    return this._window;
+  },
+  
+  get document() {
+    return this._browser.contentDocument;
+  },
+  
+  /*
+   * Helper used to setup event handlers on the XBL element
+   */
+  _watch : function(aType) {
+    var self = this;
+    this._browser.addEventListener(aType,
+      this._cleanup[aType] = function(e){ self._event(e); },
+      true);
+  },
+  
+  /*
+   * Helper event callback used to redirect events made on the XBL element
+   */
+  _event : function(aEvent) {
+    if (aEvent.type == "load" && (!aEvent.originalTarget instanceof Ci.nsIDOMHTMLDocument ||
+      aEvent.originalTarget.defaultView.frameElement))
+      return;
+      
+    this._events.dispatch(aEvent.type, "");
+  },
+  
+  /*
+   * Helper used to determine the index offset of the browsertab
+   */
+  _getTab : function() {
+    var tabs = this._tabbrowser.mTabs;
+    return tabs[this.index] || null;
+  },
+  
+  load : function(aURI) {
+    this._browser.loadURI(aURI.spec, null, null);
+  },
+  
+  focus : function() {
+    this._tabbrowser.selectedTab = this._getTab();
+    this._tabbrowser.focus();
+  },
+  
+  close : function() {
+    this._tabbrowser.removeTab(this._getTab());
+  },
+  
+  moveBefore : function(aBefore) {
+    this._tabbrowser.moveTabTo(this._getTab(), aBefore.index);
+  },
+  
+  moveToEnd : function() {
+    this._tabbrowser.moveTabTo(this._getTab(), this._tabbrowser.browsers.length);
+  },
+  
+  _shutdown : function() {
+    for (var type in this._cleanup)
+      this._browser.removeEventListener(type, this._cleanup[type]);
+    this._cleanup = null;
+    
+    this._window = null;
+    this._tabbrowser = null;
+    this._browser = null;
+    this._events = null;
+  }
+};
+
+
+//=================================================
+// Annotations implementation
+function Annotations(aId) {
+  this._id = aId;
+}
+
+Annotations.prototype = {
+  get names() {
+    return Utilities.annotations.getItemAnnotationNames(this._id, {});
+  },
+  
+  has : function(aName) {
+    return Utilities.annotations.itemHasAnnotation(this._id, aName);
+  },
+  
+  get : function(aName) {
+    var value = null;
+    var type = Utilities.annotations.getItemAnnotationType(this._id, aName);
+    switch (type) {
+      case Ci.nsIAnnotationService.TYPE_INT32:
+        value = Utilities.annotations.getItemAnnotationInt32(this._id, aName);
+        break;
+      case Ci.nsIAnnotationService.TYPE_INT64:
+        value = Utilities.annotations.getItemAnnotationInt64(this._id, aName);
+        break;
+      case Ci.nsIAnnotationService.TYPE_DOUBLE:
+        value = Utilities.annotations.getItemAnnotationDouble(this._id, aName);
+        break;
+      case Ci.nsIAnnotationService.TYPE_STRING:
+        value = Utilities.annotations.getItemAnnotationString(this._id, aName);
+        break;
+      default:
+        throw("Unknown annotation type specified.");
+    }
+    return value;
+  },
+  
+  set : function(aName, aValue, aExpiration) {
+    var type = aValue != null ? aValue.constructor.name : "";
+    
+    switch (type) {
+      case "String":
+        Utilities.annotations.setItemAnnotationString(this._id, aName, aValue, 0, aExpiration);
+        break;
+      case "Boolean":
+        Utilities.annotations.setItemAnnotationInt32(this._id, aName, aValue, 0, aExpiration);
+        break;
+      case "Number":
+        Utilities.annotations.setItemAnnotationDouble(this._id, aName, aValue, 0, aExpiration);
+        break;
+      default:
+        throw("Unknown annotation value specified.");
+    }
+  },
+    
+  remove : function(aName) {
+    if (aName)
+      Utilities.annotations.removeItemAnnotation(this._id, aName);
+  }
+};
+
+
+//=================================================
+// Bookmark implementation
+function Bookmark(aId, aParent, aType) {
+  this._id = aId;
+  this._parent = aParent;
+  this._type = aType || "bookmark";
+  this._annotations = new Annotations(this._id);
+  this._events = new Events();
+
+  Utilities.bookmarks.addObserver(this, false);  
+                                 
+  var self = this;
+  gShutdown.push(function() { self._shutdown(); });
+}
+
+Bookmark.prototype = {
+  _shutdown : function() {
+    this._annotations = null;
+    this._events = null;
+    
+    Utilities.bookmarks.removeObserver(this);  
+  },
+  
+  get id() {
+    return this._id;
+  },
+  
+  get title() {
+    return Utilities.bookmarks.getItemTitle(this._id);
+  },
+
+  set title(aTitle) {
+    Utilities.bookmarks.setItemTitle(this._id, aTitle);
+  },
+
+  get uri() {
+    return Utilities.bookmarks.getBookmarkURI(this._id);
+  },
+
+  set uri(aURI) {
+    return Utilities.bookmarks.changeBookmarkURI(this._id, aURI);
+  },
+
+  get description() {
+    return this._annotations.get("bookmarkProperties/description");
+  },
+
+  set description(aDesc) {
+    this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
+  },
+
+  get keyword() {
+    return Utilities.bookmarks.getKeywordForBookmark(this._id);
+  },
+
+  set keyword(aKeyword) {
+    Utilities.bookmarks.setKeywordForBookmark(this._id, aKeyword);
+  },
+
+  get type() {
+    return this._type;
+  },
+
+  get parent() {
+    return this._parent;
+  },
+  
+  set parent(aFolder) {
+    Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
+    // this._parent is updated in onItemMoved
+  },
+  
+  get annotations() {
+    return this._annotations;
+  },
+  
+  get events() {
+    return this._events;
+  },
+  
+  remove : function() {
+    Utilities.bookmarks.removeItem(this._id);
+  },
+  
+  // observer
+  onBeginUpdateBatch : function() {
+  },
+
+  onEndUpdateBatch : function() {
+  },
+
+  onItemAdded : function(aId, aFolder, aIndex) {
+    // bookmark object doesn't exist at this point
+  },
+
+  onItemRemoved : function(aId, aFolder, aIndex) {
+    if (this._id == aId)
+      this._events.dispatch("remove", aId);
+  },
+
+  onItemChanged : function(aId, aProperty, aIsAnnotationProperty, aValue) {
+    if (this._id == aId)
+      this._events.dispatch("change", aProperty);
+  },
+
+  onItemVisited: function(aId, aVisitID, aTime) {
+  },
+
+  onItemMoved: function(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
+    if (this._id == aId) {
+      this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));    
+      this._events.dispatch("move", aId);
+    }
+  },
+
+  QueryInterface: function(aIID) {
+    if (aIID.equals(Ci.fuelIBookmark) ||
+        aIID.equals(Ci.nsINavBookmarkObserver) ||
+        aIID.equals(Ci.nsISupports)) {
+      return this;
+    }
+    throw Component.result.NS_ERROR_NO_INTERFACE;
+  }
+}; 
+
+
+//=================================================
+// BookmarkFolder implementation
+function BookmarkFolder(aId, aParent) {
+  this._id = aId;
+  if (this._id == null)
+    this._id = Utilities.bookmarks.bookmarksRoot;
+  
+  this._parent = aParent;
+                                 
+  this._annotations = new Annotations(this._id);
+  this._events = new Events();
+
+  Utilities.bookmarks.addObserver(this, false);  
+
+  var self = this;
+  gShutdown.push(function() { self._shutdown(); });
+}
+
+BookmarkFolder.prototype = {
+  _shutdown : function() {
+    this._annotations = null;
+    this._events = null;
+    
+    Utilities.bookmarks.removeObserver(this);  
+  },
+  
+  get id() {
+    return this._id;
+  },
+  
+  get title() {
+    return Utilities.bookmarks.getItemTitle(this._id);
+  },
+
+  set title(aTitle) {
+    Utilities.bookmarks.setItemTitle(this._id, aTitle);
+  },
+
+  get description() {
+    return this._annotations.get("bookmarkProperties/description");
+  },
+
+  set description(aDesc) {
+    this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
+  },
+
+  get type() {
+    return "folder";
+  },
+
+  get parent() {
+    return this._parent;
+  },
+  
+  set parent(aFolder) {
+    Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
+    // this._parent is updated in onItemMoved
+  },
+
+  get annotations() {
+    return this._annotations;
+  },
+  
+  get events() {
+    return this._events;
+  },
+  
+  get children() {
+    var items = [];
+    
+    var options = Utilities.history.getNewQueryOptions();
+    var query = Utilities.history.getNewQuery();
+    query.setFolders([this._id], 1);
+    var result = Utilities.history.executeQuery(query, options);
+    var rootNode = result.root;
+    rootNode.containerOpen = true;
+    var cc = rootNode.childCount;
+    for (var i=0; i<cc; ++i) {
+      var node = rootNode.getChild(i);
+      if (node.type == node.RESULT_TYPE_FOLDER) {
+        var folder = new BookmarkFolder(node.itemId, this._id);
+        items.push(folder);
+      }
+      else if (node.type == node.RESULT_TYPE_SEPARATOR) {
+        var separator = new Bookmark(node.itemId, this._id, "separator");
+        items.push(separator);
+      }
+      else {
+        var bookmark = new Bookmark(node.itemId, this._id, "bookmark");
+        items.push(bookmark);
+      }
+    }
+    rootNode.containerOpen = false;
+
+    return items;
+  },
+  
+  addBookmark : function(aTitle, aUri) {
+    var newBookmarkID = Utilities.bookmarks.insertBookmark(this._id, aUri, Utilities.bookmarks.DEFAULT_INDEX, aTitle);
+    var newBookmark = new Bookmark(newBookmarkID, this, "bookmark");
+    return newBookmark;
+  },
+  
+  addSeparator : function() {
+    var newBookmarkID = Utilities.bookmarks.insertSeparator(this._id, Utilities.bookmarks.DEFAULT_INDEX);
+    var newBookmark = new Bookmark(newBookmarkID, this, "separator");
+    return newBookmark;
+  },
+  
+  addFolder : function(aTitle) {
+    var newFolderID = Utilities.bookmarks.createFolder(this._id, aTitle, Utilities.bookmarks.DEFAULT_INDEX);
+    var newFolder = new BookmarkFolder(newFolderID, this);
+    return newFolder;
+  },
+  
+  remove : function() {
+    Utilities.bookmarks.removeFolder(this._id);
+  },
+  
+  // observer
+  onBeginUpdateBatch : function() {
+  },
+
+  onEndUpdateBatch : function() {
+  },
+
+  onItemAdded : function(aId, aFolder, aIndex) {
+    // handle root folder events
+    if (!this._parent)
+      this._events.dispatch("add", aId);
+    
+    // handle this folder events  
+    if (this._id == aFolder)
+      this._events.dispatch("addchild", aId);
+  },
+
+  onItemRemoved : function(aId, aFolder, aIndex) {
+    // handle root folder events
+    if (!this._parent || this._id == aId)
+      this._events.dispatch("remove", aId);
+
+    // handle this folder events      
+    if (this._id == aFolder)
+      this._events.dispatch("removechild", aId);
+  },
+
+  onItemChanged : function(aId, aProperty, aIsAnnotationProperty, aValue) {
+    // handle root folder and this folder events
+    if (!this._parent || this._id == aId)
+      this._events.dispatch("change", aProperty);
+  },
+
+  onItemVisited: function(aId, aVisitID, aTime) {
+  },
+
+  onItemMoved: function(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
+    // handle this folder event, root folder cannot be moved
+    if (this._id == aId) {
+      this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));    
+      this._events.dispatch("move", aId);
+    }
+  },
+
+  QueryInterface: function(aIID) {
+    if (aIID.equals(Ci.fuelIBookmarkFolder) ||
+        aIID.equals(Ci.nsINavBookmarkObserver) ||
+        aIID.equals(Ci.nsISupports)) {
+      return this;
+    }
+    throw Component.result.NS_ERROR_NO_INTERFACE;
+  }
+}; 
+
 
 const CLASS_ID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66");
 const CLASS_NAME = "Application wrapper";
 const CONTRACT_ID = "@mozilla.org/fuel/application;1";
 
 //=================================================
 // Application constructor
 function Application() {
   this._console = null;
   this._prefs = null;
   this._storage = null;
   this._events = null;
+  this._bookmarks = null;
   
   this._info = Components.classes["@mozilla.org/xre/app-info;1"]
-                     .getService(Components.interfaces.nsIXULAppInfo);
+                     .getService(Ci.nsIXULAppInfo);
     
   var os = Components.classes["@mozilla.org/observer-service;1"]
-                     .getService(Components.interfaces.nsIObserverService);
+                     .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);
 }
 
@@ -573,59 +1160,62 @@ Application.prototype = {
 
       // 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);
+                         .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
   classDescription : "Application",
   classID : CLASS_ID,
   contractID : CONTRACT_ID,
-  flags : nsIClassInfo.SINGLETON,
-  implementationLanguage : Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
+  flags : Ci.nsIClassInfo.SINGLETON,
+  implementationLanguage : Ci.nsIProgrammingLanguage.JAVASCRIPT,
 
   getInterfaces : function app_gi(aCount) {
-    var interfaces = [fuelIApplication, nsIObserver, nsIClassInfo];
+    var interfaces = [Ci.fuelIApplication, Ci.nsIObserver, Ci.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))
+    if (aIID.equals(Ci.fuelIApplication) ||
+        aIID.equals(Ci.nsIObserver) ||
+        aIID.equals(Ci.nsIClassInfo) ||
+        aIID.equals(Ci.nsISupports))
     {
       return this;
     }
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
   
   get console() {
     if (this._console == null)
@@ -652,65 +1242,85 @@ Application.prototype = {
     return this._extensions;
   },
 
   get events() {
     if (this._events == null)
         this._events = new Events();
 
     return this._events;
+  },
+
+  get bookmarks() {
+    if (this._bookmarks == null)
+      this._bookmarks = new BookmarkFolder(null, null);
+
+    return this._bookmarks;
+  },
+  
+  get windows() {
+    var win = [];
+    var enum = Utilities.windowMediator.getEnumerator("navigator:browser");
+    
+    while (enum.hasMoreElements())
+      win.push(new Window(enum.getNext()));
+
+    return win;
+  },
+  
+  get activeWindow() {
+    return new Window(Utilities.windowMediator.getMostRecentWindow("navigator:browser"));
   }
-}
+};
 
 //=================================================
 // Factory - Treat Application as a singleton
 var gSingleton = null;
 var ApplicationFactory = {
-  
   createInstance: function af_ci(aOuter, aIID) {
     if (aOuter != null)
       throw Components.results.NS_ERROR_NO_AGGREGATION;
       
     if (gSingleton == null) {
       gSingleton = new Application();
     }
 
     return gSingleton.QueryInterface(aIID);
   }
 };
 
 //=================================================
 // Module
 var ApplicationModule = {
   registerSelf: function am_rs(aCompMgr, aFileSpec, aLocation, aType) {
-    aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+    aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
     aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
     
     var categoryManager = Components.classes["@mozilla.org/categorymanager;1"]
-                                    .getService(Components.interfaces.nsICategoryManager);
+                                    .getService(Ci.nsICategoryManager);
     // make Application a startup observer
     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 = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
     aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);        
 
     // cleanup categories
     var categoryManager = Components.classes["@mozilla.org/categorymanager;1"]
-                                    .getService(Components.interfaces.nsICategoryManager);
+                                    .getService(Ci.nsICategoryManager);
     categoryManager.deleteCategoryEntry("app-startup", "service," + CONTRACT_ID, true);
     categoryManager.deleteCategoryEntry("JavaScript global property", CONTRACT_ID, true);
   },
   
   getClassObject: function am_gco(aCompMgr, aCID, aIID) {
-    if (!aIID.equals(Components.interfaces.nsIFactory))
+    if (!aIID.equals(Ci.nsIFactory))
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
 
     if (aCID.equals(CLASS_ID))
       return ApplicationFactory;
 
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
 
--- a/browser/fuel/test/Makefile.in
+++ b/browser/fuel/test/Makefile.in
@@ -39,16 +39,20 @@ 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 \
+_CHROME_FILES =	test_Application.xul \
+		test_ApplicationPrefs.xul \
+		test_ApplicationStorage.xul \
+		test_Extensions.xul \
+		test_Bookmarks.xul \
+		test_Browser.xul \
+		test_ContentA.html \
+		test_ContentB.html \
 		$(NULL)
 
-libs::	$(_TEST_FILES)
-	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+libs::	$(_CHROME_FILES)
+	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_Application.xul
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window title="Testing Application"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  xmlns:html="http://www.w3.org/1999/xhtml">
+  
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml" />
+
+<script type="application/javascript">
+<![CDATA[
+
+test_Application();
+
+function test_Application() {
+  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>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_ApplicationPrefs.xul
@@ -0,0 +1,181 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window title="Testing Application Prefs"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  xmlns:html="http://www.w3.org/1999/xhtml">
+  
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml" />
+
+<script type="application/javascript">
+<![CDATA[
+
+// 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() {
+  // 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) {
+  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) {
+  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>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_ApplicationStorage.xul
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window title="Testing Application Storage"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  xmlns:html="http://www.w3.org/1999/xhtml">
+  
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml" />
+
+<script type="application/javascript">
+<![CDATA[
+
+test_Storage();
+
+function test_Storage() {
+  // 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) {
+  is(evt.data, "fuel-test", "Check 'Application.storage.set' fired a change event");
+  Application.storage.events.removeListener("change", onStorageChange);
+  SimpleTest.finish();
+}
+
+]]>
+</script>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_Bookmarks.xul
@@ -0,0 +1,212 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window title="Testing Bookmarks"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  xmlns:html="http://www.w3.org/1999/xhtml">
+  
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml" />
+
+<script type="application/javascript">
+<![CDATA[
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+var gLastFolderAction = "";
+var gLastBookmarkAction = "";
+
+test_Bookmarks();
+
+function url(spec) {
+  var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+  return ios.newURI(spec, null, null);
+}
+
+function test_Bookmarks() {
+  var root = Application.bookmarks;
+  ok(root, "Check access to bookmark root");
+  ok(!root.parent, "Check root parent (should be null)");
+
+  var rootKidCount = root.children.length;
+  
+  // test adding folders
+  var testFolder = root.addFolder("FUEL");
+  ok(testFolder, "Check folder creation");
+  is(testFolder.type, "folder", "Check 'folder.type' after creation");
+  ok(testFolder.parent, "Check parent after folder creation");
+
+  rootKidCount++;
+  is(root.children.length, rootKidCount, "Check root folder child count after adding a child folder");
+
+  // test modifying a folder
+  testFolder.events.addListener("change", onFolderChange);
+  testFolder.description = "FUEL folder";
+  is(testFolder.description, "FUEL folder", "Check setting 'folder.description'");
+  is(gLastFolderAction, "bookmarkProperties/description", "Check event handler for setting 'folder.description'");
+
+  testFolder.title = "fuel-is-cool";
+  is(testFolder.title, "fuel-is-cool", "Check setting 'folder.title'");
+  is(gLastFolderAction, "title", "Check event handler for setting 'folder.title'");
+
+  testFolder.annotations.set("testing/folder", "annotate-this", 0);
+  ok(testFolder.annotations.has("testing/folder"), "Checking existence of added annotation");
+  is(gLastFolderAction, "testing/folder", "Check event handler for setting annotation");
+  gLastFolderAction = "";
+  is(testFolder.annotations.get("testing/folder"), "annotate-this", "Checking existence of added annotation");
+  testFolder.annotations.remove("testing/folder");
+  ok(!testFolder.annotations.has("testing/folder"), "Checking existence of removed annotation");
+  is(gLastFolderAction, "testing/folder", "Check event handler for removing annotation");
+  
+  testFolder.events.addListener("addchild", onFolderAddChild);
+  testFolder.events.addListener("removechild", onFolderRemoveChild);
+
+  // test adding a bookmark
+  var testBookmark = testFolder.addBookmark("Mozilla", url("http://www.mozilla.com/"));
+  ok(testBookmark, "Check bookmark creation");
+  ok(testBookmark.parent, "Check parent after bookmark creation");
+  is(gLastFolderAction, "addchild", "Check event handler for adding a child to a folder");
+  is(testBookmark.type, "bookmark", "Check 'bookmark.type' after creation");
+  is(testBookmark.title, "Mozilla", "Check 'bookmark.title' after creation");
+  is(testBookmark.uri.spec, "http://www.mozilla.com/", "Check 'bookmark.uri' after creation");
+  
+  is(testFolder.children.length, 1, "Check test folder child count after adding a child bookmark");
+
+  // test modifying a bookmark
+  testBookmark.events.addListener("change", onBookmarkChange);
+  testBookmark.description = "mozcorp";
+  is(testBookmark.description, "mozcorp", "Check setting 'bookmark.description'");
+  is(gLastBookmarkAction, "bookmarkProperties/description", "Check event handler for setting 'bookmark.description'");
+
+  testBookmark.keyword = "moz"
+  is(testBookmark.keyword, "moz", "Check setting 'bookmark.keyword'");
+  is(gLastBookmarkAction, "keyword", "Check event handler for setting 'bookmark.keyword'");
+  
+  testBookmark.title = "MozCorp"
+  is(testBookmark.title, "MozCorp", "Check setting 'bookmark.title'");
+  is(gLastBookmarkAction, "title", "Check event handler for setting 'bookmark.title'");
+
+  testBookmark.uri = url("http://www.mozilla.org/");
+  is(testBookmark.uri.spec, "http://www.mozilla.org/", "Check setting 'bookmark.uri'");
+  is(gLastBookmarkAction, "uri", "Check event handler for setting 'bookmark.uri'");
+
+  // test adding and removing a bookmark annotation
+  testBookmark.annotations.set("testing/bookmark", "annotate-this", 0);
+  ok(testBookmark.annotations.has("testing/bookmark"), "Checking existence of added annotation");
+  is(gLastBookmarkAction, "testing/bookmark", "Check event handler for setting annotation");
+  gLastBookmarkAction = "";
+  is(testBookmark.annotations.get("testing/bookmark"), "annotate-this", "Checking existence of added annotation");
+  testBookmark.annotations.remove("testing/bookmark");
+  ok(!testBookmark.annotations.has("testing/bookmark"), "Checking existence of removed annotation");
+  is(gLastBookmarkAction, "testing/bookmark", "Check event handler for removing annotation");
+
+  // quick annotation type tests
+  testBookmark.annotations.set("testing/bookmark/string", "annotate-this", 0);
+  ok(testBookmark.annotations.has("testing/bookmark/string"), "Checking existence of added string annotation");
+  is(testBookmark.annotations.get("testing/bookmark/string"), "annotate-this", "Checking value of added string annotation");
+  is(gLastBookmarkAction, "testing/bookmark/string", "Check event handler for setting annotation");
+  gLastBookmarkAction = "";
+  testBookmark.annotations.set("testing/bookmark/int", 100, 0);
+  ok(testBookmark.annotations.has("testing/bookmark/int"), "Checking existence of added integer annotation");
+  is(testBookmark.annotations.get("testing/bookmark/int"), 100, "Checking value of added integer annotation");
+  is(gLastBookmarkAction, "testing/bookmark/int", "Check event handler for setting annotation");
+  gLastBookmarkAction = "";
+  testBookmark.annotations.set("testing/bookmark/double", 3.333, 0);
+  ok(testBookmark.annotations.has("testing/bookmark/double"), "Checking existence of added double annotation");
+  is(testBookmark.annotations.get("testing/bookmark/double"), 3.333, "Checking value of added double annotation");
+  is(gLastBookmarkAction, "testing/bookmark/double", "Check event handler for setting annotation");
+  gLastBookmarkAction = "";
+  
+  // test names array - NOTE: "bookmarkProperties/description" is an annotation too
+  var names = testBookmark.annotations.names;
+  is(names[1], "testing/bookmark/string", "Checking contents of annotation names array");
+  is(names.length, 4, "Checking the annotation names array after adding 3 annotations");
+
+  // test adding a separator
+  var testSeparator = testFolder.addSeparator();
+  ok(testSeparator, "Check bookmark creation");
+  ok(testSeparator.parent, "Check parent after separator creation");
+  is(gLastFolderAction, "addchild", "Check event handler for adding a child separator to a folder");
+  is(testSeparator.type, "separator", "Check 'bookmark.type' after separator creation");
+
+  is(testFolder.children.length, 2, "Check test folder child count after adding a child separator");
+
+  // test removing separator
+  testSeparator.events.addListener("remove", onBookmarkRemove);
+  testSeparator.remove();
+  is(gLastBookmarkAction, "remove", "Check event handler for removing separator");
+  is(gLastFolderAction, "removechild", "Check event handler for removing a child separator from a folder");
+  is(testFolder.children.length, 1, "Check test folder child count after removing a child separator");
+
+  // test removing bookmark
+  testBookmark.events.addListener("remove", onBookmarkRemove);
+  testBookmark.remove();
+  is(gLastBookmarkAction, "remove", "Check event handler for removing bookmark");
+  is(gLastFolderAction, "removechild", "Check event handler for removing a child from a folder");
+  is(testFolder.children.length, 0, "Check test folder child count after removing a child bookmark");
+  
+  // test removing a folder
+  testFolder.events.addListener("remove", onFolderRemove);
+  testFolder.remove();
+  is(gLastFolderAction, "remove", "Check event handler for removing child folder");
+  rootKidCount--;
+  is(root.children.length, rootKidCount, "Check root folder child count after removing a child folder");
+  
+  // test moving between folders
+  var testFolderA = root.addFolder("folder-a");
+  var testFolderB = root.addFolder("folder-b");
+  
+  var testMove = testFolderA.addBookmark("Mozilla", url("http://www.mozilla.com/"));
+  testMove.events.addListener("move", onBookmarkMove);
+  is(testMove.parent.title, "folder-a", "Checking for new parent before moving bookmark");
+
+  testMove.parent = testFolderB;
+  is(testMove.parent.title, "folder-b", "Checking for new parent after moving bookmark");
+  is(gLastBookmarkAction, "move", "Checking for event handler after moving bookmark");
+
+  // test moving a folder
+  testFolderA.events.addListener("move", onFolderMove);
+  testFolderA.parent = testFolderB;
+  is(testFolderA.parent.title, "folder-b", "Checking for new parent after moving folder");
+  is(gLastFolderAction, "move", "Checking for event handler after moving folder");
+}
+
+function onFolderChange(evt) {
+  gLastFolderAction = evt.data;
+}
+
+function onFolderRemove(evt) {
+  gLastFolderAction = evt.type;
+}
+
+function onFolderAddChild(evt) {
+  gLastFolderAction = evt.type;
+}
+
+function onFolderRemoveChild(evt) {
+  gLastFolderAction = evt.type;
+}
+
+function onFolderMove(evt) {
+  gLastFolderAction = evt.type;
+}
+
+function onBookmarkChange(evt) {
+  gLastBookmarkAction = evt.data;
+}
+
+function onBookmarkRemove(evt) {
+  gLastBookmarkAction = evt.type;
+}
+
+function onBookmarkMove(evt) {
+  gLastBookmarkAction = evt.type;
+}
+
+]]>
+</script>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_Browser.xul
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window title="Testing Browser"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  xmlns:html="http://www.w3.org/1999/xhtml">
+  
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml" />
+
+<script type="application/javascript">
+<![CDATA[
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+test_Browser();
+
+function url(spec) {
+  var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+  return ios.newURI(spec, null, null);
+}
+
+var gTabOpenCount = 0;
+var gTabCloseCount = 0;
+var gTabMoveCount = 0;
+
+function test_Browser() {
+  var windows = Application.windows;
+  ok(windows, "Check access to browser windows");
+  ok(windows.length, "There should be at least one browser window open");
+  
+  var activeWin = Application.activeWindow;
+  activeWin.events.addListener("TabOpen", onTabOpen);
+  activeWin.events.addListener("TabClose", onTabClose);
+  activeWin.events.addListener("TabMove", onTabMove);
+  
+  var pageA = activeWin.open(url("chrome://mochikit/content/chrome/browser/fuel/test/test_ContentA.html"));
+  var pageB = activeWin.open(url("chrome://mochikit/content/chrome/browser/fuel/test/test_ContentB.html"));
+  pageB.focus();
+
+  is(activeWin.tabs.length, 3, "Checking length of 'Browser.tabs' after opening 2 additional tabs");
+  is(activeWin.activeTab.index, pageB.index, "Checking 'Browser.activeTab' after setting focus");
+  
+  SimpleTest.waitForExplicitFinish();
+  setTimeout(afterOpen, 1000);
+  
+  // need to wait for the url's to be refreshed during the load
+  function afterOpen() {
+    is(pageA.uri.spec, "chrome://mochikit/content/chrome/browser/fuel/test/test_ContentA.html", "Checking 'BrowserTab.uri' after opening");  
+    is(pageB.uri.spec, "chrome://mochikit/content/chrome/browser/fuel/test/test_ContentB.html", "Checking 'BrowserTab.uri' after opening");  
+
+    // check event
+    todo_is(gTabOpenCount, 2, "Checking event handler for tab open");
+    
+    // test document access
+    var test1 = pageA.document.getElementById("test1");
+    ok(test1, "Checking existence of element in content DOM");
+    is(test1.innerHTML, "A", "Checking content of element in content DOM");
+    
+    // test moving tab
+    pageA.moveToEnd();
+    is(pageA.index, 2, "Checking index after moving tab");
+    
+    // check event
+    is(gTabMoveCount, 1, "Checking event handler for tab move");
+
+    // test loading new content into a tab
+    // the event will be checked in afterClose
+    pageA.events.addListener("load", onPageLoad);
+    pageA.load(pageB.uri);
+  }
+  
+  function onPageLoad(event) {
+    is(pageA.uri.spec, "chrome://mochikit/content/chrome/browser/fuel/test/test_ContentB.html", "Checking 'BrowserTab.uri' after loading new content");  
+
+    // start testing closing tabs
+    pageA.close();
+    pageB.close();
+    setTimeout(afterClose, 1000);
+  }
+
+  function afterClose() {
+    // check event
+    is(gTabCloseCount, 2, "Checking event handler for tab close");
+    
+    is(activeWin.tabs.length, 1, "Checking length of 'Browser.tabs' after closing 2 tabs");
+    
+    SimpleTest.finish();
+  }
+}
+
+function onTabOpen(event) {
+  gTabOpenCount++;
+}
+
+function onTabClose(event) {
+  gTabCloseCount++;
+}
+
+function onTabMove(event) {
+  gTabMoveCount++;
+}
+
+]]>
+</script>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_ContentA.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" >
+<head>
+    <title>Content Page A</title>
+</head>
+<body>
+<h1>Content Page A</h1>
+<div id="desc">This is a simple content page used for testing FUEL Browser API</div>
+<div id="test1">A</div>
+<div id="test2">B</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_ContentB.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" >
+<head>
+    <title>Content Page B</title>
+</head>
+<body>
+<h1>Content Page B</h1>
+<div id="desc">This is a simple content page used for testing FUEL Browser API</div>
+<div id="test1">1</div>
+<div id="test2">2</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/fuel/test/test_Extensions.xul
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window title="Testing Extensions"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  xmlns:html="http://www.w3.org/1999/xhtml">
+  
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml" />
+
+<script type="application/javascript">
+<![CDATA[
+
+// 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() {
+  // 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>
+
+</window>