1mod listener;
6pub(crate) mod plugin;
7use std::{convert::Infallible, str::FromStr};
8
9pub(crate) use listener::Listeners;
10use serde::{Deserialize, Serialize};
11
12mod event_name;
13
14pub(crate) use event_name::EventName;
15
16use crate::ipc::CallbackFn;
17
18pub type EventId = u32;
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
23#[serde(tag = "kind")]
24#[non_exhaustive]
25pub enum EventTarget {
26 Any,
28
29 AnyLabel {
31 label: String,
33 },
34
35 App,
37
38 Window {
40 label: String,
42 },
43
44 Webview {
46 label: String,
48 },
49
50 WebviewWindow {
52 label: String,
54 },
55}
56
57impl EventTarget {
58 pub fn any() -> Self {
60 Self::Any
61 }
62
63 pub fn app() -> Self {
65 Self::App
66 }
67
68 pub fn labeled(label: impl Into<String>) -> Self {
70 Self::AnyLabel {
71 label: label.into(),
72 }
73 }
74
75 pub fn window(label: impl Into<String>) -> Self {
77 Self::Window {
78 label: label.into(),
79 }
80 }
81
82 pub fn webview(label: impl Into<String>) -> Self {
84 Self::Webview {
85 label: label.into(),
86 }
87 }
88
89 pub fn webview_window(label: impl Into<String>) -> Self {
91 Self::WebviewWindow {
92 label: label.into(),
93 }
94 }
95}
96
97impl<T: AsRef<str>> From<T> for EventTarget {
98 fn from(value: T) -> Self {
99 Self::AnyLabel {
100 label: value.as_ref().to_string(),
101 }
102 }
103}
104
105impl FromStr for EventTarget {
106 type Err = Infallible;
107
108 fn from_str(s: &str) -> Result<Self, Self::Err> {
109 Ok(Self::AnyLabel {
110 label: s.to_string(),
111 })
112 }
113}
114
115#[derive(Clone)]
117pub struct EmitArgs {
118 event: EventName,
120 payload: String,
122}
123
124impl EmitArgs {
125 pub fn new<S: Serialize>(event: EventName<&str>, payload: &S) -> crate::Result<Self> {
126 #[cfg(feature = "tracing")]
127 let _span = tracing::debug_span!("window::emit::serialize").entered();
128 Ok(EmitArgs {
129 event: event.into_owned(),
130 payload: serde_json::to_string(payload)?,
131 })
132 }
133
134 pub fn new_str(event: EventName<&str>, payload: String) -> crate::Result<Self> {
135 #[cfg(feature = "tracing")]
136 let _span = tracing::debug_span!("window::emit::json").entered();
137 Ok(EmitArgs {
138 event: event.into_owned(),
139 payload,
140 })
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct Event {
147 id: EventId,
148 data: String,
149}
150
151impl Event {
152 fn new(id: EventId, data: String) -> Self {
153 Self { id, data }
154 }
155
156 pub fn id(&self) -> EventId {
158 self.id
159 }
160
161 pub fn payload(&self) -> &str {
163 &self.data
164 }
165}
166
167pub(crate) fn listen_js_script(
168 listeners_object_name: &str,
169 serialized_target: &str,
170 event: EventName<&str>,
171 event_id: EventId,
172 handler: CallbackFn,
173) -> String {
174 let handler_id = handler.0;
175 format!(
176 "(function () {{
177 if (window['{listeners_object_name}'] === void 0) {{
178 Object.defineProperty(window, '{listeners_object_name}', {{ value: Object.create(null) }});
179 }}
180 if (window['{listeners_object_name}']['{event}'] === void 0) {{
181 Object.defineProperty(window['{listeners_object_name}'], '{event}', {{ value: Object.create(null) }});
182 }}
183 const eventListeners = window['{listeners_object_name}']['{event}']
184 const listener = {{
185 target: {serialized_target},
186 handlerId: {handler_id}
187 }};
188 Object.defineProperty(eventListeners, '{event_id}', {{ value: listener, configurable: true }});
189 }})()
190 ",
191 )
192}
193
194pub(crate) fn emit_js_script(
195 event_emit_function_name: &str,
196 emit_args: &EmitArgs,
197 serialized_ids: &str,
198) -> crate::Result<String> {
199 Ok(format!(
200 "(function () {{ const fn = window['{}']; fn && fn({{event: '{}', payload: {}}}, {ids}) }})()",
201 event_emit_function_name,
202 emit_args.event,
203 emit_args.payload,
204 ids = serialized_ids,
205 ))
206}
207
208pub(crate) fn unlisten_js_script(
209 listeners_object_name: &str,
210 event_arg: &str,
211 event_id_arg: &str,
212) -> String {
213 format!(
214 "(function () {{
215 const listeners = (window['{listeners_object_name}'] || {{}})[{event_arg}]
216 if (listeners) {{
217 window.__TAURI_INTERNALS__.unregisterCallback(listeners[{event_id_arg}].handlerId)
218 }}
219 }})()
220 ",
221 )
222}
223
224pub(crate) fn event_initialization_script(function_name: &str, listeners: &str) -> String {
225 format!(
226 "Object.defineProperty(window, '{function_name}', {{
227 value: function (eventData, ids) {{
228 const listeners = (window['{listeners}'] && window['{listeners}'][eventData.event]) || []
229 for (const id of ids) {{
230 const listener = listeners[id]
231 if (listener) {{
232 eventData.id = id
233 window.__TAURI_INTERNALS__.runCallback(listener.handlerId, eventData)
234 }}
235 }}
236 }}
237 }});
238 "
239 )
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 #[test]
246 fn test_illegal_event_name() {
247 let s = EventName::new("some\r illegal event name")
248 .unwrap_err()
249 .to_string();
250 assert_eq!("only alphanumeric, '-', '/', ':', '_' permitted for event names: \"some\\r illegal event name\"", s);
251 }
252}