| OLD | NEW |
| 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2009 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 #if defined(BROWSER_SYNC) | 5 #if defined(BROWSER_SYNC) |
| 6 | 6 |
| 7 #include "chrome/browser/sync/glue/model_associator.h" | 7 #include "chrome/browser/sync/glue/model_associator.h" |
| 8 | 8 |
| 9 #include <stack> | 9 #include <stack> |
| 10 | 10 |
| 11 #include "base/hash_tables.h" |
| 11 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
| 12 #include "base/task.h" | 13 #include "base/task.h" |
| 13 #include "chrome/browser/bookmarks/bookmark_model.h" | 14 #include "chrome/browser/bookmarks/bookmark_model.h" |
| 14 #include "chrome/browser/sync/engine/syncapi.h" | 15 #include "chrome/browser/sync/engine/syncapi.h" |
| 15 #include "chrome/browser/sync/profile_sync_service.h" | 16 #include "chrome/browser/sync/profile_sync_service.h" |
| 16 | 17 |
| 17 namespace browser_sync { | 18 namespace browser_sync { |
| 18 | 19 |
| 19 // The sync protocol identifies top-level entities by means of well-known tags, | 20 // The sync protocol identifies top-level entities by means of well-known tags, |
| 20 // which should not be confused with titles. Each tag corresponds to a | 21 // which should not be confused with titles. Each tag corresponds to a |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 BookmarkNodesSet::iterator iter = child_nodes_.find(&temp_node); | 106 BookmarkNodesSet::iterator iter = child_nodes_.find(&temp_node); |
| 106 if (iter != child_nodes_.end()) { | 107 if (iter != child_nodes_.end()) { |
| 107 result = *iter; | 108 result = *iter; |
| 108 // Remove the matched node so we don't match with it again. | 109 // Remove the matched node so we don't match with it again. |
| 109 child_nodes_.erase(iter); | 110 child_nodes_.erase(iter); |
| 110 } | 111 } |
| 111 | 112 |
| 112 return result; | 113 return result; |
| 113 } | 114 } |
| 114 | 115 |
| 116 // Helper class to build an index of bookmark nodes by their IDs. |
| 117 class BookmarkNodeIdIndex { |
| 118 public: |
| 119 BookmarkNodeIdIndex() { } |
| 120 ~BookmarkNodeIdIndex() { } |
| 121 |
| 122 // Adds the given bookmark node and all its descendants to the ID index. |
| 123 // Does nothing if node is NULL. |
| 124 void AddAll(const BookmarkNode* node); |
| 125 |
| 126 // Finds the bookmark node with the given ID. |
| 127 // Returns NULL if none exists with the given id. |
| 128 const BookmarkNode* Find(int64 id) const; |
| 129 |
| 130 // Returns the count of nodes in the index. |
| 131 size_t count() const { return node_index_.size(); } |
| 132 |
| 133 private: |
| 134 typedef base::hash_map<int64, const BookmarkNode*> BookmarkIdMap; |
| 135 // Map that holds nodes indexed by their ids. |
| 136 BookmarkIdMap node_index_; |
| 137 |
| 138 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeIdIndex); |
| 139 }; |
| 140 |
| 141 void BookmarkNodeIdIndex::AddAll(const BookmarkNode* node) { |
| 142 if (!node) |
| 143 return; |
| 144 |
| 145 node_index_[node->id()] = node; |
| 146 |
| 147 if (!node->is_folder()) |
| 148 return; |
| 149 |
| 150 for (int i = 0; i < node->GetChildCount(); ++i) |
| 151 AddAll(node->GetChild(i)); |
| 152 } |
| 153 |
| 154 const BookmarkNode* BookmarkNodeIdIndex::Find(int64 id) const { |
| 155 BookmarkIdMap::const_iterator iter = node_index_.find(id); |
| 156 return iter == node_index_.end() ? NULL : iter->second; |
| 157 } |
| 158 |
| 115 ModelAssociator::ModelAssociator(ProfileSyncService* sync_service) | 159 ModelAssociator::ModelAssociator(ProfileSyncService* sync_service) |
| 116 : sync_service_(sync_service), | 160 : sync_service_(sync_service), |
| 117 task_pending_(false) { | 161 task_pending_(false) { |
| 118 DCHECK(sync_service_); | 162 DCHECK(sync_service_); |
| 119 } | 163 } |
| 120 | 164 |
| 121 void ModelAssociator::ClearAll() { | 165 void ModelAssociator::ClearAll() { |
| 122 id_map_.clear(); | 166 id_map_.clear(); |
| 123 id_map_inverse_.clear(); | 167 id_map_inverse_.clear(); |
| 124 dirty_associations_sync_ids_.clear(); | 168 dirty_associations_sync_ids_.clear(); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 149 return false; | 193 return false; |
| 150 DCHECK(sync_node->GetId() == sync_id); | 194 DCHECK(sync_node->GetId() == sync_id); |
| 151 return true; | 195 return true; |
| 152 } | 196 } |
| 153 | 197 |
| 154 const BookmarkNode* ModelAssociator::GetBookmarkNodeFromSyncId(int64 sync_id) { | 198 const BookmarkNode* ModelAssociator::GetBookmarkNodeFromSyncId(int64 sync_id) { |
| 155 int64 node_id; | 199 int64 node_id; |
| 156 if (!GetBookmarkIdFromSyncId(sync_id, &node_id)) | 200 if (!GetBookmarkIdFromSyncId(sync_id, &node_id)) |
| 157 return false; | 201 return false; |
| 158 BookmarkModel* model = sync_service_->profile()->GetBookmarkModel(); | 202 BookmarkModel* model = sync_service_->profile()->GetBookmarkModel(); |
| 203 // TODO(munjal): Fix issue 26141. |
| 159 return model->GetNodeByID(node_id); | 204 return model->GetNodeByID(node_id); |
| 160 } | 205 } |
| 161 | 206 |
| 162 void ModelAssociator::AssociateIds(int64 node_id, int64 sync_id) { | 207 void ModelAssociator::AssociateIds(int64 node_id, int64 sync_id) { |
| 163 DCHECK_NE(sync_id, sync_api::kInvalidId); | 208 DCHECK_NE(sync_id, sync_api::kInvalidId); |
| 164 DCHECK(id_map_.find(node_id) == id_map_.end()); | 209 DCHECK(id_map_.find(node_id) == id_map_.end()); |
| 165 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); | 210 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); |
| 166 id_map_[node_id] = sync_id; | 211 id_map_[node_id] = sync_id; |
| 167 id_map_inverse_[sync_id] = node_id; | 212 id_map_inverse_[sync_id] = node_id; |
| 168 dirty_associations_sync_ids_.insert(sync_id); | 213 dirty_associations_sync_ids_.insert(sync_id); |
| (...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 446 // create the tagged nodes on demand, and the order in which we probe for | 491 // create the tagged nodes on demand, and the order in which we probe for |
| 447 // them here will impact their positional ordering in that case. | 492 // them here will impact their positional ordering in that case. |
| 448 int64 bookmark_bar_id; | 493 int64 bookmark_bar_id; |
| 449 if (!GetSyncIdForTaggedNode(WideToUTF16(kBookmarkBarTag), &bookmark_bar_id)) { | 494 if (!GetSyncIdForTaggedNode(WideToUTF16(kBookmarkBarTag), &bookmark_bar_id)) { |
| 450 // We should always be able to find the permanent nodes. | 495 // We should always be able to find the permanent nodes. |
| 451 sync_service_->OnUnrecoverableError(); | 496 sync_service_->OnUnrecoverableError(); |
| 452 return false; | 497 return false; |
| 453 } | 498 } |
| 454 int64 other_bookmarks_id; | 499 int64 other_bookmarks_id; |
| 455 if (!GetSyncIdForTaggedNode(WideToUTF16(kOtherBookmarksTag), | 500 if (!GetSyncIdForTaggedNode(WideToUTF16(kOtherBookmarksTag), |
| 456 &other_bookmarks_id)) { | 501 &other_bookmarks_id)) { |
| 457 // We should always be able to find the permanent nodes. | 502 // We should always be able to find the permanent nodes. |
| 458 sync_service_->OnUnrecoverableError(); | 503 sync_service_->OnUnrecoverableError(); |
| 459 return false; | 504 return false; |
| 460 } | 505 } |
| 461 | 506 |
| 507 // Build a bookmark node ID index since we are going to repeatedly search for |
| 508 // bookmark nodes by their IDs. |
| 509 BookmarkNodeIdIndex id_index; |
| 510 id_index.AddAll(model->GetBookmarkBarNode()); |
| 511 id_index.AddAll(model->other_node()); |
| 512 |
| 462 std::stack<int64> dfs_stack; | 513 std::stack<int64> dfs_stack; |
| 463 dfs_stack.push(other_bookmarks_id); | 514 dfs_stack.push(other_bookmarks_id); |
| 464 dfs_stack.push(bookmark_bar_id); | 515 dfs_stack.push(bookmark_bar_id); |
| 465 | 516 |
| 466 sync_api::ReadTransaction trans( | 517 sync_api::ReadTransaction trans( |
| 467 sync_service_->backend()->GetUserShareHandle()); | 518 sync_service_->backend()->GetUserShareHandle()); |
| 468 | 519 |
| 520 // Count total number of nodes in sync model so that we can compare that |
| 521 // with the total number of nodes in the bookmark model. |
| 522 int64 sync_node_count = 0; |
| 469 while (!dfs_stack.empty()) { | 523 while (!dfs_stack.empty()) { |
| 470 int64 parent_id = dfs_stack.top(); | 524 int64 parent_id = dfs_stack.top(); |
| 471 dfs_stack.pop(); | 525 dfs_stack.pop(); |
| 526 ++sync_node_count; |
| 472 sync_api::ReadNode sync_parent(&trans); | 527 sync_api::ReadNode sync_parent(&trans); |
| 473 if (!sync_parent.InitByIdLookup(parent_id)) { | 528 if (!sync_parent.InitByIdLookup(parent_id)) { |
| 474 sync_service_->OnUnrecoverableError(); | 529 sync_service_->OnUnrecoverableError(); |
| 475 return false; | 530 return false; |
| 476 } | 531 } |
| 477 | 532 |
| 478 int64 external_id = sync_parent.GetExternalId(); | 533 int64 external_id = sync_parent.GetExternalId(); |
| 479 if (external_id == 0) | 534 if (external_id == 0) |
| 480 return false; | 535 return false; |
| 481 | 536 |
| 482 const BookmarkNode* node = model->GetNodeByID(external_id); | 537 const BookmarkNode* node = id_index.Find(external_id); |
| 483 if (!node) | 538 if (!node) |
| 484 return false; | 539 return false; |
| 485 | 540 |
| 486 // Don't try to call NodesMatch on permanent nodes like bookmark bar and | 541 // Don't try to call NodesMatch on permanent nodes like bookmark bar and |
| 487 // other bookmarks. They are not expected to match. | 542 // other bookmarks. They are not expected to match. |
| 488 if (node != model->GetBookmarkBarNode() && | 543 if (node != model->GetBookmarkBarNode() && |
| 489 node != model->other_node() && | 544 node != model->other_node() && |
| 490 !NodesMatch(node, &sync_parent)) | 545 !NodesMatch(node, &sync_parent)) |
| 491 return false; | 546 return false; |
| 492 | 547 |
| 493 AssociateIds(external_id, sync_parent.GetId()); | 548 AssociateIds(external_id, sync_parent.GetId()); |
| 494 | 549 |
| 495 // Add all children of the current node to the stack. | 550 // Add all children of the current node to the stack. |
| 496 int64 child_id = sync_parent.GetFirstChildId(); | 551 int64 child_id = sync_parent.GetFirstChildId(); |
| 497 while (child_id != sync_api::kInvalidId) { | 552 while (child_id != sync_api::kInvalidId) { |
| 498 dfs_stack.push(child_id); | 553 dfs_stack.push(child_id); |
| 499 sync_api::ReadNode child_node(&trans); | 554 sync_api::ReadNode child_node(&trans); |
| 500 if (!child_node.InitByIdLookup(child_id)) { | 555 if (!child_node.InitByIdLookup(child_id)) { |
| 501 sync_service_->OnUnrecoverableError(); | 556 sync_service_->OnUnrecoverableError(); |
| 502 return false; | 557 return false; |
| 503 } | 558 } |
| 504 child_id = child_node.GetSuccessorId(); | 559 child_id = child_node.GetSuccessorId(); |
| 505 } | 560 } |
| 506 } | 561 } |
| 507 DCHECK(dfs_stack.empty()); | 562 DCHECK(dfs_stack.empty()); |
| 508 return true; | 563 |
| 564 // It's possible that the number of nodes in the bookmark model is not the |
| 565 // same as number of nodes in the sync model. This can happen when the sync |
| 566 // model doesn't get a chance to persist its changes, for example when |
| 567 // Chrome does not shut down gracefully. In such cases we can't trust the |
| 568 // loaded associations. |
| 569 return sync_node_count == id_index.count(); |
| 509 } | 570 } |
| 510 | 571 |
| 511 } // namespace browser_sync | 572 } // namespace browser_sync |
| 512 | 573 |
| 513 #endif // defined(BROWSER_SYNC) | 574 #endif // defined(BROWSER_SYNC) |
| OLD | NEW |