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

Side by Side Diff: chrome/renderer/resources/extensions/binding.js

Issue 12647017: Lazily require types when validating Extensions API calls (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: more fixes Created 7 years, 9 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 | Annotate | Revision Log
OLDNEW
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 require('json_schema'); 5 require('json_schema');
6 require('event_bindings'); 6 require('event_bindings');
7 var chrome = requireNative('chrome').GetChrome(); 7 var chrome = requireNative('chrome').GetChrome();
8 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); 8 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
9 var forEach = require('utils').forEach; 9 var forEach = require('utils').forEach;
10 var GetAvailability = requireNative('v8_context').GetAvailability; 10 var GetAvailability = requireNative('v8_context').GetAvailability;
11 var logging = requireNative('logging'); 11 var logging = requireNative('logging');
12 var process = requireNative('process'); 12 var process = requireNative('process');
13 var contextType = process.GetContextType(); 13 var contextType = process.GetContextType();
14 var extensionId = process.GetExtensionId(); 14 var extensionId = process.GetExtensionId();
15 var manifestVersion = process.GetManifestVersion(); 15 var manifestVersion = process.GetManifestVersion();
16 var schemaRegistry = requireNative('schema_registry'); 16 var schemaRegistry = requireNative('schema_registry');
17 var schemaUtils = require('schemaUtils'); 17 var schemaUtils = require('schemaUtils');
18 var sendRequest = require('sendRequest').sendRequest; 18 var sendRequest = require('sendRequest').sendRequest;
19 var utils = require('utils'); 19 var utils = require('utils');
20 var CHECK = requireNative('logging').CHECK;
20 21
21 // Stores the name and definition of each API function, with methods to 22 // Stores the name and definition of each API function, with methods to
22 // modify their behaviour (such as a custom way to handle requests to the 23 // modify their behaviour (such as a custom way to handle requests to the
23 // API, a custom callback, etc). 24 // API, a custom callback, etc).
24 function APIFunctions() { 25 function APIFunctions() {
25 this.apiFunctions_ = {}; 26 this.apiFunctions_ = {};
26 this.unavailableApiFunctions_ = {}; 27 this.unavailableApiFunctions_ = {};
27 } 28 }
28 29
29 APIFunctions.prototype.register = function(apiName, apiFunction) { 30 APIFunctions.prototype.register = function(apiName, apiFunction) {
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 function isManifestVersionSupported(schemaNode, manifestVersion) { 111 function isManifestVersionSupported(schemaNode, manifestVersion) {
111 return !schemaNode.maximumManifestVersion || 112 return !schemaNode.maximumManifestVersion ||
112 manifestVersion <= schemaNode.maximumManifestVersion; 113 manifestVersion <= schemaNode.maximumManifestVersion;
113 } 114 }
114 115
115 function isSchemaNodeSupported(schemaNode, platform, manifestVersion) { 116 function isSchemaNodeSupported(schemaNode, platform, manifestVersion) {
116 return isPlatformSupported(schemaNode, platform) && 117 return isPlatformSupported(schemaNode, platform) &&
117 isManifestVersionSupported(schemaNode, manifestVersion); 118 isManifestVersionSupported(schemaNode, manifestVersion);
118 } 119 }
119 120
121 function createCustomType(type) {
122 var jsModuleName = type.js_module;
123 CHECK(jsModuleName, 'Custom type ' + type.id +
124 ' has no "js_module" property.');
125 var jsModule = require(jsModuleName);
126 CHECK(jsModule, 'No module ' + jsModuleName + ' found for ' + type.id + '.');
127 var customType = jsModule[jsModuleName];
128 CHECK(customType, jsModuleName + ' must export itself.');
129 customType.prototype = new CustomBindingsObject();
130 customType.prototype.setSchema(type);
131 return customType;
132 }
133
120 var platform = getPlatform(); 134 var platform = getPlatform();
121 135
122 function Binding(schema) { 136 function Binding(schema) {
123 this.schema_ = schema; 137 this.schema_ = schema;
124 this.apiFunctions_ = new APIFunctions(); 138 this.apiFunctions_ = new APIFunctions();
125 this.customEvent_ = null; 139 this.customEvent_ = null;
126 this.customTypes_ = {};
127 this.customHooks_ = []; 140 this.customHooks_ = [];
128 }; 141 };
129 142
130 Binding.create = function(apiName) { 143 Binding.create = function(apiName) {
131 return new Binding(schemaRegistry.GetSchema(apiName)); 144 return new Binding(schemaRegistry.GetSchema(apiName));
132 }; 145 };
133 146
134 Binding.prototype = { 147 Binding.prototype = {
135 // The API through which the ${api_name}_custom_bindings.js files customize 148 // The API through which the ${api_name}_custom_bindings.js files customize
136 // their API bindings beyond what can be generated. 149 // their API bindings beyond what can be generated.
137 // 150 //
138 // There are 2 types of customizations available: those which are required in 151 // There are 2 types of customizations available: those which are required in
139 // order to do the schema generation (registerCustomEvent and 152 // order to do the schema generation (registerCustomEvent and
140 // registerCustomType), and those which can only run after the bindings have 153 // registerCustomType), and those which can only run after the bindings have
141 // been generated (registerCustomHook). 154 // been generated (registerCustomHook).
142 //
143
144 // Registers a custom type referenced via "$ref" fields in the API schema
145 // JSON.
146 registerCustomType: function(typeName, customTypeFactory) {
147 var customType = customTypeFactory();
148 customType.prototype = new CustomBindingsObject();
149 this.customTypes_[typeName] = customType;
150 },
151 155
152 // Registers a custom event type for the API identified by |namespace|. 156 // Registers a custom event type for the API identified by |namespace|.
153 // |event| is the event's constructor. 157 // |event| is the event's constructor.
154 registerCustomEvent: function(event) { 158 registerCustomEvent: function(event) {
155 this.customEvent_ = event; 159 this.customEvent_ = event;
156 }, 160 },
157 161
158 // Registers a function |hook| to run after the schema for all APIs has been 162 // Registers a function |hook| to run after the schema for all APIs has been
159 // generated. The hook is passed as its first argument an "API" object to 163 // generated. The hook is passed as its first argument an "API" object to
160 // interact with, and second the current extension ID. See where 164 // interact with, and second the current extension ID. See where
(...skipping 16 matching lines...) Expand all
177 schema: this.schema_, 181 schema: this.schema_,
178 compiledApi: api 182 compiledApi: api
179 }, extensionId, contextType); 183 }, extensionId, contextType);
180 }, this); 184 }, this);
181 }, 185 },
182 186
183 // Generates the bindings from |this.schema_| and integrates any custom 187 // Generates the bindings from |this.schema_| and integrates any custom
184 // bindings that might be present. 188 // bindings that might be present.
185 generate: function() { 189 generate: function() {
186 var schema = this.schema_; 190 var schema = this.schema_;
187 var customTypes = this.customTypes_;
188 191
189 // TODO(kalman/cduvall): Make GetAvailability handle this, then delete the 192 // TODO(kalman/cduvall): Make GetAvailability handle this, then delete the
190 // supporting code. 193 // supporting code.
191 if (!isSchemaNodeSupported(schema, platform, manifestVersion)) { 194 if (!isSchemaNodeSupported(schema, platform, manifestVersion)) {
192 console.error('chrome.' + schema.namespace + ' is not supported on ' + 195 console.error('chrome.' + schema.namespace + ' is not supported on ' +
193 'this platform or manifest version'); 196 'this platform or manifest version');
194 return undefined; 197 return undefined;
195 } 198 }
196 199
197 var availability = GetAvailability(schema.namespace); 200 var availability = GetAvailability(schema.namespace);
198 if (!availability.is_available) { 201 if (!availability.is_available) {
199 console.error('chrome.' + schema.namespace + ' is not available: ' + 202 console.error('chrome.' + schema.namespace + ' is not available: ' +
200 availability.message); 203 availability.message);
201 return undefined; 204 return undefined;
202 } 205 }
203 206
204 // See comment on internalAPIs at the top. 207 // See comment on internalAPIs at the top.
205 var mod = {}; 208 var mod = {};
206 209
207 var namespaces = schema.namespace.split('.'); 210 var namespaces = schema.namespace.split('.');
208 for (var index = 0, name; name = namespaces[index]; index++) { 211 for (var index = 0, name; name = namespaces[index]; index++) {
209 mod[name] = mod[name] || {}; 212 mod[name] = mod[name] || {};
210 mod = mod[name]; 213 mod = mod[name];
211 } 214 }
212 215
213 // Add types to global schemaValidator 216 // Add types to global schemaValidator, the types we depend on from other
217 // namespaces will be added as needed.
214 if (schema.types) { 218 if (schema.types) {
215 forEach(schema.types, function(i, t) { 219 forEach(schema.types, function(i, t) {
216 if (!isSchemaNodeSupported(t, platform, manifestVersion)) 220 if (!isSchemaNodeSupported(t, platform, manifestVersion))
217 return; 221 return;
218 222
219 schemaUtils.schemaValidator.addTypes(t); 223 schemaUtils.schemaValidator.addTypes(t);
220 if (t.type == 'object' && this.customTypes_[t.id]) {
221 var parts = t.id.split(".");
222 this.customTypes_[t.id].prototype.setSchema(t);
223 mod[parts[parts.length - 1]] = this.customTypes_[t.id];
224 }
225 }, this); 224 }, this);
226 } 225 }
227 226
228 // Returns whether access to the content of a schema should be denied, 227 // Returns whether access to the content of a schema should be denied,
229 // based on the presence of "unprivileged" and whether this is an 228 // based on the presence of "unprivileged" and whether this is an
230 // extension process (versus e.g. a content script). 229 // extension process (versus e.g. a content script).
231 function isSchemaAccessAllowed(itemSchema) { 230 function isSchemaAccessAllowed(itemSchema) {
232 return (contextType == 'BLESSED_EXTENSION') || 231 return (contextType == 'BLESSED_EXTENSION') ||
233 schema.unprivileged || 232 schema.unprivileged ||
234 itemSchema.unprivileged; 233 itemSchema.unprivileged;
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 // Values may just have raw types as defined in the JSON, such 363 // Values may just have raw types as defined in the JSON, such
365 // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here. 364 // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here.
366 // TODO(kalman): enforce that things with a "value" property can't 365 // TODO(kalman): enforce that things with a "value" property can't
367 // define their own types. 366 // define their own types.
368 var type = propertyDef.type || typeof(value); 367 var type = propertyDef.type || typeof(value);
369 if (type === 'integer' || type === 'number') { 368 if (type === 'integer' || type === 'number') {
370 value = parseInt(value); 369 value = parseInt(value);
371 } else if (type === 'boolean') { 370 } else if (type === 'boolean') {
372 value = value === 'true'; 371 value = value === 'true';
373 } else if (propertyDef['$ref']) { 372 } else if (propertyDef['$ref']) {
374 if (propertyDef['$ref'] in customTypes) { 373 var ref = propertyDef['$ref'];
375 var constructor = customTypes[propertyDef['$ref']]; 374 var type = utils.loadTypeSchema(propertyDef['$ref'], schema);
376 } else { 375 CHECK(type, 'Schema for $ref type ' + ref + ' not found');
377 var refParts = propertyDef['$ref'].split('.'); 376 var constructor = createCustomType(type);
378 // This should never try to load a $ref in the current namespace.
379 var constructor = utils.loadRefDependency(
380 propertyDef['$ref'])[refParts[refParts.length - 1]];
381 }
382 if (!constructor)
383 throw new Error('No custom binding for ' + propertyDef['$ref']);
384 var args = value; 377 var args = value;
385 // For an object propertyDef, |value| is an array of constructor 378 // For an object propertyDef, |value| is an array of constructor
386 // arguments, but we want to pass the arguments directly (i.e. 379 // arguments, but we want to pass the arguments directly (i.e.
387 // not as an array), so we have to fake calling |new| on the 380 // not as an array), so we have to fake calling |new| on the
388 // constructor. 381 // constructor.
389 value = { __proto__: constructor.prototype }; 382 value = { __proto__: constructor.prototype };
390 constructor.apply(value, args); 383 constructor.apply(value, args);
391 // Recursively add properties. 384 // Recursively add properties.
392 addProperties(value, propertyDef); 385 addProperties(value, propertyDef);
393 } else if (type === 'object') { 386 } else if (type === 'object') {
394 // Recursively add properties. 387 // Recursively add properties.
395 addProperties(value, propertyDef); 388 addProperties(value, propertyDef);
396 } else if (type !== 'string') { 389 } else if (type !== 'string') {
397 throw new Error('NOT IMPLEMENTED (extension_api.json error): ' + 390 throw new Error('NOT IMPLEMENTED (extension_api.json error): ' +
398 'Cannot parse values for type "' + type + '"'); 391 'Cannot parse values for type "' + type + '"');
399 } 392 }
400 m[propertyName] = value; 393 m[propertyName] = value;
401 } 394 }
402 }); 395 });
403 }; 396 };
404 397
405 addProperties(mod, schema); 398 addProperties(mod, schema);
406 this.runHooks_(mod); 399 this.runHooks_(mod);
407 return mod; 400 return mod;
408 } 401 }
409 }; 402 };
410 403
411 exports.Binding = Binding; 404 exports.Binding = Binding;
OLDNEW
« no previous file with comments | « chrome/renderer/extensions/json_schema_unittest.cc ('k') | chrome/renderer/resources/extensions/chrome_setting.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698