binding.js revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)require('json_schema');
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)require('event_bindings');
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var chrome = requireNative('chrome').GetChrome();
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var forEach = require('utils').forEach;
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var GetAvailability = requireNative('v8_context').GetAvailability;
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var logging = requireNative('logging');
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var process = requireNative('process');
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var contextType = process.GetContextType();
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var extensionId = process.GetExtensionId();
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var manifestVersion = process.GetManifestVersion();
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var schemaRegistry = requireNative('schema_registry');
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var schemaUtils = require('schemaUtils');
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var utils = require('utils');
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var CHECK = requireNative('logging').CHECK;
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var sendRequestHandler = require('sendRequest');
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var sendRequest = sendRequestHandler.sendRequest;
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var logActivity = requireNative('activityLogger');
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Stores the name and definition of each API function, with methods to
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// modify their behaviour (such as a custom way to handle requests to the
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// API, a custom callback, etc).
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function APIFunctions(namespace) {
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.apiFunctions_ = {};
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.unavailableApiFunctions_ = {};
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  this.namespace = namespace;
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.register = function(apiName, apiFunction) {
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.apiFunctions_[apiName] = apiFunction;
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Registers a function as existing but not available, meaning that calls to
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the set* methods that reference this function should be ignored rather
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// than throwing Errors.
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.registerUnavailable = function(apiName) {
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.unavailableApiFunctions_[apiName] = apiName;
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setHook_ =
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, propertyName, customizedFunction) {
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (this.unavailableApiFunctions_.hasOwnProperty(apiName))
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!this.apiFunctions_.hasOwnProperty(apiName))
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    throw new Error('Tried to set hook for unknown API "' + apiName + '"');
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.apiFunctions_[apiName][propertyName] = customizedFunction;
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setHandleRequest =
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, customizedFunction) {
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  var prefix = this.namespace;
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // TODO(ataly): Need to replace/redefine apply and slice.
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return this.setHook_(apiName, 'handleRequest',
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    function() {
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      var ret = customizedFunction.apply(this, arguments);
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // Logs API calls to the Activity Log if it doesn't go through an
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // ExtensionFunction.
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (!sendRequestHandler.getCalledSendRequest())
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        logActivity.LogAPICall(extensionId, prefix + "." + apiName,
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            Array.prototype.slice.call(arguments));
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return ret;
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    });
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setUpdateArgumentsPostValidate =
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, customizedFunction) {
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return this.setHook_(
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    apiName, 'updateArgumentsPostValidate', customizedFunction);
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setUpdateArgumentsPreValidate =
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, customizedFunction) {
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return this.setHook_(
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    apiName, 'updateArgumentsPreValidate', customizedFunction);
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setCustomCallback =
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, customizedFunction) {
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return this.setHook_(apiName, 'customCallback', customizedFunction);
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function CustomBindingsObject() {
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)CustomBindingsObject.prototype.setSchema = function(schema) {
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The functions in the schema are in list form, so we move them into a
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // dictionary for easier access.
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var self = this;
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  self.functionSchemas = {};
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  schema.functions.forEach(function(f) {
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.functionSchemas[f.name] = {
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      name: f.name,
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      definition: f
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  });
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Get the platform from navigator.appVersion.
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function getPlatform() {
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var platforms = [
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/CrOS Touch/, "chromeos touch"],
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/CrOS/, "chromeos"],
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/Linux/, "linux"],
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/Mac/, "mac"],
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/Win/, "win"],
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ];
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (var i = 0; i < platforms.length; i++) {
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (platforms[i][0].test(navigator.appVersion)) {
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return platforms[i][1];
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return "unknown";
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function isPlatformSupported(schemaNode, platform) {
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return !schemaNode.platforms ||
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      schemaNode.platforms.indexOf(platform) > -1;
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function isManifestVersionSupported(schemaNode, manifestVersion) {
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return !schemaNode.maximumManifestVersion ||
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      manifestVersion <= schemaNode.maximumManifestVersion;
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function isSchemaNodeSupported(schemaNode, platform, manifestVersion) {
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return isPlatformSupported(schemaNode, platform) &&
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      isManifestVersionSupported(schemaNode, manifestVersion);
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function createCustomType(type) {
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var jsModuleName = type.js_module;
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK(jsModuleName, 'Custom type ' + type.id +
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ' has no "js_module" property.');
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var jsModule = require(jsModuleName);
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK(jsModule, 'No module ' + jsModuleName + ' found for ' + type.id + '.');
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var customType = jsModule[jsModuleName];
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK(customType, jsModuleName + ' must export itself.');
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  customType.prototype = new CustomBindingsObject();
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  customType.prototype.setSchema(type);
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return customType;
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var platform = getPlatform();
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function Binding(schema) {
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.schema_ = schema;
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  this.apiFunctions_ = new APIFunctions(schema.namespace);
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.customEvent_ = null;
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.customHooks_ = [];
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Binding.create = function(apiName) {
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return new Binding(schemaRegistry.GetSchema(apiName));
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Binding.prototype = {
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The API through which the ${api_name}_custom_bindings.js files customize
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // their API bindings beyond what can be generated.
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  //
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // There are 2 types of customizations available: those which are required in
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // order to do the schema generation (registerCustomEvent and
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // registerCustomType), and those which can only run after the bindings have
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // been generated (registerCustomHook).
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Registers a custom event type for the API identified by |namespace|.
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |event| is the event's constructor.
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registerCustomEvent: function(event) {
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.customEvent_ = event;
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  },
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Registers a function |hook| to run after the schema for all APIs has been
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // generated.  The hook is passed as its first argument an "API" object to
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // interact with, and second the current extension ID. See where
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |customHooks| is used.
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registerCustomHook: function(fn) {
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.customHooks_.push(fn);
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  },
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(kalman/cduvall): Refactor this so |runHooks_| is not needed.
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  runHooks_: function(api) {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    forEach(this.customHooks_, function(i, hook) {
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!isSchemaNodeSupported(this.schema_, platform, manifestVersion))
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!hook)
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      hook({
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        apiFunctions: this.apiFunctions_,
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        schema: this.schema_,
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        compiledApi: api
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }, extensionId, contextType);
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }, this);
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  },
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Generates the bindings from |this.schema_| and integrates any custom
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // bindings that might be present.
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  generate: function() {
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var schema = this.schema_;
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(kalman/cduvall): Make GetAvailability handle this, then delete the
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // supporting code.
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!isSchemaNodeSupported(schema, platform, manifestVersion)) {
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      console.error('chrome.' + schema.namespace + ' is not supported on ' +
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    'this platform or manifest version');
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return undefined;
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var availability = GetAvailability(schema.namespace);
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!availability.is_available) {
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      console.error('chrome.' + schema.namespace + ' is not available: ' +
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    availability.message);
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return undefined;
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // See comment on internalAPIs at the top.
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var mod = {};
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var namespaces = schema.namespace.split('.');
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (var index = 0, name; name = namespaces[index]; index++) {
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mod[name] = mod[name] || {};
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mod = mod[name];
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Add types to global schemaValidator, the types we depend on from other
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // namespaces will be added as needed.
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (schema.types) {
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      forEach(schema.types, function(i, t) {
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaNodeSupported(t, platform, manifestVersion))
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        schemaUtils.schemaValidator.addTypes(t);
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }, this);
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Returns whether access to the content of a schema should be denied,
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // based on the presence of "unprivileged" and whether this is an
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // extension process (versus e.g. a content script).
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function isSchemaAccessAllowed(itemSchema) {
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return (contextType == 'BLESSED_EXTENSION') ||
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             schema.unprivileged ||
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             itemSchema.unprivileged;
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    };
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Adds a getter that throws an access denied error to object |mod|
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // for property |name|.
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function addUnprivilegedAccessGetter(mod, name) {
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mod.__defineGetter__(name, function() {
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        throw new Error(
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            '"' + name + '" can only be used in extension processes. See ' +
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            'the content scripts documentation for more details.');
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      });
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Setup Functions.
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (schema.functions) {
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      forEach(schema.functions, function(i, functionDef) {
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (functionDef.name in mod) {
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new Error('Function ' + functionDef.name +
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          ' already defined in ' + schema.namespace);
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) {
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          this.apiFunctions_.registerUnavailable(functionDef.name);
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaAccessAllowed(functionDef)) {
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          this.apiFunctions_.registerUnavailable(functionDef.name);
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          addUnprivilegedAccessGetter(mod, functionDef.name);
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        var apiFunction = {};
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        apiFunction.definition = functionDef;
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        apiFunction.name = schema.namespace + '.' + functionDef.name;
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // TODO(aa): It would be best to run this in a unit test, but in order
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // to do that we would need to better factor this code so that it
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // doesn't depend on so much v8::Extension machinery.
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (chromeHidden.validateAPI &&
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            schemaUtils.isFunctionSignatureAmbiguous(
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                apiFunction.definition)) {
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new Error(
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              apiFunction.name + ' has ambiguous optional arguments. ' +
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              'To implement custom disambiguation logic, add ' +
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              '"allowAmbiguousOptionalArguments" to the function\'s schema.');
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.apiFunctions_.register(functionDef.name, apiFunction);
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        mod[functionDef.name] = (function() {
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          var args = Array.prototype.slice.call(arguments);
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (this.updateArgumentsPreValidate)
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            args = this.updateArgumentsPreValidate.apply(this, args);
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          args = schemaUtils.normalizeArgumentsAndValidate(args, this);
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (this.updateArgumentsPostValidate)
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            args = this.updateArgumentsPostValidate.apply(this, args);
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
305c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          sendRequestHandler.clearCalledSendRequest();
306c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          var retval;
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (this.handleRequest) {
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            retval = this.handleRequest.apply(this, args);
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else {
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var optArgs = {
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              customCallback: this.customCallback
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            };
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            retval = sendRequest(this.name, args,
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 this.definition.parameters,
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 optArgs);
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          sendRequestHandler.clearCalledSendRequest();
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Validate return value if defined - only in debug.
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (chromeHidden.validateCallbacks &&
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              this.definition.returns) {
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            schemaUtils.validate([retval], [this.definition.returns]);
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return retval;
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }).bind(apiFunction);
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }, this);
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Setup Events
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (schema.events) {
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      forEach(schema.events, function(i, eventDef) {
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (eventDef.name in mod) {
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new Error('Event ' + eventDef.name +
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          ' already defined in ' + schema.namespace);
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaNodeSupported(eventDef, platform, manifestVersion))
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaAccessAllowed(eventDef)) {
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          addUnprivilegedAccessGetter(mod, eventDef.name);
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        var eventName = schema.namespace + "." + eventDef.name;
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        var options = eventDef.options || {};
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (eventDef.filters && eventDef.filters.length > 0)
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          options.supportsFilters = true;
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (this.customEvent_) {
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          mod[eventDef.name] = new this.customEvent_(
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              eventName, eventDef.parameters, eventDef.extraParameters,
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              options);
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else if (eventDef.anonymous) {
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          mod[eventDef.name] = new chrome.Event();
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else {
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          mod[eventDef.name] = new chrome.Event(
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              eventName, eventDef.parameters, options);
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }, this);
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function addProperties(m, parentDef) {
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      var properties = parentDef.properties;
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!properties)
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      forEach(properties, function(propertyName, propertyDef) {
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (propertyName in m)
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;  // TODO(kalman): be strict like functions/events somehow.
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion))
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaAccessAllowed(propertyDef)) {
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          addUnprivilegedAccessGetter(m, propertyName);
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        var value = propertyDef.value;
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (value) {
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Values may just have raw types as defined in the JSON, such
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here.
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // TODO(kalman): enforce that things with a "value" property can't
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // define their own types.
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          var type = propertyDef.type || typeof(value);
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (type === 'integer' || type === 'number') {
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            value = parseInt(value);
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else if (type === 'boolean') {
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            value = value === 'true';
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else if (propertyDef['$ref']) {
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var ref = propertyDef['$ref'];
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var type = utils.loadTypeSchema(propertyDef['$ref'], schema);
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            CHECK(type, 'Schema for $ref type ' + ref + ' not found');
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var constructor = createCustomType(type);
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var args = value;
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // For an object propertyDef, |value| is an array of constructor
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // arguments, but we want to pass the arguments directly (i.e.
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // not as an array), so we have to fake calling |new| on the
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // constructor.
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            value = { __proto__: constructor.prototype };
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            constructor.apply(value, args);
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // Recursively add properties.
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            addProperties(value, propertyDef);
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else if (type === 'object') {
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // Recursively add properties.
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            addProperties(value, propertyDef);
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else if (type !== 'string') {
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throw new Error('NOT IMPLEMENTED (extension_api.json error): ' +
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                'Cannot parse values for type "' + type + '"');
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          m[propertyName] = value;
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      });
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    };
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    addProperties(mod, schema);
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.runHooks_(mod);
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return mod;
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)exports.Binding = Binding;
422