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

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

Issue 10535030: Allow updateArgumentsPostValidate to support callbacks and added / removed arguments. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Proper syntax Created 8 years, 6 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 | Annotate | Revision Log
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 // TODO(battre): cleanup the usage of packages everywhere, as described here 8 // TODO(battre): cleanup the usage of packages everywhere, as described here
9 // http://codereview.chromium.org/10392008/diff/38/chrome/renderer/resources/e xtensions/schema_generated_bindings.js 9 // http://codereview.chromium.org/10392008/diff/38/chrome/renderer/resources/e xtensions/schema_generated_bindings.js
10 10
11 require('json_schema'); 11 require('json_schema');
12 require('event_bindings'); 12 require('event_bindings');
13 var GetExtensionAPIDefinition = 13 var GetExtensionAPIDefinition =
14 requireNative('apiDefinitions').GetExtensionAPIDefinition; 14 requireNative('apiDefinitions').GetExtensionAPIDefinition;
15 var sendRequest = require('sendRequest').sendRequest; 15 var sendRequest = require('sendRequest').sendRequest;
16 var utils = require('utils'); 16 var utils = require('utils');
17 var isDevChannel = requireNative('channel').IsDevChannel; 17 var isDevChannel = requireNative('channel').IsDevChannel;
18 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); 18 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
19 var schemaUtils = require('schemaUtils');
19 20
20 // The object to generate the bindings for "internal" APIs in, so that 21 // The object to generate the bindings for "internal" APIs in, so that
21 // extensions can't directly call them (without access to chromeHidden), 22 // extensions can't directly call them (without access to chromeHidden),
22 // but are still needed for internal mechanisms of extensions (e.g. events). 23 // but are still needed for internal mechanisms of extensions (e.g. events).
23 // 24 //
24 // This is distinct to the "*Private" APIs which are controlled via 25 // This is distinct to the "*Private" APIs which are controlled via
25 // having strict permissions and aren't generated *anywhere* unless needed. 26 // having strict permissions and aren't generated *anywhere* unless needed.
26 var internalAPIs = {}; 27 var internalAPIs = {};
27 chromeHidden.internalAPIs = internalAPIs; 28 chromeHidden.internalAPIs = internalAPIs;
28 29
29 // Validate arguments.
30 var schemaValidator = new chromeHidden.JSONSchemaValidator();
31 chromeHidden.validate = function(args, parameterSchemas) {
32 if (args.length > parameterSchemas.length)
33 throw new Error("Too many arguments.");
34
35 for (var i = 0; i < parameterSchemas.length; i++) {
36 if (i in args && args[i] !== null && args[i] !== undefined) {
37 schemaValidator.resetErrors();
38 schemaValidator.validate(args[i], parameterSchemas[i]);
39 if (schemaValidator.errors.length == 0)
40 continue;
41
42 var message = "Invalid value for argument " + (i + 1) + ". ";
43 for (var i = 0, err; err = schemaValidator.errors[i]; i++) {
44 if (err.path) {
45 message += "Property '" + err.path + "': ";
46 }
47 message += err.message;
48 message = message.substring(0, message.length - 1);
49 message += ", ";
50 }
51 message = message.substring(0, message.length - 2);
52 message += ".";
53
54 throw new Error(message);
55 } else if (!parameterSchemas[i].optional) {
56 throw new Error("Parameter " + (i + 1) + " is required.");
57 }
58 }
59 };
60
61 // Generate all possible signatures for a given API function.
62 function getSignatures(parameterSchemas) {
63 if (parameterSchemas.length === 0)
64 return [[]];
65
66 var signatures = [];
67 var remaining = getSignatures(parameterSchemas.slice(1));
68 for (var i = 0; i < remaining.length; i++)
69 signatures.push([parameterSchemas[0]].concat(remaining[i]))
70
71 if (parameterSchemas[0].optional)
72 return signatures.concat(remaining);
73 return signatures;
74 };
75
76 // Return true if arguments match a given signature's schema.
77 function argumentsMatchSignature(args, candidateSignature) {
78 if (args.length != candidateSignature.length)
79 return false;
80
81 for (var i = 0; i < candidateSignature.length; i++) {
82 var argType = chromeHidden.JSONSchemaValidator.getType(args[i]);
83 if (!schemaValidator.isValidSchemaType(argType, candidateSignature[i]))
84 return false;
85 }
86 return true;
87 };
88
89 // Finds the function signature for the given arguments.
90 function resolveSignature(args, definedSignature) {
91 var candidateSignatures = getSignatures(definedSignature);
92 for (var i = 0; i < candidateSignatures.length; i++) {
93 if (argumentsMatchSignature(args, candidateSignatures[i]))
94 return candidateSignatures[i];
95 }
96 return null;
97 };
98
99 // Returns a string representing the defined signature of the API function.
100 // Example return value for chrome.windows.getCurrent:
101 // "windows.getCurrent(optional object populate, function callback)"
102 function getParameterSignatureString(name, definedSignature) {
103 var getSchemaTypeString = function(schema) {
104 var schemaTypes = schemaValidator.getAllTypesForSchema(schema);
105 var typeName = schemaTypes.join(" or ") + " " + schema.name;
106 if (schema.optional)
107 return "optional " + typeName;
108 return typeName;
109 };
110
111 var typeNames = definedSignature.map(getSchemaTypeString);
112 return name + "(" + typeNames.join(", ") + ")";
113 };
114
115 // Returns a string representing a call to an API function.
116 // Example return value for call: chrome.windows.get(1, callback) is:
117 // "windows.get(int, function)"
118 function getArgumentSignatureString(name, args) {
119 var typeNames = args.map(chromeHidden.JSONSchemaValidator.getType);
120 return name + "(" + typeNames.join(", ") + ")";
121 };
122
123 // Finds the correct signature for the given arguments, then validates the
124 // arguments against that signature. Returns a 'normalized' arguments list
125 // where nulls are inserted where optional parameters were omitted.
126 function normalizeArgumentsAndValidate(args, funDef) {
127 if (funDef.allowAmbiguousOptionalArguments) {
128 chromeHidden.validate(args, funDef.definition.parameters);
129 return args;
130 }
131
132 var definedSignature = funDef.definition.parameters;
133 var resolvedSignature = resolveSignature(args, definedSignature);
134 if (!resolvedSignature)
135 throw new Error("Invocation of form " +
136 getArgumentSignatureString(funDef.name, args) +
137 " doesn't match definition " +
138 getParameterSignatureString(funDef.name, definedSignature));
139
140 chromeHidden.validate(args, resolvedSignature);
141
142 var normalizedArgs = [];
143 var ai = 0;
144 for (var si = 0; si < definedSignature.length; si++) {
145 if (definedSignature[si] === resolvedSignature[ai])
146 normalizedArgs.push(args[ai++]);
147 else
148 normalizedArgs.push(null);
149 }
150 return normalizedArgs;
151 };
152
153 // Validates that a given schema for an API function is not ambiguous.
154 function isFunctionSignatureAmbiguous(functionDef) {
155 if (functionDef.allowAmbiguousOptionalArguments)
156 return false;
157
158 var signaturesAmbiguous = function(signature1, signature2) {
159 if (signature1.length != signature2.length)
160 return false;
161
162 for (var i = 0; i < signature1.length; i++) {
163 if (!schemaValidator.checkSchemaOverlap(signature1[i], signature2[i]))
164 return false;
165 }
166 return true;
167 };
168
169 var candidateSignatures = getSignatures(functionDef.parameters);
170 for (var i = 0; i < candidateSignatures.length; i++) {
171 for (var j = i + 1; j < candidateSignatures.length; j++) {
172 if (signaturesAmbiguous(candidateSignatures[i], candidateSignatures[j]))
173 return true;
174 }
175 }
176 return false;
177 };
178
179 // Stores the name and definition of each API function, with methods to 30 // Stores the name and definition of each API function, with methods to
180 // modify their behaviour (such as a custom way to handle requests to the 31 // modify their behaviour (such as a custom way to handle requests to the
181 // API, a custom callback, etc). 32 // API, a custom callback, etc).
182 function APIFunctions() { 33 function APIFunctions() {
183 this._apiFunctions = {}; 34 this._apiFunctions = {};
184 this._unavailableApiFunctions = {}; 35 this._unavailableApiFunctions = {};
185 } 36 }
186 APIFunctions.prototype.register = function(apiName, apiFunction) { 37 APIFunctions.prototype.register = function(apiName, apiFunction) {
187 this._apiFunctions[apiName] = apiFunction; 38 this._apiFunctions[apiName] = apiFunction;
188 }; 39 };
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 } 139 }
289 customHooks[namespace] = fn; 140 customHooks[namespace] = fn;
290 }; 141 };
291 142
292 function CustomBindingsObject() { 143 function CustomBindingsObject() {
293 } 144 }
294 CustomBindingsObject.prototype.setSchema = function(schema) { 145 CustomBindingsObject.prototype.setSchema = function(schema) {
295 // The functions in the schema are in list form, so we move them into a 146 // The functions in the schema are in list form, so we move them into a
296 // dictionary for easier access. 147 // dictionary for easier access.
297 var self = this; 148 var self = this;
298 self.parameters = {}; 149 self.functionSchemas = {};
299 schema.functions.forEach(function(f) { 150 schema.functions.forEach(function(f) {
300 self.parameters[f.name] = f.parameters; 151 self.functionSchemas[f.name] = {
152 name: f.name,
153 definition: f
154 }
301 }); 155 });
302 }; 156 };
303 157
304 // Registers a custom type referenced via "$ref" fields in the API schema 158 // Registers a custom type referenced via "$ref" fields in the API schema
305 // JSON. 159 // JSON.
306 var customTypes = {}; 160 var customTypes = {};
307 chromeHidden.registerCustomType = function(typeName, customTypeFactory) { 161 chromeHidden.registerCustomType = function(typeName, customTypeFactory) {
308 var customType = customTypeFactory(); 162 var customType = customTypeFactory();
309 customType.prototype = new CustomBindingsObject(); 163 customType.prototype = new CustomBindingsObject();
310 customTypes[typeName] = customType; 164 customTypes[typeName] = customType;
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 mod[name] = mod[name] || {}; 237 mod[name] = mod[name] || {};
384 mod = mod[name]; 238 mod = mod[name];
385 } 239 }
386 240
387 // Add types to global schemaValidator 241 // Add types to global schemaValidator
388 if (apiDef.types) { 242 if (apiDef.types) {
389 apiDef.types.forEach(function(t) { 243 apiDef.types.forEach(function(t) {
390 if (!isSchemaNodeSupported(t, platform, manifestVersion)) 244 if (!isSchemaNodeSupported(t, platform, manifestVersion))
391 return; 245 return;
392 246
393 schemaValidator.addTypes(t); 247 schemaUtils.schemaValidator.addTypes(t);
394 if (t.type == 'object' && customTypes[t.id]) { 248 if (t.type == 'object' && customTypes[t.id]) {
395 customTypes[t.id].prototype.setSchema(t); 249 customTypes[t.id].prototype.setSchema(t);
396 } 250 }
397 }); 251 });
398 } 252 }
399 253
400 // Returns whether access to the content of a schema should be denied, 254 // Returns whether access to the content of a schema should be denied,
401 // based on the presence of "unprivileged" and whether this is an 255 // based on the presence of "unprivileged" and whether this is an
402 // extension process (versus e.g. a content script). 256 // extension process (versus e.g. a content script).
403 function isSchemaAccessAllowed(itemSchema) { 257 function isSchemaAccessAllowed(itemSchema) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
437 } 291 }
438 292
439 var apiFunction = {}; 293 var apiFunction = {};
440 apiFunction.definition = functionDef; 294 apiFunction.definition = functionDef;
441 apiFunction.name = apiFunctionName; 295 apiFunction.name = apiFunctionName;
442 296
443 // TODO(aa): It would be best to run this in a unit test, but in order 297 // TODO(aa): It would be best to run this in a unit test, but in order
444 // to do that we would need to better factor this code so that it 298 // to do that we would need to better factor this code so that it
445 // doesn't depend on so much v8::Extension machinery. 299 // doesn't depend on so much v8::Extension machinery.
446 if (chromeHidden.validateAPI && 300 if (chromeHidden.validateAPI &&
447 isFunctionSignatureAmbiguous(apiFunction.definition)) { 301 schemaUtils.isFunctionSignatureAmbiguous(
302 apiFunction.definition)) {
448 throw new Error( 303 throw new Error(
449 apiFunction.name + ' has ambiguous optional arguments. ' + 304 apiFunction.name + ' has ambiguous optional arguments. ' +
450 'To implement custom disambiguation logic, add ' + 305 'To implement custom disambiguation logic, add ' +
451 '"allowAmbiguousOptionalArguments" to the function\'s schema.'); 306 '"allowAmbiguousOptionalArguments" to the function\'s schema.');
452 } 307 }
453 308
454 apiFunctions.register(apiFunction.name, apiFunction); 309 apiFunctions.register(apiFunction.name, apiFunction);
455 310
456 mod[functionDef.name] = (function() { 311 mod[functionDef.name] = (function() {
457 var args = Array.prototype.slice.call(arguments); 312 var args = Array.prototype.slice.call(arguments);
458 if (this.updateArgumentsPreValidate) 313 if (this.updateArgumentsPreValidate)
459 args = this.updateArgumentsPreValidate.apply(this, args); 314 args = this.updateArgumentsPreValidate.apply(this, args);
460 315
461 args = normalizeArgumentsAndValidate(args, this); 316 args = schemaUtils.normalizeArgumentsAndValidate(args, this);
462 if (this.updateArgumentsPostValidate) 317 if (this.updateArgumentsPostValidate)
463 args = this.updateArgumentsPostValidate.apply(this, args); 318 args = this.updateArgumentsPostValidate.apply(this, args);
464 319
465 var retval; 320 var retval;
466 if (this.handleRequest) { 321 if (this.handleRequest) {
467 retval = this.handleRequest.apply(this, args); 322 retval = this.handleRequest.apply(this, args);
468 } else { 323 } else {
469 retval = sendRequest(this.name, args, 324 retval = sendRequest(this.name, args,
470 this.definition.parameters, 325 this.definition.parameters,
471 {customCallback: this.customCallback}); 326 {customCallback: this.customCallback});
472 } 327 }
473 328
474 // Validate return value if defined - only in debug. 329 // Validate return value if defined - only in debug.
475 if (chromeHidden.validateCallbacks && 330 if (chromeHidden.validateCallbacks &&
476 chromeHidden.validate &&
477 this.definition.returns) { 331 this.definition.returns) {
478 chromeHidden.validate([retval], [this.definition.returns]); 332 schemaUtils.validate([retval], [this.definition.returns]);
479 } 333 }
480 return retval; 334 return retval;
481 }).bind(apiFunction); 335 }).bind(apiFunction);
482 }); 336 });
483 } 337 }
484 338
485 // Setup Events 339 // Setup Events
486 if (apiDef.events) { 340 if (apiDef.events) {
487 apiDef.events.forEach(function(eventDef) { 341 apiDef.events.forEach(function(eventDef) {
488 if (eventDef.name in mod) { 342 if (eventDef.name in mod) {
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
589 // beginInstallWithManifest2. 443 // beginInstallWithManifest2.
590 // See http://crbug.com/100242 444 // See http://crbug.com/100242
591 if (chrome.webstorePrivate) { 445 if (chrome.webstorePrivate) {
592 chrome.webstorePrivate.beginInstallWithManifest2 = 446 chrome.webstorePrivate.beginInstallWithManifest2 =
593 chrome.webstorePrivate.beginInstallWithManifest3; 447 chrome.webstorePrivate.beginInstallWithManifest3;
594 } 448 }
595 449
596 if (chrome.test) 450 if (chrome.test)
597 chrome.test.getApiDefinitions = GetExtensionAPIDefinition; 451 chrome.test.getApiDefinitions = GetExtensionAPIDefinition;
598 }); 452 });
OLDNEW
« no previous file with comments | « chrome/renderer/resources/extensions/event.js ('k') | chrome/renderer/resources/extensions/schema_utils.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698