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   const 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_, location_, GetManifestVersion());
    162     if (!result.is_available())
    163       warnings->push_back(InstallWarning(result.message(), *feature_name));
    164   }
    165 
    166   // Also generate warnings for keys that are not features.
    167   for (base::DictionaryValue::Iterator it(*value_); !it.IsAtEnd();
    168        it.Advance()) {
    169     if (!manifest_feature_provider->GetFeature(it.key())) {
    170       warnings->push_back(InstallWarning(
    171           ErrorUtils::FormatErrorMessage(
    172               manifest_errors::kUnrecognizedManifestKey, it.key()),
    173           it.key()));
    174     }
    175   }
    176   return true;
    177 }
    178 
    179 bool Manifest::HasKey(const std::string& key) const {
    180   return CanAccessKey(key) && value_->HasKey(key);
    181 }
    182 
    183 bool Manifest::HasPath(const std::string& path) const {
    184   base::Value* ignored = NULL;
    185   return CanAccessPath(path) && value_->Get(path, &ignored);
    186 }
    187 
    188 bool Manifest::Get(
    189     const std::string& path, const base::Value** out_value) const {
    190   return CanAccessPath(path) && value_->Get(path, out_value);
    191 }
    192 
    193 bool Manifest::GetBoolean(
    194     const std::string& path, bool* out_value) const {
    195   return CanAccessPath(path) && value_->GetBoolean(path, out_value);
    196 }
    197 
    198 bool Manifest::GetInteger(
    199     const std::string& path, int* out_value) const {
    200   return CanAccessPath(path) && value_->GetInteger(path, out_value);
    201 }
    202 
    203 bool Manifest::GetString(
    204     const std::string& path, std::string* out_value) const {
    205   return CanAccessPath(path) && value_->GetString(path, out_value);
    206 }
    207 
    208 bool Manifest::GetString(
    209     const std::string& path, base::string16* out_value) const {
    210   return CanAccessPath(path) && value_->GetString(path, out_value);
    211 }
    212 
    213 bool Manifest::GetDictionary(
    214     const std::string& path, const base::DictionaryValue** out_value) const {
    215   return CanAccessPath(path) && value_->GetDictionary(path, out_value);
    216 }
    217 
    218 bool Manifest::GetList(
    219     const std::string& path, const base::ListValue** out_value) const {
    220   return CanAccessPath(path) && value_->GetList(path, out_value);
    221 }
    222 
    223 Manifest* Manifest::DeepCopy() const {
    224   Manifest* manifest = new Manifest(
    225       location_, scoped_ptr<base::DictionaryValue>(value_->DeepCopy()));
    226   manifest->set_extension_id(extension_id_);
    227   return manifest;
    228 }
    229 
    230 bool Manifest::Equals(const Manifest* other) const {
    231   return other && value_->Equals(other->value());
    232 }
    233 
    234 int Manifest::GetManifestVersion() const {
    235   // Platform apps were launched after manifest version 2 was the preferred
    236   // version, so they default to that.
    237   int manifest_version = type_ == TYPE_PLATFORM_APP ? 2 : 1;
    238   value_->GetInteger(keys::kManifestVersion, &manifest_version);
    239   return manifest_version;
    240 }
    241 
    242 bool Manifest::CanAccessPath(const std::string& path) const {
    243   std::vector<std::string> components;
    244   base::SplitString(path, '.', &components);
    245   std::string key;
    246   for (size_t i = 0; i < components.size(); ++i) {
    247     key += components[i];
    248     if (!CanAccessKey(key))
    249       return false;
    250     key += '.';
    251   }
    252   return true;
    253 }
    254 
    255 bool Manifest::CanAccessKey(const std::string& key) const {
    256   Feature* feature = FeatureProvider::GetManifestFeatures()->GetFeature(key);
    257   if (!feature)
    258     return true;
    259 
    260   return feature->IsAvailableToManifest(
    261                       extension_id_, type_, location_, GetManifestVersion())
    262       .is_available();
    263 }
    264 
    265 }  // namespace extensions
    266