extension_sync_service.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1// Copyright 2013 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/extensions/extension_sync_service.h" 6 7#include <iterator> 8 9#include "base/basictypes.h" 10#include "base/threading/sequenced_worker_pool.h" 11#include "base/threading/thread_restrictions.h" 12#include "chrome/browser/extensions/app_sync_data.h" 13#include "chrome/browser/extensions/extension_service.h" 14#include "chrome/browser/extensions/extension_sync_data.h" 15#include "chrome/browser/extensions/extension_sync_service_factory.h" 16#include "chrome/browser/extensions/extension_util.h" 17#include "chrome/browser/extensions/launch_util.h" 18#include "chrome/browser/profiles/profile.h" 19#include "chrome/browser/sync/glue/sync_start_util.h" 20#include "chrome/common/extensions/sync_helper.h" 21#include "components/sync_driver/sync_prefs.h" 22#include "extensions/browser/app_sorting.h" 23#include "extensions/browser/extension_prefs.h" 24#include "extensions/browser/extension_registry.h" 25#include "extensions/common/extension.h" 26#include "extensions/common/feature_switch.h" 27#include "extensions/common/manifest_constants.h" 28#include "sync/api/sync_change.h" 29#include "sync/api/sync_error_factory.h" 30 31using extensions::Extension; 32using extensions::ExtensionPrefs; 33using extensions::ExtensionRegistry; 34using extensions::FeatureSwitch; 35 36ExtensionSyncService::ExtensionSyncService(Profile* profile, 37 ExtensionPrefs* extension_prefs, 38 ExtensionService* extension_service) 39 : profile_(profile), 40 extension_prefs_(extension_prefs), 41 extension_service_(extension_service), 42 app_sync_bundle_(this), 43 extension_sync_bundle_(this), 44 pending_app_enables_(make_scoped_ptr(new sync_driver::SyncPrefs( 45 extension_prefs_->pref_service())), 46 &app_sync_bundle_, 47 syncer::APPS), 48 pending_extension_enables_(make_scoped_ptr(new sync_driver::SyncPrefs( 49 extension_prefs_->pref_service())), 50 &extension_sync_bundle_, 51 syncer::EXTENSIONS) { 52 SetSyncStartFlare(sync_start_util::GetFlareForSyncableService( 53 profile_->GetPath())); 54 55 extension_service_->set_extension_sync_service(this); 56 extension_prefs_->app_sorting()->SetExtensionSyncService(this); 57} 58 59ExtensionSyncService::~ExtensionSyncService() {} 60 61// static 62ExtensionSyncService* ExtensionSyncService::Get(Profile* profile) { 63 return ExtensionSyncServiceFactory::GetForProfile(profile); 64} 65 66syncer::SyncChange ExtensionSyncService::PrepareToSyncUninstallExtension( 67 const extensions::Extension* extension, bool extensions_ready) { 68 // Extract the data we need for sync now, but don't actually sync until we've 69 // completed the uninstallation. 70 // TODO(tim): If we get here and IsSyncing is false, this will cause 71 // "back from the dead" style bugs, because sync will add-back the extension 72 // that was uninstalled here when MergeDataAndStartSyncing is called. 73 // See crbug.com/256795. 74 if (extensions::sync_helper::IsSyncableApp(extension)) { 75 if (app_sync_bundle_.IsSyncing()) 76 return app_sync_bundle_.CreateSyncChangeToDelete(extension); 77 else if (extensions_ready && !flare_.is_null()) 78 flare_.Run(syncer::APPS); // Tell sync to start ASAP. 79 } else if (extensions::sync_helper::IsSyncableExtension(extension)) { 80 if (extension_sync_bundle_.IsSyncing()) 81 return extension_sync_bundle_.CreateSyncChangeToDelete(extension); 82 else if (extensions_ready && !flare_.is_null()) 83 flare_.Run(syncer::EXTENSIONS); // Tell sync to start ASAP. 84 } 85 86 return syncer::SyncChange(); 87} 88 89void ExtensionSyncService::ProcessSyncUninstallExtension( 90 const std::string& extension_id, 91 const syncer::SyncChange& sync_change) { 92 if (app_sync_bundle_.HasExtensionId(extension_id) && 93 sync_change.sync_data().GetDataType() == syncer::APPS) { 94 app_sync_bundle_.ProcessDeletion(extension_id, sync_change); 95 } else if (extension_sync_bundle_.HasExtensionId(extension_id) && 96 sync_change.sync_data().GetDataType() == syncer::EXTENSIONS) { 97 extension_sync_bundle_.ProcessDeletion(extension_id, sync_change); 98 } 99} 100 101void ExtensionSyncService::SyncEnableExtension( 102 const extensions::Extension& extension) { 103 104 // Syncing may not have started yet, so handle pending enables. 105 if (extensions::sync_helper::IsSyncableApp(&extension)) 106 pending_app_enables_.OnExtensionEnabled(extension.id()); 107 108 if (extensions::sync_helper::IsSyncableExtension(&extension)) 109 pending_extension_enables_.OnExtensionEnabled(extension.id()); 110 111 SyncExtensionChangeIfNeeded(extension); 112} 113 114void ExtensionSyncService::SyncDisableExtension( 115 const extensions::Extension& extension) { 116 117 // Syncing may not have started yet, so handle pending enables. 118 if (extensions::sync_helper::IsSyncableApp(&extension)) 119 pending_app_enables_.OnExtensionDisabled(extension.id()); 120 121 if (extensions::sync_helper::IsSyncableExtension(&extension)) 122 pending_extension_enables_.OnExtensionDisabled(extension.id()); 123 124 SyncExtensionChangeIfNeeded(extension); 125} 126 127syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing( 128 syncer::ModelType type, 129 const syncer::SyncDataList& initial_sync_data, 130 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, 131 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) { 132 CHECK(sync_processor.get()); 133 CHECK(sync_error_factory.get()); 134 135 switch (type) { 136 case syncer::EXTENSIONS: 137 extension_sync_bundle_.SetupSync(sync_processor.release(), 138 sync_error_factory.release(), 139 initial_sync_data); 140 pending_extension_enables_.OnSyncStarted(extension_service_); 141 break; 142 143 case syncer::APPS: 144 app_sync_bundle_.SetupSync(sync_processor.release(), 145 sync_error_factory.release(), 146 initial_sync_data); 147 pending_app_enables_.OnSyncStarted(extension_service_); 148 break; 149 150 default: 151 LOG(FATAL) << "Got " << type << " ModelType"; 152 } 153 154 // Process local extensions. 155 // TODO(yoz): Determine whether pending extensions should be considered too. 156 // See crbug.com/104399. 157 syncer::SyncDataList sync_data_list = GetAllSyncData(type); 158 syncer::SyncChangeList sync_change_list; 159 for (syncer::SyncDataList::const_iterator i = sync_data_list.begin(); 160 i != sync_data_list.end(); 161 ++i) { 162 switch (type) { 163 case syncer::EXTENSIONS: 164 sync_change_list.push_back( 165 extension_sync_bundle_.CreateSyncChange(*i)); 166 break; 167 case syncer::APPS: 168 sync_change_list.push_back(app_sync_bundle_.CreateSyncChange(*i)); 169 break; 170 default: 171 LOG(FATAL) << "Got " << type << " ModelType"; 172 } 173 } 174 175 176 if (type == syncer::EXTENSIONS) { 177 extension_sync_bundle_.ProcessSyncChangeList(sync_change_list); 178 } else if (type == syncer::APPS) { 179 app_sync_bundle_.ProcessSyncChangeList(sync_change_list); 180 } 181 182 return syncer::SyncMergeResult(type); 183} 184 185void ExtensionSyncService::StopSyncing(syncer::ModelType type) { 186 if (type == syncer::APPS) { 187 app_sync_bundle_.Reset(); 188 } else if (type == syncer::EXTENSIONS) { 189 extension_sync_bundle_.Reset(); 190 } 191} 192 193syncer::SyncDataList ExtensionSyncService::GetAllSyncData( 194 syncer::ModelType type) const { 195 if (type == syncer::EXTENSIONS) 196 return extension_sync_bundle_.GetAllSyncData(); 197 if (type == syncer::APPS) 198 return app_sync_bundle_.GetAllSyncData(); 199 200 // We should only get sync data for extensions and apps. 201 NOTREACHED(); 202 203 return syncer::SyncDataList(); 204} 205 206syncer::SyncError ExtensionSyncService::ProcessSyncChanges( 207 const tracked_objects::Location& from_here, 208 const syncer::SyncChangeList& change_list) { 209 for (syncer::SyncChangeList::const_iterator i = change_list.begin(); 210 i != change_list.end(); 211 ++i) { 212 syncer::ModelType type = i->sync_data().GetDataType(); 213 if (type == syncer::EXTENSIONS) { 214 extension_sync_bundle_.ProcessSyncChange( 215 extensions::ExtensionSyncData(*i)); 216 } else if (type == syncer::APPS) { 217 app_sync_bundle_.ProcessSyncChange(extensions::AppSyncData(*i)); 218 } 219 } 220 221 extension_prefs_->app_sorting()->FixNTPOrdinalCollisions(); 222 223 return syncer::SyncError(); 224} 225 226extensions::ExtensionSyncData ExtensionSyncService::GetExtensionSyncData( 227 const Extension& extension) const { 228 return extensions::ExtensionSyncData( 229 extension, 230 extension_service_->IsExtensionEnabled(extension.id()), 231 extensions::util::IsIncognitoEnabled(extension.id(), profile_)); 232} 233 234extensions::AppSyncData ExtensionSyncService::GetAppSyncData( 235 const Extension& extension) const { 236 return extensions::AppSyncData( 237 extension, 238 extension_service_->IsExtensionEnabled(extension.id()), 239 extensions::util::IsIncognitoEnabled(extension.id(), profile_), 240 extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()), 241 extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()), 242 extensions::GetLaunchTypePrefValue(extension_prefs_, extension.id())); 243} 244 245std::vector<extensions::ExtensionSyncData> 246 ExtensionSyncService::GetExtensionSyncDataList() const { 247 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); 248 std::vector<extensions::ExtensionSyncData> extension_sync_list; 249 extension_sync_bundle_.GetExtensionSyncDataListHelper( 250 registry->enabled_extensions(), &extension_sync_list); 251 extension_sync_bundle_.GetExtensionSyncDataListHelper( 252 registry->disabled_extensions(), &extension_sync_list); 253 extension_sync_bundle_.GetExtensionSyncDataListHelper( 254 registry->terminated_extensions(), &extension_sync_list); 255 256 std::vector<extensions::ExtensionSyncData> pending_extensions = 257 extension_sync_bundle_.GetPendingData(); 258 extension_sync_list.insert(extension_sync_list.begin(), 259 pending_extensions.begin(), 260 pending_extensions.end()); 261 262 return extension_sync_list; 263} 264 265std::vector<extensions::AppSyncData> ExtensionSyncService::GetAppSyncDataList() 266 const { 267 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); 268 std::vector<extensions::AppSyncData> app_sync_list; 269 app_sync_bundle_.GetAppSyncDataListHelper( 270 registry->enabled_extensions(), &app_sync_list); 271 app_sync_bundle_.GetAppSyncDataListHelper( 272 registry->disabled_extensions(), &app_sync_list); 273 app_sync_bundle_.GetAppSyncDataListHelper( 274 registry->terminated_extensions(), &app_sync_list); 275 276 std::vector<extensions::AppSyncData> pending_apps = 277 app_sync_bundle_.GetPendingData(); 278 app_sync_list.insert(app_sync_list.begin(), 279 pending_apps.begin(), 280 pending_apps.end()); 281 282 return app_sync_list; 283} 284 285bool ExtensionSyncService::ProcessExtensionSyncData( 286 const extensions::ExtensionSyncData& extension_sync_data) { 287 if (!ProcessExtensionSyncDataHelper(extension_sync_data, 288 syncer::EXTENSIONS)) { 289 extension_sync_bundle_.AddPendingExtension(extension_sync_data.id(), 290 extension_sync_data); 291 extension_service_->CheckForUpdatesSoon(); 292 return false; 293 } 294 295 return true; 296} 297 298bool ExtensionSyncService::ProcessAppSyncData( 299 const extensions::AppSyncData& app_sync_data) { 300 const std::string& id = app_sync_data.id(); 301 302 if (app_sync_data.app_launch_ordinal().IsValid() && 303 app_sync_data.page_ordinal().IsValid()) { 304 extension_prefs_->app_sorting()->SetAppLaunchOrdinal( 305 id, 306 app_sync_data.app_launch_ordinal()); 307 extension_prefs_->app_sorting()->SetPageOrdinal( 308 id, 309 app_sync_data.page_ordinal()); 310 } 311 312 // The corresponding validation of this value during AppSyncData population 313 // is in AppSyncData::PopulateAppSpecifics. 314 if (app_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST && 315 app_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) { 316 extensions::SetLaunchType(extension_service_, id, 317 app_sync_data.launch_type()); 318 } 319 320 if (!ProcessExtensionSyncDataHelper(app_sync_data.extension_sync_data(), 321 syncer::APPS)) { 322 app_sync_bundle_.AddPendingApp(id, app_sync_data); 323 extension_service_->CheckForUpdatesSoon(); 324 return false; 325 } 326 327 return true; 328} 329 330void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) { 331 const extensions::Extension* ext = extension_service_->GetInstalledExtension( 332 extension_id); 333 334 if (ext) 335 SyncExtensionChangeIfNeeded(*ext); 336} 337 338void ExtensionSyncService::SetSyncStartFlare( 339 const syncer::SyncableService::StartSyncFlare& flare) { 340 flare_ = flare; 341} 342 343bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension, 344 syncer::ModelType type) const { 345 if (type == syncer::EXTENSIONS && 346 extensions::sync_helper::IsSyncableExtension(&extension)) { 347 return true; 348 } 349 350 if (type == syncer::APPS && 351 extensions::sync_helper::IsSyncableApp(&extension)) { 352 return true; 353 } 354 355 return false; 356} 357 358bool ExtensionSyncService::IsPendingEnable( 359 const std::string& extension_id) const { 360 return pending_app_enables_.Contains(extension_id) || 361 pending_extension_enables_.Contains(extension_id); 362} 363 364bool ExtensionSyncService::ProcessExtensionSyncDataHelper( 365 const extensions::ExtensionSyncData& extension_sync_data, 366 syncer::ModelType type) { 367 const std::string& id = extension_sync_data.id(); 368 const Extension* extension = extension_service_->GetInstalledExtension(id); 369 370 // TODO(bolms): we should really handle this better. The particularly bad 371 // case is where an app becomes an extension or vice versa, and we end up with 372 // a zombie extension that won't go away. 373 if (extension && !IsCorrectSyncType(*extension, type)) 374 return true; 375 376 // Handle uninstalls first. 377 if (extension_sync_data.uninstalled()) { 378 if (!extension_service_->UninstallExtensionHelper(extension_service_, id)) { 379 LOG(WARNING) << "Could not uninstall extension " << id 380 << " for sync"; 381 } 382 return true; 383 } 384 385 // Extension from sync was uninstalled by the user as external extensions. 386 // Honor user choice and skip installation/enabling. 387 if (extensions::ExtensionPrefs::Get(profile_) 388 ->IsExternalExtensionUninstalled(id)) { 389 LOG(WARNING) << "Extension with id " << id 390 << " from sync was uninstalled as external extension"; 391 return true; 392 } 393 394 // Set user settings. 395 // If the extension has been disabled from sync, it may not have 396 // been installed yet, so we don't know if the disable reason was a 397 // permissions increase. That will be updated once CheckPermissionsIncrease 398 // is called for it. 399 if (extension_sync_data.enabled()) 400 extension_service_->EnableExtension(id); 401 else if (!IsPendingEnable(id)) 402 extension_service_->DisableExtension( 403 id, Extension::DISABLE_UNKNOWN_FROM_SYNC); 404 405 // We need to cache some version information here because setting the 406 // incognito flag invalidates the |extension| pointer (it reloads the 407 // extension). 408 bool extension_installed = (extension != NULL); 409 int result = extension ? 410 extension->version()->CompareTo(extension_sync_data.version()) : 0; 411 extensions::util::SetIsIncognitoEnabled( 412 id, profile_, extension_sync_data.incognito_enabled()); 413 extension = NULL; // No longer safe to use. 414 415 if (extension_installed) { 416 // If the extension is already installed, check if it's outdated. 417 if (result < 0) { 418 // Extension is outdated. 419 return false; 420 } 421 } else { 422 // TODO(akalin): Replace silent update with a list of enabled 423 // permissions. 424 const bool kInstallSilently = true; 425 426 CHECK(type == syncer::EXTENSIONS || type == syncer::APPS); 427 extensions::PendingExtensionInfo::ShouldAllowInstallPredicate filter = 428 (type == syncer::APPS) ? extensions::sync_helper::IsSyncableApp : 429 extensions::sync_helper::IsSyncableExtension; 430 431 if (!extension_service_->pending_extension_manager()->AddFromSync( 432 id, 433 extension_sync_data.update_url(), 434 filter, 435 kInstallSilently)) { 436 LOG(WARNING) << "Could not add pending extension for " << id; 437 // This means that the extension is already pending installation, with a 438 // non-INTERNAL location. Add to pending_sync_data, even though it will 439 // never be removed (we'll never install a syncable version of the 440 // extension), so that GetAllSyncData() continues to send it. 441 } 442 // Track pending extensions so that we can return them in GetAllSyncData(). 443 return false; 444 } 445 446 return true; 447} 448 449void ExtensionSyncService::SyncExtensionChangeIfNeeded( 450 const Extension& extension) { 451 if (extensions::sync_helper::IsSyncableApp(&extension)) { 452 if (app_sync_bundle_.IsSyncing()) 453 app_sync_bundle_.SyncChangeIfNeeded(extension); 454 else if (extension_service_->is_ready() && !flare_.is_null()) 455 flare_.Run(syncer::APPS); 456 } else if (extensions::sync_helper::IsSyncableExtension(&extension)) { 457 if (extension_sync_bundle_.IsSyncing()) 458 extension_sync_bundle_.SyncChangeIfNeeded(extension); 459 else if (extension_service_->is_ready() && !flare_.is_null()) 460 flare_.Run(syncer::EXTENSIONS); 461 } 462} 463