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

Side by Side Diff: extensions/renderer/bindings/argument_spec.cc

Issue 2947223003: [Extensions Bindings] Consider argument type more in signature parsing (Closed)
Patch Set: rebase 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/renderer/bindings/argument_spec.h" 5 #include "extensions/renderer/bindings/argument_spec.h"
6 6
7 #include "base/memory/ptr_util.h" 7 #include "base/memory/ptr_util.h"
8 #include "base/strings/string_piece.h" 8 #include "base/strings/string_piece.h"
9 #include "base/strings/string_util.h" 9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h" 10 #include "base/strings/stringprintf.h"
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 198
199 // Check if we should preserve null in objects. Right now, this is only used 199 // Check if we should preserve null in objects. Right now, this is only used
200 // on arguments of type object and any (in fact, it's only used in the storage 200 // on arguments of type object and any (in fact, it's only used in the storage
201 // API), but it could potentially make sense for lists or functions as well. 201 // API), but it could potentially make sense for lists or functions as well.
202 if (type_ == ArgumentType::OBJECT || type_ == ArgumentType::ANY) 202 if (type_ == ArgumentType::OBJECT || type_ == ArgumentType::ANY)
203 dict->GetBoolean("preserveNull", &preserve_null_); 203 dict->GetBoolean("preserveNull", &preserve_null_);
204 } 204 }
205 205
206 ArgumentSpec::~ArgumentSpec() {} 206 ArgumentSpec::~ArgumentSpec() {}
207 207
208 bool ArgumentSpec::IsCorrectType(v8::Local<v8::Value> value,
209 const APITypeReferenceMap& refs,
210 std::string* error) const {
211 bool is_valid_type = false;
212
213 switch (type_) {
214 case ArgumentType::INTEGER:
215 is_valid_type = value->IsInt32();
216 break;
217 case ArgumentType::DOUBLE:
218 is_valid_type = value->IsNumber();
219 break;
220 case ArgumentType::BOOLEAN:
221 is_valid_type = value->IsBoolean();
222 break;
223 case ArgumentType::STRING:
224 is_valid_type = value->IsString();
225 break;
226 case ArgumentType::OBJECT:
227 // Don't allow functions or arrays (even though they are technically
228 // objects). This is to make it easier to match otherwise-ambiguous
229 // signatures. For instance, if an API method has an optional object
230 // parameter and then an optional callback, we wouldn't necessarily be
231 // able to match the arguments if we allowed functions as objects.
232 // TODO(devlin): What about other subclasses of Object, like Map and Set?
233 is_valid_type =
234 value->IsObject() && !value->IsFunction() && !value->IsArray();
235 break;
236 case ArgumentType::LIST:
237 is_valid_type = value->IsArray();
238 break;
239 case ArgumentType::BINARY:
240 is_valid_type = value->IsArrayBuffer() || value->IsArrayBufferView();
241 break;
242 case ArgumentType::FUNCTION:
243 is_valid_type = value->IsFunction();
244 break;
245 case ArgumentType::ANY:
246 is_valid_type = true;
247 break;
248 case ArgumentType::REF: {
249 DCHECK(ref_);
250 const ArgumentSpec* reference = refs.GetSpec(ref_.value());
251 DCHECK(reference) << ref_.value();
252 is_valid_type = reference->IsCorrectType(value, refs, error);
253 break;
254 }
255 case ArgumentType::CHOICES:
256 for (const auto& choice : choices_) {
257 if (choice->IsCorrectType(value, refs, error)) {
258 is_valid_type = true;
259 break;
260 }
261 }
262 break;
263 }
264
265 if (!is_valid_type)
266 *error = GetInvalidTypeError(value);
267 return is_valid_type;
268 }
269
208 bool ArgumentSpec::ParseArgument(v8::Local<v8::Context> context, 270 bool ArgumentSpec::ParseArgument(v8::Local<v8::Context> context,
209 v8::Local<v8::Value> value, 271 v8::Local<v8::Value> value,
210 const APITypeReferenceMap& refs, 272 const APITypeReferenceMap& refs,
211 std::unique_ptr<base::Value>* out_value, 273 std::unique_ptr<base::Value>* out_value,
212 std::string* error) const { 274 std::string* error) const {
213 if (type_ == ArgumentType::FUNCTION) { 275 // Note: for top-level arguments (i.e., those passed directly to the function,
214 if (!value->IsFunction()) { 276 // as opposed to a property on an object, or the item of an array), we will
215 *error = GetInvalidTypeError(value); 277 // have already checked the type. Doing so again should be nearly free, but
278 // if we do find this to be an issue, we could avoid the second call.
279 if (!IsCorrectType(value, refs, error))
280 return false;
281
282 switch (type_) {
283 case ArgumentType::INTEGER:
284 case ArgumentType::DOUBLE:
285 case ArgumentType::BOOLEAN:
286 case ArgumentType::STRING:
287 return ParseArgumentToFundamental(context, value, out_value, error);
288 case ArgumentType::OBJECT:
289 return ParseArgumentToObject(context, value.As<v8::Object>(), refs,
290 out_value, error);
291 case ArgumentType::LIST:
292 return ParseArgumentToArray(context, value.As<v8::Array>(), refs,
293 out_value, error);
294 case ArgumentType::BINARY:
295 return ParseArgumentToAny(context, value, out_value, error);
296 case ArgumentType::FUNCTION:
297 if (out_value) {
298 // Certain APIs (contextMenus) have functions as parameters other than
299 // the callback (contextMenus uses it for an onclick listener). Our
300 // generated types have adapted to consider functions "objects" and
301 // serialize them as dictionaries.
302 // TODO(devlin): It'd be awfully nice to get rid of this eccentricity.
303 *out_value = base::MakeUnique<base::DictionaryValue>();
304 }
305 return true;
306 case ArgumentType::REF: {
307 DCHECK(ref_);
308 const ArgumentSpec* reference = refs.GetSpec(ref_.value());
309 DCHECK(reference) << ref_.value();
310 return reference->ParseArgument(context, value, refs, out_value, error);
311 }
312 case ArgumentType::CHOICES: {
313 for (const auto& choice : choices_) {
314 if (choice->ParseArgument(context, value, refs, out_value, error))
315 return true;
316 }
317 *error = api_errors::InvalidChoice();
216 return false; 318 return false;
217 } 319 }
218 320 case ArgumentType::ANY:
219 if (out_value) { 321 return ParseArgumentToAny(context, value, out_value, error);
220 // Certain APIs (contextMenus) have functions as parameters other than the
221 // callback (contextMenus uses it for an onclick listener). Our generated
222 // types have adapted to consider functions "objects" and serialize them
223 // as dictionaries.
224 // TODO(devlin): It'd be awfully nice to get rid of this eccentricity.
225 *out_value = base::MakeUnique<base::DictionaryValue>();
226 }
227 return true;
228 } 322 }
229 323
230 if (type_ == ArgumentType::REF) {
231 DCHECK(ref_);
232 const ArgumentSpec* reference = refs.GetSpec(ref_.value());
233 DCHECK(reference) << ref_.value();
234 return reference->ParseArgument(context, value, refs, out_value, error);
235 }
236
237 if (type_ == ArgumentType::CHOICES) {
238 for (const auto& choice : choices_) {
239 if (choice->ParseArgument(context, value, refs, out_value, error))
240 return true;
241 }
242 *error = api_errors::InvalidChoice();
243 return false;
244 }
245
246 if (IsFundamentalType())
247 return ParseArgumentToFundamental(context, value, out_value, error);
248 if (type_ == ArgumentType::OBJECT) {
249 // Don't allow functions or arrays (even though they are technically
250 // objects). This is to make it easier to match otherwise-ambiguous
251 // signatures. For instance, if an API method has an optional object
252 // parameter and then an optional callback, we wouldn't necessarily be able
253 // to match the arguments if we allowed functions as objects.
254 if (!value->IsObject() || value->IsFunction() || value->IsArray()) {
255 *error = GetInvalidTypeError(value);
256 return false;
257 }
258 v8::Local<v8::Object> object = value.As<v8::Object>();
259 return ParseArgumentToObject(context, object, refs, out_value, error);
260 }
261 if (type_ == ArgumentType::LIST) {
262 if (!value->IsArray()) {
263 *error = GetInvalidTypeError(value);
264 return false;
265 }
266 v8::Local<v8::Array> array = value.As<v8::Array>();
267 return ParseArgumentToArray(context, array, refs, out_value, error);
268 }
269 if (type_ == ArgumentType::BINARY) {
270 if (!value->IsArrayBuffer() && !value->IsArrayBufferView()) {
271 *error = GetInvalidTypeError(value);
272 return false;
273 }
274 return ParseArgumentToAny(context, value, out_value, error);
275 }
276 if (type_ == ArgumentType::ANY)
277 return ParseArgumentToAny(context, value, out_value, error);
278 NOTREACHED(); 324 NOTREACHED();
279 return false; 325 return false;
280 } 326 }
281 327
282 const std::string& ArgumentSpec::GetTypeName() const { 328 const std::string& ArgumentSpec::GetTypeName() const {
283 if (!type_name_.empty()) 329 if (!type_name_.empty())
284 return type_name_; 330 return type_name_;
285 331
286 switch (type_) { 332 switch (type_) {
287 case ArgumentType::INTEGER: 333 case ArgumentType::INTEGER:
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 break; 367 break;
322 } 368 }
323 case ArgumentType::ANY: 369 case ArgumentType::ANY:
324 type_name_ = api_errors::kTypeAny; 370 type_name_ = api_errors::kTypeAny;
325 break; 371 break;
326 } 372 }
327 DCHECK(!type_name_.empty()); 373 DCHECK(!type_name_.empty());
328 return type_name_; 374 return type_name_;
329 } 375 }
330 376
331 bool ArgumentSpec::IsFundamentalType() const {
332 return type_ == ArgumentType::INTEGER || type_ == ArgumentType::DOUBLE ||
333 type_ == ArgumentType::BOOLEAN || type_ == ArgumentType::STRING;
334 }
335
336 bool ArgumentSpec::ParseArgumentToFundamental( 377 bool ArgumentSpec::ParseArgumentToFundamental(
337 v8::Local<v8::Context> context, 378 v8::Local<v8::Context> context,
338 v8::Local<v8::Value> value, 379 v8::Local<v8::Value> value,
339 std::unique_ptr<base::Value>* out_value, 380 std::unique_ptr<base::Value>* out_value,
340 std::string* error) const { 381 std::string* error) const {
341 DCHECK(IsFundamentalType());
342
343 switch (type_) { 382 switch (type_) {
344 case ArgumentType::INTEGER: { 383 case ArgumentType::INTEGER: {
345 if (!value->IsInt32()) { 384 DCHECK(value->IsInt32());
346 *error = GetInvalidTypeError(value);
347 return false;
348 }
349 int int_val = value.As<v8::Int32>()->Value(); 385 int int_val = value.As<v8::Int32>()->Value();
350 if (!CheckFundamentalBounds(int_val, minimum_, maximum_, error)) 386 if (!CheckFundamentalBounds(int_val, minimum_, maximum_, error))
351 return false; 387 return false;
352 if (out_value) 388 if (out_value)
353 *out_value = base::MakeUnique<base::Value>(int_val); 389 *out_value = base::MakeUnique<base::Value>(int_val);
354 return true; 390 return true;
355 } 391 }
356 case ArgumentType::DOUBLE: { 392 case ArgumentType::DOUBLE: {
357 if (!value->IsNumber()) { 393 DCHECK(value->IsNumber());
358 *error = GetInvalidTypeError(value);
359 return false;
360 }
361 double double_val = value.As<v8::Number>()->Value(); 394 double double_val = value.As<v8::Number>()->Value();
362 if (!CheckFundamentalBounds(double_val, minimum_, maximum_, error)) 395 if (!CheckFundamentalBounds(double_val, minimum_, maximum_, error))
363 return false; 396 return false;
364 if (out_value) 397 if (out_value)
365 *out_value = base::MakeUnique<base::Value>(double_val); 398 *out_value = base::MakeUnique<base::Value>(double_val);
366 return true; 399 return true;
367 } 400 }
368 case ArgumentType::STRING: { 401 case ArgumentType::STRING: {
369 if (!value->IsString()) { 402 DCHECK(value->IsString());
370 *error = GetInvalidTypeError(value);
371 return false;
372 }
373 403
374 v8::Local<v8::String> v8_string = value.As<v8::String>(); 404 v8::Local<v8::String> v8_string = value.As<v8::String>();
375 size_t length = static_cast<size_t>(v8_string->Length()); 405 size_t length = static_cast<size_t>(v8_string->Length());
376 if (min_length_ && length < *min_length_) { 406 if (min_length_ && length < *min_length_) {
377 *error = api_errors::TooFewStringChars(*min_length_, length); 407 *error = api_errors::TooFewStringChars(*min_length_, length);
378 return false; 408 return false;
379 } 409 }
380 410
381 if (max_length_ && length > *max_length_) { 411 if (max_length_ && length > *max_length_) {
382 *error = api_errors::TooManyStringChars(*max_length_, length); 412 *error = api_errors::TooManyStringChars(*max_length_, length);
(...skipping 14 matching lines...) Expand all
397 return false; 427 return false;
398 } 428 }
399 if (out_value) { 429 if (out_value) {
400 // TODO(devlin): If base::Value ever takes a std::string&&, we 430 // TODO(devlin): If base::Value ever takes a std::string&&, we
401 // could use std::move to construct. 431 // could use std::move to construct.
402 *out_value = base::MakeUnique<base::Value>(s); 432 *out_value = base::MakeUnique<base::Value>(s);
403 } 433 }
404 return true; 434 return true;
405 } 435 }
406 case ArgumentType::BOOLEAN: { 436 case ArgumentType::BOOLEAN: {
407 if (!value->IsBoolean()) { 437 DCHECK(value->IsBoolean());
408 *error = GetInvalidTypeError(value);
409 return false;
410 }
411 if (out_value) { 438 if (out_value) {
412 *out_value = 439 *out_value =
413 base::MakeUnique<base::Value>(value.As<v8::Boolean>()->Value()); 440 base::MakeUnique<base::Value>(value.As<v8::Boolean>()->Value());
414 } 441 }
415 return true; 442 return true;
416 } 443 }
417 default: 444 default:
418 NOTREACHED(); 445 NOTREACHED();
419 } 446 }
420 return false; 447 return false;
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
634 return true; 661 return true;
635 } 662 }
636 663
637 std::string ArgumentSpec::GetInvalidTypeError( 664 std::string ArgumentSpec::GetInvalidTypeError(
638 v8::Local<v8::Value> value) const { 665 v8::Local<v8::Value> value) const {
639 return api_errors::InvalidType(GetTypeName().c_str(), 666 return api_errors::InvalidType(GetTypeName().c_str(),
640 GetV8ValueTypeString(value)); 667 GetV8ValueTypeString(value));
641 } 668 }
642 669
643 } // namespace extensions 670 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/bindings/argument_spec.h ('k') | extensions/renderer/bindings/argument_spec_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698