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
19namespace schema = json_schema_constants;
20
21namespace {
22
23double 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
30bool 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.
46struct ExpectedType {
47  const char* key;
48  base::Value::Type type;
49};
50
51// Helper for std::lower_bound.
52bool CompareToString(const ExpectedType& entry, const std::string& key) {
53  return entry.key < key;
54}
55
56bool 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
239JSONSchemaValidator::Error::Error() {
240}
241
242JSONSchemaValidator::Error::Error(const std::string& message)
243    : path(message) {
244}
245
246JSONSchemaValidator::Error::Error(const std::string& path,
247                                  const std::string& message)
248    : path(path), message(message) {
249}
250
251
252const char JSONSchemaValidator::kUnknownTypeReference[] =
253    "Unknown schema reference: *.";
254const char JSONSchemaValidator::kInvalidChoice[] =
255    "Value does not match any valid type choices.";
256const char JSONSchemaValidator::kInvalidEnum[] =
257    "Value does not match any valid enum choices.";
258const char JSONSchemaValidator::kObjectPropertyIsRequired[] =
259    "Property is required.";
260const char JSONSchemaValidator::kUnexpectedProperty[] =
261    "Unexpected property.";
262const char JSONSchemaValidator::kArrayMinItems[] =
263    "Array must have at least * items.";
264const char JSONSchemaValidator::kArrayMaxItems[] =
265    "Array must not have more than * items.";
266const char JSONSchemaValidator::kArrayItemRequired[] =
267    "Item is required.";
268const char JSONSchemaValidator::kStringMinLength[] =
269    "String must be at least * characters long.";
270const char JSONSchemaValidator::kStringMaxLength[] =
271    "String must not be more than * characters long.";
272const char JSONSchemaValidator::kStringPattern[] =
273    "String must match the pattern: *.";
274const char JSONSchemaValidator::kNumberMinimum[] =
275    "Value must not be less than *.";
276const char JSONSchemaValidator::kNumberMaximum[] =
277    "Value must not be greater than *.";
278const char JSONSchemaValidator::kInvalidType[] =
279    "Expected '*' but got '*'.";
280const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] =
281    "Expected 'integer' but got 'number', consider using Math.round().";
282
283
284// static
285std::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
316std::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
324std::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
334scoped_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
353JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue* schema)
354    : schema_root_(schema), default_allow_additional_properties_(false) {
355}
356
357JSONSchemaValidator::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
375JSONSchemaValidator::~JSONSchemaValidator() {}
376
377bool JSONSchemaValidator::Validate(const base::Value* instance) {
378  errors_.clear();
379  Validate(instance, schema_root_, std::string());
380  return errors_.empty();
381}
382
383void 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
453void 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
476void 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
507void 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
553void 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
595void 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
643void 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
670void 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
693bool 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
711bool 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