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/extension_change_processor.h" 6 7 #include <sstream> 8 #include <string> 9 10 #include "base/logging.h" 11 #include "base/stl_util-inl.h" 12 #include "chrome/browser/extensions/extension_service.h" 13 #include "chrome/browser/extensions/extension_sync_data.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/sync/glue/extension_sync.h" 16 #include "chrome/browser/sync/glue/extension_util.h" 17 #include "chrome/browser/sync/profile_sync_service.h" 18 #include "chrome/browser/sync/protocol/extension_specifics.pb.h" 19 #include "chrome/common/extensions/extension.h" 20 #include "content/browser/browser_thread.h" 21 #include "content/common/notification_details.h" 22 #include "content/common/notification_source.h" 23 24 namespace browser_sync { 25 26 ExtensionChangeProcessor::ExtensionChangeProcessor( 27 const ExtensionSyncTraits& traits, 28 UnrecoverableErrorHandler* error_handler) 29 : ChangeProcessor(error_handler), 30 traits_(traits), 31 profile_(NULL), 32 extension_service_(NULL), 33 user_share_(NULL) { 34 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 35 DCHECK(error_handler); 36 } 37 38 ExtensionChangeProcessor::~ExtensionChangeProcessor() { 39 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 40 } 41 42 // TODO(akalin): We need to make sure events we receive from either 43 // the browser or the syncapi are done in order; this is tricky since 44 // some events (e.g., extension installation) are done asynchronously. 45 46 void ExtensionChangeProcessor::Observe(NotificationType type, 47 const NotificationSource& source, 48 const NotificationDetails& details) { 49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 50 DCHECK(running()); 51 DCHECK(profile_); 52 if ((type != NotificationType::EXTENSION_INSTALLED) && 53 (type != NotificationType::EXTENSION_UNINSTALLED) && 54 (type != NotificationType::EXTENSION_LOADED) && 55 (type != NotificationType::EXTENSION_UPDATE_DISABLED) && 56 (type != NotificationType::EXTENSION_UNLOADED)) { 57 LOG(DFATAL) << "Received unexpected notification of type " 58 << type.value; 59 return; 60 } 61 62 DCHECK_EQ(Source<Profile>(source).ptr(), profile_); 63 if (type == NotificationType::EXTENSION_UNINSTALLED) { 64 const UninstalledExtensionInfo* uninstalled_extension_info = 65 Details<UninstalledExtensionInfo>(details).ptr(); 66 CHECK(uninstalled_extension_info); 67 if (traits_.should_handle_extension_uninstall( 68 *uninstalled_extension_info)) { 69 const std::string& id = uninstalled_extension_info->extension_id; 70 VLOG(1) << "Removing server data for uninstalled extension " << id 71 << " of type " << uninstalled_extension_info->extension_type; 72 RemoveServerData(traits_, id, user_share_); 73 } 74 } else { 75 const Extension* extension = NULL; 76 if (type == NotificationType::EXTENSION_UNLOADED) { 77 extension = Details<UnloadedExtensionInfo>(details)->extension; 78 } else { 79 extension = Details<const Extension>(details).ptr(); 80 } 81 CHECK(extension); 82 VLOG(1) << "Updating server data for extension " << extension->id() 83 << " (notification type = " << type.value << ")"; 84 if (!traits_.is_valid_and_syncable(*extension)) { 85 return; 86 } 87 std::string error; 88 if (!UpdateServerData(traits_, *extension, *extension_service_, 89 user_share_, &error)) { 90 error_handler()->OnUnrecoverableError(FROM_HERE, error); 91 } 92 } 93 } 94 95 void ExtensionChangeProcessor::ApplyChangesFromSyncModel( 96 const sync_api::BaseTransaction* trans, 97 const sync_api::SyncManager::ChangeRecord* changes, 98 int change_count) { 99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 100 if (!running()) { 101 return; 102 } 103 for (int i = 0; i < change_count; ++i) { 104 const sync_api::SyncManager::ChangeRecord& change = changes[i]; 105 sync_pb::ExtensionSpecifics specifics; 106 switch (change.action) { 107 case sync_api::SyncManager::ChangeRecord::ACTION_ADD: 108 case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: { 109 sync_api::ReadNode node(trans); 110 if (!node.InitByIdLookup(change.id)) { 111 std::stringstream error; 112 error << "Extension node lookup failed for change " << change.id 113 << " of action type " << change.action; 114 error_handler()->OnUnrecoverableError(FROM_HERE, error.str()); 115 return; 116 } 117 DCHECK_EQ(node.GetModelType(), traits_.model_type); 118 specifics = (*traits_.extension_specifics_getter)(node); 119 break; 120 } 121 case sync_api::SyncManager::ChangeRecord::ACTION_DELETE: { 122 if (!(*traits_.extension_specifics_entity_getter)( 123 change.specifics, &specifics)) { 124 std::stringstream error; 125 error << "Could not get extension specifics from deleted node " 126 << change.id; 127 error_handler()->OnUnrecoverableError(FROM_HERE, error.str()); 128 LOG(DFATAL) << error.str(); 129 } 130 break; 131 } 132 } 133 ExtensionSyncData sync_data; 134 if (!GetExtensionSyncData(specifics, &sync_data)) { 135 // TODO(akalin): Should probably recover or drop. 136 std::string error = 137 std::string("Invalid server specifics: ") + 138 ExtensionSpecificsToString(specifics); 139 error_handler()->OnUnrecoverableError(FROM_HERE, error); 140 return; 141 } 142 sync_data.uninstalled = 143 (change.action == sync_api::SyncManager::ChangeRecord::ACTION_DELETE); 144 StopObserving(); 145 extension_service_->ProcessSyncData(sync_data, 146 traits_.is_valid_and_syncable); 147 StartObserving(); 148 } 149 } 150 151 void ExtensionChangeProcessor::StartImpl(Profile* profile) { 152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 153 profile_ = profile; 154 extension_service_ = profile_->GetExtensionService(); 155 user_share_ = profile_->GetProfileSyncService()->GetUserShare(); 156 DCHECK(profile_); 157 DCHECK(extension_service_); 158 DCHECK(user_share_); 159 StartObserving(); 160 } 161 162 void ExtensionChangeProcessor::StopImpl() { 163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 164 StopObserving(); 165 profile_ = NULL; 166 extension_service_ = NULL; 167 user_share_ = NULL; 168 } 169 170 void ExtensionChangeProcessor::StartObserving() { 171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 172 DCHECK(profile_); 173 notification_registrar_.Add( 174 this, NotificationType::EXTENSION_INSTALLED, 175 Source<Profile>(profile_)); 176 notification_registrar_.Add( 177 this, NotificationType::EXTENSION_UNINSTALLED, 178 Source<Profile>(profile_)); 179 180 notification_registrar_.Add( 181 this, NotificationType::EXTENSION_LOADED, 182 Source<Profile>(profile_)); 183 // Despite the name, this notification is exactly like 184 // EXTENSION_LOADED but with an initial state of DISABLED. 185 notification_registrar_.Add( 186 this, NotificationType::EXTENSION_UPDATE_DISABLED, 187 Source<Profile>(profile_)); 188 189 notification_registrar_.Add( 190 this, NotificationType::EXTENSION_UNLOADED, 191 Source<Profile>(profile_)); 192 } 193 194 void ExtensionChangeProcessor::StopObserving() { 195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 196 DCHECK(profile_); 197 VLOG(1) << "Unobserving all notifications"; 198 notification_registrar_.RemoveAll(); 199 } 200 201 } // namespace browser_sync 202