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