From cfc327668c85b49117994d494e92e56a46225296 Mon Sep 17 00:00:00 2001 From: def Date: Fri, 1 Dec 2023 13:13:08 +0800 Subject: [PATCH 1/3] add host object implement and example --- examples/host_object.rs | 161 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 18 ++++- src/webview2/mod.rs | 18 +++++ 3 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 examples/host_object.rs diff --git a/examples/host_object.rs b/examples/host_object.rs new file mode 100644 index 0000000000..ea769c73dd --- /dev/null +++ b/examples/host_object.rs @@ -0,0 +1,161 @@ +// 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, EventLoop}, + window::WindowBuilder, +}; +use wry::{WebViewBuilder, WebViewExtWindows}; + +#[cfg(target_os = "windows")] +fn main() -> wry::Result<()> { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + + use windows::{ + core::{w, BSTR}, + Win32::{ + System::Variant::{ + VARIANT, VARIANT_0, VARIANT_0_0, VARIANT_0_0_0, VARENUM, + VT_BSTR, VT_I4, VT_DISPATCH, + }, + System::Com::{ + IDispatch, IDispatch_Impl, ITypeInfo, + DISPATCH_FLAGS, DISPPARAMS, EXCEPINFO, + } + } + }; + use std::mem::ManuallyDrop; + struct Variant(VARIANT); + impl Variant { + pub fn new(num: VARENUM, contents: VARIANT_0_0_0) -> Variant { + Variant { + 0: VARIANT { + Anonymous: VARIANT_0 { + Anonymous: ManuallyDrop::new(VARIANT_0_0 { + vt: num, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: contents, + }), + }, + }, + } + } + } + impl From for Variant { + fn from(value: String) -> Variant { Variant::new( + VT_BSTR, + VARIANT_0_0_0 { + bstrVal: ManuallyDrop::new(BSTR::from(value)) + } + ) } + } + impl From<&str> for Variant { + fn from(value: &str) -> Variant { Variant::from(value.to_string()) } + } + impl From for Variant { + fn from(value: i32) -> Variant { Variant::new(VT_I4, VARIANT_0_0_0 { lVal: value }) } + } + impl From>> for Variant { + fn from(value: std::mem::ManuallyDrop<::core::option::Option>) -> Variant { Variant::new(VT_DISPATCH, VARIANT_0_0_0 { pdispVal: value }) } + } + impl Drop for Variant { + fn drop(&mut self) { + match VARENUM(unsafe { self.0.Anonymous.Anonymous.vt.0 }) { + VT_BSTR => unsafe { + drop(&mut &self.0.Anonymous.Anonymous.Anonymous.bstrVal) + } + _ => {} + } + unsafe { drop(&mut self.0.Anonymous.Anonymous) } + } + } + #[windows::core::implement(IDispatch)] + struct FunctionWithStringArgument; + impl IDispatch_Impl for FunctionWithStringArgument { + #![allow(non_snake_case)] + fn GetTypeInfoCount(&self) -> windows::core::Result {Ok(0)} + fn GetTypeInfo(&self, _itinfo: u32, _lcid: u32) -> windows::core::Result {Err(windows::core::Error::new(windows::Win32::Foundation + ::E_FAIL, "GetTypeInfo Error \t\n\r".into()))} + fn GetIDsOfNames(&self, _riid: *const ::windows::core::GUID, _rgsznames: *const ::windows::core::PCWSTR, _cnames: u32, _lcid: u32, _rgdispid: *mut i32) -> windows::core::Result<()> {Ok(())} + fn Invoke( + &self, + _dispidmember: i32, + _riid: *const windows::core::GUID, + _lcid: u32, + _wflags: DISPATCH_FLAGS, + pdispparams: *const DISPPARAMS, + pvarresult: *mut VARIANT, + _pexcepinfo: *mut EXCEPINFO, + _puargerr: *mut u32 + ) -> windows::core::Result<()> { + let pdispparams = unsafe { *pdispparams }; + let rgvarg = unsafe { &*(pdispparams.rgvarg) }; + let rgvarg_0_0 = unsafe { &rgvarg.Anonymous.Anonymous }; + unsafe { dbg!(&rgvarg_0_0.Anonymous.bstrVal); } + let b_str_val = unsafe { &rgvarg_0_0.Anonymous.bstrVal.to_string() }; + dbg!(b_str_val); + + let pvarresult_0_0 = unsafe { &mut (*pvarresult).Anonymous.Anonymous }; + pvarresult_0_0.vt = VT_BSTR; + pvarresult_0_0.Anonymous.bstrVal = ManuallyDrop::new(BSTR::from(format!(r#"Successful sync call functionWithStringArgument, and the argument is "{}"."#, b_str_val).to_string())) ; + Ok(()) + } + } + let mut i32_variant = Variant::from(1234); + let mut string_variant = Variant::from("string variant"); + let mut function_with_string_argument_variant = Variant::from(ManuallyDrop::new(Some(IDispatch::from(FunctionWithStringArgument)))); + + let webview = WebViewBuilder::new(&window) + .with_url("https://tauri.app")? + .with_initialization_script(r#" + alert(chrome.webview.hostObjects.sync.i32) + alert(chrome.webview.hostObjects.sync.string) + alert(chrome.webview.hostObjects.sync.functionWithStringArgument("hi")) + "#) + .build()?; + + unsafe { + let _ = webview.add_host_object_to_script(w!("i32"), &mut i32_variant.0); + let _ = webview.add_host_object_to_script(w!("string"), &mut string_variant.0); + let _ = webview.add_host_object_to_script(w!("functionWithStringArgument"), &mut function_with_string_argument_variant.0); + } + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit + } + }); +} + +#[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 a0b71f9d3b..ce41b36dbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -212,7 +212,6 @@ pub(crate) mod webview2; use self::webview2::*; #[cfg(target_os = "windows")] use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; - use std::{borrow::Cow, path::PathBuf, rc::Rc}; use http::{Request, Response}; @@ -1402,6 +1401,14 @@ 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 add_host_object_to_script( + &self, + name: P0, + object: *mut ::windows::Win32::System::Variant::VARIANT, + ) -> ::windows::core::Result<()> + where + P0: ::windows::core::IntoParam<::windows::core::PCWSTR>; } #[cfg(target_os = "windows")] @@ -1417,6 +1424,15 @@ impl WebViewExtWindows for WebView { fn set_memory_usage_level(&self, level: MemoryUsageLevel) { self.webview.set_memory_usage_level(level); } + + unsafe fn add_host_object_to_script( + &self, + name: P0, + object: *mut ::windows::Win32::System::Variant::VARIANT, + ) -> ::windows::core::Result<()> where P0: ::windows::core::IntoParam<::windows::core::PCWSTR>, + { + self.webview.add_host_object_to_script(name, object) + } } /// Additional methods on `WebView` that are specific to Linux. diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 3fd954d907..3c3d4d1299 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -1061,6 +1061,24 @@ impl InnerWebView { let level = COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL(level); let _ = unsafe { webview.SetMemoryUsageTargetLevel(level) }; } + + pub unsafe fn add_host_object_to_script( + &self, + name: P0, + object: *mut ::windows::Win32::System::Variant::VARIANT, + ) -> ::windows::core::Result<()> + where + P0: ::windows::core::IntoParam<::windows::core::PCWSTR>, + { + match self.webview.cast::() { + Ok(webview) => { + webview.AddHostObjectToScript(name, object) + }, + Err(error) => { + Err(error) + } + } + } } unsafe fn prepare_web_request_response( From bd6f0bb9abcd0fd1b4174e1237e734a2058f9462 Mon Sep 17 00:00:00 2001 From: def Date: Mon, 4 Dec 2023 22:02:26 +0800 Subject: [PATCH 2/3] fix build error --- examples/host_object.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/host_object.rs b/examples/host_object.rs index ea769c73dd..cd9ee8ca90 100644 --- a/examples/host_object.rs +++ b/examples/host_object.rs @@ -7,10 +7,12 @@ use tao::{ event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; -use wry::{WebViewBuilder, WebViewExtWindows}; +use wry::WebViewBuilder; #[cfg(target_os = "windows")] fn main() -> wry::Result<()> { + use wry::WebViewExtWindows; + let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); From 214b53b3365c599b6babf8cceb7e23be48d775dc Mon Sep 17 00:00:00 2001 From: def Date: Mon, 4 Dec 2023 22:16:57 +0800 Subject: [PATCH 3/3] add comments --- examples/host_object.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/host_object.rs b/examples/host_object.rs index cd9ee8ca90..c9c14a7833 100644 --- a/examples/host_object.rs +++ b/examples/host_object.rs @@ -11,11 +11,11 @@ use wry::WebViewBuilder; #[cfg(target_os = "windows")] fn main() -> wry::Result<()> { - use wry::WebViewExtWindows; let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); + use wry::WebViewExtWindows; use windows::{ core::{w, BSTR}, Win32::{ @@ -30,6 +30,8 @@ fn main() -> wry::Result<()> { } }; use std::mem::ManuallyDrop; + + // This is a simple usage example. add_host_object_to_script is a mapping of the native [addHostObjectToScript](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2#addhostobjecttoscript) method of webview2. It requires manual creation of hostobject and memory management. Please use it with caution. struct Variant(VARIANT); impl Variant { pub fn new(num: VARENUM, contents: VARIANT_0_0_0) -> Variant {