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

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

Powered by Google App Engine
This is Rietveld 408576698