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