script/dom/bluetooth/
bluetoothremotegattdescriptor.rs1use 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#[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 fn Characteristic(&self) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
87 DomRoot::from_ref(&self.characteristic)
88 }
89
90 fn Uuid(&self) -> DOMString {
92 self.uuid.clone().root()
93 }
94
95 fn GetValue(&self) -> Option<ByteString> {
97 self.value.borrow().clone()
98 }
99
100 fn ReadValue(&self, cx: &mut CurrentRealm) -> RootedPromise {
102 let p = Promise::new_in_realm(cx);
103
104 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
106 p.reject_error(cx, Security(None));
107 return p;
108 }
109
110 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 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 fn WriteValue(
134 &self,
135 cx: &mut CurrentRealm,
136 value: ArrayBufferViewOrArrayBuffer,
137 ) -> RootedPromise {
138 let p = Promise::new_in_realm(cx);
139
140 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Writes) {
142 p.reject_error(cx, Security(None));
143 return p;
144 }
145
146 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 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 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 BluetoothResponse::ReadValue(result) => {
190 let value = ByteString::new(result);
195 *self.value.safe_borrow_mut(cx.no_gc()) = Some(value.clone());
196
197 promise.resolve_native(cx, &value);
199 },
200 BluetoothResponse::WriteValue(result) => {
202 *self.value.safe_borrow_mut(cx.no_gc()) = Some(ByteString::new(result));
207
208 promise.resolve_native(cx, &());
211 },
212 _ => promise.reject_error(cx, Error::Type(c"Something went wrong...".to_owned())),
213 }
214 }
215}