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_prefs.h" 15 #include "chrome/browser/extensions/extension_service.h" 16 #include "chrome/browser/extensions/extension_sync_data.h" 17 #include "chrome/browser/extensions/extension_sync_service_factory.h" 18 #include "chrome/browser/extensions/extension_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 "content/public/browser/browser_thread.h" 24 #include "extensions/browser/app_sorting.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 31 using extensions::Extension; 32 using extensions::ExtensionPrefs; 33 using extensions::FeatureSwitch; 34 35 ExtensionSyncService::ExtensionSyncService(Profile* profile, 36 ExtensionPrefs* extension_prefs, 37 ExtensionService* extension_service) 38 : profile_(profile), 39 extension_prefs_(extension_prefs), 40 extension_service_(extension_service), 41 app_sync_bundle_(this), 42 extension_sync_bundle_(this), 43 pending_app_enables_( 44 make_scoped_ptr(new browser_sync::SyncPrefs( 45 extension_prefs_->pref_service())), 46 &app_sync_bundle_, 47 syncer::APPS), 48 pending_extension_enables_( 49 make_scoped_ptr(new browser_sync::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 60 ExtensionSyncService::~ExtensionSyncService() {} 61 62 // static 63 ExtensionSyncService* ExtensionSyncService::Get(Profile* profile) { 64 return ExtensionSyncServiceFactory::GetForProfile(profile); 65 } 66 67 syncer::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 90 void 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 102 void 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 115 void 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 128 syncer::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 186 void 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 194 syncer::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 207 syncer::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 227 extensions::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 235 extensions::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 245 std::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 264 std::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 283 bool 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 296 bool 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 320 void 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 328 void ExtensionSyncService::SetSyncStartFlare( 329 const syncer::SyncableService::StartSyncFlare& flare) { 330 flare_ = flare; 331 } 332 333 bool 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 348 bool 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 354 bool 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 438 void 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