| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/sync/glue/bookmark_model_associator.h" | 5 #include "chrome/browser/sync/glue/bookmark_model_associator.h" |
| 6 | 6 |
| 7 #include <stack> | 7 #include <stack> |
| 8 | 8 |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/hash_tables.h" | 10 #include "base/hash_tables.h" |
| 11 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
| 12 #include "base/task.h" | 12 #include "base/task.h" |
| 13 #include "base/tracked.h" | 13 #include "base/tracked.h" |
| 14 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
| 15 #include "chrome/browser/bookmarks/bookmark_model.h" | 15 #include "chrome/browser/bookmarks/bookmark_model.h" |
| 16 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
| 17 #include "chrome/browser/sync/api/sync_error.h" |
| 17 #include "chrome/browser/sync/engine/syncapi.h" | 18 #include "chrome/browser/sync/engine/syncapi.h" |
| 18 #include "chrome/browser/sync/glue/bookmark_change_processor.h" | 19 #include "chrome/browser/sync/glue/bookmark_change_processor.h" |
| 19 #include "chrome/browser/sync/syncable/nigori_util.h" | 20 #include "chrome/browser/sync/syncable/nigori_util.h" |
| 20 #include "chrome/browser/sync/util/cryptographer.h" | 21 #include "chrome/browser/sync/util/cryptographer.h" |
| 21 #include "chrome/common/chrome_switches.h" | 22 #include "chrome/common/chrome_switches.h" |
| 22 #include "content/browser/browser_thread.h" | 23 #include "content/browser/browser_thread.h" |
| 23 | 24 |
| 24 namespace browser_sync { | 25 namespace browser_sync { |
| 25 | 26 |
| 26 // The sync protocol identifies top-level entities by means of well-known tags, | 27 // The sync protocol identifies top-level entities by means of well-known tags, |
| (...skipping 10 matching lines...) Expand all Loading... |
| 37 // the sync server) to create these tagged nodes when initializing sync | 38 // the sync server) to create these tagged nodes when initializing sync |
| 38 // for the first time for a user. Thus, once the backend finishes | 39 // for the first time for a user. Thus, once the backend finishes |
| 39 // initializing, the ProfileSyncService can rely on the presence of tagged | 40 // initializing, the ProfileSyncService can rely on the presence of tagged |
| 40 // nodes. | 41 // nodes. |
| 41 // | 42 // |
| 42 // TODO(ncarter): Pull these tags from an external protocol specification | 43 // TODO(ncarter): Pull these tags from an external protocol specification |
| 43 // rather than hardcoding them here. | 44 // rather than hardcoding them here. |
| 44 static const char kBookmarkBarTag[] = "bookmark_bar"; | 45 static const char kBookmarkBarTag[] = "bookmark_bar"; |
| 45 static const char kSyncedBookmarksTag[] = "synced_bookmarks"; | 46 static const char kSyncedBookmarksTag[] = "synced_bookmarks"; |
| 46 static const char kOtherBookmarksTag[] = "other_bookmarks"; | 47 static const char kOtherBookmarksTag[] = "other_bookmarks"; |
| 48 static const char kServerError[] = |
| 49 "Server did not create top-level nodes. Possibly we are running against " |
| 50 "an out-of-date server?"; |
| 47 | 51 |
| 48 // Bookmark comparer for map of bookmark nodes. | 52 // Bookmark comparer for map of bookmark nodes. |
| 49 class BookmarkComparer { | 53 class BookmarkComparer { |
| 50 public: | 54 public: |
| 51 // Compares the two given nodes and returns whether node1 should appear | 55 // Compares the two given nodes and returns whether node1 should appear |
| 52 // before node2 in strict weak ordering. | 56 // before node2 in strict weak ordering. |
| 53 bool operator()(const BookmarkNode* node1, | 57 bool operator()(const BookmarkNode* node1, |
| 54 const BookmarkNode* node2) const { | 58 const BookmarkNode* node2) const { |
| 55 DCHECK(node1); | 59 DCHECK(node1); |
| 56 DCHECK(node2); | 60 DCHECK(node2); |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 172 DCHECK(bookmark_model_); | 176 DCHECK(bookmark_model_); |
| 173 DCHECK(user_share_); | 177 DCHECK(user_share_); |
| 174 DCHECK(unrecoverable_error_handler_); | 178 DCHECK(unrecoverable_error_handler_); |
| 175 } | 179 } |
| 176 | 180 |
| 177 BookmarkModelAssociator::~BookmarkModelAssociator() { | 181 BookmarkModelAssociator::~BookmarkModelAssociator() { |
| 178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 179 } | 183 } |
| 180 | 184 |
| 181 bool BookmarkModelAssociator::DisassociateModels() { | 185 bool BookmarkModelAssociator::DisassociateModels(SyncError* error) { |
| 182 id_map_.clear(); | 186 id_map_.clear(); |
| 183 id_map_inverse_.clear(); | 187 id_map_inverse_.clear(); |
| 184 dirty_associations_sync_ids_.clear(); | 188 dirty_associations_sync_ids_.clear(); |
| 185 return true; | 189 return true; |
| 186 } | 190 } |
| 187 | 191 |
| 188 int64 BookmarkModelAssociator::GetSyncIdFromChromeId(const int64& node_id) { | 192 int64 BookmarkModelAssociator::GetSyncIdFromChromeId(const int64& node_id) { |
| 189 BookmarkIdToSyncIdMap::const_iterator iter = id_map_.find(node_id); | 193 BookmarkIdToSyncIdMap::const_iterator iter = id_map_.find(node_id); |
| 190 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; | 194 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; |
| 191 } | 195 } |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 308 bool BookmarkModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, | 312 bool BookmarkModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, |
| 309 int64* sync_id) { | 313 int64* sync_id) { |
| 310 sync_api::ReadTransaction trans(FROM_HERE, user_share_); | 314 sync_api::ReadTransaction trans(FROM_HERE, user_share_); |
| 311 sync_api::ReadNode sync_node(&trans); | 315 sync_api::ReadNode sync_node(&trans); |
| 312 if (!sync_node.InitByTagLookup(tag.c_str())) | 316 if (!sync_node.InitByTagLookup(tag.c_str())) |
| 313 return false; | 317 return false; |
| 314 *sync_id = sync_node.GetId(); | 318 *sync_id = sync_node.GetId(); |
| 315 return true; | 319 return true; |
| 316 } | 320 } |
| 317 | 321 |
| 318 bool BookmarkModelAssociator::AssociateModels() { | 322 bool BookmarkModelAssociator::AssociateModels(SyncError* error) { |
| 319 // Try to load model associations from persisted associations first. If that | 323 // Try to load model associations from persisted associations first. If that |
| 320 // succeeds, we don't need to run the complex model matching algorithm. | 324 // succeeds, we don't need to run the complex model matching algorithm. |
| 321 if (LoadAssociations()) | 325 if (LoadAssociations()) |
| 322 return true; | 326 return true; |
| 323 | 327 |
| 324 DisassociateModels(); | 328 DisassociateModels(error); |
| 325 | 329 |
| 326 // We couldn't load model associations from persisted associations. So build | 330 // We couldn't load model associations from persisted associations. So build |
| 327 // them. | 331 // them. |
| 328 return BuildAssociations(); | 332 return BuildAssociations(error); |
| 329 } | 333 } |
| 330 | 334 |
| 331 bool BookmarkModelAssociator::BuildAssociations() { | 335 bool BookmarkModelAssociator::BuildAssociations(SyncError* error) { |
| 332 // Algorithm description: | 336 // Algorithm description: |
| 333 // Match up the roots and recursively do the following: | 337 // Match up the roots and recursively do the following: |
| 334 // * For each sync node for the current sync parent node, find the best | 338 // * For each sync node for the current sync parent node, find the best |
| 335 // matching bookmark node under the corresponding bookmark parent node. | 339 // matching bookmark node under the corresponding bookmark parent node. |
| 336 // If no matching node is found, create a new bookmark node in the same | 340 // If no matching node is found, create a new bookmark node in the same |
| 337 // position as the corresponding sync node. | 341 // position as the corresponding sync node. |
| 338 // If a matching node is found, update the properties of it from the | 342 // If a matching node is found, update the properties of it from the |
| 339 // corresponding sync node. | 343 // corresponding sync node. |
| 340 // * When all children sync nodes are done, add the extra children bookmark | 344 // * When all children sync nodes are done, add the extra children bookmark |
| 341 // nodes to the sync parent node. | 345 // nodes to the sync parent node. |
| 342 // | 346 // |
| 343 // This algorithm will do a good job of merging when folder names are a good | 347 // This algorithm will do a good job of merging when folder names are a good |
| 344 // indicator of the two folders being the same. It will handle reordering and | 348 // indicator of the two folders being the same. It will handle reordering and |
| 345 // new node addition very well (without creating duplicates). | 349 // new node addition very well (without creating duplicates). |
| 346 // This algorithm will not do well if the folder name has changes but the | 350 // This algorithm will not do well if the folder name has changes but the |
| 347 // children under them are all the same. | 351 // children under them are all the same. |
| 348 | 352 |
| 349 DCHECK(bookmark_model_->IsLoaded()); | 353 DCHECK(bookmark_model_->IsLoaded()); |
| 350 | 354 |
| 351 // To prime our association, we associate the top-level nodes, Bookmark Bar | 355 // To prime our association, we associate the top-level nodes, Bookmark Bar |
| 352 // and Other Bookmarks. | 356 // and Other Bookmarks. |
| 353 if (!AssociateTaggedPermanentNode(bookmark_model_->other_node(), | 357 if (!AssociateTaggedPermanentNode(bookmark_model_->other_node(), |
| 354 kOtherBookmarksTag)) { | 358 kOtherBookmarksTag)) { |
| 355 LOG(ERROR) << "Server did not create top-level nodes. Possibly we " | 359 error->Reset(FROM_HERE, kServerError, model_type()); |
| 356 << "are running against an out-of-date server?"; | |
| 357 return false; | 360 return false; |
| 358 } | 361 } |
| 359 if (!AssociateTaggedPermanentNode(bookmark_model_->bookmark_bar_node(), | 362 if (!AssociateTaggedPermanentNode(bookmark_model_->bookmark_bar_node(), |
| 360 kBookmarkBarTag)) { | 363 kBookmarkBarTag)) { |
| 361 LOG(ERROR) << "Server did not create top-level nodes. Possibly we " | 364 error->Reset(FROM_HERE, kServerError, model_type()); |
| 362 << "are running against an out-of-date server?"; | |
| 363 return false; | 365 return false; |
| 364 } | 366 } |
| 365 if (!AssociateTaggedPermanentNode(bookmark_model_->synced_node(), | 367 if (!AssociateTaggedPermanentNode(bookmark_model_->synced_node(), |
| 366 kSyncedBookmarksTag) && | 368 kSyncedBookmarksTag) && |
| 367 // We only need to ensure that the "synced bookmarks" folder exists on the | 369 // We only need to ensure that the "synced bookmarks" folder exists on the |
| 368 // server if the command line flag is set. | 370 // server if the command line flag is set. |
| 369 CommandLine::ForCurrentProcess()->HasSwitch( | 371 CommandLine::ForCurrentProcess()->HasSwitch( |
| 370 switches::kEnableSyncedBookmarksFolder)) { | 372 switches::kEnableSyncedBookmarksFolder)) { |
| 371 LOG(ERROR) << "Server did not create top-level synced nodes. Possibly " | 373 error->Reset(FROM_HERE, kServerError, model_type()); |
| 372 << "we are running against an out-of-date server?"; | |
| 373 return false; | 374 return false; |
| 374 } | 375 } |
| 375 int64 bookmark_bar_sync_id = GetSyncIdFromChromeId( | 376 int64 bookmark_bar_sync_id = GetSyncIdFromChromeId( |
| 376 bookmark_model_->bookmark_bar_node()->id()); | 377 bookmark_model_->bookmark_bar_node()->id()); |
| 377 DCHECK_NE(bookmark_bar_sync_id, sync_api::kInvalidId); | 378 DCHECK_NE(bookmark_bar_sync_id, sync_api::kInvalidId); |
| 378 int64 other_bookmarks_sync_id = GetSyncIdFromChromeId( | 379 int64 other_bookmarks_sync_id = GetSyncIdFromChromeId( |
| 379 bookmark_model_->other_node()->id()); | 380 bookmark_model_->other_node()->id()); |
| 380 DCHECK_NE(other_bookmarks_sync_id, sync_api::kInvalidId); | 381 DCHECK_NE(other_bookmarks_sync_id, sync_api::kInvalidId); |
| 381 int64 synced_bookmarks_sync_id = GetSyncIdFromChromeId( | 382 int64 synced_bookmarks_sync_id = GetSyncIdFromChromeId( |
| 382 bookmark_model_->synced_node()->id()); | 383 bookmark_model_->synced_node()->id()); |
| 383 if (CommandLine::ForCurrentProcess()->HasSwitch( | 384 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 384 switches::kEnableSyncedBookmarksFolder)) { | 385 switches::kEnableSyncedBookmarksFolder)) { |
| 385 DCHECK_NE(synced_bookmarks_sync_id, sync_api::kInvalidId); | 386 DCHECK_NE(synced_bookmarks_sync_id, sync_api::kInvalidId); |
| 386 } | 387 } |
| 387 | 388 |
| 388 std::stack<int64> dfs_stack; | 389 std::stack<int64> dfs_stack; |
| 389 if (synced_bookmarks_sync_id != sync_api::kInvalidId) | 390 if (synced_bookmarks_sync_id != sync_api::kInvalidId) |
| 390 dfs_stack.push(synced_bookmarks_sync_id); | 391 dfs_stack.push(synced_bookmarks_sync_id); |
| 391 dfs_stack.push(other_bookmarks_sync_id); | 392 dfs_stack.push(other_bookmarks_sync_id); |
| 392 dfs_stack.push(bookmark_bar_sync_id); | 393 dfs_stack.push(bookmark_bar_sync_id); |
| 393 | 394 |
| 394 sync_api::WriteTransaction trans(FROM_HERE, user_share_); | 395 sync_api::WriteTransaction trans(FROM_HERE, user_share_); |
| 395 | 396 |
| 396 while (!dfs_stack.empty()) { | 397 while (!dfs_stack.empty()) { |
| 397 int64 sync_parent_id = dfs_stack.top(); | 398 int64 sync_parent_id = dfs_stack.top(); |
| 398 dfs_stack.pop(); | 399 dfs_stack.pop(); |
| 399 | 400 |
| 400 sync_api::ReadNode sync_parent(&trans); | 401 sync_api::ReadNode sync_parent(&trans); |
| 401 if (!sync_parent.InitByIdLookup(sync_parent_id)) { | 402 if (!sync_parent.InitByIdLookup(sync_parent_id)) { |
| 403 error->Reset(FROM_HERE, "Failed to lookup node.", model_type()); |
| 402 return false; | 404 return false; |
| 403 } | 405 } |
| 404 // Only folder nodes are pushed on to the stack. | 406 // Only folder nodes are pushed on to the stack. |
| 405 DCHECK(sync_parent.GetIsFolder()); | 407 DCHECK(sync_parent.GetIsFolder()); |
| 406 | 408 |
| 407 const BookmarkNode* parent_node = GetChromeNodeFromSyncId(sync_parent_id); | 409 const BookmarkNode* parent_node = GetChromeNodeFromSyncId(sync_parent_id); |
| 408 DCHECK(parent_node->is_folder()); | 410 DCHECK(parent_node->is_folder()); |
| 409 | 411 |
| 410 BookmarkNodeFinder node_finder(parent_node); | 412 BookmarkNodeFinder node_finder(parent_node); |
| 411 | 413 |
| 412 int index = 0; | 414 int index = 0; |
| 413 int64 sync_child_id = sync_parent.GetFirstChildId(); | 415 int64 sync_child_id = sync_parent.GetFirstChildId(); |
| 414 while (sync_child_id != sync_api::kInvalidId) { | 416 while (sync_child_id != sync_api::kInvalidId) { |
| 415 sync_api::WriteNode sync_child_node(&trans); | 417 sync_api::WriteNode sync_child_node(&trans); |
| 416 if (!sync_child_node.InitByIdLookup(sync_child_id)) { | 418 if (!sync_child_node.InitByIdLookup(sync_child_id)) { |
| 419 error->Reset(FROM_HERE, "Failed to lookup node.", model_type()); |
| 417 return false; | 420 return false; |
| 418 } | 421 } |
| 419 | 422 |
| 420 const BookmarkNode* child_node = NULL; | 423 const BookmarkNode* child_node = NULL; |
| 421 child_node = node_finder.FindBookmarkNode(sync_child_node); | 424 child_node = node_finder.FindBookmarkNode(sync_child_node); |
| 422 if (child_node) { | 425 if (child_node) { |
| 423 bookmark_model_->Move(child_node, parent_node, index); | 426 bookmark_model_->Move(child_node, parent_node, index); |
| 424 // Set the favicon for bookmark node from sync node or vice versa. | 427 // Set the favicon for bookmark node from sync node or vice versa. |
| 425 if (BookmarkChangeProcessor::SetBookmarkFavicon( | 428 if (BookmarkChangeProcessor::SetBookmarkFavicon( |
| 426 &sync_child_node, child_node, bookmark_model_)) { | 429 &sync_child_node, child_node, bookmark_model_)) { |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 598 bool BookmarkModelAssociator::CryptoReadyIfNecessary() { | 601 bool BookmarkModelAssociator::CryptoReadyIfNecessary() { |
| 599 // We only access the cryptographer while holding a transaction. | 602 // We only access the cryptographer while holding a transaction. |
| 600 sync_api::ReadTransaction trans(FROM_HERE, user_share_); | 603 sync_api::ReadTransaction trans(FROM_HERE, user_share_); |
| 601 const syncable::ModelTypeSet& encrypted_types = | 604 const syncable::ModelTypeSet& encrypted_types = |
| 602 sync_api::GetEncryptedTypes(&trans); | 605 sync_api::GetEncryptedTypes(&trans); |
| 603 return encrypted_types.count(syncable::BOOKMARKS) == 0 || | 606 return encrypted_types.count(syncable::BOOKMARKS) == 0 || |
| 604 trans.GetCryptographer()->is_ready(); | 607 trans.GetCryptographer()->is_ready(); |
| 605 } | 608 } |
| 606 | 609 |
| 607 } // namespace browser_sync | 610 } // namespace browser_sync |
| OLD | NEW |