| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 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 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 const int kTitleLimitBytes = 255; | 69 const int kTitleLimitBytes = 255; |
| 70 | 70 |
| 71 // Provides the following abstraction: given a parent bookmark node, find best | 71 // Provides the following abstraction: given a parent bookmark node, find best |
| 72 // matching child node for many sync nodes. | 72 // matching child node for many sync nodes. |
| 73 class BookmarkNodeFinder { | 73 class BookmarkNodeFinder { |
| 74 public: | 74 public: |
| 75 // Creates an instance with the given parent bookmark node. | 75 // Creates an instance with the given parent bookmark node. |
| 76 explicit BookmarkNodeFinder(const BookmarkNode* parent_node); | 76 explicit BookmarkNodeFinder(const BookmarkNode* parent_node); |
| 77 | 77 |
| 78 // Finds the bookmark node that matches the given url, title and folder | 78 // Finds the bookmark node that matches the given url, title and folder |
| 79 // attribute. Returns the matching node if one exists; NULL otherwise. | 79 // attribute. Returns the matching node if one exists; NULL otherwise. If a |
| 80 // If there are multiple matches then a node with ID matching |preferred_id| | 80 // matching node is found, it's removed for further matches. |
| 81 // is returned; otherwise the first matching node is returned. | |
| 82 // If a matching node is found, it's removed for further matches. | |
| 83 const BookmarkNode* FindBookmarkNode(const GURL& url, | 81 const BookmarkNode* FindBookmarkNode(const GURL& url, |
| 84 const std::string& title, | 82 const std::string& title, |
| 85 bool is_folder, | 83 bool is_folder); |
| 86 int64 preferred_id); | |
| 87 | |
| 88 // Returns true if |bookmark_node| matches the specified |url|, | |
| 89 // |title|, and |is_folder| flags. | |
| 90 static bool NodeMatches(const BookmarkNode* bookmark_node, | |
| 91 const GURL& url, | |
| 92 const std::string& title, | |
| 93 bool is_folder); | |
| 94 | 84 |
| 95 private: | 85 private: |
| 96 // Maps bookmark node titles to instances, duplicates allowed. | 86 // Maps bookmark node titles to instances, duplicates allowed. |
| 97 // Titles are converted to the sync internal format before | 87 // Titles are converted to the sync internal format before |
| 98 // being used as keys for the map. | 88 // being used as keys for the map. |
| 99 typedef base::hash_multimap<std::string, | 89 typedef base::hash_multimap<std::string, |
| 100 const BookmarkNode*> BookmarkNodeMap; | 90 const BookmarkNode*> BookmarkNodeMap; |
| 101 typedef std::pair<BookmarkNodeMap::iterator, | 91 typedef std::pair<BookmarkNodeMap::iterator, |
| 102 BookmarkNodeMap::iterator> BookmarkNodeRange; | 92 BookmarkNodeMap::iterator> BookmarkNodeRange; |
| 103 | 93 |
| 104 // Converts and truncates bookmark titles in the form sync does internally | 94 // Converts and truncates bookmark titles in the form sync does internally |
| 105 // to avoid mismatches due to sync munging titles. | 95 // to avoid mismatches due to sync munging titles. |
| 106 static void ConvertTitleToSyncInternalFormat(const std::string& input, | 96 void ConvertTitleToSyncInternalFormat( |
| 107 std::string* output); | 97 const std::string& input, std::string* output); |
| 108 | 98 |
| 109 const BookmarkNode* parent_node_; | 99 const BookmarkNode* parent_node_; |
| 110 BookmarkNodeMap child_nodes_; | 100 BookmarkNodeMap child_nodes_; |
| 111 | 101 |
| 112 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeFinder); | 102 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeFinder); |
| 113 }; | 103 }; |
| 114 | 104 |
| 115 class ScopedAssociationUpdater { | 105 class ScopedAssociationUpdater { |
| 116 public: | 106 public: |
| 117 explicit ScopedAssociationUpdater(BookmarkModel* model) { | 107 explicit ScopedAssociationUpdater(BookmarkModel* model) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 135 const BookmarkNode* child_node = parent_node_->GetChild(i); | 125 const BookmarkNode* child_node = parent_node_->GetChild(i); |
| 136 | 126 |
| 137 std::string title = base::UTF16ToUTF8(child_node->GetTitle()); | 127 std::string title = base::UTF16ToUTF8(child_node->GetTitle()); |
| 138 ConvertTitleToSyncInternalFormat(title, &title); | 128 ConvertTitleToSyncInternalFormat(title, &title); |
| 139 | 129 |
| 140 child_nodes_.insert(std::make_pair(title, child_node)); | 130 child_nodes_.insert(std::make_pair(title, child_node)); |
| 141 } | 131 } |
| 142 } | 132 } |
| 143 | 133 |
| 144 const BookmarkNode* BookmarkNodeFinder::FindBookmarkNode( | 134 const BookmarkNode* BookmarkNodeFinder::FindBookmarkNode( |
| 145 const GURL& url, | 135 const GURL& url, const std::string& title, bool is_folder) { |
| 146 const std::string& title, | |
| 147 bool is_folder, | |
| 148 int64 preferred_id) { | |
| 149 const BookmarkNode* match = nullptr; | |
| 150 | |
| 151 // First lookup a range of bookmarks with the same title. | 136 // First lookup a range of bookmarks with the same title. |
| 152 std::string adjusted_title; | 137 std::string adjusted_title; |
| 153 ConvertTitleToSyncInternalFormat(title, &adjusted_title); | 138 ConvertTitleToSyncInternalFormat(title, &adjusted_title); |
| 154 BookmarkNodeRange range = child_nodes_.equal_range(adjusted_title); | 139 BookmarkNodeRange range = child_nodes_.equal_range(adjusted_title); |
| 155 BookmarkNodeMap::iterator match_iter = range.second; | |
| 156 for (BookmarkNodeMap::iterator iter = range.first; | 140 for (BookmarkNodeMap::iterator iter = range.first; |
| 157 iter != range.second; | 141 iter != range.second; |
| 158 ++iter) { | 142 ++iter) { |
| 143 |
| 159 // Then within the range match the node by the folder bit | 144 // Then within the range match the node by the folder bit |
| 160 // and the url. | 145 // and the url. |
| 161 const BookmarkNode* node = iter->second; | 146 const BookmarkNode* node = iter->second; |
| 162 if (is_folder == node->is_folder() && url == node->url()) { | 147 if (is_folder == node->is_folder() && url == node->url()) { |
| 163 if (node->id() == preferred_id || preferred_id == 0) { | 148 // Remove the matched node so we don't match with it again. |
| 164 // Preferred match - use this node. | 149 child_nodes_.erase(iter); |
| 165 match = node; | 150 return node; |
| 166 match_iter = iter; | |
| 167 break; | |
| 168 } else if (match == nullptr) { | |
| 169 // First match - continue iterating. | |
| 170 match = node; | |
| 171 match_iter = iter; | |
| 172 } | |
| 173 } | 151 } |
| 174 } | 152 } |
| 175 | 153 |
| 176 if (match_iter != range.second) { | 154 return NULL; |
| 177 // Remove the matched node so we don't match with it again. | |
| 178 child_nodes_.erase(match_iter); | |
| 179 } | |
| 180 | |
| 181 return match; | |
| 182 } | 155 } |
| 183 | 156 |
| 184 /* static */ | |
| 185 bool BookmarkNodeFinder::NodeMatches(const BookmarkNode* bookmark_node, | |
| 186 const GURL& url, | |
| 187 const std::string& title, | |
| 188 bool is_folder) { | |
| 189 if (url != bookmark_node->url() || is_folder != bookmark_node->is_folder()) { | |
| 190 return false; | |
| 191 } | |
| 192 | |
| 193 // The title passed to this method comes from a sync directory entry. | |
| 194 // The following two lines are needed to make the native bookmark title | |
| 195 // comparable. The same conversion is used in BookmarkNodeFinder constructor. | |
| 196 std::string bookmark_title = base::UTF16ToUTF8(bookmark_node->GetTitle()); | |
| 197 ConvertTitleToSyncInternalFormat(bookmark_title, &bookmark_title); | |
| 198 return title == bookmark_title; | |
| 199 } | |
| 200 | |
| 201 /* static */ | |
| 202 void BookmarkNodeFinder::ConvertTitleToSyncInternalFormat( | 157 void BookmarkNodeFinder::ConvertTitleToSyncInternalFormat( |
| 203 const std::string& input, std::string* output) { | 158 const std::string& input, std::string* output) { |
| 204 syncer::SyncAPINameToServerName(input, output); | 159 syncer::SyncAPINameToServerName(input, output); |
| 205 base::TruncateUTF8ToByteSize(*output, kTitleLimitBytes, output); | 160 base::TruncateUTF8ToByteSize(*output, kTitleLimitBytes, output); |
| 206 } | 161 } |
| 207 | 162 |
| 208 // Helper class to build an index of bookmark nodes by their IDs. | 163 // Helper class to build an index of bookmark nodes by their IDs. |
| 209 class BookmarkNodeIdIndex { | 164 class BookmarkNodeIdIndex { |
| 210 public: | 165 public: |
| 211 BookmarkNodeIdIndex() { } | 166 BookmarkNodeIdIndex() { } |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 384 } | 339 } |
| 385 | 340 |
| 386 // Sync model has user created nodes if any of the permanent nodes has | 341 // Sync model has user created nodes if any of the permanent nodes has |
| 387 // children. | 342 // children. |
| 388 *has_nodes = bookmark_bar_node.HasChildren() || | 343 *has_nodes = bookmark_bar_node.HasChildren() || |
| 389 other_bookmarks_node.HasChildren() || | 344 other_bookmarks_node.HasChildren() || |
| 390 (has_mobile_folder && mobile_bookmarks_node.HasChildren()); | 345 (has_mobile_folder && mobile_bookmarks_node.HasChildren()); |
| 391 return true; | 346 return true; |
| 392 } | 347 } |
| 393 | 348 |
| 349 bool BookmarkModelAssociator::NodesMatch( |
| 350 const BookmarkNode* bookmark, |
| 351 const syncer::BaseNode* sync_node) const { |
| 352 std::string truncated_title = base::UTF16ToUTF8(bookmark->GetTitle()); |
| 353 base::TruncateUTF8ToByteSize(truncated_title, |
| 354 kTitleLimitBytes, |
| 355 &truncated_title); |
| 356 if (truncated_title != sync_node->GetTitle()) |
| 357 return false; |
| 358 if (bookmark->is_folder() != sync_node->GetIsFolder()) |
| 359 return false; |
| 360 if (bookmark->is_url()) { |
| 361 if (bookmark->url() != GURL(sync_node->GetBookmarkSpecifics().url())) |
| 362 return false; |
| 363 } |
| 364 // Don't compare favicons here, because they are not really |
| 365 // user-updated and we don't have versioning information -- a site changing |
| 366 // its favicon shouldn't result in a bookmark mismatch. |
| 367 return true; |
| 368 } |
| 369 |
| 394 bool BookmarkModelAssociator::AssociateTaggedPermanentNode( | 370 bool BookmarkModelAssociator::AssociateTaggedPermanentNode( |
| 395 const BookmarkNode* permanent_node, const std::string&tag) { | 371 const BookmarkNode* permanent_node, const std::string&tag) { |
| 396 // Do nothing if |permanent_node| is already initialized and associated. | 372 // Do nothing if |permanent_node| is already initialized and associated. |
| 397 int64 sync_id = GetSyncIdFromChromeId(permanent_node->id()); | 373 int64 sync_id = GetSyncIdFromChromeId(permanent_node->id()); |
| 398 if (sync_id != syncer::kInvalidId) | 374 if (sync_id != syncer::kInvalidId) |
| 399 return true; | 375 return true; |
| 400 if (!GetSyncIdForTaggedNode(tag, &sync_id)) | 376 if (!GetSyncIdForTaggedNode(tag, &sync_id)) |
| 401 return false; | 377 return false; |
| 402 | 378 |
| 403 Associate(permanent_node, sync_id); | 379 Associate(permanent_node, sync_id); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 441 // Match up the roots and recursively do the following: | 417 // Match up the roots and recursively do the following: |
| 442 // * For each sync node for the current sync parent node, find the best | 418 // * For each sync node for the current sync parent node, find the best |
| 443 // matching bookmark node under the corresponding bookmark parent node. | 419 // matching bookmark node under the corresponding bookmark parent node. |
| 444 // If no matching node is found, create a new bookmark node in the same | 420 // If no matching node is found, create a new bookmark node in the same |
| 445 // position as the corresponding sync node. | 421 // position as the corresponding sync node. |
| 446 // If a matching node is found, update the properties of it from the | 422 // If a matching node is found, update the properties of it from the |
| 447 // corresponding sync node. | 423 // corresponding sync node. |
| 448 // * When all children sync nodes are done, add the extra children bookmark | 424 // * When all children sync nodes are done, add the extra children bookmark |
| 449 // nodes to the sync parent node. | 425 // nodes to the sync parent node. |
| 450 // | 426 // |
| 451 // The best match algorithm uses folder title or bookmark title/url to | 427 // This algorithm will do a good job of merging when folder names are a good |
| 452 // perform the primary match. If there are multiple match candidates it | 428 // indicator of the two folders being the same. It will handle reordering and |
| 453 // selects the preferred one based on sync node external ID match to the | 429 // new node addition very well (without creating duplicates). |
| 454 // bookmark folder ID. | 430 // This algorithm will not do well if the folder name has changes but the |
| 431 // children under them are all the same. |
| 455 | 432 |
| 456 DCHECK(bookmark_model_->loaded()); | 433 DCHECK(bookmark_model_->loaded()); |
| 457 | 434 |
| 458 // To prime our association, we associate the top-level nodes, Bookmark Bar | 435 // To prime our association, we associate the top-level nodes, Bookmark Bar |
| 459 // and Other Bookmarks. | 436 // and Other Bookmarks. |
| 460 if (!AssociateTaggedPermanentNode(bookmark_model_->bookmark_bar_node(), | 437 if (!AssociateTaggedPermanentNode(bookmark_model_->bookmark_bar_node(), |
| 461 kBookmarkBarTag)) { | 438 kBookmarkBarTag)) { |
| 462 return unrecoverable_error_handler_->CreateAndUploadError( | 439 return unrecoverable_error_handler_->CreateAndUploadError( |
| 463 FROM_HERE, | 440 FROM_HERE, |
| 464 "Bookmark bar node not found", | 441 "Bookmark bar node not found", |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 508 syncer::WriteTransaction trans(FROM_HERE, user_share_); | 485 syncer::WriteTransaction trans(FROM_HERE, user_share_); |
| 509 syncer::ReadNode bm_root(&trans); | 486 syncer::ReadNode bm_root(&trans); |
| 510 if (bm_root.InitTypeRoot(syncer::BOOKMARKS) == syncer::BaseNode::INIT_OK) { | 487 if (bm_root.InitTypeRoot(syncer::BOOKMARKS) == syncer::BaseNode::INIT_OK) { |
| 511 syncer_merge_result->set_num_items_before_association( | 488 syncer_merge_result->set_num_items_before_association( |
| 512 bm_root.GetTotalNodeCount()); | 489 bm_root.GetTotalNodeCount()); |
| 513 } | 490 } |
| 514 local_merge_result->set_num_items_before_association( | 491 local_merge_result->set_num_items_before_association( |
| 515 bookmark_model_->root_node()->GetTotalNodeCount()); | 492 bookmark_model_->root_node()->GetTotalNodeCount()); |
| 516 | 493 |
| 517 // Remove obsolete bookmarks according to sync delete journal. | 494 // Remove obsolete bookmarks according to sync delete journal. |
| 518 // TODO (stanisc): crbug.com/456876: rewrite this to avoid a separate | |
| 519 // traversal and instead perform deletes at the end of the loop below where | |
| 520 // the unmatched bookmark nodes are created as sync nodes. | |
| 521 local_merge_result->set_num_items_deleted( | 495 local_merge_result->set_num_items_deleted( |
| 522 ApplyDeletesFromSyncJournal(&trans)); | 496 ApplyDeletesFromSyncJournal(&trans)); |
| 523 | 497 |
| 524 while (!dfs_stack.empty()) { | 498 while (!dfs_stack.empty()) { |
| 525 int64 sync_parent_id = dfs_stack.top(); | 499 int64 sync_parent_id = dfs_stack.top(); |
| 526 dfs_stack.pop(); | 500 dfs_stack.pop(); |
| 527 | 501 |
| 528 syncer::ReadNode sync_parent(&trans); | 502 syncer::ReadNode sync_parent(&trans); |
| 529 if (sync_parent.InitByIdLookup(sync_parent_id) != | 503 if (sync_parent.InitByIdLookup(sync_parent_id) != |
| 530 syncer::BaseNode::INIT_OK) { | 504 syncer::BaseNode::INIT_OK) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 553 int64 sync_child_id = *it; | 527 int64 sync_child_id = *it; |
| 554 syncer::ReadNode sync_child_node(&trans); | 528 syncer::ReadNode sync_child_node(&trans); |
| 555 if (sync_child_node.InitByIdLookup(sync_child_id) != | 529 if (sync_child_node.InitByIdLookup(sync_child_id) != |
| 556 syncer::BaseNode::INIT_OK) { | 530 syncer::BaseNode::INIT_OK) { |
| 557 return unrecoverable_error_handler_->CreateAndUploadError( | 531 return unrecoverable_error_handler_->CreateAndUploadError( |
| 558 FROM_HERE, | 532 FROM_HERE, |
| 559 "Failed to lookup node.", | 533 "Failed to lookup node.", |
| 560 model_type()); | 534 model_type()); |
| 561 } | 535 } |
| 562 | 536 |
| 563 const BookmarkNode* child_node = node_finder.FindBookmarkNode( | 537 const BookmarkNode* child_node = NULL; |
| 538 child_node = node_finder.FindBookmarkNode( |
| 564 GURL(sync_child_node.GetBookmarkSpecifics().url()), | 539 GURL(sync_child_node.GetBookmarkSpecifics().url()), |
| 565 sync_child_node.GetTitle(), sync_child_node.GetIsFolder(), | 540 sync_child_node.GetTitle(), |
| 566 sync_child_node.GetExternalId()); | 541 sync_child_node.GetIsFolder()); |
| 567 if (child_node) { | 542 if (child_node) { |
| 568 Associate(child_node, sync_child_id); | 543 Associate(child_node, sync_child_id); |
| 569 | 544 |
| 570 // All bookmarks are currently modified at association time, even if | 545 // All bookmarks are currently modified at association time, even if |
| 571 // nothing has changed. | 546 // nothing has changed. |
| 572 // TODO(sync): Only modify the bookmark model if necessary. | 547 // TODO(sync): Only modify the bookmark model if necessary. |
| 573 BookmarkChangeProcessor::UpdateBookmarkWithSyncData( | 548 BookmarkChangeProcessor::UpdateBookmarkWithSyncData( |
| 574 sync_child_node, bookmark_model_, child_node, profile_); | 549 sync_child_node, bookmark_model_, child_node, profile_); |
| 575 bookmark_model_->Move(child_node, parent_node, index); | 550 bookmark_model_->Move(child_node, parent_node, index); |
| 576 local_merge_result->set_num_items_modified( | 551 local_merge_result->set_num_items_modified( |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 634 int64 BookmarkModelAssociator::ApplyDeletesFromSyncJournal( | 609 int64 BookmarkModelAssociator::ApplyDeletesFromSyncJournal( |
| 635 syncer::BaseTransaction* trans) { | 610 syncer::BaseTransaction* trans) { |
| 636 int64 num_bookmark_deleted = 0; | 611 int64 num_bookmark_deleted = 0; |
| 637 | 612 |
| 638 syncer::BookmarkDeleteJournalList bk_delete_journals; | 613 syncer::BookmarkDeleteJournalList bk_delete_journals; |
| 639 syncer::DeleteJournal::GetBookmarkDeleteJournals(trans, &bk_delete_journals); | 614 syncer::DeleteJournal::GetBookmarkDeleteJournals(trans, &bk_delete_journals); |
| 640 if (bk_delete_journals.empty()) | 615 if (bk_delete_journals.empty()) |
| 641 return 0; | 616 return 0; |
| 642 size_t num_journals_unmatched = bk_delete_journals.size(); | 617 size_t num_journals_unmatched = bk_delete_journals.size(); |
| 643 | 618 |
| 644 // Make a set of all external IDs in the delete journal, | |
| 645 // ignore entries with unset external IDs. | |
| 646 std::set<int64> journaled_external_ids; | |
| 647 for (size_t i = 0; i < num_journals_unmatched; i++) { | |
| 648 if (bk_delete_journals[i].external_id != 0) | |
| 649 journaled_external_ids.insert(bk_delete_journals[i].external_id); | |
| 650 } | |
| 651 | |
| 652 // Check bookmark model from top to bottom. | 619 // Check bookmark model from top to bottom. |
| 653 std::stack<const BookmarkNode*> dfs_stack; | 620 std::stack<const BookmarkNode*> dfs_stack; |
| 654 dfs_stack.push(bookmark_model_->bookmark_bar_node()); | 621 dfs_stack.push(bookmark_model_->bookmark_bar_node()); |
| 655 dfs_stack.push(bookmark_model_->other_node()); | 622 dfs_stack.push(bookmark_model_->other_node()); |
| 656 if (expect_mobile_bookmarks_folder_) | 623 if (expect_mobile_bookmarks_folder_) |
| 657 dfs_stack.push(bookmark_model_->mobile_node()); | 624 dfs_stack.push(bookmark_model_->mobile_node()); |
| 658 // Note: the root node may have additional extra nodes. Currently none of | 625 // Note: the root node may have additional extra nodes. Currently none of |
| 659 // them are meant to sync. | 626 // them are meant to sync. |
| 660 | 627 |
| 661 // Remember folders that match delete journals in first pass but don't delete | 628 // Remember folders that match delete journals in first pass but don't delete |
| 662 // them in case there are bookmarks left under them. After non-folder | 629 // them in case there are bookmarks left under them. After non-folder |
| 663 // bookmarks are removed in first pass, recheck the folders in reverse order | 630 // bookmarks are removed in first pass, recheck the folders in reverse order |
| 664 // to remove empty ones. | 631 // to remove empty ones. |
| 665 FolderInfoList folders_matched; | 632 FolderInfoList folders_matched; |
| 666 while (!dfs_stack.empty() && num_journals_unmatched > 0) { | 633 while (!dfs_stack.empty()) { |
| 667 const BookmarkNode* parent = dfs_stack.top(); | 634 const BookmarkNode* parent = dfs_stack.top(); |
| 668 dfs_stack.pop(); | 635 dfs_stack.pop(); |
| 669 DCHECK(parent->is_folder()); | |
| 670 | 636 |
| 671 // Enumerate folder children in reverse order to make it easier to remove | 637 BookmarkNodeFinder finder(parent); |
| 672 // bookmarks matching entries in the delete journal. | 638 // Iterate through journals from back to front. Remove matched journal by |
| 673 for (int child_index = parent->child_count() - 1; | 639 // moving an unmatched journal at the tail to its position so that we can |
| 674 child_index >= 0 && num_journals_unmatched > 0; --child_index) { | 640 // read unmatched journals off the head in next loop. |
| 675 const BookmarkNode* child = parent->GetChild(child_index); | 641 for (int i = num_journals_unmatched - 1; i >= 0; --i) { |
| 676 if (child->is_folder()) | 642 const BookmarkNode* child = finder.FindBookmarkNode( |
| 677 dfs_stack.push(child); | 643 GURL(bk_delete_journals[i].specifics.bookmark().url()), |
| 644 bk_delete_journals[i].specifics.bookmark().title(), |
| 645 bk_delete_journals[i].is_folder); |
| 646 if (child) { |
| 647 if (child->is_folder()) { |
| 648 // Remember matched folder without removing and delete only empty |
| 649 // ones later. |
| 650 folders_matched.push_back(FolderInfo(child, parent, |
| 651 bk_delete_journals[i].id)); |
| 652 } else { |
| 653 bookmark_model_->Remove(parent, parent->GetIndexOf(child)); |
| 654 ++num_bookmark_deleted; |
| 655 } |
| 656 // Move unmatched journal here and decrement counter. |
| 657 bk_delete_journals[i] = bk_delete_journals[--num_journals_unmatched]; |
| 658 } |
| 659 } |
| 660 if (num_journals_unmatched == 0) |
| 661 break; |
| 678 | 662 |
| 679 if (journaled_external_ids.find(child->id()) == | 663 for (int i = 0; i < parent->child_count(); ++i) { |
| 680 journaled_external_ids.end()) { | 664 if (parent->GetChild(i)->is_folder()) |
| 681 // Skip bookmark node which id is not in the set of external IDs. | 665 dfs_stack.push(parent->GetChild(i)); |
| 682 continue; | |
| 683 } | |
| 684 | |
| 685 // Iterate through the journal entries from back to front. Remove matched | |
| 686 // journal by moving an unmatched entry at the tail to the matched | |
| 687 // position so that we can read unmatched entries off the head in next | |
| 688 // loop. | |
| 689 for (int journal_index = num_journals_unmatched - 1; journal_index >= 0; | |
| 690 --journal_index) { | |
| 691 const syncer::BookmarkDeleteJournal& delete_entry = | |
| 692 bk_delete_journals[journal_index]; | |
| 693 if (child->id() == delete_entry.external_id && | |
| 694 BookmarkNodeFinder::NodeMatches( | |
| 695 child, GURL(delete_entry.specifics.bookmark().url()), | |
| 696 delete_entry.specifics.bookmark().title(), | |
| 697 delete_entry.is_folder)) { | |
| 698 if (child->is_folder()) { | |
| 699 // Remember matched folder without removing and delete only empty | |
| 700 // ones later. | |
| 701 folders_matched.push_back( | |
| 702 FolderInfo(child, parent, delete_entry.id)); | |
| 703 } else { | |
| 704 bookmark_model_->Remove(parent, child_index); | |
| 705 ++num_bookmark_deleted; | |
| 706 } | |
| 707 // Move unmatched journal here and decrement counter. | |
| 708 bk_delete_journals[journal_index] = | |
| 709 bk_delete_journals[--num_journals_unmatched]; | |
| 710 break; | |
| 711 } | |
| 712 } | |
| 713 } | 666 } |
| 714 } | 667 } |
| 715 | 668 |
| 716 // Ids of sync nodes not found in bookmark model, meaning the deletions are | 669 // Ids of sync nodes not found in bookmark model, meaning the deletions are |
| 717 // persisted and correponding delete journals can be dropped. | 670 // persisted and correponding delete journals can be dropped. |
| 718 std::set<int64> journals_to_purge; | 671 std::set<int64> journals_to_purge; |
| 719 | 672 |
| 720 // Remove empty folders from bottom to top. | 673 // Remove empty folders from bottom to top. |
| 721 for (FolderInfoList::reverse_iterator it = folders_matched.rbegin(); | 674 for (FolderInfoList::reverse_iterator it = folders_matched.rbegin(); |
| 722 it != folders_matched.rend(); ++it) { | 675 it != folders_matched.rend(); ++it) { |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 834 syncer::SyncError::PERSISTENCE_ERROR, | 787 syncer::SyncError::PERSISTENCE_ERROR, |
| 835 message, | 788 message, |
| 836 syncer::BOOKMARKS); | 789 syncer::BOOKMARKS); |
| 837 } | 790 } |
| 838 } | 791 } |
| 839 } | 792 } |
| 840 return syncer::SyncError(); | 793 return syncer::SyncError(); |
| 841 } | 794 } |
| 842 | 795 |
| 843 } // namespace browser_sync | 796 } // namespace browser_sync |
| OLD | NEW |