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_handlers/requirements_info.h"
6
7#include "base/lazy_instance.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/values.h"
11#include "extensions/common/error_utils.h"
12#include "extensions/common/manifest_constants.h"
13
14namespace extensions {
15
16namespace keys = manifest_keys;
17namespace errors = manifest_errors;
18
19RequirementsInfo::RequirementsInfo(const Manifest* manifest)
20    : webgl(false),
21      npapi(false),
22      window_shape(false) {
23  // Before parsing requirements from the manifest, automatically default the
24  // NPAPI plugin requirement based on whether it includes NPAPI plugins.
25  const base::ListValue* list_value = NULL;
26  npapi = manifest->GetList(keys::kPlugins, &list_value) &&
27          !list_value->empty();
28}
29
30RequirementsInfo::~RequirementsInfo() {
31}
32
33// static
34const RequirementsInfo& RequirementsInfo::GetRequirements(
35    const Extension* extension) {
36  RequirementsInfo* info = static_cast<RequirementsInfo*>(
37      extension->GetManifestData(keys::kRequirements));
38
39  // We should be guaranteed to have requirements, since they are parsed for all
40  // extension types.
41  CHECK(info);
42  return *info;
43}
44
45RequirementsHandler::RequirementsHandler() {
46}
47
48RequirementsHandler::~RequirementsHandler() {
49}
50
51const std::vector<std::string> RequirementsHandler::PrerequisiteKeys() const {
52  return SingleKey(keys::kPlugins);
53}
54
55const std::vector<std::string> RequirementsHandler::Keys() const {
56  return SingleKey(keys::kRequirements);
57}
58
59bool RequirementsHandler::AlwaysParseForType(Manifest::Type type) const {
60  return true;
61}
62
63bool RequirementsHandler::Parse(Extension* extension, base::string16* error) {
64  scoped_ptr<RequirementsInfo> requirements(
65      new RequirementsInfo(extension->manifest()));
66
67  if (!extension->manifest()->HasKey(keys::kRequirements)) {
68    extension->SetManifestData(keys::kRequirements, requirements.release());
69    return true;
70  }
71
72  const base::DictionaryValue* requirements_value = NULL;
73  if (!extension->manifest()->GetDictionary(keys::kRequirements,
74                                            &requirements_value)) {
75    *error = base::ASCIIToUTF16(errors::kInvalidRequirements);
76    return false;
77  }
78
79  for (base::DictionaryValue::Iterator iter(*requirements_value);
80       !iter.IsAtEnd();
81       iter.Advance()) {
82    const base::DictionaryValue* requirement_value;
83    if (!iter.value().GetAsDictionary(&requirement_value)) {
84      *error = ErrorUtils::FormatErrorMessageUTF16(
85          errors::kInvalidRequirement, iter.key());
86      return false;
87    }
88
89    if (iter.key() == "plugins") {
90      for (base::DictionaryValue::Iterator plugin_iter(*requirement_value);
91           !plugin_iter.IsAtEnd(); plugin_iter.Advance()) {
92        bool plugin_required = false;
93        if (!plugin_iter.value().GetAsBoolean(&plugin_required)) {
94          *error = ErrorUtils::FormatErrorMessageUTF16(
95              errors::kInvalidRequirement, iter.key());
96          return false;
97        }
98        if (plugin_iter.key() == "npapi") {
99          requirements->npapi = plugin_required;
100        } else {
101          *error = ErrorUtils::FormatErrorMessageUTF16(
102              errors::kInvalidRequirement, iter.key());
103          return false;
104        }
105      }
106    } else if (iter.key() == "3D") {
107      const base::ListValue* features = NULL;
108      if (!requirement_value->GetListWithoutPathExpansion("features",
109                                                          &features) ||
110          !features) {
111        *error = ErrorUtils::FormatErrorMessageUTF16(
112            errors::kInvalidRequirement, iter.key());
113        return false;
114      }
115
116      for (base::ListValue::const_iterator feature_iter = features->begin();
117           feature_iter != features->end(); ++feature_iter) {
118        std::string feature;
119        if ((*feature_iter)->GetAsString(&feature)) {
120          if (feature == "webgl") {
121            requirements->webgl = true;
122          } else if (feature == "css3d") {
123            // css3d is always available, so no check is needed, but no error is
124            // generated.
125          } else {
126            *error = ErrorUtils::FormatErrorMessageUTF16(
127                errors::kInvalidRequirement, iter.key());
128            return false;
129          }
130        }
131      }
132    } else if (iter.key() == "window") {
133      for (base::DictionaryValue::Iterator feature_iter(*requirement_value);
134           !feature_iter.IsAtEnd(); feature_iter.Advance()) {
135        bool feature_required = false;
136        if (!feature_iter.value().GetAsBoolean(&feature_required)) {
137          *error = ErrorUtils::FormatErrorMessageUTF16(
138              errors::kInvalidRequirement, iter.key());
139          return false;
140        }
141        if (feature_iter.key() == "shape") {
142          requirements->window_shape = feature_required;
143        } else {
144          *error = ErrorUtils::FormatErrorMessageUTF16(
145              errors::kInvalidRequirement, iter.key());
146          return false;
147        }
148      }
149    } else {
150      *error = base::ASCIIToUTF16(errors::kInvalidRequirements);
151      return false;
152    }
153  }
154
155  extension->SetManifestData(keys::kRequirements, requirements.release());
156  return true;
157}
158
159}  // namespace extensions
160