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