mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 750454 - Fix FUEL leaks. Part 4: Fix bookmarks leaks. r=mak
--HG-- extra : rebase_source : 1f3c91c0ebdaf883e78e0bd317f0dfda35334838
This commit is contained in:
parent
759bce65ba
commit
fffb3b814c
@ -20,6 +20,12 @@ var Utilities = {
|
||||
return this.bookmarks;
|
||||
},
|
||||
|
||||
get bookmarksObserver() {
|
||||
let bookmarksObserver = new BookmarksObserver();
|
||||
this.__defineGetter__("bookmarksObserver", function() bookmarksObserver);
|
||||
return this.bookmarksObserver;
|
||||
},
|
||||
|
||||
get livemarks() {
|
||||
let livemarks = Cc["@mozilla.org/browser/livemark-service;2"].
|
||||
getService[Ci.mozIAsyncLivemarks].
|
||||
@ -58,6 +64,7 @@ var Utilities = {
|
||||
|
||||
free : function() {
|
||||
delete this.bookmarks;
|
||||
delete this.bookmarksObserver;
|
||||
delete this.livemarks
|
||||
delete this.annotations;
|
||||
delete this.history;
|
||||
@ -272,29 +279,155 @@ Annotations.prototype = {
|
||||
};
|
||||
|
||||
|
||||
//=================================================
|
||||
// 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() {},
|
||||
onBeforeItemRemoved : function(aId) {},
|
||||
|
||||
onItemAdded : function(aId, aFolder, aIndex, aItemType, aURI) {
|
||||
this._rootEvents.dispatch("add", aId);
|
||||
this._dispatchToEvents("addchild", aId, this._folderEventsDict[aFolder]);
|
||||
},
|
||||
|
||||
onItemVisited: function(aId, aVisitID, aTime) {},
|
||||
|
||||
onItemRemoved : function(aId, aFolder, aIndex) {
|
||||
this._rootEvents.dispatch("remove", aId);
|
||||
this._dispatchToEvents("remove", aId, this._eventsDict[aId]);
|
||||
this._dispatchToEvents("removechild", aId, this._folderEventsDict[aFolder]);
|
||||
},
|
||||
|
||||
onItemChanged : function(aId, aProperty, aIsAnnotationProperty, aValue) {
|
||||
this._rootEvents.dispatch("change", aProperty);
|
||||
this._dispatchToEvents("change", aProperty, this._eventsDict[aId]);
|
||||
},
|
||||
|
||||
onItemMoved: function(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
|
||||
this._dispatchToEvents("move", aId, this._eventsDict[aId]);
|
||||
},
|
||||
|
||||
_dispatchToEvents: function(aEvent, aData, aEvents) {
|
||||
if (aEvents) {
|
||||
aEvents.dispatch(aEvent, aData);
|
||||
}
|
||||
},
|
||||
|
||||
_addListenerToDict: function(aId, aEvent, aListener, aDict) {
|
||||
var events = aDict[aId];
|
||||
if (!events) {
|
||||
events = new Events();
|
||||
aDict[aId] = events;
|
||||
}
|
||||
events.addListener(aEvent, aListener);
|
||||
},
|
||||
|
||||
_removeListenerFromDict: function(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(aId, aEvent, aListener) {
|
||||
this._addListenerToDict(aId, aEvent, aListener, this._eventsDict);
|
||||
},
|
||||
|
||||
removeListener: function(aId, aEvent, aListener) {
|
||||
this._removeListenerFromDict(aId, aEvent, aListener, this._eventsDict);
|
||||
},
|
||||
|
||||
addFolderListener: function(aId, aEvent, aListener) {
|
||||
this._addListenerToDict(aId, aEvent, aListener, this._folderEventsDict);
|
||||
},
|
||||
|
||||
removeFolderListener: function(aId, aEvent, aListener) {
|
||||
this._removeListenerFromDict(aId, aEvent, aListener, this._folderEventsDict);
|
||||
},
|
||||
|
||||
addRootListener: function(aEvent, aListener) {
|
||||
this._rootEvents.addListener(aEvent, aListener);
|
||||
},
|
||||
|
||||
removeRootListener: function(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);
|
||||
this._events = new Events();
|
||||
|
||||
Utilities.bookmarks.addObserver(this, false);
|
||||
|
||||
// Our _events object forwards to bookmarksObserver.
|
||||
var self = this;
|
||||
gShutdown.push(function() { self._shutdown(); });
|
||||
this._events = {
|
||||
addListener: function(aEvent, aListener) {
|
||||
Utilities.bookmarksObserver.addListener(self._id, aEvent, aListener);
|
||||
},
|
||||
removeListener: function(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 = {
|
||||
_shutdown : function bm_shutdown() {
|
||||
this._annotations = null;
|
||||
this._events = null;
|
||||
|
||||
Utilities.bookmarks.removeObserver(this);
|
||||
},
|
||||
|
||||
get id() {
|
||||
return this._id;
|
||||
},
|
||||
@ -356,66 +489,86 @@ Bookmark.prototype = {
|
||||
Utilities.bookmarks.removeItem(this._id);
|
||||
},
|
||||
|
||||
// observer
|
||||
onBeginUpdateBatch : function bm_obub() {
|
||||
},
|
||||
onBeginUpdateBatch : function() {},
|
||||
onEndUpdateBatch : function() {},
|
||||
onItemAdded : function(aId, aFolder, aIndex, aItemType, aURI) {},
|
||||
onBeforeItemRemoved : function(aId) {},
|
||||
onItemVisited: function(aId, aVisitID, aTime) {},
|
||||
onItemRemoved : function(aId, aFolder, aIndex) {},
|
||||
onItemChanged : function(aId, aProperty, aIsAnnotationProperty, aValue) {},
|
||||
|
||||
onEndUpdateBatch : function bm_oeub() {
|
||||
},
|
||||
|
||||
onItemAdded : function bm_oia(aId, aFolder, aIndex, aItemType, aURI) {
|
||||
// bookmark object doesn't exist at this point
|
||||
},
|
||||
|
||||
onBeforeItemRemoved : function bm_obir(aId) {
|
||||
},
|
||||
|
||||
onItemRemoved : function bm_oir(aId, aFolder, aIndex) {
|
||||
if (this._id == aId)
|
||||
this._events.dispatch("remove", aId);
|
||||
},
|
||||
|
||||
onItemChanged : function bm_oic(aId, aProperty, aIsAnnotationProperty, aValue) {
|
||||
if (this._id == aId)
|
||||
this._events.dispatch("change", aProperty);
|
||||
},
|
||||
|
||||
onItemVisited: function bm_oiv(aId, aVisitID, aTime) {
|
||||
},
|
||||
|
||||
onItemMoved: function bm_oim(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
|
||||
if (this._id == aId) {
|
||||
onItemMoved: function(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
|
||||
if (aId == this._id) {
|
||||
this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));
|
||||
this._events.dispatch("move", aId);
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.fuelIBookmark, Ci.nsINavBookmarkObserver])
|
||||
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);
|
||||
this._events = new Events();
|
||||
|
||||
Utilities.bookmarks.addObserver(this, false);
|
||||
// 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;
|
||||
gShutdown.push(function() { self._shutdown(); });
|
||||
this._events = {
|
||||
addListener: function(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(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 = {
|
||||
_shutdown : function bmf_shutdown() {
|
||||
this._annotations = null;
|
||||
this._events = null;
|
||||
|
||||
Utilities.bookmarks.removeObserver(this);
|
||||
},
|
||||
|
||||
get id() {
|
||||
return this._id;
|
||||
},
|
||||
@ -510,70 +663,30 @@ BookmarkFolder.prototype = {
|
||||
},
|
||||
|
||||
// observer
|
||||
onBeginUpdateBatch : function bmf_obub() {
|
||||
},
|
||||
onBeginUpdateBatch : function() {},
|
||||
onEndUpdateBatch : function() {},
|
||||
onItemAdded : function(aId, aFolder, aIndex, aItemType, aURI) {},
|
||||
onBeforeItemRemoved : function(aId) {},
|
||||
onItemRemoved : function(aId, aFolder, aIndex) {},
|
||||
onItemChanged : function(aId, aProperty, aIsAnnotationProperty, aValue) {},
|
||||
|
||||
onEndUpdateBatch : function bmf_oeub() {
|
||||
},
|
||||
|
||||
onItemAdded : function bmf_oia(aId, aFolder, aIndex, aItemType, aURI) {
|
||||
// handle root folder events
|
||||
if (!this._parent)
|
||||
this._events.dispatch("add", aId);
|
||||
|
||||
// handle this folder events
|
||||
if (this._id == aFolder)
|
||||
this._events.dispatch("addchild", aId);
|
||||
},
|
||||
|
||||
onBeforeItemRemoved : function bmf_oir(aId) {
|
||||
},
|
||||
|
||||
onItemRemoved : function bmf_oir(aId, aFolder, aIndex) {
|
||||
// handle root folder events
|
||||
if (!this._parent || this._id == aId)
|
||||
this._events.dispatch("remove", aId);
|
||||
|
||||
// handle this folder events
|
||||
if (this._id == aFolder)
|
||||
this._events.dispatch("removechild", aId);
|
||||
},
|
||||
|
||||
onItemChanged : function bmf_oic(aId, aProperty, aIsAnnotationProperty, aValue) {
|
||||
// handle root folder and this folder events
|
||||
if (!this._parent || this._id == aId)
|
||||
this._events.dispatch("change", aProperty);
|
||||
},
|
||||
|
||||
onItemVisited: function bmf_oiv(aId, aVisitID, aTime) {
|
||||
},
|
||||
|
||||
onItemMoved: function bmf_oim(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
|
||||
// handle this folder event, root folder cannot be moved
|
||||
onItemMoved: function(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
|
||||
if (this._id == aId) {
|
||||
this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent));
|
||||
this._events.dispatch("move", aId);
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder, Ci.nsINavBookmarkObserver])
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder,
|
||||
Ci.nsINavBookmarksObserver,
|
||||
Ci.nsISupportsWeakReference])
|
||||
};
|
||||
|
||||
//=================================================
|
||||
// BookmarkRoots implementation
|
||||
function BookmarkRoots() {
|
||||
var self = this;
|
||||
gShutdown.push(function() { self._shutdown(); });
|
||||
}
|
||||
|
||||
BookmarkRoots.prototype = {
|
||||
_shutdown : function bmr_shutdown() {
|
||||
this._menu = null;
|
||||
this._toolbar = null;
|
||||
this._tags = null;
|
||||
this._unfiled = null;
|
||||
},
|
||||
|
||||
get menu() {
|
||||
if (!this._menu)
|
||||
this._menu = new BookmarkFolder(Utilities.bookmarks.bookmarksMenuFolder, null);
|
||||
@ -630,7 +743,6 @@ var ApplicationFactory = {
|
||||
// Application constructor
|
||||
function Application() {
|
||||
this.initToolkitHelpers();
|
||||
this._bookmarks = null;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
@ -660,7 +772,6 @@ Application.prototype = {
|
||||
this.__proto__.__proto__.observe.call(this, aSubject, aTopic, aData);
|
||||
if (aTopic == "xpcom-shutdown") {
|
||||
this._obs.removeObserver(this, "xpcom-shutdown");
|
||||
this._bookmarks = null;
|
||||
Utilities.free();
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user