Home | History | Annotate | Download | only in extensions
      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