browser/fuel/src/fuelApplication.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 const Ci = Components.interfaces;
     6 const Cc = Components.classes;
     8 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
    10 const APPLICATION_CID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66");
    11 const APPLICATION_CONTRACTID = "@mozilla.org/fuel/application;1";
    13 //=================================================
    14 // Singleton that holds services and utilities
    15 var Utilities = {
    16   get bookmarks() {
    17     let bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
    18                     getService(Ci.nsINavBookmarksService);
    19     this.__defineGetter__("bookmarks", function() bookmarks);
    20     return this.bookmarks;
    21   },
    23   get bookmarksObserver() {
    24     let bookmarksObserver = new BookmarksObserver();
    25     this.__defineGetter__("bookmarksObserver", function() bookmarksObserver);
    26     return this.bookmarksObserver;
    27   },
    29   get annotations() {
    30     let annotations = Cc["@mozilla.org/browser/annotation-service;1"].
    31                       getService(Ci.nsIAnnotationService);
    32     this.__defineGetter__("annotations", function() annotations);
    33     return this.annotations;
    34   },
    36   get history() {
    37     let history = Cc["@mozilla.org/browser/nav-history-service;1"].
    38                   getService(Ci.nsINavHistoryService);
    39     this.__defineGetter__("history", function() history);
    40     return this.history;
    41   },
    43   get windowMediator() {
    44     let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
    45                          getService(Ci.nsIWindowMediator);
    46     this.__defineGetter__("windowMediator", function() windowMediator);
    47     return this.windowMediator;
    48   },
    50   makeURI: function fuelutil_makeURI(aSpec) {
    51     if (!aSpec)
    52       return null;
    53     var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
    54     return ios.newURI(aSpec, null, null);
    55   },
    57   free: function fuelutil_free() {
    58     delete this.bookmarks;
    59     delete this.bookmarksObserver;
    60     delete this.annotations;
    61     delete this.history;
    62     delete this.windowMediator;
    63   }
    64 };
    67 //=================================================
    68 // Window implementation
    70 var fuelWindowMap = new WeakMap();
    71 function getWindow(aWindow) {
    72   let fuelWindow = fuelWindowMap.get(aWindow);
    73   if (!fuelWindow) {
    74     fuelWindow = new Window(aWindow);
    75     fuelWindowMap.set(aWindow, fuelWindow);
    76   }
    77   return fuelWindow;
    78 }
    80 // Don't call new Window() directly; use getWindow instead.
    81 function Window(aWindow) {
    82   this._window = aWindow;
    83   this._events = new Events();
    85   this._watch("TabOpen");
    86   this._watch("TabMove");
    87   this._watch("TabClose");
    88   this._watch("TabSelect");
    89 }
    91 Window.prototype = {
    92   get events() {
    93     return this._events;
    94   },
    96   get _tabbrowser() {
    97     return this._window.getBrowser();
    98   },
   100   /*
   101    * Helper used to setup event handlers on the XBL element. Note that the events
   102    * are actually dispatched to tabs, so we capture them.
   103    */
   104   _watch: function win_watch(aType) {
   105     this._tabbrowser.tabContainer.addEventListener(aType, this,
   106                                                    /* useCapture = */ true);
   107   },
   109   handleEvent: function win_handleEvent(aEvent) {
   110     this._events.dispatch(aEvent.type, getBrowserTab(this, aEvent.originalTarget.linkedBrowser));
   111   },
   113   get tabs() {
   114     var tabs = [];
   115     var browsers = this._tabbrowser.browsers;
   116     for (var i=0; i<browsers.length; i++)
   117       tabs.push(getBrowserTab(this, browsers[i]));
   118     return tabs;
   119   },
   121   get activeTab() {
   122     return getBrowserTab(this, this._tabbrowser.selectedBrowser);
   123   },
   125   open: function win_open(aURI) {
   126     return getBrowserTab(this, this._tabbrowser.addTab(aURI.spec).linkedBrowser);
   127   },
   129   QueryInterface: XPCOMUtils.generateQI([Ci.fuelIWindow])
   130 };
   132 //=================================================
   133 // BrowserTab implementation
   135 var fuelBrowserTabMap = new WeakMap();
   136 function getBrowserTab(aFUELWindow, aBrowser) {
   137   let fuelBrowserTab = fuelBrowserTabMap.get(aBrowser);
   138   if (!fuelBrowserTab) {
   139     fuelBrowserTab = new BrowserTab(aFUELWindow, aBrowser);
   140     fuelBrowserTabMap.set(aBrowser, fuelBrowserTab);
   141   }
   142   else {
   143     // This tab may have moved to another window, so make sure its cached
   144     // window is up-to-date.
   145     fuelBrowserTab._window = aFUELWindow;
   146   }
   148   return fuelBrowserTab;
   149 }
   151 // Don't call new BrowserTab() directly; call getBrowserTab instead.
   152 function BrowserTab(aFUELWindow, aBrowser) {
   153   this._window = aFUELWindow;
   154   this._browser = aBrowser;
   155   this._events = new Events();
   157   this._watch("load");
   158 }
   160 BrowserTab.prototype = {
   161   get _tabbrowser() {
   162     return this._window._tabbrowser;
   163   },
   165   get uri() {
   166     return this._browser.currentURI;
   167   },
   169   get index() {
   170     var tabs = this._tabbrowser.tabs;
   171     for (var i=0; i<tabs.length; i++) {
   172       if (tabs[i].linkedBrowser == this._browser)
   173         return i;
   174     }
   175     return -1;
   176   },
   178   get events() {
   179     return this._events;
   180   },
   182   get window() {
   183     return this._window;
   184   },
   186   get document() {
   187     return this._browser.contentDocument;
   188   },
   190   /*
   191    * Helper used to setup event handlers on the XBL element
   192    */
   193   _watch: function bt_watch(aType) {
   194     this._browser.addEventListener(aType, this,
   195                                    /* useCapture = */ true);
   196   },
   198   handleEvent: function bt_handleEvent(aEvent) {
   199     if (aEvent.type == "load") {
   200       if (!(aEvent.originalTarget instanceof Ci.nsIDOMDocument))
   201         return;
   203       if (aEvent.originalTarget.defaultView instanceof Ci.nsIDOMWindow &&
   204           aEvent.originalTarget.defaultView.frameElement)
   205         return;
   206     }
   207     this._events.dispatch(aEvent.type, this);
   208   },
   209   /*
   210    * Helper used to determine the index offset of the browsertab
   211    */
   212   _getTab: function bt_gettab() {
   213     var tabs = this._tabbrowser.tabs;
   214     return tabs[this.index] || null;
   215   },
   217   load: function bt_load(aURI) {
   218     this._browser.loadURI(aURI.spec, null, null);
   219   },
   221   focus: function bt_focus() {
   222     this._tabbrowser.selectedTab = this._getTab();
   223     this._tabbrowser.focus();
   224   },
   226   close: function bt_close() {
   227     this._tabbrowser.removeTab(this._getTab());
   228   },
   230   moveBefore: function bt_movebefore(aBefore) {
   231     this._tabbrowser.moveTabTo(this._getTab(), aBefore.index);
   232   },
   234   moveToEnd: function bt_moveend() {
   235     this._tabbrowser.moveTabTo(this._getTab(), this._tabbrowser.browsers.length);
   236   },
   238   QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBrowserTab])
   239 };
   242 //=================================================
   243 // Annotations implementation
   244 function Annotations(aId) {
   245   this._id = aId;
   246 }
   248 Annotations.prototype = {
   249   get names() {
   250     return Utilities.annotations.getItemAnnotationNames(this._id);
   251   },
   253   has: function ann_has(aName) {
   254     return Utilities.annotations.itemHasAnnotation(this._id, aName);
   255   },
   257   get: function ann_get(aName) {
   258     if (this.has(aName))
   259       return Utilities.annotations.getItemAnnotation(this._id, aName);
   260     return null;
   261   },
   263   set: function ann_set(aName, aValue, aExpiration) {
   264     Utilities.annotations.setItemAnnotation(this._id, aName, aValue, 0, aExpiration);
   265   },
   267   remove: function ann_remove(aName) {
   268     if (aName)
   269       Utilities.annotations.removeItemAnnotation(this._id, aName);
   270   },
   272   QueryInterface: XPCOMUtils.generateQI([Ci.fuelIAnnotations])
   273 };
   276 //=================================================
   277 // BookmarksObserver implementation (internal class)
   278 //
   279 // BookmarksObserver is a global singleton which watches the browser's
   280 // bookmarks and sends you events when things change.
   281 //
   282 // You can register three different kinds of event listeners on
   283 // BookmarksObserver, using addListener, addFolderListener, and
   284 // addRootlistener.
   285 //
   286 //  - addListener(aId, aEvent, aListener) lets you listen to a specific
   287 //    bookmark.  You can listen to the "change", "move", and "remove" events.
   288 //
   289 //  - addFolderListener(aId, aEvent, aListener) lets you listen to a specific
   290 //    bookmark folder.  You can listen to "addchild" and "removechild".
   291 //
   292 //  - addRootListener(aEvent, aListener) lets you listen to the root bookmark
   293 //    node.  This lets you hear "add", "remove", and "change" events on all
   294 //    bookmarks.
   295 //
   297 function BookmarksObserver() {
   298   this._eventsDict = {};
   299   this._folderEventsDict = {};
   300   this._rootEvents = new Events();
   301   Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
   302 }
   304 BookmarksObserver.prototype = {
   305   onBeginUpdateBatch: function () {},
   306   onEndUpdateBatch: function () {},
   307   onItemVisited: function () {},
   309   onItemAdded: function bo_onItemAdded(aId, aFolder, aIndex, aItemType, aURI) {
   310     this._rootEvents.dispatch("add", aId);
   311     this._dispatchToEvents("addchild", aId, this._folderEventsDict[aFolder]);
   312   },
   314   onItemRemoved: function bo_onItemRemoved(aId, aFolder, aIndex) {
   315     this._rootEvents.dispatch("remove", aId);
   316     this._dispatchToEvents("remove", aId, this._eventsDict[aId]);
   317     this._dispatchToEvents("removechild", aId, this._folderEventsDict[aFolder]);
   318   },
   320   onItemChanged: function bo_onItemChanged(aId, aProperty, aIsAnnotationProperty, aValue) {
   321     this._rootEvents.dispatch("change", aProperty);
   322     this._dispatchToEvents("change", aProperty, this._eventsDict[aId]);
   323   },
   325   onItemMoved: function bo_onItemMoved(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
   326     this._dispatchToEvents("move", aId, this._eventsDict[aId]);
   327   },
   329   _dispatchToEvents: function bo_dispatchToEvents(aEvent, aData, aEvents) {
   330     if (aEvents) {
   331       aEvents.dispatch(aEvent, aData);
   332     }
   333   },
   335   _addListenerToDict: function bo_addListenerToDict(aId, aEvent, aListener, aDict) {
   336     var events = aDict[aId];
   337     if (!events) {
   338       events = new Events();
   339       aDict[aId] = events;
   340     }
   341     events.addListener(aEvent, aListener);
   342   },
   344   _removeListenerFromDict: function bo_removeListenerFromDict(aId, aEvent, aListener, aDict) {
   345     var events = aDict[aId];
   346     if (!events) {
   347       return;
   348     }
   349     events.removeListener(aEvent, aListener);
   350     if (events._listeners.length == 0) {
   351       delete aDict[aId];
   352     }
   353   },
   355   addListener: function bo_addListener(aId, aEvent, aListener) {
   356     this._addListenerToDict(aId, aEvent, aListener, this._eventsDict);
   357   },
   359   removeListener: function bo_removeListener(aId, aEvent, aListener) {
   360     this._removeListenerFromDict(aId, aEvent, aListener, this._eventsDict);
   361   },
   363   addFolderListener: function addFolderListener(aId, aEvent, aListener) {
   364     this._addListenerToDict(aId, aEvent, aListener, this._folderEventsDict);
   365   },
   367   removeFolderListener: function removeFolderListener(aId, aEvent, aListener) {
   368     this._removeListenerFromDict(aId, aEvent, aListener, this._folderEventsDict);
   369   },
   371   addRootListener: function addRootListener(aEvent, aListener) {
   372     this._rootEvents.addListener(aEvent, aListener);
   373   },
   375   removeRootListener: function removeRootListener(aEvent, aListener) {
   376     this._rootEvents.removeListener(aEvent, aListener);
   377   },
   379   QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarksObserver,
   380                                          Ci.nsISupportsWeakReference])
   381 };
   383 //=================================================
   384 // Bookmark implementation
   385 //
   386 // Bookmark event listeners are stored in BookmarksObserver, not in the
   387 // Bookmark objects themselves.  Thus, you don't have to hold on to a Bookmark
   388 // object in order for your event listener to stay valid, and Bookmark objects
   389 // not kept alive by the extension can be GC'ed.
   390 //
   391 // A consequence of this is that if you have two different Bookmark objects x
   392 // and y for the same bookmark (i.e., x != y but x.id == y.id), and you do
   393 //
   394 //   x.addListener("foo", fun);
   395 //   y.removeListener("foo", fun);
   396 //
   397 // the second line will in fact remove the listener added in the first line.
   398 //
   400 function Bookmark(aId, aParent, aType) {
   401   this._id = aId;
   402   this._parent = aParent;
   403   this._type = aType || "bookmark";
   404   this._annotations = new Annotations(this._id);
   406   // Our _events object forwards to bookmarksObserver.
   407   var self = this;
   408   this._events = {
   409     addListener: function bookmarkevents_al(aEvent, aListener) {
   410       Utilities.bookmarksObserver.addListener(self._id, aEvent, aListener);
   411     },
   412     removeListener: function bookmarkevents_rl(aEvent, aListener) {
   413       Utilities.bookmarksObserver.removeListener(self._id, aEvent, aListener);
   414     },
   415     QueryInterface: XPCOMUtils.generateQI([Ci.extIEvents])
   416   };
   418   // For our onItemMoved listener, which updates this._parent.
   419   Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
   420 }
   422 Bookmark.prototype = {
   423   get id() {
   424     return this._id;
   425   },
   427   get title() {
   428     return Utilities.bookmarks.getItemTitle(this._id);
   429   },
   431   set title(aTitle) {
   432     Utilities.bookmarks.setItemTitle(this._id, aTitle);
   433   },
   435   get uri() {
   436     return Utilities.bookmarks.getBookmarkURI(this._id);
   437   },
   439   set uri(aURI) {
   440     return Utilities.bookmarks.changeBookmarkURI(this._id, aURI);
   441   },
   443   get description() {
   444     return this._annotations.get("bookmarkProperties/description");
   445   },
   447   set description(aDesc) {
   448     this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
   449   },
   451   get keyword() {
   452     return Utilities.bookmarks.getKeywordForBookmark(this._id);
   453   },
   455   set keyword(aKeyword) {
   456     Utilities.bookmarks.setKeywordForBookmark(this._id, aKeyword);
   457   },
   459   get type() {
   460     return this._type;
   461   },
   463   get parent() {
   464     return this._parent;
   465   },
   467   set parent(aFolder) {
   468     Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
   469     // this._parent is updated in onItemMoved
   470   },
   472   get annotations() {
   473     return this._annotations;
   474   },
   476   get events() {
   477     return this._events;
   478   },
   480   remove : function bm_remove() {
   481     Utilities.bookmarks.removeItem(this._id);
   482   },
   484   onBeginUpdateBatch: function () {},
   485   onEndUpdateBatch: function () {},
   486   onItemAdded: function () {},
   487   onItemVisited: function () {},
   488   onItemRemoved: function () {},
   489   onItemChanged: function () {},
   491   onItemMoved: function(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
   492     if (aId == this._id) {
   493       this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));
   494     }
   495   },
   497   QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmark,
   498                                          Ci.nsINavBookmarksObserver,
   499                                          Ci.nsISupportsWeakReference])
   500 };
   503 //=================================================
   504 // BookmarkFolder implementation
   505 //
   506 // As with Bookmark, events on BookmarkFolder are handled by the
   507 // BookmarksObserver singleton.
   508 //
   510 function BookmarkFolder(aId, aParent) {
   511   this._id = aId;
   512   this._parent = aParent;
   513   this._annotations = new Annotations(this._id);
   515   // Our event listeners are handled by the BookmarksObserver singleton.  This
   516   // is a bit complicated because there are three different kinds of events we
   517   // might want to listen to here:
   518   //
   519   //  - If this._parent is null, we're the root bookmark folder, and all our
   520   //    listeners should be root listeners.
   521   //
   522   //  - Otherwise, events ending with "child" (addchild, removechild) are
   523   //    handled by a folder listener.
   524   //
   525   //  - Other events are handled by a vanilla bookmark listener.
   527   var self = this;
   528   this._events = {
   529     addListener: function bmfevents_al(aEvent, aListener) {
   530       if (self._parent) {
   531         if (/child$/.test(aEvent)) {
   532           Utilities.bookmarksObserver.addFolderListener(self._id, aEvent, aListener);
   533         }
   534         else {
   535           Utilities.bookmarksObserver.addListener(self._id, aEvent, aListener);
   536         }
   537       }
   538       else {
   539         Utilities.bookmarksObserver.addRootListener(aEvent, aListener);
   540       }
   541     },
   542     removeListener: function bmfevents_rl(aEvent, aListener) {
   543       if (self._parent) {
   544         if (/child$/.test(aEvent)) {
   545           Utilities.bookmarksObserver.removeFolderListener(self._id, aEvent, aListener);
   546         }
   547         else {
   548           Utilities.bookmarksObserver.removeListener(self._id, aEvent, aListener);
   549         }
   550       }
   551       else {
   552         Utilities.bookmarksObserver.removeRootListener(aEvent, aListener);
   553       }
   554     },
   555     QueryInterface: XPCOMUtils.generateQI([Ci.extIEvents])
   556   };
   558   // For our onItemMoved listener, which updates this._parent.
   559   Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
   560 }
   562 BookmarkFolder.prototype = {
   563   get id() {
   564     return this._id;
   565   },
   567   get title() {
   568     return Utilities.bookmarks.getItemTitle(this._id);
   569   },
   571   set title(aTitle) {
   572     Utilities.bookmarks.setItemTitle(this._id, aTitle);
   573   },
   575   get description() {
   576     return this._annotations.get("bookmarkProperties/description");
   577   },
   579   set description(aDesc) {
   580     this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
   581   },
   583   get type() {
   584     return "folder";
   585   },
   587   get parent() {
   588     return this._parent;
   589   },
   591   set parent(aFolder) {
   592     Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
   593     // this._parent is updated in onItemMoved
   594   },
   596   get annotations() {
   597     return this._annotations;
   598   },
   600   get events() {
   601     return this._events;
   602   },
   604   get children() {
   605     var items = [];
   607     var options = Utilities.history.getNewQueryOptions();
   608     var query = Utilities.history.getNewQuery();
   609     query.setFolders([this._id], 1);
   610     var result = Utilities.history.executeQuery(query, options);
   611     var rootNode = result.root;
   612     rootNode.containerOpen = true;
   613     var cc = rootNode.childCount;
   614     for (var i=0; i<cc; ++i) {
   615       var node = rootNode.getChild(i);
   616       if (node.type == node.RESULT_TYPE_FOLDER) {
   617         var folder = new BookmarkFolder(node.itemId, this._id);
   618         items.push(folder);
   619       }
   620       else if (node.type == node.RESULT_TYPE_SEPARATOR) {
   621         var separator = new Bookmark(node.itemId, this._id, "separator");
   622         items.push(separator);
   623       }
   624       else {
   625         var bookmark = new Bookmark(node.itemId, this._id, "bookmark");
   626         items.push(bookmark);
   627       }
   628     }
   629     rootNode.containerOpen = false;
   631     return items;
   632   },
   634   addBookmark: function bmf_addbm(aTitle, aUri) {
   635     var newBookmarkID = Utilities.bookmarks.insertBookmark(this._id, aUri, Utilities.bookmarks.DEFAULT_INDEX, aTitle);
   636     var newBookmark = new Bookmark(newBookmarkID, this, "bookmark");
   637     return newBookmark;
   638   },
   640   addSeparator: function bmf_addsep() {
   641     var newBookmarkID = Utilities.bookmarks.insertSeparator(this._id, Utilities.bookmarks.DEFAULT_INDEX);
   642     var newBookmark = new Bookmark(newBookmarkID, this, "separator");
   643     return newBookmark;
   644   },
   646   addFolder: function bmf_addfolder(aTitle) {
   647     var newFolderID = Utilities.bookmarks.createFolder(this._id, aTitle, Utilities.bookmarks.DEFAULT_INDEX);
   648     var newFolder = new BookmarkFolder(newFolderID, this);
   649     return newFolder;
   650   },
   652   remove: function bmf_remove() {
   653     Utilities.bookmarks.removeItem(this._id);
   654   },
   656   // observer
   657   onBeginUpdateBatch: function () {},
   658   onEndUpdateBatch : function () {},
   659   onItemAdded : function () {},
   660   onItemRemoved : function () {},
   661   onItemChanged : function () {},
   663   onItemMoved: function bf_onItemMoved(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
   664     if (this._id == aId) {
   665       this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));
   666     }
   667   },
   669   QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder,
   670                                          Ci.nsINavBookmarksObserver,
   671                                          Ci.nsISupportsWeakReference])
   672 };
   674 //=================================================
   675 // BookmarkRoots implementation
   676 function BookmarkRoots() {
   677 }
   679 BookmarkRoots.prototype = {
   680   get menu() {
   681     if (!this._menu)
   682       this._menu = new BookmarkFolder(Utilities.bookmarks.bookmarksMenuFolder, null);
   684     return this._menu;
   685   },
   687   get toolbar() {
   688     if (!this._toolbar)
   689       this._toolbar = new BookmarkFolder(Utilities.bookmarks.toolbarFolder, null);
   691     return this._toolbar;
   692   },
   694   get tags() {
   695     if (!this._tags)
   696       this._tags = new BookmarkFolder(Utilities.bookmarks.tagsFolder, null);
   698     return this._tags;
   699   },
   701   get unfiled() {
   702     if (!this._unfiled)
   703       this._unfiled = new BookmarkFolder(Utilities.bookmarks.unfiledBookmarksFolder, null);
   705     return this._unfiled;
   706   },
   708   QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkRoots])
   709 };
   712 //=================================================
   713 // Factory - Treat Application as a singleton
   714 // XXX This is required, because we're registered for the 'JavaScript global
   715 // privileged property' category, whose handler always calls createInstance.
   716 // See bug 386535.
   717 var gSingleton = null;
   718 var ApplicationFactory = {
   719   createInstance: function af_ci(aOuter, aIID) {
   720     if (aOuter != null)
   721       throw Components.results.NS_ERROR_NO_AGGREGATION;
   723     if (gSingleton == null) {
   724       gSingleton = new Application();
   725     }
   727     return gSingleton.QueryInterface(aIID);
   728   }
   729 };
   732 #include ../../../toolkit/components/exthelper/extApplication.js
   734 //=================================================
   735 // Application constructor
   736 function Application() {
   737   this.initToolkitHelpers();
   738 }
   740 //=================================================
   741 // Application implementation
   742 function ApplicationPrototype() {
   743   // for nsIClassInfo + XPCOMUtils
   744   this.classID = APPLICATION_CID;
   746   // redefine the default factory for XPCOMUtils
   747   this._xpcom_factory = ApplicationFactory;
   749   // for nsISupports
   750   this.QueryInterface = XPCOMUtils.generateQI([
   751     Ci.fuelIApplication,
   752     Ci.extIApplication,
   753     Ci.nsIObserver,
   754     Ci.nsISupportsWeakReference
   755   ]);
   757   // for nsIClassInfo
   758   this.classInfo = XPCOMUtils.generateCI({
   759     classID: APPLICATION_CID,
   760     contractID: APPLICATION_CONTRACTID,
   761     interfaces: [
   762       Ci.fuelIApplication,
   763       Ci.extIApplication,
   764       Ci.nsIObserver
   765     ],
   766     flags: Ci.nsIClassInfo.SINGLETON
   767   });
   769   // for nsIObserver
   770   this.observe = function (aSubject, aTopic, aData) {
   771     // Call the extApplication version of this function first
   772     var superPrototype = Object.getPrototypeOf(Object.getPrototypeOf(this));
   773     superPrototype.observe.call(this, aSubject, aTopic, aData);
   774     if (aTopic == "xpcom-shutdown") {
   775       this._obs.removeObserver(this, "xpcom-shutdown");
   776       Utilities.free();
   777     }
   778   };
   780   Object.defineProperty(this, "bookmarks", {
   781     get: function bookmarks () {
   782       let bookmarks = new BookmarkRoots();
   783       Object.defineProperty(this, "bookmarks", { value: bookmarks });
   784       return this.bookmarks;
   785     },
   786     enumerable: true,
   787     configurable: true
   788   });
   790   Object.defineProperty(this, "windows", {
   791     get: function windows() {
   792       var win = [];
   793       var browserEnum = Utilities.windowMediator.getEnumerator("navigator:browser");
   795       while (browserEnum.hasMoreElements())
   796         win.push(getWindow(browserEnum.getNext()));
   798       return win;
   799     },
   800     enumerable: true,
   801     configurable: true
   802   });
   804   Object.defineProperty(this, "activeWindow", {
   805     get: () => getWindow(Utilities.windowMediator.getMostRecentWindow("navigator:browser")),
   806     enumerable: true,
   807     configurable: true
   808   });
   810 };
   812 // set the proto, defined in extApplication.js
   813 ApplicationPrototype.prototype = extApplication.prototype;
   815 Application.prototype = new ApplicationPrototype();
   817 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Application]);

mercurial