1 // Copyright (c) 2011 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/sync/glue/theme_change_processor.h" 6 7 #include "base/logging.h" 8 #include "chrome/browser/profiles/profile.h" 9 #include "chrome/browser/sync/engine/syncapi.h" 10 #include "chrome/browser/sync/glue/theme_util.h" 11 #include "chrome/browser/sync/protocol/theme_specifics.pb.h" 12 #include "chrome/browser/themes/theme_service.h" 13 #include "chrome/browser/themes/theme_service_factory.h" 14 #include "chrome/common/extensions/extension.h" 15 #include "content/common/notification_details.h" 16 #include "content/common/notification_source.h" 17 18 namespace browser_sync { 19 20 ThemeChangeProcessor::ThemeChangeProcessor( 21 UnrecoverableErrorHandler* error_handler) 22 : ChangeProcessor(error_handler), 23 profile_(NULL) { 24 DCHECK(error_handler); 25 } 26 27 ThemeChangeProcessor::~ThemeChangeProcessor() {} 28 29 void ThemeChangeProcessor::Observe(NotificationType type, 30 const NotificationSource& source, 31 const NotificationDetails& details) { 32 DCHECK(running()); 33 DCHECK(profile_); 34 DCHECK(type == NotificationType::BROWSER_THEME_CHANGED); 35 36 sync_api::WriteTransaction trans(share_handle()); 37 sync_api::WriteNode node(&trans); 38 if (!node.InitByClientTagLookup(syncable::THEMES, 39 kCurrentThemeClientTag)) { 40 std::string err = "Could not create node with client tag: "; 41 error_handler()->OnUnrecoverableError(FROM_HERE, 42 err + kCurrentThemeClientTag); 43 return; 44 } 45 46 sync_pb::ThemeSpecifics old_theme_specifics = node.GetThemeSpecifics(); 47 // Make sure to base new_theme_specifics on old_theme_specifics so 48 // we preserve the state of use_system_theme_by_default. 49 sync_pb::ThemeSpecifics new_theme_specifics = old_theme_specifics; 50 GetThemeSpecificsFromCurrentTheme(profile_, &new_theme_specifics); 51 // Do a write only if something actually changed so as to guard 52 // against cycles. 53 if (!AreThemeSpecificsEqual(old_theme_specifics, new_theme_specifics)) { 54 node.SetThemeSpecifics(new_theme_specifics); 55 } 56 return; 57 } 58 59 void ThemeChangeProcessor::ApplyChangesFromSyncModel( 60 const sync_api::BaseTransaction* trans, 61 const sync_api::SyncManager::ChangeRecord* changes, 62 int change_count) { 63 if (!running()) { 64 return; 65 } 66 // TODO(akalin): Normally, we should only have a single change and 67 // it should be an update. However, the syncapi may occasionally 68 // generates multiple changes. When we fix syncapi to not do that, 69 // we can remove the extra logic below. See: 70 // http://code.google.com/p/chromium/issues/detail?id=41696 . 71 if (change_count < 1) { 72 std::string err("Unexpected change_count: "); 73 err += change_count; 74 error_handler()->OnUnrecoverableError(FROM_HERE, err); 75 return; 76 } 77 if (change_count > 1) { 78 LOG(WARNING) << change_count << " theme changes detected; " 79 << "only applying the last one"; 80 } 81 const sync_api::SyncManager::ChangeRecord& change = 82 changes[change_count - 1]; 83 if (change.action != sync_api::SyncManager::ChangeRecord::ACTION_UPDATE && 84 change.action != sync_api::SyncManager::ChangeRecord::ACTION_DELETE) { 85 std::string err = "strange theme change.action " + change.action; 86 error_handler()->OnUnrecoverableError(FROM_HERE, err); 87 return; 88 } 89 sync_pb::ThemeSpecifics theme_specifics; 90 // If the action is a delete, simply use the default values for 91 // ThemeSpecifics, which would cause the default theme to be set. 92 if (change.action != sync_api::SyncManager::ChangeRecord::ACTION_DELETE) { 93 sync_api::ReadNode node(trans); 94 if (!node.InitByIdLookup(change.id)) { 95 error_handler()->OnUnrecoverableError(FROM_HERE, 96 "Theme node lookup failed."); 97 return; 98 } 99 DCHECK_EQ(node.GetModelType(), syncable::THEMES); 100 DCHECK(profile_); 101 theme_specifics = node.GetThemeSpecifics(); 102 } 103 StopObserving(); 104 SetCurrentThemeFromThemeSpecificsIfNecessary(theme_specifics, profile_); 105 StartObserving(); 106 } 107 108 void ThemeChangeProcessor::StartImpl(Profile* profile) { 109 DCHECK(profile); 110 profile_ = profile; 111 StartObserving(); 112 } 113 114 void ThemeChangeProcessor::StopImpl() { 115 StopObserving(); 116 profile_ = NULL; 117 } 118 119 void ThemeChangeProcessor::StartObserving() { 120 DCHECK(profile_); 121 VLOG(1) << "Observing BROWSER_THEME_CHANGED"; 122 notification_registrar_.Add( 123 this, NotificationType::BROWSER_THEME_CHANGED, 124 Source<ThemeService>( 125 ThemeServiceFactory::GetForProfile(profile_))); 126 } 127 128 void ThemeChangeProcessor::StopObserving() { 129 DCHECK(profile_); 130 VLOG(1) << "Unobserving all notifications"; 131 notification_registrar_.RemoveAll(); 132 } 133 134 } // namespace browser_sync 135