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