| Index: chrome/browser/sync/glue/bookmark_change_processor.cc
|
| diff --git a/chrome/browser/sync/glue/bookmark_change_processor.cc b/chrome/browser/sync/glue/bookmark_change_processor.cc
|
| index 7d058a6c2ded8f1927c655c65b83cccbc129e536..77d2b992e29f337da174db850a31fd9b12b5c7dd 100644
|
| --- a/chrome/browser/sync/glue/bookmark_change_processor.cc
|
| +++ b/chrome/browser/sync/glue/bookmark_change_processor.cc
|
| @@ -26,6 +26,7 @@
|
| #include "sync/internal_api/public/write_node.h"
|
| #include "sync/internal_api/public/write_transaction.h"
|
| #include "sync/syncable/entry.h" // TODO(tim): Investigating bug 121587.
|
| +#include "sync/syncable/write_transaction.h"
|
| #include "ui/gfx/favicon_size.h"
|
| #include "ui/gfx/image/image_util.h"
|
|
|
| @@ -113,40 +114,49 @@ void BookmarkChangeProcessor::RemoveOneSyncNode(
|
|
|
| void BookmarkChangeProcessor::RemoveSyncNodeHierarchy(
|
| const BookmarkNode* topmost) {
|
| - syncer::WriteTransaction trans(FROM_HERE, share_handle());
|
| -
|
| - // Later logic assumes that |topmost| has been unlinked.
|
| - DCHECK(topmost->is_root());
|
| -
|
| - // A BookmarkModel deletion event means that |node| and all its children were
|
| - // deleted. Sync backend expects children to be deleted individually, so we do
|
| - // a depth-first-search here. At each step, we consider the |index|-th child
|
| - // of |node|. |index_stack| stores index values for the parent levels.
|
| - std::stack<int> index_stack;
|
| - index_stack.push(0); // For the final pop. It's never used.
|
| - const BookmarkNode* node = topmost;
|
| - int index = 0;
|
| - while (node) {
|
| - // The top of |index_stack| should always be |node|'s index.
|
| - DCHECK(node->is_root() || (node->parent()->GetIndexOf(node) ==
|
| - index_stack.top()));
|
| - if (index == node->child_count()) {
|
| - // If we've processed all of |node|'s children, delete |node| and move
|
| - // on to its successor.
|
| - RemoveOneSyncNode(&trans, node);
|
| - node = node->parent();
|
| - index = index_stack.top() + 1; // (top() + 0) was what we removed.
|
| - index_stack.pop();
|
| - } else {
|
| - // If |node| has an unprocessed child, process it next after pushing the
|
| - // current state onto the stack.
|
| - DCHECK_LT(index, node->child_count());
|
| - index_stack.push(index);
|
| - node = node->GetChild(index);
|
| - index = 0;
|
| + int64 new_version =
|
| + syncer::syncable::kInvalidTransactionVersion;
|
| + {
|
| + syncer::WriteTransaction trans(FROM_HERE, share_handle(), &new_version);
|
| +
|
| + // Later logic assumes that |topmost| has been unlinked.
|
| + DCHECK(topmost->is_root());
|
| +
|
| + // A BookmarkModel deletion event means that |node| and all its children
|
| + // were deleted. Sync backend expects children to be deleted individually,
|
| + // so we do a depth-first-search here. At each step, we consider the
|
| + // |index|-th child of |node|. |index_stack| stores index values for the
|
| + // parent levels.
|
| + std::stack<int> index_stack;
|
| + index_stack.push(0); // For the final pop. It's never used.
|
| + const BookmarkNode* node = topmost;
|
| + int index = 0;
|
| + while (node) {
|
| + // The top of |index_stack| should always be |node|'s index.
|
| + DCHECK(node->is_root() || (node->parent()->GetIndexOf(node) ==
|
| + index_stack.top()));
|
| + if (index == node->child_count()) {
|
| + // If we've processed all of |node|'s children, delete |node| and move
|
| + // on to its successor.
|
| + RemoveOneSyncNode(&trans, node);
|
| + node = node->parent();
|
| + index = index_stack.top() + 1; // (top() + 0) was what we removed.
|
| + index_stack.pop();
|
| + } else {
|
| + // If |node| has an unprocessed child, process it next after pushing the
|
| + // current state onto the stack.
|
| + DCHECK_LT(index, node->child_count());
|
| + index_stack.push(index);
|
| + node = node->GetChild(index);
|
| + index = 0;
|
| + }
|
| }
|
| + DCHECK(index_stack.empty()); // Nothing should be left on the stack.
|
| }
|
| - DCHECK(index_stack.empty()); // Nothing should be left on the stack.
|
| +
|
| + // Don't need to update versions of deleted nodes.
|
| + UpdateTransactionVersion(new_version, bookmark_model_,
|
| + std::vector<const BookmarkNode*>());
|
| }
|
|
|
| void BookmarkChangeProcessor::Loaded(BookmarkModel* model,
|
| @@ -165,11 +175,24 @@ void BookmarkChangeProcessor::BookmarkNodeAdded(BookmarkModel* model,
|
| int index) {
|
| DCHECK(share_handle());
|
|
|
| - // Acquire a scoped write lock via a transaction.
|
| - syncer::WriteTransaction trans(FROM_HERE, share_handle());
|
| + int64 new_version = syncer::syncable::kInvalidTransactionVersion;
|
| + int64 sync_id = syncer::kInvalidId;
|
| + {
|
| + // Acquire a scoped write lock via a transaction.
|
| + syncer::WriteTransaction trans(FROM_HERE, share_handle(), &new_version);
|
| + sync_id = CreateSyncNode(parent, model, index, &trans,
|
| + model_associator_, error_handler());
|
| + }
|
|
|
| - CreateSyncNode(parent, model, index, &trans, model_associator_,
|
| - error_handler());
|
| + if (syncer::kInvalidId != sync_id) {
|
| + // Siblings of added node in sync DB will also be updated to reflect new
|
| + // PREV_ID/NEXT_ID and thus get a new version. But we only update version
|
| + // of added node here. After switching to ordinals for positioning,
|
| + // PREV_ID/NEXT_ID will be deprecated and siblings will not be updated.
|
| + UpdateTransactionVersion(
|
| + new_version, model,
|
| + std::vector<const BookmarkNode*>(1, parent->GetChild(index)));
|
| + }
|
| }
|
|
|
| // static
|
| @@ -199,11 +222,10 @@ int64 BookmarkChangeProcessor::CreateSyncNode(const BookmarkNode* parent,
|
| return sync_child.GetId();
|
| }
|
|
|
| -
|
| void BookmarkChangeProcessor::BookmarkNodeRemoved(BookmarkModel* model,
|
| - const BookmarkNode* parent,
|
| - int index,
|
| - const BookmarkNode* node) {
|
| + const BookmarkNode* parent,
|
| + int index,
|
| + const BookmarkNode* node) {
|
| RemoveSyncNodeHierarchy(node);
|
| }
|
|
|
| @@ -215,77 +237,82 @@ void BookmarkChangeProcessor::BookmarkNodeChanged(BookmarkModel* model,
|
| return;
|
| }
|
|
|
| - // Acquire a scoped write lock via a transaction.
|
| - syncer::WriteTransaction trans(FROM_HERE, share_handle());
|
| -
|
| - // Lookup the sync node that's associated with |node|.
|
| - syncer::WriteNode sync_node(&trans);
|
| - if (!model_associator_->InitSyncNodeFromChromeId(node->id(), &sync_node)) {
|
| - // TODO(tim): Investigating bug 121587.
|
| - if (model_associator_->GetSyncIdFromChromeId(node->id()) ==
|
| - syncer::kInvalidId) {
|
| - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - "Bookmark id not found in model associator on BookmarkNodeChanged");
|
| - LOG(ERROR) << "Bad id.";
|
| - } else if (!sync_node.GetEntry()->good()) {
|
| - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - "Could not InitByIdLookup on BookmarkNodeChanged, good() failed");
|
| - LOG(ERROR) << "Bad entry.";
|
| - } else if (sync_node.GetEntry()->Get(syncer::syncable::IS_DEL)) {
|
| - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - "Could not InitByIdLookup on BookmarkNodeChanged, is_del true");
|
| - LOG(ERROR) << "Deleted entry.";
|
| - } else {
|
| - syncer::Cryptographer* crypto = trans.GetCryptographer();
|
| - syncer::ModelTypeSet encrypted_types(trans.GetEncryptedTypes());
|
| - const sync_pb::EntitySpecifics& specifics =
|
| - sync_node.GetEntry()->Get(syncer::syncable::SPECIFICS);
|
| - CHECK(specifics.has_encrypted());
|
| - const bool can_decrypt = crypto->CanDecrypt(specifics.encrypted());
|
| - const bool agreement = encrypted_types.Has(syncer::BOOKMARKS);
|
| - if (!agreement && !can_decrypt) {
|
| + int64 new_version = syncer::syncable::kInvalidTransactionVersion;
|
| + {
|
| + // Acquire a scoped write lock via a transaction.
|
| + syncer::WriteTransaction trans(FROM_HERE, share_handle(), &new_version);
|
| +
|
| + // Lookup the sync node that's associated with |node|.
|
| + syncer::WriteNode sync_node(&trans);
|
| + if (!model_associator_->InitSyncNodeFromChromeId(node->id(), &sync_node)) {
|
| + // TODO(tim): Investigating bug 121587.
|
| + if (model_associator_->GetSyncIdFromChromeId(node->id()) ==
|
| + syncer::kInvalidId) {
|
| error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - "Could not InitByIdLookup on BookmarkNodeChanged, "
|
| - " Cryptographer thinks bookmarks not encrypted, and CanDecrypt"
|
| - " failed.");
|
| - LOG(ERROR) << "Case 1.";
|
| - } else if (agreement && can_decrypt) {
|
| + "Bookmark id not found in model associator on BookmarkNodeChanged");
|
| + LOG(ERROR) << "Bad id.";
|
| + } else if (!sync_node.GetEntry()->good()) {
|
| error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - "Could not InitByIdLookup on BookmarkNodeChanged, "
|
| - " Cryptographer thinks bookmarks are encrypted, and CanDecrypt"
|
| - " succeeded (?!), but DecryptIfNecessary failed.");
|
| - LOG(ERROR) << "Case 2.";
|
| - } else if (agreement) {
|
| + "Could not InitByIdLookup on BookmarkNodeChanged, good() failed");
|
| + LOG(ERROR) << "Bad entry.";
|
| + } else if (sync_node.GetEntry()->Get(syncer::syncable::IS_DEL)) {
|
| error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - "Could not InitByIdLookup on BookmarkNodeChanged, "
|
| - " Cryptographer thinks bookmarks are encrypted, but CanDecrypt"
|
| - " failed.");
|
| - LOG(ERROR) << "Case 3.";
|
| + "Could not InitByIdLookup on BookmarkNodeChanged, is_del true");
|
| + LOG(ERROR) << "Deleted entry.";
|
| } else {
|
| - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - "Could not InitByIdLookup on BookmarkNodeChanged, "
|
| - " Cryptographer thinks bookmarks not encrypted, but CanDecrypt"
|
| - " succeeded (super weird, btw)");
|
| - LOG(ERROR) << "Case 4.";
|
| + syncer::Cryptographer* crypto = trans.GetCryptographer();
|
| + syncer::ModelTypeSet encrypted_types(trans.GetEncryptedTypes());
|
| + const sync_pb::EntitySpecifics& specifics =
|
| + sync_node.GetEntry()->Get(syncer::syncable::SPECIFICS);
|
| + CHECK(specifics.has_encrypted());
|
| + const bool can_decrypt = crypto->CanDecrypt(specifics.encrypted());
|
| + const bool agreement = encrypted_types.Has(syncer::BOOKMARKS);
|
| + if (!agreement && !can_decrypt) {
|
| + error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| + "Could not InitByIdLookup on BookmarkNodeChanged, "
|
| + " Cryptographer thinks bookmarks not encrypted, and CanDecrypt"
|
| + " failed.");
|
| + LOG(ERROR) << "Case 1.";
|
| + } else if (agreement && can_decrypt) {
|
| + error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| + "Could not InitByIdLookup on BookmarkNodeChanged, "
|
| + " Cryptographer thinks bookmarks are encrypted, and CanDecrypt"
|
| + " succeeded (?!), but DecryptIfNecessary failed.");
|
| + LOG(ERROR) << "Case 2.";
|
| + } else if (agreement) {
|
| + error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| + "Could not InitByIdLookup on BookmarkNodeChanged, "
|
| + " Cryptographer thinks bookmarks are encrypted, but CanDecrypt"
|
| + " failed.");
|
| + LOG(ERROR) << "Case 3.";
|
| + } else {
|
| + error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| + "Could not InitByIdLookup on BookmarkNodeChanged, "
|
| + " Cryptographer thinks bookmarks not encrypted, but CanDecrypt"
|
| + " succeeded (super weird, btw)");
|
| + LOG(ERROR) << "Case 4.";
|
| + }
|
| }
|
| + return;
|
| }
|
| - return;
|
| +
|
| + UpdateSyncNodeProperties(node, model, &sync_node);
|
| +
|
| + DCHECK_EQ(sync_node.GetIsFolder(), node->is_folder());
|
| + DCHECK_EQ(model_associator_->GetChromeNodeFromSyncId(
|
| + sync_node.GetParentId()),
|
| + node->parent());
|
| + // This node's index should be one more than the predecessor's index.
|
| + DCHECK_EQ(node->parent()->GetIndexOf(node),
|
| + CalculateBookmarkModelInsertionIndex(node->parent(),
|
| + &sync_node,
|
| + model_associator_));
|
| }
|
|
|
| - UpdateSyncNodeProperties(node, model, &sync_node);
|
| -
|
| - DCHECK_EQ(sync_node.GetIsFolder(), node->is_folder());
|
| - DCHECK_EQ(model_associator_->GetChromeNodeFromSyncId(
|
| - sync_node.GetParentId()),
|
| - node->parent());
|
| - // This node's index should be one more than the predecessor's index.
|
| - DCHECK_EQ(node->parent()->GetIndexOf(node),
|
| - CalculateBookmarkModelInsertionIndex(node->parent(),
|
| - &sync_node,
|
| - model_associator_));
|
| + UpdateTransactionVersion(new_version, model,
|
| + std::vector<const BookmarkNode*>(1, node));
|
| }
|
|
|
| -
|
| void BookmarkChangeProcessor::BookmarkNodeMoved(BookmarkModel* model,
|
| const BookmarkNode* old_parent, int old_index,
|
| const BookmarkNode* new_parent, int new_index) {
|
| @@ -296,23 +323,29 @@ void BookmarkChangeProcessor::BookmarkNodeMoved(BookmarkModel* model,
|
| return;
|
| }
|
|
|
| - // Acquire a scoped write lock via a transaction.
|
| - syncer::WriteTransaction trans(FROM_HERE, share_handle());
|
| + int64 new_version = syncer::syncable::kInvalidTransactionVersion;
|
| + {
|
| + // Acquire a scoped write lock via a transaction.
|
| + syncer::WriteTransaction trans(FROM_HERE, share_handle(), &new_version);
|
|
|
| - // Lookup the sync node that's associated with |child|.
|
| - syncer::WriteNode sync_node(&trans);
|
| - if (!model_associator_->InitSyncNodeFromChromeId(child->id(), &sync_node)) {
|
| - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - std::string());
|
| - return;
|
| - }
|
| + // Lookup the sync node that's associated with |child|.
|
| + syncer::WriteNode sync_node(&trans);
|
| + if (!model_associator_->InitSyncNodeFromChromeId(child->id(), &sync_node)) {
|
| + error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| + std::string());
|
| + return;
|
| + }
|
|
|
| - if (!PlaceSyncNode(MOVE, new_parent, new_index, &trans, &sync_node,
|
| - model_associator_)) {
|
| - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - std::string());
|
| - return;
|
| + if (!PlaceSyncNode(MOVE, new_parent, new_index, &trans, &sync_node,
|
| + model_associator_)) {
|
| + error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| + std::string());
|
| + return;
|
| + }
|
| }
|
| +
|
| + UpdateTransactionVersion(new_version, model,
|
| + std::vector<const BookmarkNode*>(1, child));
|
| }
|
|
|
| void BookmarkChangeProcessor::BookmarkNodeFaviconChanged(
|
| @@ -323,30 +356,39 @@ void BookmarkChangeProcessor::BookmarkNodeFaviconChanged(
|
|
|
| void BookmarkChangeProcessor::BookmarkNodeChildrenReordered(
|
| BookmarkModel* model, const BookmarkNode* node) {
|
| + int64 new_version = syncer::syncable::kInvalidTransactionVersion;
|
| + std::vector<const BookmarkNode*> children;
|
| + {
|
| + // Acquire a scoped write lock via a transaction.
|
| + syncer::WriteTransaction trans(FROM_HERE, share_handle(), &new_version);
|
| +
|
| + // The given node's children got reordered. We need to reorder all the
|
| + // children of the corresponding sync node.
|
| + for (int i = 0; i < node->child_count(); ++i) {
|
| + const BookmarkNode* child = node->GetChild(i);
|
| + children.push_back(child);
|
| +
|
| + syncer::WriteNode sync_child(&trans);
|
| + if (!model_associator_->InitSyncNodeFromChromeId(child->id(),
|
| + &sync_child)) {
|
| + error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| + std::string());
|
| + return;
|
| + }
|
| + DCHECK_EQ(sync_child.GetParentId(),
|
| + model_associator_->GetSyncIdFromChromeId(node->id()));
|
|
|
| - // Acquire a scoped write lock via a transaction.
|
| - syncer::WriteTransaction trans(FROM_HERE, share_handle());
|
| -
|
| - // The given node's children got reordered. We need to reorder all the
|
| - // children of the corresponding sync node.
|
| - for (int i = 0; i < node->child_count(); ++i) {
|
| - syncer::WriteNode sync_child(&trans);
|
| - if (!model_associator_->InitSyncNodeFromChromeId(node->GetChild(i)->id(),
|
| - &sync_child)) {
|
| - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - std::string());
|
| - return;
|
| - }
|
| - DCHECK_EQ(sync_child.GetParentId(),
|
| - model_associator_->GetSyncIdFromChromeId(node->id()));
|
| -
|
| - if (!PlaceSyncNode(MOVE, node, i, &trans, &sync_child,
|
| - model_associator_)) {
|
| - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| - std::string());
|
| - return;
|
| + if (!PlaceSyncNode(MOVE, node, i, &trans, &sync_child,
|
| + model_associator_)) {
|
| + error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
|
| + std::string());
|
| + return;
|
| + }
|
| }
|
| }
|
| +
|
| + // TODO(haitaol): Filter out children that didn't actually change.
|
| + UpdateTransactionVersion(new_version, model, children);
|
| }
|
|
|
| // static
|
| @@ -505,7 +547,12 @@ void BookmarkChangeProcessor::ApplyChangesFromSyncModel(
|
| return;
|
| }
|
|
|
| - if (!CreateOrUpdateBookmarkNode(&src, model, model_associator_)) {
|
| + const BookmarkNode* node = CreateOrUpdateBookmarkNode(&src, model,
|
| + model_associator_);
|
| + if (node) {
|
| + bookmark_model_->SetNodeMetaInfo(node, kBookmarkTransactionVersionKey,
|
| + base::Int64ToString(model_version));
|
| + } else {
|
| // Because the Synced Bookmarks node can be created server side, it's
|
| // possible it'll arrive at the client as an update. In that case it
|
| // won't have been associated at startup, the GetChromeNodeFromSyncId
|
| @@ -599,6 +646,21 @@ const BookmarkNode* BookmarkChangeProcessor::CreateOrUpdateBookmarkNode(
|
| }
|
|
|
| // static
|
| +void BookmarkChangeProcessor::UpdateTransactionVersion(
|
| + int64 new_version,
|
| + BookmarkModel* model,
|
| + const std::vector<const BookmarkNode*>& nodes) {
|
| + if (new_version != syncer::syncable::kInvalidTransactionVersion) {
|
| + model->SetNodeMetaInfo(model->root_node(), kBookmarkTransactionVersionKey,
|
| + base::Int64ToString(new_version));
|
| + for (size_t i = 0; i < nodes.size(); ++i) {
|
| + model->SetNodeMetaInfo(nodes[i], kBookmarkTransactionVersionKey,
|
| + base::Int64ToString(new_version));
|
| + }
|
| + }
|
| +}
|
| +
|
| +// static
|
| // Creates a bookmark node under the given parent node from the given sync
|
| // node. Returns the newly created node.
|
| const BookmarkNode* BookmarkChangeProcessor::CreateBookmarkNode(
|
|
|