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 16 matching lines...) Expand all Loading... | |
27 return !!resolved; | 27 return !!resolved; |
28 } | 28 } |
29 | 29 |
30 function forEach(dict, f) { | 30 function forEach(dict, f) { |
31 for (key in dict) { | 31 for (key in dict) { |
32 if (dict.hasOwnProperty(key)) | 32 if (dict.hasOwnProperty(key)) |
33 f(key, dict[key]); | 33 f(key, dict[key]); |
34 } | 34 } |
35 } | 35 } |
36 | 36 |
37 // Returns the defined signature of the API function. | |
38 chromeHidden.fullSignature = function(name, schemas) { | |
Aaron Boodman
2012/02/06 01:30:52
Function names should be a verb phrase, like getFu
| |
39 var getTypeName = function(schema) { | |
40 var typeName = schema.type + " " + schema.name; | |
41 if (schema.optional) | |
42 return "optional " + typeName; | |
43 return typeName; | |
44 }; | |
45 | |
46 var typeNames = schemas.map(getTypeName); | |
47 return name +"(" + typeNames.join(", ") + ")"; | |
48 }; | |
49 | |
50 // Return true if arguments match a given candidate's schema. | |
51 chromeHidden.matchCandidate = function(args, schemas) { | |
Aaron Boodman
2012/02/06 01:30:52
argumentsMatchSignature()?
| |
52 if (args.length != schemas.length) | |
53 return false; | |
54 | |
55 var validator = new chromeHidden.JSONSchemaValidator(); | |
56 validator.addTypes(chromeHidden.validationTypes); | |
57 for (var i = 0; i < schemas.length; i++) { | |
58 if (args[i] !== null || !schemas[i].optional) | |
59 validator.validate(args[i], schemas[i]); | |
Aaron Boodman
2012/02/06 01:30:52
This does not do the right thing. It will check al
| |
60 } | |
61 return validator.errors.length == 0; | |
62 }; | |
63 | |
64 // Generate candidates for a given API function scehma. | |
65 chromeHidden.getCandidates = function(schemas) { | |
Aaron Boodman
2012/02/06 01:30:52
Can we call this something other than 'candidate'.
| |
66 if (schemas.length === 0) | |
67 return [[]]; | |
68 | |
69 var candidates = []; | |
70 var remaining = chromeHidden.getCandidates(schemas.slice(1)); | |
71 for (var i = 0; i < remaining.length; i++) { | |
72 var candidate = remaining[i].slice(0); | |
73 candidate.unshift(schemas[0]); | |
74 candidates.push(candidate); | |
75 } | |
76 if (schemas[0].optional) | |
77 return candidates.concat(remaining); | |
78 return candidates; | |
79 }; | |
80 | |
37 // Validate arguments. | 81 // Validate arguments. |
38 chromeHidden.validationTypes = []; | 82 chromeHidden.validationTypes = []; |
39 chromeHidden.validate = function(args, schemas) { | 83 chromeHidden.validate = function(args, schemas) { |
40 if (args.length > schemas.length) | 84 var candidates = chromeHidden.getCandidates(schemas); |
41 throw new Error("Too many arguments."); | 85 var match = null; |
86 for (var i = 0; i < candidates.length && match === null; i++) { | |
87 if (chromeHidden.matchCandidate(args, candidates[i])) | |
88 match = candidates[i]; | |
89 } | |
90 if (match === null) | |
91 throw new Error("Invocation does not match definition."); | |
42 | 92 |
43 for (var i = 0; i < schemas.length; i++) { | 93 // Insert nulls into omitted optional parameters. |
44 if (i in args && args[i] !== null && args[i] !== undefined) { | 94 var normalized_args = []; |
45 var validator = new chromeHidden.JSONSchemaValidator(); | 95 var ai = 0; |
46 validator.addTypes(chromeHidden.validationTypes); | 96 for (var si = 0; si < schemas.length; si++) { |
47 validator.validate(args[i], schemas[i]); | 97 if (schemas[si] === match[ai]) |
48 if (validator.errors.length == 0) | 98 normalized_args.push(args[ai++]); |
49 continue; | 99 else |
100 normalized_args.push(null); | |
101 } | |
102 return normalized_args; | |
Aaron Boodman
2012/02/06 01:30:52
Hm. This doesn't seem right. We never throw the va
| |
103 }; | |
50 | 104 |
51 var message = "Invalid value for argument " + (i + 1) + ". "; | 105 // Returns true if there is a non-null value that both would accept. |
52 for (var i = 0, err; err = validator.errors[i]; i++) { | 106 chromeHidden.schemaOverlap = function(schema1, schema2) { |
53 if (err.path) { | 107 if (schema1.choices) { |
54 message += "Property '" + err.path + "': "; | 108 for (var i = 0; i < schema1.choices.length; i++) |
55 } | 109 if (chromeHidden.schemaOverlap(schema1.choices[i], schema2)) |
56 message += err.message; | 110 return true; |
57 message = message.substring(0, message.length - 1); | 111 } else if (schema2.choices) { |
58 message += ", "; | 112 for (var i = 0; i < schema2.choices.length; i++) |
59 } | 113 if (chromeHidden.schemaOverlap(schema1, schema2.choices[i])) |
60 message = message.substring(0, message.length - 2); | 114 return true; |
61 message += "."; | 115 } else { |
116 var type1 = schema1.type || | |
117 chromeHidden.validationTypes[schema1['$ref']].type; | |
118 var type2 = schema2.type || | |
119 chromeHidden.validationTypes[schema2['$ref']].type; | |
62 | 120 |
63 throw new Error(message); | 121 if (type1 == "any" || type2 == "any") |
64 } else if (!schemas[i].optional) { | 122 return true; |
65 throw new Error("Parameter " + (i + 1) + " is required."); | 123 return type1 == type2; |
124 } | |
125 return false; | |
126 }; | |
127 | |
128 // Validate schema for API function. | |
129 chromeHidden.schemaAmbiguous = function(schema) { | |
130 var candidatesAmbiguous = function(candidate1, candidate2) { | |
131 if (candidate1.length != candidate2.length) | |
132 return false; | |
133 | |
134 for (var i = 0; i < candidate1.length; i++) { | |
135 if (!chromeHidden.schemaOverlap(candidate1[i], candidate2[i])) | |
136 return false; | |
137 } | |
138 return true; | |
139 }; | |
140 | |
141 candidates = chromeHidden.getCandidates(schema); | |
142 for (var i = 0; i < candidates.length; i++) { | |
143 for (var j = i + 1; j < candidates.length; j++) { | |
144 if (candidatesAmbiguous(candidates[i], candidates[j])) | |
145 return true; | |
66 } | 146 } |
67 } | 147 } |
148 return false; | |
68 }; | 149 }; |
69 | 150 |
70 // Callback handling. | 151 // Callback handling. |
71 var requests = []; | 152 var requests = []; |
72 chromeHidden.handleResponse = function(requestId, name, | 153 chromeHidden.handleResponse = function(requestId, name, |
73 success, response, error) { | 154 success, response, error) { |
74 try { | 155 try { |
75 var request = requests[requestId]; | 156 var request = requests[requestId]; |
76 if (success) { | 157 if (success) { |
77 delete chrome.extension.lastError; | 158 delete chrome.extension.lastError; |
(...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
430 | 511 |
431 if (functionDef.name in module || | 512 if (functionDef.name in module || |
432 addUnprivilegedAccessGetter(module, functionDef.name, | 513 addUnprivilegedAccessGetter(module, functionDef.name, |
433 functionDef.unprivileged)) { | 514 functionDef.unprivileged)) { |
434 return; | 515 return; |
435 } | 516 } |
436 | 517 |
437 var apiFunction = {}; | 518 var apiFunction = {}; |
438 apiFunction.definition = functionDef; | 519 apiFunction.definition = functionDef; |
439 apiFunction.name = apiDef.namespace + "." + functionDef.name; | 520 apiFunction.name = apiDef.namespace + "." + functionDef.name; |
521 if (chromeHidden.validateAPI && | |
522 chromeHidden.schemaAmbiguous(apiFunction.definition.parameters)) | |
523 throw new Error(apiFunction.name + " is ambiguous"); | |
524 | |
440 apiFunctions.register(apiFunction.name, apiFunction); | 525 apiFunctions.register(apiFunction.name, apiFunction); |
441 | 526 |
442 module[functionDef.name] = (function() { | 527 module[functionDef.name] = (function() { |
443 var args = arguments; | 528 var args = arguments; |
444 if (this.updateArgumentsPreValidate) | 529 if (this.updateArgumentsPreValidate) |
445 args = this.updateArgumentsPreValidate.apply(this, args); | 530 args = this.updateArgumentsPreValidate.apply(this, args); |
446 chromeHidden.validate(args, this.definition.parameters); | 531 try { |
Aaron Boodman
2012/02/06 01:30:52
I think it would be a bit cleaner to just have val
| |
532 args = chromeHidden.validate(args, this.definition.parameters); | |
533 } catch (err) { | |
534 throw new Error("Invocation doesn't match definition " + | |
535 chromeHidden.fullSignature(this.name, | |
Aaron Boodman
2012/02/06 01:30:52
Can we also include the signature that was actuall
| |
536 this.definition.parameters)); | |
537 } | |
447 if (this.updateArgumentsPostValidate) | 538 if (this.updateArgumentsPostValidate) |
448 args = this.updateArgumentsPostValidate.apply(this, args); | 539 args = this.updateArgumentsPostValidate.apply(this, args); |
449 | 540 |
450 var retval; | 541 var retval; |
451 if (this.handleRequest) { | 542 if (this.handleRequest) { |
452 retval = this.handleRequest.apply(this, args); | 543 retval = this.handleRequest.apply(this, args); |
453 } else { | 544 } else { |
454 retval = sendRequest(this.name, args, | 545 retval = sendRequest(this.name, args, |
455 this.definition.parameters, | 546 this.definition.parameters, |
456 {customCallback: this.customCallback}); | 547 {customCallback: this.customCallback}); |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
574 // See http://crbug.com/100242 | 665 // See http://crbug.com/100242 |
575 if (apiExists("webstorePrivate")) { | 666 if (apiExists("webstorePrivate")) { |
576 chrome.webstorePrivate.beginInstallWithManifest2 = | 667 chrome.webstorePrivate.beginInstallWithManifest2 = |
577 chrome.webstorePrivate.beginInstallWithManifest3; | 668 chrome.webstorePrivate.beginInstallWithManifest3; |
578 } | 669 } |
579 | 670 |
580 if (apiExists("test")) | 671 if (apiExists("test")) |
581 chrome.test.getApiDefinitions = GetExtensionAPIDefinition; | 672 chrome.test.getApiDefinitions = GetExtensionAPIDefinition; |
582 }); | 673 }); |
583 })(); | 674 })(); |
OLD | NEW |