diff options
author | Jeena Lee <ijeenalee@gmail.com> | 2016-07-19 18:38:02 -0700 |
---|---|---|
committer | Jeena Lee <ijeenalee@gmail.com> | 2016-08-12 15:39:40 -0700 |
commit | fabe2b8f7e5693d705427959a07a380d52c16e26 (patch) | |
tree | d15718e7f30f6182be6774c470e394b6bdc1cf18 /components/script | |
parent | b7facf41cbc7ba727666e95fd0c390d432d862fa (diff) | |
download | servo-fabe2b8f7e5693d705427959a07a380d52c16e26.tar.gz servo-fabe2b8f7e5693d705427959a07a380d52c16e26.zip |
Implement the Request API for the Fetch API.
This commit adds new files related to implementing the [Request
API](https://fetch.spec.whatwg.org/#request-class). This commit also
changes the expected web platform tests results. It also modifies the
following files:
components/net_traits/request.rs
HeapSizeOf is implemented in net_traits/request so that dom::request can
be used as a wrapper around net_traits::request::Request.
components/script/dom/headers.rs
Several methods are added to Headers so that request can access and
modify some of the headers fields.
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/bindings/trace.rs | 2 | ||||
-rw-r--r-- | components/script/dom/headers.rs | 135 | ||||
-rw-r--r-- | components/script/dom/mod.rs | 1 | ||||
-rw-r--r-- | components/script/dom/request.rs | 820 | ||||
-rw-r--r-- | components/script/dom/webidls/Body.webidl | 19 | ||||
-rw-r--r-- | components/script/dom/webidls/Request.webidl | 108 |
6 files changed, 1033 insertions, 52 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 618061fe769..89b3ab88e11 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -60,6 +60,7 @@ use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeType, R use net_traits::filemanager_thread::{SelectedFileId, RelativePos}; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; +use net_traits::request::Request; use net_traits::response::HttpsState; use net_traits::storage_thread::StorageType; use net_traits::{Metadata, NetworkError, ResourceThreads}; @@ -325,6 +326,7 @@ no_jsmanaged_fields!(AttrIdentifier); no_jsmanaged_fields!(AttrValue); no_jsmanaged_fields!(ElementSnapshot); no_jsmanaged_fields!(HttpsState); +no_jsmanaged_fields!(Request); no_jsmanaged_fields!(SharedRt); no_jsmanaged_fields!(TouchpadPressurePhase); no_jsmanaged_fields!(USVString); diff --git a/components/script/dom/headers.rs b/components/script/dom/headers.rs index 4ae212aee78..3329952e0a9 100644 --- a/components/script/dom/headers.rs +++ b/components/script/dom/headers.rs @@ -12,18 +12,19 @@ use dom::bindings::js::Root; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::{ByteString, is_token}; use hyper::header::Headers as HyperHeaders; +use std::cell::Cell; use std::result::Result; #[dom_struct] pub struct Headers { reflector_: Reflector, - guard: Guard, + guard: Cell<Guard>, #[ignore_heap_size_of = "Defined in hyper"] header_list: DOMRefCell<HyperHeaders> } // https://fetch.spec.whatwg.org/#concept-headers-guard -#[derive(JSTraceable, HeapSizeOf, PartialEq)] +#[derive(Copy, Clone, JSTraceable, HeapSizeOf, PartialEq)] pub enum Guard { Immutable, Request, @@ -36,76 +37,46 @@ impl Headers { pub fn new_inherited() -> Headers { Headers { reflector_: Reflector::new(), - guard: Guard::None, + guard: Cell::new(Guard::None), header_list: DOMRefCell::new(HyperHeaders::new()), } } - // https://fetch.spec.whatwg.org/#concept-headers-fill - pub fn new(global: GlobalRef, init: Option<HeadersBinding::HeadersInit>) - -> Fallible<Root<Headers>> { - let dom_headers_new = reflect_dom_object(box Headers::new_inherited(), global, HeadersBinding::Wrap); - match init { - // Step 1 - Some(HeadersOrByteStringSequenceSequence::Headers(h)) => { - // header_list_copy has type hyper::header::Headers - let header_list_copy = h.header_list.clone(); - for header in header_list_copy.borrow().iter() { - try!(dom_headers_new.Append( - ByteString::new(Vec::from(header.name())), - ByteString::new(Vec::from(header.value_string().into_bytes())) - )); - } - Ok(dom_headers_new) - }, - // Step 2 - Some(HeadersOrByteStringSequenceSequence::ByteStringSequenceSequence(v)) => { - for mut seq in v { - if seq.len() == 2 { - let val = seq.pop().unwrap(); - let name = seq.pop().unwrap(); - try!(dom_headers_new.Append(name, val)); - } else { - return Err(Error::Type( - format!("Each header object must be a sequence of length 2 - found one with length {}", - seq.len()))); - } - } - Ok(dom_headers_new) - }, - // Step 3 TODO constructor for when init is an open-ended dictionary - None => Ok(dom_headers_new), - } + pub fn new(global: GlobalRef) -> Root<Headers> { + reflect_dom_object(box Headers::new_inherited(), global, HeadersBinding::Wrap) } + // https://fetch.spec.whatwg.org/#dom-headers pub fn Constructor(global: GlobalRef, init: Option<HeadersBinding::HeadersInit>) -> Fallible<Root<Headers>> { - Headers::new(global, init) + let dom_headers_new = Headers::new(global); + try!(dom_headers_new.fill(init)); + Ok(dom_headers_new) } } impl HeadersMethods for Headers { // https://fetch.spec.whatwg.org/#concept-headers-append - fn Append(&self, name: ByteString, value: ByteString) -> Result<(), Error> { + fn Append(&self, name: ByteString, value: ByteString) -> ErrorResult { // Step 1 let value = normalize_value(value); // Step 2 let (mut valid_name, valid_value) = try!(validate_name_and_value(name, value)); valid_name = valid_name.to_lowercase(); // Step 3 - if self.guard == Guard::Immutable { + if self.guard.get() == Guard::Immutable { return Err(Error::Type("Guard is immutable".to_string())); } // Step 4 - if self.guard == Guard::Request && is_forbidden_header_name(&valid_name) { + if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) { return Ok(()); } // Step 5 - if self.guard == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) { + if self.guard.get() == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) { return Ok(()); } // Step 6 - if self.guard == Guard::Response && is_forbidden_response_header(&valid_name) { + if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) { return Ok(()); } // Step 7 @@ -121,19 +92,19 @@ impl HeadersMethods for Headers { // Step 1 let valid_name = try!(validate_name(name)); // Step 2 - if self.guard == Guard::Immutable { + if self.guard.get() == Guard::Immutable { return Err(Error::Type("Guard is immutable".to_string())); } // Step 3 - if self.guard == Guard::Request && is_forbidden_header_name(&valid_name) { + if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) { return Ok(()); } // Step 4 - if self.guard == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) { + if self.guard.get() == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) { return Ok(()); } // Step 5 - if self.guard == Guard::Response && is_forbidden_response_header(&valid_name) { + if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) { return Ok(()); } // Step 6 @@ -166,19 +137,19 @@ impl HeadersMethods for Headers { let (mut valid_name, valid_value) = try!(validate_name_and_value(name, value)); valid_name = valid_name.to_lowercase(); // Step 3 - if self.guard == Guard::Immutable { + if self.guard.get() == Guard::Immutable { return Err(Error::Type("Guard is immutable".to_string())); } // Step 4 - if self.guard == Guard::Request && is_forbidden_header_name(&valid_name) { + if self.guard.get() == Guard::Request && is_forbidden_header_name(&valid_name) { return Ok(()); } // Step 5 - if self.guard == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) { + if self.guard.get() == Guard::RequestNoCors && !is_cors_safelisted_request_header(&valid_name) { return Ok(()); } // Step 6 - if self.guard == Guard::Response && is_forbidden_response_header(&valid_name) { + if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) { return Ok(()); } // Step 7 @@ -188,6 +159,66 @@ impl HeadersMethods for Headers { } } +impl Headers { + // https://fetch.spec.whatwg.org/#concept-headers-fill + pub fn fill(&self, filler: Option<HeadersBinding::HeadersInit>) -> ErrorResult { + match filler { + // Step 1 + Some(HeadersOrByteStringSequenceSequence::Headers(h)) => { + // header_list_copy has type hyper::header::Headers + let header_list_copy = h.header_list.clone(); + for header in h.header_list.borrow().iter() { + try!(self.Append( + ByteString::new(Vec::from(header.name())), + ByteString::new(Vec::from(header.value_string().into_bytes())) + )); + } + Ok(()) + }, + // Step 2 + Some(HeadersOrByteStringSequenceSequence::ByteStringSequenceSequence(v)) => { + for mut seq in v { + if seq.len() == 2 { + let val = seq.pop().unwrap(); + let name = seq.pop().unwrap(); + try!(self.Append(name, val)); + } else { + return Err(Error::Type( + format!("Each header object must be a sequence of length 2 - found one with length {}", + seq.len()))); + } + } + Ok(()) + }, + // Step 3 TODO constructor for when init is an open-ended dictionary + None => Ok(()), + } + } + + pub fn for_request(global: GlobalRef) -> Root<Headers> { + let headers_for_request = Headers::new(global); + headers_for_request.guard.set(Guard::Request); + headers_for_request + } + + pub fn set_guard(&self, new_guard: Guard) { + self.guard.set(new_guard) + } + + pub fn get_guard(&self) -> Guard { + self.guard.get() + } + + pub fn empty_header_list(&self) { + *self.header_list.borrow_mut() = HyperHeaders::new(); + } + + // 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()) + } +} + // TODO // "Content-Type" once parsed, the value should be // `application/x-www-form-urlencoded`, `multipart/form-data`, diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index a2112526228..c2121c84139 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -368,6 +368,7 @@ pub mod processinginstruction; pub mod progressevent; pub mod radionodelist; pub mod range; +pub mod request; pub mod screen; pub mod serviceworker; pub mod serviceworkercontainer; diff --git a/components/script/dom/request.rs b/components/script/dom/request.rs new file mode 100644 index 00000000000..a764aec6265 --- /dev/null +++ b/components/script/dom/request.rs @@ -0,0 +1,820 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::RequestBinding; +use dom::bindings::codegen::Bindings::RequestBinding::ReferrerPolicy; +use dom::bindings::codegen::Bindings::RequestBinding::RequestCache; +use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials; +use dom::bindings::codegen::Bindings::RequestBinding::RequestDestination; +use dom::bindings::codegen::Bindings::RequestBinding::RequestInfo; +use dom::bindings::codegen::Bindings::RequestBinding::RequestInit; +use dom::bindings::codegen::Bindings::RequestBinding::RequestMethods; +use dom::bindings::codegen::Bindings::RequestBinding::RequestMode; +use dom::bindings::codegen::Bindings::RequestBinding::RequestRedirect; +use dom::bindings::codegen::Bindings::RequestBinding::RequestType; +use dom::bindings::codegen::UnionTypes::HeadersOrByteStringSequenceSequence; +use dom::bindings::error::{Error, Fallible}; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::{JS, MutNullableHeap, Root}; +use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::bindings::str::{ByteString, USVString, DOMString}; +use dom::headers::{Headers, Guard}; +use hyper; +use msg::constellation_msg::{ReferrerPolicy as MsgReferrerPolicy}; +use net_traits::request::CacheMode as NetTraitsRequestCache; +use net_traits::request::CredentialsMode as NetTraitsRequestCredentials; +use net_traits::request::Destination as NetTraitsRequestDestination; +use net_traits::request::RedirectMode as NetTraitsRequestRedirect; +use net_traits::request::Referer as NetTraitsRequestReferer; +use net_traits::request::Request as NetTraitsRequest; +use net_traits::request::RequestMode as NetTraitsRequestMode; +use net_traits::request::Type as NetTraitsRequestType; +use net_traits::request::{Origin, Window}; +use std::cell::{Cell, Ref}; +use url::Url; + +#[dom_struct] +pub struct Request { + reflector_: Reflector, + request: DOMRefCell<NetTraitsRequest>, + body_used: Cell<bool>, + headers: MutNullableHeap<JS<Headers>>, + mime_type: DOMRefCell<Vec<u8>>, +} + +impl Request { + fn new_inherited(global: GlobalRef, + url: Url, + is_service_worker_global_scope: bool) -> Request { + Request { + reflector_: Reflector::new(), + request: DOMRefCell::new( + net_request_from_global(global, + url, + is_service_worker_global_scope)), + body_used: Cell::new(false), + headers: Default::default(), + mime_type: DOMRefCell::new("".to_string().into_bytes()), + } + } + + pub fn new(global: GlobalRef, + url: Url, + is_service_worker_global_scope: bool) -> Root<Request> { + reflect_dom_object(box Request::new_inherited(global, + url, + is_service_worker_global_scope), + global, RequestBinding::Wrap) + } + + // https://fetch.spec.whatwg.org/#dom-request + pub fn Constructor(global: GlobalRef, + input: RequestInfo, + init: &RequestInit) + -> Fallible<Root<Request>> { + // Step 1 + let temporary_request: NetTraitsRequest; + + // Step 2 + let mut fallback_mode: Option<NetTraitsRequestMode> = None; + + // Step 3 + let mut fallback_credentials: Option<NetTraitsRequestCredentials> = None; + + // Step 4 + // TODO: `entry settings object` is not implemented in Servo yet. + let base_url = global.get_url(); + + match input { + // Step 5 + RequestInfo::USVString(USVString(ref usv_string)) => { + // Step 5.1 + let parsed_url = base_url.join(&usv_string); + // Step 5.2 + if parsed_url.is_err() { + return Err(Error::Type("Url could not be parsed".to_string())) + } + // Step 5.3 + let url = parsed_url.unwrap(); + if includes_credentials(&url) { + return Err(Error::Type("Url includes credentials".to_string())) + } + // Step 5.4 + temporary_request = net_request_from_global(global, + url, + false); + // Step 5.5 + fallback_mode = Some(NetTraitsRequestMode::CORSMode); + // Step 5.6 + fallback_credentials = Some(NetTraitsRequestCredentials::Omit); + } + // Step 6 + RequestInfo::Request(ref input_request) => { + // Step 6.1 + if request_is_disturbed(input_request) || request_is_locked(input_request) { + return Err(Error::Type("Input is disturbed or locked".to_string())) + } + // Step 6.2 + temporary_request = input_request.request.borrow().clone(); + } + } + + // Step 7 + // TODO: `entry settings object` is not implemented yet. + let origin = global.get_url().origin(); + + // Step 8 + let mut window = Window::Client; + + // Step 9 + // TODO: `environment settings object` is not implemented in Servo yet. + + // Step 10 + if !init.window.is_undefined() && !init.window.is_null() { + return Err(Error::Type("Window is present and is not null".to_string())) + } + + // Step 11 + if !init.window.is_undefined() { + window = Window::NoWindow; + } + + // Step 12 + let mut request: NetTraitsRequest; + request = net_request_from_global(global, + get_current_url(&temporary_request).unwrap().clone(), + false); + request.method = temporary_request.method; + request.headers = temporary_request.headers.clone(); + request.unsafe_request = true; + request.window.set(window); + // TODO: `entry settings object` is not implemented in Servo yet. + *request.origin.borrow_mut() = Origin::Client; + request.omit_origin_header = temporary_request.omit_origin_header; + request.same_origin_data.set(true); + request.referer = temporary_request.referer; + request.referrer_policy = temporary_request.referrer_policy; + request.mode = temporary_request.mode; + request.credentials_mode = temporary_request.credentials_mode; + request.cache_mode = temporary_request.cache_mode; + request.redirect_mode = temporary_request.redirect_mode; + request.integrity_metadata = temporary_request.integrity_metadata; + + // Step 13 + if init.body.is_some() || + init.cache.is_some() || + init.credentials.is_some() || + init.integrity.is_some() || + init.headers.is_some() || + init.method.is_some() || + init.mode.is_some() || + init.redirect.is_some() || + init.referrer.is_some() || + init.referrerPolicy.is_some() || + !init.window.is_undefined() { + // Step 13.1 + if request.mode == NetTraitsRequestMode::Navigate { + return Err(Error::Type( + "Init is present and request mode is 'navigate'".to_string())); + } + // Step 13.2 + request.omit_origin_header.set(false); + // Step 13.3 + *request.referer.borrow_mut() = NetTraitsRequestReferer::Client; + // Step 13.4 + request.referrer_policy.set(None); + } + + // Step 14 + if let Some(init_referrer) = init.referrer.as_ref() { + // Step 14.1 + let ref referrer = init_referrer.0; + // Step 14.2 + if referrer.is_empty() { + *request.referer.borrow_mut() = NetTraitsRequestReferer::NoReferer; + } else { + // Step 14.3 + let parsed_referrer = base_url.join(referrer); + // Step 14.4 + if parsed_referrer.is_err() { + return Err(Error::Type( + "Failed to parse referrer url".to_string())); + } + // Step 14.5 + if let Ok(parsed_referrer) = parsed_referrer { + if parsed_referrer.cannot_be_a_base() && + parsed_referrer.scheme() == "about" && + parsed_referrer.path() == "client" { + *request.referer.borrow_mut() = NetTraitsRequestReferer::Client; + } else { + // Step 14.6 + if parsed_referrer.origin() != origin { + return Err(Error::Type( + "RequestInit's referrer has invalid origin".to_string())); + } + // TODO: Requires Step 7. + + // Step 14.7 + *request.referer.borrow_mut() = NetTraitsRequestReferer::RefererUrl(parsed_referrer); + } + } + } + } + + // Step 15 + if let Some(init_referrerpolicy) = init.referrerPolicy.as_ref() { + let init_referrer_policy = init_referrerpolicy.clone().into(); + request.referrer_policy.set(Some(init_referrer_policy)); + } + + // Step 16 + let mode = init.mode.as_ref().map(|m| m.clone().into()).or(fallback_mode); + + // Step 17 + if let Some(NetTraitsRequestMode::Navigate) = mode { + return Err(Error::Type("Request mode is Navigate".to_string())); + } + + // Step 18 + if let Some(m) = mode { + request.mode = m; + } + + // Step 19 + let credentials = init.credentials.as_ref().map(|m| m.clone().into()).or(fallback_credentials); + + // Step 20 + if let Some(c) = credentials { + request.credentials_mode = c; + } + + // Step 21 + if let Some(init_cache) = init.cache.as_ref() { + let cache = init_cache.clone().into(); + request.cache_mode.set(cache); + } + + // Step 22 + if request.cache_mode.get() == NetTraitsRequestCache::OnlyIfCached { + if request.mode != NetTraitsRequestMode::SameOrigin { + return Err(Error::Type( + "Cache is 'only-if-cached' and mode is not 'same-origin'".to_string())); + } + } + + // Step 23 + if let Some(init_redirect) = init.redirect.as_ref() { + let redirect = init_redirect.clone().into(); + request.redirect_mode.set(redirect); + } + + // Step 24 + if let Some(init_integrity) = init.integrity.as_ref() { + let integrity = init_integrity.clone().to_string(); + *request.integrity_metadata.borrow_mut() = integrity; + } + + // Step 25 + if let Some(init_method) = init.method.as_ref() { + // Step 25.1 + if !is_method(&init_method) { + return Err(Error::Type("Method is not a method".to_string())); + } + if is_forbidden_method(&init_method) { + return Err(Error::Type("Method is forbidden".to_string())); + } + // Step 25.2 + let method_lower = init_method.to_lower(); + let method_string = match method_lower.as_str() { + Some(s) => s, + None => return Err(Error::Type("Method is not a valid UTF8".to_string())), + }; + let normalized_method = normalize_method(method_string); + // Step 25.3 + let hyper_method = normalized_method_to_typed_method(&normalized_method); + *request.method.borrow_mut() = hyper_method; + } + + // Step 26 + let r = Request::from_net_request(global, + false, + request); + r.headers.or_init(|| Headers::for_request(r.global().r())); + + // Step 27 + let mut headers_copy = r.Headers(); + + // This is equivalent to the specification's concept of + // "associated headers list". + if let RequestInfo::Request(ref input_request) = input { + headers_copy = input_request.Headers(); + } + + // Step 28 + if let Some(possible_header) = init.headers.as_ref() { + if let &HeadersOrByteStringSequenceSequence::Headers(ref init_headers) = possible_header { + headers_copy = init_headers.clone(); + } + } + + // Step 29 + r.Headers().empty_header_list(); + + // Step 30 + if r.request.borrow().mode == NetTraitsRequestMode::NoCORS { + let borrowed_request = r.request.borrow(); + // Step 30.1 + if !is_cors_safelisted_method(&borrowed_request.method.borrow()) { + return Err(Error::Type( + "The mode is 'no-cors' but the method is not a cors-safelisted method".to_string())); + } + // Step 30.2 + if !borrowed_request.integrity_metadata.borrow().is_empty() { + return Err(Error::Type("Integrity metadata is not an empty string".to_string())); + } + // Step 30.3 + r.Headers().set_guard(Guard::RequestNoCors); + } + + // Step 31 + r.Headers().fill(Some(HeadersOrByteStringSequenceSequence::Headers(headers_copy))); + + // Step 32 + let input_body = if let RequestInfo::Request(ref input_request) = input { + let input_request_request = input_request.request.borrow(); + let body = input_request_request.body.borrow(); + body.clone() + } else { + None + }; + + // Step 33 + if let Some(init_body_option) = init.body.as_ref() { + if init_body_option.is_some() || input_body.is_some() { + let req = r.request.borrow(); + let req_method = req.method.borrow(); + match &*req_method { + &hyper::method::Method::Get => return Err(Error::Type( + "Init's body is non-null, and request method is GET".to_string())), + &hyper::method::Method::Head => return Err(Error::Type( + "Init's body is non-null, and request method is HEAD".to_string())), + _ => {}, + } + } + } + + // Step 34 + // TODO: `ReadableStream` object is not implemented in Servo yet. + + // Step 35 + { + let borrowed_request = r.request.borrow(); + *borrowed_request.body.borrow_mut() = input_body; + } + + // Step 36 + let extracted_mime_type = r.Headers().extract_mime_type(); + *r.mime_type.borrow_mut() = extracted_mime_type; + + // Step 37 + // TODO: `ReadableStream` object is not implemented in Servo yet. + + // Step 38 + Ok(r) + } +} + +impl Request { + fn from_net_request(global: GlobalRef, + is_service_worker_global_scope: bool, + net_request: NetTraitsRequest) -> Root<Request> { + let r = Request::new(global, + net_request.current_url(), + is_service_worker_global_scope); + *r.request.borrow_mut() = net_request; + r + } + + fn clone_from(r: &Request) -> Root<Request> { + let req = r.request.borrow(); + let url = req.url(); + let is_service_worker_global_scope = req.is_service_worker_global_scope; + let body_used = r.body_used.get(); + let mime_type = r.mime_type.borrow().clone(); + let headers_guard = r.Headers().get_guard(); + let r_clone = reflect_dom_object( + box Request::new_inherited(r.global().r(), + url, + is_service_worker_global_scope), + r.global().r(), RequestBinding::Wrap); + r_clone.request.borrow_mut().pipeline_id.set(req.pipeline_id.get()); + { + let mut borrowed_r_request = r_clone.request.borrow_mut(); + *borrowed_r_request.origin.borrow_mut() = req.origin.borrow().clone(); + } + *r_clone.request.borrow_mut() = req.clone(); + r_clone.body_used.set(body_used); + *r_clone.mime_type.borrow_mut() = mime_type; + r_clone.Headers().set_guard(headers_guard); + r_clone + } +} + +fn net_request_from_global(global: GlobalRef, + url: Url, + is_service_worker_global_scope: bool) -> NetTraitsRequest { + let origin = Origin::Origin(global.get_url().origin()); + let pipeline_id = global.pipeline(); + NetTraitsRequest::new(url, + Some(origin), + is_service_worker_global_scope, + Some(pipeline_id)) +} + +// https://fetch.spec.whatwg.org/#concept-request-current-url +fn get_current_url(req: &NetTraitsRequest) -> Option<Ref<Url>> { + let url_list = req.url_list.borrow(); + if url_list.len() > 0 { + Some(Ref::map(url_list, |urls| urls.last().unwrap())) + } else { + None + } +} + +fn normalized_method_to_typed_method(m: &str) -> hyper::method::Method { + match m { + "DELETE" => hyper::method::Method::Delete, + "GET" => hyper::method::Method::Get, + "HEAD" => hyper::method::Method::Head, + "OPTIONS" => hyper::method::Method::Options, + "POST" => hyper::method::Method::Post, + "PUT" => hyper::method::Method::Put, + a => hyper::method::Method::Extension(a.to_string()) + } +} + +// https://fetch.spec.whatwg.org/#concept-method-normalize +fn normalize_method(m: &str) -> String { + match m { + "delete" => "DELETE".to_string(), + "get" => "GET".to_string(), + "head" => "HEAD".to_string(), + "options" => "OPTIONS".to_string(), + "post" => "POST".to_string(), + "put" => "PUT".to_string(), + a => a.to_string(), + } +} + +// https://fetch.spec.whatwg.org/#concept-method +fn is_method(m: &ByteString) -> bool { + match m.to_lower().as_str() { + Some("get") => true, + Some("head") => true, + Some("post") => true, + Some("put") => true, + Some("delete") => true, + Some("connect") => true, + Some("options") => true, + Some("trace") => true, + _ => false, + } +} + +// https://fetch.spec.whatwg.org/#forbidden-method +fn is_forbidden_method(m: &ByteString) -> bool { + match m.to_lower().as_str() { + Some("connect") => true, + Some("trace") => true, + Some("track") => true, + _ => false, + } +} + +// https://fetch.spec.whatwg.org/#cors-safelisted-method +fn is_cors_safelisted_method(m: &hyper::method::Method) -> bool { + m == &hyper::method::Method::Get || + m == &hyper::method::Method::Head || + m == &hyper::method::Method::Post +} + +// https://url.spec.whatwg.org/#include-credentials +fn includes_credentials(input: &Url) -> bool { + !input.username().is_empty() || input.password().is_some() +} + +// TODO: `Readable Stream` object is not implemented in Servo yet. +// https://fetch.spec.whatwg.org/#concept-body-disturbed +fn request_is_disturbed(input: &Request) -> bool { + false +} + +// TODO: `Readable Stream` object is not implemented in Servo yet. +// https://fetch.spec.whatwg.org/#concept-body-locked +fn request_is_locked(input: &Request) -> bool { + false +} + +impl RequestMethods for Request { + // https://fetch.spec.whatwg.org/#dom-request-method + fn Method(&self) -> ByteString { + let r = self.request.borrow(); + let m = r.method.borrow(); + ByteString::new(m.as_ref().as_bytes().into()) + } + + // https://fetch.spec.whatwg.org/#dom-request-url + fn Url(&self) -> USVString { + let r = self.request.borrow(); + let url = r.url_list.borrow(); + USVString(url.get(0).map_or("", |u| u.as_str()).into()) + } + + // https://fetch.spec.whatwg.org/#dom-request-headers + fn Headers(&self) -> Root<Headers> { + self.headers.or_init(|| Headers::new(self.global().r())) + } + + // https://fetch.spec.whatwg.org/#dom-request-type + fn Type(&self) -> RequestType { + self.request.borrow().type_.into() + } + + // https://fetch.spec.whatwg.org/#dom-request-destination + fn Destination(&self) -> RequestDestination { + self.request.borrow().destination.into() + } + + // https://fetch.spec.whatwg.org/#dom-request-referrer + fn Referrer(&self) -> USVString { + let r = self.request.borrow(); + let referrer = r.referer.borrow(); + USVString(match &*referrer { + &NetTraitsRequestReferer::NoReferer => String::from("no-referrer"), + &NetTraitsRequestReferer::Client => String::from("client"), + &NetTraitsRequestReferer::RefererUrl(ref u) => { + let u_c = u.clone(); + u_c.into_string() + } + }) + } + + // https://fetch.spec.whatwg.org/#dom-request-referrerpolicy + fn ReferrerPolicy(&self) -> ReferrerPolicy { + self.request.borrow().referrer_policy.get().map(|m| m.into()).unwrap_or(ReferrerPolicy::_empty) + } + + // https://fetch.spec.whatwg.org/#dom-request-mode + fn Mode(&self) -> RequestMode { + self.request.borrow().mode.into() + } + + // https://fetch.spec.whatwg.org/#dom-request-credentials + fn Credentials(&self) -> RequestCredentials { + let r = self.request.borrow().clone(); + r.credentials_mode.into() + } + + // https://fetch.spec.whatwg.org/#dom-request-cache + fn Cache(&self) -> RequestCache { + let r = self.request.borrow().clone(); + r.cache_mode.get().into() + } + + // https://fetch.spec.whatwg.org/#dom-request-redirect + fn Redirect(&self) -> RequestRedirect { + let r = self.request.borrow().clone(); + r.redirect_mode.get().into() + } + + // https://fetch.spec.whatwg.org/#dom-request-integrity + fn Integrity(&self) -> DOMString { + let r = self.request.borrow(); + let integrity = r.integrity_metadata.borrow(); + DOMString::from_string(integrity.clone()) + } + + // https://fetch.spec.whatwg.org/#dom-body-bodyused + fn BodyUsed(&self) -> bool { + self.body_used.get() + } + + // https://fetch.spec.whatwg.org/#dom-request-clone + fn Clone(&self) -> Fallible<Root<Request>> { + // Step 1 + if request_is_locked(self) { + return Err(Error::Type("Request is locked".to_string())); + } + if request_is_disturbed(self) { + return Err(Error::Type("Request is disturbed".to_string())); + } + + // Step 2 + Ok(Request::clone_from(self)) + } +} + +impl Into<NetTraitsRequestCache> for RequestCache { + fn into(self) -> NetTraitsRequestCache { + match self { + RequestCache::Default => NetTraitsRequestCache::Default, + RequestCache::No_store => NetTraitsRequestCache::NoStore, + RequestCache::Reload => NetTraitsRequestCache::Reload, + RequestCache::No_cache => NetTraitsRequestCache::NoCache, + RequestCache::Force_cache => NetTraitsRequestCache::ForceCache, + RequestCache::Only_if_cached => NetTraitsRequestCache::OnlyIfCached, + } + } +} + +impl Into<RequestCache> for NetTraitsRequestCache { + fn into(self) -> RequestCache { + match self { + NetTraitsRequestCache::Default => RequestCache::Default, + NetTraitsRequestCache::NoStore => RequestCache::No_store, + NetTraitsRequestCache::Reload => RequestCache::Reload, + NetTraitsRequestCache::NoCache => RequestCache::No_cache, + NetTraitsRequestCache::ForceCache => RequestCache::Force_cache, + NetTraitsRequestCache::OnlyIfCached => RequestCache::Only_if_cached, + } + } +} + +impl Into<NetTraitsRequestCredentials> for RequestCredentials { + fn into(self) -> NetTraitsRequestCredentials { + match self { + RequestCredentials::Omit => NetTraitsRequestCredentials::Omit, + RequestCredentials::Same_origin => NetTraitsRequestCredentials::CredentialsSameOrigin, + RequestCredentials::Include => NetTraitsRequestCredentials::Include, + } + } +} + +impl Into<RequestCredentials> for NetTraitsRequestCredentials { + fn into(self) -> RequestCredentials { + match self { + NetTraitsRequestCredentials::Omit => RequestCredentials::Omit, + NetTraitsRequestCredentials::CredentialsSameOrigin => RequestCredentials::Same_origin, + NetTraitsRequestCredentials::Include => RequestCredentials::Include, + } + } +} + +impl Into<NetTraitsRequestDestination> for RequestDestination { + fn into(self) -> NetTraitsRequestDestination { + match self { + RequestDestination::_empty => NetTraitsRequestDestination::None, + RequestDestination::Document => NetTraitsRequestDestination::Document, + RequestDestination::Embed => NetTraitsRequestDestination::Embed, + RequestDestination::Font => NetTraitsRequestDestination::Font, + RequestDestination::Image => NetTraitsRequestDestination::Image, + RequestDestination::Manifest => NetTraitsRequestDestination::Manifest, + RequestDestination::Media => NetTraitsRequestDestination::Media, + RequestDestination::Object => NetTraitsRequestDestination::Object, + RequestDestination::Report => NetTraitsRequestDestination::Report, + RequestDestination::Script => NetTraitsRequestDestination::Script, + RequestDestination::Serviceworker => NetTraitsRequestDestination::ServiceWorker, + RequestDestination::Sharedworker => NetTraitsRequestDestination::SharedWorker, + RequestDestination::Style => NetTraitsRequestDestination::Style, + RequestDestination::Worker => NetTraitsRequestDestination::Worker, + RequestDestination::Xslt => NetTraitsRequestDestination::XSLT, + } + } +} + +impl Into<RequestDestination> for NetTraitsRequestDestination { + fn into(self) -> RequestDestination { + match self { + NetTraitsRequestDestination::None => RequestDestination::_empty, + NetTraitsRequestDestination::Document => RequestDestination::Document, + NetTraitsRequestDestination::Embed => RequestDestination::Embed, + NetTraitsRequestDestination::Font => RequestDestination::Font, + NetTraitsRequestDestination::Image => RequestDestination::Image, + NetTraitsRequestDestination::Manifest => RequestDestination::Manifest, + NetTraitsRequestDestination::Media => RequestDestination::Media, + NetTraitsRequestDestination::Object => RequestDestination::Object, + NetTraitsRequestDestination::Report => RequestDestination::Report, + NetTraitsRequestDestination::Script => RequestDestination::Script, + NetTraitsRequestDestination::ServiceWorker => RequestDestination::Serviceworker, + NetTraitsRequestDestination::SharedWorker => RequestDestination::Sharedworker, + NetTraitsRequestDestination::Style => RequestDestination::Style, + NetTraitsRequestDestination::XSLT => RequestDestination::Xslt, + NetTraitsRequestDestination::Worker => RequestDestination::Worker, + } + } +} + +impl Into<NetTraitsRequestType> for RequestType { + fn into(self) -> NetTraitsRequestType { + match self { + RequestType::_empty => NetTraitsRequestType::None, + RequestType::Audio => NetTraitsRequestType::Audio, + RequestType::Font => NetTraitsRequestType::Font, + RequestType::Image => NetTraitsRequestType::Image, + RequestType::Script => NetTraitsRequestType::Script, + RequestType::Style => NetTraitsRequestType::Style, + RequestType::Track => NetTraitsRequestType::Track, + RequestType::Video => NetTraitsRequestType::Video, + } + } +} + +impl Into<RequestType> for NetTraitsRequestType { + fn into(self) -> RequestType { + match self { + NetTraitsRequestType::None => RequestType::_empty, + NetTraitsRequestType::Audio => RequestType::Audio, + NetTraitsRequestType::Font => RequestType::Font, + NetTraitsRequestType::Image => RequestType::Image, + NetTraitsRequestType::Script => RequestType::Script, + NetTraitsRequestType::Style => RequestType::Style, + NetTraitsRequestType::Track => RequestType::Track, + NetTraitsRequestType::Video => RequestType::Video, + } + } +} + +impl Into<NetTraitsRequestMode> for RequestMode { + fn into(self) -> NetTraitsRequestMode { + match self { + RequestMode::Navigate => NetTraitsRequestMode::Navigate, + RequestMode::Same_origin => NetTraitsRequestMode::SameOrigin, + RequestMode::No_cors => NetTraitsRequestMode::NoCORS, + RequestMode::Cors => NetTraitsRequestMode::CORSMode, + } + } +} + +impl Into<RequestMode> for NetTraitsRequestMode { + fn into(self) -> RequestMode { + match self { + NetTraitsRequestMode::Navigate => RequestMode::Navigate, + NetTraitsRequestMode::SameOrigin => RequestMode::Same_origin, + NetTraitsRequestMode::NoCORS => RequestMode::No_cors, + NetTraitsRequestMode::CORSMode => RequestMode::Cors, + } + } +} + +// TODO +// When whatwg/fetch PR #346 is merged, fix this. +impl Into<MsgReferrerPolicy> for ReferrerPolicy { + fn into(self) -> MsgReferrerPolicy { + match self { + ReferrerPolicy::_empty => MsgReferrerPolicy::NoReferrer, + ReferrerPolicy::No_referrer => MsgReferrerPolicy::NoReferrer, + ReferrerPolicy::No_referrer_when_downgrade => + MsgReferrerPolicy::NoReferrerWhenDowngrade, + ReferrerPolicy::Origin => MsgReferrerPolicy::Origin, + ReferrerPolicy::Origin_when_cross_origin => MsgReferrerPolicy::OriginWhenCrossOrigin, + ReferrerPolicy::Unsafe_url => MsgReferrerPolicy::UnsafeUrl, + } + } +} + +impl Into<ReferrerPolicy> for MsgReferrerPolicy { + fn into(self) -> ReferrerPolicy { + match self { + MsgReferrerPolicy::NoReferrer => ReferrerPolicy::No_referrer, + MsgReferrerPolicy::NoReferrerWhenDowngrade => + ReferrerPolicy::No_referrer_when_downgrade, + MsgReferrerPolicy::Origin => ReferrerPolicy::Origin, + MsgReferrerPolicy::SameOrigin => ReferrerPolicy::Origin, + MsgReferrerPolicy::OriginWhenCrossOrigin => ReferrerPolicy::Origin_when_cross_origin, + MsgReferrerPolicy::UnsafeUrl => ReferrerPolicy::Unsafe_url, + } + } +} + +impl Into<NetTraitsRequestRedirect> for RequestRedirect { + fn into(self) -> NetTraitsRequestRedirect { + match self { + RequestRedirect::Follow => NetTraitsRequestRedirect::Follow, + RequestRedirect::Error => NetTraitsRequestRedirect::Error, + RequestRedirect::Manual => NetTraitsRequestRedirect::Manual, + } + } +} + +impl Into<RequestRedirect> for NetTraitsRequestRedirect { + fn into(self) -> RequestRedirect { + match self { + NetTraitsRequestRedirect::Follow => RequestRedirect::Follow, + NetTraitsRequestRedirect::Error => RequestRedirect::Error, + NetTraitsRequestRedirect::Manual => RequestRedirect::Manual, + } + } +} + +impl Clone for HeadersOrByteStringSequenceSequence { + fn clone(&self) -> HeadersOrByteStringSequenceSequence { + match self { + &HeadersOrByteStringSequenceSequence::Headers(ref h) => + HeadersOrByteStringSequenceSequence::Headers(h.clone()), + &HeadersOrByteStringSequenceSequence::ByteStringSequenceSequence(ref b) => + HeadersOrByteStringSequenceSequence::ByteStringSequenceSequence(b.clone()), + } + } +} diff --git a/components/script/dom/webidls/Body.webidl b/components/script/dom/webidls/Body.webidl new file mode 100644 index 00000000000..a020228a01d --- /dev/null +++ b/components/script/dom/webidls/Body.webidl @@ -0,0 +1,19 @@ +/* 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/. */ + +// https://fetch.spec.whatwg.org/#body + +[NoInterfaceObject, + Exposed=(Window,Worker)] + +interface Body { + readonly attribute boolean bodyUsed; + + // Servo does not support Promise at this moment. + // [NewObject] Promise<ArrayBuffer> arrayBuffer(); + // [NewObject] Promise<Blob> blob(); + // [NewObject] Promise<FormData> formData(); + // [NewObject] Promise<JSON> json(); + // [NewObject] Promise<USVString> text(); +}; diff --git a/components/script/dom/webidls/Request.webidl b/components/script/dom/webidls/Request.webidl new file mode 100644 index 00000000000..11393b07be0 --- /dev/null +++ b/components/script/dom/webidls/Request.webidl @@ -0,0 +1,108 @@ +/* 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/. */ + +// https://fetch.spec.whatwg.org/#request-class + +typedef (Request or USVString) RequestInfo; + +[Constructor(RequestInfo input, optional RequestInit init), + Exposed=(Window,Worker)] + +interface Request { + readonly attribute ByteString method; + readonly attribute USVString url; + [SameObject] readonly attribute Headers headers; + readonly attribute RequestType type; + readonly attribute RequestDestination destination; + readonly attribute USVString referrer; + readonly attribute ReferrerPolicy referrerPolicy; + readonly attribute RequestMode mode; + readonly attribute RequestCredentials credentials; + readonly attribute RequestCache cache; + readonly attribute RequestRedirect redirect; + readonly attribute DOMString integrity; + [NewObject, Throws] Request clone(); +}; + +Request implements Body; + +dictionary RequestInit { + ByteString method; + HeadersInit headers; + BodyInit? body; + USVString referrer; + ReferrerPolicy referrerPolicy; + RequestMode mode; + RequestCredentials credentials; + RequestCache cache; + RequestRedirect redirect; + DOMString integrity; + any window; // can only be set to null +}; + +enum RequestType { + "", + "audio", + "font", + "image", + "script", + "style", + "track", + "video" +}; + +enum RequestDestination { + "", + "document", + "embed", + "font", + "image", + "manifest", + "media", + "object", + "report", + "script", + "serviceworker", + "sharedworker", + "style", + "worker", + "xslt" +}; + +enum RequestMode { + "navigate", + "same-origin", + "no-cors", + "cors" +}; + +enum RequestCredentials { + "omit", + "same-origin", + "include" +}; + +enum RequestCache { + "default", + "no-store", + "reload", + "no-cache", + "force-cache", + "only-if-cached" +}; + +enum RequestRedirect { + "follow", + "error", + "manual" +}; + +enum ReferrerPolicy { + "", + "no-referrer", + "no-referrer-when-downgrade", + "origin", + "origin-when-cross-origin", + "unsafe-url" +}; |