1use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::realm::CurrentRealm;
9use script_bindings::cell::DomRefCell;
10use script_bindings::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, GATTType};
14
15use crate::dom::bindings::codegen::Bindings::BluetoothCharacteristicPropertiesBinding::BluetoothCharacteristicPropertiesMethods;
16use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::BluetoothRemoteGATTCharacteristicMethods;
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::{
21 self, InvalidModification, Network, NotSupported, Security,
22};
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::reflector::DomGlobal;
25use crate::dom::bindings::root::{Dom, DomRoot};
26use crate::dom::bindings::str::{ByteString, DOMString};
27use crate::dom::bluetooth::{AsyncBluetoothListener, get_gatt_children, response_async};
28use crate::dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
29use crate::dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
30use crate::dom::bluetoothuuid::{BluetoothDescriptorUUID, BluetoothUUID};
31use crate::dom::eventtarget::EventTarget;
32use crate::dom::globalscope::GlobalScope;
33use crate::dom::promise::Promise;
34use crate::script_runtime::CanGc;
35
36pub(crate) const MAXIMUM_ATTRIBUTE_LENGTH: usize = 512;
39
40#[dom_struct]
42pub(crate) struct BluetoothRemoteGATTCharacteristic {
43 eventtarget: EventTarget,
44 service: Dom<BluetoothRemoteGATTService>,
45 uuid: DOMString,
46 properties: Dom<BluetoothCharacteristicProperties>,
47 value: DomRefCell<Option<ByteString>>,
48 instance_id: String,
49}
50
51impl BluetoothRemoteGATTCharacteristic {
52 pub(crate) fn new_inherited(
53 service: &BluetoothRemoteGATTService,
54 uuid: DOMString,
55 properties: &BluetoothCharacteristicProperties,
56 instance_id: String,
57 ) -> BluetoothRemoteGATTCharacteristic {
58 BluetoothRemoteGATTCharacteristic {
59 eventtarget: EventTarget::new_inherited(),
60 service: Dom::from_ref(service),
61 uuid,
62 properties: Dom::from_ref(properties),
63 value: DomRefCell::new(None),
64 instance_id,
65 }
66 }
67
68 pub(crate) fn new(
69 cx: &mut js::context::JSContext,
70 global: &GlobalScope,
71 service: &BluetoothRemoteGATTService,
72 uuid: DOMString,
73 properties: &BluetoothCharacteristicProperties,
74 instance_id: String,
75 ) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
76 reflect_dom_object_with_cx(
77 Box::new(BluetoothRemoteGATTCharacteristic::new_inherited(
78 service,
79 uuid,
80 properties,
81 instance_id,
82 )),
83 global,
84 cx,
85 )
86 }
87
88 fn get_bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
89 self.global().as_window().bluetooth_thread()
90 }
91
92 fn get_instance_id(&self) -> String {
93 self.instance_id.clone()
94 }
95}
96
97impl BluetoothRemoteGATTCharacteristicMethods<crate::DomTypeHolder>
98 for BluetoothRemoteGATTCharacteristic
99{
100 fn Properties(&self) -> DomRoot<BluetoothCharacteristicProperties> {
102 DomRoot::from_ref(&self.properties)
103 }
104
105 fn Service(&self) -> DomRoot<BluetoothRemoteGATTService> {
107 DomRoot::from_ref(&self.service)
108 }
109
110 fn Uuid(&self) -> DOMString {
112 self.uuid.clone()
113 }
114
115 fn GetDescriptor(
117 &self,
118 cx: &mut CurrentRealm,
119 descriptor: BluetoothDescriptorUUID,
120 ) -> Rc<Promise> {
121 let is_connected = self.Service().Device().get_gatt(cx).Connected();
122 get_gatt_children(
123 cx,
124 self,
125 true,
126 BluetoothUUID::descriptor,
127 Some(descriptor),
128 self.get_instance_id(),
129 is_connected,
130 GATTType::Descriptor,
131 )
132 }
133
134 fn GetDescriptors(
136 &self,
137 cx: &mut CurrentRealm,
138 descriptor: Option<BluetoothDescriptorUUID>,
139 ) -> Rc<Promise> {
140 let is_connected = self.Service().Device().get_gatt(cx).Connected();
141 get_gatt_children(
142 cx,
143 self,
144 false,
145 BluetoothUUID::descriptor,
146 descriptor,
147 self.get_instance_id(),
148 is_connected,
149 GATTType::Descriptor,
150 )
151 }
152
153 fn GetValue(&self) -> Option<ByteString> {
155 self.value.borrow().clone()
156 }
157
158 fn ReadValue(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
160 let p = Promise::new_in_realm(cx);
161
162 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
164 p.reject_error(Security(None), CanGc::from_cx(cx));
165 return p;
166 }
167
168 if !self.Service().Device().get_gatt(cx).Connected() {
170 p.reject_error(Network(None), CanGc::from_cx(cx));
171 return p;
172 }
173
174 if !self.Properties().Read() {
178 p.reject_error(NotSupported(None), CanGc::from_cx(cx));
179 return p;
180 }
181
182 let sender = response_async(&p, self);
185 self.get_bluetooth_thread()
186 .send(BluetoothRequest::ReadValue(self.get_instance_id(), sender))
187 .unwrap();
188 p
189 }
190
191 fn WriteValue(
193 &self,
194 cx: &mut CurrentRealm,
195 value: ArrayBufferViewOrArrayBuffer,
196 ) -> Rc<Promise> {
197 let p = Promise::new_in_realm(cx);
198
199 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Writes) {
201 p.reject_error(Security(None), CanGc::from_cx(cx));
202 return p;
203 }
204
205 let vec = match value {
207 ArrayBufferViewOrArrayBuffer::ArrayBufferView(avb) => avb.to_vec(),
208 ArrayBufferViewOrArrayBuffer::ArrayBuffer(ab) => ab.to_vec(),
209 };
210
211 if vec.len() > MAXIMUM_ATTRIBUTE_LENGTH {
212 p.reject_error(InvalidModification(None), CanGc::from_cx(cx));
213 return p;
214 }
215
216 if !self.Service().Device().get_gatt(cx).Connected() {
218 p.reject_error(Network(None), CanGc::from_cx(cx));
219 return p;
220 }
221
222 if !(self.Properties().Write() ||
226 self.Properties().WriteWithoutResponse() ||
227 self.Properties().AuthenticatedSignedWrites())
228 {
229 p.reject_error(NotSupported(None), CanGc::from_cx(cx));
230 return p;
231 }
232
233 let sender = response_async(&p, self);
236 self.get_bluetooth_thread()
237 .send(BluetoothRequest::WriteValue(
238 self.get_instance_id(),
239 vec,
240 sender,
241 ))
242 .unwrap();
243 p
244 }
245
246 fn StartNotifications(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
248 let p = Promise::new_in_realm(cx);
249
250 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
252 p.reject_error(Security(None), CanGc::from_cx(cx));
253 return p;
254 }
255
256 if !self.Service().Device().get_gatt(cx).Connected() {
258 p.reject_error(Network(None), CanGc::from_cx(cx));
259 return p;
260 }
261
262 if !(self.Properties().Notify() || self.Properties().Indicate()) {
264 p.reject_error(NotSupported(None), CanGc::from_cx(cx));
265 return p;
266 }
267
268 let sender = response_async(&p, self);
273 self.get_bluetooth_thread()
274 .send(BluetoothRequest::EnableNotification(
275 self.get_instance_id(),
276 true,
277 sender,
278 ))
279 .unwrap();
280 p
281 }
282
283 fn StopNotifications(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
285 let p = Promise::new_in_realm(cx);
286 let sender = response_async(&p, self);
287
288 self.get_bluetooth_thread()
293 .send(BluetoothRequest::EnableNotification(
294 self.get_instance_id(),
295 false,
296 sender,
297 ))
298 .unwrap();
299 p
300 }
301
302 event_handler!(
304 characteristicvaluechanged,
305 GetOncharacteristicvaluechanged,
306 SetOncharacteristicvaluechanged
307 );
308}
309
310impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic {
311 fn handle_response(
312 &self,
313 cx: &mut js::context::JSContext,
314 response: BluetoothResponse,
315 promise: &Rc<Promise>,
316 ) {
317 let device = self.Service().Device();
318 match response {
319 BluetoothResponse::GetDescriptors(descriptors_vec, single) => {
322 if single {
323 promise.resolve_native(
324 &device.get_or_create_descriptor(cx, &descriptors_vec[0], self),
325 CanGc::from_cx(cx),
326 );
327 return;
328 }
329 let mut descriptors = vec![];
330 for descriptor in descriptors_vec {
331 let bt_descriptor = device.get_or_create_descriptor(cx, &descriptor, self);
332 descriptors.push(bt_descriptor);
333 }
334 promise.resolve_native(&descriptors, CanGc::from_cx(cx));
335 },
336 BluetoothResponse::ReadValue(result) => {
338 let value = ByteString::new(result);
343 *self.value.borrow_mut() = Some(value.clone());
344
345 self.upcast::<EventTarget>()
347 .fire_bubbling_event(cx, atom!("characteristicvaluechanged"));
348
349 promise.resolve_native(&value, CanGc::from_cx(cx));
351 },
352 BluetoothResponse::WriteValue(result) => {
354 *self.value.borrow_mut() = Some(ByteString::new(result));
359
360 promise.resolve_native(&(), CanGc::from_cx(cx));
362 },
363 BluetoothResponse::EnableNotification(_result) => {
366 promise.resolve_native(self, CanGc::from_cx(cx));
372 },
373 _ => promise.reject_error(
374 Error::Type(c"Something went wrong...".to_owned()),
375 CanGc::from_cx(cx),
376 ),
377 }
378 }
379}