blob: 45be0dab297d9c187cc822de75bfd64318860b96 [file] [log] [blame]
// 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/BluetoothRemoteGATTCharacteristic.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/BluetoothCharacteristicProperties.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 {
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
BluetoothRemoteGATTCharacteristic::BluetoothRemoteGATTCharacteristic(ExecutionContext* context, std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit> webCharacteristic, BluetoothRemoteGATTService* service)
: ActiveDOMObject(context)
, m_webCharacteristic(std::move(webCharacteristic))
, m_service(service)
, m_stopped(false)
{
m_properties = BluetoothCharacteristicProperties::create(m_webCharacteristic->characteristicProperties);
// See example in Source/platform/heap/ThreadState.h
ThreadState::current()->registerPreFinalizer(this);
}
BluetoothRemoteGATTCharacteristic* BluetoothRemoteGATTCharacteristic::take(ScriptPromiseResolver* resolver, std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit> webCharacteristic, BluetoothRemoteGATTService* service)
{
if (!webCharacteristic) {
return nullptr;
}
BluetoothRemoteGATTCharacteristic* characteristic = new BluetoothRemoteGATTCharacteristic(resolver->getExecutionContext(), std::move(webCharacteristic), service);
// See note in ActiveDOMObject about suspendIfNeeded.
characteristic->suspendIfNeeded();
return characteristic;
}
void BluetoothRemoteGATTCharacteristic::setValue(DOMDataView* domDataView)
{
m_value = domDataView;
}
void BluetoothRemoteGATTCharacteristic::dispatchCharacteristicValueChanged(
const WebVector<uint8_t>& value)
{
this->setValue(ConvertWebVectorToDataView(value));
dispatchEvent(Event::create(EventTypeNames::characteristicvaluechanged));
}
void BluetoothRemoteGATTCharacteristic::stop()
{
notifyCharacteristicObjectRemoved();
}
void BluetoothRemoteGATTCharacteristic::dispose()
{
notifyCharacteristicObjectRemoved();
}
void BluetoothRemoteGATTCharacteristic::notifyCharacteristicObjectRemoved()
{
if (!m_stopped) {
m_stopped = true;
WebBluetooth* webbluetooth = BluetoothSupplement::fromExecutionContext(ActiveDOMObject::getExecutionContext());
webbluetooth->characteristicObjectRemoved(m_webCharacteristic->characteristicInstanceID, this);
}
}
const WTF::AtomicString& BluetoothRemoteGATTCharacteristic::interfaceName() const
{
return EventTargetNames::BluetoothRemoteGATTCharacteristic;
}
ExecutionContext* BluetoothRemoteGATTCharacteristic::getExecutionContext() const
{
return ActiveDOMObject::getExecutionContext();
}
void BluetoothRemoteGATTCharacteristic::addedEventListener(const AtomicString& eventType, RegisteredEventListener& registeredListener)
{
EventTargetWithInlineData::addedEventListener(eventType, registeredListener);
// We will also need to unregister a characteristic once all the event
// listeners have been removed. See http://crbug.com/541390
if (eventType == EventTypeNames::characteristicvaluechanged) {
WebBluetooth* webbluetooth = BluetoothSupplement::fromExecutionContext(getExecutionContext());
webbluetooth->registerCharacteristicObject(m_webCharacteristic->characteristicInstanceID, this);
}
}
class ReadValueCallback : public WebBluetoothReadValueCallbacks {
public:
ReadValueCallback(BluetoothRemoteGATTCharacteristic* characteristic, ScriptPromiseResolver* resolver) : m_webCharacteristic(characteristic), m_resolver(resolver) {}
void onSuccess(const WebVector<uint8_t>& value) override
{
if (!m_resolver->getExecutionContext() || m_resolver->getExecutionContext()->activeDOMObjectsAreStopped())
return;
DOMDataView* domDataView = ConvertWebVectorToDataView(value);
if (m_webCharacteristic)
m_webCharacteristic->setValue(domDataView);
m_resolver->resolve(domDataView);
}
void onError(int32_t error /* Corresponds to WebBluetoothError in web_bluetooth.mojom */) override
{
if (!m_resolver->getExecutionContext() || m_resolver->getExecutionContext()->activeDOMObjectsAreStopped())
return;
m_resolver->reject(BluetoothError::take(m_resolver, error));
}
private:
WeakPersistent<BluetoothRemoteGATTCharacteristic> m_webCharacteristic;
Persistent<ScriptPromiseResolver> m_resolver;
};
ScriptPromise BluetoothRemoteGATTCharacteristic::readValue(ScriptState* scriptState)
{
WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState);
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise();
webbluetooth->readValue(m_webCharacteristic->characteristicInstanceID, new ReadValueCallback(this, resolver));
return promise;
}
class WriteValueCallback : public WebBluetoothWriteValueCallbacks {
public:
WriteValueCallback(BluetoothRemoteGATTCharacteristic* characteristic, ScriptPromiseResolver* resolver) : m_webCharacteristic(characteristic), m_resolver(resolver) {}
void onSuccess(const WebVector<uint8_t>& value) override
{
if (!m_resolver->getExecutionContext() || m_resolver->getExecutionContext()->activeDOMObjectsAreStopped())
return;
if (m_webCharacteristic) {
m_webCharacteristic->setValue(ConvertWebVectorToDataView(value));
}
m_resolver->resolve();
}
void onError(int32_t error /* Corresponds to WebBluetoothError in web_bluetooth.mojom */) override
{
if (!m_resolver->getExecutionContext() || m_resolver->getExecutionContext()->activeDOMObjectsAreStopped())
return;
m_resolver->reject(BluetoothError::take(m_resolver, error));
}
private:
WeakPersistent<BluetoothRemoteGATTCharacteristic> m_webCharacteristic;
Persistent<ScriptPromiseResolver> m_resolver;
};
ScriptPromise BluetoothRemoteGATTCharacteristic::writeValue(ScriptState* scriptState, const DOMArrayPiece& value)
{
WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState);
// Partial implementation of writeValue algorithm:
// https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothgattcharacteristic-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->writeValue(m_webCharacteristic->characteristicInstanceID, valueVector, new WriteValueCallback(this, resolver));
return promise;
}
class NotificationsCallback : public WebBluetoothNotificationsCallbacks {
public:
NotificationsCallback(BluetoothRemoteGATTCharacteristic* characteristic, ScriptPromiseResolver* resolver) : m_webCharacteristic(characteristic), m_resolver(resolver) {}
void onSuccess() override
{
if (!m_resolver->getExecutionContext() || m_resolver->getExecutionContext()->activeDOMObjectsAreStopped())
return;
m_resolver->resolve(m_webCharacteristic);
}
void onError(int32_t error /* Corresponds to WebBluetoothError in web_bluetooth.mojom */) override
{
if (!m_resolver->getExecutionContext() || m_resolver->getExecutionContext()->activeDOMObjectsAreStopped())
return;
m_resolver->reject(BluetoothError::take(m_resolver, error));
}
private:
Persistent<BluetoothRemoteGATTCharacteristic> m_webCharacteristic;
Persistent<ScriptPromiseResolver> m_resolver;
};
ScriptPromise BluetoothRemoteGATTCharacteristic::startNotifications(ScriptState* scriptState)
{
WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState);
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise();
webbluetooth->startNotifications(m_webCharacteristic->characteristicInstanceID, new NotificationsCallback(this, resolver));
return promise;
}
ScriptPromise BluetoothRemoteGATTCharacteristic::stopNotifications(ScriptState* scriptState)
{
#if OS(MACOSX)
// TODO(jlebel): Remove when stopNotifications is implemented.
return ScriptPromise::rejectWithDOMException(scriptState,
DOMException::create(NotSupportedError,
"stopNotifications is not implemented yet. See https://goo.gl/J6ASzs"));
#endif // OS(MACOSX)
WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState);
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise();
webbluetooth->stopNotifications(m_webCharacteristic->characteristicInstanceID, new NotificationsCallback(this, resolver));
return promise;
}
DEFINE_TRACE(BluetoothRemoteGATTCharacteristic)
{
visitor->trace(m_service);
visitor->trace(m_properties);
visitor->trace(m_value);
EventTargetWithInlineData::trace(visitor);
ActiveDOMObject::trace(visitor);
}
} // namespace blink