use crate::{LogLevel, WebViewError};
use std::future::Future;
use std::path::PathBuf;
use std::pin::Pin;
use std::sync::Arc;
#[derive(Debug)]
pub enum SchemeOutcome {
Handled(WebResourceResponse),
PassThrough,
}
pub(crate) type AsyncSchemeFuture = Pin<Box<dyn Future<Output = SchemeOutcome> + Send + 'static>>;
pub(crate) type AsyncSchemeHandler =
Arc<dyn Fn(http::Request<Vec<u8>>) -> AsyncSchemeFuture + Send + Sync>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NavigationPolicy {
Allow,
Cancel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NewWindowPolicy {
LoadInSelf,
Cancel,
}
pub type NavigationHandler = Box<dyn Fn(&str) -> NavigationPolicy + Send + Sync>;
pub type NewWindowHandler = Box<dyn Fn(&str) -> NewWindowPolicy + Send + Sync>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DownloadRequest {
pub url: String,
pub user_agent: Option<String>,
pub content_disposition: Option<String>,
pub mime_type: Option<String>,
pub content_length: Option<u64>,
pub suggested_filename: Option<String>,
pub source_page_url: Option<String>,
pub cookie: Option<String>,
}
pub type DownloadHandler = Box<dyn Fn(DownloadRequest) + Send + Sync>;
#[derive(Debug)]
pub enum WebResourceBody {
Path(PathBuf),
Pipe(SystemPipeReader),
Bytes(Vec<u8>),
}
#[derive(Debug)]
pub struct SystemPipeReader {
#[cfg(unix)]
fd: std::os::fd::RawFd,
}
impl SystemPipeReader {
#[cfg(unix)]
pub fn into_raw_fd(self) -> std::os::fd::RawFd {
self.fd
}
#[cfg(unix)]
pub unsafe fn from_raw_fd(fd: std::os::fd::RawFd) -> Self {
Self { fd }
}
#[cfg(unix)]
pub fn into_file(self) -> std::fs::File {
use std::os::fd::FromRawFd;
unsafe { std::fs::File::from_raw_fd(self.into_raw_fd()) }
}
}
pub trait WebViewController: Send + Sync {
fn load_url(&self, url: &str) -> Result<(), WebViewError>;
fn load_data(&self, request: LoadDataRequest<'_>) -> Result<(), WebViewError>;
fn evaluate_javascript(&self, js: &str) -> Result<(), WebViewError>;
fn post_message(&self, message: &str) -> Result<(), WebViewError>;
fn clear_browsing_data(&self) -> Result<(), WebViewError>;
fn set_user_agent(&self, ua: &str) -> Result<(), WebViewError>;
}
#[derive(Debug, Clone, Copy)]
pub struct LoadDataRequest<'a> {
pub data: &'a str,
pub base_url: &'a str,
pub history_url: Option<&'a str>,
}
impl<'a> LoadDataRequest<'a> {
pub fn new(data: &'a str, base_url: &'a str) -> Self {
Self {
data,
base_url,
history_url: None,
}
}
pub fn with_history_url(mut self, history_url: &'a str) -> Self {
self.history_url = Some(history_url);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoadErrorKind {
Dns,
Network,
Timeout,
Security,
Cancelled,
InvalidUrl,
NotFound,
Unknown,
}
#[derive(Debug, Clone)]
pub struct LoadError {
pub url: Option<String>,
pub kind: LoadErrorKind,
pub description: String,
}
pub trait WebViewDelegate: Send + Sync {
fn on_page_started(&self);
fn on_page_finished(&self);
fn on_load_error(&self, _error: &LoadError) {}
fn handle_post_message(&self, msg: String);
fn log(&self, level: LogLevel, message: &str);
}
#[derive(Debug)]
pub struct WebResourceResponse {
parts: http::response::Parts,
body: WebResourceBody,
}
impl From<Option<WebResourceResponse>> for SchemeOutcome {
fn from(value: Option<WebResourceResponse>) -> Self {
match value {
Some(response) => SchemeOutcome::Handled(response),
None => SchemeOutcome::PassThrough,
}
}
}
impl WebResourceResponse {
pub fn parts(&self) -> &http::response::Parts {
&self.parts
}
pub fn into_parts(self) -> (http::response::Parts, WebResourceBody) {
(self.parts, self.body)
}
}
impl From<(http::response::Parts, PathBuf)> for WebResourceResponse {
fn from(value: (http::response::Parts, PathBuf)) -> Self {
WebResourceResponse {
parts: value.0,
body: WebResourceBody::Path(value.1),
}
}
}
impl From<(http::response::Parts, SystemPipeReader)> for WebResourceResponse {
fn from(value: (http::response::Parts, SystemPipeReader)) -> Self {
WebResourceResponse {
parts: value.0,
body: WebResourceBody::Pipe(value.1),
}
}
}
impl From<(http::response::Parts, Vec<u8>)> for WebResourceResponse {
fn from(value: (http::response::Parts, Vec<u8>)) -> Self {
WebResourceResponse {
parts: value.0,
body: WebResourceBody::Bytes(value.1),
}
}
}
impl WebResourceResponse {
fn response_parts_with_status(status: u16) -> http::response::Parts {
let response = match http::Response::builder().status(status).body(()) {
Ok(response) => response,
Err(_) => http::Response::new(()),
};
let (parts, _) = response.into_parts();
parts
}
pub fn file(path: impl Into<PathBuf>) -> Self {
let path = path.into();
let content_length = std::fs::metadata(&path).ok().map(|m| m.len());
let mut parts = Self::response_parts_with_status(200);
if let Some(len) = content_length {
parts
.headers
.insert(http::header::CONTENT_LENGTH, http::HeaderValue::from(len));
}
Self {
parts,
body: WebResourceBody::Path(path),
}
}
pub fn bytes(data: impl Into<Vec<u8>>) -> Self {
let data = data.into();
let len = data.len();
let mut parts = Self::response_parts_with_status(200);
parts
.headers
.insert(http::header::CONTENT_LENGTH, http::HeaderValue::from(len));
Self {
parts,
body: WebResourceBody::Bytes(data),
}
}
pub fn stream(reader: SystemPipeReader) -> Self {
let parts = Self::response_parts_with_status(200);
Self {
parts,
body: WebResourceBody::Pipe(reader),
}
}
pub fn mime(mut self, content_type: &str) -> Self {
if let Ok(value) = http::HeaderValue::from_str(content_type) {
self.parts.headers.insert(http::header::CONTENT_TYPE, value);
}
self
}
pub fn status(mut self, code: u16) -> Self {
self.parts.status = http::StatusCode::from_u16(code).unwrap_or(self.parts.status);
self
}
pub fn header(mut self, name: &str, value: &str) -> Self {
if let (Ok(header_name), Ok(header_value)) = (
name.parse::<http::header::HeaderName>(),
http::HeaderValue::from_str(value),
) {
self.parts.headers.insert(header_name, header_value);
}
self
}
pub fn cors(self) -> Self {
self.header("access-control-allow-origin", "null")
}
}