Bug 613588 (Backend) - Load-on-demand livemarks.

r=dietrich,mano
This commit is contained in:
Marco Bonardo 2012-02-24 13:42:24 +01:00
parent 9bc089b365
commit 9927c72144
15 changed files with 1825 additions and 802 deletions

View File

@ -95,6 +95,10 @@
// Places string bundle, contains internationalized bookmark root names.
#define PLACES_BUNDLE "chrome://places/locale/places.properties"
// Livemarks annotations.
#define LMANNO_FEEDURI "livemark/feedURI"
#define LMANNO_SITEURI "livemark/siteURI"
using namespace mozilla;
namespace mozilla {
@ -749,7 +753,12 @@ Database::InitSchema(bool* aDatabaseMigrated)
NS_ENSURE_SUCCESS(rv, rv);
}
// Firefox 13 uses schema version 18.
if (currentSchemaVersion < 19) {
rv = MigrateV19Up();
NS_ENSURE_SUCCESS(rv, rv);
}
// Firefox 13 uses schema version 19.
// Schema Upgrades must add migration code here.
@ -1750,6 +1759,85 @@ Database::MigrateV18Up()
return NS_OK;
}
nsresult
Database::MigrateV19Up()
{
MOZ_ASSERT(NS_IsMainThread());
// Livemarks children are no longer bookmarks.
// Remove all children of folders annotated as livemarks.
nsCOMPtr<mozIStorageStatement> deleteLivemarksChildrenStmt;
nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_bookmarks WHERE parent IN("
"SELECT b.id FROM moz_bookmarks b "
"JOIN moz_items_annos a ON a.item_id = b.id "
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
"WHERE b.type = :item_type AND n.name = :anno_name "
")"
), getter_AddRefs(deleteLivemarksChildrenStmt));
rv = deleteLivemarksChildrenStmt->BindUTF8StringByName(
NS_LITERAL_CSTRING("anno_name"), NS_LITERAL_CSTRING(LMANNO_FEEDURI)
);
NS_ENSURE_SUCCESS(rv, rv);
rv = deleteLivemarksChildrenStmt->BindInt32ByName(
NS_LITERAL_CSTRING("item_type"), nsINavBookmarksService::TYPE_FOLDER
);
NS_ENSURE_SUCCESS(rv, rv);
rv = deleteLivemarksChildrenStmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
// Clear obsolete livemark prefs.
(void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_seconds");
(void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_limit_count");
(void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_delay_time");
// Remove the old status annotations.
nsCOMPtr<mozIStorageStatement> deleteLivemarksAnnosStmt;
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_items_annos WHERE anno_attribute_id IN("
"SELECT id FROM moz_anno_attributes "
"WHERE name IN (:anno_loading, :anno_loadfailed, :anno_expiration) "
")"
), getter_AddRefs(deleteLivemarksAnnosStmt));
rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
NS_LITERAL_CSTRING("anno_loading"), NS_LITERAL_CSTRING("livemark/loading")
);
NS_ENSURE_SUCCESS(rv, rv);
rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
NS_LITERAL_CSTRING("anno_loadfailed"), NS_LITERAL_CSTRING("livemark/loadfailed")
);
NS_ENSURE_SUCCESS(rv, rv);
rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
NS_LITERAL_CSTRING("anno_expiration"), NS_LITERAL_CSTRING("livemark/expiration")
);
NS_ENSURE_SUCCESS(rv, rv);
rv = deleteLivemarksAnnosStmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
// Remove orphan annotation names.
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_anno_attributes "
"WHERE name IN (:anno_loading, :anno_loadfailed, :anno_expiration) "
), getter_AddRefs(deleteLivemarksAnnosStmt));
rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
NS_LITERAL_CSTRING("anno_loading"), NS_LITERAL_CSTRING("livemark/loading")
);
NS_ENSURE_SUCCESS(rv, rv);
rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
NS_LITERAL_CSTRING("anno_loadfailed"), NS_LITERAL_CSTRING("livemark/loadfailed")
);
NS_ENSURE_SUCCESS(rv, rv);
rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
NS_LITERAL_CSTRING("anno_expiration"), NS_LITERAL_CSTRING("livemark/expiration")
);
NS_ENSURE_SUCCESS(rv, rv);
rv = deleteLivemarksAnnosStmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
Database::Shutdown()
{

View File

@ -47,7 +47,7 @@
// This is the schema version. Update it at any schema change and add a
// corresponding migrateVxx method below.
#define DATABASE_SCHEMA_VERSION 18
#define DATABASE_SCHEMA_VERSION 19
// Fired after Places inited.
#define TOPIC_PLACES_INIT_COMPLETE "places-init-complete"
@ -301,6 +301,7 @@ protected:
nsresult MigrateV16Up();
nsresult MigrateV17Up();
nsresult MigrateV18Up();
nsresult MigrateV19Up();
nsresult UpdateBookmarkRootTitles();
nsresult CheckAndUpdateGUIDs();

View File

@ -46,6 +46,7 @@
#if defined(XP_OS2)
#include "nsIRandomGenerator.h"
#endif
#include "nsContentUtils.h"
// The length of guids that are used by history and bookmarks.
#define GUID_LENGTH 12
@ -444,5 +445,64 @@ AsyncStatementTelemetryTimer::HandleCompletion(PRUint16 aReason)
return NS_OK;
}
// This is a temporary converter used by nsPlacesImportExportService until
// bug 482911 completes its js rewrite.
jsval
livemarkInfoToJSVal(PRInt64 aId,
const nsACString& aGUID,
const nsAString& aTitle,
PRInt64 aParentId,
PRInt32 aIndex,
nsCOMPtr<nsIURI>& aFeedURI,
nsCOMPtr<nsIURI>& aSiteURI)
{
nsCOMPtr<nsIXPConnect> xpc = mozilla::services::GetXPConnect();
NS_ENSURE_TRUE(xpc, JSVAL_NULL);
nsAXPCNativeCallContext *ncc;
nsresult rv = xpc->GetCurrentNativeCallContext(&ncc);
NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
JSContext *cx = nsnull;
rv = ncc->GetJSContext(&cx);
NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
NS_ENSURE_TRUE(obj, JSVAL_NULL);
jsval id;
NS_ENSURE_TRUE(JS_NewNumberValue(cx, jsdouble(aId), &id), JSVAL_NULL);
JSString* guid = JS_NewStringCopyN(cx, PromiseFlatCString(aGUID).get(),
aGUID.Length());
NS_ENSURE_TRUE(guid, JSVAL_NULL);
JSString* title = JS_NewUCStringCopyN(cx, PromiseFlatString(aTitle).get(),
aTitle.Length());
NS_ENSURE_TRUE(title, JSVAL_NULL);
jsval parentId;
NS_ENSURE_TRUE(JS_NewNumberValue(cx, jsdouble(aParentId), &parentId), JSVAL_NULL);
jsval feedURI;
rv = nsContentUtils::WrapNative(cx, JS_GetGlobalForScopeChain(cx),
NS_ISUPPORTS_CAST(nsIURI*, aFeedURI), &feedURI);
NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
jsval siteURI;
rv = nsContentUtils::WrapNative(cx, JS_GetGlobalForScopeChain(cx),
NS_ISUPPORTS_CAST(nsIURI*, aSiteURI), &siteURI);
NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
if (!JS_DefineProperty(cx, obj, "id", id, NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "guid", STRING_TO_JSVAL(guid), NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "title", STRING_TO_JSVAL(title), NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "parentId", parentId, NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "index", INT_TO_JSVAL(aIndex), NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "feedURI", feedURI, NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "siteURI", siteURI, NULL, NULL, JSPROP_ENUMERATE)) {
return JSVAL_NULL;
}
return OBJECT_TO_JSVAL(obj);
}
} // namespace places
} // namespace mozilla

View File

@ -48,6 +48,7 @@
#include "nsThreadUtils.h"
#include "nsProxyRelease.h"
#include "mozilla/Telemetry.h"
#include "jsapi.h"
namespace mozilla {
namespace places {
@ -295,6 +296,16 @@ private:
const Telemetry::ID mHistogramId;
const TimeStamp mStart;
};
jsval
livemarkInfoToJSVal(PRInt64 aId,
const nsACString& aGUID,
const nsAString& aTitle,
PRInt64 aParentId,
PRInt32 aIndex,
nsCOMPtr<nsIURI>& aFeedURI,
nsCOMPtr<nsIURI>& aSiteURI);
} // namespace places
} // namespace mozilla

View File

@ -611,18 +611,6 @@ let PlacesDBUtils = {
"DROP TABLE moz_bm_reindex_temp "
));
// D.11 remove old livemarks status items
// Livemark status items are now static but some livemark has still old
// status items bookmarks inside it. We should remove them.
let removeLivemarkStaticItems = DBConn.createAsyncStatement(
"DELETE FROM moz_bookmarks WHERE type = :bookmark_type AND fk IN ( " +
"SELECT id FROM moz_places WHERE url = :lmloading OR url = :lmfailed " +
")");
removeLivemarkStaticItems.params["bookmark_type"] = PlacesUtils.bookmarks.TYPE_BOOKMARK;
removeLivemarkStaticItems.params["lmloading"] = "about:livemark-loading";
removeLivemarkStaticItems.params["lmfailed"] = "about:livemark-failed";
cleanupStatements.push(removeLivemarkStaticItems);
// D.12 Fix empty-named tags.
// Tags were allowed to have empty names due to a UI bug. Fix them
// replacing their title with "(notitle)".

View File

@ -55,8 +55,6 @@ const EXPORTED_SYMBOLS = [
, "PlacesSetPageAnnotationTransaction"
, "PlacesEditBookmarkKeywordTransaction"
, "PlacesEditBookmarkPostDataTransaction"
, "PlacesEditLivemarkSiteURITransaction"
, "PlacesEditLivemarkFeedURITransaction"
, "PlacesEditItemDateAddedTransaction"
, "PlacesEditItemLastModifiedTransaction"
, "PlacesSortFolderByNameTransaction"
@ -327,8 +325,8 @@ var PlacesUtils = {
case "bookmarks-service-ready":
this._bookmarksServiceReady = true;
while (this._bookmarksServiceObserversQueue.length > 0) {
let observer = this._bookmarksServiceObserversQueue.shift();
this.bookmarks.addObserver(observer, false);
let observerInfo = this._bookmarksServiceObserversQueue.shift();
this.bookmarks.addObserver(observerInfo.observer, observerInfo.weak);
}
break;
}
@ -497,8 +495,13 @@ var PlacesUtils = {
* @param aItemId
* The id of the potential livemark.
* @returns true if the item is a livemark.
* @deprecated see the new API in mozIAsyncLivemarks.
*/
itemIsLivemark: function PU_itemIsLivemark(aItemId) {
Cu.reportError("Synchronous livemarks methods and PlacesUtils livemarks " +
"utils (itemIsLivemark, nodeIsLivemarkContainer, " +
"nodeIsLivemarkItem) are deprecated and will be removed " +
"in a future release.");
// If the Livemark service hasn't yet been initialized then
// use the annotations service directly to avoid instanciating
// it on startup. (bug 398300)
@ -513,6 +516,7 @@ var PlacesUtils = {
* @param aNode
* A result Node
* @returns true if the node is a livemark container item
* @deprecated see the new API in mozIAsyncLivemarks.
*/
nodeIsLivemarkContainer: function PU_nodeIsLivemarkContainer(aNode) {
return this.nodeIsFolder(aNode) && this.itemIsLivemark(aNode.itemId);
@ -523,6 +527,7 @@ var PlacesUtils = {
* @param aNode
* A result node
* @returns true if the node is a livemark container item
* @deprecated see the new API in mozIAsyncLivemarks.
*/
nodeIsLivemarkItem: function PU_nodeIsLivemarkItem(aNode) {
return aNode.parent && this.nodeIsLivemarkContainer(aNode.parent);
@ -588,6 +593,22 @@ var PlacesUtils = {
return [cNode, false];
}
function gatherLivemarkUrl(aNode) {
try {
return PlacesUtils.annotations.getItemAnnotation(aNode.itemId,
this.LMANNO_SITEURI);
} catch (ex) {
return PlacesUtils.annotations.getItemAnnotation(aNode.itemId,
this.LMANNO_FEEDURI);
}
}
function isLivemark(aNode) {
return PlacesUtils.nodeIsFolder(aNode) &&
PlacesUtils.annotations.itemHasAnnotation(aNode.itemId,
this.LMANNO_FEEDURI);
}
switch (aType) {
case this.TYPE_X_MOZ_PLACE:
case this.TYPE_X_MOZ_PLACE_SEPARATOR:
@ -608,11 +629,10 @@ var PlacesUtils = {
}
case this.TYPE_X_MOZ_URL: {
function gatherDataUrl(bNode) {
if (PlacesUtils.nodeIsLivemarkContainer(bNode)) {
let uri = PlacesUtils.livemarks.getSiteURI(bNode.itemId) ||
PlacesUtils.livemarks.getFeedURI(bNode.itemId);
return uri.spec + NEWLINE + bNode.title;
if (isLivemark(bNode)) {
return gatherLivemarkUrl(bNode) + NEWLINE + bNode.title;
}
if (PlacesUtils.nodeIsURI(bNode))
return (aOverrideURI || bNode.uri) + NEWLINE + bNode.title;
// ignore containers and separators - items without valid URIs
@ -638,11 +658,11 @@ var PlacesUtils = {
}
// escape out potential HTML in the title
let escapedTitle = bNode.title ? htmlEscape(bNode.title) : "";
if (PlacesUtils.nodeIsLivemarkContainer(bNode)) {
let uri = PlacesUtils.livemarks.getSiteURI(bNode.itemId) ||
PlacesUtils.livemarks.getFeedURI(bNode.itemId);
return "<A HREF=\"" + uri.spec + "\">" + escapedTitle + "</A>" + NEWLINE;
if (isLivemark(bNode)) {
return "<A HREF=\"" + gatherLivemarkUrl(bNode) + "\">" + escapedTitle + "</A>" + NEWLINE;
}
if (PlacesUtils.nodeIsContainer(bNode)) {
asContainer(bNode);
let wasOpen = bNode.containerOpen;
@ -678,9 +698,10 @@ var PlacesUtils = {
// Otherwise, we wrap as TYPE_UNICODE.
function gatherDataText(bNode) {
if (PlacesUtils.nodeIsLivemarkContainer(bNode))
return PlacesUtils.livemarks.getSiteURI(bNode.itemId) ||
PlacesUtils.livemarks.getFeedURI(bNode.itemId);
if (isLivemark(bNode)) {
return gatherLivemarkUrl(bNode);
}
if (PlacesUtils.nodeIsContainer(bNode)) {
asContainer(bNode);
let wasOpen = bNode.containerOpen;
@ -1040,8 +1061,7 @@ var PlacesUtils = {
},
/**
* Get all bookmarks for a URL, excluding items under tag or livemark
* containers.
* Get all bookmarks for a URL, excluding items under tags.
*/
getBookmarksForURI:
function PU_getBookmarksForURI(aURI) {
@ -1050,9 +1070,6 @@ var PlacesUtils = {
// filter the ids list
return bmkIds.filter(function(aID) {
var parentId = this.bookmarks.getFolderIdForItem(aID);
// Livemark child
if (this.itemIsLivemark(parentId))
return false;
var grandparentId = this.bookmarks.getFolderIdForItem(parentId);
// item under a tag container
if (grandparentId == this.tagsFolderId)
@ -1063,7 +1080,7 @@ var PlacesUtils = {
/**
* Get the most recently added/modified bookmark for a URL, excluding items
* under tag or livemark containers.
* under tags.
*
* @param aURI
* nsIURI of the page we will look for.
@ -1084,42 +1101,12 @@ var PlacesUtils = {
return itemId;
var grandparentId = this.bookmarks.getFolderIdForItem(parentId);
if (grandparentId != this.tagsFolderId &&
!this.itemIsLivemark(parentId))
if (grandparentId != this.tagsFolderId)
return itemId;
}
return -1;
},
/**
* Get the most recent folder item id for a feed URI.
*
* @param aURI
* nsIURI of the feed we will look for.
* @returns folder item id of the found livemark, or -1 if nothing is found.
*/
getMostRecentFolderForFeedURI:
function PU_getMostRecentFolderForFeedURI(aFeedURI) {
// If the Livemark service hasn't yet been initialized then
// use the annotations service directly to avoid instanciating
// it on startup. (bug 398300)
if (Object.getOwnPropertyDescriptor(this, "livemarks").value === undefined) {
var feedSpec = aFeedURI.spec
var annosvc = this.annotations;
var livemarks = annosvc.getItemsWithAnnotation(this.LMANNO_FEEDURI);
for (var i = 0; i < livemarks.length; i++) {
if (annosvc.getItemAnnotation(livemarks[i], this.LMANNO_FEEDURI) == feedSpec)
return livemarks[i];
}
}
else {
// If the livemark service has already been instanciated, use it.
return this.livemarks.getLivemarkIdForFeedURI(aFeedURI);
}
return -1;
},
/**
* Returns a nsNavHistoryContainerResultNode with forced excludeItems and
* expandQueries.
@ -1409,10 +1396,24 @@ var PlacesUtils = {
}, this);
if (feedURI) {
id = this.livemarks.createLivemarkFolderOnly(aContainer,
aData.title,
siteURI, feedURI,
aIndex);
this.livemarks.addLivemark(
{ title: aData.title
, feedURI: feedURI
, parentId: aContainer
, index: aIndex
, lastModified: aData.lastModified
, siteURI: siteURI
},
(function(aStatus, aLivemark) {
if (Components.isSuccessCode(aStatus)) {
let id = aLivemark.id;
if (aData.dateAdded)
this.bookmarks.setItemDateAdded(id, aData.dateAdded);
if (aData.annos && aData.annos.length)
this.setAnnotationsForItem(id, aData.annos);
}
}).bind(this)
);
}
}
else {
@ -2086,11 +2087,6 @@ var PlacesUtils = {
+ "FROM moz_bookmarks b "
+ "JOIN moz_places h on h.id = b.fk "
+ "WHERE h.url = :url "
+ "AND NOT EXISTS( "
+ "SELECT 1 FROM moz_items_annos a "
+ "JOIN moz_anno_attributes n ON a.anno_attribute_id = n.id "
+ "WHERE a.item_id = b.parent AND n.name = :name "
+ ") "
);
this.registerShutdownFunction(function () {
this._asyncGetBookmarksStmt.finalize();
@ -2099,7 +2095,6 @@ var PlacesUtils = {
let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
this._asyncGetBookmarksStmt.params.url = url;
this._asyncGetBookmarksStmt.params.name = this.LMANNO_FEEDURI;
// Storage does not guarantee that invoking cancel() on a statement
// will cause a REASON_CANCELED. Thus we wrap the statement.
@ -2127,6 +2122,9 @@ var PlacesUtils = {
*
* @param aObserver
* Object implementing nsINavBookmarkObserver
* @param [optional]aWeakOwner
* Whether to use weak ownership.
*
* @note Correct functionality of lazy observers relies on the fact Places
* notifies categories before real observers, and uses
* PlacesCategoriesStarter component to kick-off the registration.
@ -2134,12 +2132,13 @@ var PlacesUtils = {
_bookmarksServiceReady: false,
_bookmarksServiceObserversQueue: [],
addLazyBookmarkObserver:
function PU_addLazyBookmarkObserver(aObserver) {
function PU_addLazyBookmarkObserver(aObserver, aWeakOwner) {
if (this._bookmarksServiceReady) {
this.bookmarks.addObserver(aObserver, false);
this.bookmarks.addObserver(aObserver, aWeakOwner === true);
return;
}
this._bookmarksServiceObserversQueue.push(aObserver);
this._bookmarksServiceObserversQueue.push({ observer: aObserver,
weak: aWeakOwner === true });
},
/**
@ -2151,14 +2150,19 @@ var PlacesUtils = {
removeLazyBookmarkObserver:
function PU_removeLazyBookmarkObserver(aObserver) {
if (this._bookmarksServiceReady) {
this.bookmarks.removeObserver(aObserver, false);
this.bookmarks.removeObserver(aObserver);
return;
}
let index = this._bookmarksServiceObserversQueue.indexOf(aObserver);
let index = -1;
for (let i = 0;
i < this._bookmarksServiceObserversQueue.length && index == -1; i++) {
if (this._bookmarksServiceObserversQueue[i].observer === aObserver)
index = i;
}
if (index != -1) {
this._bookmarksServiceObserversQueue.splice(index, 1);
}
},
}
};
/**
@ -2224,9 +2228,11 @@ XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "tagging",
"@mozilla.org/browser/tagging-service;1",
"nsITaggingService");
XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "livemarks",
"@mozilla.org/browser/livemark-service;2",
"nsILivemarkService");
XPCOMUtils.defineLazyGetter(PlacesUtils, "livemarks", function() {
return Cc["@mozilla.org/browser/livemark-service;2"].
getService(Ci.nsILivemarkService).
QueryInterface(Ci.mozIAsyncLivemarks);
});
XPCOMUtils.defineLazyGetter(PlacesUtils, "transactionManager", function() {
let tm = Cc["@mozilla.org/transactionmanager;1"].
@ -2629,8 +2635,7 @@ PlacesCreateSeparatorTransaction.prototype = {
/**
* Transaction for creating a new livemark item.
*
* @see nsILivemarksService::createLivemark for documentation regarding the
* first three arguments.
* @see mozIAsyncLivemarks for documentation regarding the arguments.
*
* @param aFeedURI
* nsIURI of the feed
@ -2664,25 +2669,41 @@ PlacesCreateLivemarkTransaction.prototype = {
doTransaction: function CLTXN_doTransaction()
{
this.item.id = PlacesUtils.livemarks.createLivemark(this.item.parentId,
this.item.title,
this.item.siteURI,
this.item.feedURI,
this.item.index);
if (this.item.annotations && this.item.annotations.length > 0)
PlacesUtils.setAnnotationsForItem(this.item.id, this.item.annotations);
if (this.item.guid)
PlacesUtils.bookmarks.setItemGUID(this.item.id, this.item.guid);
PlacesUtils.livemarks.addLivemark(
{ title: this.item.title
, feedURI: this.item.feedURI
, parentId: this.item.parentId
, index: this.item.index
, siteURI: this.item.siteURI
},
(function(aStatus, aLivemark) {
if (Components.isSuccessCode(aStatus)) {
this.item.id = aLivemark.id;
if (this.item.annotations && this.item.annotations.length > 0) {
PlacesUtils.setAnnotationsForItem(this.item.id,
this.item.annotations);
}
if (this.item.guid)
PlacesUtils.bookmarks.setItemGUID(this.item.id, this.item.guid);
}
}).bind(this)
);
},
undoTransaction: function CLTXN_undoTransaction()
{
// If a GUID exists for this item, preserve it before removing the item.
if (PlacesUtils.annotations.itemHasAnnotation(this.item.id,
PlacesUtils.GUID_ANNO))
this.item.guid = PlacesUtils.bookmarks.getItemGUID(this.item.id);
// The getLivemark callback is expected to receive a failure status but it
// is used just to serialize, so doesn't matter.
PlacesUtils.livemarks.getLivemark(
{ id: this.item.id },
(function (aStatus, aLivemark) {
// If a GUID exists for this item, preserve it before removing the item.
if (PlacesUtils.annotations.itemHasAnnotation(this.item.id, PlacesUtils.GUID_ANNO))
this.item.guid = PlacesUtils.bookmarks.getItemGUID(this.item.id);
PlacesUtils.bookmarks.removeItem(this.item.id);
PlacesUtils.bookmarks.removeItem(this.item.id);
}).bind(this)
);
}
};
@ -2713,8 +2734,6 @@ function PlacesRemoveLivemarkTransaction(aLivemarkId)
this.item.annotations = annos.filter(function(aValue, aIndex, aArray) {
return annosToExclude.indexOf(aValue.name) == -1;
});
this.item.feedURI = PlacesUtils.livemarks.getFeedURI(this.item.id);
this.item.siteURI = PlacesUtils.livemarks.getSiteURI(this.item.id);
this.item.dateAdded = PlacesUtils.bookmarks.getItemDateAdded(this.item.id);
this.item.lastModified =
PlacesUtils.bookmarks.getItemLastModified(this.item.id);
@ -2725,22 +2744,45 @@ PlacesRemoveLivemarkTransaction.prototype = {
doTransaction: function RLTXN_doTransaction()
{
this.item.index = PlacesUtils.bookmarks.getItemIndex(this.item.id);
PlacesUtils.bookmarks.removeItem(this.item.id);
PlacesUtils.livemarks.getLivemark(
{ id: this.item.id },
(function (aStatus, aLivemark) {
if (Components.isSuccessCode(aStatus)) {
this.item.feedURI = aLivemark.feedURI;
this.item.siteURI = aLivemark.siteURI;
PlacesUtils.bookmarks.removeItem(this.item.id);
}
}).bind(this)
);
},
undoTransaction: function RLTXN_undoTransaction()
{
this.item.id = PlacesUtils.livemarks.createLivemark(this.item.parentId,
this.item.title,
this.item.siteURI,
this.item.feedURI,
this.item.index);
PlacesUtils.bookmarks.setItemDateAdded(this.item.id, this.item.dateAdded);
PlacesUtils.bookmarks.setItemLastModified(this.item.id,
this.item.lastModified);
// Restore annotations
PlacesUtils.setAnnotationsForItem(this.item.id, this.item.annotations);
// Undo work must be serialized, otherwise won't be able to know the
// feedURI and siteURI of the livemark.
// The getLivemark callback is expected to receive a failure status but it
// is used just to serialize, so doesn't matter.
PlacesUtils.livemarks.getLivemark(
{ id: this.item.id },
(function () {
let addLivemarkCallback = (function(aStatus, aLivemark) {
if (Components.isSuccessCode(aStatus)) {
let itemId = aLivemark.id;
PlacesUtils.bookmarks.setItemDateAdded(itemId, this.item.dateAdded);
PlacesUtils.setAnnotationsForItem(itemId, this.item.annotations);
}
}).bind(this);
PlacesUtils.livemarks.addLivemark({ parentId: this.item.parentId
, title: this.item.title
, siteURI: this.item.siteURI
, feedURI: this.item.feedURI
, index: this.item.index
, lastModified: this.item.lastModified
},
addLivemarkCallback);
}).bind(this)
);
}
};
@ -2816,9 +2858,9 @@ function PlacesRemoveItemTransaction(aItemId)
return new PlacesUntagURITransaction(uri, [parent]);
}
// if the item is a livemark container we will not save its children and
// will use createLivemark to undo.
if (PlacesUtils.itemIsLivemark(aItemId))
// if the item is a livemark container we will not save its children.
if (PlacesUtils.annotations.itemHasAnnotation(aItemId,
PlacesUtils.LMANNO_FEEDURI))
return new PlacesRemoveLivemarkTransaction(aItemId);
this.item = new TransactionItemCache();
@ -2865,8 +2907,8 @@ PlacesRemoveItemTransaction.prototype = {
PlacesUtils.bookmarks.removeItem(this.item.id);
// If this was the last bookmark (excluding tag-items and livemark
// children) for this url, persist the tags.
// If this was the last bookmark (excluding tag-items) for this url,
// persist the tags.
if (tags && PlacesUtils.getMostRecentBookmarkForURI(this.item.uri) == -1) {
this.item.tags = tags;
}
@ -3194,76 +3236,6 @@ PlacesEditBookmarkPostDataTransaction.prototype = {
};
/**
* Transaction for editing a live bookmark's site URI.
*
* @param aLivemarkId
* id of the livemark
* @param aSiteURI
* new site uri
*
* @return nsITransaction object
*/
function PlacesEditLivemarkSiteURITransaction(aLivemarkId, aSiteURI)
{
this.item = new TransactionItemCache();
this.item.id = aLivemarkId;
this.new = new TransactionItemCache();
this.new.siteURI = aSiteURI;
}
PlacesEditLivemarkSiteURITransaction.prototype = {
__proto__: BaseTransaction.prototype,
doTransaction: function ELSUTXN_doTransaction()
{
this.item.siteURI = PlacesUtils.livemarks.getSiteURI(this.item.id);
PlacesUtils.livemarks.setSiteURI(this.item.id, this.new.siteURI);
},
undoTransaction: function ELSUTXN_undoTransaction()
{
PlacesUtils.livemarks.setSiteURI(this.item.id, this.item.siteURI);
}
};
/**
* Transaction for editting a live bookmark's feed URI.
*
* @param aLivemarkId
* id of the livemark
* @param aFeedURI
* new feed uri
*
* @return nsITransaction object
*/
function PlacesEditLivemarkFeedURITransaction(aLivemarkId, aFeedURI)
{
this.item = new TransactionItemCache();
this.item.id = aLivemarkId;
this.new = new TransactionItemCache();
this.new.feedURI = aFeedURI;
}
PlacesEditLivemarkFeedURITransaction.prototype = {
__proto__: BaseTransaction.prototype,
doTransaction: function ELFUTXN_doTransaction()
{
this.item.feedURI = PlacesUtils.livemarks.getFeedURI(this.item.id);
PlacesUtils.livemarks.setFeedURI(this.item.id, this.new.feedURI);
PlacesUtils.livemarks.reloadLivemarkFolder(this.item.id);
},
undoTransaction: function ELFUTXN_undoTransaction()
{
PlacesUtils.livemarks.setFeedURI(this.item.id, this.item.feedURI);
PlacesUtils.livemarks.reloadLivemarkFolder(this.item.id);
}
};
/**
* Transaction for editing an item's date added property.
*

View File

@ -492,17 +492,7 @@ namespace places {
nsRefPtr<mozIStorageStatement> getPageInfo = DB->GetStatement(
"SELECT typed, hidden, visit_count, "
"(SELECT count(*) FROM moz_historyvisits WHERE place_id = :page_id), "
"EXISTS ( "
"SELECT 1 FROM moz_bookmarks "
"WHERE fk = :page_id "
"AND NOT EXISTS( "
"SELECT 1 "
"FROM moz_items_annos a "
"JOIN moz_anno_attributes n ON a.anno_attribute_id = n.id "
"WHERE n.name = :anno_name "
"AND a.item_id = parent "
") "
"), "
"EXISTS (SELECT 1 FROM moz_bookmarks WHERE fk = :page_id), "
"(url > 'place:' AND url < 'place;') "
"FROM moz_places "
"WHERE id = :page_id "
@ -512,9 +502,6 @@ namespace places {
rv = getPageInfo->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
NS_ENSURE_SUCCESS(rv, rv);
rv = getPageInfo->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
NS_LITERAL_CSTRING("livemark/feedURI"));
NS_ENSURE_SUCCESS(rv, rv);
bool hasResult;
rv = getPageInfo->ExecuteStep(&hasResult);

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,6 @@
#include "nsNavHistory.h"
#include "nsAnnotationService.h"
#include "nsILivemarkService.h"
#include "nsPlacesMacros.h"
#include "Helpers.h"

View File

@ -51,7 +51,6 @@
#include "nsNavHistory.h"
#include "mozIPlacesAutoComplete.h"
#include "nsILivemarkService.h"
#include "nsNavBookmarks.h"
#include "nsAnnotationService.h"
#include "nsFaviconService.h"
@ -1198,19 +1197,11 @@ nsNavHistory::GetHasHistoryEntries(bool* aHasEntries)
nsresult
nsNavHistory::invalidateFrecencies(const nsCString& aPlaceIdsQueryString)
{
// Exclude place: queries and unvisited livemark children from autocomplete,
// by setting their frecency to zero.
// Exclude place: queries by setting their frecency to zero.
nsCAutoString invalideFrecenciesSQLFragment(
"UPDATE moz_places SET frecency = (CASE "
"WHEN url BETWEEN 'place:' AND 'place;' "
"THEN 0 "
"WHEN id IN (SELECT b.fk FROM moz_bookmarks b "
"JOIN moz_bookmarks bp ON bp.id = b.parent "
"JOIN moz_items_annos a ON a.item_id = bp.id "
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
"WHERE b.fk = moz_places.id AND visit_count = 0 "
"AND n.name = :anno_name) "
"THEN 0 "
"ELSE -1 "
"END) "
);
@ -1226,13 +1217,8 @@ nsNavHistory::invalidateFrecencies(const nsCString& aPlaceIdsQueryString)
);
NS_ENSURE_STATE(stmt);
nsresult rv = stmt->BindUTF8StringByName(
NS_LITERAL_CSTRING("anno_name"), NS_LITERAL_CSTRING(LMANNO_FEEDURI)
);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStoragePendingStatement> ps;
rv = stmt->ExecuteAsync(nsnull, getter_AddRefs(ps));
nsresult rv = stmt->ExecuteAsync(nsnull, getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;

View File

@ -106,8 +106,10 @@
#include "nsISupportsPrimitives.h"
#include "nsPlacesMacros.h"
#include "mozilla/Util.h"
#include "Helpers.h"
using namespace mozilla;
using namespace mozilla::places;
static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
@ -143,6 +145,9 @@ static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
#define RESTORE_NSIOBSERVER_DATA NS_LITERAL_STRING("html")
#define RESTORE_INITIAL_NSIOBSERVER_DATA NS_LITERAL_STRING("html-initial")
#define LMANNO_FEEDURI "livemark/feedURI"
#define LMANNO_SITEURI "livemark/siteURI"
// define to get debugging messages on console about import/export
//#define DEBUG_IMPORT
//#define DEBUG_EXPORT
@ -378,7 +383,7 @@ protected:
nsCOMPtr<nsINavBookmarksService> mBookmarksService;
nsCOMPtr<nsINavHistoryService> mHistoryService;
nsCOMPtr<nsIAnnotationService> mAnnotationService;
nsCOMPtr<nsILivemarkService> mLivemarkService;
nsCOMPtr<mozIAsyncLivemarks> mLivemarkService;
// If set, we will move root items to from their existing position
// in the hierarchy, to where we find them in the bookmarks file
@ -982,30 +987,19 @@ BookmarkContentSink::HandleLinkEnd()
if (frame.mPreviousFeed) {
// The is a live bookmark. We create it here since in HandleLinkBegin we
// don't know the title.
jsval livemark = livemarkInfoToJSVal(
0, EmptyCString(), frame.mPreviousText, frame.mContainerID,
mBookmarksService->DEFAULT_INDEX, frame.mPreviousFeed, frame.mPreviousLink
);
// Create the live bookmark.
rv = mLivemarkService->AddLivemark(livemark, nsnull);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "AddLivemark failed!");
if (mIsImportDefaults) {
// Create the live bookmark but don't update it immediately.
rv = mLivemarkService->CreateLivemarkFolderOnly(frame.mContainerID,
frame.mPreviousText,
frame.mPreviousLink,
frame.mPreviousFeed,
-1,
&frame.mPreviousId);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "CreateLivemarkFolderOnly failed!");
}
else {
rv = mLivemarkService->CreateLivemark(frame.mContainerID,
frame.mPreviousText,
frame.mPreviousLink,
frame.mPreviousFeed,
-1,
&frame.mPreviousId);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "CreateLivemark failed!");
}
#ifdef DEBUG_IMPORT
PrintNesting();
printf("Created livemark '%s' %lld\n",
NS_ConvertUTF16toUTF8(frame.mPreviousText).get(), frame.mPreviousId);
printf("Created livemark '%s'\n",
NS_ConvertUTF16toUTF8(frame.mPreviousText).get());
#endif
}
else if (frame.mPreviousLink) {
@ -1852,36 +1846,31 @@ nsPlacesImportExportService::WriteLivemark(nsINavHistoryResultNode* aFolder, con
NS_ENSURE_SUCCESS(rv, rv);
// get feed URI
nsCOMPtr<nsIURI> feedURI;
rv = mLivemarkService->GetFeedURI(folderId, getter_AddRefs(feedURI));
nsString feedSpec;
rv = mAnnotationService->GetItemAnnotationString(folderId,
NS_LITERAL_CSTRING(LMANNO_FEEDURI),
feedSpec);
NS_ENSURE_SUCCESS(rv, rv);
if (feedURI) {
nsCString feedSpec;
rv = feedURI->GetSpec(feedSpec);
NS_ENSURE_SUCCESS(rv, rv);
// write feed URI
rv = aOutput->Write(kFeedURIAttribute, sizeof(kFeedURIAttribute)-1, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
rv = WriteEscapedUrl(feedSpec, aOutput);
NS_ENSURE_SUCCESS(rv, rv);
rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
}
// write feed URI
rv = aOutput->Write(kFeedURIAttribute, sizeof(kFeedURIAttribute)-1, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
rv = WriteEscapedUrl(NS_ConvertUTF16toUTF8(feedSpec), aOutput);
NS_ENSURE_SUCCESS(rv, rv);
rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
// get the optional site URI
nsCOMPtr<nsIURI> siteURI;
rv = mLivemarkService->GetSiteURI(folderId, getter_AddRefs(siteURI));
NS_ENSURE_SUCCESS(rv, rv);
if (siteURI) {
nsCString siteSpec;
rv = siteURI->GetSpec(siteSpec);
NS_ENSURE_SUCCESS(rv, rv);
nsString siteSpec;
rv = mAnnotationService->GetItemAnnotationString(folderId,
NS_LITERAL_CSTRING(LMANNO_SITEURI),
siteSpec);
if (NS_SUCCEEDED(rv) && !siteSpec.IsEmpty()) {
// write site URI
rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
rv = WriteEscapedUrl(siteSpec, aOutput);
rv = WriteEscapedUrl(NS_ConvertUTF16toUTF8(siteSpec), aOutput);
NS_ENSURE_SUCCESS(rv, rv);
rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
@ -2023,9 +2012,13 @@ nsPlacesImportExportService::WriteContainerContents(nsINavHistoryResultNode* aFo
rv = child->GetItemId(&childFolderId);
NS_ENSURE_SUCCESS(rv, rv);
// it could be a regular folder or it could be a livemark
// it could be a regular folder or it could be a livemark.
// Livemarks service is async, for now just workaround using annotations
// service.
bool isLivemark;
rv = mLivemarkService->IsLivemark(childFolderId, &isLivemark);
nsresult rv = mAnnotationService->ItemHasAnnotation(childFolderId,
NS_LITERAL_CSTRING(LMANNO_FEEDURI),
&isLivemark);
NS_ENSURE_SUCCESS(rv, rv);
if (isLivemark)

View File

@ -8,7 +8,7 @@
#include "nsIOutputStream.h"
#include "nsIFaviconService.h"
#include "nsIAnnotationService.h"
#include "nsILivemarkService.h"
#include "mozIAsyncLivemarks.h"
#include "nsINavHistoryService.h"
#include "nsINavBookmarksService.h"
#include "nsIChannel.h"
@ -41,7 +41,7 @@ class nsPlacesImportExportService : public nsIPlacesImportExportService,
nsCOMPtr<nsIAnnotationService> mAnnotationService;
nsCOMPtr<nsINavBookmarksService> mBookmarksService;
nsCOMPtr<nsINavHistoryService> mHistoryService;
nsCOMPtr<nsILivemarkService> mLivemarkService;
nsCOMPtr<mozIAsyncLivemarks> mLivemarkService;
nsCOMPtr<nsIChannel> mImportChannel;
bool mIsImportDefaults;

View File

@ -723,6 +723,53 @@ function do_check_guid_for_uri(aURI,
}
}
/**
* Retrieves the guid for a given bookmark.
*
* @param aId
* The bookmark id to check.
* @param [optional] aStack
* The stack frame used to report the error.
* @return the associated the guid.
*/
function do_get_guid_for_bookmark(aId,
aStack)
{
if (!aStack) {
aStack = Components.stack.caller;
}
let stmt = DBConn().createStatement(
"SELECT guid "
+ "FROM moz_bookmarks "
+ "WHERE id = :item_id "
);
stmt.params.item_id = aId;
do_check_true(stmt.executeStep(), aStack);
let guid = stmt.row.guid;
stmt.finalize();
do_check_valid_places_guid(guid, aStack);
return guid;
}
/**
* Tests that a guid was set in moz_places for a given bookmark.
*
* @param aId
* The bookmark id to check.
* @param [optional] aGUID
* The expected guid in the database.
*/
function do_check_guid_for_bookmark(aId,
aGUID)
{
let caller = Components.stack.caller;
let guid = do_get_guid_for_bookmark(aId, caller);
if (aGUID) {
do_check_valid_places_guid(aGUID, caller);
do_check_eq(guid, aGUID, caller);
}
}
/**
* Logs info to the console in the standard way (includes the filename).
*

View File

@ -0,0 +1,551 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests functionality of the mozIAsyncLivemarks interface.
const FEED_URI = NetUtil.newURI("http://feed.rss/");
const SITE_URI = NetUtil.newURI("http://site.org/");
function run_test()
{
run_next_test();
}
add_test(function test_addLivemark_noArguments_throws()
{
try {
PlacesUtils.livemarks.addLivemark();
do_throw("Invoking addLivemark with no arguments should throw");
} catch (ex) {
// The error is actually generated by XPConnect.
do_check_eq(ex.result, Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS);
}
run_next_test();
});
add_test(function test_addLivemark_emptyObject_throws()
{
try {
PlacesUtils.livemarks.addLivemark({});
do_throw("Invoking addLivemark with empty object should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_badParentId_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: "test" });
do_throw("Invoking addLivemark with a bad parent id should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_invalidParentId_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: -2 });
do_throw("Invoking addLivemark with an invalid parent id should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_noIndex_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId });
do_throw("Invoking addLivemark with no index should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_badIndex_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
, index: "test"
});
do_throw("Invoking addLivemark with a bad index should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_invalidIndex_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
, index: -2
});
do_throw("Invoking addLivemark with an invalid index should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_noFeedURI_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
});
do_throw("Invoking addLivemark with no feedURI should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_badFeedURI_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: "test"
});
do_throw("Invoking addLivemark with a bad feedURI should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_badSiteURI_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
, siteURI: "test"
});
do_throw("Invoking addLivemark with a bad siteURI should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_badGuid_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
, guid: "123456"
});
do_throw("Invoking addLivemark with a bad guid should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_addLivemark_badCallback_throws()
{
try {
PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
}, "test");
do_throw("Invoking addLivemark with a bad callback should throw");
} catch (ex) {
// The error is actually generated by XPConnect.
do_check_eq(ex.result, Cr.NS_ERROR_XPC_BAD_CONVERT_JS);
}
run_next_test();
});
add_test(function test_addLivemark_noCallback_succeeds()
{
PlacesUtils.bookmarks.addObserver({
onItemAdded: function onItemAdded(aItemId, aParentId, aIndex, aItemType,
aURI, aTitle)
{
PlacesUtils.bookmarks.removeObserver(this);
do_check_eq(aParentId, PlacesUtils.unfiledBookmarksFolderId);
do_check_eq(aIndex, 0);
do_check_eq(aItemType, Ci.nsINavBookmarksService.TYPE_FOLDER);
do_check_eq(aTitle, "test");
run_next_test();
},
onBeginUpdateBatch: function onBeginUpdateBatch() {},
onEndUpdateBatch: function onEndUpdateBatch() {},
onBeforeItemRemoved: function onBeforeItemRemoved() {},
onItemRemoved: function onItemRemoved() {},
onItemChanged: function onItemChanged() {},
onItemVisited: function onItemVisited() {},
onItemMoved: function onItemMoved() {},
}, false);
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
});
});
add_test(function test_addLivemark_noSiteURI_callback_succeeds()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
}, function (aStatus, aLivemark)
{
do_check_true(Components.isSuccessCode(aStatus));
do_check_true(aLivemark.id > 0);
do_check_valid_places_guid(aLivemark.guid);
do_check_eq(aLivemark.title, "test");
do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
do_check_eq(aLivemark.lastModified, PlacesUtils.bookmarks.getItemLastModified(aLivemark.id));
do_check_true(aLivemark.feedURI.equals(FEED_URI));
do_check_eq(aLivemark.siteURI, null);
run_next_test();
});
});
add_test(function test_addLivemark_callback_succeeds()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
, siteURI: SITE_URI
}, function (aStatus, aLivemark)
{
do_check_true(Components.isSuccessCode(aStatus));
do_check_true(aLivemark.id > 0);
do_check_valid_places_guid(aLivemark.guid);
do_check_eq(aLivemark.title, "test");
do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
do_check_eq(aLivemark.lastModified, PlacesUtils.bookmarks.getItemLastModified(aLivemark.id));
do_check_true(aLivemark.feedURI.equals(FEED_URI));
do_check_true(aLivemark.siteURI.equals(SITE_URI));
do_check_true(PlacesUtils.annotations
.itemHasAnnotation(aLivemark.id,
PlacesUtils.LMANNO_FEEDURI));
do_check_true(PlacesUtils.annotations
.itemHasAnnotation(aLivemark.id,
PlacesUtils.LMANNO_SITEURI));
run_next_test();
});
});
add_test(function test_addLivemark_bogusParent_callback_fails()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: 187
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
}, function (aStatus, aLivemark)
{
do_check_false(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark, null);
run_next_test();
});
});
add_test(function test_addLivemark_intoLivemark_callback_fails()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
}, function (aStatus, aLivemark)
{
do_check_true(Components.isSuccessCode(aStatus));
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: aLivemark.id
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
}, function (aStatus, aLivemark)
{
do_check_false(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark, null);
run_next_test();
});
});
});
add_test(function test_addLivemark_forceGuid_callback_succeeds()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
, guid: "1234567890AB"
}, function (aStatus, aLivemark)
{
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.guid, "1234567890AB");
do_check_guid_for_bookmark(aLivemark.id, "1234567890AB");
run_next_test();
});
});
add_test(function test_addLivemark_lastModified_callback_succeeds()
{
let now = Date.now() * 1000;
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
, lastModified: now
}, function (aStatus, aLivemark)
{
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.lastModified, now);
run_next_test();
});
});
add_test(function test_removeLivemark_emptyObject_throws()
{
try {
PlacesUtils.livemarks.removeLivemark({});
do_throw("Invoking removeLivemark with empty object should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_removeLivemark_noValidId_throws()
{
try {
PlacesUtils.livemarks.removeLivemark({ id: -10, guid: "test"});
do_throw("Invoking removeLivemark with no valid id should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_removeLivemark_nonExistent_fails()
{
PlacesUtils.livemarks.removeLivemark(
{ id: 1337 },
function (aStatus, aLivemark) {
do_check_false(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark, null);
run_next_test();
}
);
});
add_test(function test_removeLivemark_guid_succeeds()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
, guid: "234567890ABC"
}, function (aStatus, aLivemark)
{
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.guid, "234567890ABC");
// invalid id to check the guid wins.
PlacesUtils.livemarks.removeLivemark(
{ id: 789, guid: "234567890ABC" },
function (aStatus, aRemovedLivemark) {
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(PlacesUtils.bookmarks.getItemIndex(aLivemark.id), -1);
do_check_eq(aRemovedLivemark, null);
run_next_test();
}
);
});
});
add_test(function test_removeLivemark_id_succeeds()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
}, function (aStatus, aLivemark)
{
do_check_true(Components.isSuccessCode(aStatus));
PlacesUtils.livemarks.removeLivemark(
{ id: aLivemark.id },
function (aStatus, aRemovedLivemark) {
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(PlacesUtils.bookmarks.getItemIndex(aLivemark.id), -1);
do_check_eq(aRemovedLivemark, null);
run_next_test();
}
);
});
});
add_test(function test_getLivemark_emptyObject_throws()
{
try {
PlacesUtils.livemarks.getLivemark({}, function () {});
do_throw("Invoking getLivemark with empty object should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_getLivemark_noValidId_throws()
{
try {
PlacesUtils.livemarks.getLivemark({ id: -10, guid: "test"}, function () {});
do_throw("Invoking getLivemark with no valid id should throw");
} catch (ex) {
do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
});
add_test(function test_getLivemark_nonExistentId_fails()
{
PlacesUtils.livemarks.getLivemark({ id: 1234 },
function (aStatus, aLivemark){
do_check_false(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark, null);
run_next_test();
}
);
});
add_test(function test_getLivemark_nonExistentGUID_fails()
{
PlacesUtils.livemarks.getLivemark({ guid: "34567890ABCD" },
function (aStatus, aLivemark){
do_check_false(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark, null);
run_next_test();
}
);
});
add_test(function test_getLivemark_guid_succeeds()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
, guid: "34567890ABCD"
}, function (aStatus, aLivemark)
{
do_check_true(Components.isSuccessCode(aStatus));
// invalid id to check the guid wins.
PlacesUtils.livemarks.getLivemark({ id: 789, guid: "34567890ABCD" },
function(aStatus, aLivemark) {
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.title, "test");
do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
do_check_true(aLivemark.feedURI.equals(FEED_URI));
do_check_eq(aLivemark.siteURI, null);
do_check_eq(aLivemark.guid, "34567890ABCD");
run_next_test();
}
);
});
});
add_test(function test_getLivemark_id_succeeds()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
}, function (aStatus, aLivemark)
{
do_check_true(Components.isSuccessCode(aStatus));
// invalid id to check the guid wins.
PlacesUtils.livemarks.getLivemark({ id: aLivemark.id },
function(aStatus, aLivemark) {
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.title, "test");
do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
do_check_true(aLivemark.feedURI.equals(FEED_URI));
do_check_eq(aLivemark.siteURI, null);
do_check_guid_for_bookmark(aLivemark.id, aLivemark.guid);
run_next_test();
}
);
});
});
add_test(function test_getLivemark_removeItem_contention()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
});
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
});
let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX);
PlacesUtils.livemarks.getLivemark(
{ id: id },
function(aStatus, aLivemark) {
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.title, "test");
do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
do_check_true(aLivemark.feedURI.equals(FEED_URI));
do_check_eq(aLivemark.siteURI, null);
do_check_guid_for_bookmark(aLivemark.id, aLivemark.guid);
run_next_test();
}
);
});
add_test(function test_title_change()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
}, function(aStatus, aLivemark) {
PlacesUtils.bookmarks.setItemTitle(aLivemark.id, "test2");
do_check_eq(aLivemark.title, "test2");
run_next_test();
});
});
add_test(function test_livemark_move()
{
PlacesUtils.livemarks.addLivemark({ title: "test"
, parentId: PlacesUtils.unfiledBookmarksFolderId
, index: PlacesUtils.bookmarks.DEFAULT_INDEX
, feedURI: FEED_URI
}, function(aStatus, aLivemark) {
PlacesUtils.bookmarks.moveItem(aLivemark.id,
PlacesUtils.toolbarFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX);
do_check_eq(aLivemark.parentId, PlacesUtils.toolbarFolderId);
do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
run_next_test();
});
});

View File

@ -95,6 +95,7 @@ skip-if = os == "android"
[test_lastModified.js]
[test_livemarkService_getLivemarkIdForFeedURI.js]
[test_markpageas.js]
[test_mozIAsyncLivemarks.js]
[test_moz-anno_favicon_mime_type.js]
[test_multi_queries.js]
# Bug 676989: test fails consistently on Android