@@ -11,8 +11,9 @@ use crate::{
1111use serde:: de:: DeserializeOwned ;
1212use serde_json:: Value as JsonValue ;
1313use tauri_macros:: default_runtime;
14+ use url:: Url ;
1415
15- use std:: { collections :: HashMap , fmt, result:: Result as StdResult , sync:: Arc } ;
16+ use std:: { fmt, result:: Result as StdResult , sync:: Arc } ;
1617
1718/// Mobile APIs.
1819#[ cfg( mobile) ]
@@ -48,6 +49,12 @@ pub trait Plugin<R: Runtime>: Send {
4849 #[ allow( unused_variables) ]
4950 fn created ( & mut self , window : Window < R > ) { }
5051
52+ /// Callback invoked when webview tries to navigate to the given Url. Returning falses cancels navigation.
53+ #[ allow( unused_variables) ]
54+ fn on_navigation ( & mut self , window : & Window < R > , url : & Url ) -> bool {
55+ true
56+ }
57+
5158 /// Callback invoked when the webview performs a navigation to a page.
5259 #[ allow( unused_variables) ]
5360 fn on_page_load ( & mut self , window : Window < R > , payload : PageLoadPayload ) { }
@@ -66,6 +73,7 @@ pub trait Plugin<R: Runtime>: Send {
6673type SetupHook < R , C > = dyn FnOnce ( & AppHandle < R > , PluginApi < R , C > ) -> Result < ( ) > + Send ;
6774type OnWebviewReady < R > = dyn FnMut ( Window < R > ) + Send ;
6875type OnEvent < R > = dyn FnMut ( & AppHandle < R > , & RunEvent ) + Send ;
76+ type OnNavigation < R > = dyn Fn ( & Window < R > , & Url ) -> bool + Send ;
6977type OnPageLoad < R > = dyn FnMut ( Window < R > , PageLoadPayload ) + Send ;
7078type OnDrop < R > = dyn FnOnce ( AppHandle < R > ) + Send ;
7179
@@ -192,6 +200,7 @@ pub struct Builder<R: Runtime, C: DeserializeOwned = ()> {
192200 invoke_handler : Box < InvokeHandler < R > > ,
193201 setup : Option < Box < SetupHook < R , C > > > ,
194202 js_init_script : Option < String > ,
203+ on_navigation : Box < OnNavigation < R > > ,
195204 on_page_load : Box < OnPageLoad < R > > ,
196205 on_webview_ready : Box < OnWebviewReady < R > > ,
197206 on_event : Box < OnEvent < R > > ,
@@ -206,6 +215,7 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
206215 setup : None ,
207216 js_init_script : None ,
208217 invoke_handler : Box :: new ( |_| false ) ,
218+ on_navigation : Box :: new ( |_, _| true ) ,
209219 on_page_load : Box :: new ( |_, _| ( ) ) ,
210220 on_webview_ready : Box :: new ( |_| ( ) ) ,
211221 on_event : Box :: new ( |_, _| ( ) ) ,
@@ -313,6 +323,31 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
313323 self
314324 }
315325
326+ /// Callback invoked when the webview tries to navigate to a URL. Returning false cancels the navigation.
327+ ///
328+ /// #Example
329+ ///
330+ /// ```
331+ /// use tauri::{plugin::{Builder, TauriPlugin}, Runtime};
332+ ///
333+ /// fn init<R: Runtime>() -> TauriPlugin<R> {
334+ /// Builder::new("example")
335+ /// .on_navigation(|window, url| {
336+ /// // allow the production URL or localhost on dev
337+ /// url.scheme() == "tauri" || (cfg!(dev) && url.host_str() == Some("localhost"))
338+ /// })
339+ /// .build()
340+ /// }
341+ /// ```
342+ #[ must_use]
343+ pub fn on_navigation < F > ( mut self , on_navigation : F ) -> Self
344+ where
345+ F : Fn ( & Window < R > , & Url ) -> bool + Send + ' static ,
346+ {
347+ self . on_navigation = Box :: new ( on_navigation) ;
348+ self
349+ }
350+
316351 /// Callback invoked when the webview performs a navigation to a page.
317352 ///
318353 /// # Examples
@@ -426,6 +461,7 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
426461 invoke_handler : self . invoke_handler ,
427462 setup : self . setup ,
428463 js_init_script : self . js_init_script ,
464+ on_navigation : self . on_navigation ,
429465 on_page_load : self . on_page_load ,
430466 on_webview_ready : self . on_webview_ready ,
431467 on_event : self . on_event ,
@@ -441,6 +477,7 @@ pub struct TauriPlugin<R: Runtime, C: DeserializeOwned = ()> {
441477 invoke_handler : Box < InvokeHandler < R > > ,
442478 setup : Option < Box < SetupHook < R , C > > > ,
443479 js_init_script : Option < String > ,
480+ on_navigation : Box < OnNavigation < R > > ,
444481 on_page_load : Box < OnPageLoad < R > > ,
445482 on_webview_ready : Box < OnWebviewReady < R > > ,
446483 on_event : Box < OnEvent < R > > ,
@@ -484,6 +521,10 @@ impl<R: Runtime, C: DeserializeOwned> Plugin<R> for TauriPlugin<R, C> {
484521 ( self . on_webview_ready ) ( window)
485522 }
486523
524+ fn on_navigation ( & mut self , window : & Window < R > , url : & Url ) -> bool {
525+ ( self . on_navigation ) ( window, url)
526+ }
527+
487528 fn on_page_load ( & mut self , window : Window < R > , payload : PageLoadPayload ) {
488529 ( self . on_page_load ) ( window, payload)
489530 }
@@ -500,22 +541,21 @@ impl<R: Runtime, C: DeserializeOwned> Plugin<R> for TauriPlugin<R, C> {
500541/// Plugin collection type.
501542#[ default_runtime( crate :: Wry , wry) ]
502543pub ( crate ) struct PluginStore < R : Runtime > {
503- store : HashMap < & ' static str , Box < dyn Plugin < R > > > ,
544+ store : Vec < Box < dyn Plugin < R > > > ,
504545}
505546
506547impl < R : Runtime > fmt:: Debug for PluginStore < R > {
507548 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
549+ let plugins: Vec < & str > = self . store . iter ( ) . map ( |plugins| plugins. name ( ) ) . collect ( ) ;
508550 f. debug_struct ( "PluginStore" )
509- . field ( "plugins" , & self . store . keys ( ) )
551+ . field ( "plugins" , & plugins )
510552 . finish ( )
511553 }
512554}
513555
514556impl < R : Runtime > Default for PluginStore < R > {
515557 fn default ( ) -> Self {
516- Self {
517- store : HashMap :: new ( ) ,
518- }
558+ Self { store : Vec :: new ( ) }
519559 }
520560}
521561
@@ -524,12 +564,18 @@ impl<R: Runtime> PluginStore<R> {
524564 ///
525565 /// Returns `true` if a plugin with the same name is already in the store.
526566 pub fn register < P : Plugin < R > + ' static > ( & mut self , plugin : P ) -> bool {
527- self . store . insert ( plugin. name ( ) , Box :: new ( plugin) ) . is_some ( )
567+ let len = self . store . len ( ) ;
568+ self . store . retain ( |p| p. name ( ) != plugin. name ( ) ) ;
569+ let result = len != self . store . len ( ) ;
570+ self . store . push ( Box :: new ( plugin) ) ;
571+ result
528572 }
529573
530574 /// Removes the plugin with the given name from the store.
531575 pub fn unregister ( & mut self , plugin : & ' static str ) -> bool {
532- self . store . remove ( plugin) . is_some ( )
576+ let len = self . store . len ( ) ;
577+ self . store . retain ( |p| p. name ( ) != plugin) ;
578+ len != self . store . len ( )
533579 }
534580
535581 /// Initializes all plugins in the store.
@@ -538,7 +584,7 @@ impl<R: Runtime> PluginStore<R> {
538584 app : & AppHandle < R > ,
539585 config : & PluginConfig ,
540586 ) -> crate :: Result < ( ) > {
541- self . store . values_mut ( ) . try_for_each ( |plugin| {
587+ self . store . iter_mut ( ) . try_for_each ( |plugin| {
542588 plugin
543589 . initialize (
544590 app,
@@ -552,7 +598,7 @@ impl<R: Runtime> PluginStore<R> {
552598 pub ( crate ) fn initialization_script ( & self ) -> String {
553599 self
554600 . store
555- . values ( )
601+ . iter ( )
556602 . filter_map ( |p| p. initialization_script ( ) )
557603 . fold ( String :: new ( ) , |acc, script| {
558604 format ! ( "{acc}\n (function () {{ {script} }})();" )
@@ -563,35 +609,45 @@ impl<R: Runtime> PluginStore<R> {
563609 pub ( crate ) fn created ( & mut self , window : Window < R > ) {
564610 self
565611 . store
566- . values_mut ( )
612+ . iter_mut ( )
567613 . for_each ( |plugin| plugin. created ( window. clone ( ) ) )
568614 }
569615
616+ pub ( crate ) fn on_navigation ( & mut self , window : & Window < R > , url : & Url ) -> bool {
617+ for plugin in self . store . iter_mut ( ) {
618+ if !plugin. on_navigation ( window, url) {
619+ return false ;
620+ }
621+ }
622+ true
623+ }
624+
570625 /// Runs the on_page_load hook for all plugins in the store.
571626 pub ( crate ) fn on_page_load ( & mut self , window : Window < R > , payload : PageLoadPayload ) {
572627 self
573628 . store
574- . values_mut ( )
629+ . iter_mut ( )
575630 . for_each ( |plugin| plugin. on_page_load ( window. clone ( ) , payload. clone ( ) ) )
576631 }
577632
578633 /// Runs the on_event hook for all plugins in the store.
579634 pub ( crate ) fn on_event ( & mut self , app : & AppHandle < R > , event : & RunEvent ) {
580635 self
581636 . store
582- . values_mut ( )
637+ . iter_mut ( )
583638 . for_each ( |plugin| plugin. on_event ( app, event) )
584639 }
585640
586641 /// Runs the plugin `extend_api` hook if it exists. Returns whether the invoke message was handled or not.
587642 ///
588643 /// The message is not handled when the plugin exists **and** the command does not.
589644 pub ( crate ) fn extend_api ( & mut self , plugin : & str , invoke : Invoke < R > ) -> bool {
590- if let Some ( plugin) = self . store . get_mut ( plugin) {
591- plugin. extend_api ( invoke)
592- } else {
593- invoke. resolver . reject ( format ! ( "plugin {plugin} not found" ) ) ;
594- true
645+ for p in self . store . iter_mut ( ) {
646+ if p. name ( ) == plugin {
647+ return p. extend_api ( invoke) ;
648+ }
595649 }
650+ invoke. resolver . reject ( format ! ( "plugin {plugin} not found" ) ) ;
651+ true
596652 }
597653}
0 commit comments