diff options
author | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-22 06:01:32 +0000 |
---|---|---|
committer | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-22 06:01:32 +0000 |
commit | d7424eb95597971eb17f0858af0ebe1e2a74072c (patch) | |
tree | 710a9d75556c5ad3064b20e4f0881a4dd241c2af /chrome/renderer/resources | |
parent | 1810cfccee05909e1d9cfa2684a401f71be7b4f5 (diff) | |
download | chromium_src-d7424eb95597971eb17f0858af0ebe1e2a74072c.zip chromium_src-d7424eb95597971eb17f0858af0ebe1e2a74072c.tar.gz chromium_src-d7424eb95597971eb17f0858af0ebe1e2a74072c.tar.bz2 |
Validation of extension api callbacks and event parameters in DEBUG
BUG=18711
Review URL: http://codereview.chromium.org/173034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24068 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer/resources')
-rw-r--r-- | chrome/renderer/resources/event_bindings.js | 25 | ||||
-rw-r--r-- | chrome/renderer/resources/extension_process_bindings.js | 68 |
2 files changed, 74 insertions, 19 deletions
diff --git a/chrome/renderer/resources/event_bindings.js b/chrome/renderer/resources/event_bindings.js index 5fb16f9..3def161 100644 --- a/chrome/renderer/resources/event_bindings.js +++ b/chrome/renderer/resources/event_bindings.js @@ -24,9 +24,24 @@ var chrome = chrome || {}; // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); // chromeHidden.Event.dispatch("tab-changed", "hi"); // will result in an alert dialog that says 'hi'. - chrome.Event = function(opt_eventName) { + chrome.Event = function(opt_eventName, opt_argSchemas) { this.eventName_ = opt_eventName; this.listeners_ = []; + + // Validate event parameters if we are in debug. + if (opt_argSchemas && + chromeHidden.validateCallbacks && + chromeHidden.validate) { + + this.validate_ = function(args) { + try { + chromeHidden.validate(args, opt_argSchemas); + } catch (exception) { + return "Event validation error during " + opt_eventName + " -- " + + exception; + } + } + } }; // A map of event names to the event object that is registered to that name. @@ -45,7 +60,7 @@ var chrome = chrome || {}; if (args) { args = JSON.parse(args); } - attachedNamedEvents[name].dispatch.apply( + return attachedNamedEvents[name].dispatch.apply( attachedNamedEvents[name], args); } }; @@ -106,6 +121,12 @@ var chrome = chrome || {}; // arguments to this function each listener. chrome.Event.prototype.dispatch = function(varargs) { var args = Array.prototype.slice.call(arguments); + if (this.validate_) { + var validationErrors = this.validate_(args); + if (validationErrors) { + return validationErrors; + } + } for (var i = 0; i < this.listeners_.length; i++) { try { this.listeners_[i].apply(null, args); diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js index 3155c05..66872f9 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -26,13 +26,15 @@ var chrome = chrome || {}; var chromeHidden = GetChromeHidden(); // Validate arguments. - function validate(args, schemas) { + chromeHidden.validationTypes = []; + chromeHidden.validate = function(args, schemas) { if (args.length > schemas.length) throw new Error("Too many arguments."); for (var i = 0; i < schemas.length; i++) { if (i in args && args[i] !== null && args[i] !== undefined) { var validator = new chrome.JSONSchemaValidator(); + validator.addTypes(chromeHidden.validationTypes); validator.validate(args[i], schemas[i]); if (validator.errors.length == 0) continue; @@ -57,10 +59,11 @@ var chrome = chrome || {}; } // Callback handling. - var callbacks = []; + var requests = []; chromeHidden.handleResponse = function(requestId, name, success, response, error) { try { + var request = requests[requestId]; if (success) { delete chrome.extension.lastError; } else { @@ -72,16 +75,41 @@ var chrome = chrome || {}; "message": error }; } + + if (request.callback) { + // Callbacks currently only support one callback argument. + var callbackArgs = response ? [JSON.parse(response)] : []; + + // Validate callback in debug only -- and only when the + // caller has provided a callback. Implementations of api + // calls my not return data if they observe the caller + // has not provided a callback. + if (chromeHidden.validateCallbacks && !error) { + try { + if (!request.callbackSchema.parameters) { + throw "No callback schemas defined"; + } + + if (request.callbackSchema.parameters.length > 1) { + throw "Callbacks may only define one parameter"; + } + + chromeHidden.validate(callbackArgs, + request.callbackSchema.parameters); + } catch (exception) { + return "Callback validation error during " + name + " -- " + + exception; + } + } - if (callbacks[requestId]) { if (response) { - callbacks[requestId](JSON.parse(response)); + request.callback(callbackArgs[0]); } else { - callbacks[requestId](); + request.callback(); } } } finally { - delete callbacks[requestId]; + delete requests[requestId]; delete chrome.extension.lastError; } }; @@ -89,15 +117,16 @@ var chrome = chrome || {}; function prepareRequest(args, argSchemas) { var request = {}; var argCount = args.length; - + // Look for callback param. if (argSchemas.length > 0 && args.length == argSchemas.length && argSchemas[argSchemas.length - 1].type == "function") { request.callback = args[argSchemas.length - 1]; + request.callbackSchema = argSchemas[argSchemas.length - 1]; --argCount; } - + // Calls with one argument expect singular argument. Calls with multiple // expect a list. if (argCount == 1) { @@ -121,12 +150,9 @@ var chrome = chrome || {}; request.args = null; var sargs = JSON.stringify(request.args); var requestId = GetNextRequestId(); - var hasCallback = false; - if (request.callback) { - hasCallback = true; - callbacks[requestId] = request.callback; - } - return StartRequest(functionName, sargs, requestId, hasCallback); + requests[requestId] = request; + return StartRequest(functionName, sargs, requestId, + request.callback ? true : false); } // Using forEach for convenience, and to bind |module|s & |apiDefs|s via @@ -179,7 +205,14 @@ var chrome = chrome || {}; forEach(apiDefinitions, function(apiDef) { chrome[apiDef.namespace] = chrome[apiDef.namespace] || {}; var module = chrome[apiDef.namespace]; - + + // Add types to global validationTypes + if (apiDef.types) { + forEach(apiDef.types, function(t) { + chromeHidden.validationTypes.push(t); + }); + } + // Setup Functions. if (apiDef.functions) { forEach(apiDef.functions, function(functionDef) { @@ -194,7 +227,7 @@ var chrome = chrome || {}; apiFunctions[apiFunction.name] = apiFunction; module[functionDef.name] = bind(apiFunction, function() { - validate(arguments, this.definition.parameters); + chromeHidden.validate(arguments, this.definition.parameters); if (this.handleRequest) return this.handleRequest.apply(this, arguments); @@ -214,7 +247,8 @@ var chrome = chrome || {}; return; var eventName = apiDef.namespace + "." + eventDef.name; - module[eventDef.name] = new chrome.Event(eventName); + module[eventDef.name] = new chrome.Event(eventName, + eventDef.parameters); }); } }); |