| 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 |