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 dom_struct::dom_struct;
6use js::context::JSContext;
7use js::realm::CurrentRealm;
8use script_bindings::cell::DomRefCell;
9use script_bindings::domstring::TracedDOMString;
10use script_bindings::reflector::{Reflector, reflect_dom_object};
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::buffer_source::get_buffer_source_copy;
16use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::BluetoothRemoteGATTCharacteristicMethods;
17use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods;
18use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
19use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
20use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
21use crate::dom::bindings::error::Error::{self, InvalidModification, Network, Security};
22use crate::dom::bindings::reflector::DomGlobal;
23use crate::dom::bindings::root::{Dom, DomRoot};
24use crate::dom::bindings::str::{ByteString, DOMString};
25use crate::dom::bluetooth::{AsyncBluetoothListener, response_async};
26use crate::dom::bluetoothremotegattcharacteristic::{
27    BluetoothRemoteGATTCharacteristic, MAXIMUM_ATTRIBUTE_LENGTH,
28};
29use crate::dom::globalscope::GlobalScope;
30use crate::dom::promise::{Promise, RootedPromise};
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: TracedDOMString,
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: uuid.traced(),
52            value: DomRefCell::new(None),
53            instance_id,
54        }
55    }
56
57    pub(crate) fn new(
58        cx: &mut JSContext,
59        global: &GlobalScope,
60        characteristic: &BluetoothRemoteGATTCharacteristic,
61        uuid: DOMString,
62        instance_id: String,
63    ) -> DomRoot<BluetoothRemoteGATTDescriptor> {
64        reflect_dom_object(
65            cx,
66            Box::new(BluetoothRemoteGATTDescriptor::new_inherited(
67                characteristic,
68                uuid,
69                instance_id,
70            )),
71            global,
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().root()
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) -> RootedPromise {
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(cx, Security(None));
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(cx, Network(None));
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    ) -> RootedPromise {
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(cx, Security(None));
143            return p;
144        }
145
146        // Step 2 - 3.
147        let vec = get_buffer_source_copy((&value).into());
148        if vec.len() > MAXIMUM_ATTRIBUTE_LENGTH {
149            p.reject_error(cx, InvalidModification(None));
150            return p;
151        }
152
153        // Step 4.
154        if !self
155            .Characteristic()
156            .Service()
157            .Device()
158            .get_gatt(cx)
159            .Connected()
160        {
161            p.reject_error(cx, Network(None));
162            return p;
163        }
164
165        // TODO: Step 7: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer.
166        // Note: Steps 5 - 6 and substeps of Step 7 are implemented in components/bluetooth/lib.rs
167        // in writeValue function and in handle_response function.
168        let sender = response_async(&p, self);
169        self.get_bluetooth_thread()
170            .send(BluetoothRequest::WriteValue(
171                self.get_instance_id(),
172                vec,
173                sender,
174            ))
175            .unwrap();
176        p
177    }
178}
179
180impl AsyncBluetoothListener for BluetoothRemoteGATTDescriptor {
181    fn handle_response(
182        &self,
183        cx: &mut JSContext,
184        response: BluetoothResponse,
185        promise: &RootedPromise,
186    ) {
187        match response {
188            // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
189            BluetoothResponse::ReadValue(result) => {
190                // TODO: Step 5.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
191
192                // Step 5.4.2.
193                // TODO(#5014): Replace ByteString with ArrayBuffer when it is implemented.
194                let value = ByteString::new(result);
195                *self.value.safe_borrow_mut(cx.no_gc()) = Some(value.clone());
196
197                // Step 5.4.3.
198                promise.resolve_native(cx, &value);
199            },
200            // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
201            BluetoothResponse::WriteValue(result) => {
202                // TODO: Step 7.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
203
204                // Step 7.4.2.
205                // TODO(#5014): Replace ByteString with an ArrayBuffer wrapped in a DataView.
206                *self.value.safe_borrow_mut(cx.no_gc()) = Some(ByteString::new(result));
207
208                // Step 7.4.3.
209                // TODO: Resolve promise with undefined instead of a value.
210                promise.resolve_native(cx, &());
211            },
212            _ => promise.reject_error(cx, Error::Type(c"Something went wrong...".to_owned())),
213        }
214    }
215}