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