Skip to content

Instantly share code, notes, and snippets.

@Ms2ger
Created January 19, 2016 14:26
Show Gist options
  • Save Ms2ger/95a483aade63bf257521 to your computer and use it in GitHub Desktop.
Save Ms2ger/95a483aade63bf257521 to your computer and use it in GitHub Desktop.
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);
}
@Ms2ger
Copy link
Author

Ms2ger commented Jan 19, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment