home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 December / PCWorld_2007-12_cd.bin / audio-video / songbird / Songbird_0.3_windows-i686.exe / components / fuelApplication.js < prev    next >
Text File  |  2007-10-27  |  34KB  |  1,299 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is FUEL.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla Corporation.
  17.  * Portions created by the Initial Developer are Copyright (C) 2006
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *  Mark Finkle <mfinkle@mozilla.com> (Original Author)
  22.  *  John Resig  <jresig@mozilla.com> (Original Author)
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const Ci = Components.interfaces;
  39. const Cc = Components.classes;
  40.  
  41. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  42.  
  43. //=================================================
  44. // Shutdown - used to store cleanup functions which will
  45. //            be called on Application shutdown
  46. var gShutdown = [];
  47.  
  48. //=================================================
  49. // Console constructor
  50. function Console() {
  51.   this._console = Components.classes["@mozilla.org/consoleservice;1"]
  52.     .getService(Ci.nsIConsoleService);
  53. }
  54.  
  55. //=================================================
  56. // Console implementation
  57. Console.prototype = {
  58.   log : function cs_log(aMsg) {
  59.     this._console.logStringMessage(aMsg);
  60.   },
  61.   
  62.   open : function cs_open() {
  63.     var wMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  64.                               .getService(Ci.nsIWindowMediator);
  65.     var console = wMediator.getMostRecentWindow("global:console");
  66.     if (!console) {
  67.       var wWatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  68.                              .getService(Ci.nsIWindowWatcher);
  69.       wWatch.openWindow(null, "chrome://global/content/console.xul", "_blank",
  70.                         "chrome,dialog=no,all", null);
  71.     } else {
  72.       // console was already open
  73.       console.focus();
  74.     }
  75.   },
  76.   
  77.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIConsole])
  78. };
  79.  
  80.  
  81. //=================================================
  82. // EventItem constructor
  83. function EventItem(aType, aData) {
  84.   this._type = aType;
  85.   this._data = aData;
  86. }
  87.  
  88. //=================================================
  89. // EventItem implementation
  90. EventItem.prototype = {
  91.   _cancel : false,
  92.   
  93.   get type() {
  94.     return this._type;
  95.   },
  96.   
  97.   get data() {
  98.     return this._data;
  99.   },
  100.   
  101.   preventDefault : function ei_pd() {
  102.     this._cancel = true;
  103.   },
  104.   
  105.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIEventItem])
  106. };
  107.  
  108.  
  109. //=================================================
  110. // Events constructor
  111. function Events() {
  112.   this._listeners = [];
  113. }
  114.  
  115. //=================================================
  116. // Events implementation
  117. Events.prototype = {
  118.   addListener : function evts_al(aEvent, aListener) {
  119.     if (this._listeners.some(hasFilter))
  120.       return;
  121.  
  122.     this._listeners.push({
  123.       event: aEvent,
  124.       listener: aListener
  125.     });
  126.     
  127.     function hasFilter(element) {
  128.       return element.event == aEvent && element.listener == aListener;
  129.     }
  130.   },
  131.   
  132.   removeListener : function evts_rl(aEvent, aListener) {
  133.     this._listeners = this._listeners.filter(function(element){
  134.       return element.event != aEvent && element.listener != aListener;
  135.     });
  136.   },
  137.   
  138.   dispatch : function evts_dispatch(aEvent, aEventItem) {
  139.     eventItem = new EventItem(aEvent, aEventItem);
  140.     
  141.     this._listeners.forEach(function(key){
  142.       if (key.event == aEvent) {
  143.         key.listener.handleEvent ?
  144.           key.listener.handleEvent(eventItem) :
  145.           key.listener(eventItem);
  146.       }
  147.     });
  148.     
  149.     return !eventItem._cancel;
  150.   },
  151.   
  152.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIEvents])
  153. };
  154.  
  155.  
  156. //=================================================
  157. // PreferenceBranch constructor
  158. function PreferenceBranch(aBranch) {
  159.   if (!aBranch)
  160.     aBranch = "";
  161.   
  162.   this._root = aBranch;
  163.   this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
  164.                           .getService(Ci.nsIPrefService);
  165.  
  166.   if (aBranch)
  167.     this._prefs = this._prefs.getBranch(aBranch);
  168.     
  169.   this._prefs.QueryInterface(Ci.nsIPrefBranch);
  170.   this._prefs.QueryInterface(Ci.nsIPrefBranch2);
  171.   
  172.   // we want to listen to "all" changes for this branch, so pass in a blank domain
  173.   this._prefs.addObserver("", this, true);
  174.   this._events = new Events();
  175.   
  176.   var self = this;
  177.   gShutdown.push(function() { self._shutdown(); });
  178. }
  179.  
  180. //=================================================
  181. // PreferenceBranch implementation
  182. PreferenceBranch.prototype = {
  183.   // cleanup observer so we don't leak
  184.   _shutdown: function prefs_shutdown() {
  185.     this._prefs.removeObserver(this._root, this);
  186.  
  187.     this._prefs = null;
  188.     this._events = null;
  189.   },
  190.   
  191.   // for nsIObserver
  192.   observe: function prefs_observe(aSubject, aTopic, aData) {
  193.     if (aTopic == "nsPref:changed")
  194.       this._events.dispatch("change", aData);
  195.   },
  196.  
  197.   get root() {
  198.     return this._root;
  199.   },
  200.   
  201.   get all() {
  202.     return this.find({});
  203.   },
  204.   
  205.   get events() {
  206.     return this._events;
  207.   },
  208.   
  209.   // XXX: Disabled until we can figure out the wrapped object issues
  210.   // name: "name" or /name/
  211.   // path: "foo.bar." or "" or /fo+\.bar/
  212.   // type: Boolean, Number, String (getPrefType)
  213.   // locked: true, false (prefIsLocked)
  214.   // modified: true, false (prefHasUserValue)
  215.   find : function prefs_find(aOptions) {
  216.     var retVal = [];
  217.     var items = this._prefs.getChildList("", []);
  218.     
  219.     for (var i = 0; i < items.length; i++) {
  220.       retVal.push(new Preference(items[i], this));
  221.     }
  222.  
  223.     return retVal;
  224.   },
  225.   
  226.   has : function prefs_has(aName) {
  227.     return (this._prefs.getPrefType(aName) != Ci.nsIPrefBranch.PREF_INVALID);
  228.   },
  229.   
  230.   get : function prefs_get(aName) {
  231.     return this.has(aName) ? new Preference(aName, this) : null;
  232.   },
  233.  
  234.   getValue : function prefs_gv(aName, aValue) {
  235.     var type = this._prefs.getPrefType(aName);
  236.     
  237.     switch (type) {
  238.       case Ci.nsIPrefBranch2.PREF_STRING:
  239.         aValue = this._prefs.getComplexValue(aName, Ci.nsISupportsString).data;
  240.         break;
  241.       case Ci.nsIPrefBranch2.PREF_BOOL:
  242.         aValue = this._prefs.getBoolPref(aName);
  243.         break;
  244.       case Ci.nsIPrefBranch2.PREF_INT:
  245.         aValue = this._prefs.getIntPref(aName);
  246.         break;
  247.     }
  248.     
  249.     return aValue;
  250.   },
  251.   
  252.   setValue : function prefs_sv(aName, aValue) {
  253.     var type = aValue != null ? aValue.constructor.name : "";
  254.     
  255.     switch (type) {
  256.       case "String":
  257.         var str = Components.classes["@mozilla.org/supports-string;1"]
  258.                             .createInstance(Ci.nsISupportsString);
  259.         str.data = aValue;
  260.         this._prefs.setComplexValue(aName, Ci.nsISupportsString, str);
  261.         break;
  262.       case "Boolean":
  263.         this._prefs.setBoolPref(aName, aValue);
  264.         break;
  265.       case "Number":
  266.         this._prefs.setIntPref(aName, aValue);
  267.         break;
  268.       default:
  269.         throw("Unknown preference value specified.");
  270.     }
  271.   },
  272.   
  273.   reset : function prefs_reset() {
  274.     this._prefs.resetBranch("");
  275.   },
  276.   
  277.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIPreferenceBranch, Ci.nsISupportsWeakReference])
  278. };
  279.  
  280.  
  281. //=================================================
  282. // Preference constructor
  283. function Preference(aName, aBranch) {
  284.   this._name = aName;
  285.   this._branch = aBranch;
  286.   this._events = new Events();
  287.   
  288.   var self = this;
  289.   
  290.   this.branch.events.addListener("change", function(aEvent){
  291.     if (aEvent.data == self.name)
  292.       self.events.dispatch(aEvent.type, aEvent.data);
  293.   });
  294. }
  295.  
  296. //=================================================
  297. // Preference implementation
  298. Preference.prototype = {
  299.   get name() {
  300.     return this._name;
  301.   },
  302.   
  303.   get type() {
  304.     var value = "";
  305.     var type = this._prefs.getPrefType(name);
  306.     
  307.     switch (type) {
  308.       case Ci.nsIPrefBranch2.PREF_STRING:
  309.         value = "String";
  310.         break;
  311.       case Ci.nsIPrefBranch2.PREF_BOOL:
  312.         value = "Boolean";
  313.         break;
  314.       case Ci.nsIPrefBranch2.PREF_INT:
  315.         value = "Number";
  316.         break;
  317.     }
  318.     
  319.     return value;
  320.   },
  321.   
  322.   get value() {
  323.     return this.branch.getValue(this._name, null);
  324.   },
  325.   
  326.   set value(aValue) {
  327.     return this.branch.setValue(this._name, aValue);
  328.   },
  329.   
  330.   get locked() {
  331.     return this.branch._prefs.prefIsLocked(this.name);
  332.   },
  333.   
  334.   set locked(aValue) {
  335.     this.branch._prefs[ aValue ? "lockPref" : "unlockPref" ](this.name);
  336.   },
  337.   
  338.   get modified() {
  339.     return this.branch._prefs.prefHasUserValue(this.name);
  340.   },
  341.   
  342.   get branch() {
  343.     return this._branch;
  344.   },
  345.   
  346.   get events() {
  347.     return this._events;
  348.   },
  349.   
  350.   reset : function pref_reset() {
  351.     this.branch._prefs.clearUserPref(this.name);
  352.   },
  353.   
  354.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIPreference])
  355. };
  356.  
  357.  
  358. //=================================================
  359. // SessionStorage constructor
  360. function SessionStorage() {
  361.   this._storage = {};
  362.   this._events = new Events();
  363. }
  364.  
  365. //=================================================
  366. // SessionStorage implementation
  367. SessionStorage.prototype = {
  368.   get events() {
  369.     return this._events;
  370.   },
  371.   
  372.   has : function ss_has(aName) {
  373.     return this._storage.hasOwnProperty(aName);
  374.   },
  375.   
  376.   set : function ss_set(aName, aValue) {
  377.     this._storage[aName] = aValue;
  378.     this._events.dispatch("change", aName);
  379.   },
  380.   
  381.   get : function ss_get(aName, aDefaultValue) {
  382.     return this.has(aName) ? this._storage[aName] : aDefaultValue;
  383.   },
  384.   
  385.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelISessionStorage])
  386. };
  387.  
  388.  
  389. //=================================================
  390. // Extension constructor
  391. function Extension(aItem) {
  392.   this._item = aItem;
  393.   this._firstRun = false;
  394.   this._prefs = new PreferenceBranch("extensions." + this._item.id + ".");
  395.   this._storage = new SessionStorage();
  396.   this._events = new Events();
  397.   
  398.   var installPref = "install-event-fired";
  399.   if (!this._prefs.has(installPref)) {
  400.     this._prefs.setValue(installPref, true);
  401.     this._firstRun = true;
  402.   }
  403.  
  404.   var os = Components.classes["@mozilla.org/observer-service;1"]
  405.                      .getService(Ci.nsIObserverService);
  406.   os.addObserver(this, "em-action-requested", false);
  407.   
  408.   var self = this;
  409.   gShutdown.push(function(){ self._shutdown(); });
  410. }
  411.  
  412. //=================================================
  413. // Extension implementation
  414. Extension.prototype = {
  415.   // cleanup observer so we don't leak
  416.   _shutdown: function ext_shutdown() {
  417.     var os = Components.classes["@mozilla.org/observer-service;1"]
  418.                        .getService(Ci.nsIObserverService);
  419.     os.removeObserver(this, "em-action-requested");
  420.  
  421.     this._prefs = null;
  422.     this._storage = null;
  423.     this._events = null;
  424.   },
  425.   
  426.   // for nsIObserver  
  427.   observe: function ext_observe(aSubject, aTopic, aData)
  428.   {
  429.     if ((aSubject instanceof Ci.nsIUpdateItem) && (aSubject.id == this._item.id))
  430.     {
  431.       if (aData == "item-uninstalled")
  432.         this._events.dispatch("uninstall", this._item.id);
  433.       else if (aData == "item-disabled")
  434.         this._events.dispatch("disable", this._item.id);
  435.       else if (aData == "item-enabled")
  436.         this._events.dispatch("enable", this._item.id);
  437.       else if (aData == "item-cancel-action")
  438.         this._events.dispatch("cancel", this._item.id);
  439.       else if (aData == "item-upgraded")
  440.         this._events.dispatch("upgrade", this._item.id);
  441.     }
  442.   },
  443.  
  444.   get id() {
  445.     return this._item.id;
  446.   },
  447.   
  448.   get name() {
  449.     return this._item.name;
  450.   },
  451.   
  452.   get version() {
  453.     return this._item.version;
  454.   },
  455.   
  456.   get firstRun() {
  457.     return this._firstRun;
  458.   },
  459.   
  460.   get storage() {
  461.     return this._storage;
  462.   },
  463.   
  464.   get prefs() {
  465.     return this._prefs;
  466.   },
  467.   
  468.   get events() {
  469.     return this._events;
  470.   },
  471.   
  472.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIExtension])
  473. };
  474.  
  475.  
  476. //=================================================
  477. // Extensions constructor
  478. function Extensions() {
  479.   this._extmgr = Components.classes["@mozilla.org/extensions/manager;1"]
  480.                            .getService(Ci.nsIExtensionManager);
  481.                            
  482.   this._cache = {};
  483.   
  484.   var self = this;
  485.   gShutdown.push(function() { self._shutdown(); });
  486. }
  487.  
  488. //=================================================
  489. // Extensions implementation
  490. Extensions.prototype = {
  491.   _shutdown : function exts_shutdown() {
  492.     this._extmgr = null;
  493.     this._cache = null;
  494.   },
  495.   
  496.   /*
  497.    * Helper method to check cache before creating a new extension
  498.    */
  499.   _get : function exts_get(aId) {
  500.     if (this._cache.hasOwnProperty(aId))
  501.       return this._cache[aId];
  502.       
  503.     var newExt = new Extension(this._extmgr.getItemForID(aId));
  504.     this._cache[aId] = newExt;
  505.     return newExt;
  506.   },
  507.   
  508.   get all() {
  509.     return this.find({});
  510.   },
  511.   
  512.   // XXX: Disabled until we can figure out the wrapped object issues
  513.   // id: "some@id" or /id/
  514.   // name: "name" or /name/
  515.   // version: "1.0.1"
  516.   // minVersion: "1.0"
  517.   // maxVersion: "2.0"
  518.   find : function exts_find(aOptions) {
  519.     var retVal = [];
  520.     var items = this._extmgr.getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION, {});
  521.     
  522.     for (var i = 0; i < items.length; i++) {
  523.       retVal.push(this._get(items[i].id));
  524.     }
  525.  
  526.     return retVal;
  527.   },
  528.   
  529.   has : function exts_has(aId) {
  530.     return this._extmgr.getItemForID(aId) != null;
  531.   },
  532.   
  533.   get : function exts_get(aId) {
  534.     return this.has(aId) ? this._get(aId) : null;
  535.   },
  536.   
  537.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIExtensions])
  538. };
  539.  
  540. //=================================================
  541. // Singleton that holds services and utilities
  542. var Utilities = {
  543.   _bookmarks : null,
  544.   get bookmarks() {
  545.     if (!this._bookmarks) {
  546.       this._bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
  547.                         getService(Ci.nsINavBookmarksService);
  548.     }
  549.     return this._bookmarks;
  550.   },
  551.  
  552.   _livemarks : null,
  553.   get livemarks() {
  554.     if (!this._livemarks) {
  555.       this._livemarks = Cc["@mozilla.org/browser/livemark-service;2"].
  556.                         getService(Ci.nsILivemarkService);
  557.     }
  558.     return this._livemarks;
  559.   },
  560.  
  561.   _annotations : null,
  562.   get annotations() {
  563.     if (!this._annotations) {
  564.       this._annotations = Cc["@mozilla.org/browser/annotation-service;1"].
  565.                           getService(Ci.nsIAnnotationService);
  566.     }
  567.     return this._annotations;
  568.   },
  569.   
  570.   _history : null,
  571.   get history() {
  572.     if (!this._history) {
  573.       this._history = Cc["@mozilla.org/browser/nav-history-service;1"].
  574.                       getService(Ci.nsINavHistoryService);
  575.     }
  576.     return this._history;
  577.   },
  578.   
  579.   _windowMediator : null,
  580.   get windowMediator() {
  581.     if (!this._windowMediator) {
  582.       this._windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
  583.                              getService(Ci.nsIWindowMediator);
  584.     }
  585.     return this._windowMediator;
  586.   },
  587.   
  588.   makeURI : function(aSpec) {
  589.     if (!aSpec)
  590.       return null;
  591.     var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  592.     return ios.newURI(aSpec, null, null);
  593.   },
  594.   
  595.   free : function() {
  596.     this._bookmarks = null;
  597.     this._livemarks = null;
  598.     this._annotations = null;
  599.     this._history = null;
  600.     this._windowMediator = null;
  601.   }
  602. };
  603.  
  604.  
  605. //=================================================
  606. // Window implementation
  607. function Window(aWindow) {
  608.   this._window = aWindow;
  609.   this._tabbrowser = aWindow.getBrowser();
  610.   this._events = new Events();
  611.   this._cleanup = {};
  612.   
  613.   this._watch("TabOpen");
  614.   this._watch("TabMove");
  615.   this._watch("TabClose");
  616.   this._watch("TabSelect");
  617.                                  
  618.   var self = this;
  619.   gShutdown.push(function() { self._shutdown(); });
  620. }
  621.  
  622. Window.prototype = {
  623.   get events() {
  624.     return this._events;
  625.   },
  626.  
  627.   /*
  628.    * Helper used to setup event handlers on the XBL element. Note that the events
  629.    * are actually dispatched to tabs, so we capture them.
  630.    */
  631.   _watch : function win_watch(aType) {
  632.     var self = this;
  633.     this._tabbrowser.addEventListener(aType, 
  634.       this._cleanup[aType] = function(e){ self._event(e); },
  635.       true);
  636.   },
  637.   
  638.   /*
  639.    * Helper event callback used to redirect events made on the XBL element
  640.    */
  641.   _event : function win_event(aEvent) {
  642.     this._events.dispatch(aEvent.type, "");
  643.   },
  644.   
  645.   get tabs() {
  646.     var tabs = [];
  647.     var browsers = this._tabbrowser.browsers;
  648.     
  649.     for (var i=0; i<browsers.length; i++)
  650.       tabs.push(new BrowserTab(this._window, browsers[i]));
  651.     
  652.     return tabs;
  653.   },
  654.   
  655.   get activeTab() {
  656.     return new BrowserTab(this._window, this._tabbrowser.selectedBrowser);
  657.   },
  658.   
  659.   open : function win_open(aURI) {
  660.     return new BrowserTab(this._window, this._tabbrowser.addTab(aURI.spec).linkedBrowser);
  661.   },
  662.   
  663.   _shutdown : function win_shutdown() {
  664.     for (var type in this._cleanup)
  665.       this._tabbrowser.removeEventListener(type, this._cleanup[type], true);
  666.     this._cleanup = null;
  667.  
  668.     this._window = null;
  669.     this._tabbrowser = null;
  670.     this._events = null;
  671.   },
  672.   
  673.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIWindow])
  674. };
  675.  
  676.  
  677. //=================================================
  678. // BrowserTab implementation
  679. function BrowserTab(aWindow, aBrowser) {
  680.   this._window = aWindow;
  681.   this._tabbrowser = aWindow.getBrowser();
  682.   this._browser = aBrowser;
  683.   this._events = new Events();
  684.   this._cleanup = {};
  685.   
  686.   this._watch("load");
  687.                                  
  688.   var self = this;
  689.   gShutdown.push(function() { self._shutdown(); });
  690. }
  691.  
  692. BrowserTab.prototype = {
  693.   get uri() {
  694.     return this._browser.currentURI;
  695.   },
  696.   
  697.   get index() {
  698.     var tabs = this._tabbrowser.mTabs;
  699.     for (var i=0; i<tabs.length; i++) {
  700.       if (tabs[i].linkedBrowser == this._browser)
  701.         return i;
  702.     }
  703.     return -1;
  704.   },
  705.  
  706.   get events() {
  707.     return this._events;
  708.   },
  709.   
  710.   get window() {
  711.     return this._window;
  712.   },
  713.   
  714.   get document() {
  715.     return this._browser.contentDocument;
  716.   },
  717.   
  718.   /*
  719.    * Helper used to setup event handlers on the XBL element
  720.    */
  721.   _watch : function bt_watch(aType) {
  722.     var self = this;
  723.     this._browser.addEventListener(aType,
  724.       this._cleanup[aType] = function(e){ self._event(e); },
  725.       true);
  726.   },
  727.   
  728.   /*
  729.    * Helper event callback used to redirect events made on the XBL element
  730.    */
  731.   _event : function bt_event(aEvent) {
  732.     if (aEvent.type == "load") {
  733.       if (!(aEvent.originalTarget instanceof Ci.nsIDOMHTMLDocument))
  734.         return;
  735.         
  736.       if (aEvent.originalTarget.defaultView instanceof Ci.nsIDOMWindowInternal &&
  737.           aEvent.originalTarget.defaultView.frameElement)
  738.         return;
  739.     }
  740.       
  741.     this._events.dispatch(aEvent.type, "");
  742.   },
  743.   
  744.   /*
  745.    * Helper used to determine the index offset of the browsertab
  746.    */
  747.   _getTab : function bt_gettab() {
  748.     var tabs = this._tabbrowser.mTabs;
  749.     return tabs[this.index] || null;
  750.   },
  751.   
  752.   load : function bt_load(aURI) {
  753.     this._browser.loadURI(aURI.spec, null, null);
  754.   },
  755.   
  756.   focus : function bt_focus() {
  757.     this._tabbrowser.selectedTab = this._getTab();
  758.     this._tabbrowser.focus();
  759.   },
  760.   
  761.   close : function bt_close() {
  762.     this._tabbrowser.removeTab(this._getTab());
  763.   },
  764.   
  765.   moveBefore : function bt_movebefore(aBefore) {
  766.     this._tabbrowser.moveTabTo(this._getTab(), aBefore.index);
  767.   },
  768.   
  769.   moveToEnd : function bt_moveend() {
  770.     this._tabbrowser.moveTabTo(this._getTab(), this._tabbrowser.browsers.length);
  771.   },
  772.   
  773.   _shutdown : function bt_shutdown() {
  774.     for (var type in this._cleanup)
  775.       this._browser.removeEventListener(type, this._cleanup[type], true);
  776.     this._cleanup = null;
  777.     
  778.     this._window = null;
  779.     this._tabbrowser = null;
  780.     this._browser = null;
  781.     this._events = null;
  782.   },
  783.   
  784.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIBrowserTab])
  785. };
  786.  
  787.  
  788. //=================================================
  789. // Annotations implementation
  790. function Annotations(aId) {
  791.   this._id = aId;
  792. }
  793.  
  794. Annotations.prototype = {
  795.   get names() {
  796.     return Utilities.annotations.getItemAnnotationNames(this._id, {});
  797.   },
  798.   
  799.   has : function ann_has(aName) {
  800.     return Utilities.annotations.itemHasAnnotation(this._id, aName);
  801.   },
  802.   
  803.   get : function(aName) {
  804.     return Utilities.annotations.getItemAnnotation(this._id, aName);
  805.   },
  806.   
  807.   set : function(aName, aValue, aExpiration) {
  808.     Utilities.annotations.setItemAnnotation(this._id, aName, aValue, 0, aExpiration);
  809.   },
  810.     
  811.   remove : function ann_remove(aName) {
  812.     if (aName)
  813.       Utilities.annotations.removeItemAnnotation(this._id, aName);
  814.   },
  815.   
  816.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIAnnotations])
  817. };
  818.  
  819.  
  820. //=================================================
  821. // Bookmark implementation
  822. function Bookmark(aId, aParent, aType) {
  823.   this._id = aId;
  824.   this._parent = aParent;
  825.   this._type = aType || "bookmark";
  826.   this._annotations = new Annotations(this._id);
  827.   this._events = new Events();
  828.  
  829.   Utilities.bookmarks.addObserver(this, false);  
  830.                                  
  831.   var self = this;
  832.   gShutdown.push(function() { self._shutdown(); });
  833. }
  834.  
  835. Bookmark.prototype = {
  836.   _shutdown : function bm_shutdown() {
  837.     this._annotations = null;
  838.     this._events = null;
  839.     
  840.     Utilities.bookmarks.removeObserver(this);  
  841.   },
  842.   
  843.   get id() {
  844.     return this._id;
  845.   },
  846.   
  847.   get title() {
  848.     return Utilities.bookmarks.getItemTitle(this._id);
  849.   },
  850.  
  851.   set title(aTitle) {
  852.     Utilities.bookmarks.setItemTitle(this._id, aTitle);
  853.   },
  854.  
  855.   get uri() {
  856.     return Utilities.bookmarks.getBookmarkURI(this._id);
  857.   },
  858.  
  859.   set uri(aURI) {
  860.     return Utilities.bookmarks.changeBookmarkURI(this._id, aURI);
  861.   },
  862.  
  863.   get description() {
  864.     return this._annotations.get("bookmarkProperties/description");
  865.   },
  866.  
  867.   set description(aDesc) {
  868.     this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
  869.   },
  870.  
  871.   get keyword() {
  872.     return Utilities.bookmarks.getKeywordForBookmark(this._id);
  873.   },
  874.  
  875.   set keyword(aKeyword) {
  876.     Utilities.bookmarks.setKeywordForBookmark(this._id, aKeyword);
  877.   },
  878.  
  879.   get type() {
  880.     return this._type;
  881.   },
  882.  
  883.   get parent() {
  884.     return this._parent;
  885.   },
  886.   
  887.   set parent(aFolder) {
  888.     Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
  889.     // this._parent is updated in onItemMoved
  890.   },
  891.   
  892.   get annotations() {
  893.     return this._annotations;
  894.   },
  895.   
  896.   get events() {
  897.     return this._events;
  898.   },
  899.   
  900.   remove : function bm_remove() {
  901.     Utilities.bookmarks.removeItem(this._id);
  902.   },
  903.   
  904.   // observer
  905.   onBeginUpdateBatch : function bm_obub() {
  906.   },
  907.  
  908.   onEndUpdateBatch : function bm_oeub() {
  909.   },
  910.  
  911.   onItemAdded : function bm_oia(aId, aFolder, aIndex) {
  912.     // bookmark object doesn't exist at this point
  913.   },
  914.  
  915.   onItemRemoved : function bm_oir(aId, aFolder, aIndex) {
  916.     if (this._id == aId)
  917.       this._events.dispatch("remove", aId);
  918.   },
  919.  
  920.   onItemChanged : function bm_oic(aId, aProperty, aIsAnnotationProperty, aValue) {
  921.     if (this._id == aId)
  922.       this._events.dispatch("change", aProperty);
  923.   },
  924.  
  925.   onItemVisited: function bm_oiv(aId, aVisitID, aTime) {
  926.   },
  927.  
  928.   onItemMoved: function bm_oim(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
  929.     if (this._id == aId) {
  930.       this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));    
  931.       this._events.dispatch("move", aId);
  932.     }
  933.   },
  934.  
  935.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIBookmark, Ci.nsINavBookmarkObserver])
  936. }; 
  937.  
  938.  
  939. //=================================================
  940. // BookmarkFolder implementation
  941. function BookmarkFolder(aId, aParent) {
  942.   this._id = aId;
  943.   if (this._id == null)
  944.     this._id = Utilities.bookmarks.bookmarksRoot;
  945.   
  946.   this._parent = aParent;
  947.                                  
  948.   this._annotations = new Annotations(this._id);
  949.   this._events = new Events();
  950.  
  951.   Utilities.bookmarks.addObserver(this, false);  
  952.  
  953.   var self = this;
  954.   gShutdown.push(function() { self._shutdown(); });
  955. }
  956.  
  957. BookmarkFolder.prototype = {
  958.   _shutdown : function bmf_shutdown() {
  959.     this._annotations = null;
  960.     this._events = null;
  961.     
  962.     Utilities.bookmarks.removeObserver(this);  
  963.   },
  964.   
  965.   get id() {
  966.     return this._id;
  967.   },
  968.   
  969.   get title() {
  970.     return Utilities.bookmarks.getItemTitle(this._id);
  971.   },
  972.  
  973.   set title(aTitle) {
  974.     Utilities.bookmarks.setItemTitle(this._id, aTitle);
  975.   },
  976.  
  977.   get description() {
  978.     return this._annotations.get("bookmarkProperties/description");
  979.   },
  980.  
  981.   set description(aDesc) {
  982.     this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
  983.   },
  984.  
  985.   get type() {
  986.     return "folder";
  987.   },
  988.  
  989.   get parent() {
  990.     return this._parent;
  991.   },
  992.   
  993.   set parent(aFolder) {
  994.     Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
  995.     // this._parent is updated in onItemMoved
  996.   },
  997.  
  998.   get annotations() {
  999.     return this._annotations;
  1000.   },
  1001.   
  1002.   get events() {
  1003.     return this._events;
  1004.   },
  1005.   
  1006.   get children() {
  1007.     var items = [];
  1008.     
  1009.     var options = Utilities.history.getNewQueryOptions();
  1010.     var query = Utilities.history.getNewQuery();
  1011.     query.setFolders([this._id], 1);
  1012.     var result = Utilities.history.executeQuery(query, options);
  1013.     var rootNode = result.root;
  1014.     rootNode.containerOpen = true;
  1015.     var cc = rootNode.childCount;
  1016.     for (var i=0; i<cc; ++i) {
  1017.       var node = rootNode.getChild(i);
  1018.       if (node.type == node.RESULT_TYPE_FOLDER) {
  1019.         var folder = new BookmarkFolder(node.itemId, this._id);
  1020.         items.push(folder);
  1021.       }
  1022.       else if (node.type == node.RESULT_TYPE_SEPARATOR) {
  1023.         var separator = new Bookmark(node.itemId, this._id, "separator");
  1024.         items.push(separator);
  1025.       }
  1026.       else {
  1027.         var bookmark = new Bookmark(node.itemId, this._id, "bookmark");
  1028.         items.push(bookmark);
  1029.       }
  1030.     }
  1031.     rootNode.containerOpen = false;
  1032.  
  1033.     return items;
  1034.   },
  1035.   
  1036.   addBookmark : function bmf_addbm(aTitle, aUri) {
  1037.     var newBookmarkID = Utilities.bookmarks.insertBookmark(this._id, aUri, Utilities.bookmarks.DEFAULT_INDEX, aTitle);
  1038.     var newBookmark = new Bookmark(newBookmarkID, this, "bookmark");
  1039.     return newBookmark;
  1040.   },
  1041.   
  1042.   addSeparator : function bmf_addsep() {
  1043.     var newBookmarkID = Utilities.bookmarks.insertSeparator(this._id, Utilities.bookmarks.DEFAULT_INDEX);
  1044.     var newBookmark = new Bookmark(newBookmarkID, this, "separator");
  1045.     return newBookmark;
  1046.   },
  1047.   
  1048.   addFolder : function bmf_addfolder(aTitle) {
  1049.     var newFolderID = Utilities.bookmarks.createFolder(this._id, aTitle, Utilities.bookmarks.DEFAULT_INDEX);
  1050.     var newFolder = new BookmarkFolder(newFolderID, this);
  1051.     return newFolder;
  1052.   },
  1053.   
  1054.   remove : function bmf_remove() {
  1055.     Utilities.bookmarks.removeFolder(this._id);
  1056.   },
  1057.   
  1058.   // observer
  1059.   onBeginUpdateBatch : function bmf_obub() {
  1060.   },
  1061.  
  1062.   onEndUpdateBatch : function bmf_oeub() {
  1063.   },
  1064.  
  1065.   onItemAdded : function bmf_oia(aId, aFolder, aIndex) {
  1066.     // handle root folder events
  1067.     if (!this._parent)
  1068.       this._events.dispatch("add", aId);
  1069.     
  1070.     // handle this folder events  
  1071.     if (this._id == aFolder)
  1072.       this._events.dispatch("addchild", aId);
  1073.   },
  1074.  
  1075.   onItemRemoved : function bmf_oir(aId, aFolder, aIndex) {
  1076.     // handle root folder events
  1077.     if (!this._parent || this._id == aId)
  1078.       this._events.dispatch("remove", aId);
  1079.  
  1080.     // handle this folder events      
  1081.     if (this._id == aFolder)
  1082.       this._events.dispatch("removechild", aId);
  1083.   },
  1084.  
  1085.   onItemChanged : function bmf_oic(aId, aProperty, aIsAnnotationProperty, aValue) {
  1086.     // handle root folder and this folder events
  1087.     if (!this._parent || this._id == aId)
  1088.       this._events.dispatch("change", aProperty);
  1089.   },
  1090.  
  1091.   onItemVisited: function bmf_oiv(aId, aVisitID, aTime) {
  1092.   },
  1093.  
  1094.   onItemMoved: function bmf_oim(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
  1095.     // handle this folder event, root folder cannot be moved
  1096.     if (this._id == aId) {
  1097.       this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));    
  1098.       this._events.dispatch("move", aId);
  1099.     }
  1100.   },
  1101.  
  1102.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder, Ci.nsINavBookmarkObserver])
  1103. };
  1104.  
  1105. //=================================================
  1106. // Factory - Treat Application as a singleton
  1107. // XXX This is required, because we're registered for the 'JavaScript global
  1108. // privileged property' category, whose handler always calls createInstance.
  1109. // See bug 386535.
  1110. var gSingleton = null;
  1111. var ApplicationFactory = {
  1112.   createInstance: function af_ci(aOuter, aIID) {
  1113.     if (aOuter != null)
  1114.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  1115.  
  1116.     if (gSingleton == null) {
  1117.       gSingleton = new Application();
  1118.     }
  1119.  
  1120.     return gSingleton.QueryInterface(aIID);
  1121.   }
  1122. };
  1123.  
  1124. //=================================================
  1125. // Application constructor
  1126. function Application() {
  1127.   this._console = null;
  1128.   this._prefs = null;
  1129.   this._storage = null;
  1130.   this._events = null;
  1131.   this._bookmarks = null;
  1132.   
  1133.   this._info = Components.classes["@mozilla.org/xre/app-info;1"]
  1134.                      .getService(Ci.nsIXULAppInfo);
  1135.     
  1136.   var os = Components.classes["@mozilla.org/observer-service;1"]
  1137.                      .getService(Ci.nsIObserverService);
  1138.  
  1139.   os.addObserver(this, "final-ui-startup", false);
  1140.   os.addObserver(this, "quit-application-requested", false);
  1141.   os.addObserver(this, "quit-application-granted", false);
  1142.   os.addObserver(this, "quit-application", false);
  1143.   os.addObserver(this, "xpcom-shutdown", false);
  1144. }
  1145.  
  1146. //=================================================
  1147. // Application implementation
  1148. Application.prototype = {
  1149.   // for nsIClassInfo + XPCOMUtils
  1150.   classDescription: "Application",
  1151.   classID:          Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66"),
  1152.   contractID:       "@mozilla.org/fuel/application;1",
  1153.  
  1154.   // redefine the default factory for XPCOMUtils
  1155.   _xpcom_factory: ApplicationFactory,
  1156.  
  1157.   // get this contractID registered for certain categories via XPCOMUtils
  1158.   _xpcom_categories: [
  1159.     // make Application a startup observer
  1160.     { category: "app-startup", service: true },
  1161.  
  1162.     // add Application as a global property for easy access
  1163.     { category: "JavaScript global privileged property" }
  1164.   ],
  1165.  
  1166.   get id() {
  1167.     return this._info.ID;
  1168.   },
  1169.   
  1170.   get name() {
  1171.     return this._info.name;
  1172.   },
  1173.   
  1174.   get version() {
  1175.     return this._info.version;
  1176.   },
  1177.   
  1178.   // for nsIObserver
  1179.   observe: function app_observe(aSubject, aTopic, aData) {
  1180.     if (aTopic == "app-startup") {
  1181.       this._extensions = new Extensions();
  1182.       this.events.dispatch("load", "application");
  1183.     }
  1184.     else if (aTopic == "final-ui-startup") {
  1185.       this.events.dispatch("ready", "application");
  1186.     }
  1187.     else if (aTopic == "quit-application-requested") {
  1188.       // we can stop the quit by checking the return value
  1189.       if (this.events.dispatch("quit", "application") == false)
  1190.         aSubject.data = true;
  1191.     }
  1192.     else if (aTopic == "xpcom-shutdown") {
  1193.       this.events.dispatch("unload", "application");
  1194.  
  1195.       // call the cleanup functions and empty the array
  1196.       while (gShutdown.length) {
  1197.         gShutdown.shift()();
  1198.       }
  1199.  
  1200.       // release our observers      
  1201.       var os = Components.classes["@mozilla.org/observer-service;1"]
  1202.                          .getService(Ci.nsIObserverService);
  1203.  
  1204.       os.removeObserver(this, "final-ui-startup");
  1205.  
  1206.       os.removeObserver(this, "quit-application-requested");
  1207.       os.removeObserver(this, "quit-application-granted");
  1208.       os.removeObserver(this, "quit-application");
  1209.       
  1210.       os.removeObserver(this, "xpcom-shutdown");
  1211.  
  1212.       this._info = null;
  1213.       this._console = null;
  1214.       this._prefs = null;
  1215.       this._storage = null;
  1216.       this._events = null;
  1217.       this._extensions = null;
  1218.       this._bookmarks = null;
  1219.       
  1220.       Utilities.free();
  1221.     }
  1222.   },
  1223.  
  1224.   // for nsIClassInfo
  1225.   flags : Ci.nsIClassInfo.SINGLETON,
  1226.   implementationLanguage : Ci.nsIProgrammingLanguage.JAVASCRIPT,
  1227.  
  1228.   getInterfaces : function app_gi(aCount) {
  1229.     var interfaces = [Ci.fuelIApplication, Ci.nsIObserver, Ci.nsIClassInfo];
  1230.     aCount.value = interfaces.length;
  1231.     return interfaces;
  1232.   },
  1233.  
  1234.   getHelperForLanguage : function app_ghfl(aCount) {
  1235.     return null;
  1236.   },
  1237.   
  1238.   // for nsISupports
  1239.   QueryInterface : XPCOMUtils.generateQI([Ci.fuelIApplication, Ci.nsIObserver, Ci.nsIClassInfo]),
  1240.   
  1241.   get console() {
  1242.     if (this._console == null)
  1243.         this._console = new Console();
  1244.  
  1245.     return this._console;
  1246.   },
  1247.   
  1248.   get storage() {
  1249.     if (this._storage == null)
  1250.         this._storage = new SessionStorage();
  1251.  
  1252.     return this._storage;
  1253.   },
  1254.   
  1255.   get prefs() {
  1256.     if (this._prefs == null)
  1257.         this._prefs = new PreferenceBranch("");
  1258.  
  1259.     return this._prefs;
  1260.   },
  1261.   
  1262.   get extensions() {
  1263.     return this._extensions;
  1264.   },
  1265.  
  1266.   get events() {
  1267.     if (this._events == null)
  1268.         this._events = new Events();
  1269.  
  1270.     return this._events;
  1271.   },
  1272.  
  1273.   get bookmarks() {
  1274.     if (this._bookmarks == null)
  1275.       this._bookmarks = new BookmarkFolder(null, null);
  1276.  
  1277.     return this._bookmarks;
  1278.   },
  1279.   
  1280.   get windows() {
  1281.     var win = [];
  1282.     var enum = Utilities.windowMediator.getEnumerator("navigator:browser");
  1283.     
  1284.     while (enum.hasMoreElements())
  1285.       win.push(new Window(enum.getNext()));
  1286.  
  1287.     return win;
  1288.   },
  1289.   
  1290.   get activeWindow() {
  1291.     return new Window(Utilities.windowMediator.getMostRecentWindow("navigator:browser"));
  1292.   }
  1293. };
  1294.  
  1295. //module initialization
  1296. function NSGetModule(aCompMgr, aFileSpec) {
  1297.   return XPCOMUtils.generateModule([Application]);
  1298. }
  1299.