#![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 = "macos", target_os = "ios"))]
#[macro_use]
extern crate objc;
mod error;
mod proxy;
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"))]
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};
#[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::ICoreWebView2Controller;
use std::{borrow::Cow, collections::HashMap, path::PathBuf, rc::Rc};
use http::{Request, Response};
pub use dpi;
pub use error::*;
pub use http;
pub use proxy::{ProxyConfig, ProxyEndpoint};
pub use web_context::WebContext;
#[derive(Clone, Copy, Debug)]
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 struct WebViewAttributes {
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<String>,
pub custom_protocols: HashMap<String, Box<dyn Fn(Request<Vec<u8>>, RequestAsyncResponder)>>,
pub ipc_handler: Option<Box<dyn Fn(Request<String>)>>,
#[cfg(feature = "drag-drop")]
#[cfg_attr(docsrs, doc(cfg(feature = "drag-drop")))]
pub drag_drop_handler: Option<Box<dyn Fn(DragDropEvent) -> bool>>,
#[cfg(not(feature = "drag-drop"))]
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>>,
pub download_completed_handler: Option<Rc<dyn Fn(String, Option<PathBuf>, bool) + 'static>>,
pub new_window_req_handler: Option<Box<dyn Fn(String) -> bool>>,
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>,
}
impl Default for WebViewAttributes {
fn default() -> Self {
Self {
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: None,
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(),
}),
}
}
}
pub struct WebViewBuilder<'a> {
pub attrs: WebViewAttributes,
as_child: bool,
window: Option<&'a dyn HasWindowHandle>,
platform_specific: PlatformSpecificWebViewAttributes,
web_context: Option<&'a mut WebContext>,
#[cfg(gtk)]
gtk_widget: Option<&'a gtk::Container>,
}
impl<'a> WebViewBuilder<'a> {
pub fn new(window: &'a impl HasWindowHandle) -> Self {
Self {
attrs: WebViewAttributes::default(),
window: Some(window),
as_child: false,
#[allow(clippy::default_constructed_unit_structs)]
platform_specific: PlatformSpecificWebViewAttributes::default(),
web_context: None,
#[cfg(gtk)]
gtk_widget: None,
}
}
pub fn new_as_child(parent: &'a impl HasWindowHandle) -> Self {
Self {
attrs: WebViewAttributes::default(),
window: Some(parent),
as_child: true,
#[allow(clippy::default_constructed_unit_structs)]
platform_specific: PlatformSpecificWebViewAttributes::default(),
web_context: None,
#[cfg(gtk)]
gtk_widget: None,
}
}
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(mut self, js: &str) -> Self {
if !js.is_empty() {
self.attrs.initialization_scripts.push(js.to_string());
}
self
}
#[cfg(feature = "protocol")]
pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
where
F: Fn(Request<Vec<u8>>) -> Response<Cow<'static, [u8]>> + 'static,
{
self.attrs.custom_protocols.insert(
name,
Box::new(move |request, responder| {
let http_response = handler(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(Request<Vec<u8>>, RequestAsyncResponder) + 'static,
{
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
}
#[cfg(feature = "drag-drop")]
#[cfg_attr(docsrs, doc(cfg(feature = "drag-drop")))]
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_web_context(mut self, web_context: &'a mut WebContext) -> Self {
self.web_context = Some(web_context);
self
}
pub fn with_user_agent(mut self, user_agent: &str) -> Self {
self.attrs.user_agent = Some(user_agent.to_string());
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,
started_handler: impl FnMut(String, &mut PathBuf) -> bool + 'static,
) -> Self {
self.attrs.download_started_handler = Some(Box::new(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) -> bool + '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.replace(bounds);
self
}
pub fn build(self) -> Result<WebView> {
let webview = if let Some(window) = &self.window {
if self.as_child {
InnerWebView::new_as_child(window, self.attrs, self.platform_specific, self.web_context)?
} else {
InnerWebView::new(window, self.attrs, self.platform_specific, self.web_context)?
}
} else {
#[cfg(gtk)]
if let Some(widget) = self.gtk_widget {
InnerWebView::new_gtk(widget, self.attrs, self.platform_specific, self.web_context)?
} else {
unreachable!()
}
#[cfg(not(gtk))]
unreachable!()
};
Ok(WebView { webview })
}
}
#[cfg(any(target_os = "macos", target_os = "ios",))]
#[derive(Clone)]
pub(crate) struct PlatformSpecificWebViewAttributes {
data_store_identifier: Option<[u8; 16]>,
}
#[cfg(any(target_os = "macos", target_os = "ios",))]
impl Default for PlatformSpecificWebViewAttributes {
fn default() -> Self {
Self {
data_store_identifier: None,
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios",))]
pub trait WebViewBuilderExtDarwin {
fn with_data_store_identifier(self, identifier: [u8; 16]) -> 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
}
}
#[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,
}
#[cfg(windows)]
impl Default for PlatformSpecificWebViewAttributes {
fn default() -> Self {
Self {
additional_browser_args: None,
browser_accelerator_keys: true, theme: None,
use_https: false, scroll_bar_style: ScrollBarStyle::default(),
browser_extensions_enabled: false,
}
}
}
#[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_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;
}
#[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_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
}
}
#[cfg(target_os = "android")]
#[derive(Default)]
pub(crate) struct PlatformSpecificWebViewAttributes {
on_webview_created:
Option<Box<dyn Fn(prelude::Context) -> std::result::Result<(), jni::errors::Error> + Send>>,
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 + '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 + 'static,
>(
mut self,
f: F,
) -> Self {
self.platform_specific.on_webview_created = Some(Box::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",
))]
pub trait WebViewBuilderExtUnix<'a> {
fn new_gtk<W>(widget: &'a W) -> Self
where
W: gtk::prelude::IsA<gtk::Container>;
}
#[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 new_gtk<W>(widget: &'a W) -> Self
where
W: gtk::prelude::IsA<gtk::Container>,
{
use gdkx11::glib::Cast;
Self {
attrs: WebViewAttributes::default(),
window: None,
as_child: false,
#[allow(clippy::default_constructed_unit_structs)]
platform_specific: PlatformSpecificWebViewAttributes::default(),
web_context: None,
gtk_widget: Some(widget.dynamic_cast_ref().unwrap()),
}
}
}
pub struct WebView {
webview: InnerWebView,
}
impl WebView {
pub fn new(window: &impl HasWindowHandle) -> Result<Self> {
WebViewBuilder::new(window).build()
}
pub fn new_as_child(parent: &impl HasWindowHandle) -> Result<Self> {
WebViewBuilder::new_as_child(parent).build()
}
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()
}
#[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 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()
}
}
#[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,
}
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 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 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_gtk(widget).build()
}
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(target_os = "macos")]
pub trait WebViewExtMacOS {
fn webview(&self) -> cocoa::base::id;
fn manager(&self) -> cocoa::base::id;
fn ns_window(&self) -> cocoa::base::id;
fn reparent(&self, window: cocoa::base::id) -> Result<()>;
fn print_with_options(&self, options: &PrintOptions) -> Result<()>;
}
#[cfg(target_os = "macos")]
impl WebViewExtMacOS for WebView {
fn webview(&self) -> cocoa::base::id {
self.webview.webview
}
fn manager(&self) -> cocoa::base::id {
self.webview.manager
}
fn ns_window(&self) -> cocoa::base::id {
unsafe {
let ns_window: cocoa::base::id = msg_send![self.webview.webview, window];
ns_window
}
}
fn reparent(&self, window: cocoa::base::id) -> Result<()> {
self.webview.reparent(window)
}
fn print_with_options(&self, options: &PrintOptions) -> Result<()> {
self.webview.print_with_options(options)
}
}
#[cfg(target_os = "ios")]
pub trait WebViewExtIOS {
fn webview(&self) -> cocoa::base::id;
fn manager(&self) -> cocoa::base::id;
}
#[cfg(target_os = "ios")]
impl WebViewExtIOS for WebView {
fn webview(&self) -> cocoa::base::id {
self.webview.webview
}
fn manager(&self) -> cocoa::base::id {
self.webview.manager
}
}
#[cfg(target_os = "android")]
pub trait WebViewExtAndroid {
fn handle(&self) -> JniHandle;
}
#[cfg(target_os = "android")]
impl WebViewExtAndroid for WebView {
fn handle(&self) -> JniHandle {
JniHandle
}
}
#[derive(Debug, Clone, Copy)]
pub enum Theme {
Dark,
Light,
Auto,
}
pub type RGBA = (u8, u8, u8, u8);
pub enum PageLoadEvent {
Started,
Finished,
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
#[derive(Default)]
pub(crate) struct PlatformSpecificWebViewAttributes;
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg_attr(miri, ignore)]
fn should_get_webview_version() {
if let Err(error) = webview_version() {
panic!("{}", error);
}
}
}