Chromium Code Reviews| 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 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 80 // Provides the following abstraction: given a parent bookmark node, find best | 80 // Provides the following abstraction: given a parent bookmark node, find best |
| 81 // matching child node for many sync nodes. | 81 // matching child node for many sync nodes. |
| 82 class BookmarkNodeFinder { | 82 class BookmarkNodeFinder { |
| 83 public: | 83 public: |
| 84 // Creates an instance with the given parent bookmark node. | 84 // Creates an instance with the given parent bookmark node. |
| 85 explicit BookmarkNodeFinder(const BookmarkNode* parent_node); | 85 explicit BookmarkNodeFinder(const BookmarkNode* parent_node); |
| 86 | 86 |
| 87 // Finds best matching node for the given sync node. | 87 // Finds best matching node for the given sync node. |
| 88 // Returns the matching node if one exists; NULL otherwise. If a matching | 88 // Returns the matching node if one exists; NULL otherwise. If a matching |
| 89 // node is found, it's removed for further matches. | 89 // node is found, it's removed for further matches. |
| 90 const BookmarkNode* FindBookmarkNode(const syncer::BaseNode& sync_node); | 90 const BookmarkNode* FindBookmarkNode(const GURL& url, |
| 91 const std::string& title, | |
| 92 bool is_folder); | |
| 91 | 93 |
| 92 private: | 94 private: |
| 93 typedef std::multiset<const BookmarkNode*, BookmarkComparer> BookmarkNodesSet; | 95 typedef std::multiset<const BookmarkNode*, BookmarkComparer> BookmarkNodesSet; |
| 94 | 96 |
| 95 const BookmarkNode* parent_node_; | 97 const BookmarkNode* parent_node_; |
| 96 BookmarkNodesSet child_nodes_; | 98 BookmarkNodesSet child_nodes_; |
| 97 | 99 |
| 98 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeFinder); | 100 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeFinder); |
| 99 }; | 101 }; |
| 100 | 102 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 116 }; | 118 }; |
| 117 | 119 |
| 118 BookmarkNodeFinder::BookmarkNodeFinder(const BookmarkNode* parent_node) | 120 BookmarkNodeFinder::BookmarkNodeFinder(const BookmarkNode* parent_node) |
| 119 : parent_node_(parent_node) { | 121 : parent_node_(parent_node) { |
| 120 for (int i = 0; i < parent_node_->child_count(); ++i) { | 122 for (int i = 0; i < parent_node_->child_count(); ++i) { |
| 121 child_nodes_.insert(parent_node_->GetChild(i)); | 123 child_nodes_.insert(parent_node_->GetChild(i)); |
| 122 } | 124 } |
| 123 } | 125 } |
| 124 | 126 |
| 125 const BookmarkNode* BookmarkNodeFinder::FindBookmarkNode( | 127 const BookmarkNode* BookmarkNodeFinder::FindBookmarkNode( |
| 126 const syncer::BaseNode& sync_node) { | 128 const GURL& url, const std::string& title, bool is_folder) { |
| 127 // Create a bookmark node from the given sync node. | 129 // Create a bookmark node from the given bookmark attributes. |
| 128 BookmarkNode temp_node(GURL(sync_node.GetBookmarkSpecifics().url())); | 130 BookmarkNode temp_node(url); |
| 129 temp_node.SetTitle(UTF8ToUTF16(sync_node.GetTitle())); | 131 temp_node.SetTitle(UTF8ToUTF16(title)); |
| 130 if (sync_node.GetIsFolder()) | 132 if (is_folder) |
| 131 temp_node.set_type(BookmarkNode::FOLDER); | 133 temp_node.set_type(BookmarkNode::FOLDER); |
| 132 else | 134 else |
| 133 temp_node.set_type(BookmarkNode::URL); | 135 temp_node.set_type(BookmarkNode::URL); |
| 134 | 136 |
| 135 const BookmarkNode* result = NULL; | 137 const BookmarkNode* result = NULL; |
| 136 BookmarkNodesSet::iterator iter = child_nodes_.find(&temp_node); | 138 BookmarkNodesSet::iterator iter = child_nodes_.find(&temp_node); |
| 137 if (iter != child_nodes_.end()) { | 139 if (iter != child_nodes_.end()) { |
| 138 result = *iter; | 140 result = *iter; |
| 139 // Remove the matched node so we don't match with it again. | 141 // Remove the matched node so we don't match with it again. |
| 140 child_nodes_.erase(iter); | 142 child_nodes_.erase(iter); |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 431 | 433 |
| 432 // WARNING: The order in which we push these should match their order in the | 434 // WARNING: The order in which we push these should match their order in the |
| 433 // bookmark model (see BookmarkModel::DoneLoading(..)). | 435 // bookmark model (see BookmarkModel::DoneLoading(..)). |
| 434 std::stack<int64> dfs_stack; | 436 std::stack<int64> dfs_stack; |
| 435 dfs_stack.push(bookmark_bar_sync_id); | 437 dfs_stack.push(bookmark_bar_sync_id); |
| 436 dfs_stack.push(other_bookmarks_sync_id); | 438 dfs_stack.push(other_bookmarks_sync_id); |
| 437 if (mobile_bookmarks_sync_id != syncer::kInvalidId) | 439 if (mobile_bookmarks_sync_id != syncer::kInvalidId) |
| 438 dfs_stack.push(mobile_bookmarks_sync_id); | 440 dfs_stack.push(mobile_bookmarks_sync_id); |
| 439 | 441 |
| 440 syncer::WriteTransaction trans(FROM_HERE, user_share_); | 442 syncer::WriteTransaction trans(FROM_HERE, user_share_); |
| 443 | |
| 444 ApplyDeletesFromSyncJournal(&trans); | |
| 445 | |
| 441 syncer::ReadNode bm_root(&trans); | 446 syncer::ReadNode bm_root(&trans); |
| 442 if (bm_root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::BOOKMARKS)) == | 447 if (bm_root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::BOOKMARKS)) == |
| 443 syncer::BaseNode::INIT_OK) { | 448 syncer::BaseNode::INIT_OK) { |
| 444 syncer_merge_result->set_num_items_before_association( | 449 syncer_merge_result->set_num_items_before_association( |
| 445 bm_root.GetTotalNodeCount()); | 450 bm_root.GetTotalNodeCount()); |
| 446 } | 451 } |
| 447 local_merge_result->set_num_items_before_association( | 452 local_merge_result->set_num_items_before_association( |
| 448 bookmark_model_->root_node()->GetTotalNodeCount()); | 453 bookmark_model_->root_node()->GetTotalNodeCount()); |
| 449 | 454 |
| 450 while (!dfs_stack.empty()) { | 455 while (!dfs_stack.empty()) { |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 473 syncer::WriteNode sync_child_node(&trans); | 478 syncer::WriteNode sync_child_node(&trans); |
| 474 if (sync_child_node.InitByIdLookup(sync_child_id) != | 479 if (sync_child_node.InitByIdLookup(sync_child_id) != |
| 475 syncer::BaseNode::INIT_OK) { | 480 syncer::BaseNode::INIT_OK) { |
| 476 return unrecoverable_error_handler_->CreateAndUploadError( | 481 return unrecoverable_error_handler_->CreateAndUploadError( |
| 477 FROM_HERE, | 482 FROM_HERE, |
| 478 "Failed to lookup node.", | 483 "Failed to lookup node.", |
| 479 model_type()); | 484 model_type()); |
| 480 } | 485 } |
| 481 | 486 |
| 482 const BookmarkNode* child_node = NULL; | 487 const BookmarkNode* child_node = NULL; |
| 483 child_node = node_finder.FindBookmarkNode(sync_child_node); | 488 child_node = node_finder.FindBookmarkNode( |
| 489 GURL(sync_child_node.GetBookmarkSpecifics().url()), | |
| 490 sync_child_node.GetTitle(), | |
| 491 sync_child_node.GetIsFolder()); | |
| 484 if (child_node) | 492 if (child_node) |
| 485 Associate(child_node, sync_child_id); | 493 Associate(child_node, sync_child_id); |
| 486 // All bookmarks are currently modified at association time (even if | 494 // All bookmarks are currently modified at association time (even if |
| 487 // it doesn't change anything). | 495 // it doesn't change anything). |
| 488 // TODO(sync): introduce logic to only modify the bookmark model if | 496 // TODO(sync): introduce logic to only modify the bookmark model if |
| 489 // necessary. | 497 // necessary. |
| 490 const BookmarkNode* new_child_node = | 498 const BookmarkNode* new_child_node = |
| 491 BookmarkChangeProcessor::CreateOrUpdateBookmarkNode( | 499 BookmarkChangeProcessor::CreateOrUpdateBookmarkNode( |
| 492 &sync_child_node, | 500 &sync_child_node, |
| 493 bookmark_model_, | 501 bookmark_model_, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 529 } | 537 } |
| 530 | 538 |
| 531 local_merge_result->set_num_items_after_association( | 539 local_merge_result->set_num_items_after_association( |
| 532 bookmark_model_->root_node()->GetTotalNodeCount()); | 540 bookmark_model_->root_node()->GetTotalNodeCount()); |
| 533 syncer_merge_result->set_num_items_after_association( | 541 syncer_merge_result->set_num_items_after_association( |
| 534 bm_root.GetTotalNodeCount()); | 542 bm_root.GetTotalNodeCount()); |
| 535 | 543 |
| 536 return syncer::SyncError(); | 544 return syncer::SyncError(); |
| 537 } | 545 } |
| 538 | 546 |
| 547 struct FolderInfo { | |
| 548 FolderInfo(const BookmarkNode* f, const BookmarkNode* p, int64 id) | |
| 549 : folder(f), parent(p), sync_id(id) {} | |
| 550 const BookmarkNode* folder; | |
| 551 const BookmarkNode* parent; | |
| 552 int64 sync_id; | |
| 553 }; | |
| 554 typedef std::vector<FolderInfo> FolderInfoList; | |
| 555 | |
| 556 int64 BookmarkModelAssociator::ApplyDeletesFromSyncJournal( | |
| 557 syncer::BaseTransaction* trans) { | |
| 558 int64 num_deleted = 0; | |
| 559 | |
| 560 syncer::SyncDataList deleted_sync_bookmarks; | |
| 561 trans->GetDeletedSyncData(syncer::BOOKMARKS, &deleted_sync_bookmarks); | |
|
tim (not reviewing)
2012/12/11 19:58:33
How can we make it so that this data is *sent* to
| |
| 562 if (deleted_sync_bookmarks.empty()) | |
| 563 return 0; | |
| 564 | |
| 565 std::stack<const BookmarkNode*> dfs_stack; | |
| 566 dfs_stack.push(bookmark_model_->bookmark_bar_node()); | |
| 567 dfs_stack.push(bookmark_model_->other_node()); | |
| 568 if (expect_mobile_bookmarks_folder_) | |
| 569 dfs_stack.push(bookmark_model_->mobile_node()); | |
| 570 | |
| 571 FolderInfoList folders_deleted; | |
| 572 while (!dfs_stack.empty()) { | |
| 573 const BookmarkNode* parent = dfs_stack.top(); | |
| 574 dfs_stack.pop(); | |
| 575 | |
| 576 BookmarkNodeFinder finder(parent); | |
| 577 syncer::SyncDataList::iterator it = deleted_sync_bookmarks.begin(); | |
| 578 while (it != deleted_sync_bookmarks.end()) { | |
| 579 const BookmarkNode* child = finder.FindBookmarkNode( | |
| 580 GURL(it->GetSpecifics().bookmark().url()), it->GetTitle(), | |
| 581 it->IsFolder()); | |
| 582 if (child) { | |
| 583 if (child->is_folder()) { | |
| 584 // Save folders first and delete only empty ones later. | |
| 585 folders_deleted.push_back(FolderInfo(child, parent, | |
| 586 it->GetRemoteId())); | |
| 587 } else { | |
| 588 bookmark_model_->Remove(parent, child->GetIndexOf(parent)); | |
| 589 ++num_deleted; | |
| 590 } | |
| 591 deleted_sync_bookmarks.erase(it++); | |
| 592 } else { | |
| 593 ++it; | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 if (deleted_sync_bookmarks.empty()) | |
| 598 break; | |
| 599 } | |
| 600 | |
| 601 // Ids of sync nodes not found in bookmark model, i.e. deletion is persisted. | |
| 602 std::set<int64> confirmed_sync_deletes; | |
| 603 | |
| 604 // Remove empty folders from bottom to top. | |
| 605 for (FolderInfoList::const_reverse_iterator it = folders_deleted.rbegin(); | |
| 606 it != folders_deleted.rend(); ++it) { | |
| 607 if (it->folder->child_count() == 0) { | |
| 608 bookmark_model_->Remove(it->parent, it->folder->GetIndexOf(it->parent)); | |
| 609 ++num_deleted; | |
| 610 } else { | |
| 611 // Keep non-empty folder and remove its journal so that it won't match | |
| 612 // again in the future. | |
| 613 confirmed_sync_deletes.insert(it->sync_id); | |
| 614 } | |
| 615 } | |
| 616 | |
| 617 // Consider deletion of unmatched sync nodes to be confirmed. | |
| 618 for (size_t i = 0; i < deleted_sync_bookmarks.size(); ++i) | |
| 619 confirmed_sync_deletes.insert(deleted_sync_bookmarks[i].GetRemoteId()); | |
| 620 trans->PurgeDeletedSyncData(confirmed_sync_deletes); | |
| 621 | |
| 622 return num_deleted; | |
| 623 } | |
| 624 | |
| 539 void BookmarkModelAssociator::PostPersistAssociationsTask() { | 625 void BookmarkModelAssociator::PostPersistAssociationsTask() { |
| 540 // No need to post a task if a task is already pending. | 626 // No need to post a task if a task is already pending. |
| 541 if (weak_factory_.HasWeakPtrs()) | 627 if (weak_factory_.HasWeakPtrs()) |
| 542 return; | 628 return; |
| 543 MessageLoop::current()->PostTask( | 629 MessageLoop::current()->PostTask( |
| 544 FROM_HERE, | 630 FROM_HERE, |
| 545 base::Bind( | 631 base::Bind( |
| 546 &BookmarkModelAssociator::PersistAssociations, | 632 &BookmarkModelAssociator::PersistAssociations, |
| 547 weak_factory_.GetWeakPtr())); | 633 weak_factory_.GetWeakPtr())); |
| 548 } | 634 } |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 606 UMA_HISTOGRAM_ENUMERATION("Sync.LocalModelOutOfSync", | 692 UMA_HISTOGRAM_ENUMERATION("Sync.LocalModelOutOfSync", |
| 607 syncer::BOOKMARKS, syncer::MODEL_TYPE_COUNT); | 693 syncer::BOOKMARKS, syncer::MODEL_TYPE_COUNT); |
| 608 // Clear version on bookmark model so that we only report error once. | 694 // Clear version on bookmark model so that we only report error once. |
| 609 bookmark_model_->DeleteNodeMetaInfo(bookmark_model_->root_node(), | 695 bookmark_model_->DeleteNodeMetaInfo(bookmark_model_->root_node(), |
| 610 kBookmarkTransactionVersionKey); | 696 kBookmarkTransactionVersionKey); |
| 611 } | 697 } |
| 612 } | 698 } |
| 613 } | 699 } |
| 614 | 700 |
| 615 } // namespace browser_sync | 701 } // namespace browser_sync |
| OLD | NEW |