1use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::realm::CurrentRealm;
10use script_bindings::cell::DomRefCell;
11use script_bindings::reflector::reflect_dom_object_with_cx;
12use servo_base::generic_channel::GenericSender;
13use servo_bluetooth_traits::blocklist::{Blocklist, uuid_is_blocklisted};
14use servo_bluetooth_traits::{BluetoothRequest, BluetoothResponse, GATTType};
15
16use crate::dom::bindings::buffer_source::get_buffer_source_copy;
17use crate::dom::bindings::codegen::Bindings::BluetoothCharacteristicPropertiesBinding::BluetoothCharacteristicPropertiesMethods;
18use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::BluetoothRemoteGATTCharacteristicMethods;
19use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
20use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
21use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
22use crate::dom::bindings::error::Error::{
23 self, InvalidModification, Network, NotSupported, Security,
24};
25use crate::dom::bindings::inheritance::Castable;
26use crate::dom::bindings::reflector::DomGlobal;
27use crate::dom::bindings::root::{Dom, DomRoot};
28use crate::dom::bindings::str::{ByteString, DOMString};
29use crate::dom::bluetooth::{AsyncBluetoothListener, get_gatt_children, response_async};
30use crate::dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
31use crate::dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
32use crate::dom::bluetoothuuid::{BluetoothDescriptorUUID, BluetoothUUID};
33use crate::dom::eventtarget::EventTarget;
34use crate::dom::globalscope::GlobalScope;
35use crate::dom::promise::Promise;
36
37pub(crate) const MAXIMUM_ATTRIBUTE_LENGTH: usize = 512;
40
41#[dom_struct]
43pub(crate) struct BluetoothRemoteGATTCharacteristic {
44 eventtarget: EventTarget,
45 service: Dom<BluetoothRemoteGATTService>,
46 uuid: DOMString,
47 properties: Dom<BluetoothCharacteristicProperties>,
48 value: DomRefCell<Option<ByteString>>,
49 instance_id: String,
50}
51
52impl BluetoothRemoteGATTCharacteristic {
53 pub(crate) fn new_inherited(
54 service: &BluetoothRemoteGATTService,
55 uuid: DOMString,
56 properties: &BluetoothCharacteristicProperties,
57 instance_id: String,
58 ) -> BluetoothRemoteGATTCharacteristic {
59 BluetoothRemoteGATTCharacteristic {
60 eventtarget: EventTarget::new_inherited(),
61 service: Dom::from_ref(service),
62 uuid,
63 properties: Dom::from_ref(properties),
64 value: DomRefCell::new(None),
65 instance_id,
66 }
67 }
68
69 pub(crate) fn new(
70 cx: &mut JSContext,
71 global: &GlobalScope,
72 service: &BluetoothRemoteGATTService,
73 uuid: DOMString,
74 properties: &BluetoothCharacteristicProperties,
75 instance_id: String,
76 ) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
77 reflect_dom_object_with_cx(
78 Box::new(BluetoothRemoteGATTCharacteristic::new_inherited(
79 service,
80 uuid,
81 properties,
82 instance_id,
83 )),
84 global,
85 cx,
86 )
87 }
88
89 fn get_bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
90 self.global().as_window().bluetooth_thread()
91 }
92
93 fn get_instance_id(&self) -> String {
94 self.instance_id.clone()
95 }
96}
97
98impl BluetoothRemoteGATTCharacteristicMethods<crate::DomTypeHolder>
99 for BluetoothRemoteGATTCharacteristic
100{
101 fn Properties(&self) -> DomRoot<BluetoothCharacteristicProperties> {
103 DomRoot::from_ref(&self.properties)
104 }
105
106 fn Service(&self) -> DomRoot<BluetoothRemoteGATTService> {
108 DomRoot::from_ref(&self.service)
109 }
110
111 fn Uuid(&self) -> DOMString {
113 self.uuid.clone()
114 }
115
116 fn GetDescriptor(
118 &self,
119 cx: &mut CurrentRealm,
120 descriptor: BluetoothDescriptorUUID,
121 ) -> Rc<Promise> {
122 let is_connected = self.Service().Device().get_gatt(cx).Connected();
123 get_gatt_children(
124 cx,
125 self,
126 true,
127 BluetoothUUID::descriptor,
128 Some(descriptor),
129 self.get_instance_id(),
130 is_connected,
131 GATTType::Descriptor,
132 )
133 }
134
135 fn GetDescriptors(
137 &self,
138 cx: &mut CurrentRealm,
139 descriptor: Option<BluetoothDescriptorUUID>,
140 ) -> Rc<Promise> {
141 let is_connected = self.Service().Device().get_gatt(cx).Connected();
142 get_gatt_children(
143 cx,
144 self,
145 false,
146 BluetoothUUID::descriptor,
147 descriptor,
148 self.get_instance_id(),
149 is_connected,
150 GATTType::Descriptor,
151 )
152 }
153
154 fn GetValue(&self) -> Option<ByteString> {
156 self.value.borrow().clone()
157 }
158
159 fn ReadValue(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
161 let p = Promise::new_in_realm(cx);
162
163 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
165 p.reject_error(cx, Security(None));
166 return p;
167 }
168
169 if !self.Service().Device().get_gatt(cx).Connected() {
171 p.reject_error(cx, Network(None));
172 return p;
173 }
174
175 if !self.Properties().Read() {
179 p.reject_error(cx, NotSupported(None));
180 return p;
181 }
182
183 let sender = response_async(&p, self);
186 self.get_bluetooth_thread()
187 .send(BluetoothRequest::ReadValue(self.get_instance_id(), sender))
188 .unwrap();
189 p
190 }
191
192 fn WriteValue(
194 &self,
195 cx: &mut CurrentRealm,
196 value: ArrayBufferViewOrArrayBuffer,
197 ) -> Rc<Promise> {
198 let p = Promise::new_in_realm(cx);
199
200 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Writes) {
202 p.reject_error(cx, Security(None));
203 return p;
204 }
205
206 let vec = get_buffer_source_copy((&value).into());
208
209 if vec.len() > MAXIMUM_ATTRIBUTE_LENGTH {
210 p.reject_error(cx, InvalidModification(None));
211 return p;
212 }
213
214 if !self.Service().Device().get_gatt(cx).Connected() {
216 p.reject_error(cx, Network(None));
217 return p;
218 }
219
220 if !(self.Properties().Write() ||
224 self.Properties().WriteWithoutResponse() ||
225 self.Properties().AuthenticatedSignedWrites())
226 {
227 p.reject_error(cx, NotSupported(None));
228 return p;
229 }
230
231 let sender = response_async(&p, self);
234 self.get_bluetooth_thread()
235 .send(BluetoothRequest::WriteValue(
236 self.get_instance_id(),
237 vec,
238 sender,
239 ))
240 .unwrap();
241 p
242 }
243
244 fn StartNotifications(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
246 let p = Promise::new_in_realm(cx);
247
248 if uuid_is_blocklisted(&self.uuid.str(), Blocklist::Reads) {
250 p.reject_error(cx, Security(None));
251 return p;
252 }
253
254 if !self.Service().Device().get_gatt(cx).Connected() {
256 p.reject_error(cx, Network(None));
257 return p;
258 }
259
260 if !(self.Properties().Notify() || self.Properties().Indicate()) {
262 p.reject_error(cx, NotSupported(None));
263 return p;
264 }
265
266 let sender = response_async(&p, self);
271 self.get_bluetooth_thread()
272 .send(BluetoothRequest::EnableNotification(
273 self.get_instance_id(),
274 true,
275 sender,
276 ))
277 .unwrap();
278 p
279 }
280
281 fn StopNotifications(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
283 let p = Promise::new_in_realm(cx);
284 let sender = response_async(&p, self);
285
286 self.get_bluetooth_thread()
291 .send(BluetoothRequest::EnableNotification(
292 self.get_instance_id(),
293 false,
294 sender,
295 ))
296 .unwrap();
297 p
298 }
299
300 event_handler!(
302 characteristicvaluechanged,
303 GetOncharacteristicvaluechanged,
304 SetOncharacteristicvaluechanged
305 );
306}
307
308impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic {
309 fn handle_response(
310 &self,
311 cx: &mut JSContext,
312 response: BluetoothResponse,
313 promise: &Rc<Promise>,
314 ) {
315 let device = self.Service().Device();
316 match response {
317 BluetoothResponse::GetDescriptors(descriptors_vec, single) => {
320 if single {
321 let descriptor = device.get_or_create_descriptor(cx, &descriptors_vec[0], self);
322 promise.resolve_native(cx, &descriptor);
323 return;
324 }
325 let mut descriptors = vec![];
326 for descriptor in descriptors_vec {
327 let bt_descriptor = device.get_or_create_descriptor(cx, &descriptor, self);
328 descriptors.push(bt_descriptor);
329 }
330 promise.resolve_native(cx, &descriptors);
331 },
332 BluetoothResponse::ReadValue(result) => {
334 let value = ByteString::new(result);
339 *self.value.safe_borrow_mut(cx.no_gc()) = Some(value.clone());
340
341 self.upcast::<EventTarget>()
343 .fire_bubbling_event(cx, atom!("characteristicvaluechanged"));
344
345 promise.resolve_native(cx, &value);
347 },
348 BluetoothResponse::WriteValue(result) => {
350 *self.value.safe_borrow_mut(cx.no_gc()) = Some(ByteString::new(result));
355
356 promise.resolve_native(cx, &());
358 },
359 BluetoothResponse::EnableNotification(_result) => {
362 promise.resolve_native(cx, self);
368 },
369 _ => promise.reject_error(cx, Error::Type(c"Something went wrong...".to_owned())),
370 }
371 }
372}