From 248c849261e89fad7bcf6798b622556498c79d96 Mon Sep 17 00:00:00 2001 From: def Date: Thu, 7 Dec 2023 17:02:14 +0800 Subject: [PATCH] feat: add host_object implement and example --- examples/host_object.rs | 165 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 18 ++++- src/webview2/mod.rs | 18 +++++ 3 files changed, 200 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..c9c14a7833 --- /dev/null +++ b/examples/host_object.rs @@ -0,0 +1,165 @@ +// 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; + +#[cfg(target_os = "windows")] +fn main() -> wry::Result<()> { + + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + + use wry::WebViewExtWindows; + 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; + + // 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 { + 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 aa4297d93f..f191b4d60f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -210,7 +210,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}; @@ -1404,6 +1403,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")] @@ -1419,6 +1426,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 cdb53bd4af..26e0c269dd 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -1092,6 +1092,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(