فهرست منبع

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>
Amr Bashir 1 سال پیش
والد
کامیت
5541aafef3

+ 5 - 0
.changes/core-emit-js-all-targets.md

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

+ 45 - 56
core/tauri/src/event/listener.rs

@@ -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::*;

+ 1 - 1
core/tauri/src/webview/mod.rs

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

+ 22 - 16
examples/api/src-tauri/Cargo.lock

@@ -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",

+ 5 - 3
examples/api/src/views/Communication.svelte

@@ -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>