web: Refactor `WebNavigatorBackend`
Store `base_url` as `Option<Url>` instead of `Option<String>`, so we don't need to parse it on each URL resolve.
This commit is contained in:
parent
a99c7e381b
commit
d70697b4f3
|
@ -7,21 +7,7 @@ use std::future::Future;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::pin::Pin;
|
||||
use swf::avm1::types::SendVarsMethod;
|
||||
use url::{ParseError, Url};
|
||||
|
||||
/// Attempt to convert a relative URL into an absolute URL, using the base URL
|
||||
/// if necessary.
|
||||
///
|
||||
/// If the relative URL is actually absolute, then the base will not be used.
|
||||
pub fn url_from_relative_url(base: &str, relative: &str) -> Result<Url, ParseError> {
|
||||
let parsed = Url::parse(relative);
|
||||
if let Err(ParseError::RelativeUrlWithoutBase) = parsed {
|
||||
let base = Url::parse(base)?;
|
||||
return base.join(relative);
|
||||
}
|
||||
|
||||
parsed
|
||||
}
|
||||
use url::Url;
|
||||
|
||||
/// Enumerates all possible navigation methods.
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -55,7 +55,7 @@ version = "0.3.58"
|
|||
features = [
|
||||
"AddEventListenerOptions", "AudioBuffer", "AudioBufferSourceNode", "AudioContext", "AudioDestinationNode",
|
||||
"AudioNode", "AudioParam", "Blob", "BlobPropertyBag", "ChannelMergerNode",
|
||||
"ChannelSplitterNode", "Document", "Element", "Event", "EventTarget", "GainNode", "HtmlCanvasElement",
|
||||
"ChannelSplitterNode", "Element", "Event", "EventTarget", "GainNode", "HtmlCanvasElement",
|
||||
"HtmlElement", "HtmlFormElement", "KeyboardEvent", "Location", "PointerEvent", "Request", "RequestInit", "Response",
|
||||
"Storage", "WheelEvent", "Window",
|
||||
]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Navigator backend for web
|
||||
use js_sys::{Array, ArrayBuffer, Uint8Array};
|
||||
use ruffle_core::backend::navigator::{
|
||||
url_from_relative_url, NavigationMethod, NavigatorBackend, OwnedFuture, Request, Response,
|
||||
NavigationMethod, NavigatorBackend, OwnedFuture, Request, Response,
|
||||
};
|
||||
use ruffle_core::indexmap::IndexMap;
|
||||
use ruffle_core::loader::Error;
|
||||
|
@ -10,51 +10,52 @@ use url::Url;
|
|||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::{spawn_local, JsFuture};
|
||||
use web_sys::{
|
||||
window, Blob, BlobPropertyBag, Document, Request as WebRequest, RequestInit,
|
||||
Response as WebResponse,
|
||||
window, Blob, BlobPropertyBag, Request as WebRequest, RequestInit, Response as WebResponse,
|
||||
};
|
||||
|
||||
pub struct WebNavigatorBackend {
|
||||
allow_script_access: bool,
|
||||
upgrade_to_https: bool,
|
||||
base_url: Option<String>,
|
||||
base_url: Option<Url>,
|
||||
}
|
||||
|
||||
impl WebNavigatorBackend {
|
||||
pub fn new(
|
||||
allow_script_access: bool,
|
||||
upgrade_to_https: bool,
|
||||
mut base_url: Option<String>,
|
||||
base_url: Option<String>,
|
||||
) -> Self {
|
||||
let window = web_sys::window().expect("window()");
|
||||
|
||||
// Upgrade to HTTPS takes effect if the current page is hosted on HTTPS.
|
||||
let upgrade_to_https =
|
||||
upgrade_to_https && window.location().protocol().unwrap_or_default() == "https:";
|
||||
upgrade_to_https && window.location().protocol().expect("protocol()") == "https:";
|
||||
|
||||
if let Some(base) = &mut base_url {
|
||||
// Adding trailing slash so url::parse will not drop last part
|
||||
if !base.ends_with('/') {
|
||||
base.push('/');
|
||||
// Retrieve and parse `document.baseURI`.
|
||||
let document_base_uri = || {
|
||||
let document = window.document().expect("document()");
|
||||
if let Ok(Some(base_uri)) = document.base_uri() {
|
||||
return Url::parse(&base_uri).ok();
|
||||
}
|
||||
|
||||
if Url::parse(base).is_err() {
|
||||
let document = window.document().expect("Could not get document");
|
||||
if let Ok(Some(doc_base_uri)) = document.base_uri() {
|
||||
let doc_url =
|
||||
Url::parse(&doc_base_uri).expect("Could not parse document base uri");
|
||||
None
|
||||
};
|
||||
|
||||
if let Ok(joined_url) = doc_url.join(base) {
|
||||
base_url = Some(joined_url.into());
|
||||
let base_url = if let Some(mut base_url) = base_url {
|
||||
// Adding trailing slash so `Url::parse` will not drop the last part.
|
||||
if !base_url.ends_with('/') {
|
||||
base_url.push('/');
|
||||
}
|
||||
|
||||
Url::parse(&base_url)
|
||||
.ok()
|
||||
.or_else(|| document_base_uri().and_then(|base_uri| base_uri.join(&base_url).ok()))
|
||||
} else {
|
||||
log::error!("Bad base directory {}", base);
|
||||
base_url = None;
|
||||
}
|
||||
} else {
|
||||
log::error!("Could not get document base_uri for base directory inference");
|
||||
base_url = None;
|
||||
}
|
||||
}
|
||||
document_base_uri()
|
||||
};
|
||||
|
||||
if base_url.is_none() {
|
||||
log::error!("Could not get base URL for base directory inference.");
|
||||
}
|
||||
|
||||
Self {
|
||||
|
@ -64,23 +65,10 @@ impl WebNavigatorBackend {
|
|||
}
|
||||
}
|
||||
|
||||
fn base_uri(&self, document: &Document) -> Option<String> {
|
||||
if let Some(base_url) = self.base_url.clone() {
|
||||
Some(base_url)
|
||||
} else if let Ok(Some(base_uri)) = document.base_uri() {
|
||||
Some(base_uri)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_relative_url<'a>(&self, url: &'a str) -> Cow<'a, str> {
|
||||
let window = web_sys::window().expect("window()");
|
||||
let document = window.document().expect("document()");
|
||||
|
||||
if let Some(base_uri) = self.base_uri(&document) {
|
||||
if let Ok(new_url) = url_from_relative_url(&base_uri, url) {
|
||||
return String::from(new_url).into();
|
||||
fn resolve_url<'a>(&self, url: &'a str) -> Cow<'a, str> {
|
||||
if let Some(base_url) = &self.base_url {
|
||||
if let Ok(url) = base_url.join(url) {
|
||||
return self.pre_process_url(url).to_string().into();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,36 +83,26 @@ impl NavigatorBackend for WebNavigatorBackend {
|
|||
target: String,
|
||||
vars_method: Option<(NavigationMethod, IndexMap<String, String>)>,
|
||||
) {
|
||||
// If the URL is empty, we ignore the request
|
||||
// If the URL is empty, ignore the request.
|
||||
if url.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(window) = window() {
|
||||
let document = window.document().expect("Could not get document");
|
||||
let url = self.resolve_url(&url);
|
||||
|
||||
let base_uri = match self.base_uri(&document) {
|
||||
Some(base_uri) => base_uri,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let url = if let Ok(new_url) = url_from_relative_url(&base_uri, &url) {
|
||||
new_url
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// If allowScriptAccess is disabled, we should reject the javascript scheme
|
||||
// If `allowScriptAccess` is disabled, reject the `javascript:` scheme.
|
||||
if let Ok(url) = Url::parse(&url) {
|
||||
if !self.allow_script_access && url.scheme() == "javascript" {
|
||||
log::warn!("SWF tried to run a script, but script access is not allowed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Should we return a result for failed opens? Does Flash care?
|
||||
// TODO: Should we return a result for failed opens? Does Flash care?
|
||||
let window = window().expect("window()");
|
||||
match vars_method {
|
||||
Some((navmethod, formvars)) => {
|
||||
let form_url = self.pre_process_url(url).to_string();
|
||||
|
||||
let document = window.document().expect("document()");
|
||||
let body = match document.body() {
|
||||
Some(body) => body,
|
||||
None => return,
|
||||
|
@ -144,7 +122,7 @@ impl NavigatorBackend for WebNavigatorBackend {
|
|||
},
|
||||
);
|
||||
|
||||
let _ = form.set_attribute("action", &form_url);
|
||||
let _ = form.set_attribute("action", &url);
|
||||
|
||||
if !target.is_empty() {
|
||||
let _ = form.set_attribute("target", &target);
|
||||
|
@ -165,21 +143,16 @@ impl NavigatorBackend for WebNavigatorBackend {
|
|||
}
|
||||
None => {
|
||||
if target.is_empty() {
|
||||
let _ = window.location().assign(url.as_str());
|
||||
let _ = window.location().assign(&url);
|
||||
} else {
|
||||
let _ = window.open_with_url_and_target(url.as_str(), &target);
|
||||
let _ = window.open_with_url_and_target(&url, &target);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch(&self, request: Request) -> OwnedFuture<Response, Error> {
|
||||
let url = if let Ok(parsed_url) = Url::parse(request.url()) {
|
||||
self.pre_process_url(parsed_url).to_string()
|
||||
} else {
|
||||
self.resolve_relative_url(request.url()).to_string()
|
||||
};
|
||||
let url = self.resolve_url(request.url()).into_owned();
|
||||
|
||||
Box::pin(async move {
|
||||
let mut init = RequestInit::new();
|
||||
|
@ -215,7 +188,7 @@ impl NavigatorBackend for WebNavigatorBackend {
|
|||
let request = WebRequest::new_with_str_and_init(&url, &init)
|
||||
.map_err(|_| Error::FetchError(format!("Unable to create request for {}", url)))?;
|
||||
|
||||
let window = web_sys::window().unwrap();
|
||||
let window = web_sys::window().expect("window()");
|
||||
let fetchval = JsFuture::from(window.fetch_with_request(&request))
|
||||
.await
|
||||
.map_err(|_| Error::FetchError("Got JS error".to_string()))?;
|
||||
|
|
Loading…
Reference in New Issue