Index: trunk/src/extensions/renderer/resources/binding.js |
=================================================================== |
--- trunk/src/extensions/renderer/resources/binding.js (revision 274563) |
+++ trunk/src/extensions/renderer/resources/binding.js (working copy) |
@@ -1,434 +0,0 @@ |
-// Copyright 2014 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-var Event = require('event_bindings').Event; |
-var forEach = require('utils').forEach; |
-var GetAvailability = requireNative('v8_context').GetAvailability; |
-var logActivity = requireNative('activityLogger'); |
-var logging = requireNative('logging'); |
-var process = requireNative('process'); |
-var schemaRegistry = requireNative('schema_registry'); |
-var schemaUtils = require('schemaUtils'); |
-var utils = require('utils'); |
-var sendRequestHandler = require('sendRequest'); |
- |
-var contextType = process.GetContextType(); |
-var extensionId = process.GetExtensionId(); |
-var manifestVersion = process.GetManifestVersion(); |
-var sendRequest = sendRequestHandler.sendRequest; |
- |
-// Stores the name and definition of each API function, with methods to |
-// modify their behaviour (such as a custom way to handle requests to the |
-// API, a custom callback, etc). |
-function APIFunctions(namespace) { |
- this.apiFunctions_ = {}; |
- this.unavailableApiFunctions_ = {}; |
- this.namespace = namespace; |
-} |
- |
-APIFunctions.prototype.register = function(apiName, apiFunction) { |
- this.apiFunctions_[apiName] = apiFunction; |
-}; |
- |
-// Registers a function as existing but not available, meaning that calls to |
-// the set* methods that reference this function should be ignored rather |
-// than throwing Errors. |
-APIFunctions.prototype.registerUnavailable = function(apiName) { |
- this.unavailableApiFunctions_[apiName] = apiName; |
-}; |
- |
-APIFunctions.prototype.setHook_ = |
- function(apiName, propertyName, customizedFunction) { |
- if ($Object.hasOwnProperty(this.unavailableApiFunctions_, apiName)) |
- return; |
- if (!$Object.hasOwnProperty(this.apiFunctions_, apiName)) |
- throw new Error('Tried to set hook for unknown API "' + apiName + '"'); |
- this.apiFunctions_[apiName][propertyName] = customizedFunction; |
-}; |
- |
-APIFunctions.prototype.setHandleRequest = |
- function(apiName, customizedFunction) { |
- var prefix = this.namespace; |
- return this.setHook_(apiName, 'handleRequest', |
- function() { |
- var ret = $Function.apply(customizedFunction, this, arguments); |
- // Logs API calls to the Activity Log if it doesn't go through an |
- // ExtensionFunction. |
- if (!sendRequestHandler.getCalledSendRequest()) |
- logActivity.LogAPICall(extensionId, prefix + "." + apiName, |
- $Array.slice(arguments)); |
- return ret; |
- }); |
-}; |
- |
-APIFunctions.prototype.setUpdateArgumentsPostValidate = |
- function(apiName, customizedFunction) { |
- return this.setHook_( |
- apiName, 'updateArgumentsPostValidate', customizedFunction); |
-}; |
- |
-APIFunctions.prototype.setUpdateArgumentsPreValidate = |
- function(apiName, customizedFunction) { |
- return this.setHook_( |
- apiName, 'updateArgumentsPreValidate', customizedFunction); |
-}; |
- |
-APIFunctions.prototype.setCustomCallback = |
- function(apiName, customizedFunction) { |
- return this.setHook_(apiName, 'customCallback', customizedFunction); |
-}; |
- |
-function CustomBindingsObject() { |
-} |
- |
-CustomBindingsObject.prototype.setSchema = function(schema) { |
- // The functions in the schema are in list form, so we move them into a |
- // dictionary for easier access. |
- var self = this; |
- self.functionSchemas = {}; |
- $Array.forEach(schema.functions, function(f) { |
- self.functionSchemas[f.name] = { |
- name: f.name, |
- definition: f |
- } |
- }); |
-}; |
- |
-// Get the platform from navigator.appVersion. |
-function getPlatform() { |
- var platforms = [ |
- [/CrOS Touch/, "chromeos touch"], |
- [/CrOS/, "chromeos"], |
- [/Linux/, "linux"], |
- [/Mac/, "mac"], |
- [/Win/, "win"], |
- ]; |
- |
- for (var i = 0; i < platforms.length; i++) { |
- if ($RegExp.test(platforms[i][0], navigator.appVersion)) { |
- return platforms[i][1]; |
- } |
- } |
- return "unknown"; |
-} |
- |
-function isPlatformSupported(schemaNode, platform) { |
- return !schemaNode.platforms || |
- schemaNode.platforms.indexOf(platform) > -1; |
-} |
- |
-function isManifestVersionSupported(schemaNode, manifestVersion) { |
- return !schemaNode.maximumManifestVersion || |
- manifestVersion <= schemaNode.maximumManifestVersion; |
-} |
- |
-function isSchemaNodeSupported(schemaNode, platform, manifestVersion) { |
- return isPlatformSupported(schemaNode, platform) && |
- isManifestVersionSupported(schemaNode, manifestVersion); |
-} |
- |
-function createCustomType(type) { |
- var jsModuleName = type.js_module; |
- logging.CHECK(jsModuleName, 'Custom type ' + type.id + |
- ' has no "js_module" property.'); |
- var jsModule = require(jsModuleName); |
- logging.CHECK(jsModule, 'No module ' + jsModuleName + ' found for ' + |
- type.id + '.'); |
- var customType = jsModule[jsModuleName]; |
- logging.CHECK(customType, jsModuleName + ' must export itself.'); |
- customType.prototype = new CustomBindingsObject(); |
- customType.prototype.setSchema(type); |
- return customType; |
-} |
- |
-var platform = getPlatform(); |
- |
-function Binding(schema) { |
- this.schema_ = schema; |
- this.apiFunctions_ = new APIFunctions(schema.namespace); |
- this.customEvent_ = null; |
- this.customHooks_ = []; |
-}; |
- |
-Binding.create = function(apiName) { |
- return new Binding(schemaRegistry.GetSchema(apiName)); |
-}; |
- |
-Binding.prototype = { |
- // The API through which the ${api_name}_custom_bindings.js files customize |
- // their API bindings beyond what can be generated. |
- // |
- // There are 2 types of customizations available: those which are required in |
- // order to do the schema generation (registerCustomEvent and |
- // registerCustomType), and those which can only run after the bindings have |
- // been generated (registerCustomHook). |
- |
- // Registers a custom event type for the API identified by |namespace|. |
- // |event| is the event's constructor. |
- registerCustomEvent: function(event) { |
- this.customEvent_ = event; |
- }, |
- |
- // Registers a function |hook| to run after the schema for all APIs has been |
- // generated. The hook is passed as its first argument an "API" object to |
- // interact with, and second the current extension ID. See where |
- // |customHooks| is used. |
- registerCustomHook: function(fn) { |
- $Array.push(this.customHooks_, fn); |
- }, |
- |
- // TODO(kalman/cduvall): Refactor this so |runHooks_| is not needed. |
- runHooks_: function(api) { |
- $Array.forEach(this.customHooks_, function(hook) { |
- if (!isSchemaNodeSupported(this.schema_, platform, manifestVersion)) |
- return; |
- |
- if (!hook) |
- return; |
- |
- hook({ |
- apiFunctions: this.apiFunctions_, |
- schema: this.schema_, |
- compiledApi: api |
- }, extensionId, contextType); |
- }, this); |
- }, |
- |
- // Generates the bindings from |this.schema_| and integrates any custom |
- // bindings that might be present. |
- generate: function() { |
- var schema = this.schema_; |
- |
- function shouldCheckUnprivileged() { |
- var shouldCheck = 'unprivileged' in schema; |
- if (shouldCheck) |
- return shouldCheck; |
- |
- $Array.forEach(['functions', 'events'], function(type) { |
- if ($Object.hasOwnProperty(schema, type)) { |
- $Array.forEach(schema[type], function(node) { |
- if ('unprivileged' in node) |
- shouldCheck = true; |
- }); |
- } |
- }); |
- if (shouldCheck) |
- return shouldCheck; |
- |
- for (var property in schema.properties) { |
- if ($Object.hasOwnProperty(schema, property) && |
- 'unprivileged' in schema.properties[property]) { |
- shouldCheck = true; |
- break; |
- } |
- } |
- return shouldCheck; |
- } |
- var checkUnprivileged = shouldCheckUnprivileged(); |
- |
- // TODO(kalman/cduvall): Make GetAvailability handle this, then delete the |
- // supporting code. |
- if (!isSchemaNodeSupported(schema, platform, manifestVersion)) { |
- console.error('chrome.' + schema.namespace + ' is not supported on ' + |
- 'this platform or manifest version'); |
- return undefined; |
- } |
- |
- var mod = {}; |
- |
- var namespaces = $String.split(schema.namespace, '.'); |
- for (var index = 0, name; name = namespaces[index]; index++) { |
- mod[name] = mod[name] || {}; |
- mod = mod[name]; |
- } |
- |
- // Add types to global schemaValidator, the types we depend on from other |
- // namespaces will be added as needed. |
- if (schema.types) { |
- $Array.forEach(schema.types, function(t) { |
- if (!isSchemaNodeSupported(t, platform, manifestVersion)) |
- return; |
- schemaUtils.schemaValidator.addTypes(t); |
- }, this); |
- } |
- |
- // TODO(cduvall): Take out when all APIs have been converted to features. |
- // Returns whether access to the content of a schema should be denied, |
- // based on the presence of "unprivileged" and whether this is an |
- // extension process (versus e.g. a content script). |
- function isSchemaAccessAllowed(itemSchema) { |
- return (contextType == 'BLESSED_EXTENSION') || |
- schema.unprivileged || |
- itemSchema.unprivileged; |
- }; |
- |
- // Setup Functions. |
- if (schema.functions) { |
- $Array.forEach(schema.functions, function(functionDef) { |
- if (functionDef.name in mod) { |
- throw new Error('Function ' + functionDef.name + |
- ' already defined in ' + schema.namespace); |
- } |
- |
- if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) { |
- this.apiFunctions_.registerUnavailable(functionDef.name); |
- return; |
- } |
- |
- var apiFunction = {}; |
- apiFunction.definition = functionDef; |
- apiFunction.name = schema.namespace + '.' + functionDef.name; |
- |
- if (!GetAvailability(apiFunction.name).is_available || |
- (checkUnprivileged && !isSchemaAccessAllowed(functionDef))) { |
- this.apiFunctions_.registerUnavailable(functionDef.name); |
- return; |
- } |
- |
- // TODO(aa): It would be best to run this in a unit test, but in order |
- // to do that we would need to better factor this code so that it |
- // doesn't depend on so much v8::Extension machinery. |
- if (logging.DCHECK_IS_ON() && |
- schemaUtils.isFunctionSignatureAmbiguous(apiFunction.definition)) { |
- throw new Error( |
- apiFunction.name + ' has ambiguous optional arguments. ' + |
- 'To implement custom disambiguation logic, add ' + |
- '"allowAmbiguousOptionalArguments" to the function\'s schema.'); |
- } |
- |
- this.apiFunctions_.register(functionDef.name, apiFunction); |
- |
- mod[functionDef.name] = $Function.bind(function() { |
- var args = $Array.slice(arguments); |
- if (this.updateArgumentsPreValidate) |
- args = $Function.apply(this.updateArgumentsPreValidate, this, args); |
- |
- args = schemaUtils.normalizeArgumentsAndValidate(args, this); |
- if (this.updateArgumentsPostValidate) { |
- args = $Function.apply(this.updateArgumentsPostValidate, |
- this, |
- args); |
- } |
- |
- sendRequestHandler.clearCalledSendRequest(); |
- |
- var retval; |
- if (this.handleRequest) { |
- retval = $Function.apply(this.handleRequest, this, args); |
- } else { |
- var optArgs = { |
- customCallback: this.customCallback |
- }; |
- retval = sendRequest(this.name, args, |
- this.definition.parameters, |
- optArgs); |
- } |
- sendRequestHandler.clearCalledSendRequest(); |
- |
- // Validate return value if in sanity check mode. |
- if (logging.DCHECK_IS_ON() && this.definition.returns) |
- schemaUtils.validate([retval], [this.definition.returns]); |
- return retval; |
- }, apiFunction); |
- }, this); |
- } |
- |
- // Setup Events |
- if (schema.events) { |
- $Array.forEach(schema.events, function(eventDef) { |
- if (eventDef.name in mod) { |
- throw new Error('Event ' + eventDef.name + |
- ' already defined in ' + schema.namespace); |
- } |
- if (!isSchemaNodeSupported(eventDef, platform, manifestVersion)) |
- return; |
- |
- var eventName = schema.namespace + "." + eventDef.name; |
- if (!GetAvailability(eventName).is_available || |
- (checkUnprivileged && !isSchemaAccessAllowed(eventDef))) { |
- return; |
- } |
- |
- var options = eventDef.options || {}; |
- if (eventDef.filters && eventDef.filters.length > 0) |
- options.supportsFilters = true; |
- |
- var parameters = eventDef.parameters; |
- if (this.customEvent_) { |
- mod[eventDef.name] = new this.customEvent_( |
- eventName, parameters, eventDef.extraParameters, options); |
- } else { |
- mod[eventDef.name] = new Event(eventName, parameters, options); |
- } |
- }, this); |
- } |
- |
- function addProperties(m, parentDef) { |
- var properties = parentDef.properties; |
- if (!properties) |
- return; |
- |
- forEach(properties, function(propertyName, propertyDef) { |
- if (propertyName in m) |
- return; // TODO(kalman): be strict like functions/events somehow. |
- if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion)) |
- return; |
- if (!GetAvailability(schema.namespace + "." + |
- propertyName).is_available || |
- (checkUnprivileged && !isSchemaAccessAllowed(propertyDef))) { |
- return; |
- } |
- |
- var value = propertyDef.value; |
- if (value) { |
- // Values may just have raw types as defined in the JSON, such |
- // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here. |
- // TODO(kalman): enforce that things with a "value" property can't |
- // define their own types. |
- var type = propertyDef.type || typeof(value); |
- if (type === 'integer' || type === 'number') { |
- value = parseInt(value); |
- } else if (type === 'boolean') { |
- value = value === 'true'; |
- } else if (propertyDef['$ref']) { |
- var ref = propertyDef['$ref']; |
- var type = utils.loadTypeSchema(propertyDef['$ref'], schema); |
- logging.CHECK(type, 'Schema for $ref type ' + ref + ' not found'); |
- var constructor = createCustomType(type); |
- var args = value; |
- // For an object propertyDef, |value| is an array of constructor |
- // arguments, but we want to pass the arguments directly (i.e. |
- // not as an array), so we have to fake calling |new| on the |
- // constructor. |
- value = { __proto__: constructor.prototype }; |
- $Function.apply(constructor, value, args); |
- // Recursively add properties. |
- addProperties(value, propertyDef); |
- } else if (type === 'object') { |
- // Recursively add properties. |
- addProperties(value, propertyDef); |
- } else if (type !== 'string') { |
- throw new Error('NOT IMPLEMENTED (extension_api.json error): ' + |
- 'Cannot parse values for type "' + type + '"'); |
- } |
- m[propertyName] = value; |
- } |
- }); |
- }; |
- |
- addProperties(mod, schema); |
- |
- var availability = GetAvailability(schema.namespace); |
- if (!availability.is_available && $Object.keys(mod).length == 0) { |
- console.error('chrome.' + schema.namespace + ' is not available: ' + |
- availability.message); |
- return; |
- } |
- |
- this.runHooks_(mod); |
- return mod; |
- } |
-}; |
- |
-exports.Binding = Binding; |