extension_sync_service.cc revision f2477e01787aa58f445919b809d89e252beef54f
190c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein// Copyright 2013 The Chromium Authors. All rights reserved. 290c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein// Use of this source code is governed by a BSD-style license that can be 390c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein// found in the LICENSE file. 45d9d10e8217d2138b5514a4d4216f95373240942mtklein 540b32be3718f0f2e01c4a21bb0004b7f93670c42mtklein#include "chrome/browser/extensions/extension_sync_service.h" 640b32be3718f0f2e01c4a21bb0004b7f93670c42mtklein 76238688af0d758660d344ec047243d4efefd6f4dmtklein#include <iterator> 86238688af0d758660d344ec047243d4efefd6f4dmtklein 96238688af0d758660d344ec047243d4efefd6f4dmtklein#include "base/basictypes.h" 106238688af0d758660d344ec047243d4efefd6f4dmtklein#include "base/threading/sequenced_worker_pool.h" 116238688af0d758660d344ec047243d4efefd6f4dmtklein#include "base/threading/thread_restrictions.h" 125d9d10e8217d2138b5514a4d4216f95373240942mtklein#include "chrome/browser/extensions/app_sync_data.h" 1390c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/browser/extensions/extension_error_ui.h" 1490c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/browser/extensions/extension_prefs.h" 1590c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/browser/extensions/extension_service.h" 1690c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/browser/extensions/extension_sync_data.h" 1790c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/browser/extensions/extension_sync_service_factory.h" 1890c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/browser/extensions/extension_util.h" 1990c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/browser/profiles/profile.h" 2090c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/browser/sync/glue/sync_start_util.h" 2190c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/browser/sync/sync_prefs.h" 2290c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "chrome/common/extensions/sync_helper.h" 2390c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "content/public/browser/browser_thread.h" 2490c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "extensions/browser/app_sorting.h" 2590c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "extensions/common/extension.h" 2690c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "extensions/common/feature_switch.h" 2790c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "extensions/common/manifest_constants.h" 2890c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "sync/api/sync_change.h" 2990c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein#include "sync/api/sync_error_factory.h" 3090c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein 3190c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtkleinusing extensions::Extension; 3290c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtkleinusing extensions::ExtensionPrefs; 3340b32be3718f0f2e01c4a21bb0004b7f93670c42mtkleinusing extensions::FeatureSwitch; 3440b32be3718f0f2e01c4a21bb0004b7f93670c42mtklein 3540b32be3718f0f2e01c4a21bb0004b7f93670c42mtkleinExtensionSyncService::ExtensionSyncService(Profile* profile, 3640b32be3718f0f2e01c4a21bb0004b7f93670c42mtklein ExtensionPrefs* extension_prefs, 3740b32be3718f0f2e01c4a21bb0004b7f93670c42mtklein ExtensionService* extension_service) 385d9d10e8217d2138b5514a4d4216f95373240942mtklein : profile_(profile), 39a189ccdb4d5efdbbfa34a26c08656634b615f930mtklein extension_prefs_(extension_prefs), 405d9d10e8217d2138b5514a4d4216f95373240942mtklein extension_service_(extension_service), 41a189ccdb4d5efdbbfa34a26c08656634b615f930mtklein app_sync_bundle_(this), 42a189ccdb4d5efdbbfa34a26c08656634b615f930mtklein extension_sync_bundle_(this), 43a189ccdb4d5efdbbfa34a26c08656634b615f930mtklein pending_app_enables_( 44a189ccdb4d5efdbbfa34a26c08656634b615f930mtklein make_scoped_ptr(new browser_sync::SyncPrefs( 45a189ccdb4d5efdbbfa34a26c08656634b615f930mtklein extension_prefs_->pref_service())), 46a189ccdb4d5efdbbfa34a26c08656634b615f930mtklein &app_sync_bundle_, 475d9d10e8217d2138b5514a4d4216f95373240942mtklein syncer::APPS), 485d9d10e8217d2138b5514a4d4216f95373240942mtklein pending_extension_enables_( 495d9d10e8217d2138b5514a4d4216f95373240942mtklein make_scoped_ptr(new browser_sync::SyncPrefs( 505d9d10e8217d2138b5514a4d4216f95373240942mtklein extension_prefs_->pref_service())), 51912947737a973421f4c58682b6171cb5ee00ad3aMike Klein &extension_sync_bundle_, 525d9d10e8217d2138b5514a4d4216f95373240942mtklein syncer::EXTENSIONS) { 535d9d10e8217d2138b5514a4d4216f95373240942mtklein SetSyncStartFlare(sync_start_util::GetFlareForSyncableService( 545d9d10e8217d2138b5514a4d4216f95373240942mtklein profile_->GetPath())); 5590c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein 5690c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein extension_service_->set_extension_sync_service(this); 5790c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein extension_prefs_->app_sorting()->SetExtensionSyncService(this); 5890c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein} 595d9d10e8217d2138b5514a4d4216f95373240942mtklein 605d9d10e8217d2138b5514a4d4216f95373240942mtkleinExtensionSyncService::~ExtensionSyncService() {} 6140b32be3718f0f2e01c4a21bb0004b7f93670c42mtklein 625d9d10e8217d2138b5514a4d4216f95373240942mtklein// static 6390c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtkleinExtensionSyncService* ExtensionSyncService::Get(Profile* profile) { 6490c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein return ExtensionSyncServiceFactory::GetForProfile(profile); 6590c471e73fb0bd09aef8ad1e3f0842e5d46fb342mtklein} 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 extension_util::IsIncognitoEnabled(extension.id(), extension_service_)); 233} 234 235extensions::AppSyncData ExtensionSyncService::GetAppSyncData( 236 const Extension& extension) const { 237 return extensions::AppSyncData( 238 extension, 239 extension_service_->IsExtensionEnabled(extension.id()), 240 extension_util::IsIncognitoEnabled(extension.id(), extension_service_), 241 extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()), 242 extension_prefs_->app_sorting()->GetPageOrdinal(extension.id())); 243} 244 245std::vector<extensions::ExtensionSyncData> 246 ExtensionSyncService::GetExtensionSyncDataList() const { 247 std::vector<extensions::ExtensionSyncData> extension_sync_list; 248 extension_sync_bundle_.GetExtensionSyncDataListHelper( 249 extension_service_->extensions(), &extension_sync_list); 250 extension_sync_bundle_.GetExtensionSyncDataListHelper( 251 extension_service_->disabled_extensions(), &extension_sync_list); 252 extension_sync_bundle_.GetExtensionSyncDataListHelper( 253 extension_service_->terminated_extensions(), &extension_sync_list); 254 255 std::vector<extensions::ExtensionSyncData> pending_extensions = 256 extension_sync_bundle_.GetPendingData(); 257 extension_sync_list.insert(extension_sync_list.begin(), 258 pending_extensions.begin(), 259 pending_extensions.end()); 260 261 return extension_sync_list; 262} 263 264std::vector<extensions::AppSyncData> ExtensionSyncService::GetAppSyncDataList() 265 const { 266 std::vector<extensions::AppSyncData> app_sync_list; 267 app_sync_bundle_.GetAppSyncDataListHelper( 268 extension_service_->extensions(), &app_sync_list); 269 app_sync_bundle_.GetAppSyncDataListHelper( 270 extension_service_->disabled_extensions(), &app_sync_list); 271 app_sync_bundle_.GetAppSyncDataListHelper( 272 extension_service_->terminated_extensions(), &app_sync_list); 273 274 std::vector<extensions::AppSyncData> pending_apps = 275 app_sync_bundle_.GetPendingData(); 276 app_sync_list.insert(app_sync_list.begin(), 277 pending_apps.begin(), 278 pending_apps.end()); 279 280 return app_sync_list; 281} 282 283bool ExtensionSyncService::ProcessExtensionSyncData( 284 const extensions::ExtensionSyncData& extension_sync_data) { 285 if (!ProcessExtensionSyncDataHelper(extension_sync_data, 286 syncer::EXTENSIONS)) { 287 extension_sync_bundle_.AddPendingExtension(extension_sync_data.id(), 288 extension_sync_data); 289 extension_service_->CheckForUpdatesSoon(); 290 return false; 291 } 292 293 return true; 294} 295 296bool ExtensionSyncService::ProcessAppSyncData( 297 const extensions::AppSyncData& app_sync_data) { 298 const std::string& id = app_sync_data.id(); 299 300 if (app_sync_data.app_launch_ordinal().IsValid() && 301 app_sync_data.page_ordinal().IsValid()) { 302 extension_prefs_->app_sorting()->SetAppLaunchOrdinal( 303 id, 304 app_sync_data.app_launch_ordinal()); 305 extension_prefs_->app_sorting()->SetPageOrdinal( 306 id, 307 app_sync_data.page_ordinal()); 308 } 309 310 if (!ProcessExtensionSyncDataHelper(app_sync_data.extension_sync_data(), 311 syncer::APPS)) { 312 app_sync_bundle_.AddPendingApp(id, app_sync_data); 313 extension_service_->CheckForUpdatesSoon(); 314 return false; 315 } 316 317 return true; 318} 319 320void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) { 321 const extensions::Extension* ext = extension_service_->GetInstalledExtension( 322 extension_id); 323 324 if (ext) 325 SyncExtensionChangeIfNeeded(*ext); 326} 327 328void ExtensionSyncService::SetSyncStartFlare( 329 const syncer::SyncableService::StartSyncFlare& flare) { 330 flare_ = flare; 331} 332 333bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension, 334 syncer::ModelType type) const { 335 if (type == syncer::EXTENSIONS && 336 extensions::sync_helper::IsSyncableExtension(&extension)) { 337 return true; 338 } 339 340 if (type == syncer::APPS && 341 extensions::sync_helper::IsSyncableApp(&extension)) { 342 return true; 343 } 344 345 return false; 346} 347 348bool ExtensionSyncService::IsPendingEnable( 349 const std::string& extension_id) const { 350 return pending_app_enables_.Contains(extension_id) || 351 pending_extension_enables_.Contains(extension_id); 352} 353 354bool ExtensionSyncService::ProcessExtensionSyncDataHelper( 355 const extensions::ExtensionSyncData& extension_sync_data, 356 syncer::ModelType type) { 357 const std::string& id = extension_sync_data.id(); 358 const Extension* extension = extension_service_->GetInstalledExtension(id); 359 360 // TODO(bolms): we should really handle this better. The particularly bad 361 // case is where an app becomes an extension or vice versa, and we end up with 362 // a zombie extension that won't go away. 363 if (extension && !IsCorrectSyncType(*extension, type)) 364 return true; 365 366 // Handle uninstalls first. 367 if (extension_sync_data.uninstalled()) { 368 if (!extension_service_->UninstallExtensionHelper(extension_service_, id)) { 369 LOG(WARNING) << "Could not uninstall extension " << id 370 << " for sync"; 371 } 372 return true; 373 } 374 375 // Extension from sync was uninstalled by the user as external extensions. 376 // Honor user choice and skip installation/enabling. 377 if (extension_service_->IsExternalExtensionUninstalled(id)) { 378 LOG(WARNING) << "Extension with id " << id 379 << " from sync was uninstalled as external extension"; 380 return true; 381 } 382 383 // Set user settings. 384 // If the extension has been disabled from sync, it may not have 385 // been installed yet, so we don't know if the disable reason was a 386 // permissions increase. That will be updated once CheckPermissionsIncrease 387 // is called for it. 388 if (extension_sync_data.enabled()) 389 extension_service_->EnableExtension(id); 390 else if (!IsPendingEnable(id)) 391 extension_service_->DisableExtension( 392 id, Extension::DISABLE_UNKNOWN_FROM_SYNC); 393 394 // We need to cache some version information here because setting the 395 // incognito flag invalidates the |extension| pointer (it reloads the 396 // extension). 397 bool extension_installed = (extension != NULL); 398 int result = extension ? 399 extension->version()->CompareTo(extension_sync_data.version()) : 0; 400 extension_util::SetIsIncognitoEnabled( 401 id, extension_service_, extension_sync_data.incognito_enabled()); 402 extension = NULL; // No longer safe to use. 403 404 if (extension_installed) { 405 // If the extension is already installed, check if it's outdated. 406 if (result < 0) { 407 // Extension is outdated. 408 return false; 409 } 410 } else { 411 // TODO(akalin): Replace silent update with a list of enabled 412 // permissions. 413 const bool kInstallSilently = true; 414 415 CHECK(type == syncer::EXTENSIONS || type == syncer::APPS); 416 extensions::PendingExtensionInfo::ShouldAllowInstallPredicate filter = 417 (type == syncer::APPS) ? extensions::sync_helper::IsSyncableApp : 418 extensions::sync_helper::IsSyncableExtension; 419 420 if (!extension_service_->pending_extension_manager()->AddFromSync( 421 id, 422 extension_sync_data.update_url(), 423 filter, 424 kInstallSilently)) { 425 LOG(WARNING) << "Could not add pending extension for " << id; 426 // This means that the extension is already pending installation, with a 427 // non-INTERNAL location. Add to pending_sync_data, even though it will 428 // never be removed (we'll never install a syncable version of the 429 // extension), so that GetAllSyncData() continues to send it. 430 } 431 // Track pending extensions so that we can return them in GetAllSyncData(). 432 return false; 433 } 434 435 return true; 436} 437 438void ExtensionSyncService::SyncExtensionChangeIfNeeded( 439 const Extension& extension) { 440 if (extensions::sync_helper::IsSyncableApp(&extension)) { 441 if (app_sync_bundle_.IsSyncing()) 442 app_sync_bundle_.SyncChangeIfNeeded(extension); 443 else if (extension_service_->is_ready() && !flare_.is_null()) 444 flare_.Run(syncer::APPS); 445 } else if (extensions::sync_helper::IsSyncableExtension(&extension)) { 446 if (extension_sync_bundle_.IsSyncing()) 447 extension_sync_bundle_.SyncChangeIfNeeded(extension); 448 else if (extension_service_->is_ready() && !flare_.is_null()) 449 flare_.Run(syncer::EXTENSIONS); 450 } 451} 452