1use 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::reflect_dom_object;
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::buffer_source::get_buffer_source_copy;
16use crate::dom::bindings::codegen::Bindings::BluetoothCharacteristicPropertiesBinding::BluetoothCharacteristicPropertiesMethods;
17use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::BluetoothRemoteGATTCharacteristicMethods;
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::{
22 self, InvalidModification, Network, NotSupported, Security,
23};
24use crate::dom::bindings::inheritance::Castable;
25use crate::dom::bindings::reflector::DomGlobal;
26use crate::dom::bindings::root::{Dom, DomRoot};
27use crate::dom::bindings::str::{ByteString, DOMString};
28use crate::dom::bluetooth::{AsyncBluetoothListener, get_gatt_children, response_async};
29use crate::dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
30use crate::dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
31use crate::dom::bluetoothuuid::{BluetoothDescriptorUUID, BluetoothUUID};
32use crate::dom::eventtarget::EventTarget;
33use crate::dom::globalscope::GlobalScope;
34use crate::dom::promise::{Promise, RootedPromise};
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: TracedDOMString,
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: uuid.traced(),
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 JSContext,
70 global: &GlobalScope,
71 service: &BluetoothRemoteGATTService,
72 uuid: DOMString,
73 properties: &BluetoothCharacteristicProperties,
74 instance_id: String,
75 ) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
76 reflect_dom_object(
77 cx,
78 Box::new(BluetoothRemoteGATTCharacteristic::new_inherited(
79 service,
80 uuid,
81 properties,
82 instance_id,
83 )),
84 global,
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().root()
113 }
114
115 fn GetDescriptor(
117 &self,
118 cx: &mut CurrentRealm,
119 descriptor: BluetoothDescriptorUUID,
120 ) -> RootedPromise {
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 ) -> RootedPromise {
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) -> RootedPromise {
160 let p = Promise::new_in_realm(cx);
161
162 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
164 p.reject_error(cx, Security(None));
165 return p;
166 }
167
168 if !self.Service().Device().get_gatt(cx).Connected() {
170 p.reject_error(cx, Network(None));
171 return p;
172 }
173
174 if !self.Properties().Read() {
178 p.reject_error(cx, NotSupported(None));
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 ) -> RootedPromise {
197 let p = Promise::new_in_realm(cx);
198
199 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Writes) {
201 p.reject_error(cx, Security(None));
202 return p;
203 }
204
205 let vec = get_buffer_source_copy((&value).into());
207
208 if vec.len() > MAXIMUM_ATTRIBUTE_LENGTH {
209 p.reject_error(cx, InvalidModification(None));
210 return p;
211 }
212
213 if !self.Service().Device().get_gatt(cx).Connected() {
215 p.reject_error(cx, Network(None));
216 return p;
217 }
218
219 if !(self.Properties().Write() ||
223 self.Properties().WriteWithoutResponse() ||
224 self.Properties().AuthenticatedSignedWrites())
225 {
226 p.reject_error(cx, NotSupported(None));
227 return p;
228 }
229
230 let sender = response_async(&p, self);
233 self.get_bluetooth_thread()
234 .send(BluetoothRequest::WriteValue(
235 self.get_instance_id(),
236 vec,
237 sender,
238 ))
239 .unwrap();
240 p
241 }
242
243 fn StartNotifications(&self, cx: &mut CurrentRealm) -> RootedPromise {
245 let p = Promise::new_in_realm(cx);
246
247 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
249 p.reject_error(cx, Security(None));
250 return p;
251 }
252
253 if !self.Service().Device().get_gatt(cx).Connected() {
255 p.reject_error(cx, Network(None));
256 return p;
257 }
258
259 if !(self.Properties().Notify() || self.Properties().Indicate()) {
261 p.reject_error(cx, NotSupported(None));
262 return p;
263 }
264
265 let sender = response_async(&p, self);
270 self.get_bluetooth_thread()
271 .send(BluetoothRequest::EnableNotification(
272 self.get_instance_id(),
273 true,
274 sender,
275 ))
276 .unwrap();
277 p
278 }
279
280 fn StopNotifications(&self, cx: &mut CurrentRealm) -> RootedPromise {
282 let p = Promise::new_in_realm(cx);
283 let sender = response_async(&p, self);
284
285 self.get_bluetooth_thread()
290 .send(BluetoothRequest::EnableNotification(
291 self.get_instance_id(),
292 false,
293 sender,
294 ))
295 .unwrap();
296 p
297 }
298
299 event_handler!(
301 characteristicvaluechanged,
302 GetOncharacteristicvaluechanged,
303 SetOncharacteristicvaluechanged
304 );
305}
306
307impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic {
308 fn handle_response(
309 &self,
310 cx: &mut JSContext,
311 response: BluetoothResponse,
312 promise: &RootedPromise,
313 ) {
314 let device = self.Service().Device();
315 match response {
316 BluetoothResponse::GetDescriptors(descriptors_vec, single) => {
319 if single {
320 let descriptor = device.get_or_create_descriptor(cx, &descriptors_vec[0], self);
321 promise.resolve_native(cx, &descriptor);
322 return;
323 }
324 let mut descriptors = vec![];
325 for descriptor in descriptors_vec {
326 let bt_descriptor = device.get_or_create_descriptor(cx, &descriptor, self);
327 descriptors.push(bt_descriptor);
328 }
329 promise.resolve_native(cx, &descriptors);
330 },
331 BluetoothResponse::ReadValue(result) => {
333 let value = ByteString::new(result);
338 *self.value.safe_borrow_mut(cx.no_gc()) = Some(value.clone());
339
340 self.upcast::<EventTarget>()
342 .fire_bubbling_event(cx, atom!("characteristicvaluechanged"));
343
344 promise.resolve_native(cx, &value);
346 },
347 BluetoothResponse::WriteValue(result) => {
349 *self.value.safe_borrow_mut(cx.no_gc()) = Some(ByteString::new(result));
354
355 promise.resolve_native(cx, &());
357 },
358 BluetoothResponse::EnableNotification(_result) => {
361 promise.resolve_native(cx, self);
367 },
368 _ => promise.reject_error(cx, Error::Type(c"Something went wrong...".to_owned())),
369 }
370 }
371}