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

Side by Side Diff: chrome/renderer/resources/extension_process_bindings.js

Issue 173034: Validation of extension api callback and event parameters in DEBUG (Closed)
Patch Set: build docs Created 11 years, 4 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) 2009 The chrome Authors. All rights reserved. 1 // Copyright (c) 2009 The chrome 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 // ----------------------------------------------------------------------------- 5 // -----------------------------------------------------------------------------
6 // NOTE: If you change this file you need to touch renderer_resources.grd to 6 // NOTE: If you change this file you need to touch renderer_resources.grd to
7 // have your change take effect. 7 // have your change take effect.
8 // ----------------------------------------------------------------------------- 8 // -----------------------------------------------------------------------------
9 9
10 // This script contains privileged chrome extension related javascript APIs. 10 // This script contains privileged chrome extension related javascript APIs.
11 // It is loaded by pages whose URL has the chrome-extension protocol. 11 // It is loaded by pages whose URL has the chrome-extension protocol.
12 12
13 var chrome = chrome || {}; 13 var chrome = chrome || {};
14 (function() { 14 (function() {
15 native function GetExtensionAPIDefinition(); 15 native function GetExtensionAPIDefinition();
16 native function StartRequest(); 16 native function StartRequest();
17 native function GetCurrentPageActions(extensionId); 17 native function GetCurrentPageActions(extensionId);
18 native function GetExtensionViews(); 18 native function GetExtensionViews();
19 native function GetChromeHidden(); 19 native function GetChromeHidden();
20 native function GetNextRequestId(); 20 native function GetNextRequestId();
21 native function OpenChannelToTab(); 21 native function OpenChannelToTab();
22 22
23 if (!chrome) 23 if (!chrome)
24 chrome = {}; 24 chrome = {};
25 25
26 var chromeHidden = GetChromeHidden(); 26 var chromeHidden = GetChromeHidden();
27 27
28 // Validate arguments. 28 // Validate arguments.
29 function validate(args, schemas) { 29 chromeHidden.validationTypes = [];
30 chromeHidden.validate = function(args, schemas) {
30 if (args.length > schemas.length) 31 if (args.length > schemas.length)
31 throw new Error("Too many arguments."); 32 throw new Error("Too many arguments.");
32 33
33 for (var i = 0; i < schemas.length; i++) { 34 for (var i = 0; i < schemas.length; i++) {
34 if (i in args && args[i] !== null && args[i] !== undefined) { 35 if (i in args && args[i] !== null && args[i] !== undefined) {
35 var validator = new chrome.JSONSchemaValidator(); 36 var validator = new chrome.JSONSchemaValidator();
37 validator.addTypes(chromeHidden.validationTypes);
36 validator.validate(args[i], schemas[i]); 38 validator.validate(args[i], schemas[i]);
37 if (validator.errors.length == 0) 39 if (validator.errors.length == 0)
38 continue; 40 continue;
39 41
40 var message = "Invalid value for argument " + i + ". "; 42 var message = "Invalid value for argument " + i + ". ";
41 for (var i = 0, err; err = validator.errors[i]; i++) { 43 for (var i = 0, err; err = validator.errors[i]; i++) {
42 if (err.path) { 44 if (err.path) {
43 message += "Property '" + err.path + "': "; 45 message += "Property '" + err.path + "': ";
44 } 46 }
45 message += err.message; 47 message += err.message;
46 message = message.substring(0, message.length - 1); 48 message = message.substring(0, message.length - 1);
47 message += ", "; 49 message += ", ";
48 } 50 }
49 message = message.substring(0, message.length - 2); 51 message = message.substring(0, message.length - 2);
50 message += "."; 52 message += ".";
51 53
52 throw new Error(message); 54 throw new Error(message);
53 } else if (!schemas[i].optional) { 55 } else if (!schemas[i].optional) {
54 throw new Error("Parameter " + i + " is required."); 56 throw new Error("Parameter " + i + " is required.");
55 } 57 }
56 } 58 }
57 } 59 }
58 60
59 // Callback handling. 61 // Callback handling.
60 var callbacks = []; 62 var requests = [];
61 chromeHidden.handleResponse = function(requestId, name, 63 chromeHidden.handleResponse = function(requestId, name,
62 success, response, error) { 64 success, response, error) {
63 try { 65 try {
66 var request = requests[requestId];
64 if (success) { 67 if (success) {
65 delete chrome.extension.lastError; 68 delete chrome.extension.lastError;
66 } else { 69 } else {
67 if (!error) { 70 if (!error) {
68 error = "Unknown error." 71 error = "Unknown error."
69 } 72 }
70 console.error("Error during " + name + ": " + error); 73 console.error("Error during " + name + ": " + error);
71 chrome.extension.lastError = { 74 chrome.extension.lastError = {
72 "message": error 75 "message": error
73 }; 76 };
74 } 77 }
78
79 if (request.callback) {
80 // Callbacks currently only support one callback argument.
81 var callbackArgs = response ? [JSON.parse(response)] : [];
75 82
76 if (callbacks[requestId]) { 83 // Validate callback in debug only -- and only when the
84 // caller has provided a callback. Implementations of api
85 // calls my not return data if they observe the caller
86 // has not provided a callback.
87 if (chromeHidden.validateCallbacks && !error) {
88 try {
89 if (!request.callbackSchema.parameters) {
90 throw "No callback schemas defined";
91 }
92
93 if (request.callbackSchema.parameters.length > 1) {
94 throw "Callbacks may only define one parameter";
95 }
96
97 chromeHidden.validate(callbackArgs,
98 request.callbackSchema.parameters);
99 } catch (exception) {
100 return "Callback validation error during " + name + " -- " +
101 exception;
102 }
103 }
104
77 if (response) { 105 if (response) {
78 callbacks[requestId](JSON.parse(response)); 106 request.callback(callbackArgs[0]);
79 } else { 107 } else {
80 callbacks[requestId](); 108 request.callback();
81 } 109 }
82 } 110 }
83 } finally { 111 } finally {
84 delete callbacks[requestId]; 112 delete requests[requestId];
85 delete chrome.extension.lastError; 113 delete chrome.extension.lastError;
86 } 114 }
87 }; 115 };
88 116
89 function prepareRequest(args, argSchemas) { 117 function prepareRequest(args, argSchemas) {
90 var request = {}; 118 var request = {};
91 var argCount = args.length; 119 var argCount = args.length;
92 120
93 // Look for callback param. 121 // Look for callback param.
94 if (argSchemas.length > 0 && 122 if (argSchemas.length > 0 &&
95 args.length == argSchemas.length && 123 args.length == argSchemas.length &&
96 argSchemas[argSchemas.length - 1].type == "function") { 124 argSchemas[argSchemas.length - 1].type == "function") {
97 request.callback = args[argSchemas.length - 1]; 125 request.callback = args[argSchemas.length - 1];
126 request.callbackSchema = argSchemas[argSchemas.length - 1];
98 --argCount; 127 --argCount;
99 } 128 }
100 129
101 // Calls with one argument expect singular argument. Calls with multiple 130 // Calls with one argument expect singular argument. Calls with multiple
102 // expect a list. 131 // expect a list.
103 if (argCount == 1) { 132 if (argCount == 1) {
104 request.args = args[0]; 133 request.args = args[0];
105 } 134 }
106 if (argCount > 1) { 135 if (argCount > 1) {
107 request.args = []; 136 request.args = [];
108 for (var k = 0; k < argCount; k++) { 137 for (var k = 0; k < argCount; k++) {
109 request.args[k] = args[k]; 138 request.args[k] = args[k];
110 } 139 }
111 } 140 }
112 141
113 return request; 142 return request;
114 } 143 }
115 144
116 // Send an API request and optionally register a callback. 145 // Send an API request and optionally register a callback.
117 function sendRequest(functionName, args, argSchemas) { 146 function sendRequest(functionName, args, argSchemas) {
118 var request = prepareRequest(args, argSchemas); 147 var request = prepareRequest(args, argSchemas);
119 // JSON.stringify doesn't support a root object which is undefined. 148 // JSON.stringify doesn't support a root object which is undefined.
120 if (request.args === undefined) 149 if (request.args === undefined)
121 request.args = null; 150 request.args = null;
122 var sargs = JSON.stringify(request.args); 151 var sargs = JSON.stringify(request.args);
123 var requestId = GetNextRequestId(); 152 var requestId = GetNextRequestId();
124 var hasCallback = false; 153 requests[requestId] = request;
125 if (request.callback) { 154 return StartRequest(functionName, sargs, requestId,
126 hasCallback = true; 155 request.callback ? true : false);
127 callbacks[requestId] = request.callback;
128 }
129 return StartRequest(functionName, sargs, requestId, hasCallback);
130 } 156 }
131 157
132 // Using forEach for convenience, and to bind |module|s & |apiDefs|s via 158 // Using forEach for convenience, and to bind |module|s & |apiDefs|s via
133 // closures. 159 // closures.
134 function forEach(a, f) { 160 function forEach(a, f) {
135 for (var i = 0; i < a.length; i++) { 161 for (var i = 0; i < a.length; i++) {
136 f(a[i], i); 162 f(a[i], i);
137 } 163 }
138 } 164 }
139 165
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 // TODO(rafaelw): Consider defining a json schema for an api definition 198 // TODO(rafaelw): Consider defining a json schema for an api definition
173 // and validating either here, in a unit_test or both. 199 // and validating either here, in a unit_test or both.
174 // TODO(rafaelw): Handle synchronous functions. 200 // TODO(rafaelw): Handle synchronous functions.
175 // TOOD(rafaelw): Consider providing some convenient override points 201 // TOOD(rafaelw): Consider providing some convenient override points
176 // for api functions that wish to insert themselves into the call. 202 // for api functions that wish to insert themselves into the call.
177 var apiDefinitions = JSON.parse(GetExtensionAPIDefinition()); 203 var apiDefinitions = JSON.parse(GetExtensionAPIDefinition());
178 204
179 forEach(apiDefinitions, function(apiDef) { 205 forEach(apiDefinitions, function(apiDef) {
180 chrome[apiDef.namespace] = chrome[apiDef.namespace] || {}; 206 chrome[apiDef.namespace] = chrome[apiDef.namespace] || {};
181 var module = chrome[apiDef.namespace]; 207 var module = chrome[apiDef.namespace];
182 208
209 // Add types to global validationTypes
210 if (apiDef.types) {
211 forEach(apiDef.types, function(t) {
212 chromeHidden.validationTypes.push(t);
213 });
214 }
215
183 // Setup Functions. 216 // Setup Functions.
184 if (apiDef.functions) { 217 if (apiDef.functions) {
185 forEach(apiDef.functions, function(functionDef) { 218 forEach(apiDef.functions, function(functionDef) {
186 // Module functions may have been defined earlier by hand. Don't 219 // Module functions may have been defined earlier by hand. Don't
187 // clobber them. 220 // clobber them.
188 if (module[functionDef.name]) 221 if (module[functionDef.name])
189 return; 222 return;
190 223
191 var apiFunction = {}; 224 var apiFunction = {};
192 apiFunction.definition = functionDef; 225 apiFunction.definition = functionDef;
193 apiFunction.name = apiDef.namespace + "." + functionDef.name;; 226 apiFunction.name = apiDef.namespace + "." + functionDef.name;;
194 apiFunctions[apiFunction.name] = apiFunction; 227 apiFunctions[apiFunction.name] = apiFunction;
195 228
196 module[functionDef.name] = bind(apiFunction, function() { 229 module[functionDef.name] = bind(apiFunction, function() {
197 validate(arguments, this.definition.parameters); 230 chromeHidden.validate(arguments, this.definition.parameters);
198 231
199 if (this.handleRequest) 232 if (this.handleRequest)
200 return this.handleRequest.apply(this, arguments); 233 return this.handleRequest.apply(this, arguments);
201 else 234 else
202 return sendRequest(this.name, arguments, 235 return sendRequest(this.name, arguments,
203 this.definition.parameters); 236 this.definition.parameters);
204 }); 237 });
205 }); 238 });
206 } 239 }
207 240
208 // Setup Events 241 // Setup Events
209 if (apiDef.events) { 242 if (apiDef.events) {
210 forEach(apiDef.events, function(eventDef) { 243 forEach(apiDef.events, function(eventDef) {
211 // Module events may have been defined earlier by hand. Don't clobber 244 // Module events may have been defined earlier by hand. Don't clobber
212 // them. 245 // them.
213 if (module[eventDef.name]) 246 if (module[eventDef.name])
214 return; 247 return;
215 248
216 var eventName = apiDef.namespace + "." + eventDef.name; 249 var eventName = apiDef.namespace + "." + eventDef.name;
217 module[eventDef.name] = new chrome.Event(eventName); 250 module[eventDef.name] = new chrome.Event(eventName,
251 eventDef.parameters);
218 }); 252 });
219 } 253 }
220 }); 254 });
221 255
222 apiFunctions["tabs.connect"].handleRequest = function(tabId, opt_name) { 256 apiFunctions["tabs.connect"].handleRequest = function(tabId, opt_name) {
223 var portId = OpenChannelToTab( 257 var portId = OpenChannelToTab(
224 tabId, chrome.extension.id_, opt_name || ""); 258 tabId, chrome.extension.id_, opt_name || "");
225 return chromeHidden.Port.createPort(portId, opt_name); 259 return chromeHidden.Port.createPort(portId, opt_name);
226 } 260 }
227 261
(...skipping 15 matching lines...) Expand all
243 apiFunctions["extension.getTabContentses"].handleRequest = 277 apiFunctions["extension.getTabContentses"].handleRequest =
244 function(windowId) { 278 function(windowId) {
245 if (typeof(windowId) == "undefined") 279 if (typeof(windowId) == "undefined")
246 windowId = -1; 280 windowId = -1;
247 return GetExtensionViews(windowId, "TAB"); 281 return GetExtensionViews(windowId, "TAB");
248 } 282 }
249 283
250 setupPageActionEvents(extensionId); 284 setupPageActionEvents(extensionId);
251 }); 285 });
252 })(); 286 })();
OLDNEW
« no previous file with comments | « chrome/renderer/resources/event_bindings.js ('k') | chrome/test/data/extensions/samples/tabs/manifest.json » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698