aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
Diffstat (limited to 'components/script')
-rw-r--r--components/script/Cargo.toml1
-rw-r--r--components/script/cors.rs490
-rw-r--r--components/script/dom/htmllinkelement.rs2
-rw-r--r--components/script/dom/htmlmediaelement.rs2
-rw-r--r--components/script/dom/htmlscriptelement.rs2
-rw-r--r--components/script/dom/xmlhttprequest.rs324
-rw-r--r--components/script/lib.rs2
-rw-r--r--components/script/network_listener.rs35
-rw-r--r--components/script/script_thread.rs2
9 files changed, 166 insertions, 694 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index 056bb6f72bb..95f1044fd36 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -60,7 +60,6 @@ smallvec = "0.1"
string_cache = {version = "0.2.18", features = ["heap_size", "unstable"]}
style = {path = "../style"}
time = "0.1.12"
-unicase = "1.0"
url = {version = "1.0.0", features = ["heap_size", "query_encoding"]}
util = {path = "../util"}
uuid = {version = "0.2", features = ["v4"]}
diff --git a/components/script/cors.rs b/components/script/cors.rs
deleted file mode 100644
index f9d0bbbd264..00000000000
--- a/components/script/cors.rs
+++ /dev/null
@@ -1,490 +0,0 @@
-/* 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/. */
-
-//! A partial implementation of CORS
-//! For now this library is XHR-specific.
-//! For stuff involving `<img>`, `<iframe>`, `<form>`, etc please check what
-//! the request mode should be and compare with the fetch spec
-//! This library will eventually become the core of the Fetch crate
-//! with CORSRequest being expanded into FetchRequest (etc)
-
-use hyper::client::Request;
-use hyper::header::{AccessControlAllowHeaders, AccessControlRequestHeaders};
-use hyper::header::{AccessControlAllowMethods, AccessControlRequestMethod};
-use hyper::header::{AccessControlAllowOrigin, AccessControlMaxAge};
-use hyper::header::{ContentType, Host};
-use hyper::header::{HeaderView, Headers};
-use hyper::method::Method;
-use hyper::mime::{Mime, SubLevel, TopLevel};
-use hyper::status::StatusClass::Success;
-use net_traits::{AsyncResponseListener, Metadata, NetworkError, ResponseAction};
-use network_listener::{NetworkListener, PreInvoke};
-use script_runtime::ScriptChan;
-use std::ascii::AsciiExt;
-use std::borrow::ToOwned;
-use std::sync::{Arc, Mutex};
-use time::{self, Timespec, now};
-use unicase::UniCase;
-use url::Url;
-use util::thread::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);
-}
-
-#[derive(Clone, HeapSizeOf)]
-pub struct CORSRequest {
- pub origin: Url,
- pub destination: Url,
- pub mode: RequestMode,
- #[ignore_heap_size_of = "Defined in hyper"]
- pub method: Method,
- #[ignore_heap_size_of = "Defined in hyper"]
- pub headers: Headers,
- /// CORS preflight flag (https://fetch.spec.whatwg.org/#concept-http-fetch)
- /// Indicates that a CORS preflight request and/or cache check is to be performed
- pub preflight_flag: bool,
-}
-
-/// https://fetch.spec.whatwg.org/#concept-request-mode
-/// This only covers some of the request modes. The
-/// `same-origin` and `no CORS` modes are unnecessary for XHR.
-#[derive(PartialEq, Copy, Clone, HeapSizeOf)]
-pub enum RequestMode {
- CORS, // CORS
- ForcedPreflight, // CORS-with-forced-preflight
-}
-
-impl CORSRequest {
- /// Creates a CORS request if necessary. Will return an error when fetching is forbidden
- pub fn maybe_new(referer: Url,
- destination: Url,
- mode: RequestMode,
- method: Method,
- headers: Headers,
- same_origin_data_url_flag: bool)
- -> Result<Option<CORSRequest>, ()> {
- if referer.origin() == destination.origin() {
- return Ok(None); // Not cross-origin, proceed with a normal fetch
- }
- match destination.scheme() {
- // As per (https://fetch.spec.whatwg.org/#main-fetch 5.1.9), about URLs can be fetched
- // the same as a basic request.
- "about" if destination.path() == "blank" => Ok(None),
- // As per (https://fetch.spec.whatwg.org/#main-fetch 5.1.9), data URLs can be fetched
- // the same as a basic request if the request's method is GET and the
- // same-origin data-URL flag is set.
- "data" if same_origin_data_url_flag && method == Method::Get => Ok(None),
- "http" | "https" => {
- let mut req = CORSRequest::new(referer, destination, mode, method, headers);
- req.preflight_flag = !is_simple_method(&req.method) ||
- mode == RequestMode::ForcedPreflight;
- if req.headers.iter().any(|h| !is_simple_header(&h)) {
- req.preflight_flag = true;
- }
- Ok(Some(req))
- },
- _ => Err(()),
- }
- }
-
- fn new(mut referer: Url,
- destination: Url,
- mode: RequestMode,
- method: Method,
- headers: Headers)
- -> CORSRequest {
- referer.set_fragment(None);
- referer.set_query(None);
- referer.set_path("");
- CORSRequest {
- origin: referer,
- destination: destination,
- mode: mode,
- method: method,
- headers: headers,
- preflight_flag: false,
- }
- }
-
- pub fn http_fetch_async(&self,
- listener: Box<AsyncCORSResponseListener + Send>,
- script_chan: Box<ScriptChan + Send>) {
- struct CORSContext {
- listener: Box<AsyncCORSResponseListener + Send>,
- response: 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(&mut self, _metadata: Result<Metadata, NetworkError>) {
- }
-
- fn data_available(&mut self, _payload: Vec<u8>) {
- }
-
- fn response_complete(&mut self, _status: Result<(), NetworkError>) {
- let response = self.response.take().unwrap();
- self.listener.response_available(response);
- }
- }
- impl PreInvoke for CORSContext {}
-
- let context = CORSContext {
- listener: listener,
- response: 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 thread?
- let req = self.clone();
- spawn_named("cors".to_owned(), move || {
- let response = req.http_fetch();
- let mut context = listener.context.lock();
- let context = context.as_mut().unwrap();
- context.response = Some(response);
- listener.notify(ResponseAction::ResponseComplete(Ok(())));
- });
- }
-
- /// http://fetch.spec.whatwg.org/#concept-http-fetch
- /// This method assumes that the CORS flag is set
- /// This does not perform the full HTTP fetch, rather it handles part of the CORS filtering
- /// if self.mode is ForcedPreflight, then the CORS-with-forced-preflight
- /// fetch flag is set as well
- pub fn http_fetch(&self) -> CORSResponse {
- let response = CORSResponse::new();
- // Step 2: Handle service workers (unimplemented)
- // Step 3
- // Substep 1: Service workers (unimplemented )
- // Substep 2
- let cache = &mut CORSCache(vec!()); // XXXManishearth Should come from user agent
- if self.preflight_flag &&
- !cache.match_method(self, &self.method) &&
- !self.headers.iter().all(|h| is_simple_header(&h) && cache.match_header(self, h.name())) &&
- (!is_simple_method(&self.method) || self.mode == RequestMode::ForcedPreflight) {
- return self.preflight_fetch();
- // Everything after this is part of XHR::fetch()
- // Expect the organization of code to improve once we have a fetch crate
- }
- response
- }
-
- /// https://fetch.spec.whatwg.org/#cors-preflight-fetch
- fn preflight_fetch(&self) -> CORSResponse {
- let error = CORSResponse::new_error();
- let mut cors_response = CORSResponse::new();
-
- // Step 1
- let mut preflight = self.clone();
- preflight.method = Method::Options;
- preflight.headers = Headers::new();
- // Step 2
- preflight.headers.set(AccessControlRequestMethod(self.method.clone()));
-
- // Steps 3-5
- let mut header_names = vec![];
- for header in self.headers.iter() {
- header_names.push(header.name().to_owned());
- }
- header_names.sort();
- preflight.headers
- .set(AccessControlRequestHeaders(header_names.into_iter().map(UniCase).collect()));
-
- let preflight_request = Request::new(preflight.method, preflight.destination);
- let mut req = match preflight_request {
- Ok(req) => req,
- Err(_) => return error,
- };
-
- let host = req.headers().get::<Host>().unwrap().clone();
- *req.headers_mut() = preflight.headers.clone();
- req.headers_mut().set(host);
- let stream = match req.start() {
- Ok(s) => s,
- Err(_) => return error,
- };
- // Step 6
- let response = match stream.send() {
- Ok(r) => r,
- Err(_) => return error,
- };
-
- // Step 7: We don't perform a CORS check here
- // FYI, fn allow_cross_origin_request() performs the CORS check
- match response.status.class() {
- Success => {}
- _ => return error,
- }
- cors_response.headers = response.headers.clone();
- // Substeps 1-3 (parsing rules: https://fetch.spec.whatwg.org/#http-new-header-syntax)
- let methods_substep4 = [self.method.clone()];
- let mut methods = match response.headers.get() {
- Some(&AccessControlAllowMethods(ref v)) => &**v,
- _ => return error,
- };
- let headers = match response.headers.get() {
- Some(&AccessControlAllowHeaders(ref h)) => h,
- _ => return error,
- };
- // Substep 4
- if methods.is_empty() && preflight.mode == RequestMode::ForcedPreflight {
- methods = &methods_substep4;
- }
- // Substep 5
- if !is_simple_method(&self.method) && !methods.iter().any(|m| m == &self.method) {
- return error;
- }
- // Substep 6
- for h in self.headers.iter() {
- if is_simple_header(&h) {
- continue;
- }
- if !headers.iter().any(|ref h2| h.name().eq_ignore_ascii_case(h2)) {
- return error;
- }
- }
- // Substeps 7-8
- let max_age = match response.headers.get() {
- Some(&AccessControlMaxAge(num)) => num,
- None => 0,
- };
- // Substep 9: Impose restrictions on max-age, if any (unimplemented)
- // Substeps 10-12: Add a cache (partially implemented, XXXManishearth)
- // This cache should come from the user agent, creating a new one here to check
- // for compile time errors
- let cache = &mut CORSCache(vec![]);
- for m in methods {
- let cache_match = cache.match_method_and_update(self, m, max_age);
- if !cache_match {
- cache.insert(CORSCacheEntry::new(self.origin.clone(),
- self.destination.clone(),
- max_age,
- false,
- HeaderOrMethod::MethodData(m.clone())));
- }
- }
- // Substeps 13-14
- for h in response.headers.iter() {
- let cache_match = cache.match_header_and_update(self, h.name(), max_age);
- if !cache_match {
- cache.insert(CORSCacheEntry::new(self.origin.clone(),
- self.destination.clone(),
- max_age,
- false,
- HeaderOrMethod::HeaderData(h.to_string())));
- }
- }
- // Substep 15
- cors_response
- }
-}
-
-
-pub struct CORSResponse {
- pub network_error: bool,
- pub headers: Headers,
-}
-
-impl CORSResponse {
- fn new() -> CORSResponse {
- CORSResponse {
- network_error: false,
- headers: Headers::new(),
- }
- }
-
- fn new_error() -> CORSResponse {
- CORSResponse {
- network_error: true,
- headers: Headers::new(),
- }
- }
-}
-
-// CORS Cache stuff
-
-/// A CORS cache object. Anchor it somewhere to the user agent.
-#[derive(Clone)]
-pub struct CORSCache(Vec<CORSCacheEntry>);
-
-/// Union type for CORS cache entries
-/// Each entry might pertain to a header or method
-#[derive(Clone)]
-pub enum HeaderOrMethod {
- HeaderData(String),
- MethodData(Method),
-}
-
-impl HeaderOrMethod {
- fn match_header(&self, header_name: &str) -> bool {
- match *self {
- HeaderOrMethod::HeaderData(ref s) => (&**s).eq_ignore_ascii_case(header_name),
- _ => false,
- }
- }
-
- fn match_method(&self, method: &Method) -> bool {
- match *self {
- HeaderOrMethod::MethodData(ref m) => m == method,
- _ => false,
- }
- }
-}
-
-// An entry in the CORS cache
-#[derive(Clone)]
-pub struct CORSCacheEntry {
- pub origin: Url,
- pub url: Url,
- pub max_age: u32,
- pub credentials: bool,
- pub header_or_method: HeaderOrMethod,
- created: Timespec,
-}
-
-impl CORSCacheEntry {
- fn new(origin: Url,
- url: Url,
- max_age: u32,
- credentials: bool,
- header_or_method: HeaderOrMethod)
- -> CORSCacheEntry {
- CORSCacheEntry {
- origin: origin,
- url: url,
- max_age: max_age,
- credentials: credentials,
- header_or_method: header_or_method,
- created: time::now().to_timespec(),
- }
- }
-}
-
-impl CORSCache {
- /// https://fetch.spec.whatwg.org/#concept-cache-clear
- #[allow(dead_code)]
- fn clear(&mut self, request: &CORSRequest) {
- let CORSCache(buf) = self.clone();
- let new_buf: Vec<CORSCacheEntry> =
- buf.into_iter()
- .filter(|e| e.origin == request.origin && request.destination == e.url)
- .collect();
- *self = CORSCache(new_buf);
- }
-
- // Remove old entries
- fn cleanup(&mut self) {
- let CORSCache(buf) = self.clone();
- let now = time::now().to_timespec();
- let new_buf: Vec<CORSCacheEntry> = buf.into_iter()
- .filter(|e| now.sec > e.created.sec + e.max_age as i64)
- .collect();
- *self = CORSCache(new_buf);
- }
-
- /// https://fetch.spec.whatwg.org/#concept-cache-match-header
- fn find_entry_by_header<'a>(&'a mut self,
- request: &CORSRequest,
- header_name: &str)
- -> Option<&'a mut CORSCacheEntry> {
- self.cleanup();
- // Credentials are not yet implemented here
- self.0.iter_mut().find(|e| {
- e.origin.scheme() == request.origin.scheme() &&
- e.origin.host_str() == request.origin.host_str() &&
- e.origin.port() == request.origin.port() &&
- e.url == request.destination &&
- e.header_or_method.match_header(header_name)
- })
- }
-
- fn match_header(&mut self, request: &CORSRequest, header_name: &str) -> bool {
- self.find_entry_by_header(request, header_name).is_some()
- }
-
- fn match_header_and_update(&mut self,
- request: &CORSRequest,
- header_name: &str,
- new_max_age: u32)
- -> bool {
- self.find_entry_by_header(request, header_name).map(|e| e.max_age = new_max_age).is_some()
- }
-
- fn find_entry_by_method<'a>(&'a mut self,
- request: &CORSRequest,
- method: &Method)
- -> Option<&'a mut CORSCacheEntry> {
- // we can take the method from CORSRequest itself
- self.cleanup();
- // Credentials are not yet implemented here
- self.0.iter_mut().find(|e| {
- e.origin.scheme() == request.origin.scheme() &&
- e.origin.host_str() == request.origin.host_str() &&
- e.origin.port() == request.origin.port() &&
- e.url == request.destination &&
- e.header_or_method.match_method(method)
- })
- }
-
- /// https://fetch.spec.whatwg.org/#concept-cache-match-method
- fn match_method(&mut self, request: &CORSRequest, method: &Method) -> bool {
- self.find_entry_by_method(request, method).is_some()
- }
-
- fn match_method_and_update(&mut self,
- request: &CORSRequest,
- method: &Method,
- new_max_age: u32)
- -> bool {
- self.find_entry_by_method(request, method).map(|e| e.max_age = new_max_age).is_some()
- }
-
- fn insert(&mut self, entry: CORSCacheEntry) {
- self.cleanup();
- self.0.push(entry);
- }
-}
-
-fn is_simple_header(h: &HeaderView) -> bool {
- // FIXME: use h.is::<HeaderType>() when AcceptLanguage and
- // ContentLanguage headers exist
- match &*h.name().to_ascii_lowercase() {
- "accept" | "accept-language" | "content-language" => true,
- "content-type" => match h.value() {
- Some(&ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) |
- Some(&ContentType(Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _))) |
- Some(&ContentType(Mime(TopLevel::Multipart, SubLevel::FormData, _))) => true,
-
- _ => false,
-
- },
- _ => false,
- }
-}
-
-fn is_simple_method(m: &Method) -> bool {
- match *m {
- Method::Get | Method::Head | Method::Post => true,
- _ => false,
- }
-}
-
-/// Perform a CORS check on a header list and CORS request
-/// https://fetch.spec.whatwg.org/#cors-check
-pub fn allow_cross_origin_request(req: &CORSRequest, headers: &Headers) -> bool {
- match headers.get::<AccessControlAllowOrigin>() {
- Some(&AccessControlAllowOrigin::Any) => true, // Not always true, depends on credentials mode
- Some(&AccessControlAllowOrigin::Value(ref url)) => req.origin.as_str() == *url,
- Some(&AccessControlAllowOrigin::Null) |
- None => false,
- }
-}
diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
index 2b0056a8d65..79b6ed32f0b 100644
--- a/components/script/dom/htmllinkelement.rs
+++ b/components/script/dom/htmllinkelement.rs
@@ -227,7 +227,7 @@ impl HTMLLinkElement {
sender: action_sender,
};
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
- listener.notify(message.to().unwrap());
+ listener.notify_action(message.to().unwrap());
});
if self.parser_inserted.get() {
diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
index 7256ae1b131..70563ec95c7 100644
--- a/components/script/dom/htmlmediaelement.rs
+++ b/components/script/dom/htmlmediaelement.rs
@@ -484,7 +484,7 @@ impl HTMLMediaElement {
sender: action_sender,
};
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
- listener.notify(message.to().unwrap());
+ listener.notify_action(message.to().unwrap());
});
// FIXME: we're supposed to block the load event much earlier than now
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 1f9f49516dd..f280f670e4b 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -314,7 +314,7 @@ impl HTMLScriptElement {
sender: action_sender,
};
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
- listener.notify(message.to().unwrap());
+ listener.notify_action(message.to().unwrap());
});
doc.load_async(LoadType::Script(url), response_target);
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index 318d3bbc1ec..785a5928c9f 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -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;
@@ -35,27 +33,28 @@ use encoding::label::encoding_from_whatwg_label;
use encoding::types::{DecoderTrap, EncoderTrap, Encoding, EncodingRef};
use euclid::length::Length;
use hyper::header::Headers;
-use hyper::header::{Accept, ContentLength, ContentType, qitem};
+use hyper::header::{ContentLength, ContentType};
use hyper::http::RawStatus;
use hyper::method::Method;
-use hyper::mime::{self, Mime};
+use hyper::mime::{self, Mime, Attr as MimeAttr, Value as MimeValue};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
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::request::{CredentialsMode, Destination, RequestInit, RequestMode};
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::{CoreResourceThread, LoadOrigin};
+use net_traits::{FetchResponseListener, Metadata, NetworkError, RequestSource};
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};
@@ -82,7 +81,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>>,
}
@@ -142,7 +140,6 @@ pub struct XMLHttpRequest {
request_body_len: Cell<usize>,
sync: Cell<bool>,
upload_complete: Cell<bool>,
- upload_events: Cell<bool>,
send_flag: Cell<bool>,
timeout_cancel: DOMRefCell<Option<OneshotTimerHandle>>,
@@ -187,7 +184,6 @@ impl XMLHttpRequest {
request_body_len: Cell::new(0),
sync: Cell::new(false),
upload_complete: Cell::new(false),
- upload_events: Cell::new(false),
send_flag: Cell::new(false),
timeout_cancel: DOMRefCell::new(None),
@@ -216,75 +212,40 @@ 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() {
+ init: RequestInit) {
+ 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_chunk(&mut self, mut chunk: Vec<u8>) {
+ self.buf.borrow_mut().append(&mut chunk);
+ self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone());
+ }
+ fn process_response_eof(&mut self, response: Result<(), NetworkError>) {
+ let rv = match response {
+ Ok(()) => {
+ self.xhr.root().process_response_complete(self.gen_id, Ok(()))
+ }
+ Err(e) => {
+ 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 +259,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(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(init, action_sender)).unwrap();
}
}
@@ -563,12 +521,15 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
Method::Get | Method::Head => None,
_ => data
};
- // Step 4
+ // Step 4 (first half)
let extracted = data.as_ref().map(|d| d.extract());
+
self.request_body_len.set(extracted.as_ref().map_or(0, |e| e.0.len()));
+ // todo preserved headers?
+
// Step 6
- self.upload_events.set(false);
+ self.upload_complete.set(false);
// Step 7
self.upload_complete.set(match extracted {
None => true,
@@ -580,11 +541,6 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
// Step 9
if !self.sync.get() {
- let event_target = self.upload.upcast::<EventTarget>();
- if event_target.has_handlers() {
- self.upload_events.set(true);
- }
-
// If one of the event handlers below aborts the fetch by calling
// abort or open we will need the current generation id to detect it.
// Substep 1
@@ -604,67 +560,108 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
}
// Step 5
- let global = self.global();
+ //TODO - set referrer_policy/referrer_url in request
+ let has_handlers = self.upload.upcast::<EventTarget>().has_handlers();
+ let credentials_mode = if self.with_credentials.get() {
+ CredentialsMode::Include
+ } else {
+ CredentialsMode::CredentialsSameOrigin
+ };
+ let use_url_credentials = if let Some(ref url) = *self.request_url.borrow() {
+ !url.username().is_empty() || url.password().is_some()
+ } else {
+ unreachable!()
+ };
- let mut load_data =
- LoadData::new(LoadContext::Browsing,
- self.request_url.borrow().clone().unwrap(),
- self);
+ 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 load_data.url.origin().ne(&global.r().get_url().origin()) {
- load_data.credentials_flag = self.WithCredentials();
- }
- load_data.data = extracted.as_ref().map(|e| e.0.clone());
+ let mut request = RequestInit {
+ method: self.request_method.borrow().clone(),
+ url: self.request_url.borrow().clone().unwrap(),
+ headers: (*self.request_headers.borrow()).clone(),
+ unsafe_request: true,
+ same_origin_data: true,
+ // XXXManishearth figure out how to avoid this clone
+ body: extracted.as_ref().map(|e| e.0.clone()),
+ // XXXManishearth actually "subresource", but it doesn't exist
+ // https://github.com/whatwg/xhr/issues/71
+ destination: Destination::None,
+ synchronous: self.sync.get(),
+ mode: RequestMode::CORSMode,
+ use_cors_preflight: has_handlers,
+ credentials_mode: credentials_mode,
+ use_url_credentials: use_url_credentials,
+ origin: self.global().r().get_url(),
+ referer_url: self.referrer_url.clone(),
+ referrer_policy: self.referrer_policy.clone(),
+ };
- // XHR spec differs from http, and says UTF-8 should be in capitals,
- // instead of "utf-8", which is what Hyper defaults to. So not
- // using content types provided by Hyper.
- let n = "content-type";
- match extracted {
- Some((_, Some(ref content_type))) =>
- load_data.headers.set_raw(n.to_owned(), vec![content_type.bytes().collect()]),
- _ => (),
+ if bypass_cross_origin_check {
+ request.mode = RequestMode::Navigate;
}
- load_data.preserved_headers = (*self.request_headers.borrow()).clone();
+ // step 4 (second half)
+ match extracted {
+ Some((_, ref content_type)) => {
+ // this should handle Document bodies too, not just BodyInit
+ let encoding = if let Some(BodyInit::String(_)) = data {
+ // XHR spec differs from http, and says UTF-8 should be in capitals,
+ // instead of "utf-8", which is what Hyper defaults to. So not
+ // using content types provided by Hyper.
+ Some(MimeValue::Ext("UTF-8".to_string()))
+ } else {
+ None
+ };
- if !load_data.preserved_headers.has::<Accept>() {
- let mime = Mime(mime::TopLevel::Star, mime::SubLevel::Star, vec![]);
- load_data.preserved_headers.set(Accept(vec![qitem(mime)]));
- }
+ let mut content_type_set = false;
+ if let Some(ref ct) = *content_type {
+ if !request.headers.has::<ContentType>() {
+ request.headers.set_raw("content-type", vec![ct.bytes().collect()]);
+ content_type_set = true;
+ }
+ }
- load_data.method = (*self.request_method.borrow()).clone();
+ if !content_type_set {
+ let ct = request.headers.get::<ContentType>().map(|x| x.clone());
+ if let Some(mut ct) = ct {
+ if let Some(encoding) = encoding {
+ for param in &mut (ct.0).2 {
+ if param.0 == MimeAttr::Charset {
+ if !param.0.as_str().eq_ignore_ascii_case(encoding.as_str()) {
+ *param = (MimeAttr::Charset, encoding.clone());
+ }
+ }
+ }
+ }
+ // remove instead of mutate in place
+ // https://github.com/hyperium/hyper/issues/821
+ request.headers.remove_raw("content-type");
+ request.headers.set(ct);
+ }
+ }
- // 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());
+ debug!("request.headers = {:?}", request.headers);
self.fetch_time.set(time::now().to_timespec().sec);
- let rv = self.fetch(load_data, cors_request, global.r());
+
+ let rv = self.fetch(request, self.global().r());
// Step 10
if self.sync.get() {
return rv;
@@ -879,7 +876,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,35 +887,6 @@ 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");
- }
-
*self.response_url.borrow_mut() = metadata.final_url[..Position::AfterQuery].to_owned();
// XXXManishearth Clear cache entries in case of a network error
@@ -1318,25 +1286,12 @@ impl XMLHttpRequest {
}
fn fetch(&self,
- load_data: LoadData,
- cors_request: Result<Option<CORSRequest>, ()>,
+ init: RequestInit,
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 +1305,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, init);
if let Some(script_port) = script_port {
loop {
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 71e53141d9a..2f76a112c0a 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -79,7 +79,6 @@ extern crate style;
extern crate time;
#[cfg(any(target_os = "macos", target_os = "linux"))]
extern crate tinyfiledialogs;
-extern crate unicase;
extern crate url;
#[macro_use]
extern crate util;
@@ -91,7 +90,6 @@ extern crate xml5ever;
mod blob_url_store;
pub mod bluetooth_blacklist;
pub mod clipboard_provider;
-pub mod cors;
mod devtools;
pub mod document_loader;
#[macro_use]
diff --git a/components/script/network_listener.rs b/components/script/network_listener.rs
index cff0723d277..13aa26c25e4 100644
--- a/components/script/network_listener.rs
+++ b/components/script/network_listener.rs
@@ -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::{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;
@@ -10,13 +11,13 @@ 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 struct NetworkListener<Listener: PreInvoke + Send + 'static> {
+ pub context: Arc<Mutex<Listener>>,
pub script_chan: Box<ScriptChan + Send>,
}
-impl<T: AsyncResponseListener + PreInvoke + Send + 'static> NetworkListener<T> {
- pub fn notify(&self, action: ResponseAction) {
+impl<Listener: PreInvoke + Send + 'static> NetworkListener<Listener> {
+ pub fn notify<A: Action<Listener> + Send + 'static>(&self, action: A) {
if let Err(err) = self.script_chan.send(CommonScriptMsg::RunnableMsg(NetworkEvent, box ListenerRunnable {
context: self.context.clone(),
action: action,
@@ -26,6 +27,20 @@ impl<T: AsyncResponseListener + PreInvoke + Send + 'static> NetworkListener<T> {
}
}
+// helps type inference
+impl<Listener: AsyncResponseListener + PreInvoke + Send + 'static> NetworkListener<Listener> {
+ pub fn notify_action(&self, action: ResponseAction) {
+ self.notify(action);
+ }
+}
+
+// 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.
@@ -36,13 +51,13 @@ pub trait PreInvoke {
}
/// A runnable for moving the async network events between threads.
-struct ListenerRunnable<T: AsyncResponseListener + PreInvoke + Send> {
- context: Arc<Mutex<T>>,
- action: ResponseAction,
+struct ListenerRunnable<A: Action<Listener> + Send + 'static, Listener: PreInvoke + Send> {
+ context: Arc<Mutex<Listener>>,
+ action: A,
}
-impl<T: AsyncResponseListener + PreInvoke + Send> Runnable for ListenerRunnable<T> {
- fn handler(self: Box<ListenerRunnable<T>>) {
+impl<A: Action<Listener> + Send + 'static, Listener: PreInvoke + Send> Runnable for ListenerRunnable<A, Listener> {
+ fn handler(self: Box<ListenerRunnable<A, Listener>>) {
let this = *self;
let mut context = this.context.lock().unwrap();
if context.should_invoke() {
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 7684eb9e336..8cf97710bc7 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -1962,7 +1962,7 @@ impl ScriptThread {
script_chan: self.chan.clone(),
};
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
- listener.notify(message.to().unwrap());
+ listener.notify_action(message.to().unwrap());
});
let response_target = AsyncResponseTarget {
sender: action_sender,