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