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_syncable_service.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/files/file_path.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/no_destructor.h"
15 #include "base/run_loop.h"
16 #include "base/time/time.h"
17 #include "base/values.h"
18 #include "build/build_config.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/extensions/test_extension_system.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/themes/theme_helper.h"
23 #include "chrome/browser/themes/theme_service.h"
24 #include "chrome/browser/themes/theme_service_factory.h"
25 #include "chrome/common/extensions/extension_test_util.h"
26 #include "chrome/test/base/testing_profile.h"
27 #include "components/sync/model/sync_error.h"
28 #include "components/sync/protocol/sync.pb.h"
29 #include "components/sync/protocol/theme_specifics.pb.h"
30 #include "components/sync/test/model/fake_sync_change_processor.h"
31 #include "components/sync/test/model/sync_change_processor_wrapper_for_test.h"
32 #include "components/sync/test/model/sync_error_factory_mock.h"
33 #include "content/public/test/browser_task_environment.h"
34 #include "extensions/browser/extension_prefs.h"
35 #include "extensions/browser/extension_registry.h"
36 #include "extensions/common/extension.h"
37 #include "extensions/common/manifest_constants.h"
38 #include "extensions/common/manifest_url_handlers.h"
39 #include "extensions/common/permissions/api_permission_set.h"
40 #include "extensions/common/permissions/permission_set.h"
41 #include "testing/gtest/include/gtest/gtest.h"
42
43 #if defined(OS_CHROMEOS)
44 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
45 #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
46 #endif
47
48 using std::string;
49
50 namespace {
51
52 static const char kCustomThemeName[] = "name";
53 static const char kCustomThemeUrl[] = "http://update.url/foo";
54
55 #if defined(OS_WIN)
56 const base::FilePath::CharType kExtensionFilePath[] =
57 FILE_PATH_LITERAL("c:\\foo");
58 #elif defined(OS_POSIX)
59 const base::FilePath::CharType kExtensionFilePath[] = FILE_PATH_LITERAL("/oo");
60 #endif
61
GetThemeHelper()62 const ThemeHelper& GetThemeHelper() {
63 static base::NoDestructor<std::unique_ptr<ThemeHelper>> theme_helper(
64 std::make_unique<ThemeHelper>());
65 return **theme_helper;
66 }
67
68 class FakeThemeService : public ThemeService {
69 public:
FakeThemeService()70 FakeThemeService() : ThemeService(nullptr, GetThemeHelper()) {}
71
72 // ThemeService implementation
DoSetTheme(const extensions::Extension * extension,bool suppress_infobar)73 void DoSetTheme(const extensions::Extension* extension,
74 bool suppress_infobar) override {
75 is_dirty_ = true;
76 theme_extension_ = extension;
77 using_system_theme_ = false;
78 using_default_theme_ = false;
79 might_show_infobar_ = !suppress_infobar;
80 color_ = 0;
81 }
82
BuildAutogeneratedThemeFromColor(SkColor color)83 void BuildAutogeneratedThemeFromColor(SkColor color) override {
84 is_dirty_ = true;
85 color_ = color;
86 theme_extension_.reset();
87 using_system_theme_ = false;
88 using_default_theme_ = false;
89 }
90
UseDefaultTheme()91 void UseDefaultTheme() override {
92 is_dirty_ = true;
93 using_default_theme_ = true;
94 using_system_theme_ = false;
95 theme_extension_.reset();
96 color_ = 0;
97 }
98
UseSystemTheme()99 void UseSystemTheme() override {
100 is_dirty_ = true;
101 using_system_theme_ = true;
102 using_default_theme_ = false;
103 theme_extension_.reset();
104 color_ = 0;
105 }
106
IsSystemThemeDistinctFromDefaultTheme() const107 bool IsSystemThemeDistinctFromDefaultTheme() const override {
108 return distinct_from_default_theme_;
109 }
110
set_distinct_from_default_theme(bool is_distinct)111 void set_distinct_from_default_theme(bool is_distinct) {
112 distinct_from_default_theme_ = is_distinct;
113 }
114
UsingDefaultTheme() const115 bool UsingDefaultTheme() const override { return using_default_theme_; }
116
UsingSystemTheme() const117 bool UsingSystemTheme() const override { return using_system_theme_; }
118
UsingExtensionTheme() const119 bool UsingExtensionTheme() const override { return !!theme_extension_; }
120
UsingAutogeneratedTheme() const121 bool UsingAutogeneratedTheme() const override { return color_ != 0; }
122
GetThemeID() const123 string GetThemeID() const override {
124 return UsingExtensionTheme() ? theme_extension_->id() : std::string();
125 }
126
GetAutogeneratedThemeColor() const127 SkColor GetAutogeneratedThemeColor() const override { return color_; }
128
theme_extension() const129 const extensions::Extension* theme_extension() const {
130 return theme_extension_.get();
131 }
132
is_dirty() const133 bool is_dirty() const { return is_dirty_; }
134
MarkClean()135 void MarkClean() { is_dirty_ = false; }
136
might_show_infobar() const137 bool might_show_infobar() const { return might_show_infobar_; }
138
139 private:
140 bool using_system_theme_ = false;
141 bool using_default_theme_ = false;
142 bool distinct_from_default_theme_ = false;
143 scoped_refptr<const extensions::Extension> theme_extension_;
144 bool is_dirty_ = false;
145 bool might_show_infobar_ = false;
146 SkColor color_;
147 };
148
BuildMockThemeService(content::BrowserContext * profile)149 std::unique_ptr<KeyedService> BuildMockThemeService(
150 content::BrowserContext* profile) {
151 return base::WrapUnique(new FakeThemeService);
152 }
153
MakeThemeExtension(const base::FilePath & extension_path,const string & name,extensions::Manifest::Location location,const string & update_url)154 scoped_refptr<extensions::Extension> MakeThemeExtension(
155 const base::FilePath& extension_path,
156 const string& name,
157 extensions::Manifest::Location location,
158 const string& update_url) {
159 base::DictionaryValue source;
160 source.SetString(extensions::manifest_keys::kName, name);
161 source.Set(extensions::manifest_keys::kTheme,
162 std::make_unique<base::DictionaryValue>());
163 source.SetString(extensions::manifest_keys::kUpdateURL, update_url);
164 source.SetString(extensions::manifest_keys::kVersion, "0.0.0.0");
165 string error;
166 scoped_refptr<extensions::Extension> extension =
167 extensions::Extension::Create(
168 extension_path, location, source,
169 extensions::Extension::NO_FLAGS, &error);
170 EXPECT_TRUE(extension.get());
171 EXPECT_EQ("", error);
172 return extension;
173 }
174
175 } // namespace
176
177 class ThemeSyncableServiceTest : public testing::Test {
178 protected:
ThemeSyncableServiceTest()179 ThemeSyncableServiceTest() : fake_theme_service_(nullptr) {}
180
~ThemeSyncableServiceTest()181 ~ThemeSyncableServiceTest() override {}
182
SetUp()183 void SetUp() override {
184 // Setting a matching update URL is necessary to make the test theme
185 // considered syncable.
186 extension_test_util::SetGalleryUpdateURL(GURL(kCustomThemeUrl));
187
188 profile_.reset(new TestingProfile);
189 fake_theme_service_ = BuildForProfile(profile_.get());
190 theme_sync_service_.reset(new ThemeSyncableService(profile_.get(),
191 fake_theme_service_));
192 fake_change_processor_.reset(new syncer::FakeSyncChangeProcessor);
193 SetUpExtension();
194 }
195
TearDown()196 void TearDown() override {
197 profile_.reset();
198 base::RunLoop().RunUntilIdle();
199 }
200
SetUpExtension()201 void SetUpExtension() {
202 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
203 extensions::TestExtensionSystem* test_ext_system =
204 static_cast<extensions::TestExtensionSystem*>(
205 extensions::ExtensionSystem::Get(profile_.get()));
206 extensions::ExtensionService* service =
207 test_ext_system->CreateExtensionService(
208 &command_line, base::FilePath(kExtensionFilePath), false);
209 EXPECT_TRUE(service->extensions_enabled());
210 service->Init();
211 base::RunLoop().RunUntilIdle();
212
213 // Create and add custom theme extension so the ThemeSyncableService can
214 // find it.
215 theme_extension_ = MakeThemeExtension(base::FilePath(kExtensionFilePath),
216 kCustomThemeName,
217 GetThemeLocation(),
218 kCustomThemeUrl);
219 extensions::ExtensionPrefs::Get(profile_.get())
220 ->AddGrantedPermissions(theme_extension_->id(),
221 extensions::PermissionSet());
222 service->AddExtension(theme_extension_.get());
223 extensions::ExtensionRegistry* registry =
224 extensions::ExtensionRegistry::Get(profile_.get());
225 ASSERT_EQ(1u, registry->enabled_extensions().size());
226 }
227
228 // Overridden in PolicyInstalledThemeTest below.
GetThemeLocation()229 virtual extensions::Manifest::Location GetThemeLocation() {
230 return extensions::Manifest::INTERNAL;
231 }
232
BuildForProfile(Profile * profile)233 FakeThemeService* BuildForProfile(Profile* profile) {
234 return static_cast<FakeThemeService*>(
235 ThemeServiceFactory::GetInstance()->SetTestingFactoryAndUse(
236 profile, base::BindRepeating(&BuildMockThemeService)));
237 }
238
MakeThemeDataList(const sync_pb::ThemeSpecifics & theme_specifics)239 syncer::SyncDataList MakeThemeDataList(
240 const sync_pb::ThemeSpecifics& theme_specifics) {
241 syncer::SyncDataList list;
242 sync_pb::EntitySpecifics entity_specifics;
243 entity_specifics.mutable_theme()->CopyFrom(theme_specifics);
244 list.push_back(syncer::SyncData::CreateLocalData(
245 ThemeSyncableService::kCurrentThemeClientTag,
246 ThemeSyncableService::kCurrentThemeNodeTitle,
247 entity_specifics));
248 return list;
249 }
250
251 // Needed for setting up extension service.
252 content::BrowserTaskEnvironment task_environment_;
253
254 #if defined OS_CHROMEOS
255 chromeos::ScopedCrosSettingsTestHelper cros_settings_test_helper_;
256 chromeos::ScopedTestUserManager test_user_manager_;
257 #endif
258
259 std::unique_ptr<TestingProfile> profile_;
260 FakeThemeService* fake_theme_service_;
261 scoped_refptr<extensions::Extension> theme_extension_;
262 std::unique_ptr<ThemeSyncableService> theme_sync_service_;
263 std::unique_ptr<syncer::FakeSyncChangeProcessor> fake_change_processor_;
264 };
265
266 class PolicyInstalledThemeTest : public ThemeSyncableServiceTest {
GetThemeLocation()267 extensions::Manifest::Location GetThemeLocation() override {
268 return extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD;
269 }
270 };
271
TEST_F(ThemeSyncableServiceTest,AreThemeSpecificsEqual)272 TEST_F(ThemeSyncableServiceTest, AreThemeSpecificsEqual) {
273 sync_pb::ThemeSpecifics a, b;
274 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
275 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
276
277 // Custom vs. non-custom.
278
279 a.set_use_custom_theme(true);
280 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
281 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
282
283 a.set_use_custom_theme(false);
284 b.set_use_custom_theme(true);
285 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
286 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
287
288 // Custom theme equality for extensions.
289 a.set_use_custom_theme(true);
290 b.set_use_custom_theme(true);
291 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
292 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
293
294 a.set_custom_theme_id("id");
295 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
296 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
297
298 b.set_custom_theme_id("id");
299 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
300 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
301
302 a.set_custom_theme_update_url("http://update.url");
303 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
304 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
305
306 a.set_custom_theme_name("name");
307 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
308 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
309
310 // Theme equality for autogenerated.
311 a.set_use_custom_theme(true);
312 b.set_use_custom_theme(false);
313 b.mutable_autogenerated_theme()->set_color(SkColorSetRGB(0, 0, 0));
314 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
315 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
316
317 b.set_use_custom_theme(true);
318 b.clear_autogenerated_theme();
319 a.set_use_custom_theme(false);
320 a.mutable_autogenerated_theme()->set_color(SkColorSetRGB(0, 0, 0));
321 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
322 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
323
324 b.set_use_custom_theme(false);
325 b.mutable_autogenerated_theme()->set_color(SkColorSetRGB(0, 0, 100));
326 a.mutable_autogenerated_theme()->set_color(SkColorSetRGB(0, 0, 100));
327 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
328 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
329
330 b.mutable_autogenerated_theme()->set_color(SkColorSetRGB(0, 0, 200));
331 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
332 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
333
334 // No theme equality.
335
336 a.clear_autogenerated_theme();
337 b.clear_autogenerated_theme();
338 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
339 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
340
341 a.set_use_system_theme_by_default(true);
342 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
343 EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
344
345 b.set_use_system_theme_by_default(true);
346 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
347 EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
348 }
349
TEST_F(ThemeSyncableServiceTest,SetCurrentThemeDefaultTheme)350 TEST_F(ThemeSyncableServiceTest, SetCurrentThemeDefaultTheme) {
351 // Set up theme service to use custom theme.
352 fake_theme_service_->SetTheme(theme_extension_.get());
353
354 base::Optional<syncer::ModelError> error =
355 theme_sync_service_->MergeDataAndStartSyncing(
356 syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
357 std::unique_ptr<syncer::SyncChangeProcessor>(
358 new syncer::SyncChangeProcessorWrapperForTest(
359 fake_change_processor_.get())),
360 std::unique_ptr<syncer::SyncErrorFactory>(
361 new syncer::SyncErrorFactoryMock()));
362 EXPECT_FALSE(error.has_value()) << error.value().message();
363 EXPECT_FALSE(fake_theme_service_->UsingDefaultTheme());
364 EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
365 }
366
TEST_F(ThemeSyncableServiceTest,SetCurrentThemeSystemTheme)367 TEST_F(ThemeSyncableServiceTest, SetCurrentThemeSystemTheme) {
368 sync_pb::ThemeSpecifics theme_specifics;
369 theme_specifics.set_use_system_theme_by_default(true);
370
371 // Set up theme service to use custom theme.
372 fake_theme_service_->SetTheme(theme_extension_.get());
373 base::Optional<syncer::ModelError> error =
374 theme_sync_service_->MergeDataAndStartSyncing(
375 syncer::THEMES, MakeThemeDataList(theme_specifics),
376 std::unique_ptr<syncer::SyncChangeProcessor>(
377 new syncer::SyncChangeProcessorWrapperForTest(
378 fake_change_processor_.get())),
379 std::unique_ptr<syncer::SyncErrorFactory>(
380 new syncer::SyncErrorFactoryMock()));
381 EXPECT_FALSE(error.has_value()) << error.value().message();
382 EXPECT_FALSE(fake_theme_service_->UsingSystemTheme());
383 EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
384 }
385
TEST_F(ThemeSyncableServiceTest,SetCurrentThemeCustomTheme_Extension)386 TEST_F(ThemeSyncableServiceTest, SetCurrentThemeCustomTheme_Extension) {
387 sync_pb::ThemeSpecifics theme_specifics;
388 theme_specifics.set_use_custom_theme(true);
389 theme_specifics.set_custom_theme_id(theme_extension_->id());
390 theme_specifics.set_custom_theme_name(kCustomThemeName);
391 theme_specifics.set_custom_theme_name(kCustomThemeUrl);
392
393 // Set up theme service to use default theme.
394 fake_theme_service_->UseDefaultTheme();
395 base::Optional<syncer::ModelError> error =
396 theme_sync_service_->MergeDataAndStartSyncing(
397 syncer::THEMES, MakeThemeDataList(theme_specifics),
398 std::unique_ptr<syncer::SyncChangeProcessor>(
399 new syncer::SyncChangeProcessorWrapperForTest(
400 fake_change_processor_.get())),
401 std::unique_ptr<syncer::SyncErrorFactory>(
402 new syncer::SyncErrorFactoryMock()));
403 EXPECT_FALSE(error.has_value()) << error.value().message();
404 EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
405 }
406
TEST_F(ThemeSyncableServiceTest,SetCurrentThemeCustomTheme_Autogenerated)407 TEST_F(ThemeSyncableServiceTest, SetCurrentThemeCustomTheme_Autogenerated) {
408 sync_pb::ThemeSpecifics theme_specifics;
409 theme_specifics.set_use_custom_theme(false);
410 theme_specifics.mutable_autogenerated_theme()->set_color(
411 SkColorSetRGB(0, 0, 100));
412
413 // Set up theme service to use default theme.
414 fake_theme_service_->UseDefaultTheme();
415 base::Optional<syncer::ModelError> error =
416 theme_sync_service_->MergeDataAndStartSyncing(
417 syncer::THEMES, MakeThemeDataList(theme_specifics),
418 std::unique_ptr<syncer::SyncChangeProcessor>(
419 new syncer::SyncChangeProcessorWrapperForTest(
420 fake_change_processor_.get())),
421 std::unique_ptr<syncer::SyncErrorFactory>(
422 new syncer::SyncErrorFactoryMock()));
423 EXPECT_FALSE(error.has_value()) << error.value().message();
424 EXPECT_EQ(fake_theme_service_->GetAutogeneratedThemeColor(),
425 SkColorSetRGB(0, 0, 100));
426 }
427
TEST_F(ThemeSyncableServiceTest,DontResetThemeWhenSpecificsAreEqual)428 TEST_F(ThemeSyncableServiceTest, DontResetThemeWhenSpecificsAreEqual) {
429 // Set up theme service to use default theme and expect no changes.
430 fake_theme_service_->UseDefaultTheme();
431 fake_theme_service_->MarkClean();
432 base::Optional<syncer::ModelError> error =
433 theme_sync_service_->MergeDataAndStartSyncing(
434 syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
435 std::unique_ptr<syncer::SyncChangeProcessor>(
436 new syncer::SyncChangeProcessorWrapperForTest(
437 fake_change_processor_.get())),
438 std::unique_ptr<syncer::SyncErrorFactory>(
439 new syncer::SyncErrorFactoryMock()));
440 EXPECT_FALSE(error.has_value()) << error.value().message();
441 EXPECT_FALSE(fake_theme_service_->is_dirty());
442 }
443
TEST_F(ThemeSyncableServiceTest,UpdateThemeSpecifics_CurrentTheme_Extension)444 TEST_F(ThemeSyncableServiceTest, UpdateThemeSpecifics_CurrentTheme_Extension) {
445 // Set up theme service to use custom theme.
446 fake_theme_service_->SetTheme(theme_extension_.get());
447
448 base::Optional<syncer::ModelError> error =
449 theme_sync_service_->MergeDataAndStartSyncing(
450 syncer::THEMES, syncer::SyncDataList(),
451 std::unique_ptr<syncer::SyncChangeProcessor>(
452 new syncer::SyncChangeProcessorWrapperForTest(
453 fake_change_processor_.get())),
454 std::unique_ptr<syncer::SyncErrorFactory>(
455 new syncer::SyncErrorFactoryMock()));
456 EXPECT_FALSE(error.has_value()) << error.value().message();
457 const syncer::SyncChangeList& changes = fake_change_processor_->changes();
458 ASSERT_EQ(1u, changes.size());
459 EXPECT_TRUE(changes[0].IsValid());
460 EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
461 EXPECT_EQ(syncer::THEMES, changes[0].sync_data().GetDataType());
462
463 const sync_pb::ThemeSpecifics& theme_specifics =
464 changes[0].sync_data().GetSpecifics().theme();
465 EXPECT_TRUE(theme_specifics.use_custom_theme());
466 EXPECT_EQ(theme_extension_->id(), theme_specifics.custom_theme_id());
467 EXPECT_EQ(theme_extension_->name(), theme_specifics.custom_theme_name());
468 EXPECT_EQ(
469 extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
470 theme_specifics.custom_theme_update_url());
471 }
472
TEST_F(ThemeSyncableServiceTest,UpdateThemeSpecifics_CurrentTheme_Autogenerated)473 TEST_F(ThemeSyncableServiceTest,
474 UpdateThemeSpecifics_CurrentTheme_Autogenerated) {
475 // Set up theme service to use autogenerated theme.
476 fake_theme_service_->BuildAutogeneratedThemeFromColor(
477 SkColorSetRGB(0, 0, 100));
478
479 base::Optional<syncer::ModelError> error =
480 theme_sync_service_->MergeDataAndStartSyncing(
481 syncer::THEMES, syncer::SyncDataList(),
482 std::unique_ptr<syncer::SyncChangeProcessor>(
483 new syncer::SyncChangeProcessorWrapperForTest(
484 fake_change_processor_.get())),
485 std::unique_ptr<syncer::SyncErrorFactory>(
486 new syncer::SyncErrorFactoryMock()));
487 EXPECT_FALSE(error.has_value()) << error.value().message();
488 const syncer::SyncChangeList& changes = fake_change_processor_->changes();
489 ASSERT_EQ(1u, changes.size());
490 EXPECT_TRUE(changes[0].IsValid());
491 EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
492 EXPECT_EQ(syncer::THEMES, changes[0].sync_data().GetDataType());
493
494 const sync_pb::ThemeSpecifics& theme_specifics =
495 changes[0].sync_data().GetSpecifics().theme();
496 EXPECT_FALSE(theme_specifics.use_custom_theme());
497 EXPECT_EQ(fake_theme_service_->GetAutogeneratedThemeColor(),
498 theme_specifics.autogenerated_theme().color());
499 }
500
TEST_F(ThemeSyncableServiceTest,GetAllSyncDataForTesting_Extension)501 TEST_F(ThemeSyncableServiceTest, GetAllSyncDataForTesting_Extension) {
502 // Set up theme service to use custom theme.
503 fake_theme_service_->SetTheme(theme_extension_.get());
504
505 syncer::SyncDataList data_list =
506 theme_sync_service_->GetAllSyncDataForTesting(syncer::THEMES);
507
508 ASSERT_EQ(1u, data_list.size());
509 const sync_pb::ThemeSpecifics& theme_specifics =
510 data_list[0].GetSpecifics().theme();
511 EXPECT_TRUE(theme_specifics.use_custom_theme());
512 EXPECT_EQ(theme_extension_->id(), theme_specifics.custom_theme_id());
513 EXPECT_EQ(theme_extension_->name(), theme_specifics.custom_theme_name());
514 EXPECT_EQ(
515 extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
516 theme_specifics.custom_theme_update_url());
517 }
518
TEST_F(ThemeSyncableServiceTest,GetAllSyncDataForTesting_Autogenerated)519 TEST_F(ThemeSyncableServiceTest, GetAllSyncDataForTesting_Autogenerated) {
520 // Set up theme service to use autogenerated theme.
521 fake_theme_service_->BuildAutogeneratedThemeFromColor(
522 SkColorSetRGB(0, 0, 100));
523
524 syncer::SyncDataList data_list =
525 theme_sync_service_->GetAllSyncDataForTesting(syncer::THEMES);
526
527 ASSERT_EQ(1u, data_list.size());
528 const sync_pb::ThemeSpecifics& theme_specifics =
529 data_list[0].GetSpecifics().theme();
530 EXPECT_FALSE(theme_specifics.use_custom_theme());
531 EXPECT_EQ(fake_theme_service_->GetAutogeneratedThemeColor(),
532 theme_specifics.autogenerated_theme().color());
533 }
534
TEST_F(ThemeSyncableServiceTest,ProcessSyncThemeChange_Extension)535 TEST_F(ThemeSyncableServiceTest, ProcessSyncThemeChange_Extension) {
536 // Set up theme service to use default theme.
537 fake_theme_service_->UseDefaultTheme();
538 fake_theme_service_->MarkClean();
539
540 // Start syncing.
541 base::Optional<syncer::ModelError> error =
542 theme_sync_service_->MergeDataAndStartSyncing(
543 syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
544 std::unique_ptr<syncer::SyncChangeProcessor>(
545 new syncer::SyncChangeProcessorWrapperForTest(
546 fake_change_processor_.get())),
547 std::unique_ptr<syncer::SyncErrorFactory>(
548 new syncer::SyncErrorFactoryMock()));
549 EXPECT_FALSE(error.has_value()) << error.value().message();
550 // Don't expect theme change initially because specifics are equal.
551 EXPECT_FALSE(fake_theme_service_->is_dirty());
552
553 // Change specifics to use custom theme and update.
554 sync_pb::ThemeSpecifics theme_specifics;
555 theme_specifics.set_use_custom_theme(true);
556 theme_specifics.set_custom_theme_id(theme_extension_->id());
557 theme_specifics.set_custom_theme_name(kCustomThemeName);
558 theme_specifics.set_custom_theme_name(kCustomThemeUrl);
559 sync_pb::EntitySpecifics entity_specifics;
560 entity_specifics.mutable_theme()->CopyFrom(theme_specifics);
561 syncer::SyncChangeList change_list;
562 change_list.push_back(
563 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
564 syncer::SyncData::CreateRemoteData(entity_specifics)));
565 base::Optional<syncer::ModelError> process_error =
566 theme_sync_service_->ProcessSyncChanges(FROM_HERE, change_list);
567 EXPECT_FALSE(process_error.has_value()) << process_error.value().message();
568 EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
569 // Don't show an infobar for theme installation. Regression test for
570 // crbug.com/731688
571 EXPECT_FALSE(fake_theme_service_->might_show_infobar());
572 }
573
TEST_F(ThemeSyncableServiceTest,ProcessSyncThemeChange_Autogenerated)574 TEST_F(ThemeSyncableServiceTest, ProcessSyncThemeChange_Autogenerated) {
575 // Set up theme service to use default theme.
576 fake_theme_service_->UseDefaultTheme();
577 fake_theme_service_->MarkClean();
578
579 // Start syncing.
580 base::Optional<syncer::ModelError> error =
581 theme_sync_service_->MergeDataAndStartSyncing(
582 syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
583 std::unique_ptr<syncer::SyncChangeProcessor>(
584 new syncer::SyncChangeProcessorWrapperForTest(
585 fake_change_processor_.get())),
586 std::unique_ptr<syncer::SyncErrorFactory>(
587 new syncer::SyncErrorFactoryMock()));
588 EXPECT_FALSE(error.has_value()) << error.value().message();
589 // Don't expect theme change initially because specifics are equal.
590 EXPECT_FALSE(fake_theme_service_->is_dirty());
591
592 // Change specifics to use custom theme and update.
593 sync_pb::ThemeSpecifics theme_specifics;
594 theme_specifics.set_use_custom_theme(false);
595 theme_specifics.mutable_autogenerated_theme()->set_color(
596 SkColorSetRGB(0, 0, 100));
597 sync_pb::EntitySpecifics entity_specifics;
598 entity_specifics.mutable_theme()->CopyFrom(theme_specifics);
599 syncer::SyncChangeList change_list;
600 change_list.push_back(
601 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
602 syncer::SyncData::CreateRemoteData(entity_specifics)));
603 base::Optional<syncer::ModelError> process_error =
604 theme_sync_service_->ProcessSyncChanges(FROM_HERE, change_list);
605 EXPECT_FALSE(process_error.has_value()) << process_error.value().message();
606 EXPECT_EQ(fake_theme_service_->GetAutogeneratedThemeColor(),
607 SkColorSetRGB(0, 0, 100));
608 }
609
TEST_F(ThemeSyncableServiceTest,OnThemeChangeByUser_Extension)610 TEST_F(ThemeSyncableServiceTest, OnThemeChangeByUser_Extension) {
611 // Set up theme service to use default theme.
612 fake_theme_service_->UseDefaultTheme();
613
614 // Start syncing.
615 base::Optional<syncer::ModelError> error =
616 theme_sync_service_->MergeDataAndStartSyncing(
617 syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
618 std::unique_ptr<syncer::SyncChangeProcessor>(
619 new syncer::SyncChangeProcessorWrapperForTest(
620 fake_change_processor_.get())),
621 std::unique_ptr<syncer::SyncErrorFactory>(
622 new syncer::SyncErrorFactoryMock()));
623 EXPECT_FALSE(error.has_value()) << error.value().message();
624 const syncer::SyncChangeList& changes = fake_change_processor_->changes();
625 EXPECT_EQ(0u, changes.size());
626
627 // Change current theme to custom theme and notify theme_sync_service_.
628 fake_theme_service_->SetTheme(theme_extension_.get());
629 theme_sync_service_->OnThemeChange();
630 EXPECT_EQ(1u, changes.size());
631 const sync_pb::ThemeSpecifics& change_specifics =
632 changes[0].sync_data().GetSpecifics().theme();
633 EXPECT_TRUE(change_specifics.use_custom_theme());
634 EXPECT_EQ(theme_extension_->id(), change_specifics.custom_theme_id());
635 EXPECT_EQ(theme_extension_->name(), change_specifics.custom_theme_name());
636 EXPECT_EQ(
637 extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
638 change_specifics.custom_theme_update_url());
639 }
640
TEST_F(ThemeSyncableServiceTest,OnThemeChangeByUser_Autogenerated)641 TEST_F(ThemeSyncableServiceTest, OnThemeChangeByUser_Autogenerated) {
642 // Set up theme service to use default theme.
643 fake_theme_service_->UseDefaultTheme();
644
645 // Start syncing.
646 base::Optional<syncer::ModelError> error =
647 theme_sync_service_->MergeDataAndStartSyncing(
648 syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
649 std::unique_ptr<syncer::SyncChangeProcessor>(
650 new syncer::SyncChangeProcessorWrapperForTest(
651 fake_change_processor_.get())),
652 std::unique_ptr<syncer::SyncErrorFactory>(
653 new syncer::SyncErrorFactoryMock()));
654 EXPECT_FALSE(error.has_value()) << error.value().message();
655 const syncer::SyncChangeList& changes = fake_change_processor_->changes();
656 EXPECT_EQ(0u, changes.size());
657
658 // Change current theme to custom theme and notify theme_sync_service_.
659 fake_theme_service_->BuildAutogeneratedThemeFromColor(
660 SkColorSetRGB(0, 0, 100));
661 theme_sync_service_->OnThemeChange();
662 EXPECT_EQ(1u, changes.size());
663 const sync_pb::ThemeSpecifics& change_specifics =
664 changes[0].sync_data().GetSpecifics().theme();
665 EXPECT_FALSE(change_specifics.use_custom_theme());
666 EXPECT_EQ(fake_theme_service_->GetAutogeneratedThemeColor(),
667 SkColorSetRGB(0, 0, 100));
668 }
669
TEST_F(ThemeSyncableServiceTest,StopSync)670 TEST_F(ThemeSyncableServiceTest, StopSync) {
671 // Set up theme service to use default theme.
672 fake_theme_service_->UseDefaultTheme();
673
674 // Start syncing.
675 base::Optional<syncer::ModelError> merge_error =
676 theme_sync_service_->MergeDataAndStartSyncing(
677 syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
678 std::unique_ptr<syncer::SyncChangeProcessor>(
679 new syncer::SyncChangeProcessorWrapperForTest(
680 fake_change_processor_.get())),
681 std::unique_ptr<syncer::SyncErrorFactory>(
682 new syncer::SyncErrorFactoryMock()));
683 EXPECT_FALSE(merge_error.has_value()) << merge_error.value().message();
684 const syncer::SyncChangeList& changes = fake_change_processor_->changes();
685 EXPECT_EQ(0u, changes.size());
686
687 // Stop syncing.
688 theme_sync_service_->StopSyncing(syncer::THEMES);
689
690 // Change current theme to custom theme and notify theme_sync_service_.
691 // No change is output because sync has stopped.
692 fake_theme_service_->SetTheme(theme_extension_.get());
693 theme_sync_service_->OnThemeChange();
694 EXPECT_EQ(0u, changes.size());
695
696 // ProcessSyncChanges() should return error when sync has stopped.
697 base::Optional<syncer::ModelError> process_error =
698 theme_sync_service_->ProcessSyncChanges(FROM_HERE, changes);
699 EXPECT_TRUE(process_error.has_value());
700 EXPECT_EQ("Theme syncable service is not started.",
701 process_error.value().message());
702 }
703
TEST_F(ThemeSyncableServiceTest,RestoreSystemThemeBitWhenChangeToCustomTheme)704 TEST_F(ThemeSyncableServiceTest, RestoreSystemThemeBitWhenChangeToCustomTheme) {
705 // Initialize to use system theme.
706 fake_theme_service_->UseDefaultTheme();
707 sync_pb::ThemeSpecifics theme_specifics;
708 theme_specifics.set_use_system_theme_by_default(true);
709 base::Optional<syncer::ModelError> error =
710 theme_sync_service_->MergeDataAndStartSyncing(
711 syncer::THEMES, MakeThemeDataList(theme_specifics),
712 std::unique_ptr<syncer::SyncChangeProcessor>(
713 new syncer::SyncChangeProcessorWrapperForTest(
714 fake_change_processor_.get())),
715 std::unique_ptr<syncer::SyncErrorFactory>(
716 new syncer::SyncErrorFactoryMock()));
717
718 // Change to custom theme and notify theme_sync_service_.
719 // use_system_theme_by_default bit should be preserved.
720 fake_theme_service_->SetTheme(theme_extension_.get());
721 theme_sync_service_->OnThemeChange();
722 const syncer::SyncChangeList& changes = fake_change_processor_->changes();
723 EXPECT_EQ(1u, changes.size());
724 const sync_pb::ThemeSpecifics& change_specifics =
725 changes[0].sync_data().GetSpecifics().theme();
726 EXPECT_TRUE(change_specifics.use_system_theme_by_default());
727 }
728
TEST_F(ThemeSyncableServiceTest,DistinctSystemTheme)729 TEST_F(ThemeSyncableServiceTest, DistinctSystemTheme) {
730 fake_theme_service_->set_distinct_from_default_theme(true);
731
732 // Initialize to use native theme.
733 fake_theme_service_->UseSystemTheme();
734 fake_theme_service_->MarkClean();
735 sync_pb::ThemeSpecifics theme_specifics;
736 theme_specifics.set_use_system_theme_by_default(true);
737 base::Optional<syncer::ModelError> error =
738 theme_sync_service_->MergeDataAndStartSyncing(
739 syncer::THEMES, MakeThemeDataList(theme_specifics),
740 std::unique_ptr<syncer::SyncChangeProcessor>(
741 new syncer::SyncChangeProcessorWrapperForTest(
742 fake_change_processor_.get())),
743 std::unique_ptr<syncer::SyncErrorFactory>(
744 new syncer::SyncErrorFactoryMock()));
745 EXPECT_FALSE(fake_theme_service_->is_dirty());
746
747 // Change to default theme and notify theme_sync_service_.
748 // use_system_theme_by_default bit should be false.
749 fake_theme_service_->UseDefaultTheme();
750 theme_sync_service_->OnThemeChange();
751 syncer::SyncChangeList& changes = fake_change_processor_->changes();
752 EXPECT_EQ(1u, changes.size());
753 EXPECT_FALSE(changes[0]
754 .sync_data()
755 .GetSpecifics()
756 .theme()
757 .use_system_theme_by_default());
758
759 // Change to native theme and notify theme_sync_service_.
760 // use_system_theme_by_default bit should be true.
761 changes.clear();
762 fake_theme_service_->UseSystemTheme();
763 theme_sync_service_->OnThemeChange();
764 EXPECT_EQ(1u, changes.size());
765 EXPECT_TRUE(changes[0]
766 .sync_data()
767 .GetSpecifics()
768 .theme()
769 .use_system_theme_by_default());
770 }
771
TEST_F(ThemeSyncableServiceTest,SystemThemeSameAsDefaultTheme)772 TEST_F(ThemeSyncableServiceTest, SystemThemeSameAsDefaultTheme) {
773 fake_theme_service_->set_distinct_from_default_theme(false);
774
775 // Set up theme service to use default theme.
776 fake_theme_service_->UseDefaultTheme();
777
778 // Initialize to use custom theme with use_system_theme_by_default set true.
779 sync_pb::ThemeSpecifics theme_specifics;
780 theme_specifics.set_use_custom_theme(true);
781 theme_specifics.set_custom_theme_id(theme_extension_->id());
782 theme_specifics.set_custom_theme_name(kCustomThemeName);
783 theme_specifics.set_custom_theme_name(kCustomThemeUrl);
784 theme_specifics.set_use_system_theme_by_default(true);
785 base::Optional<syncer::ModelError> error =
786 theme_sync_service_->MergeDataAndStartSyncing(
787 syncer::THEMES, MakeThemeDataList(theme_specifics),
788 std::unique_ptr<syncer::SyncChangeProcessor>(
789 new syncer::SyncChangeProcessorWrapperForTest(
790 fake_change_processor_.get())),
791 std::unique_ptr<syncer::SyncErrorFactory>(
792 new syncer::SyncErrorFactoryMock()));
793 EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
794
795 // Change to default theme and notify theme_sync_service_.
796 // use_system_theme_by_default bit should be preserved.
797 fake_theme_service_->UseDefaultTheme();
798 theme_sync_service_->OnThemeChange();
799 const syncer::SyncChangeList& changes = fake_change_processor_->changes();
800 EXPECT_EQ(1u, changes.size());
801 const sync_pb::ThemeSpecifics& change_specifics =
802 changes[0].sync_data().GetSpecifics().theme();
803 EXPECT_FALSE(change_specifics.use_custom_theme());
804 EXPECT_TRUE(change_specifics.use_system_theme_by_default());
805 }
806
TEST_F(PolicyInstalledThemeTest,InstallThemeByPolicy)807 TEST_F(PolicyInstalledThemeTest, InstallThemeByPolicy) {
808 // Set up theme service to use custom theme that was installed by policy.
809 fake_theme_service_->SetTheme(theme_extension_.get());
810
811 syncer::SyncDataList data_list =
812 theme_sync_service_->GetAllSyncDataForTesting(syncer::THEMES);
813
814 ASSERT_EQ(0u, data_list.size());
815 }
816