From b5451264fb55cfa0c50aa11495892965b2957df6 Mon Sep 17 00:00:00 2001 From: def Date: Mon, 4 Dec 2023 21:59:09 +0800 Subject: [PATCH 1/3] feat: add shared_buffer implement and example --- examples/shared_buffer.rs | 143 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 35 ++++++++++ src/webview2/mod.rs | 34 +++++++++ 3 files changed, 212 insertions(+) create mode 100644 examples/shared_buffer.rs diff --git a/examples/shared_buffer.rs b/examples/shared_buffer.rs new file mode 100644 index 0000000000..7339b20219 --- /dev/null +++ b/examples/shared_buffer.rs @@ -0,0 +1,143 @@ +// Copyright 2020-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoopBuilder}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; + +#[cfg(target_os = "windows")] +fn main() -> wry::Result<()> { + use wry::WebViewExtWindows; + + enum UserEvent { + InitSharedBuffer, + PingSharedBuffer, + } + + let event_loop = EventLoopBuilder::::with_user_event().build(); + let proxy = event_loop.create_proxy(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + + let webview = WebViewBuilder::new(&window) + .with_url("https://tauri.app")? + .with_ipc_handler(move |req: String| match req.as_str() { + "initSharedBuffer" => { let _ = proxy.send_event(UserEvent::InitSharedBuffer); } + "pingSharedBuffer" => { let _ = proxy.send_event(UserEvent::PingSharedBuffer); } + _ => {} + }) + .with_initialization_script(r#";(function() { + function writeStringIntoSharedBuffer(string, sharedBuffer, pathPtr) { + const path = new TextEncoder().encode(string) + const pathLen = path.length + const pathArray = new Uint8Array(sharedBuffer, pathPtr, pathLen*8) + for(let i = 0; i < pathLen; i++) { + pathArray[i] = path[i] + } + return [pathPtr, pathLen] + } + + const sharedBufferReceivedHandler = e => { + window.chrome.webview.removeEventListener("sharedbufferreceived", sharedBufferReceivedHandler); + + alert(JSON.stringify(e.additionalData)) + + var sharedBuffer = e.getBuffer() + console.log(sharedBuffer) + window.sharedBuffer = sharedBuffer + + // JS write + writeStringIntoSharedBuffer("I'm JS.", sharedBuffer, 0) + + window.ipc.postMessage('pingSharedBuffer'); + } + window.chrome.webview.addEventListener("sharedbufferreceived", sharedBufferReceivedHandler); + window.ipc.postMessage('initSharedBuffer'); + })();"#) + .build()?; + + // The Webview2 developer tools include a memory inspector, which makes it easy to debug memory issues. + webview.open_devtools(); + + let mut shared_buffer: Option = None; + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => { + *control_flow = ControlFlow::Exit + }, + + Event::UserEvent(e) => match e { + UserEvent::InitSharedBuffer => { + // Memory obtained through webview2 must be manually managed. Use it with care. + shared_buffer = Some(unsafe { webview.create_shared_buffer(1024) }.unwrap()); + if let Some(shared_buffer) = &shared_buffer { + dbg!(shared_buffer); + let _ = unsafe { + webview.post_shared_buffer_to_script( + shared_buffer, + webview2_com::Microsoft::Web::WebView2::Win32::COREWEBVIEW2_SHARED_BUFFER_ACCESS_READ_WRITE, + windows::core::w!(r#"{"jsonkey":"jsonvalue"}"#) + ) + }; + } + }, + UserEvent::PingSharedBuffer => { + if let Some(shared_buffer) = &shared_buffer { + let mut ptr: *mut u8 = &mut 0u8; + let _ = unsafe { shared_buffer.Buffer(&mut ptr) }; + + // Rust read + let len = 8; // align to 4 + let read_string: &mut [u8] = unsafe { std::slice::from_raw_parts_mut(ptr, len) }; + let read_string = std::str::from_utf8(&read_string).unwrap(); + dbg!(read_string); + + // Rust write + let mut vec = String::from("I'm Rust.").into_bytes(); + unsafe { std::ptr::copy((&mut vec).as_mut_ptr(), ptr.offset(len as isize), 9) }; + + let _ = webview.evaluate_script(r#";(function() { + // JS read + alert( + new TextDecoder() + .decode(new Uint8Array(window.sharedBuffer, 8, 9)) + ) + })()"#); + } + } + }, + + _ => (), + } + }); +} + +#[cfg(not(target_os = "windows"))] +fn main() -> wry::Result<()> { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + + let webview = WebViewBuilder::new(&window) + .with_url("https://tauri.app")? + .build()?; + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit + } + }); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index aa4297d93f..869d0c63e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1404,6 +1404,21 @@ pub trait WebViewExtWindows { /// [1]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2memoryusagetargetlevel /// [2]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.memoryusagetargetlevel?view=webview2-dotnet-1.0.2088.41#remarks fn set_memory_usage_level(&self, level: MemoryUsageLevel); + + unsafe fn create_shared_buffer( + &self, + size: u64, + ) -> ::windows::core::Result; + + unsafe fn post_shared_buffer_to_script( + &self, + sharedbuffer: P0, + access: webview2_com::Microsoft::Web::WebView2::Win32::COREWEBVIEW2_SHARED_BUFFER_ACCESS, + additionaldataasjson: P1, + ) -> ::windows::core::Result<()> + where + P0: ::windows::core::IntoParam, + P1: ::windows::core::IntoParam<::windows::core::PCWSTR>; } #[cfg(target_os = "windows")] @@ -1419,6 +1434,26 @@ impl WebViewExtWindows for WebView { fn set_memory_usage_level(&self, level: MemoryUsageLevel) { self.webview.set_memory_usage_level(level); } + + unsafe fn create_shared_buffer( + &self, + size: u64, + ) -> ::windows::core::Result { + self.webview.create_shared_buffer(size) + } + + unsafe fn post_shared_buffer_to_script( + &self, + sharedbuffer: P0, + access: webview2_com::Microsoft::Web::WebView2::Win32::COREWEBVIEW2_SHARED_BUFFER_ACCESS, + additionaldataasjson: P1, + ) -> ::windows::core::Result<()> + where + P0: ::windows::core::IntoParam, + P1: ::windows::core::IntoParam<::windows::core::PCWSTR>, + { + self.webview.post_shared_buffer_to_script(sharedbuffer, access, additionaldataasjson) + } } /// Additional methods on `WebView` that are specific to Linux. diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index cdb53bd4af..942bce4376 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -1092,6 +1092,40 @@ impl InnerWebView { let level = COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL(level); let _ = unsafe { webview.SetMemoryUsageTargetLevel(level) }; } + + pub unsafe fn create_shared_buffer( + &self, + size: u64, + ) -> ::windows::core::Result { + match self.env.cast::() { + Ok(env) => { + env.CreateSharedBuffer(size) + }, + Err(error) => { + Err(error) + } + } + } + + pub unsafe fn post_shared_buffer_to_script( + &self, + sharedbuffer: P0, + access: webview2_com::Microsoft::Web::WebView2::Win32::COREWEBVIEW2_SHARED_BUFFER_ACCESS, + additionaldataasjson: P1, + ) -> ::windows::core::Result<()> + where + P0: ::windows::core::IntoParam, + P1: ::windows::core::IntoParam<::windows::core::PCWSTR>, + { + match self.webview.cast::() { + Ok(webview) => { + webview.PostSharedBufferToScript(sharedbuffer, access, additionaldataasjson) + }, + Err(error) => { + Err(error) + } + } + } } unsafe fn prepare_web_request_response( From bef8483644436caa6120f8005ae5d8a1c61b73bd Mon Sep 17 00:00:00 2001 From: def Date: Tue, 5 Dec 2023 20:13:27 +0800 Subject: [PATCH 2/3] fix import error --- examples/shared_buffer.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/shared_buffer.rs b/examples/shared_buffer.rs index 7339b20219..f2a527a670 100644 --- a/examples/shared_buffer.rs +++ b/examples/shared_buffer.rs @@ -4,7 +4,7 @@ use tao::{ event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, + event_loop::ControlFlow, window::WindowBuilder, }; use wry::WebViewBuilder; @@ -18,7 +18,7 @@ fn main() -> wry::Result<()> { PingSharedBuffer, } - let event_loop = EventLoopBuilder::::with_user_event().build(); + let event_loop = tao::event_loop::EventLoopBuilder::::with_user_event().build(); let proxy = event_loop.create_proxy(); let window = WindowBuilder::new().build(&event_loop).unwrap(); @@ -122,7 +122,7 @@ fn main() -> wry::Result<()> { #[cfg(not(target_os = "windows"))] fn main() -> wry::Result<()> { - let event_loop = EventLoop::new(); + let event_loop = tao::event_loop::EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); let webview = WebViewBuilder::new(&window) From a13ab19b9280e7331bf1bf955f3e258ac7567af1 Mon Sep 17 00:00:00 2001 From: def Date: Tue, 5 Dec 2023 20:18:22 +0800 Subject: [PATCH 3/3] add comment --- examples/shared_buffer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/shared_buffer.rs b/examples/shared_buffer.rs index f2a527a670..1a3bebce0e 100644 --- a/examples/shared_buffer.rs +++ b/examples/shared_buffer.rs @@ -9,6 +9,7 @@ use tao::{ }; use wry::WebViewBuilder; +// Currently, only Windows platforms support shared_buffer. #[cfg(target_os = "windows")] fn main() -> wry::Result<()> { use wry::WebViewExtWindows; @@ -120,12 +121,13 @@ fn main() -> wry::Result<()> { }); } +// Non-Windows systems do not yet support shared_buffer. #[cfg(not(target_os = "windows"))] fn main() -> wry::Result<()> { let event_loop = tao::event_loop::EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); - let webview = WebViewBuilder::new(&window) + let _ = WebViewBuilder::new(&window) .with_url("https://tauri.app")? .build()?;