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