diff --git a/.changes/change_custom_protocol_handler_response_to_cow.md b/.changes/change_custom_protocol_handler_response_to_cow.md new file mode 100644 index 000000000..54daf2e43 --- /dev/null +++ b/.changes/change_custom_protocol_handler_response_to_cow.md @@ -0,0 +1,5 @@ +--- +"wry": "minor" +--- + +Change return type of [custom protocol handlers](https://docs.rs/wry/latest/wry/webview/struct.WebViewBuilder.html#method.with_custom_protocol) from `Result>>` to `Result>>`. This allows the handlers to return static resources without heap allocations. This is effective when you embed some large files like bundled JavaScript source as `&'static [u8]` using [`include_bytes!`](https://doc.rust-lang.org/std/macro.include_bytes.html). diff --git a/examples/custom_protocol.rs b/examples/custom_protocol.rs index 63256177b..703bcec2e 100644 --- a/examples/custom_protocol.rs +++ b/examples/custom_protocol.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +const PAGE1_HTML: &[u8] = include_bytes!("custom_protocol_page1.html"); + fn main() -> wry::Result<()> { use std::{ fs::{canonicalize, read}, @@ -27,21 +29,19 @@ fn main() -> wry::Result<()> { let _webview = WebViewBuilder::new(window) .unwrap() .with_custom_protocol("wry".into(), move |request| { - let path = &request.uri().path(); + let path = request.uri().path(); // Read the file content from file path - let content = read(canonicalize(PathBuf::from("examples").join( - if path == &"/" { - "custom_protocol_page1.html" - } else { - // remove leading slash - &path[1..] - }, - ))?)?; + let content = if path == "/" { + PAGE1_HTML.into() + } else { + // `1..` for removing leading slash + read(canonicalize(PathBuf::from("examples").join(&path[1..]))?)?.into() + }; // Return asset contents and mime types based on file extentions // If you don't want to do this manually, there are some crates for you. // Such as `infer` and `mime_guess`. - let mimetype = if path.ends_with(".html") || path == &"/" { + let mimetype = if path.ends_with(".html") || path == "/" { "text/html" } else if path.ends_with(".js") { "text/javascript" diff --git a/examples/form.rs b/examples/form.rs index fe8ea6ac5..bc9609a07 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -36,7 +36,7 @@ fn main() -> wry::Result<()> { Response::builder() .header(CONTENT_TYPE, "text/html") - .body(read(canonicalize(&path)?)?) + .body(read(canonicalize(&path)?)?.into()) .map_err(Into::into) }) // tell the webview to load the custom protocol diff --git a/examples/menu.rs b/examples/menu.rs index 3ef74e742..0b9b77f23 100644 --- a/examples/menu.rs +++ b/examples/menu.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +const PAGE1_HTML: &[u8] = include_bytes!("custom_protocol_page1.html"); + fn main() -> wry::Result<()> { use std::{ fs::{canonicalize, read}, @@ -53,21 +55,19 @@ fn main() -> wry::Result<()> { let _webview = WebViewBuilder::new(window) .unwrap() .with_custom_protocol("wry".into(), move |request| { - let path = &request.uri().path(); + let path = request.uri().path(); // Read the file content from file path - let content = read(canonicalize(PathBuf::from("examples").join( - if path == &"/" { - "custom_protocol_page1.html" - } else { - // remove leading slash - &path[1..] - }, - ))?)?; + let content = if path == "/" { + PAGE1_HTML.into() + } else { + // `1..` for removing leading slash + read(canonicalize(PathBuf::from("examples").join(&path[1..]))?)?.into() + }; // Return asset contents and mime types based on file extentions // If you don't want to do this manually, there are some crates for you. // Such as `infer` and `mime_guess`. - let (data, meta) = if path.ends_with(".html") || path == &"/" { + let (data, meta) = if path.ends_with(".html") || path == "/" { (content, "text/html") } else if path.ends_with(".js") { (content, "text/javascript") diff --git a/examples/stream_range.rs b/examples/stream_range.rs index 515b851f8..384d7c937 100644 --- a/examples/stream_range.rs +++ b/examples/stream_range.rs @@ -57,7 +57,7 @@ fn main() -> wry::Result<()> { let path = &request.uri().path()[1..]; // Read the file content from file path - let mut content = File::open(canonicalize(&path)?)?; + let mut content = File::open(canonicalize(path)?)?; // Return asset contents and mime types based on file extentions // If you don't want to do this manually, there are some crates for you. @@ -125,7 +125,7 @@ fn main() -> wry::Result<()> { response .header(CONTENT_TYPE, mimetype) .status(status_code) - .body(buf) + .body(buf.into()) .map_err(Into::into) }) // tell the webview to load the custom protocol diff --git a/src/webview/android/mod.rs b/src/webview/android/mod.rs index 9fbdc620a..31b1ffdc3 100644 --- a/src/webview/android/mod.rs +++ b/src/webview/android/mod.rs @@ -13,7 +13,7 @@ use http::{ use kuchiki::NodeRef; use once_cell::sync::OnceCell; use sha2::{Digest, Sha256}; -use std::rc::Rc; +use std::{borrow::Cow, rc::Rc}; use tao::platform::android::ndk_glue::{ jni::{ errors::Error as JniError, @@ -66,9 +66,11 @@ impl UnsafeIpc { unsafe impl Send for UnsafeIpc {} unsafe impl Sync for UnsafeIpc {} -pub struct UnsafeRequestHandler(Box>) -> Option>>>); +pub struct UnsafeRequestHandler( + Box>) -> Option>>>, +); impl UnsafeRequestHandler { - pub fn new(f: Box>) -> Option>>>) -> Self { + pub fn new(f: Box>) -> Option>>>) -> Self { Self(f) } } @@ -182,7 +184,7 @@ impl InnerWebView { { if !initialization_scripts.is_empty() { let mut document = - kuchiki::parse_html().one(String::from_utf8_lossy(&response.body()).into_owned()); + kuchiki::parse_html().one(String::from_utf8_lossy(response.body()).into_owned()); let csp = response.headers_mut().get_mut(CONTENT_SECURITY_POLICY); let mut hashes = Vec::new(); with_html_head(&mut document, |head| { @@ -208,7 +210,7 @@ impl InnerWebView { *csp = HeaderValue::from_str(&csp_string).unwrap(); } - *response.body_mut() = document.to_string().as_bytes().to_vec(); + *response.body_mut() = document.to_string().into_bytes().into(); } } return Some(response); diff --git a/src/webview/mod.rs b/src/webview/mod.rs index be3641418..c9dffb196 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -48,6 +48,7 @@ use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; #[cfg(target_os = "windows")] use windows::{Win32::Foundation::HWND, Win32::UI::WindowsAndMessaging::DestroyWindow}; +use std::borrow::Cow; use std::{path::PathBuf, rc::Rc}; pub use url::Url; @@ -137,7 +138,7 @@ pub struct WebViewAttributes { /// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034 pub custom_protocols: Vec<( String, - Box>) -> Result>>>, + Box>) -> Result>>>, )>, /// Set the IPC handler to receive the message from Javascript on webview to host Rust code. /// The message sent from webview should call `window.ipc.postMessage("insert_message_here");`. @@ -394,7 +395,7 @@ impl<'a> WebViewBuilder<'a> { #[cfg(feature = "protocol")] pub fn with_custom_protocol(mut self, name: String, handler: F) -> Self where - F: Fn(&Request>) -> Result>> + 'static, + F: Fn(&Request>) -> Result>> + 'static, { self .webview diff --git a/src/webview/webkitgtk/web_context.rs b/src/webview/webkitgtk/web_context.rs index 8bf7762a9..4ce54eace 100644 --- a/src/webview/webkitgtk/web_context.rs +++ b/src/webview/webkitgtk/web_context.rs @@ -8,6 +8,7 @@ use crate::{webview::web_context::WebContextData, Error}; use glib::FileError; use http::{header::CONTENT_TYPE, Request, Response}; use std::{ + borrow::Cow, cell::RefCell, collections::{HashSet, VecDeque}, path::PathBuf, @@ -110,7 +111,7 @@ pub trait WebContextExt { /// relying on the platform's implementation to properly handle duplicated scheme handlers. fn register_uri_scheme(&mut self, name: &str, handler: F) -> crate::Result<()> where - F: Fn(&Request>) -> crate::Result>> + 'static; + F: Fn(&Request>) -> crate::Result>> + 'static; /// Register a custom protocol to the web context, only if it is not a duplicate scheme. /// @@ -118,7 +119,7 @@ pub trait WebContextExt { /// function will return `Err(Error::DuplicateCustomProtocol)`. fn try_register_uri_scheme(&mut self, name: &str, handler: F) -> crate::Result<()> where - F: Fn(&Request>) -> crate::Result>> + 'static; + F: Fn(&Request>) -> crate::Result>> + 'static; /// Add a [`WebView`] to the queue waiting to be opened. /// @@ -155,7 +156,7 @@ impl WebContextExt for super::WebContext { fn register_uri_scheme(&mut self, name: &str, handler: F) -> crate::Result<()> where - F: Fn(&Request>) -> crate::Result>> + 'static, + F: Fn(&Request>) -> crate::Result>> + 'static, { actually_register_uri_scheme(self, name, handler)?; if self.os.registered_protocols.insert(name.to_string()) { @@ -167,7 +168,7 @@ impl WebContextExt for super::WebContext { fn try_register_uri_scheme(&mut self, name: &str, handler: F) -> crate::Result<()> where - F: Fn(&Request>) -> crate::Result>> + 'static, + F: Fn(&Request>) -> crate::Result>> + 'static, { if self.os.registered_protocols.insert(name.to_string()) { actually_register_uri_scheme(self, name, handler) @@ -275,7 +276,7 @@ fn actually_register_uri_scheme( handler: F, ) -> crate::Result<()> where - F: Fn(&Request>) -> crate::Result>> + 'static, + F: Fn(&Request>) -> crate::Result>> + 'static, { use webkit2gtk::traits::*; let context = &context.os.context; diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index ae2f19444..f29e8a32d 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -18,6 +18,7 @@ use cocoa::{ }; use std::{ + borrow::Cow, ffi::{c_void, CStr}, os::raw::c_char, ptr::{null, null_mut}, @@ -77,7 +78,7 @@ pub(crate) struct InnerWebView { #[cfg(target_os = "macos")] file_drop_ptr: *mut (Box bool>, Rc), download_delegate: id, - protocol_ptrs: Vec<*mut Box>) -> Result>>>>, + protocol_ptrs: Vec<*mut Box>) -> Result>>>>, } impl InnerWebView { diff --git a/src/webview/wkwebview/web_context.rs b/src/webview/wkwebview/web_context.rs index d552a9ec9..f80030555 100644 --- a/src/webview/wkwebview/web_context.rs +++ b/src/webview/wkwebview/web_context.rs @@ -6,10 +6,11 @@ use crate::Result; use crate::webview::web_context::WebContextData; use http::{Request, Response}; +use std::borrow::Cow; #[derive(Debug)] pub struct WebContextImpl { - protocols: Vec<*mut Box>) -> Result>>>>, + protocols: Vec<*mut Box>) -> Result>>>>, } impl WebContextImpl { @@ -23,7 +24,7 @@ impl WebContextImpl { pub fn registered_protocols( &mut self, - handler: *mut Box>) -> Result>>>, + handler: *mut Box>) -> Result>>>, ) { self.protocols.push(handler); }