| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "components/json_schema/json_schema_validator.h" | 5 #include "components/json_schema/json_schema_validator.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <cfloat> | 10 #include <cfloat> |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 72 return value; | 72 return value; |
| 73 } | 73 } |
| 74 | 74 |
| 75 bool IsValidSchema(const base::DictionaryValue* dict, | 75 bool IsValidSchema(const base::DictionaryValue* dict, |
| 76 int options, | 76 int options, |
| 77 std::string* error) { | 77 std::string* error) { |
| 78 // This array must be sorted, so that std::lower_bound can perform a | 78 // This array must be sorted, so that std::lower_bound can perform a |
| 79 // binary search. | 79 // binary search. |
| 80 static const ExpectedType kExpectedTypes[] = { | 80 static const ExpectedType kExpectedTypes[] = { |
| 81 // Note: kRef == "$ref", kSchema == "$schema" | 81 // Note: kRef == "$ref", kSchema == "$schema" |
| 82 { schema::kRef, base::Value::TYPE_STRING }, | 82 { schema::kRef, base::Value::Type::STRING }, |
| 83 { schema::kSchema, base::Value::TYPE_STRING }, | 83 { schema::kSchema, base::Value::Type::STRING }, |
| 84 | 84 |
| 85 { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY }, | 85 { schema::kAdditionalProperties, base::Value::Type::DICTIONARY }, |
| 86 { schema::kChoices, base::Value::TYPE_LIST }, | 86 { schema::kChoices, base::Value::Type::LIST }, |
| 87 { schema::kDescription, base::Value::TYPE_STRING }, | 87 { schema::kDescription, base::Value::Type::STRING }, |
| 88 { schema::kEnum, base::Value::TYPE_LIST }, | 88 { schema::kEnum, base::Value::Type::LIST }, |
| 89 { schema::kId, base::Value::TYPE_STRING }, | 89 { schema::kId, base::Value::Type::STRING }, |
| 90 { schema::kMaxItems, base::Value::TYPE_INTEGER }, | 90 { schema::kMaxItems, base::Value::Type::INTEGER }, |
| 91 { schema::kMaxLength, base::Value::TYPE_INTEGER }, | 91 { schema::kMaxLength, base::Value::Type::INTEGER }, |
| 92 { schema::kMaximum, base::Value::TYPE_DOUBLE }, | 92 { schema::kMaximum, base::Value::Type::DOUBLE }, |
| 93 { schema::kMinItems, base::Value::TYPE_INTEGER }, | 93 { schema::kMinItems, base::Value::Type::INTEGER }, |
| 94 { schema::kMinLength, base::Value::TYPE_INTEGER }, | 94 { schema::kMinLength, base::Value::Type::INTEGER }, |
| 95 { schema::kMinimum, base::Value::TYPE_DOUBLE }, | 95 { schema::kMinimum, base::Value::Type::DOUBLE }, |
| 96 { schema::kOptional, base::Value::TYPE_BOOLEAN }, | 96 { schema::kOptional, base::Value::Type::BOOLEAN }, |
| 97 { schema::kPattern, base::Value::TYPE_STRING }, | 97 { schema::kPattern, base::Value::Type::STRING }, |
| 98 { schema::kPatternProperties, base::Value::TYPE_DICTIONARY }, | 98 { schema::kPatternProperties, base::Value::Type::DICTIONARY }, |
| 99 { schema::kProperties, base::Value::TYPE_DICTIONARY }, | 99 { schema::kProperties, base::Value::Type::DICTIONARY }, |
| 100 { schema::kTitle, base::Value::TYPE_STRING }, | 100 { schema::kTitle, base::Value::Type::STRING }, |
| 101 }; | 101 }; |
| 102 | 102 |
| 103 bool has_type_or_ref = false; | 103 bool has_type_or_ref = false; |
| 104 const base::ListValue* list_value = NULL; | 104 const base::ListValue* list_value = NULL; |
| 105 const base::DictionaryValue* dictionary_value = NULL; | 105 const base::DictionaryValue* dictionary_value = NULL; |
| 106 std::string string_value; | 106 std::string string_value; |
| 107 | 107 |
| 108 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { | 108 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { |
| 109 // Validate the "type" attribute, which may be a string or a list. | 109 // Validate the "type" attribute, which may be a string or a list. |
| 110 if (it.key() == schema::kType) { | 110 if (it.key() == schema::kType) { |
| 111 switch (it.value().GetType()) { | 111 switch (it.value().GetType()) { |
| 112 case base::Value::TYPE_STRING: | 112 case base::Value::Type::STRING: |
| 113 it.value().GetAsString(&string_value); | 113 it.value().GetAsString(&string_value); |
| 114 if (!IsValidType(string_value)) { | 114 if (!IsValidType(string_value)) { |
| 115 *error = "Invalid value for type attribute"; | 115 *error = "Invalid value for type attribute"; |
| 116 return false; | 116 return false; |
| 117 } | 117 } |
| 118 break; | 118 break; |
| 119 case base::Value::TYPE_LIST: | 119 case base::Value::Type::LIST: |
| 120 it.value().GetAsList(&list_value); | 120 it.value().GetAsList(&list_value); |
| 121 for (size_t i = 0; i < list_value->GetSize(); ++i) { | 121 for (size_t i = 0; i < list_value->GetSize(); ++i) { |
| 122 if (!list_value->GetString(i, &string_value) || | 122 if (!list_value->GetString(i, &string_value) || |
| 123 !IsValidType(string_value)) { | 123 !IsValidType(string_value)) { |
| 124 *error = "Invalid value for type attribute"; | 124 *error = "Invalid value for type attribute"; |
| 125 return false; | 125 return false; |
| 126 } | 126 } |
| 127 } | 127 } |
| 128 break; | 128 break; |
| 129 default: | 129 default: |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 167 kExpectedTypes, end, it.key(), CompareToString); | 167 kExpectedTypes, end, it.key(), CompareToString); |
| 168 if (entry == end || entry->key != it.key()) { | 168 if (entry == end || entry->key != it.key()) { |
| 169 if (options & JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES) | 169 if (options & JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES) |
| 170 continue; | 170 continue; |
| 171 *error = base::StringPrintf("Invalid attribute %s", it.key().c_str()); | 171 *error = base::StringPrintf("Invalid attribute %s", it.key().c_str()); |
| 172 return false; | 172 return false; |
| 173 } | 173 } |
| 174 | 174 |
| 175 // Integer can be converted to double. | 175 // Integer can be converted to double. |
| 176 if (!(it.value().IsType(entry->type) || | 176 if (!(it.value().IsType(entry->type) || |
| 177 (it.value().IsType(base::Value::TYPE_INTEGER) && | 177 (it.value().IsType(base::Value::Type::INTEGER) && |
| 178 entry->type == base::Value::TYPE_DOUBLE))) { | 178 entry->type == base::Value::Type::DOUBLE))) { |
| 179 *error = base::StringPrintf("Invalid value for %s attribute", | 179 *error = base::StringPrintf("Invalid value for %s attribute", |
| 180 it.key().c_str()); | 180 it.key().c_str()); |
| 181 return false; | 181 return false; |
| 182 } | 182 } |
| 183 | 183 |
| 184 // base::Value::TYPE_INTEGER attributes must be >= 0. | 184 // base::Value::Type::INTEGER attributes must be >= 0. |
| 185 // This applies to "minItems", "maxItems", "minLength" and "maxLength". | 185 // This applies to "minItems", "maxItems", "minLength" and "maxLength". |
| 186 if (it.value().IsType(base::Value::TYPE_INTEGER)) { | 186 if (it.value().IsType(base::Value::Type::INTEGER)) { |
| 187 int integer_value; | 187 int integer_value; |
| 188 it.value().GetAsInteger(&integer_value); | 188 it.value().GetAsInteger(&integer_value); |
| 189 if (integer_value < 0) { | 189 if (integer_value < 0) { |
| 190 *error = base::StringPrintf("Value of %s must be >= 0, got %d", | 190 *error = base::StringPrintf("Value of %s must be >= 0, got %d", |
| 191 it.key().c_str(), integer_value); | 191 it.key().c_str(), integer_value); |
| 192 return false; | 192 return false; |
| 193 } | 193 } |
| 194 } | 194 } |
| 195 | 195 |
| 196 // Validate the "properties" attribute. Each entry maps a key to a schema. | 196 // Validate the "properties" attribute. Each entry maps a key to a schema. |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 244 const base::Value* value = NULL; | 244 const base::Value* value = NULL; |
| 245 list_value->Get(i, &value); | 245 list_value->Get(i, &value); |
| 246 // Sometimes the enum declaration is a dictionary with the enum value | 246 // Sometimes the enum declaration is a dictionary with the enum value |
| 247 // under "name". | 247 // under "name". |
| 248 value = ExtractNameFromDictionary(value); | 248 value = ExtractNameFromDictionary(value); |
| 249 if (!value) { | 249 if (!value) { |
| 250 *error = "Invalid value in enum attribute"; | 250 *error = "Invalid value in enum attribute"; |
| 251 return false; | 251 return false; |
| 252 } | 252 } |
| 253 switch (value->GetType()) { | 253 switch (value->GetType()) { |
| 254 case base::Value::TYPE_NULL: | 254 case base::Value::Type::NONE: |
| 255 case base::Value::TYPE_BOOLEAN: | 255 case base::Value::Type::BOOLEAN: |
| 256 case base::Value::TYPE_INTEGER: | 256 case base::Value::Type::INTEGER: |
| 257 case base::Value::TYPE_DOUBLE: | 257 case base::Value::Type::DOUBLE: |
| 258 case base::Value::TYPE_STRING: | 258 case base::Value::Type::STRING: |
| 259 break; | 259 break; |
| 260 default: | 260 default: |
| 261 *error = "Invalid value in enum attribute"; | 261 *error = "Invalid value in enum attribute"; |
| 262 return false; | 262 return false; |
| 263 } | 263 } |
| 264 } | 264 } |
| 265 } | 265 } |
| 266 | 266 |
| 267 // Validate the schemas contained in a "choices" attribute. | 267 // Validate the schemas contained in a "choices" attribute. |
| 268 if (it.key() == schema::kChoices) { | 268 if (it.key() == schema::kChoices) { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 "Expected '*' but got '*'."; | 337 "Expected '*' but got '*'."; |
| 338 const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] = | 338 const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] = |
| 339 "Expected 'integer' but got 'number', consider using Math.round()."; | 339 "Expected 'integer' but got 'number', consider using Math.round()."; |
| 340 const char JSONSchemaValidator::kInvalidRegex[] = | 340 const char JSONSchemaValidator::kInvalidRegex[] = |
| 341 "Regular expression /*/ is invalid: *"; | 341 "Regular expression /*/ is invalid: *"; |
| 342 | 342 |
| 343 | 343 |
| 344 // static | 344 // static |
| 345 std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) { | 345 std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) { |
| 346 switch (value->GetType()) { | 346 switch (value->GetType()) { |
| 347 case base::Value::TYPE_NULL: | 347 case base::Value::Type::NONE: |
| 348 return schema::kNull; | 348 return schema::kNull; |
| 349 case base::Value::TYPE_BOOLEAN: | 349 case base::Value::Type::BOOLEAN: |
| 350 return schema::kBoolean; | 350 return schema::kBoolean; |
| 351 case base::Value::TYPE_INTEGER: | 351 case base::Value::Type::INTEGER: |
| 352 return schema::kInteger; | 352 return schema::kInteger; |
| 353 case base::Value::TYPE_DOUBLE: { | 353 case base::Value::Type::DOUBLE: { |
| 354 double double_value = 0; | 354 double double_value = 0; |
| 355 value->GetAsDouble(&double_value); | 355 value->GetAsDouble(&double_value); |
| 356 if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) && | 356 if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) && |
| 357 double_value == floor(double_value)) { | 357 double_value == floor(double_value)) { |
| 358 return schema::kInteger; | 358 return schema::kInteger; |
| 359 } else { | 359 } else { |
| 360 return schema::kNumber; | 360 return schema::kNumber; |
| 361 } | 361 } |
| 362 } | 362 } |
| 363 case base::Value::TYPE_STRING: | 363 case base::Value::Type::STRING: |
| 364 return schema::kString; | 364 return schema::kString; |
| 365 case base::Value::TYPE_DICTIONARY: | 365 case base::Value::Type::DICTIONARY: |
| 366 return schema::kObject; | 366 return schema::kObject; |
| 367 case base::Value::TYPE_LIST: | 367 case base::Value::Type::LIST: |
| 368 return schema::kArray; | 368 return schema::kArray; |
| 369 default: | 369 default: |
| 370 NOTREACHED() << "Unexpected value type: " << value->GetType(); | 370 NOTREACHED() << "Unexpected value type: " << value->GetType(); |
| 371 return std::string(); | 371 return std::string(); |
| 372 } | 372 } |
| 373 } | 373 } |
| 374 | 374 |
| 375 // static | 375 // static |
| 376 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, | 376 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, |
| 377 const std::string& s1) { | 377 const std::string& s1) { |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 500 | 500 |
| 501 // These casts are safe because of checks in ValidateType(). | 501 // These casts are safe because of checks in ValidateType(). |
| 502 if (type == schema::kObject) { | 502 if (type == schema::kObject) { |
| 503 ValidateObject(static_cast<const base::DictionaryValue*>(instance), | 503 ValidateObject(static_cast<const base::DictionaryValue*>(instance), |
| 504 schema, | 504 schema, |
| 505 path); | 505 path); |
| 506 } else if (type == schema::kArray) { | 506 } else if (type == schema::kArray) { |
| 507 ValidateArray(static_cast<const base::ListValue*>(instance), | 507 ValidateArray(static_cast<const base::ListValue*>(instance), |
| 508 schema, path); | 508 schema, path); |
| 509 } else if (type == schema::kString) { | 509 } else if (type == schema::kString) { |
| 510 // Intentionally NOT downcasting to StringValue*. TYPE_STRING only implies | 510 // Intentionally NOT downcasting to StringValue*. Type::STRING only |
| 511 // GetAsString() can safely be carried out, not that it's a StringValue. | 511 // implies GetAsString() can safely be carried out, not that it's a |
| 512 // StringValue. |
| 512 ValidateString(instance, schema, path); | 513 ValidateString(instance, schema, path); |
| 513 } else if (type == schema::kNumber || type == schema::kInteger) { | 514 } else if (type == schema::kNumber || type == schema::kInteger) { |
| 514 ValidateNumber(instance, schema, path); | 515 ValidateNumber(instance, schema, path); |
| 515 } else if (type != schema::kBoolean && type != schema::kNull) { | 516 } else if (type != schema::kBoolean && type != schema::kNull) { |
| 516 NOTREACHED() << "Unexpected type: " << type; | 517 NOTREACHED() << "Unexpected type: " << type; |
| 517 } | 518 } |
| 518 } | 519 } |
| 519 } | 520 } |
| 520 | 521 |
| 521 void JSONSchemaValidator::ValidateChoices(const base::Value* instance, | 522 void JSONSchemaValidator::ValidateChoices(const base::Value* instance, |
| (...skipping 25 matching lines...) Expand all Loading... |
| 547 for (size_t i = 0; i < choices->GetSize(); ++i) { | 548 for (size_t i = 0; i < choices->GetSize(); ++i) { |
| 548 const base::Value* choice = NULL; | 549 const base::Value* choice = NULL; |
| 549 CHECK(choices->Get(i, &choice)); | 550 CHECK(choices->Get(i, &choice)); |
| 550 // Sometimes the enum declaration is a dictionary with the enum value under | 551 // Sometimes the enum declaration is a dictionary with the enum value under |
| 551 // "name". | 552 // "name". |
| 552 choice = ExtractNameFromDictionary(choice); | 553 choice = ExtractNameFromDictionary(choice); |
| 553 if (!choice) { | 554 if (!choice) { |
| 554 NOTREACHED(); | 555 NOTREACHED(); |
| 555 } | 556 } |
| 556 switch (choice->GetType()) { | 557 switch (choice->GetType()) { |
| 557 case base::Value::TYPE_NULL: | 558 case base::Value::Type::NONE: |
| 558 case base::Value::TYPE_BOOLEAN: | 559 case base::Value::Type::BOOLEAN: |
| 559 case base::Value::TYPE_STRING: | 560 case base::Value::Type::STRING: |
| 560 if (instance->Equals(choice)) | 561 if (instance->Equals(choice)) |
| 561 return; | 562 return; |
| 562 break; | 563 break; |
| 563 | 564 |
| 564 case base::Value::TYPE_INTEGER: | 565 case base::Value::Type::INTEGER: |
| 565 case base::Value::TYPE_DOUBLE: | 566 case base::Value::Type::DOUBLE: |
| 566 if (instance->IsType(base::Value::TYPE_INTEGER) || | 567 if (instance->IsType(base::Value::Type::INTEGER) || |
| 567 instance->IsType(base::Value::TYPE_DOUBLE)) { | 568 instance->IsType(base::Value::Type::DOUBLE)) { |
| 568 if (GetNumberValue(choice) == GetNumberValue(instance)) | 569 if (GetNumberValue(choice) == GetNumberValue(instance)) |
| 569 return; | 570 return; |
| 570 } | 571 } |
| 571 break; | 572 break; |
| 572 | 573 |
| 573 default: | 574 default: |
| 574 NOTREACHED() << "Unexpected type in enum: " << choice->GetType(); | 575 NOTREACHED() << "Unexpected type in enum: " << choice->GetType(); |
| 575 } | 576 } |
| 576 } | 577 } |
| 577 | 578 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 708 schema->GetList(schema::kItems, &tuple_type); | 709 schema->GetList(schema::kItems, &tuple_type); |
| 709 size_t tuple_size = tuple_type ? tuple_type->GetSize() : 0; | 710 size_t tuple_size = tuple_type ? tuple_type->GetSize() : 0; |
| 710 if (tuple_type) { | 711 if (tuple_type) { |
| 711 for (size_t i = 0; i < tuple_size; ++i) { | 712 for (size_t i = 0; i < tuple_size; ++i) { |
| 712 std::string i_str = base::Uint64ToString(i); | 713 std::string i_str = base::Uint64ToString(i); |
| 713 std::string item_path = path.empty() ? i_str : (path + "." + i_str); | 714 std::string item_path = path.empty() ? i_str : (path + "." + i_str); |
| 714 const base::DictionaryValue* item_schema = NULL; | 715 const base::DictionaryValue* item_schema = NULL; |
| 715 CHECK(tuple_type->GetDictionary(i, &item_schema)); | 716 CHECK(tuple_type->GetDictionary(i, &item_schema)); |
| 716 const base::Value* item_value = NULL; | 717 const base::Value* item_value = NULL; |
| 717 instance->Get(i, &item_value); | 718 instance->Get(i, &item_value); |
| 718 if (item_value && item_value->GetType() != base::Value::TYPE_NULL) { | 719 if (item_value && item_value->GetType() != base::Value::Type::NONE) { |
| 719 Validate(item_value, item_schema, item_path); | 720 Validate(item_value, item_schema, item_path); |
| 720 } else { | 721 } else { |
| 721 bool is_optional = false; | 722 bool is_optional = false; |
| 722 item_schema->GetBoolean(schema::kOptional, &is_optional); | 723 item_schema->GetBoolean(schema::kOptional, &is_optional); |
| 723 if (!is_optional) { | 724 if (!is_optional) { |
| 724 errors_.push_back(Error(item_path, kArrayItemRequired)); | 725 errors_.push_back(Error(item_path, kArrayItemRequired)); |
| 725 return; | 726 return; |
| 726 } | 727 } |
| 727 } | 728 } |
| 728 } | 729 } |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 840 | 841 |
| 841 if (*additional_properties_schema) { | 842 if (*additional_properties_schema) { |
| 842 std::string additional_properties_type(schema::kAny); | 843 std::string additional_properties_type(schema::kAny); |
| 843 CHECK((*additional_properties_schema)->GetString( | 844 CHECK((*additional_properties_schema)->GetString( |
| 844 schema::kType, &additional_properties_type)); | 845 schema::kType, &additional_properties_type)); |
| 845 return additional_properties_type == schema::kAny; | 846 return additional_properties_type == schema::kAny; |
| 846 } else { | 847 } else { |
| 847 return default_allow_additional_properties_; | 848 return default_allow_additional_properties_; |
| 848 } | 849 } |
| 849 } | 850 } |
| OLD | NEW |