From 59c1eef0805ecbb1f70a7c78578ff4e03b09a204 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Fri, 15 Nov 2024 05:03:03 +0200 Subject: [PATCH] fix(windows): fix init scripts running twicec (#1418) * fix(windows): fix init scripts running twice closes #2051 * clippy --- .changes/windows-double-init-script.md | 5 +++ src/lib.rs | 33 +++++++++++++++--- src/webview2/mod.rs | 46 +++++++++++++++----------- 3 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 .changes/windows-double-init-script.md diff --git a/.changes/windows-double-init-script.md b/.changes/windows-double-init-script.md new file mode 100644 index 000000000..74027229f --- /dev/null +++ b/.changes/windows-double-init-script.md @@ -0,0 +1,5 @@ +--- +"wry": "patch" +--- + +Fix initialization scripts running twice on Windows. diff --git a/src/lib.rs b/src/lib.rs index 59ea70a41..eb98f3dea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -366,7 +366,6 @@ pub struct WebViewAttributes<'a> { /// /// ## Platform-specific /// - /// - **Windows**: scripts are injected into sub frames. /// - **Android:** The Android WebView does not provide an API for initialization scripts, /// so we prepend them to each HTML head. They are only implemented on custom protocol URLs. pub initialization_scripts: Vec<(String, bool)>, @@ -695,9 +694,22 @@ impl<'a> WebViewBuilder<'a> { /// initialization code will be executed. It is guaranteed that code is executed before /// `window.onload`. /// + /// ## Example + /// ```no_run + /// # use wry::{WebViewBuilder, raw_window_handle, Rect, dpi::*}; + /// # use winit::{window::WindowBuilder, event_loop::EventLoop}; + /// let event_loop = EventLoop::new().unwrap(); + /// let window = WindowBuilder::new().build(&event_loop).unwrap(); + /// + /// let webview = WebViewBuilder::new() + /// .with_initialization_script("console.log('Running inside main frame only')") + /// .with_url("https://tauri.app") + /// .build(&window) + /// .unwrap(); + /// ``` + /// /// ## Platform-specific /// - /// - **Windows:** scripts are added to subframes as well. /// - **Android:** When [addDocumentStartJavaScript] is not supported, /// we prepend them to each HTML head (implementation only supported on custom protocol URLs). /// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts. @@ -710,9 +722,20 @@ impl<'a> WebViewBuilder<'a> { /// Same as [`with_initialization_script`](Self::with_initialization_script) but with option to inject into main frame only or sub frames. /// - /// ## Platform-specific: - /// - /// - **Windows:** scripts are always added to subframes regardless of the option. + /// ## Example + /// ```no_run + /// # use wry::{WebViewBuilder, raw_window_handle, Rect, dpi::*}; + /// # use winit::{window::WindowBuilder, event_loop::EventLoop}; + /// let event_loop = EventLoop::new().unwrap(); + /// let window = WindowBuilder::new().build(&event_loop).unwrap(); + /// + /// let webview = WebViewBuilder::new() + /// .with_initialization_script_for_main_only("console.log('Running inside main frame only')", true) + /// .with_initialization_script_for_main_only("console.log('Running main frame and sub frames')", false) + /// .with_url("https://tauri.app") + /// .build(&window) + /// .unwrap(); + /// ``` pub fn with_initialization_script_for_main_only(self, js: &str, main_only: bool) -> Self { self.and_then(|mut b| { if !js.is_empty() { diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 3c14f1837..d351c202b 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -448,9 +448,13 @@ impl InnerWebView { }; } - // Initialize main frame scripts - for (js, for_main_only) in attributes.initialization_scripts { - Self::add_script_to_execute_on_document_created(&webview, js, for_main_only)?; + // Initialize main and subframe scripts + for (js, _) in attributes + .initialization_scripts + .iter() + .filter(|(_, for_main)| !*for_main) + { + Self::add_script_to_execute_on_document_created(&webview, js.clone())?; } // Enable clipboard @@ -592,12 +596,27 @@ impl InnerWebView { token, )?; } + let scripts = attributes.initialization_scripts.clone(); + + webview.add_ContentLoading( + &ContentLoadingEventHandler::create(Box::new(move |webview, _| { + let Some(webview) = webview else { + return Ok(()); + }; + + for (script, _) in scripts.iter().filter(|(_, for_main)| *for_main) { + Self::execute_script(&webview, script.clone(), |_| ())?; + } + + Ok(()) + })), + token, + )?; // Page load handler if let Some(on_page_load_handler) = attributes.on_page_load_handler.take() { let on_page_load_handler = Rc::new(on_page_load_handler); let on_page_load_handler_ = on_page_load_handler.clone(); - let scripts = attributes.initialization_scripts.clone(); webview.add_ContentLoading( &ContentLoadingEventHandler::create(Box::new(move |webview, _| { @@ -607,12 +626,6 @@ impl InnerWebView { on_page_load_handler_(PageLoadEvent::Started, Self::url_from_webview(&webview)?); - for (script, inject_into_sub_frames) in &scripts { - if *inject_into_sub_frames { - Self::execute_script(&webview, script.clone(), |_| ())?; - } - } - Ok(()) })), token, @@ -773,7 +786,6 @@ impl InnerWebView { String::from( r#"Object.defineProperty(window, 'ipc', { value: Object.freeze({ postMessage: s=> window.chrome.webview.postMessage(s) }) });"#, ), - true, )?; let ipc_handler = attributes.ipc_handler.take(); @@ -817,7 +829,7 @@ impl InnerWebView { attributes: &mut WebViewAttributes, token: &mut EventRegistrationToken, ) -> Result<()> { - for (name, _) in &attributes.custom_protocols { + for name in attributes.custom_protocols.keys() { // WebView2 supports non-standard protocols only on Windows 10+, so we have to use this workaround // See https://github.com/MicrosoftEdge/WebView2Feedback/issues/73 let filter = HSTRING::from(format!("{scheme}://{name}.*")); @@ -1081,7 +1093,7 @@ impl InnerWebView { // adjust for borders let mut pt: POINT = unsafe { std::mem::zeroed() }; - if unsafe { ClientToScreen(hwnd, &mut pt) }.as_bool() == true { + if unsafe { ClientToScreen(hwnd, &mut pt) }.as_bool() { let mut window_rc: RECT = unsafe { std::mem::zeroed() }; if unsafe { GetWindowRect(hwnd, &mut window_rc) }.is_ok() { let top_b = pt.y - window_rc.top; @@ -1186,11 +1198,7 @@ impl InnerWebView { } #[inline] - fn add_script_to_execute_on_document_created( - webview: &ICoreWebView2, - js: String, - _for_main_only: bool, - ) -> Result<()> { + fn add_script_to_execute_on_document_created(webview: &ICoreWebView2, js: String) -> Result<()> { let webview = webview.clone(); AddScriptToExecuteOnDocumentCreatedCompletedHandler::wait_for_async_operation( Box::new(move |handler| unsafe { @@ -1360,7 +1368,7 @@ impl InnerWebView { // adjust for borders let mut pt: POINT = unsafe { std::mem::zeroed() }; - if unsafe { ClientToScreen(parent, &mut pt) }.as_bool() == true { + if unsafe { ClientToScreen(parent, &mut pt) }.as_bool() { let mut window_rc: RECT = unsafe { std::mem::zeroed() }; if unsafe { GetWindowRect(parent, &mut window_rc) }.is_ok() { let top_b = pt.y - window_rc.top;