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.
5 #include "chrome/browser/themes/theme_service.h"
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/themes/custom_theme_supplier.h"
14 #include "chrome/browser/themes/theme_service_factory.h"
15 #include "chrome/common/chrome_paths.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/test/base/testing_browser_process.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "chrome/test/base/testing_profile_manager.h"
20 #include "content/public/browser/notification_observer.h"
21 #include "content/public/browser/notification_registrar.h"
22 #include "content/public/test/test_utils.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/test_extension_registry_observer.h"
25 #include "extensions/browser/uninstall_reason.h"
26 #include "extensions/common/extension.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 #if defined(ENABLE_SUPERVISED_USERS)
30 #include "chrome/browser/supervised_user/supervised_user_service.h"
31 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
34 using extensions::ExtensionRegistry
;
36 namespace theme_service_internal
{
38 class ThemeServiceTest
: public extensions::ExtensionServiceTestBase
{
40 ThemeServiceTest() : is_supervised_(false),
42 ~ThemeServiceTest() override
{}
44 // Moves a minimal theme to |temp_dir_path| and unpacks it from that
46 std::string
LoadUnpackedThemeAt(const base::FilePath
& temp_dir
) {
47 base::FilePath dst_manifest_path
= temp_dir
.AppendASCII("manifest.json");
48 base::FilePath test_data_dir
;
49 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &test_data_dir
));
50 base::FilePath src_manifest_path
=
51 test_data_dir
.AppendASCII("extensions/theme_minimal/manifest.json");
52 EXPECT_TRUE(base::CopyFile(src_manifest_path
, dst_manifest_path
));
54 scoped_refptr
<extensions::UnpackedInstaller
> installer(
55 extensions::UnpackedInstaller::Create(service_
));
56 extensions::TestExtensionRegistryObserver
observer(
57 ExtensionRegistry::Get(profile()));
58 installer
->Load(temp_dir
);
59 std::string extension_id
= observer
.WaitForExtensionLoaded()->id();
61 // Let the ThemeService finish creating the theme pack.
62 base::MessageLoop::current()->RunUntilIdle();
67 // Update the theme with |extension_id|.
68 void UpdateUnpackedTheme(const std::string
& extension_id
) {
69 const base::FilePath
& path
=
70 service_
->GetInstalledExtension(extension_id
)->path();
72 scoped_refptr
<extensions::UnpackedInstaller
> installer(
73 extensions::UnpackedInstaller::Create(service_
));
74 if (service_
->IsExtensionEnabled(extension_id
)) {
75 extensions::TestExtensionRegistryObserver
observer(
76 ExtensionRegistry::Get(profile()));
77 installer
->Load(path
);
78 observer
.WaitForExtensionLoaded();
80 content::WindowedNotificationObserver
observer(
81 extensions::NOTIFICATION_EXTENSION_UPDATE_DISABLED
,
82 content::Source
<Profile
>(profile_
.get()));
83 installer
->Load(path
);
87 // Let the ThemeService finish creating the theme pack.
88 base::MessageLoop::current()->RunUntilIdle();
91 void SetUp() override
{
92 extensions::ExtensionServiceTestBase::SetUp();
93 extensions::ExtensionServiceTestBase::ExtensionServiceInitParams params
=
94 CreateDefaultInitParams();
95 params
.profile_is_supervised
= is_supervised_
;
96 InitializeExtensionService(params
);
98 registry_
= ExtensionRegistry::Get(profile_
.get());
99 ASSERT_TRUE(registry_
);
102 const CustomThemeSupplier
* get_theme_supplier(ThemeService
* theme_service
) {
103 return theme_service
->get_theme_supplier();
108 ExtensionRegistry
* registry_
;
112 // Installs then uninstalls a theme and makes sure that the ThemeService
113 // reverts to the default theme after the uninstall.
114 TEST_F(ThemeServiceTest
, ThemeInstallUninstall
) {
115 ThemeService
* theme_service
=
116 ThemeServiceFactory::GetForProfile(profile_
.get());
117 theme_service
->UseDefaultTheme();
118 // Let the ThemeService uninstall unused themes.
119 base::MessageLoop::current()->RunUntilIdle();
121 base::ScopedTempDir temp_dir
;
122 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
123 const std::string
& extension_id
= LoadUnpackedThemeAt(temp_dir
.path());
124 EXPECT_FALSE(theme_service
->UsingDefaultTheme());
125 EXPECT_EQ(extension_id
, theme_service
->GetThemeID());
127 // Now uninstall the extension, should revert to the default theme.
128 service_
->UninstallExtension(extension_id
,
129 extensions::UNINSTALL_REASON_FOR_TESTING
,
130 base::Bind(&base::DoNothing
),
132 EXPECT_TRUE(theme_service
->UsingDefaultTheme());
135 // Test that a theme extension is disabled when not in use. A theme may be
136 // installed but not in use if it there is an infobar to revert to the previous
138 TEST_F(ThemeServiceTest
, DisableUnusedTheme
) {
139 ThemeService
* theme_service
=
140 ThemeServiceFactory::GetForProfile(profile_
.get());
141 theme_service
->UseDefaultTheme();
142 // Let the ThemeService uninstall unused themes.
143 base::MessageLoop::current()->RunUntilIdle();
145 base::ScopedTempDir temp_dir1
;
146 ASSERT_TRUE(temp_dir1
.CreateUniqueTempDir());
147 base::ScopedTempDir temp_dir2
;
148 ASSERT_TRUE(temp_dir2
.CreateUniqueTempDir());
150 // 1) Installing a theme should disable the previously active theme.
151 const std::string
& extension1_id
= LoadUnpackedThemeAt(temp_dir1
.path());
152 EXPECT_FALSE(theme_service
->UsingDefaultTheme());
153 EXPECT_EQ(extension1_id
, theme_service
->GetThemeID());
154 EXPECT_TRUE(service_
->IsExtensionEnabled(extension1_id
));
156 // Show an infobar to prevent the current theme from being uninstalled.
157 theme_service
->OnInfobarDisplayed();
159 const std::string
& extension2_id
= LoadUnpackedThemeAt(temp_dir2
.path());
160 EXPECT_EQ(extension2_id
, theme_service
->GetThemeID());
161 EXPECT_TRUE(service_
->IsExtensionEnabled(extension2_id
));
162 EXPECT_TRUE(registry_
->GetExtensionById(extension1_id
,
163 ExtensionRegistry::DISABLED
));
165 // 2) Enabling a disabled theme extension should swap the current theme.
166 service_
->EnableExtension(extension1_id
);
167 base::MessageLoop::current()->RunUntilIdle();
168 EXPECT_EQ(extension1_id
, theme_service
->GetThemeID());
169 EXPECT_TRUE(service_
->IsExtensionEnabled(extension1_id
));
170 EXPECT_TRUE(registry_
->GetExtensionById(extension2_id
,
171 ExtensionRegistry::DISABLED
));
173 // 3) Using SetTheme() with a disabled theme should enable and set the
174 // theme. This is the case when the user reverts to the previous theme
176 const extensions::Extension
* extension2
=
177 service_
->GetInstalledExtension(extension2_id
);
178 theme_service
->SetTheme(extension2
);
179 base::MessageLoop::current()->RunUntilIdle();
180 EXPECT_EQ(extension2_id
, theme_service
->GetThemeID());
181 EXPECT_TRUE(service_
->IsExtensionEnabled(extension2_id
));
182 EXPECT_TRUE(registry_
->GetExtensionById(extension1_id
,
183 ExtensionRegistry::DISABLED
));
185 // 4) Disabling the current theme extension should revert to the default theme
186 // and uninstall any installed theme extensions.
187 theme_service
->OnInfobarDestroyed();
188 EXPECT_FALSE(theme_service
->UsingDefaultTheme());
189 service_
->DisableExtension(extension2_id
,
190 extensions::Extension::DISABLE_USER_ACTION
);
191 base::MessageLoop::current()->RunUntilIdle();
192 EXPECT_TRUE(theme_service
->UsingDefaultTheme());
193 EXPECT_FALSE(service_
->GetInstalledExtension(extension1_id
));
194 EXPECT_FALSE(service_
->GetInstalledExtension(extension2_id
));
197 // Test the ThemeService's behavior when a theme is upgraded.
198 TEST_F(ThemeServiceTest
, ThemeUpgrade
) {
200 ThemeService
* theme_service
=
201 ThemeServiceFactory::GetForProfile(profile_
.get());
202 theme_service
->UseDefaultTheme();
203 // Let the ThemeService uninstall unused themes.
204 base::MessageLoop::current()->RunUntilIdle();
206 theme_service
->OnInfobarDisplayed();
208 base::ScopedTempDir temp_dir1
;
209 ASSERT_TRUE(temp_dir1
.CreateUniqueTempDir());
210 base::ScopedTempDir temp_dir2
;
211 ASSERT_TRUE(temp_dir2
.CreateUniqueTempDir());
213 const std::string
& extension1_id
= LoadUnpackedThemeAt(temp_dir1
.path());
214 const std::string
& extension2_id
= LoadUnpackedThemeAt(temp_dir2
.path());
216 // Test the initial state.
217 EXPECT_TRUE(registry_
->GetExtensionById(extension1_id
,
218 ExtensionRegistry::DISABLED
));
219 EXPECT_EQ(extension2_id
, theme_service
->GetThemeID());
221 // 1) Upgrading the current theme should not revert to the default theme.
222 content::WindowedNotificationObserver
theme_change_observer(
223 chrome::NOTIFICATION_BROWSER_THEME_CHANGED
,
224 content::Source
<ThemeService
>(theme_service
));
225 UpdateUnpackedTheme(extension2_id
);
227 // The ThemeService should have sent an theme change notification even though
228 // the id of the current theme did not change.
229 theme_change_observer
.Wait();
231 EXPECT_EQ(extension2_id
, theme_service
->GetThemeID());
232 EXPECT_TRUE(registry_
->GetExtensionById(extension1_id
,
233 ExtensionRegistry::DISABLED
));
235 // 2) Upgrading a disabled theme should not change the current theme.
236 UpdateUnpackedTheme(extension1_id
);
237 EXPECT_EQ(extension2_id
, theme_service
->GetThemeID());
238 EXPECT_TRUE(registry_
->GetExtensionById(extension1_id
,
239 ExtensionRegistry::DISABLED
));
244 // NotificationObserver which emulates an infobar getting destroyed when the
246 class InfobarDestroyerOnThemeChange
: public content::NotificationObserver
{
248 InfobarDestroyerOnThemeChange(Profile
* profile
)
249 : theme_service_(ThemeServiceFactory::GetForProfile(profile
)) {
250 registrar_
.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED
,
251 content::Source
<ThemeService
>(theme_service_
));
254 ~InfobarDestroyerOnThemeChange() override
{}
257 void Observe(int type
,
258 const content::NotificationSource
& source
,
259 const content::NotificationDetails
& details
) override
{
260 theme_service_
->OnInfobarDestroyed();
264 ThemeService
* theme_service_
;
266 content::NotificationRegistrar registrar_
;
268 DISALLOW_COPY_AND_ASSIGN(InfobarDestroyerOnThemeChange
);
274 TEST_F(ThemeServiceTest
, UninstallThemeOnThemeChangeNotification
) {
276 ThemeService
* theme_service
=
277 ThemeServiceFactory::GetForProfile(profile_
.get());
278 theme_service
->UseDefaultTheme();
279 // Let the ThemeService uninstall unused themes.
280 base::MessageLoop::current()->RunUntilIdle();
282 base::ScopedTempDir temp_dir1
;
283 ASSERT_TRUE(temp_dir1
.CreateUniqueTempDir());
284 base::ScopedTempDir temp_dir2
;
285 ASSERT_TRUE(temp_dir2
.CreateUniqueTempDir());
287 const std::string
& extension1_id
= LoadUnpackedThemeAt(temp_dir1
.path());
288 ASSERT_EQ(extension1_id
, theme_service
->GetThemeID());
291 theme_service
->OnInfobarDisplayed();
293 // Install another theme. Emulate the infobar destroying itself (and
294 // causing unused themes to be uninstalled) as a result of the
295 // NOTIFICATION_BROWSER_THEME_CHANGED notification.
297 InfobarDestroyerOnThemeChange
destroyer(profile_
.get());
298 const std::string
& extension2_id
= LoadUnpackedThemeAt(temp_dir2
.path());
299 ASSERT_EQ(extension2_id
, theme_service
->GetThemeID());
300 ASSERT_FALSE(service_
->GetInstalledExtension(extension1_id
));
303 // Check that it is possible to reinstall extension1.
304 ASSERT_EQ(extension1_id
, LoadUnpackedThemeAt(temp_dir1
.path()));
305 EXPECT_EQ(extension1_id
, theme_service
->GetThemeID());
308 #if defined(ENABLE_SUPERVISED_USERS)
309 class ThemeServiceSupervisedUserTest
: public ThemeServiceTest
{
311 ThemeServiceSupervisedUserTest() {}
312 ~ThemeServiceSupervisedUserTest() override
{}
314 void SetUp() override
{
315 is_supervised_
= true;
316 ThemeServiceTest::SetUp();
320 // Checks that supervised users have their own default theme.
321 TEST_F(ThemeServiceSupervisedUserTest
,
322 SupervisedUserThemeReplacesDefaultTheme
) {
323 ThemeService
* theme_service
=
324 ThemeServiceFactory::GetForProfile(profile_
.get());
325 theme_service
->UseDefaultTheme();
326 EXPECT_TRUE(theme_service
->UsingDefaultTheme());
327 EXPECT_TRUE(get_theme_supplier(theme_service
));
328 EXPECT_EQ(get_theme_supplier(theme_service
)->get_theme_type(),
329 CustomThemeSupplier::SUPERVISED_USER_THEME
);
332 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
333 // Checks that supervised users don't use the system theme even if it is the
334 // default. The system theme is only available on Linux.
335 TEST_F(ThemeServiceSupervisedUserTest
, SupervisedUserThemeReplacesNativeTheme
) {
336 profile_
->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme
, true);
337 ThemeService
* theme_service
=
338 ThemeServiceFactory::GetForProfile(profile_
.get());
339 theme_service
->UseDefaultTheme();
340 EXPECT_TRUE(theme_service
->UsingDefaultTheme());
341 EXPECT_TRUE(get_theme_supplier(theme_service
));
342 EXPECT_EQ(get_theme_supplier(theme_service
)->get_theme_type(),
343 CustomThemeSupplier::SUPERVISED_USER_THEME
);
345 #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
346 #endif // defined(ENABLE_SUPERVISED_USERS)
348 }; // namespace theme_service_internal