text: // 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/BluetoothRemoteGATTService.h"
  
  #include "bindings/core/v8/ScriptPromise.h"
  #include "bindings/core/v8/ScriptPromiseResolver.h"
  #include "core/dom/DOMException.h"
  #include "core/dom/ExceptionCode.h"
  #include "core/inspector/ConsoleMessage.h"
  #include "modules/bluetooth/Bluetooth.h"
  #include "modules/bluetooth/BluetoothError.h"
  #include "modules/bluetooth/BluetoothRemoteGATTCharacteristic.h"
  #include "modules/bluetooth/BluetoothUUID.h"
  #include "wtf/PtrUtil.h"
  #include <utility>
  
  namespace blink {
  
  namespace {
  
  const char kGATTServerDisconnected[] =
      "GATT Server disconnected while retrieving characteristics.";
  const char kGATTServerNotConnected[] =
      "GATT Server is disconnected. Cannot retrieve characteristics.";
  const char kInvalidService[] =
      "Service is no longer valid. Remember to retrieve the service again after "
      "reconnecting.";
  
  }  // namespace
  
  BluetoothRemoteGATTService::BluetoothRemoteGATTService(
      mojom::blink::WebBluetoothRemoteGATTServicePtr service,
      bool isPrimary,
      const String& deviceInstanceId,
      BluetoothDevice* device)
      : m_service(std::move(service)),
        m_isPrimary(isPrimary),
        m_deviceInstanceId(deviceInstanceId),
        m_device(device) {}
  
  DEFINE_TRACE(BluetoothRemoteGATTService) {
    visitor->trace(m_device);
  }
  
  // Callback that allows us to resolve the promise with a single characteristic
  // or with a vector owning the characteristics.
  void BluetoothRemoteGATTService::GetCharacteristicsCallback(
      const String& serviceInstanceId,
      mojom::blink::WebBluetoothGATTQueryQuantity quantity,
      ScriptPromiseResolver* resolver,
      mojom::blink::WebBluetoothResult result,
      Optional<Vector<mojom::blink::WebBluetoothRemoteGATTCharacteristicPtr>>
          characteristics) {
    if (!resolver->getExecutionContext() ||
        resolver->getExecutionContext()->isContextDestroyed())
      return;
  
    // If the resolver is not in the set of ActiveAlgorithms then the frame
    // disconnected so we reject.
    if (!device()->gatt()->RemoveFromActiveAlgorithms(resolver)) {
      resolver->reject(
          DOMException::create(NetworkError, kGATTServerDisconnected));
      return;
    }
  
    if (result == mojom::blink::WebBluetoothResult::SUCCESS) {
      DCHECK(characteristics);
  
      if (quantity == mojom::blink::WebBluetoothGATTQueryQuantity::SINGLE) {
        DCHECK_EQ(1u, characteristics->size());
        resolver->resolve(device()->getOrCreateRemoteGATTCharacteristic(
            resolver->getExecutionContext(),
            std::move(characteristics.value()[0]), this));
        return;
      }
  
      HeapVector<Member<BluetoothRemoteGATTCharacteristic>> gattCharacteristics;
      gattCharacteristics.reserveInitialCapacity(characteristics->size());
      for (auto& characteristic : characteristics.value()) {
        gattCharacteristics.push_back(
            device()->getOrCreateRemoteGATTCharacteristic(
                resolver->getExecutionContext(), std::move(characteristic),
                this));
      }
      resolver->resolve(gattCharacteristics);
    } else {
      resolver->reject(BluetoothError::take(resolver, result));
    }
  }
  
  ScriptPromise BluetoothRemoteGATTService::getCharacteristic(
      ScriptState* scriptState,
      const StringOrUnsignedLong& characteristic,
      ExceptionState& exceptionState) {
    String characteristicUUID =
        BluetoothUUID::getCharacteristic(characteristic, exceptionState);
    if (exceptionState.hadException())
      return exceptionState.reject(scriptState);
  
    return getCharacteristicsImpl(
        scriptState, mojom::blink::WebBluetoothGATTQueryQuantity::SINGLE,
        characteristicUUID);
  }
  
  ScriptPromise BluetoothRemoteGATTService::getCharacteristics(
      ScriptState* scriptState,
      const StringOrUnsignedLong& characteristic,
      ExceptionState& exceptionState) {
    String characteristicUUID =
        BluetoothUUID::getCharacteristic(characteristic, exceptionState);
    if (exceptionState.hadException())
      return exceptionState.reject(scriptState);
  
    return getCharacteristicsImpl(
        scriptState, mojom::blink::WebBluetoothGATTQueryQuantity::MULTIPLE,
        characteristicUUID);
  }
  
  ScriptPromise BluetoothRemoteGATTService::getCharacteristics(
      ScriptState* scriptState,
      ExceptionState&) {
    return getCharacteristicsImpl(
        scriptState, mojom::blink::WebBluetoothGATTQueryQuantity::MULTIPLE);
  }
  
  ScriptPromise BluetoothRemoteGATTService::getCharacteristicsImpl(
      ScriptState* scriptState,
      mojom::blink::WebBluetoothGATTQueryQuantity quantity,
      const String& characteristicsUUID) {
    // We always check that the device is connected.
    if (!device()->gatt()->connected()) {
      return ScriptPromise::rejectWithDOMException(
          scriptState,
          DOMException::create(NetworkError, kGATTServerNotConnected));
    }
  
    if (!device()->isValidService(m_service->instance_id)) {
      return ScriptPromise::rejectWithDOMException(
          scriptState, DOMException::create(InvalidStateError, kInvalidService));
    }
  
    ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
    ScriptPromise promise = resolver->promise();
    device()->gatt()->AddToActiveAlgorithms(resolver);
  
    mojom::blink::WebBluetoothService* service = m_device->bluetooth()->service();
  
    WTF::Optional<String> uuid = WTF::nullopt;
    if (!characteristicsUUID.isEmpty())
      uuid = characteristicsUUID;
    service->RemoteServiceGetCharacteristics(
        m_service->instance_id, quantity, uuid,
        convertToBaseCallback(
            WTF::bind(&BluetoothRemoteGATTService::GetCharacteristicsCallback,
                      wrapPersistent(this), m_service->instance_id, quantity,
                      wrapPersistent(resolver))));
  
    return promise;
  }
  
  }  // namespace blink
  