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

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

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

Powered by Google App Engine
This is Rietveld 408576698