mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
Hook XHR up to fetch
This commit is contained in:
parent
d4f428ad95
commit
5e49873af7
2 changed files with 51 additions and 148 deletions
|
@ -2,8 +2,6 @@
|
|||
* 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 cors::CORSResponse;
|
||||
use cors::{AsyncCORSResponseListener, CORSRequest, RequestMode, allow_cross_origin_request};
|
||||
use document_loader::DocumentLoader;
|
||||
use dom::bindings::cell::DOMRefCell;
|
||||
use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
|
||||
|
@ -45,17 +43,17 @@ use js::jsapi::JS_ClearPendingException;
|
|||
use js::jsapi::{JSContext, JS_ParseJSON, RootedValue};
|
||||
use js::jsval::{JSVal, NullValue, UndefinedValue};
|
||||
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
|
||||
use net_traits::CoreResourceMsg::Load;
|
||||
use net_traits::CoreResourceMsg::Fetch;
|
||||
use net_traits::trim_http_whitespace;
|
||||
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError, RequestSource};
|
||||
use net_traits::{LoadConsumer, LoadContext, LoadData, ResourceCORSData, CoreResourceThread, LoadOrigin};
|
||||
use net_traits::{FetchResponseListener, Metadata, NetworkError, RequestSource};
|
||||
use net_traits::{LoadContext, LoadData, CoreResourceThread, LoadOrigin};
|
||||
use network_listener::{NetworkListener, PreInvoke};
|
||||
use parse::html::{ParseContext, parse_html};
|
||||
use parse::xml::{self, parse_xml};
|
||||
use script_runtime::ScriptChan;
|
||||
use std::ascii::AsciiExt;
|
||||
use std::borrow::ToOwned;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::Cell;
|
||||
use std::default::Default;
|
||||
use std::str;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
@ -63,7 +61,6 @@ use string_cache::Atom;
|
|||
use time;
|
||||
use timers::{OneshotTimerCallback, OneshotTimerHandle};
|
||||
use url::{Url, Position};
|
||||
use util::prefs::mozbrowser_enabled;
|
||||
|
||||
#[derive(JSTraceable, PartialEq, Copy, Clone, HeapSizeOf)]
|
||||
enum XMLHttpRequestState {
|
||||
|
@ -82,7 +79,6 @@ pub struct GenerationId(u32);
|
|||
struct XHRContext {
|
||||
xhr: TrustedXHRAddress,
|
||||
gen_id: GenerationId,
|
||||
cors_request: Option<CORSRequest>,
|
||||
buf: DOMRefCell<Vec<u8>>,
|
||||
sync_status: DOMRefCell<Option<ErrorResult>>,
|
||||
}
|
||||
|
@ -216,75 +212,42 @@ impl XMLHttpRequest {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_cors(context: Arc<Mutex<XHRContext>>,
|
||||
load_data: LoadData,
|
||||
req: CORSRequest,
|
||||
script_chan: Box<ScriptChan + Send>,
|
||||
core_resource_thread: CoreResourceThread) {
|
||||
struct CORSContext {
|
||||
xhr: Arc<Mutex<XHRContext>>,
|
||||
load_data: RefCell<Option<LoadData>>,
|
||||
req: CORSRequest,
|
||||
script_chan: Box<ScriptChan + Send>,
|
||||
core_resource_thread: CoreResourceThread,
|
||||
}
|
||||
|
||||
impl AsyncCORSResponseListener for CORSContext {
|
||||
fn response_available(&self, response: CORSResponse) {
|
||||
if response.network_error {
|
||||
let mut context = self.xhr.lock().unwrap();
|
||||
let xhr = context.xhr.root();
|
||||
xhr.process_partial_response(XHRProgress::Errored(context.gen_id, Error::Network));
|
||||
*context.sync_status.borrow_mut() = Some(Err(Error::Network));
|
||||
return;
|
||||
}
|
||||
|
||||
let mut load_data = self.load_data.borrow_mut().take().unwrap();
|
||||
load_data.cors = Some(ResourceCORSData {
|
||||
preflight: self.req.preflight_flag,
|
||||
origin: self.req.origin.clone()
|
||||
});
|
||||
|
||||
XMLHttpRequest::initiate_async_xhr(self.xhr.clone(), self.script_chan.clone(),
|
||||
self.core_resource_thread.clone(), load_data);
|
||||
}
|
||||
}
|
||||
|
||||
let cors_context = CORSContext {
|
||||
xhr: context,
|
||||
load_data: RefCell::new(Some(load_data)),
|
||||
req: req.clone(),
|
||||
script_chan: script_chan.clone(),
|
||||
core_resource_thread: core_resource_thread,
|
||||
};
|
||||
|
||||
req.http_fetch_async(box cors_context, script_chan);
|
||||
}
|
||||
|
||||
fn initiate_async_xhr(context: Arc<Mutex<XHRContext>>,
|
||||
script_chan: Box<ScriptChan + Send>,
|
||||
core_resource_thread: CoreResourceThread,
|
||||
load_data: LoadData) {
|
||||
impl AsyncResponseListener for XHRContext {
|
||||
fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
|
||||
let xhr = self.xhr.root();
|
||||
let rv = xhr.process_headers_available(self.cors_request.clone(),
|
||||
self.gen_id,
|
||||
metadata);
|
||||
if rv.is_err() {
|
||||
*self.sync_status.borrow_mut() = Some(rv);
|
||||
impl FetchResponseListener for XHRContext {
|
||||
fn process_request_body(&mut self) {
|
||||
// todo
|
||||
}
|
||||
fn process_request_eof(&mut self) {
|
||||
// todo
|
||||
}
|
||||
fn process_response(&mut self, metadata: Result<Metadata, NetworkError>) {
|
||||
let xhr = self.xhr.root();
|
||||
let rv = xhr.process_headers_available(self.gen_id,
|
||||
metadata);
|
||||
if rv.is_err() {
|
||||
*self.sync_status.borrow_mut() = Some(rv);
|
||||
}
|
||||
}
|
||||
fn process_response_eof(&mut self, response: Result<Vec<u8>, NetworkError>) {
|
||||
match response {
|
||||
Ok(buf) => {
|
||||
*self.buf.borrow_mut() = buf;
|
||||
// todo move to a process_chunk
|
||||
self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone());
|
||||
let rv = self.xhr.root().process_response_complete(self.gen_id, Ok(()));
|
||||
*self.sync_status.borrow_mut() = Some(rv);
|
||||
}
|
||||
Err(e) => {
|
||||
let rv = self.xhr.root().process_response_complete(self.gen_id, Err(e));
|
||||
*self.sync_status.borrow_mut() = Some(rv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fn data_available(&mut self, payload: Vec<u8>) {
|
||||
self.buf.borrow_mut().extend_from_slice(&payload);
|
||||
self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone());
|
||||
}
|
||||
|
||||
fn response_complete(&mut self, status: Result<(), NetworkError>) {
|
||||
let rv = self.xhr.root().process_response_complete(self.gen_id, status);
|
||||
*self.sync_status.borrow_mut() = Some(rv);
|
||||
}
|
||||
}
|
||||
|
||||
impl PreInvoke for XHRContext {
|
||||
|
@ -298,13 +261,10 @@ impl XMLHttpRequest {
|
|||
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_action(message.to().unwrap());
|
||||
listener.notify_fetch(message.to().unwrap());
|
||||
});
|
||||
core_resource_thread.send(Load(load_data, LoadConsumer::Listener(response_target), None)).unwrap();
|
||||
core_resource_thread.send(Fetch(load_data, action_sender)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,34 +597,13 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
|||
|
||||
// CORS stuff
|
||||
let global = self.global();
|
||||
let referer_url = self.global().r().get_url();
|
||||
let mode = if self.upload_events.get() {
|
||||
RequestMode::ForcedPreflight
|
||||
} else {
|
||||
RequestMode::CORS
|
||||
};
|
||||
let mut combined_headers = load_data.headers.clone();
|
||||
combined_headers.extend(load_data.preserved_headers.iter());
|
||||
let cors_request = CORSRequest::maybe_new(referer_url.clone(),
|
||||
load_data.url.clone(),
|
||||
mode,
|
||||
load_data.method.clone(),
|
||||
combined_headers,
|
||||
true);
|
||||
match cors_request {
|
||||
Ok(None) => {
|
||||
let bytes = referer_url[..Position::AfterPath].as_bytes().to_vec();
|
||||
self.request_headers.borrow_mut().set_raw("Referer".to_owned(), vec![bytes]);
|
||||
},
|
||||
Ok(Some(ref req)) => self.insert_trusted_header("origin".to_owned(),
|
||||
req.origin.to_string()),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
debug!("request_headers = {:?}", *self.request_headers.borrow());
|
||||
|
||||
self.fetch_time.set(time::now().to_timespec().sec);
|
||||
let rv = self.fetch(load_data, cors_request, global.r());
|
||||
let rv = self.fetch(load_data, global.r());
|
||||
// Step 10
|
||||
if self.sync.get() {
|
||||
return rv;
|
||||
|
@ -879,7 +818,7 @@ impl XMLHttpRequest {
|
|||
event.fire(self.upcast());
|
||||
}
|
||||
|
||||
fn process_headers_available(&self, cors_request: Option<CORSRequest>,
|
||||
fn process_headers_available(&self,
|
||||
gen_id: GenerationId, metadata: Result<Metadata, NetworkError>)
|
||||
-> Result<(), Error> {
|
||||
let metadata = match metadata {
|
||||
|
@ -890,34 +829,7 @@ impl XMLHttpRequest {
|
|||
},
|
||||
};
|
||||
|
||||
let bypass_cross_origin_check = {
|
||||
// We want to be able to do cross-origin requests in browser.html.
|
||||
// If the XHR happens in a top level window and the mozbrowser
|
||||
// preference is enabled, we allow bypassing the CORS check.
|
||||
// This is a temporary measure until we figure out Servo privilege
|
||||
// story. See https://github.com/servo/servo/issues/9582
|
||||
if let GlobalRoot::Window(win) = self.global() {
|
||||
let is_root_pipeline = win.parent_info().is_none();
|
||||
let is_mozbrowser_enabled = mozbrowser_enabled();
|
||||
is_root_pipeline && is_mozbrowser_enabled
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if !bypass_cross_origin_check {
|
||||
if let Some(ref req) = cors_request {
|
||||
match metadata.headers {
|
||||
Some(ref h) if allow_cross_origin_request(req, h) => {},
|
||||
_ => {
|
||||
self.process_partial_response(XHRProgress::Errored(gen_id, Error::Network));
|
||||
return Err(Error::Network);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!("Bypassing cross origin check");
|
||||
}
|
||||
// todo allow cors in mozbrowser
|
||||
|
||||
*self.response_url.borrow_mut() = metadata.final_url[..Position::AfterQuery].to_owned();
|
||||
|
||||
|
@ -1319,24 +1231,12 @@ impl XMLHttpRequest {
|
|||
|
||||
fn fetch(&self,
|
||||
load_data: LoadData,
|
||||
cors_request: Result<Option<CORSRequest>, ()>,
|
||||
global: GlobalRef) -> ErrorResult {
|
||||
let cors_request = match cors_request {
|
||||
Err(_) => {
|
||||
// Happens in case of unsupported cross-origin URI schemes.
|
||||
// Supported schemes are http, https, data, and about.
|
||||
self.process_partial_response(XHRProgress::Errored(
|
||||
self.generation_id.get(), Error::Network));
|
||||
return Err(Error::Network);
|
||||
}
|
||||
Ok(req) => req,
|
||||
};
|
||||
|
||||
let xhr = Trusted::new(self);
|
||||
|
||||
let context = Arc::new(Mutex::new(XHRContext {
|
||||
xhr: xhr,
|
||||
cors_request: cors_request.clone(),
|
||||
gen_id: self.generation_id.get(),
|
||||
buf: DOMRefCell::new(vec!()),
|
||||
sync_status: DOMRefCell::new(None),
|
||||
|
@ -1350,13 +1250,8 @@ impl XMLHttpRequest {
|
|||
};
|
||||
|
||||
let core_resource_thread = global.core_resource_thread();
|
||||
if let Some(req) = cors_request {
|
||||
XMLHttpRequest::check_cors(context.clone(), load_data, req.clone(),
|
||||
script_chan.clone(), core_resource_thread);
|
||||
} else {
|
||||
XMLHttpRequest::initiate_async_xhr(context.clone(), script_chan,
|
||||
core_resource_thread, load_data);
|
||||
}
|
||||
XMLHttpRequest::initiate_async_xhr(context.clone(), script_chan,
|
||||
core_resource_thread, load_data);
|
||||
|
||||
if let Some(script_port) = script_port {
|
||||
loop {
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
* 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 net_traits::{Action, AsyncResponseListener, ResponseAction};
|
||||
use net_traits::{Action, AsyncResponseListener, FetchResponseListener};
|
||||
use net_traits::{FetchResponseMsg, ResponseAction};
|
||||
use script_runtime::ScriptThreadEventCategory::NetworkEvent;
|
||||
use script_runtime::{CommonScriptMsg, ScriptChan};
|
||||
use script_thread::Runnable;
|
||||
|
@ -33,6 +34,13 @@ impl<Listener: AsyncResponseListener + PreInvoke + Send + 'static> NetworkListen
|
|||
}
|
||||
}
|
||||
|
||||
// helps type inference
|
||||
impl<Listener: FetchResponseListener + PreInvoke + Send + 'static> NetworkListener<Listener> {
|
||||
pub fn notify_fetch(&self, action: FetchResponseMsg) {
|
||||
self.notify(action);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue