From cc9fc4b43df79834c1b8f2c1347accba50356604 Mon Sep 17 00:00:00 2001 From: "Ngo Iok Ui (Wu Yu Wei)" Date: Sun, 13 Jun 2021 20:09:12 +0800 Subject: [PATCH] Add mimetype to return type of custom protocol (#296) * Add mimetype to return type of custom protocol * Fix macOS --- .changes/mime.md | 5 ++ Cargo.toml | 1 - examples/custom_protocol.rs | 6 +- src/webview/mimetype.rs | 122 ------------------------------ src/webview/mod.rs | 20 +++-- src/webview/webkitgtk/mod.rs | 4 +- src/webview/webview2/win32/mod.rs | 5 +- src/webview/webview2/winrt/mod.rs | 5 +- src/webview/wkwebview/mod.rs | 14 ++-- 9 files changed, 35 insertions(+), 147 deletions(-) create mode 100644 .changes/mime.md delete mode 100644 src/webview/mimetype.rs diff --git a/.changes/mime.md b/.changes/mime.md new file mode 100644 index 000000000..78a65c158 --- /dev/null +++ b/.changes/mime.md @@ -0,0 +1,5 @@ +--- +"wry": minor +--- + +Update signature of custom protocol closure. It should return a mime type string now. diff --git a/Cargo.toml b/Cargo.toml index 28929a7c4..b116c7ed1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,6 @@ serde = { version = "1.0", features = [ "derive" ] } serde_json = "1.0" thiserror = "1.0" url = "2.2" -infer = "0.4" tao = { version = "0.2.6", default-features = false, features = [ "serde" ] } [dev-dependencies] diff --git a/examples/custom_protocol.rs b/examples/custom_protocol.rs index 8c3440003..2dee82e1d 100644 --- a/examples/custom_protocol.rs +++ b/examples/custom_protocol.rs @@ -64,12 +64,12 @@ fn main() -> wry::Result<()> { match requested_asset_path.as_str() { // if our path match /hello.html - "/hello.html" => Ok(hello_html.as_bytes().into()), + "/hello.html" => Ok((hello_html.as_bytes().into(), "text/html".into())), // if our path match /hello.js - "/hello.js" => Ok(hello_js.as_bytes().into()), + "/hello.js" => Ok((hello_js.as_bytes().into(), "text/javascript".into())), // other paths should resolve index // more logic can be applied here - _ => Ok(index_html.as_bytes().into()), + _ => Ok((index_html.as_bytes().into(), "text/html".into())), } }) // tell the webview to load the custom protocol diff --git a/src/webview/mimetype.rs b/src/webview/mimetype.rs deleted file mode 100644 index 8c7a45232..000000000 --- a/src/webview/mimetype.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use std::fmt; - -const MIMETYPE_PLAIN: &str = "text/plain"; - -/// [Web Compatible MimeTypes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#important_mime_types_for_web_developers) -pub(crate) enum MimeType { - CSS, - CSV, - HTML, - ICO, - JS, - JSON, - JSONLD, - OCTETSTREAM, - RTF, - SVG, -} - -impl std::fmt::Display for MimeType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mime = match self { - MimeType::CSS => "text/css", - MimeType::CSV => "text/csv", - MimeType::HTML => "text/html", - MimeType::ICO => "image/vnd.microsoft.icon", - MimeType::JS => "text/javascript", - MimeType::JSON => "application/json", - MimeType::JSONLD => "application/ld+json", - MimeType::OCTETSTREAM => "application/octet-stream", - MimeType::RTF => "application/rtf", - MimeType::SVG => "image/svg+xml", - }; - write!(f, "{}", mime) - } -} - -impl MimeType { - /// parse a URI suffix to convert text/plain mimeType to their actual web compatible mimeType. - pub fn parse_from_uri(uri: &str) -> MimeType { - let suffix = uri.split('.').last(); - match suffix { - Some("bin") => Self::OCTETSTREAM, - Some("css") => Self::CSS, - Some("csv") => Self::CSV, - Some("html") => Self::HTML, - Some("ico") => Self::ICO, - Some("js") => Self::JS, - Some("json") => Self::JSON, - Some("jsonld") => Self::JSONLD, - Some("rtf") => Self::RTF, - Some("svg") => Self::SVG, - // Assume HTML when a TLD is found for eg. `wry:://tauri.studio` | `wry://hello.com` - Some(_) => Self::HTML, - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types - // using octet stream according to this: - None => Self::OCTETSTREAM, - } - } - - /// infer mimetype from content (or) URI if needed. - pub fn parse(content: &[u8], uri: &str) -> String { - let mime = match infer::get(content) { - Some(info) => info.mime_type(), - None => MIMETYPE_PLAIN, - }; - - if mime == MIMETYPE_PLAIN { - return Self::parse_from_uri(uri).to_string(); - } - - mime.to_string() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_parse_mimetype_from_uri() { - let css = MimeType::parse_from_uri( - "https://unpkg.com/browse/bootstrap@4.1.0/dist/css/bootstrap-grid.css", - ) - .to_string(); - assert_eq!(css, "text/css".to_string()); - - let csv: String = MimeType::parse_from_uri("https://example.com/random.csv").to_string(); - assert_eq!(csv, "text/csv".to_string()); - - let ico: String = - MimeType::parse_from_uri("https://icons.duckduckgo.com/ip3/microsoft.com.ico").to_string(); - assert_eq!(ico, String::from("image/vnd.microsoft.icon")); - - let html: String = MimeType::parse_from_uri("https://tauri.studio/index.html").to_string(); - assert_eq!(html, String::from("text/html")); - - let js: String = - MimeType::parse_from_uri("https://unpkg.com/react@17.0.1/umd/react.production.min.js") - .to_string(); - assert_eq!(js, "text/javascript".to_string()); - - let json: String = - MimeType::parse_from_uri("https://unpkg.com/browse/react@17.0.1/build-info.json").to_string(); - assert_eq!(json, String::from("application/json")); - - let jsonld: String = MimeType::parse_from_uri("https:/example.com/hello.jsonld").to_string(); - assert_eq!(jsonld, String::from("application/ld+json")); - - let rtf: String = MimeType::parse_from_uri("https://example.com/document.rtf").to_string(); - assert_eq!(rtf, String::from("application/rtf")); - - let svg: String = MimeType::parse_from_uri("https://example.com/picture.svg").to_string(); - assert_eq!(svg, String::from("image/svg+xml")); - - let custom_scheme = MimeType::parse_from_uri("wry://tauri.studio").to_string(); - assert_eq!(custom_scheme, String::from("text/html")); - } -} diff --git a/src/webview/mod.rs b/src/webview/mod.rs index fb9af2033..1521d6325 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -4,7 +4,6 @@ //! [`WebView`] struct and associated types. -mod mimetype; mod web_context; pub use web_context::WebContext; @@ -47,8 +46,15 @@ pub struct WebViewAttributes { /// initialization code will be executed. It is guaranteed that code is executed before /// `window.onload`. pub initialization_scripts: Vec, - /// Register custom file loading protocol - pub custom_protocols: Vec<(String, Box Result>>)>, + /// Register custom file loading protocols with pairs of scheme uri string and a handling + /// closure. + /// + /// The closure takes the `Window` and a url string slice as parameters, and returns a tuple of a + /// vector of bytes which is the content and a mimetype string of the conten. + pub custom_protocols: Vec<( + String, + Box Result<(Vec, String)>>, + )>, /// Set the RPC handler to Communicate between the host Rust code and Javascript on webview. /// /// The communication is done via [JSON-RPC](https://www.jsonrpc.org). Users can use this to register an incoming @@ -129,11 +135,15 @@ impl WebViewBuilder { self } - /// Register custom file loading protocol + /// Register custom file loading protocols with pairs of scheme uri string and a handling + /// closure. + /// + /// The closure takes the `Window` and a url string slice as parameters, and returns a tuple of a + /// vector of bytes which is the content and a mimetype string of the conten. #[cfg(feature = "protocol")] pub fn with_custom_protocol(mut self, name: String, handler: F) -> Self where - F: Fn(&Window, &str) -> Result> + 'static, + F: Fn(&Window, &str) -> Result<(Vec, String)> + 'static, { self .webview diff --git a/src/webview/webkitgtk/mod.rs b/src/webview/webkitgtk/mod.rs index ce3783d19..e4d6a1e95 100644 --- a/src/webview/webkitgtk/mod.rs +++ b/src/webview/webkitgtk/mod.rs @@ -21,7 +21,6 @@ use webkit2gtk_sys::{ use crate::{ application::{platform::unix::*, window::Window}, webview::{ - mimetype::MimeType, web_context::{unix::WebContextExt, WebContext}, WebViewAttributes, }, @@ -175,8 +174,7 @@ impl InnerWebView { let uri = uri.as_str(); match handler(&w, uri) { - Ok(buffer) => { - let mime = MimeType::parse(&buffer, uri); + Ok((buffer, mime)) => { let input = gio::MemoryInputStream::from_bytes(&Bytes::from(&buffer)); request.finish(&input, buffer.len() as i64, Some(&mime)) } diff --git a/src/webview/webview2/win32/mod.rs b/src/webview/webview2/win32/mod.rs index ec8c1279f..63a742c80 100644 --- a/src/webview/webview2/win32/mod.rs +++ b/src/webview/webview2/win32/mod.rs @@ -5,7 +5,7 @@ mod file_drop; use crate::{ - webview::{mimetype::MimeType, WebContext, WebViewAttributes}, + webview::{WebContext, WebViewAttributes}, Result, }; @@ -148,8 +148,7 @@ impl InnerWebView { ); match function(&window_, path) { - Ok(content) => { - let mime = MimeType::parse(&content, &uri); + Ok((content, mime)) => { let stream = webview2::Stream::from_bytes(&content); let response = env_clone.create_web_resource_response( stream, diff --git a/src/webview/webview2/winrt/mod.rs b/src/webview/webview2/winrt/mod.rs index 08c339346..a72d0b1c8 100644 --- a/src/webview/webview2/winrt/mod.rs +++ b/src/webview/webview2/winrt/mod.rs @@ -17,7 +17,7 @@ use windows_webview2::{ }; use crate::{ - webview::{mimetype::MimeType, FileDropEvent, RpcRequest, RpcResponse}, + webview::{FileDropEvent, RpcRequest, RpcResponse}, Result, }; @@ -166,8 +166,7 @@ impl InnerWebView { &format!("{}://", name), ); - if let Ok(content) = function(&window_, &path) { - let mime = MimeType::parse(&content, &uri); + if let Ok((content, mime)) = function(&window_, &path) { let stream = InMemoryRandomAccessStream::new()?; let writer = DataWriter::CreateDataWriter(stream.clone())?; writer.WriteBytes(&content)?; diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index ed2663fa5..abc3f6d14 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -34,9 +34,7 @@ use crate::application::platform::ios::WindowExtIOS; use crate::{ application::window::Window, - webview::{ - mimetype::MimeType, FileDropEvent, RpcRequest, RpcResponse, WebContext, WebViewAttributes, - }, + webview::{FileDropEvent, RpcRequest, RpcResponse, WebContext, WebViewAttributes}, Result, }; @@ -54,7 +52,10 @@ pub struct InnerWebView { ), #[cfg(target_os = "macos")] file_drop_ptr: *mut (Box bool>, Rc), - protocol_ptrs: Vec<*mut (Box Result>>, Rc)>, + protocol_ptrs: Vec<*mut ( + Box Result<(Vec, String)>>, + Rc, + )>, } impl InnerWebView { @@ -99,7 +100,7 @@ impl InnerWebView { let function = this.get_ivar::<*mut c_void>("function"); let function = &mut *(*function as *mut ( - Box Fn(&'r Window, &'s str) -> Result>>, + Box Fn(&'r Window, &'s str) -> Result<(Vec, String)>>, Rc, )); @@ -113,8 +114,7 @@ impl InnerWebView { let uri = nsstring.to_str(); // Send response - if let Ok(content) = function.0(&function.1, uri) { - let mime = MimeType::parse(&content, uri); + if let Ok((content, mime)) = function.0(&function.1, uri) { let dictionary: id = msg_send![class!(NSMutableDictionary), alloc]; let headers: id = msg_send![dictionary, initWithCapacity:1]; let () = msg_send![headers, setObject:NSString::new(&mime) forKey: NSString::new("content-type")];