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
3// found in the LICENSE file.
4
5#include "chrome/browser/sync/glue/theme_model_associator.h"
6
7#include "base/basictypes.h"
8#include "base/logging.h"
9#include "base/utf_string_conversions.h"
10#include "chrome/browser/sync/engine/syncapi.h"
11#include "chrome/browser/sync/glue/sync_backend_host.h"
12#include "chrome/browser/sync/glue/theme_util.h"
13#include "chrome/browser/sync/profile_sync_service.h"
14#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
15
16namespace browser_sync {
17
18namespace {
19
20static const char kThemesTag[] = "google_chrome_themes";
21static const char kCurrentThemeNodeTitle[] = "Current Theme";
22
23static const char kNoThemesFolderError[] =
24    "Server did not create the top-level themes node. We "
25    "might be running against an out-of-date server.";
26
27}  // namespace
28
29ThemeModelAssociator::ThemeModelAssociator(
30    ProfileSyncService* sync_service)
31    : sync_service_(sync_service) {
32  DCHECK(sync_service_);
33}
34
35ThemeModelAssociator::~ThemeModelAssociator() {}
36
37bool ThemeModelAssociator::AssociateModels() {
38  sync_api::WriteTransaction trans(sync_service_->GetUserShare());
39  sync_api::ReadNode root(&trans);
40  if (!root.InitByTagLookup(kThemesTag)) {
41    LOG(ERROR) << kNoThemesFolderError;
42    return false;
43  }
44
45  Profile* profile = sync_service_->profile();
46  sync_api::WriteNode node(&trans);
47  // TODO(akalin): When we have timestamps, we may want to do
48  // something more intelligent than preferring the sync data over our
49  // local data.
50  if (node.InitByClientTagLookup(syncable::THEMES, kCurrentThemeClientTag)) {
51    // Update the current theme from the sync data.
52    // TODO(akalin): If the sync data does not have
53    // use_system_theme_by_default and we do, update that flag on the
54    // sync data.
55    sync_pb::ThemeSpecifics theme_specifics = node.GetThemeSpecifics();
56    if (UpdateThemeSpecificsOrSetCurrentThemeIfNecessary(profile,
57                                                         &theme_specifics))
58      node.SetThemeSpecifics(theme_specifics);
59  } else {
60    // Set the sync data from the current theme.
61    sync_api::WriteNode node(&trans);
62    if (!node.InitUniqueByCreation(syncable::THEMES, root,
63                                   kCurrentThemeClientTag)) {
64      LOG(ERROR) << "Could not create current theme node.";
65      return false;
66    }
67    node.SetIsFolder(false);
68    node.SetTitle(UTF8ToWide(kCurrentThemeNodeTitle));
69    sync_pb::ThemeSpecifics theme_specifics;
70    GetThemeSpecificsFromCurrentTheme(profile, &theme_specifics);
71    node.SetThemeSpecifics(theme_specifics);
72  }
73  return true;
74}
75
76bool ThemeModelAssociator::DisassociateModels() {
77  // We don't maintain any association state, so nothing to do.
78  return true;
79}
80
81bool ThemeModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
82  DCHECK(has_nodes);
83  *has_nodes = false;
84  sync_api::ReadTransaction trans(sync_service_->GetUserShare());
85  sync_api::ReadNode root(&trans);
86  if (!root.InitByTagLookup(kThemesTag)) {
87    LOG(ERROR) << kNoThemesFolderError;
88    return false;
89  }
90  // The sync model has user created nodes iff the themes folder has
91  // any children.
92  *has_nodes = root.GetFirstChildId() != sync_api::kInvalidId;
93  return true;
94}
95
96bool ThemeModelAssociator::CryptoReadyIfNecessary() {
97  // We only access the cryptographer while holding a transaction.
98  sync_api::ReadTransaction trans(sync_service_->GetUserShare());
99  syncable::ModelTypeSet encrypted_types;
100  sync_service_->GetEncryptedDataTypes(&encrypted_types);
101  return encrypted_types.count(syncable::THEMES) == 0 ||
102         sync_service_->IsCryptographerReady(&trans);
103}
104
105}  // namespace browser_sync
106