146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)var createClassWrapper = requireNative('utils').createClassWrapper;
66d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)var nativeDeepCopy = requireNative('utils').deepCopy;
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var schemaRegistry = requireNative('schema_registry');
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var CHECK = requireNative('logging').CHECK;
903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)var DCHECK = requireNative('logging').DCHECK;
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)var WARNING = requireNative('logging').WARNING;
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch/**
130529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * An object forEach. Calls |f| with each (key, value) pair of |obj|, using
140529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * |self| as the target.
150529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * @param {Object} obj The object to iterate over.
160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * @param {function} f The function to call in each iteration.
170529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * @param {Object} self The object to use as |this| in each function call.
180529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch */
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function forEach(obj, f, self) {
207d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  for (var key in obj) {
217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if ($Object.hasOwnProperty(obj, key))
227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      $Function.call(f, self, key, obj[key]);
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch/**
270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * Assuming |array_of_dictionaries| is structured like this:
280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * [{id: 1, ... }, {id: 2, ...}, ...], you can use
290529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * lookup(array_of_dictionaries, 'id', 2) to get the dictionary with id == 2.
300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * @param {Array.<Object.<string, ?>>} array_of_dictionaries
310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * @param {string} field
320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * @param {?} value
330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch */
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function lookup(array_of_dictionaries, field, value) {
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var filter = function (dict) {return dict[field] == value;};
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var matches = array_of_dictionaries.filter(filter);
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (matches.length == 0) {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return undefined;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (matches.length == 1) {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return matches[0]
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    throw new Error("Failed lookup of field '" + field + "' with value '" +
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    value + "'");
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function loadTypeSchema(typeName, defaultSchema) {
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  var parts = $String.split(typeName, '.');
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (parts.length == 1) {
50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (defaultSchema == null) {
51868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      WARNING('Trying to reference "' + typeName + '" ' +
52868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)              'with neither namespace nor default schema.');
53868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return null;
54868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var types = defaultSchema.types;
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    var schemaName = $Array.join($Array.slice(parts, 0, parts.length - 1), '.');
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var types = schemaRegistry.GetSchema(schemaName).types;
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (var i = 0; i < types.length; ++i) {
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (types[i].id == typeName)
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return types[i];
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return null;
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch/**
680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * Takes a private class implementation |cls| and exposes a subset of its
690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * methods |functions| and properties |properties| and |readonly| in a public
700529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * wrapper class that it returns. Within bindings code, you can access the
710529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * implementation from an instance of the wrapper class using
720529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * privates(instance).impl, and from the implementation class you can access
730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * the wrapper using this.wrapper (or implInstance.wrapper if you have another
740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * instance of the implementation class).
750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * @param {string} name The name of the exposed wrapper class.
760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * @param {Object} cls The class implementation.
77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {{superclass: ?Function,
78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *          functions: ?Array.<string>,
790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch *          properties: ?Array.<string>,
800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch *          readonly: ?Array.<string>}} exposed The names of properties on the
81f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *     implementation class to be exposed. |superclass| represents the
82f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *     constructor of the class to be used as the superclass of the exposed
83f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *     class; |functions| represents the names of functions which should be
84f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *     delegated to the implementation; |properties| are gettable/settable
85f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *     properties and |readonly| are read-only properties.
860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch */
870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochfunction expose(name, cls, exposed) {
88f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var publicClass = createClassWrapper(name, cls, exposed.superclass);
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  if ('functions' in exposed) {
910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    $Array.forEach(exposed.functions, function(func) {
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      publicClass.prototype[func] = function() {
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        var impl = privates(this).impl;
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return $Function.apply(impl[func], impl, arguments);
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      };
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    });
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  if ('properties' in exposed) {
1000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    $Array.forEach(exposed.properties, function(prop) {
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      $Object.defineProperty(publicClass.prototype, prop, {
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        enumerable: true,
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        get: function() {
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          return privates(this).impl[prop];
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        },
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        set: function(value) {
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          var impl = privates(this).impl;
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          delete impl[prop];
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          impl[prop] = value;
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      });
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    });
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1150529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  if ('readonly' in exposed) {
1160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    $Array.forEach(exposed.readonly, function(readonly) {
1170529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      $Object.defineProperty(publicClass.prototype, readonly, {
1180529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        enumerable: true,
1190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        get: function() {
1200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch          return privates(this).impl[readonly];
1210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        },
1220529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      });
1230529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    });
1240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  }
1250529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return publicClass;
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1296d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)/**
1306d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) * Returns a deep copy of |value|. The copy will have no references to nested
1316d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) * values of |value|.
1326d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) */
1336d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)function deepCopy(value) {
1346d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return nativeDeepCopy(value);
1356d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
1366d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
13703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/**
13803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Wrap an asynchronous API call to a function |func| in a promise. The
13903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * remaining arguments will be passed to |func|. Returns a promise that will be
14003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * resolved to the result passed to the callback or rejected if an error occurs
14103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * (if chrome.runtime.lastError is set). If there are multiple results, the
14203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * promise will be resolved with an array containing those results.
14303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) *
14403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * For example,
14503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * promise(chrome.storage.get, 'a').then(function(result) {
14603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) *   // Use result.
14703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * }).catch(function(error) {
14803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) *   // Report error.message.
14903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * });
15003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */
15103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)function promise(func) {
15203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  var args = $Array.slice(arguments, 1);
15303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  DCHECK(typeof func == 'function');
15403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return new Promise(function(resolve, reject) {
15503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    args.push(function() {
15603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if (chrome.runtime.lastError) {
15703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        reject(new Error(chrome.runtime.lastError));
15803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        return;
15903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      }
16003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if (arguments.length <= 1)
16103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        resolve(arguments[0]);
16203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      else
16303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        resolve($Array.slice(arguments));
16403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    });
16503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    $Function.apply(func, null, args);
16603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  });
16703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
16803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)exports.forEach = forEach;
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)exports.loadTypeSchema = loadTypeSchema;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)exports.lookup = lookup;
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)exports.expose = expose;
1736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)exports.deepCopy = deepCopy;
17403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)exports.promise = promise;
175