Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(windows): fix init scripts running twicec #1418

Merged
merged 2 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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