Skip to content

Commit 7bc77a0

Browse files
Legend-MasterFabianLarslucasfernog
authored
feat: allow all frame init scripts in plugin (#13609)
* feat: allow all frame init scripts in plugin * Add change files * Update crates/tauri/src/plugin.rs Co-authored-by: Fabian-Lars <[email protected]> * Default impl initialization_script_2 from 1 * Update crates/tauri/src/plugin.rs Co-authored-by: Lucas Fernandes Nogueira <[email protected]> --------- Co-authored-by: Fabian-Lars <[email protected]> Co-authored-by: Lucas Fernandes Nogueira <[email protected]>
1 parent 371ee34 commit 7bc77a0

File tree

6 files changed

+140
-47
lines changed

6 files changed

+140
-47
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
tauri: "minor:feat"
3+
---
4+
5+
Added `tauri::plugin::Builder::js_init_script_on_all_frames` that allows plugins to add initialization scripts that runs on all frames
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
tauri: "minor:enhance"
3+
---
4+
5+
`tauri::plugin::Builder::js_init_script` now takes `impl Into<String>` instead of `String`

crates/tauri-runtime/src/webview.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,11 +376,12 @@ impl WebviewAttributes {
376376
/// It is guaranteed that code is executed before `window.onload`.
377377
///
378378
/// This is executed only on the main frame.
379-
/// If you only want to run it in all frames, use [Self::initialization_script_on_all_frames] instead.
379+
/// If you only want to run it in all frames, use [`Self::initialization_script_on_all_frames`] instead.
380380
///
381381
/// ## Platform-specific
382382
///
383-
/// - **Android on Wry:** When [addDocumentStartJavaScript] is not supported,
383+
/// - **Windows:** scripts are always added to subframes.
384+
/// - **Android:** When [addDocumentStartJavaScript] is not supported,
384385
/// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
385386
/// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
386387
///
@@ -401,11 +402,12 @@ impl WebviewAttributes {
401402
/// It is guaranteed that code is executed before `window.onload`.
402403
///
403404
/// This is executed on all frames, main frame and also sub frames.
404-
/// If you only want to run it in the main frame, use [Self::initialization_script] instead.
405+
/// If you only want to run it in the main frame, use [`Self::initialization_script`] instead.
405406
///
406407
/// ## Platform-specific
407408
///
408-
/// - **Android on Wry:** When [addDocumentStartJavaScript] is not supported,
409+
/// - **Windows:** scripts are always added to subframes.
410+
/// - **Android:** When [addDocumentStartJavaScript] is not supported,
409411
/// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
410412
/// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
411413
///

crates/tauri/src/manager/webview.rs

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,21 @@ impl<R: Runtime> WebviewManager<R> {
143143
crate::Pattern::Isolation { schema, .. } => {
144144
crate::pattern::format_real_schema(schema, use_https_scheme)
145145
}
146-
_ => "".to_string(),
146+
_ => "".to_owned(),
147147
},
148148
}
149149
.render_default(&Default::default())?;
150150

151-
let mut all_initialization_scripts: Vec<String> = vec![];
152-
all_initialization_scripts.push(
151+
let mut all_initialization_scripts: Vec<InitializationScript> = vec![];
152+
153+
fn main_frame_script(script: String) -> InitializationScript {
154+
InitializationScript {
155+
script,
156+
for_main_frame_only: true,
157+
}
158+
}
159+
160+
all_initialization_scripts.push(main_frame_script(
153161
r"
154162
Object.defineProperty(window, 'isTauri', {
155163
value: true,
@@ -163,10 +171,10 @@ impl<R: Runtime> WebviewManager<R> {
163171
})
164172
}
165173
"
166-
.to_string(),
167-
);
168-
all_initialization_scripts.push(self.invoke_initialization_script.to_string());
169-
all_initialization_scripts.push(format!(
174+
.to_owned(),
175+
));
176+
all_initialization_scripts.push(main_frame_script(self.invoke_initialization_script.clone()));
177+
all_initialization_scripts.push(main_frame_script(format!(
170178
r#"
171179
Object.defineProperty(window.__TAURI_INTERNALS__, 'metadata', {{
172180
value: {{
@@ -177,45 +185,37 @@ impl<R: Runtime> WebviewManager<R> {
177185
"#,
178186
current_window_label = serde_json::to_string(window_label)?,
179187
current_webview_label = serde_json::to_string(&label)?,
180-
));
181-
all_initialization_scripts.push(self.initialization_script(
188+
)));
189+
all_initialization_scripts.push(main_frame_script(self.initialization_script(
182190
app_manager,
183191
&ipc_init.into_string(),
184192
&pattern_init.into_string(),
185193
use_https_scheme,
186-
)?);
194+
)?));
187195

188-
for plugin_init_script in plugin_init_scripts {
189-
all_initialization_scripts.push(plugin_init_script.to_string());
190-
}
196+
all_initialization_scripts.extend(plugin_init_scripts);
191197

192198
#[cfg(feature = "isolation")]
193199
if let crate::Pattern::Isolation { schema, .. } = &*app_manager.pattern {
194-
all_initialization_scripts.push(
200+
all_initialization_scripts.push(main_frame_script(
195201
IsolationJavascript {
196202
isolation_src: &crate::pattern::format_real_schema(schema, use_https_scheme),
197203
style: tauri_utils::pattern::isolation::IFRAME_STYLE,
198204
}
199205
.render_default(&Default::default())?
200-
.to_string(),
201-
);
206+
.into_string(),
207+
));
202208
}
203209

204210
if let Some(plugin_global_api_scripts) = &*app_manager.plugin_global_api_scripts {
205-
for script in plugin_global_api_scripts.iter() {
206-
all_initialization_scripts.push(script.to_string());
211+
for &script in plugin_global_api_scripts.iter() {
212+
all_initialization_scripts.push(main_frame_script(script.to_owned()));
207213
}
208214
}
209215

210-
webview_attributes.initialization_scripts.splice(
211-
0..0,
212-
all_initialization_scripts
213-
.into_iter()
214-
.map(|script| InitializationScript {
215-
script,
216-
for_main_frame_only: true,
217-
}),
218-
);
216+
// Prepend `all_initialization_scripts` to `webview_attributes.initialization_scripts`
217+
all_initialization_scripts.extend(webview_attributes.initialization_scripts);
218+
webview_attributes.initialization_scripts = all_initialization_scripts;
219219

220220
pending.webview_attributes = webview_attributes;
221221

crates/tauri/src/plugin.rs

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use serde::{
1818
};
1919
use serde_json::Value as JsonValue;
2020
use tauri_macros::default_runtime;
21+
use tauri_runtime::webview::InitializationScript;
2122
use thiserror::Error;
2223
use url::Url;
2324

@@ -50,15 +51,37 @@ pub trait Plugin<R: Runtime>: Send {
5051
/// Add the provided JavaScript to a list of scripts that should be run after the global object has been created,
5152
/// but before the HTML document has been parsed and before any other script included by the HTML document is run.
5253
///
53-
/// Since it runs on all top-level document and child frame page navigations,
54-
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
55-
///
5654
/// The script is wrapped into its own context with `(function () { /* your script here */ })();`,
5755
/// so global variables must be assigned to `window` instead of implicitly declared.
56+
///
57+
/// This is executed only on the main frame.
58+
/// If you only want to run it in all frames, use [`Plugin::initialization_script_2`] to set that to false.
59+
///
60+
/// ## Platform-specific
61+
///
62+
/// - **Windows:** scripts are always added to subframes.
63+
/// - **Android:** When [addDocumentStartJavaScript] is not supported,
64+
/// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
65+
/// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
66+
///
67+
/// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
68+
/// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
5869
fn initialization_script(&self) -> Option<String> {
5970
None
6071
}
6172

73+
// TODO: Change `initialization_script` to this in v3
74+
/// Same as [`Plugin::initialization_script`] but returns an [`InitializationScript`] instead
75+
/// We plan to replace [`Plugin::initialization_script`] with this signature in v3
76+
fn initialization_script_2(&self) -> Option<InitializationScript> {
77+
self
78+
.initialization_script()
79+
.map(|script| InitializationScript {
80+
script,
81+
for_main_frame_only: true,
82+
})
83+
}
84+
6285
/// Callback invoked when the window is created.
6386
#[allow(unused_variables)]
6487
fn window_created(&mut self, window: Window<R>) {}
@@ -242,7 +265,7 @@ pub struct Builder<R: Runtime, C: DeserializeOwned = ()> {
242265
name: &'static str,
243266
invoke_handler: Box<InvokeHandler<R>>,
244267
setup: Option<Box<SetupHook<R, C>>>,
245-
js_init_script: Option<String>,
268+
js_init_script: Option<InitializationScript>,
246269
on_navigation: Box<OnNavigation<R>>,
247270
on_page_load: Box<OnPageLoad<R>>,
248271
on_window_ready: Box<OnWindowReady<R>>,
@@ -305,14 +328,24 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
305328
/// Sets the provided JavaScript to be run after the global object has been created,
306329
/// but before the HTML document has been parsed and before any other script included by the HTML document is run.
307330
///
308-
/// Since it runs on all top-level document and child frame page navigations,
309-
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
310-
///
311331
/// The script is wrapped into its own context with `(function () { /* your script here */ })();`,
312332
/// so global variables must be assigned to `window` instead of implicitly declared.
313333
///
314334
/// Note that calling this function multiple times overrides previous values.
315335
///
336+
/// This is executed only on the main frame.
337+
/// If you only want to run it in all frames, use [`Self::js_init_script_on_all_frames`] instead.
338+
///
339+
/// ## Platform-specific
340+
///
341+
/// - **Windows:** scripts are always added to subframes.
342+
/// - **Android:** When [addDocumentStartJavaScript] is not supported,
343+
/// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
344+
/// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
345+
///
346+
/// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
347+
/// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
348+
///
316349
/// # Examples
317350
///
318351
/// ```rust
@@ -328,13 +361,46 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
328361
///
329362
/// fn init<R: Runtime>() -> TauriPlugin<R> {
330363
/// Builder::new("example")
331-
/// .js_init_script(INIT_SCRIPT.to_string())
364+
/// .js_init_script(INIT_SCRIPT)
332365
/// .build()
333366
/// }
334367
/// ```
335368
#[must_use]
336-
pub fn js_init_script(mut self, js_init_script: String) -> Self {
337-
self.js_init_script = Some(js_init_script);
369+
// TODO: Rename to `initialization_script` in v3
370+
pub fn js_init_script(mut self, js_init_script: impl Into<String>) -> Self {
371+
self.js_init_script = Some(InitializationScript {
372+
script: js_init_script.into(),
373+
for_main_frame_only: true,
374+
});
375+
self
376+
}
377+
378+
/// Sets the provided JavaScript to be run after the global object has been created,
379+
/// but before the HTML document has been parsed and before any other script included by the HTML document is run.
380+
///
381+
/// Since it runs on all top-level document and child frame page navigations,
382+
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
383+
///
384+
/// Note that calling this function multiple times overrides previous values.
385+
///
386+
/// This is executed on all frames, main frame and also sub frames.
387+
/// If you only want to run it in the main frame, use [`Self::js_init_script`] instead.
388+
///
389+
/// ## Platform-specific
390+
///
391+
/// - **Windows:** scripts are always added to subframes.
392+
/// - **Android:** When [addDocumentStartJavaScript] is not supported,
393+
/// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
394+
/// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
395+
///
396+
/// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
397+
/// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
398+
#[must_use]
399+
pub fn js_init_script_on_all_frames(mut self, js_init_script: impl Into<String>) -> Self {
400+
self.js_init_script = Some(InitializationScript {
401+
script: js_init_script.into(),
402+
for_main_frame_only: false,
403+
});
338404
self
339405
}
340406

@@ -695,7 +761,7 @@ pub struct TauriPlugin<R: Runtime, C: DeserializeOwned = ()> {
695761
app: Option<AppHandle<R>>,
696762
invoke_handler: Box<InvokeHandler<R>>,
697763
setup: Option<Box<SetupHook<R, C>>>,
698-
js_init_script: Option<String>,
764+
js_init_script: Option<InitializationScript>,
699765
on_navigation: Box<OnNavigation<R>>,
700766
on_page_load: Box<OnPageLoad<R>>,
701767
on_window_ready: Box<OnWindowReady<R>>,
@@ -751,6 +817,13 @@ impl<R: Runtime, C: DeserializeOwned> Plugin<R> for TauriPlugin<R, C> {
751817
}
752818

753819
fn initialization_script(&self) -> Option<String> {
820+
self
821+
.js_init_script
822+
.clone()
823+
.map(|initialization_script| initialization_script.script)
824+
}
825+
826+
fn initialization_script_2(&self) -> Option<InitializationScript> {
754827
self.js_init_script.clone()
755828
}
756829

@@ -842,12 +915,20 @@ impl<R: Runtime> PluginStore<R> {
842915
}
843916

844917
/// Generates an initialization script from all plugins in the store.
845-
pub(crate) fn initialization_script(&self) -> Vec<String> {
918+
pub(crate) fn initialization_script(&self) -> Vec<InitializationScript> {
846919
self
847920
.store
848921
.iter()
849-
.filter_map(|p| p.initialization_script())
850-
.map(|script| format!("(function () {{ {script} }})();"))
922+
.filter_map(|p| p.initialization_script_2())
923+
.map(
924+
|InitializationScript {
925+
script,
926+
for_main_frame_only,
927+
}| InitializationScript {
928+
script: format!("(function () {{ {script} }})();"),
929+
for_main_frame_only,
930+
},
931+
)
851932
.collect()
852933
}
853934

crates/tauri/src/webview/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ impl<R: Runtime> WebviewBuilder<R> {
638638
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
639639
///
640640
/// This is executed only on the main frame.
641-
/// If you only want to run it in all frames, use [Self::initialization_script_for_all_frames] instead.
641+
/// If you only want to run it in all frames, use [`Self::initialization_script_for_all_frames`] instead.
642642
///
643643
/// ## Platform-specific
644644
///
@@ -698,7 +698,7 @@ fn main() {
698698
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
699699
///
700700
/// This is executed on all frames (main frame and also sub frames).
701-
/// If you only want to run the script in the main frame, use [Self::initialization_script] instead.
701+
/// If you only want to run the script in the main frame, use [`Self::initialization_script`] instead.
702702
///
703703
/// ## Platform-specific
704704
///

0 commit comments

Comments
 (0)