Skip to content

Commit 74d2464

Browse files
authored
refactor(core): implement on_page_load event using wry hook (#8116)
1 parent 2b6ffe0 commit 74d2464

File tree

19 files changed

+305
-198
lines changed

19 files changed

+305
-198
lines changed

.changes/on-page-load-hook.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-runtime": patch:feat
3+
"tauri-runtime-wry": patch:feat
4+
---
5+
6+
Added `on_page_load` hook for `PendingWindow`.

.changes/refactor-on-page-load.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri": patch:breaking
3+
---
4+
5+
Added `WindowBuilder::on_page_load` and refactored the `Builder::on_page_load` handler to take references.
6+
The page load hook is now triggered for load started and finished events, to determine what triggered it see `PageLoadPayload::event`.

core/tauri-config-schema/schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1198,7 +1198,7 @@
11981198
}
11991199
},
12001200
"name": {
1201-
"description": "The name. Maps to `CFBundleTypeName` on macOS. Default to ext[0]",
1201+
"description": "The name. Maps to `CFBundleTypeName` on macOS. Default to `ext[0]`",
12021202
"type": [
12031203
"string",
12041204
"null"

core/tauri-runtime-wry/src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,6 +2725,21 @@ fn create_webview<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
27252725
.unwrap_or(true)
27262726
});
27272727
}
2728+
2729+
if let Some(page_load_handler) = pending.on_page_load_handler {
2730+
webview_builder = webview_builder.with_on_page_load_handler(move |event, url| {
2731+
let _ = Url::parse(&url).map(|url| {
2732+
page_load_handler(
2733+
url,
2734+
match event {
2735+
wry::webview::PageLoadEvent::Started => tauri_runtime::window::PageLoadEvent::Started,
2736+
wry::webview::PageLoadEvent::Finished => tauri_runtime::window::PageLoadEvent::Finished,
2737+
},
2738+
)
2739+
});
2740+
});
2741+
}
2742+
27282743
if let Some(user_agent) = webview_attributes.user_agent {
27292744
webview_builder = webview_builder.with_user_agent(&user_agent);
27302745
}

core/tauri-runtime/src/window.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ type WebResourceRequestHandler =
3434

3535
type NavigationHandler = dyn Fn(&Url) -> bool + Send;
3636

37+
type OnPageLoadHandler = dyn Fn(Url, PageLoadEvent) + Send;
38+
39+
/// Kind of event for the page load handler.
40+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41+
pub enum PageLoadEvent {
42+
/// Page started to load.
43+
Started,
44+
/// Page finished loading.
45+
Finished,
46+
}
47+
3748
/// UI scaling utilities.
3849
pub mod dpi;
3950

@@ -238,6 +249,8 @@ pub struct PendingWindow<T: UserEvent, R: Runtime<T>> {
238249
Option<Box<dyn Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send>>,
239250

240251
pub web_resource_request_handler: Option<Box<WebResourceRequestHandler>>,
252+
253+
pub on_page_load_handler: Option<Box<OnPageLoadHandler>>,
241254
}
242255

243256
pub fn is_label_valid(label: &str) -> bool {
@@ -270,11 +283,12 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
270283
uri_scheme_protocols: Default::default(),
271284
label,
272285
ipc_handler: None,
273-
navigation_handler: Default::default(),
286+
navigation_handler: None,
274287
url: "tauri://localhost".to_string(),
275288
#[cfg(target_os = "android")]
276289
on_webview_created: None,
277-
web_resource_request_handler: Default::default(),
290+
web_resource_request_handler: None,
291+
on_page_load_handler: None,
278292
})
279293
}
280294
}
@@ -298,11 +312,12 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
298312
uri_scheme_protocols: Default::default(),
299313
label,
300314
ipc_handler: None,
301-
navigation_handler: Default::default(),
315+
navigation_handler: None,
302316
url: "tauri://localhost".to_string(),
303317
#[cfg(target_os = "android")]
304318
on_webview_created: None,
305-
web_resource_request_handler: Default::default(),
319+
web_resource_request_handler: None,
320+
on_page_load_handler: None,
306321
})
307322
}
308323
}
@@ -342,7 +357,7 @@ pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
342357
/// Name of the window
343358
pub label: String,
344359

345-
/// The [`Dispatch`](crate::Dispatch) associated with the window.
360+
/// The [`Dispatch`] associated with the window.
346361
pub dispatcher: R::Dispatcher,
347362
}
348363

core/tauri-utils/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ impl<'d> serde::Deserialize<'d> for AssociationExt {
703703
pub struct FileAssociation {
704704
/// File extensions to associate with this app. e.g. 'png'
705705
pub ext: Vec<AssociationExt>,
706-
/// The name. Maps to `CFBundleTypeName` on macOS. Default to ext[0]
706+
/// The name. Maps to `CFBundleTypeName` on macOS. Default to `ext[0]`
707707
pub name: Option<String>,
708708
/// The association description. Windows-only. It is displayed on the `Type` column on Windows Explorer.
709709
pub description: Option<String>,

core/tauri/scripts/init.js

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,8 @@
1212
__RAW_core_script__
1313

1414
__RAW_event_initialization_script__
15-
;(function () {
16-
__RAW_bundle_script__
17-
})()
1815

19-
if (window.ipc) {
20-
window.__TAURI_INTERNALS__.invoke('__initialized', {
21-
url: window.location.href
22-
})
23-
} else {
24-
window.addEventListener('DOMContentLoaded', function () {
25-
window.__TAURI_INTERNALS__.invoke('__initialized', {
26-
url: window.location.href
27-
})
28-
})
29-
}
16+
__RAW_bundle_script__
3017

3118
__RAW_plugin_initialization_script__
3219
})()

core/tauri/src/app.rs

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::{
1919
sealed::{ManagerBase, RuntimeOrDispatch},
2020
utils::config::Config,
2121
utils::{assets::Assets, Env},
22+
window::PageLoadPayload,
2223
Context, DeviceEventFilter, EventLoopMessage, Icon, Manager, Monitor, Runtime, Scopes,
2324
StateManager, Theme, Window,
2425
};
@@ -30,7 +31,6 @@ use crate::tray::{TrayIcon, TrayIconBuilder, TrayIconEvent, TrayIconId};
3031
#[cfg(desktop)]
3132
use crate::window::WindowMenu;
3233
use raw_window_handle::HasRawDisplayHandle;
33-
use serde::Deserialize;
3434
use serialize_to_javascript::{default_template, DefaultTemplate, Template};
3535
use tauri_macros::default_runtime;
3636
#[cfg(desktop)]
@@ -68,20 +68,7 @@ pub(crate) type GlobalWindowEventListener<R> = Box<dyn Fn(GlobalWindowEvent<R>)
6868
pub type SetupHook<R> =
6969
Box<dyn FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send>;
7070
/// A closure that is run once every time a window is created and loaded.
71-
pub type OnPageLoad<R> = dyn Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static;
72-
73-
/// The payload for the [`OnPageLoad`] hook.
74-
#[derive(Debug, Clone, Deserialize)]
75-
pub struct PageLoadPayload {
76-
url: String,
77-
}
78-
79-
impl PageLoadPayload {
80-
/// The page URL.
81-
pub fn url(&self) -> &str {
82-
&self.url
83-
}
84-
}
71+
pub type OnPageLoad<R> = dyn Fn(&Window<R>, &PageLoadPayload<'_>) + Send + Sync + 'static;
8572

8673
/// Api exposed on the `ExitRequested` event.
8774
#[derive(Debug)]
@@ -982,7 +969,7 @@ pub struct Builder<R: Runtime> {
982969
setup: SetupHook<R>,
983970

984971
/// Page load hook.
985-
on_page_load: Box<OnPageLoad<R>>,
972+
on_page_load: Option<Arc<OnPageLoad<R>>>,
986973

987974
/// windows to create when starting up.
988975
pending_windows: Vec<PendingWindow<EventLoopMessage, R>>,
@@ -1040,7 +1027,7 @@ impl<R: Runtime> Builder<R> {
10401027
.render_default(&Default::default())
10411028
.unwrap()
10421029
.into_string(),
1043-
on_page_load: Box::new(|_, _| ()),
1030+
on_page_load: None,
10441031
pending_windows: Default::default(),
10451032
plugins: PluginStore::default(),
10461033
uri_scheme_protocols: Default::default(),
@@ -1130,9 +1117,9 @@ impl<R: Runtime> Builder<R> {
11301117
#[must_use]
11311118
pub fn on_page_load<F>(mut self, on_page_load: F) -> Self
11321119
where
1133-
F: Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static,
1120+
F: Fn(&Window<R>, &PageLoadPayload<'_>) + Send + Sync + 'static,
11341121
{
1135-
self.on_page_load = Box::new(on_page_load);
1122+
self.on_page_load.replace(Arc::new(on_page_load));
11361123
self
11371124
}
11381125

core/tauri/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
696696
/// If the state for the `T` type has previously been set, the state is unchanged and false is returned. Otherwise true is returned.
697697
///
698698
/// Managed state can be retrieved by any command handler via the
699-
/// [`State`](crate::State) guard. In particular, if a value of type `T`
699+
/// [`State`] guard. In particular, if a value of type `T`
700700
/// is managed by Tauri, adding `State<T>` to the list of arguments in a
701701
/// command handler instructs Tauri to retrieve the managed value.
702702
/// Additionally, [`state`](Self#method.state) can be used to retrieve the value manually.

core/tauri/src/manager.rs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,8 @@ use tauri_utils::{
2727
html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN},
2828
};
2929

30-
use crate::event::EmitArgs;
3130
use crate::{
32-
app::{
33-
AppHandle, GlobalWindowEvent, GlobalWindowEventListener, OnPageLoad, PageLoadPayload,
34-
UriSchemeResponder,
35-
},
31+
app::{AppHandle, GlobalWindowEvent, GlobalWindowEventListener, OnPageLoad, UriSchemeResponder},
3632
event::{assert_event_name_is_valid, Event, EventId, Listeners},
3733
ipc::{Invoke, InvokeHandler, InvokeResponder},
3834
pattern::PatternJavascript,
@@ -52,6 +48,7 @@ use crate::{
5248
Context, EventLoopMessage, Icon, Manager, Pattern, Runtime, Scopes, StateManager, Window,
5349
WindowEvent,
5450
};
51+
use crate::{event::EmitArgs, window::PageLoadPayload};
5552

5653
#[cfg(desktop)]
5754
use crate::app::GlobalMenuEventListener;
@@ -232,7 +229,7 @@ pub struct InnerWindowManager<R: Runtime> {
232229
invoke_handler: Box<InvokeHandler<R>>,
233230

234231
/// The page load hook, invoked when the webview performs a navigation.
235-
on_page_load: Box<OnPageLoad<R>>,
232+
on_page_load: Option<Arc<OnPageLoad<R>>>,
236233

237234
config: Arc<Config>,
238235
assets: Arc<dyn Assets>,
@@ -339,7 +336,7 @@ impl<R: Runtime> WindowManager<R> {
339336
#[allow(unused_mut)] mut context: Context<impl Assets>,
340337
plugins: PluginStore<R>,
341338
invoke_handler: Box<InvokeHandler<R>>,
342-
on_page_load: Box<OnPageLoad<R>>,
339+
on_page_load: Option<Arc<OnPageLoad<R>>>,
343340
uri_scheme_protocols: HashMap<String, Arc<UriSchemeProtocol<R>>>,
344341
state: StateManager,
345342
window_event_listeners: Vec<GlobalWindowEventListener<R>>,
@@ -685,6 +682,32 @@ impl<R: Runtime> WindowManager<R> {
685682
registered_scheme_protocols.push("ipc".into());
686683
}
687684

685+
let label = pending.label.clone();
686+
let manager = self.clone();
687+
let on_page_load_handler = pending.on_page_load_handler.take();
688+
pending
689+
.on_page_load_handler
690+
.replace(Box::new(move |url, event| {
691+
let payload = PageLoadPayload { url: &url, event };
692+
693+
if let Some(w) = manager.get_window(&label) {
694+
if let Some(on_page_load) = &manager.inner.on_page_load {
695+
on_page_load(&w, &payload);
696+
}
697+
698+
manager
699+
.inner
700+
.plugins
701+
.lock()
702+
.unwrap()
703+
.on_page_load(&w, &payload);
704+
}
705+
706+
if let Some(handler) = &on_page_load_handler {
707+
handler(url, event);
708+
}
709+
}));
710+
688711
#[cfg(feature = "protocol-asset")]
689712
if !registered_scheme_protocols.contains(&"asset".into()) {
690713
let asset_scope = self.state().get::<crate::Scopes>().asset_protocol.clone();
@@ -869,16 +892,6 @@ impl<R: Runtime> WindowManager<R> {
869892
(self.inner.invoke_handler)(invoke)
870893
}
871894

872-
pub fn run_on_page_load(&self, window: Window<R>, payload: PageLoadPayload) {
873-
(self.inner.on_page_load)(window.clone(), payload.clone());
874-
self
875-
.inner
876-
.plugins
877-
.lock()
878-
.expect("poisoned plugin store")
879-
.on_page_load(window, payload);
880-
}
881-
882895
pub fn extend_api(&self, plugin: &str, invoke: Invoke<R>) -> bool {
883896
self
884897
.inner
@@ -1376,7 +1389,7 @@ mod test {
13761389
context,
13771390
PluginStore::default(),
13781391
Box::new(|_| false),
1379-
Box::new(|_, _| ()),
1392+
None,
13801393
Default::default(),
13811394
StateManager::new(),
13821395
Default::default(),

0 commit comments

Comments
 (0)