Skip to main content

wry/
lib.rs

1// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! <p align="center"><img height="100" src="https://raw.githubusercontent.com/tauri-apps/wry/refs/heads/dev/.github/splash.png" alt="WRY Webview Rendering library" /></p>
6//!
7//! [![](https://img.shields.io/crates/v/wry?style=flat-square)](https://crates.io/crates/wry) [![](https://img.shields.io/docsrs/wry?style=flat-square)](https://docs.rs/wry/)
8//! [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri)
9//! [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S)
10//! [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app)
11//! [![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation)
12//! [![support](https://img.shields.io/badge/sponsor-Open%20Collective-blue.svg)](https://opencollective.com/tauri)
13//!
14//! Wry is a cross-platform WebView rendering library.
15//!
16//! The webview requires a running event loop and a window type that implements [`HasWindowHandle`],
17//! or a gtk container widget if you need to support X11 and Wayland.
18//! You can use a windowing library like [`tao`] or [`winit`].
19//!
20//! ## Examples
21//!
22//! This example leverages the [`HasWindowHandle`] and supports Windows, macOS, iOS, Android and Linux (X11 Only).
23//! See the following example using [`winit`]:
24//!
25//! ```no_run
26//! # use wry::{WebViewBuilder, raw_window_handle};
27//! # use winit::{application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowId}};
28//! #[derive(Default)]
29//! struct App {
30//!   window: Option<Window>,
31//!   webview: Option<wry::WebView>,
32//! }
33//!
34//! impl ApplicationHandler for App {
35//!   fn resumed(&mut self, event_loop: &ActiveEventLoop) {
36//!     let window = event_loop.create_window(Window::default_attributes()).unwrap();
37//!     let webview = WebViewBuilder::new()
38//!       .with_url("https://tauri.app")
39//!       .build(&window)
40//!       .unwrap();
41//!
42//!     self.window = Some(window);
43//!     self.webview = Some(webview);
44//!   }
45//!
46//!   fn window_event(&mut self, _event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {}
47//! }
48//!
49//! let event_loop = EventLoop::new().unwrap();
50//! let mut app = App::default();
51//! event_loop.run_app(&mut app).unwrap();
52//! ```
53//!
54//! If you also want to support Wayland too, then we recommend you use [`WebViewBuilderExtUnix::build_gtk`] on Linux.
55//! See the following example using [`tao`]:
56//!
57//! ```no_run
58//! # use wry::WebViewBuilder;
59//! # use tao::{window::WindowBuilder, event_loop::EventLoop};
60//! # #[cfg(target_os = "linux")]
61//! # use tao::platform::unix::WindowExtUnix;
62//! # #[cfg(target_os = "linux")]
63//! # use wry::WebViewBuilderExtUnix;
64//! let event_loop = EventLoop::new();
65//! let window = WindowBuilder::new().build(&event_loop).unwrap();
66//!
67//! let builder = WebViewBuilder::new().with_url("https://tauri.app");
68//!
69//! #[cfg(not(target_os = "linux"))]
70//! let webview = builder.build(&window).unwrap();
71//! #[cfg(target_os = "linux")]
72//! let webview = builder.build_gtk(window.gtk_window()).unwrap();
73//! ```
74//!
75//! ## Child webviews
76//!
77//! You can use [`WebViewBuilder::build_as_child`] to create the webview as a child inside another window. This is supported on
78//! macOS, Windows and Linux (X11 Only).
79//!
80//! ```no_run
81//! # use wry::{WebViewBuilder, raw_window_handle, Rect, dpi::*};
82//! # use winit::{application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowId}};
83//! #[derive(Default)]
84//! struct App {
85//!   window: Option<Window>,
86//!   webview: Option<wry::WebView>,
87//! }
88//!
89//! impl ApplicationHandler for App {
90//!   fn resumed(&mut self, event_loop: &ActiveEventLoop) {
91//!     let window = event_loop.create_window(Window::default_attributes()).unwrap();
92//!     let webview = WebViewBuilder::new()
93//!       .with_url("https://tauri.app")
94//!       .with_bounds(Rect {
95//!         position: LogicalPosition::new(100, 100).into(),
96//!         size: LogicalSize::new(200, 200).into(),
97//!       })
98//!       .build_as_child(&window)
99//!       .unwrap();
100//!
101//!     self.window = Some(window);
102//!     self.webview = Some(webview);
103//!   }
104//!
105//!   fn window_event(&mut self, _event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {}
106//! }
107//!
108//! let event_loop = EventLoop::new().unwrap();
109//! let mut app = App::default();
110//! event_loop.run_app(&mut app).unwrap();
111//! ```
112//!
113//! If you want to support X11 and Wayland at the same time, we recommend using
114//! [`WebViewExtUnix::new_gtk`] or [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`].
115//!
116//! ```no_run
117//! # use wry::{WebViewBuilder, raw_window_handle, Rect, dpi::*};
118//! # use tao::{window::WindowBuilder, event_loop::EventLoop};
119//! # #[cfg(target_os = "linux")]
120//! # use wry::WebViewBuilderExtUnix;
121//! # #[cfg(target_os = "linux")]
122//! # use tao::platform::unix::WindowExtUnix;
123//! let event_loop = EventLoop::new();
124//! let window = WindowBuilder::new().build(&event_loop).unwrap();
125//!
126//! let builder = WebViewBuilder::new()
127//!   .with_url("https://tauri.app")
128//!   .with_bounds(Rect {
129//!     position: LogicalPosition::new(100, 100).into(),
130//!     size: LogicalSize::new(200, 200).into(),
131//!   });
132//!
133//! #[cfg(not(target_os = "linux"))]
134//! let webview = builder.build_as_child(&window).unwrap();
135//! #[cfg(target_os = "linux")]
136//! let webview = {
137//!   # use gtk::prelude::*;
138//!   let vbox = window.default_vbox().unwrap(); // tao adds a gtk::Box by default
139//!   let fixed = gtk::Fixed::new();
140//!   fixed.show_all();
141//!   vbox.pack_start(&fixed, true, true, 0);
142//!   builder.build_gtk(&fixed).unwrap()
143//! };
144//! ```
145//!
146//! ## Platform Considerations
147//!
148//! Here is the underlying web engine each platform uses, and some dependencies you might need to install.
149//!
150//! ### Linux
151//!
152//! [WebKitGTK](https://webkitgtk.org/) is used to provide webviews on Linux which requires GTK,
153//! so if the windowing library doesn't support GTK (as in [`winit`])
154//! you'll need to call [`gtk::init`] before creating the webview and then call [`gtk::main_iteration_do`] alongside
155//! your windowing library event loop.
156//!
157//! ```no_run
158//! # use wry::{WebView, WebViewBuilder};
159//! # use winit::{application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowId}};
160//! #[derive(Default)]
161//! struct App {
162//!   webview_window: Option<(Window, WebView)>,
163//! }
164//!
165//! impl ApplicationHandler for App {
166//!   fn resumed(&mut self, event_loop: &ActiveEventLoop) {
167//!     let window = event_loop.create_window(Window::default_attributes()).unwrap();
168//!     let webview = WebViewBuilder::new()
169//!       .with_url("https://tauri.app")
170//!       .build(&window)
171//!       .unwrap();
172//!
173//!     self.webview_window = Some((window, webview));
174//!   }
175//!
176//!   fn window_event(&mut self, _event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {}
177//!
178//!   // Advance GTK event loop <!----- IMPORTANT
179//!   fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
180//!     #[cfg(target_os = "linux")]
181//!     while gtk::events_pending() {
182//!       gtk::main_iteration_do(false);
183//!     }
184//!   }
185//! }
186//!
187//! let event_loop = EventLoop::new().unwrap();
188//! let mut app = App::default();
189//! event_loop.run_app(&mut app).unwrap();
190//! ```
191//!
192//! #### Linux Dependencies
193//!
194//! ##### Arch Linux / Manjaro:
195//!
196//! ```bash
197//! sudo pacman -S webkit2gtk-4.1
198//! ```
199//!
200//! ##### Debian / Ubuntu:
201//!
202//! ```bash
203//! sudo apt install libwebkit2gtk-4.1-dev
204//! ```
205//!
206//! ##### Fedora
207//!
208//! ```bash
209//! sudo dnf install gtk3-devel webkit2gtk4.1-devel
210//! ```
211//!
212//! ##### Nix & NixOS
213//!
214//! ```nix
215//! # shell.nix
216//!
217//! let
218//!    # Unstable Channel | Rolling Release
219//!    pkgs = import (fetchTarball("channel:nixpkgs-unstable")) { };
220//!    packages = with pkgs; [
221//!      pkg-config
222//!      webkitgtk_4_1
223//!    ];
224//!  in
225//!  pkgs.mkShell {
226//!    buildInputs = packages;
227//!  }
228//! ```
229//!
230//! ```sh
231//! nix-shell shell.nix
232//! ```
233//!
234//! ##### GUIX
235//!
236//! ```scheme
237//! ;; manifest.scm
238//!
239//! (specifications->manifest
240//!   '("pkg-config"                ; Helper tool used when compiling
241//!     "webkitgtk"                 ; Web content engine fot GTK+
242//!  ))
243//! ```
244//!
245//! ```bash
246//! guix shell -m manifest.scm
247//! ```
248//!
249//! ### macOS
250//!
251//! WebKit is native on macOS so everything should be fine.
252//!
253//! If you are cross-compiling for macOS using [osxcross](https://github.com/tpoechtrager/osxcross) and encounter a runtime panic like `Class with name WKWebViewConfiguration could not be found` it's possible that `WebKit.framework` has not been linked correctly, to fix this set the `RUSTFLAGS` environment variable:
254//!
255//! ```bash
256//! RUSTFLAGS="-l framework=WebKit" cargo build --target=x86_64-apple-darwin --release
257//! ```
258//!
259//! ### Windows
260//!
261//! WebView2 provided by Microsoft Edge Chromium is used. So wry supports Windows 7, 8, 10 and 11.
262//!
263//! ### Android
264//!
265//! In order for `wry` to be able to create webviews on Android, there are a few requirements that your application needs to uphold:
266//!
267//! 1. You need to set a few environment variables that will be used to generate the necessary kotlin
268//!    files that you need to include in your Android application for wry to function properly.
269//!    - `WRY_ANDROID_PACKAGE`: which is the reversed domain name of your android project and the app name in snake_case, for example, `com.wry.example.wry_app`
270//!    - `WRY_ANDROID_LIBRARY`: for example, if your cargo project has a lib name `wry_app`, it will generate `libwry_app.so` so you set this env var to `wry_app`
271//!    - `WRY_ANDROID_KOTLIN_FILES_OUT_DIR`: for example, `path/to/app/src/main/kotlin/com/wry/example`
272//! 2. Your main Android Activity needs to inherit `AppCompatActivity`, preferably it should use the generated `WryActivity` or inherit it.
273//! 3. Your Rust app needs to call `wry::android_setup` function to setup the necessary logic to be able to create webviews later on.
274//! 4. Your Rust app needs to call `wry::android_binding!` macro to setup the JNI functions that will be called by `WryActivity` and various other places.
275//!
276//! It is recommended to use the [`tao`](https://docs.rs/tao/latest/tao/) crate as it provides maximum compatibility with `wry`.
277//!
278//! ```
279//! #[cfg(target_os = "android")]
280//! {
281//!   tao::android_binding!(
282//!       com_example,
283//!       wry_app,
284//!       WryActivity,
285//!       wry::android_setup, // pass the wry::android_setup function to tao which will be invoked when the event loop is created
286//!       _start_app
287//!   );
288//!   wry::android_binding!(com_example, ttt);
289//! }
290//! ```
291//!
292//! If this feels overwhelming, you can just use the preconfigured template from [`cargo-mobile2`](https://github.com/tauri-apps/cargo-mobile2).
293//!
294//! For more information, check out [MOBILE.md](https://github.com/tauri-apps/wry/blob/dev/MOBILE.md).
295//!
296//! ## Feature flags
297//!
298//! Wry uses a set of feature flags to toggle several advanced features.
299//!
300//! - `os-webview` (default): Enables the default WebView framework on the platform. This must be enabled
301//!   for the crate to work. This feature was added in preparation of other ports like cef and servo.
302//! - `x11` (default): Enables x11 support and dependencies on Linux.
303//! - `serde`: Enables `dpi`'s `serde` feature.
304//! - `devtools`: Enables devtools on release builds. Devtools are always enabled in debug builds.
305//!   On **macOS**, enabling devtools, requires calling private APIs so you should not enable this flag in release
306//!   build if your app needs to publish to App Store.
307//! - `mac-proxy`: Enables `WebViewBuilder::with_proxy_config` on macOS.
308//! - `linux-body`: Enables body support of custom protocol request on Linux. Requires
309//!   WebKit2GTK v2.40 or above.
310//! - `tracing`: enables [`tracing`] for `evaluate_script`, `ipc_handler`, and `custom_protocols`.
311//!
312//! ## Partners
313//!
314//! <table>
315//!   <tbody>
316//!     <tr>
317//!       <td align="center" valign="middle">
318//!         <a href="https://crabnebula.dev" target="_blank">
319//!           <img src=".github/sponsors/crabnebula.svg" alt="CrabNebula" width="283">
320//!         </a>
321//!       </td>
322//!     </tr>
323//!   </tbody>
324//! </table>
325//!
326//! For the complete list of sponsors please visit our [website](https://tauri.app#sponsors) and [Open Collective](https://opencollective.com/tauri).
327//!
328//! ## License
329//!
330//! Apache-2.0/MIT
331//!
332//! [`tao`]: https://docs.rs/tao
333//! [`winit`]: https://docs.rs/winit
334//! [`tracing`]: https://docs.rs/tracing
335
336#![allow(clippy::new_without_default)]
337#![allow(clippy::default_constructed_unit_structs)]
338#![allow(clippy::type_complexity)]
339#![cfg_attr(docsrs, feature(doc_cfg))]
340
341// #[cfg(any(target_os = "macos", target_os = "ios"))]
342// #[macro_use]
343// extern crate objc;
344
345#[cfg(any(target_os = "windows", target_os = "android"))]
346mod custom_protocol_workaround;
347mod error;
348#[cfg(any(target_os = "android", test))]
349mod inject_initialization_scripts;
350mod permissions;
351mod proxy;
352#[cfg(any(target_os = "macos", target_os = "android", target_os = "ios"))]
353mod util;
354mod web_context;
355
356#[cfg(target_os = "android")]
357pub(crate) mod android;
358#[cfg(target_os = "android")]
359pub use crate::android::android_setup;
360#[cfg(target_os = "android")]
361pub mod prelude {
362  pub use crate::android::{binding::*, dispatch, find_class, Context};
363  pub use tao_macros::{android_fn, generate_package_name};
364}
365#[cfg(target_os = "android")]
366pub use android::JniHandle;
367#[cfg(target_os = "android")]
368use android::*;
369
370#[cfg(gtk)]
371pub(crate) mod webkitgtk;
372/// Re-exported [raw-window-handle](https://docs.rs/raw-window-handle/latest/raw_window_handle/) crate.
373pub use raw_window_handle;
374use raw_window_handle::HasWindowHandle;
375#[cfg(gtk)]
376use webkitgtk::*;
377
378#[cfg(any(target_os = "macos", target_os = "ios"))]
379use objc2::rc::Retained;
380#[cfg(target_os = "macos")]
381use objc2_app_kit::NSWindow;
382#[cfg(any(target_os = "macos", target_os = "ios"))]
383use objc2_web_kit::WKUserContentController;
384#[cfg(any(target_os = "macos", target_os = "ios"))]
385pub(crate) mod wkwebview;
386#[cfg(any(target_os = "macos", target_os = "ios"))]
387use wkwebview::*;
388#[cfg(any(target_os = "macos", target_os = "ios"))]
389pub use wkwebview::{PrintMargin, PrintOptions, WryWebView};
390
391#[cfg(target_os = "windows")]
392pub(crate) mod webview2;
393#[cfg(target_os = "windows")]
394pub use self::webview2::ScrollBarStyle;
395#[cfg(target_os = "windows")]
396use self::webview2::*;
397#[cfg(target_os = "windows")]
398use webview2_com::Microsoft::Web::WebView2::Win32::{
399  ICoreWebView2, ICoreWebView2Controller, ICoreWebView2Environment,
400};
401
402use std::{borrow::Cow, collections::HashMap, path::PathBuf, rc::Rc};
403
404use http::{Request, Response};
405
406pub use cookie;
407pub use dpi;
408pub use error::*;
409pub use http;
410pub use permissions::{PermissionKind, PermissionResponse};
411pub use proxy::{ProxyConfig, ProxyEndpoint};
412pub use web_context::WebContext;
413
414#[cfg(target_os = "ios")]
415pub type InputAccessoryViewBuilder =
416  dyn Fn(&objc2_ui_kit::UIView) -> Option<Retained<objc2_ui_kit::UIView>>;
417
418/// A rectangular region.
419#[derive(Clone, Copy, Debug, PartialEq)]
420pub struct Rect {
421  /// Rect position.
422  pub position: dpi::Position,
423  /// Rect size.
424  pub size: dpi::Size,
425}
426
427impl Default for Rect {
428  fn default() -> Self {
429    Self {
430      position: dpi::LogicalPosition::new(0, 0).into(),
431      size: dpi::LogicalSize::new(0, 0).into(),
432    }
433  }
434}
435
436/// Resolves a custom protocol [`Request`] asynchronously.
437///
438/// See [`WebViewBuilder::with_asynchronous_custom_protocol`] for more information.
439pub struct RequestAsyncResponder {
440  pub(crate) responder: Box<dyn FnOnce(Response<Cow<'static, [u8]>>)>,
441}
442
443// SAFETY: even though the webview bindings do not indicate the responder is Send,
444// it actually is and we need it in order to let the user do the protocol computation
445// on a separate thread or async task.
446unsafe impl Send for RequestAsyncResponder {}
447
448impl RequestAsyncResponder {
449  /// Resolves the request with the given response.
450  pub fn respond<T: Into<Cow<'static, [u8]>>>(self, response: Response<T>) {
451    let (parts, body) = response.into_parts();
452    (self.responder)(Response::from_parts(parts, body.into()))
453  }
454}
455
456/// Response for the new window request handler.
457///
458/// See [`WebViewBuilder::with_new_window_req_handler`].
459pub enum NewWindowResponse {
460  /// Allow the window to be opened with the default implementation.
461  Allow,
462  /// Allow the window to be opened, with the given platform webview instance.
463  ///
464  /// ## Platform-specific:
465  ///
466  /// **Linux**: The webview must be related to the caller webview. See [`WebViewBuilderExtUnix::with_related_view`].
467  /// **Windows**: The webview must use the same environment as the caller webview. See [`WebViewBuilderExtWindows::with_environment`].
468  /// **macOS**: The webview must use the same configuration as the caller webview. See [`WebViewBuilderExtMacos::with_webview_configuration`].
469  #[cfg(not(any(target_os = "android", target_os = "ios")))]
470  Create {
471    #[cfg(any(
472      target_os = "linux",
473      target_os = "dragonfly",
474      target_os = "freebsd",
475      target_os = "netbsd",
476      target_os = "openbsd",
477    ))]
478    webview: webkit2gtk::WebView,
479    #[cfg(windows)]
480    webview: ICoreWebView2,
481    #[cfg(target_os = "macos")]
482    webview: Retained<objc2_web_kit::WKWebView>,
483  },
484  /// Deny the window from being opened.
485  Deny,
486}
487
488/// Information about the webview that initiated a new window request.
489#[derive(Debug)]
490pub struct NewWindowOpener {
491  /// The instance of the webview that initiated the new window request.
492  ///
493  /// This must be set as the related view of the new webview. See [`WebViewBuilderExtUnix::with_related_view`].
494  #[cfg(any(
495    target_os = "linux",
496    target_os = "dragonfly",
497    target_os = "freebsd",
498    target_os = "netbsd",
499    target_os = "openbsd",
500  ))]
501  pub webview: webkit2gtk::WebView,
502  /// The instance of the webview that initiated the new window request.
503  #[cfg(windows)]
504  pub webview: ICoreWebView2,
505  /// The environment of the webview that initiated the new window request.
506  ///
507  /// The target webview environment **MUST** match the environment of the opener webview. See [`WebViewBuilderExtWindows::with_environment`].
508  #[cfg(windows)]
509  pub environment: ICoreWebView2Environment,
510  /// The instance of the webview that initiated the new window request.
511  #[cfg(target_os = "macos")]
512  pub webview: Retained<objc2_web_kit::WKWebView>,
513  /// Configuration of the target webview.
514  ///
515  /// This **MUST** be used when creating the target webview. See [`WebViewBuilderExtMacos::with_webview_configuration`].
516  #[cfg(target_os = "macos")]
517  pub target_configuration: Retained<objc2_web_kit::WKWebViewConfiguration>,
518}
519
520/// Window features of a window requested to open.
521#[non_exhaustive]
522#[derive(Debug)]
523pub struct NewWindowFeatures {
524  /// Specifies the size of the content area
525  /// as defined by the user's operating system where the new window will be generated.
526  pub size: Option<dpi::LogicalSize<f64>>,
527  /// Specifies the position of the window relative to the work area
528  /// as defined by the user's operating system where the new window will be generated.
529  pub position: Option<dpi::LogicalPosition<f64>>,
530  /// Information about the webview opener containing data that must be used when creating the new webview.
531  pub opener: NewWindowOpener,
532}
533
534/// An id for a webview
535pub type WebViewId<'a> = &'a str;
536
537// WebViewAttributes is not stable enough to be pub.
538struct WebViewAttributes<'a> {
539  /// An id that will be passed when this webview makes requests in certain callbacks.
540  pub id: Option<WebViewId<'a>>,
541
542  /// Web context to be shared with this webview.
543  #[allow(unused)]
544  pub context: Option<&'a mut WebContext>,
545
546  /// Whether the WebView should have a custom user-agent.
547  pub user_agent: Option<String>,
548
549  /// Whether the WebView window should be visible.
550  pub visible: bool,
551
552  /// Whether the WebView should be transparent.
553  ///
554  /// ## Platform-specific:
555  ///
556  /// **Windows 7**: Not supported.
557  pub transparent: bool,
558
559  /// Specify the webview background color. This will be ignored if `transparent` is set to `true`.
560  ///
561  /// The color uses the RGBA format.
562  ///
563  /// ## Platform-specific:
564  ///
565  /// - **macOS**: Disables the default white WKWebView background via the `drawsBackground` KVC key
566  ///   (same as the `transparent` feature) and sets `underPageBackgroundColor` (macOS 12+) for overscroll areas.
567  /// - **Windows**:
568  ///   - On Windows 7, transparency is not supported and the alpha value will be ignored.
569  ///   - On Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255`
570  pub background_color: Option<RGBA>,
571
572  /// Whether load the provided URL to [`WebView`].
573  ///
574  /// ## Note
575  ///
576  /// Data URLs are not supported, use [`html`](Self::html) option instead.
577  pub url: Option<String>,
578
579  /// Headers used when loading the requested [`url`](Self::url).
580  pub headers: Option<http::HeaderMap>,
581
582  /// Whether page zooming by hotkeys or gestures is enabled
583  ///
584  /// ## Platform-specific
585  ///
586  /// - Windows: Setting to `false` can't disable pinch zoom on WebView2 Runtime version before 91.0.865.0,
587  ///   see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10865-prerelease>
588  ///
589  /// - **macOS / Linux / Android / iOS**: Unsupported
590  pub zoom_hotkeys_enabled: bool,
591
592  /// Whether load the provided html string to [`WebView`].
593  /// This will be ignored if the `url` is provided.
594  ///
595  /// # Warning
596  ///
597  /// The Page loaded from html string will have `null` origin.
598  ///
599  /// ## Platform-specific:
600  ///
601  /// - **Windows:** the string can not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size
602  pub html: Option<String>,
603
604  /// A list of initialization javascript scripts to run when loading new pages.
605  /// When webview load a new page, this initialization code will be executed.
606  /// It is guaranteed that code is executed before `window.onload`.
607  ///
608  /// ## Platform-specific
609  ///
610  /// - **Windows**: scripts are always injected into sub frames.
611  /// - **Android:** When [addDocumentStartJavaScript] is not supported,
612  ///   we prepend them to each HTML head (implementation only supported on custom protocol URLs).
613  ///   For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
614  ///
615  /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
616  /// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
617  pub initialization_scripts: Vec<InitializationScript>,
618
619  /// A list of custom loading protocols with pairs of scheme uri string and a handling
620  /// closure.
621  ///
622  /// The closure takes an Id ([WebViewId]), [Request] and [RequestAsyncResponder] as arguments and returns a [Response].
623  ///
624  /// # Note
625  ///
626  /// If using a shared [WebContext], make sure custom protocols were not already registered on that web context on Linux.
627  ///
628  /// # Warning
629  ///
630  /// Pages loaded from custom protocol will have different Origin on different platforms. And
631  /// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin`
632  /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
633  /// different Origin headers across platforms:
634  ///
635  /// - macOS, iOS and Linux: `<scheme_name>://<path>` (so it will be `wry://path/to/page`).
636  /// - Windows and Android: `http://<scheme_name>.<path>` by default (so it will be `http://wry.path/to/page`). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`].
637  ///
638  /// # Reading assets on mobile
639  ///
640  /// - Android: Android has `assets` and `resource` path finder to
641  ///   locate your files in those directories. For more information, see [Loading in-app content](https://developer.android.com/guide/webapps/load-local-content) page.
642  /// - iOS: To get the path of your assets, you can call [`CFBundle::resources_path`](https://docs.rs/core-foundation/latest/core_foundation/bundle/struct.CFBundle.html#method.resources_path). So url like `wry://assets/index.html` could get the html file in assets directory.
643  pub custom_protocols:
644    HashMap<String, Box<dyn Fn(WebViewId, Request<Vec<u8>>, RequestAsyncResponder) + Send + Sync>>,
645
646  /// The IPC handler to receive the message from Javascript on webview
647  /// using `window.ipc.postMessage("insert_message_here")` to host Rust code.
648  pub ipc_handler: Option<Box<dyn Fn(Request<String>)>>,
649
650  /// A handler closure to process incoming [`DragDropEvent`] of the webview.
651  ///
652  /// ## Blocking OS Default Behavior
653  ///
654  /// Return `true` in the callback to block the OS' default behavior.
655  ///
656  /// Note, that if you do block this behavior, it won't be possible to drop files on `<input type="file">` forms.
657  /// Also note, that it's not possible to manually set the value of a `<input type="file">` via JavaScript for security reasons.
658  ///
659  /// ## Platform-specific:
660  ///
661  /// - **Windows:** This will disable the HTML Drag and Drop APIs like `draggable="true"`,
662  ///   since we replace the drag drop handler of WebView 2 on Windows.
663  ///   `handler`'s return value is ignored on Windows.
664  /// - **Android / iOS:** Unsupported.
665  pub drag_drop_handler: Option<Box<dyn Fn(DragDropEvent) -> bool>>,
666
667  /// A navigation handler to decide if incoming url is allowed to navigate.
668  ///
669  /// The closure take a `String` parameter as url and returns a `bool` to determine whether the navigation should happen.
670  /// `true` allows to navigate and `false` does not.
671  pub navigation_handler: Option<Box<dyn Fn(String) -> bool>>,
672
673  /// A download started handler to manage incoming downloads.
674  ///
675  /// The closure takes two parameters, the first is a `String` representing the url being downloaded from and the
676  /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter
677  /// parameter can be used to set the download location by assigning a new path to it, the assigned path _must_ be
678  /// absolute. The closure returns a `bool` to allow or deny the download.
679  ///
680  /// [`Self::default()`] sets a handler allowing all downloads to match browser behavior.
681  pub download_started_handler: Option<Box<dyn FnMut(String, &mut PathBuf) -> bool + 'static>>,
682
683  /// A download completion handler to manage downloads that have finished.
684  ///
685  /// The closure is fired when the download completes, whether it was successful or not.
686  /// The closure takes a `String` representing the URL of the original download request, an `Option<PathBuf>`
687  /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download
688  /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download
689  /// did not succeed, and may instead indicate some other failure, always check the third parameter if you need to
690  /// know if the download succeeded.
691  ///
692  /// ## Platform-specific:
693  ///
694  /// - **macOS**: The second parameter indicating the path the file was saved to, is always empty,
695  ///   due to API limitations.
696  pub download_completed_handler: Option<Rc<dyn Fn(String, Option<PathBuf>, bool) + 'static>>,
697
698  /// A new window request handler to decide if incoming url is allowed to be opened.
699  ///
700  /// A new window is requested to be opened by the [window.open] API.
701  ///
702  /// The closure take the URL to open and the window features object and returns [`NewWindowResponse`] to determine whether the window should open.
703  ///
704  /// [window.open]: https://developer.mozilla.org/en-US/docs/Web/API/Window/open
705  pub new_window_req_handler: Option<Box<dyn Fn(String, NewWindowFeatures) -> NewWindowResponse>>,
706
707  /// Enables clipboard access for the page rendered on **Linux** and **Windows**.
708  ///
709  /// macOS doesn't provide such method and is always enabled by default. But your app will still need to add menu
710  /// item accelerators to use the clipboard shortcuts.
711  pub clipboard: bool,
712
713  /// Enable web inspector which is usually called browser devtools.
714  ///
715  /// Note this only enables devtools to the webview. To open it, you can call
716  /// [`WebView::open_devtools`], or right click the page and open it from the context menu.
717  ///
718  /// ## Platform-specific
719  ///
720  /// - macOS: This will call private functions on **macOS**. It is enabled in **debug** builds,
721  ///   but requires `devtools` feature flag to actually enable it in **release** builds.
722  /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.
723  /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.
724  pub devtools: bool,
725
726  /// Whether clicking an inactive window also clicks through to the webview. Default is `false`.
727  ///
728  /// ## Platform-specific
729  ///
730  /// This configuration only impacts macOS.
731  pub accept_first_mouse: bool,
732
733  /// Indicates whether horizontal swipe gestures trigger backward and forward page navigation.
734  ///
735  /// ## Platform-specific:
736  ///
737  /// - Windows: Setting to `false` does nothing on WebView2 Runtime version before 92.0.902.0,
738  ///   see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10902-prerelease>
739  ///
740  /// - **Android / iOS:** Unsupported.
741  pub back_forward_navigation_gestures: bool,
742
743  /// Set a handler closure to process the change of the webview's document title.
744  pub document_title_changed_handler: Option<Box<dyn Fn(String)>>,
745
746  /// Run the WebView with incognito mode. Note that WebContext will be ignored if incognito is
747  /// enabled.
748  ///
749  /// ## Platform-specific:
750  ///
751  /// - **Windows**: Requires WebView2 Runtime version 101.0.1210.39 or higher, does nothing on older versions,
752  ///   see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10121039>
753  /// - **Android:** Unsupported yet.
754  /// - **macOS / iOS**: Uses the nonPersistent DataStore.
755  pub incognito: bool,
756
757  /// Whether all media can be played without user interaction.
758  pub autoplay: bool,
759
760  /// Set a handler closure to process page load events.
761  pub on_page_load_handler: Option<Box<dyn Fn(PageLoadEvent, String)>>,
762
763  /// Set a proxy configuration for the webview. Supports HTTP CONNECT and SOCKSv5 proxies
764  ///
765  /// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled.
766  /// - **Android / iOS:** Not supported.
767  pub proxy_config: Option<ProxyConfig>,
768
769  /// Whether the webview should be focused when created.
770  ///
771  /// ## Platform-specific:
772  ///
773  /// - **macOS / Android / iOS:** Unsupported.
774  pub focused: bool,
775
776  /// The webview bounds. Defaults to `x: 0, y: 0, width: 200, height: 200`.
777  /// This is only effective if the webview was created by [`WebViewBuilder::new_as_child`]
778  /// or on Linux, if was created by [`WebViewExtUnix::new_gtk`] or [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`].
779  pub bounds: Option<Rect>,
780
781  /// Whether background throttling should be disabled.
782  ///
783  /// By default, browsers throttle timers and even unload the whole tab (view) to free resources after roughly 5 minutes when
784  /// a view became minimized or hidden. This will permanently suspend all tasks until the documents visibility state
785  /// changes back from hidden to visible by bringing the view back to the foreground.
786  ///
787  /// ## Platform-specific
788  ///
789  /// - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.
790  /// - **iOS**: Supported since version 17.0+.
791  /// - **macOS**: Supported since version 14.0+.
792  ///
793  /// see <https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578>
794  pub background_throttling: Option<BackgroundThrottlingPolicy>,
795
796  /// Whether JavaScript should be disabled.
797  pub javascript_disabled: bool,
798
799  /// A handler to intercept permission requests from the webview.
800  ///
801  /// The handler receives the [`PermissionKind`] and should return
802  /// the desired [`PermissionResponse`].
803  ///
804  /// > [!NOTE]
805  /// > This handler only triggers for new permission requests. If the user has already
806  /// > allowed or denied a permission persistently within the webview, the browser
807  /// > will use the saved preference instead of calling this handler.
808  ///
809  /// ## Platform-specific:
810  ///
811  /// - **Windows**: Fully supported via WebView2's PermissionRequested event.
812  /// - **macOS / iOS**: Fully supported via WKUIDelegate's requestMediaCapturePermission.
813  /// - **Linux**: Fully supported via WebKitGTK's permission-request signal.
814  /// - **Android**: Supported via JNI bridge for geolocation, microphone, camera,
815  ///   protected media, and MIDI requests. Android runtime permissions may still
816  ///   trigger native OS prompts before access is granted.
817  ///
818  /// ## Example
819  ///
820  /// ```no_run
821  /// # use wry::{WebViewBuilder, PermissionKind, PermissionResponse};
822  /// let webview = WebViewBuilder::new()
823  ///     .with_permission_handler(|kind| {
824  ///         match kind {
825  ///             PermissionKind::Microphone => PermissionResponse::Allow,
826  ///             PermissionKind::Camera => PermissionResponse::Allow,
827  ///             _ => PermissionResponse::Default,
828  ///         }
829  ///     });
830  /// ```
831  pub permission_handler: Option<Box<dyn Fn(PermissionKind) -> PermissionResponse + Send + Sync>>,
832  /// Controls the WebView's browser-level general autofill behavior.
833  ///
834  /// **This option does not disable password or credit card autofill.**
835  ///
836  /// When enabled, the WebView may automatically populate form fields using
837  /// previously stored data such as addresses or contact information.
838  ///
839  /// If not specified, this is `true` by default.
840  ///
841  /// ## Platform-specific
842  ///
843  /// - **Windows**: Supported. On Windows, WebView2's autofill feature (called
844  ///   "Suggestions") may not honor `autocomplete="off"` attributes on input
845  ///   elements in some cases. When this option is `false`, that autofill
846  ///   behavior will be disabled.
847  /// - **macOS / Linux / Android / iOS**: Unsupported and ignored.
848  pub general_autofill_enabled: bool,
849}
850
851impl Default for WebViewAttributes<'_> {
852  fn default() -> Self {
853    Self {
854      id: Default::default(),
855      context: None,
856      user_agent: None,
857      visible: true,
858      transparent: false,
859      background_color: None,
860      url: None,
861      headers: None,
862      html: None,
863      initialization_scripts: Default::default(),
864      custom_protocols: Default::default(),
865      ipc_handler: None,
866      drag_drop_handler: None,
867      navigation_handler: None,
868      download_started_handler: Some(Box::new(|_, _| true)),
869      download_completed_handler: None,
870      new_window_req_handler: None,
871      clipboard: false,
872      #[cfg(debug_assertions)]
873      devtools: true,
874      #[cfg(not(debug_assertions))]
875      devtools: false,
876      zoom_hotkeys_enabled: false,
877      accept_first_mouse: false,
878      back_forward_navigation_gestures: false,
879      document_title_changed_handler: None,
880      incognito: false,
881      autoplay: true,
882      on_page_load_handler: None,
883      proxy_config: None,
884      focused: true,
885      bounds: Some(Rect {
886        position: dpi::LogicalPosition::new(0, 0).into(),
887        size: dpi::LogicalSize::new(200, 200).into(),
888      }),
889      background_throttling: None,
890      javascript_disabled: false,
891      permission_handler: None,
892      general_autofill_enabled: true,
893    }
894  }
895}
896
897/// Builder type of [`WebView`].
898///
899/// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and
900/// scripts for those who prefer to control fine grained window creation and event handling.
901/// [`WebViewBuilder`] provides ability to setup initialization before web engine starts.
902pub struct WebViewBuilder<'a> {
903  attrs: WebViewAttributes<'a>,
904  platform_specific: PlatformSpecificWebViewAttributes,
905  /// Records errors before the [`WebViewBuilder::build`] is called
906  error: crate::Result<()>,
907}
908
909impl<'a> WebViewBuilder<'a> {
910  /// Create a new [`WebViewBuilder`].
911  pub fn new() -> Self {
912    Self {
913      attrs: WebViewAttributes::default(),
914      #[allow(clippy::default_constructed_unit_structs)]
915      platform_specific: PlatformSpecificWebViewAttributes::default(),
916      error: Ok(()),
917    }
918  }
919
920  /// Create a new [`WebViewBuilder`] with a web context that can be shared with multiple [`WebView`]s.
921  pub fn new_with_web_context(web_context: &'a mut WebContext) -> Self {
922    let attrs = WebViewAttributes {
923      context: Some(web_context),
924      ..Default::default()
925    };
926
927    Self {
928      attrs,
929      #[allow(clippy::default_constructed_unit_structs)]
930      platform_specific: PlatformSpecificWebViewAttributes::default(),
931      error: Ok(()),
932    }
933  }
934
935  /// Set an id that will be passed when this webview makes requests in certain callbacks.
936  pub fn with_id(mut self, id: WebViewId<'a>) -> Self {
937    self.attrs.id = Some(id);
938    self
939  }
940
941  /// Indicates whether horizontal swipe gestures trigger backward and forward page navigation.
942  ///
943  /// ## Platform-specific:
944  ///
945  /// - **Android / iOS:** Unsupported.
946  pub fn with_back_forward_navigation_gestures(mut self, gesture: bool) -> Self {
947    self.attrs.back_forward_navigation_gestures = gesture;
948    self
949  }
950
951  /// Sets whether the WebView should be transparent.
952  ///
953  /// ## Platform-specific:
954  ///
955  /// **Windows 7**: Not supported.
956  pub fn with_transparent(mut self, transparent: bool) -> Self {
957    self.attrs.transparent = transparent;
958    self
959  }
960
961  /// Specify the webview background color. This will be ignored if `transparent` is set to `true`.
962  ///
963  /// The color uses the RGBA format.
964  ///
965  /// ## Platform-specific:
966  ///
967  /// - **macOS**: Disables the default white WKWebView background via the `drawsBackground` KVC key
968  ///   (same as the `transparent` feature) and sets `underPageBackgroundColor` (macOS 12+) for overscroll areas.
969  /// - **Windows**:
970  ///   - on Windows 7, transparency is not supported and the alpha value will be ignored.
971  ///   - on Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255`
972  pub fn with_background_color(mut self, background_color: RGBA) -> Self {
973    self.attrs.background_color = Some(background_color);
974    self
975  }
976
977  /// Sets whether the WebView should be visible or not.
978  pub fn with_visible(mut self, visible: bool) -> Self {
979    self.attrs.visible = visible;
980    self
981  }
982
983  /// Sets whether all media can be played without user interaction.
984  pub fn with_autoplay(mut self, autoplay: bool) -> Self {
985    self.attrs.autoplay = autoplay;
986    self
987  }
988
989  /// Initialize javascript code when loading new pages. When webview load a new page, this
990  /// initialization code will be executed. It is guaranteed that code is executed before
991  /// `window.onload`.
992  ///
993  /// ## Example
994  /// ```ignore
995  /// let webview = WebViewBuilder::new()
996  ///   .with_initialization_script("console.log('Running inside main frame only')")
997  ///   .with_url("https://tauri.app")
998  ///   .build(&window)
999  ///   .unwrap();
1000  /// ```
1001  ///
1002  /// ## Platform-specific
1003  ///
1004  ///- **Windows:** scripts are always added to subframes.
1005  /// - **Android:** When [addDocumentStartJavaScript] is not supported,
1006  ///   we prepend them to each HTML head (implementation only supported on custom protocol URLs).
1007  ///   For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
1008  ///
1009  /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
1010  /// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
1011  pub fn with_initialization_script<S: Into<String>>(self, js: S) -> Self {
1012    self.with_initialization_script_for_main_only(js, true)
1013  }
1014
1015  /// Same as [`with_initialization_script`](Self::with_initialization_script) but with option to inject into main frame only or sub frames.
1016  ///
1017  /// ## Example
1018  /// ```ignore
1019  /// let webview = WebViewBuilder::new()
1020  ///   .with_initialization_script_for_main_only("console.log('Running inside main frame only')", true)
1021  ///   .with_initialization_script_for_main_only("console.log('Running  main frame and sub frames')", false)
1022  ///   .with_url("https://tauri.app")
1023  ///   .build(&window)
1024  ///   .unwrap();
1025  /// ```
1026  ///
1027  /// ## Platform-specific:
1028  ///
1029  /// - **Windows:** scripts are always added to subframes regardless of the `for_main_frame_only` option.
1030  /// - **Android**: When [addDocumentStartJavaScript] is not supported, scripts are always injected into main frame only.
1031  ///
1032  /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
1033  pub fn with_initialization_script_for_main_only<S: Into<String>>(
1034    mut self,
1035    js: S,
1036    for_main_frame_only: bool,
1037  ) -> Self {
1038    let script = js.into();
1039    if !script.is_empty() {
1040      self
1041        .attrs
1042        .initialization_scripts
1043        .push(InitializationScript {
1044          script,
1045          for_main_frame_only,
1046        });
1047    }
1048    self
1049  }
1050
1051  /// Register custom loading protocols with pairs of scheme uri string and a handling
1052  /// closure.
1053  ///
1054  /// The closure takes a [Request] and returns a [Response]
1055  ///
1056  /// When registering a custom protocol with the same name, only the last registered one will be used.
1057  ///
1058  /// # Warning
1059  ///
1060  /// Pages loaded from custom protocol will have different Origin on different platforms. And
1061  /// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin`
1062  /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
1063  /// different Origin headers across platforms:
1064  ///
1065  /// - macOS, iOS and Linux: `<scheme_name>://<path>` (so it will be `wry://path/to/page).
1066  /// - Windows and Android: `http://<scheme_name>.<path>` by default (so it will be `http://wry.path/to/page`). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`].
1067  ///
1068  /// # Reading assets on mobile
1069  ///
1070  /// - Android: For loading content from the `assets` folder (which is copied to the Andorid apk) please
1071  ///   use the function [`with_asset_loader`] from [`WebViewBuilderExtAndroid`] instead.
1072  ///   This function on Android can only be used to serve assets you can embed in the binary or are
1073  ///   elsewhere in Android (provided the app has appropriate access), but not from the `assets`
1074  ///   folder which lives within the apk. For the cases where this can be used, it works the same as in macOS and Linux.
1075  /// - iOS: To get the path of your assets, you can call [`CFBundle::resources_path`](https://docs.rs/core-foundation/latest/core_foundation/bundle/struct.CFBundle.html#method.resources_path). So url like `wry://assets/index.html` could get the html file in assets directory.
1076  pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
1077  where
1078    F: Fn(WebViewId, Request<Vec<u8>>) -> Response<Cow<'static, [u8]>> + Send + Sync + 'static,
1079  {
1080    #[cfg(any(
1081      target_os = "linux",
1082      target_os = "dragonfly",
1083      target_os = "freebsd",
1084      target_os = "netbsd",
1085      target_os = "openbsd",
1086    ))]
1087    if let Some(context) = &mut self.attrs.context {
1088      if context.is_custom_protocol_registered(&name) {
1089        let err = Err(crate::Error::DuplicateCustomProtocol(name));
1090        self.error = self.error.and(err);
1091        return self;
1092      }
1093    }
1094
1095    if self.attrs.custom_protocols.contains_key(&name) {
1096      let err = Err(crate::Error::DuplicateCustomProtocol(name));
1097      self.error = self.error.and(err);
1098      return self;
1099    }
1100
1101    self.attrs.custom_protocols.insert(
1102      name,
1103      Box::new(move |id, request, responder| {
1104        let http_response = handler(id, request);
1105        responder.respond(http_response);
1106      }),
1107    );
1108    self
1109  }
1110
1111  /// Same as [`Self::with_custom_protocol`] but with an asynchronous responder.
1112  ///
1113  /// When registering a custom protocol with the same name, only the last registered one will be used.
1114  ///
1115  /// # Warning
1116  ///
1117  /// Pages loaded from custom protocol will have different Origin on different platforms. And
1118  /// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin`
1119  /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
1120  /// different Origin headers across platforms:
1121  ///
1122  /// - macOS, iOS and Linux: `<scheme_name>://<path>` (so it will be `wry://path/to/page).
1123  /// - Windows and Android: `http://<scheme_name>.<path>` by default (so it will be `http://wry.path/to/page`). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`].
1124  ///
1125  /// # Examples
1126  ///
1127  /// ```no_run
1128  /// use wry::{WebViewBuilder, raw_window_handle};
1129  /// WebViewBuilder::new()
1130  ///   .with_asynchronous_custom_protocol("wry".into(), |_webview_id, request, responder| {
1131  ///     // here you can use a tokio task, thread pool or anything
1132  ///     // to do heavy computation to resolve your request
1133  ///     // e.g. downloading files, opening the camera...
1134  ///     std::thread::spawn(move || {
1135  ///       std::thread::sleep(std::time::Duration::from_secs(2));
1136  ///       responder.respond(http::Response::builder().body(Vec::new()).unwrap());
1137  ///     });
1138  ///   });
1139  /// ```
1140  pub fn with_asynchronous_custom_protocol<F>(mut self, name: String, handler: F) -> Self
1141  where
1142    F: Fn(WebViewId, Request<Vec<u8>>, RequestAsyncResponder) + Send + Sync + 'static,
1143  {
1144    #[cfg(any(
1145      target_os = "linux",
1146      target_os = "dragonfly",
1147      target_os = "freebsd",
1148      target_os = "netbsd",
1149      target_os = "openbsd",
1150    ))]
1151    if let Some(context) = &mut self.attrs.context {
1152      if context.is_custom_protocol_registered(&name) {
1153        let err = Err(crate::Error::DuplicateCustomProtocol(name));
1154        self.error = self.error.and(err);
1155        return self;
1156      }
1157    }
1158
1159    if self.attrs.custom_protocols.contains_key(&name) {
1160      let err = Err(crate::Error::DuplicateCustomProtocol(name));
1161      self.error = self.error.and(err);
1162      return self;
1163    }
1164
1165    self.attrs.custom_protocols.insert(name, Box::new(handler));
1166    self
1167  }
1168
1169  /// Set the IPC handler to receive the message from Javascript on webview
1170  /// using `window.ipc.postMessage("insert_message_here")` to host Rust code.
1171  ///
1172  /// ## Platform-specific
1173  ///
1174  /// - **Linux / Android**: The request URL is not supported on iframes and the main frame URL is used instead.
1175  pub fn with_ipc_handler<F>(mut self, handler: F) -> Self
1176  where
1177    F: Fn(Request<String>) + 'static,
1178  {
1179    self.attrs.ipc_handler = Some(Box::new(handler));
1180    self
1181  }
1182
1183  /// A handler closure to process incoming [`DragDropEvent`] of the webview.
1184  ///
1185  /// ## Blocking OS Default Behavior
1186  ///
1187  /// Return `true` in the callback to block the OS' default behavior.
1188  ///
1189  /// Note, that if you do block this behavior, it won't be possible to drop files on `<input type="file">` forms.
1190  /// Also note, that it's not possible to manually set the value of a `<input type="file">` via JavaScript for security reasons.
1191  ///
1192  /// ## Platform-specific:
1193  ///
1194  /// - **Windows:** This will disable the HTML Drag and Drop APIs like `draggable="true"`,
1195  ///   since we replace the drag drop handler of WebView 2 on Windows.
1196  ///   `handler`'s return value is ignored on Windows.
1197  /// - **Android / iOS:** Unsupported.
1198  pub fn with_drag_drop_handler<F>(mut self, handler: F) -> Self
1199  where
1200    F: Fn(DragDropEvent) -> bool + 'static,
1201  {
1202    self.attrs.drag_drop_handler = Some(Box::new(handler));
1203    self
1204  }
1205
1206  /// Load the provided URL with given headers when the builder calling [`WebViewBuilder::build`] to create the [`WebView`].
1207  /// The provided URL must be valid.
1208  ///
1209  /// ## Note
1210  ///
1211  /// Data URLs are not supported, use [`html`](Self::with_html) option instead.
1212  ///
1213  /// ## Platform-specific:
1214  ///
1215  /// - **Windows and Android:** if the URL's scheme is a registered custom protocol,
1216  ///   a work around is used that changes the URL this navigates to
1217  ///   from `{protocol}://localhost/abc` to `{http_or_https}://{protocol}.localhost/abc`
1218  pub fn with_url_and_headers(mut self, url: impl Into<String>, headers: http::HeaderMap) -> Self {
1219    self.attrs.url = Some(url.into());
1220    self.attrs.headers = Some(headers);
1221    self
1222  }
1223
1224  /// Load the provided URL when the builder calling [`WebViewBuilder::build`] to create the [`WebView`].
1225  /// The provided URL must be valid.
1226  ///
1227  /// ## Note
1228  ///
1229  /// Data URLs are not supported, use [`html`](Self::with_html) option instead.
1230  ///
1231  /// ## Platform-specific:
1232  ///
1233  /// - **Windows and Android:** if the URL's scheme is a registered custom protocol,
1234  ///   a work around is used that changes the URL this navigates to
1235  ///   from `{protocol}://localhost/abc` to `{http_or_https}://{protocol}.localhost/abc`
1236  pub fn with_url(mut self, url: impl Into<String>) -> Self {
1237    self.attrs.url = Some(url.into());
1238    self.attrs.headers = None;
1239    self
1240  }
1241
1242  /// Set headers used when loading the requested [`url`](Self::with_url).
1243  pub fn with_headers(mut self, headers: http::HeaderMap) -> Self {
1244    self.attrs.headers = Some(headers);
1245    self
1246  }
1247
1248  /// Load the provided HTML string when the builder calling [`WebViewBuilder::build`] to create the [`WebView`].
1249  /// This will be ignored if `url` is provided.
1250  ///
1251  /// # Warning
1252  ///
1253  /// The Page loaded from html string will have `null` origin.
1254  ///
1255  /// ## Platform-specific:
1256  ///
1257  /// - **Windows:** the string can not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size
1258  pub fn with_html(mut self, html: impl Into<String>) -> Self {
1259    self.attrs.html = Some(html.into());
1260    self
1261  }
1262
1263  /// Set a custom [user-agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) for the WebView.
1264  ///
1265  /// ## Platform-specific
1266  ///
1267  /// - Windows: Requires WebView2 Runtime version 86.0.616.0 or higher, does nothing on older versions,
1268  ///   see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10790-prerelease>
1269  pub fn with_user_agent(mut self, user_agent: impl Into<String>) -> Self {
1270    self.attrs.user_agent = Some(user_agent.into());
1271    self
1272  }
1273
1274  /// Enable or disable web inspector which is usually called devtools.
1275  ///
1276  /// Note this only enables devtools to the webview. To open it, you can call
1277  /// [`WebView::open_devtools`], or right click the page and open it from the context menu.
1278  ///
1279  /// ## Platform-specific
1280  ///
1281  /// - macOS: This will call private functions on **macOS**. It is enabled in **debug** builds,
1282  ///   but requires `devtools` feature flag to actually enable it in **release** builds.
1283  /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.
1284  /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.
1285  pub fn with_devtools(mut self, devtools: bool) -> Self {
1286    self.attrs.devtools = devtools;
1287    self
1288  }
1289
1290  /// Whether page zooming by hotkeys or gestures is enabled
1291  ///
1292  /// ## Platform-specific
1293  ///
1294  /// - Windows: Setting to `false` can't disable pinch zoom on WebView2 Runtime version before 91.0.865.0,
1295  ///   see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10865-prerelease>
1296  ///
1297  /// - **macOS / Linux / Android / iOS**: Unsupported
1298  pub fn with_hotkeys_zoom(mut self, zoom: bool) -> Self {
1299    self.attrs.zoom_hotkeys_enabled = zoom;
1300    self
1301  }
1302
1303  /// Set a navigation handler to decide if incoming url is allowed to navigate.
1304  ///
1305  /// The closure take a `String` parameter as url and returns a `bool` to determine whether the navigation should happen.
1306  /// `true` allows to navigate and `false` does not.
1307  pub fn with_navigation_handler(mut self, callback: impl Fn(String) -> bool + 'static) -> Self {
1308    self.attrs.navigation_handler = Some(Box::new(callback));
1309    self
1310  }
1311
1312  /// Set a handler to intercept permission requests from the webview.
1313  ///
1314  /// The handler receives the [`PermissionKind`] and should return
1315  /// the desired [`PermissionResponse`].
1316  ///
1317  /// > [!NOTE]
1318  /// > This handler only triggers for new permission requests. If the user has already
1319  /// > allowed or denied a permission persistently within the webview, the browser
1320  /// > will use the saved preference instead of calling this handler.
1321  ///
1322  /// ## Platform-specific:
1323  ///
1324  /// - **Windows**: Fully supported via WebView2's PermissionRequested event.
1325  /// - **macOS / iOS**: Fully supported via WKUIDelegate's requestMediaCapturePermission.
1326  /// - **Linux**: Fully supported via WebKitGTK's permission-request signal.
1327  /// - **Android**: Supported via JNI bridge for geolocation, microphone, camera,
1328  ///   protected media, and MIDI requests. Android runtime permissions may still
1329  ///   trigger native OS prompts before access is granted.
1330  ///
1331  /// ## Example
1332  ///
1333  /// ```no_run
1334  /// # use wry::{WebViewBuilder, PermissionKind, PermissionResponse};
1335  /// let webview = WebViewBuilder::new()
1336  ///     .with_permission_handler(|kind| {
1337  ///         match kind {
1338  ///             PermissionKind::Microphone => PermissionResponse::Allow,
1339  ///             PermissionKind::Camera => PermissionResponse::Allow,
1340  ///             _ => PermissionResponse::Default,
1341  ///         }
1342  ///     });
1343  /// ```
1344  pub fn with_permission_handler<F>(mut self, handler: F) -> Self
1345  where
1346    F: Fn(PermissionKind) -> PermissionResponse + Send + Sync + 'static,
1347  {
1348    self.attrs.permission_handler = Some(Box::new(handler));
1349    self
1350  }
1351
1352  /// Set a download started handler to manage incoming downloads.
1353  ///
1354  /// The closure takes two parameters, the first is a `String` representing the url being downloaded from and the
1355  /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter
1356  /// parameter can be used to set the download location by assigning a new path to it, the assigned path _must_ be
1357  /// absolute. The closure returns a `bool` to allow or deny the download.
1358  ///
1359  /// By default a handler that allows all downloads is set to match browser behavior.
1360  pub fn with_download_started_handler(
1361    mut self,
1362    download_started_handler: impl FnMut(String, &mut PathBuf) -> bool + 'static,
1363  ) -> Self {
1364    self.attrs.download_started_handler = Some(Box::new(download_started_handler));
1365    self
1366  }
1367
1368  /// Sets a download completion handler to manage downloads that have finished.
1369  ///
1370  /// The closure is fired when the download completes, whether it was successful or not.
1371  /// The closure takes a `String` representing the URL of the original download request, an `Option<PathBuf>`
1372  /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download
1373  /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download
1374  /// did not succeed, and may instead indicate some other failure, always check the third parameter if you need to
1375  /// know if the download succeeded.
1376  ///
1377  /// ## Platform-specific:
1378  ///
1379  /// - **macOS**: The second parameter indicating the path the file was saved to, is always empty,
1380  ///   due to API limitations.
1381  pub fn with_download_completed_handler(
1382    mut self,
1383    download_completed_handler: impl Fn(String, Option<PathBuf>, bool) + 'static,
1384  ) -> Self {
1385    self.attrs.download_completed_handler = Some(Rc::new(download_completed_handler));
1386    self
1387  }
1388
1389  /// Enables clipboard access for the page rendered on **Linux** and **Windows**.
1390  ///
1391  /// macOS doesn't provide such method and is always enabled by default. But your app will still need to add menu
1392  /// item accelerators to use the clipboard shortcuts.
1393  pub fn with_clipboard(mut self, clipboard: bool) -> Self {
1394    self.attrs.clipboard = clipboard;
1395    self
1396  }
1397
1398  /// Set a new window request handler to decide if incoming url is allowed to be opened.
1399  ///
1400  /// A new window is requested to be opened by the [window.open] API.
1401  ///
1402  /// The closure take the URL to open and the window features object and returns [`NewWindowResponse`] to determine whether the window should open.
1403  ///
1404  /// [window.open]: https://developer.mozilla.org/en-US/docs/Web/API/Window/open
1405  pub fn with_new_window_req_handler(
1406    mut self,
1407    callback: impl Fn(String, NewWindowFeatures) -> NewWindowResponse + 'static,
1408  ) -> Self {
1409    self.attrs.new_window_req_handler = Some(Box::new(callback));
1410    self
1411  }
1412
1413  /// Sets whether clicking an inactive window also clicks through to the webview. Default is `false`.
1414  ///
1415  /// ## Platform-specific
1416  ///
1417  /// This configuration only impacts macOS.
1418  pub fn with_accept_first_mouse(mut self, accept_first_mouse: bool) -> Self {
1419    self.attrs.accept_first_mouse = accept_first_mouse;
1420    self
1421  }
1422
1423  /// Set a handler closure to process the change of the webview's document title.
1424  pub fn with_document_title_changed_handler(
1425    mut self,
1426    callback: impl Fn(String) + 'static,
1427  ) -> Self {
1428    self.attrs.document_title_changed_handler = Some(Box::new(callback));
1429    self
1430  }
1431
1432  /// Run the WebView with incognito mode. Note that WebContext will be ignored if incognito is
1433  /// enabled.
1434  ///
1435  /// ## Platform-specific:
1436  ///
1437  /// - Windows: Requires WebView2 Runtime version 101.0.1210.39 or higher, does nothing on older versions,
1438  ///   see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10121039>
1439  /// - **Android:** Unsupported yet.
1440  pub fn with_incognito(mut self, incognito: bool) -> Self {
1441    self.attrs.incognito = incognito;
1442    self
1443  }
1444
1445  /// Set a handler to process page loading events.
1446  pub fn with_on_page_load_handler(
1447    mut self,
1448    handler: impl Fn(PageLoadEvent, String) + 'static,
1449  ) -> Self {
1450    self.attrs.on_page_load_handler = Some(Box::new(handler));
1451    self
1452  }
1453
1454  /// Set a proxy configuration for the webview. Supports HTTP CONNECT and SOCKSv5 proxies
1455  ///
1456  /// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled.
1457  /// - **Android / iOS:** Not supported.
1458  pub fn with_proxy_config(mut self, configuration: ProxyConfig) -> Self {
1459    self.attrs.proxy_config = Some(configuration);
1460    self
1461  }
1462
1463  /// Set whether the webview should be focused when created.
1464  ///
1465  /// ## Platform-specific:
1466  ///
1467  /// - **macOS / Android / iOS:** Unsupported.
1468  pub fn with_focused(mut self, focused: bool) -> Self {
1469    self.attrs.focused = focused;
1470    self
1471  }
1472
1473  /// Specify the webview position relative to its parent if it will be created as a child
1474  /// or if created using [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`].
1475  ///
1476  /// Defaults to `x: 0, y: 0, width: 200, height: 200`.
1477  pub fn with_bounds(mut self, bounds: Rect) -> Self {
1478    self.attrs.bounds = Some(bounds);
1479    self
1480  }
1481
1482  /// Set whether background throttling should be disabled.
1483  ///
1484  /// By default, browsers throttle timers and even unload the whole tab (view) to free resources after roughly 5 minutes when
1485  /// a view became minimized or hidden. This will permanently suspend all tasks until the documents visibility state
1486  /// changes back from hidden to visible by bringing the view back to the foreground.
1487  ///
1488  /// ## Platform-specific
1489  ///
1490  /// - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.
1491  /// - **iOS**: Supported since version 17.0+.
1492  /// - **macOS**: Supported since version 14.0+.
1493  ///
1494  /// see <https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578>
1495  pub fn with_background_throttling(mut self, policy: BackgroundThrottlingPolicy) -> Self {
1496    self.attrs.background_throttling = Some(policy);
1497    self
1498  }
1499
1500  /// Whether JavaScript should be disabled.
1501  pub fn with_javascript_disabled(mut self) -> Self {
1502    self.attrs.javascript_disabled = true;
1503    self
1504  }
1505
1506  /// Controls the WebView's browser-level general autofill behavior.
1507  ///
1508  /// **This option does not disable password or credit card autofill.**
1509  ///
1510  /// When enabled, the WebView may automatically populate form fields using
1511  /// previously stored data such as addresses or contact information.
1512  ///
1513  /// If not specified, this is `true` by default.
1514  ///
1515  /// ## Platform-specific
1516  ///
1517  /// - **Windows**: Supported. On Windows, WebView2's autofill feature (called
1518  ///   "Suggestions") may not honor `autocomplete="off"` attributes on input
1519  ///   elements in some cases. When this option is `false`, that autofill
1520  ///   behavior will be disabled.
1521  /// - **macOS / Linux / Android / iOS**: Unsupported and ignored.
1522  pub fn with_general_autofill_enabled(mut self, enabled: bool) -> Self {
1523    self.attrs.general_autofill_enabled = enabled;
1524    self
1525  }
1526
1527  /// Consume the builder and create the [`WebView`] from a type that implements [`HasWindowHandle`].
1528  ///
1529  /// # Platform-specific:
1530  ///
1531  /// - **Linux**: Only X11 is supported, if you want to support Wayland too, use [`WebViewBuilderExtUnix::build_gtk`].
1532  ///
1533  ///   Although this method only needs an X11 window handle, we use webkit2gtk, so you still need to initialize gtk
1534  ///   by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`].
1535  ///   Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation.
1536  /// - **Windows**: The webview will auto-resize when the passed handle is resized.
1537  /// - **Linux (X11)**: Unlike macOS and Windows, the webview will not auto-resize and you'll need to call [`WebView::set_bounds`] manually.
1538  ///
1539  /// # Panics:
1540  ///
1541  /// - Panics if the provided handle was not supported or invalid.
1542  /// - Panics on Linux, if [`gtk::init`] was not called in this thread.
1543  pub fn build<W: HasWindowHandle>(self, window: &'a W) -> Result<WebView> {
1544    self.error?;
1545
1546    InnerWebView::new(window, self.attrs, self.platform_specific).map(|webview| WebView { webview })
1547  }
1548
1549  /// Consume the builder and create the [`WebView`] as a child window inside the provided [`HasWindowHandle`].
1550  ///
1551  /// ## Platform-specific
1552  ///
1553  /// - **Windows**: This will create the webview as a child window of the `parent` window.
1554  /// - **macOS**: This will create the webview as a `NSView` subview of the `parent` window's
1555  ///   content view.
1556  /// - **Linux**: This will create the webview as a child window of the `parent` window. Only X11
1557  ///   is supported. This method won't work on Wayland.
1558  ///
1559  ///   Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk
1560  ///   by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`].
1561  ///   Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation.
1562  ///
1563  ///   If you want to support child webviews on X11 and Wayland at the same time,
1564  ///   we recommend using [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`].
1565  /// - **Android/iOS:** Unsupported.
1566  ///
1567  /// # Panics:
1568  ///
1569  /// - Panics if the provided handle was not support or invalid.
1570  /// - Panics on Linux, if [`gtk::init`] was not called in this thread.
1571  pub fn build_as_child<W: HasWindowHandle>(self, window: &'a W) -> Result<WebView> {
1572    self.error?;
1573
1574    InnerWebView::new_as_child(window, self.attrs, self.platform_specific)
1575      .map(|webview| WebView { webview })
1576  }
1577}
1578
1579#[cfg(any(target_os = "macos", target_os = "ios"))]
1580pub(crate) struct PlatformSpecificWebViewAttributes {
1581  data_store_identifier: Option<[u8; 16]>,
1582  traffic_light_inset: Option<dpi::Position>,
1583  allow_link_preview: bool,
1584  on_web_content_process_terminate_handler: Option<Box<dyn Fn()>>,
1585  #[cfg(target_os = "ios")]
1586  input_accessory_view_builder: Option<Box<InputAccessoryViewBuilder>>,
1587  #[cfg(target_os = "ios")]
1588  limit_navigations_to_app_bound_domains: bool,
1589  #[cfg(target_os = "macos")]
1590  webview_configuration: Option<Retained<objc2_web_kit::WKWebViewConfiguration>>,
1591}
1592
1593#[cfg(any(target_os = "macos", target_os = "ios"))]
1594impl Default for PlatformSpecificWebViewAttributes {
1595  fn default() -> Self {
1596    Self {
1597      data_store_identifier: None,
1598      traffic_light_inset: None,
1599      // platform default for this is true
1600      allow_link_preview: true,
1601      on_web_content_process_terminate_handler: None,
1602      #[cfg(target_os = "ios")]
1603      input_accessory_view_builder: None,
1604      #[cfg(target_os = "ios")]
1605      limit_navigations_to_app_bound_domains: false,
1606      #[cfg(target_os = "macos")]
1607      webview_configuration: None,
1608    }
1609  }
1610}
1611
1612#[cfg(any(target_os = "macos", target_os = "ios"))]
1613pub trait WebViewBuilderExtDarwin {
1614  /// Initialize the WebView with a custom data store identifier.
1615  /// Can be used as a replacement for data_directory not being available in WKWebView.
1616  ///
1617  /// - **macOS / iOS**: Available on macOS >= 14 and iOS >= 17
1618  ///
1619  /// Note: Enable incognito mode to use the `nonPersistent` DataStore.
1620  fn with_data_store_identifier(self, identifier: [u8; 16]) -> Self;
1621  /// Move the window controls to the specified position.
1622  /// Normally this is handled by the Window but because `WebViewBuilder::build()` overwrites the window's NSView the controls will flicker on resizing.
1623  /// Note: This method has no effects if the WebView is injected via `WebViewBuilder::build_as_child();` and there should be no flickers.
1624  /// Warning: Do not use this if your chosen window library does not support traffic light insets.
1625  /// Warning: Only use this in **decorated** windows with a **hidden titlebar**!
1626  fn with_traffic_light_inset<P: Into<dpi::Position>>(self, position: P) -> Self;
1627  /// Whether to show a link preview when long pressing on links. Available on macOS and iOS only.
1628  ///
1629  /// Default is true.
1630  ///
1631  /// See https://developer.apple.com/documentation/webkit/wkwebview/allowslinkpreview
1632  fn with_allow_link_preview(self, allow_link_preview: bool) -> Self;
1633  /// Set a handler closure to respond to web content process termination. Available on macOS and iOS only.
1634  fn with_on_web_content_process_terminate_handler(self, handler: impl Fn() + 'static) -> Self;
1635}
1636
1637#[cfg(any(target_os = "macos", target_os = "ios"))]
1638impl WebViewBuilderExtDarwin for WebViewBuilder<'_> {
1639  fn with_data_store_identifier(mut self, identifier: [u8; 16]) -> Self {
1640    self.platform_specific.data_store_identifier = Some(identifier);
1641    self
1642  }
1643
1644  fn with_traffic_light_inset<P: Into<dpi::Position>>(mut self, position: P) -> Self {
1645    self.platform_specific.traffic_light_inset = Some(position.into());
1646    self
1647  }
1648
1649  fn with_allow_link_preview(mut self, allow_link_preview: bool) -> Self {
1650    self.platform_specific.allow_link_preview = allow_link_preview;
1651    self
1652  }
1653
1654  fn with_on_web_content_process_terminate_handler(mut self, handler: impl Fn() + 'static) -> Self {
1655    self
1656      .platform_specific
1657      .on_web_content_process_terminate_handler = Some(Box::new(handler));
1658    self
1659  }
1660}
1661
1662#[cfg(target_os = "macos")]
1663pub trait WebViewBuilderExtMacos {
1664  /// Set the webview configuration that must be used to create the new webview.
1665  fn with_webview_configuration(
1666    self,
1667    configuration: Retained<objc2_web_kit::WKWebViewConfiguration>,
1668  ) -> Self;
1669}
1670
1671#[cfg(target_os = "macos")]
1672impl WebViewBuilderExtMacos for WebViewBuilder<'_> {
1673  fn with_webview_configuration(
1674    mut self,
1675    configuration: Retained<objc2_web_kit::WKWebViewConfiguration>,
1676  ) -> Self {
1677    self
1678      .platform_specific
1679      .webview_configuration
1680      .replace(configuration);
1681    self
1682  }
1683}
1684
1685#[cfg(target_os = "ios")]
1686pub trait WebViewBuilderExtIos {
1687  /// Allows overriding the the keyboard accessory view on iOS.
1688  /// Returning `None` effectively removes the view.
1689  ///
1690  /// The closure parameter is the webview instance.
1691  ///
1692  /// The accessory view is the view that appears above the keyboard when a text input element is focused.
1693  /// It usually displays a view with "Done", "Next" buttons.
1694  fn with_input_accessory_view_builder<
1695    F: Fn(&objc2_ui_kit::UIView) -> Option<Retained<objc2_ui_kit::UIView>> + 'static,
1696  >(
1697    self,
1698    builder: F,
1699  ) -> Self;
1700  /// Whether to limit navigations to App-Bound Domains. This is necessary
1701  /// to enable Service Workers on iOS.
1702  ///
1703  /// Note: If you set limit_navigations to true
1704  /// make sure to add the following to Info.plist in the iOS project:
1705  /// ```xml
1706  /// <plist>
1707  /// <dict>
1708  /// 	<key>WKAppBoundDomains</key>
1709  /// 	<array>
1710  /// 		<string>localhost</string>
1711  /// 	</array>
1712  /// </dict>
1713  /// </plist>
1714  /// ```
1715  /// You should also add any additional domains which your app requests assets from.
1716  /// Assets served through custom protocols like Tauri's IPC are added to the
1717  /// list automatically. Available on iOS only.
1718  ///
1719  /// Default is false.
1720  ///
1721  /// See https://webkit.org/blog/10882/app-bound-domains/ and
1722  /// https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/limitsnavigationstoappbounddomains
1723  fn with_limit_navigations_to_app_bound_domains(self, limit_navigations: bool) -> Self;
1724}
1725
1726#[cfg(target_os = "ios")]
1727impl WebViewBuilderExtIos for WebViewBuilder<'_> {
1728  fn with_input_accessory_view_builder<
1729    F: Fn(&objc2_ui_kit::UIView) -> Option<Retained<objc2_ui_kit::UIView>> + 'static,
1730  >(
1731    mut self,
1732    builder: F,
1733  ) -> Self {
1734    self
1735      .platform_specific
1736      .input_accessory_view_builder
1737      .replace(Box::new(builder));
1738    self
1739  }
1740  fn with_limit_navigations_to_app_bound_domains(mut self, limit_navigations: bool) -> Self {
1741    self
1742      .platform_specific
1743      .limit_navigations_to_app_bound_domains = limit_navigations;
1744    self
1745  }
1746}
1747
1748#[cfg(windows)]
1749#[derive(Clone)]
1750pub(crate) struct PlatformSpecificWebViewAttributes {
1751  additional_browser_args: Option<String>,
1752  browser_accelerator_keys: bool,
1753  theme: Option<Theme>,
1754  use_https: bool,
1755  scroll_bar_style: ScrollBarStyle,
1756  browser_extensions_enabled: bool,
1757  extension_path: Option<PathBuf>,
1758  default_context_menus: bool,
1759  environment: Option<ICoreWebView2Environment>,
1760  profile_name: Option<String>,
1761}
1762
1763#[cfg(windows)]
1764impl Default for PlatformSpecificWebViewAttributes {
1765  fn default() -> Self {
1766    Self {
1767      additional_browser_args: None,
1768      browser_accelerator_keys: true, // This is WebView2's default behavior
1769      default_context_menus: true,    // This is WebView2's default behavior
1770      theme: None,
1771      use_https: false, // To match macOS & Linux behavior in the context of mixed content.
1772      scroll_bar_style: ScrollBarStyle::default(),
1773      browser_extensions_enabled: false,
1774      extension_path: None,
1775      environment: None,
1776      profile_name: None,
1777    }
1778  }
1779}
1780
1781#[cfg(windows)]
1782pub trait WebViewBuilderExtWindows {
1783  /// Pass additional args to WebView2 upon creating the webview.
1784  ///
1785  /// ## Warning
1786  ///
1787  /// - Webview instances with different browser arguments must also have different [data directories](WebContext::new).
1788  /// - By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`
1789  ///   `--autoplay-policy=no-user-gesture-required` if autoplay is enabled
1790  ///   and `--proxy-server=<scheme>://<host>:<port>` if a proxy is set.
1791  ///   so if you use this method, you have to add these arguments yourself if you want to keep the same behavior.
1792  fn with_additional_browser_args<S: Into<String>>(self, additional_args: S) -> Self;
1793
1794  /// Determines whether browser-specific accelerator keys are enabled. When this setting is set to
1795  /// `false`, it disables all accelerator keys that access features specific to a web browser.
1796  /// The default value is `true`. See the following link to know more details.
1797  ///
1798  /// Setting to `false` does nothing on WebView2 Runtime version before 92.0.902.0,
1799  /// see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10824-prerelease>
1800  ///
1801  /// <https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings#arebrowseracceleratorkeysenabled>
1802  fn with_browser_accelerator_keys(self, enabled: bool) -> Self;
1803
1804  /// Determines whether the webview's default context menus are enabled. When this setting is set to `false`,
1805  /// it disables all context menus on the webview - menus on the window's native decorations for example are not affected.
1806  ///
1807  /// The default value is `true` (context menus are enabled).
1808  ///
1809  /// <https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings#aredefaultcontextmenusenabled>
1810  fn with_default_context_menus(self, enabled: bool) -> Self;
1811
1812  /// Specifies the theme of webview2. This affects things like `prefers-color-scheme`.
1813  ///
1814  /// Defaults to [`Theme::Auto`] which will follow the OS defaults.
1815  ///
1816  /// Requires WebView2 Runtime version 101.0.1210.39 or higher, does nothing on older versions,
1817  /// see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10121039>
1818  fn with_theme(self, theme: Theme) -> Self;
1819
1820  /// Determines whether the custom protocols should use `https://<scheme>.path/to/page` instead of the default `http://<scheme>.path/to/page`.
1821  ///
1822  /// Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints
1823  /// and is therefore less secure but will match the behavior of the `<scheme>://path/to/page` protocols used on macOS and Linux.
1824  ///
1825  /// The default value is `false`.
1826  fn with_https_scheme(self, enabled: bool) -> Self;
1827
1828  /// Specifies the native scrollbar style to use with webview2.
1829  /// CSS styles that modify the scrollbar are applied on top of the native appearance configured here.
1830  ///
1831  /// Defaults to [`ScrollBarStyle::Default`] which is the browser default used by Microsoft Edge.
1832  ///
1833  /// Requires WebView2 Runtime version 125.0.2535.41 or higher, does nothing on older versions,
1834  /// see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/?tabs=dotnetcsharp#10253541>
1835  ///
1836  /// ## Warning
1837  ///
1838  /// Webview instances with different scroll bar styles must also have different [data directories](WebContext::new).
1839  fn with_scroll_bar_style(self, style: ScrollBarStyle) -> Self;
1840
1841  /// Determines whether the ability to install and enable extensions is enabled.
1842  ///
1843  /// By default, extensions are disabled.
1844  ///
1845  /// Requires WebView2 Runtime version 120.0.2210.55 or higher, does nothing on older versions,
1846  /// see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10221055>
1847  ///
1848  /// ## Warning
1849  ///
1850  /// Webview instances with different browser extensions enabled settings must also have different [data directories](WebContext::new).
1851  fn with_browser_extensions_enabled(self, enabled: bool) -> Self;
1852
1853  /// Set the path from which to load extensions from. Extensions stored in this path should be unpacked.
1854  ///
1855  /// Does nothing if browser extensions are disabled. See [`with_browser_extensions_enabled`](Self::with_browser_extensions_enabled)
1856  fn with_extensions_path(self, path: impl Into<PathBuf>) -> Self;
1857
1858  /// Set the environment for the webview.
1859  /// Useful if you need to share the same environment, for instance when using the [`WebViewBuilder::with_new_window_req_handler`].
1860  fn with_environment(self, environment: ICoreWebView2Environment) -> Self;
1861
1862  /// Set the WebView2 profile name for this webview. Webviews with different
1863  /// profile names within the same environment have isolated cookies, storage,
1864  /// IndexedDB, cache, and other site data, while sharing the runtime.
1865  ///
1866  /// When `None` (the default), the webview uses the unnamed default profile.
1867  ///
1868  /// See <https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/multi-profile-support>
1869  /// for the underlying WebView2 multi-profile feature.
1870  ///
1871  /// Profile names must follow the WebView2 naming rules (alphanumeric, `.`,
1872  /// `_`, `-`, ` `, up to 64 chars, not starting/ending with `.` or ` `).
1873  fn with_profile_name<S: Into<String>>(self, name: S) -> Self;
1874}
1875
1876#[cfg(windows)]
1877impl WebViewBuilderExtWindows for WebViewBuilder<'_> {
1878  fn with_additional_browser_args<S: Into<String>>(mut self, additional_args: S) -> Self {
1879    self.platform_specific.additional_browser_args = Some(additional_args.into());
1880    self
1881  }
1882
1883  fn with_browser_accelerator_keys(mut self, enabled: bool) -> Self {
1884    self.platform_specific.browser_accelerator_keys = enabled;
1885    self
1886  }
1887
1888  fn with_default_context_menus(mut self, enabled: bool) -> Self {
1889    self.platform_specific.default_context_menus = enabled;
1890    self
1891  }
1892
1893  fn with_theme(mut self, theme: Theme) -> Self {
1894    self.platform_specific.theme = Some(theme);
1895    self
1896  }
1897
1898  fn with_https_scheme(mut self, enabled: bool) -> Self {
1899    self.platform_specific.use_https = enabled;
1900    self
1901  }
1902
1903  fn with_scroll_bar_style(mut self, style: ScrollBarStyle) -> Self {
1904    self.platform_specific.scroll_bar_style = style;
1905    self
1906  }
1907
1908  fn with_browser_extensions_enabled(mut self, enabled: bool) -> Self {
1909    self.platform_specific.browser_extensions_enabled = enabled;
1910    self
1911  }
1912
1913  fn with_extensions_path(mut self, path: impl Into<PathBuf>) -> Self {
1914    self.platform_specific.extension_path = Some(path.into());
1915    self
1916  }
1917
1918  fn with_environment(mut self, environment: ICoreWebView2Environment) -> Self {
1919    self.platform_specific.environment.replace(environment);
1920    self
1921  }
1922
1923  fn with_profile_name<S: Into<String>>(mut self, name: S) -> Self {
1924    self.platform_specific.profile_name = Some(name.into());
1925    self
1926  }
1927}
1928
1929#[cfg(target_os = "android")]
1930#[derive(Default)]
1931pub(crate) struct PlatformSpecificWebViewAttributes {
1932  on_webview_created: Option<
1933    std::sync::Arc<
1934      dyn Fn(prelude::Context) -> std::result::Result<(), jni::errors::Error>
1935        + Send
1936        + Sync
1937        + 'static,
1938    >,
1939  >,
1940  asset_loader_domain: Option<String>,
1941  https_scheme: bool,
1942}
1943
1944#[cfg(target_os = "android")]
1945pub trait WebViewBuilderExtAndroid {
1946  fn on_webview_created<
1947    F: Fn(prelude::Context<'_, '_>) -> std::result::Result<(), jni::errors::Error>
1948      + Send
1949      + Sync
1950      + 'static,
1951  >(
1952    self,
1953    f: F,
1954  ) -> Self;
1955
1956  /// Use [WebViewAssetLoader](https://developer.android.com/reference/kotlin/androidx/webkit/WebViewAssetLoader)
1957  /// to load assets from Android's `asset` folder when using `with_url` as `<protocol>://assets/` (e.g.:
1958  /// `wry://assets/index.html`). Note that this registers a custom protocol with the provided
1959  /// String, similar to [`with_custom_protocol`], but also sets the WebViewAssetLoader with the
1960  /// necessary domain (which is fixed as `<protocol>.assets`). This cannot be used in conjunction
1961  /// to `with_custom_protocol` for Android, as it changes the way in which requests are handled.
1962  fn with_asset_loader(self, protocol: String) -> Self;
1963
1964  /// Determines whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost`.
1965  ///
1966  /// Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints
1967  /// and is therefore less secure but will match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
1968  ///
1969  /// The default value is `false`.
1970  fn with_https_scheme(self, enabled: bool) -> Self;
1971}
1972
1973#[cfg(target_os = "android")]
1974impl WebViewBuilderExtAndroid for WebViewBuilder<'_> {
1975  fn on_webview_created<
1976    F: Fn(prelude::Context<'_, '_>) -> std::result::Result<(), jni::errors::Error>
1977      + Send
1978      + Sync
1979      + 'static,
1980  >(
1981    mut self,
1982    f: F,
1983  ) -> Self {
1984    self.platform_specific.on_webview_created = Some(std::sync::Arc::new(f));
1985    self
1986  }
1987
1988  fn with_asset_loader(mut self, protocol: String) -> Self {
1989    // register custom protocol with empty Response return,
1990    // this is necessary due to the need of fixing a domain
1991    // in WebViewAssetLoader.
1992    self.attrs.custom_protocols.insert(
1993      protocol.clone(),
1994      Box::new(|_, _, api| {
1995        api.respond(Response::builder().body(Vec::new()).unwrap());
1996      }),
1997    );
1998    self.platform_specific.asset_loader_domain = Some(format!("{protocol}.assets"));
1999    self
2000  }
2001
2002  fn with_https_scheme(mut self, enabled: bool) -> Self {
2003    self.platform_specific.https_scheme = enabled;
2004    self
2005  }
2006}
2007
2008#[cfg(any(
2009  target_os = "linux",
2010  target_os = "dragonfly",
2011  target_os = "freebsd",
2012  target_os = "netbsd",
2013  target_os = "openbsd",
2014))]
2015#[derive(Default)]
2016pub(crate) struct PlatformSpecificWebViewAttributes {
2017  extension_path: Option<PathBuf>,
2018  related_view: Option<webkit2gtk::WebView>,
2019}
2020
2021#[cfg(any(
2022  target_os = "linux",
2023  target_os = "dragonfly",
2024  target_os = "freebsd",
2025  target_os = "netbsd",
2026  target_os = "openbsd",
2027))]
2028pub trait WebViewBuilderExtUnix<'a> {
2029  /// Consume the builder and create the webview inside a GTK container widget, such as GTK window.
2030  ///
2031  /// - If the container is [`gtk::Box`], it is added using [`Box::pack_start(webview, true, true, 0)`](gtk::prelude::BoxExt::pack_start).
2032  /// - If the container is [`gtk::Fixed`], its [size request](gtk::prelude::WidgetExt::set_size_request) will be set using the (width, height) bounds passed in
2033  ///   and will be added to the container using [`Fixed::put`](gtk::prelude::FixedExt::put) using the (x, y) bounds passed in.
2034  /// - For all other containers, it will be added using [`gtk::prelude::ContainerExt::add`]
2035  ///
2036  /// # Panics:
2037  ///
2038  /// - Panics if [`gtk::init`] was not called in this thread.
2039  fn build_gtk<W>(self, widget: &'a W) -> Result<WebView>
2040  where
2041    W: gtk::prelude::IsA<gtk::Container>;
2042
2043  /// Set the path from which to load extensions from.
2044  fn with_extensions_path(self, path: impl Into<PathBuf>) -> Self;
2045
2046  /// Creates a new webview sharing the same web process with the provided webview.
2047  /// Useful if you need to link a webview to another, for instance when using the [`WebViewBuilder::with_new_window_req_handler`].
2048  fn with_related_view(self, webview: webkit2gtk::WebView) -> Self;
2049}
2050
2051#[cfg(any(
2052  target_os = "linux",
2053  target_os = "dragonfly",
2054  target_os = "freebsd",
2055  target_os = "netbsd",
2056  target_os = "openbsd",
2057))]
2058impl<'a> WebViewBuilderExtUnix<'a> for WebViewBuilder<'a> {
2059  fn build_gtk<W>(self, widget: &'a W) -> Result<WebView>
2060  where
2061    W: gtk::prelude::IsA<gtk::Container>,
2062  {
2063    self.error?;
2064
2065    InnerWebView::new_gtk(widget, self.attrs, self.platform_specific)
2066      .map(|webview| WebView { webview })
2067  }
2068
2069  fn with_extensions_path(mut self, path: impl Into<PathBuf>) -> Self {
2070    self.platform_specific.extension_path = Some(path.into());
2071    self
2072  }
2073
2074  fn with_related_view(mut self, webview: webkit2gtk::WebView) -> Self {
2075    self.platform_specific.related_view.replace(webview);
2076    self
2077  }
2078}
2079
2080/// The fundamental type to present a [`WebView`].
2081///
2082/// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and
2083/// scripts for those who prefer to control fine grained window creation and event handling.
2084/// [`WebView`] presents the actual WebView window and let you still able to perform actions on it.
2085pub struct WebView {
2086  webview: InnerWebView,
2087}
2088
2089impl WebView {
2090  /// Returns the id of this webview.
2091  pub fn id(&self) -> WebViewId<'_> {
2092    self.webview.id()
2093  }
2094
2095  /// Get the current url of the webview
2096  pub fn url(&self) -> Result<String> {
2097    self.webview.url()
2098  }
2099
2100  /// Evaluate and run javascript code.
2101  pub fn evaluate_script(&self, js: &str) -> Result<()> {
2102    self
2103      .webview
2104      .eval(js, None::<Box<dyn Fn(String) + Send + 'static>>)
2105  }
2106
2107  /// Evaluate and run javascript code with callback function. The evaluation result will be
2108  /// serialized into a JSON string and passed to the callback function.
2109  ///
2110  /// Exception is ignored because of the limitation on windows. You can catch it yourself and return as string as a workaround.
2111  pub fn evaluate_script_with_callback(
2112    &self,
2113    js: &str,
2114    callback: impl Fn(String) + Send + 'static,
2115  ) -> Result<()> {
2116    self.webview.eval(js, Some(callback))
2117  }
2118
2119  /// Launch print modal for the webview content.
2120  pub fn print(&self) -> Result<()> {
2121    self.webview.print()
2122  }
2123
2124  /// Get a list of cookies for specific url.
2125  pub fn cookies_for_url(&self, url: &str) -> Result<Vec<cookie::Cookie<'static>>> {
2126    self.webview.cookies_for_url(url)
2127  }
2128
2129  /// Get the list of cookies.
2130  ///
2131  /// ## Platform-specific
2132  ///
2133  /// - **Android**: Unsupported, always returns an empty [`Vec`].
2134  pub fn cookies(&self) -> Result<Vec<cookie::Cookie<'static>>> {
2135    self.webview.cookies()
2136  }
2137
2138  /// Set a cookie for the webview.
2139  ///
2140  /// ## Platform-specific
2141  ///
2142  /// - **Android**: Not supported.
2143  pub fn set_cookie(&self, cookie: &cookie::Cookie<'_>) -> Result<()> {
2144    self.webview.set_cookie(cookie)
2145  }
2146
2147  /// Delete a cookie for the webview.
2148  ///
2149  /// ## Platform-specific
2150  ///
2151  /// - **Android**: Not supported.
2152  pub fn delete_cookie(&self, cookie: &cookie::Cookie<'_>) -> Result<()> {
2153    self.webview.delete_cookie(cookie)
2154  }
2155
2156  /// Open the web inspector which is usually called dev tool.
2157  ///
2158  /// ## Platform-specific
2159  ///
2160  /// - **Android / iOS:** Not supported.
2161  #[cfg(any(debug_assertions, feature = "devtools"))]
2162  pub fn open_devtools(&self) {
2163    self.webview.open_devtools()
2164  }
2165
2166  /// Close the web inspector which is usually called dev tool.
2167  ///
2168  /// ## Platform-specific
2169  ///
2170  /// - **Windows / Android / iOS:** Not supported.
2171  #[cfg(any(debug_assertions, feature = "devtools"))]
2172  pub fn close_devtools(&self) {
2173    self.webview.close_devtools()
2174  }
2175
2176  /// Gets the devtool window's current visibility state.
2177  ///
2178  /// ## Platform-specific
2179  ///
2180  /// - **Windows / Android / iOS:** Not supported.
2181  #[cfg(any(debug_assertions, feature = "devtools"))]
2182  pub fn is_devtools_open(&self) -> bool {
2183    self.webview.is_devtools_open()
2184  }
2185
2186  /// Set the webview zoom level
2187  ///
2188  /// ## Platform-specific:
2189  ///
2190  /// - **Android**: Not supported.
2191  /// - **macOS**: available on macOS 11+ only.
2192  /// - **iOS**: available on iOS 14+ only.
2193  pub fn zoom(&self, scale_factor: f64) -> Result<()> {
2194    self.webview.zoom(scale_factor)
2195  }
2196
2197  /// Specify the webview background color.
2198  ///
2199  /// The color uses the RGBA format.
2200  ///
2201  /// ## Platform-specific:
2202  ///
2203  /// - **macOS**: Disables the default white WKWebView background via the `drawsBackground` KVC key
2204  ///   (same as the `transparent` feature) and sets `underPageBackgroundColor` (macOS 12+) for overscroll areas.
2205  /// - **Windows**:
2206  ///   - On Windows 7, transparency is not supported and the alpha value will be ignored.
2207  ///   - On Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255`
2208  pub fn set_background_color(&self, background_color: RGBA) -> Result<()> {
2209    self.webview.set_background_color(background_color)
2210  }
2211
2212  /// Navigate to the specified url
2213  pub fn load_url(&self, url: &str) -> Result<()> {
2214    self.webview.load_url(url)
2215  }
2216
2217  /// Reloads the current page.
2218  pub fn reload(&self) -> crate::Result<()> {
2219    self.webview.reload()
2220  }
2221
2222  /// Go to the next page.
2223  pub fn go_forward(&self) -> Result<()> {
2224    self.webview.go_forward()
2225  }
2226
2227  /// Go to the previous page.
2228  pub fn go_back(&self) -> Result<()> {
2229    self.webview.go_back()
2230  }
2231
2232  pub fn can_go_forward(&self) -> Result<bool> {
2233    self.webview.can_go_forward()
2234  }
2235
2236  pub fn can_go_back(&self) -> Result<bool> {
2237    self.webview.can_go_back()
2238  }
2239
2240  /// Navigate to the specified url using the specified headers
2241  pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) -> Result<()> {
2242    self.webview.load_url_with_headers(url, headers)
2243  }
2244
2245  /// Load html content into the webview
2246  pub fn load_html(&self, html: &str) -> Result<()> {
2247    self.webview.load_html(html)
2248  }
2249
2250  /// Clear all browsing data
2251  pub fn clear_all_browsing_data(&self) -> Result<()> {
2252    self.webview.clear_all_browsing_data()
2253  }
2254
2255  pub fn bounds(&self) -> Result<Rect> {
2256    self.webview.bounds()
2257  }
2258
2259  /// Set the webview bounds.
2260  ///
2261  /// This is only effective if the webview was created as a child
2262  /// or created using [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`].
2263  pub fn set_bounds(&self, bounds: Rect) -> Result<()> {
2264    self.webview.set_bounds(bounds)
2265  }
2266
2267  /// Shows or hides the webview.
2268  pub fn set_visible(&self, visible: bool) -> Result<()> {
2269    self.webview.set_visible(visible)
2270  }
2271
2272  /// Try moving focus to the webview.
2273  pub fn focus(&self) -> Result<()> {
2274    self.webview.focus()
2275  }
2276
2277  /// Try moving focus away from the webview back to the parent window.
2278  ///
2279  /// ## Platform-specific:
2280  ///
2281  /// - **Android**: Not implemented.
2282  pub fn focus_parent(&self) -> Result<()> {
2283    self.webview.focus_parent()
2284  }
2285}
2286
2287/// An event describing drag and drop operations on the webview.
2288#[non_exhaustive]
2289#[derive(Debug, Clone)]
2290pub enum DragDropEvent {
2291  /// A drag operation has entered the webview.
2292  Enter {
2293    /// List of paths that are being dragged onto the webview.
2294    paths: Vec<PathBuf>,
2295    /// Position of the drag operation, relative to the webview top-left corner.
2296    position: (i32, i32),
2297  },
2298  /// A drag operation is moving over the window.
2299  Over {
2300    /// Position of the drag operation, relative to the webview top-left corner.
2301    position: (i32, i32),
2302  },
2303  /// The file(s) have been dropped onto the window.
2304  Drop {
2305    /// List of paths that are being dropped onto the window.
2306    paths: Vec<PathBuf>,
2307    /// Position of the drag operation, relative to the webview top-left corner.
2308    position: (i32, i32),
2309  },
2310  /// The drag operation has been cancelled or left the window.
2311  Leave,
2312}
2313
2314/// Get WebView/Webkit version on current platform.
2315#[cfg(feature = "os-webview")]
2316#[cfg_attr(docsrs, doc(cfg(feature = "os-webview")))]
2317pub fn webview_version() -> Result<String> {
2318  platform_webview_version()
2319}
2320
2321/// The [memory usage target level][1]. There are two levels 'Low' and 'Normal' and the default
2322/// level is 'Normal'. When the application is going inactive, setting the level to 'Low' can
2323/// significantly reduce the application's memory consumption.
2324///
2325/// [1]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2memoryusagetargetlevel
2326#[cfg(target_os = "windows")]
2327#[non_exhaustive]
2328#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
2329pub enum MemoryUsageLevel {
2330  /// The 'Normal' memory usage. Applications should set this level when they are becoming active.
2331  #[default]
2332  Normal,
2333  /// The 'Low' memory usage. Applications can reduce memory comsumption by setting this level when
2334  /// they are becoming inactive.
2335  Low,
2336}
2337
2338/// Additional methods on `WebView` that are specific to Windows.
2339#[cfg(target_os = "windows")]
2340pub trait WebViewExtWindows {
2341  /// Returns the WebView2 controller.
2342  fn controller(&self) -> ICoreWebView2Controller;
2343
2344  /// Webview environment.
2345  fn environment(&self) -> ICoreWebView2Environment;
2346
2347  /// Webview instance.
2348  fn webview(&self) -> ICoreWebView2;
2349
2350  /// Changes the webview2 theme.
2351  ///
2352  /// Requires WebView2 Runtime version 101.0.1210.39 or higher, returns error on older versions,
2353  /// see <https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/archive?tabs=dotnetcsharp#10121039>
2354  fn set_theme(&self, theme: Theme) -> Result<()>;
2355
2356  /// Sets the [memory usage target level][1].
2357  ///
2358  /// When to best use this mode depends on the app in question. Most commonly it's called when
2359  /// the app's visiblity state changes.
2360  ///
2361  /// Please read the [guide for WebView2][2] for more details.
2362  ///
2363  /// This method uses a WebView2 API added in Runtime version 114.0.1823.32. When it is used in
2364  /// an older Runtime version, it does nothing.
2365  ///
2366  /// [1]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2memoryusagetargetlevel
2367  /// [2]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.memoryusagetargetlevel?view=webview2-dotnet-1.0.2088.41#remarks
2368  fn set_memory_usage_level(&self, level: MemoryUsageLevel) -> Result<()>;
2369
2370  /// Attaches this webview to the given HWND and removes it from the current one.
2371  fn reparent(&self, hwnd: isize) -> Result<()>;
2372
2373  /// Returns the child HWND hosting this webview.
2374  fn hwnd(&self) -> windows::Win32::Foundation::HWND;
2375}
2376
2377#[cfg(target_os = "windows")]
2378impl WebViewExtWindows for WebView {
2379  fn controller(&self) -> ICoreWebView2Controller {
2380    self.webview.controller.clone()
2381  }
2382
2383  fn environment(&self) -> ICoreWebView2Environment {
2384    self.webview.env.clone()
2385  }
2386
2387  fn webview(&self) -> ICoreWebView2 {
2388    self.webview.webview.clone()
2389  }
2390
2391  fn set_theme(&self, theme: Theme) -> Result<()> {
2392    self.webview.set_theme(theme)
2393  }
2394
2395  fn set_memory_usage_level(&self, level: MemoryUsageLevel) -> Result<()> {
2396    self.webview.set_memory_usage_level(level)
2397  }
2398
2399  fn reparent(&self, hwnd: isize) -> Result<()> {
2400    self.webview.reparent(hwnd)
2401  }
2402
2403  /// Returns the child HWND hosting this webview.
2404  fn hwnd(&self) -> windows::Win32::Foundation::HWND {
2405    self.webview.hwnd()
2406  }
2407}
2408
2409/// Additional methods on `WebView` that are specific to Linux.
2410#[cfg(gtk)]
2411pub trait WebViewExtUnix: Sized {
2412  /// Create the webview inside a GTK container widget, such as GTK window.
2413  ///
2414  /// - If the container is [`gtk::Box`], it is added using [`Box::pack_start(webview, true, true, 0)`](gtk::prelude::BoxExt::pack_start).
2415  /// - If the container is [`gtk::Fixed`], its [size request](gtk::prelude::WidgetExt::set_size_request) will be set using the (width, height) bounds passed in
2416  ///   and will be added to the container using [`Fixed::put`](gtk::prelude::FixedExt::put) using the (x, y) bounds passed in.
2417  /// - For all other containers, it will be added using [`gtk::prelude::ContainerExt::add`]
2418  ///
2419  /// # Panics:
2420  ///
2421  /// - Panics if [`gtk::init`] was not called in this thread.
2422  fn new_gtk<W>(widget: &W) -> Result<Self>
2423  where
2424    W: gtk::prelude::IsA<gtk::Container>;
2425
2426  /// Returns Webkit2gtk Webview handle
2427  fn webview(&self) -> webkit2gtk::WebView;
2428
2429  /// Attaches this webview to the given Widget and removes it from the current one.
2430  fn reparent<W>(&self, widget: &W) -> Result<()>
2431  where
2432    W: gtk::prelude::IsA<gtk::Container>;
2433}
2434
2435#[cfg(gtk)]
2436impl WebViewExtUnix for WebView {
2437  fn new_gtk<W>(widget: &W) -> Result<Self>
2438  where
2439    W: gtk::prelude::IsA<gtk::Container>,
2440  {
2441    WebViewBuilder::new().build_gtk(widget)
2442  }
2443
2444  fn webview(&self) -> webkit2gtk::WebView {
2445    self.webview.webview.clone()
2446  }
2447
2448  fn reparent<W>(&self, widget: &W) -> Result<()>
2449  where
2450    W: gtk::prelude::IsA<gtk::Container>,
2451  {
2452    self.webview.reparent(widget)
2453  }
2454}
2455
2456/// Additional methods on `WebView` that are specific to macOS or iOS.
2457#[cfg(any(target_os = "macos", target_os = "ios"))]
2458pub trait WebViewExtDarwin {
2459  /// Prints with extra options
2460  fn print_with_options(&self, options: &PrintOptions) -> Result<()>;
2461  /// Fetches all Data Store Identifiers of this application
2462  ///
2463  /// Needs to run on main thread and needs an event loop to run.
2464  fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(cb: F) -> Result<()>;
2465  /// Deletes a Data Store by an identifier.
2466  ///
2467  /// You must drop any WebView instances using the data store before you call this method.
2468  ///
2469  /// Needs to run on main thread and needs an event loop to run.
2470  fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(uuid: &[u8; 16], cb: F);
2471}
2472
2473#[cfg(any(target_os = "macos", target_os = "ios"))]
2474impl WebViewExtDarwin for WebView {
2475  fn print_with_options(&self, options: &PrintOptions) -> Result<()> {
2476    self.webview.print_with_options(options)
2477  }
2478
2479  fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(cb: F) -> Result<()> {
2480    wkwebview::InnerWebView::fetch_data_store_identifiers(cb)
2481  }
2482
2483  fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(uuid: &[u8; 16], cb: F) {
2484    wkwebview::InnerWebView::remove_data_store(uuid, cb)
2485  }
2486}
2487
2488/// Additional methods on `WebView` that are specific to macOS.
2489#[cfg(target_os = "macos")]
2490pub trait WebViewExtMacOS {
2491  /// Returns WKWebView handle
2492  fn webview(&self) -> Retained<WryWebView>;
2493  /// Returns WKWebView manager [(userContentController)](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler/1396222-usercontentcontroller) handle
2494  fn manager(&self) -> Retained<WKUserContentController>;
2495  /// Returns NSWindow associated with the WKWebView webview
2496  fn ns_window(&self) -> Retained<NSWindow>;
2497  /// Attaches this webview to the given NSWindow and removes it from the current one.
2498  fn reparent(&self, window: *mut NSWindow) -> Result<()>;
2499  /// Prints with extra options
2500  fn print_with_options(&self, options: &PrintOptions) -> Result<()>;
2501  /// Move the window controls to the specified position.
2502  /// Normally this is handled by the Window but because `WebViewBuilder::build()` overwrites the window's NSView the controls will flicker on resizing.
2503  /// Note: This method has no effects if the WebView is injected via `WebViewBuilder::build_as_child();` and there should be no flickers.
2504  /// Warning: Do not use this if your chosen window library does not support traffic light insets.
2505  /// Warning: Only use this in **decorated** windows with a **hidden titlebar**!
2506  fn set_traffic_light_inset<P: Into<dpi::Position>>(&self, position: P) -> Result<()>;
2507}
2508
2509#[cfg(target_os = "macos")]
2510impl WebViewExtMacOS for WebView {
2511  fn webview(&self) -> Retained<WryWebView> {
2512    self.webview.webview.clone()
2513  }
2514
2515  fn manager(&self) -> Retained<WKUserContentController> {
2516    self.webview.manager.clone()
2517  }
2518
2519  fn ns_window(&self) -> Retained<NSWindow> {
2520    self.webview.webview.window().unwrap()
2521  }
2522
2523  fn reparent(&self, window: *mut NSWindow) -> Result<()> {
2524    self.webview.reparent(window)
2525  }
2526
2527  fn print_with_options(&self, options: &PrintOptions) -> Result<()> {
2528    self.webview.print_with_options(options)
2529  }
2530
2531  fn set_traffic_light_inset<P: Into<dpi::Position>>(&self, position: P) -> Result<()> {
2532    self.webview.set_traffic_light_inset(position.into())
2533  }
2534}
2535
2536/// Additional methods on `WebView` that are specific to iOS.
2537#[cfg(target_os = "ios")]
2538pub trait WebViewExtIOS {
2539  /// Returns WKWebView handle
2540  fn webview(&self) -> Retained<WryWebView>;
2541  /// Returns WKWebView manager [(userContentController)](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler/1396222-usercontentcontroller) handle
2542  fn manager(&self) -> Retained<WKUserContentController>;
2543}
2544
2545#[cfg(target_os = "ios")]
2546impl WebViewExtIOS for WebView {
2547  fn webview(&self) -> Retained<WryWebView> {
2548    self.webview.webview.clone()
2549  }
2550
2551  fn manager(&self) -> Retained<WKUserContentController> {
2552    self.webview.manager.clone()
2553  }
2554}
2555
2556#[cfg(target_os = "android")]
2557/// Additional methods on `WebView` that are specific to Android
2558pub trait WebViewExtAndroid {
2559  fn handle(&self) -> JniHandle;
2560}
2561
2562#[cfg(target_os = "android")]
2563impl WebViewExtAndroid for WebView {
2564  fn handle(&self) -> JniHandle {
2565    JniHandle {
2566      activity_id: self.webview.activity_id,
2567    }
2568  }
2569}
2570
2571/// WebView theme.
2572#[derive(Debug, Clone, Copy)]
2573pub enum Theme {
2574  /// Dark
2575  Dark,
2576  /// Light
2577  Light,
2578  /// System preference
2579  Auto,
2580}
2581
2582/// Type alias for a color in the RGBA format.
2583///
2584/// Each value can be 0..255 inclusive.
2585pub type RGBA = (u8, u8, u8, u8);
2586
2587/// Type of of page loading event
2588pub enum PageLoadEvent {
2589  /// Indicates that the content of the page has started loading
2590  Started,
2591  /// Indicates that the page content has finished loading
2592  Finished,
2593}
2594
2595/// Background throttling policy
2596#[derive(Debug, Clone)]
2597pub enum BackgroundThrottlingPolicy {
2598  /// A policy where background throttling is disabled
2599  Disabled,
2600  /// A policy where a web view that's not in a window fully suspends tasks.
2601  Suspend,
2602  /// A policy where a web view that's not in a window limits processing, but does not fully suspend tasks.
2603  Throttle,
2604}
2605
2606/// An initialization script
2607#[derive(Debug, Clone)]
2608pub struct InitializationScript {
2609  /// The script to run
2610  pub script: String,
2611  /// Whether the script should be injected to main frame only.
2612  ///
2613  /// When set to false, the script is also injected to subframes.
2614  ///
2615  /// ## Platform-specific
2616  ///
2617  /// - **Windows**: scripts are always injected into subframes regardless of this option.
2618  ///   This will be the case until Webview2 implements a proper API to inject a script only on the main frame.
2619  /// - **Android**: When [addDocumentStartJavaScript] is not supported, scripts are always injected into main frame only.
2620  ///
2621  /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
2622  pub for_main_frame_only: bool,
2623}
2624
2625#[cfg(test)]
2626mod tests {
2627  use super::*;
2628
2629  #[test]
2630  #[cfg_attr(miri, ignore)]
2631  fn should_get_webview_version() {
2632    if let Err(error) = webview_version() {
2633      panic!("{}", error);
2634    }
2635  }
2636}