Index: chrome/renderer/resources/extensions/schema_generated_bindings.js |
diff --git a/chrome/renderer/resources/extensions/schema_generated_bindings.js b/chrome/renderer/resources/extensions/schema_generated_bindings.js |
deleted file mode 100644 |
index 65d5cad57b2ddd836beb76b60bb2b3c799c1b35d..0000000000000000000000000000000000000000 |
--- a/chrome/renderer/resources/extensions/schema_generated_bindings.js |
+++ /dev/null |
@@ -1,437 +0,0 @@ |
-// Copyright (c) 2012 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. |
- |
-// Generates the chrome.* API bindings from a list of schemas. |
- |
- // TODO(battre): cleanup the usage of packages everywhere, as described here |
- // http://codereview.chromium.org/10392008/diff/38/chrome/renderer/resources/extensions/schema_generated_bindings.js |
- |
- require('json_schema'); |
- require('event_bindings'); |
- var GetExtensionAPIDefinition = |
- requireNative('apiDefinitions').GetExtensionAPIDefinition; |
- var sendRequest = require('sendRequest').sendRequest; |
- var utils = require('utils'); |
- var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); |
- var schemaUtils = require('schemaUtils'); |
- |
- // The object to generate the bindings for "internal" APIs in, so that |
- // extensions can't directly call them (without access to chromeHidden), |
- // but are still needed for internal mechanisms of extensions (e.g. events). |
- // |
- // This is distinct to the "*Private" APIs which are controlled via |
- // having strict permissions and aren't generated *anywhere* unless needed. |
- var internalAPIs = {}; |
- chromeHidden.internalAPIs = internalAPIs; |
- |
- // 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() { |
- this._apiFunctions = {}; |
- this._unavailableApiFunctions = {}; |
- } |
- 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 (this._unavailableApiFunctions.hasOwnProperty(apiName)) |
- return; |
- if (!this._apiFunctions.hasOwnProperty(apiName)) |
- throw new Error('Tried to set hook for unknown API "' + apiName + '"'); |
- this._apiFunctions[apiName][propertyName] = customizedFunction; |
- }; |
- APIFunctions.prototype.setHandleRequest = |
- function(apiName, customizedFunction) { |
- return this._setHook(apiName, 'handleRequest', customizedFunction); |
- }; |
- 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); |
- }; |
- |
- var apiFunctions = new APIFunctions(); |
- |
- // Wraps the calls to the set* methods of APIFunctions with the namespace of |
- // an API, and validates that all calls to set* methods aren't prefixed with |
- // a namespace. |
- // |
- // For example, if constructed with 'browserAction', a call to |
- // handleRequest('foo') will be transformed into |
- // handleRequest('browserAction.foo'). |
- // |
- // Likewise, if a call to handleRequest is called with 'browserAction.foo', |
- // it will throw an error. |
- // |
- // These help with isolating custom bindings from each other. |
- function NamespacedAPIFunctions(namespace, delegate) { |
- var self = this; |
- function wrap(methodName) { |
- self[methodName] = function(apiName, customizedFunction) { |
- var prefix = namespace + '.'; |
- if (apiName.indexOf(prefix) === 0) { |
- throw new Error(methodName + ' called with "' + apiName + |
- '" which has a "' + prefix + '" prefix. ' + |
- 'This is unnecessary and must be left out.'); |
- } |
- return delegate[methodName].call(delegate, |
- prefix + apiName, customizedFunction); |
- }; |
- } |
- |
- wrap('contains'); |
- wrap('setHandleRequest'); |
- wrap('setUpdateArgumentsPostValidate'); |
- wrap('setUpdateArgumentsPreValidate'); |
- wrap('setCustomCallback'); |
- } |
- |
- // |
- // 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. |
- var customEvents = {}; |
- chromeHidden.registerCustomEvent = function(namespace, event) { |
- if (typeof(namespace) !== 'string') { |
- throw new Error("registerCustomEvent requires the namespace of the " + |
- "API as its first argument"); |
- } |
- customEvents[namespace] = 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. |
- var customHooks = {}; |
- chromeHidden.registerCustomHook = function(namespace, fn) { |
- if (typeof(namespace) !== 'string') { |
- throw new Error("registerCustomHook requires the namespace of the " + |
- "API as its first argument"); |
- } |
- customHooks[namespace] = fn; |
- }; |
- |
- 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 = {}; |
- schema.functions.forEach(function(f) { |
- self.functionSchemas[f.name] = { |
- name: f.name, |
- definition: f |
- } |
- }); |
- }; |
- |
- // Registers a custom type referenced via "$ref" fields in the API schema |
- // JSON. |
- var customTypes = {}; |
- chromeHidden.registerCustomType = function(typeName, customTypeFactory) { |
- var customType = customTypeFactory(); |
- customType.prototype = new CustomBindingsObject(); |
- customTypes[typeName] = customType; |
- }; |
- |
- // 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 (platforms[i][0].test(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); |
- } |
- |
- chromeHidden.onLoad.addListener(function(extensionId, |
- contextType, |
- isIncognitoProcess, |
- manifestVersion) { |
- var apiDefinitions = GetExtensionAPIDefinition(); |
- |
- // Read api definitions and setup api functions in the chrome namespace. |
- var platform = getPlatform(); |
- |
- apiDefinitions.forEach(function(apiDef) { |
- // TODO(kalman): Remove this, or refactor schema_generated_bindings.js so |
- // that it isn't necessary. For now, chrome.app and chrome.webstore are |
- // entirely handwritten. |
- if (['app', 'webstore'].indexOf(apiDef.namespace) >= 0) |
- return; |
- |
- if (!isSchemaNodeSupported(apiDef, platform, manifestVersion)) |
- return; |
- |
- // See comment on internalAPIs at the top. |
- var mod = apiDef.internal ? internalAPIs : chrome; |
- |
- var namespaces = apiDef.namespace.split('.'); |
- for (var index = 0, name; name = namespaces[index]; index++) { |
- mod[name] = mod[name] || {}; |
- mod = mod[name]; |
- } |
- |
- // Add types to global schemaValidator |
- if (apiDef.types) { |
- apiDef.types.forEach(function(t) { |
- if (!isSchemaNodeSupported(t, platform, manifestVersion)) |
- return; |
- |
- schemaUtils.schemaValidator.addTypes(t); |
- if (t.type == 'object' && customTypes[t.id]) { |
- customTypes[t.id].prototype.setSchema(t); |
- } |
- }); |
- } |
- |
- // 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') || |
- apiDef.unprivileged || |
- itemSchema.unprivileged; |
- } |
- |
- // Adds a getter that throws an access denied error to object |mod| |
- // for property |name|. |
- function addUnprivilegedAccessGetter(mod, name) { |
- mod.__defineGetter__(name, function() { |
- throw new Error( |
- '"' + name + '" can only be used in extension processes. See ' + |
- 'the content scripts documentation for more details.'); |
- }); |
- } |
- |
- // Setup Functions. |
- if (apiDef.functions) { |
- apiDef.functions.forEach(function(functionDef) { |
- if (functionDef.name in mod) { |
- throw new Error('Function ' + functionDef.name + |
- ' already defined in ' + apiDef.namespace); |
- } |
- |
- var apiFunctionName = apiDef.namespace + "." + functionDef.name; |
- |
- if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) { |
- apiFunctions.registerUnavailable(apiFunctionName); |
- return; |
- } |
- if (!isSchemaAccessAllowed(functionDef)) { |
- apiFunctions.registerUnavailable(apiFunctionName); |
- addUnprivilegedAccessGetter(mod, functionDef.name); |
- return; |
- } |
- |
- var apiFunction = {}; |
- apiFunction.definition = functionDef; |
- apiFunction.name = apiFunctionName; |
- |
- // 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 (chromeHidden.validateAPI && |
- 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.'); |
- } |
- |
- apiFunctions.register(apiFunction.name, apiFunction); |
- |
- mod[functionDef.name] = (function() { |
- var args = Array.prototype.slice.call(arguments); |
- if (this.updateArgumentsPreValidate) |
- args = this.updateArgumentsPreValidate.apply(this, args); |
- |
- args = schemaUtils.normalizeArgumentsAndValidate(args, this); |
- if (this.updateArgumentsPostValidate) |
- args = this.updateArgumentsPostValidate.apply(this, args); |
- |
- var retval; |
- if (this.handleRequest) { |
- retval = this.handleRequest.apply(this, args); |
- } else { |
- var optArgs = { |
- customCallback: this.customCallback |
- }; |
- retval = sendRequest(this.name, args, |
- this.definition.parameters, |
- optArgs); |
- } |
- |
- // Validate return value if defined - only in debug. |
- if (chromeHidden.validateCallbacks && |
- this.definition.returns) { |
- schemaUtils.validate([retval], [this.definition.returns]); |
- } |
- return retval; |
- }).bind(apiFunction); |
- }); |
- } |
- |
- // Setup Events |
- if (apiDef.events) { |
- apiDef.events.forEach(function(eventDef) { |
- if (eventDef.name in mod) { |
- throw new Error('Event ' + eventDef.name + |
- ' already defined in ' + apiDef.namespace); |
- } |
- if (!isSchemaNodeSupported(eventDef, platform, manifestVersion)) |
- return; |
- if (!isSchemaAccessAllowed(eventDef)) { |
- addUnprivilegedAccessGetter(mod, eventDef.name); |
- return; |
- } |
- |
- var eventName = apiDef.namespace + "." + eventDef.name; |
- var customEvent = customEvents[apiDef.namespace]; |
- var options = eventDef.options || {}; |
- |
- if (eventDef.filters && eventDef.filters.length > 0) |
- options.supportsFilters = true; |
- |
- if (customEvent) { |
- mod[eventDef.name] = new customEvent( |
- eventName, eventDef.parameters, eventDef.extraParameters, |
- options); |
- } else if (eventDef.anonymous) { |
- mod[eventDef.name] = new chrome.Event(); |
- } else { |
- mod[eventDef.name] = new chrome.Event( |
- eventName, eventDef.parameters, options); |
- } |
- }); |
- } |
- |
- function addProperties(m, parentDef) { |
- var properties = parentDef.properties; |
- if (!properties) |
- return; |
- |
- utils.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 (!isSchemaAccessAllowed(propertyDef)) { |
- addUnprivilegedAccessGetter(m, propertyName); |
- 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 constructor = customTypes[propertyDef["$ref"]]; |
- if (!constructor) |
- throw new Error("No custom binding for " + propertyDef["$ref"]); |
- 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 }; |
- constructor.apply(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, apiDef); |
- }); |
- |
- // Run the non-declarative custom hooks after all the schemas have been |
- // generated, in case hooks depend on other APIs being available. |
- apiDefinitions.forEach(function(apiDef) { |
- if (!isSchemaNodeSupported(apiDef, platform, manifestVersion)) |
- return; |
- |
- var hook = customHooks[apiDef.namespace]; |
- if (!hook) |
- return; |
- |
- // Pass through the public API of schema_generated_bindings, to be used |
- // by custom bindings JS files. Create a new one so that bindings can't |
- // interfere with each other. |
- hook({ |
- apiFunctions: new NamespacedAPIFunctions(apiDef.namespace, |
- apiFunctions), |
- apiDefinitions: apiDefinitions, |
- }, extensionId, contextType); |
- }); |
- |
- if (chrome.test) |
- chrome.test.getApiDefinitions = GetExtensionAPIDefinition; |
- }); |