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