Index: third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.cpp |
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5ebd7daf14252c8cd69ef80e2efa27208737c216 |
--- /dev/null |
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.cpp |
@@ -0,0 +1,260 @@ |
+// Copyright 2015 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. |
+ |
+#include "modules/bluetooth/BluetoothRemoteGATTDescriptor.h" |
+ |
+#include "bindings/core/v8/CallbackPromiseAdapter.h" |
+#include "bindings/core/v8/ScriptPromise.h" |
+#include "bindings/core/v8/ScriptPromiseResolver.h" |
+#include "core/dom/DOMDataView.h" |
+#include "core/dom/DOMException.h" |
+#include "core/dom/ExceptionCode.h" |
+#include "core/events/Event.h" |
+#include "core/inspector/ConsoleMessage.h" |
+#include "modules/bluetooth/BluetoothError.h" |
+#include "modules/bluetooth/BluetoothRemoteGATTService.h" |
+#include "modules/bluetooth/BluetoothSupplement.h" |
+#include "public/platform/modules/bluetooth/WebBluetooth.h" |
+#include <memory> |
+ |
+namespace blink { |
+ |
+namespace { |
+ |
+// TODO dft move to some utility! |
+ |
+const char kGATTServerDisconnected[] = |
+ "GATT Server disconnected while performing a GATT operation."; |
+const char kGATTServerNotConnected[] = |
+ "GATT Server is disconnected. Cannot perform GATT operations."; |
+ |
+DOMDataView* ConvertWebVectorToDataView(const WebVector<uint8_t>& webVector) { |
+ static_assert(sizeof(*webVector.data()) == 1, |
+ "uint8_t should be a single byte"); |
+ DOMArrayBuffer* domBuffer = |
+ DOMArrayBuffer::create(webVector.data(), webVector.size()); |
+ return DOMDataView::create(domBuffer, 0, webVector.size()); |
+} |
+ |
+} // anonymous namespace |
+ |
+BluetoothRemoteGATTDescriptor::BluetoothRemoteGATTDescriptor( |
+ ExecutionContext* context, |
+ std::unique_ptr<WebBluetoothRemoteGATTDescriptorInit> webDescriptor, |
+ BluetoothRemoteGATTService* service) |
+ : ActiveDOMObject(context), |
+ m_webDescriptor(std::move(webDescriptor)), |
+ m_service(service), |
+ m_stopped(false) { |
+ // See example in Source/platform/heap/ThreadState.h |
+ ThreadState::current()->registerPreFinalizer(this); |
+} |
+ |
+BluetoothRemoteGATTDescriptor* BluetoothRemoteGATTDescriptor::take( |
+ ScriptPromiseResolver* resolver, |
+ std::unique_ptr<WebBluetoothRemoteGATTDescriptorInit> webDescriptor, |
+ BluetoothRemoteGATTService* service) { |
+ if (!webDescriptor) { |
+ return nullptr; |
+ } |
+ BluetoothRemoteGATTDescriptor* descriptor = new BluetoothRemoteGATTDescriptor( |
+ resolver->getExecutionContext(), std::move(webDescriptor), service); |
+ // See note in ActiveDOMObject about suspendIfNeeded. |
+ descriptor->suspendIfNeeded(); |
+ return descriptor; |
+} |
+ |
+void BluetoothRemoteGATTDescriptor::setValue(DOMDataView* domDataView) { |
+ m_value = domDataView; |
+} |
+ |
+void BluetoothRemoteGATTDescriptor::dispatchDescriptorValueChanged( |
+ const WebVector<uint8_t>& value) { |
+ this->setValue(ConvertWebVectorToDataView(value)); |
+ dispatchEvent(Event::create(EventTypeNames::descriptorvaluechanged)); |
+} |
+ |
+void BluetoothRemoteGATTDescriptor::contextDestroyed() { |
+ notifyDescriptorObjectRemoved(); |
+} |
+ |
+void BluetoothRemoteGATTDescriptor::dispose() { |
+ notifyDescriptorObjectRemoved(); |
+} |
+ |
+void BluetoothRemoteGATTDescriptor::notifyDescriptorObjectRemoved() { |
+ if (!m_stopped) { |
+ m_stopped = true; |
+ WebBluetooth* webbluetooth = BluetoothSupplement::fromExecutionContext( |
+ ActiveDOMObject::getExecutionContext()); |
+ webbluetooth->descriptorObjectRemoved(m_webDescriptor->descriptorInstanceID, |
+ this); |
+ } |
+} |
+ |
+const WTF::AtomicString& BluetoothRemoteGATTDescriptor::interfaceName() const { |
+ return EventTargetNames::BluetoothRemoteGATTDescriptor; |
+} |
+ |
+ExecutionContext* BluetoothRemoteGATTDescriptor::getExecutionContext() const { |
+ return ActiveDOMObject::getExecutionContext(); |
+} |
+ |
+void BluetoothRemoteGATTDescriptor::addedEventListener( |
+ const AtomicString& eventType, |
+ RegisteredEventListener& registeredListener) { |
+ EventTargetWithInlineData::addedEventListener(eventType, registeredListener); |
+ // We will also need to unregister a descriptor once all the event |
+ // listeners have been removed. See http://crbug.com/541390 |
+ if (eventType == EventTypeNames::descriptorvaluechanged) { |
+ WebBluetooth* webbluetooth = |
+ BluetoothSupplement::fromExecutionContext(getExecutionContext()); |
+ webbluetooth->registerDescriptorObject( |
+ m_webDescriptor->descriptorInstanceID, this); |
+ } |
+} |
+ |
+// TODO dft -- can we move out into a utility |
+class ReadValueCallback : public WebBluetoothReadValueCallbacks { |
+ public: |
+ ReadValueCallback(BluetoothRemoteGATTDescriptor* descriptor, |
+ ScriptPromiseResolver* resolver) |
+ : m_descriptor(descriptor), m_resolver(resolver) { |
+ // We always check that the device is connected before constructing this |
+ // object. |
+ CHECK(m_descriptor->gatt()->connected()); |
+ m_descriptor->gatt()->AddToActiveAlgorithms(m_resolver.get()); |
+ } |
+ |
+ void onSuccess(const WebVector<uint8_t>& value) override { |
+ if (!m_resolver->getExecutionContext() || |
+ m_resolver->getExecutionContext()->activeDOMObjectsAreStopped()) |
+ return; |
+ |
+ if (!m_descriptor->gatt()->RemoveFromActiveAlgorithms(m_resolver.get())) { |
+ m_resolver->reject( |
+ DOMException::create(NetworkError, kGATTServerDisconnected)); |
+ return; |
+ } |
+ |
+ DOMDataView* domDataView = ConvertWebVectorToDataView(value); |
+ if (m_descriptor) |
+ m_descriptor->setValue(domDataView); |
+ |
+ m_resolver->resolve(domDataView); |
+ } |
+ |
+ void onError( |
+ int32_t |
+ error /* Corresponds to WebBluetoothResult in web_bluetooth.mojom */) |
+ override { |
+ if (!m_resolver->getExecutionContext() || |
+ m_resolver->getExecutionContext()->activeDOMObjectsAreStopped()) |
+ return; |
+ |
+ if (!m_descriptor->gatt()->RemoveFromActiveAlgorithms(m_resolver.get())) { |
+ m_resolver->reject( |
+ DOMException::create(NetworkError, kGATTServerDisconnected)); |
+ return; |
+ } |
+ |
+ m_resolver->reject(BluetoothError::take(m_resolver, error)); |
+ } |
+ |
+ private: |
+ WeakPersistent<BluetoothRemoteGATTDescriptor> m_descriptor; |
+ Persistent<ScriptPromiseResolver> m_resolver; |
+}; |
+ |
+ScriptPromise BluetoothRemoteGATTDescriptor::readValue( |
+ ScriptState* scriptState) { |
+ if (!gatt()->connected()) { |
+ return ScriptPromise::rejectWithDOMException( |
+ scriptState, |
+ DOMException::create(NetworkError, kGATTServerNotConnected)); |
+ } |
+ |
+ WebBluetooth* webbluetooth = |
+ BluetoothSupplement::fromScriptState(scriptState); |
+ |
+ ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
+ ScriptPromise promise = resolver->promise(); |
+ |
+ webbluetooth->descriptorReadValue(m_webDescriptor->descriptorInstanceID, |
+ new ReadValueCallback(this, resolver)); |
+ return promise; |
+} |
+ |
+class WriteValueCallback : public WebBluetoothWriteValueCallbacks { |
+ public: |
+ WriteValueCallback(BluetoothRemoteGATTDescriptor* descriptor, |
+ ScriptPromiseResolver* resolver) |
+ : m_webDescriptor(descriptor), m_resolver(resolver) {} |
+ |
+ void onSuccess(const WebVector<uint8_t>& value) override { |
+ if (!m_resolver->getExecutionContext() || |
+ m_resolver->getExecutionContext()->activeDOMObjectsAreStopped()) |
+ return; |
+ |
+ if (m_webDescriptor) { |
+ m_webDescriptor->setValue(ConvertWebVectorToDataView(value)); |
+ } |
+ m_resolver->resolve(); |
+ } |
+ |
+ void onError( |
+ int32_t |
+ error /* Corresponds to WebBluetoothResult in web_bluetooth.mojom */) |
+ override { |
+ if (!m_resolver->getExecutionContext() || |
+ m_resolver->getExecutionContext()->activeDOMObjectsAreStopped()) |
+ return; |
+ m_resolver->reject(BluetoothError::take(m_resolver, error)); |
+ } |
+ |
+ private: |
+ WeakPersistent<BluetoothRemoteGATTDescriptor> m_webDescriptor; |
+ Persistent<ScriptPromiseResolver> m_resolver; |
+}; |
+ |
+ScriptPromise BluetoothRemoteGATTDescriptor::writeValue( |
+ ScriptState* scriptState, |
+ const DOMArrayPiece& value) { |
+ WebBluetooth* webbluetooth = |
+ BluetoothSupplement::fromScriptState(scriptState); |
+ |
+ // Partial implementation of writeValue algorithm: |
+ // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothgattdescriptor-writevalue |
+ |
+ // If bytes is more than 512 bytes long (the maximum length of an attribute |
+ // value, per Long Attribute Values) return a promise rejected with an |
+ // InvalidModificationError and abort. |
+ if (value.byteLength() > 512) { |
+ return ScriptPromise::rejectWithDOMException( |
+ scriptState, DOMException::create(InvalidModificationError, |
+ "Value can't exceed 512 bytes.")); |
+ } |
+ |
+ // Let valueVector be a copy of the bytes held by value. |
+ WebVector<uint8_t> valueVector(value.bytes(), value.byteLength()); |
+ |
+ ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
+ |
+ ScriptPromise promise = resolver->promise(); |
+ webbluetooth->descriptorWriteValue(m_webDescriptor->descriptorInstanceID, |
+ valueVector, |
+ new WriteValueCallback(this, resolver)); |
+ |
+ return promise; |
+} |
+ |
+DEFINE_TRACE(BluetoothRemoteGATTDescriptor) { |
+ visitor->trace(m_service); |
+ visitor->trace(m_characteristic); |
+ visitor->trace(m_value); |
+ EventTargetWithInlineData::trace(visitor); |
+ ActiveDOMObject::trace(visitor); |
+} |
+ |
+} // namespace blink |