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/command_line.h" |
10 #include "base/hash_tables.h" | 10 #include "base/hash_tables.h" |
11 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
12 #include "base/task.h" | 12 #include "base/task.h" |
13 #include "base/tracked.h" | 13 #include "base/tracked.h" |
14 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
15 #include "chrome/browser/bookmarks/bookmark_model.h" | 15 #include "chrome/browser/bookmarks/bookmark_model.h" |
16 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
| 17 #include "chrome/browser/sync/api/sync_error.h" |
17 #include "chrome/browser/sync/engine/syncapi.h" | 18 #include "chrome/browser/sync/engine/syncapi.h" |
18 #include "chrome/browser/sync/glue/bookmark_change_processor.h" | 19 #include "chrome/browser/sync/glue/bookmark_change_processor.h" |
19 #include "chrome/browser/sync/syncable/nigori_util.h" | 20 #include "chrome/browser/sync/syncable/nigori_util.h" |
20 #include "chrome/browser/sync/util/cryptographer.h" | 21 #include "chrome/browser/sync/util/cryptographer.h" |
21 #include "chrome/common/chrome_switches.h" | 22 #include "chrome/common/chrome_switches.h" |
22 #include "content/browser/browser_thread.h" | 23 #include "content/browser/browser_thread.h" |
23 | 24 |
24 namespace browser_sync { | 25 namespace browser_sync { |
25 | 26 |
26 // The sync protocol identifies top-level entities by means of well-known tags, | 27 // The sync protocol identifies top-level entities by means of well-known tags, |
(...skipping 10 matching lines...) Expand all Loading... |
37 // the sync server) to create these tagged nodes when initializing sync | 38 // the sync server) to create these tagged nodes when initializing sync |
38 // for the first time for a user. Thus, once the backend finishes | 39 // for the first time for a user. Thus, once the backend finishes |
39 // initializing, the ProfileSyncService can rely on the presence of tagged | 40 // initializing, the ProfileSyncService can rely on the presence of tagged |
40 // nodes. | 41 // nodes. |
41 // | 42 // |
42 // TODO(ncarter): Pull these tags from an external protocol specification | 43 // TODO(ncarter): Pull these tags from an external protocol specification |
43 // rather than hardcoding them here. | 44 // rather than hardcoding them here. |
44 static const char kBookmarkBarTag[] = "bookmark_bar"; | 45 static const char kBookmarkBarTag[] = "bookmark_bar"; |
45 static const char kSyncedBookmarksTag[] = "synced_bookmarks"; | 46 static const char kSyncedBookmarksTag[] = "synced_bookmarks"; |
46 static const char kOtherBookmarksTag[] = "other_bookmarks"; | 47 static const char kOtherBookmarksTag[] = "other_bookmarks"; |
| 48 static const char kServerError[] = |
| 49 "Server did not create top-level nodes. Possibly we are running against " |
| 50 "an out-of-date server?"; |
47 | 51 |
48 // Bookmark comparer for map of bookmark nodes. | 52 // Bookmark comparer for map of bookmark nodes. |
49 class BookmarkComparer { | 53 class BookmarkComparer { |
50 public: | 54 public: |
51 // Compares the two given nodes and returns whether node1 should appear | 55 // Compares the two given nodes and returns whether node1 should appear |
52 // before node2 in strict weak ordering. | 56 // before node2 in strict weak ordering. |
53 bool operator()(const BookmarkNode* node1, | 57 bool operator()(const BookmarkNode* node1, |
54 const BookmarkNode* node2) const { | 58 const BookmarkNode* node2) const { |
55 DCHECK(node1); | 59 DCHECK(node1); |
56 DCHECK(node2); | 60 DCHECK(node2); |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
172 DCHECK(bookmark_model_); | 176 DCHECK(bookmark_model_); |
173 DCHECK(user_share_); | 177 DCHECK(user_share_); |
174 DCHECK(unrecoverable_error_handler_); | 178 DCHECK(unrecoverable_error_handler_); |
175 } | 179 } |
176 | 180 |
177 BookmarkModelAssociator::~BookmarkModelAssociator() { | 181 BookmarkModelAssociator::~BookmarkModelAssociator() { |
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
179 } | 183 } |
180 | 184 |
181 bool BookmarkModelAssociator::DisassociateModels() { | 185 bool BookmarkModelAssociator::DisassociateModels(SyncError* error) { |
182 id_map_.clear(); | 186 id_map_.clear(); |
183 id_map_inverse_.clear(); | 187 id_map_inverse_.clear(); |
184 dirty_associations_sync_ids_.clear(); | 188 dirty_associations_sync_ids_.clear(); |
185 return true; | 189 return true; |
186 } | 190 } |
187 | 191 |
188 int64 BookmarkModelAssociator::GetSyncIdFromChromeId(const int64& node_id) { | 192 int64 BookmarkModelAssociator::GetSyncIdFromChromeId(const int64& node_id) { |
189 BookmarkIdToSyncIdMap::const_iterator iter = id_map_.find(node_id); | 193 BookmarkIdToSyncIdMap::const_iterator iter = id_map_.find(node_id); |
190 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; | 194 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; |
191 } | 195 } |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
308 bool BookmarkModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, | 312 bool BookmarkModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, |
309 int64* sync_id) { | 313 int64* sync_id) { |
310 sync_api::ReadTransaction trans(FROM_HERE, user_share_); | 314 sync_api::ReadTransaction trans(FROM_HERE, user_share_); |
311 sync_api::ReadNode sync_node(&trans); | 315 sync_api::ReadNode sync_node(&trans); |
312 if (!sync_node.InitByTagLookup(tag.c_str())) | 316 if (!sync_node.InitByTagLookup(tag.c_str())) |
313 return false; | 317 return false; |
314 *sync_id = sync_node.GetId(); | 318 *sync_id = sync_node.GetId(); |
315 return true; | 319 return true; |
316 } | 320 } |
317 | 321 |
318 bool BookmarkModelAssociator::AssociateModels() { | 322 bool BookmarkModelAssociator::AssociateModels(SyncError* error) { |
319 // Try to load model associations from persisted associations first. If that | 323 // Try to load model associations from persisted associations first. If that |
320 // succeeds, we don't need to run the complex model matching algorithm. | 324 // succeeds, we don't need to run the complex model matching algorithm. |
321 if (LoadAssociations()) | 325 if (LoadAssociations()) |
322 return true; | 326 return true; |
323 | 327 |
324 DisassociateModels(); | 328 DisassociateModels(error); |
325 | 329 |
326 // We couldn't load model associations from persisted associations. So build | 330 // We couldn't load model associations from persisted associations. So build |
327 // them. | 331 // them. |
328 return BuildAssociations(); | 332 return BuildAssociations(error); |
329 } | 333 } |
330 | 334 |
331 bool BookmarkModelAssociator::BuildAssociations() { | 335 bool BookmarkModelAssociator::BuildAssociations(SyncError* error) { |
332 // Algorithm description: | 336 // Algorithm description: |
333 // Match up the roots and recursively do the following: | 337 // Match up the roots and recursively do the following: |
334 // * For each sync node for the current sync parent node, find the best | 338 // * For each sync node for the current sync parent node, find the best |
335 // matching bookmark node under the corresponding bookmark parent node. | 339 // matching bookmark node under the corresponding bookmark parent node. |
336 // If no matching node is found, create a new bookmark node in the same | 340 // If no matching node is found, create a new bookmark node in the same |
337 // position as the corresponding sync node. | 341 // position as the corresponding sync node. |
338 // If a matching node is found, update the properties of it from the | 342 // If a matching node is found, update the properties of it from the |
339 // corresponding sync node. | 343 // corresponding sync node. |
340 // * When all children sync nodes are done, add the extra children bookmark | 344 // * When all children sync nodes are done, add the extra children bookmark |
341 // nodes to the sync parent node. | 345 // nodes to the sync parent node. |
342 // | 346 // |
343 // This algorithm will do a good job of merging when folder names are a good | 347 // This algorithm will do a good job of merging when folder names are a good |
344 // indicator of the two folders being the same. It will handle reordering and | 348 // indicator of the two folders being the same. It will handle reordering and |
345 // new node addition very well (without creating duplicates). | 349 // new node addition very well (without creating duplicates). |
346 // This algorithm will not do well if the folder name has changes but the | 350 // This algorithm will not do well if the folder name has changes but the |
347 // children under them are all the same. | 351 // children under them are all the same. |
348 | 352 |
349 DCHECK(bookmark_model_->IsLoaded()); | 353 DCHECK(bookmark_model_->IsLoaded()); |
350 | 354 |
351 // To prime our association, we associate the top-level nodes, Bookmark Bar | 355 // To prime our association, we associate the top-level nodes, Bookmark Bar |
352 // and Other Bookmarks. | 356 // and Other Bookmarks. |
353 if (!AssociateTaggedPermanentNode(bookmark_model_->other_node(), | 357 if (!AssociateTaggedPermanentNode(bookmark_model_->other_node(), |
354 kOtherBookmarksTag)) { | 358 kOtherBookmarksTag)) { |
355 LOG(ERROR) << "Server did not create top-level nodes. Possibly we " | 359 error->Reset(FROM_HERE, kServerError, model_type()); |
356 << "are running against an out-of-date server?"; | |
357 return false; | 360 return false; |
358 } | 361 } |
359 if (!AssociateTaggedPermanentNode(bookmark_model_->bookmark_bar_node(), | 362 if (!AssociateTaggedPermanentNode(bookmark_model_->bookmark_bar_node(), |
360 kBookmarkBarTag)) { | 363 kBookmarkBarTag)) { |
361 LOG(ERROR) << "Server did not create top-level nodes. Possibly we " | 364 error->Reset(FROM_HERE, kServerError, model_type()); |
362 << "are running against an out-of-date server?"; | |
363 return false; | 365 return false; |
364 } | 366 } |
365 if (!AssociateTaggedPermanentNode(bookmark_model_->synced_node(), | 367 if (!AssociateTaggedPermanentNode(bookmark_model_->synced_node(), |
366 kSyncedBookmarksTag) && | 368 kSyncedBookmarksTag) && |
367 // We only need to ensure that the "synced bookmarks" folder exists on the | 369 // We only need to ensure that the "synced bookmarks" folder exists on the |
368 // server if the command line flag is set. | 370 // server if the command line flag is set. |
369 CommandLine::ForCurrentProcess()->HasSwitch( | 371 CommandLine::ForCurrentProcess()->HasSwitch( |
370 switches::kEnableSyncedBookmarksFolder)) { | 372 switches::kEnableSyncedBookmarksFolder)) { |
371 LOG(ERROR) << "Server did not create top-level synced nodes. Possibly " | 373 error->Reset(FROM_HERE, kServerError, model_type()); |
372 << "we are running against an out-of-date server?"; | |
373 return false; | 374 return false; |
374 } | 375 } |
375 int64 bookmark_bar_sync_id = GetSyncIdFromChromeId( | 376 int64 bookmark_bar_sync_id = GetSyncIdFromChromeId( |
376 bookmark_model_->bookmark_bar_node()->id()); | 377 bookmark_model_->bookmark_bar_node()->id()); |
377 DCHECK_NE(bookmark_bar_sync_id, sync_api::kInvalidId); | 378 DCHECK_NE(bookmark_bar_sync_id, sync_api::kInvalidId); |
378 int64 other_bookmarks_sync_id = GetSyncIdFromChromeId( | 379 int64 other_bookmarks_sync_id = GetSyncIdFromChromeId( |
379 bookmark_model_->other_node()->id()); | 380 bookmark_model_->other_node()->id()); |
380 DCHECK_NE(other_bookmarks_sync_id, sync_api::kInvalidId); | 381 DCHECK_NE(other_bookmarks_sync_id, sync_api::kInvalidId); |
381 int64 synced_bookmarks_sync_id = GetSyncIdFromChromeId( | 382 int64 synced_bookmarks_sync_id = GetSyncIdFromChromeId( |
382 bookmark_model_->synced_node()->id()); | 383 bookmark_model_->synced_node()->id()); |
383 if (CommandLine::ForCurrentProcess()->HasSwitch( | 384 if (CommandLine::ForCurrentProcess()->HasSwitch( |
384 switches::kEnableSyncedBookmarksFolder)) { | 385 switches::kEnableSyncedBookmarksFolder)) { |
385 DCHECK_NE(synced_bookmarks_sync_id, sync_api::kInvalidId); | 386 DCHECK_NE(synced_bookmarks_sync_id, sync_api::kInvalidId); |
386 } | 387 } |
387 | 388 |
388 std::stack<int64> dfs_stack; | 389 std::stack<int64> dfs_stack; |
389 if (synced_bookmarks_sync_id != sync_api::kInvalidId) | 390 if (synced_bookmarks_sync_id != sync_api::kInvalidId) |
390 dfs_stack.push(synced_bookmarks_sync_id); | 391 dfs_stack.push(synced_bookmarks_sync_id); |
391 dfs_stack.push(other_bookmarks_sync_id); | 392 dfs_stack.push(other_bookmarks_sync_id); |
392 dfs_stack.push(bookmark_bar_sync_id); | 393 dfs_stack.push(bookmark_bar_sync_id); |
393 | 394 |
394 sync_api::WriteTransaction trans(FROM_HERE, user_share_); | 395 sync_api::WriteTransaction trans(FROM_HERE, user_share_); |
395 | 396 |
396 while (!dfs_stack.empty()) { | 397 while (!dfs_stack.empty()) { |
397 int64 sync_parent_id = dfs_stack.top(); | 398 int64 sync_parent_id = dfs_stack.top(); |
398 dfs_stack.pop(); | 399 dfs_stack.pop(); |
399 | 400 |
400 sync_api::ReadNode sync_parent(&trans); | 401 sync_api::ReadNode sync_parent(&trans); |
401 if (!sync_parent.InitByIdLookup(sync_parent_id)) { | 402 if (!sync_parent.InitByIdLookup(sync_parent_id)) { |
| 403 error->Reset(FROM_HERE, "Failed to lookup node.", model_type()); |
402 return false; | 404 return false; |
403 } | 405 } |
404 // Only folder nodes are pushed on to the stack. | 406 // Only folder nodes are pushed on to the stack. |
405 DCHECK(sync_parent.GetIsFolder()); | 407 DCHECK(sync_parent.GetIsFolder()); |
406 | 408 |
407 const BookmarkNode* parent_node = GetChromeNodeFromSyncId(sync_parent_id); | 409 const BookmarkNode* parent_node = GetChromeNodeFromSyncId(sync_parent_id); |
408 DCHECK(parent_node->is_folder()); | 410 DCHECK(parent_node->is_folder()); |
409 | 411 |
410 BookmarkNodeFinder node_finder(parent_node); | 412 BookmarkNodeFinder node_finder(parent_node); |
411 | 413 |
412 int index = 0; | 414 int index = 0; |
413 int64 sync_child_id = sync_parent.GetFirstChildId(); | 415 int64 sync_child_id = sync_parent.GetFirstChildId(); |
414 while (sync_child_id != sync_api::kInvalidId) { | 416 while (sync_child_id != sync_api::kInvalidId) { |
415 sync_api::WriteNode sync_child_node(&trans); | 417 sync_api::WriteNode sync_child_node(&trans); |
416 if (!sync_child_node.InitByIdLookup(sync_child_id)) { | 418 if (!sync_child_node.InitByIdLookup(sync_child_id)) { |
| 419 error->Reset(FROM_HERE, "Failed to lookup node.", model_type()); |
417 return false; | 420 return false; |
418 } | 421 } |
419 | 422 |
420 const BookmarkNode* child_node = NULL; | 423 const BookmarkNode* child_node = NULL; |
421 child_node = node_finder.FindBookmarkNode(sync_child_node); | 424 child_node = node_finder.FindBookmarkNode(sync_child_node); |
422 if (child_node) { | 425 if (child_node) { |
423 bookmark_model_->Move(child_node, parent_node, index); | 426 bookmark_model_->Move(child_node, parent_node, index); |
424 // Set the favicon for bookmark node from sync node or vice versa. | 427 // Set the favicon for bookmark node from sync node or vice versa. |
425 if (BookmarkChangeProcessor::SetBookmarkFavicon( | 428 if (BookmarkChangeProcessor::SetBookmarkFavicon( |
426 &sync_child_node, child_node, bookmark_model_)) { | 429 &sync_child_node, child_node, bookmark_model_)) { |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
598 bool BookmarkModelAssociator::CryptoReadyIfNecessary() { | 601 bool BookmarkModelAssociator::CryptoReadyIfNecessary() { |
599 // We only access the cryptographer while holding a transaction. | 602 // We only access the cryptographer while holding a transaction. |
600 sync_api::ReadTransaction trans(FROM_HERE, user_share_); | 603 sync_api::ReadTransaction trans(FROM_HERE, user_share_); |
601 const syncable::ModelTypeSet& encrypted_types = | 604 const syncable::ModelTypeSet& encrypted_types = |
602 sync_api::GetEncryptedTypes(&trans); | 605 sync_api::GetEncryptedTypes(&trans); |
603 return encrypted_types.count(syncable::BOOKMARKS) == 0 || | 606 return encrypted_types.count(syncable::BOOKMARKS) == 0 || |
604 trans.GetCryptographer()->is_ready(); | 607 trans.GetCryptographer()->is_ready(); |
605 } | 608 } |
606 | 609 |
607 } // namespace browser_sync | 610 } // namespace browser_sync |
OLD | NEW |