Home | History | Annotate | Download | only in themes
      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 "base/command_line.h"
      8 #include "base/compiler_specific.h"
      9 #include "base/files/file_path.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/time/time.h"
     12 #include "chrome/browser/extensions/extension_service.h"
     13 #include "chrome/browser/extensions/test_extension_system.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/themes/theme_service.h"
     16 #include "chrome/browser/themes/theme_service_factory.h"
     17 #include "chrome/common/extensions/manifest_url_handler.h"
     18 #include "chrome/test/base/testing_profile.h"
     19 #include "content/public/test/test_browser_thread.h"
     20 #include "extensions/browser/extension_prefs.h"
     21 #include "extensions/common/extension.h"
     22 #include "extensions/common/extension_messages.h"
     23 #include "extensions/common/manifest_constants.h"
     24 #include "extensions/common/permissions/api_permission_set.h"
     25 #include "extensions/common/permissions/permission_set.h"
     26 #include "sync/api/attachments/attachment_id.h"
     27 #include "sync/api/attachments/attachment_service_proxy_for_test.h"
     28 #include "sync/api/fake_sync_change_processor.h"
     29 #include "sync/api/sync_change_processor_wrapper_for_test.h"
     30 #include "sync/api/sync_error.h"
     31 #include "sync/api/sync_error_factory_mock.h"
     32 #include "sync/protocol/sync.pb.h"
     33 #include "sync/protocol/theme_specifics.pb.h"
     34 #include "testing/gtest/include/gtest/gtest.h"
     35 
     36 #if defined(OS_CHROMEOS)
     37 #include "chrome/browser/chromeos/login/users/user_manager.h"
     38 #include "chrome/browser/chromeos/settings/cros_settings.h"
     39 #include "chrome/browser/chromeos/settings/device_settings_service.h"
     40 #endif
     41 
     42 using std::string;
     43 
     44 namespace {
     45 
     46 static const char kCustomThemeName[] = "name";
     47 static const char kCustomThemeUrl[] = "http://update.url/foo";
     48 
     49 #if defined(OS_WIN)
     50 const base::FilePath::CharType kExtensionFilePath[] =
     51     FILE_PATH_LITERAL("c:\\foo");
     52 #elif defined(OS_POSIX)
     53 const base::FilePath::CharType kExtensionFilePath[] = FILE_PATH_LITERAL("/oo");
     54 #endif
     55 
     56 class FakeThemeService : public ThemeService {
     57  public:
     58   FakeThemeService() :
     59     using_system_theme_(false),
     60     using_default_theme_(false),
     61     theme_extension_(NULL),
     62     is_dirty_(false) {}
     63 
     64   // ThemeService implementation
     65   virtual void SetTheme(const extensions::Extension* extension) OVERRIDE {
     66     is_dirty_ = true;
     67     theme_extension_ = extension;
     68     using_system_theme_ = false;
     69     using_default_theme_ = false;
     70   }
     71 
     72   virtual void UseDefaultTheme() OVERRIDE {
     73     is_dirty_ = true;
     74     using_default_theme_ = true;
     75     using_system_theme_ = false;
     76     theme_extension_ = NULL;
     77   }
     78 
     79   virtual void UseSystemTheme() OVERRIDE {
     80     is_dirty_ = true;
     81     using_system_theme_ = true;
     82     using_default_theme_ = false;
     83     theme_extension_ = NULL;
     84   }
     85 
     86   virtual bool UsingDefaultTheme() const OVERRIDE {
     87     return using_default_theme_;
     88   }
     89 
     90   virtual bool UsingSystemTheme() const OVERRIDE {
     91     return using_system_theme_;
     92   }
     93 
     94   virtual string GetThemeID() const OVERRIDE {
     95     if (theme_extension_.get())
     96       return theme_extension_->id();
     97     else
     98       return std::string();
     99   }
    100 
    101   const extensions::Extension* theme_extension() const {
    102     return theme_extension_.get();
    103   }
    104 
    105   bool is_dirty() const {
    106     return is_dirty_;
    107   }
    108 
    109   void MarkClean() {
    110     is_dirty_ = false;
    111   }
    112 
    113  private:
    114   bool using_system_theme_;
    115   bool using_default_theme_;
    116   scoped_refptr<const extensions::Extension> theme_extension_;
    117   bool is_dirty_;
    118 };
    119 
    120 KeyedService* BuildMockThemeService(content::BrowserContext* profile) {
    121   return new FakeThemeService;
    122 }
    123 
    124 scoped_refptr<extensions::Extension> MakeThemeExtension(
    125     const base::FilePath& extension_path,
    126     const string& name,
    127     extensions::Manifest::Location location,
    128     const string& update_url) {
    129   base::DictionaryValue source;
    130   source.SetString(extensions::manifest_keys::kName, name);
    131   source.Set(extensions::manifest_keys::kTheme, new base::DictionaryValue());
    132   source.SetString(extensions::manifest_keys::kUpdateURL, update_url);
    133   source.SetString(extensions::manifest_keys::kVersion, "0.0.0.0");
    134   string error;
    135   scoped_refptr<extensions::Extension> extension =
    136       extensions::Extension::Create(
    137           extension_path, location, source,
    138           extensions::Extension::NO_FLAGS, &error);
    139   EXPECT_TRUE(extension.get());
    140   EXPECT_EQ("", error);
    141   return extension;
    142 }
    143 
    144 }  // namespace
    145 
    146 class ThemeSyncableServiceTest : public testing::Test {
    147  protected:
    148   ThemeSyncableServiceTest()
    149       : ui_thread_(content::BrowserThread::UI, &loop_),
    150         file_thread_(content::BrowserThread::FILE, &loop_),
    151         fake_theme_service_(NULL) {}
    152 
    153   virtual ~ThemeSyncableServiceTest() {}
    154 
    155   virtual void SetUp() {
    156     profile_.reset(new TestingProfile);
    157     fake_theme_service_ = BuildForProfile(profile_.get());
    158     theme_sync_service_.reset(new ThemeSyncableService(profile_.get(),
    159                                                        fake_theme_service_));
    160     fake_change_processor_.reset(new syncer::FakeSyncChangeProcessor);
    161     SetUpExtension();
    162   }
    163 
    164   virtual void TearDown() {
    165     profile_.reset();
    166     loop_.RunUntilIdle();
    167   }
    168 
    169   void SetUpExtension() {
    170     CommandLine command_line(CommandLine::NO_PROGRAM);
    171     extensions::TestExtensionSystem* test_ext_system =
    172         static_cast<extensions::TestExtensionSystem*>(
    173                 extensions::ExtensionSystem::Get(profile_.get()));
    174     ExtensionService* service = test_ext_system->CreateExtensionService(
    175         &command_line, base::FilePath(kExtensionFilePath), false);
    176     EXPECT_TRUE(service->extensions_enabled());
    177     service->Init();
    178     loop_.RunUntilIdle();
    179 
    180     // Create and add custom theme extension so the ThemeSyncableService can
    181     // find it.
    182     theme_extension_ = MakeThemeExtension(base::FilePath(kExtensionFilePath),
    183                                           kCustomThemeName,
    184                                           GetThemeLocation(),
    185                                           kCustomThemeUrl);
    186     extensions::APIPermissionSet empty_set;
    187     extensions::ManifestPermissionSet empty_manifest_permissions;
    188     extensions::URLPatternSet empty_extent;
    189     scoped_refptr<extensions::PermissionSet> permissions =
    190         new extensions::PermissionSet(empty_set, empty_manifest_permissions,
    191                                       empty_extent, empty_extent);
    192     extensions::ExtensionPrefs::Get(profile_.get())
    193         ->AddGrantedPermissions(theme_extension_->id(), permissions.get());
    194     service->AddExtension(theme_extension_.get());
    195     ASSERT_EQ(1u, service->extensions()->size());
    196   }
    197 
    198   // Overridden in PolicyInstalledThemeTest below.
    199   virtual extensions::Manifest::Location GetThemeLocation() {
    200     return extensions::Manifest::INTERNAL;
    201   }
    202 
    203   FakeThemeService* BuildForProfile(Profile* profile) {
    204     return static_cast<FakeThemeService*>(
    205         ThemeServiceFactory::GetInstance()->SetTestingFactoryAndUse(
    206             profile, &BuildMockThemeService));
    207   }
    208 
    209   syncer::SyncDataList MakeThemeDataList(
    210       const sync_pb::ThemeSpecifics& theme_specifics) {
    211     syncer::SyncDataList list;
    212     sync_pb::EntitySpecifics entity_specifics;
    213     entity_specifics.mutable_theme()->CopyFrom(theme_specifics);
    214     list.push_back(syncer::SyncData::CreateLocalData(
    215         ThemeSyncableService::kCurrentThemeClientTag,
    216         ThemeSyncableService::kCurrentThemeNodeTitle,
    217         entity_specifics));
    218     return list;
    219   }
    220 
    221   // Needed for setting up extension service.
    222   base::MessageLoop loop_;
    223   content::TestBrowserThread ui_thread_;
    224   content::TestBrowserThread file_thread_;
    225 
    226 #if defined OS_CHROMEOS
    227   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
    228   chromeos::ScopedTestCrosSettings test_cros_settings_;
    229   chromeos::ScopedTestUserManager test_user_manager_;
    230 #endif
    231 
    232   scoped_ptr<TestingProfile> profile_;
    233   FakeThemeService* fake_theme_service_;
    234   scoped_refptr<extensions::Extension> theme_extension_;
    235   scoped_ptr<ThemeSyncableService> theme_sync_service_;
    236   scoped_ptr<syncer::FakeSyncChangeProcessor> fake_change_processor_;
    237 };
    238 
    239 class PolicyInstalledThemeTest : public ThemeSyncableServiceTest {
    240   virtual extensions::Manifest::Location GetThemeLocation() OVERRIDE {
    241     return extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD;
    242   }
    243 };
    244 
    245 TEST_F(ThemeSyncableServiceTest, AreThemeSpecificsEqual) {
    246   sync_pb::ThemeSpecifics a, b;
    247   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    248   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    249 
    250   // Custom vs. non-custom.
    251 
    252   a.set_use_custom_theme(true);
    253   EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    254   EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    255 
    256   // Custom theme equality.
    257 
    258   b.set_use_custom_theme(true);
    259   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    260   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    261 
    262   a.set_custom_theme_id("id");
    263   EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    264   EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    265 
    266   b.set_custom_theme_id("id");
    267   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    268   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    269 
    270   a.set_custom_theme_update_url("http://update.url");
    271   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    272   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    273 
    274   a.set_custom_theme_name("name");
    275   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    276   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    277 
    278   // Non-custom theme equality.
    279 
    280   a.set_use_custom_theme(false);
    281   b.set_use_custom_theme(false);
    282   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    283   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    284 
    285   a.set_use_system_theme_by_default(true);
    286   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    287   EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    288 
    289   b.set_use_system_theme_by_default(true);
    290   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
    291   EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
    292 }
    293 
    294 TEST_F(ThemeSyncableServiceTest, SetCurrentThemeDefaultTheme) {
    295   // Set up theme service to use custom theme.
    296   fake_theme_service_->SetTheme(theme_extension_.get());
    297 
    298   syncer::SyncError error =
    299       theme_sync_service_
    300           ->MergeDataAndStartSyncing(
    301                 syncer::THEMES,
    302                 MakeThemeDataList(sync_pb::ThemeSpecifics()),
    303                 scoped_ptr<syncer::SyncChangeProcessor>(
    304                     new syncer::SyncChangeProcessorWrapperForTest(
    305                         fake_change_processor_.get())),
    306                 scoped_ptr<syncer::SyncErrorFactory>(
    307                     new syncer::SyncErrorFactoryMock()))
    308           .error();
    309   EXPECT_FALSE(error.IsSet()) << error.message();
    310   EXPECT_TRUE(fake_theme_service_->UsingDefaultTheme());
    311 }
    312 
    313 TEST_F(ThemeSyncableServiceTest, SetCurrentThemeSystemTheme) {
    314   sync_pb::ThemeSpecifics theme_specifics;
    315   theme_specifics.set_use_system_theme_by_default(true);
    316 
    317   // Set up theme service to use custom theme.
    318   fake_theme_service_->SetTheme(theme_extension_.get());
    319   syncer::SyncError error =
    320       theme_sync_service_
    321           ->MergeDataAndStartSyncing(
    322                 syncer::THEMES,
    323                 MakeThemeDataList(theme_specifics),
    324                 scoped_ptr<syncer::SyncChangeProcessor>(
    325                     new syncer::SyncChangeProcessorWrapperForTest(
    326                         fake_change_processor_.get())),
    327                 scoped_ptr<syncer::SyncErrorFactory>(
    328                     new syncer::SyncErrorFactoryMock()))
    329           .error();
    330   EXPECT_FALSE(error.IsSet()) << error.message();
    331   EXPECT_TRUE(fake_theme_service_->UsingSystemTheme());
    332 }
    333 
    334 TEST_F(ThemeSyncableServiceTest, SetCurrentThemeCustomTheme) {
    335   sync_pb::ThemeSpecifics theme_specifics;
    336   theme_specifics.set_use_custom_theme(true);
    337   theme_specifics.set_custom_theme_id(theme_extension_->id());
    338   theme_specifics.set_custom_theme_name(kCustomThemeName);
    339   theme_specifics.set_custom_theme_name(kCustomThemeUrl);
    340 
    341   // Set up theme service to use default theme.
    342   fake_theme_service_->UseDefaultTheme();
    343   syncer::SyncError error =
    344       theme_sync_service_
    345           ->MergeDataAndStartSyncing(
    346                 syncer::THEMES,
    347                 MakeThemeDataList(theme_specifics),
    348                 scoped_ptr<syncer::SyncChangeProcessor>(
    349                     new syncer::SyncChangeProcessorWrapperForTest(
    350                         fake_change_processor_.get())),
    351                 scoped_ptr<syncer::SyncErrorFactory>(
    352                     new syncer::SyncErrorFactoryMock()))
    353           .error();
    354   EXPECT_FALSE(error.IsSet()) << error.message();
    355   EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
    356 }
    357 
    358 TEST_F(ThemeSyncableServiceTest, DontResetThemeWhenSpecificsAreEqual) {
    359   // Set up theme service to use default theme and expect no changes.
    360   fake_theme_service_->UseDefaultTheme();
    361   fake_theme_service_->MarkClean();
    362   syncer::SyncError error =
    363       theme_sync_service_
    364           ->MergeDataAndStartSyncing(
    365                 syncer::THEMES,
    366                 MakeThemeDataList(sync_pb::ThemeSpecifics()),
    367                 scoped_ptr<syncer::SyncChangeProcessor>(
    368                     new syncer::SyncChangeProcessorWrapperForTest(
    369                         fake_change_processor_.get())),
    370                 scoped_ptr<syncer::SyncErrorFactory>(
    371                     new syncer::SyncErrorFactoryMock()))
    372           .error();
    373   EXPECT_FALSE(error.IsSet()) << error.message();
    374   EXPECT_FALSE(fake_theme_service_->is_dirty());
    375 }
    376 
    377 TEST_F(ThemeSyncableServiceTest, UpdateThemeSpecificsFromCurrentTheme) {
    378   // Set up theme service to use custom theme.
    379   fake_theme_service_->SetTheme(theme_extension_.get());
    380 
    381   syncer::SyncError error =
    382       theme_sync_service_
    383           ->MergeDataAndStartSyncing(
    384                 syncer::THEMES,
    385                 syncer::SyncDataList(),
    386                 scoped_ptr<syncer::SyncChangeProcessor>(
    387                     new syncer::SyncChangeProcessorWrapperForTest(
    388                         fake_change_processor_.get())),
    389                 scoped_ptr<syncer::SyncErrorFactory>(
    390                     new syncer::SyncErrorFactoryMock()))
    391           .error();
    392   EXPECT_FALSE(error.IsSet()) << error.message();
    393   const syncer::SyncChangeList& changes = fake_change_processor_->changes();
    394   ASSERT_EQ(1u, changes.size());
    395   EXPECT_TRUE(changes[0].IsValid());
    396   EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
    397   EXPECT_EQ(syncer::THEMES, changes[0].sync_data().GetDataType());
    398 
    399   const sync_pb::ThemeSpecifics& theme_specifics =
    400       changes[0].sync_data().GetSpecifics().theme();
    401   EXPECT_TRUE(theme_specifics.use_custom_theme());
    402   EXPECT_EQ(theme_extension_->id(), theme_specifics.custom_theme_id());
    403   EXPECT_EQ(theme_extension_->name(), theme_specifics.custom_theme_name());
    404   EXPECT_EQ(
    405       extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
    406       theme_specifics.custom_theme_update_url());
    407 }
    408 
    409 TEST_F(ThemeSyncableServiceTest, GetAllSyncData) {
    410   // Set up theme service to use custom theme.
    411   fake_theme_service_->SetTheme(theme_extension_.get());
    412 
    413   syncer::SyncDataList data_list =
    414       theme_sync_service_->GetAllSyncData(syncer::THEMES);
    415 
    416   ASSERT_EQ(1u, data_list.size());
    417   const sync_pb::ThemeSpecifics& theme_specifics =
    418       data_list[0].GetSpecifics().theme();
    419   EXPECT_TRUE(theme_specifics.use_custom_theme());
    420   EXPECT_EQ(theme_extension_->id(), theme_specifics.custom_theme_id());
    421   EXPECT_EQ(theme_extension_->name(), theme_specifics.custom_theme_name());
    422   EXPECT_EQ(
    423       extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
    424       theme_specifics.custom_theme_update_url());
    425 }
    426 
    427 TEST_F(ThemeSyncableServiceTest, ProcessSyncThemeChange) {
    428   // Set up theme service to use default theme.
    429   fake_theme_service_->UseDefaultTheme();
    430   fake_theme_service_->MarkClean();
    431 
    432   // Start syncing.
    433   syncer::SyncError error =
    434       theme_sync_service_
    435           ->MergeDataAndStartSyncing(
    436                 syncer::THEMES,
    437                 MakeThemeDataList(sync_pb::ThemeSpecifics()),
    438                 scoped_ptr<syncer::SyncChangeProcessor>(
    439                     new syncer::SyncChangeProcessorWrapperForTest(
    440                         fake_change_processor_.get())),
    441                 scoped_ptr<syncer::SyncErrorFactory>(
    442                     new syncer::SyncErrorFactoryMock()))
    443           .error();
    444   EXPECT_FALSE(error.IsSet()) << error.message();
    445   // Don't expect theme change initially because specifics are equal.
    446   EXPECT_FALSE(fake_theme_service_->is_dirty());
    447 
    448   // Change specifics to use custom theme and update.
    449   sync_pb::ThemeSpecifics theme_specifics;
    450   theme_specifics.set_use_custom_theme(true);
    451   theme_specifics.set_custom_theme_id(theme_extension_->id());
    452   theme_specifics.set_custom_theme_name(kCustomThemeName);
    453   theme_specifics.set_custom_theme_name(kCustomThemeUrl);
    454   sync_pb::EntitySpecifics entity_specifics;
    455   entity_specifics.mutable_theme()->CopyFrom(theme_specifics);
    456   syncer::SyncChangeList change_list;
    457   change_list.push_back(
    458       syncer::SyncChange(FROM_HERE,
    459                          syncer::SyncChange::ACTION_UPDATE,
    460                          syncer::SyncData::CreateRemoteData(
    461                              1,
    462                              entity_specifics,
    463                              base::Time(),
    464                              syncer::AttachmentIdList(),
    465                              syncer::AttachmentServiceProxyForTest::Create())));
    466   error = theme_sync_service_->ProcessSyncChanges(FROM_HERE, change_list);
    467   EXPECT_FALSE(error.IsSet()) << error.message();
    468   EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
    469 }
    470 
    471 TEST_F(ThemeSyncableServiceTest, OnThemeChangeByUser) {
    472   // Set up theme service to use default theme.
    473   fake_theme_service_->UseDefaultTheme();
    474 
    475   // Start syncing.
    476   syncer::SyncError error =
    477       theme_sync_service_
    478           ->MergeDataAndStartSyncing(
    479                 syncer::THEMES,
    480                 MakeThemeDataList(sync_pb::ThemeSpecifics()),
    481                 scoped_ptr<syncer::SyncChangeProcessor>(
    482                     new syncer::SyncChangeProcessorWrapperForTest(
    483                         fake_change_processor_.get())),
    484                 scoped_ptr<syncer::SyncErrorFactory>(
    485                     new syncer::SyncErrorFactoryMock()))
    486           .error();
    487   EXPECT_FALSE(error.IsSet()) << error.message();
    488   const syncer::SyncChangeList& changes = fake_change_processor_->changes();
    489   EXPECT_EQ(0u, changes.size());
    490 
    491   // Change current theme to custom theme and notify theme_sync_service_.
    492   fake_theme_service_->SetTheme(theme_extension_.get());
    493   theme_sync_service_->OnThemeChange();
    494   EXPECT_EQ(1u, changes.size());
    495   const sync_pb::ThemeSpecifics& change_specifics =
    496       changes[0].sync_data().GetSpecifics().theme();
    497   EXPECT_TRUE(change_specifics.use_custom_theme());
    498   EXPECT_EQ(theme_extension_->id(), change_specifics.custom_theme_id());
    499   EXPECT_EQ(theme_extension_->name(), change_specifics.custom_theme_name());
    500   EXPECT_EQ(
    501       extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
    502       change_specifics.custom_theme_update_url());
    503 }
    504 
    505 TEST_F(ThemeSyncableServiceTest, StopSync) {
    506   // Set up theme service to use default theme.
    507   fake_theme_service_->UseDefaultTheme();
    508 
    509   // Start syncing.
    510   syncer::SyncError error =
    511       theme_sync_service_
    512           ->MergeDataAndStartSyncing(
    513                 syncer::THEMES,
    514                 MakeThemeDataList(sync_pb::ThemeSpecifics()),
    515                 scoped_ptr<syncer::SyncChangeProcessor>(
    516                     new syncer::SyncChangeProcessorWrapperForTest(
    517                         fake_change_processor_.get())),
    518                 scoped_ptr<syncer::SyncErrorFactory>(
    519                     new syncer::SyncErrorFactoryMock()))
    520           .error();
    521   EXPECT_FALSE(error.IsSet()) << error.message();
    522   const syncer::SyncChangeList& changes = fake_change_processor_->changes();
    523   EXPECT_EQ(0u, changes.size());
    524 
    525   // Stop syncing.
    526   theme_sync_service_->StopSyncing(syncer::THEMES);
    527 
    528   // Change current theme to custom theme and notify theme_sync_service_.
    529   // No change is output because sync has stopped.
    530   fake_theme_service_->SetTheme(theme_extension_.get());
    531   theme_sync_service_->OnThemeChange();
    532   EXPECT_EQ(0u, changes.size());
    533 
    534   // ProcessSyncChanges() should return error when sync has stopped.
    535   error = theme_sync_service_->ProcessSyncChanges(FROM_HERE, changes);
    536   EXPECT_TRUE(error.IsSet());
    537   EXPECT_EQ(syncer::THEMES, error.model_type());
    538   EXPECT_EQ("datatype error was encountered: Theme syncable service is not "
    539                 "started.",
    540             error.message());
    541 }
    542 
    543 TEST_F(ThemeSyncableServiceTest, RestoreSystemThemeBitWhenChangeToCustomTheme) {
    544   // Initialize to use system theme.
    545   fake_theme_service_->UseDefaultTheme();
    546   sync_pb::ThemeSpecifics theme_specifics;
    547   theme_specifics.set_use_system_theme_by_default(true);
    548   syncer::SyncError error =
    549       theme_sync_service_
    550           ->MergeDataAndStartSyncing(
    551                 syncer::THEMES,
    552                 MakeThemeDataList(theme_specifics),
    553                 scoped_ptr<syncer::SyncChangeProcessor>(
    554                     new syncer::SyncChangeProcessorWrapperForTest(
    555                         fake_change_processor_.get())),
    556                 scoped_ptr<syncer::SyncErrorFactory>(
    557                     new syncer::SyncErrorFactoryMock()))
    558           .error();
    559 
    560   // Change to custom theme and notify theme_sync_service_.
    561   // use_system_theme_by_default bit should be preserved.
    562   fake_theme_service_->SetTheme(theme_extension_.get());
    563   theme_sync_service_->OnThemeChange();
    564   const syncer::SyncChangeList& changes = fake_change_processor_->changes();
    565   EXPECT_EQ(1u, changes.size());
    566   const sync_pb::ThemeSpecifics& change_specifics =
    567       changes[0].sync_data().GetSpecifics().theme();
    568   EXPECT_TRUE(change_specifics.use_system_theme_by_default());
    569 }
    570 
    571 #if defined(TOOLKIT_GTK)
    572 TEST_F(ThemeSyncableServiceTest,
    573        GtkUpdateSystemThemeBitWhenChangeBetweenSystemAndDefault) {
    574   // Initialize to use native theme.
    575   fake_theme_service_->UseSystemTheme();
    576   fake_theme_service_->MarkClean();
    577   sync_pb::ThemeSpecifics theme_specifics;
    578   theme_specifics.set_use_system_theme_by_default(true);
    579   syncer::SyncError error =
    580       theme_sync_service_
    581           ->MergeDataAndStartSyncing(
    582                 syncer::THEMES,
    583                 MakeThemeDataList(theme_specifics),
    584                 scoped_ptr<syncer::SyncChangeProcessor>(
    585                     new syncer::SyncChangeProcessorWrapperForTest(
    586                         fake_change_processor_.get())),
    587                 scoped_ptr<syncer::SyncErrorFactory>(
    588                     new syncer::SyncErrorFactoryMock()))
    589           .error();
    590   EXPECT_FALSE(fake_theme_service_->is_dirty());
    591 
    592   // Change to default theme and notify theme_sync_service_.
    593   // use_system_theme_by_default bit should be false.
    594   fake_theme_service_->UseDefaultTheme();
    595   theme_sync_service_->OnThemeChange();
    596   syncer::SyncChangeList& changes = fake_change_processor_->changes();
    597   EXPECT_EQ(1u, changes.size());
    598   EXPECT_FALSE(changes[0]
    599                    .sync_data()
    600                    .GetSpecifics()
    601                    .theme()
    602                    .use_system_theme_by_default());
    603 
    604   // Change to native theme and notify theme_sync_service_.
    605   // use_system_theme_by_default bit should be true.
    606   changes.clear();
    607   fake_theme_service_->UseSystemTheme();
    608   theme_sync_service_->OnThemeChange();
    609   EXPECT_EQ(1u, changes.size());
    610   EXPECT_TRUE(changes[0]
    611                   .sync_data()
    612                   .GetSpecifics()
    613                   .theme()
    614                   .use_system_theme_by_default());
    615 }
    616 #endif
    617 
    618 #ifndef TOOLKIT_GTK
    619 TEST_F(ThemeSyncableServiceTest,
    620        NonGtkPreserveSystemThemeBitWhenChangeToDefaultTheme) {
    621   // Set up theme service to use default theme.
    622   fake_theme_service_->UseDefaultTheme();
    623 
    624   // Initialize to use custom theme with use_system_theme_by_default set true.
    625   sync_pb::ThemeSpecifics theme_specifics;
    626   theme_specifics.set_use_custom_theme(true);
    627   theme_specifics.set_custom_theme_id(theme_extension_->id());
    628   theme_specifics.set_custom_theme_name(kCustomThemeName);
    629   theme_specifics.set_custom_theme_name(kCustomThemeUrl);
    630   theme_specifics.set_use_system_theme_by_default(true);
    631   syncer::SyncError error =
    632       theme_sync_service_
    633           ->MergeDataAndStartSyncing(
    634                 syncer::THEMES,
    635                 MakeThemeDataList(theme_specifics),
    636                 scoped_ptr<syncer::SyncChangeProcessor>(
    637                     new syncer::SyncChangeProcessorWrapperForTest(
    638                         fake_change_processor_.get())),
    639                 scoped_ptr<syncer::SyncErrorFactory>(
    640                     new syncer::SyncErrorFactoryMock()))
    641           .error();
    642   EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
    643 
    644   // Change to default theme and notify theme_sync_service_.
    645   // use_system_theme_by_default bit should be preserved.
    646   fake_theme_service_->UseDefaultTheme();
    647   theme_sync_service_->OnThemeChange();
    648   const syncer::SyncChangeList& changes = fake_change_processor_->changes();
    649   EXPECT_EQ(1u, changes.size());
    650   const sync_pb::ThemeSpecifics& change_specifics =
    651       changes[0].sync_data().GetSpecifics().theme();
    652   EXPECT_FALSE(change_specifics.use_custom_theme());
    653   EXPECT_TRUE(change_specifics.use_system_theme_by_default());
    654 }
    655 #endif
    656 
    657 TEST_F(PolicyInstalledThemeTest, InstallThemeByPolicy) {
    658   // Set up theme service to use custom theme that was installed by policy.
    659   fake_theme_service_->SetTheme(theme_extension_.get());
    660 
    661   syncer::SyncDataList data_list =
    662       theme_sync_service_->GetAllSyncData(syncer::THEMES);
    663 
    664   ASSERT_EQ(0u, data_list.size());
    665 }
    666