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_util.h"
6
7#include <string>
8
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "chrome/browser/extensions/extension_install_ui.h"
12#include "chrome/browser/extensions/extension_service.h"
13#include "chrome/browser/extensions/extension_updater.h"
14#if defined(TOOLKIT_USES_GTK)
15#include "chrome/browser/ui/gtk/gtk_theme_service.h"
16#endif
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
19#include "chrome/browser/themes/theme_service.h"
20#include "chrome/browser/themes/theme_service_factory.h"
21#include "chrome/common/extensions/extension.h"
22#include "chrome/common/extensions/extension_constants.h"
23#include "googleurl/src/gurl.h"
24
25namespace browser_sync {
26
27const char kCurrentThemeClientTag[] = "current_theme";
28
29namespace {
30
31bool IsSystemThemeDistinctFromDefaultTheme() {
32#if defined(TOOLKIT_USES_GTK)
33  return true;
34#else
35  return false;
36#endif
37}
38
39bool UseSystemTheme(Profile* profile) {
40#if defined(TOOLKIT_USES_GTK)
41  return GtkThemeService::GetFrom(profile)->UseGtkTheme();
42#else
43  return false;
44#endif
45}
46
47}  // namespace
48
49bool AreThemeSpecificsEqual(const sync_pb::ThemeSpecifics& a,
50                            const sync_pb::ThemeSpecifics& b) {
51  return AreThemeSpecificsEqualHelper(
52      a, b, IsSystemThemeDistinctFromDefaultTheme());
53}
54
55bool AreThemeSpecificsEqualHelper(
56    const sync_pb::ThemeSpecifics& a,
57    const sync_pb::ThemeSpecifics& b,
58    bool is_system_theme_distinct_from_default_theme) {
59  if (a.use_custom_theme() != b.use_custom_theme()) {
60    return false;
61  }
62
63  if (a.use_custom_theme()) {
64    // We're using a custom theme, so simply compare IDs since those
65    // are guaranteed unique.
66    return a.custom_theme_id() == b.custom_theme_id();
67  } else if (is_system_theme_distinct_from_default_theme) {
68    // We're not using a custom theme, but we care about system
69    // vs. default.
70    return a.use_system_theme_by_default() == b.use_system_theme_by_default();
71  } else {
72    // We're not using a custom theme, and we don't care about system
73    // vs. default.
74    return true;
75  }
76}
77
78namespace {
79
80bool IsTheme(const Extension& extension) {
81  return extension.is_theme();
82}
83
84}  // namespace
85
86void SetCurrentThemeFromThemeSpecifics(
87    const sync_pb::ThemeSpecifics& theme_specifics,
88    Profile* profile) {
89  DCHECK(profile);
90  if (theme_specifics.use_custom_theme()) {
91    // TODO(akalin): Figure out what to do about third-party themes
92    // (i.e., those not on either Google gallery).
93    std::string id(theme_specifics.custom_theme_id());
94    GURL update_url(theme_specifics.custom_theme_update_url());
95    VLOG(1) << "Applying theme " << id << " with update_url " << update_url;
96    ExtensionServiceInterface* extensions_service =
97        profile->GetExtensionService();
98    CHECK(extensions_service);
99    const Extension* extension = extensions_service->GetExtensionById(id, true);
100    if (extension) {
101      if (!extension->is_theme()) {
102        VLOG(1) << "Extension " << id << " is not a theme; aborting";
103        return;
104      }
105      if (!extensions_service->IsExtensionEnabled(id)) {
106        VLOG(1) << "Theme " << id << " is not enabled; aborting";
107        return;
108      }
109      // Get previous theme info before we set the new theme.
110      std::string previous_theme_id;
111      {
112        const Extension* current_theme =
113            ThemeServiceFactory::GetThemeForProfile(profile);
114        if (current_theme) {
115          DCHECK(current_theme->is_theme());
116          previous_theme_id = current_theme->id();
117        }
118      }
119      bool previous_use_system_theme = UseSystemTheme(profile);
120      // An enabled theme extension with the given id was found, so
121      // just set the current theme to it.
122      ThemeServiceFactory::GetForProfile(profile)->SetTheme(extension);
123      // Pretend the theme was just installed.
124      ExtensionInstallUI::ShowThemeInfoBar(
125          previous_theme_id, previous_use_system_theme,
126          extension, profile);
127    } else {
128      // No extension with this id exists -- we must install it; we do
129      // so by adding it as a pending extension and then triggering an
130      // auto-update cycle.
131      // Themes don't need to install silently as they just pop up an
132      // informational dialog after installation instead of a
133      // confirmation dialog.
134      const bool kInstallSilently = false;
135      const bool kEnableOnInstall = true;
136      const bool kEnableIncognitoOnInstall = false;
137      extensions_service->pending_extension_manager()->AddFromSync(
138          id, update_url, &IsTheme,
139          kInstallSilently, kEnableOnInstall, kEnableIncognitoOnInstall);
140      extensions_service->CheckForUpdatesSoon();
141    }
142  } else if (theme_specifics.use_system_theme_by_default()) {
143    ThemeServiceFactory::GetForProfile(profile)->SetNativeTheme();
144  } else {
145    ThemeServiceFactory::GetForProfile(profile)->UseDefaultTheme();
146  }
147}
148
149bool UpdateThemeSpecificsOrSetCurrentThemeIfNecessary(
150    Profile* profile, sync_pb::ThemeSpecifics* theme_specifics) {
151  if (!theme_specifics->use_custom_theme() &&
152      (ThemeServiceFactory::GetThemeForProfile(profile) ||
153       (UseSystemTheme(profile) &&
154        IsSystemThemeDistinctFromDefaultTheme()))) {
155    GetThemeSpecificsFromCurrentTheme(profile, theme_specifics);
156    return true;
157  } else {
158    SetCurrentThemeFromThemeSpecificsIfNecessary(*theme_specifics, profile);
159    return false;
160  }
161}
162
163void GetThemeSpecificsFromCurrentTheme(
164    Profile* profile,
165    sync_pb::ThemeSpecifics* theme_specifics) {
166  DCHECK(profile);
167  const Extension* current_theme =
168      ThemeServiceFactory::GetThemeForProfile(profile);
169  if (current_theme) {
170    DCHECK(current_theme->is_theme());
171  }
172  GetThemeSpecificsFromCurrentThemeHelper(
173      current_theme,
174      IsSystemThemeDistinctFromDefaultTheme(),
175      UseSystemTheme(profile),
176      theme_specifics);
177}
178
179void GetThemeSpecificsFromCurrentThemeHelper(
180    const Extension* current_theme,
181    bool is_system_theme_distinct_from_default_theme,
182    bool use_system_theme_by_default,
183    sync_pb::ThemeSpecifics* theme_specifics) {
184  bool use_custom_theme = (current_theme != NULL);
185  theme_specifics->set_use_custom_theme(use_custom_theme);
186  if (is_system_theme_distinct_from_default_theme) {
187    theme_specifics->set_use_system_theme_by_default(
188        use_system_theme_by_default);
189  } else {
190    DCHECK(!use_system_theme_by_default);
191  }
192  if (use_custom_theme) {
193    DCHECK(current_theme);
194    DCHECK(current_theme->is_theme());
195    theme_specifics->set_custom_theme_name(current_theme->name());
196    theme_specifics->set_custom_theme_id(current_theme->id());
197    theme_specifics->set_custom_theme_update_url(
198        current_theme->update_url().spec());
199  } else {
200    DCHECK(!current_theme);
201    theme_specifics->clear_custom_theme_name();
202    theme_specifics->clear_custom_theme_id();
203    theme_specifics->clear_custom_theme_update_url();
204  }
205}
206
207void SetCurrentThemeFromThemeSpecificsIfNecessary(
208    const sync_pb::ThemeSpecifics& theme_specifics, Profile* profile) {
209  DCHECK(profile);
210  sync_pb::ThemeSpecifics old_theme_specifics;
211  GetThemeSpecificsFromCurrentTheme(profile, &old_theme_specifics);
212  if (!AreThemeSpecificsEqual(old_theme_specifics, theme_specifics)) {
213    SetCurrentThemeFromThemeSpecifics(theme_specifics, profile);
214  }
215}
216
217}  // namespace browser_sync
218