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