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)
5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)var Event = require('event_bindings').Event;
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var forEach = require('utils').forEach;
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var GetAvailability = requireNative('v8_context').GetAvailability;
87d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)var logActivity = requireNative('activityLogger');
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var logging = requireNative('logging');
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var process = requireNative('process');
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var schemaRegistry = requireNative('schema_registry');
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var schemaUtils = require('schemaUtils');
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var utils = require('utils');
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var sendRequestHandler = require('sendRequest');
157d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)var contextType = process.GetContextType();
177d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)var extensionId = process.GetExtensionId();
187d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)var manifestVersion = process.GetManifestVersion();
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var sendRequest = sendRequestHandler.sendRequest;
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Stores the name and definition of each API function, with methods to
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// modify their behaviour (such as a custom way to handle requests to the
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// API, a custom callback, etc).
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function APIFunctions(namespace) {
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.apiFunctions_ = {};
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.unavailableApiFunctions_ = {};
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  this.namespace = namespace;
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.register = function(apiName, apiFunction) {
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.apiFunctions_[apiName] = apiFunction;
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Registers a function as existing but not available, meaning that calls to
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the set* methods that reference this function should be ignored rather
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// than throwing Errors.
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.registerUnavailable = function(apiName) {
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.unavailableApiFunctions_[apiName] = apiName;
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setHook_ =
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, propertyName, customizedFunction) {
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if ($Object.hasOwnProperty(this.unavailableApiFunctions_, apiName))
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!$Object.hasOwnProperty(this.apiFunctions_, apiName))
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    throw new Error('Tried to set hook for unknown API "' + apiName + '"');
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.apiFunctions_[apiName][propertyName] = customizedFunction;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setHandleRequest =
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, customizedFunction) {
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  var prefix = this.namespace;
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return this.setHook_(apiName, 'handleRequest',
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    function() {
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      var ret = $Function.apply(customizedFunction, this, arguments);
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // Logs API calls to the Activity Log if it doesn't go through an
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // ExtensionFunction.
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (!sendRequestHandler.getCalledSendRequest())
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        logActivity.LogAPICall(extensionId, prefix + "." + apiName,
60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            $Array.slice(arguments));
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return ret;
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    });
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setUpdateArgumentsPostValidate =
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, customizedFunction) {
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return this.setHook_(
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    apiName, 'updateArgumentsPostValidate', customizedFunction);
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setUpdateArgumentsPreValidate =
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, customizedFunction) {
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return this.setHook_(
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    apiName, 'updateArgumentsPreValidate', customizedFunction);
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)APIFunctions.prototype.setCustomCallback =
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function(apiName, customizedFunction) {
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return this.setHook_(apiName, 'customCallback', customizedFunction);
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function CustomBindingsObject() {
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)CustomBindingsObject.prototype.setSchema = function(schema) {
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The functions in the schema are in list form, so we move them into a
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // dictionary for easier access.
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var self = this;
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  self.functionSchemas = {};
907d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  $Array.forEach(schema.functions, function(f) {
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.functionSchemas[f.name] = {
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      name: f.name,
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      definition: f
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  });
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Get the platform from navigator.appVersion.
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function getPlatform() {
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var platforms = [
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/CrOS Touch/, "chromeos touch"],
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/CrOS/, "chromeos"],
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/Linux/, "linux"],
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/Mac/, "mac"],
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    [/Win/, "win"],
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ];
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (var i = 0; i < platforms.length; i++) {
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if ($RegExp.test(platforms[i][0], navigator.appVersion)) {
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return platforms[i][1];
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return "unknown";
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function isPlatformSupported(schemaNode, platform) {
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return !schemaNode.platforms ||
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      schemaNode.platforms.indexOf(platform) > -1;
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function isManifestVersionSupported(schemaNode, manifestVersion) {
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return !schemaNode.maximumManifestVersion ||
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      manifestVersion <= schemaNode.maximumManifestVersion;
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function isSchemaNodeSupported(schemaNode, platform, manifestVersion) {
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return isPlatformSupported(schemaNode, platform) &&
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      isManifestVersionSupported(schemaNode, manifestVersion);
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function createCustomType(type) {
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var jsModuleName = type.js_module;
1337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  logging.CHECK(jsModuleName, 'Custom type ' + type.id +
1347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                ' has no "js_module" property.');
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var jsModule = require(jsModuleName);
1367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  logging.CHECK(jsModule, 'No module ' + jsModuleName + ' found for ' +
1377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                type.id + '.');
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var customType = jsModule[jsModuleName];
1397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  logging.CHECK(customType, jsModuleName + ' must export itself.');
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  customType.prototype = new CustomBindingsObject();
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  customType.prototype.setSchema(type);
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return customType;
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var platform = getPlatform();
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function Binding(schema) {
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.schema_ = schema;
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  this.apiFunctions_ = new APIFunctions(schema.namespace);
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.customEvent_ = null;
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.customHooks_ = [];
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Binding.create = function(apiName) {
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return new Binding(schemaRegistry.GetSchema(apiName));
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Binding.prototype = {
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The API through which the ${api_name}_custom_bindings.js files customize
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // their API bindings beyond what can be generated.
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  //
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // There are 2 types of customizations available: those which are required in
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // order to do the schema generation (registerCustomEvent and
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // registerCustomType), and those which can only run after the bindings have
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // been generated (registerCustomHook).
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Registers a custom event type for the API identified by |namespace|.
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |event| is the event's constructor.
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registerCustomEvent: function(event) {
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.customEvent_ = event;
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  },
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Registers a function |hook| to run after the schema for all APIs has been
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // generated.  The hook is passed as its first argument an "API" object to
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // interact with, and second the current extension ID. See where
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |customHooks| is used.
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registerCustomHook: function(fn) {
178eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    $Array.push(this.customHooks_, fn);
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  },
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(kalman/cduvall): Refactor this so |runHooks_| is not needed.
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  runHooks_: function(api) {
1837d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    $Array.forEach(this.customHooks_, function(hook) {
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!isSchemaNodeSupported(this.schema_, platform, manifestVersion))
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!hook)
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      hook({
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        apiFunctions: this.apiFunctions_,
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        schema: this.schema_,
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        compiledApi: api
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }, extensionId, contextType);
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }, this);
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  },
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Generates the bindings from |this.schema_| and integrates any custom
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // bindings that might be present.
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  generate: function() {
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var schema = this.schema_;
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
203b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    function shouldCheckUnprivileged() {
204b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      var shouldCheck = 'unprivileged' in schema;
205b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      if (shouldCheck)
206b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        return shouldCheck;
207b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
2087d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      $Array.forEach(['functions', 'events'], function(type) {
209eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if ($Object.hasOwnProperty(schema, type)) {
2107d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)          $Array.forEach(schema[type], function(node) {
211b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            if ('unprivileged' in node)
212b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)              shouldCheck = true;
213b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          });
214b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        }
215b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      });
216b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      if (shouldCheck)
217b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        return shouldCheck;
218b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
219b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      for (var property in schema.properties) {
220eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if ($Object.hasOwnProperty(schema, property) &&
221b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            'unprivileged' in schema.properties[property]) {
222b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          shouldCheck = true;
223b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          break;
224b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        }
225b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      }
226b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      return shouldCheck;
227b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
228b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    var checkUnprivileged = shouldCheckUnprivileged();
229b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(kalman/cduvall): Make GetAvailability handle this, then delete the
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // supporting code.
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!isSchemaNodeSupported(schema, platform, manifestVersion)) {
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      console.error('chrome.' + schema.namespace + ' is not supported on ' +
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    'this platform or manifest version');
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return undefined;
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var mod = {};
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
240eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    var namespaces = $String.split(schema.namespace, '.');
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (var index = 0, name; name = namespaces[index]; index++) {
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mod[name] = mod[name] || {};
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mod = mod[name];
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Add types to global schemaValidator, the types we depend on from other
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // namespaces will be added as needed.
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (schema.types) {
2497d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      $Array.forEach(schema.types, function(t) {
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaNodeSupported(t, platform, manifestVersion))
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        schemaUtils.schemaValidator.addTypes(t);
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }, this);
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
256b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // TODO(cduvall): Take out when all APIs have been converted to features.
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Returns whether access to the content of a schema should be denied,
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // based on the presence of "unprivileged" and whether this is an
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // extension process (versus e.g. a content script).
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function isSchemaAccessAllowed(itemSchema) {
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return (contextType == 'BLESSED_EXTENSION') ||
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             schema.unprivileged ||
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             itemSchema.unprivileged;
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    };
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Setup Functions.
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (schema.functions) {
2687d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      $Array.forEach(schema.functions, function(functionDef) {
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (functionDef.name in mod) {
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new Error('Function ' + functionDef.name +
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          ' already defined in ' + schema.namespace);
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) {
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          this.apiFunctions_.registerUnavailable(functionDef.name);
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        var apiFunction = {};
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        apiFunction.definition = functionDef;
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        apiFunction.name = schema.namespace + '.' + functionDef.name;
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
283b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        if (!GetAvailability(apiFunction.name).is_available ||
284b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            (checkUnprivileged && !isSchemaAccessAllowed(functionDef))) {
285b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          this.apiFunctions_.registerUnavailable(functionDef.name);
286b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          return;
287b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        }
288b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // TODO(aa): It would be best to run this in a unit test, but in order
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // to do that we would need to better factor this code so that it
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // doesn't depend on so much v8::Extension machinery.
292868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        if (logging.DCHECK_IS_ON() &&
293868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            schemaUtils.isFunctionSignatureAmbiguous(apiFunction.definition)) {
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new Error(
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              apiFunction.name + ' has ambiguous optional arguments. ' +
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              'To implement custom disambiguation logic, add ' +
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              '"allowAmbiguousOptionalArguments" to the function\'s schema.');
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        this.apiFunctions_.register(functionDef.name, apiFunction);
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3027d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        mod[functionDef.name] = $Function.bind(function() {
303eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          var args = $Array.slice(arguments);
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (this.updateArgumentsPreValidate)
305eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            args = $Function.apply(this.updateArgumentsPreValidate, this, args);
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          args = schemaUtils.normalizeArgumentsAndValidate(args, this);
308eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          if (this.updateArgumentsPostValidate) {
309eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            args = $Function.apply(this.updateArgumentsPostValidate,
310eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   this,
311eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   args);
312eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          }
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          sendRequestHandler.clearCalledSendRequest();
315c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          var retval;
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (this.handleRequest) {
318eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            retval = $Function.apply(this.handleRequest, this, args);
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else {
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var optArgs = {
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              customCallback: this.customCallback
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            };
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            retval = sendRequest(this.name, args,
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 this.definition.parameters,
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 optArgs);
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
327c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          sendRequestHandler.clearCalledSendRequest();
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
329868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          // Validate return value if in sanity check mode.
330868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          if (logging.DCHECK_IS_ON() && this.definition.returns)
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            schemaUtils.validate([retval], [this.definition.returns]);
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return retval;
3337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        }, apiFunction);
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }, this);
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Setup Events
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (schema.events) {
3397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      $Array.forEach(schema.events, function(eventDef) {
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (eventDef.name in mod) {
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          throw new Error('Event ' + eventDef.name +
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          ' already defined in ' + schema.namespace);
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaNodeSupported(eventDef, platform, manifestVersion))
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
346b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
347b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        var eventName = schema.namespace + "." + eventDef.name;
348b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        if (!GetAvailability(eventName).is_available ||
349b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            (checkUnprivileged && !isSchemaAccessAllowed(eventDef))) {
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        var options = eventDef.options || {};
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (eventDef.filters && eventDef.filters.length > 0)
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          options.supportsFilters = true;
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
357ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        var parameters = eventDef.parameters;
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (this.customEvent_) {
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          mod[eventDef.name] = new this.customEvent_(
360ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch              eventName, parameters, eventDef.extraParameters, options);
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else {
362ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          mod[eventDef.name] = new Event(eventName, parameters, options);
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }, this);
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function addProperties(m, parentDef) {
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      var properties = parentDef.properties;
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!properties)
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      forEach(properties, function(propertyName, propertyDef) {
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (propertyName in m)
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;  // TODO(kalman): be strict like functions/events somehow.
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion))
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
377b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        if (!GetAvailability(schema.namespace + "." +
378b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)              propertyName).is_available ||
379b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            (checkUnprivileged && !isSchemaAccessAllowed(propertyDef))) {
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        var value = propertyDef.value;
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (value) {
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Values may just have raw types as defined in the JSON, such
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here.
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // TODO(kalman): enforce that things with a "value" property can't
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // define their own types.
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          var type = propertyDef.type || typeof(value);
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (type === 'integer' || type === 'number') {
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            value = parseInt(value);
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else if (type === 'boolean') {
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            value = value === 'true';
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else if (propertyDef['$ref']) {
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var ref = propertyDef['$ref'];
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var type = utils.loadTypeSchema(propertyDef['$ref'], schema);
3977d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)            logging.CHECK(type, 'Schema for $ref type ' + ref + ' not found');
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var constructor = createCustomType(type);
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            var args = value;
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // For an object propertyDef, |value| is an array of constructor
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // arguments, but we want to pass the arguments directly (i.e.
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // not as an array), so we have to fake calling |new| on the
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // constructor.
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            value = { __proto__: constructor.prototype };
405eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            $Function.apply(constructor, value, args);
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // Recursively add properties.
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            addProperties(value, propertyDef);
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else if (type === 'object') {
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // Recursively add properties.
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            addProperties(value, propertyDef);
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else if (type !== 'string') {
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            throw new Error('NOT IMPLEMENTED (extension_api.json error): ' +
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                'Cannot parse values for type "' + type + '"');
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          m[propertyName] = value;
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      });
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    };
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    addProperties(mod, schema);
421b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
422b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    var availability = GetAvailability(schema.namespace);
423eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (!availability.is_available && $Object.keys(mod).length == 0) {
424b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      console.error('chrome.' + schema.namespace + ' is not available: ' +
425b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                        availability.message);
426b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      return;
427b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
428b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.runHooks_(mod);
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return mod;
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)exports.Binding = Binding;
435