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