1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "extensions/renderer/bindings/argument_spec.h"
6 
7 #include "base/strings/string_piece.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/values.h"
11 #include "content/public/renderer/v8_value_converter.h"
12 #include "extensions/renderer/bindings/api_invocation_errors.h"
13 #include "extensions/renderer/bindings/api_type_reference_map.h"
14 #include "gin/converter.h"
15 #include "gin/data_object_builder.h"
16 #include "gin/dictionary.h"
17 
18 namespace extensions {
19 
20 namespace {
21 
22 // Returns a type string for the given |value|.
GetV8ValueTypeString(v8::Local<v8::Value> value)23 const char* GetV8ValueTypeString(v8::Local<v8::Value> value) {
24   DCHECK(!value.IsEmpty());
25 
26   if (value->IsNull())
27     return api_errors::kTypeNull;
28   if (value->IsUndefined())
29     return api_errors::kTypeUndefined;
30   if (value->IsInt32())
31     return api_errors::kTypeInteger;
32   if (value->IsNumber())
33     return api_errors::kTypeDouble;
34   if (value->IsBoolean())
35     return api_errors::kTypeBoolean;
36   if (value->IsString())
37     return api_errors::kTypeString;
38 
39   // Note: check IsArray(), IsFunction(), and IsArrayBuffer[View]() before
40   // IsObject() since arrays, functions, and array buffers are objects.
41   if (value->IsArray())
42     return api_errors::kTypeList;
43   if (value->IsFunction())
44     return api_errors::kTypeFunction;
45   if (value->IsArrayBuffer() || value->IsArrayBufferView())
46     return api_errors::kTypeBinary;
47   if (value->IsObject())
48     return api_errors::kTypeObject;
49 
50   // TODO(devlin): The list above isn't exhaustive (it's missing at least
51   // Symbol and Uint32). We may want to include those, since saying
52   // "expected int, found other" isn't super helpful. On the other hand, authors
53   // should be able to see what they passed.
54   return "other";
55 }
56 
57 // Returns true if |value| is within the bounds specified by |minimum| and
58 // |maximum|, populating |error| otherwise.
59 template <class T>
CheckFundamentalBounds(T value,const base::Optional<int> & minimum,const base::Optional<int> & maximum,std::string * error)60 bool CheckFundamentalBounds(T value,
61                             const base::Optional<int>& minimum,
62                             const base::Optional<int>& maximum,
63                             std::string* error) {
64   if (minimum && value < *minimum) {
65     *error = api_errors::NumberTooSmall(*minimum);
66     return false;
67   }
68   if (maximum && value > *maximum) {
69     *error = api_errors::NumberTooLarge(*maximum);
70     return false;
71   }
72   return true;
73 }
74 
75 }  // namespace
76 
ArgumentSpec(const base::Value & value)77 ArgumentSpec::ArgumentSpec(const base::Value& value) {
78   const base::DictionaryValue* dict = nullptr;
79   CHECK(value.GetAsDictionary(&dict));
80   dict->GetBoolean("optional", &optional_);
81   dict->GetString("name", &name_);
82 
83   InitializeType(dict);
84 }
85 
ArgumentSpec(ArgumentType type)86 ArgumentSpec::ArgumentSpec(ArgumentType type) : type_(type) {}
87 
InitializeType(const base::DictionaryValue * dict)88 void ArgumentSpec::InitializeType(const base::DictionaryValue* dict) {
89   std::string ref_string;
90   if (dict->GetString("$ref", &ref_string)) {
91     ref_ = std::move(ref_string);
92     type_ = ArgumentType::REF;
93     return;
94   }
95 
96   {
97     const base::ListValue* choices = nullptr;
98     if (dict->GetList("choices", &choices)) {
99       DCHECK(!choices->empty());
100       type_ = ArgumentType::CHOICES;
101       choices_.reserve(choices->GetSize());
102       for (const auto& choice : *choices)
103         choices_.push_back(std::make_unique<ArgumentSpec>(choice));
104       return;
105     }
106   }
107 
108   std::string type_string;
109   CHECK(dict->GetString("type", &type_string));
110   if (type_string == "integer")
111     type_ = ArgumentType::INTEGER;
112   else if (type_string == "number")
113     type_ = ArgumentType::DOUBLE;
114   else if (type_string == "object")
115     type_ = ArgumentType::OBJECT;
116   else if (type_string == "array")
117     type_ = ArgumentType::LIST;
118   else if (type_string == "boolean")
119     type_ = ArgumentType::BOOLEAN;
120   else if (type_string == "string")
121     type_ = ArgumentType::STRING;
122   else if (type_string == "binary")
123     type_ = ArgumentType::BINARY;
124   else if (type_string == "any")
125     type_ = ArgumentType::ANY;
126   else if (type_string == "function")
127     type_ = ArgumentType::FUNCTION;
128   else
129     NOTREACHED();
130 
131   int min = 0;
132   if (dict->GetInteger("minimum", &min))
133     minimum_ = min;
134 
135   int max = 0;
136   if (dict->GetInteger("maximum", &max))
137     maximum_ = max;
138 
139   int min_length = 0;
140   if (dict->GetInteger("minLength", &min_length) ||
141       dict->GetInteger("minItems", &min_length)) {
142     DCHECK_GE(min_length, 0);
143     min_length_ = min_length;
144   }
145 
146   int max_length = 0;
147   if (dict->GetInteger("maxLength", &max_length) ||
148       dict->GetInteger("maxItems", &max_length)) {
149     DCHECK_GE(max_length, 0);
150     max_length_ = max_length;
151   }
152 
153   if (type_ == ArgumentType::OBJECT) {
154     const base::DictionaryValue* properties_value = nullptr;
155     if (dict->GetDictionary("properties", &properties_value)) {
156       for (base::DictionaryValue::Iterator iter(*properties_value);
157            !iter.IsAtEnd(); iter.Advance()) {
158         properties_[iter.key()] = std::make_unique<ArgumentSpec>(iter.value());
159       }
160     }
161     const base::DictionaryValue* additional_properties_value = nullptr;
162     if (dict->GetDictionary("additionalProperties",
163                             &additional_properties_value)) {
164       additional_properties_ =
165           std::make_unique<ArgumentSpec>(*additional_properties_value);
166       // Additional properties are always optional.
167       additional_properties_->optional_ = true;
168     }
169   } else if (type_ == ArgumentType::LIST) {
170     const base::DictionaryValue* item_value = nullptr;
171     CHECK(dict->GetDictionary("items", &item_value));
172     list_element_type_ = std::make_unique<ArgumentSpec>(*item_value);
173   } else if (type_ == ArgumentType::STRING) {
174     // Technically, there's no reason enums couldn't be other objects (e.g.
175     // numbers), but right now they seem to be exclusively strings. We could
176     // always update this if need be.
177     const base::ListValue* enums = nullptr;
178     if (dict->GetList("enum", &enums)) {
179       size_t size = enums->GetSize();
180       CHECK_GT(size, 0u);
181       for (size_t i = 0; i < size; ++i) {
182         std::string enum_value;
183         // Enum entries come in two versions: a list of possible strings, and
184         // a dictionary with a field 'name'.
185         if (!enums->GetString(i, &enum_value)) {
186           const base::DictionaryValue* enum_value_dictionary = nullptr;
187           CHECK(enums->GetDictionary(i, &enum_value_dictionary));
188           CHECK(enum_value_dictionary->GetString("name", &enum_value));
189         }
190         enum_values_.insert(std::move(enum_value));
191       }
192     }
193   }
194 
195   // Check if we should preserve null in objects. Right now, this is only used
196   // on arguments of type object and any (in fact, it's only used in the storage
197   // API), but it could potentially make sense for lists or functions as well.
198   if (type_ == ArgumentType::OBJECT || type_ == ArgumentType::ANY)
199     dict->GetBoolean("preserveNull", &preserve_null_);
200 
201   if (type_ == ArgumentType::OBJECT || type_ == ArgumentType::BINARY) {
202     std::string instance_of;
203     if (dict->GetString("isInstanceOf", &instance_of))
204       instance_of_ = instance_of;
205   }
206 }
207 
~ArgumentSpec()208 ArgumentSpec::~ArgumentSpec() {}
209 
IsCorrectType(v8::Local<v8::Value> value,const APITypeReferenceMap & refs,std::string * error) const210 bool ArgumentSpec::IsCorrectType(v8::Local<v8::Value> value,
211                                  const APITypeReferenceMap& refs,
212                                  std::string* error) const {
213   bool is_valid_type = false;
214 
215   switch (type_) {
216     case ArgumentType::INTEGER:
217       // -0 is treated internally as a double, but we classify it as an integer.
218       is_valid_type =
219           value->IsInt32() ||
220           (value->IsNumber() && value.As<v8::Number>()->Value() == 0.0);
221       break;
222     case ArgumentType::DOUBLE:
223       is_valid_type = value->IsNumber();
224       break;
225     case ArgumentType::BOOLEAN:
226       is_valid_type = value->IsBoolean();
227       break;
228     case ArgumentType::STRING:
229       is_valid_type = value->IsString();
230       break;
231     case ArgumentType::OBJECT:
232       // Don't allow functions or arrays (even though they are technically
233       // objects). This is to make it easier to match otherwise-ambiguous
234       // signatures. For instance, if an API method has an optional object
235       // parameter and then an optional callback, we wouldn't necessarily be
236       // able to match the arguments if we allowed functions as objects.
237       // TODO(devlin): What about other subclasses of Object, like Map and Set?
238       is_valid_type =
239           value->IsObject() && !value->IsFunction() && !value->IsArray();
240       break;
241     case ArgumentType::LIST:
242       is_valid_type = value->IsArray();
243       break;
244     case ArgumentType::BINARY:
245       is_valid_type = value->IsArrayBuffer() || value->IsArrayBufferView();
246       break;
247     case ArgumentType::FUNCTION:
248       is_valid_type = value->IsFunction();
249       break;
250     case ArgumentType::ANY:
251       is_valid_type = true;
252       break;
253     case ArgumentType::REF: {
254       DCHECK(ref_);
255       const ArgumentSpec* reference = refs.GetSpec(ref_.value());
256       DCHECK(reference) << ref_.value();
257       is_valid_type = reference->IsCorrectType(value, refs, error);
258       break;
259     }
260     case ArgumentType::CHOICES:
261       for (const auto& choice : choices_) {
262         if (choice->IsCorrectType(value, refs, error)) {
263           is_valid_type = true;
264           break;
265         }
266       }
267       break;
268   }
269 
270   if (!is_valid_type)
271     *error = GetInvalidTypeError(value);
272   return is_valid_type;
273 }
274 
ParseArgument(v8::Local<v8::Context> context,v8::Local<v8::Value> value,const APITypeReferenceMap & refs,std::unique_ptr<base::Value> * out_value,v8::Local<v8::Value> * v8_out_value,std::string * error) const275 bool ArgumentSpec::ParseArgument(v8::Local<v8::Context> context,
276                                  v8::Local<v8::Value> value,
277                                  const APITypeReferenceMap& refs,
278                                  std::unique_ptr<base::Value>* out_value,
279                                  v8::Local<v8::Value>* v8_out_value,
280                                  std::string* error) const {
281   // Note: for top-level arguments (i.e., those passed directly to the function,
282   // as opposed to a property on an object, or the item of an array), we will
283   // have already checked the type. Doing so again should be nearly free, but
284   // if we do find this to be an issue, we could avoid the second call.
285   if (!IsCorrectType(value, refs, error))
286     return false;
287 
288   switch (type_) {
289     case ArgumentType::INTEGER:
290     case ArgumentType::DOUBLE:
291     case ArgumentType::BOOLEAN:
292     case ArgumentType::STRING:
293       return ParseArgumentToFundamental(context, value, out_value, v8_out_value,
294                                         error);
295     case ArgumentType::OBJECT:
296       return ParseArgumentToObject(context, value.As<v8::Object>(), refs,
297                                    out_value, v8_out_value, error);
298     case ArgumentType::LIST:
299       return ParseArgumentToArray(context, value.As<v8::Array>(), refs,
300                                   out_value, v8_out_value, error);
301     case ArgumentType::BINARY:
302       return ParseArgumentToAny(context, value, out_value, v8_out_value, error);
303     case ArgumentType::FUNCTION:
304       if (out_value) {
305         // Certain APIs (contextMenus) have functions as parameters other than
306         // the callback (contextMenus uses it for an onclick listener). Our
307         // generated types have adapted to consider functions "objects" and
308         // serialize them as dictionaries.
309         // TODO(devlin): It'd be awfully nice to get rid of this eccentricity.
310         *out_value = std::make_unique<base::DictionaryValue>();
311       }
312 
313       if (v8_out_value)
314         *v8_out_value = value;
315 
316       return true;
317     case ArgumentType::REF: {
318       DCHECK(ref_);
319       const ArgumentSpec* reference = refs.GetSpec(ref_.value());
320       DCHECK(reference) << ref_.value();
321       return reference->ParseArgument(context, value, refs, out_value,
322                                       v8_out_value, error);
323     }
324     case ArgumentType::CHOICES: {
325       for (const auto& choice : choices_) {
326         if (choice->ParseArgument(context, value, refs, out_value, v8_out_value,
327                                   error)) {
328           return true;
329         }
330       }
331       *error = api_errors::InvalidChoice();
332       return false;
333     }
334     case ArgumentType::ANY:
335       return ParseArgumentToAny(context, value, out_value, v8_out_value, error);
336   }
337 
338   NOTREACHED();
339   return false;
340 }
341 
GetTypeName() const342 const std::string& ArgumentSpec::GetTypeName() const {
343   if (!type_name_.empty())
344     return type_name_;
345 
346   switch (type_) {
347     case ArgumentType::INTEGER:
348       type_name_ = api_errors::kTypeInteger;
349       break;
350     case ArgumentType::DOUBLE:
351       type_name_ = api_errors::kTypeDouble;
352       break;
353     case ArgumentType::BOOLEAN:
354       type_name_ = api_errors::kTypeBoolean;
355       break;
356     case ArgumentType::STRING:
357       type_name_ = api_errors::kTypeString;
358       break;
359     case ArgumentType::OBJECT:
360       type_name_ = instance_of_ ? *instance_of_ : api_errors::kTypeObject;
361       break;
362     case ArgumentType::LIST:
363       type_name_ = api_errors::kTypeList;
364       break;
365     case ArgumentType::BINARY:
366       type_name_ = api_errors::kTypeBinary;
367       break;
368     case ArgumentType::FUNCTION:
369       type_name_ = api_errors::kTypeFunction;
370       break;
371     case ArgumentType::REF:
372       type_name_ = ref_->c_str();
373       break;
374     case ArgumentType::CHOICES: {
375       std::vector<base::StringPiece> choices_strings;
376       choices_strings.reserve(choices_.size());
377       for (const auto& choice : choices_)
378         choices_strings.push_back(choice->GetTypeName());
379       type_name_ = base::StringPrintf(
380           "[%s]", base::JoinString(choices_strings, "|").c_str());
381       break;
382     }
383     case ArgumentType::ANY:
384       type_name_ = api_errors::kTypeAny;
385       break;
386   }
387   DCHECK(!type_name_.empty());
388   return type_name_;
389 }
390 
ParseArgumentToFundamental(v8::Local<v8::Context> context,v8::Local<v8::Value> value,std::unique_ptr<base::Value> * out_value,v8::Local<v8::Value> * v8_out_value,std::string * error) const391 bool ArgumentSpec::ParseArgumentToFundamental(
392     v8::Local<v8::Context> context,
393     v8::Local<v8::Value> value,
394     std::unique_ptr<base::Value>* out_value,
395     v8::Local<v8::Value>* v8_out_value,
396     std::string* error) const {
397   switch (type_) {
398     case ArgumentType::INTEGER: {
399       DCHECK(value->IsNumber());
400       int int_val = 0;
401       if (value->IsInt32()) {
402         int_val = value.As<v8::Int32>()->Value();
403       } else {
404         // See comment in IsCorrectType().
405         DCHECK_EQ(0.0, value.As<v8::Number>()->Value());
406         int_val = 0;
407       }
408       if (!CheckFundamentalBounds(int_val, minimum_, maximum_, error))
409         return false;
410       if (out_value)
411         *out_value = std::make_unique<base::Value>(int_val);
412       if (v8_out_value)
413         *v8_out_value = v8::Integer::New(context->GetIsolate(), int_val);
414       return true;
415     }
416     case ArgumentType::DOUBLE: {
417       DCHECK(value->IsNumber());
418       double double_val = value.As<v8::Number>()->Value();
419       if (!CheckFundamentalBounds(double_val, minimum_, maximum_, error))
420         return false;
421       if (out_value)
422         *out_value = std::make_unique<base::Value>(double_val);
423       if (v8_out_value)
424         *v8_out_value = value;
425       return true;
426     }
427     case ArgumentType::STRING: {
428       DCHECK(value->IsString());
429 
430       v8::Local<v8::String> v8_string = value.As<v8::String>();
431       size_t length = static_cast<size_t>(v8_string->Length());
432       if (min_length_ && length < *min_length_) {
433         *error = api_errors::TooFewStringChars(*min_length_, length);
434         return false;
435       }
436 
437       if (max_length_ && length > *max_length_) {
438         *error = api_errors::TooManyStringChars(*max_length_, length);
439         return false;
440       }
441 
442       if (!enum_values_.empty() || out_value) {
443         std::string str;
444         // We already checked that this is a string, so this should never fail.
445         CHECK(gin::Converter<std::string>::FromV8(context->GetIsolate(), value,
446                                                   &str));
447         if (!enum_values_.empty() && enum_values_.count(str) == 0) {
448           *error = api_errors::InvalidEnumValue(enum_values_);
449           return false;
450         }
451 
452         if (out_value)
453           *out_value = std::make_unique<base::Value>(std::move(str));
454       }
455 
456       if (v8_out_value)
457         *v8_out_value = value;
458 
459       return true;
460     }
461     case ArgumentType::BOOLEAN: {
462       DCHECK(value->IsBoolean());
463       if (out_value) {
464         *out_value =
465             std::make_unique<base::Value>(value.As<v8::Boolean>()->Value());
466       }
467       if (v8_out_value)
468         *v8_out_value = value;
469 
470       return true;
471     }
472     default:
473       NOTREACHED();
474   }
475   return false;
476 }
477 
ParseArgumentToObject(v8::Local<v8::Context> context,v8::Local<v8::Object> object,const APITypeReferenceMap & refs,std::unique_ptr<base::Value> * out_value,v8::Local<v8::Value> * v8_out_value,std::string * error) const478 bool ArgumentSpec::ParseArgumentToObject(
479     v8::Local<v8::Context> context,
480     v8::Local<v8::Object> object,
481     const APITypeReferenceMap& refs,
482     std::unique_ptr<base::Value>* out_value,
483     v8::Local<v8::Value>* v8_out_value,
484     std::string* error) const {
485   DCHECK_EQ(ArgumentType::OBJECT, type_);
486   std::unique_ptr<base::DictionaryValue> result;
487   // Only construct the result if we have an |out_value| to populate.
488   if (out_value)
489     result = std::make_unique<base::DictionaryValue>();
490 
491   // We don't convert to a new object in two cases:
492   // - If instanceof is specified, we don't want to create a new data object,
493   //   because then the object wouldn't be an instanceof the specified type.
494   //   e.g., if a function is expecting a RegExp, we need to make sure the
495   //   value passed in is, indeed, a RegExp, which won't be the case if we just
496   //   copy the properties to a new object.
497   // - Some methods use additional_properties_ in order to allow for arbitrary
498   //   types to be passed in (e.g., test.assertThrows allows a "self" property
499   //   to be provided). Similar to above, if we just copy the property values,
500   //   it may change the type of the object and break expectations.
501   // TODO(devlin): The latter case could be handled by specifying a different
502   // tag to indicate that we don't want to convert. This would be much clearer,
503   // and allow us to handle the other additional_properties_ cases. But first,
504   // we need to track down all the instances that use it.
505   bool convert_to_v8 = v8_out_value && !additional_properties_ && !instance_of_;
506   gin::DataObjectBuilder v8_result(context->GetIsolate());
507 
508   v8::Local<v8::Array> own_property_names;
509   if (!object->GetOwnPropertyNames(context).ToLocal(&own_property_names)) {
510     *error = api_errors::ScriptThrewError();
511     return false;
512   }
513 
514   // Track all properties we see from |properties_| to check if any are missing.
515   // Use ArgumentSpec* instead of std::string for comparison + copy efficiency.
516   std::set<const ArgumentSpec*> seen_properties;
517   uint32_t length = own_property_names->Length();
518   std::string property_error;
519   for (uint32_t i = 0; i < length; ++i) {
520     v8::Local<v8::Value> key;
521     if (!own_property_names->Get(context, i).ToLocal(&key)) {
522       *error = api_errors::ScriptThrewError();
523       return false;
524     }
525     // In JS, all keys are strings or numbers (or symbols, but those are
526     // excluded by GetOwnPropertyNames()). If you try to set anything else
527     // (e.g. an object), it is converted to a string.
528     DCHECK(key->IsString() || key->IsNumber());
529     v8::String::Utf8Value utf8_key(context->GetIsolate(), key);
530 
531     ArgumentSpec* property_spec = nullptr;
532     auto iter = properties_.find(*utf8_key);
533     bool allow_unserializable = false;
534     if (iter != properties_.end()) {
535       property_spec = iter->second.get();
536       seen_properties.insert(property_spec);
537     } else if (additional_properties_) {
538       property_spec = additional_properties_.get();
539       // additionalProperties: {type: any} is often used to allow anything
540       // through, including things that would normally break serialization like
541       // functions, or even NaN. If the additional properties are of
542       // ArgumentType::ANY, allow anything, even if it doesn't serialize.
543       allow_unserializable = property_spec->type_ == ArgumentType::ANY;
544     } else {
545       *error = api_errors::UnexpectedProperty(*utf8_key);
546       return false;
547     }
548 
549     v8::Local<v8::Value> prop_value;
550     // Fun: It's possible that a previous getter has removed the property from
551     // the object. This isn't that big of a deal, since it would only manifest
552     // in the case of some reasonably-crazy script objects, and it's probably
553     // not worth optimizing for the uncommon case to the detriment of the
554     // common (and either should be totally safe). We can always add a
555     // HasOwnProperty() check here in the future, if we desire.
556     // See also comment in ParseArgumentToArray() about passing in custom
557     // crazy values here.
558     if (!object->Get(context, key).ToLocal(&prop_value)) {
559       *error = api_errors::ScriptThrewError();
560       return false;
561     }
562 
563     // Note: We don't serialize undefined, and only serialize null if it's part
564     // of the spec.
565     // TODO(devlin): This matches current behavior, but it is correct? And
566     // we treat undefined and null the same?
567     if (prop_value->IsUndefined() || prop_value->IsNull()) {
568       if (!property_spec->optional_) {
569         *error = api_errors::MissingRequiredProperty(*utf8_key);
570         return false;
571       }
572       if (preserve_null_ && prop_value->IsNull()) {
573         if (result) {
574           result->SetWithoutPathExpansion(*utf8_key,
575                                           std::make_unique<base::Value>());
576         }
577         if (convert_to_v8)
578           v8_result.Set(*utf8_key, prop_value);
579       }
580       continue;
581     }
582 
583     std::unique_ptr<base::Value> property;
584     v8::Local<v8::Value> v8_property;
585     if (!property_spec->ParseArgument(
586             context, prop_value, refs, out_value ? &property : nullptr,
587             convert_to_v8 ? &v8_property : nullptr, &property_error)) {
588       if (allow_unserializable)
589         continue;
590       *error = api_errors::PropertyError(*utf8_key, property_error);
591       return false;
592     }
593     if (out_value)
594       result->SetWithoutPathExpansion(*utf8_key, std::move(property));
595     if (convert_to_v8)
596       v8_result.Set(*utf8_key, v8_property);
597   }
598 
599   for (const auto& pair : properties_) {
600     const ArgumentSpec* spec = pair.second.get();
601     if (!spec->optional_ && seen_properties.count(spec) == 0) {
602       *error = api_errors::MissingRequiredProperty(pair.first.c_str());
603       return false;
604     }
605   }
606 
607   if (instance_of_) {
608     // Check for the instance somewhere in the object's prototype chain.
609     // NOTE: This only checks that something in the prototype chain was
610     // constructed with the same name as the desired instance, but doesn't
611     // validate that it's the same constructor as the expected one. For
612     // instance, if we expect isInstanceOf == 'Date', script could pass in
613     // (function() {
614     //   function Date() {}
615     //   return new Date();
616     // })()
617     // Since the object contains 'Date' in its prototype chain, this check
618     // succeeds, even though the object is not of built-in type Date.
619     // Since this isn't (or at least shouldn't be) a security check, this is
620     // okay.
621     bool found = false;
622     v8::Local<v8::Value> next_check = object;
623     do {
624       v8::Local<v8::Object> current = next_check.As<v8::Object>();
625       v8::String::Utf8Value constructor(context->GetIsolate(),
626                                         current->GetConstructorName());
627       if (*instance_of_ ==
628           base::StringPiece(*constructor, constructor.length())) {
629         found = true;
630         break;
631       }
632       next_check = current->GetPrototype();
633     } while (next_check->IsObject());
634 
635     if (!found) {
636       *error = api_errors::NotAnInstance(instance_of_->c_str());
637       return false;
638     }
639   }
640 
641   if (out_value)
642     *out_value = std::move(result);
643 
644   if (v8_out_value) {
645     if (convert_to_v8) {
646       v8::Local<v8::Object> converted = v8_result.Build();
647       // We set the object's prototype to Null() so that handlers avoid
648       // triggering any tricky getters or setters on Object.prototype.
649       CHECK(converted->SetPrototype(context, v8::Null(context->GetIsolate()))
650                 .ToChecked());
651       *v8_out_value = converted;
652     } else {
653       *v8_out_value = object;
654     }
655   }
656 
657   return true;
658 }
659 
ParseArgumentToArray(v8::Local<v8::Context> context,v8::Local<v8::Array> value,const APITypeReferenceMap & refs,std::unique_ptr<base::Value> * out_value,v8::Local<v8::Value> * v8_out_value,std::string * error) const660 bool ArgumentSpec::ParseArgumentToArray(v8::Local<v8::Context> context,
661                                         v8::Local<v8::Array> value,
662                                         const APITypeReferenceMap& refs,
663                                         std::unique_ptr<base::Value>* out_value,
664                                         v8::Local<v8::Value>* v8_out_value,
665                                         std::string* error) const {
666   DCHECK_EQ(ArgumentType::LIST, type_);
667 
668   uint32_t length = value->Length();
669   if (min_length_ && length < *min_length_) {
670     *error = api_errors::TooFewArrayItems(*min_length_, length);
671     return false;
672   }
673 
674   if (max_length_ && length > *max_length_) {
675     *error = api_errors::TooManyArrayItems(*max_length_, length);
676     return false;
677   }
678 
679   std::unique_ptr<base::ListValue> result;
680   // Only construct the result if we have an |out_value| to populate.
681   if (out_value)
682     result = std::make_unique<base::ListValue>();
683   v8::Local<v8::Array> v8_result;
684   if (v8_out_value)
685     v8_result = v8::Array::New(context->GetIsolate(), length);
686 
687   std::string item_error;
688   for (uint32_t i = 0; i < length; ++i) {
689     v8::MaybeLocal<v8::Value> maybe_subvalue = value->Get(context, i);
690     v8::Local<v8::Value> subvalue;
691     // Note: This can fail in the case of a developer passing in the following:
692     // var a = [];
693     // Object.defineProperty(a, 0, { get: () => { throw new Error('foo'); } });
694     // Currently, this will cause the developer-specified error ('foo') to be
695     // thrown.
696     // TODO(devlin): This is probably fine, but it's worth contemplating
697     // catching the error and throwing our own.
698     if (!maybe_subvalue.ToLocal(&subvalue))
699       return false;
700     std::unique_ptr<base::Value> item;
701     v8::Local<v8::Value> v8_item;
702     if (!list_element_type_->ParseArgument(
703             context, subvalue, refs, out_value ? &item : nullptr,
704             v8_out_value ? &v8_item : nullptr, &item_error)) {
705       *error = api_errors::IndexError(i, item_error);
706       return false;
707     }
708     if (out_value)
709       result->Append(std::move(item));
710     if (v8_out_value) {
711       // This should never fail, since it's a newly-created array with
712       // CreateDataProperty().
713       CHECK(v8_result->CreateDataProperty(context, i, v8_item).ToChecked());
714     }
715   }
716 
717   if (out_value)
718     *out_value = std::move(result);
719   if (v8_out_value)
720     *v8_out_value = v8_result;
721 
722   return true;
723 }
724 
ParseArgumentToAny(v8::Local<v8::Context> context,v8::Local<v8::Value> value,std::unique_ptr<base::Value> * out_value,v8::Local<v8::Value> * v8_out_value,std::string * error) const725 bool ArgumentSpec::ParseArgumentToAny(v8::Local<v8::Context> context,
726                                       v8::Local<v8::Value> value,
727                                       std::unique_ptr<base::Value>* out_value,
728                                       v8::Local<v8::Value>* v8_out_value,
729                                       std::string* error) const {
730   DCHECK(type_ == ArgumentType::ANY || type_ == ArgumentType::BINARY);
731   if (out_value) {
732     std::unique_ptr<content::V8ValueConverter> converter =
733         content::V8ValueConverter::Create();
734     converter->SetStripNullFromObjects(!preserve_null_);
735     converter->SetConvertNegativeZeroToInt(true);
736     // Note: don't allow functions. Functions are handled either by the specific
737     // type (ArgumentType::FUNCTION) or by allowing arbitrary optional
738     // arguments, which allows unserializable values.
739     // TODO(devlin): Is this correct? Or do we rely on an 'any' type of function
740     // being serialized in an odd-ball API?
741     std::unique_ptr<base::Value> converted =
742         converter->FromV8Value(value, context);
743     if (!converted) {
744       *error = api_errors::UnserializableValue();
745       return false;
746     }
747     if (type_ == ArgumentType::BINARY)
748       DCHECK_EQ(base::Value::Type::BINARY, converted->type());
749     *out_value = std::move(converted);
750   }
751   if (v8_out_value)
752     *v8_out_value = value;
753 
754   return true;
755 }
756 
GetInvalidTypeError(v8::Local<v8::Value> value) const757 std::string ArgumentSpec::GetInvalidTypeError(
758     v8::Local<v8::Value> value) const {
759   return api_errors::InvalidType(GetTypeName().c_str(),
760                                  GetV8ValueTypeString(value));
761 }
762 
763 }  // namespace extensions
764