1use dom_struct::dom_struct;
6use js::context::JSContext;
7use js::realm::CurrentRealm;
8use script_bindings::cell::DomRefCell;
9use script_bindings::reflector::reflect_dom_object;
10use servo_base::generic_channel::GenericSender;
11use servo_bluetooth_traits::blocklist::{Blocklist, uuid_is_blocklisted};
12use servo_bluetooth_traits::{BluetoothRequest, BluetoothResponse, GATTType};
13
14use crate::dom::bindings::buffer_source::get_buffer_source_copy;
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, RootedPromise};
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 JSContext,
69 global: &GlobalScope,
70 service: &BluetoothRemoteGATTService,
71 uuid: DOMString,
72 properties: &BluetoothCharacteristicProperties,
73 instance_id: String,
74 ) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
75 reflect_dom_object(
76 cx,
77 Box::new(BluetoothRemoteGATTCharacteristic::new_inherited(
78 service,
79 uuid,
80 properties,
81 instance_id,
82 )),
83 global,
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 ) -> RootedPromise {
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 ) -> RootedPromise {
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) -> RootedPromise {
159 let p = Promise::new_in_realm_rooted(cx);
160
161 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
163 p.reject_error(cx, Security(None));
164 return p;
165 }
166
167 if !self.Service().Device().get_gatt(cx).Connected() {
169 p.reject_error(cx, Network(None));
170 return p;
171 }
172
173 if !self.Properties().Read() {
177 p.reject_error(cx, NotSupported(None));
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 ) -> RootedPromise {
196 let p = Promise::new_in_realm_rooted(cx);
197
198 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Writes) {
200 p.reject_error(cx, Security(None));
201 return p;
202 }
203
204 let vec = get_buffer_source_copy((&value).into());
206
207 if vec.len() > MAXIMUM_ATTRIBUTE_LENGTH {
208 p.reject_error(cx, InvalidModification(None));
209 return p;
210 }
211
212 if !self.Service().Device().get_gatt(cx).Connected() {
214 p.reject_error(cx, Network(None));
215 return p;
216 }
217
218 if !(self.Properties().Write() ||
222 self.Properties().WriteWithoutResponse() ||
223 self.Properties().AuthenticatedSignedWrites())
224 {
225 p.reject_error(cx, NotSupported(None));
226 return p;
227 }
228
229 let sender = response_async(&p, self);
232 self.get_bluetooth_thread()
233 .send(BluetoothRequest::WriteValue(
234 self.get_instance_id(),
235 vec,
236 sender,
237 ))
238 .unwrap();
239 p
240 }
241
242 fn StartNotifications(&self, cx: &mut CurrentRealm) -> RootedPromise {
244 let p = Promise::new_in_realm_rooted(cx);
245
246 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
248 p.reject_error(cx, Security(None));
249 return p;
250 }
251
252 if !self.Service().Device().get_gatt(cx).Connected() {
254 p.reject_error(cx, Network(None));
255 return p;
256 }
257
258 if !(self.Properties().Notify() || self.Properties().Indicate()) {
260 p.reject_error(cx, NotSupported(None));
261 return p;
262 }
263
264 let sender = response_async(&p, self);
269 self.get_bluetooth_thread()
270 .send(BluetoothRequest::EnableNotification(
271 self.get_instance_id(),
272 true,
273 sender,
274 ))
275 .unwrap();
276 p
277 }
278
279 fn StopNotifications(&self, cx: &mut CurrentRealm) -> RootedPromise {
281 let p = Promise::new_in_realm_rooted(cx);
282 let sender = response_async(&p, self);
283
284 self.get_bluetooth_thread()
289 .send(BluetoothRequest::EnableNotification(
290 self.get_instance_id(),
291 false,
292 sender,
293 ))
294 .unwrap();
295 p
296 }
297
298 event_handler!(
300 characteristicvaluechanged,
301 GetOncharacteristicvaluechanged,
302 SetOncharacteristicvaluechanged
303 );
304}
305
306impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic {
307 fn handle_response(
308 &self,
309 cx: &mut JSContext,
310 response: BluetoothResponse,
311 promise: &RootedPromise,
312 ) {
313 let device = self.Service().Device();
314 match response {
315 BluetoothResponse::GetDescriptors(descriptors_vec, single) => {
318 if single {
319 let descriptor = device.get_or_create_descriptor(cx, &descriptors_vec[0], self);
320 promise.resolve_native(cx, &descriptor);
321 return;
322 }
323 let mut descriptors = vec![];
324 for descriptor in descriptors_vec {
325 let bt_descriptor = device.get_or_create_descriptor(cx, &descriptor, self);
326 descriptors.push(bt_descriptor);
327 }
328 promise.resolve_native(cx, &descriptors);
329 },
330 BluetoothResponse::ReadValue(result) => {
332 let value = ByteString::new(result);
337 *self.value.safe_borrow_mut(cx.no_gc()) = Some(value.clone());
338
339 self.upcast::<EventTarget>()
341 .fire_bubbling_event(cx, atom!("characteristicvaluechanged"));
342
343 promise.resolve_native(cx, &value);
345 },
346 BluetoothResponse::WriteValue(result) => {
348 *self.value.safe_borrow_mut(cx.no_gc()) = Some(ByteString::new(result));
353
354 promise.resolve_native(cx, &());
356 },
357 BluetoothResponse::EnableNotification(_result) => {
360 promise.resolve_native(cx, self);
366 },
367 _ => promise.reject_error(cx, Error::Type(c"Something went wrong...".to_owned())),
368 }
369 }
370}