1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/glue/extension_change_processor.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <sstream> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string> 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h" 11731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/stl_util-inl.h" 1221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/extensions/extension_service.h" 13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/extensions/extension_sync_data.h" 14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/profiles/profile.h" 153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/sync/glue/extension_sync.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/glue/extension_util.h" 17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/sync/profile_sync_service.h" 183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/sync/protocol/extension_specifics.pb.h" 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h" 20dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h" 21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_details.h" 22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h" 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace browser_sync { 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionChangeProcessor::ExtensionChangeProcessor( 273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const ExtensionSyncTraits& traits, 283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick UnrecoverableErrorHandler* error_handler) 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : ChangeProcessor(error_handler), 303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick traits_(traits), 31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen profile_(NULL), 32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen extension_service_(NULL), 33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen user_share_(NULL) { 34731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(error_handler); 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionChangeProcessor::~ExtensionChangeProcessor() { 39731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TODO(akalin): We need to make sure events we receive from either 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// the browser or the syncapi are done in order; this is tricky since 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// some events (e.g., extension installation) are done asynchronously. 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::Observe(NotificationType type, 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationSource& source, 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationDetails& details) { 49731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(running()); 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(profile_); 52731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if ((type != NotificationType::EXTENSION_INSTALLED) && 53731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick (type != NotificationType::EXTENSION_UNINSTALLED) && 54731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick (type != NotificationType::EXTENSION_LOADED) && 553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick (type != NotificationType::EXTENSION_UPDATE_DISABLED) && 5621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen (type != NotificationType::EXTENSION_UNLOADED)) { 573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick LOG(DFATAL) << "Received unexpected notification of type " 583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick << type.value; 593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DCHECK_EQ(Source<Profile>(source).ptr(), profile_); 63731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (type == NotificationType::EXTENSION_UNINSTALLED) { 64731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick const UninstalledExtensionInfo* uninstalled_extension_info = 65731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick Details<UninstalledExtensionInfo>(details).ptr(); 66731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick CHECK(uninstalled_extension_info); 6721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (traits_.should_handle_extension_uninstall( 6821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen *uninstalled_extension_info)) { 69731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick const std::string& id = uninstalled_extension_info->extension_id; 70731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick VLOG(1) << "Removing server data for uninstalled extension " << id 7121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen << " of type " << uninstalled_extension_info->extension_type; 72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen RemoveServerData(traits_, id, user_share_); 73731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 74731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } else { 7521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen const Extension* extension = NULL; 7621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (type == NotificationType::EXTENSION_UNLOADED) { 7721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen extension = Details<UnloadedExtensionInfo>(details)->extension; 7821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen } else { 7921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen extension = Details<const Extension>(details).ptr(); 8021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen } 81731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick CHECK(extension); 82731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick VLOG(1) << "Updating server data for extension " << extension->id() 83731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick << " (notification type = " << type.value << ")"; 8421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (!traits_.is_valid_and_syncable(*extension)) { 85731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return; 86731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick std::string error; 88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!UpdateServerData(traits_, *extension, *extension_service_, 89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen user_share_, &error)) { 903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick error_handler()->OnUnrecoverableError(FROM_HERE, error); 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::ApplyChangesFromSyncModel( 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const sync_api::BaseTransaction* trans, 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const sync_api::SyncManager::ChangeRecord* changes, 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int change_count) { 99731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!running()) { 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < change_count; ++i) { 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const sync_api::SyncManager::ChangeRecord& change = changes[i]; 105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sync_pb::ExtensionSpecifics specifics; 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch switch (change.action) { 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case sync_api::SyncManager::ChangeRecord::ACTION_ADD: 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: { 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch sync_api::ReadNode node(trans); 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!node.InitByIdLookup(change.id)) { 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::stringstream error; 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch error << "Extension node lookup failed for change " << change.id 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch << " of action type " << change.action; 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch error_handler()->OnUnrecoverableError(FROM_HERE, error.str()); 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 1173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DCHECK_EQ(node.GetModelType(), traits_.model_type); 118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen specifics = (*traits_.extension_specifics_getter)(node); 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch break; 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case sync_api::SyncManager::ChangeRecord::ACTION_DELETE: { 122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!(*traits_.extension_specifics_entity_getter)( 1233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick change.specifics, &specifics)) { 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::stringstream error; 125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen error << "Could not get extension specifics from deleted node " 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch << change.id; 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch error_handler()->OnUnrecoverableError(FROM_HERE, error.str()); 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(DFATAL) << error.str(); 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch break; 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ExtensionSyncData sync_data; 134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!GetExtensionSyncData(specifics, &sync_data)) { 135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(akalin): Should probably recover or drop. 136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::string error = 137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::string("Invalid server specifics: ") + 138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ExtensionSpecificsToString(specifics); 139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen error_handler()->OnUnrecoverableError(FROM_HERE, error); 140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sync_data.uninstalled = 143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen (change.action == sync_api::SyncManager::ChangeRecord::ACTION_DELETE); 144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen StopObserving(); 145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen extension_service_->ProcessSyncData(sync_data, 146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen traits_.is_valid_and_syncable); 147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen StartObserving(); 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::StartImpl(Profile* profile) { 152731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_ = profile; 154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen extension_service_ = profile_->GetExtensionService(); 155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen user_share_ = profile_->GetProfileSyncService()->GetUserShare(); 156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(profile_); 157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(extension_service_); 158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(user_share_); 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StartObserving(); 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::StopImpl() { 163731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StopObserving(); 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_ = NULL; 166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen extension_service_ = NULL; 167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen user_share_ = NULL; 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::StartObserving() { 171731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(profile_); 173731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick notification_registrar_.Add( 174731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick this, NotificationType::EXTENSION_INSTALLED, 175731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick Source<Profile>(profile_)); 176731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick notification_registrar_.Add( 177731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick this, NotificationType::EXTENSION_UNINSTALLED, 178731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick Source<Profile>(profile_)); 179731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch notification_registrar_.Add( 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this, NotificationType::EXTENSION_LOADED, 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<Profile>(profile_)); 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Despite the name, this notification is exactly like 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // EXTENSION_LOADED but with an initial state of DISABLED. 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch notification_registrar_.Add( 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this, NotificationType::EXTENSION_UPDATE_DISABLED, 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<Profile>(profile_)); 188731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch notification_registrar_.Add( 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this, NotificationType::EXTENSION_UNLOADED, 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<Profile>(profile_)); 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::StopObserving() { 195731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(profile_); 197731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick VLOG(1) << "Unobserving all notifications"; 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch notification_registrar_.RemoveAll(); 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace browser_sync 202