@@ -29,8 +29,8 @@ use crate::{
2929 app:: { UriSchemeResponder , WebviewEvent } ,
3030 event:: { EmitArgs , EventTarget } ,
3131 ipc:: {
32- CallbackFn , CommandArg , CommandItem , Invoke , InvokeBody , InvokeError , InvokeMessage ,
33- InvokeResolver , Origin , OwnedInvokeResponder ,
32+ CallbackFn , CommandArg , CommandItem , CommandScope , GlobalScope , Invoke , InvokeBody ,
33+ InvokeError , InvokeMessage , InvokeResolver , Origin , OwnedInvokeResponder , ScopeObject ,
3434 } ,
3535 manager:: AppManager ,
3636 sealed:: { ManagerBase , RuntimeOrDispatch } ,
@@ -880,6 +880,83 @@ impl<R: Runtime> Webview<R> {
880880 . dispatcher
881881 . on_webview_event ( move |event| f ( & event. clone ( ) . into ( ) ) ) ;
882882 }
883+
884+ /// Resolves the given command scope for this webview on the currently loaded URL.
885+ ///
886+ /// If the command is not allowed, returns None.
887+ ///
888+ /// If the scope cannot be deserialized to the given type, an error is returned.
889+ ///
890+ /// In a command context this can be directly resolved from the command arguments via [CommandScope]:
891+ ///
892+ /// ```
893+ /// use tauri::ipc::CommandScope;
894+ ///
895+ /// #[derive(Debug, serde::Deserialize)]
896+ /// struct ScopeType {
897+ /// some_value: String,
898+ /// }
899+ /// #[tauri::command]
900+ /// fn my_command(scope: CommandScope<ScopeType>) {
901+ /// // check scope
902+ /// }
903+ /// ```
904+ ///
905+ /// # Examples
906+ ///
907+ /// ```
908+ /// use tauri::Manager;
909+ ///
910+ /// #[derive(Debug, serde::Deserialize)]
911+ /// struct ScopeType {
912+ /// some_value: String,
913+ /// }
914+ ///
915+ /// tauri::Builder::default()
916+ /// .setup(|app| {
917+ /// let webview = app.get_webview_window("main").unwrap();
918+ /// let scope = webview.resolve_command_scope::<ScopeType>("my-plugin", "read");
919+ /// Ok(())
920+ /// });
921+ /// ```
922+ pub fn resolve_command_scope < T : ScopeObject > (
923+ & self ,
924+ plugin : & str ,
925+ command : & str ,
926+ ) -> crate :: Result < Option < ResolvedScope < T > > > {
927+ let current_url = self . url ( ) ?;
928+ let is_local = self . is_local_url ( & current_url) ;
929+ let origin = if is_local {
930+ Origin :: Local
931+ } else {
932+ Origin :: Remote { url : current_url }
933+ } ;
934+
935+ let cmd_name = format ! ( "plugin:{plugin}|{command}" ) ;
936+ let resolved_access = self
937+ . manager ( )
938+ . runtime_authority
939+ . lock ( )
940+ . unwrap ( )
941+ . resolve_access ( & cmd_name, self . window ( ) . label ( ) , self . label ( ) , & origin) ;
942+
943+ if let Some ( access) = resolved_access {
944+ let scope_ids = access
945+ . iter ( )
946+ . filter_map ( |cmd| cmd. scope_id )
947+ . collect :: < Vec < _ > > ( ) ;
948+
949+ let command_scope = CommandScope :: resolve ( self , scope_ids) ?;
950+ let global_scope = GlobalScope :: resolve ( self , plugin) ?;
951+
952+ Ok ( Some ( ResolvedScope {
953+ global_scope,
954+ command_scope,
955+ } ) )
956+ } else {
957+ Ok ( None )
958+ }
959+ }
883960}
884961
885962/// Desktop webview setters and actions.
@@ -1702,6 +1779,24 @@ impl<'de, R: Runtime> CommandArg<'de, R> for Webview<R> {
17021779 }
17031780}
17041781
1782+ /// Resolved scope that can be obtained via [`Webview::resolve_command_scope`].
1783+ pub struct ResolvedScope < T : ScopeObject > {
1784+ command_scope : CommandScope < T > ,
1785+ global_scope : GlobalScope < T > ,
1786+ }
1787+
1788+ impl < T : ScopeObject > ResolvedScope < T > {
1789+ /// The global plugin scope.
1790+ pub fn global_scope ( & self ) -> & GlobalScope < T > {
1791+ & self . global_scope
1792+ }
1793+
1794+ /// The command-specific scope.
1795+ pub fn command_scope ( & self ) -> & CommandScope < T > {
1796+ & self . command_scope
1797+ }
1798+ }
1799+
17051800#[ cfg( test) ]
17061801mod tests {
17071802 #[ test]
0 commit comments