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