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