From 21fc7835e4bed155388f1d7f4f3f3dc9e2904348 Mon Sep 17 00:00:00 2001 From: UPPower Date: Sat, 22 Aug 2026 15:14:50 +0800 Subject: [PATCH] fix(webbluetooth): read characteristic values via the DataView's buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `readValue()` resolves to a DataView. `Uint8Array::new(&data_view)` treats it as a plain array-like (length undefined) and yields an empty array, so the subsequent `copy_to` length assertion panics and kills the wasm instance. Any protocol that performs a hardware read during init (e.g. sensee-v2) dies right after chooser pairing. Build the Uint8Array over the DataView's underlying buffer instead, honoring its byte offset/length — the Subscribe notification handler already uses the buffer-based pattern. Verified against a physical Sensee Capsule (CCPA10S2) via Chrome/macOS: the device identifies and runs through the browser embedded server. Fixes #941 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QitzJcyNqb1CHnMJ8rcZP1 --- .../src/webbluetooth_hardware.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/buttplug_server_hwmgr_webbluetooth/src/webbluetooth_hardware.rs b/crates/buttplug_server_hwmgr_webbluetooth/src/webbluetooth_hardware.rs index 54643b5f4..aa9b27b6f 100644 --- a/crates/buttplug_server_hwmgr_webbluetooth/src/webbluetooth_hardware.rs +++ b/crates/buttplug_server_hwmgr_webbluetooth/src/webbluetooth_hardware.rs @@ -297,9 +297,20 @@ async fn run_webbluetooth_loop( )) }) .map(|val| { - let data_view = val; + // readValue resolves to a DataView. `Uint8Array::new(dataView)` + // treats it as array-like (length undefined) => empty array => + // copy_to length assert panics and kills the wasm instance. + // Build the view over the underlying buffer instead (as the + // Subscribe handler below does), honoring the DataView's + // byte offset/length. + let data_view = js_sys::DataView::from(val); let mut body = vec![0u8; data_view.byte_length()]; - Uint8Array::new(&data_view).copy_to(&mut body[..]); + Uint8Array::new_with_byte_offset_and_length( + &JsValue::from(data_view.buffer()), + data_view.byte_offset() as u32, + data_view.byte_length() as u32, + ) + .copy_to(&mut body[..]); HardwareReading::new(read_cmd.endpoint(), &body) }); let _ = reply.send(result);