diff --git a/tauri/src/app.rs b/tauri/src/app.rs index 515dac7cc..eae94940a 100644 --- a/tauri/src/app.rs +++ b/tauri/src/app.rs @@ -1,5 +1,5 @@ use futures::future::BoxFuture; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; use tauri_api::{config::Config, private::AsTauriContext}; @@ -23,7 +23,9 @@ pub use webview_manager::{WebviewDispatcher, WebviewManager}; type InvokeHandler = dyn Fn(WebviewManager, String, JsonValue) -> BoxFuture<'static, crate::Result> + Send + Sync; -type Setup = dyn Fn(WebviewManager) -> BoxFuture<'static, ()> + Send + Sync; +type ManagerHook = dyn Fn(WebviewManager) -> BoxFuture<'static, ()> + Send + Sync; +type PageLoadHook = + dyn Fn(WebviewManager, PageLoadPayload) -> BoxFuture<'static, ()> + Send + Sync; /// `App` runtime information. pub struct Context { @@ -63,12 +65,27 @@ impl From for InvokeResponse { } } +/// The payload for the "page_load" hook. +#[derive(Debug, Clone, Deserialize)] +pub struct PageLoadPayload { + url: String, +} + +impl PageLoadPayload { + /// The page URL. + pub fn url(&self) -> &str { + &self.url + } +} + /// The application runner. pub struct App { /// The JS message handler. invoke_handler: Option>>, - /// The setup callback, invoked when the webview is ready. - setup: Option>>, + /// The page load hook, invoked when the webview performs a navigation. + on_page_load: Option>>, + /// The setup hook, invoked when the webviews have been created. + setup: Option>>, /// The context the App was created with pub(crate) context: Context, pub(crate) dispatchers: Arc>>>, @@ -118,10 +135,22 @@ impl App { } } - /// Runs the setup callback if defined. - pub(crate) async fn run_setup(&self, dispatcher: &WebviewManager) { + /// Runs the setup hook if defined. + pub(crate) async fn run_setup(&self, dispatcher: WebviewManager) { if let Some(ref setup) = self.setup { - let fut = setup(dispatcher.clone()); + let fut = setup(dispatcher); + fut.await; + } + } + + /// Runs the on page load hook if defined. + pub(crate) async fn run_on_page_load( + &self, + dispatcher: &WebviewManager, + payload: PageLoadPayload, + ) { + if let Some(ref on_page_load) = self.on_page_load { + let fut = on_page_load(dispatcher.clone(), payload); fut.await; } } @@ -197,8 +226,10 @@ where { /// The JS message handler. invoke_handler: Option>>, - /// The setup callback, invoked when the webview is ready. - setup: Option>>, + /// The setup hook. + setup: Option>>, + /// Page load hook. + on_page_load: Option>>, config: PhantomData, /// The webview dispatchers. dispatchers: Arc>>>, @@ -212,6 +243,7 @@ impl AppBuilder { Self { invoke_handler: None, setup: None, + on_page_load: None, config: Default::default(), dispatchers: Default::default(), webviews: Default::default(), @@ -232,7 +264,7 @@ impl AppBuilder { self } - /// Defines the setup callback. + /// Defines the setup hook. pub fn setup< T: futures::Future + Send + Sync + 'static, F: Fn(WebviewManager) -> T + Send + Sync + 'static, @@ -246,6 +278,20 @@ impl AppBuilder { self } + /// Defines the page load hook. + pub fn on_page_load< + T: futures::Future + Send + Sync + 'static, + F: Fn(WebviewManager, PageLoadPayload) -> T + Send + Sync + 'static, + >( + mut self, + on_page_load: F, + ) -> Self { + self.on_page_load = Some(Box::new(move |webview_manager, payload| { + Box::pin(on_page_load(webview_manager, payload)) + })); + self + } + /// Adds a plugin to the runtime. pub fn plugin( self, @@ -283,6 +329,7 @@ impl AppBuilder { Ok(App { invoke_handler: self.invoke_handler, setup: self.setup, + on_page_load: self.on_page_load, context, dispatchers: self.dispatchers, webviews: Some(self.webviews), @@ -303,6 +350,7 @@ fn run(mut application: App) -> crate::Result<() let application = Arc::new(application); let mut webview_app = A::new()?; + let mut main_webview_manager = None; for webview in webviews { let webview_label = webview.label.to_string(); @@ -311,6 +359,9 @@ fn run(mut application: App) -> crate::Result<() application.dispatchers.clone(), webview_label.to_string(), ); + if main_webview_manager.is_none() { + main_webview_manager = Some(webview_manager.clone()); + } let (webview_builder, rpc_handler, custom_protocol) = crate::async_runtime::block_on(application.init_webview(webview))?; @@ -322,6 +373,10 @@ fn run(mut application: App) -> crate::Result<() )); } + if let Some(main_webview_manager) = main_webview_manager { + crate::async_runtime::block_on(application.run_setup(main_webview_manager)); + } + webview_app.run(); Ok(()) diff --git a/tauri/src/app/utils.rs b/tauri/src/app/utils.rs index 008d98df1..4db9278cc 100644 --- a/tauri/src/app/utils.rs +++ b/tauri/src/app/utils.rs @@ -12,7 +12,7 @@ use crate::{ use super::{ webview::{CustomProtocol, WebviewBuilderExtPrivate, WebviewRpcHandler}, - App, Context, RpcRequest, Webview, WebviewManager, + App, Context, PageLoadPayload, RpcRequest, Webview, WebviewManager, }; use serde::Deserialize; @@ -85,10 +85,10 @@ pub(super) fn initialization_script( {tauri_initialization_script} {event_initialization_script} if (window.rpc) {{ - window.__TAURI__.invoke("__initialized") + window.__TAURI__.invoke("__initialized", {{ url: window.location.href }}) }} else {{ window.addEventListener('DOMContentLoaded', function () {{ - window.__TAURI__.invoke("__initialized") + window.__TAURI__.invoke("__initialized", {{ url: window.location.href }}) }}) }} {plugin_initialization_script} @@ -154,7 +154,6 @@ pub(super) fn build_webview( plugin_initialization_script: &str, context: &Context, ) -> crate::Result> { - // TODO let debug = cfg!(debug_assertions); let webview_url = match &webview.url { WindowUrl::App => content_url.to_string(), WindowUrl::Custom(url) => url.to_string(), @@ -324,8 +323,11 @@ async fn on_message( message: Message, ) -> crate::Result { if &command == "__initialized" { - application.run_setup(&webview_manager).await; - crate::plugin::ready(A::plugin_store(), &webview_manager).await; + let payload: PageLoadPayload = serde_json::from_value(message.inner)?; + application + .run_on_page_load(&webview_manager, payload.clone()) + .await; + crate::plugin::on_page_load(A::plugin_store(), &webview_manager, payload).await; Ok(().into()) } else { let response = if let Some(module) = &message.tauri_module { diff --git a/tauri/src/plugin.rs b/tauri/src/plugin.rs index 903f720b1..b7fefe44d 100644 --- a/tauri/src/plugin.rs +++ b/tauri/src/plugin.rs @@ -1,4 +1,6 @@ -use crate::{api::config::PluginConfig, async_runtime::Mutex, ApplicationExt, WebviewManager}; +use crate::{ + api::config::PluginConfig, async_runtime::Mutex, ApplicationExt, PageLoadPayload, WebviewManager, +}; use futures::future::join_all; use serde_json::Value as JsonValue; @@ -30,9 +32,9 @@ pub trait Plugin: Send + Sync { #[allow(unused_variables)] async fn created(&mut self, webview_manager: WebviewManager) {} - /// Callback invoked when the webview is ready. + /// Callback invoked when the webview performs a navigation. #[allow(unused_variables)] - async fn ready(&mut self, webview_manager: WebviewManager) {} + async fn on_page_load(&mut self, webview_manager: WebviewManager, payload: PageLoadPayload) {} /// Add invoke_handler API extension commands. #[allow(unused_variables)] @@ -109,14 +111,15 @@ pub(crate) async fn created( join_all(futures).await; } -pub(crate) async fn ready( +pub(crate) async fn on_page_load( store: &PluginStore, webview_manager: &crate::WebviewManager, + payload: PageLoadPayload, ) { let mut plugins = store.lock().await; let mut futures = Vec::new(); for plugin in plugins.iter_mut() { - futures.push(plugin.ready(webview_manager.clone())); + futures.push(plugin.on_page_load(webview_manager.clone(), payload.clone())); } join_all(futures).await; }