aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorJeena Lee <ijeenalee@gmail.com>2016-07-19 18:38:02 -0700
committerJeena Lee <ijeenalee@gmail.com>2016-08-12 15:39:40 -0700
commitfabe2b8f7e5693d705427959a07a380d52c16e26 (patch)
treed15718e7f30f6182be6774c470e394b6bdc1cf18 /components/script
parentb7facf41cbc7ba727666e95fd0c390d432d862fa (diff)
downloadservo-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.rs2
-rw-r--r--components/script/dom/headers.rs135
-rw-r--r--components/script/dom/mod.rs1
-rw-r--r--components/script/dom/request.rs820
-rw-r--r--components/script/dom/webidls/Body.webidl19
-rw-r--r--components/script/dom/webidls/Request.webidl108
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"
+};