diff options
author | tim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-18 22:36:05 +0000 |
---|---|---|
committer | tim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-18 22:36:05 +0000 |
commit | db1df0f4ac4c9897992e3674574d229845f94b25 (patch) | |
tree | 695cad509b25be65e50b913232ce5367fac72482 /chrome | |
parent | 5f3e6942ba82da6dfe02fe6f35d2919995bfcb81 (diff) | |
download | chromium_src-db1df0f4ac4c9897992e3674574d229845f94b25.zip chromium_src-db1df0f4ac4c9897992e3674574d229845f94b25.tar.gz chromium_src-db1df0f4ac4c9897992e3674574d229845f94b25.tar.bz2 |
Move model operations from ProfileSyncService to ChangeProcessor.
TEST=ProfileSyncServiceTest, sync integration tests
Review URL: http://codereview.chromium.org/172040
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23669 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/sync/glue/change_processor.cc | 541 | ||||
-rw-r--r-- | chrome/browser/sync/glue/change_processor.h | 188 | ||||
-rw-r--r-- | chrome/browser/sync/glue/model_associator.cc | 17 | ||||
-rw-r--r-- | chrome/browser/sync/glue/model_associator.h | 2 | ||||
-rw-r--r-- | chrome/browser/sync/glue/sync_backend_host.cc | 7 | ||||
-rw-r--r-- | chrome/browser/sync/glue/sync_backend_host.h | 88 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.cc | 553 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.h | 151 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service_unittest.cc | 27 | ||||
-rw-r--r-- | chrome/chrome.gyp | 2 | ||||
-rw-r--r-- | chrome/test/live_sync/profile_sync_service_test_harness.cc | 2 |
11 files changed, 885 insertions, 693 deletions
diff --git a/chrome/browser/sync/glue/change_processor.cc b/chrome/browser/sync/glue/change_processor.cc new file mode 100644 index 0000000..09ddb96 --- /dev/null +++ b/chrome/browser/sync/glue/change_processor.cc @@ -0,0 +1,541 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifdef CHROME_PERSONALIZATION + +#include "chrome/browser/sync/glue/change_processor.h" + +#include "base/gfx/png_encoder.h" +#include "base/string_util.h" +#include "chrome/browser/bookmarks/bookmark_utils.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace browser_sync { + +void ChangeProcessor::Start(BookmarkModel* model, sync_api::UserShare* handle) { + DCHECK(error_handler_ && model_associator_); + DCHECK(!share_handle_ && !bookmark_model_); + share_handle_ = handle; + bookmark_model_ = model; + DCHECK(model->IsLoaded()); + bookmark_model_->AddObserver(this); + running_ = true; +} + +void ChangeProcessor::Stop() { + if (!running_) + return; + DCHECK(bookmark_model_); + bookmark_model_->RemoveObserver(this); + bookmark_model_ = NULL; + share_handle_ = NULL; + model_associator_ = NULL; + running_ = false; +} + +void ChangeProcessor::UpdateSyncNodeProperties(const BookmarkNode* src, + BookmarkModel* model, + sync_api::WriteNode* dst) { + // Set the properties of the item. + dst->SetIsFolder(src->is_folder()); + dst->SetTitle(WideToUTF16(src->GetTitle()).c_str()); + // URL is passed as a C string here because this interface avoids + // string16. SetURL copies the data into its own memory. + string16 url = UTF8ToUTF16(src->GetURL().spec()); + dst->SetURL(url.c_str()); + SetSyncNodeFavicon(src, model, dst); +} + +// static +void ChangeProcessor::EncodeFavicon(const BookmarkNode* src, + BookmarkModel* model, + std::vector<unsigned char>* dst) { + const SkBitmap& favicon = model->GetFavIcon(src); + + dst->clear(); + + // Check for zero-dimension images. This can happen if the favicon is + // still being loaded. + if (favicon.empty()) + return; + + // Re-encode the BookmarkNode's favicon as a PNG, and pass the data to the + // sync subsystem. + if (!PNGEncoder::EncodeBGRASkBitmap(favicon, false, dst)) + return; +} + +void ChangeProcessor::RemoveOneSyncNode(sync_api::WriteTransaction* trans, + const BookmarkNode* node) { + sync_api::WriteNode sync_node(trans); + if (!model_associator_->InitSyncNodeFromBookmarkId(node->id(), &sync_node)) { + error_handler_->OnUnrecoverableError(); + return; + } + // This node should have no children. + DCHECK(sync_node.GetFirstChildId() == sync_api::kInvalidId); + // Remove association and delete the sync node. + model_associator_->DisassociateIds(sync_node.GetId()); + sync_node.Remove(); +} + + +void ChangeProcessor::RemoveSyncNodeHierarchy(const BookmarkNode* topmost) { + sync_api::WriteTransaction trans(share_handle_); + + // Later logic assumes that |topmost| has been unlinked. + DCHECK(!topmost->GetParent()); + + // 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->GetParent() || (node->GetParent()->IndexOfChild(node) == + index_stack.top())); + if (index == node->GetChildCount()) { + // If we've processed all of |node|'s children, delete |node| and move + // on to its successor. + RemoveOneSyncNode(&trans, node); + node = node->GetParent(); + 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->GetChildCount()); + index_stack.push(index); + node = node->GetChild(index); + index = 0; + } + } + DCHECK(index_stack.empty()); // Nothing should be left on the stack. +} + +void ChangeProcessor::BookmarkModelBeingDeleted(BookmarkModel* model) { + DCHECK(!running_) << "BookmarkModel deleted while ChangeProcessor running."; + bookmark_model_ = NULL; +} + +void ChangeProcessor::BookmarkNodeAdded(BookmarkModel* model, + const BookmarkNode* parent, + int index) { + DCHECK(running_); + DCHECK(share_handle_); + + // Acquire a scoped write lock via a transaction. + sync_api::WriteTransaction trans(share_handle_); + + CreateSyncNode(parent, model, index, &trans, model_associator_, + error_handler_); +} + +// static +int64 ChangeProcessor::CreateSyncNode(const BookmarkNode* parent, + BookmarkModel* model, int index, sync_api::WriteTransaction* trans, + ModelAssociator* associator, UnrecoverableErrorHandler* error_handler) { + const BookmarkNode* child = parent->GetChild(index); + DCHECK(child); + + // Create a WriteNode container to hold the new node. + sync_api::WriteNode sync_child(trans); + + // Actually create the node with the appropriate initial position. + if (!PlaceSyncNode(CREATE, parent, index, trans, &sync_child, associator, + error_handler)) { + LOG(WARNING) << "Sync node creation failed; recovery unlikely"; + error_handler->OnUnrecoverableError(); + return sync_api::kInvalidId; + } + + UpdateSyncNodeProperties(child, model, &sync_child); + + // Associate the ID from the sync domain with the bookmark node, so that we + // can refer back to this item later. + associator->AssociateIds(child->id(), sync_child.GetId()); + + return sync_child.GetId(); +} + + +void ChangeProcessor::BookmarkNodeRemoved(BookmarkModel* model, + const BookmarkNode* parent, + int index, + const BookmarkNode* node) { + DCHECK(running_); + RemoveSyncNodeHierarchy(node); +} + +void ChangeProcessor::BookmarkNodeChanged(BookmarkModel* model, + const BookmarkNode* node) { + DCHECK(running_); + // We shouldn't see changes to the top-level nodes. + DCHECK_NE(node, model->GetBookmarkBarNode()); + DCHECK_NE(node, model->other_node()); + + // Acquire a scoped write lock via a transaction. + sync_api::WriteTransaction trans(share_handle_); + + // Lookup the sync node that's associated with |node|. + sync_api::WriteNode sync_node(&trans); + if (!model_associator_->InitSyncNodeFromBookmarkId(node->id(), &sync_node)) { + error_handler_->OnUnrecoverableError(); + return; + } + + UpdateSyncNodeProperties(node, model, &sync_node); + + DCHECK_EQ(sync_node.GetIsFolder(), node->is_folder()); + DCHECK_EQ(model_associator_->GetBookmarkNodeFromSyncId( + sync_node.GetParentId()), + node->GetParent()); + // This node's index should be one more than the predecessor's index. + DCHECK_EQ(node->GetParent()->IndexOfChild(node), + CalculateBookmarkModelInsertionIndex(node->GetParent(), + &sync_node)); +} + + +void ChangeProcessor::BookmarkNodeMoved(BookmarkModel* model, + const BookmarkNode* old_parent, + int old_index, + const BookmarkNode* new_parent, + int new_index) { + DCHECK(running_); + const BookmarkNode* child = new_parent->GetChild(new_index); + // We shouldn't see changes to the top-level nodes. + DCHECK_NE(child, model->GetBookmarkBarNode()); + DCHECK_NE(child, model->other_node()); + + // Acquire a scoped write lock via a transaction. + sync_api::WriteTransaction trans(share_handle_); + + // Lookup the sync node that's associated with |child|. + sync_api::WriteNode sync_node(&trans); + if (!model_associator_->InitSyncNodeFromBookmarkId(child->id(), &sync_node)) { + error_handler_->OnUnrecoverableError(); + return; + } + + if (!PlaceSyncNode(MOVE, new_parent, new_index, &trans, &sync_node, + model_associator_, error_handler_)) { + error_handler_->OnUnrecoverableError(); + return; + } +} + +void ChangeProcessor::BookmarkNodeFavIconLoaded(BookmarkModel* model, + const BookmarkNode* node) { + DCHECK(running_); + BookmarkNodeChanged(model, node); +} + +void ChangeProcessor::BookmarkNodeChildrenReordered( + BookmarkModel* model, const BookmarkNode* node) { + + // Acquire a scoped write lock via a transaction. + sync_api::WriteTransaction trans(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->GetChildCount(); ++i) { + sync_api::WriteNode sync_child(&trans); + if (!model_associator_->InitSyncNodeFromBookmarkId(node->GetChild(i)->id(), + &sync_child)) { + error_handler_->OnUnrecoverableError(); + return; + } + DCHECK_EQ(sync_child.GetParentId(), + model_associator_->GetSyncIdFromBookmarkId(node->id())); + + if (!PlaceSyncNode(MOVE, node, i, &trans, &sync_child, model_associator_, + error_handler_)) { + error_handler_->OnUnrecoverableError(); + return; + } + } +} + +// static +bool ChangeProcessor::PlaceSyncNode(MoveOrCreate operation, + const BookmarkNode* parent, + int index, + sync_api::WriteTransaction* trans, + sync_api::WriteNode* dst, + ModelAssociator* associator, + UnrecoverableErrorHandler* error_handler) { + sync_api::ReadNode sync_parent(trans); + if (!associator->InitSyncNodeFromBookmarkId(parent->id(), &sync_parent)) { + LOG(WARNING) << "Parent lookup failed"; + error_handler->OnUnrecoverableError(); + return false; + } + + bool success = false; + if (index == 0) { + // Insert into first position. + success = (operation == CREATE) ? dst->InitByCreation(sync_parent, NULL) : + dst->SetPosition(sync_parent, NULL); + if (success) { + DCHECK_EQ(dst->GetParentId(), sync_parent.GetId()); + DCHECK_EQ(dst->GetId(), sync_parent.GetFirstChildId()); + DCHECK_EQ(dst->GetPredecessorId(), sync_api::kInvalidId); + } + } else { + // Find the bookmark model predecessor, and insert after it. + const BookmarkNode* prev = parent->GetChild(index - 1); + sync_api::ReadNode sync_prev(trans); + if (!associator->InitSyncNodeFromBookmarkId(prev->id(), &sync_prev)) { + LOG(WARNING) << "Predecessor lookup failed"; + return false; + } + success = (operation == CREATE) ? + dst->InitByCreation(sync_parent, &sync_prev) : + dst->SetPosition(sync_parent, &sync_prev); + if (success) { + DCHECK_EQ(dst->GetParentId(), sync_parent.GetId()); + DCHECK_EQ(dst->GetPredecessorId(), sync_prev.GetId()); + DCHECK_EQ(dst->GetId(), sync_prev.GetSuccessorId()); + } + } + return success; +} + +// Determine the bookmark model index to which a node must be moved so that +// predecessor of the node (in the bookmark model) matches the predecessor of +// |source| (in the sync model). +// As a precondition, this assumes that the predecessor of |source| has been +// updated and is already in the correct position in the bookmark model. +int ChangeProcessor::CalculateBookmarkModelInsertionIndex( + const BookmarkNode* parent, + const sync_api::BaseNode* child_info) const { + DCHECK(parent); + DCHECK(child_info); + int64 predecessor_id = child_info->GetPredecessorId(); + // A return ID of kInvalidId indicates no predecessor. + if (predecessor_id == sync_api::kInvalidId) + return 0; + + // Otherwise, insert after the predecessor bookmark node. + const BookmarkNode* predecessor = + model_associator_->GetBookmarkNodeFromSyncId(predecessor_id); + DCHECK(predecessor); + DCHECK_EQ(predecessor->GetParent(), parent); + return parent->IndexOfChild(predecessor) + 1; +} + +// ApplyModelChanges is called by the sync backend after changes have been made +// to the sync engine's model. Apply these changes to the browser bookmark +// model. +void ChangeProcessor::ApplyChangesFromSyncModel( + const sync_api::BaseTransaction* trans, + const sync_api::SyncManager::ChangeRecord* changes, + int change_count) { + if (!running_) + return; + // A note about ordering. Sync backend is responsible for ordering the change + // records in the following order: + // + // 1. Deletions, from leaves up to parents. + // 2. Existing items with synced parents & predecessors. + // 3. New items with synced parents & predecessors. + // 4. Items with parents & predecessors in the list. + // 5. Repeat #4 until all items are in the list. + // + // "Predecessor" here means the previous item within a given folder; an item + // in the first position is always said to have a synced predecessor. + // For the most part, applying these changes in the order given will yield + // the correct result. There is one exception, however: for items that are + // moved away from a folder that is being deleted, we will process the delete + // before the move. Since deletions in the bookmark model propagate from + // parent to child, we must move them to a temporary location. + BookmarkModel* model = bookmark_model_; + + // We are going to make changes to the bookmarks model, but don't want to end + // up in a feedback loop, so remove ourselves as an observer while applying + // changes. + model->RemoveObserver(this); + + // A parent to hold nodes temporarily orphaned by parent deletion. It is + // lazily created inside the loop. + const BookmarkNode* foster_parent = NULL; + for (int i = 0; i < change_count; ++i) { + const BookmarkNode* dst = + model_associator_->GetBookmarkNodeFromSyncId(changes[i].id); + // Ignore changes to the permanent top-level nodes. We only care about + // their children. + if ((dst == model->GetBookmarkBarNode()) || (dst == model->other_node())) + continue; + if (changes[i].action == + sync_api::SyncManager::ChangeRecord::ACTION_DELETE) { + // Deletions should always be at the front of the list. + DCHECK(i == 0 || changes[i-1].action == changes[i].action); + // Children of a deleted node should not be deleted; they may be + // reparented by a later change record. Move them to a temporary place. + DCHECK(dst) << "Could not find node to be deleted"; + const BookmarkNode* parent = dst->GetParent(); + if (dst->GetChildCount()) { + if (!foster_parent) { + foster_parent = model->AddGroup(model->other_node(), + model->other_node()->GetChildCount(), + std::wstring()); + } + for (int i = dst->GetChildCount() - 1; i >= 0; --i) { + model->Move(dst->GetChild(i), foster_parent, + foster_parent->GetChildCount()); + } + } + DCHECK_EQ(dst->GetChildCount(), 0) << "Node being deleted has children"; + model->Remove(parent, parent->IndexOfChild(dst)); + dst = NULL; + model_associator_->DisassociateIds(changes[i].id); + } else { + DCHECK_EQ((changes[i].action == + sync_api::SyncManager::ChangeRecord::ACTION_ADD), (dst == NULL)) + << "ACTION_ADD should be seen if and only if the node is unknown."; + + sync_api::ReadNode src(trans); + if (!src.InitByIdLookup(changes[i].id)) { + LOG(ERROR) << "ApplyModelChanges was passed a bad ID"; + error_handler_->OnUnrecoverableError(); + return; + } + + CreateOrUpdateBookmarkNode(&src, model); + } + } + // Clean up the temporary node. + if (foster_parent) { + // There should be no nodes left under the foster parent. + DCHECK_EQ(foster_parent->GetChildCount(), 0); + model->Remove(foster_parent->GetParent(), + foster_parent->GetParent()->IndexOfChild(foster_parent)); + foster_parent = NULL; + } + + // We are now ready to hear about bookmarks changes again. + model->AddObserver(this); +} + +// Create a bookmark node corresponding to |src| if one is not already +// associated with |src|. +const BookmarkNode* ChangeProcessor::CreateOrUpdateBookmarkNode( + sync_api::BaseNode* src, + BookmarkModel* model) { + const BookmarkNode* parent = + model_associator_->GetBookmarkNodeFromSyncId(src->GetParentId()); + if (!parent) { + DLOG(WARNING) << "Could not find parent of node being added/updated." + << " Node title: " << src->GetTitle() + << ", parent id = " << src->GetParentId(); + return NULL; + } + int index = CalculateBookmarkModelInsertionIndex(parent, src); + const BookmarkNode* dst = model_associator_->GetBookmarkNodeFromSyncId( + src->GetId()); + if (!dst) { + dst = CreateBookmarkNode(src, parent, model, index); + model_associator_->AssociateIds(dst->id(), src->GetId()); + } else { + // URL and is_folder are not expected to change. + // TODO(ncarter): Determine if such changes should be legal or not. + DCHECK_EQ(src->GetIsFolder(), dst->is_folder()); + + // Handle reparenting and/or repositioning. + model->Move(dst, parent, index); + + // Handle title update and URL changes due to possible conflict resolution + // that can happen if both a local user change and server change occur + // within a sufficiently small time interval. + const BookmarkNode* old_dst = dst; + dst = bookmark_utils::ApplyEditsWithNoGroupChange(model, parent, dst, + UTF16ToWide(src->GetTitle()), + src->GetIsFolder() ? GURL() : GURL(src->GetURL()), + NULL); // NULL because we don't need a BookmarkEditor::Handler. + if (dst != old_dst) { // dst was replaced with a new node with new URL. + model_associator_->DisassociateIds(src->GetId()); + model_associator_->AssociateIds(dst->id(), src->GetId()); + } + SetBookmarkFavicon(src, dst, model->profile()); + } + + return dst; +} + +// static +// Creates a bookmark node under the given parent node from the given sync +// node. Returns the newly created node. +const BookmarkNode* ChangeProcessor::CreateBookmarkNode( + sync_api::BaseNode* sync_node, + const BookmarkNode* parent, + BookmarkModel* model, + int index) { + DCHECK(parent); + DCHECK(index >= 0 && index <= parent->GetChildCount()); + + const BookmarkNode* node; + if (sync_node->GetIsFolder()) { + node = model->AddGroup(parent, index, UTF16ToWide(sync_node->GetTitle())); + } else { + GURL url(sync_node->GetURL()); + node = model->AddURL(parent, index, + UTF16ToWide(sync_node->GetTitle()), url); + SetBookmarkFavicon(sync_node, node, model->profile()); + } + return node; +} + +// static +// Sets the favicon of the given bookmark node from the given sync node. +bool ChangeProcessor::SetBookmarkFavicon( + sync_api::BaseNode* sync_node, + const BookmarkNode* bookmark_node, + Profile* profile) { + size_t icon_size = 0; + const unsigned char* icon_bytes = sync_node->GetFaviconBytes(&icon_size); + if (!icon_size || !icon_bytes) + return false; + + // Registering a favicon requires that we provide a source URL, but we + // don't know where these came from. Currently we just use the + // destination URL, which is not correct, but since the favicon URL + // is used as a key in the history's thumbnail DB, this gives us a value + // which does not collide with others. + GURL fake_icon_url = bookmark_node->GetURL(); + + std::vector<unsigned char> icon_bytes_vector(icon_bytes, + icon_bytes + icon_size); + + HistoryService* history = + profile->GetHistoryService(Profile::EXPLICIT_ACCESS); + + history->AddPage(bookmark_node->GetURL()); + history->SetFavIcon(bookmark_node->GetURL(), + fake_icon_url, + icon_bytes_vector); + + return true; +} + +// static +void ChangeProcessor::SetSyncNodeFavicon( + const BookmarkNode* bookmark_node, + BookmarkModel* model, + sync_api::WriteNode* sync_node) { + std::vector<unsigned char> favicon_bytes; + EncodeFavicon(bookmark_node, model, &favicon_bytes); + if (!favicon_bytes.empty()) + sync_node->SetFaviconBytes(&favicon_bytes[0], favicon_bytes.size()); +} + +} // namespace browser_sync + +#endif // CHROME_PERSONALIZATION diff --git a/chrome/browser/sync/glue/change_processor.h b/chrome/browser/sync/glue/change_processor.h new file mode 100644 index 0000000..e9b15d4 --- /dev/null +++ b/chrome/browser/sync/glue/change_processor.h @@ -0,0 +1,188 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifdef CHROME_PERSONALIZATION + +#ifndef CHROME_BROWSER_SYNC_GLUE_CHANGE_PROCESSOR_H_ +#define CHROME_BROWSER_SYNC_GLUE_CHANGE_PROCESSOR_H_ + +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/sync/engine/syncapi.h" +#include "chrome/browser/sync/glue/model_associator.h" +#include "chrome/browser/sync/glue/sync_backend_host.h" + +class ProfileSyncService; + +namespace browser_sync { + +class UnrecoverableErrorHandler; + +// This class is responsible for taking changes from the BookmarkModel +// and applying them to the sync_api 'syncable' model, and vice versa. +// All operations and use of this class are from the UI thread. +// This is currently bookmarks specific. +class ChangeProcessor : public BookmarkModelObserver, + public ChangeProcessingInterface { + public: + ChangeProcessor(UnrecoverableErrorHandler* error_handler) + : error_handler_(error_handler), model_associator_(NULL), + share_handle_(NULL), running_(false), bookmark_model_(NULL) {} + virtual ~ChangeProcessor() { Stop(); } + + // Call when the processor should accept changes from either provided model + // and apply them to the other. Both the BookmarkModel and sync_api are + // expected to be initialized and loaded. You must have set a valid + // ModelAssociator and UnrecoverableErrorHandler before using this method, + // and the two models should be associated w.r.t the ModelAssociator provided. + // It is safe to call Start again after previously Stop()ing the processor. + void Start(BookmarkModel* model, sync_api::UserShare* handle); + + // Call when processing changes between models is no longer safe / needed. + void Stop(); + + bool IsRunning() const { return running_; } + + // Injectors for the components required to Start the processor. + void set_model_associator(ModelAssociator* associator) { + model_associator_ = associator; + } + + // BookmarkModelObserver implementation. + // BookmarkModel -> sync_api model change application. + virtual void Loaded(BookmarkModel* model) { NOTREACHED(); } + virtual void BookmarkModelBeingDeleted(BookmarkModel* model); + virtual void BookmarkNodeMoved(BookmarkModel* model, + const BookmarkNode* old_parent, + int old_index, + const BookmarkNode* new_parent, + int new_index); + virtual void BookmarkNodeAdded(BookmarkModel* model, + const BookmarkNode* parent, + int index); + virtual void BookmarkNodeRemoved(BookmarkModel* model, + const BookmarkNode* parent, + int index, + const BookmarkNode* node); + virtual void BookmarkNodeChanged(BookmarkModel* model, + const BookmarkNode* node); + virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, + const BookmarkNode* node); + virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, + const BookmarkNode* node); + + // sync_api model -> BookmarkModel change application. + virtual void ApplyChangesFromSyncModel( + const sync_api::BaseTransaction* trans, + const sync_api::SyncManager::ChangeRecord* changes, + int change_count); + + // The following methods are static and hence may be invoked at any time, + // and do not depend on having a running ChangeProcessor. + // Creates a bookmark node under the given parent node from the given sync + // node. Returns the newly created node. + static const BookmarkNode* CreateBookmarkNode( + sync_api::BaseNode* sync_node, + const BookmarkNode* parent, + BookmarkModel* model, + int index); + + // Sets the favicon of the given bookmark node from the given sync node. + // Returns whether the favicon was set in the bookmark node. + // |profile| is the profile that contains the HistoryService and BookmarkModel + // for the bookmark in question. + static bool SetBookmarkFavicon(sync_api::BaseNode* sync_node, + const BookmarkNode* bookmark_node, + Profile* profile); + + // Sets the favicon of the given sync node from the given bookmark node. + static void SetSyncNodeFavicon(const BookmarkNode* bookmark_node, + BookmarkModel* model, + sync_api::WriteNode* sync_node); + + // Treat the |index|th child of |parent| as a newly added node, and create a + // corresponding node in the sync domain using |trans|. All properties + // will be transferred to the new node. A node corresponding to |parent| + // must already exist and be associated for this call to succeed. Returns + // the ID of the just-created node, or if creation fails, kInvalidID. + static int64 CreateSyncNode(const BookmarkNode* parent, + BookmarkModel* model, + int index, + sync_api::WriteTransaction* trans, + ModelAssociator* associator, + UnrecoverableErrorHandler* error_handler); + + private: + enum MoveOrCreate { + MOVE, + CREATE, + }; + + // Create a bookmark node corresponding to |src| if one is not already + // associated with |src|. Returns the node that was created or updated. + const BookmarkNode* CreateOrUpdateBookmarkNode( + sync_api::BaseNode* src, + BookmarkModel* model); + + // Helper function to determine the appropriate insertion index of sync node + // |node| under the Bookmark model node |parent|, to make the positions + // match up between the two models. This presumes that the predecessor of the + // item (in the bookmark model) has already been moved into its appropriate + // position. + int CalculateBookmarkModelInsertionIndex( + const BookmarkNode* parent, + const sync_api::BaseNode* node) const; + + // Helper function used to fix the position of a sync node so that it matches + // the position of a corresponding bookmark model node. |parent| and + // |index| identify the bookmark model position. |dst| is the node whose + // position is to be fixed. If |operation| is CREATE, treat |dst| as an + // uncreated node and set its position via InitByCreation(); otherwise, + // |dst| is treated as an existing node, and its position will be set via + // SetPosition(). |trans| is the transaction to which |dst| belongs. Returns + // false on failure. + static bool PlaceSyncNode(MoveOrCreate operation, + const BookmarkNode* parent, + int index, + sync_api::WriteTransaction* trans, + sync_api::WriteNode* dst, + ModelAssociator* associator, + UnrecoverableErrorHandler* error_handler); + + // Copy properties (but not position) from |src| to |dst|. + static void UpdateSyncNodeProperties(const BookmarkNode* src, + BookmarkModel* model, + sync_api::WriteNode* dst); + + // Helper function to encode a bookmark's favicon into a PNG byte vector. + static void EncodeFavicon(const BookmarkNode* src, + BookmarkModel* model, + std::vector<unsigned char>* dst); + + // Remove the sync node corresponding to |node|. It shouldn't have + // any children. + void RemoveOneSyncNode(sync_api::WriteTransaction* trans, + const BookmarkNode* node); + + // Remove all the sync nodes associated with |node| and its children. + void RemoveSyncNodeHierarchy(const BookmarkNode* node); + + bool running_; // True if we have been told it is safe to process changes. + UnrecoverableErrorHandler* error_handler_; // Guaranteed to outlive us. + + // The two models we are processing changes between. Non-NULL when + // |running_| is true. + BookmarkModel* bookmark_model_; + sync_api::UserShare* share_handle_; + + // The two models should be associated according to this ModelAssociator. + scoped_refptr<ModelAssociator> model_associator_; + + DISALLOW_COPY_AND_ASSIGN(ChangeProcessor); +}; + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_GLUE_CHANGE_APPLICATOR_H_ + +#endif // CHROME_PERSONALIZATION diff --git a/chrome/browser/sync/glue/model_associator.cc b/chrome/browser/sync/glue/model_associator.cc index d4c4c45..47597ff 100644 --- a/chrome/browser/sync/glue/model_associator.cc +++ b/chrome/browser/sync/glue/model_associator.cc @@ -353,13 +353,15 @@ bool ModelAssociator::BuildAssocations() { if (child_node) { model->Move(child_node, parent_node, index); // Set the favicon for bookmark node from sync node or vice versa. - if (!sync_service_->SetBookmarkFavicon(&sync_child_node, child_node)) - sync_service_->SetSyncNodeFavicon(child_node, &sync_child_node); + if (ChangeProcessor::SetBookmarkFavicon(&sync_child_node, + child_node, sync_service_->profile())) { + ChangeProcessor::SetSyncNodeFavicon(child_node, model, + &sync_child_node); + } } else { // Create a new bookmark node for the sync node. - child_node = sync_service_->CreateBookmarkNode(&sync_child_node, - parent_node, - index); + child_node = ChangeProcessor::CreateBookmarkNode(&sync_child_node, + parent_node, model, index); } AssociateIds(child_node->id(), sync_child_id); if (sync_child_node.GetIsFolder()) @@ -375,7 +377,8 @@ bool ModelAssociator::BuildAssocations() { // So the children starting from index in the parent bookmark node are the // ones that are not present in the parent sync node. So create them. for (int i = index; i < parent_node->GetChildCount(); ++i) { - sync_child_id = sync_service_->CreateSyncNode(parent_node, i, &trans); + sync_child_id = ChangeProcessor::CreateSyncNode(parent_node, model, i, + &trans, this, sync_service_); if (parent_node->GetChild(i)->is_folder()) dfs_stack.push(sync_child_id); } @@ -415,7 +418,7 @@ void ModelAssociator::PersistAssociations() { int64 sync_id = *iter; sync_api::WriteNode sync_node(&trans); if (!sync_node.InitByIdLookup(sync_id)) { - sync_service_->SetUnrecoverableError(); + sync_service_->OnUnrecoverableError(); return; } int64 node_id; diff --git a/chrome/browser/sync/glue/model_associator.h b/chrome/browser/sync/glue/model_associator.h index 9d5f825..a3b6e766e 100644 --- a/chrome/browser/sync/glue/model_associator.h +++ b/chrome/browser/sync/glue/model_associator.h @@ -28,6 +28,8 @@ class ProfileSyncService; namespace browser_sync { +class ChangeProcessor; + // Contains all model assocation related logic: // * Algorithm to associate bookmark model and sync model. // * Methods to get a bookmark node for a given sync node and vice versa. diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc index ee75a79..14c8588 100644 --- a/chrome/browser/sync/glue/sync_backend_host.cc +++ b/chrome/browser/sync/glue/sync_backend_host.cc @@ -7,6 +7,7 @@ #include "base/file_version_info.h" #include "base/file_util.h" #include "base/string_util.h" +#include "chrome/browser/sync/glue/change_processor.h" #include "chrome/browser/sync/glue/sync_backend_host.h" #include "chrome/browser/sync/glue/http_bridge.h" #include "chrome/browser/sync/glue/bookmark_model_worker.h" @@ -23,11 +24,13 @@ static const FilePath::CharType kSyncDataFolderName[] = namespace browser_sync { SyncBackendHost::SyncBackendHost(SyncFrontend* frontend, - const FilePath& profile_path) + const FilePath& profile_path, + ChangeProcessingInterface* processor) : core_thread_("Chrome_SyncCoreThread"), frontend_loop_(MessageLoop::current()), bookmark_model_worker_(NULL), frontend_(frontend), + processor_(processor), sync_data_folder_path_(profile_path.Append(kSyncDataFolderName)), last_auth_error_(AUTH_ERROR_NONE) { core_ = new Core(this); @@ -250,7 +253,7 @@ void SyncBackendHost::Core::OnChangesApplied( DLOG(WARNING) << "Could not update bookmark model from non-UI thread"; return; } - host_->frontend_->ApplyModelChanges(trans, changes, change_count); + host_->processor_->ApplyChangesFromSyncModel(trans, changes, change_count); } void SyncBackendHost::Core::OnSyncCycleCompleted() { diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h index eb89e43..a15e1a7 100644 --- a/chrome/browser/sync/glue/sync_backend_host.h +++ b/chrome/browser/sync/glue/sync_backend_host.h @@ -22,7 +22,48 @@ namespace browser_sync { -class SyncFrontend; +// SyncFrontend is the interface used by SyncBackendHost to communicate with +// the entity that created it and, presumably, is interested in sync-related +// activity. +// NOTE: All methods will be invoked by a SyncBackendHost on the same thread +// used to create that SyncBackendHost. +class SyncFrontend { + public: + SyncFrontend() {} + + // The backend has completed initialization and it is now ready to accept and + // process changes. + virtual void OnBackendInitialized() = 0; + + // The backend queried the server recently and received some updates. + virtual void OnSyncCycleCompleted() = 0; + + // The backend encountered an authentication problem and requests new + // credentials to be provided. See SyncBackendHost::Authenticate for details. + virtual void OnAuthError() = 0; + + protected: + // Don't delete through SyncFrontend interface. + virtual ~SyncFrontend() { + } + private: + DISALLOW_COPY_AND_ASSIGN(SyncFrontend); +}; + +// An interface used to apply changes from the sync model to the browser's +// native model. This does not currently distinguish between model data types. +class ChangeProcessingInterface { + public: + // Changes have been applied to the backend model and are ready to be + // applied to the frontend model. See syncapi.h for detailed instructions on + // how to interpret and process |changes|. + virtual void ApplyChangesFromSyncModel( + const sync_api::BaseTransaction* trans, + const sync_api::SyncManager::ChangeRecord* changes, + int change_count) = 0; + protected: + virtual ~ChangeProcessingInterface() { } +}; // A UI-thread safe API into the sync backend that "hosts" the top-level // syncapi element, the SyncManager, on its own thread. This class handles @@ -36,8 +77,10 @@ class SyncBackendHost { // Create a SyncBackendHost with a reference to the |frontend| that it serves // and communicates to via the SyncFrontend interface (on the same thread - // it used to call the constructor). - SyncBackendHost(SyncFrontend* frontend, const FilePath& proifle_path); + // it used to call the constructor), and push changes from sync_api through + // |processor|. + SyncBackendHost(SyncFrontend* frontend, const FilePath& profile_path, + ChangeProcessingInterface* processor); ~SyncBackendHost(); // Called on |frontend_loop_| to kick off asynchronous initialization. @@ -222,6 +265,8 @@ class SyncBackendHost { // The frontend which we serve (and are owned by). SyncFrontend* frontend_; + ChangeProcessingInterface* processor_; // Guaranteed to outlive us. + // Path of the folder that stores the sync data files. FilePath sync_data_folder_path_; @@ -231,43 +276,6 @@ class SyncBackendHost { DISALLOW_COPY_AND_ASSIGN(SyncBackendHost); }; -// SyncFrontend is the interface used by SyncBackendHost to communicate with -// the entity that created it and, presumably, is interested in sync-related -// activity. -// NOTE: All methods will be invoked by a SyncBackendHost on the same thread -// used to create that SyncBackendHost. -class SyncFrontend { - public: - typedef sync_api::BaseTransaction BaseTransaction; - typedef sync_api::SyncManager::ChangeRecord ChangeRecord; - SyncFrontend() { - } - - // The backend has completed initialization and it is now ready to accept and - // process changes. - virtual void OnBackendInitialized() = 0; - - // The backend queried the server recently and received some updates. - virtual void OnSyncCycleCompleted() = 0; - - // The backend encountered an authentication problem and requests new - // credentials to be provided. See SyncBackendHost::Authenticate for details. - virtual void OnAuthError() = 0; - - // Changes have been applied to the backend model and are ready to be - // applied to the frontend model. See syncapi.h for detailed instructions on - // how to interpret and process |changes|. - virtual void ApplyModelChanges(const BaseTransaction* trans, - const ChangeRecord* changes, - int change_count) = 0; - protected: - // Don't delete through SyncFrontend interface. - virtual ~SyncFrontend() { - } - private: - DISALLOW_COPY_AND_ASSIGN(SyncFrontend); -}; - } // namespace browser_sync #endif // CHROME_BROWSER_SYNC_GLUE_SYNC_BACKEND_HOST_H_ diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index 4de8f38..a426e53 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -25,11 +25,14 @@ #include "chrome/browser/sync/personalization.h" #include "chrome/browser/sync/personalization_strings.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" #include "chrome/common/time_format.h" #include "views/window/window.h" +using browser_sync::ChangeProcessor; using browser_sync::ModelAssociator; using browser_sync::SyncBackendHost; @@ -43,15 +46,21 @@ ProfileSyncService::ProfileSyncService(Profile* profile) backend_initialized_(false), expecting_first_run_auth_needed_event_(false), is_auth_in_progress_(false), - ready_to_process_changes_(false), unrecoverable_error_detected_(false), ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)) { + change_processor_.reset(new ChangeProcessor(this)); } ProfileSyncService::~ProfileSyncService() { Shutdown(false); } +void ProfileSyncService::set_model_associator( + browser_sync::ModelAssociator* associator) { + model_associator_ = associator; + change_processor_->set_model_associator(associator); +} + void ProfileSyncService::Initialize() { InitSettings(); RegisterPreferences(); @@ -108,15 +117,15 @@ void ProfileSyncService::StartUp() { last_synced_time_ = base::Time::FromInternalValue( profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime)); - backend_.reset(new SyncBackendHost(this, profile_->GetPath())); + backend_.reset(new SyncBackendHost(this, profile_->GetPath(), + change_processor_.get())); - // We add ourselves as an observer, and we remain one forever. Note we don't - // keep any pointer to the model, we just receive notifications from it. - BookmarkModel* model = profile_->GetBookmarkModel(); - model->AddObserver(this); + registrar_.Add(this, NotificationType::BOOKMARK_MODEL_LOADED, + Source<Profile>(profile_)); - // Create new model assocation manager. + // Create new model assocation manager and change processor. model_associator_ = new ModelAssociator(this); + change_processor_->set_model_associator(model_associator_); // TODO(timsteele): HttpBridgeFactory should take a const* to the profile's // URLRequestContext, because it needs it to create HttpBridge objects, and @@ -127,16 +136,15 @@ void ProfileSyncService::StartUp() { } void ProfileSyncService::Shutdown(bool sync_disabled) { - if (backend_.get()) { + registrar_.RemoveAll(); + + if (backend_.get()) backend_->Shutdown(sync_disabled); - backend_.reset(); - } - BookmarkModel* model = profile_->GetBookmarkModel(); - if (model) - model->RemoveObserver(this); + change_processor_->Stop(); + backend_.reset(); - // Clear all assocations and throw away the assocation manager instance. + // Clear all associations and throw away the association manager instance. if (model_associator_.get()) { model_associator_->ClearAll(); model_associator_ = NULL; @@ -146,7 +154,6 @@ void ProfileSyncService::Shutdown(bool sync_disabled) { is_auth_in_progress_ = false; backend_initialized_ = false; expecting_first_run_auth_needed_event_ = false; - ready_to_process_changes_ = false; last_attempted_user_email_.clear(); } @@ -172,90 +179,14 @@ void ProfileSyncService::DisableForUser() { FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } -void ProfileSyncService::Loaded(BookmarkModel* model) { +void ProfileSyncService::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK_EQ(NotificationType::BOOKMARK_MODEL_LOADED, type.value); + registrar_.RemoveAll(); StartProcessingChangesIfReady(); } -void ProfileSyncService::UpdateSyncNodeProperties(const BookmarkNode* src, - sync_api::WriteNode* dst) { - // Set the properties of the item. - dst->SetIsFolder(src->is_folder()); - dst->SetTitle(WideToUTF16(src->GetTitle()).c_str()); - // URL is passed as a C string here because this interface avoids - // string16. SetURL copies the data into its own memory. - string16 url = UTF8ToUTF16(src->GetURL().spec()); - dst->SetURL(url.c_str()); - SetSyncNodeFavicon(src, dst); -} - -void ProfileSyncService::EncodeFavicon(const BookmarkNode* src, - std::vector<unsigned char>* dst) const { - const SkBitmap& favicon = profile_->GetBookmarkModel()->GetFavIcon(src); - - dst->clear(); - - // Check for zero-dimension images. This can happen if the favicon is - // still being loaded. - if (favicon.empty()) - return; - - // Re-encode the BookmarkNode's favicon as a PNG, and pass the data to the - // sync subsystem. - if (!PNGEncoder::EncodeBGRASkBitmap(favicon, false, dst)) - return; -} - -void ProfileSyncService::RemoveOneSyncNode(sync_api::WriteTransaction* trans, - const BookmarkNode* node) { - sync_api::WriteNode sync_node(trans); - if (!model_associator_->InitSyncNodeFromBookmarkId(node->id(), &sync_node)) { - SetUnrecoverableError(); - return; - } - // This node should have no children. - DCHECK(sync_node.GetFirstChildId() == sync_api::kInvalidId); - // Remove association and delete the sync node. - model_associator_->DisassociateIds(sync_node.GetId()); - sync_node.Remove(); -} - -void ProfileSyncService::RemoveSyncNodeHierarchy(const BookmarkNode* topmost) { - sync_api::WriteTransaction trans(backend_->GetUserShareHandle()); - - // Later logic assumes that |topmost| has been unlinked. - DCHECK(!topmost->GetParent()); - - // 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->GetParent() || (node->GetParent()->IndexOfChild(node) == - index_stack.top())); - if (index == node->GetChildCount()) { - // If we've processed all of |node|'s children, delete |node| and move - // on to its successor. - RemoveOneSyncNode(&trans, node); - node = node->GetParent(); - 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->GetChildCount()); - index_stack.push(index); - node = node->GetChild(index); - index = 0; - } - } - DCHECK(index_stack.empty()); // Nothing should be left on the stack. -} - bool ProfileSyncService::MergeAndSyncAcceptanceNeeded() const { // If we've shown the dialog before, don't show it again. if (profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) @@ -282,222 +213,14 @@ void ProfileSyncService::UpdateLastSyncedTime() { profile_->GetPrefs()->ScheduleSavePersistentPrefs(); } -void ProfileSyncService::BookmarkNodeAdded(BookmarkModel* model, - const BookmarkNode* parent, - int index) { - if (!ShouldPushChanges()) - return; - - DCHECK(backend_->GetUserShareHandle()); - - // Acquire a scoped write lock via a transaction. - sync_api::WriteTransaction trans(backend_->GetUserShareHandle()); - - CreateSyncNode(parent, index, &trans); -} - -int64 ProfileSyncService::CreateSyncNode(const BookmarkNode* parent, - int index, - sync_api::WriteTransaction* trans) { - const BookmarkNode* child = parent->GetChild(index); - DCHECK(child); - - // Create a WriteNode container to hold the new node. - sync_api::WriteNode sync_child(trans); - - // Actually create the node with the appropriate initial position. - if (!PlaceSyncNode(CREATE, parent, index, trans, &sync_child)) { - LOG(WARNING) << "Sync node creation failed; recovery unlikely"; - SetUnrecoverableError(); - return sync_api::kInvalidId; - } - - UpdateSyncNodeProperties(child, &sync_child); - - // Associate the ID from the sync domain with the bookmark node, so that we - // can refer back to this item later. - model_associator_->AssociateIds(child->id(), sync_child.GetId()); - - return sync_child.GetId(); -} - -void ProfileSyncService::BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int index, - const BookmarkNode* node) { - if (!ShouldPushChanges()) - return; - - RemoveSyncNodeHierarchy(node); -} - -void ProfileSyncService::BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node) { - if (!ShouldPushChanges()) - return; - - // We shouldn't see changes to the top-level nodes. - DCHECK_NE(node, model->GetBookmarkBarNode()); - DCHECK_NE(node, model->other_node()); - - // Acquire a scoped write lock via a transaction. - sync_api::WriteTransaction trans(backend_->GetUserShareHandle()); - - // Lookup the sync node that's associated with |node|. - sync_api::WriteNode sync_node(&trans); - if (!model_associator_->InitSyncNodeFromBookmarkId(node->id(), &sync_node)) { - SetUnrecoverableError(); - return; - } - - UpdateSyncNodeProperties(node, &sync_node); - - DCHECK_EQ(sync_node.GetIsFolder(), node->is_folder()); - DCHECK_EQ(model_associator_->GetBookmarkNodeFromSyncId( - sync_node.GetParentId()), - node->GetParent()); - // This node's index should be one more than the predecessor's index. - DCHECK_EQ(node->GetParent()->IndexOfChild(node), - CalculateBookmarkModelInsertionIndex(node->GetParent(), - &sync_node)); -} - -void ProfileSyncService::BookmarkNodeMoved(BookmarkModel* model, - const BookmarkNode* old_parent, - int old_index, - const BookmarkNode* new_parent, - int new_index) { - if (!ShouldPushChanges()) - return; - - const BookmarkNode* child = new_parent->GetChild(new_index); - // We shouldn't see changes to the top-level nodes. - DCHECK_NE(child, model->GetBookmarkBarNode()); - DCHECK_NE(child, model->other_node()); - - // Acquire a scoped write lock via a transaction. - sync_api::WriteTransaction trans(backend_->GetUserShareHandle()); - - // Lookup the sync node that's associated with |child|. - sync_api::WriteNode sync_node(&trans); - if (!model_associator_->InitSyncNodeFromBookmarkId(child->id(), - &sync_node)) { - SetUnrecoverableError(); - return; - } - - if (!PlaceSyncNode(MOVE, new_parent, new_index, &trans, &sync_node)) { - SetUnrecoverableError(); - return; - } -} - -void ProfileSyncService::BookmarkNodeFavIconLoaded(BookmarkModel* model, - const BookmarkNode* node) { - BookmarkNodeChanged(model, node); -} - -void ProfileSyncService::BookmarkNodeChildrenReordered( - BookmarkModel* model, const BookmarkNode* node) { - if (!ShouldPushChanges()) - return; - - // Acquire a scoped write lock via a transaction. - sync_api::WriteTransaction trans(backend_->GetUserShareHandle()); - - // 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->GetChildCount(); ++i) { - sync_api::WriteNode sync_child(&trans); - if (!model_associator_->InitSyncNodeFromBookmarkId(node->GetChild(i)->id(), - &sync_child)) { - SetUnrecoverableError(); - return; - } - DCHECK_EQ(sync_child.GetParentId(), - model_associator_->GetSyncIdFromBookmarkId(node->id())); - - if (!PlaceSyncNode(MOVE, node, i, &trans, &sync_child)) { - SetUnrecoverableError(); - return; - } - } -} - -bool ProfileSyncService::PlaceSyncNode(MoveOrCreate operation, - const BookmarkNode* parent, - int index, - sync_api::WriteTransaction* trans, - sync_api::WriteNode* dst) { - sync_api::ReadNode sync_parent(trans); - if (!model_associator_->InitSyncNodeFromBookmarkId(parent->id(), - &sync_parent)) { - LOG(WARNING) << "Parent lookup failed"; - SetUnrecoverableError(); - return false; - } - - bool success = false; - if (index == 0) { - // Insert into first position. - success = (operation == CREATE) ? dst->InitByCreation(sync_parent, NULL) : - dst->SetPosition(sync_parent, NULL); - if (success) { - DCHECK_EQ(dst->GetParentId(), sync_parent.GetId()); - DCHECK_EQ(dst->GetId(), sync_parent.GetFirstChildId()); - DCHECK_EQ(dst->GetPredecessorId(), sync_api::kInvalidId); - } - } else { - // Find the bookmark model predecessor, and insert after it. - const BookmarkNode* prev = parent->GetChild(index - 1); - sync_api::ReadNode sync_prev(trans); - if (!model_associator_->InitSyncNodeFromBookmarkId(prev->id(), - &sync_prev)) { - LOG(WARNING) << "Predecessor lookup failed"; - return false; - } - success = (operation == CREATE) ? - dst->InitByCreation(sync_parent, &sync_prev) : - dst->SetPosition(sync_parent, &sync_prev); - if (success) { - DCHECK_EQ(dst->GetParentId(), sync_parent.GetId()); - DCHECK_EQ(dst->GetPredecessorId(), sync_prev.GetId()); - DCHECK_EQ(dst->GetId(), sync_prev.GetSuccessorId()); - } - } - return success; -} - // An invariant has been violated. Transition to an error state where we try // to do as little work as possible, to avoid further corruption or crashes. -void ProfileSyncService::SetUnrecoverableError() { +void ProfileSyncService::OnUnrecoverableError() { unrecoverable_error_detected_ = true; + change_processor_->Stop(); LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable."; } -// Determine the bookmark model index to which a node must be moved so that -// predecessor of the node (in the bookmark model) matches the predecessor of -// |source| (in the sync model). -// As a precondition, this assumes that the predecessor of |source| has been -// updated and is already in the correct position in the bookmark model. -int ProfileSyncService::CalculateBookmarkModelInsertionIndex( - const BookmarkNode* parent, - const sync_api::BaseNode* child_info) const { - DCHECK(parent); - DCHECK(child_info); - int64 predecessor_id = child_info->GetPredecessorId(); - // A return ID of kInvalidId indicates no predecessor. - if (predecessor_id == sync_api::kInvalidId) - return 0; - - // Otherwise, insert after the predecessor bookmark node. - const BookmarkNode* predecessor = - model_associator_->GetBookmarkNodeFromSyncId(predecessor_id); - DCHECK(predecessor); - DCHECK_EQ(predecessor->GetParent(), parent); - return parent->IndexOfChild(predecessor) + 1; -} - void ProfileSyncService::OnBackendInitialized() { backend_initialized_ = true; StartProcessingChangesIfReady(); @@ -553,206 +276,6 @@ void ProfileSyncService::ShowLoginDialog() { wizard_.Step(SyncSetupWizard::GAIA_LOGIN); } -// ApplyModelChanges is called by the sync backend after changes have been made -// to the sync engine's model. Apply these changes to the browser bookmark -// model. -void ProfileSyncService::ApplyModelChanges( - const sync_api::BaseTransaction* trans, - const sync_api::SyncManager::ChangeRecord* changes, - int change_count) { - if (!ShouldPushChanges()) - return; - - // A note about ordering. Sync backend is responsible for ordering the change - // records in the following order: - // - // 1. Deletions, from leaves up to parents. - // 2. Existing items with synced parents & predecessors. - // 3. New items with synced parents & predecessors. - // 4. Items with parents & predecessors in the list. - // 5. Repeat #4 until all items are in the list. - // - // "Predecessor" here means the previous item within a given folder; an item - // in the first position is always said to have a synced predecessor. - // For the most part, applying these changes in the order given will yield - // the correct result. There is one exception, however: for items that are - // moved away from a folder that is being deleted, we will process the delete - // before the move. Since deletions in the bookmark model propagate from - // parent to child, we must move them to a temporary location. - BookmarkModel* model = profile_->GetBookmarkModel(); - - // We are going to make changes to the bookmarks model, but don't want to end - // up in a feedback loop, so remove ourselves as an observer while applying - // changes. - model->RemoveObserver(this); - - // A parent to hold nodes temporarily orphaned by parent deletion. It is - // lazily created inside the loop. - const BookmarkNode* foster_parent = NULL; - for (int i = 0; i < change_count; ++i) { - const BookmarkNode* dst = - model_associator_->GetBookmarkNodeFromSyncId(changes[i].id); - // Ignore changes to the permanent top-level nodes. We only care about - // their children. - if ((dst == model->GetBookmarkBarNode()) || (dst == model->other_node())) - continue; - if (changes[i].action == - sync_api::SyncManager::ChangeRecord::ACTION_DELETE) { - // Deletions should always be at the front of the list. - DCHECK(i == 0 || changes[i-1].action == changes[i].action); - // Children of a deleted node should not be deleted; they may be - // reparented by a later change record. Move them to a temporary place. - DCHECK(dst) << "Could not find node to be deleted"; - const BookmarkNode* parent = dst->GetParent(); - if (dst->GetChildCount()) { - if (!foster_parent) { - foster_parent = model->AddGroup(model->other_node(), - model->other_node()->GetChildCount(), - std::wstring()); - } - for (int i = dst->GetChildCount() - 1; i >= 0; --i) { - model->Move(dst->GetChild(i), foster_parent, - foster_parent->GetChildCount()); - } - } - DCHECK_EQ(dst->GetChildCount(), 0) << "Node being deleted has children"; - model->Remove(parent, parent->IndexOfChild(dst)); - dst = NULL; - model_associator_->DisassociateIds(changes[i].id); - } else { - DCHECK_EQ((changes[i].action == - sync_api::SyncManager::ChangeRecord::ACTION_ADD), (dst == NULL)) - << "ACTION_ADD should be seen if and only if the node is unknown."; - - sync_api::ReadNode src(trans); - if (!src.InitByIdLookup(changes[i].id)) { - LOG(ERROR) << "ApplyModelChanges was passed a bad ID"; - SetUnrecoverableError(); - return; - } - - CreateOrUpdateBookmarkNode(&src, model); - } - } - // Clean up the temporary node. - if (foster_parent) { - // There should be no nodes left under the foster parent. - DCHECK_EQ(foster_parent->GetChildCount(), 0); - model->Remove(foster_parent->GetParent(), - foster_parent->GetParent()->IndexOfChild(foster_parent)); - foster_parent = NULL; - } - - // We are now ready to hear about bookmarks changes again. - model->AddObserver(this); -} - -// Create a bookmark node corresponding to |src| if one is not already -// associated with |src|. -const BookmarkNode* ProfileSyncService::CreateOrUpdateBookmarkNode( - sync_api::BaseNode* src, - BookmarkModel* model) { - const BookmarkNode* parent = - model_associator_->GetBookmarkNodeFromSyncId(src->GetParentId()); - if (!parent) { - DLOG(WARNING) << "Could not find parent of node being added/updated." - << " Node title: " << src->GetTitle() - << ", parent id = " << src->GetParentId(); - return NULL; - } - int index = CalculateBookmarkModelInsertionIndex(parent, src); - const BookmarkNode* dst = model_associator_->GetBookmarkNodeFromSyncId( - src->GetId()); - if (!dst) { - dst = CreateBookmarkNode(src, parent, index); - model_associator_->AssociateIds(dst->id(), src->GetId()); - } else { - // URL and is_folder are not expected to change. - // TODO(ncarter): Determine if such changes should be legal or not. - DCHECK_EQ(src->GetIsFolder(), dst->is_folder()); - - // Handle reparenting and/or repositioning. - model->Move(dst, parent, index); - - // Handle title update and URL changes due to possible conflict resolution - // that can happen if both a local user change and server change occur - // within a sufficiently small time interval. - const BookmarkNode* old_dst = dst; - dst = bookmark_utils::ApplyEditsWithNoGroupChange(model, parent, dst, - UTF16ToWide(src->GetTitle()), - src->GetIsFolder() ? GURL() : GURL(src->GetURL()), - NULL); // NULL because we don't need a BookmarkEditor::Handler. - if (dst != old_dst) { // dst was replaced with a new node with new URL. - model_associator_->DisassociateIds(src->GetId()); - model_associator_->AssociateIds(dst->id(), src->GetId()); - } - SetBookmarkFavicon(src, dst); - } - - return dst; -} - -// Creates a bookmark node under the given parent node from the given sync -// node. Returns the newly created node. -const BookmarkNode* ProfileSyncService::CreateBookmarkNode( - sync_api::BaseNode* sync_node, - const BookmarkNode* parent, - int index) const { - DCHECK(parent); - DCHECK(index >= 0 && index <= parent->GetChildCount()); - BookmarkModel* model = profile_->GetBookmarkModel(); - - const BookmarkNode* node; - if (sync_node->GetIsFolder()) { - node = model->AddGroup(parent, index, UTF16ToWide(sync_node->GetTitle())); - } else { - GURL url(sync_node->GetURL()); - node = model->AddURL(parent, index, - UTF16ToWide(sync_node->GetTitle()), url); - SetBookmarkFavicon(sync_node, node); - } - return node; -} - -// Sets the favicon of the given bookmark node from the given sync node. -bool ProfileSyncService::SetBookmarkFavicon( - sync_api::BaseNode* sync_node, - const BookmarkNode* bookmark_node) const { - size_t icon_size = 0; - const unsigned char* icon_bytes = sync_node->GetFaviconBytes(&icon_size); - if (!icon_size || !icon_bytes) - return false; - - // Registering a favicon requires that we provide a source URL, but we - // don't know where these came from. Currently we just use the - // destination URL, which is not correct, but since the favicon URL - // is used as a key in the history's thumbnail DB, this gives us a value - // which does not collide with others. - GURL fake_icon_url = bookmark_node->GetURL(); - - std::vector<unsigned char> icon_bytes_vector(icon_bytes, - icon_bytes + icon_size); - - HistoryService* history = - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - - history->AddPage(bookmark_node->GetURL()); - history->SetFavIcon(bookmark_node->GetURL(), - fake_icon_url, - icon_bytes_vector); - - return true; -} - -void ProfileSyncService::SetSyncNodeFavicon( - const BookmarkNode* bookmark_node, - sync_api::WriteNode* sync_node) const { - std::vector<unsigned char> favicon_bytes; - EncodeFavicon(bookmark_node, &favicon_bytes); - if (!favicon_bytes.empty()) - sync_node->SetFaviconBytes(&favicon_bytes[0], favicon_bytes.size()); -} - SyncBackendHost::StatusSummary ProfileSyncService::QuerySyncStatusSummary() { return backend_->GetStatusSummary(); } @@ -821,11 +344,12 @@ void ProfileSyncService::OnUserAcceptedMergeAndSync() { wizard_.Step(SyncSetupWizard::DONE); // TODO(timsteele): error state? if (!merge_success) { LOG(ERROR) << "Model assocation failed."; - SetUnrecoverableError(); + OnUnrecoverableError(); return; } - ready_to_process_changes_ = true; + change_processor_->Start(profile_->GetBookmarkModel(), + backend_->GetUserShareHandle()); FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } @@ -842,7 +366,7 @@ void ProfileSyncService::OnUserCancelledDialog() { void ProfileSyncService::StartProcessingChangesIfReady() { BookmarkModel* model = profile_->GetBookmarkModel(); - DCHECK(!ready_to_process_changes_); + DCHECK(!change_processor_->IsRunning()); // First check if the subsystems are ready. We can't proceed until they // both have finished loading. @@ -867,11 +391,12 @@ void ProfileSyncService::StartProcessingChangesIfReady() { wizard_.Step(SyncSetupWizard::DONE); // TODO(timsteele): error state? if (!merge_success) { LOG(ERROR) << "Model assocation failed."; - SetUnrecoverableError(); + OnUnrecoverableError(); return; } - ready_to_process_changes_ = true; + change_processor_->Start(profile_->GetBookmarkModel(), + backend_->GetUserShareHandle()); FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } @@ -892,8 +417,10 @@ void ProfileSyncService::SyncEvent(SyncEventCodes code) { } bool ProfileSyncService::ShouldPushChanges() { - return ready_to_process_changes_ && // Wait for model load and merge. - !unrecoverable_error_detected_; // Halt after any terrible events. + // True only after all bootstrapping has succeeded: the bookmark model is + // loaded, the sync backend is initialized, the two domains are + // consistent with one another, and no unrecoverable error has transpired. + return change_processor_->IsRunning(); } #endif // CHROME_PERSONALIZATION diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h index a2f956b..ae2047e 100644 --- a/chrome/browser/sync/profile_sync_service.h +++ b/chrome/browser/sync/profile_sync_service.h @@ -17,9 +17,11 @@ #include "base/scoped_ptr.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/profile.h" +#include "chrome/browser/sync/glue/change_processor.h" #include "chrome/browser/sync/glue/model_associator.h" #include "chrome/browser/sync/glue/sync_backend_host.h" #include "chrome/browser/views/sync/sync_setup_wizard.h" +#include "chrome/common/notification_registrar.h" #include "googleurl/src/gurl.h" class CommandLine; @@ -28,6 +30,18 @@ class Profile; namespace browser_sync { class ModelAssociator; + +class UnrecoverableErrorHandler { + public: + // Call this when normal operation detects that the bookmark model and the + // syncer model are inconsistent, or similar. The ProfileSyncService will + // try to avoid doing any work to avoid crashing or corrupting things + // further, and will report an error status if queried. + virtual void OnUnrecoverableError() = 0; + protected: + virtual ~UnrecoverableErrorHandler() { } +}; + } // Various UI components such as the New Tab page can be driven by observing @@ -47,8 +61,9 @@ class ProfileSyncServiceObserver { // ProfileSyncService is the layer between browser subsystems like bookmarks, // and the sync backend. -class ProfileSyncService : public BookmarkModelObserver, - public browser_sync::SyncFrontend { +class ProfileSyncService : public NotificationObserver, + public browser_sync::SyncFrontend, + public browser_sync::UnrecoverableErrorHandler { public: typedef ProfileSyncServiceObserver Observer; typedef browser_sync::SyncBackendHost::Status Status; @@ -91,36 +106,15 @@ class ProfileSyncService : public BookmarkModelObserver, bool HasSyncSetupCompleted() const; void SetSyncSetupCompleted(); - // BookmarkModelObserver implementation. - virtual void Loaded(BookmarkModel* model); - virtual void BookmarkModelBeingDeleted(BookmarkModel* model) {} - virtual void BookmarkNodeMoved(BookmarkModel* model, - const BookmarkNode* old_parent, - int old_index, - const BookmarkNode* new_parent, - int new_index); - virtual void BookmarkNodeAdded(BookmarkModel* model, - const BookmarkNode* parent, - int index); - virtual void BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int index, - const BookmarkNode* node); - virtual void BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node); - virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, - const BookmarkNode* node); - virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, - const BookmarkNode* node); + // NotificationObserver implementation. + virtual void ProfileSyncService::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); // SyncFrontend implementation. virtual void OnBackendInitialized(); virtual void OnSyncCycleCompleted(); virtual void OnAuthError(); - virtual void ApplyModelChanges( - const sync_api::BaseTransaction* trans, - const sync_api::SyncManager::ChangeRecord* changes, - int change_count); // Called when a user enters credentials through UI. virtual void OnUserSubmittedAuth(const std::string& username, @@ -196,6 +190,11 @@ class ProfileSyncService : public BookmarkModelObserver, // Record stats on various events. static void SyncEvent(SyncEventCodes code); + // UnrecoverableErrorHandler implementation. + virtual void OnUnrecoverableError(); + + browser_sync::SyncBackendHost* backend() { return backend_.get(); } + protected: // Call this after any of the subsystems being synced (the bookmark // model and the sync backend) finishes its initialization. When everything @@ -203,15 +202,6 @@ class ProfileSyncService : public BookmarkModelObserver, // initially in sync, and start forwarding changes between the two models. void StartProcessingChangesIfReady(); - // Various member accessors needed by unit tests. - browser_sync::SyncBackendHost* backend() { return backend_.get(); } - - // Call this when normal operation detects that the bookmark model and the - // syncer model are inconsistent, or similar. The ProfileSyncService will - // try to avoid doing any work to avoid crashing or corrupting things - // further, and will report an error status if queried. - void SetUnrecoverableError(); - // Returns whether processing changes is allowed. Check this before doing // any model-modifying operations. bool ShouldPushChanges(); @@ -230,9 +220,7 @@ class ProfileSyncService : public BookmarkModelObserver, virtual void InitializeBackend(); // Tests need this. - void set_model_associator(browser_sync::ModelAssociator* manager) { - model_associator_ = manager; - } + void set_model_associator(browser_sync::ModelAssociator* associator); // We keep track of the last auth error observed so we can cover up the first // "expected" auth failure from observers. @@ -244,90 +232,14 @@ class ProfileSyncService : public BookmarkModelObserver, std::string last_attempted_user_email_; private: - friend class browser_sync::ModelAssociator; friend class ProfileSyncServiceTest; friend class ProfileSyncServiceTestHarness; friend class TestModelAssociator; FRIEND_TEST(ProfileSyncServiceTest, UnrecoverableErrorSuspendsService); - enum MoveOrCreate { - MOVE, - CREATE, - }; - // Initializes the various settings from the command line. void InitSettings(); - // Treat the |index|th child of |parent| as a newly added node, and create a - // corresponding node in the sync domain using |trans|. All properties - // will be transferred to the new node. A node corresponding to |parent| - // must already exist and be associated for this call to succeed. Returns - // the ID of the just-created node, or if creation fails, kInvalidID. - int64 CreateSyncNode(const BookmarkNode* parent, - int index, - sync_api::WriteTransaction* trans); - - // Create a bookmark node corresponding to |src| if one is not already - // associated with |src|. Returns the node that was created or updated. - const BookmarkNode* CreateOrUpdateBookmarkNode( - sync_api::BaseNode* src, - BookmarkModel* model); - - // Creates a bookmark node under the given parent node from the given sync - // node. Returns the newly created node. - const BookmarkNode* CreateBookmarkNode( - sync_api::BaseNode* sync_node, - const BookmarkNode* parent, - int index) const; - - // Sets the favicon of the given bookmark node from the given sync node. - // Returns whether the favicon was set in the bookmark node. - bool SetBookmarkFavicon(sync_api::BaseNode* sync_node, - const BookmarkNode* bookmark_node) const; - - // Sets the favicon of the given sync node from the given bookmark node. - void SetSyncNodeFavicon(const BookmarkNode* bookmark_node, - sync_api::WriteNode* sync_node) const; - - // Helper function to determine the appropriate insertion index of sync node - // |node| under the Bookmark model node |parent|, to make the positions - // match up between the two models. This presumes that the predecessor of the - // item (in the bookmark model) has already been moved into its appropriate - // position. - int CalculateBookmarkModelInsertionIndex( - const BookmarkNode* parent, - const sync_api::BaseNode* node) const; - - // Helper function used to fix the position of a sync node so that it matches - // the position of a corresponding bookmark model node. |parent| and - // |index| identify the bookmark model position. |dst| is the node whose - // position is to be fixed. If |operation| is CREATE, treat |dst| as an - // uncreated node and set its position via InitByCreation(); otherwise, - // |dst| is treated as an existing node, and its position will be set via - // SetPosition(). |trans| is the transaction to which |dst| belongs. Returns - // false on failure. - bool PlaceSyncNode(MoveOrCreate operation, - const BookmarkNode* parent, - int index, - sync_api::WriteTransaction* trans, - sync_api::WriteNode* dst); - - // Copy properties (but not position) from |src| to |dst|. - void UpdateSyncNodeProperties(const BookmarkNode* src, - sync_api::WriteNode* dst); - - // Helper function to encode a bookmark's favicon into a PNG byte vector. - void EncodeFavicon(const BookmarkNode* src, - std::vector<unsigned char>* dst) const; - - // Remove the sync node corresponding to |node|. It shouldn't have - // any children. - void RemoveOneSyncNode(sync_api::WriteTransaction* trans, - const BookmarkNode* node); - - // Remove all the sync nodes associated with |node| and its children. - void RemoveSyncNodeHierarchy(const BookmarkNode* node); - // Whether the sync merge warning should be shown. bool MergeAndSyncAcceptanceNeeded() const; @@ -355,6 +267,10 @@ class ProfileSyncService : public BookmarkModelObserver, // other threads. scoped_ptr<browser_sync::SyncBackendHost> backend_; + scoped_ptr<browser_sync::ChangeProcessor> change_processor_; + + NotificationRegistrar registrar_; + // Whether the SyncBackendHost has been initialized. bool backend_initialized_; @@ -375,11 +291,6 @@ class ProfileSyncService : public BookmarkModelObserver, // As its name suggests, this should NOT be used for anything other than UI. bool is_auth_in_progress_; - // True only after all bootstrapping has succeeded: the bookmark model is - // loaded, the sync backend is initialized, and the two domains are - // consistent with one another. - bool ready_to_process_changes_; - // True if an unrecoverable error (e.g. violation of an assumed invariant) // occurred during syncer operation. This value should be checked before // doing any work that might corrupt things further. diff --git a/chrome/browser/sync/profile_sync_service_unittest.cc b/chrome/browser/sync/profile_sync_service_unittest.cc index ef12916..b2062b5 100644 --- a/chrome/browser/sync/profile_sync_service_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_unittest.cc @@ -16,11 +16,13 @@ #include "chrome/browser/profile.h" #include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/glue/model_associator.h" +#include "chrome/browser/sync/glue/sync_backend_host.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/testing_profile.h" using std::vector; +using browser_sync::ChangeProcessingInterface; using browser_sync::ModelAssociator; using browser_sync::SyncBackendHost; @@ -222,9 +224,9 @@ class FakeServerChange { } // Pass the fake change list to |service|. - void ApplyPendingChanges(ProfileSyncService* service) { - service->ApplyModelChanges(trans_, changes_.size() ? &changes_[0] : NULL, - changes_.size()); + void ApplyPendingChanges(browser_sync::ChangeProcessingInterface* processor) { + processor->ApplyChangesFromSyncModel(trans_, + changes_.size() ? &changes_[0] : NULL, changes_.size()); } const vector<sync_api::SyncManager::ChangeRecord>& changes() { @@ -272,6 +274,11 @@ class ProfileSyncServiceTest : public testing::Test { return service_->model_associator_; } + ChangeProcessingInterface* change_processor() { + DCHECK(service_.get()); + return service_->change_processor_.get(); + } + void StartSyncService() { if (!service_.get()) { service_.reset(new TestProfileSyncService(profile_.get())); @@ -539,7 +546,7 @@ TEST_F(ProfileSyncServiceTest, ServerChangeProcessing) { for (it = adds.changes().begin(); it != adds.changes().end(); ++it) ExpectBrowserNodeUnknown(it->id); - adds.ApplyPendingChanges(service_.get()); + adds.ApplyPendingChanges(change_processor()); // Make sure the bookmark model received all of the nodes in |adds|. for (it = adds.changes().begin(); it != adds.changes().end(); ++it) @@ -572,7 +579,7 @@ TEST_F(ProfileSyncServiceTest, ServerChangeProcessing) { ExpectBrowserNodeParent(u3, u3_old_parent); // Apply the changes. - mods.ApplyPendingChanges(service_.get()); + mods.ApplyPendingChanges(change_processor()); // Check for successful application. for (it = mods.changes().begin(); it != mods.changes().end(); ++it) @@ -587,7 +594,7 @@ TEST_F(ProfileSyncServiceTest, ServerChangeProcessing) { ExpectBrowserNodeKnown(u2); ExpectBrowserNodeKnown(u3); - dels.ApplyPendingChanges(service_.get()); + dels.ApplyPendingChanges(change_processor()); ExpectBrowserNodeUnknown(u2); ExpectBrowserNodeUnknown(u3); @@ -622,7 +629,7 @@ TEST_F(ProfileSyncServiceTest, ServerChangeRequiringFosterParent) { for (it = adds.changes().begin(); it != adds.changes().end(); ++it) ExpectBrowserNodeUnknown(it->id); - adds.ApplyPendingChanges(service_.get()); + adds.ApplyPendingChanges(change_processor()); // Make sure the bookmark model received all of the nodes in |adds|. for (it = adds.changes().begin(); it != adds.changes().end(); ++it) @@ -640,7 +647,7 @@ TEST_F(ProfileSyncServiceTest, ServerChangeRequiringFosterParent) { ops.ModifyPosition(u5, f6, 0); ops.Delete(f1); - ops.ApplyPendingChanges(service_.get()); + ops.ApplyPendingChanges(change_processor()); ExpectModelMatch(&trans); } @@ -658,7 +665,7 @@ TEST_F(ProfileSyncServiceTest, ServerChangeWithNonCanonicalURL) { EXPECT_NE(GURL(url).spec(), url); int64 u1 = adds.AddURL(L"u1", UTF8ToWide(url), other_bookmarks_id(), 0); - adds.ApplyPendingChanges(service_.get()); + adds.ApplyPendingChanges(change_processor()); EXPECT_TRUE(model_->other_node()->GetChildCount() == 1); ExpectModelMatch(&trans); @@ -688,7 +695,7 @@ TEST_F(ProfileSyncServiceTest, DISABLED_ServerChangeWithInvalidURL) { EXPECT_FALSE(GURL("x").is_valid()); int64 u1 = adds.AddURL(L"u1", L"x", other_bookmarks_id(), 0); - adds.ApplyPendingChanges(service_.get()); + adds.ApplyPendingChanges(change_processor()); // We're lenient about what should happen -- the model could wind up with // the node or without it; but things should be consistent, and we diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index be29b86..3a924ad 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1685,6 +1685,8 @@ 'browser/sync/engine/syncapi.h', 'browser/sync/glue/bookmark_model_worker.cc', 'browser/sync/glue/bookmark_model_worker.h', + 'browser/sync/glue/change_processor.cc', + 'browser/sync/glue/change_processor.h', 'browser/sync/glue/http_bridge.cc', 'browser/sync/glue/http_bridge.h', 'browser/sync/glue/model_associator.cc', diff --git a/chrome/test/live_sync/profile_sync_service_test_harness.cc b/chrome/test/live_sync/profile_sync_service_test_harness.cc index debb782..61c8deb 100644 --- a/chrome/test/live_sync/profile_sync_service_test_harness.cc +++ b/chrome/test/live_sync/profile_sync_service_test_harness.cc @@ -145,7 +145,7 @@ bool ProfileSyncServiceTestHarness::RunStateChangeMachine() { SignalStateCompleteWithNextState(WAITING_FOR_READY_TO_PROCESS_CHANGES); break; case WAITING_FOR_READY_TO_PROCESS_CHANGES: - if (service_->ready_to_process_changes_) { + if (service_->ShouldPushChanges()) { SignalStateCompleteWithNextState(WAITING_FOR_NOTHING); } break; |