| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Custom binding for the Bluetooth API. |
| |
| var binding = require('binding').Binding.create('bluetooth'); |
| |
| var chrome = requireNative('chrome').GetChrome(); |
| var Event = require('event_bindings').Event; |
| var lastError = require('lastError'); |
| var sendRequest = require('sendRequest').sendRequest; |
| |
| // Use custom binding to create an undocumented event listener that will |
| // receive events about device discovery and call the event listener that was |
| // provided with the request to begin discovery. |
| binding.registerCustomHook(function(api) { |
| var apiFunctions = api.apiFunctions; |
| |
| var bluetooth = {}; |
| |
| function callCallbackIfPresent(name, args, error) { |
| var callback = args[args.length - 1]; |
| if (typeof(callback) == 'function') |
| lastError.run(name, error, callback); |
| } |
| |
| bluetooth.deviceDiscoveredHandler = null; |
| bluetooth.onDeviceDiscovered = new Event('bluetooth.onDeviceDiscovered'); |
| function clearDeviceDiscoveredHandler() { |
| bluetooth.onDeviceDiscovered.removeListener( |
| bluetooth.deviceDiscoveredHandler); |
| bluetooth.deviceDiscoveredHandler = null; |
| } |
| apiFunctions.setHandleRequest('startDiscovery', |
| function() { |
| var args = arguments; |
| if (args.length > 0 && args[0] && args[0].deviceCallback) { |
| if (bluetooth.deviceDiscoveredHandler != null) { |
| callCallbackIfPresent('bluetooth.startDiscovery', |
| args, |
| 'Concurrent discovery is not allowed.'); |
| return; |
| } |
| |
| bluetooth.deviceDiscoveredHandler = args[0].deviceCallback; |
| bluetooth.onDeviceDiscovered.addListener( |
| bluetooth.deviceDiscoveredHandler); |
| sendRequest(this.name, |
| args, |
| this.definition.parameters, |
| {customCallback:this.customCallback}); |
| } else { |
| callCallbackIfPresent( |
| 'bluetooth.startDiscovery', |
| args, |
| 'deviceCallback is required in the options object'); |
| return; |
| } |
| }); |
| apiFunctions.setCustomCallback('startDiscovery', |
| function(name, request, response) { |
| if (chrome.runtime.lastError) { |
| clearDeviceDiscoveredHandler(); |
| return; |
| } |
| }); |
| apiFunctions.setHandleRequest('stopDiscovery', |
| function() { |
| clearDeviceDiscoveredHandler(); |
| sendRequest(this.name, arguments, this.definition.parameters); |
| }); |
| |
| // An object to hold state during one call to getDevices. |
| bluetooth.getDevicesState = null; |
| |
| // Hidden events used to deliver getDevices data to the client callbacks |
| bluetooth.onDeviceSearchResult = new Event('bluetooth.onDeviceSearchResult'); |
| bluetooth.onDeviceSearchFinished = |
| new Event('bluetooth.onDeviceSearchFinished'); |
| |
| function deviceSearchResultHandler(device) { |
| bluetooth.getDevicesState.actualEvents++; |
| bluetooth.getDevicesState.deviceCallback(device); |
| maybeFinishDeviceSearch(); |
| } |
| |
| function deviceSearchFinishedHandler(info) { |
| bluetooth.getDevicesState.expectedEventCount = info.expectedEventCount; |
| maybeFinishDeviceSearch(); |
| } |
| |
| function addDeviceSearchListeners() { |
| bluetooth.onDeviceSearchResult.addListener(deviceSearchResultHandler); |
| bluetooth.onDeviceSearchFinished.addListener(deviceSearchFinishedHandler); |
| } |
| |
| function removeDeviceSearchListeners() { |
| bluetooth.onDeviceSearchResult.removeListener(deviceSearchResultHandler); |
| bluetooth.onDeviceSearchFinished.removeListener( |
| deviceSearchFinishedHandler); |
| } |
| |
| function maybeFinishDeviceSearch() { |
| var state = bluetooth.getDevicesState; |
| if (typeof(state.expectedEventCount) != 'undefined' && |
| state.actualEvents >= state.expectedEventCount) { |
| finishDeviceSearch(); |
| } |
| } |
| |
| function finishDeviceSearch() { |
| var finalCallback = bluetooth.getDevicesState.finalCallback; |
| removeDeviceSearchListeners(); |
| bluetooth.getDevicesState = null; |
| |
| if (finalCallback) { |
| finalCallback(); |
| } |
| } |
| |
| apiFunctions.setUpdateArgumentsPostValidate('getDevices', |
| function() { |
| var args = $Array.slice(arguments); |
| |
| if (bluetooth.getDevicesState != null) { |
| throw new Error('Concurrent calls to getDevices are not allowed.'); |
| } |
| |
| var state = { actualEvents: 0 }; |
| |
| if (typeof(args[args.length - 1]) == 'function') { |
| state.finalCallback = args.pop(); |
| $Array.push(args, |
| function() { |
| if (chrome.runtime.lastError) { |
| finishDeviceSearch(); |
| } |
| }); |
| } else { |
| throw new Error('getDevices must have a final callback parameter.'); |
| } |
| |
| if (typeof(args[0].deviceCallback) == 'function') { |
| state.deviceCallback = args[0].deviceCallback; |
| } else { |
| throw new Error('getDevices must be passed options with a ' + |
| 'deviceCallback.'); |
| } |
| |
| bluetooth.getDevicesState = state; |
| addDeviceSearchListeners(); |
| |
| return args; |
| }); |
| }); |
| |
| exports.binding = binding.generate(); |