Skip to content

Commit

Permalink
feat(core): add channel interceptor API (#11362)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog authored Oct 14, 2024
1 parent bcf2792 commit e3b09be
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changes/channel-interceptor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch:enhance
---

Added `Builder::channel_interceptor` to intercept messages to be sent to the frontend, complemeting the `Builder::invoke_system` interface.
28 changes: 25 additions & 3 deletions crates/tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
use crate::{
image::Image,
ipc::{
channel::ChannelDataIpcQueue, CommandArg, CommandItem, Invoke, InvokeError, InvokeHandler,
channel::ChannelDataIpcQueue, CallbackFn, CommandArg, CommandItem, Invoke, InvokeError,
InvokeHandler, InvokeResponseBody,
},
manager::{webview::UriSchemeProtocol, AppManager, Asset},
plugin::{Plugin, PluginStore},
Expand All @@ -15,8 +16,7 @@ use crate::{
ExitRequestedEventAction, RunEvent as RuntimeRunEvent,
},
sealed::{ManagerBase, RuntimeOrDispatch},
utils::config::Config,
utils::Env,
utils::{config::Config, Env},
webview::PageLoadPayload,
Context, DeviceEventFilter, Emitter, EventLoopMessage, Listener, Manager, Monitor, Result,
Runtime, Scopes, StateManager, Theme, Webview, WebviewWindowBuilder, Window,
Expand Down Expand Up @@ -66,6 +66,8 @@ pub type SetupHook<R> =
Box<dyn FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send>;
/// A closure that is run every time a page starts or finishes loading.
pub type OnPageLoad<R> = dyn Fn(&Webview<R>, &PageLoadPayload<'_>) + Send + Sync + 'static;
pub type ChannelInterceptor<R> =
Box<dyn Fn(&Webview<R>, CallbackFn, usize, &InvokeResponseBody) -> bool + Send + Sync + 'static>;

/// The exit code on [`RunEvent::ExitRequested`] when [`AppHandle#method.restart`] is called.
pub const RESTART_EXIT_CODE: i32 = i32::MAX;
Expand Down Expand Up @@ -1205,6 +1207,8 @@ pub struct Builder<R: Runtime> {
/// The script that initializes the `window.__TAURI_INTERNALS__.postMessage` function.
pub(crate) invoke_initialization_script: String,

channel_interceptor: Option<ChannelInterceptor<R>>,

/// The setup hook.
setup: SetupHook<R>,

Expand Down Expand Up @@ -1291,6 +1295,7 @@ impl<R: Runtime> Builder<R> {
.render_default(&Default::default())
.unwrap()
.into_string(),
channel_interceptor: None,
on_page_load: None,
plugins: PluginStore::default(),
uri_scheme_protocols: Default::default(),
Expand Down Expand Up @@ -1370,6 +1375,22 @@ impl<R: Runtime> Builder<R> {
self
}

/// Registers a channel interceptor that can overwrite the default channel implementation.
///
/// If the event has been consumed, it must return `true`.
///
/// The channel automatically orders the messages, so the third closure argument represents the message number.
/// The payload expected by the channel receiver is in the form of `{ id: usize, message: T }`.
pub fn channel_interceptor<
F: Fn(&Webview<R>, CallbackFn, usize, &InvokeResponseBody) -> bool + Send + Sync + 'static,
>(
mut self,
interceptor: F,
) -> Self {
self.channel_interceptor.replace(Box::new(interceptor));
self
}

/// Append a custom initialization script.
///
/// Allow to append custom initialization script instend of replacing entire invoke system.
Expand Down Expand Up @@ -1856,6 +1877,7 @@ tauri::Builder::default()
#[cfg(desktop)]
HashMap::new(),
self.invoke_initialization_script,
self.channel_interceptor,
self.invoke_key,
));

Expand Down
11 changes: 8 additions & 3 deletions crates/tauri/src/ipc/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ use super::{CallbackFn, InvokeError, InvokeResponseBody, IpcResponse, Request, R

pub const IPC_PAYLOAD_PREFIX: &str = "__CHANNEL__:";
pub const CHANNEL_PLUGIN_NAME: &str = "__TAURI_CHANNEL__";
// TODO: ideally this const references CHANNEL_PLUGIN_NAME
pub const FETCH_CHANNEL_DATA_COMMAND: &str = "plugin:__TAURI_CHANNEL__|fetch";
pub(crate) const CHANNEL_ID_HEADER_NAME: &str = "Tauri-Channel-Id";

Expand Down Expand Up @@ -101,6 +100,14 @@ impl JavaScriptChannelId {
let counter = AtomicUsize::new(0);

Channel::new_with_id(callback_id.0, move |body| {
let i = counter.fetch_add(1, Ordering::Relaxed);

if let Some(interceptor) = &webview.manager.channel_interceptor {
if interceptor(&webview, callback_id, i, &body) {
return Ok(());
}
}

let data_id = CHANNEL_DATA_COUNTER.fetch_add(1, Ordering::Relaxed);

webview
Expand All @@ -110,8 +117,6 @@ impl JavaScriptChannelId {
.unwrap()
.insert(data_id, body);

let i = counter.fetch_add(1, Ordering::Relaxed);

webview.eval(&format!(
"window.__TAURI_INTERNALS__.invoke('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': '{data_id}' }} }}).then((response) => window['_' + {}]({{ message: response, id: {i} }})).catch(console.error)",
callback_id.0
Expand Down
2 changes: 2 additions & 0 deletions crates/tauri/src/ipc/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ mod tests {
Default::default(),
Default::default(),
"".into(),
None,
crate::generate_invoke_key().unwrap(),
);

Expand Down Expand Up @@ -704,6 +705,7 @@ mod tests {
Default::default(),
Default::default(),
"".into(),
None,
crate::generate_invoke_key().unwrap(),
);

Expand Down
10 changes: 9 additions & 1 deletion crates/tauri/src/manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ use tauri_utils::{
};

use crate::{
app::{AppHandle, GlobalWebviewEventListener, GlobalWindowEventListener, OnPageLoad},
app::{
AppHandle, ChannelInterceptor, GlobalWebviewEventListener, GlobalWindowEventListener,
OnPageLoad,
},
event::{assert_event_name_is_valid, Event, EventId, EventTarget, Listeners},
ipc::{Invoke, InvokeHandler, RuntimeAuthority},
plugin::PluginStore,
Expand Down Expand Up @@ -216,6 +219,8 @@ pub struct AppManager<R: Runtime> {

/// Runtime-generated invoke key.
pub(crate) invoke_key: String,

pub(crate) channel_interceptor: Option<ChannelInterceptor<R>>,
}

impl<R: Runtime> fmt::Debug for AppManager<R> {
Expand Down Expand Up @@ -256,6 +261,7 @@ impl<R: Runtime> AppManager<R> {
crate::app::GlobalMenuEventListener<Window<R>>,
>,
invoke_initialization_script: String,
channel_interceptor: Option<ChannelInterceptor<R>>,
invoke_key: String,
) -> Self {
// generate a random isolation key at runtime
Expand Down Expand Up @@ -307,6 +313,7 @@ impl<R: Runtime> AppManager<R> {
plugin_global_api_scripts: Arc::new(context.plugin_global_api_scripts),
resources_table: Arc::default(),
invoke_key,
channel_interceptor,
}
}

Expand Down Expand Up @@ -733,6 +740,7 @@ mod test {
Default::default(),
Default::default(),
"".into(),
None,
crate::generate_invoke_key().unwrap(),
);

Expand Down

0 comments on commit e3b09be

Please sign in to comment.