Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: canoncialize assets for macOS, Windows, and Linux #278

Merged
merged 3 commits into from
Feb 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/desktop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ dioxus-html = { path = "../html", features = ["serialize"], version = "^0.1.6" }
webbrowser = "0.5.5"
mime_guess = "2.0.3"
dioxus-interpreter-js = { path = "../interpreter", version = "^0.0.0" }

dunce = "1.0.2"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9.3"

[features]
default = ["tokio_runtime"]
Expand Down
12 changes: 12 additions & 0 deletions packages/desktop/src/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::path::PathBuf;

use wry::application::window::Icon;
use wry::{
application::{
Expand All @@ -18,6 +20,7 @@ pub struct DesktopConfig {
pub(crate) pre_rendered: Option<String>,
pub(crate) event_handler: Option<Box<DynEventHandlerFn>>,
pub(crate) disable_context_menu: bool,
pub(crate) resource_dir: Option<PathBuf>,
}

pub(crate) type WryProtocol = (
Expand All @@ -38,14 +41,23 @@ impl DesktopConfig {
file_drop_handler: None,
pre_rendered: None,
disable_context_menu: !cfg!(debug_assertions),
resource_dir: None,
}
}

/// set the directory from which assets will be searched in release mode
pub fn with_resource_directory(mut self, path: impl Into<PathBuf>) -> Self {
self.resource_dir = Some(path.into());
self
}

/// Set whether or not the right-click context menu should be disabled.
pub fn with_disable_context_menu(&mut self, disable: bool) -> &mut Self {
self.disable_context_menu = disable;
self
}

/// With pre-rendered HTML content
pub fn with_prerendered(&mut self, content: String) -> &mut Self {
self.pre_rendered = Some(content);
self
Expand Down
7 changes: 6 additions & 1 deletion packages/desktop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,11 @@ pub fn launch_with_props<P: 'static + Send>(
let (is_ready, sender) = (desktop.is_ready.clone(), desktop.sender.clone());

let proxy = proxy.clone();

let file_handler = cfg.file_drop_handler.take();

let resource_dir = cfg.resource_dir.clone();

let mut webview = WebViewBuilder::new(window)
.unwrap()
.with_transparent(cfg.window.window.transparent)
Expand Down Expand Up @@ -160,7 +163,9 @@ pub fn launch_with_props<P: 'static + Send>(
log::warn!("invalid IPC message received");
});
})
.with_custom_protocol(String::from("dioxus"), protocol::desktop_handler)
.with_custom_protocol(String::from("dioxus"), move |r| {
protocol::desktop_handler(r, resource_dir.clone())
})
.with_file_drop_handler(move |window, evet| {
file_handler
.as_ref()
Expand Down
51 changes: 43 additions & 8 deletions packages/desktop/src/protocol.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use wry::{
http::{status::StatusCode, Request, Response, ResponseBuilder},
Result,
};

pub(super) fn desktop_handler(request: &Request) -> Result<Response> {
pub(super) fn desktop_handler(request: &Request, asset_root: Option<PathBuf>) -> Result<Response> {
// Any content that uses the `dioxus://` scheme will be shuttled through this handler as a "special case".
// For now, we only serve two pieces of content which get included as bytes into the final binary.
let path = request.uri().replace("dioxus://", "");
Expand All @@ -21,27 +21,62 @@ pub(super) fn desktop_handler(request: &Request) -> Result<Response> {
.mimetype("text/javascript")
.body(dioxus_interpreter_js::INTERPRETER_JS.as_bytes().to_vec())
} else {
let path_buf = Path::new(trimmed).canonicalize()?;
let cur_path = Path::new(".").canonicalize()?;
let asset_root = asset_root
.unwrap_or_else(|| get_asset_root().unwrap_or_else(|| Path::new(".").to_path_buf()));

if !path_buf.starts_with(cur_path) {
let asset = asset_root.join(trimmed).canonicalize()?;

if !asset.starts_with(asset_root) {
return ResponseBuilder::new()
.status(StatusCode::FORBIDDEN)
.body(String::from("Forbidden").into_bytes());
}

if !path_buf.exists() {
if !asset.exists() {
return ResponseBuilder::new()
.status(StatusCode::NOT_FOUND)
.body(String::from("Not Found").into_bytes());
}

let mime = mime_guess::from_path(&path_buf).first_or_octet_stream();
let mime = mime_guess::from_path(&asset).first_or_octet_stream();

// do not let path searching to go two layers beyond the caller level
let data = std::fs::read(path_buf)?;
let data = std::fs::read(asset)?;
let meta = format!("{}", mime);

ResponseBuilder::new().mimetype(&meta).body(data)
}
}

#[allow(unreachable_code)]
fn get_asset_root() -> Option<PathBuf> {
/*
We're matching exactly how cargo-bundle works.

- [x] macOS
- [ ] Windows
- [ ] Linux (rpm)
- [ ] Linux (deb)
- [ ] iOS
- [ ] Android

*/

if std::env::var_os("CARGO").is_some() {
return None;
}

// TODO: support for other platforms
#[cfg(target_os = "macos")]
{
let bundle = core_foundation::bundle::CFBundle::main_bundle();
let bundle_path = dbg!(bundle.path()?);
let resources_path = dbg!(bundle.resources_path()?);
let absolute_resources_root = dbg!(bundle_path.join(resources_path));
let canonical_resources_root = dbg!(dunce::canonicalize(absolute_resources_root).ok()?);

return Some(canonical_resources_root);
}

None
}