Skip to main content

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}