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

r=rnewman
This commit is contained in:
Marco Bonardo 2012-02-24 13:42:32 +01:00
Родитель 5753121d84
Коммит 5b017180f8
3 изменённых файлов: 101 добавлений и 102 удалений

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

@ -47,23 +47,6 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const ALLBOOKMARKS_ANNO = "AllBookmarks";
const DESCRIPTION_ANNO = "bookmarkProperties/description";
const SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
const FEEDURI_ANNO = "livemark/feedURI";
const SITEURI_ANNO = "livemark/siteURI";
const MOBILEROOT_ANNO = "mobile/bookmarksRoot";
const MOBILE_ANNO = "MobileBookmarks";
const EXCLUDEBACKUP_ANNO = "places/excludeFromBackup";
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
const PARENT_ANNO = "sync/parent";
const ORGANIZERQUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
const ANNOS_TO_TRACK = [DESCRIPTION_ANNO, SIDEBAR_ANNO,
FEEDURI_ANNO, SITEURI_ANNO];
const SERVICE_NOT_SUPPORTED = "Service not supported on this platform";
const FOLDER_SORTINDEX = 1000000;
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-sync/engines.js");
@ -74,6 +57,21 @@ Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/main.js"); // For access to Service.
const ALLBOOKMARKS_ANNO = "AllBookmarks";
const DESCRIPTION_ANNO = "bookmarkProperties/description";
const SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
const MOBILEROOT_ANNO = "mobile/bookmarksRoot";
const MOBILE_ANNO = "MobileBookmarks";
const EXCLUDEBACKUP_ANNO = "places/excludeFromBackup";
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
const PARENT_ANNO = "sync/parent";
const ORGANIZERQUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
const ANNOS_TO_TRACK = [DESCRIPTION_ANNO, SIDEBAR_ANNO,
PlacesUtils.LMANNO_FEEDURI, PlacesUtils.LMANNO_SITEURI];
const SERVICE_NOT_SUPPORTED = "Service not supported on this platform";
const FOLDER_SORTINDEX = 1000000;
function PlacesItem(collection, id, type) {
CryptoWrapper.call(this, collection, id);
this.type = type || "item";
@ -633,14 +631,17 @@ BookmarksStore.prototype = {
switch (type) {
case bms.TYPE_FOLDER:
if (PlacesUtils.itemIsLivemark(itemId))
if (PlacesUtils.annotations
.itemHasAnnotation(itemId, PlacesUtils.LMANNO_FEEDURI)) {
return "livemark";
}
return "folder";
case bms.TYPE_BOOKMARK:
let bmkUri = bms.getBookmarkURI(itemId).spec;
if (bmkUri.search(/^place:/) == 0)
if (bmkUri.indexOf("place:") == 0) {
return "query";
}
return "bookmark";
case bms.TYPE_SEPARATOR:
@ -718,7 +719,8 @@ BookmarksStore.prototype = {
this._log.debug("No feed URI: skipping livemark record " + record.id);
return;
}
if (PlacesUtils.itemIsLivemark(record._parent)) {
if (PlacesUtils.annotations
.itemHasAnnotation(record._parent, PlacesUtils.LMANNO_FEEDURI)) {
this._log.debug("Invalid parent: skipping livemark record " + record.id);
return;
}
@ -726,14 +728,31 @@ BookmarksStore.prototype = {
if (record.siteUri != null)
siteURI = Utils.makeURI(record.siteUri);
// Use createLivemarkFolderOnly, not createLivemark, to avoid it
// automatically updating during a sync.
newId = PlacesUtils.livemarks.createLivemarkFolderOnly(
record._parent, record.title, siteURI, Utils.makeURI(record.feedUri),
PlacesUtils.bookmarks.DEFAULT_INDEX);
this._log.debug("Created livemark " + newId + " under " + record._parent +
" as " + record.title + ", " + record.siteUri + ", " +
record.feedUri + ", GUID " + record.id);
// Until this engine can handle asynchronous error reporting, we need to
// detect errors on creation synchronously.
let spinningCb = Async.makeSpinningCallback();
let livemarkObj = {title: record.title,
parentId: record._parent,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
feedURI: Utils.makeURI(record.feedUri),
siteURI: siteURI,
guid: record.id};
PlacesUtils.livemarks.addLivemark(livemarkObj,
function (aStatus, aLivemark) {
spinningCb(null, [aStatus, aLivemark]);
});
let [status, livemark] = spinningCb.wait();
if (!Components.isSuccessCode(status)) {
throw status;
}
this._log.debug("Created livemark " + livemark.id + " under " +
livemark.parentId + " as " + livemark.title +
", " + livemark.siteURI.spec + ", " +
livemark.feedURI.spec + ", GUID " +
livemark.guid);
break;
case "separator":
newId = PlacesUtils.bookmarks.insertSeparator(
@ -748,8 +767,12 @@ BookmarksStore.prototype = {
return;
}
this._log.trace("Setting GUID of new item " + newId + " to " + record.id);
this._setGUID(newId, record.id);
if (newId) {
// Livemarks can set the GUID through the API, so there's no need to
// do that here.
this._log.trace("Setting GUID of new item " + newId + " to " + record.id);
this._setGUID(newId, record.id);
}
},
// Factored out of `remove` to avoid redundant DB queries when the Places ID
@ -859,12 +882,6 @@ BookmarksStore.prototype = {
itemId, SMART_BOOKMARKS_ANNO, val, 0,
PlacesUtils.annotations.EXPIRE_NEVER);
break;
case "siteUri":
PlacesUtils.livemarks.setSiteURI(itemId, Utils.makeURI(val));
break;
case "feedUri":
PlacesUtils.livemarks.setFeedURI(itemId, Utils.makeURI(val));
break;
}
}
},
@ -973,7 +990,7 @@ BookmarksStore.prototype = {
switch (PlacesUtils.bookmarks.getItemType(placeId)) {
case PlacesUtils.bookmarks.TYPE_BOOKMARK:
let bmkUri = PlacesUtils.bookmarks.getBookmarkURI(placeId).spec;
if (bmkUri.search(/^place:/) == 0) {
if (bmkUri.indexOf("place:") == 0) {
record = new BookmarkQuery(collection, id);
// Get the actual tag name instead of the local itemId
@ -1013,14 +1030,14 @@ BookmarksStore.prototype = {
break;
case PlacesUtils.bookmarks.TYPE_FOLDER:
if (PlacesUtils.itemIsLivemark(placeId)) {
if (PlacesUtils.annotations
.itemHasAnnotation(placeId, PlacesUtils.LMANNO_FEEDURI)) {
record = new Livemark(collection, id);
let siteURI = PlacesUtils.livemarks.getSiteURI(placeId);
if (siteURI != null)
record.siteUri = siteURI.spec;
record.feedUri = PlacesUtils.livemarks.getFeedURI(placeId).spec;
let as = PlacesUtils.annotations;
record.feedUri = as.getItemAnnotation(placeId, PlacesUtils.LMANNO_FEEDURI);
try {
record.siteUri = as.getItemAnnotation(placeId, PlacesUtils.LMANNO_SITEURI);
} catch (ex) {}
} else {
record = new BookmarkFolder(collection, id);
}
@ -1181,8 +1198,7 @@ BookmarksStore.prototype = {
node = this._getNode(nodeID);
}
if (node.type == node.RESULT_TYPE_FOLDER &&
!PlacesUtils.itemIsLivemark(node.itemId)) {
if (node.type == node.RESULT_TYPE_FOLDER) {
node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
node.containerOpen = true;
try {
@ -1312,8 +1328,7 @@ BookmarksTracker.prototype = {
},
/**
* Determine if a change should be ignored: we're ignoring everything or the
* folder is for livemarks.
* Determine if a change should be ignored.
*
* @param itemId
* Item under consideration to ignore
@ -1339,10 +1354,6 @@ BookmarksTracker.prototype = {
}
}
// Ignore livemark children.
if (PlacesUtils.itemIsLivemark(folder))
return true;
// Ignore changes to tags (folders under the tags folder).
let tags = kSpecialIds.tags;
if (folder == tags)
@ -1423,8 +1434,7 @@ BookmarksTracker.prototype = {
// This method is oddly structured, but the idea is to return as quickly as
// possible -- this handler gets called *every time* a bookmark changes, for
// *each change*. That's particularly bad when a bunch of livemarks are
// updated.
// *each change*.
onItemChanged: function BMT_onItemChanged(itemId, property, isAnno, value,
lastModified, itemType, parentId,
guid, parentGuid) {

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

@ -356,7 +356,8 @@ add_test(function test_mismatched_types() {
let oldID = store.idForGUID(oldR.id);
_("Old ID: " + oldID);
do_check_eq(bms.getItemType(oldID), bms.TYPE_FOLDER);
do_check_false(PlacesUtils.livemarks.isLivemark(oldID));
do_check_false(PlacesUtils.annotations
.itemHasAnnotation(oldID, PlacesUtils.LMANNO_FEEDURI));
store.applyIncoming(newR);
let newID = store.idForGUID(newR.id);
@ -364,7 +365,8 @@ add_test(function test_mismatched_types() {
_("Applied new. It's a livemark.");
do_check_eq(bms.getItemType(newID), bms.TYPE_FOLDER);
do_check_true(PlacesUtils.livemarks.isLivemark(newID));
do_check_true(PlacesUtils.annotations
.itemHasAnnotation(newID, PlacesUtils.LMANNO_FEEDURI));
} finally {
store.wipe();

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

@ -50,7 +50,7 @@ const CU = Components.utils;
CU.import("resource://tps/logger.jsm");
CU.import("resource://gre/modules/Services.jsm");
CU.import("resource://gre/modules/PlacesUtils.jsm");
CU.import("resource://services-sync/async.js");
var DumpBookmarks = function TPS_Bookmarks__DumpBookmarks() {
let writer = {
@ -801,12 +801,26 @@ Livemark.prototype = {
let siteURI = null;
if (this.props.siteUri != null)
siteURI = Services.io.newURI(this.props.siteUri, null, null);
this.props.item_id = PlacesUtils.livemarks.createLivemark(
this.props.folder_id,
this.props.livemark,
siteURI,
Services.io.newURI(this.props.feedUri, null, null),
-1);
let livemarkObj = {parentId: this.props.folder_id,
title: this.props.livemark,
siteURI: siteURI,
feedURI: Services.io.newURI(this.props.feedUri, null, null),
index: PlacesUtils.bookmarks.DEFAULT_INDEX};
// Until this can handle asynchronous creation, we need to spin.
let spinningCb = Async.makeSpinningCallback();
PlacesUtils.livemarks.addLivemark(livemarkObj,
function (aStatus, aLivemark) {
spinningCb(null, [aStatus, aLivemark]);
});
let [status, livemark] = spinningCb.wait();
if (!Components.isSuccessCode(status)) {
throw status;
}
this.props.item_id = livemark.id;
return this.props.item_id;
},
@ -828,26 +842,31 @@ Livemark.prototype = {
this.props.folder_id,
CI.nsINavHistoryResultNode.RESULT_TYPE_FOLDER,
this.props.livemark);
if (!PlacesUtils.livemarks.isLivemark(this.props.item_id)) {
if (!PlacesUtils.annotations
.itemHasAnnotation(this.props.item_id, PlacesUtils.LMANNO_FEEDURI)) {
Logger.logPotentialError("livemark folder found, but it's just a regular folder, for " +
this.toString());
this.props.item_id = -1;
return -1;
}
let feedURI = Services.io.newURI(this.props.feedUri, null, null);
let lmFeedURI = PlacesUtils.livemarks.getFeedURI(this.props.item_id);
if (feedURI.spec != lmFeedURI.spec) {
let lmFeedURISpec =
PlacesUtils.annotations.getItemAnnotation(this.props.item_id,
PlacesUtils.LMANNO_FEEDURI);
if (feedURI.spec != lmFeedURISpec) {
Logger.logPotentialError("livemark feed uri not correct, expected: " +
this.props.feedUri + ", actual: " + lmFeedURI.spec +
this.props.feedUri + ", actual: " + lmFeedURISpec +
" for " + this.toString());
return -1;
}
if (this.props.siteUri != null) {
let siteURI = Services.io.newURI(this.props.siteUri, null, null);
let lmSiteURI = PlacesUtils.livemarks.getSiteURI(this.props.item_id);
if (siteURI.spec != lmSiteURI.spec) {
let lmSiteURISpec =
PlacesUtils.annotations.getItemAnnotation(this.props.item_id,
PlacesUtils.LMANNO_SITEURI);
if (siteURI.spec != lmSiteURISpec) {
Logger.logPotentialError("livemark site uri not correct, expected: " +
this.props.siteUri + ", actual: " + lmSiteURI.spec + " for " +
this.props.siteUri + ", actual: " + lmSiteURISpec + " for " +
this.toString());
return -1;
}
@ -859,36 +878,6 @@ Livemark.prototype = {
return this.props.item_id;
},
/**
* SetSiteUri
*
* Sets the siteURI property for this livemark.
*
* @param siteUri the URI to set; if null, no changes are made
* @return nothing
*/
SetSiteUri: function(siteUri) {
if (siteUri) {
let siteURI = Services.io.newURI(siteUri, null, null);
PlacesUtils.livemarks.setSiteURI(this.props.item_id, siteURI);
}
},
/**
* SetFeedUri
*
* Sets the feedURI property for this livemark.
*
* @param feedUri the URI to set; if null, no changes are made
* @return nothing
*/
SetFeedUri: function(feedUri) {
if (feedUri) {
let feedURI = Services.io.newURI(feedUri, null, null);
PlacesUtils.livemarks.setFeedURI(this.props.item_id, feedURI);
}
},
/**
* Update
*
@ -902,8 +891,6 @@ Livemark.prototype = {
"Invalid item_id during Update");
this.SetLocation(this.updateProps.location);
this.SetPosition(this.updateProps.position);
this.SetSiteUri(this.updateProps.siteUri);
this.SetFeedUri(this.updateProps.feedUri);
this.SetTitle(this.updateProps.livemark);
return true;
},