Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(265)

Side by Side Diff: chrome/browser/sync/glue/bookmark_model_associator.cc

Issue 904083002: Use external ID to match native model nodes during bookmark association. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698