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