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

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: null and undefined arguments now handled. Comments added and variables renamed. Created 8 years, 9 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 14 matching lines...) Expand all
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 // Validate arguments. 34 // Validate arguments.
35 chromeHidden.validationTypes = []; 35 chromeHidden.schemaValidator = new chromeHidden.JSONSchemaValidator();
Aaron Boodman 2012/03/01 22:59:05 It isn't necessary to put this on chromeHidden. It
36 chromeHidden.validate = function(args, schemas) { 36 chromeHidden.validate = function(args, parameterSchemas) {
37 if (args.length > schemas.length) 37 if (args.length > parameterSchemas.length)
38 throw new Error("Too many arguments."); 38 throw new Error("Too many arguments.");
39 39
40 for (var i = 0; i < schemas.length; i++) { 40 for (var i = 0; i < parameterSchemas.length; i++) {
41 if (i in args && args[i] !== null && args[i] !== undefined) { 41 if (i in args && args[i] !== null && args[i] !== undefined) {
42 var validator = new chromeHidden.JSONSchemaValidator(); 42 chromeHidden.schemaValidator.resetErrors();
43 validator.addTypes(chromeHidden.validationTypes); 43 chromeHidden.schemaValidator.validate(args[i], parameterSchemas[i]);
44 validator.validate(args[i], schemas[i]); 44 if (chromeHidden.schemaValidator.errors.length == 0)
45 if (validator.errors.length == 0)
46 continue; 45 continue;
47 46
48 var message = "Invalid value for argument " + (i + 1) + ". "; 47 var message = "Invalid value for argument " + (i + 1) + ". ";
49 for (var i = 0, err; err = validator.errors[i]; i++) { 48 for (var i = 0; i < chromeHidden.schemaValidator.length; i++) {
49 var err = chromeHidden.schemaValidator.errors[i];
50 if (err.path) { 50 if (err.path) {
51 message += "Property '" + err.path + "': "; 51 message += "Property '" + err.path + "': ";
52 } 52 }
53 message += err.message; 53 message += err.message;
54 message = message.substring(0, message.length - 1); 54 message = message.substring(0, message.length - 1);
55 message += ", "; 55 message += ", ";
56 } 56 }
57 message = message.substring(0, message.length - 2); 57 message = message.substring(0, message.length - 2);
58 message += "."; 58 message += ".";
59 59
60 throw new Error(message); 60 throw new Error(message);
61 } else if (!schemas[i].optional) { 61 } else if (!parameterSchemas[i].optional) {
62 throw new Error("Parameter " + (i + 1) + " is required."); 62 throw new Error("Parameter " + (i + 1) + " is required.");
63 } 63 }
64 } 64 }
65 }; 65 };
66 66
67 // Generate all possible signatures for a given API function.
68 function getSignatures(parameterSchemas) {
69 if (parameterSchemas.length === 0)
70 return [[]];
71
72 var signatures = [];
73 var remaining = getSignatures(parameterSchemas.slice(1));
74 for (var i = 0; i < remaining.length; i++)
75 signatures.push([parameterSchemas[0]].concat(remaining[i]))
76
77 if (parameterSchemas[0].optional)
78 return signatures.concat(remaining);
79 return signatures;
80 };
81
82 // Return true if arguments match a given signature's schema.
83 function argumentsMatchSignature(args, candidateSignature) {
84 if (args.length != candidateSignature.length)
85 return false;
86
87 for (var i = 0; i < candidateSignature.length; i++) {
88 var argType = chromeHidden.JSONSchemaValidator.getType(args[i]);
89 if (!chromeHidden.schemaValidator.isValidSchemaType(
90 argType, candidateSignature[i]))
91 return false;
92 }
93 return true;
94 };
95
96 // Finds the function signature for the given arguments.
97 function resolveSignature(args, definedSignature) {
98 var candidateSignatures = getSignatures(definedSignature);
99 for (var i = 0; i < candidateSignatures.length; i++) {
100 if (argumentsMatchSignature(args, candidateSignatures[i]))
101 return candidateSignatures[i];
102 }
103 return null;
104 };
105
106 // Returns a string representing the defined signature of the API function.
107 // Example return value for chrome.windows.getCurrent:
108 // "windows.getCurrent(optional object populate, function callback)"
109 function getParameterSignatureString(name, definedSignature) {
110 var getSchemaTypeString = function(schema) {
111 var schemaTypes =
112 chromeHidden.schemaValidator.getAllTypesForSchema(schema);
113 var typeName = schemaTypes.join(" or ") + " " + schema.name;
114 if (schema.optional)
115 return "optional " + typeName;
116 return typeName;
117 };
118
119 var typeNames = definedSignature.map(getSchemaTypeString);
120 return name + "(" + typeNames.join(", ") + ")";
121 };
122
123 // Returns a string representing a call to an API function.
124 // Example return value for call: chrome.windows.get(1, callback) is:
125 // "windows.get(int, function)"
126 function getArgumentSignatureString(name, args) {
127 var typeNames = args.map(chromeHidden.JSONSchemaValidator.getType);
128 return name + "(" + typeNames.join(", ") + ")";
129 };
130
131 // Finds the correct signature for the given arguments, then validates the
132 // arguments against that signature. Returns a 'normalized' arguments list
133 // where nulls are inserted where optional parameters were omitted.
134 chromeHidden.updateArgumentsValidate = function(args, funDef) {
135 var definedSignature = funDef.definition.parameters;
136 var resolvedSignature = resolveSignature(args, definedSignature);
137 if (!resolvedSignature)
138 throw new Error("Invocation of form " +
139 getArgumentSignatureString(funDef.name, args) +
140 " doesn't match definition " +
141 getParameterSignatureString(funDef.name, definedSignature));
142 chromeHidden.validate(args, resolvedSignature);
143
144 var normalizedArgs = [];
145 var ai = 0;
146 for (var si = 0; si < definedSignature.length; si++) {
147 if (definedSignature[si] === resolvedSignature[ai])
148 normalizedArgs.push(args[ai++]);
149 else
150 normalizedArgs.push(null);
151 }
152 return normalizedArgs;
153 };
154
155 // Validates that a given schema for an API function is not ambiguous.
156 function isDefinedSignatureAmbiguous(definedSignature) {
157 var signaturesAmbiguous = function(signature1, signature2) {
158 if (signature1.length != signature2.length)
159 return false;
160
161 for (var i = 0; i < signature1.length; i++) {
162 if (!chromeHidden.schemaValidator.checkSchemaOverlap(signature1[i],
163 signature2[i]))
164 return false;
165 }
166 return true;
167 };
168
169 var candidateSignatures = getSignatures(definedSignature);
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
67 // Callback handling. 179 // Callback handling.
68 var requests = []; 180 var requests = [];
69 chromeHidden.handleResponse = function(requestId, name, 181 chromeHidden.handleResponse = function(requestId, name,
70 success, response, error) { 182 success, response, error) {
71 try { 183 try {
72 var request = requests[requestId]; 184 var request = requests[requestId];
73 if (success) { 185 if (success) {
74 delete chrome.extension.lastError; 186 delete chrome.extension.lastError;
75 } else { 187 } else {
76 if (!error) { 188 if (!error) {
(...skipping 351 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 540
429 // See comment on internalAPIs at the top. 541 // See comment on internalAPIs at the top.
430 var mod = apiDef.internal ? internalAPIs : chrome; 542 var mod = apiDef.internal ? internalAPIs : chrome;
431 543
432 var namespaces = apiDef.namespace.split('.'); 544 var namespaces = apiDef.namespace.split('.');
433 for (var index = 0, name; name = namespaces[index]; index++) { 545 for (var index = 0, name; name = namespaces[index]; index++) {
434 mod[name] = mod[name] || {}; 546 mod[name] = mod[name] || {};
435 mod = mod[name]; 547 mod = mod[name];
436 } 548 }
437 549
438 // Add types to global validationTypes 550 // Add types to global schemaValidator
439 if (apiDef.types) { 551 if (apiDef.types) {
440 apiDef.types.forEach(function(t) { 552 apiDef.types.forEach(function(t) {
441 if (!isSchemaNodeSupported(t, platform, manifestVersion)) 553 if (!isSchemaNodeSupported(t, platform, manifestVersion))
442 return; 554 return;
443 555
444 chromeHidden.validationTypes.push(t); 556 chromeHidden.schemaValidator.addTypes(t);
445 if (t.type == 'object' && customTypes[t.id]) { 557 if (t.type == 'object' && customTypes[t.id]) {
446 customTypes[t.id].prototype.setSchema(t); 558 customTypes[t.id].prototype.setSchema(t);
447 } 559 }
448 }); 560 });
449 } 561 }
450 562
451 // Returns whether access to the content of a schema should be denied, 563 // Returns whether access to the content of a schema should be denied,
452 // based on the presence of "unprivileged" and whether this is an 564 // based on the presence of "unprivileged" and whether this is an
453 // extension process (versus e.g. a content script). 565 // extension process (versus e.g. a content script).
454 function isSchemaAccessAllowed(itemSchema) { 566 function isSchemaAccessAllowed(itemSchema) {
(...skipping 28 matching lines...) Expand all
483 } 595 }
484 if (!isSchemaAccessAllowed(functionDef)) { 596 if (!isSchemaAccessAllowed(functionDef)) {
485 apiFunctions.registerUnavailable(apiFunctionName); 597 apiFunctions.registerUnavailable(apiFunctionName);
486 addUnprivilegedAccessGetter(mod, functionDef.name); 598 addUnprivilegedAccessGetter(mod, functionDef.name);
487 return; 599 return;
488 } 600 }
489 601
490 var apiFunction = {}; 602 var apiFunction = {};
491 apiFunction.definition = functionDef; 603 apiFunction.definition = functionDef;
492 apiFunction.name = apiFunctionName; 604 apiFunction.name = apiFunctionName;
493 apiFunctions.register(apiFunctionName, apiFunction); 605
606 // Validate API for ambiguity only in DEBUG mode.
607 // We do not validate 'extension.sendRequest' because we know it is
608 // ambiguous. We disambiguate calls in 'updateArgumentsPrevalidate'.
609 // TODO(aa): It would be best to run this in a unit test, but in order
610 // to do that we would need to better factor this code so that it
611 // didn't depend on so much v8::Extension machinery.
612 if (chromeHidden.validateAPI &&
613 apiFunction.name != "extension.sendRequest" &&
614 isDefinedSignatureAmbiguous(apiFunction.definition.parameters))
615 throw new Error(apiFunction.name + " is ambiguous");
616 apiFunctions.register(apiFunction.name, apiFunction);
494 617
495 mod[functionDef.name] = (function() { 618 mod[functionDef.name] = (function() {
496 var args = arguments; 619 var args = Array.prototype.slice.call(arguments);
497 if (this.updateArgumentsPreValidate) 620 if (this.updateArgumentsPreValidate)
498 args = this.updateArgumentsPreValidate.apply(this, args); 621 args = this.updateArgumentsPreValidate.apply(this, args);
499 chromeHidden.validate(args, this.definition.parameters); 622
623 args = chromeHidden.updateArgumentsValidate(args, this);
500 if (this.updateArgumentsPostValidate) 624 if (this.updateArgumentsPostValidate)
501 args = this.updateArgumentsPostValidate.apply(this, args); 625 args = this.updateArgumentsPostValidate.apply(this, args);
502 626
503 var retval; 627 var retval;
504 if (this.handleRequest) { 628 if (this.handleRequest) {
505 retval = this.handleRequest.apply(this, args); 629 retval = this.handleRequest.apply(this, args);
506 } else { 630 } else {
507 retval = sendRequest(this.name, args, 631 retval = sendRequest(this.name, args,
508 this.definition.parameters, 632 this.definition.parameters,
509 {customCallback: this.customCallback}); 633 {customCallback: this.customCallback});
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
627 // See http://crbug.com/100242 751 // See http://crbug.com/100242
628 if (chrome.webstorePrivate) { 752 if (chrome.webstorePrivate) {
629 chrome.webstorePrivate.beginInstallWithManifest2 = 753 chrome.webstorePrivate.beginInstallWithManifest2 =
630 chrome.webstorePrivate.beginInstallWithManifest3; 754 chrome.webstorePrivate.beginInstallWithManifest3;
631 } 755 }
632 756
633 if (chrome.test) 757 if (chrome.test)
634 chrome.test.getApiDefinitions = GetExtensionAPIDefinition; 758 chrome.test.getApiDefinitions = GetExtensionAPIDefinition;
635 }); 759 });
636 })(); 760 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698