Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / browser / fuel / fuelApplication.js
blob132df0684ea070644b16a327b68e131a9b4c8fbc
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   }
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;
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");
91 Window.prototype = {
92   get events() {
93     return this._events;
94   },
96   get _tabbrowser() {
97     return this._window.gBrowser;
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])
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;
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");
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])
242 //=================================================
243 // Annotations implementation
244 function Annotations(aId) {
245   this._id = aId;
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])
276 //=================================================
277 // BookmarksObserver implementation (internal class)
279 // BookmarksObserver is a global singleton which watches the browser's
280 // bookmarks and sends you events when things change.
282 // You can register three different kinds of event listeners on
283 // BookmarksObserver, using addListener, addFolderListener, and
284 // addRootlistener.
286 //  - addListener(aId, aEvent, aListener) lets you listen to a specific
287 //    bookmark.  You can listen to the "change", "move", and "remove" events.
289 //  - addFolderListener(aId, aEvent, aListener) lets you listen to a specific
290 //    bookmark folder.  You can listen to "addchild" and "removechild".
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.
297 function BookmarksObserver() {
298   this._eventsDict = {};
299   this._folderEventsDict = {};
300   this._rootEvents = new Events();
301   Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
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.nsINavBookmarkObserver,
380                                          Ci.nsISupportsWeakReference])
383 //=================================================
384 // Bookmark implementation
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.
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
394 //   x.addListener("foo", fun);
395 //   y.removeListener("foo", fun);
397 // the second line will in fact remove the listener added in the first line.
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);
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.nsINavBookmarkObserver,
499                                          Ci.nsISupportsWeakReference])
503 //=================================================
504 // BookmarkFolder implementation
506 // As with Bookmark, events on BookmarkFolder are handled by the
507 // BookmarksObserver singleton.
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);
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.nsINavBookmarkObserver,
671                                          Ci.nsISupportsWeakReference])
674 //=================================================
675 // BookmarkRoots implementation
676 function BookmarkRoots() {
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])
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   }
732 #include ../../toolkit/components/exthelper/extApplication.js
734 //=================================================
735 // Application constructor
736 function Application() {
737   this.initToolkitHelpers();
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   });
812 // set the proto, defined in extApplication.js
813 ApplicationPrototype.prototype = extApplication.prototype;
815 Application.prototype = new ApplicationPrototype();
817 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Application]);