tauri_runtime/webview.rs
1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! A layer between raw [`Runtime`] webviews and Tauri.
6//!
7#[cfg(not(any(target_os = "android", target_os = "ios")))]
8use crate::window::WindowId;
9use crate::{window::is_label_valid, Rect, Runtime, UserEvent};
10
11use http::Request;
12use tauri_utils::config::{
13 BackgroundThrottlingPolicy, Color, ScrollBarStyle as ConfigScrollBarStyle, WebviewUrl,
14 WindowConfig, WindowEffectsConfig,
15};
16use url::Url;
17
18use std::{
19 borrow::Cow,
20 collections::HashMap,
21 hash::{Hash, Hasher},
22 path::PathBuf,
23 sync::Arc,
24};
25
26type UriSchemeProtocolHandler = dyn Fn(&str, http::Request<Vec<u8>>, Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>)
27 + Send
28 + Sync
29 + 'static;
30
31type WebResourceRequestHandler =
32 dyn Fn(http::Request<Vec<u8>>, &mut http::Response<Cow<'static, [u8]>>) + Send + Sync;
33
34type NavigationHandler = dyn Fn(&Url) -> bool + Send;
35
36type NewWindowHandler = dyn Fn(Url, NewWindowFeatures) -> NewWindowResponse + Send;
37
38type OnPageLoadHandler = dyn Fn(Url, PageLoadEvent) + Send;
39
40type DocumentTitleChangedHandler = dyn Fn(String) + Send + 'static;
41
42type DownloadHandler = dyn Fn(DownloadEvent) -> bool + Send + Sync;
43
44#[cfg(any(target_os = "macos", target_os = "ios"))]
45type OnWebContentProcessTerminateHandler = dyn Fn() + Send;
46
47#[cfg(target_os = "ios")]
48type InputAccessoryViewBuilderFn = dyn Fn(&objc2_ui_kit::UIView) -> Option<objc2::rc::Retained<objc2_ui_kit::UIView>>
49 + Send
50 + Sync
51 + 'static;
52
53/// Download event.
54pub enum DownloadEvent<'a> {
55 /// Download requested.
56 Requested {
57 /// The url being downloaded.
58 url: Url,
59 /// Represents where the file will be downloaded to.
60 /// Can be used to set the download location by assigning a new path to it.
61 /// The assigned path _must_ be absolute.
62 destination: &'a mut PathBuf,
63 },
64 /// Download finished.
65 Finished {
66 /// The URL of the original download request.
67 url: Url,
68 /// Potentially representing the filesystem path the file was downloaded to.
69 path: Option<PathBuf>,
70 /// Indicates if the download succeeded or not.
71 success: bool,
72 },
73}
74
75#[cfg(target_os = "android")]
76pub struct CreationContext<'a, 'b> {
77 pub env: &'a mut jni::JNIEnv<'b>,
78 pub activity: &'a jni::objects::JObject<'b>,
79 pub webview: &'a jni::objects::JObject<'b>,
80}
81
82/// Kind of event for the page load handler.
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum PageLoadEvent {
85 /// Page started to load.
86 Started,
87 /// Page finished loading.
88 Finished,
89}
90
91/// Information about the webview that initiated a new window request.
92#[derive(Debug)]
93pub struct NewWindowOpener {
94 /// The instance of the webview that initiated the new window request.
95 ///
96 /// This must be set as the related view of the new webview. See [`WebviewAttributes::related_view`].
97 #[cfg(any(
98 target_os = "linux",
99 target_os = "dragonfly",
100 target_os = "freebsd",
101 target_os = "netbsd",
102 target_os = "openbsd",
103 ))]
104 pub webview: webkit2gtk::WebView,
105 /// The instance of the webview that initiated the new window request.
106 ///
107 /// The target webview environment **MUST** match the environment of the opener webview. See [`WebviewAttributes::with_environment`].
108 #[cfg(windows)]
109 pub webview: webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2,
110 #[cfg(windows)]
111 pub environment: webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Environment,
112 /// The instance of the webview that initiated the new window request.
113 #[cfg(target_os = "macos")]
114 pub webview: objc2::rc::Retained<objc2_web_kit::WKWebView>,
115 /// Configuration of the target webview.
116 ///
117 /// This **MUST** be used when creating the target webview. See [`WebviewAttributes::webview_configuration`].
118 #[cfg(target_os = "macos")]
119 pub target_configuration: objc2::rc::Retained<objc2_web_kit::WKWebViewConfiguration>,
120}
121
122/// Window features of a window requested to open.
123#[derive(Debug)]
124pub struct NewWindowFeatures {
125 pub(crate) size: Option<crate::dpi::LogicalSize<f64>>,
126 pub(crate) position: Option<crate::dpi::LogicalPosition<f64>>,
127 pub(crate) opener: NewWindowOpener,
128}
129
130impl NewWindowFeatures {
131 pub fn new(
132 size: Option<crate::dpi::LogicalSize<f64>>,
133 position: Option<crate::dpi::LogicalPosition<f64>>,
134 opener: NewWindowOpener,
135 ) -> Self {
136 Self {
137 size,
138 position,
139 opener,
140 }
141 }
142
143 /// Specifies the size of the content area
144 /// as defined by the user's operating system where the new window will be generated.
145 pub fn size(&self) -> Option<crate::dpi::LogicalSize<f64>> {
146 self.size
147 }
148
149 /// Specifies the position of the window relative to the work area
150 /// as defined by the user's operating system where the new window will be generated.
151 pub fn position(&self) -> Option<crate::dpi::LogicalPosition<f64>> {
152 self.position
153 }
154
155 /// Returns information about the webview that initiated a new window request.
156 pub fn opener(&self) -> &NewWindowOpener {
157 &self.opener
158 }
159}
160
161/// Response for the new window request handler.
162pub enum NewWindowResponse {
163 /// Allow the window to be opened with the default implementation.
164 Allow,
165 /// Allow the window to be opened, with the given window.
166 ///
167 /// ## Platform-specific:
168 ///
169 /// **Linux**: The webview must be related to the caller webview. See [`WebviewAttributes::related_view`].
170 /// **Windows**: The webview must use the same environment as the caller webview. See [`WebviewAttributes::with_environment`].
171 #[cfg(not(any(target_os = "android", target_os = "ios")))]
172 Create { window_id: WindowId },
173 /// Deny the window from being opened.
174 Deny,
175}
176
177/// The scrollbar style to use in the webview.
178///
179/// ## Platform-specific
180///
181/// - **Windows**: This option must be given the same value for all webviews that target the same data directory.
182#[non_exhaustive]
183#[derive(Debug, Clone, Copy, Default)]
184pub enum ScrollBarStyle {
185 #[default]
186 /// The default scrollbar style for the webview.
187 Default,
188
189 #[cfg(windows)]
190 /// Fluent UI style overlay scrollbars. **Windows Only**
191 ///
192 /// Requires WebView2 Runtime version 125.0.2535.41 or higher, does nothing on older versions,
193 /// see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/?tabs=dotnetcsharp#10253541>
194 FluentOverlay,
195}
196
197/// A webview that has yet to be built.
198pub struct PendingWebview<T: UserEvent, R: Runtime<T>> {
199 /// The label that the webview will be named.
200 pub label: String,
201
202 /// The [`WebviewAttributes`] that the webview will be created with.
203 pub webview_attributes: WebviewAttributes,
204
205 /// Custom protocols to register on the webview
206 pub uri_scheme_protocols: HashMap<String, Box<UriSchemeProtocolHandler>>,
207
208 /// How to handle IPC calls on the webview.
209 pub ipc_handler: Option<WebviewIpcHandler<T, R>>,
210
211 /// A handler to decide if incoming url is allowed to navigate.
212 pub navigation_handler: Option<Box<NavigationHandler>>,
213
214 pub new_window_handler: Option<Box<NewWindowHandler>>,
215
216 pub document_title_changed_handler: Option<Box<DocumentTitleChangedHandler>>,
217
218 /// The resolved URL to load on the webview.
219 pub url: String,
220
221 #[cfg(target_os = "android")]
222 #[allow(clippy::type_complexity)]
223 pub on_webview_created:
224 Option<Box<dyn Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send + Sync>>,
225
226 pub web_resource_request_handler: Option<Box<WebResourceRequestHandler>>,
227
228 pub on_page_load_handler: Option<Box<OnPageLoadHandler>>,
229
230 pub download_handler: Option<Arc<DownloadHandler>>,
231
232 #[cfg(any(target_os = "macos", target_os = "ios"))]
233 pub on_web_content_process_terminate_handler: Option<Box<OnWebContentProcessTerminateHandler>>,
234}
235
236impl<T: UserEvent, R: Runtime<T>> PendingWebview<T, R> {
237 /// Create a new [`PendingWebview`] with a label from the given [`WebviewAttributes`].
238 pub fn new(
239 webview_attributes: WebviewAttributes,
240 label: impl Into<String>,
241 ) -> crate::Result<Self> {
242 let label = label.into();
243 if !is_label_valid(&label) {
244 Err(crate::Error::InvalidWindowLabel)
245 } else {
246 Ok(Self {
247 webview_attributes,
248 uri_scheme_protocols: Default::default(),
249 label,
250 ipc_handler: None,
251 navigation_handler: None,
252 new_window_handler: None,
253 document_title_changed_handler: None,
254 url: "tauri://localhost".to_string(),
255 #[cfg(target_os = "android")]
256 on_webview_created: None,
257 web_resource_request_handler: None,
258 on_page_load_handler: None,
259 download_handler: None,
260 #[cfg(any(target_os = "macos", target_os = "ios"))]
261 on_web_content_process_terminate_handler: None,
262 })
263 }
264 }
265
266 pub fn register_uri_scheme_protocol<
267 N: Into<String>,
268 H: Fn(&str, http::Request<Vec<u8>>, Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>)
269 + Send
270 + Sync
271 + 'static,
272 >(
273 &mut self,
274 uri_scheme: N,
275 protocol_handler: H,
276 ) {
277 let uri_scheme = uri_scheme.into();
278 self
279 .uri_scheme_protocols
280 .insert(uri_scheme, Box::new(protocol_handler));
281 }
282
283 #[cfg(target_os = "android")]
284 pub fn on_webview_created<
285 F: Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send + Sync + 'static,
286 >(
287 mut self,
288 f: F,
289 ) -> Self {
290 self.on_webview_created.replace(Box::new(f));
291 self
292 }
293}
294
295/// A webview that is not yet managed by Tauri.
296#[derive(Debug)]
297pub struct DetachedWebview<T: UserEvent, R: Runtime<T>> {
298 /// Name of the window
299 pub label: String,
300
301 /// The [`crate::WebviewDispatch`] associated with the window.
302 pub dispatcher: R::WebviewDispatcher,
303}
304
305impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWebview<T, R> {
306 fn clone(&self) -> Self {
307 Self {
308 label: self.label.clone(),
309 dispatcher: self.dispatcher.clone(),
310 }
311 }
312}
313
314impl<T: UserEvent, R: Runtime<T>> Hash for DetachedWebview<T, R> {
315 /// Only use the [`DetachedWebview`]'s label to represent its hash.
316 fn hash<H: Hasher>(&self, state: &mut H) {
317 self.label.hash(state)
318 }
319}
320
321impl<T: UserEvent, R: Runtime<T>> Eq for DetachedWebview<T, R> {}
322impl<T: UserEvent, R: Runtime<T>> PartialEq for DetachedWebview<T, R> {
323 /// Only use the [`DetachedWebview`]'s label to compare equality.
324 fn eq(&self, other: &Self) -> bool {
325 self.label.eq(&other.label)
326 }
327}
328
329/// The attributes used to create an webview.
330#[derive(Debug)]
331pub struct WebviewAttributes {
332 pub url: WebviewUrl,
333 pub user_agent: Option<String>,
334 /// A list of initialization javascript scripts to run when loading new pages.
335 /// When webview load a new page, this initialization code will be executed.
336 /// It is guaranteed that code is executed before `window.onload`.
337 ///
338 /// ## Platform-specific
339 ///
340 /// - **Windows:** scripts are always added to subframes.
341 /// - **Android:** When [addDocumentStartJavaScript] is not supported,
342 /// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
343 /// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
344 ///
345 /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
346 /// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
347 pub initialization_scripts: Vec<InitializationScript>,
348 pub data_directory: Option<PathBuf>,
349 pub drag_drop_handler_enabled: bool,
350 pub clipboard: bool,
351 pub accept_first_mouse: bool,
352 pub additional_browser_args: Option<String>,
353 pub window_effects: Option<WindowEffectsConfig>,
354 pub incognito: bool,
355 pub transparent: bool,
356 pub focus: bool,
357 pub bounds: Option<Rect>,
358 pub auto_resize: bool,
359 pub proxy_url: Option<Url>,
360 pub zoom_hotkeys_enabled: bool,
361 pub browser_extensions_enabled: bool,
362 pub extensions_path: Option<PathBuf>,
363 pub data_store_identifier: Option<[u8; 16]>,
364 pub use_https_scheme: bool,
365 pub devtools: Option<bool>,
366 pub background_color: Option<Color>,
367 pub traffic_light_position: Option<dpi::Position>,
368 pub background_throttling: Option<BackgroundThrottlingPolicy>,
369 pub javascript_disabled: bool,
370 /// on macOS and iOS there is a link preview on long pressing links, this is enabled by default.
371 /// see https://docs.rs/objc2-web-kit/latest/objc2_web_kit/struct.WKWebView.html#method.allowsLinkPreview
372 pub allow_link_preview: bool,
373 pub scroll_bar_style: ScrollBarStyle,
374 /// Controls the WebView's browser-level general autofill behavior.
375 ///
376 /// **This option does not disable password or credit card autofill.**
377 ///
378 /// When set to `false`, the WebView will not automatically populate
379 /// general form fields using previously stored data such as addresses
380 /// or contact information.
381 ///
382 /// If not specified, this is `true` by default.
383 ///
384 /// ## Platform-specific
385 ///
386 /// - **Windows**: Supported. WebView2's autofill feature (called
387 /// "Suggestions") may not honor `autocomplete="off"` on input
388 /// elements in some cases.
389 /// - **Linux / Android / iOS / macOS**: Unsupported and performs no
390 /// operation.
391 pub general_autofill_enabled: bool,
392 /// Allows overriding the keyboard accessory view on iOS.
393 /// Returning `None` effectively removes the view.
394 ///
395 /// The closure parameter is the webview instance.
396 ///
397 /// The accessory view is the view that appears above the keyboard when a text input element is focused.
398 /// It usually displays a view with "Done", "Next" buttons.
399 ///
400 /// # Stability
401 ///
402 /// This relies on [`objc2_ui_kit`] which does not provide a stable API yet, so it can receive breaking changes in minor releases.
403 #[cfg(target_os = "ios")]
404 pub input_accessory_view_builder: Option<InputAccessoryViewBuilder>,
405
406 /// Set the environment for the webview.
407 /// Useful if you need to share the same environment, for instance when using the [`PendingWebview::new_window_handler`].
408 #[cfg(windows)]
409 pub environment: Option<webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Environment>,
410
411 /// Creates a new webview sharing the same web process with the provided webview.
412 /// Useful if you need to link a webview to another, for instance when using the [`PendingWebview::new_window_handler`].
413 #[cfg(any(
414 target_os = "linux",
415 target_os = "dragonfly",
416 target_os = "freebsd",
417 target_os = "netbsd",
418 target_os = "openbsd",
419 ))]
420 pub related_view: Option<webkit2gtk::WebView>,
421
422 #[cfg(target_os = "macos")]
423 pub webview_configuration: Option<objc2::rc::Retained<objc2_web_kit::WKWebViewConfiguration>>,
424}
425
426unsafe impl Send for WebviewAttributes {}
427unsafe impl Sync for WebviewAttributes {}
428
429#[cfg(target_os = "ios")]
430#[non_exhaustive]
431pub struct InputAccessoryViewBuilder(pub Box<InputAccessoryViewBuilderFn>);
432
433#[cfg(target_os = "ios")]
434impl std::fmt::Debug for InputAccessoryViewBuilder {
435 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
436 f.debug_struct("InputAccessoryViewBuilder").finish()
437 }
438}
439
440#[cfg(target_os = "ios")]
441impl InputAccessoryViewBuilder {
442 pub fn new(builder: Box<InputAccessoryViewBuilderFn>) -> Self {
443 Self(builder)
444 }
445}
446
447impl From<&WindowConfig> for WebviewAttributes {
448 fn from(config: &WindowConfig) -> Self {
449 let mut builder = Self::new(config.url.clone())
450 .incognito(config.incognito)
451 .focused(config.focus)
452 .zoom_hotkeys_enabled(config.zoom_hotkeys_enabled)
453 .use_https_scheme(config.use_https_scheme)
454 .browser_extensions_enabled(config.browser_extensions_enabled)
455 .background_throttling(config.background_throttling.clone())
456 .devtools(config.devtools)
457 .scroll_bar_style(match config.scroll_bar_style {
458 ConfigScrollBarStyle::Default => ScrollBarStyle::Default,
459 #[cfg(windows)]
460 ConfigScrollBarStyle::FluentOverlay => ScrollBarStyle::FluentOverlay,
461 _ => ScrollBarStyle::Default,
462 })
463 .general_autofill_enabled(config.general_autofill_enabled);
464
465 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
466 {
467 builder = builder.transparent(config.transparent);
468 }
469 #[cfg(target_os = "macos")]
470 {
471 if let Some(position) = &config.traffic_light_position {
472 builder =
473 builder.traffic_light_position(dpi::LogicalPosition::new(position.x, position.y).into());
474 }
475 }
476 builder = builder.accept_first_mouse(config.accept_first_mouse);
477 if !config.drag_drop_enabled {
478 builder = builder.disable_drag_drop_handler();
479 }
480 if let Some(user_agent) = &config.user_agent {
481 builder = builder.user_agent(user_agent);
482 }
483 if let Some(additional_browser_args) = &config.additional_browser_args {
484 builder = builder.additional_browser_args(additional_browser_args);
485 }
486 if let Some(effects) = &config.window_effects {
487 builder = builder.window_effects(effects.clone());
488 }
489 if let Some(url) = &config.proxy_url {
490 builder = builder.proxy_url(url.to_owned());
491 }
492 if let Some(color) = config.background_color {
493 builder = builder.background_color(color);
494 }
495 builder.javascript_disabled = config.javascript_disabled;
496 builder.allow_link_preview = config.allow_link_preview;
497 #[cfg(target_os = "ios")]
498 if config.disable_input_accessory_view {
499 builder
500 .input_accessory_view_builder
501 .replace(InputAccessoryViewBuilder::new(Box::new(|_webview| None)));
502 }
503 builder
504 }
505}
506
507impl WebviewAttributes {
508 /// Initializes the default attributes for a webview.
509 pub fn new(url: WebviewUrl) -> Self {
510 Self {
511 url,
512 user_agent: None,
513 initialization_scripts: Vec::new(),
514 data_directory: None,
515 drag_drop_handler_enabled: true,
516 clipboard: false,
517 accept_first_mouse: false,
518 additional_browser_args: None,
519 window_effects: None,
520 incognito: false,
521 transparent: false,
522 focus: true,
523 bounds: None,
524 auto_resize: false,
525 proxy_url: None,
526 zoom_hotkeys_enabled: false,
527 browser_extensions_enabled: false,
528 data_store_identifier: None,
529 extensions_path: None,
530 use_https_scheme: false,
531 devtools: None,
532 background_color: None,
533 traffic_light_position: None,
534 background_throttling: None,
535 javascript_disabled: false,
536 allow_link_preview: true,
537 scroll_bar_style: ScrollBarStyle::Default,
538 general_autofill_enabled: true,
539 #[cfg(target_os = "ios")]
540 input_accessory_view_builder: None,
541 #[cfg(windows)]
542 environment: None,
543 #[cfg(any(
544 target_os = "linux",
545 target_os = "dragonfly",
546 target_os = "freebsd",
547 target_os = "netbsd",
548 target_os = "openbsd",
549 ))]
550 related_view: None,
551 #[cfg(target_os = "macos")]
552 webview_configuration: None,
553 }
554 }
555
556 /// Sets the user agent
557 #[must_use]
558 pub fn user_agent(mut self, user_agent: &str) -> Self {
559 self.user_agent = Some(user_agent.to_string());
560 self
561 }
562
563 /// Adds an init script for the main frame.
564 ///
565 /// When webview load a new page, this initialization code will be executed.
566 /// It is guaranteed that code is executed before `window.onload`.
567 ///
568 /// This is executed only on the main frame.
569 /// If you only want to run it in all frames, use [`Self::initialization_script_on_all_frames`] instead.
570 ///
571 /// ## Platform-specific
572 ///
573 /// - **Windows:** scripts are always added to subframes.
574 /// - **Android:** When [addDocumentStartJavaScript] is not supported,
575 /// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
576 /// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
577 ///
578 /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
579 /// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
580 #[must_use]
581 pub fn initialization_script(mut self, script: impl Into<String>) -> Self {
582 self.initialization_scripts.push(InitializationScript {
583 script: script.into(),
584 for_main_frame_only: true,
585 });
586 self
587 }
588
589 /// Adds an init script for all frames.
590 ///
591 /// When webview load a new page, this initialization code will be executed.
592 /// It is guaranteed that code is executed before `window.onload`.
593 ///
594 /// This is executed on all frames, main frame and also sub frames.
595 /// If you only want to run it in the main frame, use [`Self::initialization_script`] instead.
596 ///
597 /// ## Platform-specific
598 ///
599 /// - **Windows:** scripts are always added to subframes.
600 /// - **Android:** When [addDocumentStartJavaScript] is not supported,
601 /// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
602 /// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
603 ///
604 /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
605 /// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
606 #[must_use]
607 pub fn initialization_script_on_all_frames(mut self, script: impl Into<String>) -> Self {
608 self.initialization_scripts.push(InitializationScript {
609 script: script.into(),
610 for_main_frame_only: false,
611 });
612 self
613 }
614
615 /// Data directory for the webview.
616 #[must_use]
617 pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
618 self.data_directory.replace(data_directory);
619 self
620 }
621
622 /// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows.
623 #[must_use]
624 pub fn disable_drag_drop_handler(mut self) -> Self {
625 self.drag_drop_handler_enabled = false;
626 self
627 }
628
629 /// Enables clipboard access for the page rendered on **Linux** and **Windows**.
630 ///
631 /// **macOS** doesn't provide such method and is always enabled by default,
632 /// but you still need to add menu item accelerators to use shortcuts.
633 #[must_use]
634 pub fn enable_clipboard_access(mut self) -> Self {
635 self.clipboard = true;
636 self
637 }
638
639 /// Sets whether clicking an inactive window also clicks through to the webview.
640 #[must_use]
641 pub fn accept_first_mouse(mut self, accept: bool) -> Self {
642 self.accept_first_mouse = accept;
643 self
644 }
645
646 /// Sets additional browser arguments. **Windows Only**
647 #[must_use]
648 pub fn additional_browser_args(mut self, additional_args: &str) -> Self {
649 self.additional_browser_args = Some(additional_args.to_string());
650 self
651 }
652
653 /// Sets window effects
654 #[must_use]
655 pub fn window_effects(mut self, effects: WindowEffectsConfig) -> Self {
656 self.window_effects = Some(effects);
657 self
658 }
659
660 /// Enable or disable incognito mode for the WebView.
661 #[must_use]
662 pub fn incognito(mut self, incognito: bool) -> Self {
663 self.incognito = incognito;
664 self
665 }
666
667 /// Enable or disable transparency for the WebView.
668 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
669 #[must_use]
670 pub fn transparent(mut self, transparent: bool) -> Self {
671 self.transparent = transparent;
672 self
673 }
674
675 /// Whether the webview should be focused or not.
676 #[must_use]
677 pub fn focused(mut self, focus: bool) -> Self {
678 self.focus = focus;
679 self
680 }
681
682 /// Sets the webview to automatically grow and shrink its size and position when the parent window resizes.
683 #[must_use]
684 pub fn auto_resize(mut self) -> Self {
685 self.auto_resize = true;
686 self
687 }
688
689 /// Enable proxy for the WebView
690 #[must_use]
691 pub fn proxy_url(mut self, url: Url) -> Self {
692 self.proxy_url = Some(url);
693 self
694 }
695
696 /// Whether page zooming by hotkeys is enabled
697 ///
698 /// ## Platform-specific:
699 ///
700 /// - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting.
701 /// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`,
702 /// 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission
703 ///
704 /// - **Android / iOS**: Unsupported.
705 #[must_use]
706 pub fn zoom_hotkeys_enabled(mut self, enabled: bool) -> Self {
707 self.zoom_hotkeys_enabled = enabled;
708 self
709 }
710
711 /// Whether browser extensions can be installed for the webview process
712 ///
713 /// ## Platform-specific:
714 ///
715 /// - **Windows**: Enables the WebView2 environment's [`AreBrowserExtensionsEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2environmentoptions?view=webview2-winrt-1.0.2739.15#arebrowserextensionsenabled)
716 /// - **MacOS / Linux / iOS / Android** - Unsupported.
717 #[must_use]
718 pub fn browser_extensions_enabled(mut self, enabled: bool) -> Self {
719 self.browser_extensions_enabled = enabled;
720 self
721 }
722
723 /// Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.
724 ///
725 /// ## Note
726 ///
727 /// Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
728 ///
729 /// ## Warning
730 ///
731 /// Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data.
732 #[must_use]
733 pub fn use_https_scheme(mut self, enabled: bool) -> Self {
734 self.use_https_scheme = enabled;
735 self
736 }
737
738 /// Whether web inspector, which is usually called browser devtools, is enabled or not. Enabled by default.
739 ///
740 /// This API works in **debug** builds, but requires `devtools` feature flag to enable it in **release** builds.
741 ///
742 /// ## Platform-specific
743 ///
744 /// - macOS: This will call private functions on **macOS**.
745 /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.
746 /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.
747 #[must_use]
748 pub fn devtools(mut self, enabled: Option<bool>) -> Self {
749 self.devtools = enabled;
750 self
751 }
752
753 /// Set the window and webview background color.
754 /// ## Platform-specific:
755 ///
756 /// - **Windows**: On Windows 7, alpha channel is ignored for the webview layer.
757 /// - **Windows**: On Windows 8 and newer, if alpha channel is not `0`, it will be ignored.
758 #[must_use]
759 pub fn background_color(mut self, color: Color) -> Self {
760 self.background_color = Some(color);
761 self
762 }
763
764 /// Change the position of the window controls. Available on macOS only.
765 ///
766 /// Requires titleBarStyle: Overlay and decorations: true.
767 ///
768 /// ## Platform-specific
769 ///
770 /// - **Linux / Windows / iOS / Android:** Unsupported.
771 #[must_use]
772 pub fn traffic_light_position(mut self, position: dpi::Position) -> Self {
773 self.traffic_light_position = Some(position);
774 self
775 }
776
777 /// Whether to show a link preview when long pressing on links. Available on macOS and iOS only.
778 ///
779 /// Default is true.
780 ///
781 /// See https://docs.rs/objc2-web-kit/latest/objc2_web_kit/struct.WKWebView.html#method.allowsLinkPreview
782 ///
783 /// ## Platform-specific
784 ///
785 /// - **Linux / Windows / Android:** Unsupported.
786 #[must_use]
787 pub fn allow_link_preview(mut self, allow_link_preview: bool) -> Self {
788 self.allow_link_preview = allow_link_preview;
789 self
790 }
791
792 /// Change the default background throttling behavior.
793 ///
794 /// By default, browsers use a suspend policy that will throttle timers and even unload
795 /// the whole tab (view) to free resources after roughly 5 minutes when a view became
796 /// minimized or hidden. This will pause all tasks until the documents visibility state
797 /// changes back from hidden to visible by bringing the view back to the foreground.
798 ///
799 /// ## Platform-specific
800 ///
801 /// - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.
802 /// - **iOS**: Supported since version 17.0+.
803 /// - **macOS**: Supported since version 14.0+.
804 ///
805 /// see <https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578>
806 #[must_use]
807 pub fn background_throttling(mut self, policy: Option<BackgroundThrottlingPolicy>) -> Self {
808 self.background_throttling = policy;
809 self
810 }
811
812 /// Specifies the native scrollbar style to use with the webview.
813 /// CSS styles that modify the scrollbar are applied on top of the native appearance configured here.
814 ///
815 /// Defaults to [`ScrollBarStyle::Default`], which is the browser default.
816 ///
817 /// ## Platform-specific
818 ///
819 /// - **Windows**:
820 /// - [`ScrollBarStyle::FluentOverlay`] requires WebView2 Runtime version 125.0.2535.41 or higher,
821 /// and does nothing on older versions.
822 /// - This option must be given the same value for all webviews that target the same data directory. Use
823 /// [`WebviewAttributes::data_directory`] to change data directories if needed.
824 /// - **Linux / Android / iOS / macOS**: Unsupported. Only supports `Default` and performs no operation.
825 #[must_use]
826 pub fn scroll_bar_style(mut self, style: ScrollBarStyle) -> Self {
827 self.scroll_bar_style = style;
828 self
829 }
830
831 /// Controls the WebView's browser-level general autofill behavior.
832 ///
833 /// **This option does not disable password or credit card autofill.**
834 ///
835 /// When set to `false`, the WebView will not automatically populate
836 /// general form fields using previously stored data such as addresses
837 /// or contact information.
838 ///
839 /// By default, this is `true`.
840 ///
841 /// ## Platform-specific
842 ///
843 /// - **Windows**: Supported. WebView2's autofill feature (called
844 /// "Suggestions") may not honor `autocomplete="off"` on input
845 /// elements in some cases.
846 /// - **Linux / Android / iOS / macOS**: Unsupported and performs no
847 /// operation.
848 #[must_use]
849 pub fn general_autofill_enabled(mut self, enabled: bool) -> Self {
850 self.general_autofill_enabled = enabled;
851 self
852 }
853}
854
855/// IPC handler.
856pub type WebviewIpcHandler<T, R> = Box<dyn Fn(DetachedWebview<T, R>, Request<String>) + Send>;
857
858/// An initialization script
859#[derive(Debug, Clone)]
860pub struct InitializationScript {
861 /// The script to run
862 pub script: String,
863 /// Whether the script should be injected to main frame only
864 pub for_main_frame_only: bool,
865}