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