Home | History | Annotate | Download | only in extensions
      1 // Copyright 2014 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/pending_extension_manager.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 #include "base/version.h"
     11 #include "chrome/common/extensions/extension_constants.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "extensions/browser/extension_prefs.h"
     14 #include "extensions/browser/extension_registry.h"
     15 #include "extensions/common/extension.h"
     16 #include "url/gurl.h"
     17 
     18 using content::BrowserThread;
     19 
     20 namespace {
     21 
     22 // Install predicate used by AddFromExternalUpdateUrl().
     23 bool AlwaysInstall(const extensions::Extension* extension) {
     24   return true;
     25 }
     26 
     27 std::string GetVersionString(const Version& version) {
     28   return version.IsValid() ? version.GetString() : "invalid";
     29 }
     30 
     31 }  // namespace
     32 
     33 namespace extensions {
     34 
     35 PendingExtensionManager::PendingExtensionManager(
     36     content::BrowserContext* context)
     37     : context_(context) {}
     38 
     39 PendingExtensionManager::~PendingExtensionManager() {}
     40 
     41 const PendingExtensionInfo* PendingExtensionManager::GetById(
     42     const std::string& id) const {
     43   PendingExtensionList::const_iterator iter;
     44   for (iter = pending_extension_list_.begin();
     45        iter != pending_extension_list_.end();
     46        ++iter) {
     47     if (id == iter->id())
     48       return &(*iter);
     49   }
     50 
     51   return NULL;
     52 }
     53 
     54 bool PendingExtensionManager::Remove(const std::string& id) {
     55   PendingExtensionList::iterator iter;
     56   for (iter = pending_extension_list_.begin();
     57        iter != pending_extension_list_.end();
     58        ++iter) {
     59     if (id == iter->id()) {
     60       pending_extension_list_.erase(iter);
     61       return true;
     62     }
     63   }
     64 
     65   return false;
     66 }
     67 
     68 bool PendingExtensionManager::IsIdPending(const std::string& id) const {
     69   return GetById(id) != NULL;
     70 }
     71 
     72 bool PendingExtensionManager::HasPendingExtensions() const {
     73   return !pending_extension_list_.empty();
     74 }
     75 
     76 bool PendingExtensionManager::HasPendingExtensionFromSync() const {
     77   PendingExtensionList::const_iterator iter;
     78   for (iter = pending_extension_list_.begin();
     79        iter != pending_extension_list_.end();
     80        ++iter) {
     81     if (iter->is_from_sync())
     82       return true;
     83   }
     84 
     85   return false;
     86 }
     87 
     88 bool PendingExtensionManager::AddFromSync(
     89     const std::string& id,
     90     const GURL& update_url,
     91     PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
     92     bool install_silently,
     93     bool remote_install) {
     94   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     95 
     96   if (ExtensionRegistry::Get(context_)->GetExtensionById(
     97           id, ExtensionRegistry::EVERYTHING)) {
     98     LOG(ERROR) << "Trying to add pending extension " << id
     99                << " which already exists";
    100     return false;
    101   }
    102 
    103   // Make sure we don't ever try to install the CWS app, because even though
    104   // it is listed as a syncable app (because its values need to be synced) it
    105   // should already be installed on every instance.
    106   if (id == extension_misc::kWebStoreAppId) {
    107     NOTREACHED();
    108     return false;
    109   }
    110 
    111   static const bool kIsFromSync = true;
    112   static const Manifest::Location kSyncLocation = Manifest::INTERNAL;
    113   static const bool kMarkAcknowledged = false;
    114 
    115   return AddExtensionImpl(id,
    116                           std::string(),
    117                           update_url,
    118                           Version(),
    119                           should_allow_install,
    120                           kIsFromSync,
    121                           install_silently,
    122                           kSyncLocation,
    123                           Extension::NO_FLAGS,
    124                           kMarkAcknowledged,
    125                           remote_install);
    126 }
    127 
    128 bool PendingExtensionManager::AddFromExtensionImport(
    129     const std::string& id,
    130     const GURL& update_url,
    131     PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install) {
    132   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    133 
    134   if (ExtensionRegistry::Get(context_)->GetExtensionById(
    135           id, ExtensionRegistry::EVERYTHING)) {
    136     LOG(ERROR) << "Trying to add pending extension " << id
    137                << " which already exists";
    138     return false;
    139   }
    140 
    141   static const bool kIsFromSync = false;
    142   static const bool kInstallSilently = true;
    143   static const Manifest::Location kManifestLocation = Manifest::INTERNAL;
    144   static const bool kMarkAcknowledged = false;
    145   static const bool kRemoteInstall = false;
    146 
    147   return AddExtensionImpl(id,
    148                           std::string(),
    149                           update_url,
    150                           Version(),
    151                           should_allow_install,
    152                           kIsFromSync,
    153                           kInstallSilently,
    154                           kManifestLocation,
    155                           Extension::NO_FLAGS,
    156                           kMarkAcknowledged,
    157                           kRemoteInstall);
    158 }
    159 
    160 bool PendingExtensionManager::AddFromExternalUpdateUrl(
    161     const std::string& id,
    162     const std::string& install_parameter,
    163     const GURL& update_url,
    164     Manifest::Location location,
    165     int creation_flags,
    166     bool mark_acknowledged) {
    167   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    168 
    169   static const bool kIsFromSync = false;
    170   static const bool kInstallSilently = true;
    171   static const bool kRemoteInstall = false;
    172 
    173   const Extension* extension = ExtensionRegistry::Get(context_)
    174       ->GetExtensionById(id, ExtensionRegistry::EVERYTHING);
    175   if (extension && location == Manifest::GetHigherPriorityLocation(
    176                                    location, extension->location())) {
    177     // If the new location has higher priority than the location of an existing
    178     // extension, let the update process overwrite the existing extension.
    179   } else {
    180     if (ExtensionPrefs::Get(context_)->IsExternalExtensionUninstalled(id))
    181       return false;
    182 
    183     if (extension) {
    184       LOG(DFATAL) << "Trying to add extension " << id
    185                   << " by external update, but it is already installed.";
    186       return false;
    187     }
    188   }
    189 
    190   return AddExtensionImpl(id,
    191                           install_parameter,
    192                           update_url,
    193                           Version(),
    194                           &AlwaysInstall,
    195                           kIsFromSync,
    196                           kInstallSilently,
    197                           location,
    198                           creation_flags,
    199                           mark_acknowledged,
    200                           kRemoteInstall);
    201 }
    202 
    203 
    204 bool PendingExtensionManager::AddFromExternalFile(
    205     const std::string& id,
    206     Manifest::Location install_source,
    207     const Version& version,
    208     int creation_flags,
    209     bool mark_acknowledged) {
    210   // TODO(skerner): AddFromSync() checks to see if the extension is
    211   // installed, but this method assumes that the caller already
    212   // made sure it is not installed.  Make all AddFrom*() methods
    213   // consistent.
    214   const GURL& kUpdateUrl = GURL::EmptyGURL();
    215   static const bool kIsFromSync = false;
    216   static const bool kInstallSilently = true;
    217   static const bool kRemoteInstall = false;
    218 
    219   return AddExtensionImpl(id,
    220                           std::string(),
    221                           kUpdateUrl,
    222                           version,
    223                           &AlwaysInstall,
    224                           kIsFromSync,
    225                           kInstallSilently,
    226                           install_source,
    227                           creation_flags,
    228                           mark_acknowledged,
    229                           kRemoteInstall);
    230 }
    231 
    232 void PendingExtensionManager::GetPendingIdsForUpdateCheck(
    233     std::list<std::string>* out_ids_for_update_check) const {
    234   PendingExtensionList::const_iterator iter;
    235   for (iter = pending_extension_list_.begin();
    236        iter != pending_extension_list_.end();
    237        ++iter) {
    238     Manifest::Location install_source = iter->install_source();
    239 
    240     // Some install sources read a CRX from the filesystem.  They can
    241     // not be fetched from an update URL, so don't include them in the
    242     // set of ids.
    243     if (install_source == Manifest::EXTERNAL_PREF ||
    244         install_source == Manifest::EXTERNAL_REGISTRY)
    245       continue;
    246 
    247     out_ids_for_update_check->push_back(iter->id());
    248   }
    249 }
    250 
    251 bool PendingExtensionManager::AddExtensionImpl(
    252     const std::string& id,
    253     const std::string& install_parameter,
    254     const GURL& update_url,
    255     const Version& version,
    256     PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
    257     bool is_from_sync,
    258     bool install_silently,
    259     Manifest::Location install_source,
    260     int creation_flags,
    261     bool mark_acknowledged,
    262     bool remote_install) {
    263   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    264 
    265   PendingExtensionInfo info(id,
    266                             install_parameter,
    267                             update_url,
    268                             version,
    269                             should_allow_install,
    270                             is_from_sync,
    271                             install_silently,
    272                             install_source,
    273                             creation_flags,
    274                             mark_acknowledged,
    275                             remote_install);
    276 
    277   if (const PendingExtensionInfo* pending = GetById(id)) {
    278     // Bugs in this code will manifest as sporadic incorrect extension
    279     // locations in situations where multiple install sources run at the
    280     // same time. For example, on first login to a chrome os machine, an
    281     // extension may be requested by sync and the default extension set.
    282     // The following logging will help diagnose such issues.
    283     VLOG(1) << "Extension id " << id
    284             << " was entered for update more than once."
    285             << "  old location: " << pending->install_source()
    286             << "  new location: " << install_source
    287             << "  old version: " << GetVersionString(pending->version())
    288             << "  new version: " << GetVersionString(version);
    289 
    290     // Never override an existing extension with an older version. Only
    291     // extensions from local CRX files have a known version; extensions from an
    292     // update URL will get the latest version.
    293 
    294     // If |pending| has the same or higher precedence than |info| then don't
    295     // install |info| over |pending|.
    296     if (pending->CompareTo(info) >= 0)
    297       return false;
    298 
    299     VLOG(1) << "Overwrite existing record.";
    300 
    301     std::replace(pending_extension_list_.begin(),
    302                  pending_extension_list_.end(),
    303                  *pending,
    304                  info);
    305   } else {
    306     pending_extension_list_.push_back(info);
    307   }
    308 
    309   return true;
    310 }
    311 
    312 void PendingExtensionManager::AddForTesting(
    313     const PendingExtensionInfo& pending_extension_info) {
    314   pending_extension_list_.push_back(pending_extension_info);
    315 }
    316 
    317 }  // namespace extensions
    318