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