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