Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: chrome/renderer/resources/extensions/schema_generated_bindings.js

Issue 9317072: Allow omitting optional parameters for Extensions API functions (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Better error message distinguishing signature errors and type validation errors. Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 // This script contains privileged chrome extension related javascript APIs. 5 // This script contains privileged chrome extension related javascript APIs.
6 // It is loaded by pages whose URL has the chrome-extension protocol. 6 // It is loaded by pages whose URL has the chrome-extension protocol.
7 7
8 var chrome = chrome || {}; 8 var chrome = chrome || {};
9 (function() { 9 (function() {
10 native function GetChromeHidden(); 10 native function GetChromeHidden();
(...skipping 13 matching lines...) Expand all
24 var internalAPIs = {}; 24 var internalAPIs = {};
25 chromeHidden.internalAPIs = internalAPIs; 25 chromeHidden.internalAPIs = internalAPIs;
26 26
27 function forEach(dict, f) { 27 function forEach(dict, f) {
28 for (key in dict) { 28 for (key in dict) {
29 if (dict.hasOwnProperty(key)) 29 if (dict.hasOwnProperty(key))
30 f(key, dict[key]); 30 f(key, dict[key]);
31 } 31 }
32 } 32 }
33 33
34 // Returns a list of types that this schema allows.
35 chromeHidden.getAllTypesForSchema = function(schema) {
36 var validator = new chromeHidden.JSONSchemaValidator();
37 validator.addTypes(chromeHidden.validationTypes);
38 var schema_types = [];
39 if (schema.type)
40 schema_types.push(schema.type);
41 if (schema.choices) {
42 for (var i = 0; i < schema.choices.length; i++)
43 schema_types.push(schema.choices[i].type);
44 }
45 if (schema['$ref'])
46 schema_types.push(validator.types[schema['$ref']].type);
47 return schema_types;
48 };
49
50 // Returns true if |schema| would accept an argument of type |type|.
51 chromeHidden.isValidSchemaType = function(type, schema) {
52 schema_types = chromeHidden.getAllTypesForSchema(schema);
53 for (var i = 0; i < schema_types.length; i++) {
54 if (schema_types[i] == "any" || type == schema_types[i])
55 return true;
56 }
57 return type == "any";
58 };
59
60 // Returns true if there is a non-null value that both schemas would accept.
61 chromeHidden.checkSchemaOverlap = function(schema1, schema2) {
62 schema_types1 = chromeHidden.getAllTypesForSchema(schema1);
63 for (var i = 0; i < schema_types1.length; i++) {
64 if (chromeHidden.isValidSchemaType(schema_types1[i], schema2))
65 return true;
66 }
67 return false;
68 };
69
70 // Generate all possible signatures for a given API function scehma.
Matt Perry 2012/02/16 00:31:08 schema*
Matt Tytel 2012/02/24 01:29:27 Done.
71 chromeHidden.getSignatures = function(schemas) {
72 if (schemas.length === 0)
73 return [[]];
74
75 var signatures = [];
76 var remaining = chromeHidden.getSignatures(schemas.slice(1));
77 for (var i = 0; i < remaining.length; i++) {
78 var signature = remaining[i].slice(0);
79 signature.unshift(schemas[0]);
80 signatures.push(signature);
Matt Perry 2012/02/16 00:31:08 could do this more simply as signatures.push([sche
Matt Tytel 2012/02/24 01:29:27 Done.
81 }
82 if (schemas[0].optional)
83 return signatures.concat(remaining);
84 return signatures;
85 };
86
87 // Return true if arguments match a given signature's schema.
88 chromeHidden.argumentsMatchSignature = function(args, schemas) {
89 if (args.length != schemas.length)
90 return false;
91
92 for (var i = 0; i < schemas.length; i++) {
93 if (!chromeHidden.isValidSchemaType(
94 chromeHidden.JSONSchemaValidator.getType(args[i]), schemas[i]))
95 return false;
96 }
97 return true;
98 };
99
100 // Finds the function signature for the given arguments.
101 chromeHidden.matchSignature = function(args, schemas) {
Matt Perry 2012/02/16 00:31:08 The implementation here is somewhat wasteful. You
Matt Tytel 2012/02/16 04:35:02 I think this a code efficiency vs code simplicity
Matt Perry 2012/02/16 19:21:29 Yep. I think you made the right tradeoff here.
102 var signatures = chromeHidden.getSignatures(schemas);
103 for (var i = 0; i < signatures.length; i++) {
104 if (chromeHidden.argumentsMatchSignature(args, signatures[i]))
105 return signatures[i];
106 }
107 return null;
108 };
109
110 // Validates that a given schema for an API function is not ambiguous.
111 chromeHidden.isSchemaAmbiguous = function(schema) {
112 var signaturesAmbiguous = function(signature1, signature2) {
113 if (signature1.length != signature2.length)
114 return false;
115
116 for (var i = 0; i < signature1.length; i++) {
117 if (!chromeHidden.checkSchemaOverlap(signature1[i], signature2[i]))
118 return false;
119 }
120 return true;
121 };
122
123 var signatures = chromeHidden.getSignatures(schema);
124 for (var i = 0; i < signatures.length; i++) {
125 for (var j = i + 1; j < signatures.length; j++) {
126 if (signaturesAmbiguous(signatures[i], signatures[j]))
127 return true;
128 }
129 }
130 return false;
131 };
132
34 // Validate arguments. 133 // Validate arguments.
35 chromeHidden.validationTypes = []; 134 chromeHidden.validationTypes = [];
36 chromeHidden.validate = function(args, schemas) { 135 chromeHidden.validate = function(args, schemas) {
37 if (args.length > schemas.length) 136 if (args.length > schemas.length)
38 throw new Error("Too many arguments."); 137 throw new Error("Too many arguments.");
39 138
40 for (var i = 0; i < schemas.length; i++) { 139 for (var i = 0; i < schemas.length; i++) {
41 if (i in args && args[i] !== null && args[i] !== undefined) { 140 if (i in args && args[i] !== null && args[i] !== undefined) {
42 var validator = new chromeHidden.JSONSchemaValidator(); 141 var validator = new chromeHidden.JSONSchemaValidator();
43 validator.addTypes(chromeHidden.validationTypes); 142 validator.addTypes(chromeHidden.validationTypes);
(...skipping 13 matching lines...) Expand all
57 message = message.substring(0, message.length - 2); 156 message = message.substring(0, message.length - 2);
58 message += "."; 157 message += ".";
59 158
60 throw new Error(message); 159 throw new Error(message);
61 } else if (!schemas[i].optional) { 160 } else if (!schemas[i].optional) {
62 throw new Error("Parameter " + (i + 1) + " is required."); 161 throw new Error("Parameter " + (i + 1) + " is required.");
63 } 162 }
64 } 163 }
65 }; 164 };
66 165
166 // Returns a string representing the defined signature of the API function.
167 // Example return value for chrome.windows.getCurrent:
168 // "windows.getCurrent(optional object populate, function callback)"
169 chromeHidden.getSchemaSignatureString = function(name, schemas) {
170 var getSchemaTypeString = function(schema) {
171 var schema_types = chromeHidden.getAllTypesForSchema(schema);
172 var type_name = schema_types.join(" or ") + " " + schema.name;
173 if (schema.optional)
174 return "optional " + type_name;
175 return type_name;
176 };
177
178 var type_names = schemas.map(getSchemaTypeString);
179 return name +"(" + type_names.join(", ") + ")";
Matt Perry 2012/02/16 00:31:08 space before "("
Matt Tytel 2012/02/24 01:29:27 Done.
180 };
181
182 // Returns a string representing a call to an API function
183 // Example return value for call: chrome.windows.get(1, callback) is:
184 // "windows.get(int, function)"
185 chromeHidden.getArgumentSignatureString = function(name, args) {
186 var type_names = args.map(chromeHidden.JSONSchemaValidator.getType);
187 return name +"(" + type_names.join(", ") + ")";
Matt Perry 2012/02/16 00:31:08 space before "("
Matt Tytel 2012/02/24 01:29:27 Done.
188 };
189
190 // Finds the correct signature for the given arguments
Matt Perry 2012/02/16 00:31:08 end sentence with .
Matt Tytel 2012/02/24 01:29:27 Done.
191 chromeHidden.matchSignatureAndValidate = function(args, fun_def) {
192 var schemas = fun_def.definition.parameters;
193 var match = chromeHidden.matchSignature(args, schemas);
194 if (!match)
195 throw new Error("Invocation of form " +
196 chromeHidden.getArgumentSignatureString(fun_def.name, args) +
197 " doesn't match definition " +
198 chromeHidden.getSchemaSignatureString(fun_def.name, schemas));
199 chromeHidden.validate(args, match);
200
201 var normalized_args = [];
202 var ai = 0;
203 for (var si = 0; si < schemas.length; si++) {
204 if (schemas[si] === match[ai])
205 normalized_args.push(args[ai++]);
206 else
207 normalized_args.push(null);
208 }
209 return normalized_args;
210 };
211
67 // Callback handling. 212 // Callback handling.
68 var requests = []; 213 var requests = [];
69 chromeHidden.handleResponse = function(requestId, name, 214 chromeHidden.handleResponse = function(requestId, name,
70 success, response, error) { 215 success, response, error) {
71 try { 216 try {
72 var request = requests[requestId]; 217 var request = requests[requestId];
73 if (success) { 218 if (success) {
74 delete chrome.extension.lastError; 219 delete chrome.extension.lastError;
75 } else { 220 } else {
76 if (!error) { 221 if (!error) {
(...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after
429 574
430 if (functionDef.name in module || 575 if (functionDef.name in module ||
431 addUnprivilegedAccessGetter(module, functionDef.name, 576 addUnprivilegedAccessGetter(module, functionDef.name,
432 functionDef.unprivileged)) { 577 functionDef.unprivileged)) {
433 return; 578 return;
434 } 579 }
435 580
436 var apiFunction = {}; 581 var apiFunction = {};
437 apiFunction.definition = functionDef; 582 apiFunction.definition = functionDef;
438 apiFunction.name = apiDef.namespace + "." + functionDef.name; 583 apiFunction.name = apiDef.namespace + "." + functionDef.name;
584
585 // Validate API for ambiguity only in DEBUG mode.
586 if (chromeHidden.validateAPI &&
587 chromeHidden.isSchemaAmbiguous(apiFunction.definition.parameters))
588 throw new Error(apiFunction.name + " is ambiguous");
439 apiFunctions.register(apiFunction.name, apiFunction); 589 apiFunctions.register(apiFunction.name, apiFunction);
440 590
441 module[functionDef.name] = (function() { 591 module[functionDef.name] = (function() {
442 var args = arguments; 592 var args = Array.prototype.slice.call(arguments);
443 if (this.updateArgumentsPreValidate) 593 if (this.updateArgumentsPreValidate)
444 args = this.updateArgumentsPreValidate.apply(this, args); 594 args = this.updateArgumentsPreValidate.apply(this, args);
445 chromeHidden.validate(args, this.definition.parameters); 595
596 args = chromeHidden.matchSignatureAndValidate(args, this);
446 if (this.updateArgumentsPostValidate) 597 if (this.updateArgumentsPostValidate)
447 args = this.updateArgumentsPostValidate.apply(this, args); 598 args = this.updateArgumentsPostValidate.apply(this, args);
448 599
449 var retval; 600 var retval;
450 if (this.handleRequest) { 601 if (this.handleRequest) {
451 retval = this.handleRequest.apply(this, args); 602 retval = this.handleRequest.apply(this, args);
452 } else { 603 } else {
453 retval = sendRequest(this.name, args, 604 retval = sendRequest(this.name, args,
454 this.definition.parameters, 605 this.definition.parameters,
455 {customCallback: this.customCallback}); 606 {customCallback: this.customCallback});
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 // See http://crbug.com/100242 726 // See http://crbug.com/100242
576 if (chrome.webstorePrivate) { 727 if (chrome.webstorePrivate) {
577 chrome.webstorePrivate.beginInstallWithManifest2 = 728 chrome.webstorePrivate.beginInstallWithManifest2 =
578 chrome.webstorePrivate.beginInstallWithManifest3; 729 chrome.webstorePrivate.beginInstallWithManifest3;
579 } 730 }
580 731
581 if (chrome.test) 732 if (chrome.test)
582 chrome.test.getApiDefinitions = GetExtensionAPIDefinition; 733 chrome.test.getApiDefinitions = GetExtensionAPIDefinition;
583 }); 734 });
584 })(); 735 })();
OLDNEW
« no previous file with comments | « chrome/renderer/renderer_resources.grd ('k') | chrome/renderer/resources/extensions/tabs_custom_bindings.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698