servo: Merge #12700 - Implement the Request API (from jeenalee:jeena-requestAPI); r=jdm
authorJeena Lee <ijeenalee@gmail.com>
Fri, 12 Aug 2016 23:54:49 -0500
changeset 339487 b9860b026ee43a1cfd859c43d271b6ef477b566f
parent 339486 c867e0247b066cb67a48ad4fbfcb1fbaf06a5abf
child 339488 a8854cb72e4b3a929abeca5698adb6f795094cef
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
servo: Merge #12700 - Implement the Request API (from jeenalee:jeena-requestAPI); r=jdm Implement the Request API <!-- Please describe your changes on the following line: --> This PR implements the [Request API](https://fetch.spec.whatwg.org/#request-class) for the Fetch API, including its attributes and constructor, and introduces changes in relevant files. This Request integrates `net_traits::request::Request` and `dom::headers`. There are few related TODOs and comments: 1. `net_traits::request::Request`'s `headers` field does not point to `dom::request::Request`'s `headers_reflector`. 2. Every Constructor step that involves `Readable Stream` object is not implemented. 3. Every Constructor step that involves `entry settings object` or `environment settings object` is not implemented. 4. `./mach build -d` does not report any error, but prints a few warnings about unused variables related to (1) and (2). 5. Enum `ReferrerPolicy` generated by `RequestBinding` does not match `net_traits::request::Request`'s implementation. 6. `Promise`s in Body webidl are commented out. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #11895 (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [X] These changes do not require tests because tests for the Request API already exists, but this commit does not implement the interface fully. <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 78160bf3f967ad34c671fe953de578bfa0b9542b
servo/components/net_traits/request.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/headers.rs
servo/components/script/dom/mod.rs
servo/components/script/dom/request.rs
servo/components/script/dom/webidls/Body.webidl
servo/components/script/dom/webidls/Request.webidl
--- a/servo/components/net_traits/request.rs
+++ b/servo/components/net_traits/request.rs
@@ -5,102 +5,102 @@
 use hyper::header::Headers;
 use hyper::method::Method;
 use msg::constellation_msg::{PipelineId, ReferrerPolicy};
 use std::cell::{Cell, RefCell};
 use std::mem::swap;
 use url::{Origin as UrlOrigin, Url};
 
 /// An [initiator](https://fetch.spec.whatwg.org/#concept-request-initiator)
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
 pub enum Initiator {
     None,
     Download,
     ImageSet,
     Manifest,
     XSLT
 }
 
 /// A request [type](https://fetch.spec.whatwg.org/#concept-request-type)
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
 pub enum Type {
     None, Audio, Font, Image,
     Script, Style, Track, Video
 }
 
 /// A request [destination](https://fetch.spec.whatwg.org/#concept-request-destination)
-#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
 pub enum Destination {
     None, Document, Embed, Font, Image, Manifest,
     Media, Object, Report, Script, ServiceWorker,
     SharedWorker, Style, Worker, XSLT
 }
 
 /// A request [origin](https://fetch.spec.whatwg.org/#concept-request-origin)
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
 pub enum Origin {
     Client,
     Origin(UrlOrigin)
 }
 
 /// A [referer](https://fetch.spec.whatwg.org/#concept-request-referrer)
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, HeapSizeOf)]
 pub enum Referer {
     NoReferer,
     /// Default referer if nothing is specified
     Client,
     RefererUrl(Url)
 }
 
 /// A [request mode](https://fetch.spec.whatwg.org/#concept-request-mode)
-#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
 pub enum RequestMode {
     Navigate,
     SameOrigin,
     NoCORS,
     CORSMode
 }
 
 /// Request [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode)
-#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
 pub enum CredentialsMode {
     Omit,
     CredentialsSameOrigin,
     Include
 }
 
 /// [Cache mode](https://fetch.spec.whatwg.org/#concept-request-cache-mode)
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
 pub enum CacheMode {
     Default,
     NoStore,
     Reload,
     NoCache,
     ForceCache,
     OnlyIfCached
 }
 
 /// [Redirect mode](https://fetch.spec.whatwg.org/#concept-request-redirect-mode)
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
 pub enum RedirectMode {
     Follow,
     Error,
     Manual
 }
 
 /// [Response tainting](https://fetch.spec.whatwg.org/#concept-request-response-tainting)
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
 pub enum ResponseTainting {
     Basic,
     CORSTainting,
     Opaque
 }
 
 /// [Window](https://fetch.spec.whatwg.org/#concept-request-window)
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
 pub enum Window {
     NoWindow,
     Client,
     // TODO: Environmental settings object
 }
 
 /// [CORS settings attribute](https://html.spec.whatwg.org/multipage/#attr-crossorigin-anonymous)
 #[derive(Copy, Clone, PartialEq)]
@@ -133,21 +133,23 @@ pub struct RequestInit {
     pub origin: Url,
     // XXXManishearth these should be part of the client object
     pub referer_url: Option<Url>,
     pub referrer_policy: Option<ReferrerPolicy>,
     pub pipeline_id: Option<PipelineId>,
 }
 
 /// A [Request](https://fetch.spec.whatwg.org/#requests) as defined by the Fetch spec
-#[derive(Clone)]
+#[derive(Clone, HeapSizeOf)]
 pub struct Request {
+    #[ignore_heap_size_of = "Defined in hyper"]
     pub method: RefCell<Method>,
     pub local_urls_only: bool,
     pub sandboxed_storage_area_urls: bool,
+    #[ignore_heap_size_of = "Defined in hyper"]
     pub headers: RefCell<Headers>,
     pub unsafe_request: bool,
     pub body: RefCell<Option<Vec<u8>>>,
     // TODO: client object
     pub is_service_worker_global_scope: bool,
     // pub client: GlobalRef, // XXXManishearth copy over only the relevant fields of the global scope,
                               // not the entire scope to avoid the libscript dependency
     pub window: Cell<Window>,
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -55,16 +55,17 @@ use js::glue::{CallObjectTracer, CallUnb
 use js::jsapi::{GCTraceKindToAscii, Heap, TraceKind, JSObject, JSTracer};
 use js::jsval::JSVal;
 use js::rust::Runtime;
 use libc;
 use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeType, ReferrerPolicy};
 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};
 use offscreen_gl_context::GLLimits;
 use profile_traits::mem::ProfilerChan as MemProfilerChan;
 use profile_traits::time::ProfilerChan as TimeProfilerChan;
 use script_layout_interface::OpaqueStyleAndLayoutData;
 use script_layout_interface::reporter::CSSErrorReporter;
@@ -320,16 +321,17 @@ no_jsmanaged_fields!(PseudoElement);
 no_jsmanaged_fields!(Length);
 no_jsmanaged_fields!(ElementState);
 no_jsmanaged_fields!(DOMString);
 no_jsmanaged_fields!(Mime);
 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);
 no_jsmanaged_fields!(ReferrerPolicy);
 no_jsmanaged_fields!(ResourceThreads);
 no_jsmanaged_fields!(SystemTime);
 no_jsmanaged_fields!(SelectedFileId);
 no_jsmanaged_fields!(RelativePos);
--- a/servo/components/script/dom/headers.rs
+++ b/servo/components/script/dom/headers.rs
@@ -7,138 +7,112 @@ use dom::bindings::codegen::Bindings::He
 use dom::bindings::codegen::Bindings::HeadersBinding::HeadersMethods;
 use dom::bindings::codegen::UnionTypes::HeadersOrByteStringSequenceSequence;
 use dom::bindings::error::{Error, ErrorResult, Fallible};
 use dom::bindings::global::GlobalRef;
 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,
     RequestNoCors,
     Response,
     None,
 }
 
 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
-        let mut combined_value = self.header_list.borrow_mut().get_raw(&valid_name).unwrap()[0].clone();
-        combined_value.push(b","[0]);
+        let mut combined_value: Vec<u8> = vec![];
+        if let Some(v) = self.header_list.borrow().get_raw(&valid_name) {
+            combined_value = v[0].clone();
+            combined_value.push(b","[0]);
+        }
         combined_value.extend(valid_value.iter().cloned());
         self.header_list.borrow_mut().set_raw(valid_name, vec![combined_value]);
         Ok(())
     }
 
     // https://fetch.spec.whatwg.org/#dom-headers-delete
     fn Delete(&self, name: ByteString) -> ErrorResult {
         // 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
         self.header_list.borrow_mut().remove_raw(&valid_name);
         Ok(())
     }
 
     // https://fetch.spec.whatwg.org/#dom-headers-get
@@ -161,38 +135,96 @@ impl HeadersMethods for Headers {
     // https://fetch.spec.whatwg.org/#dom-headers-set
     fn Set(&self, name: ByteString, value: ByteString) -> Fallible<()> {
         // 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
         // https://fetch.spec.whatwg.org/#concept-header-list-set
         self.header_list.borrow_mut().set_raw(valid_name, vec![valid_value]);
         Ok(())
     }
 }
 
+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)) => {
+                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`,
 // or `text/plain`.
 // "DPR", "Downlink", "Save-Data", "Viewport-Width", "Width":
 // once parsed, the value should not be failure.
 // https://fetch.spec.whatwg.org/#cors-safelisted-request-header
 fn is_cors_safelisted_request_header(name: &str) -> bool {
@@ -310,30 +342,34 @@ fn is_field_name(name: &ByteString) -> b
 }
 
 // https://tools.ietf.org/html/rfc7230#section-3.2
 // http://www.rfc-editor.org/errata_search.php?rfc=7230
 // Errata ID: 4189
 // field-content = field-vchar [ 1*( SP / HTAB / field-vchar )
 //                               field-vchar ]
 fn is_field_content(value: &ByteString) -> bool {
-    if value.len() == 0 {
+    let value_len = value.len();
+
+    if value_len == 0 {
         return false;
     }
     if !is_field_vchar(value[0]) {
         return false;
     }
 
-    for &ch in &value[1..value.len() - 1] {
-        if !is_field_vchar(ch) || !is_space(ch) || !is_htab(ch) {
-            return false;
+    if value_len > 2 {
+        for &ch in &value[1..value_len - 1] {
+            if !is_field_vchar(ch) && !is_space(ch) && !is_htab(ch) {
+                return false;
+            }
         }
     }
 
-    if !is_field_vchar(value[value.len() - 1]) {
+    if !is_field_vchar(value[value_len - 1]) {
         return false;
     }
 
     return true;
 }
 
 fn is_space(x: u8) -> bool {
     x == b' '
--- a/servo/components/script/dom/mod.rs
+++ b/servo/components/script/dom/mod.rs
@@ -363,16 +363,17 @@ pub mod performance;
 pub mod performancetiming;
 pub mod plugin;
 pub mod pluginarray;
 pub mod popstateevent;
 pub mod processinginstruction;
 pub mod progressevent;
 pub mod radionodelist;
 pub mod range;
+pub mod request;
 pub mod screen;
 pub mod serviceworker;
 pub mod serviceworkercontainer;
 pub mod serviceworkerglobalscope;
 pub mod serviceworkerregistration;
 pub mod servohtmlparser;
 pub mod servoxmlparser;
 pub mod storage;
new file mode 100644
--- /dev/null
+++ b/servo/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()),
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/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();
+};
new file mode 100644
--- /dev/null
+++ b/servo/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"
+};