OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 })(); |
OLD | NEW |