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

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

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

Powered by Google App Engine
This is Rietveld 408576698