Skip to content
Closed
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
29 changes: 25 additions & 4 deletions crates/bevy_asset/src/io/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use crate::io::{
};
use alloc::{borrow::ToOwned, boxed::Box, format};
use js_sys::{Uint8Array, JSON};
use std::path::{Path, PathBuf};
use std::{
borrow::Cow,
path::{Path, PathBuf},
};
use tracing::error;
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
Expand All @@ -27,15 +30,27 @@ extern "C" {
/// Reader implementation for loading assets via HTTP in Wasm.
pub struct HttpWasmAssetReader {
root_path: PathBuf,
request_mapper: Option<Box<dyn Fn(&str) -> Cow<str> + Send + Sync + 'static>>,
}

impl HttpWasmAssetReader {
/// Creates a new `WasmAssetReader`. The path provided will be used to build URLs to query for assets.
pub fn new<P: AsRef<Path>>(path: P) -> Self {
Self {
root_path: path.as_ref().to_owned(),
request_mapper: None,
}
}

/// Sets a mapper function to modify the request URL for each asset fetch. This can be used to
/// add query parameters or modify the path in any way.
pub fn with_request_mapper<F>(mut self, mapper: F) -> Self
where
F: Fn(&str) -> Cow<str> + Send + Sync + 'static,
{
self.request_mapper = Some(Box::new(mapper));
self
}
}

fn js_value_to_err(context: &str) -> impl FnOnce(JsValue) -> std::io::Error + '_ {
Expand All @@ -57,14 +72,20 @@ impl HttpWasmAssetReader {
&self,
path: PathBuf,
) -> Result<impl Reader + use<>, AssetReaderError> {
let path = path.to_str().unwrap();
let fetch_path = self
.request_mapper
.as_ref()
.map_or_else(|| Cow::Borrowed(path), |mapper| mapper(path));

// The JS global scope includes a self-reference via a specializing name, which can be used to determine the type of global context available.
let global: Global = js_sys::global().unchecked_into();
let promise = if !global.window().is_undefined() {
let window: web_sys::Window = global.unchecked_into();
window.fetch_with_str(path.to_str().unwrap())
window.fetch_with_str(&fetch_path)
} else if !global.worker().is_undefined() {
let worker: web_sys::WorkerGlobalScope = global.unchecked_into();
worker.fetch_with_str(path.to_str().unwrap())
worker.fetch_with_str(&fetch_path)
} else {
let error = std::io::Error::other("Unsupported JavaScript global context");
return Err(AssetReaderError::Io(error.into()));
Expand All @@ -85,7 +106,7 @@ impl HttpWasmAssetReader {
// Some web servers, including itch.io's CDN, return 403 when a requested file isn't present.
// TODO: remove handling of 403 as not found when it's easier to configure
// see https://github.com/bevyengine/bevy/pull/19268#pullrequestreview-2882410105
403 | 404 => Err(AssetReaderError::NotFound(path)),
403 | 404 => Err(AssetReaderError::NotFound((*fetch_path).into())),
status => Err(AssetReaderError::HttpError(status)),
}
}
Expand Down