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