Chromium Code Reviews| 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 13 matching lines...) Expand all Loading... | |
| 24 var internalAPIs = {}; | 24 var internalAPIs = {}; |
| 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 // Generate all possible signatures for a given API function schema. | |
|
not at google - send to devlin
2012/02/24 03:59:56
i think these methods can be grouped with the othe
Matt Tytel
2012/02/29 04:36:37
Done.
| |
| 35 function getSignatures(paramList) { | |
| 36 if (paramList.length === 0) | |
| 37 return [[]]; | |
| 38 | |
| 39 var signatures = []; | |
| 40 var remaining = getSignatures(paramList.slice(1)); | |
| 41 for (var i = 0; i < remaining.length; i++) | |
| 42 signatures.push([paramList[0]].concat(remaining[i])) | |
| 43 | |
| 44 if (paramList[0].optional) | |
| 45 return signatures.concat(remaining); | |
| 46 return signatures; | |
| 47 }; | |
| 48 | |
| 49 // Return true if arguments match a given signature's schema. | |
| 50 function argumentsMatchSignature(args, paramList) { | |
| 51 if (args.length != paramList.length) | |
| 52 return false; | |
| 53 | |
| 54 for (var i = 0; i < paramList.length; i++) { | |
| 55 if (!chromeHidden.schemaValidator.isValidSchemaType( | |
| 56 chromeHidden.JSONSchemaValidator.getType(args[i]), paramList[i])) | |
| 57 return false; | |
| 58 } | |
| 59 return true; | |
| 60 }; | |
| 61 | |
| 62 // Finds the function signature for the given arguments. | |
| 63 function resolveSignature(args, paramList) { | |
| 64 var candidateSignatures = getSignatures(paramList); | |
| 65 for (var i = 0; i < candidateSignatures.length; i++) { | |
| 66 if (argumentsMatchSignature(args, candidateSignatures[i])) | |
| 67 return candidateSignatures[i]; | |
| 68 } | |
| 69 return null; | |
| 70 }; | |
| 71 | |
| 72 // Validates that a given schema for an API function is not ambiguous. | |
| 73 function isParameterSchemaAmbiguous(parameterSchema) { | |
| 74 var signaturesAmbiguous = function(signature1, signature2) { | |
| 75 if (signature1.length != signature2.length) | |
| 76 return false; | |
| 77 | |
| 78 for (var i = 0; i < signature1.length; i++) { | |
| 79 if (!chromeHidden.schemaValidator.checkSchemaOverlap(signature1[i], | |
| 80 signature2[i])) | |
| 81 return false; | |
| 82 } | |
| 83 return true; | |
| 84 }; | |
| 85 | |
| 86 var signatures = getSignatures(parameterSchema); | |
| 87 for (var i = 0; i < signatures.length; i++) { | |
| 88 for (var j = i + 1; j < signatures.length; j++) { | |
| 89 if (signaturesAmbiguous(signatures[i], signatures[j])) | |
| 90 return true; | |
| 91 } | |
| 92 } | |
| 93 return false; | |
| 94 }; | |
| 95 | |
| 34 // Validate arguments. | 96 // Validate arguments. |
| 35 chromeHidden.validationTypes = []; | 97 chromeHidden.schemaValidator = new chromeHidden.JSONSchemaValidator(); |
| 36 chromeHidden.validate = function(args, schemas) { | 98 chromeHidden.validate = function(args, paramList) { |
| 37 if (args.length > schemas.length) | 99 if (args.length > paramList.length) |
| 38 throw new Error("Too many arguments."); | 100 throw new Error("Too many arguments."); |
| 39 | 101 |
| 40 for (var i = 0; i < schemas.length; i++) { | 102 for (var i = 0; i < paramList.length; i++) { |
| 41 if (i in args && args[i] !== null && args[i] !== undefined) { | 103 if (i in args && args[i] !== null && args[i] !== undefined) { |
| 42 var validator = new chromeHidden.JSONSchemaValidator(); | 104 chromeHidden.schemaValidator.resetErrors(); |
| 43 validator.addTypes(chromeHidden.validationTypes); | 105 chromeHidden.schemaValidator.validate(args[i], paramList[i]); |
| 44 validator.validate(args[i], schemas[i]); | 106 if (chromeHidden.schemaValidator.errors.length == 0) |
| 45 if (validator.errors.length == 0) | |
| 46 continue; | 107 continue; |
| 47 | 108 |
| 48 var message = "Invalid value for argument " + (i + 1) + ". "; | 109 var message = "Invalid value for argument " + (i + 1) + ". "; |
| 49 for (var i = 0, err; err = validator.errors[i]; i++) { | 110 for (var i = 0; i < chromeHidden.schemaValidator.length; i++) { |
| 111 var err = chromeHidden.schemaValidator.errors[i]; | |
| 50 if (err.path) { | 112 if (err.path) { |
| 51 message += "Property '" + err.path + "': "; | 113 message += "Property '" + err.path + "': "; |
| 52 } | 114 } |
| 53 message += err.message; | 115 message += err.message; |
| 54 message = message.substring(0, message.length - 1); | 116 message = message.substring(0, message.length - 1); |
| 55 message += ", "; | 117 message += ", "; |
| 56 } | 118 } |
| 57 message = message.substring(0, message.length - 2); | 119 message = message.substring(0, message.length - 2); |
| 58 message += "."; | 120 message += "."; |
| 59 | 121 |
| 60 throw new Error(message); | 122 throw new Error(message); |
| 61 } else if (!schemas[i].optional) { | 123 } else if (!paramList[i].optional) { |
| 62 throw new Error("Parameter " + (i + 1) + " is required."); | 124 throw new Error("Parameter " + (i + 1) + " is required."); |
| 63 } | 125 } |
| 64 } | 126 } |
| 65 }; | 127 }; |
| 66 | 128 |
| 129 // Returns a string representing the defined signature of the API function. | |
| 130 // Example return value for chrome.windows.getCurrent: | |
| 131 // "windows.getCurrent(optional object populate, function callback)" | |
| 132 function getParameterSignatureString(name, paramList) { | |
| 133 var getSchemaTypeString = function(schema) { | |
| 134 var schemaTypes = | |
| 135 chromeHidden.schemaValidator.getAllTypesForSchema(schema); | |
| 136 var typeName = schemaTypes.join(" or ") + " " + schema.name; | |
| 137 if (schema.optional) | |
| 138 return "optional " + typeName; | |
| 139 return typeName; | |
| 140 }; | |
| 141 | |
| 142 var typeNames = paramList.map(getSchemaTypeString); | |
| 143 return name + "(" + typeNames.join(", ") + ")"; | |
| 144 }; | |
| 145 | |
| 146 // Returns a string representing a call to an API function. | |
| 147 // Example return value for call: chrome.windows.get(1, callback) is: | |
| 148 // "windows.get(int, function)" | |
| 149 function getArgumentSignatureString(name, args) { | |
| 150 var typeNames = args.map(chromeHidden.JSONSchemaValidator.getType); | |
| 151 return name + "(" + typeNames.join(", ") + ")"; | |
| 152 }; | |
| 153 | |
| 154 // Finds the correct signature for the given arguments, then validates the | |
| 155 // arguments against that signature. Returns a 'normalized' arguments list | |
| 156 // where nulls are inserted where optional parameters were omitted. | |
| 157 chromeHidden.updateArgumentsValidate = function(args, funDef) { | |
| 158 var paramList = funDef.definition.parameters; | |
| 159 var resolvedSignature = resolveSignature(args, paramList); | |
| 160 if (!resolvedSignature) | |
| 161 throw new Error("Invocation of form " + | |
| 162 getArgumentSignatureString(funDef.name, args) + | |
| 163 " doesn't match definition " + | |
| 164 getParameterSignatureString(funDef.name, paramList)); | |
| 165 chromeHidden.validate(args, resolvedSignature); | |
| 166 | |
| 167 var normalizedArgs = []; | |
| 168 var ai = 0; | |
| 169 for (var si = 0; si < paramList.length; si++) { | |
| 170 if (paramList[si] === resolvedSignature[ai]) | |
| 171 normalizedArgs.push(args[ai++]); | |
| 172 else | |
| 173 normalizedArgs.push(null); | |
| 174 } | |
| 175 return normalizedArgs; | |
| 176 }; | |
| 177 | |
| 67 // Callback handling. | 178 // Callback handling. |
| 68 var requests = []; | 179 var requests = []; |
| 69 chromeHidden.handleResponse = function(requestId, name, | 180 chromeHidden.handleResponse = function(requestId, name, |
| 70 success, response, error) { | 181 success, response, error) { |
| 71 try { | 182 try { |
| 72 var request = requests[requestId]; | 183 var request = requests[requestId]; |
| 73 if (success) { | 184 if (success) { |
| 74 delete chrome.extension.lastError; | 185 delete chrome.extension.lastError; |
| 75 } else { | 186 } else { |
| 76 if (!error) { | 187 if (!error) { |
| (...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 384 | 495 |
| 385 // See comment on internalAPIs at the top. | 496 // See comment on internalAPIs at the top. |
| 386 var mod = apiDef.internal ? internalAPIs : chrome; | 497 var mod = apiDef.internal ? internalAPIs : chrome; |
| 387 | 498 |
| 388 var namespaces = apiDef.namespace.split('.'); | 499 var namespaces = apiDef.namespace.split('.'); |
| 389 for (var index = 0, name; name = namespaces[index]; index++) { | 500 for (var index = 0, name; name = namespaces[index]; index++) { |
| 390 mod[name] = mod[name] || {}; | 501 mod[name] = mod[name] || {}; |
| 391 mod = mod[name]; | 502 mod = mod[name]; |
| 392 } | 503 } |
| 393 | 504 |
| 394 // Add types to global validationTypes | 505 // Add types to global schemaValidator |
| 395 if (apiDef.types) { | 506 if (apiDef.types) { |
| 396 apiDef.types.forEach(function(t) { | 507 apiDef.types.forEach(function(t) { |
| 397 if (!isSchemaNodeSupported(t, platform, manifestVersion)) | 508 if (!isSchemaNodeSupported(t, platform, manifestVersion)) |
| 398 return; | 509 return; |
| 399 | 510 |
| 400 chromeHidden.validationTypes.push(t); | 511 chromeHidden.schemaValidator.addTypes(t); |
| 401 if (t.type == 'object' && customTypes[t.id]) { | 512 if (t.type == 'object' && customTypes[t.id]) { |
| 402 customTypes[t.id].prototype.setSchema(t); | 513 customTypes[t.id].prototype.setSchema(t); |
| 403 } | 514 } |
| 404 }); | 515 }); |
| 405 } | 516 } |
| 406 | 517 |
| 407 // Returns whether access to the content of a schema should be denied, | 518 // Returns whether access to the content of a schema should be denied, |
| 408 // based on the presence of "unprivileged" and whether this is an | 519 // based on the presence of "unprivileged" and whether this is an |
| 409 // extension process (versus e.g. a content script). | 520 // extension process (versus e.g. a content script). |
| 410 function isSchemaAccessAllowed(itemSchema) { | 521 function isSchemaAccessAllowed(itemSchema) { |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 433 if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) | 544 if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) |
| 434 return; | 545 return; |
| 435 if (!isSchemaAccessAllowed(functionDef)) { | 546 if (!isSchemaAccessAllowed(functionDef)) { |
| 436 addUnprivilegedAccessGetter(mod, functionDef.name); | 547 addUnprivilegedAccessGetter(mod, functionDef.name); |
| 437 return; | 548 return; |
| 438 } | 549 } |
| 439 | 550 |
| 440 var apiFunction = {}; | 551 var apiFunction = {}; |
| 441 apiFunction.definition = functionDef; | 552 apiFunction.definition = functionDef; |
| 442 apiFunction.name = apiDef.namespace + "." + functionDef.name; | 553 apiFunction.name = apiDef.namespace + "." + functionDef.name; |
| 554 | |
| 555 // Validate API for ambiguity only in DEBUG mode. | |
| 556 // TODO(aa): It would be best to run this in a unit test, but in order | |
| 557 // to do that we would need to better factor this code so that it | |
| 558 // didn't depend on so much v8::Extension machinery. | |
| 559 if (chromeHidden.validateAPI && | |
| 560 isParameterSchemaAmbiguous(apiFunction.definition.parameters)) | |
| 561 throw new Error(apiFunction.name + " is ambiguous"); | |
| 562 | |
| 443 apiFunctions.register(apiFunction.name, apiFunction); | 563 apiFunctions.register(apiFunction.name, apiFunction); |
| 444 | |
| 445 mod[functionDef.name] = (function() { | 564 mod[functionDef.name] = (function() { |
| 446 var args = arguments; | 565 var args = Array.prototype.slice.call(arguments); |
| 447 if (this.updateArgumentsPreValidate) | 566 if (this.updateArgumentsPreValidate) |
| 448 args = this.updateArgumentsPreValidate.apply(this, args); | 567 args = this.updateArgumentsPreValidate.apply(this, args); |
| 449 chromeHidden.validate(args, this.definition.parameters); | 568 |
| 569 args = chromeHidden.updateArgumentsValidate(args, this); | |
| 450 if (this.updateArgumentsPostValidate) | 570 if (this.updateArgumentsPostValidate) |
| 451 args = this.updateArgumentsPostValidate.apply(this, args); | 571 args = this.updateArgumentsPostValidate.apply(this, args); |
| 452 | 572 |
| 453 var retval; | 573 var retval; |
| 454 if (this.handleRequest) { | 574 if (this.handleRequest) { |
| 455 retval = this.handleRequest.apply(this, args); | 575 retval = this.handleRequest.apply(this, args); |
| 456 } else { | 576 } else { |
| 457 retval = sendRequest(this.name, args, | 577 retval = sendRequest(this.name, args, |
| 458 this.definition.parameters, | 578 this.definition.parameters, |
| 459 {customCallback: this.customCallback}); | 579 {customCallback: this.customCallback}); |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 576 // See http://crbug.com/100242 | 696 // See http://crbug.com/100242 |
| 577 if (chrome.webstorePrivate) { | 697 if (chrome.webstorePrivate) { |
| 578 chrome.webstorePrivate.beginInstallWithManifest2 = | 698 chrome.webstorePrivate.beginInstallWithManifest2 = |
| 579 chrome.webstorePrivate.beginInstallWithManifest3; | 699 chrome.webstorePrivate.beginInstallWithManifest3; |
| 580 } | 700 } |
| 581 | 701 |
| 582 if (chrome.test) | 702 if (chrome.test) |
| 583 chrome.test.getApiDefinitions = GetExtensionAPIDefinition; | 703 chrome.test.getApiDefinitions = GetExtensionAPIDefinition; |
| 584 }); | 704 }); |
| 585 })(); | 705 })(); |
| OLD | NEW |