browser/fuel/src/fuelApplication.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:e68d9b1a47c0
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/. */
4
5 const Ci = Components.interfaces;
6 const Cc = Components.classes;
7
8 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
9
10 const APPLICATION_CID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66");
11 const APPLICATION_CONTRACTID = "@mozilla.org/fuel/application;1";
12
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 },
22
23 get bookmarksObserver() {
24 let bookmarksObserver = new BookmarksObserver();
25 this.__defineGetter__("bookmarksObserver", function() bookmarksObserver);
26 return this.bookmarksObserver;
27 },
28
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 },
35
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 },
42
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 },
49
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 },
56
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 };
65
66
67 //=================================================
68 // Window implementation
69
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 }
79
80 // Don't call new Window() directly; use getWindow instead.
81 function Window(aWindow) {
82 this._window = aWindow;
83 this._events = new Events();
84
85 this._watch("TabOpen");
86 this._watch("TabMove");
87 this._watch("TabClose");
88 this._watch("TabSelect");
89 }
90
91 Window.prototype = {
92 get events() {
93 return this._events;
94 },
95
96 get _tabbrowser() {
97 return this._window.getBrowser();
98 },
99
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 },
108
109 handleEvent: function win_handleEvent(aEvent) {
110 this._events.dispatch(aEvent.type, getBrowserTab(this, aEvent.originalTarget.linkedBrowser));
111 },
112
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 },
120
121 get activeTab() {
122 return getBrowserTab(this, this._tabbrowser.selectedBrowser);
123 },
124
125 open: function win_open(aURI) {
126 return getBrowserTab(this, this._tabbrowser.addTab(aURI.spec).linkedBrowser);
127 },
128
129 QueryInterface: XPCOMUtils.generateQI([Ci.fuelIWindow])
130 };
131
132 //=================================================
133 // BrowserTab implementation
134
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 }
147
148 return fuelBrowserTab;
149 }
150
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();
156
157 this._watch("load");
158 }
159
160 BrowserTab.prototype = {
161 get _tabbrowser() {
162 return this._window._tabbrowser;
163 },
164
165 get uri() {
166 return this._browser.currentURI;
167 },
168
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 },
177
178 get events() {
179 return this._events;
180 },
181
182 get window() {
183 return this._window;
184 },
185
186 get document() {
187 return this._browser.contentDocument;
188 },
189
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 },
197
198 handleEvent: function bt_handleEvent(aEvent) {
199 if (aEvent.type == "load") {
200 if (!(aEvent.originalTarget instanceof Ci.nsIDOMDocument))
201 return;
202
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 },
216
217 load: function bt_load(aURI) {
218 this._browser.loadURI(aURI.spec, null, null);
219 },
220
221 focus: function bt_focus() {
222 this._tabbrowser.selectedTab = this._getTab();
223 this._tabbrowser.focus();
224 },
225
226 close: function bt_close() {
227 this._tabbrowser.removeTab(this._getTab());
228 },
229
230 moveBefore: function bt_movebefore(aBefore) {
231 this._tabbrowser.moveTabTo(this._getTab(), aBefore.index);
232 },
233
234 moveToEnd: function bt_moveend() {
235 this._tabbrowser.moveTabTo(this._getTab(), this._tabbrowser.browsers.length);
236 },
237
238 QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBrowserTab])
239 };
240
241
242 //=================================================
243 // Annotations implementation
244 function Annotations(aId) {
245 this._id = aId;
246 }
247
248 Annotations.prototype = {
249 get names() {
250 return Utilities.annotations.getItemAnnotationNames(this._id);
251 },
252
253 has: function ann_has(aName) {
254 return Utilities.annotations.itemHasAnnotation(this._id, aName);
255 },
256
257 get: function ann_get(aName) {
258 if (this.has(aName))
259 return Utilities.annotations.getItemAnnotation(this._id, aName);
260 return null;
261 },
262
263 set: function ann_set(aName, aValue, aExpiration) {
264 Utilities.annotations.setItemAnnotation(this._id, aName, aValue, 0, aExpiration);
265 },
266
267 remove: function ann_remove(aName) {
268 if (aName)
269 Utilities.annotations.removeItemAnnotation(this._id, aName);
270 },
271
272 QueryInterface: XPCOMUtils.generateQI([Ci.fuelIAnnotations])
273 };
274
275
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 //
296
297 function BookmarksObserver() {
298 this._eventsDict = {};
299 this._folderEventsDict = {};
300 this._rootEvents = new Events();
301 Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
302 }
303
304 BookmarksObserver.prototype = {
305 onBeginUpdateBatch: function () {},
306 onEndUpdateBatch: function () {},
307 onItemVisited: function () {},
308
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 },
313
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 },
319
320 onItemChanged: function bo_onItemChanged(aId, aProperty, aIsAnnotationProperty, aValue) {
321 this._rootEvents.dispatch("change", aProperty);
322 this._dispatchToEvents("change", aProperty, this._eventsDict[aId]);
323 },
324
325 onItemMoved: function bo_onItemMoved(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
326 this._dispatchToEvents("move", aId, this._eventsDict[aId]);
327 },
328
329 _dispatchToEvents: function bo_dispatchToEvents(aEvent, aData, aEvents) {
330 if (aEvents) {
331 aEvents.dispatch(aEvent, aData);
332 }
333 },
334
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 },
343
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 },
354
355 addListener: function bo_addListener(aId, aEvent, aListener) {
356 this._addListenerToDict(aId, aEvent, aListener, this._eventsDict);
357 },
358
359 removeListener: function bo_removeListener(aId, aEvent, aListener) {
360 this._removeListenerFromDict(aId, aEvent, aListener, this._eventsDict);
361 },
362
363 addFolderListener: function addFolderListener(aId, aEvent, aListener) {
364 this._addListenerToDict(aId, aEvent, aListener, this._folderEventsDict);
365 },
366
367 removeFolderListener: function removeFolderListener(aId, aEvent, aListener) {
368 this._removeListenerFromDict(aId, aEvent, aListener, this._folderEventsDict);
369 },
370
371 addRootListener: function addRootListener(aEvent, aListener) {
372 this._rootEvents.addListener(aEvent, aListener);
373 },
374
375 removeRootListener: function removeRootListener(aEvent, aListener) {
376 this._rootEvents.removeListener(aEvent, aListener);
377 },
378
379 QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarksObserver,
380 Ci.nsISupportsWeakReference])
381 };
382
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 //
399
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);
405
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 };
417
418 // For our onItemMoved listener, which updates this._parent.
419 Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
420 }
421
422 Bookmark.prototype = {
423 get id() {
424 return this._id;
425 },
426
427 get title() {
428 return Utilities.bookmarks.getItemTitle(this._id);
429 },
430
431 set title(aTitle) {
432 Utilities.bookmarks.setItemTitle(this._id, aTitle);
433 },
434
435 get uri() {
436 return Utilities.bookmarks.getBookmarkURI(this._id);
437 },
438
439 set uri(aURI) {
440 return Utilities.bookmarks.changeBookmarkURI(this._id, aURI);
441 },
442
443 get description() {
444 return this._annotations.get("bookmarkProperties/description");
445 },
446
447 set description(aDesc) {
448 this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
449 },
450
451 get keyword() {
452 return Utilities.bookmarks.getKeywordForBookmark(this._id);
453 },
454
455 set keyword(aKeyword) {
456 Utilities.bookmarks.setKeywordForBookmark(this._id, aKeyword);
457 },
458
459 get type() {
460 return this._type;
461 },
462
463 get parent() {
464 return this._parent;
465 },
466
467 set parent(aFolder) {
468 Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
469 // this._parent is updated in onItemMoved
470 },
471
472 get annotations() {
473 return this._annotations;
474 },
475
476 get events() {
477 return this._events;
478 },
479
480 remove : function bm_remove() {
481 Utilities.bookmarks.removeItem(this._id);
482 },
483
484 onBeginUpdateBatch: function () {},
485 onEndUpdateBatch: function () {},
486 onItemAdded: function () {},
487 onItemVisited: function () {},
488 onItemRemoved: function () {},
489 onItemChanged: function () {},
490
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 },
496
497 QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmark,
498 Ci.nsINavBookmarksObserver,
499 Ci.nsISupportsWeakReference])
500 };
501
502
503 //=================================================
504 // BookmarkFolder implementation
505 //
506 // As with Bookmark, events on BookmarkFolder are handled by the
507 // BookmarksObserver singleton.
508 //
509
510 function BookmarkFolder(aId, aParent) {
511 this._id = aId;
512 this._parent = aParent;
513 this._annotations = new Annotations(this._id);
514
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.
526
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 };
557
558 // For our onItemMoved listener, which updates this._parent.
559 Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
560 }
561
562 BookmarkFolder.prototype = {
563 get id() {
564 return this._id;
565 },
566
567 get title() {
568 return Utilities.bookmarks.getItemTitle(this._id);
569 },
570
571 set title(aTitle) {
572 Utilities.bookmarks.setItemTitle(this._id, aTitle);
573 },
574
575 get description() {
576 return this._annotations.get("bookmarkProperties/description");
577 },
578
579 set description(aDesc) {
580 this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
581 },
582
583 get type() {
584 return "folder";
585 },
586
587 get parent() {
588 return this._parent;
589 },
590
591 set parent(aFolder) {
592 Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
593 // this._parent is updated in onItemMoved
594 },
595
596 get annotations() {
597 return this._annotations;
598 },
599
600 get events() {
601 return this._events;
602 },
603
604 get children() {
605 var items = [];
606
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;
630
631 return items;
632 },
633
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 },
639
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 },
645
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 },
651
652 remove: function bmf_remove() {
653 Utilities.bookmarks.removeItem(this._id);
654 },
655
656 // observer
657 onBeginUpdateBatch: function () {},
658 onEndUpdateBatch : function () {},
659 onItemAdded : function () {},
660 onItemRemoved : function () {},
661 onItemChanged : function () {},
662
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 },
668
669 QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder,
670 Ci.nsINavBookmarksObserver,
671 Ci.nsISupportsWeakReference])
672 };
673
674 //=================================================
675 // BookmarkRoots implementation
676 function BookmarkRoots() {
677 }
678
679 BookmarkRoots.prototype = {
680 get menu() {
681 if (!this._menu)
682 this._menu = new BookmarkFolder(Utilities.bookmarks.bookmarksMenuFolder, null);
683
684 return this._menu;
685 },
686
687 get toolbar() {
688 if (!this._toolbar)
689 this._toolbar = new BookmarkFolder(Utilities.bookmarks.toolbarFolder, null);
690
691 return this._toolbar;
692 },
693
694 get tags() {
695 if (!this._tags)
696 this._tags = new BookmarkFolder(Utilities.bookmarks.tagsFolder, null);
697
698 return this._tags;
699 },
700
701 get unfiled() {
702 if (!this._unfiled)
703 this._unfiled = new BookmarkFolder(Utilities.bookmarks.unfiledBookmarksFolder, null);
704
705 return this._unfiled;
706 },
707
708 QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkRoots])
709 };
710
711
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;
722
723 if (gSingleton == null) {
724 gSingleton = new Application();
725 }
726
727 return gSingleton.QueryInterface(aIID);
728 }
729 };
730
731
732 #include ../../../toolkit/components/exthelper/extApplication.js
733
734 //=================================================
735 // Application constructor
736 function Application() {
737 this.initToolkitHelpers();
738 }
739
740 //=================================================
741 // Application implementation
742 function ApplicationPrototype() {
743 // for nsIClassInfo + XPCOMUtils
744 this.classID = APPLICATION_CID;
745
746 // redefine the default factory for XPCOMUtils
747 this._xpcom_factory = ApplicationFactory;
748
749 // for nsISupports
750 this.QueryInterface = XPCOMUtils.generateQI([
751 Ci.fuelIApplication,
752 Ci.extIApplication,
753 Ci.nsIObserver,
754 Ci.nsISupportsWeakReference
755 ]);
756
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 });
768
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 };
779
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 });
789
790 Object.defineProperty(this, "windows", {
791 get: function windows() {
792 var win = [];
793 var browserEnum = Utilities.windowMediator.getEnumerator("navigator:browser");
794
795 while (browserEnum.hasMoreElements())
796 win.push(getWindow(browserEnum.getNext()));
797
798 return win;
799 },
800 enumerable: true,
801 configurable: true
802 });
803
804 Object.defineProperty(this, "activeWindow", {
805 get: () => getWindow(Utilities.windowMediator.getMostRecentWindow("navigator:browser")),
806 enumerable: true,
807 configurable: true
808 });
809
810 };
811
812 // set the proto, defined in extApplication.js
813 ApplicationPrototype.prototype = extApplication.prototype;
814
815 Application.prototype = new ApplicationPrototype();
816
817 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Application]);
818

mercurial