Compare commits

...

3 Commits

Author SHA1 Message Date
Lucas Nogueira
1259ca56b2 update example 2025-10-09 14:26:32 -03:00
Lucas Nogueira
2ece5d7adf added emit2 and emitTo2 instead 2025-10-09 14:21:33 -03:00
Lucas Nogueira
95f6c48dc0 feat(core): optimize raw payload in event system, closes #13405 2025-10-09 13:09:31 -03:00
16 changed files with 556 additions and 49 deletions

5
.changes/emit-raw-api.md Normal file
View File

@@ -0,0 +1,5 @@
---
"@tauri-apps/api": minor:feat
---
Optimize raw Uint8Array/ArrayBuffer/number[] payloads on the event system via the emit2 and emitTo2 functions.

View File

@@ -0,0 +1,5 @@
---
"tauri": minor:feat
---
Allow sending raw binary payloads using the event system through `Emitter::emit_raw` and `Emitter::emit_raw_to`.

View File

@@ -0,0 +1,5 @@
---
"tauri": minor:feat
---
Allow reading raw binary payloads using the event system through [`Event::payload_raw`].

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@ use crate::{
channel::ChannelDataIpcQueue, CallbackFn, CommandArg, CommandItem, Invoke, InvokeError,
InvokeHandler, InvokeResponseBody,
},
manager::{webview::UriSchemeProtocol, AppManager, Asset},
manager::{webview::UriSchemeProtocol, AppManager, Asset, EventPayloadStore},
plugin::{Plugin, PluginStore},
resources::ResourceTable,
runtime::{
@@ -2260,6 +2260,7 @@ tauri::Builder::default()
});
app.manage(ChannelDataIpcQueue::default());
app.manage(EventPayloadStore::default());
app.handle.plugin(crate::ipc::channel::plugin())?;
let handle = app.handle();

View File

@@ -201,7 +201,11 @@ impl Listeners {
let handlers = handlers.filter(|(_, h)| match_any_or_filter(&h.target, &filter));
for (&id, Handler { callback, .. }) in handlers {
maybe_pending = true;
(callback)(Event::new(id, emit_args.payload.clone()))
(callback)(Event::new(
id,
emit_args.payload.clone(),
emit_args.raw.clone(),
))
}
}
}
@@ -266,6 +270,37 @@ impl Listeners {
})
}
/// Filters the webviews that are matched by the filter has a JS listener for the given event
pub(crate) fn webiews_matching_event_filter<'a, R, I, F>(
&self,
webviews: I,
emit_args: &EmitArgs,
filter: Option<F>,
) -> Vec<&'a Webview<R>>
where
R: Runtime,
I: Iterator<Item = &'a Webview<R>>,
F: Fn(&EventTarget) -> bool,
{
let event = &emit_args.event;
let js_listeners = self.inner.js_event_listeners.lock().unwrap();
webviews
.filter(|webview| {
if let Some(handlers) = js_listeners.get(webview.label()).and_then(|s| s.get(event)) {
let ids = handlers
.iter()
.filter(|handler| match_any_or_filter(&handler.target, &filter))
.map(|handler| handler.id)
.collect::<Vec<_>>();
if !ids.is_empty() {
return true;
}
}
false
})
.collect()
}
pub(crate) fn emit_js_filter<'a, R, I, F>(
&self,
mut webviews: I,

View File

@@ -13,7 +13,11 @@ mod event_name;
pub(crate) use event_name::EventName;
use crate::ipc::CallbackFn;
use crate::{
event::plugin::{FETCH_EVENT_PAYLOAD_COMMAND, PAYLOAD_ID_HEADER_NAME},
ipc::CallbackFn,
manager::BINARY_EVENT_PAYLOAD_PREFIX,
};
/// Unique id of an event.
pub type EventId = u32;
@@ -119,6 +123,8 @@ pub struct EmitArgs {
event: EventName,
/// Serialized payload.
payload: String,
/// Raw payload bytes.
raw: Option<Vec<u8>>,
}
impl EmitArgs {
@@ -128,17 +134,33 @@ impl EmitArgs {
Ok(EmitArgs {
event: event.into_owned(),
payload: serde_json::to_string(payload)?,
raw: None,
})
}
pub fn new_str(event: EventName<&str>, payload: String) -> crate::Result<Self> {
pub fn new_raw<S: Serialize>(
event: EventName<&str>,
payload: &S,
raw_data: Vec<u8>,
) -> crate::Result<Self> {
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("window::emit::json").entered();
let _span = tracing::debug_span!("window::emit::serialize").entered();
Ok(EmitArgs {
event: event.into_owned(),
payload,
payload: serde_json::to_string(payload)?,
raw: Some(raw_data),
})
}
pub fn new_str(event: EventName<&str>, payload: String) -> Self {
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("window::emit::json").entered();
EmitArgs {
event: event.into_owned(),
payload,
raw: None,
}
}
}
/// An event that was emitted.
@@ -146,11 +168,12 @@ impl EmitArgs {
pub struct Event {
id: EventId,
data: String,
raw: Option<Vec<u8>>,
}
impl Event {
fn new(id: EventId, data: String) -> Self {
Self { id, data }
fn new(id: EventId, data: String, raw: Option<Vec<u8>>) -> Self {
Self { id, data, raw }
}
/// The [`EventId`] of the handler that was triggered.
@@ -159,9 +182,28 @@ impl Event {
}
/// The event payload.
///
/// Note that you must use [`Self::payload_raw`] for events emitted with a binary payload.
pub fn payload(&self) -> &str {
&self.data
}
/// Consumes the event and return its payload.
///
/// Note that you must use [`Self::into_payload_raw`] for events emitted with a binary payload.
pub fn into_payload(self) -> String {
self.data
}
/// The event payload bytes.
pub fn payload_raw(&self) -> &[u8] {
self.raw.as_deref().unwrap_or(self.data.as_bytes())
}
/// Consumes the event and return its payload bytes.
pub fn into_payload_raw(self) -> Vec<u8> {
self.raw.unwrap_or_else(|| self.data.into_bytes())
}
}
pub(crate) fn listen_js_script(
@@ -224,8 +266,15 @@ pub(crate) fn unlisten_js_script(
pub(crate) fn event_initialization_script(function_name: &str, listeners: &str) -> String {
format!(
"Object.defineProperty(window, '{function_name}', {{
value: function (eventData, ids) {{
value: async function (eventData, ids) {{
const listeners = (window['{listeners}'] && window['{listeners}'][eventData.event]) || []
if (typeof eventData.payload === 'string' && eventData.payload.startsWith('{BINARY_EVENT_PAYLOAD_PREFIX}')) {{
const payloadId = eventData.payload.slice('{BINARY_EVENT_PAYLOAD_PREFIX}'.length)
const data = await window.__TAURI_INTERNALS__.invoke('{FETCH_EVENT_PAYLOAD_COMMAND}', null, {{ headers: {{ '{PAYLOAD_ID_HEADER_NAME}': payloadId }} }})
eventData.payload = data
}}
for (const id of ids) {{
const listener = listeners[id]
if (listener) {{
@@ -235,7 +284,7 @@ pub(crate) fn event_initialization_script(function_name: &str, listeners: &str)
}}
}}
}});
"
",
)
}

View File

@@ -1,16 +1,23 @@
use serde::Deserialize;
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use serde_json::Value as JsonValue;
use serialize_to_javascript::{default_template, DefaultTemplate, Template};
use crate::ipc::{InvokeBody, Request, Response};
use crate::manager::EventPayloadStore;
use crate::plugin::{Builder, TauriPlugin};
use crate::{command, ipc::CallbackFn, EventId, Result, Runtime};
use crate::{AppHandle, Emitter, Manager, Webview};
use crate::{AppHandle, Emitter, Manager, State, Webview};
use super::EventName;
use super::EventTarget;
const EVENT_NAME_HEADER_NAME: &str = "Tauri-Event-Name";
const EVENT_TARGET_HEADER_NAME: &str = "Tauri-Event-Target";
pub const FETCH_EVENT_PAYLOAD_COMMAND: &str = "plugin:event|fetch_payload";
pub const PAYLOAD_ID_HEADER_NAME: &str = "Tauri-Payload-Id";
#[command(root = "crate")]
async fn listen<R: Runtime>(
webview: Webview<R>,
@@ -31,22 +38,106 @@ async fn unlisten<R: Runtime>(
}
#[command(root = "crate")]
async fn emit<R: Runtime>(
app: AppHandle<R>,
async fn emit<R: Runtime>(app: AppHandle<R>, request: Request<'_>) -> Result<()> {
if let Some(event_header) = request
.headers()
.get(EVENT_NAME_HEADER_NAME)
.and_then(|v| v.to_str().ok())
{
let event = EventName::new(event_header)?;
match request.body() {
InvokeBody::Json(payload) => app.emit(event.as_str(), payload),
InvokeBody::Raw(payload) => app.emit_raw(event.as_str(), payload.clone()),
}
} else if let InvokeBody::Json(payload) = request.body() {
let args: EmitArgs = serde_json::from_value(payload.clone())?;
app.emit(args.event.as_str(), args.payload)
} else {
Err(anyhow::anyhow!("unexpected emit request format").into())
}
}
#[derive(Deserialize)]
struct EmitArgs {
event: EventName,
payload: Option<JsonValue>,
) -> Result<()> {
app.emit(event.as_str(), payload)
payload: Option<serde_json::Value>,
}
#[command(root = "crate")]
async fn emit_to<R: Runtime>(
app: AppHandle<R>,
target: EventTarget,
async fn emit_to<R: Runtime>(app: AppHandle<R>, request: Request<'_>) -> Result<()> {
if let Some(event_header) = request
.headers()
.get(EVENT_NAME_HEADER_NAME)
.and_then(|v| v.to_str().ok())
{
let event = EventName::new(event_header)?;
let target = if let Some(target_header) = request
.headers()
.get(EVENT_TARGET_HEADER_NAME)
.and_then(|v| v.to_str().ok())
{
serde_json::from_str::<EventTarget>(target_header)?
} else {
return Err(anyhow::anyhow!("missing event target header").into());
};
match request.body() {
InvokeBody::Json(payload) => app.emit_to(target, event.as_str(), payload),
InvokeBody::Raw(payload) => app.emit_raw_to(target, event.as_str(), payload.clone()),
}
} else if let InvokeBody::Json(payload) = request.body() {
let args: EmitToArgs = serde_json::from_value(payload.clone())?;
app.emit_to(args.target, args.event.as_str(), args.payload)
} else {
Err(anyhow::anyhow!("unexpected emit request format").into())
}
}
#[derive(Deserialize)]
struct EmitToArgs {
event: EventName,
payload: Option<JsonValue>,
) -> Result<()> {
app.emit_to(target, event.as_str(), payload)
payload: Option<serde_json::Value>,
target: EventTarget,
}
#[command(root = "crate")]
fn fetch_payload(
request: Request<'_>,
store: State<'_, EventPayloadStore>,
) -> std::result::Result<Response, &'static str> {
if let Some(id) = request
.headers()
.get(PAYLOAD_ID_HEADER_NAME)
.and_then(|v| v.to_str().ok())
.and_then(|id| id.parse().ok())
{
let r = if let Some(data) = store.0.lock().unwrap().get(&id) {
// fetch_add returns the previous value so we must +1
let read_count = data
.read_count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
+ 1;
Ok((
Response::new(data.payload.clone()),
read_count == data.target_count,
))
} else {
Err("data not found")
};
match r {
Ok((response, done)) => {
if done {
store.0.lock().unwrap().remove(&id);
}
Ok(response)
}
Err(e) => Err(e),
}
} else {
Err("missing payload id header")
}
}
/// Initializes the event plugin.
@@ -70,7 +161,7 @@ pub(crate) fn init<R: Runtime, M: Manager<R>>(manager: &M) -> TauriPlugin<R> {
Builder::new("event")
.invoke_handler(crate::generate_handler![
#![plugin(event)]
listen, unlisten, emit, emit_to
listen, unlisten, emit, emit_to, fetch_payload
])
.js_init_script(
init_script

View File

@@ -990,6 +990,16 @@ pub trait Emitter<R: Runtime>: sealed::ManagerBase<R> {
self.manager().emit(event, payload)
}
/// Similar to [`Emitter::emit`] but the payload is raw binary data.
///
/// This function is optimized to emit event with large binary payloads,
/// avoiding serialization costs.
fn emit_raw(&self, event: &str, payload: Vec<u8>) -> Result<()> {
let event = EventName::new(event)?;
let payload = EmitPayload::<()>::Binary(payload);
self.manager().emit(event, payload)
}
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
@@ -1032,6 +1042,16 @@ pub trait Emitter<R: Runtime>: sealed::ManagerBase<R> {
self.manager().emit_to(target.into(), event, payload)
}
/// Similar to [`Emitter::emit_to`] but the payload is json serialized.
fn emit_raw_to<I>(&self, target: I, event: &str, payload: Vec<u8>) -> Result<()>
where
I: Into<EventTarget>,
{
let event = EventName::new(event)?;
let payload = EmitPayload::<()>::Binary(payload);
self.manager().emit_to(target.into(), event, payload)
}
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples

View File

@@ -6,7 +6,10 @@ use std::{
borrow::Cow,
collections::HashMap,
fmt,
sync::{atomic::AtomicBool, Arc, Mutex, MutexGuard},
sync::{
atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering},
Arc, Mutex, MutexGuard,
},
};
use serde::Serialize;
@@ -38,6 +41,9 @@ mod tray;
pub mod webview;
pub mod window;
static EVENT_ID_COUNTER: AtomicU32 = AtomicU32::new(0);
pub const BINARY_EVENT_PAYLOAD_PREFIX: &str = "__tauri_event_payload_id__:";
#[derive(Default)]
/// Spaced and quoted Content-Security-Policy hash values.
struct CspHashStrings {
@@ -249,6 +255,7 @@ impl<R: Runtime> fmt::Debug for AppManager<R> {
pub(crate) enum EmitPayload<'a, S: Serialize> {
Serialize(&'a S),
Str(String),
Binary(Vec<u8>),
}
impl<R: Runtime> AppManager<R> {
@@ -546,16 +553,48 @@ impl<R: Runtime> AppManager<R> {
event: EventName<&str>,
payload: EmitPayload<'_, S>,
) -> crate::Result<()> {
let webviews = self
.webview
.webviews_lock()
.values()
.cloned()
.collect::<Vec<_>>();
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("emit::run").entered();
let emit_args = match payload {
EmitPayload::Serialize(payload) => EmitArgs::new(event, payload)?,
EmitPayload::Str(payload) => EmitArgs::new_str(event, payload)?,
EmitPayload::Str(payload) => EmitArgs::new_str(event, payload),
EmitPayload::Binary(payload) => {
let id = EVENT_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
if !webviews.is_empty() {
self
.state()
.get::<EventPayloadStore>()
.0
.lock()
.unwrap()
.insert(
id,
PendingEventPayload {
target_count: webviews.len(),
read_count: AtomicUsize::new(0),
payload: payload.clone(),
},
);
}
EmitArgs::new_raw(
event,
&format!("{BINARY_EVENT_PAYLOAD_PREFIX}{id}"),
payload,
)?
}
};
let listeners = self.listeners();
listeners.emit_js(self.webview.webviews_lock().values(), &emit_args)?;
listeners.emit_js(webviews.iter(), &emit_args)?;
listeners.emit(emit_args)?;
Ok(())
@@ -577,18 +616,50 @@ impl<R: Runtime> AppManager<R> {
{
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("emit::run").entered();
let emit_args = match payload {
EmitPayload::Serialize(payload) => EmitArgs::new(event, payload)?,
EmitPayload::Str(payload) => EmitArgs::new_str(event, payload)?,
};
let listeners = self.listeners();
let webviews = self
.webview
.webviews_lock()
.values()
.cloned()
.collect::<Vec<_>>();
listeners.emit_js_filter(
self.webview.webviews_lock().values(),
&emit_args,
Some(&filter),
)?;
let emit_args = match payload {
EmitPayload::Serialize(payload) => EmitArgs::new(event, payload)?,
EmitPayload::Str(payload) => EmitArgs::new_str(event, payload),
EmitPayload::Binary(payload) => {
let id = EVENT_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
let emit_args = EmitArgs::new_raw(
event,
&format!("{BINARY_EVENT_PAYLOAD_PREFIX}{id}"),
payload.clone(),
)?;
if !webviews.is_empty() {
self
.state()
.get::<EventPayloadStore>()
.0
.lock()
.unwrap()
.insert(
id,
PendingEventPayload {
target_count: listeners
.webiews_matching_event_filter(webviews.iter(), &emit_args, Some(&filter))
.len(),
read_count: AtomicUsize::new(0),
payload,
},
);
}
emit_args
}
};
listeners.emit_js_filter(webviews.iter(), &emit_args, Some(&filter))?;
listeners.emit_filter(emit_args, Some(filter))?;
@@ -714,6 +785,16 @@ impl<R: Runtime> AppManager<R> {
}
}
/// Maps a binary event payload id to a pending payload that must be send to the JavaScript side via the IPC.
#[derive(Default, Clone)]
pub struct EventPayloadStore(pub Arc<Mutex<HashMap<u32, PendingEventPayload>>>);
pub struct PendingEventPayload {
pub target_count: usize,
pub read_count: AtomicUsize,
pub payload: Vec<u8>,
}
#[cfg(test)]
mod tests {
use super::replace_with_callback;

View File

@@ -1802,6 +1802,7 @@ tauri::Builder::default()
if (plugin_command.is_some() || has_app_acl_manifest)
// TODO: Remove this special check in v3
&& request.cmd != crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND
&& request.cmd != crate::event::plugin::FETCH_EVENT_PAYLOAD_COMMAND
&& invoke.acl.is_none()
{
#[cfg(debug_assertions)]

View File

@@ -164,6 +164,15 @@ pub fn run_app<R: Runtime, F: FnOnce(&App<R>) + Send + 'static>(
.emit("rust-event", Some(reply))
.expect("failed to emit");
});
let webview_ = webview.clone();
webview.listen("raw-js-event", move |event| {
println!("got raw-js-event with message '{:?}'", event.payload_raw());
webview_
.emit_raw("raw-rust-event", event.into_payload_raw())
.expect("failed to emit");
});
}
});

View File

@@ -4,16 +4,25 @@
import { onMount, onDestroy } from 'svelte'
let { onMessage } = $props()
let unlisten
const unlisten = []
const webviewWindow = getCurrentWebviewWindow()
onMount(async () => {
unlisten = await webviewWindow.listen('rust-event', onMessage)
const unlistenFn1 = await webviewWindow.listen('rust-event', onMessage)
unlisten.push(unlistenFn1)
const unlistenFn2 = await webviewWindow.listen('raw-rust-event', (event) => {
onMessage({
event: event.event,
id: event.id,
payload: Array.from(new Uint8Array(event.payload)),
})
})
unlisten.push(unlistenFn2)
})
onDestroy(() => {
if (unlisten) {
unlisten()
for (const unlistenFn of unlisten) {
unlistenFn()
}
})
@@ -55,6 +64,10 @@
function emitEvent() {
webviewWindow.emit('js-event', 'this is the payload string')
}
function emitRawEvent() {
webviewWindow.emit2('raw-js-event', new Uint8Array([1, 2, 3]))
}
</script>
<div>
@@ -65,6 +78,9 @@
<button class="btn" id="event" onclick={emitEvent}>
Send event to Rust
</button>
<button class="btn" id="event" onclick={emitRawEvent}>
Send raw event to Rust
</button>
<button class="btn" id="request" onclick={echo}> Echo </button>
<button class="btn" id="request" onclick={spam}> Spam </button>
</div>

View File

@@ -172,6 +172,8 @@ async function once<T>(
/**
* Emits an event to all {@link EventTarget|targets}.
*
* Prefer {@link emit2} to send binary data.
*
* @example
* ```typescript
* import { emit } from '@tauri-apps/api/event';
@@ -184,15 +186,36 @@ async function once<T>(
* @since 1.0.0
*/
async function emit<T>(event: string, payload?: T): Promise<void> {
await invoke('plugin:event|emit', {
event,
payload
await invoke('plugin:event|emit', { event, payload })
}
/**
* Emits an event to all {@link EventTarget|targets}.
*
* This function is similar to {@link emit} but it is optimized for binary payloads.
*
* @example
* ```typescript
* import { emit2 } from '@tauri-apps/api/event';
* await emit2('frontend-loaded', { loggedIn: true, token: 'authToken' });
* ```
*
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*
* @since 2.10.0
*/
async function emit2<T>(event: string, payload?: T): Promise<void> {
await invoke('plugin:event|emit', payload as any, {
headers: { 'Tauri-Event-Name': event }
})
}
/**
* Emits an event to all {@link EventTarget|targets} matching the given target.
*
* Prefer {@link emitTo2} to send binary data.
*
* @example
* ```typescript
* import { emitTo } from '@tauri-apps/api/event';
@@ -212,10 +235,38 @@ async function emitTo<T>(
): Promise<void> {
const eventTarget: EventTarget =
typeof target === 'string' ? { kind: 'AnyLabel', label: target } : target
await invoke('plugin:event|emit_to', {
target: eventTarget,
event,
payload
await invoke('plugin:event|emit_to', { event, payload, target: eventTarget })
}
/**
* Emits an event to all {@link EventTarget|targets} matching the given target.
*
* This function is similar to {@link emitTo} but it is optimized for binary payloads.
*
* @example
* ```typescript
* import { emitTo2 } from '@tauri-apps/api/event';
* await emitTo2('main', 'frontend-loaded', { loggedIn: true, token: 'authToken' });
* ```
*
* @param target Label of the target Window/Webview/WebviewWindow or raw {@link EventTarget} object.
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*
* @since 2.10.0
*/
async function emitTo2<T>(
target: EventTarget | string,
event: string,
payload?: T
): Promise<void> {
const eventTarget: EventTarget =
typeof target === 'string' ? { kind: 'AnyLabel', label: target } : target
await invoke('plugin:event|emit_to', payload as any, {
headers: {
'Tauri-Event-Name': event,
'Tauri-Event-Target': JSON.stringify(eventTarget)
}
})
}
@@ -228,4 +279,4 @@ export type {
Options
}
export { listen, once, emit, emitTo, TauriEvent }
export { listen, once, emit, emitTo, emit2, emitTo2, TauriEvent }

View File

@@ -25,7 +25,9 @@ import {
// imported for documentation purposes
type EventTarget,
emit,
emit2,
emitTo,
emitTo2,
listen,
once
} from './event'
@@ -313,6 +315,8 @@ class Webview {
/**
* Emits an event to all {@link EventTarget|targets}.
*
* Prefer {@link Webview.emit2} to send binary data.
*
* @example
* ```typescript
* import { getCurrentWebview } from '@tauri-apps/api/webview';
@@ -337,9 +341,40 @@ class Webview {
return emit<T>(event, payload)
}
/**
* Emits an event to all {@link EventTarget|targets}.
*
* This function is similar to {@link Webview.emit} but it is optimized for binary payloads.
*
* @example
* ```typescript
* import { getCurrentWebview } from '@tauri-apps/api/webview';
* await getCurrentWebview().emit('webview-loaded', { loggedIn: true, token: 'authToken' });
* ```
*
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*/
async emit2<T>(event: string, payload?: T): Promise<void> {
if (localTauriEvents.includes(event)) {
// eslint-disable-next-line
for (const handler of this.listeners[event] || []) {
handler({
event,
id: -1,
payload
})
}
return
}
return emit2<T>(event, payload)
}
/**
* Emits an event to all {@link EventTarget|targets} matching the given target.
*
* Prefer {@link Webview.emitTo2} to send binary data.
*
* @example
* ```typescript
* import { getCurrentWebview } from '@tauri-apps/api/webview';
@@ -369,6 +404,40 @@ class Webview {
return emitTo<T>(target, event, payload)
}
/**
* Emits an event to all {@link EventTarget|targets} matching the given target.
*
* This function is similar to {@link Webview.emitTo} but it is optimized for binary payloads.
*
* @example
* ```typescript
* import { getCurrentWebview } from '@tauri-apps/api/webview';
* await getCurrentWebview().emitTo('main', 'webview-loaded', { loggedIn: true, token: 'authToken' });
* ```
*
* @param target Label of the target Window/Webview/WebviewWindow or raw {@link EventTarget} object.
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*/
async emitTo2<T>(
target: string | EventTarget,
event: string,
payload?: T
): Promise<void> {
if (localTauriEvents.includes(event)) {
// eslint-disable-next-line
for (const handler of this.listeners[event] || []) {
handler({
event,
id: -1,
payload
})
}
return
}
return emitTo2<T>(target, event, payload)
}
/** @ignore */
_handleTauriEvent<T>(event: string, handler: EventCallback<T>): boolean {
if (localTauriEvents.includes(event)) {

View File

@@ -30,7 +30,9 @@ import {
// imported for documentation purposes
type EventTarget,
emit,
emit2,
emitTo,
emitTo2,
listen,
once
} from './event'
@@ -437,6 +439,9 @@ class Window {
/**
* Emits an event to all {@link EventTarget|targets}.
*
* Prefer {@link Window.emit2} to send binary data.
*
* @example
* ```typescript
* import { getCurrentWindow } from '@tauri-apps/api/window';
@@ -461,9 +466,40 @@ class Window {
return emit<T>(event, payload)
}
/**
* Emits an event to all {@link EventTarget|targets}.
*
* This function is similar to {@link Window.emit} but it is optimized for binary payloads.
*
* @example
* ```typescript
* import { getCurrentWindow } from '@tauri-apps/api/window';
* await getCurrentWindow().emit('window-loaded', { loggedIn: true, token: 'authToken' });
* ```
*
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*/
async emit2<T>(event: string, payload?: T): Promise<void> {
if (localTauriEvents.includes(event)) {
// eslint-disable-next-line
for (const handler of this.listeners[event] || []) {
handler({
event,
id: -1,
payload
})
}
return
}
return emit2<T>(event, payload)
}
/**
* Emits an event to all {@link EventTarget|targets} matching the given target.
*
* Prefer {@link Window.emitTo2} to send binary data.
*
* @example
* ```typescript
* import { getCurrentWindow } from '@tauri-apps/api/window';
@@ -492,6 +528,39 @@ class Window {
return emitTo<T>(target, event, payload)
}
/**
* Emits an event to all {@link EventTarget|targets} matching the given target.
*
* This function is similar to {@link Window.emitTo} but it is optimized for binary payloads.
*
* @example
* ```typescript
* import { getCurrentWindow } from '@tauri-apps/api/window';
* await getCurrentWindow().emit('main', 'window-loaded', { loggedIn: true, token: 'authToken' });
* ```
* @param target Label of the target Window/Webview/WebviewWindow or raw {@link EventTarget} object.
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*/
async emitTo2<T>(
target: string | EventTarget,
event: string,
payload?: T
): Promise<void> {
if (localTauriEvents.includes(event)) {
// eslint-disable-next-line security/detect-object-injection
for (const handler of this.listeners[event] || []) {
handler({
event,
id: -1,
payload
})
}
return
}
return emitTo2<T>(target, event, payload)
}
/** @ignore */
_handleTauriEvent<T>(event: string, handler: EventCallback<T>): boolean {
if (localTauriEvents.includes(event)) {