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