Skip to content
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
9 changes: 9 additions & 0 deletions .changes/init-script-on-all-frames.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
tauri: minor:feat
tauri-runtime: minor:feat
---

- add API to run initialization scripts on all frames
- `WebviewBuilder::initialization_script_on_all_frames`
- `WebviewWindowBuilder::initialization_script_on_all_frames`
- `WebviewAttributes::initialization_script_on_all_frames`
3 changes: 2 additions & 1 deletion crates/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4557,7 +4557,8 @@ fn create_webview<T: UserEvent>(
));

for script in webview_attributes.initialization_scripts {
webview_builder = webview_builder.with_initialization_script(&script);
webview_builder = webview_builder
.with_initialization_script_for_main_only(&script.script, script.for_main_frame_only);
}

for (scheme, protocol) in uri_scheme_protocols {
Expand Down
68 changes: 65 additions & 3 deletions crates/tauri-runtime/src/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,15 @@ impl<T: UserEvent, R: Runtime<T>> PartialEq for DetachedWebview<T, R> {
pub struct WebviewAttributes {
pub url: WebviewUrl,
pub user_agent: Option<String>,
pub initialization_scripts: Vec<String>,
/// A list of initialization javascript scripts to run when loading new pages.
/// When webview load a new page, this initialization code will be executed.
/// It is guaranteed that code is executed before `window.onload`.
///
/// ## Platform-specific
///
/// - **Android on Wry:** 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<InitializationScript>,
Comment thread
Legend-Master marked this conversation as resolved.
pub data_directory: Option<PathBuf>,
pub drag_drop_handler_enabled: bool,
pub clipboard: bool,
Expand Down Expand Up @@ -307,10 +315,46 @@ impl WebviewAttributes {
self
}

/// Sets the init script.
/// Adds an init script for the main frame.
///
/// When webview load a new page, this initialization code will be executed.
/// It is guaranteed that code is executed before `window.onload`.
///
/// This is executed only on the main frame.
/// If you only want to run it in all frames, use [Self::initialization_script_on_all_frames] instead.
///
///
/// ## Platform-specific
///
/// - **Android on Wry:** 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.
#[must_use]
pub fn initialization_script(mut self, script: &str) -> Self {
self.initialization_scripts.push(script.to_string());
self.initialization_scripts.push(InitializationScript {
script: script.to_string(),
for_main_frame_only: true,
});
self
}

/// Adds an init script for all frames.
///
/// When webview load a new page, this initialization code will be executed.
/// It is guaranteed that code is executed before `window.onload`.
///
/// This is executed on all frames, main frame and also sub frames.
/// If you only want to run it in the main frame, use [Self::initialization_script] instead.
///
/// ## Platform-specific
///
/// - **Android on Wry:** 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.
#[must_use]
pub fn initialization_script_on_all_frames(mut self, script: &str) -> Self {
self.initialization_scripts.push(InitializationScript {
script: script.to_string(),
for_main_frame_only: false,
});
self
}

Expand Down Expand Up @@ -499,3 +543,21 @@ impl WebviewAttributes {

/// IPC handler.
pub type WebviewIpcHandler<T, R> = Box<dyn Fn(DetachedWebview<T, R>, Request<String>) + Send>;

/// An initialization script
#[derive(Debug, Clone)]
pub struct InitializationScript {
/// The script to run
pub script: String,
/// Whether the script should be injected to main frame only
pub for_main_frame_only: bool,
}

impl InitializationScript {
pub fn new(script: &str, for_main_frame_only: bool) -> Self {
Self {
script: script.to_owned(),
for_main_frame_only,
}
}
}
32 changes: 21 additions & 11 deletions crates/tauri/src/manager/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::{
use serde::Serialize;
use serialize_to_javascript::{default_template, DefaultTemplate, Template};
use tauri_runtime::{
webview::{DetachedWebview, PendingWebview},
webview::{DetachedWebview, InitializationScript, PendingWebview},
window::DragDropEvent,
};
use tauri_utils::config::WebviewUrl;
Expand Down Expand Up @@ -211,9 +211,15 @@ impl<R: Runtime> WebviewManager<R> {
}
}

webview_attributes
.initialization_scripts
.splice(0..0, all_initialization_scripts);
webview_attributes.initialization_scripts.splice(
0..0,
all_initialization_scripts
.into_iter()
.map(|script| InitializationScript {
script,
for_main_frame_only: true,
}),
);

pending.webview_attributes = webview_attributes;

Expand Down Expand Up @@ -527,13 +533,17 @@ impl<R: Runtime> WebviewManager<R> {
os_name: &'a str,
}

pending.webview_attributes.initialization_scripts.push(
HotkeyZoom {
os_name: std::env::consts::OS,
}
.render_default(&Default::default())?
.into_string(),
)
pending
.webview_attributes
.initialization_scripts
.push(InitializationScript {
script: HotkeyZoom {
os_name: std::env::consts::OS,
}
.render_default(&Default::default())?
.into_string(),
for_main_frame_only: true,
})
}

#[cfg(feature = "isolation")]
Expand Down
62 changes: 59 additions & 3 deletions crates/tauri/src/webview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use tauri_runtime::{
WindowDispatch,
};
use tauri_runtime::{
webview::{DetachedWebview, PendingWebview, WebviewAttributes},
webview::{DetachedWebview, InitializationScript, PendingWebview, WebviewAttributes},
WebviewDispatch,
};
pub use tauri_utils::config::Color;
Expand Down Expand Up @@ -634,9 +634,12 @@ impl<R: Runtime> WebviewBuilder<R> {
/// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,
/// but before the HTML document has been parsed and before any other script included by the HTML document is run.
///
/// Since it runs on all top-level document and child frame page navigations,
/// Since it runs on all top-level document navigations,
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
///
/// This is executed only on the main frame.
/// If you only want to run it in all frames, use [Self::initialization_script_for_all_frames] instead.
///
/// # Examples
///
#[cfg_attr(
Expand Down Expand Up @@ -671,7 +674,60 @@ fn main() {
self
.webview_attributes
.initialization_scripts
.push(script.to_string());
.push(InitializationScript {
script: script.to_string(),
for_main_frame_only: true,
});
self
}

/// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,
/// but before the HTML document has been parsed and before any other script included by the HTML document is run.
///
/// Since it runs on all top-level document navigations and also child frame page navigations,
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
///
/// This is executed on all frames, main frame and also sub frames.
/// If you only want to run it in the main frame, use [Self::initialization_script] instead.
///
/// # Examples
///
#[cfg_attr(
feature = "unstable",
doc = r####"
```rust
use tauri::{WindowBuilder, Runtime};

const INIT_SCRIPT: &str = r#"
if (window.location.origin === 'https://tauri.app') {
console.log("hello world from js init script");

window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
}
"#;

fn main() {
tauri::Builder::default()
.setup(|app| {
let window = tauri::window::WindowBuilder::new(app, "label").build()?;
let webview_builder = tauri::webview::WebviewBuilder::new("label", tauri::WebviewUrl::App("index.html".into()))
.initialization_script_for_all_frames(INIT_SCRIPT);
let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?;
Ok(())
});
}
```
"####
)]
#[must_use]
pub fn initialization_script_for_all_frames(mut self, script: &str) -> Self {
self
.webview_attributes
.initialization_scripts
.push(InitializationScript {
script: script.to_string(),
for_main_frame_only: false,
});
self
}

Expand Down
44 changes: 43 additions & 1 deletion crates/tauri/src/webview/webview_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,9 +765,12 @@ impl<R: Runtime, M: Manager<R>> WebviewWindowBuilder<'_, R, M> {
/// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,
/// but before the HTML document has been parsed and before any other script included by the HTML document is run.
///
/// Since it runs on all top-level document and child frame page navigations,
/// Since it runs on all top-level document navigations (and also child frame page navigations, if you set `run_only_on_main_frame` to false),
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
///
/// This is executed only on the main frame.
/// If you only want to run it in all frames, use [Self::initialization_script_for_all_frames] instead.
///
/// # Examples
///
/// ```rust
Expand Down Expand Up @@ -797,6 +800,45 @@ impl<R: Runtime, M: Manager<R>> WebviewWindowBuilder<'_, R, M> {
self
}

/// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,
/// but before the HTML document has been parsed and before any other script included by the HTML document is run.
///
/// Since it runs on all top-level document navigastions (and also child frame page navigations, if you set `run_only_on_main_frame` to false),
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
///
/// This is executed on all frames, main frame and also sub frames.
/// If you only want to run it in the main frame, use [Self::initialization_script] instead.
/// # Examples
///
/// ```rust
/// use tauri::{WebviewWindowBuilder, Runtime};
///
/// const INIT_SCRIPT: &str = r#"
/// if (window.location.origin === 'https://tauri.app') {
/// console.log("hello world from js init script");
///
/// window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
/// }
/// "#;
///
/// fn main() {
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()))
/// .initialization_script_for_all_frames(INIT_SCRIPT)
/// .build()?;
/// Ok(())
/// });
/// }
/// ```
#[must_use]
pub fn initialization_script_for_all_frames(mut self, script: &str) -> Self {
self.webview_builder = self
.webview_builder
.initialization_script_for_all_frames(script);
self
}

/// Set the user agent for the webview
#[must_use]
pub fn user_agent(mut self, user_agent: &str) -> Self {
Expand Down