fix(core): fix emit skipping webview listeners if filter wasn't provided (#9107)

* fix(core): fix emit skipping webview listeners if filter wasn't provided

* Update .changes/core-emit-js-all-targets.md

* update api example

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
Amr Bashir 2024-03-07 20:36:52 +02:00 committed by GitHub
parent 80c12ead46
commit 5541aafef3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 78 additions and 76 deletions

View File

@ -0,0 +1,5 @@
---
'tauri': 'patch:bug'
---
Fix `emit` and `emit_to` (when used with `EventTarget::Any`) always skipping the webview listeners.

View File

@ -16,12 +16,6 @@ use std::{
},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct JsHandler {
target: EventTarget,
id: EventId,
}
/// What to do with the pending handler when resolving it?
enum Pending {
Unlisten(EventId),
@ -48,6 +42,18 @@ impl Handler {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct JsHandler {
target: EventTarget,
id: EventId,
}
impl JsHandler {
fn new(target: EventTarget, id: EventId) -> Self {
Self { target, id }
}
}
type WebviewLabel = String;
type EventName = String;
@ -189,23 +195,16 @@ impl Listeners {
F: Fn(&EventTarget) -> bool,
{
let mut maybe_pending = false;
match self.inner.handlers.try_lock() {
Err(_) => self.insert_pending(Pending::Emit(emit_args.clone())),
Ok(lock) => {
if let Some(handlers) = lock.get(&emit_args.event_name) {
let handlers: Vec<_> = match filter {
Some(filter) => handlers
.iter()
.filter(|(_, Handler { target, .. })| *target == EventTarget::Any || filter(target))
.collect(),
None => handlers.iter().collect(),
};
if !handlers.is_empty() {
let handlers = handlers.iter();
let handlers = handlers.filter(|(_, h)| match_any_or_filter(&h.target, &filter));
for (&id, Handler { callback, .. }) in handlers {
maybe_pending = true;
for (&id, Handler { callback, .. }) in handlers {
(callback)(Event::new(id, emit_args.payload.clone()))
}
(callback)(Event::new(id, emit_args.payload.clone()))
}
}
}
@ -236,44 +235,26 @@ impl Listeners {
.or_default()
.entry(event.to_string())
.or_default()
.insert(JsHandler { id, target });
.insert(JsHandler::new(target, id));
}
pub(crate) fn unlisten_js(&self, id: EventId) {
let mut listeners = self.inner.js_event_listeners.lock().unwrap();
let mut empty = None;
let listeners = listeners.values_mut();
'outer: for listeners in listeners {
for (key, handlers) in listeners.iter_mut() {
let mut found = false;
handlers.retain(|h| {
let keep = h.id != id;
if !found {
found = !keep
}
keep
});
pub(crate) fn unlisten_js(&self, event: &str, id: EventId) {
let mut js_listeners = self.inner.js_event_listeners.lock().unwrap();
let js_listeners = js_listeners.values_mut();
for js_listeners in js_listeners {
if let Some(handlers) = js_listeners.get_mut(event) {
handlers.retain(|h| h.id != id);
if handlers.is_empty() {
empty.replace(key.clone());
js_listeners.remove(event);
}
if found {
break 'outer;
}
}
if let Some(key) = &empty {
listeners.remove(key);
}
}
}
pub(crate) fn unlisten_all_js(&self, webview_label: &str) {
let inner_listeners = self.inner.as_ref();
let mut js_listeners = inner_listeners.js_event_listeners.lock().unwrap();
let js_listeners = self.inner.as_ref();
let mut js_listeners = js_listeners.js_event_listeners.lock().unwrap();
js_listeners.remove(webview_label);
}
@ -282,11 +263,11 @@ impl Listeners {
event: &str,
filter: F,
) -> bool {
let listeners = self.inner.js_event_listeners.lock().unwrap();
listeners.values().any(|events| {
let js_listeners = self.inner.js_event_listeners.lock().unwrap();
js_listeners.values().any(|events| {
events
.get(event)
.map(|handlers| handlers.iter().any(|h| filter(&h.target)))
.map(|handlers| handlers.iter().any(|handler| filter(&handler.target)))
.unwrap_or(false)
})
}
@ -296,20 +277,20 @@ impl Listeners {
mut webviews: I,
event: &str,
emit_args: &EmitArgs,
filter: Option<&F>,
filter: Option<F>,
) -> crate::Result<()>
where
R: Runtime,
I: Iterator<Item = &'a Webview<R>>,
F: Fn(&EventTarget) -> bool,
{
let listeners = self.inner.js_event_listeners.lock().unwrap();
let js_listeners = self.inner.js_event_listeners.lock().unwrap();
webviews.try_for_each(|webview| {
if let Some(handlers) = listeners.get(webview.label()).and_then(|s| s.get(event)) {
if let Some(handlers) = js_listeners.get(webview.label()).and_then(|s| s.get(event)) {
let handlers = handlers.iter();
let handlers = handlers.filter(|handler| match_any_or_filter(&handler.target, &filter));
for JsHandler { target, .. } in handlers {
if *target == EventTarget::Any || filter.as_ref().map(|f| f(target)).unwrap_or(false) {
webview.emit_js(emit_args, target)?;
}
webview.emit_js(emit_args, target)?;
}
}
@ -331,11 +312,19 @@ impl Listeners {
webviews,
event,
emit_args,
None::<&&dyn Fn(&EventTarget) -> bool>,
None::<&dyn Fn(&EventTarget) -> bool>,
)
}
}
#[inline(always)]
fn match_any_or_filter<F: Fn(&EventTarget) -> bool>(
target: &EventTarget,
filter: &Option<F>,
) -> bool {
*target == EventTarget::Any || filter.as_ref().map(|f| f(target)).unwrap_or(true)
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -1297,7 +1297,7 @@ fn main() {
id,
))?;
listeners.unlisten_js(id);
listeners.unlisten_js(event, id);
Ok(())
}

View File

@ -172,6 +172,12 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -1769,9 +1775,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.20"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "loom"
@ -2265,7 +2271,7 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef"
dependencies = [
"base64",
"base64 0.21.7",
"indexmap 2.2.3",
"line-wrap",
"quick-xml",
@ -2556,7 +2562,7 @@ version = "0.11.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
dependencies = [
"base64",
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
@ -2790,7 +2796,7 @@ version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270"
dependencies = [
"base64",
"base64 0.21.7",
"chrono",
"hex",
"indexmap 1.9.3",
@ -3019,7 +3025,7 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bbdb58577b6301f8d17ae2561f32002a5bae056d444e0f69e611e504a276204"
dependencies = [
"base64",
"base64 0.21.7",
"serde",
"serde_json",
]
@ -3145,7 +3151,7 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
[[package]]
name = "tauri"
version = "2.0.0-beta.8"
version = "2.0.0-beta.9"
dependencies = [
"anyhow",
"bytes",
@ -3195,7 +3201,7 @@ dependencies = [
[[package]]
name = "tauri-build"
version = "2.0.0-beta.6"
version = "2.0.0-beta.7"
dependencies = [
"anyhow",
"cargo_toml",
@ -3217,9 +3223,9 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.0.0-beta.6"
version = "2.0.0-beta.7"
dependencies = [
"base64",
"base64 0.22.0",
"brotli",
"ico",
"json-patch",
@ -3242,7 +3248,7 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.0.0-beta.6"
version = "2.0.0-beta.7"
dependencies = [
"heck",
"proc-macro2",
@ -3254,7 +3260,7 @@ dependencies = [
[[package]]
name = "tauri-plugin"
version = "2.0.0-beta.6"
version = "2.0.0-beta.7"
dependencies = [
"anyhow",
"glob",
@ -3280,7 +3286,7 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.0.0-beta.6"
version = "2.0.0-beta.7"
dependencies = [
"gtk",
"http",
@ -3296,7 +3302,7 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.0.0-beta.6"
version = "2.0.0-beta.7"
dependencies = [
"cocoa",
"gtk",
@ -3318,7 +3324,7 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "2.0.0-beta.6"
version = "2.0.0-beta.7"
dependencies = [
"aes-gcm",
"brotli",
@ -4384,7 +4390,7 @@ version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e"
dependencies = [
"base64",
"base64 0.21.7",
"block",
"cfg_aliases 0.1.1",
"cocoa",

View File

@ -1,13 +1,15 @@
<script>
import { listen, emit } from '@tauri-apps/api/event'
import { getCurrent } from '@tauri-apps/api/webviewWindow'
import { invoke } from '@tauri-apps/api/core'
import { onMount, onDestroy } from 'svelte'
export let onMessage
let unlisten
const webviewWindow = getCurrent()
onMount(async () => {
unlisten = await listen('rust-event', onMessage)
unlisten = await webviewWindow.listen('rust-event', onMessage)
})
onDestroy(() => {
if (unlisten) {
@ -35,7 +37,7 @@
}
function emitEvent() {
emit('js-event', 'this is the payload string')
webviewWindow.emit('js-event', 'this is the payload string')
}
</script>