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