tauri/ipc/
mod.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! Types and functions related to Inter Procedure Call(IPC).
6//!
7//! This module includes utilities to send messages to the JS layer of the webview.
8
9use std::{
10  future::Future,
11  sync::{Arc, Mutex},
12};
13
14use http::HeaderMap;
15use serde::{
16  de::{DeserializeOwned, IntoDeserializer},
17  Deserialize, Serialize,
18};
19use serde_json::Value as JsonValue;
20pub use serialize_to_javascript::Options as SerializeOptions;
21use tauri_macros::default_runtime;
22use tauri_utils::acl::resolved::ResolvedCommand;
23
24use crate::{webview::Webview, Runtime, StateManager};
25
26mod authority;
27#[cfg(feature = "dynamic-acl")]
28mod capability_builder;
29pub(crate) mod channel;
30mod command;
31pub(crate) mod format_callback;
32pub(crate) mod protocol;
33
34pub use authority::{
35  CommandScope, GlobalScope, Origin, RuntimeAuthority, ScopeObject, ScopeObjectMatch, ScopeValue,
36};
37#[cfg(feature = "dynamic-acl")]
38pub use capability_builder::{CapabilityBuilder, RuntimeCapability};
39pub use channel::{Channel, JavaScriptChannelId};
40pub use command::{private, CommandArg, CommandItem};
41
42/// A closure that is run every time Tauri receives a message it doesn't explicitly handle.
43pub type InvokeHandler<R> = dyn Fn(Invoke<R>) -> bool + Send + Sync + 'static;
44
45/// A closure that is responsible for respond a JS message.
46pub type InvokeResponder<R> =
47  dyn Fn(&Webview<R>, &str, &InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static;
48/// Similar to [`InvokeResponder`] but taking owned arguments.
49pub type OwnedInvokeResponder<R> =
50  dyn FnOnce(Webview<R>, String, InvokeResponse, CallbackFn, CallbackFn) + Send + 'static;
51
52/// Possible values of an IPC payload.
53///
54/// ### Android
55/// On Android, [InvokeBody::Raw] is not supported. The enum will always contain [InvokeBody::Json].
56/// When targeting Android Devices, consider passing raw bytes as a base64 [[std::string::String]], which is still more efficient than passing them as a number array in [InvokeBody::Json]
57#[derive(Debug, Clone)]
58#[cfg_attr(test, derive(PartialEq))]
59pub enum InvokeBody {
60  /// Json payload.
61  Json(JsonValue),
62  /// Bytes payload.
63  Raw(Vec<u8>),
64}
65
66impl Default for InvokeBody {
67  fn default() -> Self {
68    Self::Json(Default::default())
69  }
70}
71
72impl From<JsonValue> for InvokeBody {
73  fn from(value: JsonValue) -> Self {
74    Self::Json(value)
75  }
76}
77
78impl From<Vec<u8>> for InvokeBody {
79  fn from(value: Vec<u8>) -> Self {
80    Self::Raw(value)
81  }
82}
83
84impl InvokeBody {
85  #[cfg(mobile)]
86  pub(crate) fn into_json(self) -> JsonValue {
87    match self {
88      Self::Json(v) => v,
89      Self::Raw(v) => {
90        JsonValue::Array(v.into_iter().map(|n| JsonValue::Number(n.into())).collect())
91      }
92    }
93  }
94}
95
96/// Possible values of an IPC response.
97#[derive(Debug, Clone)]
98#[cfg_attr(test, derive(PartialEq))]
99pub enum InvokeResponseBody {
100  /// Json payload.
101  Json(String),
102  /// Bytes payload.
103  Raw(Vec<u8>),
104}
105
106impl From<String> for InvokeResponseBody {
107  fn from(value: String) -> Self {
108    Self::Json(value)
109  }
110}
111
112impl From<Vec<u8>> for InvokeResponseBody {
113  fn from(value: Vec<u8>) -> Self {
114    Self::Raw(value)
115  }
116}
117
118impl From<InvokeBody> for InvokeResponseBody {
119  fn from(value: InvokeBody) -> Self {
120    match value {
121      InvokeBody::Json(v) => Self::Json(serde_json::to_string(&v).unwrap()),
122      InvokeBody::Raw(v) => Self::Raw(v),
123    }
124  }
125}
126
127impl IpcResponse for InvokeResponseBody {
128  fn body(self) -> crate::Result<InvokeResponseBody> {
129    Ok(self)
130  }
131}
132
133impl InvokeResponseBody {
134  /// Attempts to deserialize the response.
135  pub fn deserialize<T: DeserializeOwned>(self) -> serde_json::Result<T> {
136    match self {
137      Self::Json(v) => serde_json::from_str(&v),
138      Self::Raw(v) => T::deserialize(v.into_deserializer()),
139    }
140  }
141}
142
143/// The IPC request.
144///
145/// Includes the `body` and `headers` parameters of a Tauri command invocation.
146/// This allows commands to accept raw bytes - on all platforms except Android.
147#[derive(Debug)]
148pub struct Request<'a> {
149  body: &'a InvokeBody,
150  headers: &'a HeaderMap,
151}
152
153impl Request<'_> {
154  /// The request body.
155  pub fn body(&self) -> &InvokeBody {
156    self.body
157  }
158
159  /// Thr request headers.
160  pub fn headers(&self) -> &HeaderMap {
161    self.headers
162  }
163}
164
165impl<'a, R: Runtime> CommandArg<'a, R> for Request<'a> {
166  /// Returns the invoke [`Request`].
167  fn from_command(command: CommandItem<'a, R>) -> Result<Self, InvokeError> {
168    Ok(Self {
169      body: command.message.payload(),
170      headers: command.message.headers(),
171    })
172  }
173}
174
175/// Marks a type as a response to an IPC call.
176pub trait IpcResponse {
177  /// Resolve the IPC response body.
178  fn body(self) -> crate::Result<InvokeResponseBody>;
179}
180
181impl<T: Serialize> IpcResponse for T {
182  fn body(self) -> crate::Result<InvokeResponseBody> {
183    serde_json::to_string(&self)
184      .map(Into::into)
185      .map_err(Into::into)
186  }
187}
188
189/// The IPC response.
190pub struct Response {
191  body: InvokeResponseBody,
192}
193
194impl IpcResponse for Response {
195  fn body(self) -> crate::Result<InvokeResponseBody> {
196    Ok(self.body)
197  }
198}
199
200impl Response {
201  /// Defines a response with the given body.
202  pub fn new(body: impl Into<InvokeResponseBody>) -> Self {
203    Self { body: body.into() }
204  }
205}
206
207/// The message and resolver given to a custom command.
208///
209/// This struct is used internally by macros and is explicitly **NOT** stable.
210#[default_runtime(crate::Wry, wry)]
211pub struct Invoke<R: Runtime> {
212  /// The message passed.
213  pub message: InvokeMessage<R>,
214
215  /// The resolver of the message.
216  pub resolver: InvokeResolver<R>,
217
218  /// Resolved ACL for this IPC invoke.
219  pub acl: Option<Vec<ResolvedCommand>>,
220}
221
222/// Error response from an [`InvokeMessage`].
223#[derive(Debug)]
224pub struct InvokeError(pub serde_json::Value);
225
226impl InvokeError {
227  /// Create an [`InvokeError`] as a string of the [`std::error::Error`] message.
228  #[inline(always)]
229  pub fn from_error<E: std::error::Error>(error: E) -> Self {
230    Self(serde_json::Value::String(error.to_string()))
231  }
232
233  /// Create an [`InvokeError`] as a string of the [`anyhow::Error`] message.
234  #[inline(always)]
235  pub fn from_anyhow(error: anyhow::Error) -> Self {
236    Self(serde_json::Value::String(format!("{error:#}")))
237  }
238}
239
240impl<T: Serialize> From<T> for InvokeError {
241  #[inline]
242  fn from(value: T) -> Self {
243    serde_json::to_value(value)
244      .map(Self)
245      .unwrap_or_else(Self::from_error)
246  }
247}
248
249impl From<crate::Error> for InvokeError {
250  #[inline(always)]
251  fn from(error: crate::Error) -> Self {
252    Self(serde_json::Value::String(error.to_string()))
253  }
254}
255
256/// Response from a [`InvokeMessage`] passed to the [`InvokeResolver`].
257#[derive(Debug)]
258pub enum InvokeResponse {
259  /// Resolve the promise.
260  Ok(InvokeResponseBody),
261  /// Reject the promise.
262  Err(InvokeError),
263}
264
265impl<T: IpcResponse, E: Into<InvokeError>> From<Result<T, E>> for InvokeResponse {
266  #[inline]
267  fn from(result: Result<T, E>) -> Self {
268    match result {
269      Ok(ok) => match ok.body() {
270        Ok(value) => Self::Ok(value),
271        Err(err) => Self::Err(InvokeError::from_error(err)),
272      },
273      Err(err) => Self::Err(err.into()),
274    }
275  }
276}
277
278impl From<InvokeError> for InvokeResponse {
279  fn from(error: InvokeError) -> Self {
280    Self::Err(error)
281  }
282}
283
284/// Resolver of a invoke message.
285#[default_runtime(crate::Wry, wry)]
286pub struct InvokeResolver<R: Runtime> {
287  webview: Webview<R>,
288  responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
289  cmd: String,
290  pub(crate) callback: CallbackFn,
291  pub(crate) error: CallbackFn,
292}
293
294impl<R: Runtime> Clone for InvokeResolver<R> {
295  fn clone(&self) -> Self {
296    Self {
297      webview: self.webview.clone(),
298      responder: self.responder.clone(),
299      cmd: self.cmd.clone(),
300      callback: self.callback,
301      error: self.error,
302    }
303  }
304}
305
306impl<R: Runtime> InvokeResolver<R> {
307  pub(crate) fn new(
308    webview: Webview<R>,
309    responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
310    cmd: String,
311    callback: CallbackFn,
312    error: CallbackFn,
313  ) -> Self {
314    Self {
315      webview,
316      responder,
317      cmd,
318      callback,
319      error,
320    }
321  }
322
323  /// Reply to the invoke promise with an async task.
324  pub fn respond_async<T, F>(self, task: F)
325  where
326    T: IpcResponse,
327    F: Future<Output = Result<T, InvokeError>> + Send + 'static,
328  {
329    crate::async_runtime::spawn(async move {
330      Self::return_task(
331        self.webview,
332        self.responder,
333        task,
334        self.cmd,
335        self.callback,
336        self.error,
337      )
338      .await;
339    });
340  }
341
342  /// Reply to the invoke promise with an async task which is already serialized.
343  pub fn respond_async_serialized<F>(self, task: F)
344  where
345    F: Future<Output = Result<InvokeResponseBody, InvokeError>> + Send + 'static,
346  {
347    // Dynamic dispatch the call in dev for a faster compile time
348    // TODO: Revisit this and see if we can do this for the release build as well if the performace hit is not a problem
349    #[cfg(debug_assertions)]
350    {
351      self.respond_async_serialized_dyn(Box::pin(task))
352    }
353    #[cfg(not(debug_assertions))]
354    {
355      self.respond_async_serialized_inner(task)
356    }
357  }
358
359  /// Dynamic dispatch the [`Self::respond_async_serialized`] call
360  #[cfg(debug_assertions)]
361  fn respond_async_serialized_dyn(
362    self,
363    task: std::pin::Pin<
364      Box<dyn Future<Output = Result<InvokeResponseBody, InvokeError>> + Send + 'static>,
365    >,
366  ) {
367    self.respond_async_serialized_inner(task)
368  }
369
370  /// Reply to the invoke promise with an async task which is already serialized.
371  fn respond_async_serialized_inner<F>(self, task: F)
372  where
373    F: Future<Output = Result<InvokeResponseBody, InvokeError>> + Send + 'static,
374  {
375    crate::async_runtime::spawn(async move {
376      let response = match task.await {
377        Ok(ok) => InvokeResponse::Ok(ok),
378        Err(err) => InvokeResponse::Err(err),
379      };
380      Self::return_result(
381        self.webview,
382        self.responder,
383        response,
384        self.cmd,
385        self.callback,
386        self.error,
387      )
388    });
389  }
390
391  /// Reply to the invoke promise with a serializable value.
392  pub fn respond<T: IpcResponse>(self, value: Result<T, InvokeError>) {
393    Self::return_result(
394      self.webview,
395      self.responder,
396      value.into(),
397      self.cmd,
398      self.callback,
399      self.error,
400    )
401  }
402
403  /// Resolve the invoke promise with a value.
404  pub fn resolve<T: IpcResponse>(self, value: T) {
405    self.respond(Ok(value))
406  }
407
408  /// Reject the invoke promise with a value.
409  pub fn reject<T: Serialize>(self, value: T) {
410    Self::return_result(
411      self.webview,
412      self.responder,
413      Result::<(), _>::Err(value).into(),
414      self.cmd,
415      self.callback,
416      self.error,
417    )
418  }
419
420  /// Reject the invoke promise with an [`InvokeError`].
421  pub fn invoke_error(self, error: InvokeError) {
422    Self::return_result(
423      self.webview,
424      self.responder,
425      error.into(),
426      self.cmd,
427      self.callback,
428      self.error,
429    )
430  }
431
432  /// Asynchronously executes the given task
433  /// and evaluates its Result to the JS promise described by the `success_callback` and `error_callback` function names.
434  ///
435  /// If the Result `is_ok()`, the callback will be the `success_callback` function name and the argument will be the Ok value.
436  /// If the Result `is_err()`, the callback will be the `error_callback` function name and the argument will be the Err value.
437  pub async fn return_task<T, F>(
438    webview: Webview<R>,
439    responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
440    task: F,
441    cmd: String,
442    success_callback: CallbackFn,
443    error_callback: CallbackFn,
444  ) where
445    T: IpcResponse,
446    F: Future<Output = Result<T, InvokeError>> + Send + 'static,
447  {
448    let result = task.await;
449    Self::return_closure(
450      webview,
451      responder,
452      || result,
453      cmd,
454      success_callback,
455      error_callback,
456    )
457  }
458
459  pub(crate) fn return_closure<T: IpcResponse, F: FnOnce() -> Result<T, InvokeError>>(
460    webview: Webview<R>,
461    responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
462    f: F,
463    cmd: String,
464    success_callback: CallbackFn,
465    error_callback: CallbackFn,
466  ) {
467    Self::return_result(
468      webview,
469      responder,
470      f().into(),
471      cmd,
472      success_callback,
473      error_callback,
474    )
475  }
476
477  pub(crate) fn return_result(
478    webview: Webview<R>,
479    responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
480    response: InvokeResponse,
481    cmd: String,
482    success_callback: CallbackFn,
483    error_callback: CallbackFn,
484  ) {
485    (responder.lock().unwrap().take().expect("resolver consumed"))(
486      webview,
487      cmd,
488      response,
489      success_callback,
490      error_callback,
491    );
492  }
493}
494
495/// An invoke message.
496#[default_runtime(crate::Wry, wry)]
497#[derive(Debug)]
498pub struct InvokeMessage<R: Runtime> {
499  /// The webview that received the invoke message.
500  pub(crate) webview: Webview<R>,
501  /// Application managed state.
502  pub(crate) state: Arc<StateManager>,
503  /// The IPC command.
504  pub(crate) command: String,
505  /// The JSON argument passed on the invoke message.
506  pub(crate) payload: InvokeBody,
507  /// The request headers.
508  pub(crate) headers: HeaderMap,
509}
510
511impl<R: Runtime> Clone for InvokeMessage<R> {
512  fn clone(&self) -> Self {
513    Self {
514      webview: self.webview.clone(),
515      state: self.state.clone(),
516      command: self.command.clone(),
517      payload: self.payload.clone(),
518      headers: self.headers.clone(),
519    }
520  }
521}
522
523impl<R: Runtime> InvokeMessage<R> {
524  /// Create an new [`InvokeMessage`] from a payload send by a webview.
525  pub(crate) fn new(
526    webview: Webview<R>,
527    state: Arc<StateManager>,
528    command: String,
529    payload: InvokeBody,
530    headers: HeaderMap,
531  ) -> Self {
532    Self {
533      webview,
534      state,
535      command,
536      payload,
537      headers,
538    }
539  }
540
541  /// The invoke command.
542  #[inline(always)]
543  pub fn command(&self) -> &str {
544    &self.command
545  }
546
547  /// The webview that received the invoke.
548  #[inline(always)]
549  pub fn webview(&self) -> Webview<R> {
550    self.webview.clone()
551  }
552
553  /// A reference to webview that received the invoke.
554  #[inline(always)]
555  pub fn webview_ref(&self) -> &Webview<R> {
556    &self.webview
557  }
558
559  /// A reference to the payload the invoke received.
560  #[inline(always)]
561  pub fn payload(&self) -> &InvokeBody {
562    &self.payload
563  }
564
565  /// The state manager associated with the application
566  #[inline(always)]
567  pub fn state(&self) -> Arc<StateManager> {
568    self.state.clone()
569  }
570
571  /// A reference to the state manager associated with application.
572  #[inline(always)]
573  pub fn state_ref(&self) -> &StateManager {
574    &self.state
575  }
576
577  /// The request headers.
578  #[inline(always)]
579  pub fn headers(&self) -> &HeaderMap {
580    &self.headers
581  }
582}
583
584/// The `Callback` type is the return value of the `transformCallback` JavaScript function.
585#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
586pub struct CallbackFn(pub u32);
587
588#[cfg(test)]
589mod tests {
590  use super::*;
591
592  #[test]
593  fn deserialize_invoke_response_body() {
594    let json = InvokeResponseBody::Json("[1, 123, 1231]".to_string());
595    assert_eq!(json.deserialize::<Vec<u16>>().unwrap(), vec![1, 123, 1231]);
596
597    let json = InvokeResponseBody::Json("\"string value\"".to_string());
598    assert_eq!(json.deserialize::<String>().unwrap(), "string value");
599
600    let json = InvokeResponseBody::Json("\"string value\"".to_string());
601    assert!(json.deserialize::<Vec<u16>>().is_err());
602
603    let values = vec![1, 2, 3, 4, 5, 6, 1];
604    let raw = InvokeResponseBody::Raw(values.clone());
605    assert_eq!(raw.deserialize::<Vec<u8>>().unwrap(), values);
606  }
607}