browser/fuel/src/fuelApplication.js

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

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

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

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

mercurial