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