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