1use std::{borrow::Cow, path::PathBuf};
2
3use pyo3::{
4 prelude::*,
5 pybacked::PyBackedStr,
6 types::{PyDict, PyString},
7};
8use pyo3_utils::{
9 from_py_dict::{derive_from_py_dict, FromPyDict as _, NotRequired},
10 py_wrapper::{PyWrapper, PyWrapperT0},
11 serde::PySerde,
12 ungil::UnsafeUngilExt,
13};
14use tauri::webview::{
15 self,
16 cookie::{self, time::OffsetDateTime},
17};
18
19use crate::{
20 ext_mod::{
21 image::Image,
22 manager_method_impl,
23 menu::{context_menu_impl, ImplContextMenu, Menu, MenuEvent},
24 window::{Effects, Monitor, ProgressBarState, TitleBarStyle, Window},
25 CursorIcon, ImplManager, PhysicalPositionF64, PhysicalPositionI32, PhysicalSizeU32,
26 Position, Size, Theme, Url, UserAttentionType, WebviewEvent, WebviewUrl, WindowEvent,
27 },
28 tauri_runtime::Runtime,
29 utils::{cfg_impl, delegate_inner, PyResultExt as _, TauriError},
30};
31
32pub(crate) type TauriWebviewWindow = webview::WebviewWindow<Runtime>;
33type TauriWebview = webview::Webview<Runtime>;
34
35pub(crate) type WindowConfigFrom = PySerde<tauri::utils::config::WindowConfig>;
39#[expect(dead_code)] pub(crate) type WindowConfigInto<'a> = PySerde<Cow<'a, tauri::utils::config::WindowConfig>>;
42
43#[pyclass(frozen)]
45#[non_exhaustive]
46pub struct WebviewWindow(pub PyWrapper<PyWrapperT0<TauriWebviewWindow>>);
47
48impl WebviewWindow {
49 pub(crate) fn new(webview_window: TauriWebviewWindow) -> Self {
50 Self(PyWrapper::new0(webview_window))
51 }
52}
53
54#[pymethods]
55impl WebviewWindow {
56 #[new]
58 #[pyo3(signature = (manager, label, url, /, **kwargs))]
59 fn __new__(
60 py: Python<'_>,
61 manager: ImplManager,
62 label: String,
63 url: &Bound<'_, WebviewUrl>,
64 kwargs: Option<&Bound<'_, PyDict>>,
65 ) -> PyResult<Self> {
66 WebviewWindowBuilder::build(py, manager, label, url, kwargs)
67 }
68
69 fn run_on_main_thread(&self, py: Python<'_>, handler: PyObject) -> PyResult<()> {
70 py.allow_threads(|| {
71 delegate_inner!(self, run_on_main_thread, move || {
72 Python::with_gil(|py| {
73 let handler = handler.bind(py);
74 let result = handler.call0();
75 result.unwrap_unraisable_py_result(py, Some(handler), || {
76 "Python exception occurred in `WebviewWindow::run_on_main_thread`"
77 });
78 })
79 })
80 })
81 }
82
83 fn label<'py>(&self, py: Python<'py>) -> Bound<'py, PyString> {
84 let webview_window = self.0.inner_ref();
85 PyString::intern(py, webview_window.label())
87 }
88
89 fn on_window_event(&self, py: Python<'_>, handler: PyObject) {
90 py.allow_threads(|| {
91 self.0.inner_ref().on_window_event(move |window_event| {
92 Python::with_gil(|py| {
93 let window_event: WindowEvent = WindowEvent::from_tauri(py, window_event)
94 .expect("Failed to convert `WindowEvent` to pyobject");
96
97 let handler = handler.bind(py);
98 let result = handler.call1((window_event,));
99 result.unwrap_unraisable_py_result(py, Some(handler), || {
100 "Python exception occurred in `WebviewWindow::on_window_event` handler"
101 });
102 })
103 })
104 })
105 }
106
107 fn on_webview_event(&self, py: Python<'_>, handler: PyObject) {
108 py.allow_threads(|| {
109 self.0.inner_ref().on_webview_event(move |webview_event| {
110 Python::with_gil(|py| {
111 let webview_event: WebviewEvent = WebviewEvent::from_tauri(py, webview_event)
112 .expect("Failed to convert `WebviewEvent` to pyobject");
114
115 let handler = handler.bind(py);
116 let result = handler.call1((webview_event,));
117 result.unwrap_unraisable_py_result(py, Some(handler), || {
118 "Python exception occurred in `WebviewWindow::on_webview_event` handler"
119 });
120 })
121 })
122 })
123 }
124
125 fn on_menu_event(slf: Py<Self>, py: Python<'_>, handler: PyObject) {
126 let moved_slf = slf.clone_ref(py);
127 py.allow_threads(|| {
128 slf.get()
129 .0
130 .inner_ref()
131 .on_menu_event(move |_window, menu_event| {
132 Python::with_gil(|py| {
133 let window: &Py<Self> = &moved_slf; debug_assert_eq!(
138 &*window.get().0.inner_ref().as_ref().window_ref(),
139 _window
140 );
141 let menu_event: Bound<'_, MenuEvent> =
142 MenuEvent::intern(py, &menu_event.id.0);
143
144 let handler = handler.bind(py);
145 let result = handler.call1((window, menu_event));
146 result.unwrap_unraisable_py_result(py, Some(handler), || {
147 "Python exception occurred in `WebviewWindow::on_menu_event` handler"
148 });
149 })
150 })
151 })
152 }
153
154 fn menu(&self, py: Python<'_>) -> Option<Menu> {
155 py.allow_threads(|| self.0.inner_ref().menu().map(Menu::new))
156 }
157
158 fn set_menu(&self, py: Python<'_>, menu: Py<Menu>) -> PyResult<Option<Menu>> {
159 py.allow_threads(|| {
160 let menu = menu.get().0.inner_ref().clone();
161 let returned_menu = delegate_inner!(self, set_menu, menu)?;
162 PyResult::Ok(returned_menu.map(Menu::new))
163 })
164 }
165
166 fn remove_menu(&self, py: Python<'_>) -> PyResult<Option<Menu>> {
167 py.allow_threads(|| {
168 let returned_menu = delegate_inner!(self, remove_menu,)?;
169 PyResult::Ok(returned_menu.map(Menu::new))
170 })
171 }
172
173 fn hide_menu(&self, py: Python<'_>) -> PyResult<()> {
174 py.allow_threads(|| delegate_inner!(self, hide_menu,))
175 }
176
177 fn show_menu(&self, py: Python<'_>) -> PyResult<()> {
178 py.allow_threads(|| delegate_inner!(self, show_menu,))
179 }
180
181 fn is_menu_visible(&self, py: Python<'_>) -> PyResult<bool> {
182 py.allow_threads(|| delegate_inner!(self, is_menu_visible,))
183 }
184
185 fn popup_menu(&self, py: Python<'_>, menu: ImplContextMenu) -> PyResult<()> {
186 py.allow_threads(|| {
187 context_menu_impl!(&menu, |menu| delegate_inner!(self, popup_menu, menu))
188 })
189 }
190
191 fn popup_menu_at(
192 &self,
193 py: Python<'_>,
194 menu: ImplContextMenu,
195 position: Py<Position>,
196 ) -> PyResult<()> {
197 let position = position.get().to_tauri(py)?;
198 py.allow_threads(|| {
199 context_menu_impl!(&menu, |menu| delegate_inner!(
200 self,
201 popup_menu_at,
202 menu,
203 position
204 ))
205 })
206 }
207
208 fn scale_factor(&self, py: Python<'_>) -> PyResult<f64> {
209 py.allow_threads(|| delegate_inner!(self, scale_factor,))
210 }
211
212 fn inner_position(&self, py: Python<'_>) -> PyResult<PhysicalPositionI32> {
213 let position = py.allow_threads(|| delegate_inner!(self, inner_position,))?;
214 PhysicalPositionI32::from_tauri(py, position)
215 }
216
217 fn outer_position(&self, py: Python<'_>) -> PyResult<PhysicalPositionI32> {
218 let position = py.allow_threads(|| delegate_inner!(self, outer_position,))?;
219 PhysicalPositionI32::from_tauri(py, position)
220 }
221
222 fn inner_size(&self, py: Python<'_>) -> PyResult<PhysicalSizeU32> {
223 let size = py.allow_threads(|| delegate_inner!(self, inner_size,))?;
224 PhysicalSizeU32::from_tauri(py, size)
225 }
226
227 fn outer_size(&self, py: Python<'_>) -> PyResult<PhysicalSizeU32> {
228 let size = py.allow_threads(|| delegate_inner!(self, outer_size,))?;
229 PhysicalSizeU32::from_tauri(py, size)
230 }
231
232 fn is_fullscreen(&self, py: Python<'_>) -> PyResult<bool> {
233 py.allow_threads(|| delegate_inner!(self, is_fullscreen,))
234 }
235
236 fn is_minimized(&self, py: Python<'_>) -> PyResult<bool> {
237 py.allow_threads(|| delegate_inner!(self, is_minimized,))
238 }
239
240 fn is_maximized(&self, py: Python<'_>) -> PyResult<bool> {
241 py.allow_threads(|| delegate_inner!(self, is_maximized,))
242 }
243
244 fn is_focused(&self, py: Python<'_>) -> PyResult<bool> {
245 py.allow_threads(|| delegate_inner!(self, is_focused,))
246 }
247
248 fn is_decorated(&self, py: Python<'_>) -> PyResult<bool> {
249 py.allow_threads(|| delegate_inner!(self, is_decorated,))
250 }
251
252 fn is_resizable(&self, py: Python<'_>) -> PyResult<bool> {
253 py.allow_threads(|| delegate_inner!(self, is_resizable,))
254 }
255
256 fn is_enabled(&self, py: Python<'_>) -> PyResult<bool> {
257 py.allow_threads(|| delegate_inner!(self, is_enabled,))
258 }
259
260 fn is_always_on_top(&self, py: Python<'_>) -> PyResult<bool> {
261 py.allow_threads(|| delegate_inner!(self, is_always_on_top,))
262 }
263
264 fn is_maximizable(&self, py: Python<'_>) -> PyResult<bool> {
265 py.allow_threads(|| delegate_inner!(self, is_maximizable,))
266 }
267
268 fn is_minimizable(&self, py: Python<'_>) -> PyResult<bool> {
269 py.allow_threads(|| delegate_inner!(self, is_minimizable,))
270 }
271
272 fn is_closable(&self, py: Python<'_>) -> PyResult<bool> {
273 py.allow_threads(|| delegate_inner!(self, is_closable,))
274 }
275
276 fn is_visible(&self, py: Python<'_>) -> PyResult<bool> {
277 py.allow_threads(|| delegate_inner!(self, is_visible,))
278 }
279
280 fn title(&self, py: Python<'_>) -> PyResult<String> {
281 py.allow_threads(|| delegate_inner!(self, title,))
282 }
283
284 fn current_monitor(&self, py: Python<'_>) -> PyResult<Option<Monitor>> {
285 let monitor = py.allow_threads(|| delegate_inner!(self, current_monitor,))?;
286 let monitor = monitor.map(|m| Monitor::from_tauri(py, m)).transpose()?;
287 Ok(monitor)
288 }
289
290 fn primary_monitor(&self, py: Python<'_>) -> PyResult<Option<Monitor>> {
291 let monitor = py.allow_threads(|| delegate_inner!(self, primary_monitor,))?;
292 let monitor = monitor.map(|m| Monitor::from_tauri(py, m)).transpose()?;
293 Ok(monitor)
294 }
295
296 fn monitor_from_point(&self, py: Python<'_>, x: f64, y: f64) -> PyResult<Option<Monitor>> {
297 let monitor = py.allow_threads(|| delegate_inner!(self, monitor_from_point, x, y))?;
298 let monitor = monitor.map(|m| Monitor::from_tauri(py, m)).transpose()?;
299 Ok(monitor)
300 }
301
302 fn available_monitors(&self, py: Python<'_>) -> PyResult<Vec<Monitor>> {
303 let monitors = py.allow_threads(|| delegate_inner!(self, available_monitors,))?;
304 let monitors = monitors
305 .into_iter()
306 .map(|m| Monitor::from_tauri(py, m))
307 .collect::<PyResult<Vec<_>>>()?;
308 Ok(monitors)
309 }
310
311 fn theme(&self, py: Python<'_>) -> PyResult<Theme> {
312 py.allow_threads(|| delegate_inner!(self, theme,).map(Into::into))
313 }
314
315 fn cursor_position(&self, py: Python<'_>) -> PyResult<PhysicalPositionF64> {
316 let position = py.allow_threads(|| delegate_inner!(self, cursor_position,))?;
317 PhysicalPositionF64::from_tauri(py, position)
318 }
319
320 fn center(&self, py: Python<'_>) -> PyResult<()> {
321 py.allow_threads(|| delegate_inner!(self, center,))
322 }
323
324 fn request_user_attention(
325 &self,
326 py: Python<'_>,
327 attention_type: Option<UserAttentionType>,
328 ) -> PyResult<()> {
329 py.allow_threads(|| {
330 delegate_inner!(self, request_user_attention, attention_type.map(Into::into))
331 })
332 }
333
334 fn set_resizable(&self, py: Python<'_>, resizable: bool) -> PyResult<()> {
335 py.allow_threads(|| delegate_inner!(self, set_resizable, resizable))
336 }
337
338 fn set_enabled(&self, py: Python<'_>, enabled: bool) -> PyResult<()> {
339 py.allow_threads(|| delegate_inner!(self, set_enabled, enabled))
340 }
341
342 fn set_maximizable(&self, py: Python<'_>, maximizable: bool) -> PyResult<()> {
343 py.allow_threads(|| delegate_inner!(self, set_maximizable, maximizable))
344 }
345
346 fn set_minimizable(&self, py: Python<'_>, minimizable: bool) -> PyResult<()> {
347 py.allow_threads(|| delegate_inner!(self, set_minimizable, minimizable))
348 }
349
350 fn set_closable(&self, py: Python<'_>, closable: bool) -> PyResult<()> {
351 py.allow_threads(|| delegate_inner!(self, set_closable, closable))
352 }
353
354 fn set_title(&self, py: Python<'_>, title: &str) -> PyResult<()> {
355 py.allow_threads(|| delegate_inner!(self, set_title, title))
356 }
357
358 fn maximize(&self, py: Python<'_>) -> PyResult<()> {
359 py.allow_threads(|| delegate_inner!(self, maximize,))
360 }
361
362 fn unmaximize(&self, py: Python<'_>) -> PyResult<()> {
363 py.allow_threads(|| delegate_inner!(self, unmaximize,))
364 }
365
366 fn minimize(&self, py: Python<'_>) -> PyResult<()> {
367 py.allow_threads(|| delegate_inner!(self, minimize,))
368 }
369
370 fn unminimize(&self, py: Python<'_>) -> PyResult<()> {
371 py.allow_threads(|| delegate_inner!(self, unminimize,))
372 }
373
374 fn show(&self, py: Python<'_>) -> PyResult<()> {
375 py.allow_threads(|| delegate_inner!(self, show,))
376 }
377
378 fn hide(&self, py: Python<'_>) -> PyResult<()> {
379 py.allow_threads(|| delegate_inner!(self, hide,))
380 }
381
382 fn close(&self, py: Python<'_>) -> PyResult<()> {
383 py.allow_threads(|| delegate_inner!(self, close,))
384 }
385
386 fn destroy(&self, py: Python<'_>) -> PyResult<()> {
387 py.allow_threads(|| delegate_inner!(self, destroy,))
388 }
389
390 fn set_decorations(&self, py: Python<'_>, decorations: bool) -> PyResult<()> {
391 py.allow_threads(|| delegate_inner!(self, set_decorations, decorations))
392 }
393
394 fn set_shadow(&self, py: Python<'_>, shadow: bool) -> PyResult<()> {
395 py.allow_threads(|| delegate_inner!(self, set_shadow, shadow))
396 }
397
398 fn set_effects(&self, py: Python<'_>, effects: Option<Effects>) -> PyResult<()> {
399 py.allow_threads(|| {
400 let effects = effects.map(|e| e.into_tauri().build());
401 delegate_inner!(self, set_effects, effects)
402 })
403 }
404
405 fn set_always_on_bottom(&self, py: Python<'_>, always_on_bottom: bool) -> PyResult<()> {
406 py.allow_threads(|| delegate_inner!(self, set_always_on_bottom, always_on_bottom))
407 }
408
409 fn set_always_on_top(&self, py: Python<'_>, always_on_top: bool) -> PyResult<()> {
410 py.allow_threads(|| delegate_inner!(self, set_always_on_top, always_on_top))
411 }
412
413 fn set_visible_on_all_workspaces(
414 &self,
415 py: Python<'_>,
416 visible_on_all_workspaces: bool,
417 ) -> PyResult<()> {
418 py.allow_threads(|| {
419 delegate_inner!(
420 self,
421 set_visible_on_all_workspaces,
422 visible_on_all_workspaces
423 )
424 })
425 }
426
427 fn set_content_protected(&self, py: Python<'_>, protected: bool) -> PyResult<()> {
428 py.allow_threads(|| delegate_inner!(self, set_content_protected, protected))
429 }
430
431 fn set_size(&self, py: Python<'_>, size: Py<Size>) -> PyResult<()> {
432 let size = size.get().to_tauri(py)?;
433 py.allow_threads(|| delegate_inner!(self, set_size, size))
434 }
435
436 fn set_min_size(&self, py: Python<'_>, size: Option<Py<Size>>) -> PyResult<()> {
437 let size = size.map(|s| s.get().to_tauri(py)).transpose()?;
438 py.allow_threads(|| delegate_inner!(self, set_min_size, size))
439 }
440
441 fn set_max_size(&self, py: Python<'_>, size: Option<Py<Size>>) -> PyResult<()> {
442 let size = size.map(|s| s.get().to_tauri(py)).transpose()?;
443 py.allow_threads(|| delegate_inner!(self, set_max_size, size))
444 }
445
446 fn set_position(&self, py: Python<'_>, position: Py<Position>) -> PyResult<()> {
450 let position = position.get().to_tauri(py)?;
451 py.allow_threads(|| delegate_inner!(self, set_position, position))
452 }
453
454 fn set_fullscreen(&self, py: Python<'_>, fullscreen: bool) -> PyResult<()> {
455 py.allow_threads(|| delegate_inner!(self, set_fullscreen, fullscreen))
456 }
457
458 fn set_focus(&self, py: Python<'_>) -> PyResult<()> {
459 py.allow_threads(|| delegate_inner!(self, set_focus,))
460 }
461
462 fn set_icon(&self, py: Python<'_>, icon: Py<Image>) -> PyResult<()> {
463 let icon = icon.get().to_tauri(py);
464 py.allow_threads(|| delegate_inner!(self, set_icon, icon))
465 }
466
467 fn set_background_color(&self, py: Python<'_>, color: Option<Color>) -> PyResult<()> {
468 let color = color.map(|c| c.0);
469 py.allow_threads(|| delegate_inner!(self, set_background_color, color))
470 }
471
472 fn set_skip_taskbar(&self, py: Python<'_>, skip: bool) -> PyResult<()> {
473 py.allow_threads(|| delegate_inner!(self, set_skip_taskbar, skip))
474 }
475
476 fn set_cursor_grab(&self, py: Python<'_>, grab: bool) -> PyResult<()> {
477 py.allow_threads(|| delegate_inner!(self, set_cursor_grab, grab))
478 }
479
480 fn set_cursor_visible(&self, py: Python<'_>, visible: bool) -> PyResult<()> {
481 py.allow_threads(|| delegate_inner!(self, set_cursor_visible, visible))
482 }
483
484 fn set_cursor_icon(&self, py: Python<'_>, icon: CursorIcon) -> PyResult<()> {
485 py.allow_threads(|| delegate_inner!(self, set_cursor_icon, icon.into()))
486 }
487
488 fn set_cursor_position(&self, py: Python<'_>, position: Py<Position>) -> PyResult<()> {
489 let position = position.get().to_tauri(py)?;
490 py.allow_threads(|| delegate_inner!(self, set_cursor_position, position))
491 }
492
493 fn set_ignore_cursor_events(&self, py: Python<'_>, ignore: bool) -> PyResult<()> {
494 py.allow_threads(|| delegate_inner!(self, set_ignore_cursor_events, ignore))
495 }
496
497 fn start_dragging(&self, py: Python<'_>) -> PyResult<()> {
498 py.allow_threads(|| delegate_inner!(self, start_dragging,))
499 }
500
501 #[cfg(windows)]
502 fn set_overlay_icon(&self, py: Python<'_>, icon: Option<Py<Image>>) -> PyResult<()> {
503 let icon = icon.as_ref().map(|i| i.get().to_tauri(py));
504 py.allow_threads(|| delegate_inner!(self, set_overlay_icon, icon))
505 }
506
507 fn set_badge_count(&self, py: Python<'_>, count: Option<i64>) -> PyResult<()> {
508 py.allow_threads(|| delegate_inner!(self, set_badge_count, count))
509 }
510
511 #[cfg(target_os = "macos")]
512 fn set_badge_label(&self, py: Python<'_>, label: Option<String>) -> PyResult<()> {
513 py.allow_threads(|| delegate_inner!(self, set_badge_label, label))
514 }
515
516 fn set_progress_bar(&self, py: Python<'_>, progress_state: ProgressBarState) -> PyResult<()> {
517 py.allow_threads(|| delegate_inner!(self, set_progress_bar, progress_state.into_tauri()))
518 }
519
520 fn set_title_bar_style(&self, py: Python<'_>, style: TitleBarStyle) -> PyResult<()> {
521 py.allow_threads(|| delegate_inner!(self, set_title_bar_style, style.into()))
522 }
523
524 fn set_theme(&self, py: Python<'_>, theme: Option<Theme>) -> PyResult<()> {
525 py.allow_threads(|| delegate_inner!(self, set_theme, theme.map(Into::into)))
526 }
527
528 fn print(&self, py: Python<'_>) -> PyResult<()> {
529 py.allow_threads(|| delegate_inner!(self, print,))
530 }
531
532 fn url(&self, py: Python<'_>) -> PyResult<Url<'_>> {
533 let url = py.allow_threads(|| delegate_inner!(self, url,))?;
534 Ok(url.into())
535 }
536
537 fn navigate(&self, py: Python<'_>, url: Url<'_>) -> PyResult<()> {
538 py.allow_threads(|| delegate_inner!(self, navigate, url.into()))
539 }
540
541 fn reload(&self, py: Python<'_>) -> PyResult<()> {
542 py.allow_threads(|| delegate_inner!(self, reload,))
543 }
544
545 fn eval(&self, py: Python<'_>, js: &str) -> PyResult<()> {
546 py.allow_threads(|| delegate_inner!(self, eval, js))
547 }
548
549 fn open_devtools(&self, py: Python<'_>) -> PyResult<()> {
550 cfg_impl!(|any(debug_assertions, feature = "tauri-devtools")| -> () {
551 py.allow_threads(|| {
552 self.0.inner_ref().open_devtools();
553 Ok(())
554 })
555 })
556 }
557
558 fn close_devtools(&self, py: Python<'_>) -> PyResult<()> {
559 cfg_impl!(|any(debug_assertions, feature = "tauri-devtools")| -> () {
560 py.allow_threads(|| {
561 self.0.inner_ref().close_devtools();
562 Ok(())
563 })
564 })
565 }
566
567 fn is_devtools_open(&self, py: Python<'_>) -> PyResult<bool> {
568 cfg_impl!(|any(debug_assertions, feature = "tauri-devtools")| -> bool {
569 py.allow_threads(|| {
570 Ok(self.0.inner_ref().is_devtools_open())
571 })
572 })
573 }
574
575 fn set_zoom(&self, py: Python<'_>, scale_factor: f64) -> PyResult<()> {
576 py.allow_threads(|| delegate_inner!(self, set_zoom, scale_factor))
577 }
578
579 fn clear_all_browsing_data(&self, py: Python<'_>) -> PyResult<()> {
580 py.allow_threads(|| delegate_inner!(self, clear_all_browsing_data,))
581 }
582
583 fn cookies_for_url(&self, py: Python<'_>, url: Url<'_>) -> PyResult<Vec<Cookie>> {
584 let cookies = py.allow_threads(|| delegate_inner!(self, cookies_for_url, url.into()))?;
585 let cookies = cookies
586 .into_iter()
587 .map(|c| Cookie::from_tauri(py, &c))
588 .collect::<Vec<_>>();
589 Ok(cookies)
590 }
591
592 fn cookies(&self, py: Python<'_>) -> PyResult<Vec<Cookie>> {
593 let cookies = py.allow_threads(|| delegate_inner!(self, cookies,))?;
594 let cookies = cookies
595 .into_iter()
596 .map(|c| Cookie::from_tauri(py, &c))
597 .collect::<Vec<_>>();
598 Ok(cookies)
599 }
600
601 fn set_cookie(&self, py: Python<'_>, cookie: Cookie) -> PyResult<()> {
602 let cookie = cookie.to_tauri(py)?;
603 py.allow_threads(|| delegate_inner!(self, set_cookie, cookie))
604 }
605
606 fn delete_cookie(&self, py: Python<'_>, cookie: Cookie) -> PyResult<()> {
607 let cookie = cookie.to_tauri(py)?;
608 py.allow_threads(|| delegate_inner!(self, delete_cookie, cookie))
609 }
610
611 fn as_ref_webview(&self) -> Webview {
613 let webview = self.0.inner_ref().as_ref().clone();
614 Webview::new(webview)
615 }
616
617 }
619
620#[pyclass(frozen)]
622#[non_exhaustive]
623pub struct Webview(pub PyWrapper<PyWrapperT0<TauriWebview>>);
624
625impl Webview {
626 pub(crate) fn new(webview: TauriWebview) -> Self {
627 Self(PyWrapper::new0(webview))
628 }
629}
630
631#[pymethods]
632impl Webview {
633 fn window(&self) -> Window {
634 let window = self.0.inner_ref().window();
635 Window::new(window)
636 }
637}
638
639pub struct Color(pub(crate) webview::Color);
643
644impl<'py> FromPyObject<'py> for Color {
645 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
646 let (r, g, b, a): (u8, u8, u8, u8) = ob.extract()?;
647 Ok(Self(webview::Color(r, g, b, a)))
648 }
649}
650
651macro_rules! same_site_impl {
652 ($ident:ident => : $( $(#[$meta:meta])* $variant:ident ),*) => {
653 #[pyclass(frozen, eq, eq_int)]
655 #[derive(PartialEq, Clone, Copy)]
656 #[non_exhaustive]
657 pub enum $ident {
658 $(
659 $(#[$meta])*
660 $variant,
661 )*
662 }
663
664 impl From<cookie::SameSite> for $ident {
665 fn from(val: cookie::SameSite) -> Self {
666 match val {
667 $(cookie::SameSite::$variant => $ident::$variant,)*
668 }
669 }
670 }
671
672 impl From<$ident> for cookie::SameSite {
673 fn from(val: $ident) -> Self {
674 match val {
675 $($ident::$variant => cookie::SameSite::$variant,)*
676 }
677 }
678 }
679 };
680}
681
682same_site_impl!(SameSite => : Strict, Lax, #[pyo3(name = "None_")] None);
683
684impl<'py> IntoPyObject<'py> for &SameSite {
687 type Error = <SameSite as IntoPyObject<'py>>::Error;
688 type Output = <SameSite as IntoPyObject<'py>>::Output;
689 type Target = <SameSite as IntoPyObject<'py>>::Target;
690
691 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
692 <SameSite as IntoPyObject<'py>>::into_pyobject(*self, py)
693 }
694}
695
696#[derive(IntoPyObject, IntoPyObjectRef)]
704pub struct Cookie {
705 key: Py<PyString>,
706 value: Py<PyString>,
707 max_age: Option<i64>,
708 expires: Option<OffsetDateTime>,
709 path: Option<Py<PyString>>,
710 domain: Option<Py<PyString>>,
711 secure: Option<bool>,
712 httponly: Option<bool>,
713 samesite: Option<SameSite>,
714 partitioned: Option<bool>,
715}
716
717derive_from_py_dict!(Cookie {
718 key,
719 value,
720 #[pyo3(default)]
721 max_age,
722 #[pyo3(default)]
723 expires,
724 #[pyo3(default)]
725 path,
726 #[pyo3(default)]
727 domain,
728 #[pyo3(default)]
729 secure,
730 #[pyo3(default)]
731 httponly,
732 #[pyo3(default)]
733 samesite,
734 #[pyo3(default)]
735 partitioned,
736});
737
738impl<'py> FromPyObject<'py> for Cookie {
739 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
740 let dict = ob.downcast::<PyDict>()?;
741 Self::from_py_dict(dict)
742 }
743}
744
745impl Cookie {
746 pub(crate) fn from_tauri(py: Python<'_>, cookie: &webview::Cookie<'_>) -> Self {
747 let key = PyString::new(py, cookie.name()).unbind();
748 let value = PyString::new(py, cookie.value()).unbind();
749 let max_age = cookie.max_age().map(|d| d.whole_seconds());
750 let expires = cookie.expires_datetime();
751 let path = cookie.path().map(|p| PyString::new(py, p).unbind());
752 let domain = cookie.domain().map(|d| PyString::new(py, d).unbind());
753 let secure = cookie.secure();
754 let httponly = cookie.http_only();
755 let samesite = cookie.same_site().map(|s| s.into());
756 let partitioned = cookie.partitioned();
757 Self {
758 key,
759 value,
760 max_age,
761 expires,
762 path,
763 domain,
764 secure,
765 httponly,
766 samesite,
767 partitioned,
768 }
769 }
770
771 pub(crate) fn to_tauri(&self, py: Python<'_>) -> PyResult<webview::Cookie<'_>> {
772 let Self {
773 key,
774 value,
775 max_age,
776 expires,
777 path,
778 domain,
779 secure,
780 httponly,
781 samesite,
782 partitioned,
783 } = self;
784
785 let key = key.to_cow(py)?;
787 let value = value.to_cow(py)?;
788 let mut cookie_builder = cookie::CookieBuilder::new(key, value);
789
790 if let Some(max_age) = max_age {
791 cookie_builder = cookie_builder.max_age(cookie::time::Duration::seconds(*max_age));
792 }
793 if let Some(expires) = expires {
794 cookie_builder = cookie_builder.expires(*expires);
795 }
796 if let Some(path) = path {
797 cookie_builder = cookie_builder.path(path.to_cow(py)?);
798 }
799 if let Some(domain) = domain {
800 cookie_builder = cookie_builder.domain(domain.to_cow(py)?);
801 }
802 if let Some(secure) = secure {
803 cookie_builder = cookie_builder.secure(*secure);
804 }
805 if let Some(httponly) = httponly {
806 cookie_builder = cookie_builder.http_only(*httponly);
807 }
808 if let Some(samesite) = samesite {
809 cookie_builder = cookie_builder.same_site((*samesite).into());
810 }
811 if let Some(partitioned) = partitioned {
812 cookie_builder = cookie_builder.partitioned(*partitioned);
813 }
814 Ok(cookie_builder.build())
815 }
816}
817
818#[non_exhaustive]
820pub struct WebviewWindowBuilderArgs {
821 on_navigation: NotRequired<PyObject>,
828 on_document_title_changed: NotRequired<PyObject>,
830 menu: NotRequired<Py<Menu>>,
832 center: NotRequired<bool>,
833 position: NotRequired<(f64, f64)>,
834 inner_size: NotRequired<(f64, f64)>,
835 min_inner_size: NotRequired<(f64, f64)>,
836 max_inner_size: NotRequired<(f64, f64)>,
837 prevent_overflow: NotRequired<bool>,
839 prevent_overflow_with_margin: NotRequired<Py<Size>>,
840 resizable: NotRequired<bool>,
841 maximizable: NotRequired<bool>,
842 minimizable: NotRequired<bool>,
843 closable: NotRequired<bool>,
844 title: NotRequired<String>,
845 fullscreen: NotRequired<bool>,
846 focusable: NotRequired<bool>,
847 focused: NotRequired<bool>,
848 maximized: NotRequired<bool>,
849 visible: NotRequired<bool>,
850 theme: NotRequired<Option<Theme>>,
851 decorations: NotRequired<bool>,
852 always_on_bottom: NotRequired<bool>,
853 always_on_top: NotRequired<bool>,
854 visible_on_all_workspaces: NotRequired<bool>,
855 content_protected: NotRequired<bool>,
856 icon: NotRequired<Py<Image>>,
857 skip_taskbar: NotRequired<bool>,
858 window_classname: NotRequired<String>,
859 shadow: NotRequired<bool>,
860 parent: NotRequired<Py<WebviewWindow>>,
861 #[cfg(windows)]
862 owner: NotRequired<Py<WebviewWindow>>,
863 #[cfg(any(
864 target_os = "linux",
865 target_os = "dragonfly",
866 target_os = "freebsd",
867 target_os = "netbsd",
868 target_os = "openbsd"
869 ))]
870 transient_for: NotRequired<Py<WebviewWindow>>,
871 #[cfg(windows)]
872 drag_and_drop: NotRequired<bool>,
873 #[cfg(target_os = "macos")]
874 title_bar_style: NotRequired<TitleBarStyle>,
875 #[cfg(target_os = "macos")]
876 traffic_light_position: NotRequired<Py<Position>>,
877 #[cfg(target_os = "macos")]
878 allow_link_preview: NotRequired<bool>,
879 #[cfg(target_os = "macos")]
880 hidden_title: NotRequired<bool>,
881 #[cfg(target_os = "macos")]
882 tabbing_identifier: NotRequired<PyBackedStr>,
883 effects: NotRequired<Effects>,
884 accept_first_mouse: NotRequired<bool>,
885 initialization_script: NotRequired<String>,
886 initialization_script_for_all_frames: NotRequired<String>,
887 user_agent: NotRequired<PyBackedStr>,
888 additional_browser_args: NotRequired<PyBackedStr>,
889 data_directory: NotRequired<PathBuf>,
890 disable_drag_drop_handler: NotRequired<bool>,
891 enable_clipboard_access: NotRequired<bool>,
892 incognito: NotRequired<bool>,
893 auto_resize: NotRequired<bool>,
894 proxy_url: NotRequired<Url<'static>>,
896 transparent: NotRequired<bool>,
897 zoom_hotkeys_enabled: NotRequired<bool>,
898 browser_extensions_enabled: NotRequired<bool>,
899 extensions_path: NotRequired<PathBuf>,
900 use_https_scheme: NotRequired<bool>,
902 devtools: NotRequired<bool>,
903 background_color: NotRequired<Color>,
904 disable_javascript: NotRequired<bool>,
906 }
908
909derive_from_py_dict!(WebviewWindowBuilderArgs {
910 #[pyo3(default)]
911 on_navigation,
912 #[pyo3(default)]
913 on_document_title_changed,
914 #[pyo3(default)]
915 menu,
916 #[pyo3(default)]
917 center,
918 #[pyo3(default)]
919 position,
920 #[pyo3(default)]
921 inner_size,
922 #[pyo3(default)]
923 min_inner_size,
924 #[pyo3(default)]
925 max_inner_size,
926 #[pyo3(default)]
927 prevent_overflow,
928 #[pyo3(default)]
929 prevent_overflow_with_margin,
930 #[pyo3(default)]
931 resizable,
932 #[pyo3(default)]
933 maximizable,
934 #[pyo3(default)]
935 minimizable,
936 #[pyo3(default)]
937 closable,
938 #[pyo3(default)]
939 title,
940 #[pyo3(default)]
941 fullscreen,
942 #[pyo3(default)]
943 focusable,
944 #[pyo3(default)]
945 focused,
946 #[pyo3(default)]
947 maximized,
948 #[pyo3(default)]
949 visible,
950 #[pyo3(default)]
951 theme,
952 #[pyo3(default)]
953 decorations,
954 #[pyo3(default)]
955 always_on_bottom,
956 #[pyo3(default)]
957 always_on_top,
958 #[pyo3(default)]
959 visible_on_all_workspaces,
960 #[pyo3(default)]
961 content_protected,
962 #[pyo3(default)]
963 icon,
964 #[pyo3(default)]
965 skip_taskbar,
966 #[pyo3(default)]
967 window_classname,
968 #[pyo3(default)]
969 shadow,
970 #[pyo3(default)]
971 parent,
972 #[cfg(windows)]
973 #[pyo3(default)]
974 owner,
975 #[cfg(any(
976 target_os = "linux",
977 target_os = "dragonfly",
978 target_os = "freebsd",
979 target_os = "netbsd",
980 target_os = "openbsd"
981 ))]
982 #[pyo3(default)]
983 transient_for,
984 #[cfg(windows)]
985 #[pyo3(default)]
986 drag_and_drop,
987 #[cfg(target_os = "macos")]
988 #[pyo3(default)]
989 title_bar_style,
990 #[cfg(target_os = "macos")]
991 #[pyo3(default)]
992 traffic_light_position,
993 #[cfg(target_os = "macos")]
994 #[pyo3(default)]
995 allow_link_preview,
996 #[cfg(target_os = "macos")]
997 #[pyo3(default)]
998 hidden_title,
999 #[cfg(target_os = "macos")]
1000 #[pyo3(default)]
1001 tabbing_identifier,
1002 #[pyo3(default)]
1003 effects,
1004 #[pyo3(default)]
1005 accept_first_mouse,
1006 #[pyo3(default)]
1007 initialization_script,
1008 #[pyo3(default)]
1009 initialization_script_for_all_frames,
1010 #[pyo3(default)]
1011 user_agent,
1012 #[pyo3(default)]
1013 additional_browser_args,
1014 #[pyo3(default)]
1015 data_directory,
1016 #[pyo3(default)]
1017 disable_drag_drop_handler,
1018 #[pyo3(default)]
1019 enable_clipboard_access,
1020 #[pyo3(default)]
1021 incognito,
1022 #[pyo3(default)]
1023 auto_resize,
1024 #[pyo3(default)]
1025 proxy_url,
1026 #[pyo3(default)]
1027 transparent,
1028 #[pyo3(default)]
1029 zoom_hotkeys_enabled,
1030 #[pyo3(default)]
1031 browser_extensions_enabled,
1032 #[pyo3(default)]
1033 extensions_path,
1034 #[pyo3(default)]
1035 use_https_scheme,
1036 #[pyo3(default)]
1037 devtools,
1038 #[pyo3(default)]
1039 background_color,
1040 #[pyo3(default)]
1041 disable_javascript,
1042});
1043
1044impl WebviewWindowBuilderArgs {
1045 fn from_kwargs(kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<Option<Self>> {
1046 kwargs.map(Self::from_py_dict).transpose()
1047 }
1048
1049 fn apply_to_builder<'a, M>(
1050 self,
1051 py: Python<'_>,
1052 mut builder: webview::WebviewWindowBuilder<'a, Runtime, M>,
1053 ) -> PyResult<webview::WebviewWindowBuilder<'a, Runtime, M>>
1054 where
1055 M: tauri::Manager<Runtime>,
1056 {
1057 let Self {
1058 on_navigation,
1059 on_document_title_changed,
1060 menu,
1061 center,
1062 position,
1063 inner_size,
1064 min_inner_size,
1065 max_inner_size,
1066 prevent_overflow,
1067 prevent_overflow_with_margin,
1068 resizable,
1069 maximizable,
1070 minimizable,
1071 closable,
1072 title,
1073 fullscreen,
1074 focusable,
1075 focused,
1076 maximized,
1077 visible,
1078 theme,
1079 decorations,
1080 always_on_bottom,
1081 always_on_top,
1082 visible_on_all_workspaces,
1083 content_protected,
1084 icon,
1085 skip_taskbar,
1086 window_classname,
1087 shadow,
1088 parent,
1089 #[cfg(windows)]
1090 owner,
1091 #[cfg(any(
1092 target_os = "linux",
1093 target_os = "dragonfly",
1094 target_os = "freebsd",
1095 target_os = "netbsd",
1096 target_os = "openbsd"
1097 ))]
1098 transient_for,
1099 #[cfg(windows)]
1100 drag_and_drop,
1101 #[cfg(target_os = "macos")]
1102 title_bar_style,
1103 #[cfg(target_os = "macos")]
1104 traffic_light_position,
1105 #[cfg(target_os = "macos")]
1106 allow_link_preview,
1107 #[cfg(target_os = "macos")]
1108 hidden_title,
1109 #[cfg(target_os = "macos")]
1110 tabbing_identifier,
1111 effects,
1112 accept_first_mouse,
1113 initialization_script,
1114 initialization_script_for_all_frames,
1115 user_agent,
1116 additional_browser_args,
1117 data_directory,
1118 disable_drag_drop_handler,
1119 enable_clipboard_access,
1120 incognito,
1121 auto_resize,
1122 proxy_url,
1123 transparent,
1124 zoom_hotkeys_enabled,
1125 browser_extensions_enabled,
1126 extensions_path,
1127 use_https_scheme,
1128 devtools,
1129 background_color,
1130 disable_javascript,
1131 } = self;
1132
1133 if let Some(on_navigation) = on_navigation.0 {
1134 builder = builder.on_navigation(move |url| {
1135 Python::with_gil(|py| {
1136 let url = Url::from(url);
1137
1138 let handler = on_navigation.bind(py);
1139 let ret = handler
1140 .call1((url,))
1141 .unwrap_unraisable_py_result(py, Some(handler), || {
1142 "Python exception occurred in `WebviewWindowBuilder::on_navigation` handler"
1143 });
1144 ret.extract::<bool>()
1145 .unwrap_unraisable_py_result(py, Some(&ret), || {
1146 "`WebviewWindowBuilder::on_navigation` return non-bool value"
1147 })
1148 })
1149 });
1150 };
1151 if let Some(on_document_title_changed) = on_document_title_changed.0 {
1152 builder = builder.on_document_title_changed(move |webview_window, title| {
1153 Python::with_gil(|py| {
1154 let webview_window = WebviewWindow::new(webview_window);
1155
1156 let handler = on_document_title_changed.bind(py);
1157 handler
1158 .call1((webview_window, title))
1159 .unwrap_unraisable_py_result(py, Some(handler), || {
1160 "Python exception occurred in `WebviewWindowBuilder::on_document_title_changed` handler"
1161 });
1162 })
1163 });
1164 };
1165 if let Some(menu) = menu.0 {
1166 let menu = menu.get().0.inner_ref().clone();
1167 builder = builder.menu(menu);
1168 }
1169 if let Some(true) = center.0 {
1170 builder = builder.center();
1171 }
1172 if let Some((x, y)) = position.0 {
1173 builder = builder.position(x, y);
1174 }
1175 if let Some((width, height)) = inner_size.0 {
1176 builder = builder.inner_size(width, height);
1177 }
1178 if let Some((min_width, min_height)) = min_inner_size.0 {
1179 builder = builder.min_inner_size(min_width, min_height);
1180 }
1181 if let Some((max_width, max_height)) = max_inner_size.0 {
1182 builder = builder.max_inner_size(max_width, max_height);
1183 }
1184 if let Some(true) = prevent_overflow.0 {
1185 builder = builder.prevent_overflow();
1186 }
1187 if let Some(margin) = prevent_overflow_with_margin.0 {
1188 let margin = margin.get().to_tauri(py)?;
1189 builder = builder.prevent_overflow_with_margin(margin);
1190 }
1191 if let Some(resizable) = resizable.0 {
1192 builder = builder.resizable(resizable);
1193 }
1194 if let Some(maximizable) = maximizable.0 {
1195 builder = builder.maximizable(maximizable);
1196 }
1197 if let Some(minimizable) = minimizable.0 {
1198 builder = builder.minimizable(minimizable);
1199 }
1200 if let Some(closable) = closable.0 {
1201 builder = builder.closable(closable);
1202 }
1203 if let Some(title) = title.0 {
1204 builder = builder.title(title);
1205 }
1206 if let Some(fullscreen) = fullscreen.0 {
1207 builder = builder.fullscreen(fullscreen);
1208 }
1209 if let Some(focusable) = focusable.0 {
1210 builder = builder.focusable(focusable);
1211 }
1212 if let Some(focused) = focused.0 {
1213 builder = builder.focused(focused);
1214 }
1215 if let Some(maximized) = maximized.0 {
1216 builder = builder.maximized(maximized);
1217 }
1218 if let Some(visible) = visible.0 {
1219 builder = builder.visible(visible);
1220 }
1221 if let Some(theme) = theme.0 {
1222 builder = builder.theme(theme.map(Into::into));
1223 }
1224 if let Some(decorations) = decorations.0 {
1225 builder = builder.decorations(decorations);
1226 }
1227 if let Some(always_on_bottom) = always_on_bottom.0 {
1228 builder = builder.always_on_bottom(always_on_bottom);
1229 }
1230 if let Some(always_on_top) = always_on_top.0 {
1231 builder = builder.always_on_top(always_on_top);
1232 }
1233 if let Some(visible_on_all_workspaces) = visible_on_all_workspaces.0 {
1234 builder = builder.visible_on_all_workspaces(visible_on_all_workspaces);
1235 }
1236 if let Some(content_protected) = content_protected.0 {
1237 builder = builder.content_protected(content_protected);
1238 }
1239 if let Some(icon) = icon.0 {
1240 let icon = icon.get().to_tauri(py).to_owned();
1245 builder = builder.icon(icon).map_err(TauriError::from)?;
1246 }
1247 if let Some(skip_taskbar) = skip_taskbar.0 {
1248 builder = builder.skip_taskbar(skip_taskbar);
1249 }
1250 if let Some(window_classname) = window_classname.0 {
1251 builder = builder.window_classname(window_classname);
1252 }
1253 if let Some(shadow) = shadow.0 {
1254 builder = builder.shadow(shadow);
1255 }
1256 if let Some(parent) = parent.0 {
1257 let parent = parent.get().0.inner_ref();
1258 builder = builder.parent(&parent).map_err(TauriError::from)?;
1259 }
1260 #[cfg(windows)]
1261 if let Some(owner) = owner.0 {
1262 let owner = owner.get().0.inner_ref();
1263 builder = builder.owner(&owner).map_err(TauriError::from)?;
1264 }
1265 #[cfg(any(
1266 target_os = "linux",
1267 target_os = "dragonfly",
1268 target_os = "freebsd",
1269 target_os = "netbsd",
1270 target_os = "openbsd"
1271 ))]
1272 if let Some(transient_for) = transient_for.0 {
1273 let transient_for = transient_for.get().0.inner_ref();
1274 builder = builder
1275 .transient_for(&transient_for)
1276 .map_err(TauriError::from)?;
1277 }
1278 #[cfg(windows)]
1279 if let Some(drag_and_drop) = drag_and_drop.0 {
1280 builder = builder.drag_and_drop(drag_and_drop);
1281 }
1282 #[cfg(target_os = "macos")]
1283 if let Some(title_bar_style) = title_bar_style.0 {
1284 builder = builder.title_bar_style(title_bar_style.into());
1285 }
1286 #[cfg(target_os = "macos")]
1287 if let Some(traffic_light_position) = traffic_light_position.0 {
1288 let traffic_light_position = traffic_light_position.get().to_tauri(py)?;
1289 builder = builder.traffic_light_position(traffic_light_position);
1290 }
1291 #[cfg(target_os = "macos")]
1292 if let Some(allow_link_preview) = allow_link_preview.0 {
1293 builder = builder.allow_link_preview(allow_link_preview);
1294 }
1295 #[cfg(target_os = "macos")]
1296 if let Some(hidden_title) = hidden_title.0 {
1297 builder = builder.hidden_title(hidden_title);
1298 }
1299 #[cfg(target_os = "macos")]
1300 if let Some(tabbing_identifier) = tabbing_identifier.0 {
1301 builder = builder.tabbing_identifier(&tabbing_identifier);
1302 }
1303 if let Some(effects) = effects.0 {
1304 let effects = effects.into_tauri().build();
1305 builder = builder.effects(effects);
1306 }
1307 if let Some(accept_first_mouse) = accept_first_mouse.0 {
1308 builder = builder.accept_first_mouse(accept_first_mouse);
1309 }
1310 if let Some(initialization_script) = initialization_script.0 {
1311 builder = builder.initialization_script(initialization_script);
1312 }
1313 if let Some(initialization_script_for_all_frames) = initialization_script_for_all_frames.0 {
1314 builder =
1315 builder.initialization_script_for_all_frames(initialization_script_for_all_frames);
1316 }
1317 if let Some(user_agent) = user_agent.0 {
1318 builder = builder.user_agent(&user_agent);
1319 }
1320 if let Some(additional_browser_args) = additional_browser_args.0 {
1321 builder = builder.additional_browser_args(&additional_browser_args);
1322 }
1323 if let Some(data_directory) = data_directory.0 {
1324 builder = builder.data_directory(data_directory);
1325 }
1326 if let Some(true) = disable_drag_drop_handler.0 {
1327 builder = builder.disable_drag_drop_handler();
1328 }
1329 if let Some(true) = enable_clipboard_access.0 {
1330 builder = builder.enable_clipboard_access();
1331 }
1332 if let Some(incognito) = incognito.0 {
1333 builder = builder.incognito(incognito);
1334 }
1335 if let Some(true) = auto_resize.0 {
1336 builder = builder.auto_resize();
1337 }
1338 if let Some(proxy_url) = proxy_url.0 {
1339 builder = builder.proxy_url(proxy_url.into());
1340 }
1341 builder = cfg_impl!(
1342 |any(not(target_os = "macos"), feature = "tauri-macos-private-api")| -> _ {
1343 if let Some(transparent) = transparent.0 {
1344 builder = builder.transparent(transparent);
1345 }
1346 Ok(builder)
1347 }
1348 )?;
1349 if let Some(zoom_hotkeys_enabled) = zoom_hotkeys_enabled.0 {
1350 builder = builder.zoom_hotkeys_enabled(zoom_hotkeys_enabled);
1351 }
1352 if let Some(browser_extensions_enabled) = browser_extensions_enabled.0 {
1353 builder = builder.browser_extensions_enabled(browser_extensions_enabled);
1354 }
1355 if let Some(extensions_path) = extensions_path.0 {
1356 builder = builder.extensions_path(extensions_path);
1357 }
1358 if let Some(use_https_scheme) = use_https_scheme.0 {
1359 builder = builder.use_https_scheme(use_https_scheme);
1360 }
1361 if let Some(devtools) = devtools.0 {
1362 builder = builder.devtools(devtools);
1363 }
1364 if let Some(background_color) = background_color.0 {
1365 builder = builder.background_color(background_color.0);
1366 }
1367 if let Some(true) = disable_javascript.0 {
1368 builder = builder.disable_javascript();
1369 }
1370
1371 Ok(builder)
1372 }
1373}
1374
1375#[pyclass(frozen)]
1377#[non_exhaustive]
1378pub struct WebviewWindowBuilder;
1379
1380#[pymethods]
1381impl WebviewWindowBuilder {
1382 #[staticmethod]
1383 #[pyo3(signature = (manager, label, url, /, **kwargs))]
1384 fn build(
1385 py: Python<'_>,
1386 manager: ImplManager,
1387 label: String,
1388 url: &Bound<'_, WebviewUrl>,
1389 kwargs: Option<&Bound<'_, PyDict>>,
1390 ) -> PyResult<WebviewWindow> {
1391 let url = url.get().to_tauri()?;
1392 let args = WebviewWindowBuilderArgs::from_kwargs(kwargs)?;
1393 manager_method_impl!(py, &manager, move |py, manager| {
1394 let mut builder = webview::WebviewWindowBuilder::new(manager, label, url);
1395 if let Some(args) = args {
1396 builder = args.apply_to_builder(py, builder)?;
1397 }
1398
1399 let webview_window = unsafe {
1400 py.allow_threads_unsend(builder, |builder| builder.build())
1402 }
1403 .map_err(TauriError::from)?;
1404
1405 PyResult::Ok(WebviewWindow::new(webview_window))
1406 })?
1407 }
1408
1409 #[staticmethod]
1410 #[pyo3(signature = (manager, config, /, **kwargs))]
1411 fn from_config(
1412 py: Python<'_>,
1413 manager: ImplManager,
1414 config: WindowConfigFrom,
1415 kwargs: Option<&Bound<'_, PyDict>>,
1416 ) -> PyResult<WebviewWindow> {
1417 let config = config.into_inner();
1418 let args = WebviewWindowBuilderArgs::from_kwargs(kwargs)?;
1419 manager_method_impl!(py, &manager, move |py, manager| {
1420 let mut builder = webview::WebviewWindowBuilder::from_config(manager, &config)
1421 .map_err(TauriError::from)?;
1422 if let Some(args) = args {
1423 builder = args.apply_to_builder(py, builder)?;
1424 }
1425
1426 let webview_window = unsafe {
1427 py.allow_threads_unsend(builder, |builder| builder.build())
1429 }
1430 .map_err(TauriError::from)?;
1431
1432 PyResult::Ok(WebviewWindow::new(webview_window))
1433 })?
1434 }
1435}