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