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