| 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 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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; |
| OLD | NEW |