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