Home | History | Annotate | Download | only in json_schema
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/common/json_schema/json_schema_validator.h"
      6 
      7 #include <algorithm>
      8 #include <cfloat>
      9 #include <cmath>
     10 
     11 #include "base/json/json_reader.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "base/values.h"
     16 #include "chrome/common/json_schema/json_schema_constants.h"
     17 #include "ui/base/l10n/l10n_util.h"
     18 
     19 namespace schema = json_schema_constants;
     20 
     21 namespace {
     22 
     23 double GetNumberValue(const base::Value* value) {
     24   double result = 0;
     25   CHECK(value->GetAsDouble(&result))
     26       << "Unexpected value type: " << value->GetType();
     27   return result;
     28 }
     29 
     30 bool IsValidType(const std::string& type) {
     31   static const char* kValidTypes[] = {
     32     schema::kAny,
     33     schema::kArray,
     34     schema::kBoolean,
     35     schema::kInteger,
     36     schema::kNull,
     37     schema::kNumber,
     38     schema::kObject,
     39     schema::kString,
     40   };
     41   const char** end = kValidTypes + arraysize(kValidTypes);
     42   return std::find(kValidTypes, end, type) != end;
     43 }
     44 
     45 // Maps a schema attribute name to its expected type.
     46 struct ExpectedType {
     47   const char* key;
     48   base::Value::Type type;
     49 };
     50 
     51 // Helper for std::lower_bound.
     52 bool CompareToString(const ExpectedType& entry, const std::string& key) {
     53   return entry.key < key;
     54 }
     55 
     56 bool IsValidSchema(const base::DictionaryValue* dict, std::string* error) {
     57   // This array must be sorted, so that std::lower_bound can perform a
     58   // binary search.
     59   static const ExpectedType kExpectedTypes[] = {
     60     // Note: kRef == "$ref", kSchema == "$schema"
     61     { schema::kRef,                     base::Value::TYPE_STRING      },
     62     { schema::kSchema,                  base::Value::TYPE_STRING      },
     63 
     64     { schema::kAdditionalProperties,    base::Value::TYPE_DICTIONARY  },
     65     { schema::kChoices,                 base::Value::TYPE_LIST        },
     66     { schema::kDescription,             base::Value::TYPE_STRING      },
     67     { schema::kEnum,                    base::Value::TYPE_LIST        },
     68     { schema::kId,                      base::Value::TYPE_STRING      },
     69     { schema::kMaxItems,                base::Value::TYPE_INTEGER     },
     70     { schema::kMaxLength,               base::Value::TYPE_INTEGER     },
     71     { schema::kMaximum,                 base::Value::TYPE_DOUBLE      },
     72     { schema::kMinItems,                base::Value::TYPE_INTEGER     },
     73     { schema::kMinLength,               base::Value::TYPE_INTEGER     },
     74     { schema::kMinimum,                 base::Value::TYPE_DOUBLE      },
     75     { schema::kOptional,                base::Value::TYPE_BOOLEAN     },
     76     { schema::kProperties,              base::Value::TYPE_DICTIONARY  },
     77     { schema::kTitle,                   base::Value::TYPE_STRING      },
     78   };
     79 
     80   bool has_type = false;
     81   const base::ListValue* list_value = NULL;
     82   const base::DictionaryValue* dictionary_value = NULL;
     83   std::string string_value;
     84 
     85   for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
     86     // Validate the "type" attribute, which may be a string or a list.
     87     if (it.key() == schema::kType) {
     88       switch (it.value().GetType()) {
     89         case base::Value::TYPE_STRING:
     90           it.value().GetAsString(&string_value);
     91           if (!IsValidType(string_value)) {
     92             *error = "Invalid value for type attribute";
     93             return false;
     94           }
     95           break;
     96         case base::Value::TYPE_LIST:
     97           it.value().GetAsList(&list_value);
     98           for (size_t i = 0; i < list_value->GetSize(); ++i) {
     99             if (!list_value->GetString(i, &string_value) ||
    100                 !IsValidType(string_value)) {
    101               *error = "Invalid value for type attribute";
    102               return false;
    103             }
    104           }
    105           break;
    106         default:
    107           *error = "Invalid value for type attribute";
    108           return false;
    109       }
    110       has_type = true;
    111       continue;
    112     }
    113 
    114     // Validate the "items" attribute, which is a schema or a list of schemas.
    115     if (it.key() == schema::kItems) {
    116       if (it.value().GetAsDictionary(&dictionary_value)) {
    117         if (!IsValidSchema(dictionary_value, error)) {
    118           DCHECK(!error->empty());
    119           return false;
    120         }
    121       } else if (it.value().GetAsList(&list_value)) {
    122         for (size_t i = 0; i < list_value->GetSize(); ++i) {
    123           if (!list_value->GetDictionary(i, &dictionary_value)) {
    124             *error = base::StringPrintf(
    125                 "Invalid entry in items attribute at index %d",
    126                 static_cast<int>(i));
    127             return false;
    128           }
    129           if (!IsValidSchema(dictionary_value, error)) {
    130             DCHECK(!error->empty());
    131             return false;
    132           }
    133         }
    134       } else {
    135         *error = "Invalid value for items attribute";
    136         return false;
    137       }
    138       continue;
    139     }
    140 
    141     // All the other attributes have a single valid type.
    142     const ExpectedType* end = kExpectedTypes + arraysize(kExpectedTypes);
    143     const ExpectedType* entry = std::lower_bound(
    144         kExpectedTypes, end, it.key(), CompareToString);
    145     if (entry == end || entry->key != it.key()) {
    146       *error = base::StringPrintf("Invalid attribute %s", it.key().c_str());
    147       return false;
    148     }
    149     if (!it.value().IsType(entry->type)) {
    150       *error = base::StringPrintf("Invalid value for %s attribute",
    151                                   it.key().c_str());
    152       return false;
    153     }
    154 
    155     // base::Value::TYPE_INTEGER attributes must be >= 0.
    156     // This applies to "minItems", "maxItems", "minLength" and "maxLength".
    157     if (it.value().IsType(base::Value::TYPE_INTEGER)) {
    158       int integer_value;
    159       it.value().GetAsInteger(&integer_value);
    160       if (integer_value < 0) {
    161         *error = base::StringPrintf("Value of %s must be >= 0, got %d",
    162                                     it.key().c_str(), integer_value);
    163         return false;
    164       }
    165     }
    166 
    167     // Validate the "properties" attribute. Each entry maps a key to a schema.
    168     if (it.key() == schema::kProperties) {
    169       it.value().GetAsDictionary(&dictionary_value);
    170       for (base::DictionaryValue::Iterator it(*dictionary_value);
    171            !it.IsAtEnd(); it.Advance()) {
    172         if (!it.value().GetAsDictionary(&dictionary_value)) {
    173           *error = "Invalid value for properties attribute";
    174           return false;
    175         }
    176         if (!IsValidSchema(dictionary_value, error)) {
    177           DCHECK(!error->empty());
    178           return false;
    179         }
    180       }
    181     }
    182 
    183     // Validate "additionalProperties" attribute, which is a schema.
    184     if (it.key() == schema::kAdditionalProperties) {
    185       it.value().GetAsDictionary(&dictionary_value);
    186       if (!IsValidSchema(dictionary_value, error)) {
    187         DCHECK(!error->empty());
    188         return false;
    189       }
    190     }
    191 
    192     // Validate the values contained in an "enum" attribute.
    193     if (it.key() == schema::kEnum) {
    194       it.value().GetAsList(&list_value);
    195       for (size_t i = 0; i < list_value->GetSize(); ++i) {
    196         const base::Value* value = NULL;
    197         list_value->Get(i, &value);
    198         switch (value->GetType()) {
    199           case base::Value::TYPE_NULL:
    200           case base::Value::TYPE_BOOLEAN:
    201           case base::Value::TYPE_INTEGER:
    202           case base::Value::TYPE_DOUBLE:
    203           case base::Value::TYPE_STRING:
    204             break;
    205           default:
    206             *error = "Invalid value in enum attribute";
    207             return false;
    208         }
    209       }
    210     }
    211 
    212     // Validate the schemas contained in a "choices" attribute.
    213     if (it.key() == schema::kChoices) {
    214       it.value().GetAsList(&list_value);
    215       for (size_t i = 0; i < list_value->GetSize(); ++i) {
    216         if (!list_value->GetDictionary(i, &dictionary_value)) {
    217           *error = "Invalid choices attribute";
    218           return false;
    219         }
    220         if (!IsValidSchema(dictionary_value, error)) {
    221           DCHECK(!error->empty());
    222           return false;
    223         }
    224       }
    225     }
    226   }
    227 
    228   if (!has_type) {
    229     *error = "Schema must have a type attribute";
    230     return false;
    231   }
    232 
    233   return true;
    234 }
    235 
    236 }  // namespace
    237 
    238 
    239 JSONSchemaValidator::Error::Error() {
    240 }
    241 
    242 JSONSchemaValidator::Error::Error(const std::string& message)
    243     : path(message) {
    244 }
    245 
    246 JSONSchemaValidator::Error::Error(const std::string& path,
    247                                   const std::string& message)
    248     : path(path), message(message) {
    249 }
    250 
    251 
    252 const char JSONSchemaValidator::kUnknownTypeReference[] =
    253     "Unknown schema reference: *.";
    254 const char JSONSchemaValidator::kInvalidChoice[] =
    255     "Value does not match any valid type choices.";
    256 const char JSONSchemaValidator::kInvalidEnum[] =
    257     "Value does not match any valid enum choices.";
    258 const char JSONSchemaValidator::kObjectPropertyIsRequired[] =
    259     "Property is required.";
    260 const char JSONSchemaValidator::kUnexpectedProperty[] =
    261     "Unexpected property.";
    262 const char JSONSchemaValidator::kArrayMinItems[] =
    263     "Array must have at least * items.";
    264 const char JSONSchemaValidator::kArrayMaxItems[] =
    265     "Array must not have more than * items.";
    266 const char JSONSchemaValidator::kArrayItemRequired[] =
    267     "Item is required.";
    268 const char JSONSchemaValidator::kStringMinLength[] =
    269     "String must be at least * characters long.";
    270 const char JSONSchemaValidator::kStringMaxLength[] =
    271     "String must not be more than * characters long.";
    272 const char JSONSchemaValidator::kStringPattern[] =
    273     "String must match the pattern: *.";
    274 const char JSONSchemaValidator::kNumberMinimum[] =
    275     "Value must not be less than *.";
    276 const char JSONSchemaValidator::kNumberMaximum[] =
    277     "Value must not be greater than *.";
    278 const char JSONSchemaValidator::kInvalidType[] =
    279     "Expected '*' but got '*'.";
    280 const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] =
    281     "Expected 'integer' but got 'number', consider using Math.round().";
    282 
    283 
    284 // static
    285 std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) {
    286   switch (value->GetType()) {
    287     case base::Value::TYPE_NULL:
    288       return schema::kNull;
    289     case base::Value::TYPE_BOOLEAN:
    290       return schema::kBoolean;
    291     case base::Value::TYPE_INTEGER:
    292       return schema::kInteger;
    293     case base::Value::TYPE_DOUBLE: {
    294       double double_value = 0;
    295       value->GetAsDouble(&double_value);
    296       if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) &&
    297           double_value == floor(double_value)) {
    298         return schema::kInteger;
    299       } else {
    300         return schema::kNumber;
    301       }
    302     }
    303     case base::Value::TYPE_STRING:
    304       return schema::kString;
    305     case base::Value::TYPE_DICTIONARY:
    306       return schema::kObject;
    307     case base::Value::TYPE_LIST:
    308       return schema::kArray;
    309     default:
    310       NOTREACHED() << "Unexpected value type: " << value->GetType();
    311       return std::string();
    312   }
    313 }
    314 
    315 // static
    316 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format,
    317                                                     const std::string& s1) {
    318   std::string ret_val = format;
    319   ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
    320   return ret_val;
    321 }
    322 
    323 // static
    324 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format,
    325                                                     const std::string& s1,
    326                                                     const std::string& s2) {
    327   std::string ret_val = format;
    328   ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
    329   ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
    330   return ret_val;
    331 }
    332 
    333 // static
    334 scoped_ptr<base::DictionaryValue> JSONSchemaValidator::IsValidSchema(
    335     const std::string& schema,
    336     std::string* error) {
    337   base::JSONParserOptions options = base::JSON_PARSE_RFC;
    338   scoped_ptr<base::Value> json(
    339       base::JSONReader::ReadAndReturnError(schema, options, NULL, error));
    340   if (!json)
    341     return scoped_ptr<base::DictionaryValue>();
    342   base::DictionaryValue* dict = NULL;
    343   if (!json->GetAsDictionary(&dict)) {
    344     *error = "Schema must be a JSON object";
    345     return scoped_ptr<base::DictionaryValue>();
    346   }
    347   if (!::IsValidSchema(dict, error))
    348     return scoped_ptr<base::DictionaryValue>();
    349   ignore_result(json.release());
    350   return make_scoped_ptr(dict);
    351 }
    352 
    353 JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue* schema)
    354     : schema_root_(schema), default_allow_additional_properties_(false) {
    355 }
    356 
    357 JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue* schema,
    358                                          base::ListValue* types)
    359     : schema_root_(schema), default_allow_additional_properties_(false) {
    360   if (!types)
    361     return;
    362 
    363   for (size_t i = 0; i < types->GetSize(); ++i) {
    364     base::DictionaryValue* type = NULL;
    365     CHECK(types->GetDictionary(i, &type));
    366 
    367     std::string id;
    368     CHECK(type->GetString(schema::kId, &id));
    369 
    370     CHECK(types_.find(id) == types_.end());
    371     types_[id] = type;
    372   }
    373 }
    374 
    375 JSONSchemaValidator::~JSONSchemaValidator() {}
    376 
    377 bool JSONSchemaValidator::Validate(const base::Value* instance) {
    378   errors_.clear();
    379   Validate(instance, schema_root_, std::string());
    380   return errors_.empty();
    381 }
    382 
    383 void JSONSchemaValidator::Validate(const base::Value* instance,
    384                                    const base::DictionaryValue* schema,
    385                                    const std::string& path) {
    386   // If this schema defines itself as reference type, save it in this.types.
    387   std::string id;
    388   if (schema->GetString(schema::kId, &id)) {
    389     TypeMap::iterator iter = types_.find(id);
    390     if (iter == types_.end())
    391       types_[id] = schema;
    392     else
    393       DCHECK(iter->second == schema);
    394   }
    395 
    396   // If the schema has a $ref property, the instance must validate against
    397   // that schema. It must be present in types_ to be referenced.
    398   std::string ref;
    399   if (schema->GetString(schema::kRef, &ref)) {
    400     TypeMap::iterator type = types_.find(ref);
    401     if (type == types_.end()) {
    402       errors_.push_back(
    403           Error(path, FormatErrorMessage(kUnknownTypeReference, ref)));
    404     } else {
    405       Validate(instance, type->second, path);
    406     }
    407     return;
    408   }
    409 
    410   // If the schema has a choices property, the instance must validate against at
    411   // least one of the items in that array.
    412   const base::ListValue* choices = NULL;
    413   if (schema->GetList(schema::kChoices, &choices)) {
    414     ValidateChoices(instance, choices, path);
    415     return;
    416   }
    417 
    418   // If the schema has an enum property, the instance must be one of those
    419   // values.
    420   const base::ListValue* enumeration = NULL;
    421   if (schema->GetList(schema::kEnum, &enumeration)) {
    422     ValidateEnum(instance, enumeration, path);
    423     return;
    424   }
    425 
    426   std::string type;
    427   schema->GetString(schema::kType, &type);
    428   CHECK(!type.empty());
    429   if (type != schema::kAny) {
    430     if (!ValidateType(instance, type, path))
    431       return;
    432 
    433     // These casts are safe because of checks in ValidateType().
    434     if (type == schema::kObject) {
    435       ValidateObject(static_cast<const base::DictionaryValue*>(instance),
    436                      schema,
    437                      path);
    438     } else if (type == schema::kArray) {
    439       ValidateArray(static_cast<const base::ListValue*>(instance),
    440                     schema, path);
    441     } else if (type == schema::kString) {
    442       // Intentionally NOT downcasting to StringValue*. TYPE_STRING only implies
    443       // GetAsString() can safely be carried out, not that it's a StringValue.
    444       ValidateString(instance, schema, path);
    445     } else if (type == schema::kNumber || type == schema::kInteger) {
    446       ValidateNumber(instance, schema, path);
    447     } else if (type != schema::kBoolean && type != schema::kNull) {
    448       NOTREACHED() << "Unexpected type: " << type;
    449     }
    450   }
    451 }
    452 
    453 void JSONSchemaValidator::ValidateChoices(const base::Value* instance,
    454                                           const base::ListValue* choices,
    455                                           const std::string& path) {
    456   size_t original_num_errors = errors_.size();
    457 
    458   for (size_t i = 0; i < choices->GetSize(); ++i) {
    459     const base::DictionaryValue* choice = NULL;
    460     CHECK(choices->GetDictionary(i, &choice));
    461 
    462     Validate(instance, choice, path);
    463     if (errors_.size() == original_num_errors)
    464       return;
    465 
    466     // We discard the error from each choice. We only want to know if any of the
    467     // validations succeeded.
    468     errors_.resize(original_num_errors);
    469   }
    470 
    471   // Now add a generic error that no choices matched.
    472   errors_.push_back(Error(path, kInvalidChoice));
    473   return;
    474 }
    475 
    476 void JSONSchemaValidator::ValidateEnum(const base::Value* instance,
    477                                        const base::ListValue* choices,
    478                                        const std::string& path) {
    479   for (size_t i = 0; i < choices->GetSize(); ++i) {
    480     const base::Value* choice = NULL;
    481     CHECK(choices->Get(i, &choice));
    482     switch (choice->GetType()) {
    483       case base::Value::TYPE_NULL:
    484       case base::Value::TYPE_BOOLEAN:
    485       case base::Value::TYPE_STRING:
    486         if (instance->Equals(choice))
    487           return;
    488         break;
    489 
    490       case base::Value::TYPE_INTEGER:
    491       case base::Value::TYPE_DOUBLE:
    492         if (instance->IsType(base::Value::TYPE_INTEGER) ||
    493             instance->IsType(base::Value::TYPE_DOUBLE)) {
    494           if (GetNumberValue(choice) == GetNumberValue(instance))
    495             return;
    496         }
    497         break;
    498 
    499       default:
    500        NOTREACHED() << "Unexpected type in enum: " << choice->GetType();
    501     }
    502   }
    503 
    504   errors_.push_back(Error(path, kInvalidEnum));
    505 }
    506 
    507 void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance,
    508                                          const base::DictionaryValue* schema,
    509                                          const std::string& path) {
    510   const base::DictionaryValue* properties = NULL;
    511   schema->GetDictionary(schema::kProperties, &properties);
    512   if (properties) {
    513     for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd();
    514          it.Advance()) {
    515       std::string prop_path = path.empty() ? it.key() : (path + "." + it.key());
    516       const base::DictionaryValue* prop_schema = NULL;
    517       CHECK(it.value().GetAsDictionary(&prop_schema));
    518 
    519       const base::Value* prop_value = NULL;
    520       if (instance->Get(it.key(), &prop_value)) {
    521         Validate(prop_value, prop_schema, prop_path);
    522       } else {
    523         // Properties are required unless there is an optional field set to
    524         // 'true'.
    525         bool is_optional = false;
    526         prop_schema->GetBoolean(schema::kOptional, &is_optional);
    527         if (!is_optional) {
    528           errors_.push_back(Error(prop_path, kObjectPropertyIsRequired));
    529         }
    530       }
    531     }
    532   }
    533 
    534   const base::DictionaryValue* additional_properties_schema = NULL;
    535   if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema))
    536     return;
    537 
    538   // Validate additional properties.
    539   for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd();
    540        it.Advance()) {
    541     if (properties && properties->HasKey(it.key()))
    542       continue;
    543 
    544     std::string prop_path = path.empty() ? it.key() : path + "." + it.key();
    545     if (!additional_properties_schema) {
    546       errors_.push_back(Error(prop_path, kUnexpectedProperty));
    547     } else {
    548       Validate(&it.value(), additional_properties_schema, prop_path);
    549     }
    550   }
    551 }
    552 
    553 void JSONSchemaValidator::ValidateArray(const base::ListValue* instance,
    554                                         const base::DictionaryValue* schema,
    555                                         const std::string& path) {
    556   const base::DictionaryValue* single_type = NULL;
    557   size_t instance_size = instance->GetSize();
    558   if (schema->GetDictionary(schema::kItems, &single_type)) {
    559     int min_items = 0;
    560     if (schema->GetInteger(schema::kMinItems, &min_items)) {
    561       CHECK(min_items >= 0);
    562       if (instance_size < static_cast<size_t>(min_items)) {
    563         errors_.push_back(Error(path, FormatErrorMessage(
    564             kArrayMinItems, base::IntToString(min_items))));
    565       }
    566     }
    567 
    568     int max_items = 0;
    569     if (schema->GetInteger(schema::kMaxItems, &max_items)) {
    570       CHECK(max_items >= 0);
    571       if (instance_size > static_cast<size_t>(max_items)) {
    572         errors_.push_back(Error(path, FormatErrorMessage(
    573             kArrayMaxItems, base::IntToString(max_items))));
    574       }
    575     }
    576 
    577     // If the items property is a single schema, each item in the array must
    578     // validate against that schema.
    579     for (size_t i = 0; i < instance_size; ++i) {
    580       const base::Value* item = NULL;
    581       CHECK(instance->Get(i, &item));
    582       std::string i_str = base::UintToString(i);
    583       std::string item_path = path.empty() ? i_str : (path + "." + i_str);
    584       Validate(item, single_type, item_path);
    585     }
    586 
    587     return;
    588   }
    589 
    590   // Otherwise, the list must be a tuple type, where each item in the list has a
    591   // particular schema.
    592   ValidateTuple(instance, schema, path);
    593 }
    594 
    595 void JSONSchemaValidator::ValidateTuple(const base::ListValue* instance,
    596                                         const base::DictionaryValue* schema,
    597                                         const std::string& path) {
    598   const base::ListValue* tuple_type = NULL;
    599   schema->GetList(schema::kItems, &tuple_type);
    600   size_t tuple_size = tuple_type ? tuple_type->GetSize() : 0;
    601   if (tuple_type) {
    602     for (size_t i = 0; i < tuple_size; ++i) {
    603       std::string i_str = base::UintToString(i);
    604       std::string item_path = path.empty() ? i_str : (path + "." + i_str);
    605       const base::DictionaryValue* item_schema = NULL;
    606       CHECK(tuple_type->GetDictionary(i, &item_schema));
    607       const base::Value* item_value = NULL;
    608       instance->Get(i, &item_value);
    609       if (item_value && item_value->GetType() != base::Value::TYPE_NULL) {
    610         Validate(item_value, item_schema, item_path);
    611       } else {
    612         bool is_optional = false;
    613         item_schema->GetBoolean(schema::kOptional, &is_optional);
    614         if (!is_optional) {
    615           errors_.push_back(Error(item_path, kArrayItemRequired));
    616           return;
    617         }
    618       }
    619     }
    620   }
    621 
    622   const base::DictionaryValue* additional_properties_schema = NULL;
    623   if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema))
    624     return;
    625 
    626   size_t instance_size = instance->GetSize();
    627   if (additional_properties_schema) {
    628     // Any additional properties must validate against the additionalProperties
    629     // schema.
    630     for (size_t i = tuple_size; i < instance_size; ++i) {
    631       std::string i_str = base::UintToString(i);
    632       std::string item_path = path.empty() ? i_str : (path + "." + i_str);
    633       const base::Value* item_value = NULL;
    634       CHECK(instance->Get(i, &item_value));
    635       Validate(item_value, additional_properties_schema, item_path);
    636     }
    637   } else if (instance_size > tuple_size) {
    638     errors_.push_back(Error(path, FormatErrorMessage(
    639         kArrayMaxItems, base::UintToString(tuple_size))));
    640   }
    641 }
    642 
    643 void JSONSchemaValidator::ValidateString(const base::Value* instance,
    644                                          const base::DictionaryValue* schema,
    645                                          const std::string& path) {
    646   std::string value;
    647   CHECK(instance->GetAsString(&value));
    648 
    649   int min_length = 0;
    650   if (schema->GetInteger(schema::kMinLength, &min_length)) {
    651     CHECK(min_length >= 0);
    652     if (value.size() < static_cast<size_t>(min_length)) {
    653       errors_.push_back(Error(path, FormatErrorMessage(
    654           kStringMinLength, base::IntToString(min_length))));
    655     }
    656   }
    657 
    658   int max_length = 0;
    659   if (schema->GetInteger(schema::kMaxLength, &max_length)) {
    660     CHECK(max_length >= 0);
    661     if (value.size() > static_cast<size_t>(max_length)) {
    662       errors_.push_back(Error(path, FormatErrorMessage(
    663           kStringMaxLength, base::IntToString(max_length))));
    664     }
    665   }
    666 
    667   CHECK(!schema->HasKey(schema::kPattern)) << "Pattern is not supported.";
    668 }
    669 
    670 void JSONSchemaValidator::ValidateNumber(const base::Value* instance,
    671                                          const base::DictionaryValue* schema,
    672                                          const std::string& path) {
    673   double value = GetNumberValue(instance);
    674 
    675   // TODO(aa): It would be good to test that the double is not infinity or nan,
    676   // but isnan and isinf aren't defined on Windows.
    677 
    678   double minimum = 0;
    679   if (schema->GetDouble(schema::kMinimum, &minimum)) {
    680     if (value < minimum)
    681       errors_.push_back(Error(path, FormatErrorMessage(
    682           kNumberMinimum, base::DoubleToString(minimum))));
    683   }
    684 
    685   double maximum = 0;
    686   if (schema->GetDouble(schema::kMaximum, &maximum)) {
    687     if (value > maximum)
    688       errors_.push_back(Error(path, FormatErrorMessage(
    689           kNumberMaximum, base::DoubleToString(maximum))));
    690   }
    691 }
    692 
    693 bool JSONSchemaValidator::ValidateType(const base::Value* instance,
    694                                        const std::string& expected_type,
    695                                        const std::string& path) {
    696   std::string actual_type = GetJSONSchemaType(instance);
    697   if (expected_type == actual_type ||
    698       (expected_type == schema::kNumber && actual_type == schema::kInteger)) {
    699     return true;
    700   } else if (expected_type == schema::kInteger &&
    701              actual_type == schema::kNumber) {
    702     errors_.push_back(Error(path, kInvalidTypeIntegerNumber));
    703     return false;
    704   } else {
    705     errors_.push_back(Error(path, FormatErrorMessage(
    706         kInvalidType, expected_type, actual_type)));
    707     return false;
    708   }
    709 }
    710 
    711 bool JSONSchemaValidator::SchemaAllowsAnyAdditionalItems(
    712     const base::DictionaryValue* schema,
    713     const base::DictionaryValue** additional_properties_schema) {
    714   // If the validator allows additional properties globally, and this schema
    715   // doesn't override, then we can exit early.
    716   schema->GetDictionary(schema::kAdditionalProperties,
    717                         additional_properties_schema);
    718 
    719   if (*additional_properties_schema) {
    720     std::string additional_properties_type(schema::kAny);
    721     CHECK((*additional_properties_schema)->GetString(
    722         schema::kType, &additional_properties_type));
    723     return additional_properties_type == schema::kAny;
    724   } else {
    725     return default_allow_additional_properties_;
    726   }
    727 }
    728