mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 23:35:34 +00:00
Bug 891303 - New Places Async Transaction manager (backend part. affects nothing for now). r=mak. sr=gavin
This commit is contained in:
parent
72f33bb3e4
commit
caa37c32d3
1182
toolkit/components/places/PlacesTransactions.jsm
Normal file
1182
toolkit/components/places/PlacesTransactions.jsm
Normal file
File diff suppressed because it is too large
Load Diff
@ -1523,7 +1523,31 @@ this.PlacesUtils = {
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the unique id for an item (a bookmark, a folder or a separator) given
|
||||
* its item id.
|
||||
*
|
||||
* @param aItemId
|
||||
* an item id
|
||||
* @return {Promise}
|
||||
* @resolves to the GUID.
|
||||
* @rejects if aItemId is invalid.
|
||||
*/
|
||||
promiseItemGUID: function (aItemId) GUIDHelper.getItemGUID(aItemId),
|
||||
|
||||
/**
|
||||
* Get the item id for an item (a bookmark, a folder or a separator) given
|
||||
* its unique id.
|
||||
*
|
||||
* @param aGUID
|
||||
* an item GUID
|
||||
* @retrun {Promise}
|
||||
* @resolves to the GUID.
|
||||
* @rejects if there's no item for the given GUID.
|
||||
*/
|
||||
promiseItemId: function (aGUID) GUIDHelper.getItemId(aGUID)
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1640,6 +1664,138 @@ XPCOMUtils.defineLazyServiceGetter(this, "focusManager",
|
||||
"@mozilla.org/focus-manager;1",
|
||||
"nsIFocusManager");
|
||||
|
||||
// Sometime soon, likely as part of the transition to mozIAsyncBookmarks,
|
||||
// itemIds will be deprecated in favour of GUIDs, which play much better
|
||||
// with multiple undo/redo operations. Because these GUIDs are already stored,
|
||||
// and because we don't want to revise the transactions API once more when this
|
||||
// happens, transactions are set to work with GUIDs exclusively, in the sense
|
||||
// that they may never expose itemIds, nor do they accept them as input.
|
||||
// More importantly, transactions which add or remove items guarantee to
|
||||
// restore the guids on undo/redo, so that the following transactions that may
|
||||
// done or undo can assume the items they're interested in are stil accessible
|
||||
// through the same GUID.
|
||||
// The current bookmarks API, however, doesn't expose the necessary means for
|
||||
// working with GUIDs. So, until it does, this helper object accesses the
|
||||
// Places database directly in order to switch between GUIDs and itemIds, and
|
||||
// "restore" GUIDs on items re-created items.
|
||||
const REASON_FINISHED = Ci.mozIStorageStatementCallback.REASON_FINISHED;
|
||||
let GUIDHelper = {
|
||||
// Cache for guid<->itemId paris.
|
||||
GUIDsForIds: new Map(),
|
||||
idsForGUIDs: new Map(),
|
||||
|
||||
getItemId: function (aGUID) {
|
||||
if (this.idsForGUIDs.has(aGUID))
|
||||
return Promise.resolve(this.idsForGUIDs.get(aGUID));
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let itemId = -1;
|
||||
|
||||
this._getIDStatement.params.guid = aGUID;
|
||||
this._getIDStatement.executeAsync({
|
||||
handleResult: function (aResultSet) {
|
||||
let row = aResultSet.getNextRow();
|
||||
if (row)
|
||||
itemId = row.getResultByIndex(0);
|
||||
},
|
||||
handleCompletion: function (aReason) {
|
||||
if (aReason == REASON_FINISHED && itemId != -1) {
|
||||
deferred.resolve(itemId);
|
||||
|
||||
this.ensureObservingRemovedItems();
|
||||
this.idsForGUIDs.set(aGUID, itemId);
|
||||
}
|
||||
else if (itemId != -1) {
|
||||
deferred.reject("no item found for the given guid");
|
||||
}
|
||||
else {
|
||||
deferred.reject("SQLite Error: " + aReason);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
getItemGUID: function (aItemId) {
|
||||
if (this.GUIDsForIds.has(aItemId))
|
||||
return Promise.resolve(this.GUIDsForIds.has(aItemId));
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let guid = "";
|
||||
|
||||
this._getGUIDStatement.params.id = aItemId;
|
||||
this._getGUIDStatement.executeAsync({
|
||||
handleResult: function (aResultSet) {
|
||||
let row = aResultSet.getNextRow();
|
||||
if (row) {
|
||||
guid = row.getResultByIndex(1);
|
||||
}
|
||||
},
|
||||
handleCompletion: function (aReason) {
|
||||
if (aReason == REASON_FINISHED && guid) {
|
||||
deferred.resolve(guid);
|
||||
|
||||
this.ensureObservingRemovedItems();
|
||||
this.GUIDsForIds.set(aItemId, guid);
|
||||
}
|
||||
else if (!guid) {
|
||||
deferred.reject("no item found for the given itemId");
|
||||
}
|
||||
else {
|
||||
deferred.reject("SQLite Error: " + aReason);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
ensureObservingRemovedItems: function () {
|
||||
if (!("observer" in this)) {
|
||||
/**
|
||||
* This observers serves two purposes:
|
||||
* (1) Invalidate cached id<->guid paris on when items are removed.
|
||||
* (2) Cache GUIDs given us free of charge by onItemAdded/onItemRemoved.
|
||||
* So, for exmaple, when the NewBookmark needs the new GUID, we already
|
||||
* have it cached.
|
||||
*/
|
||||
this.observer = {
|
||||
onItemAdded: (aItemId, aParentId, aIndex, aItemType, aURI, aTitle,
|
||||
aDateAdded, aGUID, aParentGUID) => {
|
||||
this.GUIDsForIds.set(aItemId, aGUID);
|
||||
this.GUIDsForIds.set(aParentId, aParentGUID);
|
||||
},
|
||||
onItemRemoved:
|
||||
(aItemId, aParentId, aIndex, aItemTyep, aURI, aGUID, aParentGUID) => {
|
||||
this.GUIDsForIds.delete(aItemId);
|
||||
this.idsForGUIDs.delete(aGUID);
|
||||
this.GUIDsForIds.set(aParentId, aParentGUID);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI(Ci.nsINavBookmarkObserver),
|
||||
__noSuchMethod__: () => {}, // Catch all all onItem* methods.
|
||||
};
|
||||
PlacesUtils.bookmarks.addObserver(this.observer, false);
|
||||
PlacesUtils.registerShutdownFunction(() => {
|
||||
PlacesUtils.bookmarks.removeObserver(this.observer);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
XPCOMUtils.defineLazyGetter(GUIDHelper, "_getIDStatement", () => {
|
||||
let s = PlacesUtils.history.DBConnection.createAsyncStatement(
|
||||
"SELECT b.id, b.guid from moz_bookmarks b WHERE b.guid = :guid");
|
||||
PlacesUtils.registerShutdownFunction( () => s.finalize() );
|
||||
return s;
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(GUIDHelper, "_getGUIDStatement", () => {
|
||||
let s = PlacesUtils.history.DBConnection.createAsyncStatement(
|
||||
"SELECT b.id, b.guid from moz_bookmarks b WHERE b.id = :id");
|
||||
PlacesUtils.registerShutdownFunction( () => s.finalize() );
|
||||
return s;
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Transactions handlers.
|
||||
|
||||
|
@ -67,6 +67,7 @@ if CONFIG['MOZ_PLACES']:
|
||||
'ColorConversion.js',
|
||||
'PlacesBackups.jsm',
|
||||
'PlacesDBUtils.jsm',
|
||||
'PlacesTransactions.jsm',
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
|
@ -126,9 +126,9 @@ LivemarkService.prototype = {
|
||||
stmt.finalize();
|
||||
},
|
||||
|
||||
_onCacheReady: function LS__onCacheReady(aCallback, aWaitForAsyncWrites)
|
||||
_onCacheReady: function LS__onCacheReady(aCallback)
|
||||
{
|
||||
if (this._pendingStmt || aWaitForAsyncWrites) {
|
||||
if (this._pendingStmt) {
|
||||
// The cache is still being populated, so enqueue the job to the Storage
|
||||
// async thread. Ideally this should just dispatch a runnable to it,
|
||||
// that would call back on the main thread, but bug 608142 made that
|
||||
@ -235,9 +235,7 @@ LivemarkService.prototype = {
|
||||
});
|
||||
if (this._itemAdded && this._itemAdded.id == livemark.id) {
|
||||
livemark.index = this._itemAdded.index;
|
||||
if (!aLivemarkInfo.guid) {
|
||||
livemark.guid = this._itemAdded.guid;
|
||||
}
|
||||
livemark.guid = this._itemAdded.guid;
|
||||
if (!aLivemarkInfo.lastModified) {
|
||||
livemark.lastModified = this._itemAdded.lastModified;
|
||||
}
|
||||
@ -246,7 +244,7 @@ LivemarkService.prototype = {
|
||||
// Updating the cache even if it has not yet been populated doesn't
|
||||
// matter since it will just be overwritten.
|
||||
this._livemarks[livemark.id] = livemark;
|
||||
this._guids[aLivemarkInfo.guid] = livemark.id;
|
||||
this._guids[livemark.guid] = livemark.id;
|
||||
}
|
||||
catch (ex) {
|
||||
addLivemarkEx = ex;
|
||||
@ -272,7 +270,7 @@ LivemarkService.prototype = {
|
||||
}
|
||||
deferred.resolve(livemark);
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
@ -558,11 +556,9 @@ function Livemark(aLivemarkInfo)
|
||||
// Create a new livemark.
|
||||
this.id = PlacesUtils.bookmarks.createFolder(aLivemarkInfo.parentId,
|
||||
aLivemarkInfo.title,
|
||||
aLivemarkInfo.index);
|
||||
aLivemarkInfo.index,
|
||||
aLivemarkInfo.guid);
|
||||
PlacesUtils.bookmarks.setFolderReadonly(this.id, true);
|
||||
if (aLivemarkInfo.guid) {
|
||||
this.writeGuid(aLivemarkInfo.guid);
|
||||
}
|
||||
this.writeFeedURI(aLivemarkInfo.feedURI);
|
||||
if (aLivemarkInfo.siteURI) {
|
||||
this.writeSiteURI(aLivemarkInfo.siteURI);
|
||||
@ -630,31 +626,6 @@ Livemark.prototype = {
|
||||
this.siteURI = aSiteURI;
|
||||
},
|
||||
|
||||
writeGuid: function LM_writeGuid(aGUID)
|
||||
{
|
||||
// There isn't a way to create a bookmark with a given guid yet, nor to
|
||||
// set a guid on an existing one. So, for now, just go the dirty way.
|
||||
let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
|
||||
.DBConnection;
|
||||
let stmt = db.createAsyncStatement("UPDATE moz_bookmarks " +
|
||||
"SET guid = :guid " +
|
||||
"WHERE id = :item_id");
|
||||
stmt.params.guid = aGUID;
|
||||
stmt.params.item_id = this.id;
|
||||
let livemark = this;
|
||||
stmt.executeAsync({
|
||||
handleError: function () {},
|
||||
handleResult: function () {},
|
||||
handleCompletion: function ETAT_handleCompletion(aReason)
|
||||
{
|
||||
if (aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED) {
|
||||
livemark._guid = aGUID;
|
||||
}
|
||||
}
|
||||
});
|
||||
stmt.finalize();
|
||||
},
|
||||
|
||||
set guid(aGUID) {
|
||||
this._guid = aGUID;
|
||||
return aGUID;
|
||||
|
@ -36,6 +36,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "BookmarkJSONUtils",
|
||||
"resource://gre/modules/BookmarkJSONUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups",
|
||||
"resource://gre/modules/PlacesBackups.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTransactions",
|
||||
"resource://gre/modules/PlacesTransactions.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
|
||||
|
1053
toolkit/components/places/tests/unit/test_async_transactions.js
Normal file
1053
toolkit/components/places/tests/unit/test_async_transactions.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -138,3 +138,4 @@ skip-if = os == "android"
|
||||
[test_telemetry.js]
|
||||
[test_getPlacesInfo.js]
|
||||
[test_pageGuid_bookmarkGuid.js]
|
||||
[test_async_transactions.js]
|
||||
|
Loading…
Reference in New Issue
Block a user