diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/Cargo.toml | 15 | ||||
-rw-r--r-- | components/script/body.rs | 33 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 10 | ||||
-rw-r--r-- | components/script/dom/document.rs | 35 | ||||
-rw-r--r-- | components/script/dom/domimplementation.rs | 16 | ||||
-rw-r--r-- | components/script/dom/eventsource.rs | 28 | ||||
-rw-r--r-- | components/script/dom/filereader.rs | 11 | ||||
-rw-r--r-- | components/script/dom/headers.rs | 63 | ||||
-rwxr-xr-x | components/script/dom/htmlformelement.rs | 71 | ||||
-rw-r--r-- | components/script/dom/htmlimageelement.rs | 23 | ||||
-rw-r--r-- | components/script/dom/htmlmediaelement.rs | 22 | ||||
-rw-r--r-- | components/script/dom/request.rs | 41 | ||||
-rw-r--r-- | components/script/dom/response.rs | 12 | ||||
-rw-r--r-- | components/script/dom/servoparser/mod.rs | 31 | ||||
-rw-r--r-- | components/script/dom/xmlhttprequest.rs | 238 | ||||
-rw-r--r-- | components/script/lib.rs | 5 | ||||
-rw-r--r-- | components/script/script_thread.rs | 92 | ||||
-rw-r--r-- | components/script/stylesheet_loader.rs | 22 | ||||
-rw-r--r-- | components/script/webdriver_handlers.rs | 6 |
19 files changed, 380 insertions, 394 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 12cb7c6c667..614f9fcded1 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -31,13 +31,13 @@ tinyfiledialogs = "3.0" [dependencies] app_units = "0.7" backtrace = {version = "0.3", optional = true} -base64 = "0.6" +base64 = "0.9" bitflags = "1.0" bluetooth_traits = {path = "../bluetooth_traits"} byteorder = "1.0" canvas_traits = {path = "../canvas_traits"} caseless = "0.2" -cookie = "0.10" +cookie = "0.11" chrono = "0.4" cssparser = "0.24" deny_public_fields = {path = "../deny_public_fields"} @@ -51,9 +51,12 @@ euclid = "0.19" fnv = "1.0" gleam = "0.6" half = "1.0" +headers-core = "0.0.1" +headers-ext = "0.0.3" html5ever = "0.22" -hyper = "0.10" -hyper_serde = "0.8" +http = "0.1" +hyper = "0.12" +hyper_serde = "0.9" image = "0.19" ipc-channel = "0.11" itertools = "0.7.6" @@ -66,8 +69,8 @@ malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = { path = "../malloc_size_of_derive" } metrics = {path = "../metrics"} mitochondria = "1.1.2" -mime = "0.2.1" -mime_guess = "1.8.0" +mime = "0.3" +mime_guess = "2.0.0-alpha.6" mozjs = "0.9.3" msg = {path = "../msg"} net_traits = {path = "../net_traits"} diff --git a/components/script/body.rs b/components/script/body.rs index 9709043912b..cfc167d25b4 100644 --- a/components/script/body.rs +++ b/components/script/body.rs @@ -22,7 +22,7 @@ use js::jsval::UndefinedValue; use js::rust::wrappers::JS_GetPendingException; use js::rust::wrappers::JS_ParseJSON; use js::typedarray::{ArrayBuffer, CreateWith}; -use mime::{Mime, TopLevel, SubLevel}; +use mime::{self, Mime}; use std::cell::Ref; use std::ptr; use std::rc::Rc; @@ -175,23 +175,22 @@ fn run_form_data_algorithm( } else { "" }; - let mime: Mime = mime_str - .parse() - .map_err(|_| Error::Type("Inappropriate MIME-type for Body".to_string()))?; - match mime { - // TODO - // ... Parser for Mime(TopLevel::Multipart, SubLevel::FormData, _) - // ... is not fully determined yet. - Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _) => { - let entries = form_urlencoded::parse(&bytes); - let formdata = FormData::new(None, root); - for (k, e) in entries { - formdata.Append(USVString(k.into_owned()), USVString(e.into_owned())); - } - return Ok(FetchedData::FormData(formdata)); - }, - _ => return Err(Error::Type("Inappropriate MIME-type for Body".to_string())), + let mime: Mime = mime_str.parse().map_err( + |_| Error::Type("Inappropriate MIME-type for Body".to_string()))?; + + // TODO + // ... Parser for Mime(TopLevel::Multipart, SubLevel::FormData, _) + // ... is not fully determined yet. + if mime.type_() == mime::APPLICATION && mime.subtype() == mime::WWW_FORM_URLENCODED { + let entries = form_urlencoded::parse(&bytes); + let formdata = FormData::new(None, root); + for (k, e) in entries { + formdata.Append(USVString(k.into_owned()), USVString(e.into_owned())); + } + return Ok(FetchedData::FormData(formdata)); } + + Err(Error::Type("Inappropriate MIME-type for Body".to_string())) } #[allow(unsafe_code)] diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 8e720f28e25..75fcfb42c16 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -55,10 +55,9 @@ use euclid::Length as EuclidLength; use html5ever::{Prefix, LocalName, Namespace, QualName}; use html5ever::buffer_queue::BufferQueue; use html5ever::tendril::IncompleteUtf8; -use hyper::header::Headers; -use hyper::method::Method; -use hyper::mime::Mime; -use hyper::status::StatusCode; +use http::header::HeaderMap; +use hyper::Method; +use hyper::StatusCode; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use js::glue::{CallObjectTracer, CallValueTracer}; use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind}; @@ -67,6 +66,7 @@ use js::rust::{GCMethods, Handle, Runtime}; use js::typedarray::TypedArray; use js::typedarray::TypedArrayElement; use metrics::{InteractiveMetrics, InteractiveWindow}; +use mime::Mime; use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId}; use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; use net_traits::filemanager_thread::RelativePos; @@ -391,7 +391,7 @@ unsafe_no_jsmanaged_fields!(TimelineMarkerType); unsafe_no_jsmanaged_fields!(WorkerId); unsafe_no_jsmanaged_fields!(BufferQueue, QuirksMode, IncompleteUtf8); unsafe_no_jsmanaged_fields!(Runtime); -unsafe_no_jsmanaged_fields!(Headers, Method); +unsafe_no_jsmanaged_fields!(HeaderMap, Method); unsafe_no_jsmanaged_fields!(WindowProxyHandler); unsafe_no_jsmanaged_fields!(UntrustedNodeAddress); unsafe_no_jsmanaged_fields!(LengthOrPercentageOrAuto); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 9b52ea86b08..75527a90efc 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -95,14 +95,13 @@ use encoding_rs::{Encoding, UTF_8}; use euclid::Point2D; use fetch::FetchCanceller; use html5ever::{LocalName, Namespace, QualName}; -use hyper::header::{Header, SetCookie}; use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcSender}; use js::jsapi::{JSContext, JSObject, JSRuntime}; use js::jsapi::JS_GetRuntime; use keyboard_types::{Key, KeyState, Modifiers}; use metrics::{InteractiveFlag, InteractiveMetrics, InteractiveWindow, ProfilerMetadataFactory, ProgressiveWebMetric}; -use mime::{Mime, TopLevel, SubLevel}; +use mime::{self, Mime}; use msg::constellation_msg::BrowsingContextId; use net_traits::{FetchResponseMsg, IpcSend, ReferrerPolicy}; use net_traits::CookieSource::NonHTTP; @@ -2530,14 +2529,12 @@ impl Document { implementation: Default::default(), content_type: match content_type { Some(mime_data) => mime_data, - None => Mime::from(match is_html_document { + None => match is_html_document { // https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument - IsHTMLDocument::HTMLDocument => Mime(TopLevel::Text, SubLevel::Html, vec![]), + IsHTMLDocument::HTMLDocument => mime::TEXT_HTML, // https://dom.spec.whatwg.org/#concept-document-content-type - IsHTMLDocument::NonHTMLDocument => { - Mime(TopLevel::Application, SubLevel::Xml, vec![]) - }, - }), + IsHTMLDocument::NonHTMLDocument => "application/xml".parse().unwrap(), + }, }, last_modified: last_modified, url: DomRefCell::new(url), @@ -3345,8 +3342,10 @@ impl DocumentMethods for Document { local_name.make_ascii_lowercase(); } - let is_xhtml = self.content_type.0 == TopLevel::Application && - self.content_type.1.as_str() == "xhtml+xml"; + let is_xhtml = self.content_type.type_() == mime::APPLICATION && + self.content_type.subtype().as_str() == "xhtml" && + self.content_type.suffix() == Some(mime::XML); + let ns = if self.is_html_document || is_xhtml { ns!(html) } else { @@ -3947,18 +3946,16 @@ impl DocumentMethods for Document { return Err(Error::Security); } - if let Ok(cookie_header) = SetCookie::parse_header(&vec![cookie.to_string().into_bytes()]) { - let cookies = cookie_header - .0 - .into_iter() - .filter_map(|cookie| cookie_rs::Cookie::parse(cookie).ok().map(Serde)) - .collect(); - let _ = self - .window + let cookies = if let Some(cookie) = cookie_rs::Cookie::parse(cookie.to_string()).ok().map(Serde) { + vec![cookie] + } else { + vec![] + }; + + let _ = self.window .upcast::<GlobalScope>() .resource_threads() .send(SetCookiesForUrl(self.url(), cookies, NonHTTP)); - } Ok(()) } diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs index f20cc61f3df..8936cc8b391 100644 --- a/components/script/dom/domimplementation.rs +++ b/components/script/dom/domimplementation.rs @@ -24,7 +24,7 @@ use dom::node::Node; use dom::text::Text; use dom::xmldocument::XMLDocument; use dom_struct::dom_struct; -use mime::{Mime, TopLevel, SubLevel}; +use mime; use script_traits::DocumentActivity; // https://dom.spec.whatwg.org/#domimplementation @@ -82,17 +82,9 @@ impl DOMImplementationMethods for DOMImplementation { let namespace = namespace_from_domstring(maybe_namespace.to_owned()); let content_type = match namespace { - ns!(html) => Mime( - TopLevel::Application, - SubLevel::Ext("xhtml+xml".to_string()), - vec![], - ), - ns!(svg) => Mime( - TopLevel::Image, - SubLevel::Ext("svg+xml".to_string()), - vec![], - ), - _ => Mime(TopLevel::Application, SubLevel::Xml, vec![]), + ns!(html) => "application/xhtml+xml".parse().unwrap(), + ns!(svg) => mime::IMAGE_SVG, + _ => "application/xml".parse().unwrap(), }; // Step 1. diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs index 7aad98406a4..208d8aee8ed 100644 --- a/components/script/dom/eventsource.rs +++ b/components/script/dom/eventsource.rs @@ -17,13 +17,14 @@ use dom::messageevent::MessageEvent; use dom_struct::dom_struct; use euclid::Length; use fetch::FetchCanceller; -use hyper::header::{Accept, qitem}; +use headers_ext::ContentType; +use http::header::{self, HeaderName, HeaderValue}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use js::conversions::ToJSValConvertible; use js::jsapi::JSAutoCompartment; use js::jsval::UndefinedValue; -use mime::{Mime, TopLevel, SubLevel}; +use mime::{self, Mime}; use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata}; use net_traits::{FetchResponseMsg, FetchResponseListener, NetworkError}; use net_traits::request::{CacheMode, CorsSettings, CredentialsMode}; @@ -39,7 +40,6 @@ use task_source::{TaskSource, TaskSourceName}; use timers::OneshotTimerCallback; use utf8; -header! { (LastEventId, "Last-Event-ID") => [String] } const DEFAULT_RECONNECTION_TIME: u64 = 5000; @@ -338,13 +338,14 @@ impl FetchResponseListener for EventSourceContext { }; match meta.content_type { None => self.fail_the_connection(), - Some(ct) => match ct.into_inner().0 { - Mime(TopLevel::Text, SubLevel::EventStream, _) => { + Some(ct) => { + if <ContentType as Into<Mime>>::into(ct.into_inner()) == mime::TEXT_EVENT_STREAM { self.origin = meta.final_url.origin().ascii_serialization(); self.announce_the_connection(); - }, - _ => self.fail_the_connection(), - }, + } else { + self.fail_the_connection() + } + } } }, Err(_) => { @@ -501,9 +502,8 @@ impl EventSource { ..RequestInit::default() }; // Step 10 - request - .headers - .set(Accept(vec![qitem(mime!(Text / EventStream))])); + // TODO(eijebong): Replace once typed headers allow it + request.headers.insert(header::ACCEPT, HeaderValue::from_static("text/event-stream")); // Step 11 request.cache_mode = CacheMode::NoStore; // Step 12 @@ -613,9 +613,9 @@ impl EventSourceTimeoutCallback { let mut request = event_source.request(); // Step 5.3 if !event_source.last_event_id.borrow().is_empty() { - request.headers.set(LastEventId(String::from( - event_source.last_event_id.borrow().clone(), - ))); + //TODO(eijebong): Change this once typed header support custom values + request.headers.insert(HeaderName::from_static("last-event-id"), + HeaderValue::from_str(&String::from(event_source.last_event_id.borrow().clone())).unwrap()); } // Step 5.4 global diff --git a/components/script/dom/filereader.rs b/components/script/dom/filereader.rs index c816c943303..69ca537a75b 100644 --- a/components/script/dom/filereader.rs +++ b/components/script/dom/filereader.rs @@ -22,13 +22,13 @@ use dom::globalscope::GlobalScope; use dom::progressevent::ProgressEvent; use dom_struct::dom_struct; use encoding_rs::{Encoding, UTF_8}; -use hyper::mime::{Attr, Mime}; use js::jsapi::Heap; use js::jsapi::JSAutoCompartment; use js::jsapi::JSContext; use js::jsapi::JSObject; use js::jsval::{self, JSVal}; use js::typedarray::{ArrayBuffer, CreateWith}; +use mime::{self, Mime}; use servo_atoms::Atom; use std::cell::Cell; use std::ptr; @@ -115,11 +115,10 @@ impl FileReaderSharedFunctionality { // Step 4 & 5 encoding = encoding.or_else(|| { let resultmime = blob_type.parse::<Mime>().ok(); - resultmime.and_then(|Mime(_, _, ref parameters)| { - parameters - .iter() - .find(|&&(ref k, _)| &Attr::Charset == k) - .and_then(|&(_, ref v)| Encoding::for_label(v.as_str().as_bytes())) + resultmime.and_then(|mime| { + mime.params() + .find(|(ref k, _)| &mime::CHARSET == k) + .and_then(|(_, ref v)| Encoding::for_label(v.as_ref().as_bytes())) }) }); diff --git a/components/script/dom/headers.rs b/components/script/dom/headers.rs index 9ff08e7c07a..2cdb955b43f 100644 --- a/components/script/dom/headers.rs +++ b/components/script/dom/headers.rs @@ -11,11 +11,11 @@ use dom::bindings::root::DomRoot; use dom::bindings::str::{ByteString, is_token}; use dom::globalscope::GlobalScope; use dom_struct::dom_struct; -use hyper::header::Headers as HyperHeaders; -use mime::{Mime, TopLevel, SubLevel}; +use http::header::{self, HeaderMap as HyperHeaders, HeaderName, HeaderValue}; +use mime::{self, Mime}; use std::cell::Cell; use std::result::Result; -use std::str; +use std::str::{self, FromStr}; #[dom_struct] pub struct Headers { @@ -87,14 +87,14 @@ impl HeadersMethods for Headers { } // Step 7 let mut combined_value: Vec<u8> = vec![]; - if let Some(v) = self.header_list.borrow().get_raw(&valid_name) { - combined_value = v[0].clone(); + if let Some(v) = self.header_list.borrow().get(HeaderName::from_str(&valid_name).unwrap()) { + combined_value = v.as_bytes().to_vec(); combined_value.push(b','); } combined_value.extend(valid_value.iter().cloned()); self.header_list .borrow_mut() - .set_raw(valid_name, vec![combined_value]); + .insert(HeaderName::from_str(&valid_name).unwrap(), HeaderValue::from_bytes(&combined_value).unwrap()); Ok(()) } @@ -121,19 +121,17 @@ impl HeadersMethods for Headers { return Ok(()); } // Step 6 - self.header_list.borrow_mut().remove_raw(&valid_name); + self.header_list.borrow_mut().remove(&valid_name); Ok(()) } // https://fetch.spec.whatwg.org/#dom-headers-get fn Get(&self, name: ByteString) -> Fallible<Option<ByteString>> { // Step 1 - let valid_name = &validate_name(name)?; - Ok(self - .header_list - .borrow() - .get_raw(&valid_name) - .map(|v| ByteString::new(v[0].clone()))) + let valid_name = validate_name(name)?; + Ok(self.header_list.borrow().get(HeaderName::from_str(&valid_name).unwrap()).map(|v| { + ByteString::new(v.as_bytes().to_vec()) + })) } // https://fetch.spec.whatwg.org/#dom-headers-has @@ -141,7 +139,7 @@ impl HeadersMethods for Headers { // Step 1 let valid_name = validate_name(name)?; // Step 2 - Ok(self.header_list.borrow_mut().get_raw(&valid_name).is_some()) + Ok(self.header_list.borrow_mut().get(&valid_name).is_some()) } // https://fetch.spec.whatwg.org/#dom-headers-set @@ -173,7 +171,7 @@ impl HeadersMethods for Headers { // https://fetch.spec.whatwg.org/#concept-header-list-set self.header_list .borrow_mut() - .set_raw(valid_name, vec![valid_value]); + .insert(HeaderName::from_str(&valid_name).unwrap(), HeaderValue::from_bytes(&valid_value).unwrap()); Ok(()) } } @@ -184,10 +182,10 @@ impl Headers { match filler { // Step 1 Some(HeadersInit::Headers(h)) => { - for header in h.header_list.borrow().iter() { + for (name, value) in h.header_list.borrow().iter() { self.Append( - ByteString::new(Vec::from(header.name())), - ByteString::new(Vec::from(header.value_string().into_bytes())), + ByteString::new(Vec::from(name.as_str())), + ByteString::new(Vec::from(value.to_str().unwrap().as_bytes())) )?; } Ok(()) @@ -248,26 +246,21 @@ impl Headers { } pub fn get_headers_list(&self) -> HyperHeaders { - let mut headers = HyperHeaders::new(); - headers.extend(self.header_list.borrow_mut().iter()); - headers + self.header_list.borrow_mut().clone() } // https://fetch.spec.whatwg.org/#concept-header-extract-mime-type pub fn extract_mime_type(&self) -> Vec<u8> { - self.header_list - .borrow() - .get_raw("content-type") - .map_or(vec![], |v| v[0].clone()) + self.header_list.borrow().get(header::CONTENT_TYPE).map_or(vec![], |v| v.as_bytes().to_owned()) } pub fn sort_header_list(&self) -> Vec<(String, String)> { let borrowed_header_list = self.header_list.borrow(); let headers_iter = borrowed_header_list.iter(); let mut header_vec = vec![]; - for header in headers_iter { - let name = header.name().to_string(); - let value = header.value_string(); + for (name, value) in headers_iter { + let name = name.as_str().to_owned(); + let value = value.to_str().unwrap().to_owned(); let name_value = (name, value); header_vec.push(name_value); } @@ -306,12 +299,14 @@ fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool { let value_mime_result: Result<Mime, _> = value_string.parse(); match value_mime_result { Err(_) => false, - Ok(value_mime) => match value_mime { - Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _) | - Mime(TopLevel::Multipart, SubLevel::FormData, _) | - Mime(TopLevel::Text, SubLevel::Plain, _) => true, - _ => false, - }, + Ok(value_mime) => { + match (value_mime.type_(), value_mime.subtype()) { + (mime::APPLICATION, mime::WWW_FORM_URLENCODED) | + (mime::MULTIPART, mime::FORM_DATA) | + (mime::TEXT, mime::PLAIN) => true, + _ => false, + } + } } } diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 96f011bbb8e..c1c3e0e4446 100755 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -44,9 +44,12 @@ use dom::virtualmethods::VirtualMethods; use dom::window::Window; use dom_struct::dom_struct; use encoding_rs::{Encoding, UTF_8}; +use headers_core::HeaderMapExt; +use headers_ext::ContentType; use html5ever::{LocalName, Prefix}; -use hyper::header::{Charset, ContentDisposition, ContentType, DispositionParam, DispositionType}; -use hyper::method::Method; +use hyper::Method; +use mime::{self, Mime}; +use net_traits::http_percent_encode; use script_thread::MainThreadScriptMsg; use script_traits::LoadData; use servo_rand::random; @@ -379,23 +382,15 @@ impl HTMLFormElement { // https://html.spec.whatwg.org/multipage/#submit-dialog }, // https://html.spec.whatwg.org/multipage/#submit-mutate-action - ("http", FormMethod::FormGet) | - ("https", FormMethod::FormGet) | - ("data", FormMethod::FormGet) => { - load_data.headers.set(ContentType::form_url_encoded()); + ("http", FormMethod::FormGet) | ("https", FormMethod::FormGet) | ("data", FormMethod::FormGet) => { + load_data.headers.typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED)); self.mutate_action_url(&mut form_data, load_data, encoding, &target_window); }, // https://html.spec.whatwg.org/multipage/#submit-body ("http", FormMethod::FormPost) | ("https", FormMethod::FormPost) => { - load_data.method = Method::Post; - self.submit_entity_body( - &mut form_data, - load_data, - enctype, - encoding, - &target_window, - ); - }, + load_data.method = Method::POST; + self.submit_entity_body(&mut form_data, load_data, enctype, encoding, &target_window); + } // https://html.spec.whatwg.org/multipage/#submit-get-action ("file", _) | ("about", _) | @@ -450,7 +445,7 @@ impl HTMLFormElement { let bytes = match enctype { FormEncType::UrlEncoded => { let charset = encoding.name(); - load_data.headers.set(ContentType::form_url_encoded()); + load_data.headers.typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED)); self.set_encoding_override(load_data.url.as_mut_url().query_pairs_mut()) .clear() @@ -463,12 +458,12 @@ impl HTMLFormElement { load_data.url.query().unwrap_or("").to_string().into_bytes() }, FormEncType::FormDataEncoded => { - let mime = mime!(Multipart / FormData; Boundary =(&boundary)); - load_data.headers.set(ContentType(mime)); + let mime: Mime = format!("multipart/form-data; boundary={}", boundary).parse().unwrap(); + load_data.headers.typed_insert(ContentType::from(mime)); encode_multipart_form_data(form_data, boundary, encoding) }, FormEncType::TextPlainEncoded => { - load_data.headers.set(ContentType(mime!(Text / Plain))); + load_data.headers.typed_insert(ContentType::from(mime::TEXT_PLAIN)); self.encode_plaintext(form_data).into_bytes() }, }; @@ -1231,40 +1226,30 @@ pub fn encode_multipart_form_data( // what spec says (that it should start with a '\r\n'). let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes(); result.append(&mut boundary_bytes); - let mut content_disposition = ContentDisposition { - disposition: DispositionType::Ext("form-data".to_owned()), - parameters: vec![DispositionParam::Ext( - "name".to_owned(), - String::from(entry.name.clone()), - )], - }; + // TODO(eijebong): Everthing related to content-disposition it to redo once typed headers + // are capable of it. match entry.value { FormDatumValue::String(ref s) => { + let content_disposition = format!("form-data; name=\"{}\"", entry.name); let mut bytes = format!("Content-Disposition: {}\r\n\r\n{}", content_disposition, s) .into_bytes(); result.append(&mut bytes); }, FormDatumValue::File(ref f) => { - content_disposition - .parameters - .push(DispositionParam::Filename( - Charset::Ext(String::from(charset.clone())), - None, - f.name().clone().into(), - )); + let extra = if charset.to_lowercase() == "utf-8" { + format!("filename=\"{}\"", String::from_utf8(f.name().as_bytes().into()).unwrap()) + } else { + format!("filename*=\"{}\"''{}", charset, http_percent_encode(f.name().as_bytes())) + }; + + let content_disposition = format!("form-data; name=\"{}\"; {}", entry.name, extra); // https://tools.ietf.org/html/rfc7578#section-4.4 - let content_type = ContentType( - f.upcast::<Blob>() - .Type() - .parse() - .unwrap_or(mime!(Text / Plain)), - ); - let mut type_bytes = format!( - "Content-Disposition: {}\r\ncontent-type: {}\r\n\r\n", - content_disposition, content_type - ).into_bytes(); + let content_type: Mime = f.upcast::<Blob>().Type().parse().unwrap_or(mime::TEXT_PLAIN); + let mut type_bytes = format!("Content-Disposition: {}\r\ncontent-type: {}\r\n\r\n", + content_disposition, + content_type).into_bytes(); result.append(&mut type_bytes); let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]); diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index b31c5e886af..c9de1995230 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -44,7 +44,7 @@ use html5ever::{LocalName, Prefix}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use microtask::{Microtask, MicrotaskRunnable}; -use mime::{Mime, TopLevel, SubLevel}; +use mime::{self, Mime}; use net_traits::{FetchResponseListener, FetchMetadata, NetworkError, FetchResponseMsg}; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache::{CanRequestImages, ImageCache, ImageOrMetadataAvailable}; @@ -180,13 +180,9 @@ impl FetchResponseListener for ImageContext { // Step 14.5 of https://html.spec.whatwg.org/multipage/#img-environment-changes if let Some(metadata) = metadata.as_ref() { if let Some(ref content_type) = metadata.content_type { - match content_type.clone().into_inner().0 { - Mime(TopLevel::Multipart, SubLevel::Ext(s), _) => { - if s == "x-mixed-replace" { - self.aborted.set(true); - } - }, - _ => (), + let mime: Mime = content_type.clone().into_inner().into(); + if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" { + self.aborted.set(true); } } } @@ -570,11 +566,12 @@ impl HTMLImageElement { // TODO Handle unsupported mime type let mime = x.value().parse::<Mime>(); match mime { - Ok(m) => match m { - Mime(TopLevel::Image, _, _) => (), - _ => continue, - }, - _ => continue, + Ok(m) => + match m.type_() { + mime::IMAGE => (), + _ => continue + }, + _ => continue } } diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 04754712887..1cd0b5e825a 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -34,12 +34,14 @@ use dom::promise::Promise; use dom::virtualmethods::VirtualMethods; use dom_struct::dom_struct; use fetch::FetchCanceller; +use headers_core::HeaderMapExt; +use headers_ext::ContentLength; use html5ever::{LocalName, Prefix}; -use hyper::header::{ByteRangeSpec, ContentLength, Headers, Range as HyperRange}; +use http::header::{self, HeaderMap, HeaderValue}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use microtask::{Microtask, MicrotaskRunnable}; -use mime::{Mime, SubLevel, TopLevel}; +use mime::{self, Mime}; use net_traits::{CoreResourceMsg, FetchChannels, FetchResponseListener, FetchMetadata, Metadata}; use net_traits::NetworkError; use net_traits::request::{CredentialsMode, Destination, RequestInit}; @@ -686,10 +688,9 @@ impl HTMLMediaElement { HTMLMediaElementTypeId::HTMLAudioElement => Destination::Audio, HTMLMediaElementTypeId::HTMLVideoElement => Destination::Video, }; - let mut headers = Headers::new(); - headers.set(HyperRange::Bytes(vec![ByteRangeSpec::AllFrom( - offset.unwrap_or(0), - )])); + let mut headers = HeaderMap::new(); + // FIXME(eijebong): Use typed headers once we have a constructor for the range header + headers.insert(header::RANGE, HeaderValue::from_str(&format!("bytes={}-", offset.unwrap_or(0))).unwrap()); let request = RequestInit { url: self.resource_url.borrow().as_ref().unwrap().clone(), headers, @@ -1271,7 +1272,10 @@ impl HTMLMediaElementMethods for HTMLMediaElement { // https://html.spec.whatwg.org/multipage/#dom-navigator-canplaytype fn CanPlayType(&self, type_: DOMString) -> CanPlayTypeResult { match type_.parse::<Mime>() { - Ok(Mime(TopLevel::Application, SubLevel::OctetStream, _)) | Err(_) => { + Ok(ref mime) if (mime.type_() == mime::APPLICATION && mime.subtype() == mime::OCTET_STREAM) => { + CanPlayTypeResult::_empty + }, + Err(_) => { CanPlayTypeResult::_empty }, _ => CanPlayTypeResult::Maybe, @@ -1464,8 +1468,8 @@ impl FetchResponseListener for HTMLMediaElementContext { if let Some(metadata) = self.metadata.as_ref() { if let Some(headers) = metadata.headers.as_ref() { - if let Some(content_length) = headers.get::<ContentLength>() { - if let Err(e) = self.elem.root().player.set_input_size(**content_length) { + if let Some(content_length) = headers.typed_get::<ContentLength>() { + if let Err(e) = self.elem.root().player.set_input_size(content_length.0) { eprintln!("Could not set player input size {:?}", e); } } diff --git a/components/script/dom/request.rs b/components/script/dom/request.rs index 0415aba9203..ceb64ec45ce 100644 --- a/components/script/dom/request.rs +++ b/components/script/dom/request.rs @@ -25,7 +25,8 @@ use dom::headers::{Guard, Headers}; use dom::promise::Promise; use dom::xmlhttprequest::Extractable; use dom_struct::dom_struct; -use hyper::method::Method as HttpMethod; +use http::Method as HttpMethod; +use http::method::InvalidMethod; use net_traits::ReferrerPolicy as MsgReferrerPolicy; use net_traits::request::{Origin, Window}; use net_traits::request::CacheMode as NetTraitsRequestCache; @@ -38,6 +39,7 @@ use net_traits::request::RequestMode as NetTraitsRequestMode; use servo_url::ServoUrl; use std::cell::{Cell, Ref}; use std::rc::Rc; +use std::str::FromStr; #[dom_struct] pub struct Request { @@ -283,7 +285,7 @@ impl Request { } // Step 25.2 let method = match init_method.as_str() { - Some(s) => normalize_method(s), + Some(s) => normalize_method(s).map_err(|e| Error::Type(format!("Method is not valid: {:?}", e)))?, None => return Err(Error::Type("Method is not a valid UTF8".to_string())), }; // Step 25.3 @@ -373,16 +375,10 @@ impl Request { let req = r.request.borrow(); let req_method = &req.method; match *req_method { - HttpMethod::Get => { - return Err(Error::Type( - "Init's body is non-null, and request method is GET".to_string(), - )) - }, - HttpMethod::Head => { - return Err(Error::Type( - "Init's body is non-null, and request method is HEAD".to_string(), - )) - }, + HttpMethod::GET => return Err(Error::Type( + "Init's body is non-null, and request method is GET".to_string())), + HttpMethod::HEAD => return Err(Error::Type( + "Init's body is non-null, and request method is HEAD".to_string())), _ => {}, } } @@ -473,17 +469,18 @@ fn net_request_from_global(global: &GlobalScope, url: ServoUrl) -> NetTraitsRequ } // https://fetch.spec.whatwg.org/#concept-method-normalize -fn normalize_method(m: &str) -> HttpMethod { +fn normalize_method(m: &str) -> Result<HttpMethod, InvalidMethod> { match_ignore_ascii_case! { m, - "delete" => return HttpMethod::Delete, - "get" => return HttpMethod::Get, - "head" => return HttpMethod::Head, - "options" => return HttpMethod::Options, - "post" => return HttpMethod::Post, - "put" => return HttpMethod::Put, + "delete" => return Ok(HttpMethod::DELETE), + "get" => return Ok(HttpMethod::GET), + "head" => return Ok(HttpMethod::HEAD), + "options" => return Ok(HttpMethod::OPTIONS), + "post" => return Ok(HttpMethod::POST), + "put" => return Ok(HttpMethod::PUT), _ => (), } - HttpMethod::Extension(m.to_string()) + debug!("Method: {:?}", m); + HttpMethod::from_str(m) } // https://fetch.spec.whatwg.org/#concept-method @@ -503,7 +500,9 @@ fn is_forbidden_method(m: &ByteString) -> bool { // https://fetch.spec.whatwg.org/#cors-safelisted-method fn is_cors_safelisted_method(m: &HttpMethod) -> bool { - m == &HttpMethod::Get || m == &HttpMethod::Head || m == &HttpMethod::Post + m == &HttpMethod::GET || + m == &HttpMethod::HEAD || + m == &HttpMethod::POST } // https://url.spec.whatwg.org/#include-credentials diff --git a/components/script/dom/response.rs b/components/script/dom/response.rs index 613145a370a..828dfc56515 100644 --- a/components/script/dom/response.rs +++ b/components/script/dom/response.rs @@ -18,8 +18,8 @@ use dom::headers::{is_vchar, is_obs_text}; use dom::promise::Promise; use dom::xmlhttprequest::Extractable; use dom_struct::dom_struct; -use hyper::header::Headers as HyperHeaders; -use hyper::status::StatusCode; +use http::header::HeaderMap as HyperHeaders; +use hyper::StatusCode; use hyper_serde::Serde; use net_traits::response::{ResponseBody as NetTraitsResponseBody}; use servo_url::ServoUrl; @@ -55,7 +55,7 @@ impl Response { headers_reflector: Default::default(), mime_type: DomRefCell::new("".to_string().into_bytes()), body_used: Cell::new(false), - status: DomRefCell::new(Some(StatusCode::Ok)), + status: DomRefCell::new(Some(StatusCode::OK)), raw_status: DomRefCell::new(Some((200, b"OK".to_vec()))), response_type: DomRefCell::new(DOMResponseType::Default), url: DomRefCell::new(None), @@ -99,7 +99,7 @@ impl Response { let r = Response::new(global); // Step 4 - *r.status.borrow_mut() = Some(StatusCode::from_u16(init.status)); + *r.status.borrow_mut() = Some(StatusCode::from_u16(init.status).unwrap()); // Step 5 *r.raw_status.borrow_mut() = Some((init.status, init.statusText.clone().into())); @@ -189,7 +189,7 @@ impl Response { let r = Response::new(global); // Step 5 - *r.status.borrow_mut() = Some(StatusCode::from_u16(status)); + *r.status.borrow_mut() = Some(StatusCode::from_u16(status).unwrap()); *r.raw_status.borrow_mut() = Some((status, b"".to_vec())); // Step 6 @@ -300,7 +300,7 @@ impl ResponseMethods for Response { fn Ok(&self) -> bool { match *self.status.borrow() { Some(s) => { - let status_num = s.to_u16(); + let status_num = s.as_u16(); return status_num >= 200 && status_num <= 299; }, None => false, diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index e64580f68c1..cacd54d2f1a 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -35,9 +35,8 @@ use html5ever::{Attribute, ExpandedName, LocalName, QualName}; use html5ever::buffer_queue::BufferQueue; use html5ever::tendril::{StrTendril, ByteTendril, IncompleteUtf8}; use html5ever::tree_builder::{NodeOrText, TreeSink, NextParserState, QuirksMode, ElementFlags}; -use hyper::header::ContentType; -use hyper::mime::{Mime, SubLevel, TopLevel}; use hyper_serde::Serde; +use mime::{self, Mime}; use msg::constellation_msg::PipelineId; use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; use network_listener::PreInvoke; @@ -697,10 +696,11 @@ impl FetchResponseListener for ParserContext { }, Err(_) => None, }; - let content_type = metadata + let content_type: Option<Mime> = metadata .clone() .and_then(|meta| meta.content_type) - .map(Serde::into_inner); + .map(Serde::into_inner) + .map(Into::into); let parser = match ScriptThread::page_headers_available(&self.id, metadata) { Some(parser) => parser, None => return, @@ -712,7 +712,7 @@ impl FetchResponseListener for ParserContext { self.parser = Some(Trusted::new(&*parser)); match content_type { - Some(ContentType(Mime(TopLevel::Image, _, _))) => { + Some(ref mime) if mime.type_() == mime::IMAGE => { self.is_synthesized_document = true; let page = "<html><body></body></html>".into(); parser.push_string_input_chunk(page); @@ -725,14 +725,14 @@ impl FetchResponseListener for ParserContext { doc_body.AppendChild(&DomRoot::upcast::<Node>(img)).expect("Appending failed"); }, - Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { + Some(ref mime) if mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN => { // https://html.spec.whatwg.org/multipage/#read-text let page = "<pre>\n".into(); parser.push_string_input_chunk(page); parser.parse_sync(); parser.tokenizer.borrow_mut().set_plaintext_state(); }, - Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { + Some(ref mime) if mime.type_() == mime::TEXT && mime.subtype() == mime::HTML => { // Handle text/html if let Some(reason) = ssl_error { self.is_synthesized_document = true; @@ -749,15 +749,18 @@ impl FetchResponseListener for ParserContext { parser.parse_sync(); } }, - Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) | // Handle text/xml, application/xml - Some(ContentType(Mime(TopLevel::Application, SubLevel::Xml, _))) => {}, - Some(ContentType(Mime(TopLevel::Application, SubLevel::Ext(ref sub), _))) - if sub.as_str() == "xhtml+xml".to_owned() => {}, // Handle xhtml (application/xhtml+xml) - Some(ContentType(Mime(toplevel, sublevel, _))) => { + // Handle text/xml, application/xml + Some(ref mime) if (mime.type_() == mime::TEXT && mime.subtype() == mime::XML) || + (mime.type_() == mime::APPLICATION && mime.subtype() == mime::XML) => {}, + Some(ref mime) if mime.type_() == mime::APPLICATION && + mime.subtype().as_str() == "xhtml" && + mime.suffix() == Some(mime::XML) + => {}, // Handle xhtml (application/xhtml+xml) + Some(ref mime) => { // Show warning page for unknown mime types. let page = format!("<html><body><p>Unknown content type ({}/{}).</p></body></html>", - toplevel.as_str(), - sublevel.as_str()); + mime.type_().as_str(), + mime.subtype().as_str()); self.is_synthesized_document = true; parser.push_string_input_chunk(page); parser.parse_sync(); diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 38d206b3a1b..6ed6a7ffab4 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -39,12 +39,12 @@ use dom_struct::dom_struct; use encoding_rs::{Encoding, UTF_8}; use euclid::Length; use fetch::FetchCanceller; +use headers_core::HeaderMapExt; +use headers_ext::{ContentLength, ContentType}; use html5ever::serialize; use html5ever::serialize::SerializeOpts; -use hyper::header::{ContentLength, ContentType, ContentEncoding}; -use hyper::header::Headers; -use hyper::method::Method; -use hyper::mime::{self, Attr as MimeAttr, Mime, Value as MimeValue}; +use http::header::{self, HeaderMap, HeaderName, HeaderValue}; +use hyper::Method; use hyper_serde::Serde; use ipc_channel::ipc; use ipc_channel::router::ROUTER; @@ -53,6 +53,7 @@ use js::jsapi::JS_ClearPendingException; use js::jsval::{JSVal, NullValue, UndefinedValue}; use js::rust::wrappers::JS_ParseJSON; use js::typedarray::{ArrayBuffer, CreateWith}; +use mime::{self, Mime, Name}; use net_traits::{FetchChannels, FetchMetadata, FilteredMetadata}; use net_traits::{FetchResponseListener, NetworkError, ReferrerPolicy}; use net_traits::CoreResourceMsg::Fetch; @@ -68,7 +69,7 @@ use std::default::Default; use std::ptr; use std::ptr::NonNull; use std::slice; -use std::str; +use std::str::{self, FromStr}; use std::sync::{Arc, Mutex}; use task_source::TaskSourceName; use task_source::networking::NetworkingTaskSource; @@ -100,7 +101,7 @@ struct XHRContext { #[derive(Clone)] pub enum XHRProgress { /// Notify that headers have been received - HeadersReceived(GenerationId, Option<Headers>, Option<(u16, Vec<u8>)>), + HeadersReceived(GenerationId, Option<HeaderMap>, Option<(u16, Vec<u8>)>), /// Partial progress (after receiving headers), containing portion of the response Loading(GenerationId, ByteString), /// Loading is done @@ -138,7 +139,7 @@ pub struct XMLHttpRequest { #[ignore_malloc_size_of = "Defined in rust-mozjs"] response_json: Heap<JSVal>, #[ignore_malloc_size_of = "Defined in hyper"] - response_headers: DomRefCell<Headers>, + response_headers: DomRefCell<HeaderMap>, #[ignore_malloc_size_of = "Defined in hyper"] override_mime_type: DomRefCell<Option<Mime>>, override_charset: DomRefCell<Option<&'static Encoding>>, @@ -148,7 +149,7 @@ pub struct XMLHttpRequest { request_method: DomRefCell<Method>, request_url: DomRefCell<Option<ServoUrl>>, #[ignore_malloc_size_of = "Defined in hyper"] - request_headers: DomRefCell<Headers>, + request_headers: DomRefCell<HeaderMap>, request_body_len: Cell<usize>, sync: Cell<bool>, upload_complete: Cell<bool>, @@ -188,13 +189,13 @@ impl XMLHttpRequest { response_blob: Default::default(), response_arraybuffer: Heap::default(), response_json: Heap::default(), - response_headers: DomRefCell::new(Headers::new()), + response_headers: DomRefCell::new(HeaderMap::new()), override_mime_type: DomRefCell::new(None), override_charset: DomRefCell::new(None), - request_method: DomRefCell::new(Method::Get), + request_method: DomRefCell::new(Method::GET), request_url: DomRefCell::new(None), - request_headers: DomRefCell::new(Headers::new()), + request_headers: DomRefCell::new(HeaderMap::new()), request_body_len: Cell::new(0), sync: Cell::new(false), upload_complete: Cell::new(false), @@ -347,8 +348,8 @@ impl XMLHttpRequestMethods for XMLHttpRequest { match maybe_method { // Step 4 - Some(Method::Connect) | Some(Method::Trace) => Err(Error::Security), - Some(Method::Extension(ref t)) if &**t == "TRACK" => Err(Error::Security), + Some(Method::CONNECT) | Some(Method::TRACE) => Err(Error::Security), + Some(ref t) if t.as_str() == "TRACK" => Err(Error::Security), Some(parsed_method) => { // Step 3 if !is_token(&method) { @@ -395,7 +396,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest { *self.request_method.borrow_mut() = parsed_method; *self.request_url.borrow_mut() = Some(parsed_url); self.sync.set(!async); - *self.request_headers.borrow_mut() = Headers::new(); + *self.request_headers.borrow_mut() = HeaderMap::new(); self.send_flag.set(false); *self.status_text.borrow_mut() = ByteString::new(vec![]); self.status.set(0); @@ -450,19 +451,17 @@ impl XMLHttpRequestMethods for XMLHttpRequest { let mut headers = self.request_headers.borrow_mut(); // Step 6 - let value = match headers.get_raw(name_str) { + let value = match headers.get(name_str).map(HeaderValue::as_bytes) { Some(raw) => { - debug!("SetRequestHeader: old value = {:?}", raw[0]); - let mut buf = raw[0].clone(); + let mut buf = raw.to_vec(); buf.extend_from_slice(b", "); buf.extend_from_slice(value); - debug!("SetRequestHeader: new value = {:?}", buf); buf }, None => value.into(), }; - headers.set_raw(name_str.to_owned(), vec![value]); + headers.insert(HeaderName::from_str(name_str).unwrap(), HeaderValue::from_bytes(&value).unwrap()); Ok(()) } @@ -532,8 +531,8 @@ impl XMLHttpRequestMethods for XMLHttpRequest { // Step 3 let data = match *self.request_method.borrow() { - Method::Get | Method::Head => None, - _ => data, + Method::GET | Method::HEAD => None, + _ => data }; // Step 4 (first half) let extracted_or_serialized = match data { @@ -638,30 +637,46 @@ impl XMLHttpRequestMethods for XMLHttpRequest { // 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())) - }, + Some("UTF-8"), _ => None, }; 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()]); + if !request.headers.contains_key(header::CONTENT_TYPE) { + request.headers.insert(header::CONTENT_TYPE, HeaderValue::from_str(ct).unwrap()); content_type_set = true; } } if !content_type_set { - let ct = request.headers.get_mut::<ContentType>(); + let ct = request.headers.typed_get::<ContentType>(); if let Some(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()); + let mime: Mime = ct.into(); + for param in mime.params() { + if param.0 == mime::CHARSET { + if !param.1.as_ref().eq_ignore_ascii_case(encoding) { + let new_params: Vec<(Name, Name)> = + mime.params() + .filter(|p| p.0 != mime::CHARSET) + .map(|p| (p.0, p.1)) + .collect(); + + let new_mime = + format!("{}/{}; charset={}{}{}", + mime.type_().as_ref(), + mime.subtype().as_ref(), + encoding, + if new_params.is_empty() { "" } else { "; " }, + new_params + .iter() + .map(|p| format!("{}={}", p.0, p.1)) + .collect::<Vec<String>>() + .join("; ") + ); + let new_mime: Mime = new_mime.parse().unwrap(); + request.headers.typed_insert(ContentType::from(new_mime)) } } } @@ -672,8 +687,6 @@ impl XMLHttpRequestMethods for XMLHttpRequest { _ => (), } - debug!("request.headers = {:?}", request.headers); - self.fetch_time.set(time::now().to_timespec().sec); let rv = self.fetch(request, &self.global()); @@ -728,15 +741,48 @@ impl XMLHttpRequestMethods for XMLHttpRequest { // https://xhr.spec.whatwg.org/#the-getresponseheader()-method fn GetResponseHeader(&self, name: ByteString) -> Option<ByteString> { - self.filter_response_headers() - .iter() - .find(|h| name.eq_ignore_case(&h.name().parse().unwrap())) - .map(|h| ByteString::new(h.value_string().into_bytes())) + let headers = self.filter_response_headers(); + let headers = headers.get_all(HeaderName::from_str(&name.as_str()?.to_lowercase()).ok()?); + let mut first = true; + let s = headers.iter() + .fold(Vec::new(), |mut vec, value| { + if !first { + vec.extend(", ".as_bytes()); + } + first = false; + vec.extend(value.as_bytes()); + vec + }); + + // There was no header with that name so we never got to change that value + if first { + None + } else { + Some(ByteString::new(s)) + } } // https://xhr.spec.whatwg.org/#the-getallresponseheaders()-method fn GetAllResponseHeaders(&self) -> ByteString { - ByteString::new(self.filter_response_headers().to_string().into_bytes()) + let headers = self.filter_response_headers(); + let keys = headers.keys(); + let v = keys.fold(Vec::new(), |mut vec, k| { + let values = headers.get_all(k); + vec.extend(k.as_str().as_bytes()); + vec.extend(": ".as_bytes()); + let mut first = true; + for value in values { + if !first { + vec.extend(", ".as_bytes()); + first = false; + } + vec.extend(value.as_bytes()); + } + vec.extend("\r\n".as_bytes()); + vec + }); + + ByteString::new(v) } // https://xhr.spec.whatwg.org/#the-overridemimetype()-method @@ -751,12 +797,20 @@ impl XMLHttpRequestMethods for XMLHttpRequest { // Step 2 let override_mime = mime.parse::<Mime>().map_err(|_| Error::Syntax)?; // Step 3 - let mime_no_params = Mime(override_mime.clone().0, override_mime.clone().1, vec![]); + let mime_str = override_mime.as_ref(); + let mime_parts: Vec<&str> = mime_str.split(";").collect(); + let mime_no_params = if mime_parts.len() > 1 { + mime_parts[0].parse().unwrap() + } else { + override_mime.clone() + }; + *self.override_mime_type.borrow_mut() = Some(mime_no_params); // Step 4 - let value = override_mime.get_param(mime::Attr::Charset); - *self.override_charset.borrow_mut() = - value.and_then(|value| Encoding::for_label(value.as_bytes())); + let value = override_mime.get_param(mime::CHARSET); + *self.override_charset.borrow_mut() = value.and_then(|value| { + Encoding::for_label(value.as_ref().as_bytes()) + }); Ok(()) } @@ -1077,20 +1131,17 @@ impl XMLHttpRequest { fn dispatch_progress_event(&self, upload: bool, type_: Atom, loaded: u64, total: Option<u64>) { let (total_length, length_computable) = - if self.response_headers.borrow().has::<ContentEncoding>() { + if self.response_headers.borrow().contains_key(header::CONTENT_ENCODING) { (0, false) } else { (total.unwrap_or(0), total.is_some()) }; - let progressevent = ProgressEvent::new( - &self.global(), - type_, - EventBubbles::DoesNotBubble, - EventCancelable::NotCancelable, - length_computable, - loaded, - total_length, - ); + let progressevent = ProgressEvent::new(&self.global(), + type_, + EventBubbles::DoesNotBubble, + EventCancelable::NotCancelable, + length_computable, loaded, + total_length); let target = if upload { self.upload.upcast() } else { @@ -1108,13 +1159,10 @@ impl XMLHttpRequest { fn dispatch_response_progress_event(&self, type_: Atom) { let len = self.response.borrow().len() as u64; - let total = self - .response_headers - .borrow() - .get::<ContentLength>() - .map(|x| **x as u64); + let total = self.response_headers.borrow().typed_get::<ContentLength>().map(|v| v.0); self.dispatch_progress_event(false, type_, len, total); } + fn set_timeout(&self, duration_ms: u32) { // Sets up the object to timeout in a given number of milliseconds // This will cancel all previous timeouts @@ -1154,11 +1202,7 @@ impl XMLHttpRequest { return response; } // Step 2 - let mime = self - .final_mime_type() - .as_ref() - .map(Mime::to_string) - .unwrap_or("".to_owned()); + let mime = self.final_mime_type().as_ref().map(|m| m.to_string()).unwrap_or("".to_owned()); // Step 3, 4 let bytes = self.response.borrow().to_vec(); @@ -1200,7 +1244,7 @@ impl XMLHttpRequest { let charset = self.final_charset().unwrap_or(UTF_8); let temp_doc: DomRoot<Document>; match mime_type { - Some(Mime(mime::TopLevel::Text, mime::SubLevel::Html, _)) => { + Some(ref mime) if mime.type_() == mime::TEXT && mime.subtype() == mime::HTML => { // Step 5 if self.response_type.get() == XMLHttpRequestResponseType::_empty { return None; @@ -1210,17 +1254,15 @@ impl XMLHttpRequest { } }, // Step 7 - Some(Mime(mime::TopLevel::Text, mime::SubLevel::Xml, _)) | - Some(Mime(mime::TopLevel::Application, mime::SubLevel::Xml, _)) | + Some(ref mime) if (mime.type_() == mime::TEXT && mime.subtype() == mime::XML) || + (mime.type_() == mime::APPLICATION && mime.subtype() == mime::XML) => { + temp_doc = self.handle_xml(); + }, None => { temp_doc = self.handle_xml(); }, - Some(Mime(_, mime::SubLevel::Ext(sub), _)) => { - if sub.ends_with("+xml") { - temp_doc = self.handle_xml(); - } else { - return None; - } + Some(ref mime) if mime.suffix() == Some(mime::XML) => { + temp_doc = self.handle_xml(); }, // Step 4 _ => { @@ -1336,34 +1378,13 @@ impl XMLHttpRequest { ) } - fn filter_response_headers(&self) -> Headers { + fn filter_response_headers(&self) -> HeaderMap { // https://fetch.spec.whatwg.org/#concept-response-header-list - use hyper::error::Result; - use hyper::header::{Header, HeaderFormat}; - use hyper::header::SetCookie; - use std::fmt; - - // a dummy header so we can use headers.remove::<SetCookie2>() - #[derive(Clone, Debug, MallocSizeOf)] - struct SetCookie2; - impl Header for SetCookie2 { - fn header_name() -> &'static str { - "set-cookie2" - } - - fn parse_header(_: &[Vec<u8>]) -> Result<SetCookie2> { - unimplemented!() - } - } - impl HeaderFormat for SetCookie2 { - fn fmt_header(&self, _f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() - } - } + use http::header::{self, HeaderName}; let mut headers = self.response_headers.borrow().clone(); - headers.remove::<SetCookie>(); - headers.remove::<SetCookie2>(); + headers.remove(header::SET_COOKIE); + headers.remove(HeaderName::from_static("set-cookie2")); // XXXManishearth additional CORS filtering goes here headers } @@ -1416,12 +1437,15 @@ impl XMLHttpRequest { if self.override_charset.borrow().is_some() { self.override_charset.borrow().clone() } else { - match self.response_headers.borrow().get() { - Some(&ContentType(ref mime)) => { - let value = mime.get_param(mime::Attr::Charset); - value.and_then(|value| Encoding::for_label(value.as_bytes())) - }, - None => None, + match self.response_headers.borrow().typed_get::<ContentType>() { + Some(ct) => { + let mime: Mime = ct.into(); + let value = mime.get_param(mime::CHARSET); + value.and_then(|value|{ + Encoding::for_label(value.as_ref().as_bytes()) + }) + } + None => { None } } } } @@ -1430,9 +1454,9 @@ impl XMLHttpRequest { if self.override_mime_type.borrow().is_some() { self.override_mime_type.borrow().clone() } else { - match self.response_headers.borrow().get() { - Some(&ContentType(ref mime)) => Some(mime.clone()), - None => None, + match self.response_headers.borrow().typed_get::<ContentType>() { + Some(ct) => { Some(ct.into()) }, + None => { None } } } } diff --git a/components/script/lib.rs b/components/script/lib.rs index a402bec7cf3..ef0205b68b6 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -45,9 +45,11 @@ extern crate euclid; extern crate fnv; extern crate gleam; extern crate half; +extern crate headers_core; +extern crate headers_ext; #[macro_use] extern crate html5ever; -#[macro_use] +extern crate http; extern crate hyper; extern crate hyper_serde; extern crate image; @@ -65,7 +67,6 @@ extern crate malloc_size_of; #[macro_use] extern crate malloc_size_of_derive; extern crate metrics; -#[macro_use] extern crate mime; extern crate mime_guess; extern crate mitochondria; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 8795a65cd13..e1d9fd4c0e4 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -65,9 +65,9 @@ use dom::workletglobalscope::WorkletGlobalScopeInit; use embedder_traits::EmbedderMsg; use euclid::{Point2D, Vector2D, Rect}; use fetch::FetchCanceller; -use hyper::header::{ContentType, HttpDate, Headers, LastModified}; -use hyper::header::ReferrerPolicy as ReferrerPolicyHeader; -use hyper::mime::{Mime, SubLevel, TopLevel}; +use headers_core::HeaderMapExt; +use headers_ext::LastModified; +use headers_ext::ReferrerPolicy as ReferrerPolicyHeader; use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcSender}; use js::glue::GetWindowProxyClass; @@ -76,6 +76,7 @@ use js::jsapi::{JSTracer, SetWindowProxyClass}; use js::jsval::UndefinedValue; use metrics::{MAX_TASK_NS, PaintTimeMetrics}; use microtask::{MicrotaskQueue, Microtask}; +use mime::{self, Mime}; use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId}; use msg::constellation_msg::{PipelineNamespace, TopLevelBrowsingContextId}; use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg}; @@ -115,6 +116,7 @@ use std::rc::Rc; use std::result::Result; use std::sync::Arc; use std::thread; +use std::time::SystemTime; use style::thread_state::{self, ThreadState}; use task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; use task_source::TaskSourceName; @@ -127,7 +129,7 @@ use task_source::performance_timeline::PerformanceTimelineTaskSource; use task_source::remote_event::RemoteEventTaskSource; use task_source::user_interaction::UserInteractionTaskSource; use task_source::websocket::WebsocketTaskSource; -use time::{get_time, precise_time_ns, Tm}; +use time::{at_utc, get_time, precise_time_ns, Timespec}; use url::Position; use url::percent_encoding::percent_decode; use webdriver_handlers; @@ -2611,37 +2613,27 @@ impl ScriptThread { window.init_window_proxy(&window_proxy); let last_modified = metadata.headers.as_ref().and_then(|headers| { - headers - .get() - .map(|&LastModified(HttpDate(ref tm))| dom_last_modified(tm)) + headers.typed_get::<LastModified>() + .map(|tm| dom_last_modified(&tm.into())) }); - let content_type = metadata - .content_type - .as_ref() - .map(|&Serde(ContentType(ref mimetype))| mimetype.clone()); - let loader = DocumentLoader::new_with_threads( self.resource_threads.clone(), Some(final_url.clone()), ); - let is_html_document = match metadata.content_type { - Some(Serde(ContentType(Mime( - TopLevel::Application, - SubLevel::Ext(ref sub_level), - _, - )))) - if sub_level.ends_with("+xml") => - { - IsHTMLDocument::NonHTMLDocument - }, + let content_type: Option<Mime> = metadata.content_type + .map(Serde::into_inner) + .map(Into::into); - Some(Serde(ContentType(Mime(TopLevel::Application, SubLevel::Xml, _)))) | - Some(Serde(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _)))) => { - IsHTMLDocument::NonHTMLDocument - }, + let is_html_document = match content_type { + Some(ref mime) if mime.type_() == mime::APPLICATION && + mime.suffix() == Some(mime::XML) => IsHTMLDocument::NonHTMLDocument, + Some(ref mime) if + (mime.type_() == mime::TEXT && mime.subtype() == mime::XML) || + (mime.type_() == mime::APPLICATION && mime.subtype() == mime::XML) + => IsHTMLDocument::NonHTMLDocument, _ => IsHTMLDocument::HTMLDocument, }; @@ -2650,28 +2642,25 @@ impl ScriptThread { None => None, }; - let referrer_policy = metadata - .headers - .as_ref() - .map(Serde::deref) - .and_then(Headers::get::<ReferrerPolicyHeader>) - .map(ReferrerPolicy::from); - - let document = Document::new( - &window, - HasBrowsingContext::Yes, - Some(final_url.clone()), - incomplete.origin, - is_html_document, - content_type, - last_modified, - incomplete.activity, - DocumentSource::FromParser, - loader, - referrer, - referrer_policy, - incomplete.canceller, - ); + let referrer_policy = metadata.headers + .as_ref() + .map(Serde::deref) + .and_then(|h| h.typed_get::<ReferrerPolicyHeader>()) + .map(ReferrerPolicy::from); + + let document = Document::new(&window, + HasBrowsingContext::Yes, + Some(final_url.clone()), + incomplete.origin, + is_html_document, + content_type, + last_modified, + incomplete.activity, + DocumentSource::FromParser, + loader, + referrer, + referrer_policy, + incomplete.canceller); document.set_ready_state(DocumentReadyState::Loading); self.documents @@ -3112,7 +3101,7 @@ impl ScriptThread { let mut context = ParserContext::new(id, url.clone()); let mut meta = Metadata::default(url); - meta.set_content_type(Some(&mime!(Text / Html))); + meta.set_content_type(Some(&mime::TEXT_HTML)); // If this page load is the result of a javascript scheme url, map // the evaluation result into a response. @@ -3221,7 +3210,10 @@ impl Drop for ScriptThread { } } -fn dom_last_modified(tm: &Tm) -> String { +fn dom_last_modified(tm: &SystemTime) -> String { + let tm = tm.duration_since(SystemTime::UNIX_EPOCH).unwrap(); + let tm = Timespec::new(tm.as_secs() as i64, 0); + let tm = at_utc(tm); tm.to_local() .strftime("%m/%d/%Y %H:%M:%S") .unwrap() diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index 8ae75a6e35b..6562dcf7c2a 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -14,11 +14,9 @@ use dom::htmlelement::HTMLElement; use dom::htmllinkelement::{RequestGenerationId, HTMLLinkElement}; use dom::node::{document_from_node, window_from_node}; use encoding_rs::UTF_8; -use hyper::header::ContentType; -use hyper::mime::{Mime, TopLevel, SubLevel}; -use hyper_serde::Serde; use ipc_channel::ipc; use ipc_channel::router::ROUTER; +use mime::{self, Mime}; use net_traits::{FetchResponseListener, FetchMetadata, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy}; use net_traits::request::{CorsSettings, CredentialsMode, Destination, RequestInit, RequestMode}; use network_listener::{NetworkListener, PreInvoke}; @@ -117,18 +115,12 @@ impl FetchResponseListener for StylesheetContext { Some(meta) => meta, None => return, }; - let is_css = - metadata - .content_type - .map_or(false, |Serde(ContentType(Mime(top, sub, _)))| { - top == TopLevel::Text && sub == SubLevel::Css - }); - - let data = if is_css { - mem::replace(&mut self.data, vec![]) - } else { - vec![] - }; + let is_css = metadata.content_type.map_or(false, |ct| { + let mime: Mime = ct.into_inner().into(); + mime.type_() == mime::TEXT && mime.subtype() == mime::CSS + }); + + let data = if is_css { mem::replace(&mut self.data, vec![]) } else { vec![] }; // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding let environment_encoding = UTF_8; diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 6c3e7deb910..3ac4362f8bd 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -299,7 +299,11 @@ pub fn handle_add_cookie( }, }; let url = document.url(); - let method = if cookie.http_only() { HTTP } else { NonHTTP }; + let method = if cookie.http_only().unwrap_or(false) { + HTTP + } else { + NonHTTP + }; let domain = cookie.domain().map(ToOwned::to_owned); reply |