From 0f1eef902216e82612b5624b00645c2ba81592d1 Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Thu, 18 Jun 2020 00:35:36 +0200 Subject: [PATCH] core: Introduce LoaderError to all Loader methods --- core/src/backend/navigator.rs | 31 +++++---- core/src/lib.rs | 2 +- core/src/loader.rs | 117 +++++++++++++++++++++++----------- desktop/src/executor.rs | 7 +- desktop/src/navigator.rs | 15 +++-- desktop/src/task.rs | 9 +-- web/src/navigator.rs | 12 ++-- 7 files changed, 122 insertions(+), 71 deletions(-) diff --git a/core/src/backend/navigator.rs b/core/src/backend/navigator.rs index fb10731ea..0a74e4c2a 100644 --- a/core/src/backend/navigator.rs +++ b/core/src/backend/navigator.rs @@ -1,5 +1,6 @@ //! Browser-related platform functions +use crate::loader::LoaderError; use std::collections::{HashMap, VecDeque}; use std::fs; use std::future::Future; @@ -11,8 +12,6 @@ use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use std::time::Duration; use swf::avm1::types::SendVarsMethod; -pub type Error = Box; - /// Enumerates all possible navigation methods. #[derive(Copy, Clone)] pub enum NavigationMethod { @@ -118,7 +117,11 @@ pub trait NavigatorBackend { ); /// Fetch data at a given URL and return it some time in the future. - fn fetch(&self, url: &str, request_options: RequestOptions) -> OwnedFuture, Error>; + fn fetch( + &self, + url: &str, + request_options: RequestOptions, + ) -> OwnedFuture, LoaderError>; /// Get the amount of time since the SWF was launched. /// Used by the `getTimer` ActionScript call. @@ -132,16 +135,16 @@ pub trait NavigatorBackend { /// /// TODO: For some reason, `wasm_bindgen_futures` wants unpinnable futures. /// This seems highly limiting. - fn spawn_future(&mut self, future: OwnedFuture<(), Error>); + fn spawn_future(&mut self, future: OwnedFuture<(), LoaderError>); } /// A null implementation of an event loop that only supports blocking. pub struct NullExecutor { /// The list of outstanding futures spawned on this executor. - futures_queue: VecDeque>, + futures_queue: VecDeque>, /// The source of any additional futures. - channel: Receiver>, + channel: Receiver>, } unsafe fn do_nothing(_data: *const ()) {} @@ -157,7 +160,7 @@ impl NullExecutor { /// /// The sender yielded as part of construction should be given to a /// `NullNavigatorBackend` so that it can spawn futures on this executor. - pub fn new() -> (Self, Sender>) { + pub fn new() -> (Self, Sender>) { let (send, recv) = channel(); ( @@ -191,7 +194,7 @@ impl NullExecutor { /// stop polling futures and return that error. Otherwise, it will yield /// `Ok`, indicating that no errors occured. More work may still be /// available, - pub fn poll_all(&mut self) -> Result<(), Error> { + pub fn poll_all(&mut self) -> Result<(), LoaderError> { self.flush_channel(); let mut unfinished_futures = VecDeque::new(); @@ -226,7 +229,7 @@ impl NullExecutor { } /// Block until all futures complete or an error occurs. - pub fn block_all(&mut self) -> Result<(), Error> { + pub fn block_all(&mut self) -> Result<(), LoaderError> { while self.has_work() { self.poll_all()?; } @@ -241,7 +244,7 @@ impl NullExecutor { /// futures and runs them to completion, blockingly. pub struct NullNavigatorBackend { /// The channel upon which all spawned futures will be sent. - channel: Option>>, + channel: Option>>, /// The base path for all relative fetches. relative_base_path: PathBuf, @@ -260,7 +263,7 @@ impl NullNavigatorBackend { /// Construct a navigator backend with fetch and async capability. pub fn with_base_path>( path: P, - channel: Sender>, + channel: Sender>, ) -> Self { let mut relative_base_path = PathBuf::new(); @@ -288,18 +291,18 @@ impl NavigatorBackend for NullNavigatorBackend { ) { } - fn fetch(&self, url: &str, _opts: RequestOptions) -> OwnedFuture, Error> { + fn fetch(&self, url: &str, _opts: RequestOptions) -> OwnedFuture, LoaderError> { let mut path = self.relative_base_path.clone(); path.push(url); - Box::pin(async move { fs::read(path).map_err(|e| e.into()) }) + Box::pin(async move { fs::read(path).map_err(LoaderError::NetworkError) }) } fn time_since_launch(&mut self) -> Duration { Duration::from_millis(0) } - fn spawn_future(&mut self, future: OwnedFuture<(), Error>) { + fn spawn_future(&mut self, future: OwnedFuture<(), LoaderError>) { if let Some(channel) = self.channel.as_ref() { channel.send(future).unwrap(); } diff --git a/core/src/lib.rs b/core/src/lib.rs index fe5df10d9..046d7e7f6 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -18,7 +18,7 @@ mod drawing; pub mod events; mod font; mod library; -mod loader; +pub mod loader; mod player; mod prelude; mod property_map; diff --git a/core/src/loader.rs b/core/src/loader.rs index 4dc83f91d..0e7179abf 100644 --- a/core/src/loader.rs +++ b/core/src/loader.rs @@ -9,12 +9,39 @@ use crate::tag_utils::SwfMovie; use crate::xml::XMLNode; use gc_arena::{Collect, CollectionContext}; use generational_arena::{Arena, Index}; +use std::fmt; +use std::string::FromUtf8Error; use std::sync::{Arc, Mutex, Weak}; use url::form_urlencoded; pub type Handle = Index; -type Error = Box; +#[derive(Debug)] +pub enum LoaderError { + Cancelled, + NotMovieLoader, + NotFormLoader, + NotXmlLoader, + InvalidSwf(crate::tag_utils::Error), + InvalidXmlEncoding(FromUtf8Error), + NetworkError(std::io::Error), + Avm1Error(Box), +} + +impl fmt::Display for LoaderError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LoaderError::Cancelled => f.write_str("Load cancelled"), + LoaderError::NotMovieLoader => f.write_str("Non-movie loader spawned as movie loader"), + LoaderError::NotFormLoader => f.write_str("Non-form loader spawned as form loader"), + LoaderError::NotXmlLoader => f.write_str("Non-XML loader spawned as XML loader"), + LoaderError::InvalidSwf(error) => write!(f, "Invalid SWF: {}", error), + LoaderError::InvalidXmlEncoding(error) => write!(f, "Invalid XML encoding: {}", error), + LoaderError::NetworkError(error) => write!(f, "Network error: {}", error), + LoaderError::Avm1Error(error) => write!(f, "Could not run avm1 script: {}", error), + } + } +} /// Holds all in-progress loads for the player. pub struct LoadManager<'gc>(Arena>); @@ -66,9 +93,9 @@ impl<'gc> LoadManager<'gc> { &mut self, player: Weak>, target_clip: DisplayObject<'gc>, - fetch: OwnedFuture, Error>, + fetch: OwnedFuture, LoaderError>, target_broadcaster: Option>, - ) -> OwnedFuture<(), Error> { + ) -> OwnedFuture<(), LoaderError> { let loader = Loader::Movie { self_handle: None, target_clip, @@ -112,8 +139,8 @@ impl<'gc> LoadManager<'gc> { &mut self, player: Weak>, target_object: Object<'gc>, - fetch: OwnedFuture, Error>, - ) -> OwnedFuture<(), Error> { + fetch: OwnedFuture, LoaderError>, + ) -> OwnedFuture<(), LoaderError> { let loader = Loader::Form { self_handle: None, target_object, @@ -134,8 +161,8 @@ impl<'gc> LoadManager<'gc> { player: Weak>, target_node: XMLNode<'gc>, active_clip: DisplayObject<'gc>, - fetch: OwnedFuture, Error>, - ) -> OwnedFuture<(), Error> { + fetch: OwnedFuture, LoaderError>, + ) -> OwnedFuture<(), LoaderError> { let loader = Loader::XML { self_handle: None, active_clip, @@ -248,11 +275,11 @@ impl<'gc> Loader<'gc> { pub fn movie_loader( &mut self, player: Weak>, - fetch: OwnedFuture, Error>, - ) -> OwnedFuture<(), Error> { + fetch: OwnedFuture, LoaderError>, + ) -> OwnedFuture<(), LoaderError> { let handle = match self { Loader::Movie { self_handle, .. } => self_handle.expect("Loader not self-introduced"), - _ => return Box::pin(async { Err("Non-movie loader spawned as movie loader".into()) }), + _ => return Box::pin(async { Err(LoaderError::NotMovieLoader) }), }; let player = player @@ -261,14 +288,14 @@ impl<'gc> Loader<'gc> { Box::pin(async move { player.lock().expect("Could not lock player!!").update( - |avm, uc| -> Result<(), Error> { + |avm, uc| -> Result<(), LoaderError> { let (clip, broadcaster) = match uc.load_manager.get_loader(handle) { Some(Loader::Movie { target_clip, target_broadcaster, .. }) => (*target_clip, *target_broadcaster), - None => return Err("Load cancelled".into()), + None => return Err(LoaderError::Cancelled), _ => unreachable!(), }; @@ -287,14 +314,20 @@ impl<'gc> Loader<'gc> { "broadcastMessage", &["onLoadStart".into(), Value::Object(broadcaster)], ); - avm.run_stack_till_empty(uc)?; + avm.run_stack_till_empty(uc) + .map_err(|e| LoaderError::Avm1Error(e))?; } Ok(()) }, )?; - let data = (fetch.await).and_then(|data| Ok((data.len(), SwfMovie::from_data(&data)?))); + let data = (fetch.await).and_then(|data| { + Ok(( + data.len(), + SwfMovie::from_data(&data).map_err(|e| LoaderError::InvalidSwf(e))?, + )) + }); if let Ok((length, movie)) = data { let movie = Arc::new(movie); @@ -308,7 +341,7 @@ impl<'gc> Loader<'gc> { target_broadcaster, .. }) => (*target_clip, *target_broadcaster), - None => return Err("Load cancelled".into()), + None => return Err(LoaderError::Cancelled), _ => unreachable!(), }; @@ -326,7 +359,8 @@ impl<'gc> Loader<'gc> { length.into(), ], ); - avm.run_stack_till_empty(uc)?; + avm.run_stack_till_empty(uc) + .map_err(|e| LoaderError::Avm1Error(e))?; } let mut mc = clip @@ -359,7 +393,8 @@ impl<'gc> Loader<'gc> { "broadcastMessage", &["onLoadComplete".into(), Value::Object(broadcaster)], ); - avm.run_stack_till_empty(uc)?; + avm.run_stack_till_empty(uc) + .map_err(|e| LoaderError::Avm1Error(e))?; } if let Some(Loader::Movie { load_complete, .. }) = @@ -377,14 +412,14 @@ impl<'gc> Loader<'gc> { //This also can get errors from decoding an invalid SWF file, //too. We should distinguish those to player code. player.lock().expect("Could not lock player!!").update( - |avm, uc| -> Result<(), Error> { + |avm, uc| -> Result<(), LoaderError> { let (clip, broadcaster) = match uc.load_manager.get_loader(handle) { Some(Loader::Movie { target_clip, target_broadcaster, .. }) => (*target_clip, *target_broadcaster), - None => return Err("Load cancelled".into()), + None => return Err(LoaderError::Cancelled), _ => unreachable!(), }; @@ -401,7 +436,8 @@ impl<'gc> Loader<'gc> { "LoadNeverCompleted".into(), ], ); - avm.run_stack_till_empty(uc)?; + avm.run_stack_till_empty(uc) + .map_err(|e| LoaderError::Avm1Error(e))?; } if let Some(Loader::Movie { load_complete, .. }) = @@ -420,11 +456,11 @@ impl<'gc> Loader<'gc> { pub fn form_loader( &mut self, player: Weak>, - fetch: OwnedFuture, Error>, - ) -> OwnedFuture<(), Error> { + fetch: OwnedFuture, LoaderError>, + ) -> OwnedFuture<(), LoaderError> { let handle = match self { Loader::Form { self_handle, .. } => self_handle.expect("Loader not self-introduced"), - _ => return Box::pin(async { Err("Non-form loader spawned as form loader".into()) }), + _ => return Box::pin(async { Err(LoaderError::NotFormLoader) }), }; let player = player @@ -438,12 +474,13 @@ impl<'gc> Loader<'gc> { let loader = uc.load_manager.get_loader(handle); let that = match loader { Some(Loader::Form { target_object, .. }) => *target_object, - None => return Err("Load cancelled".into()), - _ => return Err("Non-movie loader spawned as movie loader".into()), + None => return Err(LoaderError::Cancelled), + _ => return Err(LoaderError::NotMovieLoader), }; for (k, v) in form_urlencoded::parse(&data) { - that.set(&k, v.into_owned().into(), avm, uc)?; + that.set(&k, v.into_owned().into(), avm, uc) + .map_err(|e| LoaderError::Avm1Error(e))?; } Ok(()) @@ -497,11 +534,11 @@ impl<'gc> Loader<'gc> { pub fn xml_loader( &mut self, player: Weak>, - fetch: OwnedFuture, Error>, - ) -> OwnedFuture<(), Error> { + fetch: OwnedFuture, LoaderError>, + ) -> OwnedFuture<(), LoaderError> { let handle = match self { Loader::XML { self_handle, .. } => self_handle.expect("Loader not self-introduced"), - _ => return Box::pin(async { Err("Non-XML loader spawned as XML loader".into()) }), + _ => return Box::pin(async { Err(LoaderError::NotXmlLoader) }), }; let player = player @@ -511,17 +548,17 @@ impl<'gc> Loader<'gc> { Box::pin(async move { let data = fetch.await; if let Ok(data) = data { - let xmlstring = String::from_utf8(data)?; + let xmlstring = String::from_utf8(data).map_err(LoaderError::InvalidXmlEncoding)?; player.lock().expect("Could not lock player!!").update( - |avm, uc| -> Result<(), Error> { + |avm, uc| -> Result<(), LoaderError> { let (mut node, active_clip) = match uc.load_manager.get_loader(handle) { Some(Loader::XML { target_node, active_clip, .. }) => (*target_node, *active_clip), - None => return Err("Load cancelled".into()), + None => return Err(LoaderError::Cancelled), _ => unreachable!(), }; @@ -535,7 +572,8 @@ impl<'gc> Loader<'gc> { "onHTTPStatus", &[200.into()], ); - avm.run_stack_till_empty(uc)?; + avm.run_stack_till_empty(uc) + .map_err(|e| LoaderError::Avm1Error(e))?; avm.insert_stack_frame_for_method( active_clip, @@ -545,21 +583,22 @@ impl<'gc> Loader<'gc> { "onData", &[xmlstring.into()], ); - avm.run_stack_till_empty(uc)?; + avm.run_stack_till_empty(uc) + .map_err(|e| LoaderError::Avm1Error(e))?; Ok(()) }, )?; } else { player.lock().expect("Could not lock player!!").update( - |avm, uc| -> Result<(), Error> { + |avm, uc| -> Result<(), LoaderError> { let (mut node, active_clip) = match uc.load_manager.get_loader(handle) { Some(Loader::XML { target_node, active_clip, .. }) => (*target_node, *active_clip), - None => return Err("Load cancelled".into()), + None => return Err(LoaderError::Cancelled), _ => unreachable!(), }; @@ -573,7 +612,8 @@ impl<'gc> Loader<'gc> { "onHTTPStatus", &[404.into()], ); - avm.run_stack_till_empty(uc)?; + avm.run_stack_till_empty(uc) + .map_err(|e| LoaderError::Avm1Error(e))?; avm.insert_stack_frame_for_method( active_clip, @@ -583,7 +623,8 @@ impl<'gc> Loader<'gc> { "onData", &[], ); - avm.run_stack_till_empty(uc)?; + avm.run_stack_till_empty(uc) + .map_err(|e| LoaderError::Avm1Error(e))?; Ok(()) }, diff --git a/desktop/src/executor.rs b/desktop/src/executor.rs index 1de52a1fe..9d1ae5600 100644 --- a/desktop/src/executor.rs +++ b/desktop/src/executor.rs @@ -3,7 +3,8 @@ use crate::custom_event::RuffleEvent; use crate::task::Task; use generational_arena::{Arena, Index}; -use ruffle_core::backend::navigator::{Error, OwnedFuture}; +use ruffle_core::backend::navigator::OwnedFuture; +use ruffle_core::loader::LoaderError; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex, Weak}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; @@ -125,7 +126,7 @@ pub struct GlutinAsyncExecutor { task_queue: Arena, /// Source of tasks sent to us by the `NavigatorBackend`. - channel: Receiver>, + channel: Receiver>, /// Weak reference to ourselves. self_ref: Weak>, @@ -144,7 +145,7 @@ impl GlutinAsyncExecutor { /// to spawn new tasks. pub fn new( event_loop: EventLoopProxy, - ) -> (Arc>, Sender>) { + ) -> (Arc>, Sender>) { let (send, recv) = channel(); let new_self = Arc::new(Mutex::new(Self { task_queue: Arena::new(), diff --git a/desktop/src/navigator.rs b/desktop/src/navigator.rs index 72a420646..d042e1e0c 100644 --- a/desktop/src/navigator.rs +++ b/desktop/src/navigator.rs @@ -2,8 +2,9 @@ use crate::custom_event::RuffleEvent; use ruffle_core::backend::navigator::{ - Error, NavigationMethod, NavigatorBackend, OwnedFuture, RequestOptions, + NavigationMethod, NavigatorBackend, OwnedFuture, RequestOptions, }; +use ruffle_core::loader::LoaderError; use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; @@ -16,7 +17,7 @@ use winit::event_loop::EventLoopProxy; /// out to a web browser. pub struct ExternalNavigatorBackend { /// Sink for tasks sent to us through `spawn_future`. - channel: Sender>, + channel: Sender>, /// Event sink to trigger a new task poll. event_loop: EventLoopProxy, @@ -31,7 +32,7 @@ pub struct ExternalNavigatorBackend { impl ExternalNavigatorBackend { #[allow(dead_code)] pub fn new( - channel: Sender>, + channel: Sender>, event_loop: EventLoopProxy, ) -> Self { Self { @@ -45,7 +46,7 @@ impl ExternalNavigatorBackend { /// Construct a navigator backend with fetch and async capability. pub fn with_base_path>( path: P, - channel: Sender>, + channel: Sender>, event_loop: EventLoopProxy, ) -> Self { let mut relative_base_path = PathBuf::new(); @@ -110,16 +111,16 @@ impl NavigatorBackend for ExternalNavigatorBackend { Instant::now().duration_since(self.start_time) } - fn fetch(&self, url: &str, _options: RequestOptions) -> OwnedFuture, Error> { + fn fetch(&self, url: &str, _options: RequestOptions) -> OwnedFuture, LoaderError> { // Load from local filesystem. // TODO: Support network loads, honor sandbox type (local-with-filesystem, local-with-network, remote, ...) let mut path = self.relative_base_path.clone(); path.push(url); - Box::pin(async move { fs::read(path).map_err(|e| e.into()) }) + Box::pin(async move { fs::read(path).map_err(LoaderError::NetworkError) }) } - fn spawn_future(&mut self, future: OwnedFuture<(), Error>) { + fn spawn_future(&mut self, future: OwnedFuture<(), LoaderError>) { self.channel.send(future).expect("working channel send"); if self.event_loop.send_event(RuffleEvent::TaskPoll).is_err() { diff --git a/desktop/src/task.rs b/desktop/src/task.rs index 5441a1562..ba8ecbdeb 100644 --- a/desktop/src/task.rs +++ b/desktop/src/task.rs @@ -1,6 +1,7 @@ //! Task state information -use ruffle_core::backend::navigator::{Error, OwnedFuture}; +use ruffle_core::backend::navigator::OwnedFuture; +use ruffle_core::loader::LoaderError; use std::task::{Context, Poll}; /// Indicates the state of a given task. @@ -22,12 +23,12 @@ pub struct Task { state: TaskState, /// The future to poll in order to progress the task. - future: OwnedFuture<(), Error>, + future: OwnedFuture<(), LoaderError>, } impl Task { /// Box an owned future into a task structure. - pub fn from_future(future: OwnedFuture<(), Error>) -> Self { + pub fn from_future(future: OwnedFuture<(), LoaderError>) -> Self { Self { state: TaskState::Ready, future, @@ -54,7 +55,7 @@ impl Task { /// /// This wrapper function ensures that futures cannot be polled after they /// have completed. Future polls will return `Ok(())`. - pub fn poll(&mut self, context: &mut Context) -> Poll> { + pub fn poll(&mut self, context: &mut Context) -> Poll> { if self.is_completed() { return Poll::Ready(Ok(())); } diff --git a/web/src/navigator.rs b/web/src/navigator.rs index 1bc2e8071..13642e605 100644 --- a/web/src/navigator.rs +++ b/web/src/navigator.rs @@ -2,8 +2,9 @@ use js_sys::{Array, ArrayBuffer, Uint8Array}; use ruffle_core::backend::navigator::{ - Error, NavigationMethod, NavigatorBackend, OwnedFuture, RequestOptions, + NavigationMethod, NavigatorBackend, OwnedFuture, RequestOptions, }; +use ruffle_core::loader::LoaderError; use std::collections::HashMap; use std::time::Duration; use wasm_bindgen::JsCast; @@ -92,7 +93,7 @@ impl NavigatorBackend for WebNavigatorBackend { Duration::from_millis(dt as u64) } - fn fetch(&self, url: &str, options: RequestOptions) -> OwnedFuture, Error> { + fn fetch(&self, url: &str, options: RequestOptions) -> OwnedFuture, LoaderError> { let url = url.to_string(); Box::pin(async move { let mut init = RequestInit::new(); @@ -130,7 +131,10 @@ impl NavigatorBackend for WebNavigatorBackend { let window = web_sys::window().unwrap(); let fetchval = JsFuture::from(window.fetch_with_request(&request)).await; if fetchval.is_err() { - return Err("Could not fetch, got JS Error".into()); + return Err(LoaderError::NetworkError(std::io::Error::new( + std::io::ErrorKind::Other, + "Could not fetch, got JS Error", + ))); } let resp: Response = fetchval.unwrap().dyn_into().unwrap(); @@ -147,7 +151,7 @@ impl NavigatorBackend for WebNavigatorBackend { }) } - fn spawn_future(&mut self, future: OwnedFuture<(), Error>) { + fn spawn_future(&mut self, future: OwnedFuture<(), LoaderError>) { spawn_local(async move { if let Err(e) = future.await { log::error!("Asynchronous error occured: {}", e);