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