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
19namespace extensions {
20
21namespace keys = manifest_keys;
22
23namespace {
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.
30int 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
95Manifest::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
111Manifest::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
135Manifest::~Manifest() {
136}
137
138bool 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
180bool Manifest::HasKey(const std::string& key) const {
181  return CanAccessKey(key) && value_->HasKey(key);
182}
183
184bool Manifest::HasPath(const std::string& path) const {
185  base::Value* ignored = NULL;
186  return CanAccessPath(path) && value_->Get(path, &ignored);
187}
188
189bool Manifest::Get(
190    const std::string& path, const base::Value** out_value) const {
191  return CanAccessPath(path) && value_->Get(path, out_value);
192}
193
194bool Manifest::GetBoolean(
195    const std::string& path, bool* out_value) const {
196  return CanAccessPath(path) && value_->GetBoolean(path, out_value);
197}
198
199bool Manifest::GetInteger(
200    const std::string& path, int* out_value) const {
201  return CanAccessPath(path) && value_->GetInteger(path, out_value);
202}
203
204bool Manifest::GetString(
205    const std::string& path, std::string* out_value) const {
206  return CanAccessPath(path) && value_->GetString(path, out_value);
207}
208
209bool Manifest::GetString(
210    const std::string& path, base::string16* out_value) const {
211  return CanAccessPath(path) && value_->GetString(path, out_value);
212}
213
214bool Manifest::GetDictionary(
215    const std::string& path, const base::DictionaryValue** out_value) const {
216  return CanAccessPath(path) && value_->GetDictionary(path, out_value);
217}
218
219bool Manifest::GetList(
220    const std::string& path, const base::ListValue** out_value) const {
221  return CanAccessPath(path) && value_->GetList(path, out_value);
222}
223
224Manifest* 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
231bool Manifest::Equals(const Manifest* other) const {
232  return other && value_->Equals(other->value());
233}
234
235int 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
243bool 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
256bool 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