|
@@ -27,7 +27,7 @@ use tauri_utils::{
|
|
|
html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN},
|
|
|
};
|
|
|
|
|
|
-use crate::window::WindowEmitArgs;
|
|
|
+use crate::event::EmitArgs;
|
|
|
use crate::{
|
|
|
app::{
|
|
|
AppHandle, GlobalWindowEvent, GlobalWindowEventListener, OnPageLoad, PageLoadPayload,
|
|
@@ -225,7 +225,7 @@ fn replace_csp_nonce(
|
|
|
pub struct InnerWindowManager<R: Runtime> {
|
|
|
pub(crate) windows: Mutex<HashMap<String, Window<R>>>,
|
|
|
pub(crate) plugins: Mutex<PluginStore<R>>,
|
|
|
- listeners: Listeners,
|
|
|
+ listeners: Listeners<R>,
|
|
|
pub(crate) state: Arc<StateManager>,
|
|
|
|
|
|
/// The JS message handler.
|
|
@@ -847,7 +847,10 @@ impl<R: Runtime> WindowManager<R> {
|
|
|
}
|
|
|
.render_default(&Default::default())?
|
|
|
.into_string(),
|
|
|
- event_initialization_script: &self.event_initialization_script(),
|
|
|
+ event_initialization_script: &crate::event::event_initialization_script(
|
|
|
+ self.listeners().function_name(),
|
|
|
+ self.listeners().listeners_object_name(),
|
|
|
+ ),
|
|
|
plugin_initialization_script,
|
|
|
freeze_prototype,
|
|
|
}
|
|
@@ -856,29 +859,7 @@ impl<R: Runtime> WindowManager<R> {
|
|
|
.map_err(Into::into)
|
|
|
}
|
|
|
|
|
|
- fn event_initialization_script(&self) -> String {
|
|
|
- format!(
|
|
|
- "
|
|
|
- Object.defineProperty(window, '{function}', {{
|
|
|
- value: function (eventData) {{
|
|
|
- const listeners = (window['{listeners}'] && window['{listeners}'][eventData.event]) || []
|
|
|
-
|
|
|
- for (let i = listeners.length - 1; i >= 0; i--) {{
|
|
|
- const listener = listeners[i]
|
|
|
- if (listener.windowLabel === null || eventData.windowLabel === null || listener.windowLabel === eventData.windowLabel) {{
|
|
|
- eventData.id = listener.id
|
|
|
- listener.handler(eventData)
|
|
|
- }}
|
|
|
- }}
|
|
|
- }}
|
|
|
- }});
|
|
|
- ",
|
|
|
- function = self.listeners().function_name(),
|
|
|
- listeners = self.listeners().listeners_object_name()
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- pub(crate) fn listeners(&self) -> &Listeners {
|
|
|
+ pub(crate) fn listeners(&self) -> &Listeners<R> {
|
|
|
&self.inner.listeners
|
|
|
}
|
|
|
}
|
|
@@ -1146,26 +1127,6 @@ impl<R: Runtime> WindowManager<R> {
|
|
|
self.windows_lock().remove(label);
|
|
|
}
|
|
|
|
|
|
- pub fn emit_filter<S, F>(
|
|
|
- &self,
|
|
|
- event: &str,
|
|
|
- source_window_label: Option<&str>,
|
|
|
- payload: S,
|
|
|
- filter: F,
|
|
|
- ) -> crate::Result<()>
|
|
|
- where
|
|
|
- S: Serialize + Clone,
|
|
|
- F: Fn(&Window<R>) -> bool,
|
|
|
- {
|
|
|
- let emit_args = WindowEmitArgs::from(event, source_window_label, payload)?;
|
|
|
- assert_event_name_is_valid(event);
|
|
|
- self
|
|
|
- .windows()
|
|
|
- .values()
|
|
|
- .filter(|&w| filter(w))
|
|
|
- .try_for_each(|window| window.emit_internal(&emit_args))
|
|
|
- }
|
|
|
-
|
|
|
pub fn eval_script_all<S: Into<String>>(&self, script: S) -> crate::Result<()> {
|
|
|
let script = script.into();
|
|
|
self
|
|
@@ -1186,21 +1147,20 @@ impl<R: Runtime> WindowManager<R> {
|
|
|
&self.inner.package_info
|
|
|
}
|
|
|
|
|
|
- pub fn trigger(&self, event: &str, window: Option<String>, data: Option<String>) {
|
|
|
- assert_event_name_is_valid(event);
|
|
|
- self.listeners().trigger(event, window, data)
|
|
|
- }
|
|
|
-
|
|
|
pub fn listen<F: Fn(Event) + Send + 'static>(
|
|
|
&self,
|
|
|
event: String,
|
|
|
- window: Option<String>,
|
|
|
+ window: Option<Window<R>>,
|
|
|
handler: F,
|
|
|
) -> EventId {
|
|
|
assert_event_name_is_valid(&event);
|
|
|
self.listeners().listen(event, window, handler)
|
|
|
}
|
|
|
|
|
|
+ pub fn unlisten(&self, id: EventId) {
|
|
|
+ self.listeners().unlisten(id)
|
|
|
+ }
|
|
|
+
|
|
|
pub fn once<F: FnOnce(Event) + Send + 'static>(
|
|
|
&self,
|
|
|
event: String,
|
|
@@ -1208,19 +1168,63 @@ impl<R: Runtime> WindowManager<R> {
|
|
|
handler: F,
|
|
|
) {
|
|
|
assert_event_name_is_valid(&event);
|
|
|
- self.listeners().once(event, window, handler)
|
|
|
+ self
|
|
|
+ .listeners()
|
|
|
+ .once(event, window.and_then(|w| self.get_window(&w)), handler)
|
|
|
}
|
|
|
|
|
|
- pub fn event_listeners_object_name(&self) -> &str {
|
|
|
- self.inner.listeners.listeners_object_name()
|
|
|
- }
|
|
|
+ pub fn emit_filter<S, F>(
|
|
|
+ &self,
|
|
|
+ event: &str,
|
|
|
+ source_window_label: Option<&str>,
|
|
|
+ payload: S,
|
|
|
+ filter: F,
|
|
|
+ ) -> crate::Result<()>
|
|
|
+ where
|
|
|
+ S: Serialize + Clone,
|
|
|
+ F: Fn(&Window<R>) -> bool,
|
|
|
+ {
|
|
|
+ assert_event_name_is_valid(event);
|
|
|
+
|
|
|
+ let emit_args = EmitArgs::from(event, source_window_label, payload)?;
|
|
|
+
|
|
|
+ self
|
|
|
+ .windows_lock()
|
|
|
+ .values()
|
|
|
+ .filter(|w| {
|
|
|
+ w.has_js_listener(None, event)
|
|
|
+ || w.has_js_listener(source_window_label.map(Into::into), event)
|
|
|
+ })
|
|
|
+ .filter(|w| filter(w))
|
|
|
+ .try_for_each(|window| window.emit_js(&emit_args))?;
|
|
|
+
|
|
|
+ self.listeners().emit_filter(&emit_args, Some(filter))?;
|
|
|
|
|
|
- pub fn event_emit_function_name(&self) -> &str {
|
|
|
- self.inner.listeners.function_name()
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
- pub fn unlisten(&self, id: EventId) {
|
|
|
- self.listeners().unlisten(id)
|
|
|
+ pub fn emit<S: Serialize + Clone>(
|
|
|
+ &self,
|
|
|
+ event: &str,
|
|
|
+ source_window_label: Option<&str>,
|
|
|
+ payload: S,
|
|
|
+ ) -> crate::Result<()> {
|
|
|
+ assert_event_name_is_valid(event);
|
|
|
+
|
|
|
+ let emit_args = EmitArgs::from(event, source_window_label, payload)?;
|
|
|
+
|
|
|
+ self
|
|
|
+ .windows_lock()
|
|
|
+ .values()
|
|
|
+ .filter(|w| {
|
|
|
+ w.has_js_listener(None, event)
|
|
|
+ || w.has_js_listener(source_window_label.map(Into::into), event)
|
|
|
+ })
|
|
|
+ .try_for_each(|window| window.emit_js(&emit_args))?;
|
|
|
+
|
|
|
+ self.listeners().emit(&emit_args)?;
|
|
|
+
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
pub fn get_window(&self, label: &str) -> Option<Window<R>> {
|
|
@@ -1346,10 +1350,25 @@ mod tests {
|
|
|
|
|
|
#[cfg(test)]
|
|
|
mod test {
|
|
|
- use crate::{generate_context, plugin::PluginStore, StateManager, Wry};
|
|
|
+ use std::{
|
|
|
+ sync::mpsc::{channel, Receiver, Sender},
|
|
|
+ time::Duration,
|
|
|
+ };
|
|
|
+
|
|
|
+ use crate::{
|
|
|
+ generate_context,
|
|
|
+ plugin::PluginStore,
|
|
|
+ test::{mock_app, MockRuntime},
|
|
|
+ App, Manager, StateManager, Window, WindowBuilder, Wry,
|
|
|
+ };
|
|
|
|
|
|
use super::WindowManager;
|
|
|
|
|
|
+ const WINDOW_LISTEN_ID: &str = "Window::listen";
|
|
|
+ const WINDOW_LISTEN_GLOBAL_ID: &str = "Window::listen_global";
|
|
|
+ const APP_LISTEN_GLOBAL_ID: &str = "App::listen_global";
|
|
|
+ const TEST_EVENT_NAME: &str = "event";
|
|
|
+
|
|
|
#[test]
|
|
|
fn check_get_url() {
|
|
|
let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate);
|
|
@@ -1380,4 +1399,164 @@ mod test {
|
|
|
#[cfg(dev)]
|
|
|
assert_eq!(manager.get_url().to_string(), "http://localhost:4000/");
|
|
|
}
|
|
|
+
|
|
|
+ struct EventSetup {
|
|
|
+ app: App<MockRuntime>,
|
|
|
+ window: Window<MockRuntime>,
|
|
|
+ tx: Sender<(&'static str, String)>,
|
|
|
+ rx: Receiver<(&'static str, String)>,
|
|
|
+ }
|
|
|
+
|
|
|
+ fn setup_events() -> EventSetup {
|
|
|
+ let app = mock_app();
|
|
|
+ let window = WindowBuilder::new(&app, "main", Default::default())
|
|
|
+ .build()
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ let (tx, rx) = channel();
|
|
|
+
|
|
|
+ let tx_ = tx.clone();
|
|
|
+ window.listen(TEST_EVENT_NAME, move |evt| {
|
|
|
+ tx_
|
|
|
+ .send((
|
|
|
+ WINDOW_LISTEN_ID,
|
|
|
+ serde_json::from_str::<String>(evt.payload()).unwrap(),
|
|
|
+ ))
|
|
|
+ .unwrap();
|
|
|
+ });
|
|
|
+
|
|
|
+ let tx_ = tx.clone();
|
|
|
+ window.listen_global(TEST_EVENT_NAME, move |evt| {
|
|
|
+ tx_
|
|
|
+ .send((
|
|
|
+ WINDOW_LISTEN_GLOBAL_ID,
|
|
|
+ serde_json::from_str::<String>(evt.payload()).unwrap(),
|
|
|
+ ))
|
|
|
+ .unwrap();
|
|
|
+ });
|
|
|
+
|
|
|
+ let tx_ = tx.clone();
|
|
|
+ app.listen_global(TEST_EVENT_NAME, move |evt| {
|
|
|
+ tx_
|
|
|
+ .send((
|
|
|
+ APP_LISTEN_GLOBAL_ID,
|
|
|
+ serde_json::from_str::<String>(evt.payload()).unwrap(),
|
|
|
+ ))
|
|
|
+ .unwrap();
|
|
|
+ });
|
|
|
+
|
|
|
+ EventSetup {
|
|
|
+ app,
|
|
|
+ window,
|
|
|
+ tx,
|
|
|
+ rx,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn assert_events(received: &[&str], expected: &[&str]) {
|
|
|
+ for e in expected {
|
|
|
+ assert!(received.contains(e), "{e} did not receive global event");
|
|
|
+ }
|
|
|
+ assert_eq!(
|
|
|
+ received.len(),
|
|
|
+ expected.len(),
|
|
|
+ "received {:?} events but expected {:?}",
|
|
|
+ received,
|
|
|
+ expected
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn app_global_events() {
|
|
|
+ let EventSetup {
|
|
|
+ app,
|
|
|
+ window: _,
|
|
|
+ tx: _,
|
|
|
+ rx,
|
|
|
+ } = setup_events();
|
|
|
+
|
|
|
+ let mut received = Vec::new();
|
|
|
+ let payload = "global-payload";
|
|
|
+ app.emit(TEST_EVENT_NAME, payload).unwrap();
|
|
|
+ while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
|
|
|
+ assert_eq!(p, payload);
|
|
|
+ received.push(source);
|
|
|
+ }
|
|
|
+ assert_events(
|
|
|
+ &received,
|
|
|
+ &[
|
|
|
+ WINDOW_LISTEN_ID,
|
|
|
+ WINDOW_LISTEN_GLOBAL_ID,
|
|
|
+ APP_LISTEN_GLOBAL_ID,
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn window_global_events() {
|
|
|
+ let EventSetup {
|
|
|
+ app: _,
|
|
|
+ window,
|
|
|
+ tx: _,
|
|
|
+ rx,
|
|
|
+ } = setup_events();
|
|
|
+
|
|
|
+ let mut received = Vec::new();
|
|
|
+ let payload = "global-payload";
|
|
|
+ window.emit(TEST_EVENT_NAME, payload).unwrap();
|
|
|
+ while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
|
|
|
+ assert_eq!(p, payload);
|
|
|
+ received.push(source);
|
|
|
+ }
|
|
|
+ assert_events(
|
|
|
+ &received,
|
|
|
+ &[
|
|
|
+ WINDOW_LISTEN_ID,
|
|
|
+ WINDOW_LISTEN_GLOBAL_ID,
|
|
|
+ APP_LISTEN_GLOBAL_ID,
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn window_local_events() {
|
|
|
+ let EventSetup {
|
|
|
+ app,
|
|
|
+ window,
|
|
|
+ tx,
|
|
|
+ rx,
|
|
|
+ } = setup_events();
|
|
|
+
|
|
|
+ let mut received = Vec::new();
|
|
|
+ let payload = "global-payload";
|
|
|
+ window
|
|
|
+ .emit_to(window.label(), TEST_EVENT_NAME, payload)
|
|
|
+ .unwrap();
|
|
|
+ while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
|
|
|
+ assert_eq!(p, payload);
|
|
|
+ received.push(source);
|
|
|
+ }
|
|
|
+ assert_events(&received, &[WINDOW_LISTEN_ID]);
|
|
|
+
|
|
|
+ received.clear();
|
|
|
+ let other_window_listen_id = "OtherWindow::listen";
|
|
|
+ let other_window = WindowBuilder::new(&app, "other", Default::default())
|
|
|
+ .build()
|
|
|
+ .unwrap();
|
|
|
+ other_window.listen(TEST_EVENT_NAME, move |evt| {
|
|
|
+ tx.send((
|
|
|
+ other_window_listen_id,
|
|
|
+ serde_json::from_str::<String>(evt.payload()).unwrap(),
|
|
|
+ ))
|
|
|
+ .unwrap();
|
|
|
+ });
|
|
|
+ window
|
|
|
+ .emit_to(other_window.label(), TEST_EVENT_NAME, payload)
|
|
|
+ .unwrap();
|
|
|
+ while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
|
|
|
+ assert_eq!(p, payload);
|
|
|
+ received.push(source);
|
|
|
+ }
|
|
|
+ assert_events(&received, &[other_window_listen_id]);
|
|
|
+ }
|
|
|
}
|