#![allow(clippy::new_without_default)]
#![allow(clippy::default_constructed_unit_structs)]
#![allow(clippy::type_complexity)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(any(target_os = "windows", target_os = "android"))]
mod custom_protocol_workaround;
mod error;
#[cfg(any(target_os = "android", test))]
mod inject_initialization_scripts;
mod proxy;
#[cfg(any(target_os = "macos", target_os = "android", target_os = "ios"))]
mod util;
mod web_context;
#[cfg(target_os = "android")]
pub(crate) mod android;
#[cfg(target_os = "android")]
pub use crate::android::android_setup;
#[cfg(target_os = "android")]
pub mod prelude {
pub use crate::android::{binding::*, dispatch, find_class, Context};
pub use tao_macros::{android_fn, generate_package_name};
}
#[cfg(target_os = "android")]
pub use android::JniHandle;
#[cfg(target_os = "android")]
use android::*;
#[cfg(gtk)]
pub(crate) mod webkitgtk;
pub use raw_window_handle;
use raw_window_handle::HasWindowHandle;
#[cfg(gtk)]
use webkitgtk::*;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use objc2::rc::Retained;
#[cfg(target_os = "macos")]
use objc2_app_kit::NSWindow;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use objc2_web_kit::WKUserContentController;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) mod wkwebview;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use wkwebview::*;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub use wkwebview::{PrintMargin, PrintOptions, WryWebView};
#[cfg(target_os = "windows")]
pub(crate) mod webview2;
#[cfg(target_os = "windows")]
pub use self::webview2::ScrollBarStyle;
#[cfg(target_os = "windows")]
use self::webview2::*;
#[cfg(target_os = "windows")]
use webview2_com::Microsoft::Web::WebView2::Win32::{
ICoreWebView2, ICoreWebView2Controller, ICoreWebView2Environment,
};
use std::{borrow::Cow, collections::HashMap, path::PathBuf, rc::Rc};
use http::{Request, Response};
pub use cookie;
pub use dpi;
pub use error::*;
pub use http;
pub use proxy::{ProxyConfig, ProxyEndpoint};
pub use web_context::WebContext;
#[cfg(target_os = "ios")]
pub type InputAccessoryViewBuilder =
dyn Fn(&objc2_ui_kit::UIView) -> Option<Retained<objc2_ui_kit::UIView>>;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Rect {
pub position: dpi::Position,
pub size: dpi::Size,
}
impl Default for Rect {
fn default() -> Self {
Self {
position: dpi::LogicalPosition::new(0, 0).into(),
size: dpi::LogicalSize::new(0, 0).into(),
}
}
}
pub struct RequestAsyncResponder {
pub(crate) responder: Box<dyn FnOnce(Response<Cow<'static, [u8]>>)>,
}
unsafe impl Send for RequestAsyncResponder {}
impl RequestAsyncResponder {
pub fn respond<T: Into<Cow<'static, [u8]>>>(self, response: Response<T>) {
let (parts, body) = response.into_parts();
(self.responder)(Response::from_parts(parts, body.into()))
}
}
pub enum NewWindowResponse {
Allow,
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Create {
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
webview: webkit2gtk::WebView,
#[cfg(windows)]
webview: ICoreWebView2,
#[cfg(target_os = "macos")]
webview: Retained<objc2_web_kit::WKWebView>,
},
Deny,
}
#[derive(Debug)]
pub struct NewWindowOpener {
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
pub webview: webkit2gtk::WebView,
#[cfg(windows)]
pub webview: ICoreWebView2,
#[cfg(windows)]
pub environment: ICoreWebView2Environment,
#[cfg(target_os = "macos")]
pub webview: Retained<objc2_web_kit::WKWebView>,
#[cfg(target_os = "macos")]
pub target_configuration: Retained<objc2_web_kit::WKWebViewConfiguration>,
}
unsafe impl Send for NewWindowOpener {}
unsafe impl Sync for NewWindowOpener {}
#[non_exhaustive]
#[derive(Debug)]
pub struct NewWindowFeatures {
pub size: Option<dpi::LogicalSize<f64>>,
pub position: Option<dpi::LogicalPosition<f64>>,
pub opener: NewWindowOpener,
}
pub type WebViewId<'a> = &'a str;
struct WebViewAttributes<'a> {
pub id: Option<WebViewId<'a>>,
#[allow(unused)]
pub context: Option<&'a mut WebContext>,
pub user_agent: Option<String>,
pub visible: bool,
pub transparent: bool,
pub background_color: Option<RGBA>,
pub url: Option<String>,
pub headers: Option<http::HeaderMap>,
pub zoom_hotkeys_enabled: bool,
pub html: Option<String>,
pub initialization_scripts: Vec<InitializationScript>,
pub custom_protocols:
HashMap<String, Box<dyn Fn(WebViewId, Request<Vec<u8>>, RequestAsyncResponder)>>,
pub ipc_handler: Option<Box<dyn Fn(Request<String>)>>,
pub drag_drop_handler: Option<Box<dyn Fn(DragDropEvent) -> bool>>,
pub navigation_handler: Option<Box<dyn Fn(String) -> bool>>,
pub download_started_handler: Option<Box<dyn FnMut(String, &mut PathBuf) -> bool + 'static>>,
pub download_completed_handler: Option<Rc<dyn Fn(String, Option<PathBuf>, bool) + 'static>>,
pub new_window_req_handler: Option<Box<dyn Fn(String, NewWindowFeatures) -> NewWindowResponse>>,
pub clipboard: bool,
pub devtools: bool,
pub accept_first_mouse: bool,
pub back_forward_navigation_gestures: bool,
pub document_title_changed_handler: Option<Box<dyn Fn(String)>>,
pub incognito: bool,
pub autoplay: bool,
pub on_page_load_handler: Option<Box<dyn Fn(PageLoadEvent, String)>>,
pub proxy_config: Option<ProxyConfig>,
pub focused: bool,
pub bounds: Option<Rect>,
pub background_throttling: Option<BackgroundThrottlingPolicy>,
pub javascript_disabled: bool,
pub general_autofill_enabled: bool,
}
impl Default for WebViewAttributes<'_> {
fn default() -> Self {
Self {
id: Default::default(),
context: None,
user_agent: None,
visible: true,
transparent: false,
background_color: None,
url: None,
headers: None,
html: None,
initialization_scripts: Default::default(),
custom_protocols: Default::default(),
ipc_handler: None,
drag_drop_handler: None,
navigation_handler: None,
download_started_handler: Some(Box::new(|_, _| true)),
download_completed_handler: None,
new_window_req_handler: None,
clipboard: false,
#[cfg(debug_assertions)]
devtools: true,
#[cfg(not(debug_assertions))]
devtools: false,
zoom_hotkeys_enabled: false,
accept_first_mouse: false,
back_forward_navigation_gestures: false,
document_title_changed_handler: None,
incognito: false,
autoplay: true,
on_page_load_handler: None,
proxy_config: None,
focused: true,
bounds: Some(Rect {
position: dpi::LogicalPosition::new(0, 0).into(),
size: dpi::LogicalSize::new(200, 200).into(),
}),
background_throttling: None,
javascript_disabled: false,
general_autofill_enabled: true,
}
}
}
pub struct WebViewBuilder<'a> {
attrs: WebViewAttributes<'a>,
platform_specific: PlatformSpecificWebViewAttributes,
error: crate::Result<()>,
}
impl<'a> WebViewBuilder<'a> {
pub fn new() -> Self {
Self {
attrs: WebViewAttributes::default(),
#[allow(clippy::default_constructed_unit_structs)]
platform_specific: PlatformSpecificWebViewAttributes::default(),
error: Ok(()),
}
}
pub fn new_with_web_context(web_context: &'a mut WebContext) -> Self {
let attrs = WebViewAttributes {
context: Some(web_context),
..Default::default()
};
Self {
attrs,
#[allow(clippy::default_constructed_unit_structs)]
platform_specific: PlatformSpecificWebViewAttributes::default(),
error: Ok(()),
}
}
pub fn with_id(mut self, id: WebViewId<'a>) -> Self {
self.attrs.id = Some(id);
self
}
pub fn with_back_forward_navigation_gestures(mut self, gesture: bool) -> Self {
self.attrs.back_forward_navigation_gestures = gesture;
self
}
pub fn with_transparent(mut self, transparent: bool) -> Self {
self.attrs.transparent = transparent;
self
}
pub fn with_background_color(mut self, background_color: RGBA) -> Self {
self.attrs.background_color = Some(background_color);
self
}
pub fn with_visible(mut self, visible: bool) -> Self {
self.attrs.visible = visible;
self
}
pub fn with_autoplay(mut self, autoplay: bool) -> Self {
self.attrs.autoplay = autoplay;
self
}
pub fn with_initialization_script<S: Into<String>>(self, js: S) -> Self {
self.with_initialization_script_for_main_only(js, true)
}
pub fn with_initialization_script_for_main_only<S: Into<String>>(
mut self,
js: S,
for_main_frame_only: bool,
) -> Self {
let script = js.into();
if !script.is_empty() {
self
.attrs
.initialization_scripts
.push(InitializationScript {
script,
for_main_frame_only,
});
}
self
}
#[cfg(feature = "protocol")]
pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
where
F: Fn(WebViewId, Request<Vec<u8>>) -> Response<Cow<'static, [u8]>> + 'static,
{
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
if let Some(context) = &mut self.attrs.context {
if context.is_custom_protocol_registered(&name) {
let err = Err(crate::Error::DuplicateCustomProtocol(name));
self.error = self.error.and(err);
return self;
}
}
if self.attrs.custom_protocols.contains_key(&name) {
let err = Err(crate::Error::DuplicateCustomProtocol(name));
self.error = self.error.and(err);
return self;
}
self.attrs.custom_protocols.insert(
name,
Box::new(move |id, request, responder| {
let http_response = handler(id, request);
responder.respond(http_response);
}),
);
self
}
#[cfg(feature = "protocol")]
pub fn with_asynchronous_custom_protocol<F>(mut self, name: String, handler: F) -> Self
where
F: Fn(WebViewId, Request<Vec<u8>>, RequestAsyncResponder) + 'static,
{
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
if let Some(context) = &mut self.attrs.context {
if context.is_custom_protocol_registered(&name) {
let err = Err(crate::Error::DuplicateCustomProtocol(name));
self.error = self.error.and(err);
return self;
}
}
if self.attrs.custom_protocols.contains_key(&name) {
let err = Err(crate::Error::DuplicateCustomProtocol(name));
self.error = self.error.and(err);
return self;
}
self.attrs.custom_protocols.insert(name, Box::new(handler));
self
}
pub fn with_ipc_handler<F>(mut self, handler: F) -> Self
where
F: Fn(Request<String>) + 'static,
{
self.attrs.ipc_handler = Some(Box::new(handler));
self
}
pub fn with_drag_drop_handler<F>(mut self, handler: F) -> Self
where
F: Fn(DragDropEvent) -> bool + 'static,
{
self.attrs.drag_drop_handler = Some(Box::new(handler));
self
}
pub fn with_url_and_headers(mut self, url: impl Into<String>, headers: http::HeaderMap) -> Self {
self.attrs.url = Some(url.into());
self.attrs.headers = Some(headers);
self
}
pub fn with_url(mut self, url: impl Into<String>) -> Self {
self.attrs.url = Some(url.into());
self.attrs.headers = None;
self
}
pub fn with_headers(mut self, headers: http::HeaderMap) -> Self {
self.attrs.headers = Some(headers);
self
}
pub fn with_html(mut self, html: impl Into<String>) -> Self {
self.attrs.html = Some(html.into());
self
}
pub fn with_user_agent(mut self, user_agent: impl Into<String>) -> Self {
self.attrs.user_agent = Some(user_agent.into());
self
}
pub fn with_devtools(mut self, devtools: bool) -> Self {
self.attrs.devtools = devtools;
self
}
pub fn with_hotkeys_zoom(mut self, zoom: bool) -> Self {
self.attrs.zoom_hotkeys_enabled = zoom;
self
}
pub fn with_navigation_handler(mut self, callback: impl Fn(String) -> bool + 'static) -> Self {
self.attrs.navigation_handler = Some(Box::new(callback));
self
}
pub fn with_download_started_handler(
mut self,
download_started_handler: impl FnMut(String, &mut PathBuf) -> bool + 'static,
) -> Self {
self.attrs.download_started_handler = Some(Box::new(download_started_handler));
self
}
pub fn with_download_completed_handler(
mut self,
download_completed_handler: impl Fn(String, Option<PathBuf>, bool) + 'static,
) -> Self {
self.attrs.download_completed_handler = Some(Rc::new(download_completed_handler));
self
}
pub fn with_clipboard(mut self, clipboard: bool) -> Self {
self.attrs.clipboard = clipboard;
self
}
pub fn with_new_window_req_handler(
mut self,
callback: impl Fn(String, NewWindowFeatures) -> NewWindowResponse + 'static,
) -> Self {
self.attrs.new_window_req_handler = Some(Box::new(callback));
self
}
pub fn with_accept_first_mouse(mut self, accept_first_mouse: bool) -> Self {
self.attrs.accept_first_mouse = accept_first_mouse;
self
}
pub fn with_document_title_changed_handler(
mut self,
callback: impl Fn(String) + 'static,
) -> Self {
self.attrs.document_title_changed_handler = Some(Box::new(callback));
self
}
pub fn with_incognito(mut self, incognito: bool) -> Self {
self.attrs.incognito = incognito;
self
}
pub fn with_on_page_load_handler(
mut self,
handler: impl Fn(PageLoadEvent, String) + 'static,
) -> Self {
self.attrs.on_page_load_handler = Some(Box::new(handler));
self
}
pub fn with_proxy_config(mut self, configuration: ProxyConfig) -> Self {
self.attrs.proxy_config = Some(configuration);
self
}
pub fn with_focused(mut self, focused: bool) -> Self {
self.attrs.focused = focused;
self
}
pub fn with_bounds(mut self, bounds: Rect) -> Self {
self.attrs.bounds = Some(bounds);
self
}
pub fn with_background_throttling(mut self, policy: BackgroundThrottlingPolicy) -> Self {
self.attrs.background_throttling = Some(policy);
self
}
pub fn with_javascript_disabled(mut self) -> Self {
self.attrs.javascript_disabled = true;
self
}
pub fn with_general_autofill_enabled(mut self, enabled: bool) -> Self {
self.attrs.general_autofill_enabled = enabled;
self
}
pub fn build<W: HasWindowHandle>(self, window: &'a W) -> Result<WebView> {
self.error?;
InnerWebView::new(window, self.attrs, self.platform_specific).map(|webview| WebView { webview })
}
pub fn build_as_child<W: HasWindowHandle>(self, window: &'a W) -> Result<WebView> {
self.error?;
InnerWebView::new_as_child(window, self.attrs, self.platform_specific)
.map(|webview| WebView { webview })
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) struct PlatformSpecificWebViewAttributes {
data_store_identifier: Option<[u8; 16]>,
traffic_light_inset: Option<dpi::Position>,
allow_link_preview: bool,
on_web_content_process_terminate_handler: Option<Box<dyn Fn()>>,
#[cfg(target_os = "ios")]
input_accessory_view_builder: Option<Box<InputAccessoryViewBuilder>>,
#[cfg(target_os = "ios")]
limit_navigations_to_app_bound_domains: bool,
#[cfg(target_os = "macos")]
webview_configuration: Option<Retained<objc2_web_kit::WKWebViewConfiguration>>,
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
impl Default for PlatformSpecificWebViewAttributes {
fn default() -> Self {
Self {
data_store_identifier: None,
traffic_light_inset: None,
allow_link_preview: true,
on_web_content_process_terminate_handler: None,
#[cfg(target_os = "ios")]
input_accessory_view_builder: None,
#[cfg(target_os = "ios")]
limit_navigations_to_app_bound_domains: false,
#[cfg(target_os = "macos")]
webview_configuration: None,
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub trait WebViewBuilderExtDarwin {
fn with_data_store_identifier(self, identifier: [u8; 16]) -> Self;
fn with_traffic_light_inset<P: Into<dpi::Position>>(self, position: P) -> Self;
fn with_allow_link_preview(self, allow_link_preview: bool) -> Self;
fn with_on_web_content_process_terminate_handler(self, handler: impl Fn() + 'static) -> Self;
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
impl WebViewBuilderExtDarwin for WebViewBuilder<'_> {
fn with_data_store_identifier(mut self, identifier: [u8; 16]) -> Self {
self.platform_specific.data_store_identifier = Some(identifier);
self
}
fn with_traffic_light_inset<P: Into<dpi::Position>>(mut self, position: P) -> Self {
self.platform_specific.traffic_light_inset = Some(position.into());
self
}
fn with_allow_link_preview(mut self, allow_link_preview: bool) -> Self {
self.platform_specific.allow_link_preview = allow_link_preview;
self
}
fn with_on_web_content_process_terminate_handler(mut self, handler: impl Fn() + 'static) -> Self {
self
.platform_specific
.on_web_content_process_terminate_handler = Some(Box::new(handler));
self
}
}
#[cfg(target_os = "macos")]
pub trait WebViewBuilderExtMacos {
fn with_webview_configuration(
self,
configuration: Retained<objc2_web_kit::WKWebViewConfiguration>,
) -> Self;
}
#[cfg(target_os = "macos")]
impl WebViewBuilderExtMacos for WebViewBuilder<'_> {
fn with_webview_configuration(
mut self,
configuration: Retained<objc2_web_kit::WKWebViewConfiguration>,
) -> Self {
self
.platform_specific
.webview_configuration
.replace(configuration);
self
}
}
#[cfg(target_os = "ios")]
pub trait WebViewBuilderExtIos {
fn with_input_accessory_view_builder<
F: Fn(&objc2_ui_kit::UIView) -> Option<Retained<objc2_ui_kit::UIView>> + 'static,
>(
self,
builder: F,
) -> Self;
fn with_limit_navigations_to_app_bound_domains(self, limit_navigations: bool) -> Self;
}
#[cfg(target_os = "ios")]
impl WebViewBuilderExtIos for WebViewBuilder<'_> {
fn with_input_accessory_view_builder<
F: Fn(&objc2_ui_kit::UIView) -> Option<Retained<objc2_ui_kit::UIView>> + 'static,
>(
mut self,
builder: F,
) -> Self {
self
.platform_specific
.input_accessory_view_builder
.replace(Box::new(builder));
self
}
fn with_limit_navigations_to_app_bound_domains(mut self, limit_navigations: bool) -> Self {
self
.platform_specific
.limit_navigations_to_app_bound_domains = limit_navigations;
self
}
}
#[cfg(windows)]
#[derive(Clone)]
pub(crate) struct PlatformSpecificWebViewAttributes {
additional_browser_args: Option<String>,
browser_accelerator_keys: bool,
theme: Option<Theme>,
use_https: bool,
scroll_bar_style: ScrollBarStyle,
browser_extensions_enabled: bool,
extension_path: Option<PathBuf>,
default_context_menus: bool,
environment: Option<ICoreWebView2Environment>,
}
#[cfg(windows)]
impl Default for PlatformSpecificWebViewAttributes {
fn default() -> Self {
Self {
additional_browser_args: None,
browser_accelerator_keys: true, default_context_menus: true, theme: None,
use_https: false, scroll_bar_style: ScrollBarStyle::default(),
browser_extensions_enabled: false,
extension_path: None,
environment: None,
}
}
}
#[cfg(windows)]
pub trait WebViewBuilderExtWindows {
fn with_additional_browser_args<S: Into<String>>(self, additional_args: S) -> Self;
fn with_browser_accelerator_keys(self, enabled: bool) -> Self;
fn with_default_context_menus(self, enabled: bool) -> Self;
fn with_theme(self, theme: Theme) -> Self;
fn with_https_scheme(self, enabled: bool) -> Self;
fn with_scroll_bar_style(self, style: ScrollBarStyle) -> Self;
fn with_browser_extensions_enabled(self, enabled: bool) -> Self;
fn with_extensions_path(self, path: impl Into<PathBuf>) -> Self;
fn with_environment(self, environment: ICoreWebView2Environment) -> Self;
}
#[cfg(windows)]
impl WebViewBuilderExtWindows for WebViewBuilder<'_> {
fn with_additional_browser_args<S: Into<String>>(mut self, additional_args: S) -> Self {
self.platform_specific.additional_browser_args = Some(additional_args.into());
self
}
fn with_browser_accelerator_keys(mut self, enabled: bool) -> Self {
self.platform_specific.browser_accelerator_keys = enabled;
self
}
fn with_default_context_menus(mut self, enabled: bool) -> Self {
self.platform_specific.default_context_menus = enabled;
self
}
fn with_theme(mut self, theme: Theme) -> Self {
self.platform_specific.theme = Some(theme);
self
}
fn with_https_scheme(mut self, enabled: bool) -> Self {
self.platform_specific.use_https = enabled;
self
}
fn with_scroll_bar_style(mut self, style: ScrollBarStyle) -> Self {
self.platform_specific.scroll_bar_style = style;
self
}
fn with_browser_extensions_enabled(mut self, enabled: bool) -> Self {
self.platform_specific.browser_extensions_enabled = enabled;
self
}
fn with_extensions_path(mut self, path: impl Into<PathBuf>) -> Self {
self.platform_specific.extension_path = Some(path.into());
self
}
fn with_environment(mut self, environment: ICoreWebView2Environment) -> Self {
self.platform_specific.environment.replace(environment);
self
}
}
#[cfg(target_os = "android")]
#[derive(Default)]
pub(crate) struct PlatformSpecificWebViewAttributes {
on_webview_created: Option<
std::sync::Arc<
dyn Fn(prelude::Context) -> std::result::Result<(), jni::errors::Error>
+ Send
+ Sync
+ 'static,
>,
>,
with_asset_loader: bool,
asset_loader_domain: Option<String>,
https_scheme: bool,
}
#[cfg(target_os = "android")]
pub trait WebViewBuilderExtAndroid {
fn on_webview_created<
F: Fn(prelude::Context<'_, '_>) -> std::result::Result<(), jni::errors::Error>
+ Send
+ Sync
+ 'static,
>(
self,
f: F,
) -> Self;
#[cfg(feature = "protocol")]
fn with_asset_loader(self, protocol: String) -> Self;
fn with_https_scheme(self, enabled: bool) -> Self;
}
#[cfg(target_os = "android")]
impl WebViewBuilderExtAndroid for WebViewBuilder<'_> {
fn on_webview_created<
F: Fn(prelude::Context<'_, '_>) -> std::result::Result<(), jni::errors::Error>
+ Send
+ Sync
+ 'static,
>(
mut self,
f: F,
) -> Self {
self.platform_specific.on_webview_created = Some(std::sync::Arc::new(f));
self
}
#[cfg(feature = "protocol")]
fn with_asset_loader(mut self, protocol: String) -> Self {
self.attrs.custom_protocols.insert(
protocol.clone(),
Box::new(|_, _, api| {
api.respond(Response::builder().body(Vec::new()).unwrap());
}),
);
self.platform_specific.with_asset_loader = true;
self.platform_specific.asset_loader_domain = Some(format!("{}.assets", protocol));
self
}
fn with_https_scheme(mut self, enabled: bool) -> Self {
self.platform_specific.https_scheme = enabled;
self
}
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
#[derive(Default)]
pub(crate) struct PlatformSpecificWebViewAttributes {
extension_path: Option<PathBuf>,
related_view: Option<webkit2gtk::WebView>,
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
pub trait WebViewBuilderExtUnix<'a> {
fn build_gtk<W>(self, widget: &'a W) -> Result<WebView>
where
W: gtk::prelude::IsA<gtk::Container>;
fn with_extensions_path(self, path: impl Into<PathBuf>) -> Self;
fn with_related_view(self, webview: webkit2gtk::WebView) -> Self;
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
impl<'a> WebViewBuilderExtUnix<'a> for WebViewBuilder<'a> {
fn build_gtk<W>(self, widget: &'a W) -> Result<WebView>
where
W: gtk::prelude::IsA<gtk::Container>,
{
self.error?;
InnerWebView::new_gtk(widget, self.attrs, self.platform_specific)
.map(|webview| WebView { webview })
}
fn with_extensions_path(mut self, path: impl Into<PathBuf>) -> Self {
self.platform_specific.extension_path = Some(path.into());
self
}
fn with_related_view(mut self, webview: webkit2gtk::WebView) -> Self {
self.platform_specific.related_view.replace(webview);
self
}
}
pub struct WebView {
webview: InnerWebView,
}
impl WebView {
pub fn id(&self) -> WebViewId<'_> {
self.webview.id()
}
pub fn url(&self) -> Result<String> {
self.webview.url()
}
pub fn evaluate_script(&self, js: &str) -> Result<()> {
self
.webview
.eval(js, None::<Box<dyn Fn(String) + Send + 'static>>)
}
pub fn evaluate_script_with_callback(
&self,
js: &str,
callback: impl Fn(String) + Send + 'static,
) -> Result<()> {
self.webview.eval(js, Some(callback))
}
pub fn print(&self) -> Result<()> {
self.webview.print()
}
pub fn cookies_for_url(&self, url: &str) -> Result<Vec<cookie::Cookie<'static>>> {
self.webview.cookies_for_url(url)
}
pub fn cookies(&self) -> Result<Vec<cookie::Cookie<'static>>> {
self.webview.cookies()
}
pub fn set_cookie(&self, cookie: &cookie::Cookie<'_>) -> Result<()> {
self.webview.set_cookie(cookie)
}
pub fn delete_cookie(&self, cookie: &cookie::Cookie<'_>) -> Result<()> {
self.webview.delete_cookie(cookie)
}
#[cfg(any(debug_assertions, feature = "devtools"))]
pub fn open_devtools(&self) {
self.webview.open_devtools()
}
#[cfg(any(debug_assertions, feature = "devtools"))]
pub fn close_devtools(&self) {
self.webview.close_devtools()
}
#[cfg(any(debug_assertions, feature = "devtools"))]
pub fn is_devtools_open(&self) -> bool {
self.webview.is_devtools_open()
}
pub fn zoom(&self, scale_factor: f64) -> Result<()> {
self.webview.zoom(scale_factor)
}
pub fn set_background_color(&self, background_color: RGBA) -> Result<()> {
self.webview.set_background_color(background_color)
}
pub fn load_url(&self, url: &str) -> Result<()> {
self.webview.load_url(url)
}
pub fn reload(&self) -> crate::Result<()> {
self.webview.reload()
}
pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) -> Result<()> {
self.webview.load_url_with_headers(url, headers)
}
pub fn load_html(&self, html: &str) -> Result<()> {
self.webview.load_html(html)
}
pub fn clear_all_browsing_data(&self) -> Result<()> {
self.webview.clear_all_browsing_data()
}
pub fn bounds(&self) -> Result<Rect> {
self.webview.bounds()
}
pub fn set_bounds(&self, bounds: Rect) -> Result<()> {
self.webview.set_bounds(bounds)
}
pub fn set_visible(&self, visible: bool) -> Result<()> {
self.webview.set_visible(visible)
}
pub fn focus(&self) -> Result<()> {
self.webview.focus()
}
pub fn focus_parent(&self) -> Result<()> {
self.webview.focus_parent()
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum DragDropEvent {
Enter {
paths: Vec<PathBuf>,
position: (i32, i32),
},
Over {
position: (i32, i32),
},
Drop {
paths: Vec<PathBuf>,
position: (i32, i32),
},
Leave,
}
#[cfg(feature = "os-webview")]
#[cfg_attr(docsrs, doc(cfg(feature = "os-webview")))]
pub fn webview_version() -> Result<String> {
platform_webview_version()
}
#[cfg(target_os = "windows")]
#[non_exhaustive]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MemoryUsageLevel {
#[default]
Normal,
Low,
}
#[cfg(target_os = "windows")]
pub trait WebViewExtWindows {
fn controller(&self) -> ICoreWebView2Controller;
fn environment(&self) -> ICoreWebView2Environment;
fn webview(&self) -> ICoreWebView2;
fn set_theme(&self, theme: Theme) -> Result<()>;
fn set_memory_usage_level(&self, level: MemoryUsageLevel) -> Result<()>;
fn reparent(&self, hwnd: isize) -> Result<()>;
}
#[cfg(target_os = "windows")]
impl WebViewExtWindows for WebView {
fn controller(&self) -> ICoreWebView2Controller {
self.webview.controller.clone()
}
fn environment(&self) -> ICoreWebView2Environment {
self.webview.env.clone()
}
fn webview(&self) -> ICoreWebView2 {
self.webview.webview.clone()
}
fn set_theme(&self, theme: Theme) -> Result<()> {
self.webview.set_theme(theme)
}
fn set_memory_usage_level(&self, level: MemoryUsageLevel) -> Result<()> {
self.webview.set_memory_usage_level(level)
}
fn reparent(&self, hwnd: isize) -> Result<()> {
self.webview.reparent(hwnd)
}
}
#[cfg(gtk)]
pub trait WebViewExtUnix: Sized {
fn new_gtk<W>(widget: &W) -> Result<Self>
where
W: gtk::prelude::IsA<gtk::Container>;
fn webview(&self) -> webkit2gtk::WebView;
fn reparent<W>(&self, widget: &W) -> Result<()>
where
W: gtk::prelude::IsA<gtk::Container>;
}
#[cfg(gtk)]
impl WebViewExtUnix for WebView {
fn new_gtk<W>(widget: &W) -> Result<Self>
where
W: gtk::prelude::IsA<gtk::Container>,
{
WebViewBuilder::new().build_gtk(widget)
}
fn webview(&self) -> webkit2gtk::WebView {
self.webview.webview.clone()
}
fn reparent<W>(&self, widget: &W) -> Result<()>
where
W: gtk::prelude::IsA<gtk::Container>,
{
self.webview.reparent(widget)
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub trait WebViewExtDarwin {
fn print_with_options(&self, options: &PrintOptions) -> Result<()>;
fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(cb: F) -> Result<()>;
fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(uuid: &[u8; 16], cb: F);
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
impl WebViewExtDarwin for WebView {
fn print_with_options(&self, options: &PrintOptions) -> Result<()> {
self.webview.print_with_options(options)
}
fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(cb: F) -> Result<()> {
wkwebview::InnerWebView::fetch_data_store_identifiers(cb)
}
fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(uuid: &[u8; 16], cb: F) {
wkwebview::InnerWebView::remove_data_store(uuid, cb)
}
}
#[cfg(target_os = "macos")]
pub trait WebViewExtMacOS {
fn webview(&self) -> Retained<WryWebView>;
fn manager(&self) -> Retained<WKUserContentController>;
fn ns_window(&self) -> Retained<NSWindow>;
fn reparent(&self, window: *mut NSWindow) -> Result<()>;
fn print_with_options(&self, options: &PrintOptions) -> Result<()>;
fn set_traffic_light_inset<P: Into<dpi::Position>>(&self, position: P) -> Result<()>;
}
#[cfg(target_os = "macos")]
impl WebViewExtMacOS for WebView {
fn webview(&self) -> Retained<WryWebView> {
self.webview.webview.clone()
}
fn manager(&self) -> Retained<WKUserContentController> {
self.webview.manager.clone()
}
fn ns_window(&self) -> Retained<NSWindow> {
self.webview.webview.window().unwrap()
}
fn reparent(&self, window: *mut NSWindow) -> Result<()> {
self.webview.reparent(window)
}
fn print_with_options(&self, options: &PrintOptions) -> Result<()> {
self.webview.print_with_options(options)
}
fn set_traffic_light_inset<P: Into<dpi::Position>>(&self, position: P) -> Result<()> {
self.webview.set_traffic_light_inset(position.into())
}
}
#[cfg(target_os = "ios")]
pub trait WebViewExtIOS {
fn webview(&self) -> Retained<WryWebView>;
fn manager(&self) -> Retained<WKUserContentController>;
}
#[cfg(target_os = "ios")]
impl WebViewExtIOS for WebView {
fn webview(&self) -> Retained<WryWebView> {
self.webview.webview.clone()
}
fn manager(&self) -> Retained<WKUserContentController> {
self.webview.manager.clone()
}
}
#[cfg(target_os = "android")]
pub trait WebViewExtAndroid {
fn handle(&self) -> JniHandle;
}
#[cfg(target_os = "android")]
impl WebViewExtAndroid for WebView {
fn handle(&self) -> JniHandle {
JniHandle {
activity_id: self.webview.activity_id,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Theme {
Dark,
Light,
Auto,
}
pub type RGBA = (u8, u8, u8, u8);
pub enum PageLoadEvent {
Started,
Finished,
}
#[derive(Debug, Clone)]
pub enum BackgroundThrottlingPolicy {
Disabled,
Suspend,
Throttle,
}
#[derive(Debug, Clone)]
pub struct InitializationScript {
pub script: String,
pub for_main_frame_only: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg_attr(miri, ignore)]
fn should_get_webview_version() {
if let Err(error) = webview_version() {
panic!("{}", error);
}
}
}