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

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

Issue 2837023003: [Extensions Bindings] Add argument parsing errors (Closed)
Patch Set: rebase Created 3 years, 7 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/argument_spec.h" 5 #include "extensions/renderer/argument_spec.h"
6 6
7 #include "base/memory/ptr_util.h" 7 #include "base/memory/ptr_util.h"
8 #include "base/strings/string_util.h"
8 #include "base/strings/stringprintf.h" 9 #include "base/strings/stringprintf.h"
9 #include "base/values.h" 10 #include "base/values.h"
10 #include "content/public/child/v8_value_converter.h" 11 #include "content/public/child/v8_value_converter.h"
12 #include "extensions/renderer/api_invocation_errors.h"
11 #include "extensions/renderer/api_type_reference_map.h" 13 #include "extensions/renderer/api_type_reference_map.h"
12 #include "gin/converter.h" 14 #include "gin/converter.h"
13 #include "gin/dictionary.h" 15 #include "gin/dictionary.h"
14 16
15 namespace extensions { 17 namespace extensions {
16 18
17 namespace { 19 namespace {
18 20
21 // Returns a type string for the given |value|.
22 const char* GetV8ValueTypeString(v8::Local<v8::Value> value) {
23 DCHECK(!value.IsEmpty());
24
25 if (value->IsNull())
26 return api_errors::kTypeNull;
27 if (value->IsUndefined())
28 return api_errors::kTypeUndefined;
29 if (value->IsInt32())
30 return api_errors::kTypeInteger;
31 if (value->IsNumber())
32 return api_errors::kTypeDouble;
33 if (value->IsBoolean())
34 return api_errors::kTypeBoolean;
35 if (value->IsString())
36 return api_errors::kTypeString;
37
38 // Note: check IsArray(), IsFunction(), and IsArrayBuffer[View]() before
39 // IsObject() since arrays, functions, and array buffers are objects.
40 if (value->IsArray())
41 return api_errors::kTypeList;
42 if (value->IsFunction())
43 return api_errors::kTypeFunction;
44 if (value->IsArrayBuffer() || value->IsArrayBufferView())
45 return api_errors::kTypeBinary;
46 if (value->IsObject())
47 return api_errors::kTypeObject;
48
49 return "";
50 }
51
52 // Returns true if |value| is within the bounds specified by |minimum|,
lazyboy 2017/04/25 18:23:12 within the bounds [|minimum|, |maximum|]
Devlin 2017/04/25 22:47:59 Done.
53 // populating |error| otherwise.
19 template <class T> 54 template <class T>
20 bool ParseFundamentalValueHelper(v8::Local<v8::Value> arg, 55 bool CheckFundamentalBounds(T value,
21 v8::Local<v8::Context> context, 56 const base::Optional<int>& minimum,
22 const base::Optional<int>& minimum, 57 const base::Optional<int>& maximum,
23 const base::Optional<int>& maximum, 58 std::string* error) {
24 std::unique_ptr<base::Value>* out_value) { 59 if (minimum && value < *minimum) {
25 T val; 60 *error = api_errors::GetError(api_errors::kNumberTooSmall, *minimum);
26 if (!gin::Converter<T>::FromV8(context->GetIsolate(), arg, &val))
27 return false; 61 return false;
28 if (minimum && val < *minimum) 62 }
63 if (maximum && value > *maximum) {
64 *error = api_errors::GetError(api_errors::kNumberTooLarge, *maximum);
29 return false; 65 return false;
30 if (maximum && val > *maximum) 66 }
31 return false;
32 if (out_value)
33 *out_value = base::MakeUnique<base::Value>(val);
34 return true; 67 return true;
35 } 68 }
36 69
37 } // namespace 70 } // namespace
38 71
39 ArgumentSpec::ArgumentSpec(const base::Value& value) 72 ArgumentSpec::ArgumentSpec(const base::Value& value)
40 : type_(ArgumentType::INTEGER), optional_(false) { 73 : type_(ArgumentType::INTEGER), optional_(false) {
41 const base::DictionaryValue* dict = nullptr; 74 const base::DictionaryValue* dict = nullptr;
42 CHECK(value.GetAsDictionary(&dict)); 75 CHECK(value.GetAsDictionary(&dict));
43 dict->GetBoolean("optional", &optional_); 76 dict->GetBoolean("optional", &optional_);
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 } 194 }
162 195
163 ArgumentSpec::~ArgumentSpec() {} 196 ArgumentSpec::~ArgumentSpec() {}
164 197
165 bool ArgumentSpec::ParseArgument(v8::Local<v8::Context> context, 198 bool ArgumentSpec::ParseArgument(v8::Local<v8::Context> context,
166 v8::Local<v8::Value> value, 199 v8::Local<v8::Value> value,
167 const APITypeReferenceMap& refs, 200 const APITypeReferenceMap& refs,
168 std::unique_ptr<base::Value>* out_value, 201 std::unique_ptr<base::Value>* out_value,
169 std::string* error) const { 202 std::string* error) const {
170 if (type_ == ArgumentType::FUNCTION) { 203 if (type_ == ArgumentType::FUNCTION) {
171 if (!value->IsFunction()) 204 if (!value->IsFunction()) {
205 *error = GetInvalidTypeError(value);
172 return false; 206 return false;
207 }
173 208
174 if (out_value) { 209 if (out_value) {
175 // Certain APIs (contextMenus) have functions as parameters other than the 210 // Certain APIs (contextMenus) have functions as parameters other than the
176 // callback (contextMenus uses it for an onclick listener). Our generated 211 // callback (contextMenus uses it for an onclick listener). Our generated
177 // types have adapted to consider functions "objects" and serialize them 212 // types have adapted to consider functions "objects" and serialize them
178 // as dictionaries. 213 // as dictionaries.
179 // TODO(devlin): It'd be awfully nice to get rid of this eccentricity. 214 // TODO(devlin): It'd be awfully nice to get rid of this eccentricity.
180 *out_value = base::MakeUnique<base::DictionaryValue>(); 215 *out_value = base::MakeUnique<base::DictionaryValue>();
181 } 216 }
182 return true; 217 return true;
183 } 218 }
184 219
185 if (type_ == ArgumentType::REF) { 220 if (type_ == ArgumentType::REF) {
186 DCHECK(ref_); 221 DCHECK(ref_);
187 const ArgumentSpec* reference = refs.GetSpec(ref_.value()); 222 const ArgumentSpec* reference = refs.GetSpec(ref_.value());
188 DCHECK(reference) << ref_.value(); 223 DCHECK(reference) << ref_.value();
189 return reference->ParseArgument(context, value, refs, out_value, error); 224 return reference->ParseArgument(context, value, refs, out_value, error);
190 } 225 }
191 226
192 if (type_ == ArgumentType::CHOICES) { 227 if (type_ == ArgumentType::CHOICES) {
193 for (const auto& choice : choices_) { 228 for (const auto& choice : choices_) {
194 if (choice->ParseArgument(context, value, refs, out_value, error)) 229 if (choice->ParseArgument(context, value, refs, out_value, error))
195 return true; 230 return true;
196 } 231 }
197 *error = "Did not match any of the choices"; 232 *error = api_errors::kInvalidChoice;
198 return false; 233 return false;
199 } 234 }
200 235
201 if (IsFundamentalType()) 236 if (IsFundamentalType())
202 return ParseArgumentToFundamental(context, value, out_value, error); 237 return ParseArgumentToFundamental(context, value, out_value, error);
203 if (type_ == ArgumentType::OBJECT) { 238 if (type_ == ArgumentType::OBJECT) {
204 // Don't allow functions or arrays (even though they are technically 239 // Don't allow functions or arrays (even though they are technically
205 // objects). This is to make it easier to match otherwise-ambiguous 240 // objects). This is to make it easier to match otherwise-ambiguous
206 // signatures. For instance, if an API method has an optional object 241 // signatures. For instance, if an API method has an optional object
207 // parameter and then an optional callback, we wouldn't necessarily be able 242 // parameter and then an optional callback, we wouldn't necessarily be able
208 // to match the arguments if we allowed functions as objects. 243 // to match the arguments if we allowed functions as objects.
209 if (!value->IsObject() || value->IsFunction() || value->IsArray()) { 244 if (!value->IsObject() || value->IsFunction() || value->IsArray()) {
210 *error = "Wrong type"; 245 *error = GetInvalidTypeError(value);
211 return false; 246 return false;
212 } 247 }
213 v8::Local<v8::Object> object = value.As<v8::Object>(); 248 v8::Local<v8::Object> object = value.As<v8::Object>();
214 return ParseArgumentToObject(context, object, refs, out_value, error); 249 return ParseArgumentToObject(context, object, refs, out_value, error);
215 } 250 }
216 if (type_ == ArgumentType::LIST) { 251 if (type_ == ArgumentType::LIST) {
217 if (!value->IsArray()) { 252 if (!value->IsArray()) {
218 *error = "Wrong type"; 253 *error = GetInvalidTypeError(value);
219 return false; 254 return false;
220 } 255 }
221 v8::Local<v8::Array> array = value.As<v8::Array>(); 256 v8::Local<v8::Array> array = value.As<v8::Array>();
222 return ParseArgumentToArray(context, array, refs, out_value, error); 257 return ParseArgumentToArray(context, array, refs, out_value, error);
223 } 258 }
224 if (type_ == ArgumentType::BINARY) { 259 if (type_ == ArgumentType::BINARY) {
225 if (!value->IsArrayBuffer() && !value->IsArrayBufferView()) { 260 if (!value->IsArrayBuffer() && !value->IsArrayBufferView()) {
226 *error = "Wrong type"; 261 *error = GetInvalidTypeError(value);
227 return false; 262 return false;
228 } 263 }
229 return ParseArgumentToAny(context, value, out_value, error); 264 return ParseArgumentToAny(context, value, out_value, error);
230 } 265 }
231 if (type_ == ArgumentType::ANY) 266 if (type_ == ArgumentType::ANY)
232 return ParseArgumentToAny(context, value, out_value, error); 267 return ParseArgumentToAny(context, value, out_value, error);
233 NOTREACHED(); 268 NOTREACHED();
234 return false; 269 return false;
235 } 270 }
236 271
237 bool ArgumentSpec::IsFundamentalType() const { 272 bool ArgumentSpec::IsFundamentalType() const {
238 return type_ == ArgumentType::INTEGER || type_ == ArgumentType::DOUBLE || 273 return type_ == ArgumentType::INTEGER || type_ == ArgumentType::DOUBLE ||
239 type_ == ArgumentType::BOOLEAN || type_ == ArgumentType::STRING; 274 type_ == ArgumentType::BOOLEAN || type_ == ArgumentType::STRING;
240 } 275 }
241 276
242 bool ArgumentSpec::ParseArgumentToFundamental( 277 bool ArgumentSpec::ParseArgumentToFundamental(
243 v8::Local<v8::Context> context, 278 v8::Local<v8::Context> context,
244 v8::Local<v8::Value> value, 279 v8::Local<v8::Value> value,
245 std::unique_ptr<base::Value>* out_value, 280 std::unique_ptr<base::Value>* out_value,
246 std::string* error) const { 281 std::string* error) const {
247 DCHECK(IsFundamentalType()); 282 DCHECK(IsFundamentalType());
283
248 switch (type_) { 284 switch (type_) {
249 case ArgumentType::INTEGER: 285 case ArgumentType::INTEGER: {
250 return ParseFundamentalValueHelper<int32_t>(value, context, minimum_, 286 if (!value->IsInt32()) {
251 maximum_, out_value); 287 *error = GetInvalidTypeError(value);
252 case ArgumentType::DOUBLE: 288 return false;
253 return ParseFundamentalValueHelper<double>(value, context, minimum_, 289 }
254 maximum_, out_value); 290 int int_val = value.As<v8::Int32>()->Value();
291 if (!CheckFundamentalBounds(int_val, minimum_, maximum_, error))
292 return false;
293 if (out_value)
294 *out_value = base::MakeUnique<base::Value>(int_val);
295 return true;
296 }
297 case ArgumentType::DOUBLE: {
298 if (!value->IsNumber()) {
299 *error = GetInvalidTypeError(value);
300 return false;
301 }
302 double double_val = value.As<v8::Number>()->Value();
303 if (!CheckFundamentalBounds(double_val, minimum_, maximum_, error))
304 return false;
305 if (out_value)
306 *out_value = base::MakeUnique<base::Value>(double_val);
307 return true;
308 }
255 case ArgumentType::STRING: { 309 case ArgumentType::STRING: {
256 if (!value->IsString()) 310 if (!value->IsString()) {
311 *error = GetInvalidTypeError(value);
257 return false; 312 return false;
313 }
258 314
259 v8::Local<v8::String> v8_string = value.As<v8::String>(); 315 v8::Local<v8::String> v8_string = value.As<v8::String>();
260 size_t length = static_cast<size_t>(v8_string->Length()); 316 size_t length = static_cast<size_t>(v8_string->Length());
261 if (min_length_ && length < *min_length_) { 317 if (min_length_ && length < *min_length_) {
262 *error = "Less than min length"; 318 *error =
319 api_errors::GetError(api_errors::kTooFewStringChars, *min_length_);
263 return false; 320 return false;
264 } 321 }
265 322
266 if (max_length_ && length > *max_length_) { 323 if (max_length_ && length > *max_length_) {
267 *error = "Greater than max length"; 324 *error =
325 api_errors::GetError(api_errors::kTooManyStringChars, *max_length_);
268 return false; 326 return false;
269 } 327 }
270 328
271 // If we don't need to match enum values and don't need to convert, we're 329 // If we don't need to match enum values and don't need to convert, we're
272 // done... 330 // done...
273 if (!out_value && enum_values_.empty()) 331 if (!out_value && enum_values_.empty())
274 return true; 332 return true;
275 // ...Otherwise, we need to convert to a std::string. 333 // ...Otherwise, we need to convert to a std::string.
276 std::string s; 334 std::string s;
277 // We already checked that this is a string, so this should never fail. 335 // We already checked that this is a string, so this should never fail.
278 CHECK(gin::Converter<std::string>::FromV8(context->GetIsolate(), value, 336 CHECK(gin::Converter<std::string>::FromV8(context->GetIsolate(), value,
279 &s)); 337 &s));
280 if (!enum_values_.empty() && enum_values_.count(s) == 0) 338 if (!enum_values_.empty() && enum_values_.count(s) == 0) {
339 std::vector<base::StringPiece> options(enum_values_.begin(),
340 enum_values_.end());
341 std::string options_str = base::JoinString(options, ", ");
342 *error = api_errors::GetError(api_errors::kInvalidEnumValue,
343 options_str.c_str());
281 return false; 344 return false;
345 }
282 if (out_value) { 346 if (out_value) {
283 // TODO(devlin): If base::Value ever takes a std::string&&, we 347 // TODO(devlin): If base::Value ever takes a std::string&&, we
284 // could use std::move to construct. 348 // could use std::move to construct.
285 *out_value = base::MakeUnique<base::Value>(s); 349 *out_value = base::MakeUnique<base::Value>(s);
286 } 350 }
287 return true; 351 return true;
288 } 352 }
289 case ArgumentType::BOOLEAN: { 353 case ArgumentType::BOOLEAN: {
290 if (!value->IsBoolean()) 354 if (!value->IsBoolean()) {
355 *error = GetInvalidTypeError(value);
291 return false; 356 return false;
357 }
292 if (out_value) { 358 if (out_value) {
293 *out_value = 359 *out_value =
294 base::MakeUnique<base::Value>(value.As<v8::Boolean>()->Value()); 360 base::MakeUnique<base::Value>(value.As<v8::Boolean>()->Value());
295 } 361 }
296 return true; 362 return true;
297 } 363 }
298 default: 364 default:
299 NOTREACHED(); 365 NOTREACHED();
300 } 366 }
301 return false; 367 return false;
302 } 368 }
303 369
304 bool ArgumentSpec::ParseArgumentToObject( 370 bool ArgumentSpec::ParseArgumentToObject(
305 v8::Local<v8::Context> context, 371 v8::Local<v8::Context> context,
306 v8::Local<v8::Object> object, 372 v8::Local<v8::Object> object,
307 const APITypeReferenceMap& refs, 373 const APITypeReferenceMap& refs,
308 std::unique_ptr<base::Value>* out_value, 374 std::unique_ptr<base::Value>* out_value,
309 std::string* error) const { 375 std::string* error) const {
310 DCHECK_EQ(ArgumentType::OBJECT, type_); 376 DCHECK_EQ(ArgumentType::OBJECT, type_);
311 std::unique_ptr<base::DictionaryValue> result; 377 std::unique_ptr<base::DictionaryValue> result;
312 // Only construct the result if we have an |out_value| to populate. 378 // Only construct the result if we have an |out_value| to populate.
313 if (out_value) 379 if (out_value)
314 result = base::MakeUnique<base::DictionaryValue>(); 380 result = base::MakeUnique<base::DictionaryValue>();
315 381
316 v8::Local<v8::Array> own_property_names; 382 v8::Local<v8::Array> own_property_names;
317 if (!object->GetOwnPropertyNames(context).ToLocal(&own_property_names)) 383 if (!object->GetOwnPropertyNames(context).ToLocal(&own_property_names)) {
384 *error = api_errors::kScriptThrewError;
318 return false; 385 return false;
386 }
319 387
320 // Track all properties we see from |properties_| to check if any are missing. 388 // Track all properties we see from |properties_| to check if any are missing.
321 // Use ArgumentSpec* instead of std::string for comparison + copy efficiency. 389 // Use ArgumentSpec* instead of std::string for comparison + copy efficiency.
322 std::set<const ArgumentSpec*> seen_properties; 390 std::set<const ArgumentSpec*> seen_properties;
323 uint32_t length = own_property_names->Length(); 391 uint32_t length = own_property_names->Length();
392 std::string property_error;
324 for (uint32_t i = 0; i < length; ++i) { 393 for (uint32_t i = 0; i < length; ++i) {
325 v8::Local<v8::Value> key; 394 v8::Local<v8::Value> key;
326 if (!own_property_names->Get(context, i).ToLocal(&key)) 395 if (!own_property_names->Get(context, i).ToLocal(&key)) {
396 *error = api_errors::kScriptThrewError;
327 return false; 397 return false;
398 }
328 // In JS, all keys are strings or numbers (or symbols, but those are 399 // In JS, all keys are strings or numbers (or symbols, but those are
329 // excluded by GetOwnPropertyNames()). If you try to set anything else 400 // excluded by GetOwnPropertyNames()). If you try to set anything else
330 // (e.g. an object), it is converted to a string. 401 // (e.g. an object), it is converted to a string.
331 DCHECK(key->IsString() || key->IsNumber()); 402 DCHECK(key->IsString() || key->IsNumber());
332 v8::String::Utf8Value utf8_key(key); 403 v8::String::Utf8Value utf8_key(key);
333 404
334 ArgumentSpec* property_spec = nullptr; 405 ArgumentSpec* property_spec = nullptr;
335 auto iter = properties_.find(*utf8_key); 406 auto iter = properties_.find(*utf8_key);
336 if (iter != properties_.end()) { 407 if (iter != properties_.end()) {
337 property_spec = iter->second.get(); 408 property_spec = iter->second.get();
338 seen_properties.insert(property_spec); 409 seen_properties.insert(property_spec);
339 } else if (additional_properties_) { 410 } else if (additional_properties_) {
340 property_spec = additional_properties_.get(); 411 property_spec = additional_properties_.get();
341 } else { 412 } else {
342 *error = base::StringPrintf("Unknown property: %s", *utf8_key); 413 *error = api_errors::GetError(api_errors::kUnexpectedProperty, *utf8_key);
343 return false; 414 return false;
344 } 415 }
345 416
346 v8::Local<v8::Value> prop_value; 417 v8::Local<v8::Value> prop_value;
347 // Fun: It's possible that a previous getter has removed the property from 418 // Fun: It's possible that a previous getter has removed the property from
348 // the object. This isn't that big of a deal, since it would only manifest 419 // the object. This isn't that big of a deal, since it would only manifest
349 // in the case of some reasonably-crazy script objects, and it's probably 420 // in the case of some reasonably-crazy script objects, and it's probably
350 // not worth optimizing for the uncommon case to the detriment of the 421 // not worth optimizing for the uncommon case to the detriment of the
351 // common (and either should be totally safe). We can always add a 422 // common (and either should be totally safe). We can always add a
352 // HasOwnProperty() check here in the future, if we desire. 423 // HasOwnProperty() check here in the future, if we desire.
353 // See also comment in ParseArgumentToArray() about passing in custom 424 // See also comment in ParseArgumentToArray() about passing in custom
354 // crazy values here. 425 // crazy values here.
355 if (!object->Get(context, key).ToLocal(&prop_value)) 426 if (!object->Get(context, key).ToLocal(&prop_value))
356 return false; 427 return false;
357 428
358 // Note: We don't serialize undefined or null values. 429 // Note: We don't serialize undefined or null values.
359 // TODO(devlin): This matches current behavior, but it is correct? 430 // TODO(devlin): This matches current behavior, but it is correct?
360 if (prop_value->IsUndefined() || prop_value->IsNull()) { 431 if (prop_value->IsUndefined() || prop_value->IsNull()) {
361 if (!property_spec->optional_) { 432 if (!property_spec->optional_) {
362 *error = base::StringPrintf("Missing key: %s", *utf8_key); 433 *error = api_errors::GetError(api_errors::kMissingRequiredProperty,
434 *utf8_key);
363 return false; 435 return false;
364 } 436 }
365 continue; 437 continue;
366 } 438 }
367 439
368 std::unique_ptr<base::Value> property; 440 std::unique_ptr<base::Value> property;
369 if (!property_spec->ParseArgument(context, prop_value, refs, 441 if (!property_spec->ParseArgument(context, prop_value, refs,
370 result ? &property : nullptr, error)) { 442 result ? &property : nullptr,
443 &property_error)) {
444 *error = api_errors::GetPropertyError(*utf8_key, property_error);
371 return false; 445 return false;
372 } 446 }
373 if (result) 447 if (result)
374 result->SetWithoutPathExpansion(*utf8_key, std::move(property)); 448 result->SetWithoutPathExpansion(*utf8_key, std::move(property));
375 } 449 }
376 450
377 for (const auto& pair : properties_) { 451 for (const auto& pair : properties_) {
378 const ArgumentSpec* spec = pair.second.get(); 452 const ArgumentSpec* spec = pair.second.get();
379 if (!spec->optional_ && seen_properties.count(spec) == 0) { 453 if (!spec->optional_ && seen_properties.count(spec) == 0) {
380 *error = "Missing key: " + pair.first; 454 *error = api_errors::GetError(api_errors::kMissingRequiredProperty,
455 pair.first.c_str());
381 return false; 456 return false;
382 } 457 }
383 } 458 }
384 459
385 if (instance_of_) { 460 if (instance_of_) {
386 // Check for the instance somewhere in the object's prototype chain. 461 // Check for the instance somewhere in the object's prototype chain.
387 // NOTE: This only checks that something in the prototype chain was 462 // NOTE: This only checks that something in the prototype chain was
388 // constructed with the same name as the desired instance, but doesn't 463 // constructed with the same name as the desired instance, but doesn't
389 // validate that it's the same constructor as the expected one. For 464 // validate that it's the same constructor as the expected one. For
390 // instance, if we expect isInstanceOf == 'Date', script could pass in 465 // instance, if we expect isInstanceOf == 'Date', script could pass in
(...skipping 12 matching lines...) Expand all
403 v8::String::Utf8Value constructor(current->GetConstructorName()); 478 v8::String::Utf8Value constructor(current->GetConstructorName());
404 if (*instance_of_ == 479 if (*instance_of_ ==
405 base::StringPiece(*constructor, constructor.length())) { 480 base::StringPiece(*constructor, constructor.length())) {
406 found = true; 481 found = true;
407 break; 482 break;
408 } 483 }
409 next_check = current->GetPrototype(); 484 next_check = current->GetPrototype();
410 } while (next_check->IsObject()); 485 } while (next_check->IsObject());
411 486
412 if (!found) { 487 if (!found) {
413 *error = "Object is not of correct instance"; 488 *error = api_errors::GetError(api_errors::kNotAnInstance,
489 instance_of_->c_str());
414 return false; 490 return false;
415 } 491 }
416 } 492 }
417 493
418 if (out_value) 494 if (out_value)
419 *out_value = std::move(result); 495 *out_value = std::move(result);
420 return true; 496 return true;
421 } 497 }
422 498
423 bool ArgumentSpec::ParseArgumentToArray(v8::Local<v8::Context> context, 499 bool ArgumentSpec::ParseArgumentToArray(v8::Local<v8::Context> context,
424 v8::Local<v8::Array> value, 500 v8::Local<v8::Array> value,
425 const APITypeReferenceMap& refs, 501 const APITypeReferenceMap& refs,
426 std::unique_ptr<base::Value>* out_value, 502 std::unique_ptr<base::Value>* out_value,
427 std::string* error) const { 503 std::string* error) const {
428 DCHECK_EQ(ArgumentType::LIST, type_); 504 DCHECK_EQ(ArgumentType::LIST, type_);
429 505
430 uint32_t length = value->Length(); 506 uint32_t length = value->Length();
431 if (min_length_ && length < *min_length_) { 507 if (min_length_ && length < *min_length_) {
432 *error = "Less than min length"; 508 *error = api_errors::GetError(api_errors::kTooFewArrayItems, *min_length_);
433 return false; 509 return false;
434 } 510 }
435 511
436 if (max_length_ && length > *max_length_) { 512 if (max_length_ && length > *max_length_) {
437 *error = "Greater than max length"; 513 *error = api_errors::GetError(api_errors::kTooManyArrayItems, *max_length_);
438 return false; 514 return false;
439 } 515 }
440 516
441 std::unique_ptr<base::ListValue> result; 517 std::unique_ptr<base::ListValue> result;
442 // Only construct the result if we have an |out_value| to populate. 518 // Only construct the result if we have an |out_value| to populate.
443 if (out_value) 519 if (out_value)
444 result = base::MakeUnique<base::ListValue>(); 520 result = base::MakeUnique<base::ListValue>();
445 521
522 std::string item_error;
446 for (uint32_t i = 0; i < length; ++i) { 523 for (uint32_t i = 0; i < length; ++i) {
447 v8::MaybeLocal<v8::Value> maybe_subvalue = value->Get(context, i); 524 v8::MaybeLocal<v8::Value> maybe_subvalue = value->Get(context, i);
448 v8::Local<v8::Value> subvalue; 525 v8::Local<v8::Value> subvalue;
449 // Note: This can fail in the case of a developer passing in the following: 526 // Note: This can fail in the case of a developer passing in the following:
450 // var a = []; 527 // var a = [];
451 // Object.defineProperty(a, 0, { get: () => { throw new Error('foo'); } }); 528 // Object.defineProperty(a, 0, { get: () => { throw new Error('foo'); } });
452 // Currently, this will cause the developer-specified error ('foo') to be 529 // Currently, this will cause the developer-specified error ('foo') to be
453 // thrown. 530 // thrown.
454 // TODO(devlin): This is probably fine, but it's worth contemplating 531 // TODO(devlin): This is probably fine, but it's worth contemplating
455 // catching the error and throwing our own. 532 // catching the error and throwing our own.
456 if (!maybe_subvalue.ToLocal(&subvalue)) 533 if (!maybe_subvalue.ToLocal(&subvalue))
457 return false; 534 return false;
458 std::unique_ptr<base::Value> item; 535 std::unique_ptr<base::Value> item;
459 if (!list_element_type_->ParseArgument(context, subvalue, refs, 536 if (!list_element_type_->ParseArgument(
460 result ? &item : nullptr, error)) { 537 context, subvalue, refs, result ? &item : nullptr, &item_error)) {
538 *error = api_errors::GetIndexError(i, item_error);
461 return false; 539 return false;
462 } 540 }
463 if (result) 541 if (result)
464 result->Append(std::move(item)); 542 result->Append(std::move(item));
465 } 543 }
466 if (out_value) 544 if (out_value)
467 *out_value = std::move(result); 545 *out_value = std::move(result);
468 return true; 546 return true;
469 } 547 }
470 548
471 bool ArgumentSpec::ParseArgumentToAny(v8::Local<v8::Context> context, 549 bool ArgumentSpec::ParseArgumentToAny(v8::Local<v8::Context> context,
472 v8::Local<v8::Value> value, 550 v8::Local<v8::Value> value,
473 std::unique_ptr<base::Value>* out_value, 551 std::unique_ptr<base::Value>* out_value,
474 std::string* error) const { 552 std::string* error) const {
475 DCHECK(type_ == ArgumentType::ANY || type_ == ArgumentType::BINARY); 553 DCHECK(type_ == ArgumentType::ANY || type_ == ArgumentType::BINARY);
476 if (out_value) { 554 if (out_value) {
477 std::unique_ptr<content::V8ValueConverter> converter( 555 std::unique_ptr<content::V8ValueConverter> converter(
478 content::V8ValueConverter::create()); 556 content::V8ValueConverter::create());
479 std::unique_ptr<base::Value> converted( 557 std::unique_ptr<base::Value> converted(
480 converter->FromV8Value(value, context)); 558 converter->FromV8Value(value, context));
481 if (!converted) { 559 if (!converted) {
482 *error = "Could not convert to 'any'."; 560 *error = api_errors::kUnserializableValue;
483 return false; 561 return false;
484 } 562 }
485 if (type_ == ArgumentType::BINARY) 563 if (type_ == ArgumentType::BINARY)
486 DCHECK_EQ(base::Value::Type::BINARY, converted->GetType()); 564 DCHECK_EQ(base::Value::Type::BINARY, converted->GetType());
487 *out_value = std::move(converted); 565 *out_value = std::move(converted);
488 } 566 }
489 return true; 567 return true;
490 } 568 }
491 569
570 std::string ArgumentSpec::GetInvalidTypeError(
571 v8::Local<v8::Value> value) const {
572 base::StringPiece expected_type;
573 switch (type_) {
574 case ArgumentType::INTEGER:
575 expected_type = api_errors::kTypeInteger;
576 break;
577 case ArgumentType::DOUBLE:
578 expected_type = api_errors::kTypeDouble;
579 break;
580 case ArgumentType::BOOLEAN:
581 expected_type = api_errors::kTypeBoolean;
582 break;
583 case ArgumentType::STRING:
584 expected_type = api_errors::kTypeString;
585 break;
586 case ArgumentType::OBJECT:
587 expected_type = instance_of_ ? *instance_of_ : api_errors::kTypeObject;
588 break;
589 case ArgumentType::LIST:
590 expected_type = api_errors::kTypeList;
591 break;
592 case ArgumentType::BINARY:
593 expected_type = api_errors::kTypeBinary;
594 break;
595 case ArgumentType::FUNCTION:
596 expected_type = api_errors::kTypeFunction;
597 break;
598 case ArgumentType::REF:
599 expected_type = *ref_;
600 break;
601 case ArgumentType::CHOICES:
602 case ArgumentType::ANY:
603 NOTREACHED();
604 }
605
606 return api_errors::GetError(api_errors::kInvalidType, expected_type.data(),
607 GetV8ValueTypeString(value));
608 }
609
492 } // namespace extensions 610 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698