Skip to content

Commit

Permalink
perf: Change return type of custom protocol handler from Vec<u8> to…
Browse files Browse the repository at this point in the history
… `Cow<'static, [u8]>`, closes #796 (#797)

* perf: Change return type of custom protocol handler from `Vec<u8>` to `Cow<'static, [u8]>`

to allow returning static resources without heap allocation

* chore(examples): Fix some comments

following the review #797 (review)

* fix(android): Change return type of response to `Cow`
  • Loading branch information
rhysd authored Dec 12, 2022
1 parent d458396 commit ddd3461
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 38 deletions.
5 changes: 5 additions & 0 deletions .changes/change_custom_protocol_handler_response_to_cow.md
Original file line number Diff line number Diff line change
@@ -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<Response<Vec<u8>>>` to `Result<Response<Cow<'static, [u8]>>>`. 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).
20 changes: 10 additions & 10 deletions examples/custom_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion examples/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 10 additions & 10 deletions examples/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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")
Expand Down
4 changes: 2 additions & 2 deletions examples/stream_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
12 changes: 7 additions & 5 deletions src/webview/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -66,9 +66,11 @@ impl UnsafeIpc {
unsafe impl Send for UnsafeIpc {}
unsafe impl Sync for UnsafeIpc {}

pub struct UnsafeRequestHandler(Box<dyn Fn(Request<Vec<u8>>) -> Option<Response<Vec<u8>>>>);
pub struct UnsafeRequestHandler(
Box<dyn Fn(Request<Vec<u8>>) -> Option<Response<Cow<'static, [u8]>>>>,
);
impl UnsafeRequestHandler {
pub fn new(f: Box<dyn Fn(Request<Vec<u8>>) -> Option<Response<Vec<u8>>>>) -> Self {
pub fn new(f: Box<dyn Fn(Request<Vec<u8>>) -> Option<Response<Cow<'static, [u8]>>>>) -> Self {
Self(f)
}
}
Expand Down Expand Up @@ -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| {
Expand All @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions src/webview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -137,7 +138,7 @@ pub struct WebViewAttributes {
/// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034
pub custom_protocols: Vec<(
String,
Box<dyn Fn(&Request<Vec<u8>>) -> Result<Response<Vec<u8>>>>,
Box<dyn Fn(&Request<Vec<u8>>) -> Result<Response<Cow<'static, [u8]>>>>,
)>,
/// 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");`.
Expand Down Expand Up @@ -394,7 +395,7 @@ impl<'a> WebViewBuilder<'a> {
#[cfg(feature = "protocol")]
pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
where
F: Fn(&Request<Vec<u8>>) -> Result<Response<Vec<u8>>> + 'static,
F: Fn(&Request<Vec<u8>>) -> Result<Response<Cow<'static, [u8]>>> + 'static,
{
self
.webview
Expand Down
11 changes: 6 additions & 5 deletions src/webview/webkitgtk/web_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -110,15 +111,15 @@ pub trait WebContextExt {
/// relying on the platform's implementation to properly handle duplicated scheme handlers.
fn register_uri_scheme<F>(&mut self, name: &str, handler: F) -> crate::Result<()>
where
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Vec<u8>>> + 'static;
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Cow<'static, [u8]>>> + 'static;

/// Register a custom protocol to the web context, only if it is not a duplicate scheme.
///
/// If a duplicate scheme has been passed, its handler will **NOT** be registered and the
/// function will return `Err(Error::DuplicateCustomProtocol)`.
fn try_register_uri_scheme<F>(&mut self, name: &str, handler: F) -> crate::Result<()>
where
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Vec<u8>>> + 'static;
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Cow<'static, [u8]>>> + 'static;

/// Add a [`WebView`] to the queue waiting to be opened.
///
Expand Down Expand Up @@ -155,7 +156,7 @@ impl WebContextExt for super::WebContext {

fn register_uri_scheme<F>(&mut self, name: &str, handler: F) -> crate::Result<()>
where
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Vec<u8>>> + 'static,
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Cow<'static, [u8]>>> + 'static,
{
actually_register_uri_scheme(self, name, handler)?;
if self.os.registered_protocols.insert(name.to_string()) {
Expand All @@ -167,7 +168,7 @@ impl WebContextExt for super::WebContext {

fn try_register_uri_scheme<F>(&mut self, name: &str, handler: F) -> crate::Result<()>
where
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Vec<u8>>> + 'static,
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Cow<'static, [u8]>>> + 'static,
{
if self.os.registered_protocols.insert(name.to_string()) {
actually_register_uri_scheme(self, name, handler)
Expand Down Expand Up @@ -275,7 +276,7 @@ fn actually_register_uri_scheme<F>(
handler: F,
) -> crate::Result<()>
where
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Vec<u8>>> + 'static,
F: Fn(&Request<Vec<u8>>) -> crate::Result<Response<Cow<'static, [u8]>>> + 'static,
{
use webkit2gtk::traits::*;
let context = &context.os.context;
Expand Down
3 changes: 2 additions & 1 deletion src/webview/wkwebview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use cocoa::{
};

use std::{
borrow::Cow,
ffi::{c_void, CStr},
os::raw::c_char,
ptr::{null, null_mut},
Expand Down Expand Up @@ -77,7 +78,7 @@ pub(crate) struct InnerWebView {
#[cfg(target_os = "macos")]
file_drop_ptr: *mut (Box<dyn Fn(&Window, FileDropEvent) -> bool>, Rc<Window>),
download_delegate: id,
protocol_ptrs: Vec<*mut Box<dyn Fn(&Request<Vec<u8>>) -> Result<Response<Vec<u8>>>>>,
protocol_ptrs: Vec<*mut Box<dyn Fn(&Request<Vec<u8>>) -> Result<Response<Cow<'static, [u8]>>>>>,
}

impl InnerWebView {
Expand Down
5 changes: 3 additions & 2 deletions src/webview/wkwebview/web_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<dyn Fn(&Request<Vec<u8>>) -> Result<Response<Vec<u8>>>>>,
protocols: Vec<*mut Box<dyn Fn(&Request<Vec<u8>>) -> Result<Response<Cow<'static, [u8]>>>>>,
}

impl WebContextImpl {
Expand All @@ -23,7 +24,7 @@ impl WebContextImpl {

pub fn registered_protocols(
&mut self,
handler: *mut Box<dyn Fn(&Request<Vec<u8>>) -> Result<Response<Vec<u8>>>>,
handler: *mut Box<dyn Fn(&Request<Vec<u8>>) -> Result<Response<Cow<'static, [u8]>>>>,
) {
self.protocols.push(handler);
}
Expand Down

0 comments on commit ddd3461

Please sign in to comment.