1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
14 * The Original Code is FUEL.
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.
21 * Mark Finkle <mfinkle@mozilla.com> (Original Author)
22 * John Resig <jresig@mozilla.com> (Original Author)
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.
36 * ***** END LICENSE BLOCK ***** */
38 const Ci
= Components
.interfaces
;
39 const Cc
= Components
.classes
;
41 Components
.utils
.import("resource://gre/modules/XPCOMUtils.jsm");
43 //=================================================
44 // Singleton that holds services and utilities
48 if (!this._bookmarks
) {
49 this._bookmarks
= Cc
["@mozilla.org/browser/nav-bookmarks-service;1"].
50 getService(Ci
.nsINavBookmarksService
);
52 return this._bookmarks
;
57 if (!this._livemarks
) {
58 this._livemarks
= Cc
["@mozilla.org/browser/livemark-service;2"].
59 getService(Ci
.nsILivemarkService
);
61 return this._livemarks
;
66 if (!this._annotations
) {
67 this._annotations
= Cc
["@mozilla.org/browser/annotation-service;1"].
68 getService(Ci
.nsIAnnotationService
);
70 return this._annotations
;
76 this._history
= Cc
["@mozilla.org/browser/nav-history-service;1"].
77 getService(Ci
.nsINavHistoryService
);
82 _windowMediator
: null,
83 get windowMediator() {
84 if (!this._windowMediator
) {
85 this._windowMediator
= Cc
["@mozilla.org/appshell/window-mediator;1"].
86 getService(Ci
.nsIWindowMediator
);
88 return this._windowMediator
;
91 makeURI : function(aSpec
) {
94 var ios
= Cc
["@mozilla.org/network/io-service;1"].getService(Ci
.nsIIOService
);
95 return ios
.newURI(aSpec
, null, null);
99 this._bookmarks
= null;
100 this._livemarks
= null;
101 this._annotations
= null;
102 this._history
= null;
103 this._windowMediator
= null;
108 //=================================================
109 // Window implementation
110 function Window(aWindow
) {
111 this._window
= aWindow
;
112 this._tabbrowser
= aWindow
.getBrowser();
113 this._events
= new Events();
116 this._watch("TabOpen");
117 this._watch("TabMove");
118 this._watch("TabClose");
119 this._watch("TabSelect");
122 gShutdown
.push(function() { self
._shutdown(); });
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.
134 _watch
: function win_watch(aType
) {
136 this._tabbrowser
.addEventListener(aType
,
137 this._cleanup
[aType
] = function(e
){ self
._event(e
); },
142 * Helper event callback used to redirect events made on the XBL element
144 _event
: function win_event(aEvent
) {
145 this._events
.dispatch(aEvent
.type
, "");
150 var browsers
= this._tabbrowser
.browsers
;
152 for (var i
=0; i
<browsers
.length
; i
++)
153 tabs
.push(new BrowserTab(this._window
, browsers
[i
]));
159 return new BrowserTab(this._window
, this._tabbrowser
.selectedBrowser
);
162 open
: function win_open(aURI
) {
163 return new BrowserTab(this._window
, this._tabbrowser
.addTab(aURI
.spec
).linkedBrowser
);
166 _shutdown
: function win_shutdown() {
167 for (var type
in this._cleanup
)
168 this._tabbrowser
.removeEventListener(type
, this._cleanup
[type
], true);
169 this._cleanup
= null;
172 this._tabbrowser
= null;
176 QueryInterface
: XPCOMUtils
.generateQI([Ci
.fuelIWindow
])
180 //=================================================
181 // BrowserTab implementation
182 function BrowserTab(aWindow
, aBrowser
) {
183 this._window
= aWindow
;
184 this._tabbrowser
= aWindow
.getBrowser();
185 this._browser
= aBrowser
;
186 this._events
= new Events();
192 gShutdown
.push(function() { self
._shutdown(); });
195 BrowserTab
.prototype = {
197 return this._browser
.currentURI
;
201 var tabs
= this._tabbrowser
.mTabs
;
202 for (var i
=0; i
<tabs
.length
; i
++) {
203 if (tabs
[i
].linkedBrowser
== this._browser
)
218 return this._browser
.contentDocument
;
222 * Helper used to setup event handlers on the XBL element
224 _watch
: function bt_watch(aType
) {
226 this._browser
.addEventListener(aType
,
227 this._cleanup
[aType
] = function(e
){ self
._event(e
); },
232 * Helper event callback used to redirect events made on the XBL element
234 _event
: function bt_event(aEvent
) {
235 if (aEvent
.type
== "load") {
236 if (!(aEvent
.originalTarget
instanceof Ci
.nsIDOMHTMLDocument
))
239 if (aEvent
.originalTarget
.defaultView
instanceof Ci
.nsIDOMWindowInternal
&&
240 aEvent
.originalTarget
.defaultView
.frameElement
)
244 this._events
.dispatch(aEvent
.type
, "");
248 * Helper used to determine the index offset of the browsertab
250 _getTab
: function bt_gettab() {
251 var tabs
= this._tabbrowser
.mTabs
;
252 return tabs
[this.index
] || null;
255 load
: function bt_load(aURI
) {
256 this._browser
.loadURI(aURI
.spec
, null, null);
259 focus
: function bt_focus() {
260 this._tabbrowser
.selectedTab
= this._getTab();
261 this._tabbrowser
.focus();
264 close
: function bt_close() {
265 this._tabbrowser
.removeTab(this._getTab());
268 moveBefore
: function bt_movebefore(aBefore
) {
269 this._tabbrowser
.moveTabTo(this._getTab(), aBefore
.index
);
272 moveToEnd
: function bt_moveend() {
273 this._tabbrowser
.moveTabTo(this._getTab(), this._tabbrowser
.browsers
.length
);
276 _shutdown
: function bt_shutdown() {
277 for (var type
in this._cleanup
)
278 this._browser
.removeEventListener(type
, this._cleanup
[type
], true);
279 this._cleanup
= null;
282 this._tabbrowser
= null;
283 this._browser
= null;
287 QueryInterface
: XPCOMUtils
.generateQI([Ci
.fuelIBrowserTab
])
291 //=================================================
292 // Annotations implementation
293 function Annotations(aId
) {
297 Annotations
.prototype = {
299 return Utilities
.annotations
.getItemAnnotationNames(this._id
, {});
302 has
: function ann_has(aName
) {
303 return Utilities
.annotations
.itemHasAnnotation(this._id
, aName
);
306 get : function(aName
) {
308 return Utilities
.annotations
.getItemAnnotation(this._id
, aName
);
312 set : function(aName
, aValue
, aExpiration
) {
313 Utilities
.annotations
.setItemAnnotation(this._id
, aName
, aValue
, 0, aExpiration
);
316 remove
: function ann_remove(aName
) {
318 Utilities
.annotations
.removeItemAnnotation(this._id
, aName
);
321 QueryInterface
: XPCOMUtils
.generateQI([Ci
.fuelIAnnotations
])
325 //=================================================
326 // Bookmark implementation
327 function Bookmark(aId
, aParent
, aType
) {
329 this._parent
= aParent
;
330 this._type
= aType
|| "bookmark";
331 this._annotations
= new Annotations(this._id
);
332 this._events
= new Events();
334 Utilities
.bookmarks
.addObserver(this, false);
337 gShutdown
.push(function() { self
._shutdown(); });
340 Bookmark
.prototype = {
341 _shutdown
: function bm_shutdown() {
342 this._annotations
= null;
345 Utilities
.bookmarks
.removeObserver(this);
353 return Utilities
.bookmarks
.getItemTitle(this._id
);
357 Utilities
.bookmarks
.setItemTitle(this._id
, aTitle
);
361 return Utilities
.bookmarks
.getBookmarkURI(this._id
);
365 return Utilities
.bookmarks
.changeBookmarkURI(this._id
, aURI
);
369 return this._annotations
.get("bookmarkProperties/description");
372 set description(aDesc
) {
373 this._annotations
.set("bookmarkProperties/description", aDesc
, Ci
.nsIAnnotationService
.EXPIRE_NEVER
);
377 return Utilities
.bookmarks
.getKeywordForBookmark(this._id
);
380 set keyword(aKeyword
) {
381 Utilities
.bookmarks
.setKeywordForBookmark(this._id
, aKeyword
);
392 set parent(aFolder
) {
393 Utilities
.bookmarks
.moveItem(this._id
, aFolder
.id
, Utilities
.bookmarks
.DEFAULT_INDEX
);
394 // this._parent is updated in onItemMoved
398 return this._annotations
;
405 remove
: function bm_remove() {
406 Utilities
.bookmarks
.removeItem(this._id
);
410 onBeginUpdateBatch
: function bm_obub() {
413 onEndUpdateBatch
: function bm_oeub() {
416 onItemAdded
: function bm_oia(aId
, aFolder
, aIndex
) {
417 // bookmark object doesn't exist at this point
420 onItemRemoved
: function bm_oir(aId
, aFolder
, aIndex
) {
422 this._events
.dispatch("remove", aId
);
425 onItemChanged
: function bm_oic(aId
, aProperty
, aIsAnnotationProperty
, aValue
) {
427 this._events
.dispatch("change", aProperty
);
430 onItemVisited
: function bm_oiv(aId
, aVisitID
, aTime
) {
433 onItemMoved
: function bm_oim(aId
, aOldParent
, aOldIndex
, aNewParent
, aNewIndex
) {
434 if (this._id
== aId
) {
435 this._parent
= new BookmarkFolder(aNewParent
, Utilities
.bookmarks
.getFolderIdForItem(aNewParent
));
436 this._events
.dispatch("move", aId
);
440 QueryInterface
: XPCOMUtils
.generateQI([Ci
.fuelIBookmark
, Ci
.nsINavBookmarkObserver
])
444 //=================================================
445 // BookmarkFolder implementation
446 function BookmarkFolder(aId
, aParent
) {
448 this._parent
= aParent
;
449 this._annotations
= new Annotations(this._id
);
450 this._events
= new Events();
452 Utilities
.bookmarks
.addObserver(this, false);
455 gShutdown
.push(function() { self
._shutdown(); });
458 BookmarkFolder
.prototype = {
459 _shutdown
: function bmf_shutdown() {
460 this._annotations
= null;
463 Utilities
.bookmarks
.removeObserver(this);
471 return Utilities
.bookmarks
.getItemTitle(this._id
);
475 Utilities
.bookmarks
.setItemTitle(this._id
, aTitle
);
479 return this._annotations
.get("bookmarkProperties/description");
482 set description(aDesc
) {
483 this._annotations
.set("bookmarkProperties/description", aDesc
, Ci
.nsIAnnotationService
.EXPIRE_NEVER
);
494 set parent(aFolder
) {
495 Utilities
.bookmarks
.moveItem(this._id
, aFolder
.id
, Utilities
.bookmarks
.DEFAULT_INDEX
);
496 // this._parent is updated in onItemMoved
500 return this._annotations
;
510 var options
= Utilities
.history
.getNewQueryOptions();
511 var query
= Utilities
.history
.getNewQuery();
512 query
.setFolders([this._id
], 1);
513 var result
= Utilities
.history
.executeQuery(query
, options
);
514 var rootNode
= result
.root
;
515 rootNode
.containerOpen
= true;
516 var cc
= rootNode
.childCount
;
517 for (var i
=0; i
<cc
; ++i
) {
518 var node
= rootNode
.getChild(i
);
519 if (node
.type
== node
.RESULT_TYPE_FOLDER
) {
520 var folder
= new BookmarkFolder(node
.itemId
, this._id
);
523 else if (node
.type
== node
.RESULT_TYPE_SEPARATOR
) {
524 var separator
= new Bookmark(node
.itemId
, this._id
, "separator");
525 items
.push(separator
);
528 var bookmark
= new Bookmark(node
.itemId
, this._id
, "bookmark");
529 items
.push(bookmark
);
532 rootNode
.containerOpen
= false;
537 addBookmark
: function bmf_addbm(aTitle
, aUri
) {
538 var newBookmarkID
= Utilities
.bookmarks
.insertBookmark(this._id
, aUri
, Utilities
.bookmarks
.DEFAULT_INDEX
, aTitle
);
539 var newBookmark
= new Bookmark(newBookmarkID
, this, "bookmark");
543 addSeparator
: function bmf_addsep() {
544 var newBookmarkID
= Utilities
.bookmarks
.insertSeparator(this._id
, Utilities
.bookmarks
.DEFAULT_INDEX
);
545 var newBookmark
= new Bookmark(newBookmarkID
, this, "separator");
549 addFolder
: function bmf_addfolder(aTitle
) {
550 var newFolderID
= Utilities
.bookmarks
.createFolder(this._id
, aTitle
, Utilities
.bookmarks
.DEFAULT_INDEX
);
551 var newFolder
= new BookmarkFolder(newFolderID
, this);
555 remove
: function bmf_remove() {
556 Utilities
.bookmarks
.removeFolder(this._id
);
560 onBeginUpdateBatch
: function bmf_obub() {
563 onEndUpdateBatch
: function bmf_oeub() {
566 onItemAdded
: function bmf_oia(aId
, aFolder
, aIndex
) {
567 // handle root folder events
569 this._events
.dispatch("add", aId
);
571 // handle this folder events
572 if (this._id
== aFolder
)
573 this._events
.dispatch("addchild", aId
);
576 onItemRemoved
: function bmf_oir(aId
, aFolder
, aIndex
) {
577 // handle root folder events
578 if (!this._parent
|| this._id
== aId
)
579 this._events
.dispatch("remove", aId
);
581 // handle this folder events
582 if (this._id
== aFolder
)
583 this._events
.dispatch("removechild", aId
);
586 onItemChanged
: function bmf_oic(aId
, aProperty
, aIsAnnotationProperty
, aValue
) {
587 // handle root folder and this folder events
588 if (!this._parent
|| this._id
== aId
)
589 this._events
.dispatch("change", aProperty
);
592 onItemVisited
: function bmf_oiv(aId
, aVisitID
, aTime
) {
595 onItemMoved
: function bmf_oim(aId
, aOldParent
, aOldIndex
, aNewParent
, aNewIndex
) {
596 // handle this folder event, root folder cannot be moved
597 if (this._id
== aId
) {
598 this._parent
= new BookmarkFolder(aNewParent
, Utilities
.bookmarks
.getFolderIdForItem(aNewParent
));
599 this._events
.dispatch("move", aId
);
603 QueryInterface
: XPCOMUtils
.generateQI([Ci
.fuelIBookmarkFolder
, Ci
.nsINavBookmarkObserver
])
606 //=================================================
607 // BookmarkRoots implementation
608 function BookmarkRoots() {
610 gShutdown
.push(function() { self
._shutdown(); });
613 BookmarkRoots
.prototype = {
614 _shutdown
: function bmr_shutdown() {
616 this._toolbar
= null;
618 this._unfiled
= null;
623 this._menu
= new BookmarkFolder(Utilities
.bookmarks
.bookmarksMenuFolder
, null);
630 this._toolbar
= new BookmarkFolder(Utilities
.bookmarks
.toolbarFolder
, null);
632 return this._toolbar
;
637 this._tags
= new BookmarkFolder(Utilities
.bookmarks
.tagsFolder
, null);
644 this._unfiled
= new BookmarkFolder(Utilities
.bookmarks
.unfiledBookmarksFolder
, null);
646 return this._unfiled
;
649 QueryInterface
: XPCOMUtils
.generateQI([Ci
.fuelIBookmarkRoots
])
653 //=================================================
654 // Factory - Treat Application as a singleton
655 // XXX This is required, because we're registered for the 'JavaScript global
656 // privileged property' category, whose handler always calls createInstance.
658 var gSingleton
= null;
659 var ApplicationFactory
= {
660 createInstance
: function af_ci(aOuter
, aIID
) {
662 throw Components
.results
.NS_ERROR_NO_AGGREGATION
;
664 if (gSingleton
== null) {
665 gSingleton
= new Application();
668 return gSingleton
.QueryInterface(aIID
);
674 //=================================================
675 // Application constructor
676 function Application() {
677 this.initToolkitHelpers();
678 this._bookmarks
= null;
681 //=================================================
682 // Application implementation
683 Application
.prototype = {
684 // for nsIClassInfo + XPCOMUtils
685 classDescription
: "Application",
686 classID
: Components
.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66"),
687 contractID
: "@mozilla.org/fuel/application;1",
689 // redefine the default factory for XPCOMUtils
690 _xpcom_factory
: ApplicationFactory
,
693 QueryInterface
: XPCOMUtils
.generateQI([Ci
.fuelIApplication
, Ci
.extIApplication
, Ci
.nsIObserver
, Ci
.nsIClassInfo
]),
695 getInterfaces
: function app_gi(aCount
) {
696 var interfaces
= [Ci
.fuelIApplication
, Ci
.extIApplication
, Ci
.nsIObserver
, Ci
.nsIClassInfo
];
697 aCount
.value
= interfaces
.length
;
702 observe
: function app_observe(aSubject
, aTopic
, aData
) {
703 // Call the extApplication version of this function first
704 this.__proto__
.__proto__
.observe
.call(this, aSubject
, aTopic
, aData
);
705 if (aTopic
== "xpcom-shutdown") {
706 this._bookmarks
= null;
712 if (this._bookmarks
== null)
713 this._bookmarks
= new BookmarkRoots();
715 return this._bookmarks
;
720 var enum = Utilities
.windowMediator
.getEnumerator("navigator:browser");
722 while (enum.hasMoreElements())
723 win
.push(new Window(enum.getNext()));
729 return new Window(Utilities
.windowMediator
.getMostRecentWindow("navigator:browser"));
733 //module initialization
734 function NSGetModule(aCompMgr
, aFileSpec
) {
735 // set the proto, defined in extApplication.js
736 Application
.prototype.__proto__
= extApplication
.prototype;
737 return XPCOMUtils
.generateModule([Application
]);
740 #include
../../../toolkit/components/exthelper/extApplication
.js