From f7c2a349ea7b8d795635be3387c074ff81c72c51 Mon Sep 17 00:00:00 2001 From: pashokitsme Date: Wed, 1 Nov 2023 13:55:34 +0300 Subject: [PATCH 1/5] on_download_started & on_download_completed setters --- core/tauri-runtime-wry/src/lib.rs | 14 +++++++++++--- core/tauri-runtime/src/window.rs | 12 ++++++++++++ core/tauri/src/window/mod.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 38b7abdb2752..b7afa009a161 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -2643,8 +2643,6 @@ fn create_webview( label, ipc_handler, url, - #[cfg(target_os = "android")] - on_webview_created, .. } = pending; @@ -2714,10 +2712,12 @@ fn create_webview( .unwrap() // safe to unwrap because we validate the URL beforehand .with_transparent(is_window_transparent) .with_accept_first_mouse(webview_attributes.accept_first_mouse); + if webview_attributes.file_drop_handler_enabled { webview_builder = webview_builder .with_file_drop_handler(create_file_drop_handler(window_event_listeners.clone())); } + if let Some(navigation_handler) = pending.navigation_handler { webview_builder = webview_builder.with_navigation_handler(move |url| { Url::parse(&url) @@ -2726,6 +2726,14 @@ fn create_webview( }); } + if let Some(download_started_handler) = pending.download_started_handler { + webview_builder = webview_builder.with_download_started_handler(download_started_handler) + } + + if let Some(download_completed_handler) = pending.download_completed_handler { + webview_builder = webview_builder.with_download_completed_handler(download_completed_handler); + } + if let Some(page_load_handler) = pending.on_page_load_handler { webview_builder = webview_builder.with_on_page_load_handler(move |event, url| { let _ = Url::parse(&url).map(|url| { @@ -2824,7 +2832,7 @@ fn create_webview( #[cfg(target_os = "android")] { - if let Some(on_webview_created) = on_webview_created { + if let Some(on_webview_created) = pending.on_webview_created { webview_builder = webview_builder.on_webview_created(move |ctx| { on_webview_created(tauri_runtime::window::CreationContext { env: ctx.env, diff --git a/core/tauri-runtime/src/window.rs b/core/tauri-runtime/src/window.rs index 6a436f56eab1..e4d03173d1b9 100644 --- a/core/tauri-runtime/src/window.rs +++ b/core/tauri-runtime/src/window.rs @@ -36,6 +36,10 @@ type NavigationHandler = dyn Fn(&Url) -> bool + Send; type OnPageLoadHandler = dyn Fn(Url, PageLoadEvent) + Send; +type DownloadStartedHandler = dyn Fn(String, &mut PathBuf) -> bool + Send; + +type DownloadCompletedHandler = dyn Fn(String, Option, bool) + Send; + /// Kind of event for the page load handler. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PageLoadEvent { @@ -240,6 +244,10 @@ pub struct PendingWindow> { /// A handler to decide if incoming url is allowed to navigate. pub navigation_handler: Option>, + pub download_started_handler: Option>, + + pub download_completed_handler: Option>, + /// The resolved URL to load on the webview. pub url: String, @@ -284,6 +292,8 @@ impl> PendingWindow { label, ipc_handler: None, navigation_handler: None, + download_started_handler: None, + download_completed_handler: None, url: "tauri://localhost".to_string(), #[cfg(target_os = "android")] on_webview_created: None, @@ -313,6 +323,8 @@ impl> PendingWindow { label, ipc_handler: None, navigation_handler: None, + download_started_handler: None, + download_completed_handler: None, url: "tauri://localhost".to_string(), #[cfg(target_os = "android")] on_webview_created: None, diff --git a/core/tauri/src/window/mod.rs b/core/tauri/src/window/mod.rs index 0f536baa102f..a2a3e95a52d3 100644 --- a/core/tauri/src/window/mod.rs +++ b/core/tauri/src/window/mod.rs @@ -64,6 +64,8 @@ use std::{ pub(crate) type WebResourceRequestHandler = dyn Fn(http::Request>, &mut http::Response>) + Send + Sync; pub(crate) type NavigationHandler = dyn Fn(&Url) -> bool + Send; +pub(crate) type DownloadStartedHandler = dyn Fn(String, &mut PathBuf) -> bool + Send; +pub(crate) type DownloadCompletedHandler = dyn Fn(String, Option, bool) + Send; pub(crate) type UriSchemeProtocolHandler = Box>, UriSchemeResponder) + Send + Sync>; pub(crate) type OnPageLoad = dyn Fn(Window, PageLoadPayload<'_>) + Send + Sync + 'static; @@ -149,6 +151,8 @@ pub struct WindowBuilder<'a, R: Runtime> { pub(crate) webview_attributes: WebviewAttributes, web_resource_request_handler: Option>, navigation_handler: Option>, + download_started: Option>, + download_completed: Option>, on_page_load_handler: Option>>, #[cfg(desktop)] on_menu_event: Option>>, @@ -228,6 +232,8 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { webview_attributes: WebviewAttributes::new(url), web_resource_request_handler: None, navigation_handler: None, + download_started: None, + download_completed: None, on_page_load_handler: None, #[cfg(desktop)] on_menu_event: None, @@ -267,6 +273,8 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { window_builder: >::WindowBuilder::with_config( config, ), + download_started: None, + download_completed: None, web_resource_request_handler: None, #[cfg(desktop)] menu: None, @@ -353,6 +361,22 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self } + pub fn on_download_started bool + Send + 'static>( + mut self, + f: F, + ) -> Self { + self.download_started.replace(Box::new(f)); + self + } + + pub fn on_download_completed, bool) + Send + 'static>( + mut self, + f: F, + ) -> Self { + self.download_completed.replace(Box::new(f)); + self + } + /// Defines a closure to be executed when a page load event is triggered. /// The event can be either [`PageLoadEvent::Started`] if the page has started loading /// or [`PageLoadEvent::Finished`] when the page finishes loading. @@ -441,6 +465,8 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self.label.clone(), )?; pending.navigation_handler = self.navigation_handler.take(); + pending.download_started_handler = self.download_started.take(); + pending.download_completed_handler = self.download_completed.take(); pending.web_resource_request_handler = self.web_resource_request_handler.take(); if let Some(on_page_load_handler) = self.on_page_load_handler.take() { From 8110c158ede036c037ea5fa4360ffd48f8366694 Mon Sep 17 00:00:00 2001 From: pashokitsme Date: Wed, 1 Nov 2023 14:04:04 +0300 Subject: [PATCH 2/5] macos: default handler fn if not set --- core/tauri-runtime-wry/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index b7afa009a161..5f8d2e21a550 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -2643,6 +2643,7 @@ fn create_webview( label, ipc_handler, url, + download_started_handler, .. } = pending; @@ -2726,12 +2727,15 @@ fn create_webview( }); } - if let Some(download_started_handler) = pending.download_started_handler { - webview_builder = webview_builder.with_download_started_handler(download_started_handler) + #[cfg(target_os = "macos")] + let download_started = download_started_handler.or_else(|| Some(Box::from(|_, _: &mut _| true))); + + if let Some(download_started) = download_started { + webview_builder = webview_builder.with_download_started_handler(download_started) } - if let Some(download_completed_handler) = pending.download_completed_handler { - webview_builder = webview_builder.with_download_completed_handler(download_completed_handler); + if let Some(download_completed) = pending.download_completed_handler { + webview_builder = webview_builder.with_download_completed_handler(download_completed); } if let Some(page_load_handler) = pending.on_page_load_handler { From 3a089746eaa574376102b5598d55e1fcda757771 Mon Sep 17 00:00:00 2001 From: pashokitsme Date: Wed, 1 Nov 2023 14:53:26 +0300 Subject: [PATCH 3/5] remove default macos handler --- core/tauri-runtime-wry/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 5f8d2e21a550..e1f1b8de4a10 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -2643,7 +2643,6 @@ fn create_webview( label, ipc_handler, url, - download_started_handler, .. } = pending; @@ -2727,10 +2726,7 @@ fn create_webview( }); } - #[cfg(target_os = "macos")] - let download_started = download_started_handler.or_else(|| Some(Box::from(|_, _: &mut _| true))); - - if let Some(download_started) = download_started { + if let Some(download_started) = pending.download_started_handler { webview_builder = webview_builder.with_download_started_handler(download_started) } From d1ccdd8149ee7c7364dc59b4f172c6f68ada54b8 Mon Sep 17 00:00:00 2001 From: pashokitsme Date: Fri, 10 Nov 2023 02:48:03 +0300 Subject: [PATCH 4/5] doc comments --- core/tauri/src/window/mod.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/tauri/src/window/mod.rs b/core/tauri/src/window/mod.rs index a2a3e95a52d3..8541ebc59048 100644 --- a/core/tauri/src/window/mod.rs +++ b/core/tauri/src/window/mod.rs @@ -361,6 +361,12 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self } + /// Set a download started handler to manage incoming downloads. + /// + /// The closure takes two parameters - the first is a `String` representing the url being downloaded from and and the + /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter + /// parameter can be used to set the download location by assigning a new path to it - the assigned path _must_ be + /// absolute. The closure returns a `bool` to allow or deny the download. pub fn on_download_started bool + Send + 'static>( mut self, f: F, @@ -369,6 +375,19 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self } + /// Sets a download completion handler to manage downloads that have finished. + /// + /// The closure is fired when the download completes, whether it was successful or not. + /// The closure takes a `String` representing the URL of the original download request, an `Option` + /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download + /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download + /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to + /// know if the download succeeded. + /// + /// ## Platform-specific: + /// + /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API + /// limitations. pub fn on_download_completed, bool) + Send + 'static>( mut self, f: F, From 740c2616a33388b1a8e23c4461d3368e7b4e49f5 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 28 Dec 2023 14:48:44 -0300 Subject: [PATCH 5/5] unify hooks, change files --- .changes/on-download-hook.md | 5 + .changes/runtime-on-download-hooks.md | 6 ++ core/tauri-runtime-wry/src/lib.rs | 26 ++++-- core/tauri-runtime/src/window.rs | 38 +++++--- core/tauri/src/window/mod.rs | 130 ++++++++++++++++++-------- 5 files changed, 148 insertions(+), 57 deletions(-) create mode 100644 .changes/on-download-hook.md create mode 100644 .changes/runtime-on-download-hooks.md diff --git a/.changes/on-download-hook.md b/.changes/on-download-hook.md new file mode 100644 index 000000000000..a25a749102b2 --- /dev/null +++ b/.changes/on-download-hook.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:feat +--- + +Added `WindowBuilder::on_download` to handle download request events. diff --git a/.changes/runtime-on-download-hooks.md b/.changes/runtime-on-download-hooks.md new file mode 100644 index 000000000000..3f2a0d31e651 --- /dev/null +++ b/.changes/runtime-on-download-hooks.md @@ -0,0 +1,6 @@ +--- +"tauri-runtime": patch:feat +"tauri-runtime-wry": patch:feat +--- + +Added download event closure via `PendingWindow::download_handler`. diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index b3a1a2c0de3a..61dde457bbb9 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -17,7 +17,8 @@ use tauri_runtime::{ webview::{WebviewIpcHandler, WindowBuilder, WindowBuilderBase}, window::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, - CursorIcon, DetachedWindow, FileDropEvent, PendingWindow, RawWindow, WindowEvent, + CursorIcon, DetachedWindow, DownloadEvent, FileDropEvent, PendingWindow, RawWindow, + WindowEvent, }, DeviceEventFilter, Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, Icon, Result, RunEvent, RunIteration, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent, @@ -2850,12 +2851,23 @@ fn create_webview( }); } - if let Some(download_started) = pending.download_started_handler { - webview_builder = webview_builder.with_download_started_handler(download_started) - } - - if let Some(download_completed) = pending.download_completed_handler { - webview_builder = webview_builder.with_download_completed_handler(download_completed); + if let Some(download_handler) = pending.download_handler { + let download_handler_ = download_handler.clone(); + webview_builder = webview_builder.with_download_started_handler(move |url, path| { + if let Ok(url) = url.parse() { + download_handler_(DownloadEvent::Requested { + url, + destination: path, + }) + } else { + false + } + }); + webview_builder = webview_builder.with_download_completed_handler(move |url, path, success| { + if let Ok(url) = url.parse() { + download_handler(DownloadEvent::Finished { url, path, success }); + } + }); } if let Some(page_load_handler) = pending.on_page_load_handler { diff --git a/core/tauri-runtime/src/window.rs b/core/tauri-runtime/src/window.rs index e4d03173d1b9..30b2902a2b83 100644 --- a/core/tauri-runtime/src/window.rs +++ b/core/tauri-runtime/src/window.rs @@ -19,7 +19,7 @@ use std::{ hash::{Hash, Hasher}, marker::PhantomData, path::PathBuf, - sync::mpsc::Sender, + sync::{mpsc::Sender, Arc}, }; use self::dpi::PhysicalPosition; @@ -36,9 +36,29 @@ type NavigationHandler = dyn Fn(&Url) -> bool + Send; type OnPageLoadHandler = dyn Fn(Url, PageLoadEvent) + Send; -type DownloadStartedHandler = dyn Fn(String, &mut PathBuf) -> bool + Send; - -type DownloadCompletedHandler = dyn Fn(String, Option, bool) + Send; +type DownloadHandler = dyn Fn(DownloadEvent) -> bool + Send + Sync; + +/// Download event. +pub enum DownloadEvent<'a> { + /// Download requested. + Requested { + /// The url being downloaded. + url: Url, + /// Represents where the file will be downloaded to. + /// Can be used to set the download location by assigning a new path to it. + /// The assigned path _must_ be absolute. + destination: &'a mut PathBuf, + }, + /// Download finished. + Finished { + /// The URL of the original download request. + url: Url, + /// Potentially representing the filesystem path the file was downloaded to. + path: Option, + /// Indicates if the download succeeded or not. + success: bool, + }, +} /// Kind of event for the page load handler. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -244,9 +264,7 @@ pub struct PendingWindow> { /// A handler to decide if incoming url is allowed to navigate. pub navigation_handler: Option>, - pub download_started_handler: Option>, - - pub download_completed_handler: Option>, + pub download_handler: Option>, /// The resolved URL to load on the webview. pub url: String, @@ -292,8 +310,7 @@ impl> PendingWindow { label, ipc_handler: None, navigation_handler: None, - download_started_handler: None, - download_completed_handler: None, + download_handler: None, url: "tauri://localhost".to_string(), #[cfg(target_os = "android")] on_webview_created: None, @@ -323,8 +340,7 @@ impl> PendingWindow { label, ipc_handler: None, navigation_handler: None, - download_started_handler: None, - download_completed_handler: None, + download_handler: None, url: "tauri://localhost".to_string(), #[cfg(target_os = "android")] on_webview_created: None, diff --git a/core/tauri/src/window/mod.rs b/core/tauri/src/window/mod.rs index b250eda7b4cc..d5c3bad684db 100644 --- a/core/tauri/src/window/mod.rs +++ b/core/tauri/src/window/mod.rs @@ -64,8 +64,7 @@ use std::{ pub(crate) type WebResourceRequestHandler = dyn Fn(http::Request>, &mut http::Response>) + Send + Sync; pub(crate) type NavigationHandler = dyn Fn(&Url) -> bool + Send; -pub(crate) type DownloadStartedHandler = dyn Fn(String, &mut PathBuf) -> bool + Send; -pub(crate) type DownloadCompletedHandler = dyn Fn(String, Option, bool) + Send; +pub(crate) type DownloadHandler = dyn Fn(Window, DownloadEvent<'_>) -> bool + Send + Sync; pub(crate) type UriSchemeProtocolHandler = Box>, UriSchemeResponder) + Send + Sync>; pub(crate) type OnPageLoad = dyn Fn(Window, PageLoadPayload<'_>) + Send + Sync + 'static; @@ -94,6 +93,38 @@ impl<'a> PageLoadPayload<'a> { } } +/// Download event for the [`WindowBuilder#method.on_download`] hook. +#[non_exhaustive] +pub enum DownloadEvent<'a> { + /// Download requested. + Requested { + /// The url being downloaded. + url: Url, + /// Represents where the file will be downloaded to. + /// Can be used to set the download location by assigning a new path to it. + /// The assigned path _must_ be absolute. + destination: &'a mut PathBuf, + }, + /// Download finished. + Finished { + /// The URL of the original download request. + url: Url, + /// Potentially representing the filesystem path the file was downloaded to. + /// + /// A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download + /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to + /// know if the download succeeded. + /// + /// ## Platform-specific: + /// + /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API + /// limitations. + path: Option, + /// Indicates if the download succeeded or not. + success: bool, + }, +} + /// Monitor descriptor. #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] @@ -151,8 +182,7 @@ pub struct WindowBuilder<'a, R: Runtime> { pub(crate) webview_attributes: WebviewAttributes, web_resource_request_handler: Option>, navigation_handler: Option>, - download_started: Option>, - download_completed: Option>, + download_handler: Option>>, on_page_load_handler: Option>>, #[cfg(desktop)] on_menu_event: Option>>, @@ -232,8 +262,7 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { webview_attributes: WebviewAttributes::new(url), web_resource_request_handler: None, navigation_handler: None, - download_started: None, - download_completed: None, + download_handler: None, on_page_load_handler: None, #[cfg(desktop)] on_menu_event: None, @@ -273,8 +302,7 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { window_builder: >::WindowBuilder::with_config( config, ), - download_started: None, - download_completed: None, + download_handler: None, web_resource_request_handler: None, #[cfg(desktop)] menu: None, @@ -361,38 +389,44 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self } - /// Set a download started handler to manage incoming downloads. + /// Set a download event handler to be notified when a download is requested or finished. /// - /// The closure takes two parameters - the first is a `String` representing the url being downloaded from and and the - /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter - /// parameter can be used to set the download location by assigning a new path to it - the assigned path _must_ be - /// absolute. The closure returns a `bool` to allow or deny the download. - pub fn on_download_started bool + Send + 'static>( - mut self, - f: F, - ) -> Self { - self.download_started.replace(Box::new(f)); - self - } - - /// Sets a download completion handler to manage downloads that have finished. + /// Returning `false` prevents the download from happening on a [`DownloadEvent::Requested`] event. /// - /// The closure is fired when the download completes, whether it was successful or not. - /// The closure takes a `String` representing the URL of the original download request, an `Option` - /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download - /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download - /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to - /// know if the download succeeded. + /// # Examples /// - /// ## Platform-specific: + /// ```rust,no_run + /// use tauri::{ + /// utils::config::{Csp, CspDirectiveSources, WindowUrl}, + /// window::{DownloadEvent, WindowBuilder}, + /// }; /// - /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API - /// limitations. - pub fn on_download_completed, bool) + Send + 'static>( + /// tauri::Builder::default() + /// .setup(|app| { + /// WindowBuilder::new(app, "core", WindowUrl::App("index.html".into())) + /// .on_download(|window, event| { + /// match event { + /// DownloadEvent::Requested { url, destination } => { + /// println!("downloading {}", url); + /// *destination = "/home/tauri/target/path".into(); + /// } + /// DownloadEvent::Finished { url, path, success } => { + /// println!("downloaded {} to {:?}, success: {}", url, path, success); + /// } + /// _ => (), + /// } + /// // let the download start + /// true + /// }) + /// .build()?; + /// Ok(()) + /// }); + /// ``` + pub fn on_download, DownloadEvent<'_>) -> bool + Send + Sync + 'static>( mut self, f: F, ) -> Self { - self.download_completed.replace(Box::new(f)); + self.download_handler.replace(Arc::new(f)); self } @@ -404,18 +438,16 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { /// /// ```rust,no_run /// use tauri::{ - /// utils::config::{Csp, CspDirectiveSources, WindowUrl}, + /// utils::config::WindowUrl, /// window::{PageLoadEvent, WindowBuilder}, /// }; - /// use http::header::HeaderValue; - /// use std::collections::HashMap; /// tauri::Builder::default() /// .setup(|app| { /// WindowBuilder::new(app, "core", WindowUrl::App("index.html".into())) /// .on_page_load(|window, payload| { /// match payload.event() { /// PageLoadEvent::Started => { - /// println!("{} finished loading", payload.url()); + /// println!("{} started loading", payload.url()); /// } /// PageLoadEvent::Finished => { /// println!("{} finished loading", payload.url()); @@ -485,10 +517,30 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self.label.clone(), )?; pending.navigation_handler = self.navigation_handler.take(); - pending.download_started_handler = self.download_started.take(); - pending.download_completed_handler = self.download_completed.take(); pending.web_resource_request_handler = self.web_resource_request_handler.take(); + if let Some(download_handler) = self.download_handler.take() { + let label = pending.label.clone(); + let manager = self.app_handle.manager.clone(); + pending.download_handler.replace(Arc::new(move |event| { + if let Some(w) = manager.get_window(&label) { + download_handler( + w, + match event { + tauri_runtime::window::DownloadEvent::Requested { url, destination } => { + DownloadEvent::Requested { url, destination } + } + tauri_runtime::window::DownloadEvent::Finished { url, path, success } => { + DownloadEvent::Finished { url, path, success } + } + }, + ) + } else { + false + } + })); + } + if let Some(on_page_load_handler) = self.on_page_load_handler.take() { let label = pending.label.clone(); let manager = self.app_handle.manager.clone();