Chromium Code Reviews| 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 |