diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/cors.rs | 51 | ||||
-rw-r--r-- | components/script/dom/xmlhttprequest.rs | 88 | ||||
-rw-r--r-- | components/script/lib.rs | 1 | ||||
-rw-r--r-- | components/script/network_listener.rs | 48 |
4 files changed, 110 insertions, 78 deletions
diff --git a/components/script/cors.rs b/components/script/cors.rs index 0b669ab8bf8..235ec43f779 100644 --- a/components/script/cors.rs +++ b/components/script/cors.rs @@ -9,8 +9,14 @@ //! This library will eventually become the core of the Fetch crate //! with CORSRequest being expanded into FetchRequest (etc) +use network_listener::{NetworkListener, PreInvoke}; +use script_task::ScriptChan; +use net_traits::{AsyncResponseTarget, AsyncResponseListener, ResponseAction, Metadata}; + use std::ascii::AsciiExt; use std::borrow::ToOwned; +use std::cell::RefCell; +use std::sync::{Arc, Mutex}; use time; use time::{now, Timespec}; @@ -27,14 +33,12 @@ use hyper::status::StatusClass::Success; use url::{SchemeData, Url}; use util::task::spawn_named; +/// Interface for network listeners concerned with CORS checks. Proper network requests +/// should be initiated from this method, based on the response provided. pub trait AsyncCORSResponseListener { fn response_available(&self, response: CORSResponse); } -pub trait AsyncCORSResponseTarget { - fn invoke_with_listener(&self, response: CORSResponse); -} - #[derive(Clone)] pub struct CORSRequest { pub origin: Url, @@ -98,13 +102,48 @@ impl CORSRequest { } } - pub fn http_fetch_async(&self, listener: Box<AsyncCORSResponseTarget + Send>) { + pub fn http_fetch_async(&self, + listener: Box<AsyncCORSResponseListener+Send>, + script_chan: Box<ScriptChan+Send>) { + struct CORSContext { + listener: Box<AsyncCORSResponseListener+Send>, + response: RefCell<Option<CORSResponse>>, + } + + // This is shoe-horning the CORSReponse stuff into the rest of the async network + // framework right now. It would be worth redesigning http_fetch to do this properly. + impl AsyncResponseListener for CORSContext { + fn headers_available(&self, _metadata: Metadata) { + } + + fn data_available(&self, _payload: Vec<u8>) { + } + + fn response_complete(&self, _status: Result<(), String>) { + let response = self.response.borrow_mut().take().unwrap(); + self.listener.response_available(response); + } + } + impl PreInvoke for CORSContext {} + + let context = CORSContext { + listener: listener, + response: RefCell::new(None), + }; + let listener = NetworkListener { + context: Arc::new(Mutex::new(context)), + script_chan: script_chan, + }; + // TODO: this exists only to make preflight check non-blocking // perhaps should be handled by the resource task? let req = self.clone(); spawn_named("cors".to_owned(), move || { let response = req.http_fetch(); - listener.invoke_with_listener(response); + let mut context = listener.context.lock(); + let context = context.as_mut().unwrap(); + *context.response.borrow_mut() = Some(response); + listener.invoke_with_listener(ResponseAction::ResponseComplete(Ok(()))); }); } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 431400ea5a3..a4e9e767dd5 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -26,6 +26,7 @@ use dom::urlsearchparams::URLSearchParamsHelpers; use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget; use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTargetTypeId; use dom::xmlhttprequestupload::XMLHttpRequestUpload; +use network_listener::{NetworkListener, PreInvoke}; use script_task::{ScriptChan, ScriptMsg, Runnable, ScriptPort}; use encoding::all::UTF_8; @@ -43,10 +44,10 @@ use js::jsapi::JS_ClearPendingException; use js::jsval::{JSVal, NullValue, UndefinedValue}; use net_traits::ControlMsg::Load; -use net_traits::{ResourceTask, ResourceCORSData, LoadData, LoadConsumer, AsyncResponseTarget}; -use net_traits::{AsyncResponseListener, ResponseAction, Metadata}; +use net_traits::{ResourceTask, ResourceCORSData, LoadData, LoadConsumer}; +use net_traits::{AsyncResponseListener, Metadata}; use cors::{allow_cross_origin_request, CORSRequest, RequestMode, AsyncCORSResponseListener}; -use cors::{AsyncCORSResponseTarget, CORSResponse}; +use cors::CORSResponse; use util::str::DOMString; use util::task::spawn_named; @@ -224,45 +225,15 @@ impl XMLHttpRequest { } } - struct CORSListener { - context: Arc<Mutex<CORSContext>>, - script_chan: Box<ScriptChan+Send>, - } - - impl AsyncCORSResponseTarget for CORSListener { - fn invoke_with_listener(&self, response: CORSResponse) { - self.script_chan.send(ScriptMsg::RunnableMsg(box CORSRunnable { - context: self.context.clone(), - response: response, - })).unwrap(); - } - } - - struct CORSRunnable { - context: Arc<Mutex<CORSContext>>, - response: CORSResponse, - } - - impl Runnable for CORSRunnable { - fn handler(self: Box<CORSRunnable>) { - let this = *self; - let context = this.context.lock().unwrap(); - context.response_available(this.response); - } - } - - let cors_context = Arc::new(Mutex::new(CORSContext { + let cors_context = CORSContext { xhr: context, load_data: RefCell::new(Some(load_data)), req: req.clone(), script_chan: script_chan.clone(), resource_task: resource_task, - })); + }; - req.http_fetch_async(box CORSListener { - context: cors_context, - script_chan: script_chan - }); + req.http_fetch_async(box cors_context, script_chan); } fn initiate_async_xhr(context: Arc<Mutex<XHRContext>>, @@ -293,40 +264,14 @@ impl XMLHttpRequest { } } - struct XHRRunnable { - context: Arc<Mutex<XHRContext>>, - action: ResponseAction, - } - - impl Runnable for XHRRunnable { - fn handler(self: Box<XHRRunnable>) { - let this = *self; - - let context = this.context.lock().unwrap(); - let xhr = context.xhr.to_temporary().root(); - if xhr.r().generation_id.get() != context.gen_id { - return; - } - - this.action.process(&*context); - } - } - - struct XHRListener { - context: Arc<Mutex<XHRContext>>, - script_chan: Box<ScriptChan+Send>, - } - - impl AsyncResponseTarget for XHRListener { - fn invoke_with_listener(&self, action: ResponseAction) { - self.script_chan.send(ScriptMsg::RunnableMsg(box XHRRunnable { - context: self.context.clone(), - action: action - })).unwrap(); + impl PreInvoke for XHRContext { + fn should_invoke(&self) -> bool { + let xhr = self.xhr.to_temporary().root(); + xhr.r().generation_id.get() == self.gen_id } } - let listener = box XHRListener { + let listener = box NetworkListener { context: context, script_chan: script_chan, }; @@ -635,7 +580,7 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> { debug!("request_headers = {:?}", *self.request_headers.borrow()); self.fetch_time.set(time::now().to_timespec().sec); - let rv = self.fetch2(load_data, cors_request, global.r()); + let rv = self.fetch(load_data, cors_request, global.r()); if self.sync.get() { return rv; } @@ -798,8 +743,8 @@ trait PrivateXMLHttpRequestHelpers { fn cancel_timeout(self); fn filter_response_headers(self) -> Headers; fn discard_subsequent_responses(self); - fn fetch2(self, load_data: LoadData, cors_request: Result<Option<CORSRequest>,()>, - global: GlobalRef) -> ErrorResult; + fn fetch(self, load_data: LoadData, cors_request: Result<Option<CORSRequest>,()>, + global: GlobalRef) -> ErrorResult; } impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { @@ -1119,8 +1064,7 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { self.response_status.set(Err(())); } - #[allow(unsafe_code)] - fn fetch2(self, + fn fetch(self, load_data: LoadData, cors_request: Result<Option<CORSRequest>,()>, global: GlobalRef) -> ErrorResult { diff --git a/components/script/lib.rs b/components/script/lib.rs index 4a9fcdbd09c..b9f4b42f84d 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -61,6 +61,7 @@ pub mod dom; pub mod parse; pub mod layout_interface; +mod network_listener; pub mod page; pub mod script_task; mod timers; diff --git a/components/script/network_listener.rs b/components/script/network_listener.rs new file mode 100644 index 00000000000..5f400eb76b3 --- /dev/null +++ b/components/script/network_listener.rs @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use script_task::{ScriptChan, ScriptMsg, Runnable}; +use net_traits::{AsyncResponseTarget, AsyncResponseListener, ResponseAction}; +use std::sync::{Arc, Mutex}; + +/// An off-thread sink for async network event runnables. All such events are forwarded to +/// a target thread, where they are invoked on the provided context object. +pub struct NetworkListener<T: AsyncResponseListener + PreInvoke + Send + 'static> { + pub context: Arc<Mutex<T>>, + pub script_chan: Box<ScriptChan+Send>, +} + +impl<T: AsyncResponseListener + PreInvoke + Send + 'static> AsyncResponseTarget for NetworkListener<T> { + fn invoke_with_listener(&self, action: ResponseAction) { + self.script_chan.send(ScriptMsg::RunnableMsg(box ListenerRunnable { + context: self.context.clone(), + action: action, + })).unwrap(); + } +} + +/// A gating mechanism that runs before invoking the runnable on the target thread. +/// If the `should_invoke` method returns false, the runnable is discarded without +/// being invoked. +pub trait PreInvoke { + fn should_invoke(&self) -> bool { + true + } +} + +/// A runnable for moving the async network events between threads. +struct ListenerRunnable<T: AsyncResponseListener + PreInvoke + Send> { + context: Arc<Mutex<T>>, + action: ResponseAction, +} + +impl<T: AsyncResponseListener + PreInvoke + Send> Runnable for ListenerRunnable<T> { + fn handler(self: Box<ListenerRunnable<T>>) { + let this = *self; + let context = this.context.lock().unwrap(); + if context.should_invoke() { + this.action.process(&*context); + } + } +} |