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

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

Powered by Google App Engine
This is Rietveld 408576698