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