diff --git a/index.bs b/index.bs index be05d99..8e15a42 100644 --- a/index.bs +++ b/index.bs @@ -3519,6 +3519,46 @@ Instances of {{BluetoothRemoteGATTCharacteristic}} are created with the Characteristic has been removed or otherwise invalidated. + + \[[automatedCharacteristicReadResponse]] + "not-expected" + + The simulated GATT characteristic response code for a GATT characteristic + read attempt. + + + + \[[automatedCharacteristicReadResponseData]] + Empty byte sequence + + The simulated GATT characteristic response data for a GATT characteristic + read attempt. + + + + \[[automatedCharacteristicWriteResponse]] + "not-expected" + + The simulated GATT characteristic response code for a GATT characteristic + write attempt. + + + + \[[automatedCharacteristicSubscribeToNotificationsResponse]] + "not-expected" + + The simulated GATT characteristic response code for an attempt to subscribe + to GATT characteristic notifications. + + + + \[[automatedCharacteristicUnsubscribeFromNotificationsResponse]] + "not-expected" + + The simulated GATT characteristic response code for an attempt to unsubscribe + from GATT characteristic notifications. + +
@@ -3603,17 +3643,39 @@ The readValue() task=] on the [=Bluetooth task source=] given |global| to [=reject=] |promise| with a "{{NotSupportedError}}" {{DOMException}} and abort these steps. - 1. If the UA is currently using the Bluetooth system, it MAY [=queue a - global task=] on the [=Bluetooth task source=] given |global| to - [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and - abort these steps. - - Issue(188): Implementations may be able to avoid this {{NetworkError}}, - but for now sites need to serialize their use of this API and/or give - the user a way to retry failed operations. - 1. Use any combination of the sub-procedures in the [=Characteristic Value - Read=] procedure to retrieve the value of |characteristic|. - Handle errors as described in . + 1. If |global|'s [=Window/navigable=]'s + [=navigable/top-level traversable=]'s simulated Bluetooth adapter + is not empty, run the following steps: + 1. If [=this=].{{[[automatedCharacteristicReadResponse]]}} is not `"not-expected"`, + [=queue a global task=] on the [=Bluetooth task source=] given |global| to + [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort + these steps. + 1. [=Trigger a simulated characteristic event=] given |global|'s + [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}}, + |characteristic|, and `read`. + 1. Set [=this=].{{[[automatedCharacteristicReadResponse]]}} to `"expected"`, + and wait for it to change. + 1. Let |response| be [=this=].{{[[automatedCharacteristicReadResponse]]}}. + 1. Set [=this=].{{[[automatedCharacteristicReadResponse]]}} to `"not-expected"`. + 1. If |response| is not `0`, do the following sub-steps: + 1. [=Queue a global task=] on the [=Bluetooth task source=] given + |global| to [=reject=] |promise| with a "{{NetworkError}}" + {{DOMException}} and abort these steps. + 1. Otherwise, let |buffer| be a [=new=] {{ArrayBuffer}} containing + [=this=].{{[[automatedCharacteristicReadResponseData]]}}. + 1. Otherwise, run the following steps: + 1. If the UA is currently using the Bluetooth system, it MAY [=queue a + global task=] on the [=Bluetooth task source=] given |global| to + [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and + abort these steps. + + Issue(188): Implementations may be able to avoid this {{NetworkError}}, + but for now sites need to serialize their use of this API and/or give + the user a way to retry failed operations. + 1. Use any combination of the sub-procedures in the [=Characteristic Value + Read=] procedure to retrieve the value of |characteristic| and let |buffer| + be a [=new=] {{ArrayBuffer}} holding the retrieved value. + Handle errors as described in . 1. [=Queue a global task=] on the [=Bluetooth task source=] given |global| to perform the following steps: 1. If |promise| is not in |gatt|.{{[[activeAlgorithms]]}}, [=reject=] @@ -3621,8 +3683,7 @@ The readValue() these steps. 1. If the sub-procedures above returned an error, [=reject=] |promise| with that error and abort these steps. - 1. Let |buffer| be a [=new=] {{ArrayBuffer}} holding the retrieved - value, and assign a [=new=] {{DataView}} created with |buffer| to + 1. Assign a [=new=] {{DataView}} created with |buffer| to [=this=].{{BluetoothRemoteGATTCharacteristic/value}}. 1. [=Fire an event=] named "{{characteristicvaluechanged}}" with its {{Event/bubbles}} attribute initialized to `true` at [=this=]. @@ -3657,33 +3718,52 @@ the UA MUST perform the following steps: 1. Return a |gatt|-[=connection-checking wrapper=] around [=a new promise=] |promise| and run the following steps [=in parallel=]. 1. Assert: |response| is one of "required", "never", or "optional". - 1. If the UA is currently using the Bluetooth system, it MAY [=queue a - global task=] on the [=Bluetooth task source=] given |global| to - [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and - abort these steps. - - Issue(188): Implementations may be able to avoid this {{NetworkError}}, - but for now sites need to serialize their use of this API - and/or give the user a way to retry failed operations. - 1. Write |bytes| to |characteristic| by performing the - following steps: - -
-
If |response| is "required"
-
- Use the [=Write Characteristic Value=] procedure. -
-
If |response| is "never"
-
- Use the [=Write Without Response=] procedure. -
-
Otherwise
-
- Use any combination of the sub-procedures in the [=Characteristic - Value Write=] procedure. -
-
- Handle errors as described in . + 1. If |global|'s [=Window/navigable=]'s + [=navigable/top-level traversable=]'s simulated Bluetooth adapter + is not empty, run the following steps: + 1. If [=this=].{{[[automatedCharacteristicWriteResponse]]}} is not `"not-expected"`, + [=queue a global task=] on the [=Bluetooth task source=] given |global| to + [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort + these steps. + 1. [=Trigger a simulated characteristic event=] given |global|'s + [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}}, + |characteristic|, `write`, and |bytes|. + 1. Set [=this=].{{[[automatedCharacteristicWriteResponse]]}} to `"expected"`, + and wait for it to change. + 1. Let |response| be [=this=].{{[[automatedCharacteristicWriteResponse]]}}. + 1. Set [=this=].{{[[automatedCharacteristicWriteResponse]]}} to `"not-expected"`. + 1. If |response| is not `0`, do the following sub-steps: + 1. [=Queue a global task=] on the [=Bluetooth task source=] given + |global| to [=reject=] |promise| with a "{{NetworkError}}" + {{DOMException}} and abort these steps. + 1. Otherwise, run the following steps: + 1. If the UA is currently using the Bluetooth system, it MAY [=queue a + global task=] on the [=Bluetooth task source=] given |global| to + [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and + abort these steps. + + Issue(188): Implementations may be able to avoid this {{NetworkError}}, + but for now sites need to serialize their use of this API + and/or give the user a way to retry failed operations. + 1. Write |bytes| to |characteristic| by performing the + following steps: + +
+
If |response| is "required"
+
+ Use the [=Write Characteristic Value=] procedure. +
+
If |response| is "never"
+
+ Use the [=Write Without Response=] procedure. +
+
Otherwise
+
+ Use any combination of the sub-procedures in the [=Characteristic + Value Write=] procedure. +
+
+ Handle errors as described in . 1. Queue a global task on |global| using the [=Bluetooth task source=] to perform the following steps: 1. If |promise| is not in |gatt|.{{[[activeAlgorithms]]}}, [=reject=] @@ -3778,34 +3858,53 @@ notifications. {{Navigator/bluetooth|navigator.bluetooth}}, [=queue a global task=] on the [=Bluetooth task source=] given |global| to [=resolve=] |promise| with [=this=] and abort these steps. - 1. If the UA is currently using the Bluetooth system, it MAY [=queue a - global task=] on the [=Bluetooth task source=] given |global| to - [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and - abort these steps. - - Issue(188): Implementations may be able to avoid this {{NetworkError}}, but - for now sites need to serialize their use of this API and/or give the user a - way to retry failed operations. - 1. If the characteristic has a [=Client Characteristic Configuration=] - descriptor, use any of the [=Characteristic Descriptors=] procedures to - ensure that one of the Notification or Indication - bits in |characteristic|'s [=Client Characteristic Configuration=] - descriptor is set, matching the constraints in |characteristic|'s - properties. The UA SHOULD avoid - setting both bits, and MUST deduplicate - value-change events if both bits are - set. Handle errors as described in . + 1. If |global|'s [=Window/navigable=]'s + [=navigable/top-level traversable=]'s simulated Bluetooth adapter + is not empty, run the following steps: + 1. If [=this=].{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} is not `"not-expected"`, + [=queue a global task=] on the [=Bluetooth task source=] given |global| to + [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort + these steps. + 1. [=Trigger a simulated characteristic event=] given |global|'s + [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}}, + |characteristic|, and `subscribe-to-notifications`. + 1. Set [=this=].{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} to `"expected"`, + and wait for it to change. + 1. Let |response| be [=this=].{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}}. + 1. Set [=this=].{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} to `"not-expected"`. + 1. If |response| is not `0`, do the following sub-steps: + 1. [=Queue a global task=] on the [=Bluetooth task source=] given + |global| to [=reject=] |promise| with a "{{NetworkError}}" + {{DOMException}} and abort these steps. + 1. Otherwise, let |success| to be `true`. + 1. Otherwise, run the following steps: + 1. If the UA is currently using the Bluetooth system, it MAY [=queue a + global task=] on the [=Bluetooth task source=] given |global| to + [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}} and + abort these steps. + + Issue(188): Implementations may be able to avoid this {{NetworkError}}, but + for now sites need to serialize their use of this API and/or give the user a + way to retry failed operations. + 1. If the characteristic has a [=Client Characteristic Configuration=] + descriptor, use any of the [=Characteristic Descriptors=] procedures to + ensure that one of the Notification or Indication + bits in |characteristic|'s [=Client Characteristic Configuration=] + descriptor is set, matching the constraints in |characteristic|'s + properties. The UA SHOULD avoid + setting both bits, and MUST deduplicate + value-change events if both bits are + set. Handle errors as described in . -
- Note: Some devices have characteristics whose properties include the - Notify or Indicate bit but that don't have a Client Characteristic - Configuration descriptor. These non-standard-compliant characteristics - tend to send notifications or indications unconditionally, so this - specification allows applications to simply subscribe to their messages. -
- 1. If the procedures were successful, - {{Navigator/bluetooth|navigator.bluetooth}} to - |characteristic|'s [=active notification context set=]. +
+ Note: Some devices have characteristics whose properties include the + Notify or Indicate bit but that don't have a Client Characteristic + Configuration descriptor. These non-standard-compliant characteristics + tend to send notifications or indications unconditionally, so this + specification allows applications to simply subscribe to their messages. +
+ 1. If the procedures were successful, let |success| to be `true`. + 1. If |success| is `true`, add {{Navigator/bluetooth|navigator.bluetooth}} to|characteristic|'s [=active notification context set=]. 1. [=Queue a global task=] on the [=Bluetooth task source=] given |global| to perform the following steps: 1. If |promise| is not in |gatt|.{{[[activeAlgorithms]]}}, [=reject=] @@ -3834,12 +3933,31 @@ promise promise and run the following steps in parallel: {{InvalidStateError}} and abort these steps. 1. If characteristic's active notification context set contains {{Navigator/bluetooth|navigator.bluetooth}}, remove it. -1. If characteristic's active notification context set became - empty and the characteristic has a Client Characteristic - Configuration descriptor, the UA SHOULD use any of the Characteristic - Descriptors procedures to clear the Notification and - Indication bits in characteristic's Client - Characteristic Configuration descriptor. + 1. If |global|'s [=Window/navigable=]'s + [=navigable/top-level traversable=]'s simulated Bluetooth adapter + is not empty, run the following steps: + 1. If [=this=].{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} is not `"not-expected"`, + [=queue a global task=] on the [=Bluetooth task source=] given |global| to + [=reject=] |promise| with a "{{InvalidStateError}}" {{DOMException}} and abort + these steps. + 1. [=Trigger a simulated characteristic event=] given |global|'s + [=Window/navigable=], [=this=].{{BluetoothRemoteGATTServer/device}}, + |characteristic|, and `unsubscribe-from-notifications`. + 1. Set [=this=].{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} to `"expected"`, + and wait for it to change. + 1. Let |response| be [=this=].{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}}. + 1. Set [=this=].{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} to `"not-expected"`. + 1. If |response| is not `0`, do the following sub-steps: + 1. [=Queue a global task=] on the [=Bluetooth task source=] given + |global| to [=reject=] |promise| with a "{{NetworkError}}" + {{DOMException}} and abort these steps.. + 1. Otherwise, run the following steps: + 1. If characteristic's active notification context set became + empty and the characteristic has a Client Characteristic + Configuration descriptor, the UA SHOULD use any of the Characteristic + Descriptors procedures to clear the Notification and + Indication bits in characteristic's Client + Characteristic Configuration descriptor. 1. [=Queue a global task=] on the [=Bluetooth task source=] given [=this=]'s [=relevant global object=] to [=resolve=] |promise| with [=this=]. @@ -5821,8 +5939,49 @@ bluetooth.SimulateCharacteristicResponseParameters = {
+The [=remote end steps=] with command parameters |params| are: -Issue: TODO: Finish the algorithm of bluetooth.simulateCharacteristicResponse. +1. Let |contextId| be |params|[`"context"`]. +1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|. +1. Let |deviceAddress| be |params|[`"address"`]. +1. Let |simulatedBluetoothAdapter| be |navigable|'s simulated Bluetooth adapter. +1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=]. +1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s simulated Bluetooth device mapping. +1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|]. + Otherwise, return [=error=] with [=error code=] [=invalid argument=]. +1. Let |serviceMapping| be |simulatedDevice|'s simulated GATT service mapping. +1. Let |serviceUuid| be |params|[`"serviceUuid"`]. +1. If |serviceMapping|[|serviceUuid|] [=map/exists=], let |simulatedService| be |serviceMapping|[|serviceUuid|]. +1. Otherwise, return [=error=] with [=error code=] [=invalid argument=]. +1. Let |characteristicMapping| be |simulatedService|'s simulated GATT characteristic mapping. +1. Let |characteristicUuid| be |params|[`"characteristicUuid"`]. +1. If |characteristicMapping|[|characteristicUuid|] [=map/exists=], let |simulatedGattCharacteristic| + be |characteristicMapping|[|characteristicUuid|]. +1. Otherwise, return [=error=] with [=error code=] [=invalid element state=]. +1. Let |simulatedDeviceInstance| be the result of get the BluetoothDevice representing + |simulatedDevice| inside |navigable|'s active window's associated Navigator's + [=associated Bluetooth=]. +1. Let |promise| be |simulatedDeviceInstance|.{{[[context]]}}.{{Bluetooth/[[attributeInstanceMap]]}}[|simulatedGattCharacteristic|]. +1. Upon fulfillment of |promise| with |characteristic|, run the following steps: + 1. If |params|[`"type"`] is `read`, run the following steps: + 1. If |characteristic|.{{[[automatedCharacteristicReadResponse]]}} is `expected`, + set |characteristic|.{{[[automatedCharacteristicReadResponse]]}} to |params|[`"code"`] and + |characteristic|.{{[[automatedCharacteristicReadResponseData]]}} to [=a copy of the bytes held=] + by |params|[`"data"`]. + 1. Otherwise, return [=error=] with [=error code=] [=invalid element state=]. + 1. If |params|[`"type"`] is `write`, run the following steps: + 1. If |characteristic|.{{[[automatedCharacteristicWriteResponse]]}} is `expected`, + set |characteristic|.{{[[automatedCharacteristicWriteResponse]]}} to |params|[`"code"`]. + 1. Otherwise, return [=error=] with [=error code=] [=invalid element state=]. + 1. If |params|[`"type"`] is `subscribe-to-notifications`, run the following steps: + 1. If |characteristic|.{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} is `expected`, + set |characteristic|.{{[[automatedCharacteristicSubscribeToNotificationsResponse]]}} to |params|[`"code"`]. + 1. Otherwise, return [=error=] with [=error code=] [=invalid element state=]. + 1. If |params|[`"type"`] is `unsubscribe-from-notifications`, run the following steps: + 1. If |characteristic|.{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} is `expected`, + set |characteristic|.{{[[automatedCharacteristicUnsubscribeFromNotificationsResponse]]}} to |params|[`"code"`]. + 1. Otherwise, return [=error=] with [=error code=] [=invalid element state=]. + 1. Otherwise, return [=error=] with [=error code=] [=invalid argument=].
@@ -6041,7 +6200,25 @@ bluetooth.CharacteristicEventGeneratedParameters = {
-Issue: TODO: Finish the algorithm of bluetooth.characteristicEventGenerated. +To trigger a simulated characteristic event given a [=navigable=] |navigable|, a {{BluetoothDevice}} |device|, a +simulated GATT characteristic |characteristic|, string |type|, and an optional byte sequence |bytes|: + +1. Let |navigableId| be |navigable|'s [=navigable id=]. +1. Let |params| be a [=map=] matching the bluetooth.CharacteristicEventGeneratedParameters production and run + the following steps: + 1. Set |params|[`"context"`] to |navigableId|. + 1. Set |params|[`"address"`] to |device|.{{[[representedDevice]]}}'s address. + 1. Let |service| be the simulated GATT service containing |characteristic|. + 1. Set |params|[`"serviceUuid"`] to |service|'s UUID. + 1. Set |params|[`"characteristicUuid"`] to |characteristic|'s UUID. + 1. Set |params|[`"type"`] to |type|. + 1. If |type| is `write`, set |params|[`"data"`] to a [=new=] {{Uint8Array}} wrapping a [=new=] {{ArrayBuffer}} containing |bytes|. +1. Let |body| be a [=map=] matching the bluetooth.CharacteristicEventGenerated production, with the + params field set to |params|. +1. Let |relatedNavigables| be a [=/set=] containing |navigable|. +1. For each |session| in the [=set of sessions for which an event is enabled=] given + "bluetooth.characteristicEventGenerated" and |relatedNavigables|: + 1. [=Emit an event=] with |session| and |body|.