1use crate::{window::is_label_valid, Rect, Runtime, UserEvent};
8
9use http::Request;
10use tauri_utils::config::{
11 BackgroundThrottlingPolicy, Color, WebviewUrl, WindowConfig, WindowEffectsConfig,
12};
13use url::Url;
14
15use std::{
16 borrow::Cow,
17 collections::HashMap,
18 hash::{Hash, Hasher},
19 path::PathBuf,
20 sync::Arc,
21};
22
23type UriSchemeProtocol = dyn Fn(&str, http::Request<Vec<u8>>, Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>)
24 + Send
25 + Sync
26 + 'static;
27
28type WebResourceRequestHandler =
29 dyn Fn(http::Request<Vec<u8>>, &mut http::Response<Cow<'static, [u8]>>) + Send + Sync;
30
31type NavigationHandler = dyn Fn(&Url) -> bool + Send;
32
33type OnPageLoadHandler = dyn Fn(Url, PageLoadEvent) + Send;
34
35type DownloadHandler = dyn Fn(DownloadEvent) -> bool + Send + Sync;
36
37pub enum DownloadEvent<'a> {
39 Requested {
41 url: Url,
43 destination: &'a mut PathBuf,
47 },
48 Finished {
50 url: Url,
52 path: Option<PathBuf>,
54 success: bool,
56 },
57}
58
59#[cfg(target_os = "android")]
60pub struct CreationContext<'a, 'b> {
61 pub env: &'a mut jni::JNIEnv<'b>,
62 pub activity: &'a jni::objects::JObject<'b>,
63 pub webview: &'a jni::objects::JObject<'b>,
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum PageLoadEvent {
69 Started,
71 Finished,
73}
74
75pub struct PendingWebview<T: UserEvent, R: Runtime<T>> {
77 pub label: String,
79
80 pub webview_attributes: WebviewAttributes,
82
83 pub uri_scheme_protocols: HashMap<String, Box<UriSchemeProtocol>>,
84
85 pub ipc_handler: Option<WebviewIpcHandler<T, R>>,
87
88 pub navigation_handler: Option<Box<NavigationHandler>>,
90
91 pub url: String,
93
94 #[cfg(target_os = "android")]
95 #[allow(clippy::type_complexity)]
96 pub on_webview_created:
97 Option<Box<dyn Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send>>,
98
99 pub web_resource_request_handler: Option<Box<WebResourceRequestHandler>>,
100
101 pub on_page_load_handler: Option<Box<OnPageLoadHandler>>,
102
103 pub download_handler: Option<Arc<DownloadHandler>>,
104}
105
106impl<T: UserEvent, R: Runtime<T>> PendingWebview<T, R> {
107 pub fn new(
109 webview_attributes: WebviewAttributes,
110 label: impl Into<String>,
111 ) -> crate::Result<Self> {
112 let label = label.into();
113 if !is_label_valid(&label) {
114 Err(crate::Error::InvalidWindowLabel)
115 } else {
116 Ok(Self {
117 webview_attributes,
118 uri_scheme_protocols: Default::default(),
119 label,
120 ipc_handler: None,
121 navigation_handler: None,
122 url: "tauri://localhost".to_string(),
123 #[cfg(target_os = "android")]
124 on_webview_created: None,
125 web_resource_request_handler: None,
126 on_page_load_handler: None,
127 download_handler: None,
128 })
129 }
130 }
131
132 pub fn register_uri_scheme_protocol<
133 N: Into<String>,
134 H: Fn(&str, http::Request<Vec<u8>>, Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>)
135 + Send
136 + Sync
137 + 'static,
138 >(
139 &mut self,
140 uri_scheme: N,
141 protocol: H,
142 ) {
143 let uri_scheme = uri_scheme.into();
144 self
145 .uri_scheme_protocols
146 .insert(uri_scheme, Box::new(protocol));
147 }
148
149 #[cfg(target_os = "android")]
150 pub fn on_webview_created<
151 F: Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send + 'static,
152 >(
153 mut self,
154 f: F,
155 ) -> Self {
156 self.on_webview_created.replace(Box::new(f));
157 self
158 }
159}
160
161#[derive(Debug)]
163pub struct DetachedWebview<T: UserEvent, R: Runtime<T>> {
164 pub label: String,
166
167 pub dispatcher: R::WebviewDispatcher,
169}
170
171impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWebview<T, R> {
172 fn clone(&self) -> Self {
173 Self {
174 label: self.label.clone(),
175 dispatcher: self.dispatcher.clone(),
176 }
177 }
178}
179
180impl<T: UserEvent, R: Runtime<T>> Hash for DetachedWebview<T, R> {
181 fn hash<H: Hasher>(&self, state: &mut H) {
183 self.label.hash(state)
184 }
185}
186
187impl<T: UserEvent, R: Runtime<T>> Eq for DetachedWebview<T, R> {}
188impl<T: UserEvent, R: Runtime<T>> PartialEq for DetachedWebview<T, R> {
189 fn eq(&self, other: &Self) -> bool {
191 self.label.eq(&other.label)
192 }
193}
194
195#[derive(Debug, Clone)]
197pub struct WebviewAttributes {
198 pub url: WebviewUrl,
199 pub user_agent: Option<String>,
200 pub initialization_scripts: Vec<String>,
201 pub data_directory: Option<PathBuf>,
202 pub drag_drop_handler_enabled: bool,
203 pub clipboard: bool,
204 pub accept_first_mouse: bool,
205 pub additional_browser_args: Option<String>,
206 pub window_effects: Option<WindowEffectsConfig>,
207 pub incognito: bool,
208 pub transparent: bool,
209 pub focus: bool,
210 pub bounds: Option<Rect>,
211 pub auto_resize: bool,
212 pub proxy_url: Option<Url>,
213 pub zoom_hotkeys_enabled: bool,
214 pub browser_extensions_enabled: bool,
215 pub extensions_path: Option<PathBuf>,
216 pub data_store_identifier: Option<[u8; 16]>,
217 pub use_https_scheme: bool,
218 pub devtools: Option<bool>,
219 pub background_color: Option<Color>,
220 pub traffic_light_position: Option<dpi::Position>,
221 pub background_throttling: Option<BackgroundThrottlingPolicy>,
222 pub javascript_disabled: bool,
223}
224
225impl From<&WindowConfig> for WebviewAttributes {
226 fn from(config: &WindowConfig) -> Self {
227 let mut builder = Self::new(config.url.clone())
228 .incognito(config.incognito)
229 .focused(config.focus)
230 .zoom_hotkeys_enabled(config.zoom_hotkeys_enabled)
231 .use_https_scheme(config.use_https_scheme)
232 .browser_extensions_enabled(config.browser_extensions_enabled)
233 .background_throttling(config.background_throttling.clone())
234 .devtools(config.devtools);
235
236 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
237 {
238 builder = builder.transparent(config.transparent);
239 }
240 #[cfg(target_os = "macos")]
241 {
242 if let Some(position) = &config.traffic_light_position {
243 builder =
244 builder.traffic_light_position(dpi::LogicalPosition::new(position.x, position.y).into());
245 }
246 }
247 builder = builder.accept_first_mouse(config.accept_first_mouse);
248 if !config.drag_drop_enabled {
249 builder = builder.disable_drag_drop_handler();
250 }
251 if let Some(user_agent) = &config.user_agent {
252 builder = builder.user_agent(user_agent);
253 }
254 if let Some(additional_browser_args) = &config.additional_browser_args {
255 builder = builder.additional_browser_args(additional_browser_args);
256 }
257 if let Some(effects) = &config.window_effects {
258 builder = builder.window_effects(effects.clone());
259 }
260 if let Some(url) = &config.proxy_url {
261 builder = builder.proxy_url(url.to_owned());
262 }
263 if let Some(color) = config.background_color {
264 builder = builder.background_color(color);
265 }
266 builder.javascript_disabled = config.javascript_disabled;
267 builder
268 }
269}
270
271impl WebviewAttributes {
272 pub fn new(url: WebviewUrl) -> Self {
274 Self {
275 url,
276 user_agent: None,
277 initialization_scripts: Vec::new(),
278 data_directory: None,
279 drag_drop_handler_enabled: true,
280 clipboard: false,
281 accept_first_mouse: false,
282 additional_browser_args: None,
283 window_effects: None,
284 incognito: false,
285 transparent: false,
286 focus: true,
287 bounds: None,
288 auto_resize: false,
289 proxy_url: None,
290 zoom_hotkeys_enabled: false,
291 browser_extensions_enabled: false,
292 data_store_identifier: None,
293 extensions_path: None,
294 use_https_scheme: false,
295 devtools: None,
296 background_color: None,
297 traffic_light_position: None,
298 background_throttling: None,
299 javascript_disabled: false,
300 }
301 }
302
303 #[must_use]
305 pub fn user_agent(mut self, user_agent: &str) -> Self {
306 self.user_agent = Some(user_agent.to_string());
307 self
308 }
309
310 #[must_use]
312 pub fn initialization_script(mut self, script: &str) -> Self {
313 self.initialization_scripts.push(script.to_string());
314 self
315 }
316
317 #[must_use]
319 pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
320 self.data_directory.replace(data_directory);
321 self
322 }
323
324 #[must_use]
326 pub fn disable_drag_drop_handler(mut self) -> Self {
327 self.drag_drop_handler_enabled = false;
328 self
329 }
330
331 #[must_use]
336 pub fn enable_clipboard_access(mut self) -> Self {
337 self.clipboard = true;
338 self
339 }
340
341 #[must_use]
343 pub fn accept_first_mouse(mut self, accept: bool) -> Self {
344 self.accept_first_mouse = accept;
345 self
346 }
347
348 #[must_use]
350 pub fn additional_browser_args(mut self, additional_args: &str) -> Self {
351 self.additional_browser_args = Some(additional_args.to_string());
352 self
353 }
354
355 #[must_use]
357 pub fn window_effects(mut self, effects: WindowEffectsConfig) -> Self {
358 self.window_effects = Some(effects);
359 self
360 }
361
362 #[must_use]
364 pub fn incognito(mut self, incognito: bool) -> Self {
365 self.incognito = incognito;
366 self
367 }
368
369 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
371 #[must_use]
372 pub fn transparent(mut self, transparent: bool) -> Self {
373 self.transparent = transparent;
374 self
375 }
376
377 #[must_use]
379 pub fn focused(mut self, focus: bool) -> Self {
380 self.focus = focus;
381 self
382 }
383
384 #[must_use]
386 pub fn auto_resize(mut self) -> Self {
387 self.auto_resize = true;
388 self
389 }
390
391 #[must_use]
393 pub fn proxy_url(mut self, url: Url) -> Self {
394 self.proxy_url = Some(url);
395 self
396 }
397
398 #[must_use]
408 pub fn zoom_hotkeys_enabled(mut self, enabled: bool) -> Self {
409 self.zoom_hotkeys_enabled = enabled;
410 self
411 }
412
413 #[must_use]
420 pub fn browser_extensions_enabled(mut self, enabled: bool) -> Self {
421 self.browser_extensions_enabled = enabled;
422 self
423 }
424
425 #[must_use]
435 pub fn use_https_scheme(mut self, enabled: bool) -> Self {
436 self.use_https_scheme = enabled;
437 self
438 }
439
440 #[must_use]
450 pub fn devtools(mut self, enabled: Option<bool>) -> Self {
451 self.devtools = enabled;
452 self
453 }
454
455 #[must_use]
461 pub fn background_color(mut self, color: Color) -> Self {
462 self.background_color = Some(color);
463 self
464 }
465
466 #[must_use]
474 pub fn traffic_light_position(mut self, position: dpi::Position) -> Self {
475 self.traffic_light_position = Some(position);
476 self
477 }
478
479 #[must_use]
494 pub fn background_throttling(mut self, policy: Option<BackgroundThrottlingPolicy>) -> Self {
495 self.background_throttling = policy;
496 self
497 }
498}
499
500pub type WebviewIpcHandler<T, R> = Box<dyn Fn(DetachedWebview<T, R>, Request<String>) + Send>;