Skip to main content

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 dom_struct::dom_struct;
8use js::realm::CurrentRealm;
9use script_bindings::cell::DomRefCell;
10use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
11use servo_base::generic_channel::GenericSender;
12use servo_bluetooth_traits::blocklist::{Blocklist, uuid_is_blocklisted};
13use servo_bluetooth_traits::{BluetoothRequest, BluetoothResponse};
14
15use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::BluetoothRemoteGATTCharacteristicMethods;
16use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods;
17use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
18use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
19use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
20use crate::dom::bindings::error::Error::{self, InvalidModification, Network, Security};
21use crate::dom::bindings::reflector::DomGlobal;
22use crate::dom::bindings::root::{Dom, DomRoot};
23use crate::dom::bindings::str::{ByteString, DOMString};
24use crate::dom::bluetooth::{AsyncBluetoothListener, response_async};
25use crate::dom::bluetoothremotegattcharacteristic::{
26    BluetoothRemoteGATTCharacteristic, MAXIMUM_ATTRIBUTE_LENGTH,
27};
28use crate::dom::globalscope::GlobalScope;
29use crate::dom::promise::Promise;
30use crate::script_runtime::CanGc;
31
32// http://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattdescriptor
33#[dom_struct]
34pub(crate) struct BluetoothRemoteGATTDescriptor {
35    reflector_: Reflector,
36    characteristic: Dom<BluetoothRemoteGATTCharacteristic>,
37    uuid: DOMString,
38    value: DomRefCell<Option<ByteString>>,
39    instance_id: String,
40}
41
42impl BluetoothRemoteGATTDescriptor {
43    pub(crate) fn new_inherited(
44        characteristic: &BluetoothRemoteGATTCharacteristic,
45        uuid: DOMString,
46        instance_id: String,
47    ) -> BluetoothRemoteGATTDescriptor {
48        BluetoothRemoteGATTDescriptor {
49            reflector_: Reflector::new(),
50            characteristic: Dom::from_ref(characteristic),
51            uuid,
52            value: DomRefCell::new(None),
53            instance_id,
54        }
55    }
56
57    pub(crate) fn new(
58        cx: &mut js::context::JSContext,
59        global: &GlobalScope,
60        characteristic: &BluetoothRemoteGATTCharacteristic,
61        uuid: DOMString,
62        instance_id: String,
63    ) -> DomRoot<BluetoothRemoteGATTDescriptor> {
64        reflect_dom_object_with_cx(
65            Box::new(BluetoothRemoteGATTDescriptor::new_inherited(
66                characteristic,
67                uuid,
68                instance_id,
69            )),
70            global,
71            cx,
72        )
73    }
74
75    fn get_bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
76        self.global().as_window().bluetooth_thread()
77    }
78
79    fn get_instance_id(&self) -> String {
80        self.instance_id.clone()
81    }
82}
83
84impl BluetoothRemoteGATTDescriptorMethods<crate::DomTypeHolder> for BluetoothRemoteGATTDescriptor {
85    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-characteristic>
86    fn Characteristic(&self) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
87        DomRoot::from_ref(&self.characteristic)
88    }
89
90    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-uuid>
91    fn Uuid(&self) -> DOMString {
92        self.uuid.clone()
93    }
94
95    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-value>
96    fn GetValue(&self) -> Option<ByteString> {
97        self.value.borrow().clone()
98    }
99
100    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue>
101    fn ReadValue(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
102        let p = Promise::new_in_realm(cx);
103
104        // Step 1.
105        if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
106            p.reject_error(Security(None), CanGc::from_cx(cx));
107            return p;
108        }
109
110        // Step 2.
111        if !self
112            .Characteristic()
113            .Service()
114            .Device()
115            .get_gatt(cx)
116            .Connected()
117        {
118            p.reject_error(Network(None), CanGc::from_cx(cx));
119            return p;
120        }
121
122        // TODO: Step 5: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer.
123        // Note: Steps 3 - 4 and substeps of Step 5 are implemented in components/bluetooth/lib.rs
124        // in readValue function and in handle_response function.
125        let sender = response_async(&p, self);
126        self.get_bluetooth_thread()
127            .send(BluetoothRequest::ReadValue(self.get_instance_id(), sender))
128            .unwrap();
129        p
130    }
131
132    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue>
133    fn WriteValue(
134        &self,
135        cx: &mut CurrentRealm,
136        value: ArrayBufferViewOrArrayBuffer,
137    ) -> Rc<Promise> {
138        let p = Promise::new_in_realm(cx);
139
140        // Step 1.
141        if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Writes) {
142            p.reject_error(Security(None), CanGc::from_cx(cx));
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(None), CanGc::from_cx(cx));
153            return p;
154        }
155
156        // Step 4.
157        if !self
158            .Characteristic()
159            .Service()
160            .Device()
161            .get_gatt(cx)
162            .Connected()
163        {
164            p.reject_error(Network(None), CanGc::from_cx(cx));
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(
185        &self,
186        cx: &mut js::context::JSContext,
187        response: BluetoothResponse,
188        promise: &Rc<Promise>,
189    ) {
190        match response {
191            // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
192            BluetoothResponse::ReadValue(result) => {
193                // TODO: Step 5.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
194
195                // Step 5.4.2.
196                // TODO(#5014): Replace ByteString with ArrayBuffer when it is implemented.
197                let value = ByteString::new(result);
198                *self.value.borrow_mut() = Some(value.clone());
199
200                // Step 5.4.3.
201                promise.resolve_native(&value, CanGc::from_cx(cx));
202            },
203            // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
204            BluetoothResponse::WriteValue(result) => {
205                // TODO: Step 7.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
206
207                // Step 7.4.2.
208                // TODO(#5014): Replace ByteString with an ArrayBuffer wrapped in a DataView.
209                *self.value.borrow_mut() = Some(ByteString::new(result));
210
211                // Step 7.4.3.
212                // TODO: Resolve promise with undefined instead of a value.
213                promise.resolve_native(&(), CanGc::from_cx(cx));
214            },
215            _ => promise.reject_error(
216                Error::Type(c"Something went wrong...".to_owned()),
217                CanGc::from_cx(cx),
218            ),
219        }
220    }
221}