• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
GetLocationRank(Manifest::Location location)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
GetHigherPriorityLocation(Location loc1,Location loc2)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 
Manifest(Location location,scoped_ptr<base::DictionaryValue> value)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                value_->Get(keys::kPlatformAppServiceWorker, NULL)) {
125       type_ = TYPE_PLATFORM_APP;
126     } else {
127       type_ = TYPE_LEGACY_PACKAGED_APP;
128     }
129   } else {
130     type_ = TYPE_EXTENSION;
131   }
132   CHECK_NE(type_, TYPE_UNKNOWN);
133 }
134 
~Manifest()135 Manifest::~Manifest() {
136 }
137 
ValidateManifest(std::string * error,std::vector<InstallWarning> * warnings) const138 bool Manifest::ValidateManifest(
139     std::string* error,
140     std::vector<InstallWarning>* warnings) const {
141   *error = "";
142 
143   // Check every feature to see if its in the manifest. Note that this means
144   // we will ignore keys that are not features; we do this for forward
145   // compatibility.
146   // TODO(aa): Consider having an error here in the case of strict error
147   // checking to let developers know when they screw up.
148 
149   const FeatureProvider* manifest_feature_provider =
150       FeatureProvider::GetManifestFeatures();
151   const std::vector<std::string>& feature_names =
152       manifest_feature_provider->GetAllFeatureNames();
153   for (std::vector<std::string>::const_iterator feature_name =
154            feature_names.begin();
155        feature_name != feature_names.end(); ++feature_name) {
156     // Use Get instead of HasKey because the former uses path expansion.
157     if (!value_->Get(*feature_name, NULL))
158       continue;
159 
160     Feature* feature = manifest_feature_provider->GetFeature(*feature_name);
161     Feature::Availability result = feature->IsAvailableToManifest(
162         extension_id_, type_, location_, 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 
HasKey(const std::string & key) const180 bool Manifest::HasKey(const std::string& key) const {
181   return CanAccessKey(key) && value_->HasKey(key);
182 }
183 
HasPath(const std::string & path) const184 bool Manifest::HasPath(const std::string& path) const {
185   base::Value* ignored = NULL;
186   return CanAccessPath(path) && value_->Get(path, &ignored);
187 }
188 
Get(const std::string & path,const base::Value ** out_value) const189 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 
GetBoolean(const std::string & path,bool * out_value) const194 bool Manifest::GetBoolean(
195     const std::string& path, bool* out_value) const {
196   return CanAccessPath(path) && value_->GetBoolean(path, out_value);
197 }
198 
GetInteger(const std::string & path,int * out_value) const199 bool Manifest::GetInteger(
200     const std::string& path, int* out_value) const {
201   return CanAccessPath(path) && value_->GetInteger(path, out_value);
202 }
203 
GetString(const std::string & path,std::string * out_value) const204 bool Manifest::GetString(
205     const std::string& path, std::string* out_value) const {
206   return CanAccessPath(path) && value_->GetString(path, out_value);
207 }
208 
GetString(const std::string & path,base::string16 * out_value) const209 bool Manifest::GetString(
210     const std::string& path, base::string16* out_value) const {
211   return CanAccessPath(path) && value_->GetString(path, out_value);
212 }
213 
GetDictionary(const std::string & path,const base::DictionaryValue ** out_value) const214 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 
GetList(const std::string & path,const base::ListValue ** out_value) const219 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 
DeepCopy() const224 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 
Equals(const Manifest * other) const231 bool Manifest::Equals(const Manifest* other) const {
232   return other && value_->Equals(other->value());
233 }
234 
GetManifestVersion() const235 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 
CanAccessPath(const std::string & path) const243 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 
CanAccessKey(const std::string & key) const256 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_, location_, GetManifestVersion())
263       .is_available();
264 }
265 
266 }  // namespace extensions
267