wvwasi_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//! Wry is a Cross-platform WebView rendering library.
6//!
7//! The webview requires a running event loop and a window type that implements [`HasRawWindowHandle`],
8//! or a gtk container widget if you need to support X11 and Wayland.
9//! You can use a windowing library like [`tao`] or [`winit`].
10//!
11//! ## Examples
12//!
13//! This example leverages the [`HasRawWindowHandle`] and supports Windows, macOS, iOS, Android and Linux (X11 Only)
14//!
15//! ```no_run
16//! use wry::{WebViewBuilder, raw_window_handle};
17//!
18//! # struct T;
19//! # unsafe impl raw_window_handle::HasRawWindowHandle for T {
20//! # fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
21//! # raw_window_handle::RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::empty())
22//! # }
23//! # }
24//! # let window = T;
25//! let webview = WebViewBuilder::new(&window)
26//! .with_url("https://tauri.app")
27//! .unwrap()
28//! .build()
29//! .unwrap();
30//! ```
31//!
32//! If you also want to support Wayland too, then we recommend you use [`WebViewBuilderExtUnix::new_gtk`] on Linux.
33//!
34//! ```no_run,ignore
35//! use wry::WebViewBuilder;
36//!
37//! #[cfg(any(
38//! target_os = "windows",
39//! target_os = "macos",
40//! target_os = "ios",
41//! target_os = "android"
42//! ))]
43//! let builder = WebViewBuilder::new(&window);
44//! #[cfg(not(any(
45//! target_os = "windows",
46//! target_os = "macos",
47//! target_os = "ios",
48//! target_os = "android"
49//! )))]
50//! let builder = {
51//! use tao::platform::unix::WindowExtUnix;
52//! use wry::WebViewBuilderExtUnix;
53//! WebViewBuilder::new_gtk(&window.gtk_window())
54//! };
55//!
56//! let webview = builder
57//! .with_url("https://tauri.app")
58//! .unwrap()
59//! .build()
60//! .unwrap();
61//! ```
62//!
63//! ## Child webviews
64//!
65//! You can use [`WebView::new_as_child`] to create the webview as a child inside another window. This is supported on
66//! macOS, Windows and Linux (X11 Only).
67//!
68//! ```no_run
69//! use wry::{WebViewBuilder, raw_window_handle};
70//!
71//! # struct T;
72//! # unsafe impl raw_window_handle::HasRawWindowHandle for T {
73//! # fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
74//! # raw_window_handle::RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::empty())
75//! # }
76//! # }
77//! # let window = T;
78//! let webview = WebViewBuilder::new_as_child(&window)
79//! .with_url("https://tauri.app")
80//! .unwrap()
81//! .build()
82//! .unwrap();
83//! ```
84//!
85//! ## Platform Considerations
86//!
87//! Note that on Linux, we use webkit2gtk webviews so if the windowing library doesn't support gtk (as in [`winit`])
88//! you'll need to call [`gtk::init`] before creating the webview and then call [`gtk::main_iteration_do`] alongside
89//! your windowing library event loop.
90//!
91//! ```no_run,ignore
92//! use winit::{event_loop::EventLoop, window::Window};
93//! use wry::WebView;
94//!
95//! fn main() {
96//! let event_loop = EventLoop::new().unwrap();
97//! gtk::init().unwrap(); // <----- IMPORTANT
98//! let window = Window::new(&event_loop).unwrap();
99//! let webview = WebView::new(&window);
100//! event_loop.run(|_e, _evl|{
101//! // process winit events
102//!
103//! // then advance gtk event loop <----- IMPORTANT
104//! while gtk::events_pending() {
105//! gtk::main_iteration_do(false);
106//! }
107//! }).unwrap();
108//! }
109//! ```
110//!
111//! ## Android
112//!
113//! In order for `wry` to be able to create webviews on Android, there is a few requirements that your application needs to uphold:
114//! 1. You need to set a few environment variables that will be used to generate the necessary kotlin
115//! files that you need to include in your Android application for wry to function properly.
116//! - `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`
117//! - `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`
118//! - `WRY_ANDROID_KOTLIN_FILES_OUT_DIR`: for example, `path/to/app/src/main/kotlin/com/wry/example`
119//! 2. Your main Android Activity needs to inherit `AppCompatActivity`, preferably it should use the generated `WryActivity` or inherit it.
120//! 3. Your Rust app needs to call `wry::android_setup` function to setup the necessary logic to be able to create webviews later on.
121//! 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.
122//!
123//! It is recommended to use [`tao`](https://docs.rs/tao/latest/tao/) crate as it provides maximum compatibility with `wry`
124//!
125//! ```
126//! #[cfg(target_os = "android")]
127//! {
128//! tao::android_binding!(
129//! com_example,
130//! wry_app,
131//! WryActivity,
132//! wry::android_setup, // pass the wry::android_setup function to tao which will invoke when the event loop is created
133//! _start_app
134//! );
135//! wry::android_binding!(com_example, ttt);
136//! }
137//! ```
138//!
139//! If this feels overwhelming, you can just use the preconfigured template from [`cargo-mobile2`](https://github.com/tauri-apps/cargo-mobile2).
140//!
141//! For more inforamtion, checkout [MOBILE.md](https://github.com/tauri-apps/wry/blob/dev/MOBILE.md).
142//!
143//! ## Feature flags
144//!
145//! Wry uses a set of feature flags to toggle several advanced features.
146//!
147//! - `os-webview` (default): Enables the default WebView framework on the platform. This must be enabled
148//! for the crate to work. This feature was added in preparation of other ports like cef and servo.
149//! - `protocol` (default): Enables [`WebViewBuilder::with_custom_protocol`] to define custom URL scheme for handling tasks like
150//! loading assets.
151//! - `file-drop` (default): Enables [`WebViewBuilder::with_file_drop_handler`] to control the behaviour when there are files
152//! interacting with the window.
153//! - `devtools`: Enables devtools on release builds. Devtools are always enabled in debug builds.
154//! On **macOS**, enabling devtools, requires calling private apis so you should not enable this flag in release
155//! build if your app needs to publish to App Store.
156//! - `transparent`: Transparent background on **macOS** requires calling private functions.
157//! Avoid this in release build if your app needs to publish to App Store.
158//! - `fullscreen`: Fullscreen video and other media on **macOS** requires calling private functions.
159//! Avoid this in release build if your app needs to publish to App Store.
160//! libraries and prevent from building documentation on doc.rs fails.
161//! - `linux-body`: Enables body support of custom protocol request on Linux. Requires
162//! webkit2gtk v2.40 or above.
163//!
164//! [`tao`]: https://docs.rs/tao
165//! [`winit`]: https://docs.rs/winit
166
167#![allow(clippy::new_without_default)]
168#![allow(clippy::default_constructed_unit_structs)]
169#![allow(clippy::type_complexity)]
170#![cfg_attr(docsrs, feature(doc_cfg))]
171
172#[cfg(any(target_os = "macos", target_os = "ios"))]
173#[macro_use]
174extern crate objc;
175
176mod error;
177mod proxy;
178mod web_context;
179
180#[cfg(target_os = "android")]
181pub(crate) mod android;
182#[cfg(target_os = "android")]
183pub use crate::android::android_setup;
184#[cfg(target_os = "android")]
185pub mod prelude {
186 pub use crate::android::{binding::*, dispatch, find_class, Context};
187 pub use tao_macros::{android_fn, generate_package_name};
188}
189#[cfg(target_os = "android")]
190pub use android::JniHandle;
191#[cfg(target_os = "android")]
192use android::*;
193
194#[cfg(gtk)]
195pub(crate) mod webkitgtk;
196/// Re-exported [raw-window-handle](https://docs.rs/raw-window-handle/latest/raw_window_handle/) crate.
197pub use raw_window_handle;
198use raw_window_handle::HasRawWindowHandle;
199#[cfg(gtk)]
200use webkitgtk::*;
201
202#[cfg(any(target_os = "macos", target_os = "ios"))]
203pub(crate) mod wkwebview;
204#[cfg(any(target_os = "macos", target_os = "ios"))]
205use wkwebview::*;
206
207#[cfg(target_os = "windows")]
208pub(crate) mod webview2;
209#[cfg(target_os = "windows")]
210use self::webview2::*;
211#[cfg(target_os = "windows")]
212use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller;
213use std::{borrow::Cow, path::PathBuf, rc::Rc};
214
215use http::{Request, Response};
216
217pub use error::*;
218pub use http;
219pub use proxy::{ProxyConfig, ProxyEndpoint};
220pub use url::Url;
221pub use web_context::WebContext;
222
223/// A rectangular region.
224#[derive(Clone, Copy, Debug, Default)]
225pub struct Rect {
226 /// x coordinate of top left corner
227 pub x: i32,
228 /// y coordinate of top left corner
229 pub y: i32,
230 /// width
231 pub width: u32,
232 /// height
233 pub height: u32,
234}
235
236/// Resolves a custom protocol [`Request`] asynchronously.
237///
238/// See [`WebViewBuilder::with_asynchronous_custom_protocol`] for more information.
239pub struct RequestAsyncResponder {
240 pub(crate) responder: Box<dyn FnOnce(Response<Cow<'static, [u8]>>)>,
241}
242
243// SAFETY: even though the webview bindings do not indicate the responder is Send,
244// it actually is and we need it in order to let the user do the protocol computation
245// on a separate thread or async task.
246unsafe impl Send for RequestAsyncResponder {}
247
248impl RequestAsyncResponder {
249 /// Resolves the request with the given response.
250 pub fn respond<T: Into<Cow<'static, [u8]>>>(self, response: Response<T>) {
251 let (parts, body) = response.into_parts();
252 (self.responder)(Response::from_parts(parts, body.into()))
253 }
254}
255
256pub struct WebViewAttributes {
257 /// Whether the WebView should have a custom user-agent.
258 pub user_agent: Option<String>,
259
260 /// Whether the WebView window should be visible.
261 pub visible: bool,
262
263 /// Whether the WebView should be transparent.
264 ///
265 /// ## Platform-specific:
266 ///
267 /// **Windows 7**: Not supported.
268 pub transparent: bool,
269
270 /// Specify the webview background color. This will be ignored if `transparent` is set to `true`.
271 ///
272 /// The color uses the RGBA format.
273 ///
274 /// ## Platform-specific:
275 ///
276 /// - **macOS / iOS**: Not implemented.
277 /// - **Windows**:
278 /// - On Windows 7, transparency is not supported and the alpha value will be ignored.
279 /// - On Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255`
280 pub background_color: Option<RGBA>,
281
282 /// Whether load the provided URL to [`WebView`].
283 pub url: Option<Url>,
284
285 /// Headers used when loading the requested `url`.
286 pub headers: Option<http::HeaderMap>,
287
288 /// Whether page zooming by hotkeys is enabled
289 ///
290 /// ## Platform-specific
291 ///
292 /// **macOS / Linux / Android / iOS**: Unsupported
293 pub zoom_hotkeys_enabled: bool,
294
295 /// Whether load the provided html string to [`WebView`].
296 /// This will be ignored if the `url` is provided.
297 ///
298 /// # Warning
299 ///
300 /// The Page loaded from html string will have `null` origin.
301 ///
302 /// ## PLatform-specific:
303 ///
304 /// - **Windows:** the string can not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size
305 pub html: Option<String>,
306
307 /// Initialize javascript code when loading new pages. When webview load a new page, this
308 /// initialization code will be executed. It is guaranteed that code is executed before
309 /// `window.onload`.
310 ///
311 /// ## Platform-specific
312 ///
313 /// - **Android:** The Android WebView does not provide an API for initialization scripts,
314 /// so we prepend them to each HTML head. They are only implemented on custom protocol URLs.
315 pub initialization_scripts: Vec<String>,
316
317 /// A list of custom loading protocols with pairs of scheme uri string and a handling
318 /// closure.
319 ///
320 /// The closure takes a [Request] and returns a [Response].
321 ///
322 /// # Warning
323 ///
324 /// Pages loaded from custom protocol will have different Origin on different platforms. And
325 /// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin`
326 /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
327 /// different Origin headers across platforms:
328 ///
329 /// - macOS, iOS and Linux: `<scheme_name>://<path>` (so it will be `wry://path/to/page/`).
330 /// - 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`].
331 ///
332 /// # Reading assets on mobile
333 ///
334 /// - Android: Android has `assets` and `resource` path finder to
335 /// locate your files in those directories. For more information, see [Loading in-app content](https://developer.android.com/guide/webapps/load-local-content) page.
336 /// - 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.
337 pub custom_protocols: Vec<(String, Box<dyn Fn(Request<Vec<u8>>, RequestAsyncResponder)>)>,
338
339 /// The IPC handler to receive the message from Javascript on webview
340 /// using `window.ipc.postMessage("insert_message_here")` to host Rust code.
341 pub ipc_handler: Option<Box<dyn Fn(String)>>,
342
343 /// A handler closure to process incoming [`FileDropEvent`] of the webview.
344 ///
345 /// # Blocking OS Default Behavior
346 /// Return `true` in the callback to block the OS' default behavior of handling a file drop.
347 ///
348 /// Note, that if you do block this behavior, it won't be possible to drop files on `<input type="file">` forms.
349 /// Also note, that it's not possible to manually set the value of a `<input type="file">` via JavaScript for security reasons.
350 #[cfg(feature = "file-drop")]
351 pub file_drop_handler: Option<Box<dyn Fn(FileDropEvent) -> bool>>,
352 #[cfg(not(feature = "file-drop"))]
353 file_drop_handler: Option<Box<dyn Fn(FileDropEvent) -> bool>>,
354
355 /// A navigation handler to decide if incoming url is allowed to navigate.
356 ///
357 /// The closure take a `String` parameter as url and returns a `bool` to determine whether the navigation should happen.
358 /// `true` allows to navigate and `false` does not.
359 pub navigation_handler: Option<Box<dyn Fn(String) -> bool>>,
360
361 /// A download started handler to manage incoming downloads.
362 ///
363 /// The closure takes two parameters, the first is a `String` representing the url being downloaded from and and the
364 /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter
365 /// parameter can be used to set the download location by assigning a new path to it, the assigned path _must_ be
366 /// absolute. The closure returns a `bool` to allow or deny the download.
367 pub download_started_handler: Option<Box<dyn FnMut(String, &mut PathBuf) -> bool>>,
368
369 /// A download completion handler to manage downloads that have finished.
370 ///
371 /// The closure is fired when the download completes, whether it was successful or not.
372 /// The closure takes a `String` representing the URL of the original download request, an `Option<PathBuf>`
373 /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download
374 /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download
375 /// did not succeed, and may instead indicate some other failure, always check the third parameter if you need to
376 /// know if the download succeeded.
377 ///
378 /// ## Platform-specific:
379 ///
380 /// - **macOS**: The second parameter indicating the path the file was saved to, is always empty,
381 /// due to API limitations.
382 pub download_completed_handler: Option<Rc<dyn Fn(String, Option<PathBuf>, bool) + 'static>>,
383
384 /// A new window handler to decide if incoming url is allowed to open in a new window.
385 ///
386 /// The closure take a `String` parameter as url and return `bool` to determine whether the window should open.
387 /// `true` allows to open and `false` does not.
388 pub new_window_req_handler: Option<Box<dyn Fn(String) -> bool>>,
389
390 /// Enables clipboard access for the page rendered on **Linux** and **Windows**.
391 ///
392 /// macOS doesn't provide such method and is always enabled by default. But your app will still need to add menu
393 /// item accelerators to use the clipboard shortcuts.
394 pub clipboard: bool,
395
396 /// Enable web inspector which is usually called browser devtools.
397 ///
398 /// Note this only enables devtools to the webview. To open it, you can call
399 /// [`WebView::open_devtools`], or right click the page and open it from the context menu.
400 ///
401 /// ## Platform-specific
402 ///
403 /// - macOS: This will call private functions on **macOS**. It is enabled in **debug** builds,
404 /// but requires `devtools` feature flag to actually enable it in **release** builds.
405 /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.
406 /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.
407 pub devtools: bool,
408
409 /// Whether clicking an inactive window also clicks through to the webview. Default is `false`.
410 ///
411 /// ## Platform-specific
412 ///
413 /// This configuration only impacts macOS.
414 pub accept_first_mouse: bool,
415
416 /// Indicates whether horizontal swipe gestures trigger backward and forward page navigation.
417 ///
418 /// ## Platform-specific:
419 ///
420 /// - **Android / iOS:** Unsupported.
421 pub back_forward_navigation_gestures: bool,
422
423 /// Set a handler closure to process the change of the webview's document title.
424 pub document_title_changed_handler: Option<Box<dyn Fn(String)>>,
425
426 /// Run the WebView with incognito mode. Note that WebContext will be ingored if incognito is
427 /// enabled.
428 ///
429 /// ## Platform-specific:
430 ///
431 /// - **Android:** Unsupported yet.
432 pub incognito: bool,
433
434 /// Whether all media can be played without user interaction.
435 pub autoplay: bool,
436
437 /// Set a handler closure to process page load events.
438 pub on_page_load_handler: Option<Box<dyn Fn(PageLoadEvent, String)>>,
439
440 /// Set a proxy configuration for the webview. Supports HTTP CONNECT and SOCKSv5 proxies
441 ///
442 /// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled.
443 /// - **Android / iOS:** Not supported.
444 pub proxy_config: Option<ProxyConfig>,
445
446 /// Whether the webview should be focused when created.
447 ///
448 /// ## Platform-specific:
449 ///
450 /// - **macOS / Android / iOS:** Unsupported.
451 pub focused: bool,
452
453 /// The webview bounds. Defaults to `x: 0, y: 0, width: 200, height: 200`.
454 /// This is only effective if the webview was created by [`WebView::new_as_child`] or [`WebViewBuilder::new_as_child`].
455 pub bounds: Option<Rect>,
456}
457
458impl Default for WebViewAttributes {
459 fn default() -> Self {
460 Self {
461 user_agent: None,
462 visible: true,
463 transparent: false,
464 background_color: None,
465 url: None,
466 headers: None,
467 html: None,
468 initialization_scripts: vec![],
469 custom_protocols: vec![],
470 ipc_handler: None,
471 file_drop_handler: None,
472 navigation_handler: None,
473 download_started_handler: None,
474 download_completed_handler: None,
475 new_window_req_handler: None,
476 clipboard: false,
477 #[cfg(debug_assertions)]
478 devtools: true,
479 #[cfg(not(debug_assertions))]
480 devtools: false,
481 zoom_hotkeys_enabled: false,
482 accept_first_mouse: false,
483 back_forward_navigation_gestures: false,
484 document_title_changed_handler: None,
485 incognito: false,
486 autoplay: true,
487 on_page_load_handler: None,
488 proxy_config: None,
489 focused: true,
490 bounds: Some(Rect {
491 x: 0,
492 y: 0,
493 width: 200,
494 height: 200,
495 }),
496 }
497 }
498}
499
500/// Builder type of [`WebView`].
501///
502/// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and
503/// scripts for those who prefer to control fine grained window creation and event handling.
504/// [`WebViewBuilder`] provides ability to setup initialization before web engine starts.
505pub struct WebViewBuilder<'a> {
506 pub attrs: WebViewAttributes,
507 as_child: bool,
508 window: Option<&'a dyn HasRawWindowHandle>,
509 platform_specific: PlatformSpecificWebViewAttributes,
510 web_context: Option<&'a mut WebContext>,
511 #[cfg(gtk)]
512 gtk_widget: Option<&'a gtk::Container>,
513}
514
515impl<'a> WebViewBuilder<'a> {
516 /// Create a [`WebViewBuilder`] from a type that implements [`HasRawWindowHandle`].
517 ///
518 /// # Platform-specific:
519 ///
520 /// - **Linux**: Only X11 is supported, if you want to support Wayland too, use [`WebViewBuilderExtUnix::new_gtk`].
521 ///
522 /// Although this methods only needs an X11 window handle, we use webkit2gtk, so you still need to initialize gtk
523 /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`].
524 /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation.
525 /// - **Windows**: The webview will auto-resize when the passed handle is resized.
526 /// - **Linux (X11)**: Unlike macOS and Windows, the webview will not auto-resize and you'll need to call [`WebView::set_size`] manually.
527 ///
528 /// # Panics:
529 ///
530 /// - Panics if the provided handle was not supported or invalid.
531 /// - Panics on Linux, if [`gtk::init`] was not called in this thread.
532 pub fn new(window: &'a impl HasRawWindowHandle) -> Self {
533 Self {
534 attrs: WebViewAttributes::default(),
535 window: Some(window),
536 as_child: false,
537 #[allow(clippy::default_constructed_unit_structs)]
538 platform_specific: PlatformSpecificWebViewAttributes::default(),
539 web_context: None,
540 #[cfg(gtk)]
541 gtk_widget: None,
542 }
543 }
544
545 /// Create [`WebViewBuilder`] as a child window inside the provided [`HasRawWindowHandle`].
546 ///
547 /// ## Platform-specific
548 ///
549 /// - **Windows**: This will create the webview as a child window of the `parent` window.
550 /// - **macOS**: This will create the webview as a `NSView` subview of the `parent` window's
551 /// content view.
552 /// - **Linux**: This will create the webview as a child window of the `parent` window. Only X11
553 /// is supported. This method won't work on Wayland.
554 ///
555 /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk
556 /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`].
557 /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation.
558 /// - **Android/iOS:** Unsupported.
559 ///
560 /// # Panics:
561 ///
562 /// - Panics if the provided handle was not support or invalid.
563 /// - Panics on Linux, if [`gtk::init`] was not called in this thread.
564 pub fn new_as_child(parent: &'a impl HasRawWindowHandle) -> Self {
565 Self {
566 attrs: WebViewAttributes::default(),
567 window: Some(parent),
568 as_child: true,
569 #[allow(clippy::default_constructed_unit_structs)]
570 platform_specific: PlatformSpecificWebViewAttributes::default(),
571 web_context: None,
572 #[cfg(gtk)]
573 gtk_widget: None,
574 }
575 }
576
577 /// Indicates whether horizontal swipe gestures trigger backward and forward page navigation.
578 ///
579 /// ## Platform-specific:
580 ///
581 /// - **Android / iOS:** Unsupported.
582 pub fn with_back_forward_navigation_gestures(mut self, gesture: bool) -> Self {
583 self.attrs.back_forward_navigation_gestures = gesture;
584 self
585 }
586
587 /// Sets whether the WebView should be transparent.
588 ///
589 /// ## Platform-specific:
590 ///
591 /// **Windows 7**: Not supported.
592 pub fn with_transparent(mut self, transparent: bool) -> Self {
593 self.attrs.transparent = transparent;
594 self
595 }
596
597 /// Specify the webview background color. This will be ignored if `transparent` is set to `true`.
598 ///
599 /// The color uses the RGBA format.
600 ///
601 /// ## Platfrom-specific:
602 ///
603 /// - **macOS / iOS**: Not implemented.
604 /// - **Windows**:
605 /// - on Windows 7, transparency is not supported and the alpha value will be ignored.
606 /// - on Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255`
607 pub fn with_background_color(mut self, background_color: RGBA) -> Self {
608 self.attrs.background_color = Some(background_color);
609 self
610 }
611
612 /// Sets whether the WebView should be visible or not.
613 pub fn with_visible(mut self, visible: bool) -> Self {
614 self.attrs.visible = visible;
615 self
616 }
617
618 /// Sets whether all media can be played without user interaction.
619 pub fn with_autoplay(mut self, autoplay: bool) -> Self {
620 self.attrs.autoplay = autoplay;
621 self
622 }
623
624 /// Initialize javascript code when loading new pages. When webview load a new page, this
625 /// initialization code will be executed. It is guaranteed that code is executed before
626 /// `window.onload`.
627 ///
628 /// ## Platform-specific
629 ///
630 /// - **Android:** When [addDocumentStartJavaScript] is not supported,
631 /// we prepend them to each HTML head (implementation only supported on custom protocol URLs).
632 /// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
633 ///
634 /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
635 /// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
636 pub fn with_initialization_script(mut self, js: &str) -> Self {
637 if !js.is_empty() {
638 self.attrs.initialization_scripts.push(js.to_string());
639 }
640 self
641 }
642
643 /// Register custom loading protocols with pairs of scheme uri string and a handling
644 /// closure.
645 ///
646 /// The closure takes a [Request] and returns a [Response]
647 ///
648 /// # Warning
649 ///
650 /// Pages loaded from custom protocol will have different Origin on different platforms. And
651 /// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin`
652 /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
653 /// different Origin headers across platforms:
654 ///
655 /// - macOS, iOS and Linux: `<scheme_name>://<path>` (so it will be `wry://path/to/page).
656 /// - 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`].
657 ///
658 /// # Reading assets on mobile
659 ///
660 /// - Android: For loading content from the `assets` folder (which is copied to the Andorid apk) please
661 /// use the function [`with_asset_loader`] from [`WebViewBuilderExtAndroid`] instead.
662 /// This function on Android can only be used to serve assets you can embed in the binary or are
663 /// elsewhere in Android (provided the app has appropriate access), but not from the `assets`
664 /// folder which lives within the apk. For the cases where this can be used, it works the same as in macOS and Linux.
665 /// - 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.
666 #[cfg(feature = "protocol")]
667 pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
668 where
669 F: Fn(Request<Vec<u8>>) -> Response<Cow<'static, [u8]>> + 'static,
670 {
671 self.attrs.custom_protocols.push((
672 name,
673 Box::new(move |request, responder| {
674 let http_response = handler(request);
675 responder.respond(http_response);
676 }),
677 ));
678 self
679 }
680
681 /// Same as [`Self::with_custom_protocol`] but with an asynchronous responder.
682 ///
683 /// # Examples
684 ///
685 /// ```no_run
686 /// use wry::{WebViewBuilder, raw_window_handle};
687 ///
688 /// # struct T;
689 /// # unsafe impl raw_window_handle::HasRawWindowHandle for T {
690 /// # fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
691 /// # raw_window_handle::RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::empty())
692 /// # }
693 /// # }
694 /// # let window = T;
695 /// WebViewBuilder::new(&window)
696 /// .with_asynchronous_custom_protocol("wry".into(), |request, responder| {
697 /// // here you can use a tokio task, thread pool or anything
698 /// // to do heavy computation to resolve your request
699 /// // e.g. downloading files, opening the camera...
700 /// std::thread::spawn(move || {
701 /// std::thread::sleep(std::time::Duration::from_secs(2));
702 /// responder.respond(http::Response::builder().body(Vec::new()).unwrap());
703 /// });
704 /// });
705 /// ```
706 #[cfg(feature = "protocol")]
707 pub fn with_asynchronous_custom_protocol<F>(mut self, name: String, handler: F) -> Self
708 where
709 F: Fn(Request<Vec<u8>>, RequestAsyncResponder) + 'static,
710 {
711 self.attrs.custom_protocols.push((name, Box::new(handler)));
712 self
713 }
714
715 /// Set the IPC handler to receive the message from Javascript on webview
716 /// using `window.ipc.postMessage("insert_message_here")` to host Rust code.
717 pub fn with_ipc_handler<F>(mut self, handler: F) -> Self
718 where
719 F: Fn(String) + 'static,
720 {
721 self.attrs.ipc_handler = Some(Box::new(handler));
722 self
723 }
724
725 /// Set a handler closure to process incoming [`FileDropEvent`] of the webview.
726 ///
727 /// # Blocking OS Default Behavior
728 /// Return `true` in the callback to block the OS' default behavior of handling a file drop.
729 ///
730 /// Note, that if you do block this behavior, it won't be possible to drop files on `<input type="file">` forms.
731 /// Also note, that it's not possible to manually set the value of a `<input type="file">` via JavaScript for security reasons.
732 #[cfg(feature = "file-drop")]
733 pub fn with_file_drop_handler<F>(mut self, handler: F) -> Self
734 where
735 F: Fn(FileDropEvent) -> bool + 'static,
736 {
737 self.attrs.file_drop_handler = Some(Box::new(handler));
738 self
739 }
740
741 /// Load the provided URL with given headers when the builder calling [`WebViewBuilder::build`] to create the [`WebView`].
742 /// The provided URL must be valid.
743 pub fn with_url_and_headers(mut self, url: &str, headers: http::HeaderMap) -> Result<Self> {
744 self.attrs.url = Some(url.parse()?);
745 self.attrs.headers = Some(headers);
746 Ok(self)
747 }
748
749 /// Load the provided URL when the builder calling [`WebViewBuilder::build`] to create the [`WebView`].
750 /// The provided URL must be valid.
751 pub fn with_url(mut self, url: &str) -> Result<Self> {
752 self.attrs.url = Some(Url::parse(url)?);
753 self.attrs.headers = None;
754 Ok(self)
755 }
756
757 /// Load the provided HTML string when the builder calling [`WebViewBuilder::build`] to create the [`WebView`].
758 /// This will be ignored if `url` is provided.
759 ///
760 /// # Warning
761 ///
762 /// The Page loaded from html string will have `null` origin.
763 ///
764 /// ## PLatform-specific:
765 ///
766 /// - **Windows:** the string can not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size
767 pub fn with_html(mut self, html: impl Into<String>) -> Result<Self> {
768 self.attrs.html = Some(html.into());
769 Ok(self)
770 }
771
772 /// Set the web context that can be shared with multiple [`WebView`]s.
773 pub fn with_web_context(mut self, web_context: &'a mut WebContext) -> Self {
774 self.web_context = Some(web_context);
775 self
776 }
777
778 /// Set a custom [user-agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) for the WebView.
779 pub fn with_user_agent(mut self, user_agent: &str) -> Self {
780 self.attrs.user_agent = Some(user_agent.to_string());
781 self
782 }
783
784 /// Enable or disable web inspector which is usually called devtools.
785 ///
786 /// Note this only enables devtools to the webview. To open it, you can call
787 /// [`WebView::open_devtools`], or right click the page and open it from the context menu.
788 ///
789 /// ## Platform-specific
790 ///
791 /// - macOS: This will call private functions on **macOS**. It is enabled in **debug** builds,
792 /// but requires `devtools` feature flag to actually enable it in **release** builds.
793 /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.
794 /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.
795 pub fn with_devtools(mut self, devtools: bool) -> Self {
796 self.attrs.devtools = devtools;
797 self
798 }
799
800 /// Whether page zooming by hotkeys or gestures is enabled
801 ///
802 /// ## Platform-specific
803 ///
804 /// **macOS / Linux / Android / iOS**: Unsupported
805 pub fn with_hotkeys_zoom(mut self, zoom: bool) -> Self {
806 self.attrs.zoom_hotkeys_enabled = zoom;
807 self
808 }
809
810 /// Set a navigation handler to decide if incoming url is allowed to navigate.
811 ///
812 /// The closure take a `String` parameter as url and returns a `bool` to determine whether the navigation should happen.
813 /// `true` allows to navigate and `false` does not.
814 pub fn with_navigation_handler(mut self, callback: impl Fn(String) -> bool + 'static) -> Self {
815 self.attrs.navigation_handler = Some(Box::new(callback));
816 self
817 }
818
819 /// Set a download started handler to manage incoming downloads.
820 ///
821 //// The closure takes two parameters, the first is a `String` representing the url being downloaded from and and the
822 /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter
823 /// parameter can be used to set the download location by assigning a new path to it, the assigned path _must_ be
824 /// absolute. The closure returns a `bool` to allow or deny the download.
825 pub fn with_download_started_handler(
826 mut self,
827 started_handler: impl FnMut(String, &mut PathBuf) -> bool + 'static,
828 ) -> Self {
829 self.attrs.download_started_handler = Some(Box::new(started_handler));
830 self
831 }
832
833 /// Sets a download completion handler to manage downloads that have finished.
834 ///
835 /// The closure is fired when the download completes, whether it was successful or not.
836 /// The closure takes a `String` representing the URL of the original download request, an `Option<PathBuf>`
837 /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download
838 /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download
839 /// did not succeed, and may instead indicate some other failure, always check the third parameter if you need to
840 /// know if the download succeeded.
841 ///
842 /// ## Platform-specific:
843 ///
844 /// - **macOS**: The second parameter indicating the path the file was saved to, is always empty,
845 /// due to API limitations.
846 pub fn with_download_completed_handler(
847 mut self,
848 download_completed_handler: impl Fn(String, Option<PathBuf>, bool) + 'static,
849 ) -> Self {
850 self.attrs.download_completed_handler = Some(Rc::new(download_completed_handler));
851 self
852 }
853
854 /// Enables clipboard access for the page rendered on **Linux** and **Windows**.
855 ///
856 /// macOS doesn't provide such method and is always enabled by default. But your app will still need to add menu
857 /// item accelerators to use the clipboard shortcuts.
858 pub fn with_clipboard(mut self, clipboard: bool) -> Self {
859 self.attrs.clipboard = clipboard;
860 self
861 }
862
863 /// Set a new window request handler to decide if incoming url is allowed to be opened.
864 ///
865 /// The closure take a `String` parameter as url and return `bool` to determine whether the window should open.
866 /// `true` allows to open and `false` does not.
867 pub fn with_new_window_req_handler(
868 mut self,
869 callback: impl Fn(String) -> bool + 'static,
870 ) -> Self {
871 self.attrs.new_window_req_handler = Some(Box::new(callback));
872 self
873 }
874
875 /// Sets whether clicking an inactive window also clicks through to the webview. Default is `false`.
876 ///
877 /// ## Platform-specific
878 ///
879 /// This configuration only impacts macOS.
880 pub fn with_accept_first_mouse(mut self, accept_first_mouse: bool) -> Self {
881 self.attrs.accept_first_mouse = accept_first_mouse;
882 self
883 }
884
885 /// Set a handler closure to process the change of the webview's document title.
886 pub fn with_document_title_changed_handler(
887 mut self,
888 callback: impl Fn(String) + 'static,
889 ) -> Self {
890 self.attrs.document_title_changed_handler = Some(Box::new(callback));
891 self
892 }
893
894 /// Run the WebView with incognito mode. Note that WebContext will be ingored if incognito is
895 /// enabled.
896 ///
897 /// ## Platform-specific:
898 ///
899 /// - **Android:** Unsupported yet.
900 pub fn with_incognito(mut self, incognito: bool) -> Self {
901 self.attrs.incognito = incognito;
902 self
903 }
904
905 /// Set a handler to process page loading events.
906 pub fn with_on_page_load_handler(
907 mut self,
908 handler: impl Fn(PageLoadEvent, String) + 'static,
909 ) -> Self {
910 self.attrs.on_page_load_handler = Some(Box::new(handler));
911 self
912 }
913
914 /// Set a proxy configuration for the webview.
915 ///
916 /// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled. Supports HTTP CONNECT and SOCKSv5 proxies.
917 /// - **Windows / Linux**: Supports HTTP CONNECT and SOCKSv5 proxies.
918 /// - **Android / iOS:** Not supported.
919 pub fn with_proxy_config(mut self, configuration: ProxyConfig) -> Self {
920 self.attrs.proxy_config = Some(configuration);
921 self
922 }
923
924 /// Set whether the webview should be focused when created.
925 ///
926 /// ## Platform-specific:
927 ///
928 /// - **macOS / Android / iOS:** Unsupported.
929 pub fn with_focused(mut self, focused: bool) -> Self {
930 self.attrs.focused = focused;
931 self
932 }
933
934 /// Specify the webview position relative to its parent if it will be created as a child.
935 /// Defaults to `x: 0, y: 0, width: 200, height: 200`.
936 pub fn with_bounds(mut self, bounds: Rect) -> Self {
937 self.attrs.bounds.replace(bounds);
938 self
939 }
940
941 /// Consume the builder and create the [`WebView`].
942 ///
943 /// # Panics:
944 ///
945 /// - Panics if the provided handle was not support or invalid.
946 /// - Panics on Linux, if [`gtk::init`] was not called in this thread.
947 pub fn build(self) -> Result<WebView> {
948 let webview = if let Some(window) = &self.window {
949 if self.as_child {
950 InnerWebView::new_as_child(window, self.attrs, self.platform_specific, self.web_context)?
951 } else {
952 InnerWebView::new(window, self.attrs, self.platform_specific, self.web_context)?
953 }
954 } else {
955 #[cfg(gtk)]
956 if let Some(widget) = self.gtk_widget {
957 InnerWebView::new_gtk(widget, self.attrs, self.platform_specific, self.web_context)?
958 } else {
959 unreachable!()
960 }
961
962 #[cfg(not(gtk))]
963 unreachable!()
964 };
965
966 Ok(WebView { webview })
967 }
968}
969
970#[cfg(windows)]
971#[derive(Clone)]
972pub(crate) struct PlatformSpecificWebViewAttributes {
973 additional_browser_args: Option<String>,
974 browser_accelerator_keys: bool,
975 theme: Option<Theme>,
976 https_scheme: bool,
977}
978
979#[cfg(windows)]
980impl Default for PlatformSpecificWebViewAttributes {
981 fn default() -> Self {
982 Self {
983 additional_browser_args: None,
984 browser_accelerator_keys: true, // This is WebView2's default behavior
985 theme: None,
986 https_scheme: false, // To match macOS & Linux behavior in the context of mixed content.
987 }
988 }
989}
990
991#[cfg(windows)]
992pub trait WebViewBuilderExtWindows {
993 /// Pass additional args to WebView2 upon creating the webview.
994 ///
995 /// ## Warning
996 ///
997 /// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`
998 /// `--autoplay-policy=no-user-gesture-required` if autoplay is enabled
999 /// and `--proxy-server=<scheme>://<host>:<port>` if a proxy is set.
1000 /// so if you use this method, you have to add these arguments yourself if you want to keep the same behavior.
1001 fn with_additional_browser_args<S: Into<String>>(self, additional_args: S) -> Self;
1002
1003 /// Determines whether browser-specific accelerator keys are enabled. When this setting is set to
1004 /// `false`, it disables all accelerator keys that access features specific to a web browser.
1005 /// The default value is `true`. See the following link to know more details.
1006 ///
1007 /// https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings#arebrowseracceleratorkeysenabled
1008 fn with_browser_accelerator_keys(self, enabled: bool) -> Self;
1009
1010 /// Specifies the theme of webview2. This affects things like `prefers-color-scheme`.
1011 ///
1012 /// Defaults to [`Theme::Auto`] which will follow the OS defaults.
1013 fn with_theme(self, theme: Theme) -> Self;
1014
1015 /// Determines whether the custom protocols should use `https://<scheme>.path/to/page` instead of the default `http://<scheme>.path/to/page`.
1016 ///
1017 /// Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints
1018 /// and is therefore less secure but will match the behavior of the `<scheme>://path/to/page` protocols used on macOS and Linux.
1019 ///
1020 /// The default value is `false`.
1021 fn with_https_scheme(self, enabled: bool) -> Self;
1022}
1023
1024#[cfg(windows)]
1025impl WebViewBuilderExtWindows for WebViewBuilder<'_> {
1026 fn with_additional_browser_args<S: Into<String>>(mut self, additional_args: S) -> Self {
1027 self.platform_specific.additional_browser_args = Some(additional_args.into());
1028 self
1029 }
1030
1031 fn with_browser_accelerator_keys(mut self, enabled: bool) -> Self {
1032 self.platform_specific.browser_accelerator_keys = enabled;
1033 self
1034 }
1035
1036 fn with_theme(mut self, theme: Theme) -> Self {
1037 self.platform_specific.theme = Some(theme);
1038 self
1039 }
1040
1041 fn with_https_scheme(mut self, enabled: bool) -> Self {
1042 self.platform_specific.https_scheme = enabled;
1043 self
1044 }
1045}
1046
1047#[cfg(target_os = "android")]
1048#[derive(Default)]
1049pub(crate) struct PlatformSpecificWebViewAttributes {
1050 on_webview_created:
1051 Option<Box<dyn Fn(prelude::Context) -> std::result::Result<(), jni::errors::Error> + Send>>,
1052 with_asset_loader: bool,
1053 asset_loader_domain: Option<String>,
1054 https_scheme: bool,
1055}
1056
1057#[cfg(target_os = "android")]
1058pub trait WebViewBuilderExtAndroid {
1059 fn on_webview_created<
1060 F: Fn(prelude::Context<'_, '_>) -> std::result::Result<(), jni::errors::Error> + Send + 'static,
1061 >(
1062 self,
1063 f: F,
1064 ) -> Self;
1065
1066 /// Use [WebViewAssetLoader](https://developer.android.com/reference/kotlin/androidx/webkit/WebViewAssetLoader)
1067 /// to load assets from Android's `asset` folder when using `with_url` as `<protocol>://assets/` (e.g.:
1068 /// `wry://assets/index.html`). Note that this registers a custom protocol with the provided
1069 /// String, similar to [`with_custom_protocol`], but also sets the WebViewAssetLoader with the
1070 /// necessary domain (which is fixed as `<protocol>.assets`). This cannot be used in conjunction
1071 /// to `with_custom_protocol` for Android, as it changes the way in which requests are handled.
1072 #[cfg(feature = "protocol")]
1073 fn with_asset_loader(self, protocol: String) -> Self;
1074
1075 /// Determines whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost`.
1076 ///
1077 /// Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints
1078 /// and is therefore less secure but will match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
1079 ///
1080 /// The default value is `false`.
1081 fn with_https_scheme(self, enabled: bool) -> Self;
1082}
1083
1084#[cfg(target_os = "android")]
1085impl WebViewBuilderExtAndroid for WebViewBuilder<'_> {
1086 fn on_webview_created<
1087 F: Fn(prelude::Context<'_, '_>) -> std::result::Result<(), jni::errors::Error> + Send + 'static,
1088 >(
1089 mut self,
1090 f: F,
1091 ) -> Self {
1092 self.platform_specific.on_webview_created = Some(Box::new(f));
1093 self
1094 }
1095
1096 #[cfg(feature = "protocol")]
1097 fn with_asset_loader(mut self, protocol: String) -> Self {
1098 // register custom protocol with empty Response return,
1099 // this is necessary due to the need of fixing a domain
1100 // in WebViewAssetLoader.
1101 self.attrs.custom_protocols.push((
1102 protocol.clone(),
1103 Box::new(|_, api| {
1104 api.respond(Response::builder().body(Vec::new()).unwrap());
1105 }),
1106 ));
1107 self.platform_specific.with_asset_loader = true;
1108 self.platform_specific.asset_loader_domain = Some(format!("{}.assets", protocol));
1109 self
1110 }
1111
1112 fn with_https_scheme(mut self, enabled: bool) -> Self {
1113 self.platform_specific.https_scheme = enabled;
1114 self
1115 }
1116}
1117
1118#[cfg(any(
1119 target_os = "linux",
1120 target_os = "dragonfly",
1121 target_os = "freebsd",
1122 target_os = "netbsd",
1123 target_os = "openbsd",
1124))]
1125pub trait WebViewBuilderExtUnix<'a> {
1126 /// Create the webview from a GTK container widget, such as GTK window.
1127 ///
1128 /// # Panics:
1129 ///
1130 /// - Panics if [`gtk::init`] was not called in this thread.
1131 fn new_gtk<W>(widget: &'a W) -> Self
1132 where
1133 W: gtk::prelude::IsA<gtk::Container>;
1134}
1135
1136#[cfg(any(
1137 target_os = "linux",
1138 target_os = "dragonfly",
1139 target_os = "freebsd",
1140 target_os = "netbsd",
1141 target_os = "openbsd",
1142))]
1143impl<'a> WebViewBuilderExtUnix<'a> for WebViewBuilder<'a> {
1144 fn new_gtk<W>(widget: &'a W) -> Self
1145 where
1146 W: gtk::prelude::IsA<gtk::Container>,
1147 {
1148 use gdkx11::glib::Cast;
1149
1150 Self {
1151 attrs: WebViewAttributes::default(),
1152 window: None,
1153 as_child: false,
1154 #[allow(clippy::default_constructed_unit_structs)]
1155 platform_specific: PlatformSpecificWebViewAttributes::default(),
1156 web_context: None,
1157 gtk_widget: Some(widget.dynamic_cast_ref().unwrap()),
1158 }
1159 }
1160}
1161
1162/// The fundamental type to present a [`WebView`].
1163///
1164/// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and
1165/// scripts for those who prefer to control fine grained window creation and event handling.
1166/// [`WebView`] presents the actual WebView window and let you still able to perform actions on it.
1167pub struct WebView {
1168 webview: InnerWebView,
1169}
1170
1171impl WebView {
1172 /// Create a [`WebView`] from from a type that implements [`HasRawWindowHandle`].
1173 /// Note that calling this directly loses
1174 /// abilities to initialize scripts, add ipc handler, and many more before starting WebView. To
1175 /// benefit from above features, create a [`WebViewBuilder`] instead.
1176 ///
1177 /// # Platform-specific:
1178 ///
1179 /// - **Linux**: Only X11 is supported, if you want to support Wayland too, use [`WebViewExtUnix::new_gtk`].
1180 ///
1181 /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk
1182 /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`].
1183 /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation.
1184 /// - **macOS / Windows**: The webview will auto-resize when the passed handle is resized.
1185 /// - **Linux (X11)**: Unlike macOS and Windows, the webview will not auto-resize and you'll need to call [`WebView::set_size`] manually.
1186 ///
1187 /// # Panics:
1188 ///
1189 /// - Panics if the provided handle was not supported or invalid.
1190 /// - Panics on Linux, if [`gtk::init`] was not called in this thread.
1191 pub fn new(window: &impl HasRawWindowHandle) -> Result<Self> {
1192 WebViewBuilder::new(window).build()
1193 }
1194
1195 /// Create [`WebViewBuilder`] as a child window inside the provided [`HasRawWindowHandle`].
1196 ///
1197 /// ## Platform-specific
1198 ///
1199 /// - **Windows**: This will create the webview as a child window of the `parent` window.
1200 /// - **macOS**: This will create the webview as a `NSView` subview of the `parent` window's
1201 /// content view.
1202 /// - **Linux**: This will create the webview as a child window of the `parent` window. Only X11
1203 /// is supported. This method won't work on Wayland.
1204 ///
1205 /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk
1206 /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`].
1207 /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation.
1208 /// - **Android/iOS:** Unsupported.
1209 ///
1210 /// # Panics:
1211 ///
1212 /// - Panics if the provided handle was not support or invalid.
1213 /// - Panics on Linux, if [`gtk::init`] was not called in this thread.
1214 pub fn new_as_child(parent: &impl HasRawWindowHandle) -> Result<Self> {
1215 WebViewBuilder::new_as_child(parent).build()
1216 }
1217
1218 /// Get the current url of the webview
1219 pub fn url(&self) -> Url {
1220 self.webview.url()
1221 }
1222
1223 /// Evaluate and run javascript code.
1224 pub fn evaluate_script(&self, js: &str) -> Result<()> {
1225 self
1226 .webview
1227 .eval(js, None::<Box<dyn Fn(String) + Send + 'static>>)
1228 }
1229
1230 /// Evaluate and run javascript code with callback function. The evaluation result will be
1231 /// serialized into a JSON string and passed to the callback function.
1232 ///
1233 /// Exception is ignored because of the limitation on windows. You can catch it yourself and return as string as a workaround.
1234 ///
1235 /// - ** Android:** Not implemented yet.
1236 pub fn evaluate_script_with_callback(
1237 &self,
1238 js: &str,
1239 callback: impl Fn(String) + Send + 'static,
1240 ) -> Result<()> {
1241 self.webview.eval(js, Some(callback))
1242 }
1243
1244 /// Launch print modal for the webview content.
1245 pub fn print(&self) -> Result<()> {
1246 self.webview.print();
1247 Ok(())
1248 }
1249
1250 /// Open the web inspector which is usually called dev tool.
1251 ///
1252 /// ## Platform-specific
1253 ///
1254 /// - **Android / iOS:** Not supported.
1255 #[cfg(any(debug_assertions, feature = "devtools"))]
1256 pub fn open_devtools(&self) {
1257 self.webview.open_devtools();
1258 }
1259
1260 /// Close the web inspector which is usually called dev tool.
1261 ///
1262 /// ## Platform-specific
1263 ///
1264 /// - **Windows / Android / iOS:** Not supported.
1265 #[cfg(any(debug_assertions, feature = "devtools"))]
1266 pub fn close_devtools(&self) {
1267 self.webview.close_devtools();
1268 }
1269
1270 /// Gets the devtool window's current visibility state.
1271 ///
1272 /// ## Platform-specific
1273 ///
1274 /// - **Windows / Android / iOS:** Not supported.
1275 #[cfg(any(debug_assertions, feature = "devtools"))]
1276 pub fn is_devtools_open(&self) -> bool {
1277 self.webview.is_devtools_open()
1278 }
1279
1280 /// Set the webview zoom level
1281 ///
1282 /// ## Platform-specific:
1283 ///
1284 /// - **Android**: Not supported.
1285 /// - **macOS**: available on macOS 11+ only.
1286 /// - **iOS**: available on iOS 14+ only.
1287 pub fn zoom(&self, scale_factor: f64) {
1288 self.webview.zoom(scale_factor);
1289 }
1290
1291 /// Specify the webview background color.
1292 ///
1293 /// The color uses the RGBA format.
1294 ///
1295 /// ## Platfrom-specific:
1296 ///
1297 /// - **macOS / iOS**: Not implemented.
1298 /// - **Windows**:
1299 /// - On Windows 7, transparency is not supported and the alpha value will be ignored.
1300 /// - On Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255`
1301 pub fn set_background_color(&self, background_color: RGBA) -> Result<()> {
1302 self.webview.set_background_color(background_color)
1303 }
1304
1305 /// Navigate to the specified url
1306 pub fn load_url(&self, url: &str) {
1307 self.webview.load_url(url)
1308 }
1309
1310 /// Navigate to the specified url using the specified headers
1311 pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) {
1312 self.webview.load_url_with_headers(url, headers)
1313 }
1314
1315 /// Clear all browsing data
1316 pub fn clear_all_browsing_data(&self) -> Result<()> {
1317 self.webview.clear_all_browsing_data()
1318 }
1319
1320 pub fn bounds(&self) -> Rect {
1321 self.webview.bounds()
1322 }
1323
1324 /// Set the webview bounds.
1325 ///
1326 /// This is only effective if the webview was created as a child.
1327 pub fn set_bounds(&self, bounds: Rect) {
1328 self.webview.set_bounds(bounds)
1329 }
1330
1331 /// Shows or hides the webview.
1332 pub fn set_visible(&self, visible: bool) {
1333 self.webview.set_visible(visible)
1334 }
1335
1336 /// Try moving focus to the webview.
1337 pub fn focus(&self) {
1338 self.webview.focus()
1339 }
1340}
1341
1342/// An event describing the files drop on the webview.
1343#[non_exhaustive]
1344#[derive(Debug, serde::Serialize, Clone)]
1345pub enum FileDropEvent {
1346 /// The file(s) have been dragged onto the window, but have not been dropped yet.
1347 Hovered {
1348 paths: Vec<PathBuf>,
1349 /// The position of the mouse cursor.
1350 position: (i32, i32),
1351 },
1352 /// The file(s) have been dropped onto the window.
1353 Dropped {
1354 paths: Vec<PathBuf>,
1355 /// The position of the mouse cursor.
1356 position: (i32, i32),
1357 },
1358 /// The file drop was aborted.
1359 Cancelled,
1360}
1361
1362/// Get WebView/Webkit version on current platform.
1363pub fn webview_version() -> Result<String> {
1364 platform_webview_version()
1365}
1366
1367/// The [memory usage target level][1]. There are two levels 'Low' and 'Normal' and the default
1368/// level is 'Normal'. When the application is going inactive, setting the level to 'Low' can
1369/// significantly reduce the application's memory consumption.
1370///
1371/// [1]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2memoryusagetargetlevel
1372#[cfg(target_os = "windows")]
1373#[non_exhaustive]
1374#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1375pub enum MemoryUsageLevel {
1376 /// The 'Normal' memory usage. Applications should set this level when they are becoming active.
1377 #[default]
1378 Normal,
1379 /// The 'Low' memory usage. Applications can reduce memory comsumption by setting this level when
1380 /// they are becoming inactive.
1381 Low,
1382}
1383
1384/// Additional methods on `WebView` that are specific to Windows.
1385#[cfg(target_os = "windows")]
1386pub trait WebViewExtWindows {
1387 /// Returns WebView2 Controller
1388 fn controller(&self) -> ICoreWebView2Controller;
1389
1390 /// Changes the webview2 theme.
1391 fn set_theme(&self, theme: Theme);
1392
1393 /// Sets the [memory usage target level][1].
1394 ///
1395 /// When to best use this mode depends on the app in question. Most commonly it's called when
1396 /// the app's visiblity state changes.
1397 ///
1398 /// Please read the [guide for WebView2][2] for more details.
1399 ///
1400 /// This method uses a WebView2 API added in Runtime version 114.0.1823.32. When it is used in
1401 /// an older Runtime version, it does nothing.
1402 ///
1403 /// [1]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2memoryusagetargetlevel
1404 /// [2]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.memoryusagetargetlevel?view=webview2-dotnet-1.0.2088.41#remarks
1405 fn set_memory_usage_level(&self, level: MemoryUsageLevel);
1406
1407 unsafe fn add_host_object_to_script<P0>(
1408 &self,
1409 name: P0,
1410 object: *mut ::windows::Win32::System::Variant::VARIANT,
1411 ) -> ::windows::core::Result<()>
1412 where
1413 P0: ::windows::core::IntoParam<::windows::core::PCWSTR>;
1414
1415 unsafe fn create_shared_buffer(
1416 &self,
1417 size: u64,
1418 ) -> ::windows::core::Result<webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2SharedBuffer>;
1419
1420 unsafe fn post_shared_buffer_to_script<P0, P1>(
1421 &self,
1422 sharedbuffer: P0,
1423 access: webview2_com::Microsoft::Web::WebView2::Win32::COREWEBVIEW2_SHARED_BUFFER_ACCESS,
1424 additionaldataasjson: P1,
1425 ) -> ::windows::core::Result<()>
1426 where
1427 P0: ::windows::core::IntoParam<webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2SharedBuffer>,
1428 P1: ::windows::core::IntoParam<::windows::core::PCWSTR>;
1429}
1430
1431#[cfg(target_os = "windows")]
1432impl WebViewExtWindows for WebView {
1433 fn controller(&self) -> ICoreWebView2Controller {
1434 self.webview.controller.clone()
1435 }
1436
1437 fn set_theme(&self, theme: Theme) {
1438 self.webview.set_theme(theme)
1439 }
1440
1441 fn set_memory_usage_level(&self, level: MemoryUsageLevel) {
1442 self.webview.set_memory_usage_level(level);
1443 }
1444
1445 unsafe fn add_host_object_to_script<P0>(
1446 &self,
1447 name: P0,
1448 object: *mut ::windows::Win32::System::Variant::VARIANT,
1449 ) -> ::windows::core::Result<()> where P0: ::windows::core::IntoParam<::windows::core::PCWSTR>,
1450 {
1451 self.webview.add_host_object_to_script(name, object)
1452 }
1453
1454 unsafe fn create_shared_buffer(
1455 &self,
1456 size: u64,
1457 ) -> ::windows::core::Result<webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2SharedBuffer> {
1458 self.webview.create_shared_buffer(size)
1459 }
1460
1461 unsafe fn post_shared_buffer_to_script<P0, P1>(
1462 &self,
1463 sharedbuffer: P0,
1464 access: webview2_com::Microsoft::Web::WebView2::Win32::COREWEBVIEW2_SHARED_BUFFER_ACCESS,
1465 additionaldataasjson: P1,
1466 ) -> ::windows::core::Result<()>
1467 where
1468 P0: ::windows::core::IntoParam<webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2SharedBuffer>,
1469 P1: ::windows::core::IntoParam<::windows::core::PCWSTR>,
1470 {
1471 self.webview.post_shared_buffer_to_script(sharedbuffer, access, additionaldataasjson)
1472 }
1473}
1474
1475/// Additional methods on `WebView` that are specific to Linux.
1476#[cfg(gtk)]
1477pub trait WebViewExtUnix: Sized {
1478 /// Create the webview from a GTK container widget, such as GTK window.
1479 ///
1480 /// # Panics:
1481 ///
1482 /// - Panics if [`gtk::init`] was not called in this thread.
1483 fn new_gtk<W>(widget: &W) -> Result<Self>
1484 where
1485 W: gtk::prelude::IsA<gtk::Container>;
1486
1487 /// Returns Webkit2gtk Webview handle
1488 fn webview(&self) -> webkit2gtk::WebView;
1489}
1490
1491#[cfg(gtk)]
1492impl WebViewExtUnix for WebView {
1493 fn new_gtk<W>(widget: &W) -> Result<Self>
1494 where
1495 W: gtk::prelude::IsA<gtk::Container>,
1496 {
1497 WebViewBuilder::new_gtk(widget).build()
1498 }
1499
1500 fn webview(&self) -> webkit2gtk::WebView {
1501 self.webview.webview.clone()
1502 }
1503}
1504
1505/// Additional methods on `WebView` that are specific to macOS.
1506#[cfg(target_os = "macos")]
1507pub trait WebViewExtMacOS {
1508 /// Returns WKWebView handle
1509 fn webview(&self) -> cocoa::base::id;
1510 /// Returns WKWebView manager [(userContentController)](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler/1396222-usercontentcontroller) handle
1511 fn manager(&self) -> cocoa::base::id;
1512 /// Returns NSWindow associated with the WKWebView webview
1513 fn ns_window(&self) -> cocoa::base::id;
1514}
1515
1516#[cfg(target_os = "macos")]
1517impl WebViewExtMacOS for WebView {
1518 fn webview(&self) -> cocoa::base::id {
1519 self.webview.webview
1520 }
1521
1522 fn manager(&self) -> cocoa::base::id {
1523 self.webview.manager
1524 }
1525
1526 fn ns_window(&self) -> cocoa::base::id {
1527 self.webview.ns_window
1528 }
1529}
1530
1531/// Additional methods on `WebView` that are specific to iOS.
1532#[cfg(target_os = "ios")]
1533pub trait WebViewExtIOS {
1534 /// Returns WKWebView handle
1535 fn webview(&self) -> cocoa::base::id;
1536 /// Returns WKWebView manager [(userContentController)](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler/1396222-usercontentcontroller) handle
1537 fn manager(&self) -> cocoa::base::id;
1538}
1539
1540#[cfg(target_os = "ios")]
1541impl WebViewExtIOS for WebView {
1542 fn webview(&self) -> cocoa::base::id {
1543 self.webview.webview
1544 }
1545
1546 fn manager(&self) -> cocoa::base::id {
1547 self.webview.manager
1548 }
1549}
1550
1551#[cfg(target_os = "android")]
1552/// Additional methods on `WebView` that are specific to Android
1553pub trait WebViewExtAndroid {
1554 fn handle(&self) -> JniHandle;
1555}
1556
1557#[cfg(target_os = "android")]
1558impl WebViewExtAndroid for WebView {
1559 fn handle(&self) -> JniHandle {
1560 JniHandle
1561 }
1562}
1563
1564/// WebView theme.
1565#[derive(Debug, Clone, Copy)]
1566pub enum Theme {
1567 /// Dark
1568 Dark,
1569 /// Light
1570 Light,
1571 /// System preference
1572 Auto,
1573}
1574
1575/// Type alias for a color in the RGBA format.
1576///
1577/// Each value can be 0..255 inclusive.
1578pub type RGBA = (u8, u8, u8, u8);
1579
1580/// Type of of page loading event
1581pub enum PageLoadEvent {
1582 /// Indicates that the content of the page has started loading
1583 Started,
1584 /// Indicates that the page content has finished loading
1585 Finished,
1586}
1587
1588#[cfg(any(
1589 target_os = "linux",
1590 target_os = "dragonfly",
1591 target_os = "freebsd",
1592 target_os = "netbsd",
1593 target_os = "openbsd",
1594 target_os = "ios",
1595 target_os = "macos",
1596))]
1597#[derive(Default)]
1598pub(crate) struct PlatformSpecificWebViewAttributes;
1599
1600#[cfg(test)]
1601mod tests {
1602 use super::*;
1603
1604 #[test]
1605 #[cfg_attr(miri, ignore)]
1606 fn should_get_webview_version() {
1607 if let Err(error) = webview_version() {
1608 panic!("{}", error);
1609 }
1610 }
1611}