From 1c2197530f56a3a903c156db734e5751e18b0123 Mon Sep 17 00:00:00 2001 From: yydcnjjw Date: Thu, 17 Aug 2023 17:18:38 +0800 Subject: [PATCH] feat: webview2 composition support --- src/lib.rs | 12 ++- src/webview2/mod.rs | 185 +++++++++++++++++++++----------------------- 2 files changed, 98 insertions(+), 99 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 114c1f0609..d3cbafd1c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1091,6 +1091,7 @@ pub(crate) struct PlatformSpecificWebViewAttributes { theme: Option, use_https: bool, scroll_bar_style: ScrollBarStyle, + composition: bool, } #[cfg(windows)] @@ -1102,6 +1103,7 @@ impl Default for PlatformSpecificWebViewAttributes { theme: None, use_https: false, // To match macOS & Linux behavior in the context of mixed content. scroll_bar_style: ScrollBarStyle::default(), + composition: false, } } } @@ -1153,6 +1155,7 @@ pub trait WebViewBuilderExtWindows { /// Requires WebView2 Runtime version 125.0.2535.41 or higher, does nothing on older versions, /// see https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/?tabs=dotnetcsharp#10253541 fn with_scroll_bar_style(self, style: ScrollBarStyle) -> Self; + fn with_composition(self, enabled: bool) -> Self; } #[cfg(windows)] @@ -1181,6 +1184,11 @@ impl WebViewBuilderExtWindows for WebViewBuilder<'_> { self.platform_specific.scroll_bar_style = style; self } + + fn with_composition(mut self, enabled: bool) -> Self { + self.platform_specific.composition = enabled; + self + } } #[cfg(target_os = "android")] @@ -1190,7 +1198,7 @@ pub(crate) struct PlatformSpecificWebViewAttributes { Option std::result::Result<(), jni::errors::Error> + Send>>, with_asset_loader: bool, asset_loader_domain: Option, - https_scheme: bool, + use_https: bool, } #[cfg(target_os = "android")] @@ -1249,7 +1257,7 @@ impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { } fn with_https_scheme(mut self, enabled: bool) -> Self { - self.platform_specific.https_scheme = enabled; + self.platform_specific.use_https = enabled; self } } diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index ff2fdb463e..2b8bcbea67 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -50,9 +50,8 @@ impl From for Error { } pub(crate) struct InnerWebView { - parent: RefCell, + parent: Option>, hwnd: HWND, - is_child: bool, pub controller: ICoreWebView2Controller, webview: ICoreWebView2, env: ICoreWebView2Environment, @@ -65,10 +64,10 @@ pub(crate) struct InnerWebView { impl Drop for InnerWebView { fn drop(&mut self) { let _ = unsafe { self.controller.Close() }; - if self.is_child { + if let Some(parent) = &self.parent { let _ = unsafe { DestroyWindow(self.hwnd) }; + unsafe { Self::dettach_parent_subclass(*parent.borrow()) } } - unsafe { Self::dettach_parent_subclass(*self.parent.borrow()) } } } @@ -84,7 +83,18 @@ impl InnerWebView { RawWindowHandle::Win32(window) => HWND(window.hwnd.get() as _), _ => return Err(Error::UnsupportedWindowHandle), }; - Self::new_in_hwnd(window, attributes, pl_attrs, web_context, false) + + let this = Self::new_in_hwnd(window, attributes, pl_attrs, web_context)?; + + let mut rect = RECT::default(); + unsafe { GetClientRect(this.hwnd, &mut rect)? }; + let width = rect.right - rect.left; + let height = rect.bottom - rect.top; + + this.set_bounds_inner((width, height).into(), (0, 0).into())?; + + unsafe { Self::attach_parent_subclass(this.hwnd, &this.controller) }; + Ok(this) } #[inline] @@ -99,63 +109,49 @@ impl InnerWebView { _ => return Err(Error::UnsupportedWindowHandle), }; - Self::new_in_hwnd(parent, attributes, pl_attrs, web_context, true) + let hwnd = Self::create_child_container_hwnd(parent, &attributes)?; + + let bounds = attributes.bounds.clone(); + + let mut this = Self::new_in_hwnd(hwnd, attributes, pl_attrs, web_context)?; + this.parent = Some(RefCell::new(parent)); + + this.set_bounds(bounds.unwrap_or_default())?; + Ok(this) } #[inline] fn new_in_hwnd( - parent: HWND, + hwnd: HWND, mut attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, - is_child: bool, ) -> Result { let _ = unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED) }; - let hwnd = Self::create_container_hwnd(parent, &attributes, is_child)?; - let drop_handler = attributes.drag_drop_handler.take(); - let bounds = attributes.bounds; let env = Self::create_environment(&web_context, pl_attrs.clone(), &attributes)?; - let controller = Self::create_controller(hwnd, &env, attributes.incognito)?; - let webview = Self::init_webview( - parent, - hwnd, - attributes, - &env, - &controller, - pl_attrs, - is_child, - )?; + let controller = + Self::create_controller(hwnd, &env, attributes.incognito, pl_attrs.composition)?; + let webview = Self::init_webview(hwnd, attributes, &env, &controller, pl_attrs)?; let drag_drop_controller = drop_handler.map(|handler| DragDropController::new(hwnd, handler)); let w = Self { - parent: RefCell::new(parent), + parent: None, hwnd, controller, - is_child, webview, env, drag_drop_controller, }; - if is_child { - w.set_bounds(bounds.unwrap_or_default())?; - } else { - w.resize_to_parent()?; - } - Ok(w) } #[inline] - fn create_container_hwnd( - parent: HWND, - attributes: &WebViewAttributes, - is_child: bool, - ) -> Result { + fn create_child_container_hwnd(parent: HWND, attributes: &WebViewAttributes) -> Result { unsafe extern "system" fn default_window_proc( hwnd: HWND, msg: u32, @@ -192,7 +188,7 @@ impl InnerWebView { let dpi = unsafe { util::hwnd_dpi(parent) }; let scale_factor = util::dpi_to_scale_factor(dpi); - let (x, y, width, height) = if is_child { + let (x, y, width, height) = { let (x, y) = attributes .bounds .map(|b| b.position.to_physical::(scale_factor)) @@ -205,12 +201,6 @@ impl InnerWebView { .unwrap_or((CW_USEDEFAULT, CW_USEDEFAULT)); (x, y, width, height) - } else { - let mut rect = RECT::default(); - unsafe { GetClientRect(parent, &mut rect)? }; - let width = rect.right - rect.left; - let height = rect.bottom - rect.top; - (0, 0, width, height) }; let hwnd = unsafe { @@ -330,50 +320,80 @@ impl InnerWebView { hwnd: HWND, env: &ICoreWebView2Environment, incognito: bool, + composition: bool, ) -> Result { let (tx, rx) = mpsc::channel(); let env = env.clone(); let env10 = env.cast::(); - CreateCoreWebView2ControllerCompletedHandler::wait_for_async_operation( - if let Ok(env10) = env10 { - let controller_opts = unsafe { env10.CreateCoreWebView2ControllerOptions()? }; - unsafe { controller_opts.SetIsInPrivateModeEnabled(incognito)? } - Box::new( - move |handler: ICoreWebView2CreateCoreWebView2ControllerCompletedHandler| unsafe { + if composition { + CreateCoreWebView2CompositionControllerCompletedHandler::wait_for_async_operation( + if let Ok(env10) = env10 { + let controller_opts = unsafe { env10.CreateCoreWebView2ControllerOptions()? }; + unsafe { controller_opts.SetIsInPrivateModeEnabled(incognito)? } + Box::new(move |handler| unsafe { env10 - .CreateCoreWebView2ControllerWithOptions(hwnd, &controller_opts, &handler) + .CreateCoreWebView2CompositionControllerWithOptions(hwnd, &controller_opts, &handler) .map_err(Into::into) - }, - ) - } else { - Box::new( - move |handler: ICoreWebView2CreateCoreWebView2ControllerCompletedHandler| unsafe { - env - .CreateCoreWebView2Controller(hwnd, &handler) + }) + } else if let Ok(env3) = env.cast::() { + Box::new(move |handler| unsafe { + env3 + .CreateCoreWebView2CompositionController(hwnd, &handler) .map_err(Into::into) - }, - ) - }, - Box::new(move |error_code, controller| { - error_code?; - tx.send(controller.ok_or_else(|| windows::core::Error::from(E_POINTER))) + }) + } else { + return Err(windows::core::Error::from(E_UNEXPECTED).into()); + }, + Box::new(move |error_code, controller| { + error_code?; + tx.send( + controller + .ok_or_else(|| windows::core::Error::from(E_POINTER)) + .and_then(|c| c.cast::()), + ) .map_err(|_| windows::core::Error::from(E_UNEXPECTED)) - }), - )?; + }), + )?; + } else { + CreateCoreWebView2ControllerCompletedHandler::wait_for_async_operation( + if let Ok(env10) = env10 { + let controller_opts = unsafe { env10.CreateCoreWebView2ControllerOptions()? }; + unsafe { controller_opts.SetIsInPrivateModeEnabled(incognito)? } + Box::new( + move |handler: ICoreWebView2CreateCoreWebView2ControllerCompletedHandler| unsafe { + env10 + .CreateCoreWebView2ControllerWithOptions(hwnd, &controller_opts, &handler) + .map_err(Into::into) + }, + ) + } else { + Box::new( + move |handler: ICoreWebView2CreateCoreWebView2ControllerCompletedHandler| unsafe { + env + .CreateCoreWebView2Controller(hwnd, &handler) + .map_err(Into::into) + }, + ) + }, + Box::new(move |error_code, controller| { + error_code?; + tx.send(controller.ok_or_else(|| windows::core::Error::from(E_POINTER))) + .map_err(|_| windows::core::Error::from(E_UNEXPECTED)) + }), + )?; + } rx.recv()?.map_err(Into::into) } #[inline] fn init_webview( - parent: HWND, hwnd: HWND, mut attributes: WebViewAttributes, env: &ICoreWebView2Environment, controller: &ICoreWebView2Controller, pl_attrs: super::PlatformSpecificWebViewAttributes, - is_child: bool, ) -> Result { let webview = unsafe { controller.CoreWebView2()? }; @@ -484,11 +504,6 @@ impl InnerWebView { unsafe { webview.NavigateToString(&html)? }; } - // Subclass parent for resizing and focus - if !is_child { - unsafe { Self::attach_parent_subclass(parent, controller) }; - } - unsafe { controller.SetIsVisible(attributes.visible)?; @@ -1056,7 +1071,7 @@ impl InnerWebView { }); let mut hwnd = HWND::default(); - if (*controller).ParentWindow(&mut hwnd).is_ok() { + if (*controller).ParentWindow(&mut hwnd).is_err() { let _ = SetWindowPos( hwnd, HWND::default(), @@ -1207,14 +1222,14 @@ impl InnerWebView { pub fn bounds(&self) -> Result { let mut bounds = Rect::default(); let mut rect = RECT::default(); - if self.is_child { + if let Some(parent) = &self.parent { unsafe { GetClientRect(self.hwnd, &mut rect)? }; let position_point = &mut [POINT { x: rect.left, y: rect.top, }]; - unsafe { MapWindowPoints(self.hwnd, *self.parent.borrow(), position_point) }; + unsafe { MapWindowPoints(self.hwnd, *parent.borrow(), position_point) }; bounds.position = PhysicalPosition::new(position_point[0].x, position_point[0].y).into(); } else { @@ -1262,15 +1277,6 @@ impl InnerWebView { Ok(()) } - fn resize_to_parent(&self) -> crate::Result<()> { - let mut rect = RECT::default(); - unsafe { GetClientRect(*self.parent.borrow(), &mut rect)? }; - let width = rect.right - rect.left; - let height = rect.bottom - rect.top; - - self.set_bounds_inner((width, height).into(), (0, 0).into()) - } - pub fn set_visible(&self, visible: bool) -> Result<()> { unsafe { let _ = ShowWindow( @@ -1301,21 +1307,6 @@ impl InnerWebView { unsafe { SetParent(self.hwnd, parent)?; - - if !self.is_child { - Self::dettach_parent_subclass(*self.parent.borrow()); - Self::attach_parent_subclass(parent, &self.controller); - - *self.parent.borrow_mut() = parent; - - let mut rect = RECT::default(); - GetClientRect(parent, &mut rect)?; - - let width = rect.right - rect.left; - let height = rect.bottom - rect.top; - - self.set_bounds_inner((width, height).into(), (0, 0).into())?; - } } Ok(())