1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/fuel/src/fuelApplication.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,818 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +const Ci = Components.interfaces; 1.9 +const Cc = Components.classes; 1.10 + 1.11 +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); 1.12 + 1.13 +const APPLICATION_CID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66"); 1.14 +const APPLICATION_CONTRACTID = "@mozilla.org/fuel/application;1"; 1.15 + 1.16 +//================================================= 1.17 +// Singleton that holds services and utilities 1.18 +var Utilities = { 1.19 + get bookmarks() { 1.20 + let bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. 1.21 + getService(Ci.nsINavBookmarksService); 1.22 + this.__defineGetter__("bookmarks", function() bookmarks); 1.23 + return this.bookmarks; 1.24 + }, 1.25 + 1.26 + get bookmarksObserver() { 1.27 + let bookmarksObserver = new BookmarksObserver(); 1.28 + this.__defineGetter__("bookmarksObserver", function() bookmarksObserver); 1.29 + return this.bookmarksObserver; 1.30 + }, 1.31 + 1.32 + get annotations() { 1.33 + let annotations = Cc["@mozilla.org/browser/annotation-service;1"]. 1.34 + getService(Ci.nsIAnnotationService); 1.35 + this.__defineGetter__("annotations", function() annotations); 1.36 + return this.annotations; 1.37 + }, 1.38 + 1.39 + get history() { 1.40 + let history = Cc["@mozilla.org/browser/nav-history-service;1"]. 1.41 + getService(Ci.nsINavHistoryService); 1.42 + this.__defineGetter__("history", function() history); 1.43 + return this.history; 1.44 + }, 1.45 + 1.46 + get windowMediator() { 1.47 + let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"]. 1.48 + getService(Ci.nsIWindowMediator); 1.49 + this.__defineGetter__("windowMediator", function() windowMediator); 1.50 + return this.windowMediator; 1.51 + }, 1.52 + 1.53 + makeURI: function fuelutil_makeURI(aSpec) { 1.54 + if (!aSpec) 1.55 + return null; 1.56 + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); 1.57 + return ios.newURI(aSpec, null, null); 1.58 + }, 1.59 + 1.60 + free: function fuelutil_free() { 1.61 + delete this.bookmarks; 1.62 + delete this.bookmarksObserver; 1.63 + delete this.annotations; 1.64 + delete this.history; 1.65 + delete this.windowMediator; 1.66 + } 1.67 +}; 1.68 + 1.69 + 1.70 +//================================================= 1.71 +// Window implementation 1.72 + 1.73 +var fuelWindowMap = new WeakMap(); 1.74 +function getWindow(aWindow) { 1.75 + let fuelWindow = fuelWindowMap.get(aWindow); 1.76 + if (!fuelWindow) { 1.77 + fuelWindow = new Window(aWindow); 1.78 + fuelWindowMap.set(aWindow, fuelWindow); 1.79 + } 1.80 + return fuelWindow; 1.81 +} 1.82 + 1.83 +// Don't call new Window() directly; use getWindow instead. 1.84 +function Window(aWindow) { 1.85 + this._window = aWindow; 1.86 + this._events = new Events(); 1.87 + 1.88 + this._watch("TabOpen"); 1.89 + this._watch("TabMove"); 1.90 + this._watch("TabClose"); 1.91 + this._watch("TabSelect"); 1.92 +} 1.93 + 1.94 +Window.prototype = { 1.95 + get events() { 1.96 + return this._events; 1.97 + }, 1.98 + 1.99 + get _tabbrowser() { 1.100 + return this._window.getBrowser(); 1.101 + }, 1.102 + 1.103 + /* 1.104 + * Helper used to setup event handlers on the XBL element. Note that the events 1.105 + * are actually dispatched to tabs, so we capture them. 1.106 + */ 1.107 + _watch: function win_watch(aType) { 1.108 + this._tabbrowser.tabContainer.addEventListener(aType, this, 1.109 + /* useCapture = */ true); 1.110 + }, 1.111 + 1.112 + handleEvent: function win_handleEvent(aEvent) { 1.113 + this._events.dispatch(aEvent.type, getBrowserTab(this, aEvent.originalTarget.linkedBrowser)); 1.114 + }, 1.115 + 1.116 + get tabs() { 1.117 + var tabs = []; 1.118 + var browsers = this._tabbrowser.browsers; 1.119 + for (var i=0; i<browsers.length; i++) 1.120 + tabs.push(getBrowserTab(this, browsers[i])); 1.121 + return tabs; 1.122 + }, 1.123 + 1.124 + get activeTab() { 1.125 + return getBrowserTab(this, this._tabbrowser.selectedBrowser); 1.126 + }, 1.127 + 1.128 + open: function win_open(aURI) { 1.129 + return getBrowserTab(this, this._tabbrowser.addTab(aURI.spec).linkedBrowser); 1.130 + }, 1.131 + 1.132 + QueryInterface: XPCOMUtils.generateQI([Ci.fuelIWindow]) 1.133 +}; 1.134 + 1.135 +//================================================= 1.136 +// BrowserTab implementation 1.137 + 1.138 +var fuelBrowserTabMap = new WeakMap(); 1.139 +function getBrowserTab(aFUELWindow, aBrowser) { 1.140 + let fuelBrowserTab = fuelBrowserTabMap.get(aBrowser); 1.141 + if (!fuelBrowserTab) { 1.142 + fuelBrowserTab = new BrowserTab(aFUELWindow, aBrowser); 1.143 + fuelBrowserTabMap.set(aBrowser, fuelBrowserTab); 1.144 + } 1.145 + else { 1.146 + // This tab may have moved to another window, so make sure its cached 1.147 + // window is up-to-date. 1.148 + fuelBrowserTab._window = aFUELWindow; 1.149 + } 1.150 + 1.151 + return fuelBrowserTab; 1.152 +} 1.153 + 1.154 +// Don't call new BrowserTab() directly; call getBrowserTab instead. 1.155 +function BrowserTab(aFUELWindow, aBrowser) { 1.156 + this._window = aFUELWindow; 1.157 + this._browser = aBrowser; 1.158 + this._events = new Events(); 1.159 + 1.160 + this._watch("load"); 1.161 +} 1.162 + 1.163 +BrowserTab.prototype = { 1.164 + get _tabbrowser() { 1.165 + return this._window._tabbrowser; 1.166 + }, 1.167 + 1.168 + get uri() { 1.169 + return this._browser.currentURI; 1.170 + }, 1.171 + 1.172 + get index() { 1.173 + var tabs = this._tabbrowser.tabs; 1.174 + for (var i=0; i<tabs.length; i++) { 1.175 + if (tabs[i].linkedBrowser == this._browser) 1.176 + return i; 1.177 + } 1.178 + return -1; 1.179 + }, 1.180 + 1.181 + get events() { 1.182 + return this._events; 1.183 + }, 1.184 + 1.185 + get window() { 1.186 + return this._window; 1.187 + }, 1.188 + 1.189 + get document() { 1.190 + return this._browser.contentDocument; 1.191 + }, 1.192 + 1.193 + /* 1.194 + * Helper used to setup event handlers on the XBL element 1.195 + */ 1.196 + _watch: function bt_watch(aType) { 1.197 + this._browser.addEventListener(aType, this, 1.198 + /* useCapture = */ true); 1.199 + }, 1.200 + 1.201 + handleEvent: function bt_handleEvent(aEvent) { 1.202 + if (aEvent.type == "load") { 1.203 + if (!(aEvent.originalTarget instanceof Ci.nsIDOMDocument)) 1.204 + return; 1.205 + 1.206 + if (aEvent.originalTarget.defaultView instanceof Ci.nsIDOMWindow && 1.207 + aEvent.originalTarget.defaultView.frameElement) 1.208 + return; 1.209 + } 1.210 + this._events.dispatch(aEvent.type, this); 1.211 + }, 1.212 + /* 1.213 + * Helper used to determine the index offset of the browsertab 1.214 + */ 1.215 + _getTab: function bt_gettab() { 1.216 + var tabs = this._tabbrowser.tabs; 1.217 + return tabs[this.index] || null; 1.218 + }, 1.219 + 1.220 + load: function bt_load(aURI) { 1.221 + this._browser.loadURI(aURI.spec, null, null); 1.222 + }, 1.223 + 1.224 + focus: function bt_focus() { 1.225 + this._tabbrowser.selectedTab = this._getTab(); 1.226 + this._tabbrowser.focus(); 1.227 + }, 1.228 + 1.229 + close: function bt_close() { 1.230 + this._tabbrowser.removeTab(this._getTab()); 1.231 + }, 1.232 + 1.233 + moveBefore: function bt_movebefore(aBefore) { 1.234 + this._tabbrowser.moveTabTo(this._getTab(), aBefore.index); 1.235 + }, 1.236 + 1.237 + moveToEnd: function bt_moveend() { 1.238 + this._tabbrowser.moveTabTo(this._getTab(), this._tabbrowser.browsers.length); 1.239 + }, 1.240 + 1.241 + QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBrowserTab]) 1.242 +}; 1.243 + 1.244 + 1.245 +//================================================= 1.246 +// Annotations implementation 1.247 +function Annotations(aId) { 1.248 + this._id = aId; 1.249 +} 1.250 + 1.251 +Annotations.prototype = { 1.252 + get names() { 1.253 + return Utilities.annotations.getItemAnnotationNames(this._id); 1.254 + }, 1.255 + 1.256 + has: function ann_has(aName) { 1.257 + return Utilities.annotations.itemHasAnnotation(this._id, aName); 1.258 + }, 1.259 + 1.260 + get: function ann_get(aName) { 1.261 + if (this.has(aName)) 1.262 + return Utilities.annotations.getItemAnnotation(this._id, aName); 1.263 + return null; 1.264 + }, 1.265 + 1.266 + set: function ann_set(aName, aValue, aExpiration) { 1.267 + Utilities.annotations.setItemAnnotation(this._id, aName, aValue, 0, aExpiration); 1.268 + }, 1.269 + 1.270 + remove: function ann_remove(aName) { 1.271 + if (aName) 1.272 + Utilities.annotations.removeItemAnnotation(this._id, aName); 1.273 + }, 1.274 + 1.275 + QueryInterface: XPCOMUtils.generateQI([Ci.fuelIAnnotations]) 1.276 +}; 1.277 + 1.278 + 1.279 +//================================================= 1.280 +// BookmarksObserver implementation (internal class) 1.281 +// 1.282 +// BookmarksObserver is a global singleton which watches the browser's 1.283 +// bookmarks and sends you events when things change. 1.284 +// 1.285 +// You can register three different kinds of event listeners on 1.286 +// BookmarksObserver, using addListener, addFolderListener, and 1.287 +// addRootlistener. 1.288 +// 1.289 +// - addListener(aId, aEvent, aListener) lets you listen to a specific 1.290 +// bookmark. You can listen to the "change", "move", and "remove" events. 1.291 +// 1.292 +// - addFolderListener(aId, aEvent, aListener) lets you listen to a specific 1.293 +// bookmark folder. You can listen to "addchild" and "removechild". 1.294 +// 1.295 +// - addRootListener(aEvent, aListener) lets you listen to the root bookmark 1.296 +// node. This lets you hear "add", "remove", and "change" events on all 1.297 +// bookmarks. 1.298 +// 1.299 + 1.300 +function BookmarksObserver() { 1.301 + this._eventsDict = {}; 1.302 + this._folderEventsDict = {}; 1.303 + this._rootEvents = new Events(); 1.304 + Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true); 1.305 +} 1.306 + 1.307 +BookmarksObserver.prototype = { 1.308 + onBeginUpdateBatch: function () {}, 1.309 + onEndUpdateBatch: function () {}, 1.310 + onItemVisited: function () {}, 1.311 + 1.312 + onItemAdded: function bo_onItemAdded(aId, aFolder, aIndex, aItemType, aURI) { 1.313 + this._rootEvents.dispatch("add", aId); 1.314 + this._dispatchToEvents("addchild", aId, this._folderEventsDict[aFolder]); 1.315 + }, 1.316 + 1.317 + onItemRemoved: function bo_onItemRemoved(aId, aFolder, aIndex) { 1.318 + this._rootEvents.dispatch("remove", aId); 1.319 + this._dispatchToEvents("remove", aId, this._eventsDict[aId]); 1.320 + this._dispatchToEvents("removechild", aId, this._folderEventsDict[aFolder]); 1.321 + }, 1.322 + 1.323 + onItemChanged: function bo_onItemChanged(aId, aProperty, aIsAnnotationProperty, aValue) { 1.324 + this._rootEvents.dispatch("change", aProperty); 1.325 + this._dispatchToEvents("change", aProperty, this._eventsDict[aId]); 1.326 + }, 1.327 + 1.328 + onItemMoved: function bo_onItemMoved(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) { 1.329 + this._dispatchToEvents("move", aId, this._eventsDict[aId]); 1.330 + }, 1.331 + 1.332 + _dispatchToEvents: function bo_dispatchToEvents(aEvent, aData, aEvents) { 1.333 + if (aEvents) { 1.334 + aEvents.dispatch(aEvent, aData); 1.335 + } 1.336 + }, 1.337 + 1.338 + _addListenerToDict: function bo_addListenerToDict(aId, aEvent, aListener, aDict) { 1.339 + var events = aDict[aId]; 1.340 + if (!events) { 1.341 + events = new Events(); 1.342 + aDict[aId] = events; 1.343 + } 1.344 + events.addListener(aEvent, aListener); 1.345 + }, 1.346 + 1.347 + _removeListenerFromDict: function bo_removeListenerFromDict(aId, aEvent, aListener, aDict) { 1.348 + var events = aDict[aId]; 1.349 + if (!events) { 1.350 + return; 1.351 + } 1.352 + events.removeListener(aEvent, aListener); 1.353 + if (events._listeners.length == 0) { 1.354 + delete aDict[aId]; 1.355 + } 1.356 + }, 1.357 + 1.358 + addListener: function bo_addListener(aId, aEvent, aListener) { 1.359 + this._addListenerToDict(aId, aEvent, aListener, this._eventsDict); 1.360 + }, 1.361 + 1.362 + removeListener: function bo_removeListener(aId, aEvent, aListener) { 1.363 + this._removeListenerFromDict(aId, aEvent, aListener, this._eventsDict); 1.364 + }, 1.365 + 1.366 + addFolderListener: function addFolderListener(aId, aEvent, aListener) { 1.367 + this._addListenerToDict(aId, aEvent, aListener, this._folderEventsDict); 1.368 + }, 1.369 + 1.370 + removeFolderListener: function removeFolderListener(aId, aEvent, aListener) { 1.371 + this._removeListenerFromDict(aId, aEvent, aListener, this._folderEventsDict); 1.372 + }, 1.373 + 1.374 + addRootListener: function addRootListener(aEvent, aListener) { 1.375 + this._rootEvents.addListener(aEvent, aListener); 1.376 + }, 1.377 + 1.378 + removeRootListener: function removeRootListener(aEvent, aListener) { 1.379 + this._rootEvents.removeListener(aEvent, aListener); 1.380 + }, 1.381 + 1.382 + QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarksObserver, 1.383 + Ci.nsISupportsWeakReference]) 1.384 +}; 1.385 + 1.386 +//================================================= 1.387 +// Bookmark implementation 1.388 +// 1.389 +// Bookmark event listeners are stored in BookmarksObserver, not in the 1.390 +// Bookmark objects themselves. Thus, you don't have to hold on to a Bookmark 1.391 +// object in order for your event listener to stay valid, and Bookmark objects 1.392 +// not kept alive by the extension can be GC'ed. 1.393 +// 1.394 +// A consequence of this is that if you have two different Bookmark objects x 1.395 +// and y for the same bookmark (i.e., x != y but x.id == y.id), and you do 1.396 +// 1.397 +// x.addListener("foo", fun); 1.398 +// y.removeListener("foo", fun); 1.399 +// 1.400 +// the second line will in fact remove the listener added in the first line. 1.401 +// 1.402 + 1.403 +function Bookmark(aId, aParent, aType) { 1.404 + this._id = aId; 1.405 + this._parent = aParent; 1.406 + this._type = aType || "bookmark"; 1.407 + this._annotations = new Annotations(this._id); 1.408 + 1.409 + // Our _events object forwards to bookmarksObserver. 1.410 + var self = this; 1.411 + this._events = { 1.412 + addListener: function bookmarkevents_al(aEvent, aListener) { 1.413 + Utilities.bookmarksObserver.addListener(self._id, aEvent, aListener); 1.414 + }, 1.415 + removeListener: function bookmarkevents_rl(aEvent, aListener) { 1.416 + Utilities.bookmarksObserver.removeListener(self._id, aEvent, aListener); 1.417 + }, 1.418 + QueryInterface: XPCOMUtils.generateQI([Ci.extIEvents]) 1.419 + }; 1.420 + 1.421 + // For our onItemMoved listener, which updates this._parent. 1.422 + Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true); 1.423 +} 1.424 + 1.425 +Bookmark.prototype = { 1.426 + get id() { 1.427 + return this._id; 1.428 + }, 1.429 + 1.430 + get title() { 1.431 + return Utilities.bookmarks.getItemTitle(this._id); 1.432 + }, 1.433 + 1.434 + set title(aTitle) { 1.435 + Utilities.bookmarks.setItemTitle(this._id, aTitle); 1.436 + }, 1.437 + 1.438 + get uri() { 1.439 + return Utilities.bookmarks.getBookmarkURI(this._id); 1.440 + }, 1.441 + 1.442 + set uri(aURI) { 1.443 + return Utilities.bookmarks.changeBookmarkURI(this._id, aURI); 1.444 + }, 1.445 + 1.446 + get description() { 1.447 + return this._annotations.get("bookmarkProperties/description"); 1.448 + }, 1.449 + 1.450 + set description(aDesc) { 1.451 + this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER); 1.452 + }, 1.453 + 1.454 + get keyword() { 1.455 + return Utilities.bookmarks.getKeywordForBookmark(this._id); 1.456 + }, 1.457 + 1.458 + set keyword(aKeyword) { 1.459 + Utilities.bookmarks.setKeywordForBookmark(this._id, aKeyword); 1.460 + }, 1.461 + 1.462 + get type() { 1.463 + return this._type; 1.464 + }, 1.465 + 1.466 + get parent() { 1.467 + return this._parent; 1.468 + }, 1.469 + 1.470 + set parent(aFolder) { 1.471 + Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX); 1.472 + // this._parent is updated in onItemMoved 1.473 + }, 1.474 + 1.475 + get annotations() { 1.476 + return this._annotations; 1.477 + }, 1.478 + 1.479 + get events() { 1.480 + return this._events; 1.481 + }, 1.482 + 1.483 + remove : function bm_remove() { 1.484 + Utilities.bookmarks.removeItem(this._id); 1.485 + }, 1.486 + 1.487 + onBeginUpdateBatch: function () {}, 1.488 + onEndUpdateBatch: function () {}, 1.489 + onItemAdded: function () {}, 1.490 + onItemVisited: function () {}, 1.491 + onItemRemoved: function () {}, 1.492 + onItemChanged: function () {}, 1.493 + 1.494 + onItemMoved: function(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) { 1.495 + if (aId == this._id) { 1.496 + this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent)); 1.497 + } 1.498 + }, 1.499 + 1.500 + QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmark, 1.501 + Ci.nsINavBookmarksObserver, 1.502 + Ci.nsISupportsWeakReference]) 1.503 +}; 1.504 + 1.505 + 1.506 +//================================================= 1.507 +// BookmarkFolder implementation 1.508 +// 1.509 +// As with Bookmark, events on BookmarkFolder are handled by the 1.510 +// BookmarksObserver singleton. 1.511 +// 1.512 + 1.513 +function BookmarkFolder(aId, aParent) { 1.514 + this._id = aId; 1.515 + this._parent = aParent; 1.516 + this._annotations = new Annotations(this._id); 1.517 + 1.518 + // Our event listeners are handled by the BookmarksObserver singleton. This 1.519 + // is a bit complicated because there are three different kinds of events we 1.520 + // might want to listen to here: 1.521 + // 1.522 + // - If this._parent is null, we're the root bookmark folder, and all our 1.523 + // listeners should be root listeners. 1.524 + // 1.525 + // - Otherwise, events ending with "child" (addchild, removechild) are 1.526 + // handled by a folder listener. 1.527 + // 1.528 + // - Other events are handled by a vanilla bookmark listener. 1.529 + 1.530 + var self = this; 1.531 + this._events = { 1.532 + addListener: function bmfevents_al(aEvent, aListener) { 1.533 + if (self._parent) { 1.534 + if (/child$/.test(aEvent)) { 1.535 + Utilities.bookmarksObserver.addFolderListener(self._id, aEvent, aListener); 1.536 + } 1.537 + else { 1.538 + Utilities.bookmarksObserver.addListener(self._id, aEvent, aListener); 1.539 + } 1.540 + } 1.541 + else { 1.542 + Utilities.bookmarksObserver.addRootListener(aEvent, aListener); 1.543 + } 1.544 + }, 1.545 + removeListener: function bmfevents_rl(aEvent, aListener) { 1.546 + if (self._parent) { 1.547 + if (/child$/.test(aEvent)) { 1.548 + Utilities.bookmarksObserver.removeFolderListener(self._id, aEvent, aListener); 1.549 + } 1.550 + else { 1.551 + Utilities.bookmarksObserver.removeListener(self._id, aEvent, aListener); 1.552 + } 1.553 + } 1.554 + else { 1.555 + Utilities.bookmarksObserver.removeRootListener(aEvent, aListener); 1.556 + } 1.557 + }, 1.558 + QueryInterface: XPCOMUtils.generateQI([Ci.extIEvents]) 1.559 + }; 1.560 + 1.561 + // For our onItemMoved listener, which updates this._parent. 1.562 + Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true); 1.563 +} 1.564 + 1.565 +BookmarkFolder.prototype = { 1.566 + get id() { 1.567 + return this._id; 1.568 + }, 1.569 + 1.570 + get title() { 1.571 + return Utilities.bookmarks.getItemTitle(this._id); 1.572 + }, 1.573 + 1.574 + set title(aTitle) { 1.575 + Utilities.bookmarks.setItemTitle(this._id, aTitle); 1.576 + }, 1.577 + 1.578 + get description() { 1.579 + return this._annotations.get("bookmarkProperties/description"); 1.580 + }, 1.581 + 1.582 + set description(aDesc) { 1.583 + this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER); 1.584 + }, 1.585 + 1.586 + get type() { 1.587 + return "folder"; 1.588 + }, 1.589 + 1.590 + get parent() { 1.591 + return this._parent; 1.592 + }, 1.593 + 1.594 + set parent(aFolder) { 1.595 + Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX); 1.596 + // this._parent is updated in onItemMoved 1.597 + }, 1.598 + 1.599 + get annotations() { 1.600 + return this._annotations; 1.601 + }, 1.602 + 1.603 + get events() { 1.604 + return this._events; 1.605 + }, 1.606 + 1.607 + get children() { 1.608 + var items = []; 1.609 + 1.610 + var options = Utilities.history.getNewQueryOptions(); 1.611 + var query = Utilities.history.getNewQuery(); 1.612 + query.setFolders([this._id], 1); 1.613 + var result = Utilities.history.executeQuery(query, options); 1.614 + var rootNode = result.root; 1.615 + rootNode.containerOpen = true; 1.616 + var cc = rootNode.childCount; 1.617 + for (var i=0; i<cc; ++i) { 1.618 + var node = rootNode.getChild(i); 1.619 + if (node.type == node.RESULT_TYPE_FOLDER) { 1.620 + var folder = new BookmarkFolder(node.itemId, this._id); 1.621 + items.push(folder); 1.622 + } 1.623 + else if (node.type == node.RESULT_TYPE_SEPARATOR) { 1.624 + var separator = new Bookmark(node.itemId, this._id, "separator"); 1.625 + items.push(separator); 1.626 + } 1.627 + else { 1.628 + var bookmark = new Bookmark(node.itemId, this._id, "bookmark"); 1.629 + items.push(bookmark); 1.630 + } 1.631 + } 1.632 + rootNode.containerOpen = false; 1.633 + 1.634 + return items; 1.635 + }, 1.636 + 1.637 + addBookmark: function bmf_addbm(aTitle, aUri) { 1.638 + var newBookmarkID = Utilities.bookmarks.insertBookmark(this._id, aUri, Utilities.bookmarks.DEFAULT_INDEX, aTitle); 1.639 + var newBookmark = new Bookmark(newBookmarkID, this, "bookmark"); 1.640 + return newBookmark; 1.641 + }, 1.642 + 1.643 + addSeparator: function bmf_addsep() { 1.644 + var newBookmarkID = Utilities.bookmarks.insertSeparator(this._id, Utilities.bookmarks.DEFAULT_INDEX); 1.645 + var newBookmark = new Bookmark(newBookmarkID, this, "separator"); 1.646 + return newBookmark; 1.647 + }, 1.648 + 1.649 + addFolder: function bmf_addfolder(aTitle) { 1.650 + var newFolderID = Utilities.bookmarks.createFolder(this._id, aTitle, Utilities.bookmarks.DEFAULT_INDEX); 1.651 + var newFolder = new BookmarkFolder(newFolderID, this); 1.652 + return newFolder; 1.653 + }, 1.654 + 1.655 + remove: function bmf_remove() { 1.656 + Utilities.bookmarks.removeItem(this._id); 1.657 + }, 1.658 + 1.659 + // observer 1.660 + onBeginUpdateBatch: function () {}, 1.661 + onEndUpdateBatch : function () {}, 1.662 + onItemAdded : function () {}, 1.663 + onItemRemoved : function () {}, 1.664 + onItemChanged : function () {}, 1.665 + 1.666 + onItemMoved: function bf_onItemMoved(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) { 1.667 + if (this._id == aId) { 1.668 + this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent)); 1.669 + } 1.670 + }, 1.671 + 1.672 + QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder, 1.673 + Ci.nsINavBookmarksObserver, 1.674 + Ci.nsISupportsWeakReference]) 1.675 +}; 1.676 + 1.677 +//================================================= 1.678 +// BookmarkRoots implementation 1.679 +function BookmarkRoots() { 1.680 +} 1.681 + 1.682 +BookmarkRoots.prototype = { 1.683 + get menu() { 1.684 + if (!this._menu) 1.685 + this._menu = new BookmarkFolder(Utilities.bookmarks.bookmarksMenuFolder, null); 1.686 + 1.687 + return this._menu; 1.688 + }, 1.689 + 1.690 + get toolbar() { 1.691 + if (!this._toolbar) 1.692 + this._toolbar = new BookmarkFolder(Utilities.bookmarks.toolbarFolder, null); 1.693 + 1.694 + return this._toolbar; 1.695 + }, 1.696 + 1.697 + get tags() { 1.698 + if (!this._tags) 1.699 + this._tags = new BookmarkFolder(Utilities.bookmarks.tagsFolder, null); 1.700 + 1.701 + return this._tags; 1.702 + }, 1.703 + 1.704 + get unfiled() { 1.705 + if (!this._unfiled) 1.706 + this._unfiled = new BookmarkFolder(Utilities.bookmarks.unfiledBookmarksFolder, null); 1.707 + 1.708 + return this._unfiled; 1.709 + }, 1.710 + 1.711 + QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkRoots]) 1.712 +}; 1.713 + 1.714 + 1.715 +//================================================= 1.716 +// Factory - Treat Application as a singleton 1.717 +// XXX This is required, because we're registered for the 'JavaScript global 1.718 +// privileged property' category, whose handler always calls createInstance. 1.719 +// See bug 386535. 1.720 +var gSingleton = null; 1.721 +var ApplicationFactory = { 1.722 + createInstance: function af_ci(aOuter, aIID) { 1.723 + if (aOuter != null) 1.724 + throw Components.results.NS_ERROR_NO_AGGREGATION; 1.725 + 1.726 + if (gSingleton == null) { 1.727 + gSingleton = new Application(); 1.728 + } 1.729 + 1.730 + return gSingleton.QueryInterface(aIID); 1.731 + } 1.732 +}; 1.733 + 1.734 + 1.735 +#include ../../../toolkit/components/exthelper/extApplication.js 1.736 + 1.737 +//================================================= 1.738 +// Application constructor 1.739 +function Application() { 1.740 + this.initToolkitHelpers(); 1.741 +} 1.742 + 1.743 +//================================================= 1.744 +// Application implementation 1.745 +function ApplicationPrototype() { 1.746 + // for nsIClassInfo + XPCOMUtils 1.747 + this.classID = APPLICATION_CID; 1.748 + 1.749 + // redefine the default factory for XPCOMUtils 1.750 + this._xpcom_factory = ApplicationFactory; 1.751 + 1.752 + // for nsISupports 1.753 + this.QueryInterface = XPCOMUtils.generateQI([ 1.754 + Ci.fuelIApplication, 1.755 + Ci.extIApplication, 1.756 + Ci.nsIObserver, 1.757 + Ci.nsISupportsWeakReference 1.758 + ]); 1.759 + 1.760 + // for nsIClassInfo 1.761 + this.classInfo = XPCOMUtils.generateCI({ 1.762 + classID: APPLICATION_CID, 1.763 + contractID: APPLICATION_CONTRACTID, 1.764 + interfaces: [ 1.765 + Ci.fuelIApplication, 1.766 + Ci.extIApplication, 1.767 + Ci.nsIObserver 1.768 + ], 1.769 + flags: Ci.nsIClassInfo.SINGLETON 1.770 + }); 1.771 + 1.772 + // for nsIObserver 1.773 + this.observe = function (aSubject, aTopic, aData) { 1.774 + // Call the extApplication version of this function first 1.775 + var superPrototype = Object.getPrototypeOf(Object.getPrototypeOf(this)); 1.776 + superPrototype.observe.call(this, aSubject, aTopic, aData); 1.777 + if (aTopic == "xpcom-shutdown") { 1.778 + this._obs.removeObserver(this, "xpcom-shutdown"); 1.779 + Utilities.free(); 1.780 + } 1.781 + }; 1.782 + 1.783 + Object.defineProperty(this, "bookmarks", { 1.784 + get: function bookmarks () { 1.785 + let bookmarks = new BookmarkRoots(); 1.786 + Object.defineProperty(this, "bookmarks", { value: bookmarks }); 1.787 + return this.bookmarks; 1.788 + }, 1.789 + enumerable: true, 1.790 + configurable: true 1.791 + }); 1.792 + 1.793 + Object.defineProperty(this, "windows", { 1.794 + get: function windows() { 1.795 + var win = []; 1.796 + var browserEnum = Utilities.windowMediator.getEnumerator("navigator:browser"); 1.797 + 1.798 + while (browserEnum.hasMoreElements()) 1.799 + win.push(getWindow(browserEnum.getNext())); 1.800 + 1.801 + return win; 1.802 + }, 1.803 + enumerable: true, 1.804 + configurable: true 1.805 + }); 1.806 + 1.807 + Object.defineProperty(this, "activeWindow", { 1.808 + get: () => getWindow(Utilities.windowMediator.getMostRecentWindow("navigator:browser")), 1.809 + enumerable: true, 1.810 + configurable: true 1.811 + }); 1.812 + 1.813 +}; 1.814 + 1.815 +// set the proto, defined in extApplication.js 1.816 +ApplicationPrototype.prototype = extApplication.prototype; 1.817 + 1.818 +Application.prototype = new ApplicationPrototype(); 1.819 + 1.820 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Application]); 1.821 +