| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 206 | 206 |
| 207 void BookmarkModelAssociator::UpdatePermanentNodeVisibility() { | 207 void BookmarkModelAssociator::UpdatePermanentNodeVisibility() { |
| 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 209 DCHECK(bookmark_model_->IsLoaded()); | 209 DCHECK(bookmark_model_->IsLoaded()); |
| 210 | 210 |
| 211 bookmark_model_->SetPermanentNodeVisible( | 211 bookmark_model_->SetPermanentNodeVisible( |
| 212 BookmarkNode::MOBILE, | 212 BookmarkNode::MOBILE, |
| 213 id_map_.find(bookmark_model_->mobile_node()->id()) != id_map_.end()); | 213 id_map_.find(bookmark_model_->mobile_node()->id()) != id_map_.end()); |
| 214 } | 214 } |
| 215 | 215 |
| 216 bool BookmarkModelAssociator::DisassociateModels(SyncError* error) { | 216 SyncError BookmarkModelAssociator::DisassociateModels() { |
| 217 id_map_.clear(); | 217 id_map_.clear(); |
| 218 id_map_inverse_.clear(); | 218 id_map_inverse_.clear(); |
| 219 dirty_associations_sync_ids_.clear(); | 219 dirty_associations_sync_ids_.clear(); |
| 220 return true; | 220 return SyncError(); |
| 221 } | 221 } |
| 222 | 222 |
| 223 int64 BookmarkModelAssociator::GetSyncIdFromChromeId(const int64& node_id) { | 223 int64 BookmarkModelAssociator::GetSyncIdFromChromeId(const int64& node_id) { |
| 224 BookmarkIdToSyncIdMap::const_iterator iter = id_map_.find(node_id); | 224 BookmarkIdToSyncIdMap::const_iterator iter = id_map_.find(node_id); |
| 225 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; | 225 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; |
| 226 } | 226 } |
| 227 | 227 |
| 228 const BookmarkNode* BookmarkModelAssociator::GetChromeNodeFromSyncId( | 228 const BookmarkNode* BookmarkModelAssociator::GetChromeNodeFromSyncId( |
| 229 int64 sync_id) { | 229 int64 sync_id) { |
| 230 SyncIdToBookmarkNodeMap::const_iterator iter = id_map_inverse_.find(sync_id); | 230 SyncIdToBookmarkNodeMap::const_iterator iter = id_map_inverse_.find(sync_id); |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 if (bookmark->is_url()) { | 321 if (bookmark->is_url()) { |
| 322 if (bookmark->url() != sync_node->GetURL()) | 322 if (bookmark->url() != sync_node->GetURL()) |
| 323 return false; | 323 return false; |
| 324 } | 324 } |
| 325 // Don't compare favicons here, because they are not really | 325 // Don't compare favicons here, because they are not really |
| 326 // user-updated and we don't have versioning information -- a site changing | 326 // user-updated and we don't have versioning information -- a site changing |
| 327 // its favicon shouldn't result in a bookmark mismatch. | 327 // its favicon shouldn't result in a bookmark mismatch. |
| 328 return true; | 328 return true; |
| 329 } | 329 } |
| 330 | 330 |
| 331 bool BookmarkModelAssociator::AssociateTaggedPermanentNode( | 331 SyncError BookmarkModelAssociator::AssociateTaggedPermanentNode( |
| 332 const BookmarkNode* permanent_node, const std::string&tag) { | 332 const BookmarkNode* permanent_node, const std::string&tag) { |
| 333 // Do nothing if |permanent_node| is already initialized and associated. | 333 // Do nothing if |permanent_node| is already initialized and associated. |
| 334 int64 sync_id = GetSyncIdFromChromeId(permanent_node->id()); | 334 int64 sync_id = GetSyncIdFromChromeId(permanent_node->id()); |
| 335 if (sync_id != sync_api::kInvalidId) | 335 if (sync_id != sync_api::kInvalidId) |
| 336 return true; | 336 return SyncError(); |
| 337 if (!GetSyncIdForTaggedNode(tag, &sync_id)) | 337 if (!GetSyncIdForTaggedNode(tag, &sync_id)) |
| 338 return false; | 338 return unrecoverable_error_handler_->CreateAndUploadError( |
| 339 FROM_HERE, |
| 340 "Permanent node not found", |
| 341 model_type()); |
| 339 | 342 |
| 340 Associate(permanent_node, sync_id); | 343 Associate(permanent_node, sync_id); |
| 341 return true; | 344 return SyncError(); |
| 342 } | 345 } |
| 343 | 346 |
| 344 bool BookmarkModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, | 347 bool BookmarkModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, |
| 345 int64* sync_id) { | 348 int64* sync_id) { |
| 346 sync_api::ReadTransaction trans(FROM_HERE, user_share_); | 349 sync_api::ReadTransaction trans(FROM_HERE, user_share_); |
| 347 sync_api::ReadNode sync_node(&trans); | 350 sync_api::ReadNode sync_node(&trans); |
| 348 if (!sync_node.InitByTagLookup(tag.c_str())) | 351 if (!sync_node.InitByTagLookup(tag.c_str())) |
| 349 return false; | 352 return false; |
| 350 *sync_id = sync_node.GetId(); | 353 *sync_id = sync_node.GetId(); |
| 351 return true; | 354 return true; |
| 352 } | 355 } |
| 353 | 356 |
| 354 bool BookmarkModelAssociator::AssociateModels(SyncError* error) { | 357 SyncError BookmarkModelAssociator::AssociateModels() { |
| 355 scoped_ptr<ScopedAssociationUpdater> association_updater( | 358 scoped_ptr<ScopedAssociationUpdater> association_updater( |
| 356 new ScopedAssociationUpdater(bookmark_model_)); | 359 new ScopedAssociationUpdater(bookmark_model_)); |
| 357 // Try to load model associations from persisted associations first. If that | 360 // Try to load model associations from persisted associations first. If that |
| 358 // succeeds, we don't need to run the complex model matching algorithm. | 361 // succeeds, we don't need to run the complex model matching algorithm. |
| 359 if (LoadAssociations()) | 362 if (LoadAssociations()) |
| 360 return true; | 363 return SyncError(); |
| 361 | 364 |
| 362 DisassociateModels(error); | 365 DisassociateModels(); |
| 363 | 366 |
| 364 // We couldn't load model associations from persisted associations. So build | 367 // We couldn't load model associations from persisted associations. So build |
| 365 // them. | 368 // them. |
| 366 return BuildAssociations(error); | 369 return BuildAssociations(); |
| 367 } | 370 } |
| 368 | 371 |
| 369 bool BookmarkModelAssociator::BuildAssociations(SyncError* error) { | 372 SyncError BookmarkModelAssociator::BuildAssociations() { |
| 370 // Algorithm description: | 373 // Algorithm description: |
| 371 // Match up the roots and recursively do the following: | 374 // Match up the roots and recursively do the following: |
| 372 // * For each sync node for the current sync parent node, find the best | 375 // * For each sync node for the current sync parent node, find the best |
| 373 // matching bookmark node under the corresponding bookmark parent node. | 376 // matching bookmark node under the corresponding bookmark parent node. |
| 374 // If no matching node is found, create a new bookmark node in the same | 377 // If no matching node is found, create a new bookmark node in the same |
| 375 // position as the corresponding sync node. | 378 // position as the corresponding sync node. |
| 376 // If a matching node is found, update the properties of it from the | 379 // If a matching node is found, update the properties of it from the |
| 377 // corresponding sync node. | 380 // corresponding sync node. |
| 378 // * When all children sync nodes are done, add the extra children bookmark | 381 // * When all children sync nodes are done, add the extra children bookmark |
| 379 // nodes to the sync parent node. | 382 // nodes to the sync parent node. |
| 380 // | 383 // |
| 381 // This algorithm will do a good job of merging when folder names are a good | 384 // This algorithm will do a good job of merging when folder names are a good |
| 382 // indicator of the two folders being the same. It will handle reordering and | 385 // indicator of the two folders being the same. It will handle reordering and |
| 383 // new node addition very well (without creating duplicates). | 386 // new node addition very well (without creating duplicates). |
| 384 // This algorithm will not do well if the folder name has changes but the | 387 // This algorithm will not do well if the folder name has changes but the |
| 385 // children under them are all the same. | 388 // children under them are all the same. |
| 386 | 389 |
| 390 SyncError error; |
| 387 DCHECK(bookmark_model_->IsLoaded()); | 391 DCHECK(bookmark_model_->IsLoaded()); |
| 388 | 392 |
| 389 // To prime our association, we associate the top-level nodes, Bookmark Bar | 393 // To prime our association, we associate the top-level nodes, Bookmark Bar |
| 390 // and Other Bookmarks. | 394 // and Other Bookmarks. |
| 391 if (!AssociateTaggedPermanentNode(bookmark_model_->other_node(), | 395 error = AssociateTaggedPermanentNode(bookmark_model_->other_node(), |
| 392 kOtherBookmarksTag)) { | 396 kOtherBookmarksTag); |
| 393 error->Reset(FROM_HERE, kServerError, model_type()); | 397 if (error.IsSet()) { |
| 394 return false; | 398 return error; |
| 395 } | 399 } |
| 396 if (!AssociateTaggedPermanentNode(bookmark_model_->bookmark_bar_node(), | 400 |
| 397 kBookmarkBarTag)) { | 401 error = AssociateTaggedPermanentNode(bookmark_model_->bookmark_bar_node(), |
| 398 error->Reset(FROM_HERE, kServerError, model_type()); | 402 kBookmarkBarTag); |
| 399 return false; | 403 if (error.IsSet()) { |
| 404 return error; |
| 400 } | 405 } |
| 401 if (!AssociateTaggedPermanentNode(bookmark_model_->mobile_node(), | 406 |
| 402 kMobileBookmarksTag) && | 407 if (expect_mobile_bookmarks_folder_) { |
| 403 expect_mobile_bookmarks_folder_) { | 408 error = AssociateTaggedPermanentNode(bookmark_model_->mobile_node(), |
| 404 error->Reset(FROM_HERE, kServerError, model_type()); | 409 kMobileBookmarksTag); |
| 405 return false; | 410 if (error.IsSet()) { |
| 411 return error; |
| 412 } |
| 406 } | 413 } |
| 407 | 414 |
| 408 int64 bookmark_bar_sync_id = GetSyncIdFromChromeId( | 415 int64 bookmark_bar_sync_id = GetSyncIdFromChromeId( |
| 409 bookmark_model_->bookmark_bar_node()->id()); | 416 bookmark_model_->bookmark_bar_node()->id()); |
| 410 DCHECK_NE(bookmark_bar_sync_id, sync_api::kInvalidId); | 417 DCHECK_NE(bookmark_bar_sync_id, sync_api::kInvalidId); |
| 411 int64 other_bookmarks_sync_id = GetSyncIdFromChromeId( | 418 int64 other_bookmarks_sync_id = GetSyncIdFromChromeId( |
| 412 bookmark_model_->other_node()->id()); | 419 bookmark_model_->other_node()->id()); |
| 413 DCHECK_NE(other_bookmarks_sync_id, sync_api::kInvalidId); | 420 DCHECK_NE(other_bookmarks_sync_id, sync_api::kInvalidId); |
| 414 int64 mobile_bookmarks_sync_id = GetSyncIdFromChromeId( | 421 int64 mobile_bookmarks_sync_id = GetSyncIdFromChromeId( |
| 415 bookmark_model_->mobile_node()->id()); | 422 bookmark_model_->mobile_node()->id()); |
| 416 if (expect_mobile_bookmarks_folder_) { | 423 if (expect_mobile_bookmarks_folder_) { |
| 417 DCHECK_NE(sync_api::kInvalidId, mobile_bookmarks_sync_id); | 424 DCHECK_NE(sync_api::kInvalidId, mobile_bookmarks_sync_id); |
| 418 } | 425 } |
| 419 | 426 |
| 420 std::stack<int64> dfs_stack; | 427 std::stack<int64> dfs_stack; |
| 421 if (mobile_bookmarks_sync_id != sync_api::kInvalidId) | 428 if (mobile_bookmarks_sync_id != sync_api::kInvalidId) |
| 422 dfs_stack.push(mobile_bookmarks_sync_id); | 429 dfs_stack.push(mobile_bookmarks_sync_id); |
| 423 dfs_stack.push(other_bookmarks_sync_id); | 430 dfs_stack.push(other_bookmarks_sync_id); |
| 424 dfs_stack.push(bookmark_bar_sync_id); | 431 dfs_stack.push(bookmark_bar_sync_id); |
| 425 | 432 |
| 426 sync_api::WriteTransaction trans(FROM_HERE, user_share_); | 433 sync_api::WriteTransaction trans(FROM_HERE, user_share_); |
| 427 | 434 |
| 428 while (!dfs_stack.empty()) { | 435 while (!dfs_stack.empty()) { |
| 429 int64 sync_parent_id = dfs_stack.top(); | 436 int64 sync_parent_id = dfs_stack.top(); |
| 430 dfs_stack.pop(); | 437 dfs_stack.pop(); |
| 431 | 438 |
| 432 sync_api::ReadNode sync_parent(&trans); | 439 sync_api::ReadNode sync_parent(&trans); |
| 433 if (!sync_parent.InitByIdLookup(sync_parent_id)) { | 440 if (!sync_parent.InitByIdLookup(sync_parent_id)) { |
| 434 error->Reset(FROM_HERE, "Failed to lookup node.", model_type()); | 441 return unrecoverable_error_handler_->CreateAndUploadError( |
| 435 return false; | 442 FROM_HERE, |
| 443 "Failed to lookup node.", |
| 444 model_type()); |
| 436 } | 445 } |
| 437 // Only folder nodes are pushed on to the stack. | 446 // Only folder nodes are pushed on to the stack. |
| 438 DCHECK(sync_parent.GetIsFolder()); | 447 DCHECK(sync_parent.GetIsFolder()); |
| 439 | 448 |
| 440 const BookmarkNode* parent_node = GetChromeNodeFromSyncId(sync_parent_id); | 449 const BookmarkNode* parent_node = GetChromeNodeFromSyncId(sync_parent_id); |
| 441 DCHECK(parent_node->is_folder()); | 450 DCHECK(parent_node->is_folder()); |
| 442 | 451 |
| 443 BookmarkNodeFinder node_finder(parent_node); | 452 BookmarkNodeFinder node_finder(parent_node); |
| 444 | 453 |
| 445 int index = 0; | 454 int index = 0; |
| 446 int64 sync_child_id = sync_parent.GetFirstChildId(); | 455 int64 sync_child_id = sync_parent.GetFirstChildId(); |
| 447 while (sync_child_id != sync_api::kInvalidId) { | 456 while (sync_child_id != sync_api::kInvalidId) { |
| 448 sync_api::WriteNode sync_child_node(&trans); | 457 sync_api::WriteNode sync_child_node(&trans); |
| 449 if (!sync_child_node.InitByIdLookup(sync_child_id)) { | 458 if (!sync_child_node.InitByIdLookup(sync_child_id)) { |
| 450 error->Reset(FROM_HERE, "Failed to lookup node.", model_type()); | 459 return unrecoverable_error_handler_->CreateAndUploadError( |
| 451 return false; | 460 FROM_HERE, |
| 461 "Failed to lookup node.", |
| 462 model_type()); |
| 452 } | 463 } |
| 453 | 464 |
| 454 const BookmarkNode* child_node = NULL; | 465 const BookmarkNode* child_node = NULL; |
| 455 child_node = node_finder.FindBookmarkNode(sync_child_node); | 466 child_node = node_finder.FindBookmarkNode(sync_child_node); |
| 456 if (child_node) { | 467 if (child_node) { |
| 457 bookmark_model_->Move(child_node, parent_node, index); | 468 bookmark_model_->Move(child_node, parent_node, index); |
| 458 // Set the favicon for bookmark node from sync node or vice versa. | 469 // Set the favicon for bookmark node from sync node or vice versa. |
| 459 if (BookmarkChangeProcessor::SetBookmarkFavicon( | 470 if (BookmarkChangeProcessor::SetBookmarkFavicon( |
| 460 &sync_child_node, child_node, bookmark_model_)) { | 471 &sync_child_node, child_node, bookmark_model_)) { |
| 461 BookmarkChangeProcessor::SetSyncNodeFavicon( | 472 BookmarkChangeProcessor::SetSyncNodeFavicon( |
| (...skipping 25 matching lines...) Expand all Loading... |
| 487 // At this point all the children nodes of the parent sync node have | 498 // At this point all the children nodes of the parent sync node have |
| 488 // corresponding children in the parent bookmark node and they are all in | 499 // corresponding children in the parent bookmark node and they are all in |
| 489 // the right positions: from 0 to index - 1. | 500 // the right positions: from 0 to index - 1. |
| 490 // So the children starting from index in the parent bookmark node are the | 501 // So the children starting from index in the parent bookmark node are the |
| 491 // ones that are not present in the parent sync node. So create them. | 502 // ones that are not present in the parent sync node. So create them. |
| 492 for (int i = index; i < parent_node->child_count(); ++i) { | 503 for (int i = index; i < parent_node->child_count(); ++i) { |
| 493 sync_child_id = BookmarkChangeProcessor::CreateSyncNode( | 504 sync_child_id = BookmarkChangeProcessor::CreateSyncNode( |
| 494 parent_node, bookmark_model_, i, &trans, this, | 505 parent_node, bookmark_model_, i, &trans, this, |
| 495 unrecoverable_error_handler_); | 506 unrecoverable_error_handler_); |
| 496 if (sync_api::kInvalidId == sync_child_id) { | 507 if (sync_api::kInvalidId == sync_child_id) { |
| 497 error->Reset(FROM_HERE, "Failed to create sync node.", model_type()); | 508 return unrecoverable_error_handler_->CreateAndUploadError( |
| 498 return false; // Creation failed. | 509 FROM_HERE, |
| 510 "Failed to create sync node.", |
| 511 model_type()); |
| 499 } | 512 } |
| 500 if (parent_node->GetChild(i)->is_folder()) | 513 if (parent_node->GetChild(i)->is_folder()) |
| 501 dfs_stack.push(sync_child_id); | 514 dfs_stack.push(sync_child_id); |
| 502 number_of_new_sync_nodes_created_at_association_++; | 515 number_of_new_sync_nodes_created_at_association_++; |
| 503 } | 516 } |
| 504 } | 517 } |
| 505 | 518 |
| 506 return true; | 519 return SyncError(); |
| 507 } | 520 } |
| 508 | 521 |
| 509 void BookmarkModelAssociator::PostPersistAssociationsTask() { | 522 void BookmarkModelAssociator::PostPersistAssociationsTask() { |
| 510 // No need to post a task if a task is already pending. | 523 // No need to post a task if a task is already pending. |
| 511 if (weak_factory_.HasWeakPtrs()) | 524 if (weak_factory_.HasWeakPtrs()) |
| 512 return; | 525 return; |
| 513 MessageLoop::current()->PostTask( | 526 MessageLoop::current()->PostTask( |
| 514 FROM_HERE, | 527 FROM_HERE, |
| 515 base::Bind( | 528 base::Bind( |
| 516 &BookmarkModelAssociator::PersistAssociations, | 529 &BookmarkModelAssociator::PersistAssociations, |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 644 bool BookmarkModelAssociator::CryptoReadyIfNecessary() { | 657 bool BookmarkModelAssociator::CryptoReadyIfNecessary() { |
| 645 // We only access the cryptographer while holding a transaction. | 658 // We only access the cryptographer while holding a transaction. |
| 646 sync_api::ReadTransaction trans(FROM_HERE, user_share_); | 659 sync_api::ReadTransaction trans(FROM_HERE, user_share_); |
| 647 const syncable::ModelTypeSet encrypted_types = | 660 const syncable::ModelTypeSet encrypted_types = |
| 648 sync_api::GetEncryptedTypes(&trans); | 661 sync_api::GetEncryptedTypes(&trans); |
| 649 return !encrypted_types.Has(syncable::BOOKMARKS) || | 662 return !encrypted_types.Has(syncable::BOOKMARKS) || |
| 650 trans.GetCryptographer()->is_ready(); | 663 trans.GetCryptographer()->is_ready(); |
| 651 } | 664 } |
| 652 | 665 |
| 653 } // namespace browser_sync | 666 } // namespace browser_sync |
| OLD | NEW |