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

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: renamed BluetoothUUID.typemap to Bluetooth.typemap Created 3 years, 11 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 canonicalizedFilter->services->append(validatedService);
63 } 66 }
64 canonicalizedFilter.services.assign(services);
65 } 67 }
66 68
67 canonicalizedFilter.hasName = filter.hasName();
68 if (filter.hasName()) { 69 if (filter.hasName()) {
69 size_t nameLength = filter.name().utf8().length(); 70 size_t nameLength = filter.name().utf8().length();
70 if (nameLength > kMaxDeviceNameLength) { 71 if (nameLength > kMaxDeviceNameLength) {
71 exceptionState.throwTypeError(kDeviceNameTooLong); 72 exceptionState.throwTypeError(kDeviceNameTooLong);
72 return; 73 return;
73 } 74 }
74 if (nameLength > kMaxFilterNameLength) { 75 if (nameLength > kMaxFilterNameLength) {
75 exceptionState.throwDOMException(NotFoundError, kFilterNameTooLong); 76 exceptionState.throwDOMException(NotFoundError, kFilterNameTooLong);
76 return; 77 return;
77 } 78 }
78 canonicalizedFilter.name = filter.name(); 79 canonicalizedFilter->name = filter.name();
79 } 80 }
80 81
81 if (filter.hasNamePrefix()) { 82 if (filter.hasNamePrefix()) {
82 size_t namePrefixLength = filter.namePrefix().utf8().length(); 83 size_t namePrefixLength = filter.namePrefix().utf8().length();
83 if (namePrefixLength > kMaxDeviceNameLength) { 84 if (namePrefixLength > kMaxDeviceNameLength) {
84 exceptionState.throwTypeError(kDeviceNameTooLong); 85 exceptionState.throwTypeError(kDeviceNameTooLong);
85 return; 86 return;
86 } 87 }
87 if (namePrefixLength > kMaxFilterNameLength) { 88 if (namePrefixLength > kMaxFilterNameLength) {
88 exceptionState.throwDOMException(NotFoundError, kFilterNameTooLong); 89 exceptionState.throwDOMException(NotFoundError, kFilterNameTooLong);
89 return; 90 return;
90 } 91 }
91 if (filter.namePrefix().length() == 0) { 92 if (filter.namePrefix().length() == 0) {
92 exceptionState.throwTypeError( 93 exceptionState.throwTypeError(
93 "'namePrefix', if present, must me non-empty."); 94 "'namePrefix', if present, must me non-empty.");
94 return; 95 return;
95 } 96 }
96 canonicalizedFilter.namePrefix = filter.namePrefix(); 97 canonicalizedFilter->name_prefix = filter.namePrefix();
97 } 98 }
98 } 99 }
99 100
100 static void convertRequestDeviceOptions(const RequestDeviceOptions& options, 101 static void convertRequestDeviceOptions(
101 WebRequestDeviceOptions& result, 102 const RequestDeviceOptions& options,
102 ExceptionState& exceptionState) { 103 mojom::blink::WebBluetoothRequestDeviceOptionsPtr& result,
104 ExceptionState& exceptionState) {
103 if (!(options.hasFilters() ^ options.acceptAllDevices())) { 105 if (!(options.hasFilters() ^ options.acceptAllDevices())) {
104 exceptionState.throwTypeError( 106 exceptionState.throwTypeError(
105 "Either 'filters' should be present or 'acceptAllDevices' should be " 107 "Either 'filters' should be present or 'acceptAllDevices' should be "
106 "true, but not both."); 108 "true, but not both.");
107 return; 109 return;
108 } 110 }
109 111
110 result.acceptAllDevices = options.acceptAllDevices(); 112 result->accept_all_devices = options.acceptAllDevices();
111 113
112 result.hasFilters = options.hasFilters(); 114 if (options.hasFilters()) {
113 if (result.hasFilters) {
114 if (options.filters().isEmpty()) { 115 if (options.filters().isEmpty()) {
115 exceptionState.throwTypeError( 116 exceptionState.throwTypeError(
116 "'filters' member must be non-empty to find any devices."); 117 "'filters' member must be non-empty to find any devices.");
117 return; 118 return;
118 } 119 }
119 120
120 Vector<WebBluetoothScanFilter> filters; 121 result->filters.emplace();
122
121 for (const BluetoothScanFilterInit& filter : options.filters()) { 123 for (const BluetoothScanFilterInit& filter : options.filters()) {
122 WebBluetoothScanFilter canonicalizedFilter = WebBluetoothScanFilter(); 124 auto canonicalizedFilter = mojom::blink::WebBluetoothScanFilter::New();
123 125
124 canonicalizeFilter(filter, canonicalizedFilter, exceptionState); 126 canonicalizeFilter(filter, canonicalizedFilter, exceptionState);
125 127
126 if (exceptionState.hadException()) 128 if (exceptionState.hadException())
127 return; 129 return;
128 130
129 filters.append(canonicalizedFilter); 131 result->filters.value().append(std::move(canonicalizedFilter));
130 } 132 }
131
132 result.filters.assign(filters);
133 } 133 }
134 134
135 if (options.hasOptionalServices()) { 135 if (options.hasOptionalServices()) {
136 Vector<WebString> optionalServices;
137 for (const StringOrUnsignedLong& optionalService : 136 for (const StringOrUnsignedLong& optionalService :
138 options.optionalServices()) { 137 options.optionalServices()) {
139 const String& validatedOptionalService = 138 const String& validatedOptionalService =
140 BluetoothUUID::getService(optionalService, exceptionState); 139 BluetoothUUID::getService(optionalService, exceptionState);
141 if (exceptionState.hadException()) 140 if (exceptionState.hadException())
142 return; 141 return;
143 optionalServices.append(validatedOptionalService); 142 result->optional_services.append(validatedOptionalService);
144 } 143 }
145 result.optionalServices.assign(optionalServices);
146 } 144 }
147 } 145 }
148 146
149 class RequestDeviceCallback : public WebBluetoothRequestDeviceCallbacks { 147 void Bluetooth::dispose() {
150 public: 148 // The pipe to this object must be closed when is marked unreachable to
151 RequestDeviceCallback(Bluetooth* bluetooth, ScriptPromiseResolver* resolver) 149 // prevent messages from being dispatched before lazy sweeping.
152 : m_bluetooth(bluetooth), m_resolver(resolver) {} 150 if (m_clientBinding.is_bound())
151 m_clientBinding.Close();
152 }
153 153
154 void onSuccess(std::unique_ptr<WebBluetoothDeviceInit> deviceInit) override { 154 void Bluetooth::RequestDeviceCallback(
155 if (!m_resolver->getExecutionContext() || 155 ScriptPromiseResolver* resolver,
156 m_resolver->getExecutionContext()->isContextDestroyed()) 156 mojom::blink::WebBluetoothResult result,
157 return; 157 mojom::blink::WebBluetoothDevicePtr device) {
158 if (!resolver->getExecutionContext() ||
159 resolver->getExecutionContext()->isContextDestroyed())
160 return;
158 161
159 BluetoothDevice* device = m_bluetooth->getBluetoothDeviceRepresentingDevice( 162 if (result == mojom::blink::WebBluetoothResult::SUCCESS) {
160 std::move(deviceInit), m_resolver); 163 BluetoothDevice* bluetoothDevice = getBluetoothDeviceRepresentingDevice(
161 164 device->id->device_id, device->name, resolver);
162 m_resolver->resolve(device); 165 resolver->resolve(bluetoothDevice);
166 } else {
167 resolver->reject(BluetoothError::take(resolver, result));
163 } 168 }
164 169 }
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 170
180 // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice 171 // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
181 ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState, 172 ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState,
182 const RequestDeviceOptions& options, 173 const RequestDeviceOptions& options,
183 ExceptionState& exceptionState) { 174 ExceptionState& exceptionState) {
184 ExecutionContext* context = scriptState->getExecutionContext(); 175 ExecutionContext* context = scriptState->getExecutionContext();
185 176
186 // If the incumbent settings object is not a secure context, reject promise 177 // If the incumbent settings object is not a secure context, reject promise
187 // with a SecurityError and abort these steps. 178 // with a SecurityError and abort these steps.
188 String errorMessage; 179 String errorMessage;
189 if (!context->isSecureContext(errorMessage)) { 180 if (!context->isSecureContext(errorMessage)) {
190 return ScriptPromise::rejectWithDOMException( 181 return ScriptPromise::rejectWithDOMException(
191 scriptState, DOMException::create(SecurityError, errorMessage)); 182 scriptState, DOMException::create(SecurityError, errorMessage));
192 } 183 }
193 184
194 // If the algorithm is not allowed to show a popup, reject promise with a 185 // If the algorithm is not allowed to show a popup, reject promise with a
195 // SecurityError and abort these steps. 186 // SecurityError and abort these steps.
196 if (!UserGestureIndicator::consumeUserGesture()) { 187 if (!UserGestureIndicator::consumeUserGesture()) {
197 return ScriptPromise::rejectWithDOMException( 188 return ScriptPromise::rejectWithDOMException(
198 scriptState, 189 scriptState,
199 DOMException::create( 190 DOMException::create(
200 SecurityError, 191 SecurityError,
201 "Must be handling a user gesture to show a permission request.")); 192 "Must be handling a user gesture to show a permission request."));
202 } 193 }
203 194
204 WebBluetooth* webbluetooth = 195 if (!m_service) {
205 BluetoothSupplement::fromScriptState(scriptState); 196 InterfaceProvider* interfaceProvider = nullptr;
206 if (!webbluetooth) 197 ExecutionContext* executionContext = scriptState->getExecutionContext();
198 if (executionContext->isDocument()) {
199 Document* document = toDocument(executionContext);
200 if (document->frame())
201 interfaceProvider = document->frame()->interfaceProvider();
202 }
203
204 if (interfaceProvider)
205 interfaceProvider->getInterface(mojo::MakeRequest(&m_service));
206
207 if (m_service) {
208 // Create an associated interface ptr and pass it to the
209 // WebBluetoothService so that it can send us events without us
210 // prompting.
211 mojom::blink::WebBluetoothServiceClientAssociatedPtrInfo ptrInfo;
212 m_clientBinding.Bind(&ptrInfo, m_service.associated_group());
213 m_service->SetClient(std::move(ptrInfo));
214 }
215 }
216
217 if (!m_service) {
207 return ScriptPromise::rejectWithDOMException( 218 return ScriptPromise::rejectWithDOMException(
208 scriptState, DOMException::create(NotSupportedError)); 219 scriptState, DOMException::create(NotSupportedError));
220 }
209 221
210 // In order to convert the arguments from service names and aliases to just 222 // In order to convert the arguments from service names and aliases to just
211 // UUIDs, do the following substeps: 223 // UUIDs, do the following substeps:
212 WebRequestDeviceOptions webOptions; 224 auto deviceOptions = mojom::blink::WebBluetoothRequestDeviceOptions::New();
213 convertRequestDeviceOptions(options, webOptions, exceptionState); 225 convertRequestDeviceOptions(options, deviceOptions, exceptionState);
226
214 if (exceptionState.hadException()) 227 if (exceptionState.hadException())
215 return exceptionState.reject(scriptState); 228 return exceptionState.reject(scriptState);
216 229
217 // Subsequent steps are handled in the browser process. 230 // Subsequent steps are handled in the browser process.
218 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); 231 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
219 ScriptPromise promise = resolver->promise(); 232 ScriptPromise promise = resolver->promise();
220 webbluetooth->requestDevice(webOptions, 233
221 new RequestDeviceCallback(this, resolver)); 234 service()->RequestDevice(
235 std::move(deviceOptions),
236 convertToBaseCallback(WTF::bind(&Bluetooth::RequestDeviceCallback,
237 wrapPersistent(this),
238 wrapPersistent(resolver))));
222 return promise; 239 return promise;
223 } 240 }
224 241
242 void Bluetooth::addDevice(const String& deviceId, BluetoothDevice* device) {
243 m_connectedDevices.add(deviceId, device);
244 }
245
246 void Bluetooth::removeDevice(const String& deviceId) {
247 m_connectedDevices.remove(deviceId);
248 }
249
250 void Bluetooth::registerCharacteristicObject(
251 const String& characteristicInstanceId,
252 BluetoothRemoteGATTCharacteristic* characteristic) {
253 m_activeCharacteristics.add(characteristicInstanceId, characteristic);
254 }
255
256 void Bluetooth::characteristicObjectRemoved(
257 const String& characteristicInstanceId) {
258 m_activeCharacteristics.remove(characteristicInstanceId);
259 }
260
225 DEFINE_TRACE(Bluetooth) { 261 DEFINE_TRACE(Bluetooth) {
226 visitor->trace(m_deviceInstanceMap); 262 visitor->trace(m_deviceInstanceMap);
263 visitor->trace(m_activeCharacteristics);
264 visitor->trace(m_connectedDevices);
265 }
266
267 Bluetooth::Bluetooth() : m_clientBinding(this) {}
268
269 void Bluetooth::RemoteCharacteristicValueChanged(
270 const WTF::String& characteristicInstanceId,
271 const WTF::Vector<uint8_t>& value) {
272 BluetoothRemoteGATTCharacteristic* characteristic =
273 m_activeCharacteristics.get(characteristicInstanceId);
274 if (characteristic)
275 characteristic->dispatchCharacteristicValueChanged(value);
276 }
277
278 void Bluetooth::GattServerDisconnected(
279 mojom::blink::WebBluetoothDeviceIdPtr deviceId) {
280 BluetoothDevice* device = m_connectedDevices.get(deviceId->device_id);
281 if (device) {
282 // Remove device from the map before calling dispatchGattServerDisconnected
283 // to avoid removing a device the gattserverdisconnected event handler might
284 // have re-connected.
285 m_connectedDevices.remove(deviceId->device_id);
286 device->dispatchGattServerDisconnected();
287 }
227 } 288 }
228 289
229 BluetoothDevice* Bluetooth::getBluetoothDeviceRepresentingDevice( 290 BluetoothDevice* Bluetooth::getBluetoothDeviceRepresentingDevice(
230 std::unique_ptr<WebBluetoothDeviceInit> deviceInit, 291 const String& id,
292 const String& name,
231 ScriptPromiseResolver* resolver) { 293 ScriptPromiseResolver* resolver) {
232 BluetoothDevice* device = m_deviceInstanceMap.get(deviceInit->id); 294 BluetoothDevice* device = m_deviceInstanceMap.get(id);
233 if (!device) { 295 if (!device) {
234 String deviceId = deviceInit->id; 296 device = BluetoothDevice::take(resolver, id, name, this);
235 device = BluetoothDevice::take(resolver, std::move(deviceInit)); 297 auto result = m_deviceInstanceMap.add(id, device);
236
237 auto result = m_deviceInstanceMap.add(deviceId, device);
238 DCHECK(result.isNewEntry); 298 DCHECK(result.isNewEntry);
239 } 299 }
240 return device; 300 return device;
241 } 301 }
242 302
243 } // namespace blink 303 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698