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