1// Copyright (c) 2012 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/themes/theme_service.h"
6
7#include "base/files/file_util.h"
8#include "base/path_service.h"
9#include "chrome/browser/chrome_notification_types.h"
10#include "chrome/browser/extensions/extension_service.h"
11#include "chrome/browser/extensions/extension_service_test_base.h"
12#include "chrome/browser/extensions/unpacked_installer.h"
13#include "chrome/browser/supervised_user/supervised_user_service.h"
14#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
15#include "chrome/browser/themes/custom_theme_supplier.h"
16#include "chrome/browser/themes/theme_service_factory.h"
17#include "chrome/common/chrome_paths.h"
18#include "chrome/common/pref_names.h"
19#include "chrome/test/base/testing_browser_process.h"
20#include "chrome/test/base/testing_profile.h"
21#include "chrome/test/base/testing_profile_manager.h"
22#include "content/public/test/test_utils.h"
23#include "extensions/browser/extension_registry.h"
24#include "extensions/browser/uninstall_reason.h"
25#include "extensions/common/extension.h"
26#include "testing/gtest/include/gtest/gtest.h"
27
28using extensions::ExtensionRegistry;
29
30namespace theme_service_internal {
31
32class ThemeServiceTest : public extensions::ExtensionServiceTestBase {
33 public:
34  ThemeServiceTest() : is_supervised_(false),
35                       registry_(NULL) {}
36  virtual ~ThemeServiceTest() {}
37
38  // Moves a minimal theme to |temp_dir_path| and unpacks it from that
39  // directory.
40  std::string LoadUnpackedThemeAt(const base::FilePath& temp_dir) {
41    base::FilePath dst_manifest_path = temp_dir.AppendASCII("manifest.json");
42    base::FilePath test_data_dir;
43    EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
44    base::FilePath src_manifest_path =
45        test_data_dir.AppendASCII("extensions/theme_minimal/manifest.json");
46    EXPECT_TRUE(base::CopyFile(src_manifest_path, dst_manifest_path));
47
48    scoped_refptr<extensions::UnpackedInstaller> installer(
49        extensions::UnpackedInstaller::Create(service_));
50    content::WindowedNotificationObserver observer(
51        extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
52        content::Source<Profile>(profile_.get()));
53    installer->Load(temp_dir);
54    observer.Wait();
55
56    std::string extension_id =
57        content::Details<extensions::Extension>(observer.details())->id();
58
59    // Let the ThemeService finish creating the theme pack.
60    base::MessageLoop::current()->RunUntilIdle();
61
62    return extension_id;
63  }
64
65  // Update the theme with |extension_id|.
66  void UpdateUnpackedTheme(const std::string& extension_id) {
67    int updated_notification =
68        service_->IsExtensionEnabled(extension_id)
69            ? extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED
70            : extensions::NOTIFICATION_EXTENSION_UPDATE_DISABLED;
71
72    const base::FilePath& path =
73        service_->GetInstalledExtension(extension_id)->path();
74
75    scoped_refptr<extensions::UnpackedInstaller> installer(
76        extensions::UnpackedInstaller::Create(service_));
77    content::WindowedNotificationObserver observer(updated_notification,
78        content::Source<Profile>(profile_.get()));
79    installer->Load(path);
80    observer.Wait();
81
82    // Let the ThemeService finish creating the theme pack.
83    base::MessageLoop::current()->RunUntilIdle();
84  }
85
86  virtual void SetUp() {
87    extensions::ExtensionServiceTestBase::SetUp();
88    extensions::ExtensionServiceTestBase::ExtensionServiceInitParams params =
89        CreateDefaultInitParams();
90    params.profile_is_supervised = is_supervised_;
91    InitializeExtensionService(params);
92    service_->Init();
93    registry_ = ExtensionRegistry::Get(profile_.get());
94    ASSERT_TRUE(registry_);
95  }
96
97  const CustomThemeSupplier* get_theme_supplier(ThemeService* theme_service) {
98    return theme_service->get_theme_supplier();
99  }
100
101 protected:
102  bool is_supervised_;
103  ExtensionRegistry* registry_;
104
105};
106
107// Installs then uninstalls a theme and makes sure that the ThemeService
108// reverts to the default theme after the uninstall.
109TEST_F(ThemeServiceTest, ThemeInstallUninstall) {
110  ThemeService* theme_service =
111      ThemeServiceFactory::GetForProfile(profile_.get());
112  theme_service->UseDefaultTheme();
113  // Let the ThemeService uninstall unused themes.
114  base::MessageLoop::current()->RunUntilIdle();
115
116  base::ScopedTempDir temp_dir;
117  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
118  const std::string& extension_id = LoadUnpackedThemeAt(temp_dir.path());
119  EXPECT_FALSE(theme_service->UsingDefaultTheme());
120  EXPECT_EQ(extension_id, theme_service->GetThemeID());
121
122  // Now uninstall the extension, should revert to the default theme.
123  service_->UninstallExtension(extension_id,
124                               extensions::UNINSTALL_REASON_FOR_TESTING,
125                               base::Bind(&base::DoNothing),
126                               NULL);
127  EXPECT_TRUE(theme_service->UsingDefaultTheme());
128}
129
130// Test that a theme extension is disabled when not in use. A theme may be
131// installed but not in use if it there is an infobar to revert to the previous
132// theme.
133TEST_F(ThemeServiceTest, DisableUnusedTheme) {
134  ThemeService* theme_service =
135      ThemeServiceFactory::GetForProfile(profile_.get());
136  theme_service->UseDefaultTheme();
137  // Let the ThemeService uninstall unused themes.
138  base::MessageLoop::current()->RunUntilIdle();
139
140  base::ScopedTempDir temp_dir1;
141  ASSERT_TRUE(temp_dir1.CreateUniqueTempDir());
142  base::ScopedTempDir temp_dir2;
143  ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
144
145  // 1) Installing a theme should disable the previously active theme.
146  const std::string& extension1_id = LoadUnpackedThemeAt(temp_dir1.path());
147  EXPECT_FALSE(theme_service->UsingDefaultTheme());
148  EXPECT_EQ(extension1_id, theme_service->GetThemeID());
149  EXPECT_TRUE(service_->IsExtensionEnabled(extension1_id));
150
151  // Show an infobar to prevent the current theme from being uninstalled.
152  theme_service->OnInfobarDisplayed();
153
154  const std::string& extension2_id = LoadUnpackedThemeAt(temp_dir2.path());
155  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
156  EXPECT_TRUE(service_->IsExtensionEnabled(extension2_id));
157  EXPECT_TRUE(registry_->GetExtensionById(extension1_id,
158                                          ExtensionRegistry::DISABLED));
159
160  // 2) Enabling a disabled theme extension should swap the current theme.
161  service_->EnableExtension(extension1_id);
162  base::MessageLoop::current()->RunUntilIdle();
163  EXPECT_EQ(extension1_id, theme_service->GetThemeID());
164  EXPECT_TRUE(service_->IsExtensionEnabled(extension1_id));
165  EXPECT_TRUE(registry_->GetExtensionById(extension2_id,
166                                          ExtensionRegistry::DISABLED));
167
168  // 3) Using SetTheme() with a disabled theme should enable and set the
169  // theme. This is the case when the user reverts to the previous theme
170  // via an infobar.
171  const extensions::Extension* extension2 =
172      service_->GetInstalledExtension(extension2_id);
173  theme_service->SetTheme(extension2);
174  base::MessageLoop::current()->RunUntilIdle();
175  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
176  EXPECT_TRUE(service_->IsExtensionEnabled(extension2_id));
177  EXPECT_TRUE(registry_->GetExtensionById(extension1_id,
178                                          ExtensionRegistry::DISABLED));
179
180  // 4) Disabling the current theme extension should revert to the default theme
181  // and uninstall any installed theme extensions.
182  theme_service->OnInfobarDestroyed();
183  EXPECT_FALSE(theme_service->UsingDefaultTheme());
184  service_->DisableExtension(extension2_id,
185      extensions::Extension::DISABLE_USER_ACTION);
186  base::MessageLoop::current()->RunUntilIdle();
187  EXPECT_TRUE(theme_service->UsingDefaultTheme());
188  EXPECT_FALSE(service_->GetInstalledExtension(extension1_id));
189  EXPECT_FALSE(service_->GetInstalledExtension(extension2_id));
190}
191
192// Test the ThemeService's behavior when a theme is upgraded.
193TEST_F(ThemeServiceTest, ThemeUpgrade) {
194  // Setup.
195  ThemeService* theme_service =
196      ThemeServiceFactory::GetForProfile(profile_.get());
197  theme_service->UseDefaultTheme();
198  // Let the ThemeService uninstall unused themes.
199  base::MessageLoop::current()->RunUntilIdle();
200
201  theme_service->OnInfobarDisplayed();
202
203  base::ScopedTempDir temp_dir1;
204  ASSERT_TRUE(temp_dir1.CreateUniqueTempDir());
205  base::ScopedTempDir temp_dir2;
206  ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
207
208  const std::string& extension1_id = LoadUnpackedThemeAt(temp_dir1.path());
209  const std::string& extension2_id = LoadUnpackedThemeAt(temp_dir2.path());
210
211  // Test the initial state.
212  EXPECT_TRUE(registry_->GetExtensionById(extension1_id,
213                                          ExtensionRegistry::DISABLED));
214  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
215
216  // 1) Upgrading the current theme should not revert to the default theme.
217  content::WindowedNotificationObserver theme_change_observer(
218      chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
219      content::Source<ThemeService>(theme_service));
220  UpdateUnpackedTheme(extension2_id);
221
222  // The ThemeService should have sent an theme change notification even though
223  // the id of the current theme did not change.
224  theme_change_observer.Wait();
225
226  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
227  EXPECT_TRUE(registry_->GetExtensionById(extension1_id,
228                                          ExtensionRegistry::DISABLED));
229
230  // 2) Upgrading a disabled theme should not change the current theme.
231  UpdateUnpackedTheme(extension1_id);
232  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
233  EXPECT_TRUE(registry_->GetExtensionById(extension1_id,
234                                          ExtensionRegistry::DISABLED));
235}
236
237class ThemeServiceSupervisedUserTest : public ThemeServiceTest {
238 public:
239  ThemeServiceSupervisedUserTest() {}
240  virtual ~ThemeServiceSupervisedUserTest() {}
241
242  virtual void SetUp() OVERRIDE {
243    is_supervised_ = true;
244    ThemeServiceTest::SetUp();
245  }
246};
247
248// Checks that supervised users have their own default theme.
249TEST_F(ThemeServiceSupervisedUserTest,
250       SupervisedUserThemeReplacesDefaultTheme) {
251  ThemeService* theme_service =
252      ThemeServiceFactory::GetForProfile(profile_.get());
253  theme_service->UseDefaultTheme();
254  EXPECT_TRUE(theme_service->UsingDefaultTheme());
255  EXPECT_TRUE(get_theme_supplier(theme_service));
256  EXPECT_EQ(get_theme_supplier(theme_service)->get_theme_type(),
257            CustomThemeSupplier::SUPERVISED_USER_THEME);
258}
259
260#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
261// Checks that supervised users don't use the system theme even if it is the
262// default. The system theme is only available on Linux.
263TEST_F(ThemeServiceSupervisedUserTest, SupervisedUserThemeReplacesNativeTheme) {
264  profile_->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, true);
265  ThemeService* theme_service =
266      ThemeServiceFactory::GetForProfile(profile_.get());
267  theme_service->UseDefaultTheme();
268  EXPECT_TRUE(theme_service->UsingDefaultTheme());
269  EXPECT_TRUE(get_theme_supplier(theme_service));
270  EXPECT_EQ(get_theme_supplier(theme_service)->get_theme_type(),
271            CustomThemeSupplier::SUPERVISED_USER_THEME);
272}
273#endif
274
275}; // namespace theme_service_internal
276