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