Bug 914687 - API for presetting GUIDs on bookmarks. r=mak. sr=gavin

This commit is contained in:
Asaf Romano 2013-11-19 17:13:27 +02:00
Родитель 0cf4101913
Коммит bd6038d32e
4 изменённых файлов: 145 добавлений и 49 удалений

Просмотреть файл

@ -100,7 +100,7 @@ interface nsINavBookmarkObserver : nsISupports
/**
* Notifies that an item's information has changed. This will be called
* whenever any attributes like "title" are changed.
*
*
* @param aItemId
* The id of the item that was changed.
* @param aProperty
@ -223,7 +223,7 @@ interface nsINavBookmarkObserver : nsISupports
* folders. A URI in history can be contained in one or more such folders.
*/
[scriptable, uuid(2299598b-8d83-4b67-9b13-b6d4707dcf7b)]
[scriptable, uuid(A78EA368-E28E-462E-897A-26606D4DDCE6)]
interface nsINavBookmarksService : nsISupports
{
/**
@ -276,13 +276,19 @@ interface nsINavBookmarksService : nsISupports
* The index to insert at, or DEFAULT_INDEX to append
* @param aTitle
* The title for the new bookmark
* @param [optional] aGUID
* The GUID to be set for the new item. If not set, a new GUID is
* generated. Unless you've a very sound reason, such as an undo
* manager implementation, do not pass this argument.
* @return The ID of the newly-created bookmark.
*
* @note aTitle will be truncated to TITLE_LENGTH_MAX and
* aURI will be truncated to URI_LENGTH_MAX.
* @throws if aGUID is malformed.
*/
long long insertBookmark(in long long aParentId, in nsIURI aURI,
in long aIndex, in AUTF8String aTitle);
in long aIndex, in AUTF8String aTitle,
[optional] in ACString aGUID);
/**
* Removes a child item. Used to delete a bookmark or separator.
@ -299,24 +305,30 @@ interface nsINavBookmarksService : nsISupports
* The name of the new folder
* @param aIndex
* The index to insert at, or DEFAULT_INDEX to append
* @param [optional] aGUID
* The GUID to be set for the new item. If not set, a new GUID is
* generated. Unless you've a very sound reason, such as an undo
* manager implementation, do not pass this argument.
* @return The ID of the newly-inserted folder.
* @throws if aGUID is malformed.
*/
long long createFolder(in long long aParentFolder, in AUTF8String name,
in long index);
in long index,
[optional] in ACString aGUID);
/**
* Gets an undo-able transaction for removing a folder from the bookmarks
* tree.
* tree.
* @param aItemId
* The id of the folder to remove.
* @return An object implementing nsITransaction that can be used to undo
* or redo the action.
* @return An object implementing nsITransaction that can be used to undo
* or redo the action.
*
* This method exists because complex delete->undo operations rely on
* recreated folders to have the same ID they had before they were deleted,
* so that any other items deleted in different transactions can be
* re-inserted correctly. This provides a safe encapsulation of this
* functionality without exposing the ability to recreate folders with
* This method exists because complex delete->undo operations rely on
* recreated folders to have the same ID they had before they were deleted,
* so that any other items deleted in different transactions can be
* re-inserted correctly. This provides a safe encapsulation of this
* functionality without exposing the ability to recreate folders with
* specific IDs (potentially dangerous if abused by other code!) in the
* public API.
*/
@ -352,9 +364,15 @@ interface nsINavBookmarksService : nsISupports
* The id of the parent folder
* @param aIndex
* The separator's index under folder, or DEFAULT_INDEX to append
* @param [optional] aGUID
* The GUID to be set for the new item. If not set, a new GUID is
* generated. Unless you've a very sound reason, such as an undo
* manager implementation, do not pass this argument.
* @return The ID of the new separator.
* @throws if aGUID is malformed.
*/
long long insertSeparator(in long long aParentId, in long aIndex);
long long insertSeparator(in long long aParentId, in long aIndex,
[optional] in ACString aGUID);
/**
* Get the itemId given the containing folder and the index.
@ -437,7 +455,7 @@ interface nsINavBookmarksService : nsISupports
*
* @param aItemId The id of the item to modify
* @param aNewIndex The new index
*
*
* @throws If aNewIndex is out of bounds.
*/
void setItemIndex(in long long aItemId, in long aNewIndex);
@ -554,7 +572,7 @@ interface nsINavBookmarksService : nsISupports
* Runs the passed callback inside of a database transaction.
* Use this when a lot of things are about to change, for example
* adding or deleting a large number of bookmark items. Calls can
* be nested. Observers are notified when batches begin and end, via
* be nested. Observers are notified when batches begin and end, via
* nsINavBookmarkObserver.onBeginUpdateBatch/onEndUpdateBatch.
*
* @param aCallback

Просмотреть файл

@ -368,7 +368,7 @@ nsNavBookmarks::AdjustIndices(int64_t aFolderId,
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -438,7 +438,7 @@ nsNavBookmarks::InsertBookmarkInDB(int64_t aPlaceId,
"dateAdded, lastModified, guid) "
"VALUES (:item_id, :page_id, :item_type, :parent, :item_index, "
":item_title, :date_added, :last_modified, "
"GENERATE_GUID())"
"IFNULL(:item_guid, GENERATE_GUID()))"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@ -482,6 +482,17 @@ nsNavBookmarks::InsertBookmarkInDB(int64_t aPlaceId,
}
NS_ENSURE_SUCCESS(rv, rv);
// Could use IsEmpty because our callers check for GUID validity,
// but it doesn't hurt.
if (_guid.Length() == 12) {
MOZ_ASSERT(IsValidGUID(_guid));
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_guid"), _guid);
}
else {
rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_guid"));
}
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
@ -551,12 +562,16 @@ nsNavBookmarks::InsertBookmark(int64_t aFolder,
nsIURI* aURI,
int32_t aIndex,
const nsACString& aTitle,
const nsACString& aGUID,
int64_t* aNewBookmarkId)
{
NS_ENSURE_ARG(aURI);
NS_ENSURE_ARG_POINTER(aNewBookmarkId);
NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX);
if (!aGUID.IsEmpty() && !IsValidGUID(aGUID))
return NS_ERROR_INVALID_ARG;
mozStorageTransaction transaction(mDB->MainConn(), false);
nsNavHistory* history = nsNavHistory::GetHistoryService();
@ -585,7 +600,7 @@ nsNavBookmarks::InsertBookmark(int64_t aFolder,
*aNewBookmarkId = -1;
PRTime dateAdded = PR_Now();
nsAutoCString guid;
nsAutoCString guid(aGUID);
nsCString title;
TruncateTitle(aTitle, title);
@ -753,18 +768,22 @@ nsNavBookmarks::RemoveItem(int64_t aItemId)
NS_IMETHODIMP
nsNavBookmarks::CreateFolder(int64_t aParent, const nsACString& aName,
int32_t aIndex, int64_t* aNewFolder)
int32_t aIndex, const nsACString& aGUID,
int64_t* aNewFolder)
{
// NOTE: aParent can be null for root creation, so not checked
NS_ENSURE_ARG_POINTER(aNewFolder);
if (!aGUID.IsEmpty() && !IsValidGUID(aGUID))
return NS_ERROR_INVALID_ARG;
// CreateContainerWithID returns the index of the new folder, but that's not
// used here. To avoid any risk of corrupting data should this function
// be changed, we'll use a local variable to hold it. The true argument
// will cause notifications to be sent to bookmark observers.
int32_t localIndex = aIndex;
nsresult rv = CreateContainerWithID(-1, aParent, aName, true, &localIndex,
aNewFolder);
aGUID, aNewFolder);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -815,6 +834,7 @@ nsNavBookmarks::CreateContainerWithID(int64_t aItemId,
const nsACString& aTitle,
bool aIsBookmarkFolder,
int32_t* aIndex,
const nsACString& aGUID,
int64_t* aNewFolder)
{
NS_ENSURE_ARG_MIN(*aIndex, nsINavBookmarksService::DEFAULT_INDEX);
@ -840,7 +860,7 @@ nsNavBookmarks::CreateContainerWithID(int64_t aItemId,
*aNewFolder = aItemId;
PRTime dateAdded = PR_Now();
nsAutoCString guid;
nsAutoCString guid(aGUID);
nsCString title;
TruncateTitle(aTitle, title);
@ -865,12 +885,16 @@ nsNavBookmarks::CreateContainerWithID(int64_t aItemId,
NS_IMETHODIMP
nsNavBookmarks::InsertSeparator(int64_t aParent,
int32_t aIndex,
const nsACString& aGUID,
int64_t* aNewItemId)
{
NS_ENSURE_ARG_MIN(aParent, 1);
NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX);
NS_ENSURE_ARG_POINTER(aNewItemId);
if (!aGUID.IsEmpty() && !IsValidGUID(aGUID))
return NS_ERROR_INVALID_ARG;
// Get the correct index for insertion. This also ensures the parent exists.
int32_t index, folderCount;
int64_t grandParentId;
@ -895,7 +919,7 @@ nsNavBookmarks::InsertSeparator(int64_t aParent,
// Set a NULL title rather than an empty string.
nsCString voidString;
voidString.SetIsVoid(true);
nsAutoCString guid;
nsAutoCString guid(aGUID);
PRTime dateAdded = PR_Now();
rv = InsertBookmarkInDB(-1, SEPARATOR, aParent, index, voidString, dateAdded,
0, folderGuid, grandParentId, nullptr,
@ -991,9 +1015,9 @@ nsNavBookmarks::GetRemoveFolderTransaction(int64_t aFolderId, nsITransaction** a
NS_ENSURE_ARG_POINTER(aResult);
// Create and initialize a RemoveFolderTransaction object that can be used to
// recreate the folder safely later.
// recreate the folder safely later.
RemoveFolderTransaction* rft =
RemoveFolderTransaction* rft =
new RemoveFolderTransaction(aFolderId);
if (!rft)
return NS_ERROR_OUT_OF_MEMORY;
@ -2052,7 +2076,7 @@ nsNavBookmarks::GetBookmarkedURIFor(nsIURI* aURI, nsIURI** _retval)
"UNION ALL "
"SELECT COALESCE(grandparent.place_id, parent.place_id) AS r_place_id "
"FROM moz_historyvisits dest "
"LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit "
"LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit "
"AND dest.visit_type IN (%d, %d) "
"LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id "
"AND parent.visit_type IN (%d, %d) "
@ -2836,7 +2860,7 @@ nsNavBookmarks::OnPageChanged(nsIURI* aURI,
if (isPlaceURI) {
nsNavHistory* history = nsNavHistory::GetHistoryService();
NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
nsCOMArray<nsNavHistoryQuery> queries;
nsCOMPtr<nsNavHistoryQueryOptions> options;
rv = history->QueryStringToQueryArray(changeData.bookmark.url,
@ -2846,7 +2870,7 @@ nsNavBookmarks::OnPageChanged(nsIURI* aURI,
if (queries.Count() == 1 && queries[0]->Folders().Length() == 1) {
// Fetch missing data.
rv = FetchItemInfo(queries[0]->Folders()[0], changeData.bookmark);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_SUCCESS(rv, rv);
NotifyItemChanged(changeData);
}
}

Просмотреть файл

@ -184,6 +184,7 @@ public:
const nsACString& aTitle,
bool aIsBookmarkFolder,
int32_t* aIndex,
const nsACString& aGUID,
int64_t* aNewFolder);
/**
@ -396,7 +397,7 @@ private:
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
int64_t newFolder;
return bookmarks->CreateContainerWithID(mID, mParent, mTitle, true,
&mIndex, &newFolder);
&mIndex, EmptyCString(), &newFolder);
}
NS_IMETHOD RedoTransaction() {
@ -407,7 +408,7 @@ private:
*aResult = false;
return NS_OK;
}
NS_IMETHOD Merge(nsITransaction* aTransaction, bool* aResult) {
*aResult = false;
return NS_OK;

Просмотреть файл

@ -22,13 +22,7 @@ add_task(function test_addBookmarksAndCheckGuids() {
let s1 = bmsvc.insertSeparator(folder, bmsvc.DEFAULT_INDEX);
let f1 = bmsvc.createFolder(folder, "test folder 2", bmsvc.DEFAULT_INDEX);
let options = histsvc.getNewQueryOptions();
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
let query = histsvc.getNewQuery();
query.setFolders([folder], 1);
let result = histsvc.executeQuery(query, options);
let root = result.root;
root.containerOpen = true;
let root = PlacesUtils.getFolderContents(folder).root;
do_check_eq(root.childCount, 5);
// check bookmark guids
@ -71,13 +65,7 @@ add_task(function test_updateBookmarksAndCheckGuids() {
bmsvc.DEFAULT_INDEX, "1 title");
let f1 = bmsvc.createFolder(folder, "test folder 2", bmsvc.DEFAULT_INDEX);
let options = histsvc.getNewQueryOptions();
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
let query = histsvc.getNewQuery();
query.setFolders([folder], 1);
let result = histsvc.executeQuery(query, options);
let root = result.root;
root.containerOpen = true;
let root = PlacesUtils.getFolderContents(folder).root;
do_check_eq(root.childCount, 2);
// ensure the bookmark and page guids remain the same after modifing other property.
@ -110,18 +98,83 @@ add_task(function test_addVisitAndCheckGuid() {
let options = histsvc.getNewQueryOptions();
let query = histsvc.getNewQuery();
query.uri = sourceURI;
result = histsvc.executeQuery(query, options);
let root = result.root;
let root = histsvc.executeQuery(query, options).root;
root.containerOpen = true;
do_check_eq(root.childCount, 1);
pageGuidZero = root.getChild(0).pageGuid;
do_check_eq(pageGuidZero.length, 12);
do_check_valid_places_guid(root.getChild(0).pageGuid);
do_check_eq(root.getChild(0).bookmarkGuid, "");
root.containerOpen = false;
yield promiseClearHistory();
});
add_task(function test_addItemsWithInvalidGUIDsFails() {
const INVALID_GUID = "XYZ";
try {
bmsvc.createFolder(bmsvc.placesRoot, "XYZ folder",
bmsvc.DEFAULT_INDEX, INVALID_GUID);
do_throw("Adding a folder with an invalid guid should fail");
}
catch(ex) { }
let folder = bmsvc.createFolder(bmsvc.placesRoot, "test folder",
bmsvc.DEFAULT_INDEX);
try {
bmsvc.insertBookmark(folder, uri("http://test.tld"), bmsvc.DEFAULT_INDEX,
"title", INVALID_GUID);
do_throw("Adding a bookmark with an invalid guid should fail");
}
catch(ex) { }
try {
bmsvc.insertSeparator(folder, bmsvc.DEFAULT_INDEX, INVALID_GUID);
do_throw("Adding a separator with an invalid guid should fail");
}
catch(ex) { }
remove_all_bookmarks();
});
add_task(function test_addItemsWithGUIDs() {
const FOLDER_GUID = "FOLDER--GUID";
const BOOKMARK_GUID = "BM------GUID";
const SEPARATOR_GUID = "SEP-----GUID";
let folder = bmsvc.createFolder(bmsvc.placesRoot, "test folder",
bmsvc.DEFAULT_INDEX, FOLDER_GUID);
bmsvc.insertBookmark(folder, uri("http://test1.com/"), bmsvc.DEFAULT_INDEX,
"1 title", BOOKMARK_GUID);
bmsvc.insertSeparator(folder, bmsvc.DEFAULT_INDEX, SEPARATOR_GUID);
let root = PlacesUtils.getFolderContents(folder).root;
do_check_eq(root.childCount, 2);
do_check_eq(root.bookmarkGuid, FOLDER_GUID);
do_check_eq(root.getChild(0).bookmarkGuid, BOOKMARK_GUID);
do_check_eq(root.getChild(1).bookmarkGuid, SEPARATOR_GUID);
root.containerOpen = false;
remove_all_bookmarks();
});
add_task(function test_emptyGUIDIgnored() {
let folder = bmsvc.createFolder(bmsvc.placesRoot, "test folder",
bmsvc.DEFAULT_INDEX, "");
do_check_valid_places_guid(PlacesUtils.getFolderContents(folder)
.root.bookmarkGuid);
remove_all_bookmarks();
});
add_task(function test_usingSameGUIDFails() {
const GUID = "XYZXYZXYZXYZ";
bmsvc.createFolder(bmsvc.placesRoot, "test folder",
bmsvc.DEFAULT_INDEX, GUID);
try {
bmsvc.createFolder(bmsvc.placesRoot, "test folder 2",
bmsvc.DEFAULT_INDEX, GUID);
do_throw("Using the same guid twice should fail");
}
catch(ex) { }
remove_all_bookmarks();
});