| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 // ----------------------------------------------------------------------------- | 5 // ----------------------------------------------------------------------------- |
| 6 // NOTE: If you change this file you need to touch renderer_resources.grd to | 6 // NOTE: If you change this file you need to touch renderer_resources.grd to |
| 7 // have your change take effect. | 7 // have your change take effect. |
| 8 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
| 9 | 9 |
| 10 //============================================================================== | 10 //============================================================================== |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 // - hidden | 29 // - hidden |
| 30 // | 30 // |
| 31 // There are also these departures from the JSON Schema proposal: | 31 // There are also these departures from the JSON Schema proposal: |
| 32 // - function and undefined types are supported | 32 // - function and undefined types are supported |
| 33 // - null counts as 'unspecified' for optional values | 33 // - null counts as 'unspecified' for optional values |
| 34 // - added the 'choices' property, to allow specifying a list of possible types | 34 // - added the 'choices' property, to allow specifying a list of possible types |
| 35 // for a value | 35 // for a value |
| 36 // - made additionalProperties default to false | 36 // - made additionalProperties default to false |
| 37 //============================================================================== | 37 //============================================================================== |
| 38 | 38 |
| 39 var chrome = chrome || {}; | 39 (function() { |
| 40 native function GetChromeHidden(); |
| 41 var chromeHidden = GetChromeHidden(); |
| 40 | 42 |
| 41 /** | 43 /** |
| 42 * Validates an instance against a schema and accumulates errors. Usage: | 44 * Validates an instance against a schema and accumulates errors. Usage: |
| 43 * | 45 * |
| 44 * var validator = new chrome.JSONSchemaValidator(); | 46 * var validator = new chromeHidden.JSONSchemaValidator(); |
| 45 * validator.validate(inst, schema); | 47 * validator.validate(inst, schema); |
| 46 * if (validator.errors.length == 0) | 48 * if (validator.errors.length == 0) |
| 47 * console.log("Valid!"); | 49 * console.log("Valid!"); |
| 48 * else | 50 * else |
| 49 * console.log(validator.errors); | 51 * console.log(validator.errors); |
| 50 * | 52 * |
| 51 * The errors property contains a list of objects. Each object has two | 53 * The errors property contains a list of objects. Each object has two |
| 52 * properties: "path" and "message". The "path" property contains the path to | 54 * properties: "path" and "message". The "path" property contains the path to |
| 53 * the key that had the problem, and the "message" property contains a sentence | 55 * the key that had the problem, and the "message" property contains a sentence |
| 54 * describing the error. | 56 * describing the error. |
| 55 */ | 57 */ |
| 56 chrome.JSONSchemaValidator = function() { | 58 chromeHidden.JSONSchemaValidator = function() { |
| 57 this.errors = []; | 59 this.errors = []; |
| 58 this.types = []; | 60 this.types = []; |
| 59 }; | 61 }; |
| 60 | 62 |
| 61 chrome.JSONSchemaValidator.messages = { | 63 chromeHidden.JSONSchemaValidator.messages = { |
| 62 invalidEnum: "Value must be one of: [*].", | 64 invalidEnum: "Value must be one of: [*].", |
| 63 propertyRequired: "Property is required.", | 65 propertyRequired: "Property is required.", |
| 64 unexpectedProperty: "Unexpected property.", | 66 unexpectedProperty: "Unexpected property.", |
| 65 arrayMinItems: "Array must have at least * items.", | 67 arrayMinItems: "Array must have at least * items.", |
| 66 arrayMaxItems: "Array must not have more than * items.", | 68 arrayMaxItems: "Array must not have more than * items.", |
| 67 itemRequired: "Item is required.", | 69 itemRequired: "Item is required.", |
| 68 stringMinLength: "String must be at least * characters long.", | 70 stringMinLength: "String must be at least * characters long.", |
| 69 stringMaxLength: "String must not be more than * characters long.", | 71 stringMaxLength: "String must not be more than * characters long.", |
| 70 stringPattern: "String must match the pattern: *.", | 72 stringPattern: "String must match the pattern: *.", |
| 71 numberMinValue: "Value must not be less than *.", | 73 numberMinValue: "Value must not be less than *.", |
| 72 numberMaxValue: "Value must not be greater than *.", | 74 numberMaxValue: "Value must not be greater than *.", |
| 73 numberMaxDecimal: "Value must not have more than * decimal places.", | 75 numberMaxDecimal: "Value must not have more than * decimal places.", |
| 74 invalidType: "Expected '*' but got '*'.", | 76 invalidType: "Expected '*' but got '*'.", |
| 75 invalidChoice: "Value does not match any valid type choices.", | 77 invalidChoice: "Value does not match any valid type choices.", |
| 76 invalidPropertyType: "Missing property type.", | 78 invalidPropertyType: "Missing property type.", |
| 77 schemaRequired: "Schema value required.", | 79 schemaRequired: "Schema value required.", |
| 78 unknownSchemaReference: "Unknown schema reference: *." | 80 unknownSchemaReference: "Unknown schema reference: *." |
| 79 }; | 81 }; |
| 80 | 82 |
| 81 /** | 83 /** |
| 82 * Builds an error message. Key is the property in the |errors| object, and | 84 * Builds an error message. Key is the property in the |errors| object, and |
| 83 * |opt_replacements| is an array of values to replace "*" characters with. | 85 * |opt_replacements| is an array of values to replace "*" characters with. |
| 84 */ | 86 */ |
| 85 chrome.JSONSchemaValidator.formatError = function(key, opt_replacements) { | 87 chromeHidden.JSONSchemaValidator.formatError = function(key, opt_replacements) { |
| 86 var message = this.messages[key]; | 88 var message = this.messages[key]; |
| 87 if (opt_replacements) { | 89 if (opt_replacements) { |
| 88 for (var i = 0; i < opt_replacements.length; i++) { | 90 for (var i = 0; i < opt_replacements.length; i++) { |
| 89 message = message.replace("*", opt_replacements[i]); | 91 message = message.replace("*", opt_replacements[i]); |
| 90 } | 92 } |
| 91 } | 93 } |
| 92 return message; | 94 return message; |
| 93 }; | 95 }; |
| 94 | 96 |
| 95 /** | 97 /** |
| 96 * Classifies a value as one of the JSON schema primitive types. Note that we | 98 * Classifies a value as one of the JSON schema primitive types. Note that we |
| 97 * don't explicitly disallow 'function', because we want to allow functions in | 99 * don't explicitly disallow 'function', because we want to allow functions in |
| 98 * the input values. | 100 * the input values. |
| 99 */ | 101 */ |
| 100 chrome.JSONSchemaValidator.getType = function(value) { | 102 chromeHidden.JSONSchemaValidator.getType = function(value) { |
| 101 var s = typeof value; | 103 var s = typeof value; |
| 102 | 104 |
| 103 if (s == "object") { | 105 if (s == "object") { |
| 104 if (value === null) { | 106 if (value === null) { |
| 105 return "null"; | 107 return "null"; |
| 106 } else if (value instanceof Array || | 108 } else if (value instanceof Array || |
| 107 Object.prototype.toString.call(value) == "[Object Array]") { | 109 Object.prototype.toString.call(value) == "[Object Array]") { |
| 108 return "array"; | 110 return "array"; |
| 109 } | 111 } |
| 110 } else if (s == "number") { | 112 } else if (s == "number") { |
| 111 if (value % 1 == 0) { | 113 if (value % 1 == 0) { |
| 112 return "integer"; | 114 return "integer"; |
| 113 } | 115 } |
| 114 } | 116 } |
| 115 | 117 |
| 116 return s; | 118 return s; |
| 117 }; | 119 }; |
| 118 | 120 |
| 119 /** | 121 /** |
| 120 * Add types that may be referenced by validated schemas that reference them | 122 * Add types that may be referenced by validated schemas that reference them |
| 121 * with "$ref": <typeId>. Each type must be a valid schema and define an | 123 * with "$ref": <typeId>. Each type must be a valid schema and define an |
| 122 * "id" property. | 124 * "id" property. |
| 123 */ | 125 */ |
| 124 chrome.JSONSchemaValidator.prototype.addTypes = function(typeOrTypeList) { | 126 chromeHidden.JSONSchemaValidator.prototype.addTypes = function(typeOrTypeList) { |
| 125 function addType(validator, type) { | 127 function addType(validator, type) { |
| 126 if(!type.id) | 128 if(!type.id) |
| 127 throw "Attempt to addType with missing 'id' property"; | 129 throw "Attempt to addType with missing 'id' property"; |
| 128 validator.types[type.id] = type; | 130 validator.types[type.id] = type; |
| 129 } | 131 } |
| 130 | 132 |
| 131 if (typeOrTypeList instanceof Array) { | 133 if (typeOrTypeList instanceof Array) { |
| 132 for (var i = 0; i < typeOrTypeList.length; i++) { | 134 for (var i = 0; i < typeOrTypeList.length; i++) { |
| 133 addType(this, typeOrTypeList[i]); | 135 addType(this, typeOrTypeList[i]); |
| 134 } | 136 } |
| 135 } else { | 137 } else { |
| 136 addType(this, typeOrTypeList); | 138 addType(this, typeOrTypeList); |
| 137 } | 139 } |
| 138 } | 140 } |
| 139 | 141 |
| 140 /** | 142 /** |
| 141 * Validates an instance against a schema. The instance can be any JavaScript | 143 * Validates an instance against a schema. The instance can be any JavaScript |
| 142 * value and will be validated recursively. When this method returns, the | 144 * value and will be validated recursively. When this method returns, the |
| 143 * |errors| property will contain a list of errors, if any. | 145 * |errors| property will contain a list of errors, if any. |
| 144 */ | 146 */ |
| 145 chrome.JSONSchemaValidator.prototype.validate = function(instance, schema, | 147 chromeHidden.JSONSchemaValidator.prototype.validate = function( |
| 146 opt_path) { | 148 instance, schema, opt_path) { |
| 147 var path = opt_path || ""; | 149 var path = opt_path || ""; |
| 148 | 150 |
| 149 if (!schema) { | 151 if (!schema) { |
| 150 this.addError(path, "schemaRequired"); | 152 this.addError(path, "schemaRequired"); |
| 151 return; | 153 return; |
| 152 } | 154 } |
| 153 | 155 |
| 154 // If this schema defines itself as reference type, save it in this.types. | 156 // If this schema defines itself as reference type, save it in this.types. |
| 155 if (schema.id) | 157 if (schema.id) |
| 156 this.types[schema.id] = schema; | 158 this.types[schema.id] = schema; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 203 this.validateNumber(instance, schema, path); | 205 this.validateNumber(instance, schema, path); |
| 204 break; | 206 break; |
| 205 } | 207 } |
| 206 } | 208 } |
| 207 }; | 209 }; |
| 208 | 210 |
| 209 /** | 211 /** |
| 210 * Validates an instance against a choices schema. The instance must match at | 212 * Validates an instance against a choices schema. The instance must match at |
| 211 * least one of the provided choices. | 213 * least one of the provided choices. |
| 212 */ | 214 */ |
| 213 chrome.JSONSchemaValidator.prototype.validateChoices = function(instance, | 215 chromeHidden.JSONSchemaValidator.prototype.validateChoices = function( |
| 214 schema, | 216 instance, schema, path) { |
| 215 path) { | |
| 216 var originalErrors = this.errors; | 217 var originalErrors = this.errors; |
| 217 | 218 |
| 218 for (var i = 0; i < schema.choices.length; i++) { | 219 for (var i = 0; i < schema.choices.length; i++) { |
| 219 this.errors = []; | 220 this.errors = []; |
| 220 this.validate(instance, schema.choices[i], path); | 221 this.validate(instance, schema.choices[i], path); |
| 221 if (this.errors.length == 0) { | 222 if (this.errors.length == 0) { |
| 222 this.errors = originalErrors; | 223 this.errors = originalErrors; |
| 223 return; | 224 return; |
| 224 } | 225 } |
| 225 } | 226 } |
| 226 | 227 |
| 227 this.errors = originalErrors; | 228 this.errors = originalErrors; |
| 228 this.addError(path, "invalidChoice"); | 229 this.addError(path, "invalidChoice"); |
| 229 }; | 230 }; |
| 230 | 231 |
| 231 /** | 232 /** |
| 232 * Validates an instance against a schema with an enum type. Populates the | 233 * Validates an instance against a schema with an enum type. Populates the |
| 233 * |errors| property, and returns a boolean indicating whether the instance | 234 * |errors| property, and returns a boolean indicating whether the instance |
| 234 * validates. | 235 * validates. |
| 235 */ | 236 */ |
| 236 chrome.JSONSchemaValidator.prototype.validateEnum = function(instance, schema, | 237 chromeHidden.JSONSchemaValidator.prototype.validateEnum = function( |
| 237 path) { | 238 instance, schema, path) { |
| 238 for (var i = 0; i < schema.enum.length; i++) { | 239 for (var i = 0; i < schema.enum.length; i++) { |
| 239 if (instance === schema.enum[i]) | 240 if (instance === schema.enum[i]) |
| 240 return true; | 241 return true; |
| 241 } | 242 } |
| 242 | 243 |
| 243 this.addError(path, "invalidEnum", [schema.enum.join(", ")]); | 244 this.addError(path, "invalidEnum", [schema.enum.join(", ")]); |
| 244 return false; | 245 return false; |
| 245 }; | 246 }; |
| 246 | 247 |
| 247 /** | 248 /** |
| 248 * Validates an instance against an object schema and populates the errors | 249 * Validates an instance against an object schema and populates the errors |
| 249 * property. | 250 * property. |
| 250 */ | 251 */ |
| 251 chrome.JSONSchemaValidator.prototype.validateObject = function(instance, | 252 chromeHidden.JSONSchemaValidator.prototype.validateObject = function( |
| 252 schema, path) { | 253 instance, schema, path) { |
| 253 for (var prop in schema.properties) { | 254 for (var prop in schema.properties) { |
| 254 var propPath = path ? path + "." + prop : prop; | 255 var propPath = path ? path + "." + prop : prop; |
| 255 if (schema.properties[prop] == undefined) { | 256 if (schema.properties[prop] == undefined) { |
| 256 this.addError(propPath, "invalidPropertyType"); | 257 this.addError(propPath, "invalidPropertyType"); |
| 257 } else if (prop in instance && instance[prop] !== null && | 258 } else if (prop in instance && instance[prop] !== null && |
| 258 instance[prop] !== undefined) { | 259 instance[prop] !== undefined) { |
| 259 this.validate(instance[prop], schema.properties[prop], propPath); | 260 this.validate(instance[prop], schema.properties[prop], propPath); |
| 260 } else if (!schema.properties[prop].optional) { | 261 } else if (!schema.properties[prop].optional) { |
| 261 this.addError(propPath, "propertyRequired"); | 262 this.addError(propPath, "propertyRequired"); |
| 262 } | 263 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 274 this.validate(instance[prop], schema.additionalProperties, propPath); | 275 this.validate(instance[prop], schema.additionalProperties, propPath); |
| 275 else | 276 else |
| 276 this.addError(propPath, "unexpectedProperty"); | 277 this.addError(propPath, "unexpectedProperty"); |
| 277 } | 278 } |
| 278 }; | 279 }; |
| 279 | 280 |
| 280 /** | 281 /** |
| 281 * Validates an instance against an array schema and populates the errors | 282 * Validates an instance against an array schema and populates the errors |
| 282 * property. | 283 * property. |
| 283 */ | 284 */ |
| 284 chrome.JSONSchemaValidator.prototype.validateArray = function(instance, | 285 chromeHidden.JSONSchemaValidator.prototype.validateArray = function( |
| 285 schema, path) { | 286 instance, schema, path) { |
| 286 var typeOfItems = chrome.JSONSchemaValidator.getType(schema.items); | 287 var typeOfItems = chromeHidden.JSONSchemaValidator.getType(schema.items); |
| 287 | 288 |
| 288 if (typeOfItems == 'object') { | 289 if (typeOfItems == 'object') { |
| 289 if (schema.minItems && instance.length < schema.minItems) { | 290 if (schema.minItems && instance.length < schema.minItems) { |
| 290 this.addError(path, "arrayMinItems", [schema.minItems]); | 291 this.addError(path, "arrayMinItems", [schema.minItems]); |
| 291 } | 292 } |
| 292 | 293 |
| 293 if (typeof schema.maxItems != "undefined" && | 294 if (typeof schema.maxItems != "undefined" && |
| 294 instance.length > schema.maxItems) { | 295 instance.length > schema.maxItems) { |
| 295 this.addError(path, "arrayMaxItems", [schema.maxItems]); | 296 this.addError(path, "arrayMaxItems", [schema.maxItems]); |
| 296 } | 297 } |
| (...skipping 24 matching lines...) Expand all Loading... |
| 321 if (instance.length > schema.items.length) { | 322 if (instance.length > schema.items.length) { |
| 322 this.addError(path, "arrayMaxItems", [schema.items.length]); | 323 this.addError(path, "arrayMaxItems", [schema.items.length]); |
| 323 } | 324 } |
| 324 } | 325 } |
| 325 } | 326 } |
| 326 }; | 327 }; |
| 327 | 328 |
| 328 /** | 329 /** |
| 329 * Validates a string and populates the errors property. | 330 * Validates a string and populates the errors property. |
| 330 */ | 331 */ |
| 331 chrome.JSONSchemaValidator.prototype.validateString = function(instance, | 332 chromeHidden.JSONSchemaValidator.prototype.validateString = function( |
| 332 schema, path) { | 333 instance, schema, path) { |
| 333 if (schema.minLength && instance.length < schema.minLength) | 334 if (schema.minLength && instance.length < schema.minLength) |
| 334 this.addError(path, "stringMinLength", [schema.minLength]); | 335 this.addError(path, "stringMinLength", [schema.minLength]); |
| 335 | 336 |
| 336 if (schema.maxLength && instance.length > schema.maxLength) | 337 if (schema.maxLength && instance.length > schema.maxLength) |
| 337 this.addError(path, "stringMaxLength", [schema.maxLength]); | 338 this.addError(path, "stringMaxLength", [schema.maxLength]); |
| 338 | 339 |
| 339 if (schema.pattern && !schema.pattern.test(instance)) | 340 if (schema.pattern && !schema.pattern.test(instance)) |
| 340 this.addError(path, "stringPattern", [schema.pattern]); | 341 this.addError(path, "stringPattern", [schema.pattern]); |
| 341 }; | 342 }; |
| 342 | 343 |
| 343 /** | 344 /** |
| 344 * Validates a number and populates the errors property. The instance is | 345 * Validates a number and populates the errors property. The instance is |
| 345 * assumed to be a number. | 346 * assumed to be a number. |
| 346 */ | 347 */ |
| 347 chrome.JSONSchemaValidator.prototype.validateNumber = function(instance, | 348 chromeHidden.JSONSchemaValidator.prototype.validateNumber = function( |
| 348 schema, path) { | 349 instance, schema, path) { |
| 349 if (schema.minimum && instance < schema.minimum) | 350 if (schema.minimum && instance < schema.minimum) |
| 350 this.addError(path, "numberMinValue", [schema.minimum]); | 351 this.addError(path, "numberMinValue", [schema.minimum]); |
| 351 | 352 |
| 352 if (schema.maximum && instance > schema.maximum) | 353 if (schema.maximum && instance > schema.maximum) |
| 353 this.addError(path, "numberMaxValue", [schema.maximum]); | 354 this.addError(path, "numberMaxValue", [schema.maximum]); |
| 354 | 355 |
| 355 if (schema.maxDecimal && instance * Math.pow(10, schema.maxDecimal) % 1) | 356 if (schema.maxDecimal && instance * Math.pow(10, schema.maxDecimal) % 1) |
| 356 this.addError(path, "numberMaxDecimal", [schema.maxDecimal]); | 357 this.addError(path, "numberMaxDecimal", [schema.maxDecimal]); |
| 357 }; | 358 }; |
| 358 | 359 |
| 359 /** | 360 /** |
| 360 * Validates the primitive type of an instance and populates the errors | 361 * Validates the primitive type of an instance and populates the errors |
| 361 * property. Returns true if the instance validates, false otherwise. | 362 * property. Returns true if the instance validates, false otherwise. |
| 362 */ | 363 */ |
| 363 chrome.JSONSchemaValidator.prototype.validateType = function(instance, schema, | 364 chromeHidden.JSONSchemaValidator.prototype.validateType = function( |
| 364 path) { | 365 instance, schema, path) { |
| 365 var actualType = chrome.JSONSchemaValidator.getType(instance); | 366 var actualType = chromeHidden.JSONSchemaValidator.getType(instance); |
| 366 if (schema.type != actualType && !(schema.type == "number" && | 367 if (schema.type != actualType && !(schema.type == "number" && |
| 367 actualType == "integer")) { | 368 actualType == "integer")) { |
| 368 this.addError(path, "invalidType", [schema.type, actualType]); | 369 this.addError(path, "invalidType", [schema.type, actualType]); |
| 369 return false; | 370 return false; |
| 370 } | 371 } |
| 371 | 372 |
| 372 return true; | 373 return true; |
| 373 }; | 374 }; |
| 374 | 375 |
| 375 /** | 376 /** |
| 376 * Adds an error message. |key| is an index into the |messages| object. | 377 * Adds an error message. |key| is an index into the |messages| object. |
| 377 * |replacements| is an array of values to replace '*' characters in the | 378 * |replacements| is an array of values to replace '*' characters in the |
| 378 * message. | 379 * message. |
| 379 */ | 380 */ |
| 380 chrome.JSONSchemaValidator.prototype.addError = function(path, key, | 381 chromeHidden.JSONSchemaValidator.prototype.addError = function( |
| 381 replacements) { | 382 path, key, replacements) { |
| 382 this.errors.push({ | 383 this.errors.push({ |
| 383 path: path, | 384 path: path, |
| 384 message: chrome.JSONSchemaValidator.formatError(key, replacements) | 385 message: chromeHidden.JSONSchemaValidator.formatError(key, replacements) |
| 385 }); | 386 }); |
| 386 }; | 387 }; |
| 388 |
| 389 })(); |
| OLD | NEW |