use crate::document::NATIVE_EVAL_JS; use crate::{assets::*, webview::WebviewEdits}; use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS; use dioxus_interpreter_js::NATIVE_JS; use wry::{ http::{status::StatusCode, Request, Response}, RequestAsyncResponder, }; #[cfg(any(target_os = "android", target_os = "windows"))] const EDITS_PATH: &str = "http://dioxus.index.html/__edits"; #[cfg(not(any(target_os = "android", target_os = "windows")))] const EDITS_PATH: &str = "dioxus://index.html/__edits"; #[cfg(any(target_os = "android", target_os = "windows"))] const EVENTS_PATH: &str = "http://dioxus.index.html/__events"; #[cfg(not(any(target_os = "android", target_os = "windows")))] const EVENTS_PATH: &str = "dioxus://index.html/__events"; static DEFAULT_INDEX: &str = include_str!("./index.html"); #[allow(clippy::too_many_arguments)] // just for now, should fix this eventually /// Handle a request from the webview /// /// - Tries to stream edits if they're requested. /// - If that doesn't match, tries a user provided asset handler /// - If that doesn't match, tries to serve a file from the filesystem pub(super) fn desktop_handler( request: Request>, asset_handlers: AssetHandlerRegistry, responder: RequestAsyncResponder, edit_state: &WebviewEdits, custom_head: Option, custom_index: Option, root_name: &str, headless: bool, ) { // Try to serve the index file first if let Some(index_bytes) = index_request(&request, custom_head, custom_index, root_name, headless) { return responder.respond(index_bytes); } // If the request is asking for edits (ie binary protocol streaming), do that let trimmed_uri = request.uri().path().trim_matches('/'); if trimmed_uri == "__edits" { return edit_state.wry_queue.handle_request(responder); } // If the request is asking for an event response, do that if trimmed_uri == "__events" { return edit_state.handle_event(request, responder); } // todo: we want to move the custom assets onto a different protocol or something if let Some(name) = request.uri().path().split('/').nth(1) { if asset_handlers.has_handler(name) { let _name = name.to_string(); return asset_handlers.handle_request(&_name, request, responder); } } match dioxus_asset_resolver::serve_asset_from_raw_path(request.uri().path()) { Ok(res) => responder.respond(res), Err(_e) => responder.respond( Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body(String::from("Failed to serve asset").into_bytes()) .unwrap(), ), } } /// Build the index.html file we use for bootstrapping a new app /// /// We use wry/webview by building a special index.html that forms a bridge between the webview and your rust code /// /// This is similar to tauri, except we give more power to your rust code and less power to your frontend code. /// This lets us skip a build/bundle step - your code just works - but limits how your Rust code can actually /// mess with UI elements. We make this decision since other renderers like LiveView are very separate and can /// never properly bridge the gap. Eventually of course, the idea is to build a custom CSS/HTML renderer where you /// *do* have native control over elements, but that still won't work with liveview. fn index_request( request: &Request>, custom_head: Option, custom_index: Option, root_name: &str, headless: bool, ) -> Option>> { // If the request is for the root, we'll serve the index.html file. if request.uri().path() != "/" { return None; } // Load a custom index file if provided let mut index = custom_index.unwrap_or_else(|| DEFAULT_INDEX.to_string()); // Insert a custom head if provided // We look just for the closing head tag. If a user provided a custom index with weird syntax, this might fail if let Some(head) = custom_head { index.insert_str(index.find("").expect("Head element to exist"), &head); } // Inject our module loader by looking for a body tag // A failure mode here, obviously, is if the user provided a custom index without a body tag // Might want to document this index.insert_str( index.find("").expect("Body element to exist"), &module_loader(root_name, headless), ); Response::builder() .header("Content-Type", "text/html") .header("Access-Control-Allow-Origin", "*") .body(index.into()) .ok() } /// Construct the inline script that boots up the page and bridges the webview with rust code. /// /// The arguments here: /// - root_name: the root element (by Id) that we stream edits into /// - headless: is this page being loaded but invisible? Important because not all windows are visible and the /// interpreter can't connect until the window is ready. fn module_loader(root_id: &str, headless: bool) -> String { format!( r#" "# ) }