OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/command_line.h" |
9 #include "base/hash_tables.h" | 10 #include "base/hash_tables.h" |
10 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
11 #include "base/task.h" | 12 #include "base/task.h" |
12 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
13 #include "chrome/browser/bookmarks/bookmark_model.h" | 14 #include "chrome/browser/bookmarks/bookmark_model.h" |
14 #include "chrome/browser/profiles/profile.h" | 15 #include "chrome/browser/profiles/profile.h" |
15 #include "chrome/browser/sync/engine/syncapi.h" | 16 #include "chrome/browser/sync/engine/syncapi.h" |
16 #include "chrome/browser/sync/glue/bookmark_change_processor.h" | 17 #include "chrome/browser/sync/glue/bookmark_change_processor.h" |
17 #include "chrome/browser/sync/syncable/autofill_migration.h" | 18 #include "chrome/browser/sync/syncable/autofill_migration.h" |
18 #include "chrome/browser/sync/syncable/nigori_util.h" | 19 #include "chrome/browser/sync/syncable/nigori_util.h" |
19 #include "chrome/browser/sync/util/cryptographer.h" | 20 #include "chrome/browser/sync/util/cryptographer.h" |
| 21 #include "chrome/common/chrome_switches.h" |
20 #include "content/browser/browser_thread.h" | 22 #include "content/browser/browser_thread.h" |
21 | 23 |
22 namespace browser_sync { | 24 namespace browser_sync { |
23 | 25 |
24 // The sync protocol identifies top-level entities by means of well-known tags, | 26 // The sync protocol identifies top-level entities by means of well-known tags, |
25 // which should not be confused with titles. Each tag corresponds to a | 27 // which should not be confused with titles. Each tag corresponds to a |
26 // singleton instance of a particular top-level node in a user's share; the | 28 // singleton instance of a particular top-level node in a user's share; the |
27 // tags are consistent across users. The tags allow us to locate the specific | 29 // tags are consistent across users. The tags allow us to locate the specific |
28 // folders whose contents we care about synchronizing, without having to do a | 30 // folders whose contents we care about synchronizing, without having to do a |
29 // lookup by name or path. The tags should not be made user-visible. | 31 // lookup by name or path. The tags should not be made user-visible. |
30 // For example, the tag "bookmark_bar" represents the permanent node for | 32 // For example, the tag "bookmark_bar" represents the permanent node for |
31 // bookmarks bar in Chrome. The tag "other_bookmarks" represents the permanent | 33 // bookmarks bar in Chrome. The tag "other_bookmarks" represents the permanent |
32 // folder Other Bookmarks in Chrome. | 34 // folder Other Bookmarks in Chrome. |
33 // | 35 // |
34 // It is the responsibility of something upstream (at time of writing, | 36 // It is the responsibility of something upstream (at time of writing, |
35 // the sync server) to create these tagged nodes when initializing sync | 37 // the sync server) to create these tagged nodes when initializing sync |
36 // for the first time for a user. Thus, once the backend finishes | 38 // for the first time for a user. Thus, once the backend finishes |
37 // initializing, the ProfileSyncService can rely on the presence of tagged | 39 // initializing, the ProfileSyncService can rely on the presence of tagged |
38 // nodes. | 40 // nodes. |
39 // | 41 // |
40 // TODO(ncarter): Pull these tags from an external protocol specification | 42 // TODO(ncarter): Pull these tags from an external protocol specification |
41 // rather than hardcoding them here. | 43 // rather than hardcoding them here. |
42 static const char kBookmarkBarTag[] = "bookmark_bar"; | 44 static const char kBookmarkBarTag[] = "bookmark_bar"; |
| 45 static const char kSyncedBookmarksTag[] = "synced_bookmarks"; |
43 static const char kOtherBookmarksTag[] = "other_bookmarks"; | 46 static const char kOtherBookmarksTag[] = "other_bookmarks"; |
44 | 47 |
45 // Bookmark comparer for map of bookmark nodes. | 48 // Bookmark comparer for map of bookmark nodes. |
46 class BookmarkComparer { | 49 class BookmarkComparer { |
47 public: | 50 public: |
48 // Compares the two given nodes and returns whether node1 should appear | 51 // Compares the two given nodes and returns whether node1 should appear |
49 // before node2 in strict weak ordering. | 52 // before node2 in strict weak ordering. |
50 bool operator()(const BookmarkNode* node1, | 53 bool operator()(const BookmarkNode* node1, |
51 const BookmarkNode* node2) const { | 54 const BookmarkNode* node2) const { |
52 DCHECK(node1); | 55 DCHECK(node1); |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
225 if (iter == id_map_inverse_.end()) | 228 if (iter == id_map_inverse_.end()) |
226 return; | 229 return; |
227 id_map_.erase(iter->second->id()); | 230 id_map_.erase(iter->second->id()); |
228 id_map_inverse_.erase(iter); | 231 id_map_inverse_.erase(iter); |
229 dirty_associations_sync_ids_.erase(sync_id); | 232 dirty_associations_sync_ids_.erase(sync_id); |
230 } | 233 } |
231 | 234 |
232 bool BookmarkModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { | 235 bool BookmarkModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { |
233 DCHECK(has_nodes); | 236 DCHECK(has_nodes); |
234 *has_nodes = false; | 237 *has_nodes = false; |
| 238 bool has_synced_folder = true; |
235 int64 bookmark_bar_sync_id; | 239 int64 bookmark_bar_sync_id; |
236 if (!GetSyncIdForTaggedNode(kBookmarkBarTag, &bookmark_bar_sync_id)) { | 240 if (!GetSyncIdForTaggedNode(kBookmarkBarTag, &bookmark_bar_sync_id)) { |
237 return false; | 241 return false; |
238 } | 242 } |
239 int64 other_bookmarks_sync_id; | 243 int64 other_bookmarks_sync_id; |
240 if (!GetSyncIdForTaggedNode(kOtherBookmarksTag, &other_bookmarks_sync_id)) { | 244 if (!GetSyncIdForTaggedNode(kOtherBookmarksTag, &other_bookmarks_sync_id)) { |
241 return false; | 245 return false; |
242 } | 246 } |
| 247 int64 synced_bookmarks_sync_id; |
| 248 if (!GetSyncIdForTaggedNode(kSyncedBookmarksTag, &synced_bookmarks_sync_id)) { |
| 249 has_synced_folder = false; |
| 250 } |
243 | 251 |
244 sync_api::ReadTransaction trans(user_share_); | 252 sync_api::ReadTransaction trans(user_share_); |
245 | 253 |
246 sync_api::ReadNode bookmark_bar_node(&trans); | 254 sync_api::ReadNode bookmark_bar_node(&trans); |
247 if (!bookmark_bar_node.InitByIdLookup(bookmark_bar_sync_id)) { | 255 if (!bookmark_bar_node.InitByIdLookup(bookmark_bar_sync_id)) { |
248 return false; | 256 return false; |
249 } | 257 } |
250 | 258 |
251 sync_api::ReadNode other_bookmarks_node(&trans); | 259 sync_api::ReadNode other_bookmarks_node(&trans); |
252 if (!other_bookmarks_node.InitByIdLookup(other_bookmarks_sync_id)) { | 260 if (!other_bookmarks_node.InitByIdLookup(other_bookmarks_sync_id)) { |
253 return false; | 261 return false; |
254 } | 262 } |
255 | 263 |
| 264 sync_api::ReadNode synced_bookmarks_node(&trans); |
| 265 if (has_synced_folder && |
| 266 !synced_bookmarks_node.InitByIdLookup(synced_bookmarks_sync_id)) { |
| 267 return false; |
| 268 } |
| 269 |
256 // Sync model has user created nodes if either one of the permanent nodes | 270 // Sync model has user created nodes if either one of the permanent nodes |
257 // has children. | 271 // has children. |
258 *has_nodes = bookmark_bar_node.GetFirstChildId() != sync_api::kInvalidId || | 272 *has_nodes = bookmark_bar_node.GetFirstChildId() != sync_api::kInvalidId || |
259 other_bookmarks_node.GetFirstChildId() != sync_api::kInvalidId; | 273 other_bookmarks_node.GetFirstChildId() != sync_api::kInvalidId || |
| 274 (has_synced_folder && |
| 275 synced_bookmarks_node.GetFirstChildId() != sync_api::kInvalidId); |
260 return true; | 276 return true; |
261 } | 277 } |
262 | 278 |
263 bool BookmarkModelAssociator::NodesMatch(const BookmarkNode* bookmark, | 279 bool BookmarkModelAssociator::NodesMatch(const BookmarkNode* bookmark, |
264 const sync_api::BaseNode* sync_node) const { | 280 const sync_api::BaseNode* sync_node) const { |
265 if (bookmark->GetTitle() != WideToUTF16Hack(sync_node->GetTitle())) | 281 if (bookmark->GetTitle() != WideToUTF16Hack(sync_node->GetTitle())) |
266 return false; | 282 return false; |
267 if (bookmark->is_folder() != sync_node->GetIsFolder()) | 283 if (bookmark->is_folder() != sync_node->GetIsFolder()) |
268 return false; | 284 return false; |
269 if (bookmark->is_url()) { | 285 if (bookmark->is_url()) { |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
339 LOG(ERROR) << "Server did not create top-level nodes. Possibly we " | 355 LOG(ERROR) << "Server did not create top-level nodes. Possibly we " |
340 << "are running against an out-of-date server?"; | 356 << "are running against an out-of-date server?"; |
341 return false; | 357 return false; |
342 } | 358 } |
343 if (!AssociateTaggedPermanentNode(bookmark_model_->GetBookmarkBarNode(), | 359 if (!AssociateTaggedPermanentNode(bookmark_model_->GetBookmarkBarNode(), |
344 kBookmarkBarTag)) { | 360 kBookmarkBarTag)) { |
345 LOG(ERROR) << "Server did not create top-level nodes. Possibly we " | 361 LOG(ERROR) << "Server did not create top-level nodes. Possibly we " |
346 << "are running against an out-of-date server?"; | 362 << "are running against an out-of-date server?"; |
347 return false; | 363 return false; |
348 } | 364 } |
| 365 // We only need to ensure that the "synced bookmarks" folder exists on the |
| 366 // server if the command line flag is set. |
| 367 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 368 switches::kEnableSyncedBookmarksFolder) && |
| 369 !AssociateTaggedPermanentNode(bookmark_model_->synced_node(), |
| 370 kSyncedBookmarksTag)) { |
| 371 LOG(ERROR) << "Server did not create top-level synced nodes. Possibly " |
| 372 << "we are running against an out-of-date server?"; |
| 373 return false; |
| 374 } |
349 int64 bookmark_bar_sync_id = GetSyncIdFromChromeId( | 375 int64 bookmark_bar_sync_id = GetSyncIdFromChromeId( |
350 bookmark_model_->GetBookmarkBarNode()->id()); | 376 bookmark_model_->GetBookmarkBarNode()->id()); |
351 DCHECK(bookmark_bar_sync_id != sync_api::kInvalidId); | 377 DCHECK(bookmark_bar_sync_id != sync_api::kInvalidId); |
352 int64 other_bookmarks_sync_id = GetSyncIdFromChromeId( | 378 int64 other_bookmarks_sync_id = GetSyncIdFromChromeId( |
353 bookmark_model_->other_node()->id()); | 379 bookmark_model_->other_node()->id()); |
354 DCHECK(other_bookmarks_sync_id != sync_api::kInvalidId); | 380 DCHECK(other_bookmarks_sync_id != sync_api::kInvalidId); |
| 381 int64 synced_bookmarks_sync_id = GetSyncIdFromChromeId( |
| 382 bookmark_model_->synced_node()->id()); |
| 383 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 384 switches::kEnableSyncedBookmarksFolder)) { |
| 385 DCHECK(synced_bookmarks_sync_id != sync_api::kInvalidId); |
| 386 } |
355 | 387 |
356 std::stack<int64> dfs_stack; | 388 std::stack<int64> dfs_stack; |
| 389 if (synced_bookmarks_sync_id != sync_api::kInvalidId) |
| 390 dfs_stack.push(synced_bookmarks_sync_id); |
357 dfs_stack.push(other_bookmarks_sync_id); | 391 dfs_stack.push(other_bookmarks_sync_id); |
358 dfs_stack.push(bookmark_bar_sync_id); | 392 dfs_stack.push(bookmark_bar_sync_id); |
359 | 393 |
360 sync_api::WriteTransaction trans(user_share_); | 394 sync_api::WriteTransaction trans(user_share_); |
361 | 395 |
362 while (!dfs_stack.empty()) { | 396 while (!dfs_stack.empty()) { |
363 int64 sync_parent_id = dfs_stack.top(); | 397 int64 sync_parent_id = dfs_stack.top(); |
364 dfs_stack.pop(); | 398 dfs_stack.pop(); |
365 | 399 |
366 sync_api::ReadNode sync_parent(&trans); | 400 sync_api::ReadNode sync_parent(&trans); |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
479 int64 bookmark_bar_id; | 513 int64 bookmark_bar_id; |
480 if (!GetSyncIdForTaggedNode(kBookmarkBarTag, &bookmark_bar_id)) { | 514 if (!GetSyncIdForTaggedNode(kBookmarkBarTag, &bookmark_bar_id)) { |
481 // We should always be able to find the permanent nodes. | 515 // We should always be able to find the permanent nodes. |
482 return false; | 516 return false; |
483 } | 517 } |
484 int64 other_bookmarks_id; | 518 int64 other_bookmarks_id; |
485 if (!GetSyncIdForTaggedNode(kOtherBookmarksTag, &other_bookmarks_id)) { | 519 if (!GetSyncIdForTaggedNode(kOtherBookmarksTag, &other_bookmarks_id)) { |
486 // We should always be able to find the permanent nodes. | 520 // We should always be able to find the permanent nodes. |
487 return false; | 521 return false; |
488 } | 522 } |
| 523 int64 synced_bookmarks_id = -1; |
| 524 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 525 switches::kEnableSyncedBookmarksFolder) && |
| 526 !GetSyncIdForTaggedNode(kSyncedBookmarksTag, &synced_bookmarks_id)) { |
| 527 // We should always be able to find the permanent nodes. |
| 528 return false; |
| 529 } |
489 | 530 |
490 // Build a bookmark node ID index since we are going to repeatedly search for | 531 // Build a bookmark node ID index since we are going to repeatedly search for |
491 // bookmark nodes by their IDs. | 532 // bookmark nodes by their IDs. |
492 BookmarkNodeIdIndex id_index; | 533 BookmarkNodeIdIndex id_index; |
493 id_index.AddAll(bookmark_model_->GetBookmarkBarNode()); | 534 id_index.AddAll(bookmark_model_->GetBookmarkBarNode()); |
494 id_index.AddAll(bookmark_model_->other_node()); | 535 id_index.AddAll(bookmark_model_->other_node()); |
| 536 id_index.AddAll(bookmark_model_->synced_node()); |
495 | 537 |
496 std::stack<int64> dfs_stack; | 538 std::stack<int64> dfs_stack; |
| 539 if (synced_bookmarks_id != -1) |
| 540 dfs_stack.push(synced_bookmarks_id); |
497 dfs_stack.push(other_bookmarks_id); | 541 dfs_stack.push(other_bookmarks_id); |
498 dfs_stack.push(bookmark_bar_id); | 542 dfs_stack.push(bookmark_bar_id); |
499 | 543 |
500 sync_api::ReadTransaction trans(user_share_); | 544 sync_api::ReadTransaction trans(user_share_); |
501 | 545 |
502 // Count total number of nodes in sync model so that we can compare that | 546 // Count total number of nodes in sync model so that we can compare that |
503 // with the total number of nodes in the bookmark model. | 547 // with the total number of nodes in the bookmark model. |
504 size_t sync_node_count = 0; | 548 size_t sync_node_count = 0; |
505 while (!dfs_stack.empty()) { | 549 while (!dfs_stack.empty()) { |
506 int64 parent_id = dfs_stack.top(); | 550 int64 parent_id = dfs_stack.top(); |
507 dfs_stack.pop(); | 551 dfs_stack.pop(); |
508 ++sync_node_count; | 552 ++sync_node_count; |
509 sync_api::ReadNode sync_parent(&trans); | 553 sync_api::ReadNode sync_parent(&trans); |
510 if (!sync_parent.InitByIdLookup(parent_id)) { | 554 if (!sync_parent.InitByIdLookup(parent_id)) { |
511 return false; | 555 return false; |
512 } | 556 } |
513 | 557 |
514 int64 external_id = sync_parent.GetExternalId(); | 558 int64 external_id = sync_parent.GetExternalId(); |
515 if (external_id == 0) | 559 if (external_id == 0) |
516 return false; | 560 return false; |
517 | 561 |
518 const BookmarkNode* node = id_index.Find(external_id); | 562 const BookmarkNode* node = id_index.Find(external_id); |
519 if (!node) | 563 if (!node) |
520 return false; | 564 return false; |
521 | 565 |
522 // Don't try to call NodesMatch on permanent nodes like bookmark bar and | 566 // Don't try to call NodesMatch on permanent nodes like bookmark bar and |
523 // other bookmarks. They are not expected to match. | 567 // other bookmarks. They are not expected to match. |
524 if (node != bookmark_model_->GetBookmarkBarNode() && | 568 if (node != bookmark_model_->GetBookmarkBarNode() && |
| 569 node != bookmark_model_->synced_node() && |
525 node != bookmark_model_->other_node() && | 570 node != bookmark_model_->other_node() && |
526 !NodesMatch(node, &sync_parent)) | 571 !NodesMatch(node, &sync_parent)) |
527 return false; | 572 return false; |
528 | 573 |
529 Associate(node, sync_parent.GetId()); | 574 Associate(node, sync_parent.GetId()); |
530 | 575 |
531 // Add all children of the current node to the stack. | 576 // Add all children of the current node to the stack. |
532 int64 child_id = sync_parent.GetFirstChildId(); | 577 int64 child_id = sync_parent.GetFirstChildId(); |
533 while (child_id != sync_api::kInvalidId) { | 578 while (child_id != sync_api::kInvalidId) { |
534 dfs_stack.push(child_id); | 579 dfs_stack.push(child_id); |
(...skipping 17 matching lines...) Expand all Loading... |
552 bool BookmarkModelAssociator::CryptoReadyIfNecessary() { | 597 bool BookmarkModelAssociator::CryptoReadyIfNecessary() { |
553 // We only access the cryptographer while holding a transaction. | 598 // We only access the cryptographer while holding a transaction. |
554 sync_api::ReadTransaction trans(user_share_); | 599 sync_api::ReadTransaction trans(user_share_); |
555 const syncable::ModelTypeSet& encrypted_types = | 600 const syncable::ModelTypeSet& encrypted_types = |
556 GetEncryptedDataTypes(trans.GetWrappedTrans()); | 601 GetEncryptedDataTypes(trans.GetWrappedTrans()); |
557 return encrypted_types.count(syncable::BOOKMARKS) == 0 || | 602 return encrypted_types.count(syncable::BOOKMARKS) == 0 || |
558 trans.GetCryptographer()->is_ready(); | 603 trans.GetCryptographer()->is_ready(); |
559 } | 604 } |
560 | 605 |
561 } // namespace browser_sync | 606 } // namespace browser_sync |
OLD | NEW |