Index: chrome/renderer/resources/extensions/schema_generated_bindings.js |
diff --git a/chrome/renderer/resources/extensions/schema_generated_bindings.js b/chrome/renderer/resources/extensions/schema_generated_bindings.js |
index a935ff9db7e14d2a04668a7c42f63e3f3ae8b218..067ca90d5df810ff22527f97194667f7c6ed12cc 100644 |
--- a/chrome/renderer/resources/extensions/schema_generated_bindings.js |
+++ b/chrome/renderer/resources/extensions/schema_generated_bindings.js |
@@ -34,37 +34,118 @@ var chrome = chrome || {}; |
} |
} |
+ // Returns the defined signature of the API function. |
+ chromeHidden.fullSignature = function(name, schemas) { |
Aaron Boodman
2012/02/06 01:30:52
Function names should be a verb phrase, like getFu
|
+ var getTypeName = function(schema) { |
+ var typeName = schema.type + " " + schema.name; |
+ if (schema.optional) |
+ return "optional " + typeName; |
+ return typeName; |
+ }; |
+ |
+ var typeNames = schemas.map(getTypeName); |
+ return name +"(" + typeNames.join(", ") + ")"; |
+ }; |
+ |
+ // Return true if arguments match a given candidate's schema. |
+ chromeHidden.matchCandidate = function(args, schemas) { |
Aaron Boodman
2012/02/06 01:30:52
argumentsMatchSignature()?
|
+ if (args.length != schemas.length) |
+ return false; |
+ |
+ var validator = new chromeHidden.JSONSchemaValidator(); |
+ validator.addTypes(chromeHidden.validationTypes); |
+ for (var i = 0; i < schemas.length; i++) { |
+ if (args[i] !== null || !schemas[i].optional) |
+ validator.validate(args[i], schemas[i]); |
Aaron Boodman
2012/02/06 01:30:52
This does not do the right thing. It will check al
|
+ } |
+ return validator.errors.length == 0; |
+ }; |
+ |
+ // Generate candidates for a given API function scehma. |
+ chromeHidden.getCandidates = function(schemas) { |
Aaron Boodman
2012/02/06 01:30:52
Can we call this something other than 'candidate'.
|
+ if (schemas.length === 0) |
+ return [[]]; |
+ |
+ var candidates = []; |
+ var remaining = chromeHidden.getCandidates(schemas.slice(1)); |
+ for (var i = 0; i < remaining.length; i++) { |
+ var candidate = remaining[i].slice(0); |
+ candidate.unshift(schemas[0]); |
+ candidates.push(candidate); |
+ } |
+ if (schemas[0].optional) |
+ return candidates.concat(remaining); |
+ return candidates; |
+ }; |
+ |
// Validate arguments. |
chromeHidden.validationTypes = []; |
chromeHidden.validate = function(args, schemas) { |
- if (args.length > schemas.length) |
- throw new Error("Too many arguments."); |
+ var candidates = chromeHidden.getCandidates(schemas); |
+ var match = null; |
+ for (var i = 0; i < candidates.length && match === null; i++) { |
+ if (chromeHidden.matchCandidate(args, candidates[i])) |
+ match = candidates[i]; |
+ } |
+ if (match === null) |
+ throw new Error("Invocation does not match definition."); |
+ |
+ // Insert nulls into omitted optional parameters. |
+ var normalized_args = []; |
+ var ai = 0; |
+ for (var si = 0; si < schemas.length; si++) { |
+ if (schemas[si] === match[ai]) |
+ normalized_args.push(args[ai++]); |
+ else |
+ normalized_args.push(null); |
+ } |
+ return normalized_args; |
Aaron Boodman
2012/02/06 01:30:52
Hm. This doesn't seem right. We never throw the va
|
+ }; |
- for (var i = 0; i < schemas.length; i++) { |
- if (i in args && args[i] !== null && args[i] !== undefined) { |
- var validator = new chromeHidden.JSONSchemaValidator(); |
- validator.addTypes(chromeHidden.validationTypes); |
- validator.validate(args[i], schemas[i]); |
- if (validator.errors.length == 0) |
- continue; |
+ // Returns true if there is a non-null value that both would accept. |
+ chromeHidden.schemaOverlap = function(schema1, schema2) { |
+ if (schema1.choices) { |
+ for (var i = 0; i < schema1.choices.length; i++) |
+ if (chromeHidden.schemaOverlap(schema1.choices[i], schema2)) |
+ return true; |
+ } else if (schema2.choices) { |
+ for (var i = 0; i < schema2.choices.length; i++) |
+ if (chromeHidden.schemaOverlap(schema1, schema2.choices[i])) |
+ return true; |
+ } else { |
+ var type1 = schema1.type || |
+ chromeHidden.validationTypes[schema1['$ref']].type; |
+ var type2 = schema2.type || |
+ chromeHidden.validationTypes[schema2['$ref']].type; |
- var message = "Invalid value for argument " + (i + 1) + ". "; |
- for (var i = 0, err; err = validator.errors[i]; i++) { |
- if (err.path) { |
- message += "Property '" + err.path + "': "; |
- } |
- message += err.message; |
- message = message.substring(0, message.length - 1); |
- message += ", "; |
- } |
- message = message.substring(0, message.length - 2); |
- message += "."; |
+ if (type1 == "any" || type2 == "any") |
+ return true; |
+ return type1 == type2; |
+ } |
+ return false; |
+ }; |
+ |
+ // Validate schema for API function. |
+ chromeHidden.schemaAmbiguous = function(schema) { |
+ var candidatesAmbiguous = function(candidate1, candidate2) { |
+ if (candidate1.length != candidate2.length) |
+ return false; |
- throw new Error(message); |
- } else if (!schemas[i].optional) { |
- throw new Error("Parameter " + (i + 1) + " is required."); |
+ for (var i = 0; i < candidate1.length; i++) { |
+ if (!chromeHidden.schemaOverlap(candidate1[i], candidate2[i])) |
+ return false; |
+ } |
+ return true; |
+ }; |
+ |
+ candidates = chromeHidden.getCandidates(schema); |
+ for (var i = 0; i < candidates.length; i++) { |
+ for (var j = i + 1; j < candidates.length; j++) { |
+ if (candidatesAmbiguous(candidates[i], candidates[j])) |
+ return true; |
} |
} |
+ return false; |
}; |
// Callback handling. |
@@ -437,13 +518,23 @@ var chrome = chrome || {}; |
var apiFunction = {}; |
apiFunction.definition = functionDef; |
apiFunction.name = apiDef.namespace + "." + functionDef.name; |
+ if (chromeHidden.validateAPI && |
+ chromeHidden.schemaAmbiguous(apiFunction.definition.parameters)) |
+ throw new Error(apiFunction.name + " is ambiguous"); |
+ |
apiFunctions.register(apiFunction.name, apiFunction); |
module[functionDef.name] = (function() { |
var args = arguments; |
if (this.updateArgumentsPreValidate) |
args = this.updateArgumentsPreValidate.apply(this, args); |
- chromeHidden.validate(args, this.definition.parameters); |
+ try { |
Aaron Boodman
2012/02/06 01:30:52
I think it would be a bit cleaner to just have val
|
+ args = chromeHidden.validate(args, this.definition.parameters); |
+ } catch (err) { |
+ throw new Error("Invocation doesn't match definition " + |
+ chromeHidden.fullSignature(this.name, |
Aaron Boodman
2012/02/06 01:30:52
Can we also include the signature that was actuall
|
+ this.definition.parameters)); |
+ } |
if (this.updateArgumentsPostValidate) |
args = this.updateArgumentsPostValidate.apply(this, args); |