|
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 |