Skip to content

Commit

Permalink
fix(windows): fix init scripts running twicec (#1418)
Browse files Browse the repository at this point in the history
* fix(windows): fix init scripts running twice

closes #2051

* clippy
  • Loading branch information
amrbashir authored Nov 15, 2024
1 parent 08f3c68 commit 59c1eef
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changes/windows-double-init-script.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": "patch"
---

Fix initialization scripts running twice on Windows.
33 changes: 28 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)>,
Expand Down Expand Up @@ -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.
Expand All @@ -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() {
Expand Down
46 changes: 27 additions & 19 deletions src/webview2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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, _| {
Expand All @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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}.*"));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 59c1eef

Please sign in to comment.