script/dom/bluetooth/
bluetoothremotegattdescriptor.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::rc::Rc;
6
7use bluetooth_traits::blocklist::{Blocklist, uuid_is_blocklisted};
8use bluetooth_traits::{BluetoothRequest, BluetoothResponse};
9use dom_struct::dom_struct;
10use ipc_channel::ipc::IpcSender;
11
12use crate::dom::bindings::cell::DomRefCell;
13use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::BluetoothRemoteGATTCharacteristicMethods;
14use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods;
15use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
16use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
17use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
18use crate::dom::bindings::error::Error::{self, InvalidModification, Network, Security};
19use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
20use crate::dom::bindings::root::{Dom, DomRoot};
21use crate::dom::bindings::str::{ByteString, DOMString};
22use crate::dom::bluetooth::{AsyncBluetoothListener, response_async};
23use crate::dom::bluetoothremotegattcharacteristic::{
24    BluetoothRemoteGATTCharacteristic, MAXIMUM_ATTRIBUTE_LENGTH,
25};
26use crate::dom::globalscope::GlobalScope;
27use crate::dom::promise::Promise;
28use crate::realms::InRealm;
29use crate::script_runtime::CanGc;
30
31// http://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattdescriptor
32#[dom_struct]
33pub(crate) struct BluetoothRemoteGATTDescriptor {
34    reflector_: Reflector,
35    characteristic: Dom<BluetoothRemoteGATTCharacteristic>,
36    uuid: DOMString,
37    value: DomRefCell<Option<ByteString>>,
38    instance_id: String,
39}
40
41impl BluetoothRemoteGATTDescriptor {
42    pub(crate) fn new_inherited(
43        characteristic: &BluetoothRemoteGATTCharacteristic,
44        uuid: DOMString,
45        instance_id: String,
46    ) -> BluetoothRemoteGATTDescriptor {
47        BluetoothRemoteGATTDescriptor {
48            reflector_: Reflector::new(),
49            characteristic: Dom::from_ref(characteristic),
50            uuid,
51            value: DomRefCell::new(None),
52            instance_id,
53        }
54    }
55
56    pub(crate) fn new(
57        global: &GlobalScope,
58        characteristic: &BluetoothRemoteGATTCharacteristic,
59        uuid: DOMString,
60        instance_id: String,
61        can_gc: CanGc,
62    ) -> DomRoot<BluetoothRemoteGATTDescriptor> {
63        reflect_dom_object(
64            Box::new(BluetoothRemoteGATTDescriptor::new_inherited(
65                characteristic,
66                uuid,
67                instance_id,
68            )),
69            global,
70            can_gc,
71        )
72    }
73
74    fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
75        self.global().as_window().bluetooth_thread()
76    }
77
78    fn get_instance_id(&self) -> String {
79        self.instance_id.clone()
80    }
81}
82
83impl BluetoothRemoteGATTDescriptorMethods<crate::DomTypeHolder> for BluetoothRemoteGATTDescriptor {
84    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-characteristic
85    fn Characteristic(&self) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
86        DomRoot::from_ref(&self.characteristic)
87    }
88
89    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-uuid
90    fn Uuid(&self) -> DOMString {
91        self.uuid.clone()
92    }
93
94    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-value
95    fn GetValue(&self) -> Option<ByteString> {
96        self.value.borrow().clone()
97    }
98
99    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
100    fn ReadValue(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
101        let p = Promise::new_in_current_realm(comp, can_gc);
102
103        // Step 1.
104        if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Reads) {
105            p.reject_error(Security, can_gc);
106            return p;
107        }
108
109        // Step 2.
110        if !self
111            .Characteristic()
112            .Service()
113            .Device()
114            .get_gatt()
115            .Connected()
116        {
117            p.reject_error(Network, can_gc);
118            return p;
119        }
120
121        // TODO: Step 5: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer.
122        // Note: Steps 3 - 4 and substeps of Step 5 are implemented in components/bluetooth/lib.rs
123        // in readValue function and in handle_response function.
124        let sender = response_async(&p, self);
125        self.get_bluetooth_thread()
126            .send(BluetoothRequest::ReadValue(self.get_instance_id(), sender))
127            .unwrap();
128        p
129    }
130
131    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
132    fn WriteValue(
133        &self,
134        value: ArrayBufferViewOrArrayBuffer,
135        comp: InRealm,
136        can_gc: CanGc,
137    ) -> Rc<Promise> {
138        let p = Promise::new_in_current_realm(comp, can_gc);
139
140        // Step 1.
141        if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Writes) {
142            p.reject_error(Security, can_gc);
143            return p;
144        }
145
146        // Step 2 - 3.
147        let vec = match value {
148            ArrayBufferViewOrArrayBuffer::ArrayBufferView(avb) => avb.to_vec(),
149            ArrayBufferViewOrArrayBuffer::ArrayBuffer(ab) => ab.to_vec(),
150        };
151        if vec.len() > MAXIMUM_ATTRIBUTE_LENGTH {
152            p.reject_error(InvalidModification, can_gc);
153            return p;
154        }
155
156        // Step 4.
157        if !self
158            .Characteristic()
159            .Service()
160            .Device()
161            .get_gatt()
162            .Connected()
163        {
164            p.reject_error(Network, can_gc);
165            return p;
166        }
167
168        // TODO: Step 7: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer.
169        // Note: Steps 5 - 6 and substeps of Step 7 are implemented in components/bluetooth/lib.rs
170        // in writeValue function and in handle_response function.
171        let sender = response_async(&p, self);
172        self.get_bluetooth_thread()
173            .send(BluetoothRequest::WriteValue(
174                self.get_instance_id(),
175                vec,
176                sender,
177            ))
178            .unwrap();
179        p
180    }
181}
182
183impl AsyncBluetoothListener for BluetoothRemoteGATTDescriptor {
184    fn handle_response(&self, response: BluetoothResponse, promise: &Rc<Promise>, can_gc: CanGc) {
185        match response {
186            // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
187            BluetoothResponse::ReadValue(result) => {
188                // TODO: Step 5.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
189
190                // Step 5.4.2.
191                // TODO(#5014): Replace ByteString with ArrayBuffer when it is implemented.
192                let value = ByteString::new(result);
193                *self.value.borrow_mut() = Some(value.clone());
194
195                // Step 5.4.3.
196                promise.resolve_native(&value, can_gc);
197            },
198            // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
199            BluetoothResponse::WriteValue(result) => {
200                // TODO: Step 7.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
201
202                // Step 7.4.2.
203                // TODO(#5014): Replace ByteString with an ArrayBuffer wrapped in a DataView.
204                *self.value.borrow_mut() = Some(ByteString::new(result));
205
206                // Step 7.4.3.
207                // TODO: Resolve promise with undefined instead of a value.
208                promise.resolve_native(&(), can_gc);
209            },
210            _ => promise.reject_error(Error::Type("Something went wrong...".to_owned()), can_gc),
211        }
212    }
213}