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
28 using extensions::ExtensionRegistry;
29
30 namespace theme_service_internal {
31
32 class ThemeServiceTest : public extensions::ExtensionServiceTestBase {
33 public:
ThemeServiceTest()34 ThemeServiceTest() : is_supervised_(false),
35 registry_(NULL) {}
~ThemeServiceTest()36 virtual ~ThemeServiceTest() {}
37
38 // Moves a minimal theme to |temp_dir_path| and unpacks it from that
39 // directory.
LoadUnpackedThemeAt(const base::FilePath & temp_dir)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|.
UpdateUnpackedTheme(const std::string & 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
SetUp()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
get_theme_supplier(ThemeService * theme_service)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.
TEST_F(ThemeServiceTest,ThemeInstallUninstall)109 TEST_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.
TEST_F(ThemeServiceTest,DisableUnusedTheme)133 TEST_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.
TEST_F(ThemeServiceTest,ThemeUpgrade)193 TEST_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
237 class ThemeServiceSupervisedUserTest : public ThemeServiceTest {
238 public:
ThemeServiceSupervisedUserTest()239 ThemeServiceSupervisedUserTest() {}
~ThemeServiceSupervisedUserTest()240 virtual ~ThemeServiceSupervisedUserTest() {}
241
SetUp()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.
TEST_F(ThemeServiceSupervisedUserTest,SupervisedUserThemeReplacesDefaultTheme)249 TEST_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.
TEST_F(ThemeServiceSupervisedUserTest,SupervisedUserThemeReplacesNativeTheme)263 TEST_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