Created
January 19, 2016 14:26
-
-
Save Ms2ger/95a483aade63bf257521 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum CorsSettingAttributeValue { | |
Anonymous, | |
UseCredentials, | |
NoCors, | |
} | |
struct ModuleScript { | |
settings_object: EnvironmentSettingsObject, | |
module_record: JS::SourceTextModuleRecord, | |
base_url: Url, | |
cors_setting: CorsSettingAttributeValue, | |
} | |
enum MapValue { | |
Fetching(Arc<Mutex<Vec<Callback>>>), | |
Failure, | |
Script(ModuleScript), | |
} | |
impl MapValue { | |
fn from_result(result: Result<ModuleScript, ()>) -> MapValue { | |
match result { | |
Ok(script) => MapValue::Script(script), | |
Err(_) => MapValue::Failure, | |
} | |
} | |
} | |
type ModuleMap = HashMap<Url, MapValue>; | |
type Callback = Box<Fn(Result<ModuleScript, ()>)>; | |
struct EnvironmentSettingsObject { | |
module_map: Mutex<HashMap<Url, MapValue>> | |
} | |
impl EnvironmentSettingsObject { | |
fn document(&self) -> &Document { | |
// somehow | |
} | |
} | |
fn fetch_a_module_script_tree(url: Url, | |
setting: CorsSettingAttributeValue, | |
settings_object: EnvironmentSettingsObject, | |
cb: Callback) { | |
fetch_a_single_module_script(url, setting, settings_object, box |result| { | |
match result { | |
Err(_) @ result => cb(result), | |
Ok(script) => fetch_the_descendants_of_a_module_script(script, cb) | |
} | |
}) | |
} | |
fn resolve(specifier: &str, script: &ModuleScript) -> Result<Url, ()> { | |
if let Ok(result) = Url::parse(specifier) { | |
return Ok(result); | |
} | |
if !specifier.starts_with("/") || !specifier.starts_with("./") || !specifier.starts_with("../") { | |
return Err(()); | |
} | |
script.base_url.join(specifier) | |
} | |
fn fetch_the_descendants_of_a_module_script(script: ModuleScript, | |
cb: Callback) { | |
let requested_modules = script.module_record.[[RequestedModules]]; | |
if requested_modules.is_empty() { | |
return cb(Ok(script)); | |
} | |
let urls: Result<Vec<Url>, ()> = requested_modules.into_iter().map(|requested| { | |
resolve(requested, script) | |
}).collect(); | |
let urls = match urls { | |
Ok(urls) => urls, | |
Err(_) => { | |
report_exception(TypeError); | |
cb(Err(())); | |
return; | |
} | |
}; | |
struct FetchData { | |
remaining: usize, | |
cb: Callback, | |
} | |
let data = Arc::new(Mutex::new(Some(FetchData { | |
remaining: urls.len(), | |
cb: cb, | |
}))); | |
for url in urls { | |
let data = data.clone() | |
fetch_a_module_script_tree(url, script.setting, script.settings_object, box |result| { | |
let data = data.lock(); | |
let local_data: FetchData = match data.take() { | |
Some(data) => data, | |
None => return, | |
}; | |
match result { | |
Err(_) => { | |
local_data.cb(Err(())); | |
}, | |
Ok(_) => { | |
local_data.remaining -= 1; | |
if local_data.remaining == 0 { | |
local_data.cb(Ok(script)); | |
} else { | |
*data = Some(local_data); | |
} | |
} | |
} | |
}) | |
} | |
} | |
fn fetch_a_single_module_script(url: Url, | |
setting: CorsSettingAttributeValue, | |
settings_object: EnvironmentSettingsObject, | |
cb: Callback) { | |
{ | |
let module_map = settings_object.module_map.lock(); | |
if let Some(ref value) = *module_map.get(&url) { | |
match *value { | |
MapValue::Module(ref script) => cb(Ok(script.clone())), | |
MapValue::Failure => cb(Err(())), | |
MapValue::Fetching(ref waiters) => waiters.lock().push(cb), | |
} | |
return; | |
} | |
let waiters = Arc::new(Mutex::new(vec![cb])); | |
module_map.insert(url.clone(), MapValue::Fetching(waiters)); | |
} | |
struct ScriptContext { | |
settings_object: EnvironmentSettingsObject, | |
data: Vec<u8>, | |
metadata: Option<Metadata>, | |
url: Url, | |
} | |
impl AsyncResponseListener for ScriptContext { | |
fn headers_available(&mut self, metadata: Metadata) { | |
self.metadata = Some(metadata); | |
} | |
fn data_available(&mut self, mut payload: Vec<u8>) { | |
self.data.append(&mut payload); | |
} | |
fn response_complete(&mut self, status: Result<(), String>) { | |
let metadata = self.metadata.take().unwrap(); | |
let result = MapValue::from_result(match status { | |
Err(_) => Err(()), | |
Ok(_) => { | |
let source_text = UTF_8.decode(&*bytes, DecoderTrap::Replace).unwrap(); | |
create_module_script(source_text, self.settings_object, metadata.final_url) | |
} | |
}); | |
let module_map = settings_object.module_map.lock(); | |
match module_map.insert(url.clone(), result) { | |
Some(MapValue::Fetching(waiters)) => { | |
for waiter in waiters { | |
waiter(result); | |
} | |
}, | |
_ => unreachable!(), | |
} | |
} | |
} | |
impl PreInvoke for ScriptContext {} | |
let script_chan = window.networking_thread_source(); | |
let elem = Trusted::new(self, script_chan.clone()); | |
let context = Arc::new(Mutex::new(ScriptContext { | |
settings_object: settings_object, | |
data: vec![], | |
metadata: None, | |
url: url.clone(), | |
})); | |
let (action_sender, action_receiver) = ipc::channel().unwrap(); | |
let listener = NetworkListener { | |
context: context, | |
script_chan: script_chan, | |
}; | |
let response_target = AsyncResponseTarget { | |
sender: action_sender, | |
}; | |
ROUTER.add_route(action_receiver.to_opaque(), box move |message| { | |
listener.notify(message.to().unwrap()); | |
}); | |
settings_object.document().load_async(LoadType::Script(url), response_target); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Implements https://dl.dropboxusercontent.com/u/20140634/script-type-module/multipage/webappapis.html#fetch-a-module-script-tree