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