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