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