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 base::generic_channel::GenericSender;
8use bluetooth_traits::blocklist::{Blocklist, uuid_is_blocklisted};
9use bluetooth_traits::{BluetoothRequest, BluetoothResponse};
10use dom_struct::dom_struct;
11use js::realm::CurrentRealm;
12
13use crate::dom::bindings::cell::DomRefCell;
14use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::BluetoothRemoteGATTCharacteristicMethods;
15use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods;
16use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
17use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
18use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
19use crate::dom::bindings::error::Error::{self, InvalidModification, Network, Security};
20use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_cx};
21use crate::dom::bindings::root::{Dom, DomRoot};
22use crate::dom::bindings::str::{ByteString, DOMString};
23use crate::dom::bluetooth::{AsyncBluetoothListener, response_async};
24use crate::dom::bluetoothremotegattcharacteristic::{
25    BluetoothRemoteGATTCharacteristic, MAXIMUM_ATTRIBUTE_LENGTH,
26};
27use crate::dom::globalscope::GlobalScope;
28use crate::dom::promise::Promise;
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        cx: &mut js::context::JSContext,
58        global: &GlobalScope,
59        characteristic: &BluetoothRemoteGATTCharacteristic,
60        uuid: DOMString,
61        instance_id: String,
62    ) -> DomRoot<BluetoothRemoteGATTDescriptor> {
63        reflect_dom_object_with_cx(
64            Box::new(BluetoothRemoteGATTDescriptor::new_inherited(
65                characteristic,
66                uuid,
67                instance_id,
68            )),
69            global,
70            cx,
71        )
72    }
73
74    fn get_bluetooth_thread(&self) -> GenericSender<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, cx: &mut CurrentRealm) -> Rc<Promise> {
101        let p = Promise::new_in_realm(cx);
102
103        // Step 1.
104        if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
105            p.reject_error(Security(None), CanGc::from_cx(cx));
106            return p;
107        }
108
109        // Step 2.
110        if !self
111            .Characteristic()
112            .Service()
113            .Device()
114            .get_gatt(cx)
115            .Connected()
116        {
117            p.reject_error(Network(None), CanGc::from_cx(cx));
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        cx: &mut CurrentRealm,
135        value: ArrayBufferViewOrArrayBuffer,
136    ) -> Rc<Promise> {
137        let p = Promise::new_in_realm(cx);
138
139        // Step 1.
140        if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Writes) {
141            p.reject_error(Security(None), CanGc::from_cx(cx));
142            return p;
143        }
144
145        // Step 2 - 3.
146        let vec = match value {
147            ArrayBufferViewOrArrayBuffer::ArrayBufferView(avb) => avb.to_vec(),
148            ArrayBufferViewOrArrayBuffer::ArrayBuffer(ab) => ab.to_vec(),
149        };
150        if vec.len() > MAXIMUM_ATTRIBUTE_LENGTH {
151            p.reject_error(InvalidModification(None), CanGc::from_cx(cx));
152            return p;
153        }
154
155        // Step 4.
156        if !self
157            .Characteristic()
158            .Service()
159            .Device()
160            .get_gatt(cx)
161            .Connected()
162        {
163            p.reject_error(Network(None), CanGc::from_cx(cx));
164            return p;
165        }
166
167        // TODO: Step 7: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer.
168        // Note: Steps 5 - 6 and substeps of Step 7 are implemented in components/bluetooth/lib.rs
169        // in writeValue function and in handle_response function.
170        let sender = response_async(&p, self);
171        self.get_bluetooth_thread()
172            .send(BluetoothRequest::WriteValue(
173                self.get_instance_id(),
174                vec,
175                sender,
176            ))
177            .unwrap();
178        p
179    }
180}
181
182impl AsyncBluetoothListener for BluetoothRemoteGATTDescriptor {
183    fn handle_response(
184        &self,
185        cx: &mut js::context::JSContext,
186        response: BluetoothResponse,
187        promise: &Rc<Promise>,
188    ) {
189        match response {
190            // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
191            BluetoothResponse::ReadValue(result) => {
192                // TODO: Step 5.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
193
194                // Step 5.4.2.
195                // TODO(#5014): Replace ByteString with ArrayBuffer when it is implemented.
196                let value = ByteString::new(result);
197                *self.value.borrow_mut() = Some(value.clone());
198
199                // Step 5.4.3.
200                promise.resolve_native(&value, CanGc::from_cx(cx));
201            },
202            // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
203            BluetoothResponse::WriteValue(result) => {
204                // TODO: Step 7.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
205
206                // Step 7.4.2.
207                // TODO(#5014): Replace ByteString with an ArrayBuffer wrapped in a DataView.
208                *self.value.borrow_mut() = Some(ByteString::new(result));
209
210                // Step 7.4.3.
211                // TODO: Resolve promise with undefined instead of a value.
212                promise.resolve_native(&(), CanGc::from_cx(cx));
213            },
214            _ => promise.reject_error(
215                Error::Type(c"Something went wrong...".to_owned()),
216                CanGc::from_cx(cx),
217            ),
218        }
219    }
220}