12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync/glue/bookmark_model_associator.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stack>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
117d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/containers/hash_tables.h"
12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/format_macros.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/location.h"
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/macros.h"
159ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/strings/string_util.h"
18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/stringprintf.h"
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync/glue/bookmark_change_processor.h"
22a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "chrome/browser/undo/bookmark_undo_service.h"
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "chrome/browser/undo/bookmark_undo_service_factory.h"
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/undo/bookmark_undo_utils.h"
25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "components/bookmarks/browser/bookmark_client.h"
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "components/bookmarks/browser/bookmark_model.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/api/sync_error.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/internal_api/public/delete_journal.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/read_node.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/read_transaction.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/write_node.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/write_transaction.h"
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "sync/internal_api/syncapi_internal.h"
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/syncable/syncable_write_transaction.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/util/cryptographer.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/util/data_type_histogram.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace browser_sync {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The sync protocol identifies top-level entities by means of well-known tags,
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// which should not be confused with titles.  Each tag corresponds to a
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// singleton instance of a particular top-level node in a user's share; the
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// tags are consistent across users. The tags allow us to locate the specific
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// folders whose contents we care about synchronizing, without having to do a
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// lookup by name or path.  The tags should not be made user-visible.
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For example, the tag "bookmark_bar" represents the permanent node for
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// bookmarks bar in Chrome. The tag "other_bookmarks" represents the permanent
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// folder Other Bookmarks in Chrome.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// It is the responsibility of something upstream (at time of writing,
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the sync server) to create these tagged nodes when initializing sync
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// for the first time for a user.  Thus, once the backend finishes
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// initializing, the ProfileSyncService can rely on the presence of tagged
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// nodes.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(ncarter): Pull these tags from an external protocol specification
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// rather than hardcoding them here.
6168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)const char kBookmarkBarTag[] = "bookmark_bar";
6268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)const char kMobileBookmarksTag[] = "synced_bookmarks";
6368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)const char kOtherBookmarksTag[] = "other_bookmarks";
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Maximum number of bytes to allow in a title (must match sync's internal
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// limits; see write_node.cc).
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kTitleLimitBytes = 255;
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Bookmark comparer for map of bookmark nodes.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BookmarkComparer {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Compares the two given nodes and returns whether node1 should appear
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // before node2 in strict weak ordering.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool operator()(const BookmarkNode* node1,
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const BookmarkNode* node2) const {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(node1);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(node2);
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Keep folder nodes before non-folder nodes.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (node1->is_folder() != node2->is_folder())
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return node1->is_folder();
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Truncate bookmark titles in the form sync does internally to avoid
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // mismatches due to sync munging titles.
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    std::string title1 = base::UTF16ToUTF8(node1->GetTitle());
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    syncer::SyncAPINameToServerName(title1, &title1);
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    base::TruncateUTF8ToByteSize(title1, kTitleLimitBytes, &title1);
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    std::string title2 = base::UTF16ToUTF8(node2->GetTitle());
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    syncer::SyncAPINameToServerName(title2, &title2);
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    base::TruncateUTF8ToByteSize(title2, kTitleLimitBytes, &title2);
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    int result = title1.compare(title2);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (result != 0)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return result < 0;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return node1->url() < node2->url();
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Provides the following abstraction: given a parent bookmark node, find best
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// matching child node for many sync nodes.
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BookmarkNodeFinder {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Creates an instance with the given parent bookmark node.
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit BookmarkNodeFinder(const BookmarkNode* parent_node);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Finds the bookmark node that matches the given url, title and folder
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // attribute. Returns the matching node if one exists; NULL otherwise. If a
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // matching node is found, it's removed for further matches.
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BookmarkNode* FindBookmarkNode(const GURL& url,
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       const std::string& title,
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       bool is_folder);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::multiset<const BookmarkNode*, BookmarkComparer> BookmarkNodesSet;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const BookmarkNode* parent_node_;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarkNodesSet child_nodes_;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(BookmarkNodeFinder);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ScopedAssociationUpdater {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit ScopedAssociationUpdater(BookmarkModel* model) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    model_ = model;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    model->BeginExtensiveChanges();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~ScopedAssociationUpdater() {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    model_->EndExtensiveChanges();
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarkModel* model_;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ScopedAssociationUpdater);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BookmarkNodeFinder::BookmarkNodeFinder(const BookmarkNode* parent_node)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : parent_node_(parent_node) {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < parent_node_->child_count(); ++i) {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    child_nodes_.insert(parent_node_->GetChild(i));
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const BookmarkNode* BookmarkNodeFinder::FindBookmarkNode(
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& url, const std::string& title, bool is_folder) {
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create a bookmark node from the given bookmark attributes.
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BookmarkNode temp_node(url);
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  temp_node.SetTitle(base::UTF8ToUTF16(title));
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (is_folder)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    temp_node.set_type(BookmarkNode::FOLDER);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    temp_node.set_type(BookmarkNode::URL);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const BookmarkNode* result = NULL;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarkNodesSet::iterator iter = child_nodes_.find(&temp_node);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (iter != child_nodes_.end()) {
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = *iter;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Remove the matched node so we don't match with it again.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    child_nodes_.erase(iter);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Helper class to build an index of bookmark nodes by their IDs.
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BookmarkNodeIdIndex {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarkNodeIdIndex() { }
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~BookmarkNodeIdIndex() { }
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Adds the given bookmark node and all its descendants to the ID index.
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Does nothing if node is NULL.
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddAll(const BookmarkNode* node);
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Finds the bookmark node with the given ID.
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns NULL if none exists with the given id.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const BookmarkNode* Find(int64 id) const;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns the count of nodes in the index.
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t count() const { return node_index_.size(); }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef base::hash_map<int64, const BookmarkNode*> BookmarkIdMap;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Map that holds nodes indexed by their ids.
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarkIdMap node_index_;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(BookmarkNodeIdIndex);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkNodeIdIndex::AddAll(const BookmarkNode* node) {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!node)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  node_index_[node->id()] = node;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!node->is_folder())
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < node->child_count(); ++i)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddAll(node->GetChild(i));
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const BookmarkNode* BookmarkNodeIdIndex::Find(int64 id) const {
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarkIdMap::const_iterator iter = node_index_.find(id);
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return iter == node_index_.end() ? NULL : iter->second;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BookmarkModelAssociator::BookmarkModelAssociator(
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BookmarkModel* bookmark_model,
214c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Profile* profile,
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::UserShare* user_share,
2165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    sync_driver::DataTypeErrorHandler* unrecoverable_error_handler,
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool expect_mobile_bookmarks_folder)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : bookmark_model_(bookmark_model),
219c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      profile_(profile),
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      user_share_(user_share),
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unrecoverable_error_handler_(unrecoverable_error_handler),
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expect_mobile_bookmarks_folder_(expect_mobile_bookmarks_folder),
223c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_factory_(this) {
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(bookmark_model_);
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(user_share_);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(unrecoverable_error_handler_);
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BookmarkModelAssociator::~BookmarkModelAssociator() {
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkModelAssociator::UpdatePermanentNodeVisibility() {
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
236b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(bookmark_model_->loaded());
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  BookmarkNode::Type bookmark_node_types[] = {
239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    BookmarkNode::BOOKMARK_BAR,
240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    BookmarkNode::OTHER_NODE,
241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    BookmarkNode::MOBILE,
242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  };
243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (size_t i = 0; i < arraysize(bookmark_node_types); ++i) {
244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    int64 id = bookmark_model_->PermanentNode(bookmark_node_types[i])->id();
245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    bookmark_model_->SetPermanentNodeVisible(
246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      bookmark_node_types[i],
247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      id_map_.find(id) != id_map_.end());
248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
250f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Note: the root node may have additional extra nodes. Currently their
251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // visibility is not affected by sync.
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)syncer::SyncError BookmarkModelAssociator::DisassociateModels() {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_map_.clear();
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_map_inverse_.clear();
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dirty_associations_sync_ids_.clear();
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return syncer::SyncError();
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int64 BookmarkModelAssociator::GetSyncIdFromChromeId(const int64& node_id) {
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarkIdToSyncIdMap::const_iterator iter = id_map_.find(node_id);
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return iter == id_map_.end() ? syncer::kInvalidId : iter->second;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const BookmarkNode* BookmarkModelAssociator::GetChromeNodeFromSyncId(
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int64 sync_id) {
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SyncIdToBookmarkNodeMap::const_iterator iter = id_map_inverse_.find(sync_id);
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return iter == id_map_inverse_.end() ? NULL : iter->second;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BookmarkModelAssociator::InitSyncNodeFromChromeId(
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int64& node_id,
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::BaseNode* sync_node) {
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(sync_node);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 sync_id = GetSyncIdFromChromeId(node_id);
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (sync_id == syncer::kInvalidId)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (sync_node->InitByIdLookup(sync_id) != syncer::BaseNode::INIT_OK)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(sync_node->GetId() == sync_id);
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkModelAssociator::Associate(const BookmarkNode* node,
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        int64 sync_id) {
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 node_id = node->id();
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(sync_id, syncer::kInvalidId);
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(id_map_.find(node_id) == id_map_.end());
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_map_[node_id] = sync_id;
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_map_inverse_[sync_id] = node;
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dirty_associations_sync_ids_.insert(sync_id);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PostPersistAssociationsTask();
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdatePermanentNodeVisibility();
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkModelAssociator::Disassociate(int64 sync_id) {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SyncIdToBookmarkNodeMap::iterator iter = id_map_inverse_.find(sync_id);
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (iter == id_map_inverse_.end())
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_map_.erase(iter->second->id());
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_map_inverse_.erase(iter);
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dirty_associations_sync_ids_.erase(sync_id);
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BookmarkModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(has_nodes);
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *has_nodes = false;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool has_mobile_folder = true;
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 bookmark_bar_sync_id;
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetSyncIdForTaggedNode(kBookmarkBarTag, &bookmark_bar_sync_id)) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 other_bookmarks_sync_id;
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetSyncIdForTaggedNode(kOtherBookmarksTag, &other_bookmarks_sync_id)) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 mobile_bookmarks_sync_id;
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetSyncIdForTaggedNode(kMobileBookmarksTag, &mobile_bookmarks_sync_id)) {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    has_mobile_folder = false;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadTransaction trans(FROM_HERE, user_share_);
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadNode bookmark_bar_node(&trans);
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (bookmark_bar_node.InitByIdLookup(bookmark_bar_sync_id) !=
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          syncer::BaseNode::INIT_OK) {
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadNode other_bookmarks_node(&trans);
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (other_bookmarks_node.InitByIdLookup(other_bookmarks_sync_id) !=
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          syncer::BaseNode::INIT_OK) {
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadNode mobile_bookmarks_node(&trans);
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (has_mobile_folder &&
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mobile_bookmarks_node.InitByIdLookup(mobile_bookmarks_sync_id) !=
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          syncer::BaseNode::INIT_OK) {
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sync model has user created nodes if any of the permanent nodes has
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // children.
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *has_nodes = bookmark_bar_node.HasChildren() ||
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      other_bookmarks_node.HasChildren() ||
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (has_mobile_folder && mobile_bookmarks_node.HasChildren());
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BookmarkModelAssociator::NodesMatch(
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BookmarkNode* bookmark,
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::BaseNode* sync_node) const {
358a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string truncated_title = base::UTF16ToUTF8(bookmark->GetTitle());
359a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::TruncateUTF8ToByteSize(truncated_title,
360a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                               kTitleLimitBytes,
361a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                               &truncated_title);
362a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (truncated_title != sync_node->GetTitle())
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (bookmark->is_folder() != sync_node->GetIsFolder())
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (bookmark->is_url()) {
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (bookmark->url() != GURL(sync_node->GetBookmarkSpecifics().url()))
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't compare favicons here, because they are not really
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // user-updated and we don't have versioning information -- a site changing
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // its favicon shouldn't result in a bookmark mismatch.
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BookmarkModelAssociator::AssociateTaggedPermanentNode(
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BookmarkNode* permanent_node, const std::string&tag) {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Do nothing if |permanent_node| is already initialized and associated.
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 sync_id = GetSyncIdFromChromeId(permanent_node->id());
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (sync_id != syncer::kInvalidId)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetSyncIdForTaggedNode(tag, &sync_id))
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Associate(permanent_node, sync_id);
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BookmarkModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     int64* sync_id) {
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadTransaction trans(FROM_HERE, user_share_);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadNode sync_node(&trans);
39346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (sync_node.InitByTagLookupForBookmarks(
39446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      tag.c_str()) != syncer::BaseNode::INIT_OK)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *sync_id = sync_node.GetId();
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncError BookmarkModelAssociator::AssociateModels(
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncMergeResult* local_merge_result,
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncMergeResult* syncer_merge_result) {
403a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Since any changes to the bookmark model made here are not user initiated,
404a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // these change should not be undoable and so suspend the undo tracking.
4055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ScopedSuspendBookmarkUndo suspend_undo(profile_);
4061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
407eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  syncer::SyncError error = CheckModelSyncState(local_merge_result,
408eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                                syncer_merge_result);
409eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (error.IsSet())
410eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return error;
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<ScopedAssociationUpdater> association_updater(
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new ScopedAssociationUpdater(bookmark_model_));
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DisassociateModels();
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return BuildAssociations(local_merge_result, syncer_merge_result);
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncError BookmarkModelAssociator::BuildAssociations(
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncMergeResult* local_merge_result,
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncMergeResult* syncer_merge_result) {
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Algorithm description:
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Match up the roots and recursively do the following:
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * For each sync node for the current sync parent node, find the best
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   matching bookmark node under the corresponding bookmark parent node.
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   If no matching node is found, create a new bookmark node in the same
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   position as the corresponding sync node.
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   If a matching node is found, update the properties of it from the
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   corresponding sync node.
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * When all children sync nodes are done, add the extra children bookmark
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   nodes to the sync parent node.
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This algorithm will do a good job of merging when folder names are a good
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // indicator of the two folders being the same. It will handle reordering and
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // new node addition very well (without creating duplicates).
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This algorithm will not do well if the folder name has changes but the
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // children under them are all the same.
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
439b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(bookmark_model_->loaded());
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // To prime our association, we associate the top-level nodes, Bookmark Bar
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and Other Bookmarks.
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!AssociateTaggedPermanentNode(bookmark_model_->bookmark_bar_node(),
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    kBookmarkBarTag)) {
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return unrecoverable_error_handler_->CreateAndUploadError(
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        FROM_HERE,
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "Bookmark bar node not found",
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        model_type());
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!AssociateTaggedPermanentNode(bookmark_model_->other_node(),
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    kOtherBookmarksTag)) {
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return unrecoverable_error_handler_->CreateAndUploadError(
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        FROM_HERE,
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "Other bookmarks node not found",
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        model_type());
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!AssociateTaggedPermanentNode(bookmark_model_->mobile_node(),
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    kMobileBookmarksTag) &&
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expect_mobile_bookmarks_folder_) {
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return unrecoverable_error_handler_->CreateAndUploadError(
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        FROM_HERE,
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "Mobile bookmarks node not found",
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        model_type());
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
468f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Note: the root node may have additional extra nodes. Currently none of
469f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // them are meant to sync.
470f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 bookmark_bar_sync_id = GetSyncIdFromChromeId(
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bookmark_model_->bookmark_bar_node()->id());
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(bookmark_bar_sync_id, syncer::kInvalidId);
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 other_bookmarks_sync_id = GetSyncIdFromChromeId(
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bookmark_model_->other_node()->id());
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(other_bookmarks_sync_id, syncer::kInvalidId);
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 mobile_bookmarks_sync_id = GetSyncIdFromChromeId(
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       bookmark_model_->mobile_node()->id());
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (expect_mobile_bookmarks_folder_) {
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_NE(syncer::kInvalidId, mobile_bookmarks_sync_id);
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // WARNING: The order in which we push these should match their order in the
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bookmark model (see BookmarkModel::DoneLoading(..)).
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::stack<int64> dfs_stack;
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dfs_stack.push(bookmark_bar_sync_id);
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dfs_stack.push(other_bookmarks_sync_id);
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (mobile_bookmarks_sync_id != syncer::kInvalidId)
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dfs_stack.push(mobile_bookmarks_sync_id);
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::WriteTransaction trans(FROM_HERE, user_share_);
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::ReadNode bm_root(&trans);
49346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (bm_root.InitTypeRoot(syncer::BOOKMARKS) == syncer::BaseNode::INIT_OK) {
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer_merge_result->set_num_items_before_association(
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        bm_root.GetTotalNodeCount());
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  local_merge_result->set_num_items_before_association(
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      bookmark_model_->root_node()->GetTotalNodeCount());
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Remove obsolete bookmarks according to sync delete journal.
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  local_merge_result->set_num_items_deleted(
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ApplyDeletesFromSyncJournal(&trans));
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (!dfs_stack.empty()) {
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int64 sync_parent_id = dfs_stack.top();
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dfs_stack.pop();
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::ReadNode sync_parent(&trans);
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (sync_parent.InitByIdLookup(sync_parent_id) !=
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            syncer::BaseNode::INIT_OK) {
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return unrecoverable_error_handler_->CreateAndUploadError(
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          FROM_HERE,
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "Failed to lookup node.",
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          model_type());
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only folder nodes are pushed on to the stack.
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(sync_parent.GetIsFolder());
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BookmarkNode* parent_node = GetChromeNodeFromSyncId(sync_parent_id);
5205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (!parent_node) {
5215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return unrecoverable_error_handler_->CreateAndUploadError(
5225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          FROM_HERE, "Failed to find bookmark node for sync id.", model_type());
5235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(parent_node->is_folder());
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BookmarkNodeFinder node_finder(parent_node);
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
528868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    std::vector<int64> children;
529868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    sync_parent.GetChildIds(&children);
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int index = 0;
531868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    for (std::vector<int64>::const_iterator it = children.begin();
532868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         it != children.end(); ++it) {
533868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      int64 sync_child_id = *it;
534868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      syncer::ReadNode sync_child_node(&trans);
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (sync_child_node.InitByIdLookup(sync_child_id) !=
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              syncer::BaseNode::INIT_OK) {
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return unrecoverable_error_handler_->CreateAndUploadError(
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            FROM_HERE,
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            "Failed to lookup node.",
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            model_type());
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const BookmarkNode* child_node = NULL;
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      child_node = node_finder.FindBookmarkNode(
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          GURL(sync_child_node.GetBookmarkSpecifics().url()),
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          sync_child_node.GetTitle(),
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          sync_child_node.GetIsFolder());
5487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if (child_node) {
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Associate(child_node, sync_child_id);
5507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
5517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        // All bookmarks are currently modified at association time, even if
5527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        // nothing has changed.
5537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        // TODO(sync): Only modify the bookmark model if necessary.
5547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        BookmarkChangeProcessor::UpdateBookmarkWithSyncData(
5557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            sync_child_node, bookmark_model_, child_node, profile_);
5567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        bookmark_model_->Move(child_node, parent_node, index);
5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        local_merge_result->set_num_items_modified(
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            local_merge_result->num_items_modified() + 1);
5597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      } else {
5605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        DCHECK_LE(index, parent_node->child_count());
5617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        child_node = BookmarkChangeProcessor::CreateBookmarkNode(
5627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            &sync_child_node, parent_node, bookmark_model_, profile_, index);
5635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if (!child_node) {
5645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          return unrecoverable_error_handler_->CreateAndUploadError(
5655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              FROM_HERE, "Failed to create bookmark node.", model_type());
5665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        }
5675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        Associate(child_node, sync_child_id);
5687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        local_merge_result->set_num_items_added(
5697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            local_merge_result->num_items_added() + 1);
5702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (sync_child_node.GetIsFolder())
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dfs_stack.push(sync_child_id);
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++index;
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // At this point all the children nodes of the parent sync node have
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // corresponding children in the parent bookmark node and they are all in
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the right positions: from 0 to index - 1.
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // So the children starting from index in the parent bookmark node are the
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // ones that are not present in the parent sync node. So create them.
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = index; i < parent_node->child_count(); ++i) {
582868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      int64 sync_child_id = BookmarkChangeProcessor::CreateSyncNode(
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          parent_node, bookmark_model_, i, &trans, this,
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          unrecoverable_error_handler_);
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (syncer::kInvalidId == sync_child_id) {
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return unrecoverable_error_handler_->CreateAndUploadError(
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            FROM_HERE,
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            "Failed to create sync node.",
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            model_type());
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      syncer_merge_result->set_num_items_added(
5922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          syncer_merge_result->num_items_added() + 1);
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (parent_node->GetChild(i)->is_folder())
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dfs_stack.push(sync_child_id);
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  local_merge_result->set_num_items_after_association(
5992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      bookmark_model_->root_node()->GetTotalNodeCount());
6002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer_merge_result->set_num_items_after_association(
6012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      bm_root.GetTotalNodeCount());
6022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return syncer::SyncError();
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)struct FolderInfo {
6072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FolderInfo(const BookmarkNode* f, const BookmarkNode* p, int64 id)
6082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      : folder(f), parent(p), sync_id(id) {}
6092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BookmarkNode* folder;
6102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BookmarkNode* parent;
6112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int64 sync_id;
6122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
6132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)typedef std::vector<FolderInfo> FolderInfoList;
6142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int64 BookmarkModelAssociator::ApplyDeletesFromSyncJournal(
6162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::BaseTransaction* trans) {
6172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int64 num_bookmark_deleted = 0;
6182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::BookmarkDeleteJournalList bk_delete_journals;
6202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::DeleteJournal::GetBookmarkDeleteJournals(trans, &bk_delete_journals);
6212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (bk_delete_journals.empty())
6222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 0;
6232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t num_journals_unmatched = bk_delete_journals.size();
6242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Check bookmark model from top to bottom.
6262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::stack<const BookmarkNode*> dfs_stack;
6272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  dfs_stack.push(bookmark_model_->bookmark_bar_node());
6282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  dfs_stack.push(bookmark_model_->other_node());
6292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (expect_mobile_bookmarks_folder_)
6302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    dfs_stack.push(bookmark_model_->mobile_node());
631f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Note: the root node may have additional extra nodes. Currently none of
632f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // them are meant to sync.
6332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Remember folders that match delete journals in first pass but don't delete
6352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // them in case there are bookmarks left under them. After non-folder
6362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // bookmarks are removed in first pass, recheck the folders in reverse order
6372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // to remove empty ones.
6382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FolderInfoList folders_matched;
6392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  while (!dfs_stack.empty()) {
6402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const BookmarkNode* parent = dfs_stack.top();
6412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    dfs_stack.pop();
6422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BookmarkNodeFinder finder(parent);
6442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Iterate through journals from back to front. Remove matched journal by
6452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // moving an unmatched journal at the tail to its position so that we can
6462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // read unmatched journals off the head in next loop.
6472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int i = num_journals_unmatched - 1; i >= 0; --i) {
6482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const BookmarkNode* child = finder.FindBookmarkNode(
6492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          GURL(bk_delete_journals[i].specifics.bookmark().url()),
6502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          bk_delete_journals[i].specifics.bookmark().title(),
6512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          bk_delete_journals[i].is_folder);
6522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (child) {
6532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (child->is_folder()) {
6542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Remember matched folder without removing and delete only empty
6552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // ones later.
6562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          folders_matched.push_back(FolderInfo(child, parent,
6572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                               bk_delete_journals[i].id));
6582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else {
6592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          bookmark_model_->Remove(parent, parent->GetIndexOf(child));
6602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          ++num_bookmark_deleted;
6612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
6622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Move unmatched journal here and decrement counter.
6632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        bk_delete_journals[i] = bk_delete_journals[--num_journals_unmatched];
6642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (num_journals_unmatched == 0)
6672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
6682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int i = 0; i < parent->child_count(); ++i) {
6702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (parent->GetChild(i)->is_folder())
6712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        dfs_stack.push(parent->GetChild(i));
6722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Ids of sync nodes not found in bookmark model, meaning the deletions are
6762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // persisted and correponding delete journals can be dropped.
6772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::set<int64> journals_to_purge;
6782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Remove empty folders from bottom to top.
6802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (FolderInfoList::reverse_iterator it = folders_matched.rbegin();
6812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      it != folders_matched.rend(); ++it) {
6822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (it->folder->child_count() == 0) {
6832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      bookmark_model_->Remove(it->parent, it->parent->GetIndexOf(it->folder));
6842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ++num_bookmark_deleted;
6852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
6862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Keep non-empty folder and remove its journal so that it won't match
6872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // again in the future.
6882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      journals_to_purge.insert(it->sync_id);
6892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Purge unmatched journals.
6932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < num_journals_unmatched; ++i)
6942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    journals_to_purge.insert(bk_delete_journals[i].id);
6952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::DeleteJournal::PurgeDeleteJournals(trans, journals_to_purge);
6962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return num_bookmark_deleted;
6982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkModelAssociator::PostPersistAssociationsTask() {
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No need to post a task if a task is already pending.
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (weak_factory_.HasWeakPtrs())
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
70490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::MessageLoop::current()->PostTask(
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          &BookmarkModelAssociator::PersistAssociations,
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          weak_factory_.GetWeakPtr()));
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkModelAssociator::PersistAssociations() {
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If there are no dirty associations we have nothing to do. We handle this
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // explicity instead of letting the for loop do it to avoid creating a write
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // transaction in this case.
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (dirty_associations_sync_ids_.empty()) {
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(id_map_.empty());
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(id_map_inverse_.empty());
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 new_version = syncer::syncable::kInvalidTransactionVersion;
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<const BookmarkNode*> bnodes;
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::WriteTransaction trans(FROM_HERE, user_share_, &new_version);
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DirtyAssociationsSyncIds::iterator iter;
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (iter = dirty_associations_sync_ids_.begin();
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         iter != dirty_associations_sync_ids_.end();
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ++iter) {
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int64 sync_id = *iter;
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      syncer::WriteNode sync_node(&trans);
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (sync_node.InitByIdLookup(sync_id) != syncer::BaseNode::INIT_OK) {
7326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        syncer::SyncError error(
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            FROM_HERE,
7346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            syncer::SyncError::DATATYPE_ERROR,
7356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            "Could not lookup bookmark node for ID persistence.",
7366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            syncer::BOOKMARKS);
7376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        unrecoverable_error_handler_->OnSingleDataTypeUnrecoverableError(error);
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const BookmarkNode* node = GetChromeNodeFromSyncId(sync_id);
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (node && sync_node.GetExternalId() != node->id()) {
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sync_node.SetExternalId(node->id());
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bnodes.push_back(node);
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dirty_associations_sync_ids_.clear();
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarkChangeProcessor::UpdateTransactionVersion(new_version,
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                    bookmark_model_,
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                    bnodes);
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BookmarkModelAssociator::CryptoReadyIfNecessary() {
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We only access the cryptographer while holding a transaction.
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadTransaction trans(FROM_HERE, user_share_);
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const syncer::ModelTypeSet encrypted_types = trans.GetEncryptedTypes();
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !encrypted_types.Has(syncer::BOOKMARKS) ||
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      trans.GetCryptographer()->is_ready();
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
762eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochsyncer::SyncError BookmarkModelAssociator::CheckModelSyncState(
7637d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    syncer::SyncMergeResult* local_merge_result,
7647d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    syncer::SyncMergeResult* syncer_merge_result) const {
765f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int64 native_version =
766f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      bookmark_model_->root_node()->sync_transaction_version();
767f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (native_version != syncer::syncable::kInvalidTransactionVersion) {
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::ReadTransaction trans(FROM_HERE, user_share_);
7697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    local_merge_result->set_pre_association_version(native_version);
7707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
7717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    int64 sync_version = trans.GetModelVersion(syncer::BOOKMARKS);
7727d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    syncer_merge_result->set_pre_association_version(sync_version);
7737d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
7747d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if (native_version != sync_version) {
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UMA_HISTOGRAM_ENUMERATION("Sync.LocalModelOutOfSync",
7762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                ModelTypeToHistogramInt(syncer::BOOKMARKS),
7772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                syncer::MODEL_TYPE_COUNT);
778eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Clear version on bookmark model so that we only report error once.
780f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      bookmark_model_->SetNodeSyncTransactionVersion(
781f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          bookmark_model_->root_node(),
782f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          syncer::syncable::kInvalidTransactionVersion);
783eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
784eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // If the native version is higher, there was a sync persistence failure,
785eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // and we need to delay association until after a GetUpdates.
786eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if (sync_version < native_version) {
787eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        std::string message = base::StringPrintf(
788eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            "Native version (%" PRId64 ") does not match sync version (%"
789eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                PRId64 ")",
790eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            native_version,
791eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            sync_version);
792eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        return syncer::SyncError(FROM_HERE,
793eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                 syncer::SyncError::PERSISTENCE_ERROR,
794eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                 message,
795eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                 syncer::BOOKMARKS);
796eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      }
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
799eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return syncer::SyncError();
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace browser_sync
803