Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(447)

Side by Side Diff: third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp

Issue 2565913002: [Onion Soup] Move WebBluetoothImpl from //content/renderer/bluetooth to Blink's bluetooth module (Closed)
Patch Set: check if binding is bound before close it Created 3 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "modules/bluetooth/Bluetooth.h" 5 #include "modules/bluetooth/Bluetooth.h"
6 6
7 #include "bindings/core/v8/CallbackPromiseAdapter.h" 7 #include "bindings/core/v8/CallbackPromiseAdapter.h"
8 #include "bindings/core/v8/ScriptPromise.h" 8 #include "bindings/core/v8/ScriptPromise.h"
9 #include "bindings/core/v8/ScriptPromiseResolver.h" 9 #include "bindings/core/v8/ScriptPromiseResolver.h"
10 #include "core/dom/DOMException.h" 10 #include "core/dom/DOMException.h"
11 #include "core/dom/Document.h"
11 #include "core/dom/ExceptionCode.h" 12 #include "core/dom/ExceptionCode.h"
13 #include "core/dom/ExecutionContext.h"
14 #include "core/frame/LocalFrame.h"
12 #include "modules/bluetooth/BluetoothDevice.h" 15 #include "modules/bluetooth/BluetoothDevice.h"
13 #include "modules/bluetooth/BluetoothError.h" 16 #include "modules/bluetooth/BluetoothError.h"
14 #include "modules/bluetooth/BluetoothSupplement.h" 17 #include "modules/bluetooth/BluetoothRemoteGATTCharacteristic.h"
15 #include "modules/bluetooth/BluetoothUUID.h" 18 #include "modules/bluetooth/BluetoothUUID.h"
16 #include "modules/bluetooth/RequestDeviceOptions.h" 19 #include "modules/bluetooth/RequestDeviceOptions.h"
17 #include "platform/UserGestureIndicator.h" 20 #include "platform/UserGestureIndicator.h"
18 #include "public/platform/modules/bluetooth/WebBluetooth.h" 21 #include "public/platform/InterfaceProvider.h"
19 #include "public/platform/modules/bluetooth/WebRequestDeviceOptions.h"
20 #include <memory> 22 #include <memory>
21 #include <utility> 23 #include <utility>
22 24
23 namespace blink { 25 namespace blink {
24 26
25 namespace { 27 namespace {
26 // A device name can never be longer than 29 bytes. A adv packet is at most 28 // A device name can never be longer than 29 bytes. A adv packet is at most
27 // 31 bytes long. The length and identifier of the length field take 2 bytes. 29 // 31 bytes long. The length and identifier of the length field take 2 bytes.
28 // That least 29 bytes for the name. 30 // That least 29 bytes for the name.
29 const size_t kMaxFilterNameLength = 29; 31 const size_t kMaxFilterNameLength = 29;
30 const char kFilterNameTooLong[] = 32 const char kFilterNameTooLong[] =
31 "A 'name' or 'namePrefix' longer than 29 bytes results in no devices being " 33 "A 'name' or 'namePrefix' longer than 29 bytes results in no devices being "
32 "found, because a device can't advertise a name longer than 29 bytes."; 34 "found, because a device can't advertise a name longer than 29 bytes.";
33 // Per the Bluetooth Spec: The name is a user-friendly name associated with the 35 // Per the Bluetooth Spec: The name is a user-friendly name associated with the
34 // device and consists of a maximum of 248 bytes coded according to the UTF-8 36 // device and consists of a maximum of 248 bytes coded according to the UTF-8
35 // standard. 37 // standard.
36 const size_t kMaxDeviceNameLength = 248; 38 const size_t kMaxDeviceNameLength = 248;
37 const char kDeviceNameTooLong[] = 39 const char kDeviceNameTooLong[] =
38 "A device name can't be longer than 248 bytes."; 40 "A device name can't be longer than 248 bytes.";
39 } // namespace 41 } // namespace
40 42
41 static void canonicalizeFilter(const BluetoothScanFilterInit& filter, 43 static void canonicalizeFilter(
42 WebBluetoothScanFilter& canonicalizedFilter, 44 const BluetoothScanFilterInit& filter,
43 ExceptionState& exceptionState) { 45 mojom::blink::WebBluetoothScanFilterPtr& canonicalizedFilter,
46 ExceptionState& exceptionState) {
44 if (!(filter.hasServices() || filter.hasName() || filter.hasNamePrefix())) { 47 if (!(filter.hasServices() || filter.hasName() || filter.hasNamePrefix())) {
45 exceptionState.throwTypeError( 48 exceptionState.throwTypeError(
46 "A filter must restrict the devices in some way."); 49 "A filter must restrict the devices in some way.");
47 return; 50 return;
48 } 51 }
49 52
50 if (filter.hasServices()) { 53 if (filter.hasServices()) {
51 if (filter.services().size() == 0) { 54 if (filter.services().size() == 0) {
52 exceptionState.throwTypeError( 55 exceptionState.throwTypeError(
53 "'services', if present, must contain at least one service."); 56 "'services', if present, must contain at least one service.");
54 return; 57 return;
55 } 58 }
56 Vector<WebString> services; 59 canonicalizedFilter->services.emplace();
57 for (const StringOrUnsignedLong& service : filter.services()) { 60 for (const StringOrUnsignedLong& service : filter.services()) {
58 const String& validatedService = 61 const String& validatedService =
59 BluetoothUUID::getService(service, exceptionState); 62 BluetoothUUID::getService(service, exceptionState);
60 if (exceptionState.hadException()) 63 if (exceptionState.hadException())
61 return; 64 return;
62 services.append(validatedService); 65 bluetooth::mojom::blink::UUIDPtr uuid =
66 BluetoothUUID::createMojoUuid(validatedService);
67 canonicalizedFilter->services->append(std::move(uuid));
63 } 68 }
64 canonicalizedFilter.services.assign(services);
65 } 69 }
66 70
67 canonicalizedFilter.hasName = filter.hasName();
68 if (filter.hasName()) { 71 if (filter.hasName()) {
69 size_t nameLength = filter.name().utf8().length(); 72 size_t nameLength = filter.name().utf8().length();
70 if (nameLength > kMaxDeviceNameLength) { 73 if (nameLength > kMaxDeviceNameLength) {
71 exceptionState.throwTypeError(kDeviceNameTooLong); 74 exceptionState.throwTypeError(kDeviceNameTooLong);
72 return; 75 return;
73 } 76 }
74 if (nameLength > kMaxFilterNameLength) { 77 if (nameLength > kMaxFilterNameLength) {
75 exceptionState.throwDOMException(NotFoundError, kFilterNameTooLong); 78 exceptionState.throwDOMException(NotFoundError, kFilterNameTooLong);
76 return; 79 return;
77 } 80 }
78 canonicalizedFilter.name = filter.name(); 81 canonicalizedFilter->name = filter.name();
79 } 82 }
80 83
81 if (filter.hasNamePrefix()) { 84 if (filter.hasNamePrefix()) {
82 size_t namePrefixLength = filter.namePrefix().utf8().length(); 85 size_t namePrefixLength = filter.namePrefix().utf8().length();
83 if (namePrefixLength > kMaxDeviceNameLength) { 86 if (namePrefixLength > kMaxDeviceNameLength) {
84 exceptionState.throwTypeError(kDeviceNameTooLong); 87 exceptionState.throwTypeError(kDeviceNameTooLong);
85 return; 88 return;
86 } 89 }
87 if (namePrefixLength > kMaxFilterNameLength) { 90 if (namePrefixLength > kMaxFilterNameLength) {
88 exceptionState.throwDOMException(NotFoundError, kFilterNameTooLong); 91 exceptionState.throwDOMException(NotFoundError, kFilterNameTooLong);
89 return; 92 return;
90 } 93 }
91 if (filter.namePrefix().length() == 0) { 94 if (filter.namePrefix().length() == 0) {
92 exceptionState.throwTypeError( 95 exceptionState.throwTypeError(
93 "'namePrefix', if present, must me non-empty."); 96 "'namePrefix', if present, must me non-empty.");
94 return; 97 return;
95 } 98 }
96 canonicalizedFilter.namePrefix = filter.namePrefix(); 99 canonicalizedFilter->name_prefix = filter.namePrefix();
97 } 100 }
98 } 101 }
99 102
100 static void convertRequestDeviceOptions(const RequestDeviceOptions& options, 103 static void convertRequestDeviceOptions(
101 WebRequestDeviceOptions& result, 104 const RequestDeviceOptions& options,
102 ExceptionState& exceptionState) { 105 mojom::blink::WebBluetoothRequestDeviceOptionsPtr& result,
106 ExceptionState& exceptionState) {
103 if (!(options.hasFilters() ^ options.acceptAllDevices())) { 107 if (!(options.hasFilters() ^ options.acceptAllDevices())) {
104 exceptionState.throwTypeError( 108 exceptionState.throwTypeError(
105 "Either 'filters' should be present or 'acceptAllDevices' should be " 109 "Either 'filters' should be present or 'acceptAllDevices' should be "
106 "true, but not both."); 110 "true, but not both.");
107 return; 111 return;
108 } 112 }
109 113
110 result.acceptAllDevices = options.acceptAllDevices(); 114 result->accept_all_devices = options.acceptAllDevices();
111 115
112 result.hasFilters = options.hasFilters(); 116 if (options.hasFilters()) {
113 if (result.hasFilters) {
114 if (options.filters().isEmpty()) { 117 if (options.filters().isEmpty()) {
115 exceptionState.throwTypeError( 118 exceptionState.throwTypeError(
116 "'filters' member must be non-empty to find any devices."); 119 "'filters' member must be non-empty to find any devices.");
117 return; 120 return;
118 } 121 }
119 122
120 Vector<WebBluetoothScanFilter> filters; 123 result->filters.emplace();
124
121 for (const BluetoothScanFilterInit& filter : options.filters()) { 125 for (const BluetoothScanFilterInit& filter : options.filters()) {
122 WebBluetoothScanFilter canonicalizedFilter = WebBluetoothScanFilter(); 126 auto canonicalizedFilter = mojom::blink::WebBluetoothScanFilter::New();
123 127
124 canonicalizeFilter(filter, canonicalizedFilter, exceptionState); 128 canonicalizeFilter(filter, canonicalizedFilter, exceptionState);
125 129
126 if (exceptionState.hadException()) 130 if (exceptionState.hadException())
127 return; 131 return;
128 132
129 filters.append(canonicalizedFilter); 133 result->filters.value().append(std::move(canonicalizedFilter));
130 } 134 }
131
132 result.filters.assign(filters);
133 } 135 }
134 136
135 if (options.hasOptionalServices()) { 137 if (options.hasOptionalServices()) {
136 Vector<WebString> optionalServices;
137 for (const StringOrUnsignedLong& optionalService : 138 for (const StringOrUnsignedLong& optionalService :
138 options.optionalServices()) { 139 options.optionalServices()) {
139 const String& validatedOptionalService = 140 const String& validatedOptionalService =
140 BluetoothUUID::getService(optionalService, exceptionState); 141 BluetoothUUID::getService(optionalService, exceptionState);
141 if (exceptionState.hadException()) 142 if (exceptionState.hadException())
142 return; 143 return;
143 optionalServices.append(validatedOptionalService); 144 bluetooth::mojom::blink::UUIDPtr uuid =
145 BluetoothUUID::createMojoUuid(validatedOptionalService);
146 result->optional_services.append(std::move(uuid));
144 } 147 }
145 result.optionalServices.assign(optionalServices);
146 } 148 }
147 } 149 }
148 150
149 class RequestDeviceCallback : public WebBluetoothRequestDeviceCallbacks { 151 void Bluetooth::dispose() {
150 public: 152 // The pipe to this object must be closed when is marked unreachable to
151 RequestDeviceCallback(Bluetooth* bluetooth, ScriptPromiseResolver* resolver) 153 // prevent messages from being dispatched before lazy sweeping.
152 : m_bluetooth(bluetooth), m_resolver(resolver) {} 154 if (m_clientBinding.is_bound())
155 m_clientBinding.Close();
156 }
153 157
154 void onSuccess(std::unique_ptr<WebBluetoothDeviceInit> deviceInit) override { 158 void Bluetooth::RequestDeviceCallback(
155 if (!m_resolver->getExecutionContext() || 159 ScriptPromiseResolver* resolver,
156 m_resolver->getExecutionContext()->isContextDestroyed()) 160 mojom::blink::WebBluetoothResult result,
157 return; 161 mojom::blink::WebBluetoothDevicePtr device) {
162 if (!resolver->getExecutionContext() ||
163 resolver->getExecutionContext()->isContextDestroyed())
164 return;
158 165
159 BluetoothDevice* device = m_bluetooth->getBluetoothDeviceRepresentingDevice( 166 if (result == mojom::blink::WebBluetoothResult::SUCCESS) {
160 std::move(deviceInit), m_resolver); 167 BluetoothDevice* bluetoothDevice = getBluetoothDeviceRepresentingDevice(
161 168 device->id->device_id, device->name, resolver);
162 m_resolver->resolve(device); 169 resolver->resolve(bluetoothDevice);
170 } else {
171 resolver->reject(BluetoothError::take(resolver, result));
163 } 172 }
164 173 }
165 void onError(
166 int32_t
167 error /* Corresponds to WebBluetoothResult in web_bluetooth.mojom */)
168 override {
169 if (!m_resolver->getExecutionContext() ||
170 m_resolver->getExecutionContext()->isContextDestroyed())
171 return;
172 m_resolver->reject(BluetoothError::take(m_resolver, error));
173 }
174
175 private:
176 Persistent<Bluetooth> m_bluetooth;
177 Persistent<ScriptPromiseResolver> m_resolver;
178 };
179 174
180 // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice 175 // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
181 ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState, 176 ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState,
182 const RequestDeviceOptions& options, 177 const RequestDeviceOptions& options,
183 ExceptionState& exceptionState) { 178 ExceptionState& exceptionState) {
184 ExecutionContext* context = scriptState->getExecutionContext(); 179 ExecutionContext* context = scriptState->getExecutionContext();
185 180
186 // If the incumbent settings object is not a secure context, reject promise 181 // If the incumbent settings object is not a secure context, reject promise
187 // with a SecurityError and abort these steps. 182 // with a SecurityError and abort these steps.
188 String errorMessage; 183 String errorMessage;
189 if (!context->isSecureContext(errorMessage)) { 184 if (!context->isSecureContext(errorMessage)) {
190 return ScriptPromise::rejectWithDOMException( 185 return ScriptPromise::rejectWithDOMException(
191 scriptState, DOMException::create(SecurityError, errorMessage)); 186 scriptState, DOMException::create(SecurityError, errorMessage));
192 } 187 }
193 188
194 // If the algorithm is not allowed to show a popup, reject promise with a 189 // If the algorithm is not allowed to show a popup, reject promise with a
195 // SecurityError and abort these steps. 190 // SecurityError and abort these steps.
196 if (!UserGestureIndicator::consumeUserGesture()) { 191 if (!UserGestureIndicator::consumeUserGesture()) {
197 return ScriptPromise::rejectWithDOMException( 192 return ScriptPromise::rejectWithDOMException(
198 scriptState, 193 scriptState,
199 DOMException::create( 194 DOMException::create(
200 SecurityError, 195 SecurityError,
201 "Must be handling a user gesture to show a permission request.")); 196 "Must be handling a user gesture to show a permission request."));
202 } 197 }
203 198
204 WebBluetooth* webbluetooth = 199 if (!m_service) {
205 BluetoothSupplement::fromScriptState(scriptState); 200 InterfaceProvider* interfaceProvider = nullptr;
206 if (!webbluetooth) 201 ExecutionContext* executionContext = scriptState->getExecutionContext();
202 if (executionContext->isDocument()) {
203 Document* document = toDocument(executionContext);
204 if (document->frame())
205 interfaceProvider = document->frame()->interfaceProvider();
206 }
207
208 if (interfaceProvider)
209 interfaceProvider->getInterface(mojo::MakeRequest(&m_service));
210
211 if (m_service) {
212 // Create an associated interface ptr and pass it to the
213 // WebBluetoothService so that it can send us events without us
214 // prompting.
215 mojom::blink::WebBluetoothServiceClientAssociatedPtrInfo ptrInfo;
216 m_clientBinding.Bind(&ptrInfo, m_service.associated_group());
217 m_service->SetClient(std::move(ptrInfo));
218 }
219 }
220
221 if (!m_service) {
207 return ScriptPromise::rejectWithDOMException( 222 return ScriptPromise::rejectWithDOMException(
208 scriptState, DOMException::create(NotSupportedError)); 223 scriptState, DOMException::create(NotSupportedError));
224 }
209 225
210 // In order to convert the arguments from service names and aliases to just 226 // In order to convert the arguments from service names and aliases to just
211 // UUIDs, do the following substeps: 227 // UUIDs, do the following substeps:
212 WebRequestDeviceOptions webOptions; 228 auto deviceOptions = mojom::blink::WebBluetoothRequestDeviceOptions::New();
213 convertRequestDeviceOptions(options, webOptions, exceptionState); 229 convertRequestDeviceOptions(options, deviceOptions, exceptionState);
dcheng 2016/12/22 08:37:20 Is it possible to typemap WebBluetoothRequestDevic
juncai 2016/12/22 22:15:40 Will try this in a follow-up CL, and filed a bug f
230
214 if (exceptionState.hadException()) 231 if (exceptionState.hadException())
215 return exceptionState.reject(scriptState); 232 return exceptionState.reject(scriptState);
216 233
217 // Subsequent steps are handled in the browser process. 234 // Subsequent steps are handled in the browser process.
218 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); 235 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
219 ScriptPromise promise = resolver->promise(); 236 ScriptPromise promise = resolver->promise();
220 webbluetooth->requestDevice(webOptions, 237
221 new RequestDeviceCallback(this, resolver)); 238 service()->RequestDevice(
239 std::move(deviceOptions),
240 convertToBaseCallback(WTF::bind(&Bluetooth::RequestDeviceCallback,
241 wrapPersistent(this),
242 wrapPersistent(resolver))));
222 return promise; 243 return promise;
223 } 244 }
224 245
246 void Bluetooth::addDevice(const String& deviceId, BluetoothDevice* device) {
247 m_connectedDevices.add(deviceId, device);
248 }
249
250 void Bluetooth::removeDevice(const String& deviceId) {
251 m_connectedDevices.remove(deviceId);
252 }
253
254 void Bluetooth::registerCharacteristicObject(
255 const String& characteristicInstanceId,
256 BluetoothRemoteGATTCharacteristic* characteristic) {
257 m_activeCharacteristics.add(characteristicInstanceId, characteristic);
258 }
259
260 void Bluetooth::characteristicObjectRemoved(
261 const String& characteristicInstanceId) {
262 m_activeCharacteristics.remove(characteristicInstanceId);
263 }
264
225 DEFINE_TRACE(Bluetooth) { 265 DEFINE_TRACE(Bluetooth) {
226 visitor->trace(m_deviceInstanceMap); 266 visitor->trace(m_deviceInstanceMap);
267 visitor->trace(m_activeCharacteristics);
268 visitor->trace(m_connectedDevices);
269 }
270
271 Bluetooth::Bluetooth() : m_clientBinding(this) {}
272
273 void Bluetooth::RemoteCharacteristicValueChanged(
274 const WTF::String& characteristicInstanceId,
275 const WTF::Vector<uint8_t>& value) {
276 BluetoothRemoteGATTCharacteristic* characteristic =
277 m_activeCharacteristics.get(characteristicInstanceId);
278 if (characteristic)
279 characteristic->dispatchCharacteristicValueChanged(value);
280 }
281
282 void Bluetooth::GattServerDisconnected(
283 mojom::blink::WebBluetoothDeviceIdPtr deviceId) {
284 BluetoothDevice* device = m_connectedDevices.get(deviceId->device_id);
285 if (device) {
286 // Remove device from the map before calling dispatchGattServerDisconnected
287 // to avoid removing a device the gattserverdisconnected event handler might
288 // have re-connected.
289 m_connectedDevices.remove(deviceId->device_id);
290 device->dispatchGattServerDisconnected();
291 }
227 } 292 }
228 293
229 BluetoothDevice* Bluetooth::getBluetoothDeviceRepresentingDevice( 294 BluetoothDevice* Bluetooth::getBluetoothDeviceRepresentingDevice(
230 std::unique_ptr<WebBluetoothDeviceInit> deviceInit, 295 const String& id,
296 const String& name,
231 ScriptPromiseResolver* resolver) { 297 ScriptPromiseResolver* resolver) {
232 BluetoothDevice* device = m_deviceInstanceMap.get(deviceInit->id); 298 BluetoothDevice* device = m_deviceInstanceMap.get(id);
233 if (!device) { 299 if (!device) {
234 String deviceId = deviceInit->id; 300 device = BluetoothDevice::take(resolver, id, name, this);
235 device = BluetoothDevice::take(resolver, std::move(deviceInit)); 301 auto result = m_deviceInstanceMap.add(id, device);
236
237 auto result = m_deviceInstanceMap.add(deviceId, device);
238 DCHECK(result.isNewEntry); 302 DCHECK(result.isNewEntry);
239 } 303 }
240 return device; 304 return device;
241 } 305 }
242 306
243 } // namespace blink 307 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698