| Index: trunk/src/extensions/renderer/resources/json_schema.js
|
| ===================================================================
|
| --- trunk/src/extensions/renderer/resources/json_schema.js (revision 274563)
|
| +++ trunk/src/extensions/renderer/resources/json_schema.js (working copy)
|
| @@ -1,525 +0,0 @@
|
| -// Copyright 2014 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -// -----------------------------------------------------------------------------
|
| -// NOTE: If you change this file you need to touch
|
| -// extension_renderer_resources.grd to have your change take effect.
|
| -// -----------------------------------------------------------------------------
|
| -
|
| -//==============================================================================
|
| -// This file contains a class that implements a subset of JSON Schema.
|
| -// See: http://www.json.com/json-schema-proposal/ for more details.
|
| -//
|
| -// The following features of JSON Schema are not implemented:
|
| -// - requires
|
| -// - unique
|
| -// - disallow
|
| -// - union types (but replaced with 'choices')
|
| -//
|
| -// The following properties are not applicable to the interface exposed by
|
| -// this class:
|
| -// - options
|
| -// - readonly
|
| -// - title
|
| -// - description
|
| -// - format
|
| -// - default
|
| -// - transient
|
| -// - hidden
|
| -//
|
| -// There are also these departures from the JSON Schema proposal:
|
| -// - function and undefined types are supported
|
| -// - null counts as 'unspecified' for optional values
|
| -// - added the 'choices' property, to allow specifying a list of possible types
|
| -// for a value
|
| -// - by default an "object" typed schema does not allow additional properties.
|
| -// if present, "additionalProperties" is to be a schema against which all
|
| -// additional properties will be validated.
|
| -//==============================================================================
|
| -
|
| -var loadTypeSchema = require('utils').loadTypeSchema;
|
| -var CHECK = requireNative('logging').CHECK;
|
| -
|
| -function isInstanceOfClass(instance, className) {
|
| - while ((instance = instance.__proto__)) {
|
| - if (instance.constructor.name == className)
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -function isOptionalValue(value) {
|
| - return typeof(value) === 'undefined' || value === null;
|
| -}
|
| -
|
| -function enumToString(enumValue) {
|
| - if (enumValue.name === undefined)
|
| - return enumValue;
|
| -
|
| - return enumValue.name;
|
| -}
|
| -
|
| -/**
|
| - * Validates an instance against a schema and accumulates errors. Usage:
|
| - *
|
| - * var validator = new JSONSchemaValidator();
|
| - * validator.validate(inst, schema);
|
| - * if (validator.errors.length == 0)
|
| - * console.log("Valid!");
|
| - * else
|
| - * console.log(validator.errors);
|
| - *
|
| - * The errors property contains a list of objects. Each object has two
|
| - * properties: "path" and "message". The "path" property contains the path to
|
| - * the key that had the problem, and the "message" property contains a sentence
|
| - * describing the error.
|
| - */
|
| -function JSONSchemaValidator() {
|
| - this.errors = [];
|
| - this.types = [];
|
| -}
|
| -
|
| -JSONSchemaValidator.messages = {
|
| - invalidEnum: "Value must be one of: [*].",
|
| - propertyRequired: "Property is required.",
|
| - unexpectedProperty: "Unexpected property.",
|
| - arrayMinItems: "Array must have at least * items.",
|
| - arrayMaxItems: "Array must not have more than * items.",
|
| - itemRequired: "Item is required.",
|
| - stringMinLength: "String must be at least * characters long.",
|
| - stringMaxLength: "String must not be more than * characters long.",
|
| - stringPattern: "String must match the pattern: *.",
|
| - numberFiniteNotNan: "Value must not be *.",
|
| - numberMinValue: "Value must not be less than *.",
|
| - numberMaxValue: "Value must not be greater than *.",
|
| - numberIntValue: "Value must fit in a 32-bit signed integer.",
|
| - numberMaxDecimal: "Value must not have more than * decimal places.",
|
| - invalidType: "Expected '*' but got '*'.",
|
| - invalidTypeIntegerNumber:
|
| - "Expected 'integer' but got 'number', consider using Math.round().",
|
| - invalidChoice: "Value does not match any valid type choices.",
|
| - invalidPropertyType: "Missing property type.",
|
| - schemaRequired: "Schema value required.",
|
| - unknownSchemaReference: "Unknown schema reference: *.",
|
| - notInstance: "Object must be an instance of *."
|
| -};
|
| -
|
| -/**
|
| - * Builds an error message. Key is the property in the |errors| object, and
|
| - * |opt_replacements| is an array of values to replace "*" characters with.
|
| - */
|
| -JSONSchemaValidator.formatError = function(key, opt_replacements) {
|
| - var message = this.messages[key];
|
| - if (opt_replacements) {
|
| - for (var i = 0; i < opt_replacements.length; i++) {
|
| - message = message.replace("*", opt_replacements[i]);
|
| - }
|
| - }
|
| - return message;
|
| -};
|
| -
|
| -/**
|
| - * Classifies a value as one of the JSON schema primitive types. Note that we
|
| - * don't explicitly disallow 'function', because we want to allow functions in
|
| - * the input values.
|
| - */
|
| -JSONSchemaValidator.getType = function(value) {
|
| - var s = typeof value;
|
| -
|
| - if (s == "object") {
|
| - if (value === null) {
|
| - return "null";
|
| - } else if (Object.prototype.toString.call(value) == "[object Array]") {
|
| - return "array";
|
| - } else if (typeof(ArrayBuffer) != "undefined" &&
|
| - value.constructor == ArrayBuffer) {
|
| - return "binary";
|
| - }
|
| - } else if (s == "number") {
|
| - if (value % 1 == 0) {
|
| - return "integer";
|
| - }
|
| - }
|
| -
|
| - return s;
|
| -};
|
| -
|
| -/**
|
| - * Add types that may be referenced by validated schemas that reference them
|
| - * with "$ref": <typeId>. Each type must be a valid schema and define an
|
| - * "id" property.
|
| - */
|
| -JSONSchemaValidator.prototype.addTypes = function(typeOrTypeList) {
|
| - function addType(validator, type) {
|
| - if (!type.id)
|
| - throw new Error("Attempt to addType with missing 'id' property");
|
| - validator.types[type.id] = type;
|
| - }
|
| -
|
| - if (typeOrTypeList instanceof Array) {
|
| - for (var i = 0; i < typeOrTypeList.length; i++) {
|
| - addType(this, typeOrTypeList[i]);
|
| - }
|
| - } else {
|
| - addType(this, typeOrTypeList);
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Returns a list of strings of the types that this schema accepts.
|
| - */
|
| -JSONSchemaValidator.prototype.getAllTypesForSchema = function(schema) {
|
| - var schemaTypes = [];
|
| - if (schema.type)
|
| - $Array.push(schemaTypes, schema.type);
|
| - if (schema.choices) {
|
| - for (var i = 0; i < schema.choices.length; i++) {
|
| - var choiceTypes = this.getAllTypesForSchema(schema.choices[i]);
|
| - schemaTypes = $Array.concat(schemaTypes, choiceTypes);
|
| - }
|
| - }
|
| - var ref = schema['$ref'];
|
| - if (ref) {
|
| - var type = this.getOrAddType(ref);
|
| - CHECK(type, 'Could not find type ' + ref);
|
| - schemaTypes = $Array.concat(schemaTypes, this.getAllTypesForSchema(type));
|
| - }
|
| - return schemaTypes;
|
| -};
|
| -
|
| -JSONSchemaValidator.prototype.getOrAddType = function(typeName) {
|
| - if (!this.types[typeName])
|
| - this.types[typeName] = loadTypeSchema(typeName);
|
| - return this.types[typeName];
|
| -};
|
| -
|
| -/**
|
| - * Returns true if |schema| would accept an argument of type |type|.
|
| - */
|
| -JSONSchemaValidator.prototype.isValidSchemaType = function(type, schema) {
|
| - if (type == 'any')
|
| - return true;
|
| -
|
| - // TODO(kalman): I don't understand this code. How can type be "null"?
|
| - if (schema.optional && (type == "null" || type == "undefined"))
|
| - return true;
|
| -
|
| - var schemaTypes = this.getAllTypesForSchema(schema);
|
| - for (var i = 0; i < schemaTypes.length; i++) {
|
| - if (schemaTypes[i] == "any" || type == schemaTypes[i] ||
|
| - (type == "integer" && schemaTypes[i] == "number"))
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -};
|
| -
|
| -/**
|
| - * Returns true if there is a non-null argument that both |schema1| and
|
| - * |schema2| would accept.
|
| - */
|
| -JSONSchemaValidator.prototype.checkSchemaOverlap = function(schema1, schema2) {
|
| - var schema1Types = this.getAllTypesForSchema(schema1);
|
| - for (var i = 0; i < schema1Types.length; i++) {
|
| - if (this.isValidSchemaType(schema1Types[i], schema2))
|
| - return true;
|
| - }
|
| - return false;
|
| -};
|
| -
|
| -/**
|
| - * Validates an instance against a schema. The instance can be any JavaScript
|
| - * value and will be validated recursively. When this method returns, the
|
| - * |errors| property will contain a list of errors, if any.
|
| - */
|
| -JSONSchemaValidator.prototype.validate = function(instance, schema, opt_path) {
|
| - var path = opt_path || "";
|
| -
|
| - if (!schema) {
|
| - this.addError(path, "schemaRequired");
|
| - return;
|
| - }
|
| -
|
| - // If this schema defines itself as reference type, save it in this.types.
|
| - if (schema.id)
|
| - this.types[schema.id] = schema;
|
| -
|
| - // If the schema has an extends property, the instance must validate against
|
| - // that schema too.
|
| - if (schema.extends)
|
| - this.validate(instance, schema.extends, path);
|
| -
|
| - // If the schema has a $ref property, the instance must validate against
|
| - // that schema too. It must be present in this.types to be referenced.
|
| - var ref = schema["$ref"];
|
| - if (ref) {
|
| - if (!this.getOrAddType(ref))
|
| - this.addError(path, "unknownSchemaReference", [ ref ]);
|
| - else
|
| - this.validate(instance, this.getOrAddType(ref), path)
|
| - }
|
| -
|
| - // If the schema has a choices property, the instance must validate against at
|
| - // least one of the items in that array.
|
| - if (schema.choices) {
|
| - this.validateChoices(instance, schema, path);
|
| - return;
|
| - }
|
| -
|
| - // If the schema has an enum property, the instance must be one of those
|
| - // values.
|
| - if (schema.enum) {
|
| - if (!this.validateEnum(instance, schema, path))
|
| - return;
|
| - }
|
| -
|
| - if (schema.type && schema.type != "any") {
|
| - if (!this.validateType(instance, schema, path))
|
| - return;
|
| -
|
| - // Type-specific validation.
|
| - switch (schema.type) {
|
| - case "object":
|
| - this.validateObject(instance, schema, path);
|
| - break;
|
| - case "array":
|
| - this.validateArray(instance, schema, path);
|
| - break;
|
| - case "string":
|
| - this.validateString(instance, schema, path);
|
| - break;
|
| - case "number":
|
| - case "integer":
|
| - this.validateNumber(instance, schema, path);
|
| - break;
|
| - }
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * Validates an instance against a choices schema. The instance must match at
|
| - * least one of the provided choices.
|
| - */
|
| -JSONSchemaValidator.prototype.validateChoices =
|
| - function(instance, schema, path) {
|
| - var originalErrors = this.errors;
|
| -
|
| - for (var i = 0; i < schema.choices.length; i++) {
|
| - this.errors = [];
|
| - this.validate(instance, schema.choices[i], path);
|
| - if (this.errors.length == 0) {
|
| - this.errors = originalErrors;
|
| - return;
|
| - }
|
| - }
|
| -
|
| - this.errors = originalErrors;
|
| - this.addError(path, "invalidChoice");
|
| -};
|
| -
|
| -/**
|
| - * Validates an instance against a schema with an enum type. Populates the
|
| - * |errors| property, and returns a boolean indicating whether the instance
|
| - * validates.
|
| - */
|
| -JSONSchemaValidator.prototype.validateEnum = function(instance, schema, path) {
|
| - for (var i = 0; i < schema.enum.length; i++) {
|
| - if (instance === enumToString(schema.enum[i]))
|
| - return true;
|
| - }
|
| -
|
| - this.addError(path, "invalidEnum",
|
| - [schema.enum.map(enumToString).join(", ")]);
|
| - return false;
|
| -};
|
| -
|
| -/**
|
| - * Validates an instance against an object schema and populates the errors
|
| - * property.
|
| - */
|
| -JSONSchemaValidator.prototype.validateObject =
|
| - function(instance, schema, path) {
|
| - if (schema.properties) {
|
| - for (var prop in schema.properties) {
|
| - // It is common in JavaScript to add properties to Object.prototype. This
|
| - // check prevents such additions from being interpreted as required
|
| - // schema properties.
|
| - // TODO(aa): If it ever turns out that we actually want this to work,
|
| - // there are other checks we could put here, like requiring that schema
|
| - // properties be objects that have a 'type' property.
|
| - if (!$Object.hasOwnProperty(schema.properties, prop))
|
| - continue;
|
| -
|
| - var propPath = path ? path + "." + prop : prop;
|
| - if (schema.properties[prop] == undefined) {
|
| - this.addError(propPath, "invalidPropertyType");
|
| - } else if (prop in instance && !isOptionalValue(instance[prop])) {
|
| - this.validate(instance[prop], schema.properties[prop], propPath);
|
| - } else if (!schema.properties[prop].optional) {
|
| - this.addError(propPath, "propertyRequired");
|
| - }
|
| - }
|
| - }
|
| -
|
| - // If "instanceof" property is set, check that this object inherits from
|
| - // the specified constructor (function).
|
| - if (schema.isInstanceOf) {
|
| - if (!isInstanceOfClass(instance, schema.isInstanceOf))
|
| - this.addError(propPath, "notInstance", [schema.isInstanceOf]);
|
| - }
|
| -
|
| - // Exit early from additional property check if "type":"any" is defined.
|
| - if (schema.additionalProperties &&
|
| - schema.additionalProperties.type &&
|
| - schema.additionalProperties.type == "any") {
|
| - return;
|
| - }
|
| -
|
| - // By default, additional properties are not allowed on instance objects. This
|
| - // can be overridden by setting the additionalProperties property to a schema
|
| - // which any additional properties must validate against.
|
| - for (var prop in instance) {
|
| - if (schema.properties && prop in schema.properties)
|
| - continue;
|
| -
|
| - // Any properties inherited through the prototype are ignored.
|
| - if (!$Object.hasOwnProperty(instance, prop))
|
| - continue;
|
| -
|
| - var propPath = path ? path + "." + prop : prop;
|
| - if (schema.additionalProperties)
|
| - this.validate(instance[prop], schema.additionalProperties, propPath);
|
| - else
|
| - this.addError(propPath, "unexpectedProperty");
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * Validates an instance against an array schema and populates the errors
|
| - * property.
|
| - */
|
| -JSONSchemaValidator.prototype.validateArray = function(instance, schema, path) {
|
| - var typeOfItems = JSONSchemaValidator.getType(schema.items);
|
| -
|
| - if (typeOfItems == 'object') {
|
| - if (schema.minItems && instance.length < schema.minItems) {
|
| - this.addError(path, "arrayMinItems", [schema.minItems]);
|
| - }
|
| -
|
| - if (typeof schema.maxItems != "undefined" &&
|
| - instance.length > schema.maxItems) {
|
| - this.addError(path, "arrayMaxItems", [schema.maxItems]);
|
| - }
|
| -
|
| - // If the items property is a single schema, each item in the array must
|
| - // have that schema.
|
| - for (var i = 0; i < instance.length; i++) {
|
| - this.validate(instance[i], schema.items, path + "." + i);
|
| - }
|
| - } else if (typeOfItems == 'array') {
|
| - // If the items property is an array of schemas, each item in the array must
|
| - // validate against the corresponding schema.
|
| - for (var i = 0; i < schema.items.length; i++) {
|
| - var itemPath = path ? path + "." + i : String(i);
|
| - if (i in instance && !isOptionalValue(instance[i])) {
|
| - this.validate(instance[i], schema.items[i], itemPath);
|
| - } else if (!schema.items[i].optional) {
|
| - this.addError(itemPath, "itemRequired");
|
| - }
|
| - }
|
| -
|
| - if (schema.additionalProperties) {
|
| - for (var i = schema.items.length; i < instance.length; i++) {
|
| - var itemPath = path ? path + "." + i : String(i);
|
| - this.validate(instance[i], schema.additionalProperties, itemPath);
|
| - }
|
| - } else {
|
| - if (instance.length > schema.items.length) {
|
| - this.addError(path, "arrayMaxItems", [schema.items.length]);
|
| - }
|
| - }
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * Validates a string and populates the errors property.
|
| - */
|
| -JSONSchemaValidator.prototype.validateString =
|
| - function(instance, schema, path) {
|
| - if (schema.minLength && instance.length < schema.minLength)
|
| - this.addError(path, "stringMinLength", [schema.minLength]);
|
| -
|
| - if (schema.maxLength && instance.length > schema.maxLength)
|
| - this.addError(path, "stringMaxLength", [schema.maxLength]);
|
| -
|
| - if (schema.pattern && !schema.pattern.test(instance))
|
| - this.addError(path, "stringPattern", [schema.pattern]);
|
| -};
|
| -
|
| -/**
|
| - * Validates a number and populates the errors property. The instance is
|
| - * assumed to be a number.
|
| - */
|
| -JSONSchemaValidator.prototype.validateNumber =
|
| - function(instance, schema, path) {
|
| - // Forbid NaN, +Infinity, and -Infinity. Our APIs don't use them, and
|
| - // JSON serialization encodes them as 'null'. Re-evaluate supporting
|
| - // them if we add an API that could reasonably take them as a parameter.
|
| - if (isNaN(instance) ||
|
| - instance == Number.POSITIVE_INFINITY ||
|
| - instance == Number.NEGATIVE_INFINITY )
|
| - this.addError(path, "numberFiniteNotNan", [instance]);
|
| -
|
| - if (schema.minimum !== undefined && instance < schema.minimum)
|
| - this.addError(path, "numberMinValue", [schema.minimum]);
|
| -
|
| - if (schema.maximum !== undefined && instance > schema.maximum)
|
| - this.addError(path, "numberMaxValue", [schema.maximum]);
|
| -
|
| - // Check for integer values outside of -2^31..2^31-1.
|
| - if (schema.type === "integer" && (instance | 0) !== instance)
|
| - this.addError(path, "numberIntValue", []);
|
| -
|
| - if (schema.maxDecimal && instance * Math.pow(10, schema.maxDecimal) % 1)
|
| - this.addError(path, "numberMaxDecimal", [schema.maxDecimal]);
|
| -};
|
| -
|
| -/**
|
| - * Validates the primitive type of an instance and populates the errors
|
| - * property. Returns true if the instance validates, false otherwise.
|
| - */
|
| -JSONSchemaValidator.prototype.validateType = function(instance, schema, path) {
|
| - var actualType = JSONSchemaValidator.getType(instance);
|
| - if (schema.type == actualType ||
|
| - (schema.type == "number" && actualType == "integer")) {
|
| - return true;
|
| - } else if (schema.type == "integer" && actualType == "number") {
|
| - this.addError(path, "invalidTypeIntegerNumber");
|
| - return false;
|
| - } else {
|
| - this.addError(path, "invalidType", [schema.type, actualType]);
|
| - return false;
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * Adds an error message. |key| is an index into the |messages| object.
|
| - * |replacements| is an array of values to replace '*' characters in the
|
| - * message.
|
| - */
|
| -JSONSchemaValidator.prototype.addError = function(path, key, replacements) {
|
| - $Array.push(this.errors, {
|
| - path: path,
|
| - message: JSONSchemaValidator.formatError(key, replacements)
|
| - });
|
| -};
|
| -
|
| -/**
|
| - * Resets errors to an empty list so you can call 'validate' again.
|
| - */
|
| -JSONSchemaValidator.prototype.resetErrors = function() {
|
| - this.errors = [];
|
| -};
|
| -
|
| -exports.JSONSchemaValidator = JSONSchemaValidator;
|
|
|