@@ -64,6 +64,7 @@ use std::{
6464pub ( crate ) type WebResourceRequestHandler =
6565 dyn Fn ( http:: Request < Vec < u8 > > , & mut http:: Response < Cow < ' static , [ u8 ] > > ) + Send + Sync ;
6666pub ( crate ) type NavigationHandler = dyn Fn ( & Url ) -> bool + Send ;
67+ pub ( crate ) type DownloadHandler < R > = dyn Fn ( Window < R > , DownloadEvent < ' _ > ) -> bool + Send + Sync ;
6768pub ( crate ) type UriSchemeProtocolHandler =
6869 Box < dyn Fn ( http:: Request < Vec < u8 > > , UriSchemeResponder ) + Send + Sync > ;
6970pub ( crate ) type OnPageLoad < R > = dyn Fn ( Window < R > , PageLoadPayload < ' _ > ) + Send + Sync + ' static ;
@@ -92,6 +93,38 @@ impl<'a> PageLoadPayload<'a> {
9293 }
9394}
9495
96+ /// Download event for the [`WindowBuilder#method.on_download`] hook.
97+ #[ non_exhaustive]
98+ pub enum DownloadEvent < ' a > {
99+ /// Download requested.
100+ Requested {
101+ /// The url being downloaded.
102+ url : Url ,
103+ /// Represents where the file will be downloaded to.
104+ /// Can be used to set the download location by assigning a new path to it.
105+ /// The assigned path _must_ be absolute.
106+ destination : & ' a mut PathBuf ,
107+ } ,
108+ /// Download finished.
109+ Finished {
110+ /// The URL of the original download request.
111+ url : Url ,
112+ /// Potentially representing the filesystem path the file was downloaded to.
113+ ///
114+ /// A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download
115+ /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to
116+ /// know if the download succeeded.
117+ ///
118+ /// ## Platform-specific:
119+ ///
120+ /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API
121+ /// limitations.
122+ path : Option < PathBuf > ,
123+ /// Indicates if the download succeeded or not.
124+ success : bool ,
125+ } ,
126+ }
127+
95128/// Monitor descriptor.
96129#[ derive( Debug , Clone , Serialize ) ]
97130#[ serde( rename_all = "camelCase" ) ]
@@ -149,6 +182,7 @@ pub struct WindowBuilder<'a, R: Runtime> {
149182 pub ( crate ) webview_attributes : WebviewAttributes ,
150183 web_resource_request_handler : Option < Box < WebResourceRequestHandler > > ,
151184 navigation_handler : Option < Box < NavigationHandler > > ,
185+ download_handler : Option < Arc < DownloadHandler < R > > > ,
152186 on_page_load_handler : Option < Box < OnPageLoad < R > > > ,
153187 #[ cfg( desktop) ]
154188 on_menu_event : Option < crate :: app:: GlobalMenuEventListener < Window < R > > > ,
@@ -228,6 +262,7 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
228262 webview_attributes : WebviewAttributes :: new ( url) ,
229263 web_resource_request_handler : None ,
230264 navigation_handler : None ,
265+ download_handler : None ,
231266 on_page_load_handler : None ,
232267 #[ cfg( desktop) ]
233268 on_menu_event : None ,
@@ -267,6 +302,7 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
267302 window_builder : <R :: Dispatcher as Dispatch < EventLoopMessage > >:: WindowBuilder :: with_config (
268303 config,
269304 ) ,
305+ download_handler : None ,
270306 web_resource_request_handler : None ,
271307 #[ cfg( desktop) ]
272308 menu : None ,
@@ -353,6 +389,47 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
353389 self
354390 }
355391
392+ /// Set a download event handler to be notified when a download is requested or finished.
393+ ///
394+ /// Returning `false` prevents the download from happening on a [`DownloadEvent::Requested`] event.
395+ ///
396+ /// # Examples
397+ ///
398+ /// ```rust,no_run
399+ /// use tauri::{
400+ /// utils::config::{Csp, CspDirectiveSources, WindowUrl},
401+ /// window::{DownloadEvent, WindowBuilder},
402+ /// };
403+ ///
404+ /// tauri::Builder::default()
405+ /// .setup(|app| {
406+ /// WindowBuilder::new(app, "core", WindowUrl::App("index.html".into()))
407+ /// .on_download(|window, event| {
408+ /// match event {
409+ /// DownloadEvent::Requested { url, destination } => {
410+ /// println!("downloading {}", url);
411+ /// *destination = "/home/tauri/target/path".into();
412+ /// }
413+ /// DownloadEvent::Finished { url, path, success } => {
414+ /// println!("downloaded {} to {:?}, success: {}", url, path, success);
415+ /// }
416+ /// _ => (),
417+ /// }
418+ /// // let the download start
419+ /// true
420+ /// })
421+ /// .build()?;
422+ /// Ok(())
423+ /// });
424+ /// ```
425+ pub fn on_download < F : Fn ( Window < R > , DownloadEvent < ' _ > ) -> bool + Send + Sync + ' static > (
426+ mut self ,
427+ f : F ,
428+ ) -> Self {
429+ self . download_handler . replace ( Arc :: new ( f) ) ;
430+ self
431+ }
432+
356433 /// Defines a closure to be executed when a page load event is triggered.
357434 /// The event can be either [`PageLoadEvent::Started`] if the page has started loading
358435 /// or [`PageLoadEvent::Finished`] when the page finishes loading.
@@ -361,18 +438,16 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
361438 ///
362439 /// ```rust,no_run
363440 /// use tauri::{
364- /// utils::config::{Csp, CspDirectiveSources, WindowUrl} ,
441+ /// utils::config::WindowUrl,
365442 /// window::{PageLoadEvent, WindowBuilder},
366443 /// };
367- /// use http::header::HeaderValue;
368- /// use std::collections::HashMap;
369444 /// tauri::Builder::default()
370445 /// .setup(|app| {
371446 /// WindowBuilder::new(app, "core", WindowUrl::App("index.html".into()))
372447 /// .on_page_load(|window, payload| {
373448 /// match payload.event() {
374449 /// PageLoadEvent::Started => {
375- /// println!("{} finished loading", payload.url());
450+ /// println!("{} started loading", payload.url());
376451 /// }
377452 /// PageLoadEvent::Finished => {
378453 /// println!("{} finished loading", payload.url());
@@ -444,6 +519,28 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
444519 pending. navigation_handler = self . navigation_handler . take ( ) ;
445520 pending. web_resource_request_handler = self . web_resource_request_handler . take ( ) ;
446521
522+ if let Some ( download_handler) = self . download_handler . take ( ) {
523+ let label = pending. label . clone ( ) ;
524+ let manager = self . app_handle . manager . clone ( ) ;
525+ pending. download_handler . replace ( Arc :: new ( move |event| {
526+ if let Some ( w) = manager. get_window ( & label) {
527+ download_handler (
528+ w,
529+ match event {
530+ tauri_runtime:: window:: DownloadEvent :: Requested { url, destination } => {
531+ DownloadEvent :: Requested { url, destination }
532+ }
533+ tauri_runtime:: window:: DownloadEvent :: Finished { url, path, success } => {
534+ DownloadEvent :: Finished { url, path, success }
535+ }
536+ } ,
537+ )
538+ } else {
539+ false
540+ }
541+ } ) ) ;
542+ }
543+
447544 if let Some ( on_page_load_handler) = self . on_page_load_handler . take ( ) {
448545 let label = pending. label . clone ( ) ;
449546 let manager = self . app_handle . manager . clone ( ) ;
0 commit comments