summaryrefslogtreecommitdiffstats
path: root/application/palemoon/components/fuel/fuelApplication.js
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2018-02-02 03:35:06 -0500
committerMatt A. Tobin <email@mattatobin.com>2018-02-02 03:35:06 -0500
commit49ee0794b5d912db1f95dce6eb52d781dc210db5 (patch)
tree6efefe6a09feb09d965932b24e10436b9ac8189c /application/palemoon/components/fuel/fuelApplication.js
parente72ef92b5bdc43cd2584198e2e54e951b70299e8 (diff)
downloadUXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.tar
UXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.tar.gz
UXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.tar.lz
UXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.tar.xz
UXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.zip
Add Pale Moon
Diffstat (limited to 'application/palemoon/components/fuel/fuelApplication.js')
-rw-r--r--application/palemoon/components/fuel/fuelApplication.js818
1 files changed, 818 insertions, 0 deletions
diff --git a/application/palemoon/components/fuel/fuelApplication.js b/application/palemoon/components/fuel/fuelApplication.js
new file mode 100644
index 000000000..89d568ae1
--- /dev/null
+++ b/application/palemoon/components/fuel/fuelApplication.js
@@ -0,0 +1,818 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const APPLICATION_CID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66");
+const APPLICATION_CONTRACTID = "@mozilla.org/fuel/application;1";
+
+//=================================================
+// Singleton that holds services and utilities
+var Utilities = {
+ get bookmarks() {
+ let bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+ getService(Ci.nsINavBookmarksService);
+ this.__defineGetter__("bookmarks", function() bookmarks);
+ return this.bookmarks;
+ },
+
+ get bookmarksObserver() {
+ let bookmarksObserver = new BookmarksObserver();
+ this.__defineGetter__("bookmarksObserver", function() bookmarksObserver);
+ return this.bookmarksObserver;
+ },
+
+ get annotations() {
+ let annotations = Cc["@mozilla.org/browser/annotation-service;1"].
+ getService(Ci.nsIAnnotationService);
+ this.__defineGetter__("annotations", function() annotations);
+ return this.annotations;
+ },
+
+ get history() {
+ let history = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ this.__defineGetter__("history", function() history);
+ return this.history;
+ },
+
+ get windowMediator() {
+ let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator);
+ this.__defineGetter__("windowMediator", function() windowMediator);
+ return this.windowMediator;
+ },
+
+ makeURI: function fuelutil_makeURI(aSpec) {
+ if (!aSpec)
+ return null;
+ var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+ return ios.newURI(aSpec, null, null);
+ },
+
+ free: function fuelutil_free() {
+ delete this.bookmarks;
+ delete this.bookmarksObserver;
+ delete this.annotations;
+ delete this.history;
+ delete this.windowMediator;
+ }
+};
+
+
+//=================================================
+// Window implementation
+
+var fuelWindowMap = new WeakMap();
+function getWindow(aWindow) {
+ let fuelWindow = fuelWindowMap.get(aWindow);
+ if (!fuelWindow) {
+ fuelWindow = new Window(aWindow);
+ fuelWindowMap.set(aWindow, fuelWindow);
+ }
+ return fuelWindow;
+}
+
+// Don't call new Window() directly; use getWindow instead.
+function Window(aWindow) {
+ this._window = aWindow;
+ this._events = new Events();
+
+ this._watch("TabOpen");
+ this._watch("TabMove");
+ this._watch("TabClose");
+ this._watch("TabSelect");
+}
+
+Window.prototype = {
+ get events() {
+ return this._events;
+ },
+
+ get _tabbrowser() {
+ return this._window.getBrowser();
+ },
+
+ /*
+ * Helper used to setup event handlers on the XBL element. Note that the events
+ * are actually dispatched to tabs, so we capture them.
+ */
+ _watch: function win_watch(aType) {
+ this._tabbrowser.tabContainer.addEventListener(aType, this,
+ /* useCapture = */ true);
+ },
+
+ handleEvent: function win_handleEvent(aEvent) {
+ this._events.dispatch(aEvent.type, getBrowserTab(this, aEvent.originalTarget.linkedBrowser));
+ },
+
+ get tabs() {
+ var tabs = [];
+ var browsers = this._tabbrowser.browsers;
+ for (var i=0; i<browsers.length; i++)
+ tabs.push(getBrowserTab(this, browsers[i]));
+ return tabs;
+ },
+
+ get activeTab() {
+ return getBrowserTab(this, this._tabbrowser.selectedBrowser);
+ },
+
+ open: function win_open(aURI) {
+ return getBrowserTab(this, this._tabbrowser.addTab(aURI.spec).linkedBrowser);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.fuelIWindow])
+};
+
+//=================================================
+// BrowserTab implementation
+
+var fuelBrowserTabMap = new WeakMap();
+function getBrowserTab(aFUELWindow, aBrowser) {
+ let fuelBrowserTab = fuelBrowserTabMap.get(aBrowser);
+ if (!fuelBrowserTab) {
+ fuelBrowserTab = new BrowserTab(aFUELWindow, aBrowser);
+ fuelBrowserTabMap.set(aBrowser, fuelBrowserTab);
+ }
+ else {
+ // This tab may have moved to another window, so make sure its cached
+ // window is up-to-date.
+ fuelBrowserTab._window = aFUELWindow;
+ }
+
+ return fuelBrowserTab;
+}
+
+// Don't call new BrowserTab() directly; call getBrowserTab instead.
+function BrowserTab(aFUELWindow, aBrowser) {
+ this._window = aFUELWindow;
+ this._browser = aBrowser;
+ this._events = new Events();
+
+ this._watch("load");
+}
+
+BrowserTab.prototype = {
+ get _tabbrowser() {
+ return this._window._tabbrowser;
+ },
+
+ get uri() {
+ return this._browser.currentURI;
+ },
+
+ get index() {
+ var tabs = this._tabbrowser.tabs;
+ for (var i=0; i<tabs.length; i++) {
+ if (tabs[i].linkedBrowser == this._browser)
+ return i;
+ }
+ return -1;
+ },
+
+ get events() {
+ return this._events;
+ },
+
+ get window() {
+ return this._window;
+ },
+
+ get document() {
+ return this._browser.contentDocument;
+ },
+
+ /*
+ * Helper used to setup event handlers on the XBL element
+ */
+ _watch: function bt_watch(aType) {
+ this._browser.addEventListener(aType, this,
+ /* useCapture = */ true);
+ },
+
+ handleEvent: function bt_handleEvent(aEvent) {
+ if (aEvent.type == "load") {
+ if (!(aEvent.originalTarget instanceof Ci.nsIDOMDocument))
+ return;
+
+ if (aEvent.originalTarget.defaultView instanceof Ci.nsIDOMWindow &&
+ aEvent.originalTarget.defaultView.frameElement)
+ return;
+ }
+ this._events.dispatch(aEvent.type, this);
+ },
+ /*
+ * Helper used to determine the index offset of the browsertab
+ */
+ _getTab: function bt_gettab() {
+ var tabs = this._tabbrowser.tabs;
+ return tabs[this.index] || null;
+ },
+
+ load: function bt_load(aURI) {
+ this._browser.loadURI(aURI.spec, null, null);
+ },
+
+ focus: function bt_focus() {
+ this._tabbrowser.selectedTab = this._getTab();
+ this._tabbrowser.focus();
+ },
+
+ close: function bt_close() {
+ this._tabbrowser.removeTab(this._getTab());
+ },
+
+ moveBefore: function bt_movebefore(aBefore) {
+ this._tabbrowser.moveTabTo(this._getTab(), aBefore.index);
+ },
+
+ moveToEnd: function bt_moveend() {
+ this._tabbrowser.moveTabTo(this._getTab(), this._tabbrowser.browsers.length);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBrowserTab])
+};
+
+
+//=================================================
+// Annotations implementation
+function Annotations(aId) {
+ this._id = aId;
+}
+
+Annotations.prototype = {
+ get names() {
+ return Utilities.annotations.getItemAnnotationNames(this._id);
+ },
+
+ has: function ann_has(aName) {
+ return Utilities.annotations.itemHasAnnotation(this._id, aName);
+ },
+
+ get: function ann_get(aName) {
+ if (this.has(aName))
+ return Utilities.annotations.getItemAnnotation(this._id, aName);
+ return null;
+ },
+
+ set: function ann_set(aName, aValue, aExpiration) {
+ Utilities.annotations.setItemAnnotation(this._id, aName, aValue, 0, aExpiration);
+ },
+
+ remove: function ann_remove(aName) {
+ if (aName)
+ Utilities.annotations.removeItemAnnotation(this._id, aName);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.fuelIAnnotations])
+};
+
+
+//=================================================
+// BookmarksObserver implementation (internal class)
+//
+// BookmarksObserver is a global singleton which watches the browser's
+// bookmarks and sends you events when things change.
+//
+// You can register three different kinds of event listeners on
+// BookmarksObserver, using addListener, addFolderListener, and
+// addRootlistener.
+//
+// - addListener(aId, aEvent, aListener) lets you listen to a specific
+// bookmark. You can listen to the "change", "move", and "remove" events.
+//
+// - addFolderListener(aId, aEvent, aListener) lets you listen to a specific
+// bookmark folder. You can listen to "addchild" and "removechild".
+//
+// - addRootListener(aEvent, aListener) lets you listen to the root bookmark
+// node. This lets you hear "add", "remove", and "change" events on all
+// bookmarks.
+//
+
+function BookmarksObserver() {
+ this._eventsDict = {};
+ this._folderEventsDict = {};
+ this._rootEvents = new Events();
+ Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
+}
+
+BookmarksObserver.prototype = {
+ onBeginUpdateBatch: function () {},
+ onEndUpdateBatch: function () {},
+ onItemVisited: function () {},
+
+ onItemAdded: function bo_onItemAdded(aId, aFolder, aIndex, aItemType, aURI) {
+ this._rootEvents.dispatch("add", aId);
+ this._dispatchToEvents("addchild", aId, this._folderEventsDict[aFolder]);
+ },
+
+ onItemRemoved: function bo_onItemRemoved(aId, aFolder, aIndex) {
+ this._rootEvents.dispatch("remove", aId);
+ this._dispatchToEvents("remove", aId, this._eventsDict[aId]);
+ this._dispatchToEvents("removechild", aId, this._folderEventsDict[aFolder]);
+ },
+
+ onItemChanged: function bo_onItemChanged(aId, aProperty, aIsAnnotationProperty, aValue) {
+ this._rootEvents.dispatch("change", aProperty);
+ this._dispatchToEvents("change", aProperty, this._eventsDict[aId]);
+ },
+
+ onItemMoved: function bo_onItemMoved(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
+ this._dispatchToEvents("move", aId, this._eventsDict[aId]);
+ },
+
+ _dispatchToEvents: function bo_dispatchToEvents(aEvent, aData, aEvents) {
+ if (aEvents) {
+ aEvents.dispatch(aEvent, aData);
+ }
+ },
+
+ _addListenerToDict: function bo_addListenerToDict(aId, aEvent, aListener, aDict) {
+ var events = aDict[aId];
+ if (!events) {
+ events = new Events();
+ aDict[aId] = events;
+ }
+ events.addListener(aEvent, aListener);
+ },
+
+ _removeListenerFromDict: function bo_removeListenerFromDict(aId, aEvent, aListener, aDict) {
+ var events = aDict[aId];
+ if (!events) {
+ return;
+ }
+ events.removeListener(aEvent, aListener);
+ if (events._listeners.length == 0) {
+ delete aDict[aId];
+ }
+ },
+
+ addListener: function bo_addListener(aId, aEvent, aListener) {
+ this._addListenerToDict(aId, aEvent, aListener, this._eventsDict);
+ },
+
+ removeListener: function bo_removeListener(aId, aEvent, aListener) {
+ this._removeListenerFromDict(aId, aEvent, aListener, this._eventsDict);
+ },
+
+ addFolderListener: function addFolderListener(aId, aEvent, aListener) {
+ this._addListenerToDict(aId, aEvent, aListener, this._folderEventsDict);
+ },
+
+ removeFolderListener: function removeFolderListener(aId, aEvent, aListener) {
+ this._removeListenerFromDict(aId, aEvent, aListener, this._folderEventsDict);
+ },
+
+ addRootListener: function addRootListener(aEvent, aListener) {
+ this._rootEvents.addListener(aEvent, aListener);
+ },
+
+ removeRootListener: function removeRootListener(aEvent, aListener) {
+ this._rootEvents.removeListener(aEvent, aListener);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarksObserver,
+ Ci.nsISupportsWeakReference])
+};
+
+//=================================================
+// Bookmark implementation
+//
+// Bookmark event listeners are stored in BookmarksObserver, not in the
+// Bookmark objects themselves. Thus, you don't have to hold on to a Bookmark
+// object in order for your event listener to stay valid, and Bookmark objects
+// not kept alive by the extension can be GC'ed.
+//
+// A consequence of this is that if you have two different Bookmark objects x
+// and y for the same bookmark (i.e., x != y but x.id == y.id), and you do
+//
+// x.addListener("foo", fun);
+// y.removeListener("foo", fun);
+//
+// the second line will in fact remove the listener added in the first line.
+//
+
+function Bookmark(aId, aParent, aType) {
+ this._id = aId;
+ this._parent = aParent;
+ this._type = aType || "bookmark";
+ this._annotations = new Annotations(this._id);
+
+ // Our _events object forwards to bookmarksObserver.
+ var self = this;
+ this._events = {
+ addListener: function bookmarkevents_al(aEvent, aListener) {
+ Utilities.bookmarksObserver.addListener(self._id, aEvent, aListener);
+ },
+ removeListener: function bookmarkevents_rl(aEvent, aListener) {
+ Utilities.bookmarksObserver.removeListener(self._id, aEvent, aListener);
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.extIEvents])
+ };
+
+ // For our onItemMoved listener, which updates this._parent.
+ Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
+}
+
+Bookmark.prototype = {
+ get id() {
+ return this._id;
+ },
+
+ get title() {
+ return Utilities.bookmarks.getItemTitle(this._id);
+ },
+
+ set title(aTitle) {
+ Utilities.bookmarks.setItemTitle(this._id, aTitle);
+ },
+
+ get uri() {
+ return Utilities.bookmarks.getBookmarkURI(this._id);
+ },
+
+ set uri(aURI) {
+ return Utilities.bookmarks.changeBookmarkURI(this._id, aURI);
+ },
+
+ get description() {
+ return this._annotations.get("bookmarkProperties/description");
+ },
+
+ set description(aDesc) {
+ this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
+ },
+
+ get keyword() {
+ return Utilities.bookmarks.getKeywordForBookmark(this._id);
+ },
+
+ set keyword(aKeyword) {
+ Utilities.bookmarks.setKeywordForBookmark(this._id, aKeyword);
+ },
+
+ get type() {
+ return this._type;
+ },
+
+ get parent() {
+ return this._parent;
+ },
+
+ set parent(aFolder) {
+ Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
+ // this._parent is updated in onItemMoved
+ },
+
+ get annotations() {
+ return this._annotations;
+ },
+
+ get events() {
+ return this._events;
+ },
+
+ remove : function bm_remove() {
+ Utilities.bookmarks.removeItem(this._id);
+ },
+
+ onBeginUpdateBatch: function () {},
+ onEndUpdateBatch: function () {},
+ onItemAdded: function () {},
+ onItemVisited: function () {},
+ onItemRemoved: function () {},
+ onItemChanged: function () {},
+
+ onItemMoved: function(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
+ if (aId == this._id) {
+ this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));
+ }
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmark,
+ Ci.nsINavBookmarksObserver,
+ Ci.nsISupportsWeakReference])
+};
+
+
+//=================================================
+// BookmarkFolder implementation
+//
+// As with Bookmark, events on BookmarkFolder are handled by the
+// BookmarksObserver singleton.
+//
+
+function BookmarkFolder(aId, aParent) {
+ this._id = aId;
+ this._parent = aParent;
+ this._annotations = new Annotations(this._id);
+
+ // Our event listeners are handled by the BookmarksObserver singleton. This
+ // is a bit complicated because there are three different kinds of events we
+ // might want to listen to here:
+ //
+ // - If this._parent is null, we're the root bookmark folder, and all our
+ // listeners should be root listeners.
+ //
+ // - Otherwise, events ending with "child" (addchild, removechild) are
+ // handled by a folder listener.
+ //
+ // - Other events are handled by a vanilla bookmark listener.
+
+ var self = this;
+ this._events = {
+ addListener: function bmfevents_al(aEvent, aListener) {
+ if (self._parent) {
+ if (/child$/.test(aEvent)) {
+ Utilities.bookmarksObserver.addFolderListener(self._id, aEvent, aListener);
+ }
+ else {
+ Utilities.bookmarksObserver.addListener(self._id, aEvent, aListener);
+ }
+ }
+ else {
+ Utilities.bookmarksObserver.addRootListener(aEvent, aListener);
+ }
+ },
+ removeListener: function bmfevents_rl(aEvent, aListener) {
+ if (self._parent) {
+ if (/child$/.test(aEvent)) {
+ Utilities.bookmarksObserver.removeFolderListener(self._id, aEvent, aListener);
+ }
+ else {
+ Utilities.bookmarksObserver.removeListener(self._id, aEvent, aListener);
+ }
+ }
+ else {
+ Utilities.bookmarksObserver.removeRootListener(aEvent, aListener);
+ }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.extIEvents])
+ };
+
+ // For our onItemMoved listener, which updates this._parent.
+ Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true);
+}
+
+BookmarkFolder.prototype = {
+ get id() {
+ return this._id;
+ },
+
+ get title() {
+ return Utilities.bookmarks.getItemTitle(this._id);
+ },
+
+ set title(aTitle) {
+ Utilities.bookmarks.setItemTitle(this._id, aTitle);
+ },
+
+ get description() {
+ return this._annotations.get("bookmarkProperties/description");
+ },
+
+ set description(aDesc) {
+ this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER);
+ },
+
+ get type() {
+ return "folder";
+ },
+
+ get parent() {
+ return this._parent;
+ },
+
+ set parent(aFolder) {
+ Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX);
+ // this._parent is updated in onItemMoved
+ },
+
+ get annotations() {
+ return this._annotations;
+ },
+
+ get events() {
+ return this._events;
+ },
+
+ get children() {
+ var items = [];
+
+ var options = Utilities.history.getNewQueryOptions();
+ var query = Utilities.history.getNewQuery();
+ query.setFolders([this._id], 1);
+ var result = Utilities.history.executeQuery(query, options);
+ var rootNode = result.root;
+ rootNode.containerOpen = true;
+ var cc = rootNode.childCount;
+ for (var i=0; i<cc; ++i) {
+ var node = rootNode.getChild(i);
+ if (node.type == node.RESULT_TYPE_FOLDER) {
+ var folder = new BookmarkFolder(node.itemId, this._id);
+ items.push(folder);
+ }
+ else if (node.type == node.RESULT_TYPE_SEPARATOR) {
+ var separator = new Bookmark(node.itemId, this._id, "separator");
+ items.push(separator);
+ }
+ else {
+ var bookmark = new Bookmark(node.itemId, this._id, "bookmark");
+ items.push(bookmark);
+ }
+ }
+ rootNode.containerOpen = false;
+
+ return items;
+ },
+
+ addBookmark: function bmf_addbm(aTitle, aUri) {
+ var newBookmarkID = Utilities.bookmarks.insertBookmark(this._id, aUri, Utilities.bookmarks.DEFAULT_INDEX, aTitle);
+ var newBookmark = new Bookmark(newBookmarkID, this, "bookmark");
+ return newBookmark;
+ },
+
+ addSeparator: function bmf_addsep() {
+ var newBookmarkID = Utilities.bookmarks.insertSeparator(this._id, Utilities.bookmarks.DEFAULT_INDEX);
+ var newBookmark = new Bookmark(newBookmarkID, this, "separator");
+ return newBookmark;
+ },
+
+ addFolder: function bmf_addfolder(aTitle) {
+ var newFolderID = Utilities.bookmarks.createFolder(this._id, aTitle, Utilities.bookmarks.DEFAULT_INDEX);
+ var newFolder = new BookmarkFolder(newFolderID, this);
+ return newFolder;
+ },
+
+ remove: function bmf_remove() {
+ Utilities.bookmarks.removeItem(this._id);
+ },
+
+ // observer
+ onBeginUpdateBatch: function () {},
+ onEndUpdateBatch : function () {},
+ onItemAdded : function () {},
+ onItemRemoved : function () {},
+ onItemChanged : function () {},
+
+ onItemMoved: function bf_onItemMoved(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
+ if (this._id == aId) {
+ this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));
+ }
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder,
+ Ci.nsINavBookmarksObserver,
+ Ci.nsISupportsWeakReference])
+};
+
+//=================================================
+// BookmarkRoots implementation
+function BookmarkRoots() {
+}
+
+BookmarkRoots.prototype = {
+ get menu() {
+ if (!this._menu)
+ this._menu = new BookmarkFolder(Utilities.bookmarks.bookmarksMenuFolder, null);
+
+ return this._menu;
+ },
+
+ get toolbar() {
+ if (!this._toolbar)
+ this._toolbar = new BookmarkFolder(Utilities.bookmarks.toolbarFolder, null);
+
+ return this._toolbar;
+ },
+
+ get tags() {
+ if (!this._tags)
+ this._tags = new BookmarkFolder(Utilities.bookmarks.tagsFolder, null);
+
+ return this._tags;
+ },
+
+ get unfiled() {
+ if (!this._unfiled)
+ this._unfiled = new BookmarkFolder(Utilities.bookmarks.unfiledBookmarksFolder, null);
+
+ return this._unfiled;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkRoots])
+};
+
+
+//=================================================
+// Factory - Treat Application as a singleton
+// XXX This is required, because we're registered for the 'JavaScript global
+// privileged property' category, whose handler always calls createInstance.
+// See bug 386535.
+var gSingleton = null;
+var ApplicationFactory = {
+ createInstance: function af_ci(aOuter, aIID) {
+ if (aOuter != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+
+ if (gSingleton == null) {
+ gSingleton = new Application();
+ }
+
+ return gSingleton.QueryInterface(aIID);
+ }
+};
+
+
+#include ../../../toolkit/components/exthelper/extApplication.js
+
+//=================================================
+// Application constructor
+function Application() {
+ this.initToolkitHelpers();
+}
+
+//=================================================
+// Application implementation
+function ApplicationPrototype() {
+ // for nsIClassInfo + XPCOMUtils
+ this.classID = APPLICATION_CID;
+
+ // redefine the default factory for XPCOMUtils
+ this._xpcom_factory = ApplicationFactory;
+
+ // for nsISupports
+ this.QueryInterface = XPCOMUtils.generateQI([
+ Ci.fuelIApplication,
+ Ci.extIApplication,
+ Ci.nsIObserver,
+ Ci.nsISupportsWeakReference
+ ]);
+
+ // for nsIClassInfo
+ this.classInfo = XPCOMUtils.generateCI({
+ classID: APPLICATION_CID,
+ contractID: APPLICATION_CONTRACTID,
+ interfaces: [
+ Ci.fuelIApplication,
+ Ci.extIApplication,
+ Ci.nsIObserver
+ ],
+ flags: Ci.nsIClassInfo.SINGLETON
+ });
+
+ // for nsIObserver
+ this.observe = function (aSubject, aTopic, aData) {
+ // Call the extApplication version of this function first
+ var superPrototype = Object.getPrototypeOf(Object.getPrototypeOf(this));
+ superPrototype.observe.call(this, aSubject, aTopic, aData);
+ if (aTopic == "xpcom-shutdown") {
+ this._obs.removeObserver(this, "xpcom-shutdown");
+ Utilities.free();
+ }
+ };
+
+ Object.defineProperty(this, "bookmarks", {
+ get: function bookmarks () {
+ let bookmarks = new BookmarkRoots();
+ Object.defineProperty(this, "bookmarks", { value: bookmarks });
+ return this.bookmarks;
+ },
+ enumerable: true,
+ configurable: true
+ });
+
+ Object.defineProperty(this, "windows", {
+ get: function windows() {
+ var win = [];
+ var browserEnum = Utilities.windowMediator.getEnumerator("navigator:browser");
+
+ while (browserEnum.hasMoreElements())
+ win.push(getWindow(browserEnum.getNext()));
+
+ return win;
+ },
+ enumerable: true,
+ configurable: true
+ });
+
+ Object.defineProperty(this, "activeWindow", {
+ get: () => getWindow(Utilities.windowMediator.getMostRecentWindow("navigator:browser")),
+ enumerable: true,
+ configurable: true
+ });
+
+};
+
+// set the proto, defined in extApplication.js
+ApplicationPrototype.prototype = extApplication.prototype;
+
+Application.prototype = new ApplicationPrototype();
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Application]);
+