Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(299)

Side by Side Diff: extensions/renderer/api_signature.cc

Issue 2947463002: [Extensions Bindings] Add a bindings/ subdirectory under renderer (Closed)
Patch Set: . Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « extensions/renderer/api_signature.h ('k') | extensions/renderer/api_signature_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 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/renderer/api_signature.h"
6
7 #include <algorithm>
8
9 #include "base/memory/ptr_util.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/values.h"
13 #include "content/public/child/v8_value_converter.h"
14 #include "extensions/renderer/api_invocation_errors.h"
15 #include "extensions/renderer/argument_spec.h"
16 #include "gin/arguments.h"
17
18 namespace extensions {
19
20 namespace {
21
22 bool HasCallback(const std::vector<std::unique_ptr<ArgumentSpec>>& signature) {
23 // TODO(devlin): This is how extension APIs have always determined if a
24 // function has a callback, but it seems a little silly. In the long run (once
25 // signatures are generated), it probably makes sense to indicate this
26 // differently.
27 return !signature.empty() &&
28 signature.back()->type() == ArgumentType::FUNCTION;
29 }
30
31 // A class to help with argument parsing. Note that this uses v8::Locals and
32 // const&s because it's an implementation detail of the APISignature; this
33 // should *only* be used directly on the stack!
34 class ArgumentParser {
35 public:
36 ArgumentParser(v8::Local<v8::Context> context,
37 const std::vector<std::unique_ptr<ArgumentSpec>>& signature,
38 const std::vector<v8::Local<v8::Value>>& arguments,
39 const APITypeReferenceMap& type_refs,
40 std::string* error)
41 : context_(context),
42 signature_(signature),
43 arguments_(arguments),
44 type_refs_(type_refs),
45 error_(error) {}
46
47 // Tries to parse the arguments against the expected signature.
48 bool ParseArguments();
49
50 protected:
51 v8::Isolate* GetIsolate() { return context_->GetIsolate(); }
52
53 private:
54 v8::Local<v8::Value> next_argument() {
55 return current_index_ < arguments_.size() ?
56 arguments_[current_index_] : v8::Local<v8::Value>();
57 }
58
59 void ConsumeArgument() {
60 current_index_ = std::min(arguments_.size(), current_index_ + 1);
61 }
62
63 // Attempts to match the next argument to the given |spec|.
64 // If the next argument does not match and |spec| is optional, uses a null
65 // value.
66 // Returns true on success.
67 bool ParseArgument(const ArgumentSpec& spec);
68
69 // Attempts to parse the callback from the given |spec|. Returns true on
70 // success.
71 bool ParseCallback(const ArgumentSpec& spec);
72
73 // Adds a null value to the parsed arguments.
74 virtual void AddNull() = 0;
75 virtual void AddNullCallback() = 0;
76 // Returns a base::Value to be populated during argument matching.
77 virtual std::unique_ptr<base::Value>* GetBuffer() = 0;
78 // Adds a new parsed argument.
79 virtual void AddParsedArgument(v8::Local<v8::Value> value) = 0;
80 // Adds the parsed callback.
81 virtual void SetCallback(v8::Local<v8::Function> callback) = 0;
82
83 v8::Local<v8::Context> context_;
84 const std::vector<std::unique_ptr<ArgumentSpec>>& signature_;
85 const std::vector<v8::Local<v8::Value>>& arguments_;
86 const APITypeReferenceMap& type_refs_;
87 std::string* error_;
88 size_t current_index_ = 0;
89
90 // An error to pass while parsing arguments to avoid having to allocate a new
91 // std::string on the stack multiple times.
92 std::string parse_error_;
93
94 DISALLOW_COPY_AND_ASSIGN(ArgumentParser);
95 };
96
97 class V8ArgumentParser : public ArgumentParser {
98 public:
99 V8ArgumentParser(v8::Local<v8::Context> context,
100 const std::vector<std::unique_ptr<ArgumentSpec>>& signature,
101 const std::vector<v8::Local<v8::Value>>& arguments,
102 const APITypeReferenceMap& type_refs,
103 std::string* error,
104 std::vector<v8::Local<v8::Value>>* values)
105 : ArgumentParser(context, signature, arguments, type_refs, error),
106 values_(values) {}
107
108 private:
109 void AddNull() override { values_->push_back(v8::Null(GetIsolate())); }
110 void AddNullCallback() override {
111 values_->push_back(v8::Null(GetIsolate()));
112 }
113 std::unique_ptr<base::Value>* GetBuffer() override { return nullptr; }
114 void AddParsedArgument(v8::Local<v8::Value> value) override {
115 values_->push_back(value);
116 }
117 void SetCallback(v8::Local<v8::Function> callback) override {
118 values_->push_back(callback);
119 }
120
121 std::vector<v8::Local<v8::Value>>* values_;
122
123 DISALLOW_COPY_AND_ASSIGN(V8ArgumentParser);
124 };
125
126 class BaseValueArgumentParser : public ArgumentParser {
127 public:
128 BaseValueArgumentParser(
129 v8::Local<v8::Context> context,
130 const std::vector<std::unique_ptr<ArgumentSpec>>& signature,
131 const std::vector<v8::Local<v8::Value>>& arguments,
132 const APITypeReferenceMap& type_refs,
133 std::string* error,
134 base::ListValue* list_value)
135 : ArgumentParser(context, signature, arguments, type_refs, error),
136 list_value_(list_value) {}
137
138 v8::Local<v8::Function> callback() { return callback_; }
139
140 private:
141 void AddNull() override {
142 list_value_->Append(base::MakeUnique<base::Value>());
143 }
144 void AddNullCallback() override {
145 // The base::Value conversion doesn't include the callback directly, so we
146 // don't add a null parameter here.
147 }
148 std::unique_ptr<base::Value>* GetBuffer() override { return &last_arg_; }
149 void AddParsedArgument(v8::Local<v8::Value> value) override {
150 // The corresponding base::Value is expected to have been stored in
151 // |last_arg_| already.
152 DCHECK(last_arg_);
153 list_value_->Append(std::move(last_arg_));
154 last_arg_.reset();
155 }
156 void SetCallback(v8::Local<v8::Function> callback) override {
157 callback_ = callback;
158 }
159
160 base::ListValue* list_value_;
161 std::unique_ptr<base::Value> last_arg_;
162 v8::Local<v8::Function> callback_;
163
164 DISALLOW_COPY_AND_ASSIGN(BaseValueArgumentParser);
165 };
166
167 bool ArgumentParser::ParseArguments() {
168 if (arguments_.size() > signature_.size()) {
169 *error_ = api_errors::TooManyArguments();
170 return false;
171 }
172
173 bool signature_has_callback = HasCallback(signature_);
174
175 size_t end_size =
176 signature_has_callback ? signature_.size() - 1 : signature_.size();
177 for (size_t i = 0; i < end_size; ++i) {
178 if (!ParseArgument(*signature_[i]))
179 return false;
180 }
181
182 if (signature_has_callback && !ParseCallback(*signature_.back()))
183 return false;
184
185 if (current_index_ != arguments_.size()) {
186 // This can potentially happen even if the check above for too many
187 // arguments succeeds when optional parameters are omitted. For instance,
188 // if the signature expects (optional int, function callback) and the caller
189 // provides (function callback, object random), the first size check and
190 // callback spec would succeed, but we wouldn't consume all the arguments.
191 *error_ = api_errors::TooManyArguments();
192 return false; // Extra arguments aren't allowed.
193 }
194
195 return true;
196 }
197
198 bool ArgumentParser::ParseArgument(const ArgumentSpec& spec) {
199 v8::Local<v8::Value> value = next_argument();
200 if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) {
201 if (!spec.optional()) {
202 *error_ = api_errors::MissingRequiredArgument(spec.name().c_str());
203 return false;
204 }
205 // This is safe to call even if |arguments| is at the end (which can happen
206 // if n optional arguments are omitted at the end of the signature).
207 ConsumeArgument();
208
209 AddNull();
210 return true;
211 }
212
213 if (!spec.ParseArgument(context_, value, type_refs_, GetBuffer(),
214 &parse_error_)) {
215 if (!spec.optional()) {
216 *error_ = api_errors::ArgumentError(spec.name(), parse_error_);
217 return false;
218 }
219
220 AddNull();
221 return true;
222 }
223
224 ConsumeArgument();
225 AddParsedArgument(value);
226 return true;
227 }
228
229 bool ArgumentParser::ParseCallback(const ArgumentSpec& spec) {
230 v8::Local<v8::Value> value = next_argument();
231 if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) {
232 if (!spec.optional()) {
233 *error_ = api_errors::MissingRequiredArgument(spec.name().c_str());
234 return false;
235 }
236 ConsumeArgument();
237 AddNullCallback();
238 return true;
239 }
240
241 if (!spec.ParseArgument(context_, value, type_refs_, nullptr,
242 &parse_error_)) {
243 *error_ = api_errors::ArgumentError(spec.name(), parse_error_);
244 return false;
245 }
246
247 ConsumeArgument();
248 SetCallback(value.As<v8::Function>());
249 return true;
250 }
251
252 } // namespace
253
254 APISignature::APISignature(const base::ListValue& specification) {
255 signature_.reserve(specification.GetSize());
256 for (const auto& value : specification) {
257 const base::DictionaryValue* param = nullptr;
258 CHECK(value.GetAsDictionary(&param));
259 signature_.push_back(base::MakeUnique<ArgumentSpec>(*param));
260 }
261 }
262
263 APISignature::APISignature(std::vector<std::unique_ptr<ArgumentSpec>> signature)
264 : signature_(std::move(signature)) {}
265
266 APISignature::~APISignature() {}
267
268 bool APISignature::ParseArgumentsToV8(
269 v8::Local<v8::Context> context,
270 const std::vector<v8::Local<v8::Value>>& arguments,
271 const APITypeReferenceMap& type_refs,
272 std::vector<v8::Local<v8::Value>>* v8_out,
273 std::string* error) const {
274 DCHECK(v8_out);
275 std::vector<v8::Local<v8::Value>> v8_values;
276 V8ArgumentParser parser(
277 context, signature_, arguments, type_refs, error, &v8_values);
278 if (!parser.ParseArguments())
279 return false;
280 *v8_out = std::move(v8_values);
281 return true;
282 }
283
284 bool APISignature::ParseArgumentsToJSON(
285 v8::Local<v8::Context> context,
286 const std::vector<v8::Local<v8::Value>>& arguments,
287 const APITypeReferenceMap& type_refs,
288 std::unique_ptr<base::ListValue>* json_out,
289 v8::Local<v8::Function>* callback_out,
290 std::string* error) const {
291 DCHECK(json_out);
292 DCHECK(callback_out);
293 std::unique_ptr<base::ListValue> json = base::MakeUnique<base::ListValue>();
294 BaseValueArgumentParser parser(
295 context, signature_, arguments, type_refs, error, json.get());
296 if (!parser.ParseArguments())
297 return false;
298 *json_out = std::move(json);
299 *callback_out = parser.callback();
300 return true;
301 }
302
303 bool APISignature::ConvertArgumentsIgnoringSchema(
304 v8::Local<v8::Context> context,
305 const std::vector<v8::Local<v8::Value>>& arguments,
306 std::unique_ptr<base::ListValue>* json_out,
307 v8::Local<v8::Function>* callback_out) const {
308 size_t size = arguments.size();
309 v8::Local<v8::Function> callback;
310 // TODO(devlin): This is what the current bindings do, but it's quite terribly
311 // incorrect. We only hit this flow when an API method has a hook to update
312 // the arguments post-validation, and in some cases, the arguments returned by
313 // that hook do *not* match the signature of the API method (e.g.
314 // fileSystem.getDisplayPath); see also note in api_bindings.cc for why this
315 // is bad. But then here, we *rely* on the signature to determine whether or
316 // not the last parameter is a callback, even though the hooks may not return
317 // the arguments in the signature. This is very broken.
318 if (HasCallback(signature_)) {
319 CHECK(!arguments.empty());
320 v8::Local<v8::Value> value = arguments.back();
321 --size;
322 // Bindings should ensure that the value here is appropriate, but see the
323 // comment above for limitations.
324 DCHECK(value->IsFunction() || value->IsUndefined() || value->IsNull());
325 if (value->IsFunction())
326 callback = value.As<v8::Function>();
327 }
328
329 auto json = base::MakeUnique<base::ListValue>();
330 std::unique_ptr<content::V8ValueConverter> converter =
331 content::V8ValueConverter::Create();
332 converter->SetFunctionAllowed(true);
333 for (size_t i = 0; i < size; ++i) {
334 std::unique_ptr<base::Value> converted =
335 converter->FromV8Value(arguments[i], context);
336 if (!converted)
337 return false;
338 json->Append(std::move(converted));
339 }
340
341 *json_out = std::move(json);
342 *callback_out = callback;
343 return true;
344 }
345
346 std::string APISignature::GetExpectedSignature() const {
347 if (!expected_signature_.empty() || signature_.empty())
348 return expected_signature_;
349
350 std::vector<std::string> pieces;
351 pieces.reserve(signature_.size());
352 const char* kOptionalPrefix = "optional ";
353 for (const auto& spec : signature_) {
354 pieces.push_back(
355 base::StringPrintf("%s%s %s", spec->optional() ? kOptionalPrefix : "",
356 spec->GetTypeName().c_str(), spec->name().c_str()));
357 }
358 expected_signature_ = base::JoinString(pieces, ", ");
359
360 return expected_signature_;
361 }
362
363 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/api_signature.h ('k') | extensions/renderer/api_signature_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698