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