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/test/integration/themes_helper.h"
6
7#include "base/callback.h"
8#include "base/logging.h"
9#include "base/strings/string_number_conversions.h"
10#include "base/strings/stringprintf.h"
11#include "chrome/browser/chrome_notification_types.h"
12#include "chrome/browser/sync/test/integration/status_change_checker.h"
13#include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
14#include "chrome/browser/sync/test/integration/sync_extension_helper.h"
15#include "chrome/browser/themes/theme_service.h"
16#include "chrome/browser/themes/theme_service_factory.h"
17#include "components/crx_file/id_util.h"
18#include "content/public/browser/notification_observer.h"
19#include "content/public/browser/notification_registrar.h"
20#include "content/public/browser/notification_source.h"
21#include "extensions/common/manifest.h"
22
23using sync_datatype_helper::test;
24
25namespace {
26
27// Make a name to pass to an extension helper.
28std::string MakeName(int index) {
29  return "faketheme" + base::IntToString(index);
30}
31
32ThemeService* GetThemeService(Profile* profile) {
33  return ThemeServiceFactory::GetForProfile(profile);
34}
35
36}  // namespace
37
38namespace themes_helper {
39
40std::string GetCustomTheme(int index) {
41  return crx_file::id_util::GenerateId(MakeName(index));
42}
43
44std::string GetThemeID(Profile* profile) {
45  return GetThemeService(profile)->GetThemeID();
46}
47
48bool UsingCustomTheme(Profile* profile) {
49  return GetThemeID(profile) != ThemeService::kDefaultThemeID;
50}
51
52bool UsingDefaultTheme(Profile* profile) {
53  return GetThemeService(profile)->UsingDefaultTheme();
54}
55
56bool UsingSystemTheme(Profile* profile) {
57  return GetThemeService(profile)->UsingSystemTheme();
58}
59
60bool ThemeIsPendingInstall(Profile* profile, const std::string& id) {
61  return SyncExtensionHelper::GetInstance()->
62      IsExtensionPendingInstallForSync(profile, id);
63}
64
65void UseCustomTheme(Profile* profile, int index) {
66  SyncExtensionHelper::GetInstance()->InstallExtension(
67      profile, MakeName(index), extensions::Manifest::TYPE_THEME);
68}
69
70void UseDefaultTheme(Profile* profile) {
71  GetThemeService(profile)->UseDefaultTheme();
72}
73
74void UseSystemTheme(Profile* profile) {
75  GetThemeService(profile)->UseSystemTheme();
76}
77
78namespace {
79
80// Helper to wait until the specified theme is pending for install on the
81// specified profile.
82//
83// The themes sync integration tests don't actually install any custom themes,
84// but they do occasionally check that the ThemeService attempts to install
85// synced themes.
86class ThemePendingInstallChecker : public StatusChangeChecker,
87                                   public content::NotificationObserver {
88 public:
89  ThemePendingInstallChecker(Profile* profile, const std::string& theme);
90  virtual ~ThemePendingInstallChecker();
91
92  // Implementation of StatusChangeChecker.
93  virtual std::string GetDebugMessage() const OVERRIDE;
94  virtual bool IsExitConditionSatisfied() OVERRIDE;
95
96  // Implementation of content::NotificationObserver.
97  virtual void Observe(int type,
98                       const content::NotificationSource& source,
99                       const content::NotificationDetails& details) OVERRIDE;
100
101  // Waits until the condition to be met or a timeout occurs.
102  void Wait();
103
104 private:
105  Profile* profile_;
106  const std::string& theme_;
107
108  content::NotificationRegistrar registrar_;
109};
110
111ThemePendingInstallChecker::ThemePendingInstallChecker(Profile* profile,
112                                                       const std::string& theme)
113    : profile_(profile), theme_(theme) {
114}
115
116ThemePendingInstallChecker::~ThemePendingInstallChecker() {
117}
118
119std::string ThemePendingInstallChecker::GetDebugMessage() const {
120  return base::StringPrintf("Waiting for pending theme to be '%s'",
121                            theme_.c_str());
122}
123
124bool ThemePendingInstallChecker::IsExitConditionSatisfied() {
125  return ThemeIsPendingInstall(profile_, theme_);
126}
127
128void ThemePendingInstallChecker::Observe(
129    int type,
130    const content::NotificationSource& source,
131    const content::NotificationDetails& details) {
132  DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_UPDATING_STARTED, type);
133  CheckExitCondition();
134}
135
136void ThemePendingInstallChecker::Wait() {
137  // We'll check to see if the condition is met whenever the extension system
138  // tries to contact the web store.
139  registrar_.Add(this,
140                 extensions::NOTIFICATION_EXTENSION_UPDATING_STARTED,
141                 content::Source<Profile>(profile_));
142
143  if (IsExitConditionSatisfied()) {
144    return;
145  }
146
147  StartBlockingWait();
148}
149
150}  // namespace
151
152bool AwaitThemeIsPendingInstall(Profile* profile, const std::string& theme) {
153  ThemePendingInstallChecker checker(profile, theme);
154  checker.Wait();
155  return !checker.TimedOut();
156}
157
158namespace {
159
160// Helper to wait until a given condition is met, checking every time the
161// current theme changes.
162//
163// The |exit_condition_| closure may be invoked zero or more times.
164class ThemeConditionChecker : public StatusChangeChecker,
165                              public content::NotificationObserver {
166 public:
167  ThemeConditionChecker(Profile* profile,
168                        const std::string& debug_message_,
169                        base::Callback<bool(ThemeService*)> exit_condition);
170  virtual ~ThemeConditionChecker();
171
172  // Implementation of StatusChangeChecker.
173  virtual std::string GetDebugMessage() const OVERRIDE;
174  virtual bool IsExitConditionSatisfied() OVERRIDE;
175
176  // Implementation of content::NotificationObserver.
177  virtual void Observe(int type,
178                       const content::NotificationSource& source,
179                       const content::NotificationDetails& details) OVERRIDE;
180
181  // Waits until the condition to be met or a timeout occurs.
182  void Wait();
183
184 private:
185  Profile* profile_;
186  const std::string debug_message_;
187  base::Callback<bool(ThemeService*)> exit_condition_;
188
189  content::NotificationRegistrar registrar_;
190};
191
192ThemeConditionChecker::ThemeConditionChecker(
193    Profile* profile,
194    const std::string& debug_message,
195    base::Callback<bool(ThemeService*)> exit_condition)
196    : profile_(profile),
197      debug_message_(debug_message),
198      exit_condition_(exit_condition) {
199}
200
201ThemeConditionChecker::~ThemeConditionChecker() {
202}
203
204std::string ThemeConditionChecker::GetDebugMessage() const {
205  return debug_message_;
206}
207
208bool ThemeConditionChecker::IsExitConditionSatisfied() {
209  return exit_condition_.Run(GetThemeService(profile_));
210}
211
212void ThemeConditionChecker::Observe(
213    int type,
214    const content::NotificationSource& source,
215    const content::NotificationDetails& details) {
216  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
217  CheckExitCondition();
218}
219
220void ThemeConditionChecker::Wait() {
221  registrar_.Add(this,
222                 chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
223                 content::Source<ThemeService>(GetThemeService(profile_)));
224
225  if (IsExitConditionSatisfied()) {
226    return;
227  }
228
229  StartBlockingWait();
230}
231
232// Helper function to let us bind this functionality into a base::Callback.
233bool UsingSystemThemeFunc(ThemeService* theme_service) {
234  return theme_service->UsingSystemTheme();
235}
236
237// Helper function to let us bind this functionality into a base::Callback.
238bool UsingDefaultThemeFunc(ThemeService* theme_service) {
239  return theme_service->UsingDefaultTheme();
240}
241
242}  // namespace
243
244bool AwaitUsingSystemTheme(Profile* profile) {
245  ThemeConditionChecker checker(
246      profile,
247      std::string("Waiting until profile is using system theme"),
248      base::Bind(&UsingSystemThemeFunc));
249  checker.Wait();
250  return !checker.TimedOut();
251}
252
253bool AwaitUsingDefaultTheme(Profile* profile) {
254  ThemeConditionChecker checker(
255      profile,
256      std::string("Waiting until profile is using default theme"),
257      base::Bind(&UsingDefaultThemeFunc));
258  checker.Wait();
259  return !checker.TimedOut();
260}
261
262}  // namespace themes_helper
263