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