Home | History | Annotate | Download | only in common
      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/common/manifest.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/lazy_instance.h"
      9 #include "base/logging.h"
     10 #include "base/strings/string_split.h"
     11 #include "base/strings/stringprintf.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "extensions/common/error_utils.h"
     14 #include "extensions/common/features/feature.h"
     15 #include "extensions/common/features/feature_provider.h"
     16 #include "extensions/common/install_warning.h"
     17 #include "extensions/common/manifest_constants.h"
     18 
     19 namespace extensions {
     20 
     21 namespace keys = manifest_keys;
     22 
     23 namespace {
     24 
     25 // Rank extension locations in a way that allows
     26 // Manifest::GetHigherPriorityLocation() to compare locations.
     27 // An extension installed from two locations will have the location
     28 // with the higher rank, as returned by this function. The actual
     29 // integer values may change, and should never be persisted.
     30 int GetLocationRank(Manifest::Location location) {
     31   const int kInvalidRank = -1;
     32   int rank = kInvalidRank;  // Will CHECK that rank is not kInvalidRank.
     33 
     34   switch (location) {
     35     // Component extensions can not be overriden by any other type.
     36     case Manifest::COMPONENT:
     37       rank = 9;
     38       break;
     39 
     40     case Manifest::EXTERNAL_COMPONENT:
     41       rank = 8;
     42       break;
     43 
     44     // Policy controlled extensions may not be overridden by any type
     45     // that is not part of chrome.
     46     case Manifest::EXTERNAL_POLICY:
     47       rank = 7;
     48       break;
     49 
     50     case Manifest::EXTERNAL_POLICY_DOWNLOAD:
     51       rank = 6;
     52       break;
     53 
     54     // A developer-loaded extension should override any installed type
     55     // that a user can disable. Anything specified on the command-line should
     56     // override one loaded via the extensions UI.
     57     case Manifest::COMMAND_LINE:
     58       rank = 5;
     59       break;
     60 
     61     case Manifest::UNPACKED:
     62       rank = 4;
     63       break;
     64 
     65     // The relative priority of various external sources is not important,
     66     // but having some order ensures deterministic behavior.
     67     case Manifest::EXTERNAL_REGISTRY:
     68       rank = 3;
     69       break;
     70 
     71     case Manifest::EXTERNAL_PREF:
     72       rank = 2;
     73       break;
     74 
     75     case Manifest::EXTERNAL_PREF_DOWNLOAD:
     76       rank = 1;
     77       break;
     78 
     79     // User installed extensions are overridden by any external type.
     80     case Manifest::INTERNAL:
     81       rank = 0;
     82       break;
     83 
     84     default:
     85       NOTREACHED() << "Need to add new extension location " << location;
     86   }
     87 
     88   CHECK(rank != kInvalidRank);
     89   return rank;
     90 }
     91 
     92 }  // namespace
     93 
     94 // static
     95 Manifest::Location Manifest::GetHigherPriorityLocation(
     96     Location loc1, Location loc2) {
     97   if (loc1 == loc2)
     98     return loc1;
     99 
    100   int loc1_rank = GetLocationRank(loc1);
    101   int loc2_rank = GetLocationRank(loc2);
    102 
    103   // If two different locations have the same rank, then we can not
    104   // deterministicly choose a location.
    105   CHECK(loc1_rank != loc2_rank);
    106 
    107   // Highest rank has highest priority.
    108   return (loc1_rank > loc2_rank ? loc1 : loc2 );
    109 }
    110 
    111 Manifest::Manifest(Location location, scoped_ptr<base::DictionaryValue> value)
    112     : location_(location),
    113       value_(value.Pass()),
    114       type_(TYPE_UNKNOWN) {
    115   if (value_->HasKey(keys::kTheme)) {
    116     type_ = TYPE_THEME;
    117   } else if (value_->HasKey(keys::kExport)) {
    118     type_ = TYPE_SHARED_MODULE;
    119   } else if (value_->HasKey(keys::kApp)) {
    120     if (value_->Get(keys::kWebURLs, NULL) ||
    121         value_->Get(keys::kLaunchWebURL, NULL)) {
    122       type_ = TYPE_HOSTED_APP;
    123     } else if (value_->Get(keys::kPlatformAppBackground, NULL)) {
    124       type_ = TYPE_PLATFORM_APP;
    125     } else {
    126       type_ = TYPE_LEGACY_PACKAGED_APP;
    127     }
    128   } else {
    129     type_ = TYPE_EXTENSION;
    130   }
    131   CHECK_NE(type_, TYPE_UNKNOWN);
    132 }
    133 
    134 Manifest::~Manifest() {
    135 }
    136 
    137 bool Manifest::ValidateManifest(
    138     std::string* error,
    139     std::vector<InstallWarning>* warnings) const {
    140   *error = "";
    141 
    142   // Check every feature to see if its in the manifest. Note that this means
    143   // we will ignore keys that are not features; we do this for forward
    144   // compatibility.
    145   // TODO(aa): Consider having an error here in the case of strict error
    146   // checking to let developers know when they screw up.
    147 
    148   FeatureProvider* manifest_feature_provider =
    149       FeatureProvider::GetManifestFeatures();
    150   const std::vector<std::string>& feature_names =
    151       manifest_feature_provider->GetAllFeatureNames();
    152   for (std::vector<std::string>::const_iterator feature_name =
    153            feature_names.begin();
    154        feature_name != feature_names.end(); ++feature_name) {
    155     // Use Get instead of HasKey because the former uses path expansion.
    156     if (!value_->Get(*feature_name, NULL))
    157       continue;
    158 
    159     Feature* feature = manifest_feature_provider->GetFeature(*feature_name);
    160     Feature::Availability result = feature->IsAvailableToManifest(
    161         extension_id_, type_, Feature::ConvertLocation(location_),
    162         GetManifestVersion());
    163     if (!result.is_available())
    164       warnings->push_back(InstallWarning(result.message(), *feature_name));
    165   }
    166 
    167   // Also generate warnings for keys that are not features.
    168   for (base::DictionaryValue::Iterator it(*value_); !it.IsAtEnd();
    169        it.Advance()) {
    170     if (!manifest_feature_provider->GetFeature(it.key())) {
    171       warnings->push_back(InstallWarning(
    172           ErrorUtils::FormatErrorMessage(
    173               manifest_errors::kUnrecognizedManifestKey, it.key()),
    174           it.key()));
    175     }
    176   }
    177   return true;
    178 }
    179 
    180 bool Manifest::HasKey(const std::string& key) const {
    181   return CanAccessKey(key) && value_->HasKey(key);
    182 }
    183 
    184 bool Manifest::HasPath(const std::string& path) const {
    185   base::Value* ignored = NULL;
    186   return CanAccessPath(path) && value_->Get(path, &ignored);
    187 }
    188 
    189 bool Manifest::Get(
    190     const std::string& path, const base::Value** out_value) const {
    191   return CanAccessPath(path) && value_->Get(path, out_value);
    192 }
    193 
    194 bool Manifest::GetBoolean(
    195     const std::string& path, bool* out_value) const {
    196   return CanAccessPath(path) && value_->GetBoolean(path, out_value);
    197 }
    198 
    199 bool Manifest::GetInteger(
    200     const std::string& path, int* out_value) const {
    201   return CanAccessPath(path) && value_->GetInteger(path, out_value);
    202 }
    203 
    204 bool Manifest::GetString(
    205     const std::string& path, std::string* out_value) const {
    206   return CanAccessPath(path) && value_->GetString(path, out_value);
    207 }
    208 
    209 bool Manifest::GetString(
    210     const std::string& path, string16* out_value) const {
    211   return CanAccessPath(path) && value_->GetString(path, out_value);
    212 }
    213 
    214 bool Manifest::GetDictionary(
    215     const std::string& path, const base::DictionaryValue** out_value) const {
    216   return CanAccessPath(path) && value_->GetDictionary(path, out_value);
    217 }
    218 
    219 bool Manifest::GetList(
    220     const std::string& path, const base::ListValue** out_value) const {
    221   return CanAccessPath(path) && value_->GetList(path, out_value);
    222 }
    223 
    224 Manifest* Manifest::DeepCopy() const {
    225   Manifest* manifest = new Manifest(
    226       location_, scoped_ptr<base::DictionaryValue>(value_->DeepCopy()));
    227   manifest->set_extension_id(extension_id_);
    228   return manifest;
    229 }
    230 
    231 bool Manifest::Equals(const Manifest* other) const {
    232   return other && value_->Equals(other->value());
    233 }
    234 
    235 int Manifest::GetManifestVersion() const {
    236   // Platform apps were launched after manifest version 2 was the preferred
    237   // version, so they default to that.
    238   int manifest_version = type_ == TYPE_PLATFORM_APP ? 2 : 1;
    239   value_->GetInteger(keys::kManifestVersion, &manifest_version);
    240   return manifest_version;
    241 }
    242 
    243 bool Manifest::CanAccessPath(const std::string& path) const {
    244   std::vector<std::string> components;
    245   base::SplitString(path, '.', &components);
    246   std::string key;
    247   for (size_t i = 0; i < components.size(); ++i) {
    248     key += components[i];
    249     if (!CanAccessKey(key))
    250       return false;
    251     key += '.';
    252   }
    253   return true;
    254 }
    255 
    256 bool Manifest::CanAccessKey(const std::string& key) const {
    257   Feature* feature = FeatureProvider::GetManifestFeatures()->GetFeature(key);
    258   if (!feature)
    259     return true;
    260 
    261   return feature->IsAvailableToManifest(
    262       extension_id_, type_, Feature::ConvertLocation(location_),
    263       GetManifestVersion()).is_available();
    264 }
    265 
    266 }  // namespace extensions
    267