argument_spec.cc 29 KB

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