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